From 158b3436c3381c08ac8fbcea9c7e2bb433b90c23 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Jul 2024 22:27:25 -0400 Subject: [PATCH 1/9] chore: make each items reassignable --- .changeset/thirty-guests-flow.md | 5 +++++ .../compiler/phases/3-transform/server/transform-server.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/thirty-guests-flow.md diff --git a/.changeset/thirty-guests-flow.md b/.changeset/thirty-guests-flow.md new file mode 100644 index 000000000000..7eccdb08323a --- /dev/null +++ b/.changeset/thirty-guests-flow.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: make each items reassignable diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index 5b0f16db9d31..38d986e67207 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -1347,7 +1347,7 @@ const template_visitors = { state.init.push(b.const(array_id, b.call('$.ensure_array_like', collection))); /** @type {import('estree').Statement[]} */ - const each = [b.const(item, b.member(array_id, index, true))]; + const each = [b.let(item, b.member(array_id, index, true))]; if (node.context.type !== 'Identifier') { each.push(b.const(/** @type {import('estree').Pattern} */ (node.context), item)); From 02af294e6b97c3f361e79042f3d8d4a6cfa1ab30 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Jul 2024 22:41:51 -0400 Subject: [PATCH 2/9] add failing test --- .../Child.svelte | 7 +++++++ .../_config.js | 19 +++++++++++++++++++ .../main.svelte | 13 +++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 packages/svelte/tests/runtime-legacy/samples/component-binding-each-reassigned/Child.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/component-binding-each-reassigned/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/component-binding-each-reassigned/main.svelte diff --git a/packages/svelte/tests/runtime-legacy/samples/component-binding-each-reassigned/Child.svelte b/packages/svelte/tests/runtime-legacy/samples/component-binding-each-reassigned/Child.svelte new file mode 100644 index 000000000000..75d673156921 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/component-binding-each-reassigned/Child.svelte @@ -0,0 +1,7 @@ + + +

{value}

diff --git a/packages/svelte/tests/runtime-legacy/samples/component-binding-each-reassigned/_config.js b/packages/svelte/tests/runtime-legacy/samples/component-binding-each-reassigned/_config.js new file mode 100644 index 000000000000..83937ea5c2b2 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/component-binding-each-reassigned/_config.js @@ -0,0 +1,19 @@ +import { test } from '../../test'; + +export default test({ + html: ` +

2, 3, 4

+

2

+

3

+

4

+

2, 3, 4

+ `, + + ssrHtml: ` +

1, 2, 3

+

2

+

3

+

4

+

1, 2, 3

+ ` +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/component-binding-each-reassigned/main.svelte b/packages/svelte/tests/runtime-legacy/samples/component-binding-each-reassigned/main.svelte new file mode 100644 index 000000000000..193723817e32 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/component-binding-each-reassigned/main.svelte @@ -0,0 +1,13 @@ + + +

{numbers.join(', ')}

+ +{#each numbers as n} + +{/each} + +

{numbers.join(', ')}

From fcb55322d075adc466affbd5b1d1ec1d213b5ada Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 11 Aug 2024 08:23:46 -0400 Subject: [PATCH 3/9] update test --- .../each-string-template/_expected/server/index.svelte.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js index 9258fb774783..8a71ceeb1c2f 100644 --- a/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js @@ -6,7 +6,7 @@ export default function Each_string_template($$payload) { $$payload.out += ``; for (let $$index = 0; $$index < each_array.length; $$index++) { - const thing = each_array[$$index]; + let thing = each_array[$$index]; $$payload.out += `${$.escape(thing)}, `; } From 65474354817fa4ee9a01c9e152834104b6cbe6cf Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 19 Sep 2024 21:43:55 -0400 Subject: [PATCH 4/9] transform reassigned getters inside each block --- .../3-transform/client/visitors/EachBlock.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js index 495a33e3974d..a4ae0d8697ca 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js @@ -186,8 +186,21 @@ export function EachBlock(node, context) { if (invalidate_store) sequence.push(invalidate_store); if (node.context.type === 'Identifier') { + const name = node.context.name; + const binding = /** @type {Binding} */ (context.state.scope.get(name)); + child_state.transform[node.context.name] = { - read: (flags & EACH_ITEM_REACTIVE) !== 0 ? get_value : (node) => node, + read: (node) => { + if (binding.reassigned) { + return b.member( + each_node_meta.array_name ? b.call(each_node_meta.array_name) : collection, + index, + true + ); + } + + return (flags & EACH_ITEM_REACTIVE) !== 0 ? get_value(node) : node; + }, assign: (_, value) => { uses_index = true; From 286b3f97dfd5fde1dce69d4d4a5d42846da17665 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 19 Sep 2024 21:44:24 -0400 Subject: [PATCH 5/9] Update .changeset/thirty-guests-flow.md --- .changeset/thirty-guests-flow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/thirty-guests-flow.md b/.changeset/thirty-guests-flow.md index 7eccdb08323a..7f2287af5348 100644 --- a/.changeset/thirty-guests-flow.md +++ b/.changeset/thirty-guests-flow.md @@ -2,4 +2,4 @@ 'svelte': patch --- -fix: make each items reassignable +fix: make each items reassignable in legacy mode From a8a815b1bfc8a12fd78f9877653791f667d24e2b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 19 Sep 2024 21:45:14 -0400 Subject: [PATCH 6/9] comment --- .../src/compiler/phases/3-transform/client/visitors/EachBlock.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js index a4ae0d8697ca..210b5da8a2c0 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js @@ -192,6 +192,7 @@ export function EachBlock(node, context) { child_state.transform[node.context.name] = { read: (node) => { if (binding.reassigned) { + // we need to do `array[$$index]` instead of `$$item` or whatever return b.member( each_node_meta.array_name ? b.call(each_node_meta.array_name) : collection, index, From a7dce7c1bfa8c22d3b9c49c0e9adae74ea594920 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 19 Sep 2024 21:45:41 -0400 Subject: [PATCH 7/9] add note --- .../compiler/phases/3-transform/client/visitors/EachBlock.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js index 210b5da8a2c0..26e77ac9a19e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js @@ -193,6 +193,8 @@ export function EachBlock(node, context) { read: (node) => { if (binding.reassigned) { // we need to do `array[$$index]` instead of `$$item` or whatever + // TODO this only applies in legacy mode, reassignments are + // forbidden in runes mode return b.member( each_node_meta.array_name ? b.call(each_node_meta.array_name) : collection, index, From deb5a175b2a6a23abce48d66ad80a8de5bb69b2a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 19 Sep 2024 21:51:53 -0400 Subject: [PATCH 8/9] tidy up --- .../compiler/phases/3-transform/client/visitors/EachBlock.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js index 26e77ac9a19e..a2e580403263 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js @@ -186,8 +186,7 @@ export function EachBlock(node, context) { if (invalidate_store) sequence.push(invalidate_store); if (node.context.type === 'Identifier') { - const name = node.context.name; - const binding = /** @type {Binding} */ (context.state.scope.get(name)); + const binding = /** @type {Binding} */ (context.state.scope.get(node.context.name)); child_state.transform[node.context.name] = { read: (node) => { From 3c32413ea5bd5522922508a8a6e16f977b2f99bb Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 20 Sep 2024 08:46:33 -0400 Subject: [PATCH 9/9] Update packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js --- .../compiler/phases/3-transform/client/visitors/EachBlock.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js index a2e580403263..fb6338cae312 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js @@ -192,7 +192,7 @@ export function EachBlock(node, context) { read: (node) => { if (binding.reassigned) { // we need to do `array[$$index]` instead of `$$item` or whatever - // TODO this only applies in legacy mode, reassignments are + // TODO 6.0 this only applies in legacy mode, reassignments are // forbidden in runes mode return b.member( each_node_meta.array_name ? b.call(each_node_meta.array_name) : collection,