From 5382336a2796df14d756ab39317678f6500b0784 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 25 Nov 2025 11:17:43 +0100 Subject: [PATCH 1/4] fix: generate correct code for simple destructurings --- .changeset/lovely-windows-shout.md | 5 +++++ .../3-transform/shared/transform-async.js | 22 +++++++++++-------- .../async-derived-destructured/Child.svelte | 4 ++++ .../async-derived-destructured/_config.js | 2 ++ 4 files changed, 24 insertions(+), 9 deletions(-) create mode 100644 .changeset/lovely-windows-shout.md diff --git a/.changeset/lovely-windows-shout.md b/.changeset/lovely-windows-shout.md new file mode 100644 index 000000000000..d0b748a876fa --- /dev/null +++ b/.changeset/lovely-windows-shout.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: generate correct code for simple destructurings diff --git a/packages/svelte/src/compiler/phases/3-transform/shared/transform-async.js b/packages/svelte/src/compiler/phases/3-transform/shared/transform-async.js index 6ec789345230..6ca44aca654b 100644 --- a/packages/svelte/src/compiler/phases/3-transform/shared/transform-async.js +++ b/packages/svelte/src/compiler/phases/3-transform/shared/transform-async.js @@ -61,15 +61,19 @@ export function transform_body(instance_body, runner, transform) { } // if we have multiple declarations, it indicates destructuring - return b.thunk( - b.block([ - b.var(visited.declarations[0].id, visited.declarations[0].init), - ...visited.declarations - .slice(1) - .map((d) => b.stmt(b.assignment('=', d.id, d.init ?? b.void0))) - ]), - s.has_await - ); + const is_generated = /** @type {ESTree.Identifier} */ ( + visited.declarations[0].id + ).name?.startsWith('$$d'); // bit hacky; this is the generated id from VariableDeclaration.js + const declarations = is_generated + ? [ + b.var(visited.declarations[0].id, visited.declarations[0].init), + ...visited.declarations + .slice(1) + .map((d) => b.stmt(b.assignment('=', d.id, d.init ?? b.void0))) + ] + : visited.declarations.map((d) => b.stmt(b.assignment('=', d.id, d.init ?? b.void0))); + + return b.thunk(b.block(declarations), s.has_await); } if (s.node.type === 'ClassDeclaration') { diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-destructured/Child.svelte b/packages/svelte/tests/runtime-runes/samples/async-derived-destructured/Child.svelte index 39112b12a782..9f0da2259412 100644 --- a/packages/svelte/tests/runtime-runes/samples/async-derived-destructured/Child.svelte +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-destructured/Child.svelte @@ -1,6 +1,9 @@ + +{id}-{name} From c1b1fa554bae1368b505bb6efa51623fac3b8a89 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 25 Nov 2025 22:47:43 +0100 Subject: [PATCH 3/4] adjust existing test so it fails on main --- .../_config.js | 13 ------------- .../main.svelte | 13 ------------- .../samples/async-derived-destructured/Child.svelte | 6 +++--- .../samples/async-derived-destructured/_config.js | 4 ++-- 4 files changed, 5 insertions(+), 31 deletions(-) delete mode 100644 packages/svelte/tests/runtime-runes/samples/async-derived-destructured-after-await/_config.js delete mode 100644 packages/svelte/tests/runtime-runes/samples/async-derived-destructured-after-await/main.svelte diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-destructured-after-await/_config.js b/packages/svelte/tests/runtime-runes/samples/async-derived-destructured-after-await/_config.js deleted file mode 100644 index 52ed6fdeffe9..000000000000 --- a/packages/svelte/tests/runtime-runes/samples/async-derived-destructured-after-await/_config.js +++ /dev/null @@ -1,13 +0,0 @@ -import { tick } from 'svelte'; -import { test } from '../../test'; - -export default test({ - skip_mode: ['server'], - - ssrHtml: '1-Rich', - - async test({ assert, target }) { - await tick(); - assert.htmlEqual(target.innerHTML, '1-Rich'); - } -}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-destructured-after-await/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-derived-destructured-after-await/main.svelte deleted file mode 100644 index fc976f0b967c..000000000000 --- a/packages/svelte/tests/runtime-runes/samples/async-derived-destructured-after-await/main.svelte +++ /dev/null @@ -1,13 +0,0 @@ - - -{id}-{name} diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-destructured/Child.svelte b/packages/svelte/tests/runtime-runes/samples/async-derived-destructured/Child.svelte index 9f0da2259412..fdf7184e3c87 100644 --- a/packages/svelte/tests/runtime-runes/samples/async-derived-destructured/Child.svelte +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-destructured/Child.svelte @@ -1,17 +1,17 @@

{count} ** 2 = {squared}

{count} ** 3 = {cubed}

-

{typeof toFixed}

+

{typeof toFixed} {typeof toString}

diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-destructured/_config.js b/packages/svelte/tests/runtime-runes/samples/async-derived-destructured/_config.js index 989395b31697..bfd582ea9719 100644 --- a/packages/svelte/tests/runtime-runes/samples/async-derived-destructured/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/async-derived-destructured/_config.js @@ -13,7 +13,7 @@ export default test({

1 ** 2 = 1

1 ** 3 = 1

-

function

+

function function

` ); @@ -26,7 +26,7 @@ export default test({

2 ** 2 = 4

2 ** 3 = 8

-

function

+

function function

` ); } From 45415e8e2e2eda8f5178d5f4dfe946ab26a2ba5e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 25 Nov 2025 16:47:58 -0500 Subject: [PATCH 4/4] slightly neater approach (with identical outcome) --- .../3-transform/shared/transform-async.js | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/shared/transform-async.js b/packages/svelte/src/compiler/phases/3-transform/shared/transform-async.js index 6ca44aca654b..22c4beb08ada 100644 --- a/packages/svelte/src/compiler/phases/3-transform/shared/transform-async.js +++ b/packages/svelte/src/compiler/phases/3-transform/shared/transform-async.js @@ -53,27 +53,22 @@ export function transform_body(instance_body, runner, transform) { transform(b.var(s.node.id, s.node.init)) ); - if (visited.declarations.length === 1) { - return b.thunk( - b.assignment('=', visited.declarations[0].id, visited.declarations[0].init ?? b.void0), - s.has_await - ); - } + const statements = visited.declarations.map((node) => { + if (node.id.type === 'Identifier' && node.id.name.startsWith('$$d')) { + // this is an intermediate declaration created in VariableDeclaration.js; + // subsequent statements depend on it + return b.var(node.id, node.init); + } + + return b.stmt(b.assignment('=', node.id, node.init ?? b.void0)); + }); - // if we have multiple declarations, it indicates destructuring - const is_generated = /** @type {ESTree.Identifier} */ ( - visited.declarations[0].id - ).name?.startsWith('$$d'); // bit hacky; this is the generated id from VariableDeclaration.js - const declarations = is_generated - ? [ - b.var(visited.declarations[0].id, visited.declarations[0].init), - ...visited.declarations - .slice(1) - .map((d) => b.stmt(b.assignment('=', d.id, d.init ?? b.void0))) - ] - : visited.declarations.map((d) => b.stmt(b.assignment('=', d.id, d.init ?? b.void0))); + if (statements.length === 1) { + const statement = /** @type {ESTree.ExpressionStatement} */ (statements[0]); + return b.thunk(statement.expression, s.has_await); + } - return b.thunk(b.block(declarations), s.has_await); + return b.thunk(b.block(statements), s.has_await); } if (s.node.type === 'ClassDeclaration') {