From 5b44f26bc286ccbcd0602031e4a48fbdbb605dcd Mon Sep 17 00:00:00 2001 From: Nic Polumeyv Date: Mon, 28 Sep 2026 16:57:36 -0400 Subject: [PATCH] fix: keep a reactive assignment with no dependencies when `migrate` cannot turn it into a declaration (#18817) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `migrate()` dropped a reactive assignment that has no dependencies and put nothing in its place: ```svelte $: count = 7; → (gone, `{count}` now undeclared) $: obj.x = 1; → (gone) ``` A binding the statement declares now always gets its `let`, and the statement is only removed once every target has one; otherwise it becomes `run()`: ```svelte let count = $state(7); run(() => { obj.x = 1; }); ``` --- .changeset/migrate-keep-assignment.md | 5 +++++ packages/svelte/src/compiler/migrate/index.js | 13 +++++++++++-- .../input.svelte | 8 ++++++++ .../output.svelte | 14 ++++++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 .changeset/migrate-keep-assignment.md create mode 100644 packages/svelte/tests/migrate/samples/labeled-assignment-without-dependencies/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/labeled-assignment-without-dependencies/output.svelte diff --git a/.changeset/migrate-keep-assignment.md b/.changeset/migrate-keep-assignment.md new file mode 100644 index 0000000000..6b5be9d6be --- /dev/null +++ b/.changeset/migrate-keep-assignment.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: keep a reactive assignment with no dependencies when `migrate` cannot turn it into a declaration diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 00f997e054..68d5171237 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -989,8 +989,11 @@ const instance_script = { return; } + let declared = 0; for (const binding of bindings) { - if (binding.reassigned && (ids.includes(binding.node) || expression_ids.length === 0)) { + // a binding this statement declares needs its `let` whether or not it is reassigned + if (ids.includes(binding.node) || (binding.reassigned && expression_ids.length === 0)) { + declared++; check_rune_binding('state'); const init = binding.kind === 'state' @@ -1006,7 +1009,13 @@ const instance_script = { } } - if (expression_ids.length === 0 && bindings.every((b) => b.kind !== 'store_sub')) { + // the statement goes only once every target has a declaration standing in for it + if ( + expression_ids.length === 0 && + bindings.length > 0 && + declared === bindings.length && + bindings.every((b) => b.kind !== 'store_sub') + ) { state.str.remove(/** @type {number} */ (node.start), /** @type {number} */ (node.end)); return; } diff --git a/packages/svelte/tests/migrate/samples/labeled-assignment-without-dependencies/input.svelte b/packages/svelte/tests/migrate/samples/labeled-assignment-without-dependencies/input.svelte new file mode 100644 index 0000000000..552dc3ff55 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/labeled-assignment-without-dependencies/input.svelte @@ -0,0 +1,8 @@ + + +

{count} {other} {obj.x}

diff --git a/packages/svelte/tests/migrate/samples/labeled-assignment-without-dependencies/output.svelte b/packages/svelte/tests/migrate/samples/labeled-assignment-without-dependencies/output.svelte new file mode 100644 index 0000000000..b2ec179aec --- /dev/null +++ b/packages/svelte/tests/migrate/samples/labeled-assignment-without-dependencies/output.svelte @@ -0,0 +1,14 @@ + + +

{count} {other} {obj.x}

\ No newline at end of file