fix: more accurately detect `$derived` migration use cases (#13740)

- detect store mutations and not use `$derived` in that case, fixes #13723
- better detect `let x` that can be folded into `$derived`, fixes #13727

---------

Co-authored-by: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com>
pull/13746/head
Simon H 2 months ago committed by GitHub
parent 8a06d051e8
commit a08f063b37
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: more accurately detect `$derived` migration opportunities

@ -569,14 +569,22 @@ const instance_script = {
const declaration = reference.path.find((el) => el.type === 'VariableDeclaration'); const declaration = reference.path.find((el) => el.type === 'VariableDeclaration');
const assignment = reference.path.find((el) => el.type === 'AssignmentExpression'); const assignment = reference.path.find((el) => el.type === 'AssignmentExpression');
const update = reference.path.find((el) => el.type === 'UpdateExpression'); const update = reference.path.find((el) => el.type === 'UpdateExpression');
const labeled = reference.path.find( const labeled = /** @type {LabeledStatement | undefined} */ (
(el) => el.type === 'LabeledStatement' && el.label.name === '$' reference.path.find((el) => el.type === 'LabeledStatement' && el.label.name === '$')
); );
if (assignment && labeled) { if (
assignment &&
labeled &&
// ensure that $: foo = bar * 2 is not counted as a reassignment of bar
(labeled.body.type !== 'ExpressionStatement' ||
labeled.body.expression !== assignment ||
(assignment.left.type === 'Identifier' &&
assignment.left.name === binding.node.name))
) {
if (assignment_in_labeled) return false; if (assignment_in_labeled) return false;
assignment_in_labeled = /** @type {AssignmentExpression} */ (assignment); assignment_in_labeled = /** @type {AssignmentExpression} */ (assignment);
labeled_statement = /** @type {LabeledStatement} */ (labeled); labeled_statement = labeled;
} }
return ( return (
@ -750,7 +758,12 @@ const instance_script = {
); );
const bindings = ids.map((id) => state.scope.get(id.name)); const bindings = ids.map((id) => state.scope.get(id.name));
const reassigned_bindings = bindings.filter((b) => b?.reassigned); const reassigned_bindings = bindings.filter((b) => b?.reassigned);
if (reassigned_bindings.length === 0 && !bindings.some((b) => b?.kind === 'store_sub')) {
if (
reassigned_bindings.length === 0 &&
!bindings.some((b) => b?.kind === 'store_sub') &&
node.body.expression.left.type !== 'MemberExpression'
) {
let { start, end } = /** @type {{ start: number, end: number }} */ ( let { start, end } = /** @type {{ start: number, end: number }} */ (
node.body.expression.right node.body.expression.right
); );

@ -6,6 +6,11 @@
// no semicolon at the end // no semicolon at the end
$: time_8 = count * 8 $: time_8 = count * 8
$: ({ time_16 } = { time_16: count * 16 }) $: ({ time_16 } = { time_16: count * 16 })
// preceeding let that doesn't do anything
let time_32;
$: time_32 = count * doubled;
let very_high;
$: very_high = time_32 * count;
</script> </script>
{count} / {doubled} / {quadrupled} / {time_8} / {time_16} {count} / {doubled} / {quadrupled} / {time_8} / {time_16}

@ -6,6 +6,11 @@
// no semicolon at the end // no semicolon at the end
let time_8 = $derived(count * 8) let time_8 = $derived(count * 8)
let { time_16 } = $derived({ time_16: count * 16 }) let { time_16 } = $derived({ time_16: count * 16 })
// preceeding let that doesn't do anything
let time_32 = $derived(count * doubled);
let very_high = $derived(time_32 * count);
</script> </script>
{count} / {doubled} / {quadrupled} / {time_8} / {time_16} {count} / {doubled} / {quadrupled} / {time_8} / {time_16}

@ -10,4 +10,7 @@
console.log('bar'); console.log('bar');
} }
$: $count = 1; $: $count = 1;
$: foo.x = count;
</script> </script>
<button onclick={() => count++}>increment</button>

@ -1,7 +1,7 @@
<script> <script>
import { run } from 'svelte/legacy'; import { run } from 'svelte/legacy';
let count = 0; let count = $state(0);
run(() => { run(() => {
console.log(count); console.log(count);
}); });
@ -18,4 +18,9 @@
run(() => { run(() => {
$count = 1; $count = 1;
}); });
</script> run(() => {
foo.x = count;
});
</script>
<button onclick={() => count++}>increment</button>
Loading…
Cancel
Save