diff --git a/.changeset/weak-drinks-speak.md b/.changeset/weak-drinks-speak.md new file mode 100644 index 0000000000..dc3847b112 --- /dev/null +++ b/.changeset/weak-drinks-speak.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: ensure bind:this unmount behavior for members is conditional diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index 46327006a2..8e675ce692 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -917,6 +917,15 @@ function serialize_bind_this(bind_this, context, node) { /** @type {import('estree').Expression[]} */ const args = [node, b.arrow([b.id('$$value'), ...ids], update), b.arrow([...ids], bind_this_id)]; + // If we're mutating a property, then it might already be non-existent. + // If we make all the object nodes optional, then it avoids any runtime exceptions. + /** @type {import('estree').Expression | import('estree').Super} */ + let bind_node = bind_this_id; + + while (bind_node?.type === 'MemberExpression') { + bind_node.optional = true; + bind_node = bind_node.object; + } if (each_ids.size) { args.push(b.thunk(b.array(Array.from(each_ids.values()).map((id) => id[1])))); } diff --git a/packages/svelte/tests/runtime-runes/samples/each-bind-this-member-2/Child.svelte b/packages/svelte/tests/runtime-runes/samples/each-bind-this-member-2/Child.svelte new file mode 100644 index 0000000000..410b337a2a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/each-bind-this-member-2/Child.svelte @@ -0,0 +1,7 @@ + + +