diff --git a/.changeset/weak-doors-yell.md b/.changeset/weak-doors-yell.md new file mode 100644 index 000000000000..1b21783435a4 --- /dev/null +++ b/.changeset/weak-doors-yell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't transform reassigned state in labeled statement in `$derived` diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 9d79d88b2397..523389a25aef 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -944,54 +944,53 @@ const instance_script = { node.body.type === 'ExpressionStatement' && node.body.expression.type === 'AssignmentExpression' ) { - const ids = extract_identifiers(node.body.expression.left); - const [, expression_ids] = extract_all_identifiers_from_expression( - node.body.expression.right - ); - const bindings = ids.map((id) => state.scope.get(id.name)); - const reassigned_bindings = bindings.filter((b) => b?.reassigned); + const { left, right } = node.body.expression; - if ( - node.body.expression.right.type !== 'Literal' && - !bindings.some((b) => b?.kind === 'store_sub') && - node.body.expression.left.type !== 'MemberExpression' - ) { - let { start, end } = /** @type {{ start: number, end: number }} */ ( - node.body.expression.right - ); + const ids = extract_identifiers(left); + const [, expression_ids] = extract_all_identifiers_from_expression(right); + const bindings = ids.map((id) => /** @type {Binding} */ (state.scope.get(id.name))); - check_rune_binding('derived'); + if (bindings.every((b) => b.kind === 'legacy_reactive')) { + if ( + right.type !== 'Literal' && + bindings.every((b) => b.kind !== 'store_sub') && + left.type !== 'MemberExpression' + ) { + let { start, end } = /** @type {{ start: number, end: number }} */ (right); - // $derived - state.str.update( - /** @type {number} */ (node.start), - /** @type {number} */ (node.body.expression.start), - 'let ' - ); + check_rune_binding('derived'); - if (node.body.expression.right.type === 'SequenceExpression') { - while (state.str.original[start] !== '(') start -= 1; - while (state.str.original[end - 1] !== ')') end += 1; - } + // $derived + state.str.update( + /** @type {number} */ (node.start), + /** @type {number} */ (node.body.expression.start), + 'let ' + ); + + if (right.type === 'SequenceExpression') { + while (state.str.original[start] !== '(') start -= 1; + while (state.str.original[end - 1] !== ')') end += 1; + } + + state.str.prependRight(start, `$derived(`); - state.str.prependRight(start, `$derived(`); + // in a case like `$: ({ a } = b())`, there's already a trailing parenthesis. + // otherwise, we need to add one + if (state.str.original[/** @type {number} */ (node.body.start)] !== '(') { + state.str.appendLeft(end, `)`); + } - // in a case like `$: ({ a } = b())`, there's already a trailing parenthesis. - // otherwise, we need to add one - if (state.str.original[/** @type {number} */ (node.body.start)] !== '(') { - state.str.appendLeft(end, `)`); + return; } - return; - } else { - for (const binding of reassigned_bindings) { - if (binding && (ids.includes(binding.node) || expression_ids.length === 0)) { + for (const binding of bindings) { + if (binding.reassigned && (ids.includes(binding.node) || expression_ids.length === 0)) { check_rune_binding('state'); const init = binding.kind === 'state' ? ' = $state()' : expression_ids.length === 0 - ? ` = $state(${state.str.original.substring(/** @type {number} */ (node.body.expression.right.start), node.body.expression.right.end)})` + ? ` = $state(${state.str.original.substring(/** @type {number} */ (right.start), right.end)})` : ''; // implicitly-declared variable which we need to make explicit state.str.prependLeft( @@ -1000,7 +999,8 @@ const instance_script = { ); } } - if (expression_ids.length === 0 && !bindings.some((b) => b?.kind === 'store_sub')) { + + if (expression_ids.length === 0 && 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-statement-reassign-state/input.svelte b/packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/input.svelte new file mode 100644 index 000000000000..0b5c13d8898b --- /dev/null +++ b/packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/input.svelte @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/output.svelte b/packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/output.svelte new file mode 100644 index 000000000000..c2b36a6e3028 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/output.svelte @@ -0,0 +1,10 @@ + \ No newline at end of file