From 426792492a7298fa15d0a5f2e654f233505ef459 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Fri, 28 Jun 2024 18:11:27 +0100 Subject: [PATCH 1/3] fix: deconflict multiple snippets of the same name --- .changeset/witty-hornets-think.md | 5 +++ .../3-transform/client/visitors/template.js | 38 +++++++++++++------ .../samples/svelte-element/output.svelte | 2 +- .../samples/snippets/output.json | 4 +- .../samples/snippet-deconflict/_config.js | 24 ++++++++++++ .../samples/snippet-deconflict/main.svelte | 29 ++++++++++++++ .../_expected/client/index.svelte.js | 2 +- .../_expected/client/index.svelte.js | 2 +- 8 files changed, 90 insertions(+), 16 deletions(-) create mode 100644 .changeset/witty-hornets-think.md create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-deconflict/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-deconflict/main.svelte diff --git a/.changeset/witty-hornets-think.md b/.changeset/witty-hornets-think.md new file mode 100644 index 000000000000..2d25b7f437da --- /dev/null +++ b/.changeset/witty-hornets-think.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: deconflict multiple snippets of the same name 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 b28c73c4d8aa..d836e35bb474 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 @@ -1096,13 +1096,12 @@ function serialize_update(statement) { } /** - * - * @param {import('../types.js').ComponentClientTransformState} state + * @param {import('estree').Statement[]} update */ -function serialize_render_stmt(state) { - return state.update.length === 1 - ? serialize_update(state.update[0]) - : b.stmt(b.call('$.template_effect', b.thunk(b.block(state.update)))); +function serialize_render_stmt(update) { + return update.length === 1 + ? serialize_update(update[0]) + : b.stmt(b.call('$.template_effect', b.thunk(b.block(update)))); } /** @@ -1709,7 +1708,7 @@ export const template_visitors = { } if (state.update.length > 0) { - body.push(serialize_render_stmt(state)); + body.push(serialize_render_stmt(state.update)); } body.push(...state.after_update); @@ -2157,8 +2156,21 @@ export const template_visitors = { state.options.preserveComments ); + /** + * @type {import("estree").Statement[]} + */ + const init = []; + /** + * @type {never[]} + */ + const update = []; + /** + * @type {never[]} + */ + const after_update = []; + for (const node of hoisted) { - context.visit(node, state); + context.visit(node, { ...state, init, update, after_update }); } process_children( @@ -2171,9 +2183,13 @@ export const template_visitors = { : context.state.node ), true, - { ...context, state } + { ...context, state: { ...state, init, update, after_update } } ); + if (init.length !== 0 || update.length !== 0 || after_update.length !== 0) { + context.state.init.push(b.block([...init, serialize_render_stmt(update), ...after_update])); + } + if (has_direction_attribute) { // This fixes an issue with Chromium where updates to text content within an element // does not update the direction when set to auto. If we just re-assign the dir, this fixes it. @@ -2270,7 +2286,7 @@ export const template_visitors = { /** @type {import('estree').Statement[]} */ const inner = inner_context.state.init; if (inner_context.state.update.length > 0) { - inner.push(serialize_render_stmt(inner_context.state)); + inner.push(serialize_render_stmt(inner_context.state.update)); } inner.push(...inner_context.state.after_update); inner.push( @@ -2735,7 +2751,7 @@ export const template_visitors = { snippet = b.call('$.wrap_snippet', snippet, b.id(context.state.analysis.name)); } - const declaration = b.var(node.expression, snippet); + const declaration = b.const(node.expression, snippet); // Top-level snippets are hoisted so they can be referenced in the ` + + + +
+
+ {#snippet x(n)} +

{n}

+ {/snippet} + + {#each numbers as n} + {@render x(n)} + {/each} +
+ +
+ {#snippet x(n)} +

{n}

+ {/snippet} + + {#each numbers as n} + {@render x(n)} + {/each} +
+
diff --git a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js index 0e193af12d5f..ba4352d57b80 100644 --- a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js @@ -6,7 +6,7 @@ var root_1 = $.template(`Something`, 1); var root = $.template(` `, 1); export default function Bind_component_snippet($$anchor) { - var snippet = ($$anchor) => { + const snippet = ($$anchor) => { var fragment = root_1(); $.append($$anchor, fragment); diff --git a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js index 7cb2415bf565..0f2d6f420094 100644 --- a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js @@ -30,4 +30,4 @@ export default function State_proxy_literal($$anchor) { $.append($$anchor, fragment); } -$.delegate(["click"]); +$.delegate(["click"]); \ No newline at end of file From 46cbdd43e951ce969fd574a73b3f2e1afeaad66c Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Sat, 29 Jun 2024 22:08:20 +0100 Subject: [PATCH 2/3] address feedback --- .../phases/3-transform/client/visitors/template.js | 7 ++++++- 1 file changed, 6 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 f8d04a01c1fb..659bd4223452 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 @@ -2209,7 +2209,12 @@ export const template_visitors = { ); if (init.length !== 0 || update.length !== 0 || after_update.length !== 0) { - context.state.init.push(b.block([...init, serialize_render_stmt(update), ...after_update])); + const block = b.block([...init]); + if (update.length > 0) { + block.body.push(serialize_render_stmt(update)); + } + block.body.push(...after_update); + context.state.init.push(block); } if (has_direction_attribute) { From 1c9921877773b41229d76c1b54f5000d57f9b367 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 30 Jun 2024 09:32:27 -0400 Subject: [PATCH 3/3] only create BlockStatement when necessary --- .../3-transform/client/visitors/template.js | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 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 659bd4223452..4dcd1d408779 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 @@ -2178,21 +2178,15 @@ export const template_visitors = { state.options.preserveComments ); - /** - * @type {import("estree").Statement[]} - */ - const init = []; - /** - * @type {never[]} - */ - const update = []; - /** - * @type {never[]} - */ - const after_update = []; + /** Whether or not we need to wrap the children in `{...}` to avoid declaration conflicts */ + const has_declaration = node.fragment.nodes.some((node) => node.type === 'SnippetBlock'); + + const child_state = has_declaration + ? { ...state, init: [], update: [], after_update: [] } + : state; for (const node of hoisted) { - context.visit(node, { ...state, init, update, after_update }); + context.visit(node, child_state); } process_children( @@ -2205,16 +2199,17 @@ export const template_visitors = { : context.state.node ), true, - { ...context, state: { ...state, init, update, after_update } } + { ...context, state: child_state } ); - if (init.length !== 0 || update.length !== 0 || after_update.length !== 0) { - const block = b.block([...init]); - if (update.length > 0) { - block.body.push(serialize_render_stmt(update)); - } - block.body.push(...after_update); - context.state.init.push(block); + if (has_declaration) { + context.state.init.push( + b.block([ + ...child_state.init, + child_state.update.length > 0 ? serialize_render_stmt(child_state.update) : b.empty, + ...child_state.after_update + ]) + ); } if (has_direction_attribute) {