Skip to content

Commit f341d2e

Browse files
committed
fix: properly add owners to function bindings
1 parent a00170d commit f341d2e

File tree

4 files changed

+66
-1
lines changed

4 files changed

+66
-1
lines changed

packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
/** @import { AST } from '#compiler' */
33
/** @import { ComponentContext } from '../../types.js' */
44
import { dev, is_ignored } from '../../../../../state.js';
5-
import { get_attribute_chunks, object } from '../../../../../utils/ast.js';
5+
import {
6+
extract_all_identifiers_from_expression,
7+
get_attribute_chunks,
8+
object
9+
} from '../../../../../utils/ast.js';
610
import * as b from '../../../../../utils/builders.js';
711
import { create_derived } from '../../utils.js';
812
import { build_bind_this, validate_binding } from '../shared/utils.js';
@@ -220,6 +224,32 @@ export function build_component(node, component_name, context, anchor = context.
220224

221225
push_prop(b.get(attribute.name, [b.return(b.call(get_id))]));
222226
push_prop(b.set(attribute.name, [b.stmt(b.call(set_id, b.id('$$value')))]));
227+
if (dev) {
228+
const [, get_ids] = extract_all_identifiers_from_expression(get);
229+
230+
for (let get_id of get_ids) {
231+
const binding = context.state.scope.get(get_id.name);
232+
if (
233+
binding &&
234+
binding.kind !== 'derived' &&
235+
binding.kind !== 'raw_state' &&
236+
!ownerships_effects.has(get_id.name)
237+
) {
238+
ownerships_effects.set(get_id.name, () => {
239+
binding_initializers.push(
240+
b.stmt(
241+
b.call(
242+
b.id('$.add_owner_effect'),
243+
b.thunk(get_id),
244+
b.id(component_name),
245+
is_ignored(node, 'ownership_invalid_binding') && b.true
246+
)
247+
)
248+
);
249+
});
250+
}
251+
}
252+
}
223253
}
224254
} else {
225255
if (
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script>
2+
let { arr = $bindable() } = $props();
3+
</script>
4+
5+
<button onclick={() => arr.push(arr.length)}></button>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { flushSync } from 'svelte';
2+
import { test } from '../../test';
3+
4+
export default test({
5+
compileOptions: {
6+
dev: true
7+
},
8+
test({ target, warnings, assert }) {
9+
const btn = target.querySelector('button');
10+
flushSync(() => {
11+
btn?.click();
12+
});
13+
assert.deepEqual(warnings, []);
14+
15+
flushSync(() => {
16+
btn?.click();
17+
});
18+
assert.deepEqual(warnings, []);
19+
}
20+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<script>
2+
import Child from './Child.svelte';
3+
4+
let arr = $state([]);
5+
let arr2 = $state([]);
6+
7+
let len = $derived(arr.length + arr2.length);
8+
</script>
9+
10+
<Child bind:arr={() => len % 2 === 0 ? arr : arr2, (v) => {}} />

0 commit comments

Comments
 (0)