From 9eefe34b2a83335917f52f94389f448f446095a8 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 28 Jul 2024 13:48:03 -0400 Subject: [PATCH 1/5] add each.metadata.keyed --- .../svelte/src/compiler/phases/2-analyze/index.js | 7 +++++++ .../phases/3-transform/client/visitors/template.js | 14 +++++++------- packages/svelte/src/compiler/phases/scope.js | 1 + packages/svelte/src/compiler/types/template.d.ts | 1 + 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index aa1e43196f34..aac76faa6085 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -1547,6 +1547,13 @@ const common_visitors = { }, RenderTag(node, context) { context.next({ ...context.state, render_tag: node }); + }, + EachBlock(node) { + if (node.key) { + // treat `{#each items as item, i (i)}` as a normal indexed block, everything else as keyed + node.metadata.keyed = + node.key.type !== 'Identifier' || !node.index || node.key.name !== node.index; + } } }; 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 cf0fd231e5fb..7fe24c101382 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 @@ -2409,10 +2409,7 @@ export const template_visitors = { let flags = 0; - if ( - node.key && - (node.key.type !== 'Identifier' || !node.index || node.key.name !== node.index) - ) { + if (node.metadata.keyed) { flags |= EACH_KEYED; if (node.index) { @@ -2421,7 +2418,7 @@ export const template_visitors = { // In runes mode, if key === item, we don't need to wrap the item in a source const key_is_item = - node.key.type === 'Identifier' && + /** @type {Expression} */ (node.key).type === 'Identifier' && node.context.type === 'Identifier' && node.context.name === node.key.name; @@ -2614,8 +2611,11 @@ export const template_visitors = { /** @type {Expression} */ let key_function = b.id('$.index'); - if (node.key) { - const expression = /** @type {Expression} */ (context.visit(node.key, key_state)); + if (node.metadata.keyed) { + const expression = /** @type {Expression} */ ( + context.visit(/** @type {Expression} */ (node.key), key_state) + ); + key_function = b.arrow([node.context, index], expression); } diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 1ba9851ee3c9..167f61e0345a 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -592,6 +592,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) { } node.metadata = { + keyed: false, contains_group_binding: false, array_name: needs_array_deduplication ? state.scope.root.unique('$$array') : null, index: scope.root.unique('$$index'), diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index ec35a08227f6..e5bc072c1ad8 100644 --- a/packages/svelte/src/compiler/types/template.d.ts +++ b/packages/svelte/src/compiler/types/template.d.ts @@ -393,6 +393,7 @@ export interface EachBlock extends BaseNode { index?: string; key?: Expression; metadata: { + keyed: boolean; contains_group_binding: boolean; /** Set if something in the array expression is shadowed within the each block */ array_name: Identifier | null; From 1c9d69fa272ade7ac0574edf52802d731a53056b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 28 Jul 2024 14:05:14 -0400 Subject: [PATCH 2/5] simplify --- .../phases/3-transform/client/visitors/template.js | 8 +++----- .../src/internal/client/dom/elements/bindings/input.js | 9 ++------- 2 files changed, 5 insertions(+), 12 deletions(-) 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 7fe24c101382..fb0b0442f46b 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 @@ -3049,11 +3049,9 @@ export const template_visitors = { call_expr = b.call(`$.bind_focused`, state.node, setter); break; case 'group': { - /** @type {CallExpression[]} */ - const indexes = []; - for (const parent_each_block of node.metadata.parent_each_blocks) { - indexes.push(b.call('$.unwrap', parent_each_block.metadata.index)); - } + const indexes = node.metadata.parent_each_blocks.map((each) => { + return b.call('$.unwrap', each.metadata.index); + }); // We need to additionally invoke the value attribute signal to register it as a dependency, // so that when the value is updated, the group binding is updated diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/input.js b/packages/svelte/src/internal/client/dom/elements/bindings/input.js index ca83d90f2014..35dd229d39ff 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/input.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/input.js @@ -73,13 +73,8 @@ export function bind_group(inputs, group_index, input, get_value, update) { if (group_index !== null) { for (var index of group_index) { - var group = binding_group; - // @ts-ignore - binding_group = group[index]; - if (binding_group === undefined) { - // @ts-ignore - binding_group = group[index] = []; - } + // @ts-expect-error + binding_group = binding_group[index] ??= []; } } From e5e65d071d667bb254a7e6e64699a55fd967a370 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 28 Jul 2024 14:16:39 -0400 Subject: [PATCH 3/5] feat: remove `$.unwrap` from `bind:group` code --- .../compiler/phases/3-transform/client/visitors/template.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 fb0b0442f46b..ca78a33a1c6e 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 @@ -3050,7 +3050,10 @@ export const template_visitors = { break; case 'group': { const indexes = node.metadata.parent_each_blocks.map((each) => { - return b.call('$.unwrap', each.metadata.index); + // if we have a keyed block with an index, the index is wrapped in a source + return each.metadata.keyed && each.index + ? b.call('$.get', each.metadata.index) + : each.metadata.index; }); // We need to additionally invoke the value attribute signal to register it as a dependency, From 0ca388f24485ee6248928fbaaa29acb4d79e88a5 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 28 Jul 2024 14:17:31 -0400 Subject: [PATCH 4/5] changeset --- .changeset/three-donkeys-jump.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/three-donkeys-jump.md diff --git a/.changeset/three-donkeys-jump.md b/.changeset/three-donkeys-jump.md new file mode 100644 index 000000000000..6cdc6c75561e --- /dev/null +++ b/.changeset/three-donkeys-jump.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +feat: remove `$.unwrap` calls from `bind:group` From 10faeeea9e6e802ee1b4d2544c75c0fb74807a06 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 28 Jul 2024 14:29:21 -0400 Subject: [PATCH 5/5] regenerate types --- packages/svelte/types/index.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 9de214c84798..d9537235f60d 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -1845,6 +1845,7 @@ declare module 'svelte/compiler' { index?: string; key?: Expression; metadata: { + keyed: boolean; contains_group_binding: boolean; /** Set if something in the array expression is shadowed within the each block */ array_name: Identifier | null;