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 3045814ac948..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 @@ -1098,13 +1098,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)))); } /** @@ -1732,7 +1731,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); @@ -2179,8 +2178,15 @@ export const template_visitors = { state.options.preserveComments ); + /** 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); + context.visit(node, child_state); } process_children( @@ -2193,9 +2199,19 @@ export const template_visitors = { : context.state.node ), true, - { ...context, state } + { ...context, state: child_state } ); + 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) { // 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. @@ -2292,7 +2308,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( @@ -2757,7 +2773,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 ` + + + +
{n}
+ {/snippet} + + {#each numbers as n} + {@render x(n)} + {/each} +{n}
+ {/snippet} + + {#each numbers as n} + {@render x(n)} + {/each} +