fix: deconflict multiple snippets of the same name

pull/12221/head
Dominic Gannaway 4 days ago
parent c42bb04276
commit 426792492a

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: deconflict multiple snippets of the same name

@ -1096,13 +1096,12 @@ function serialize_update(statement) {
} }
/** /**
* * @param {import('estree').Statement[]} update
* @param {import('../types.js').ComponentClientTransformState} state
*/ */
function serialize_render_stmt(state) { function serialize_render_stmt(update) {
return state.update.length === 1 return update.length === 1
? serialize_update(state.update[0]) ? serialize_update(update[0])
: b.stmt(b.call('$.template_effect', b.thunk(b.block(state.update)))); : b.stmt(b.call('$.template_effect', b.thunk(b.block(update))));
} }
/** /**
@ -1709,7 +1708,7 @@ export const template_visitors = {
} }
if (state.update.length > 0) { if (state.update.length > 0) {
body.push(serialize_render_stmt(state)); body.push(serialize_render_stmt(state.update));
} }
body.push(...state.after_update); body.push(...state.after_update);
@ -2157,8 +2156,21 @@ export const template_visitors = {
state.options.preserveComments state.options.preserveComments
); );
/**
* @type {import("estree").Statement[]}
*/
const init = [];
/**
* @type {never[]}
*/
const update = [];
/**
* @type {never[]}
*/
const after_update = [];
for (const node of hoisted) { for (const node of hoisted) {
context.visit(node, state); context.visit(node, { ...state, init, update, after_update });
} }
process_children( process_children(
@ -2171,9 +2183,13 @@ export const template_visitors = {
: context.state.node : context.state.node
), ),
true, 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) { if (has_direction_attribute) {
// This fixes an issue with Chromium where updates to text content within an element // 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. // 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[]} */ /** @type {import('estree').Statement[]} */
const inner = inner_context.state.init; const inner = inner_context.state.init;
if (inner_context.state.update.length > 0) { 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(...inner_context.state.after_update);
inner.push( inner.push(
@ -2735,7 +2751,7 @@ export const template_visitors = {
snippet = b.call('$.wrap_snippet', snippet, b.id(context.state.analysis.name)); 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 `<script>` // Top-level snippets are hoisted so they can be referenced in the `<script>`
if (context.path.length === 1 && context.path[0].type === 'Fragment') { if (context.path.length === 1 && context.path[0].type === 'Fragment') {

@ -3,4 +3,4 @@
<svelte:element this={"div"} /> <svelte:element this={"div"} />
<!-- we don't try to fix this bug, we just leave it as-is --> <!-- we don't try to fix this bug, we just leave it as-is -->
<svelte:element this="h{n}" /> <svelte:element this="h{n}" />

@ -27,8 +27,8 @@
"parameters": [ "parameters": [
{ {
"type": "Identifier", "type": "Identifier",
"name": "msg",
"start": 43, "start": 43,
"end": 46,
"loc": { "loc": {
"start": { "start": {
"line": 3, "line": 3,
@ -39,7 +39,7 @@
"column": 25 "column": 25
} }
}, },
"end": 46, "name": "msg",
"typeAnnotation": { "typeAnnotation": {
"type": "TSTypeAnnotation", "type": "TSTypeAnnotation",
"start": 46, "start": 46,

@ -0,0 +1,24 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
mode: ['client'],
test({ assert, target }) {
const btn = target.querySelector('button');
btn?.click();
btn?.click();
btn?.click();
flushSync();
assert.htmlEqual(
target.innerHTML,
`
<button>push</button><div style="display: grid; grid-template-columns: 1fr 1fr"><div><p style="color: red">1</p><p style="color: red">2</p><p style="color: red">3</p>
<p style="color: red">4</p><p style="color: red">5</p><p style="color: red">6</p></div><div><p style="color: blue">1</p><p style="color: blue">2</p><p style="color: blue">3</p>
<p style="color: blue">4</p><p style="color: blue">5</p><p style="color: blue">6</p></div></div>
`
);
}
});

@ -0,0 +1,29 @@
<script>
let numbers = $state([1, 2, 3]);
</script>
<button onclick={() => numbers.push(numbers.length + 1)}>
push
</button>
<div style="display: grid; grid-template-columns: 1fr 1fr">
<div>
{#snippet x(n)}
<p style="color: red">{n}</p>
{/snippet}
{#each numbers as n}
{@render x(n)}
{/each}
</div>
<div>
{#snippet x(n)}
<p style="color: blue">{n}</p>
{/snippet}
{#each numbers as n}
{@render x(n)}
{/each}
</div>
</div>

@ -6,7 +6,7 @@ var root_1 = $.template(`Something`, 1);
var root = $.template(`<!> `, 1); var root = $.template(`<!> `, 1);
export default function Bind_component_snippet($$anchor) { export default function Bind_component_snippet($$anchor) {
var snippet = ($$anchor) => { const snippet = ($$anchor) => {
var fragment = root_1(); var fragment = root_1();
$.append($$anchor, fragment); $.append($$anchor, fragment);

@ -30,4 +30,4 @@ export default function State_proxy_literal($$anchor) {
$.append($$anchor, fragment); $.append($$anchor, fragment);
} }
$.delegate(["click"]); $.delegate(["click"]);
Loading…
Cancel
Save