diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js index 057832fb83..e3492bfebe 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js @@ -2,7 +2,11 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../../types.js' */ import { dev, is_ignored } from '../../../../../state.js'; -import { get_attribute_chunks, object } from '../../../../../utils/ast.js'; +import { + extract_all_identifiers_from_expression, + get_attribute_chunks, + object +} from '../../../../../utils/ast.js'; import * as b from '../../../../../utils/builders.js'; import { create_derived } from '../../utils.js'; import { build_bind_this, validate_binding } from '../shared/utils.js'; @@ -220,6 +224,32 @@ export function build_component(node, component_name, context, anchor = context. push_prop(b.get(attribute.name, [b.return(b.call(get_id))])); push_prop(b.set(attribute.name, [b.stmt(b.call(set_id, b.id('$$value')))])); + if (dev) { + const [, get_ids] = extract_all_identifiers_from_expression(get); + + for (let get_id of get_ids) { + const binding = context.state.scope.get(get_id.name); + if ( + binding && + binding.kind !== 'derived' && + binding.kind !== 'raw_state' && + !ownerships_effects.has(get_id.name) + ) { + ownerships_effects.set(get_id.name, () => { + binding_initializers.push( + b.stmt( + b.call( + b.id('$.add_owner_effect'), + b.thunk(get_id), + b.id(component_name), + is_ignored(node, 'ownership_invalid_binding') && b.true + ) + ) + ); + }); + } + } + } } } else { if ( diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/Child.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/Child.svelte new file mode 100644 index 0000000000..ef91b0756d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/Child.svelte @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/_config.js b/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/_config.js new file mode 100644 index 0000000000..4c77aea206 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/_config.js @@ -0,0 +1,20 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + test({ target, warnings, assert }) { + const btn = target.querySelector('button'); + flushSync(() => { + btn?.click(); + }); + assert.deepEqual(warnings, []); + + flushSync(() => { + btn?.click(); + }); + assert.deepEqual(warnings, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/main.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/main.svelte new file mode 100644 index 0000000000..4a3ce82726 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/main.svelte @@ -0,0 +1,10 @@ + + + len % 2 === 0 ? arr : arr2, (v) => {}} /> \ No newline at end of file