mirror of
https://github.com/sveltejs/svelte.git
synced 2024-12-01 01:11:24 +01:00
fix: reorder reactive statements during migration (#12329)
fixes #12183
This commit is contained in:
parent
bbf489504d
commit
2ffb55ce7d
5
.changeset/few-cheetahs-taste.md
Normal file
5
.changeset/few-cheetahs-taste.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: reorder reactive statements during migration
|
@ -145,6 +145,47 @@ export function migrate(source) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, then we need to move all reactive statements to the end of the script block,
|
||||
* in their correct order. Svelte 4 reordered reactive statements, $derived/$effect.pre
|
||||
* don't have this behavior.
|
||||
*/
|
||||
let needs_reordering = false;
|
||||
|
||||
for (const [node, { dependencies }] of state.analysis.reactive_statements) {
|
||||
/** @type {Compiler.Binding[]} */
|
||||
let ids = [];
|
||||
if (
|
||||
node.body.type === 'ExpressionStatement' &&
|
||||
node.body.expression.type === 'AssignmentExpression'
|
||||
) {
|
||||
ids = extract_identifiers(node.body.expression.left)
|
||||
.map((id) => state.scope.get(id.name))
|
||||
.filter((id) => !!id);
|
||||
}
|
||||
|
||||
if (
|
||||
dependencies.some(
|
||||
(dep) =>
|
||||
!ids.includes(dep) &&
|
||||
/** @type {number} */ (dep.node.start) > /** @type {number} */ (node.start)
|
||||
)
|
||||
) {
|
||||
needs_reordering = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_reordering) {
|
||||
const nodes = Array.from(state.analysis.reactive_statements.keys());
|
||||
for (const node of nodes) {
|
||||
const { start, end } = get_node_range(source, node);
|
||||
str.appendLeft(end, '\n');
|
||||
str.move(start, end, /** @type {number} */ (parsed.instance?.content.end));
|
||||
str.remove(start - (source[start - 2] === '\r' ? 2 : 1), start);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.needs_run && !added_legacy_import) {
|
||||
if (parsed.instance) {
|
||||
str.appendRight(
|
||||
@ -377,7 +418,7 @@ const instance_script = {
|
||||
/** @type {number} */ (node.body.expression.start),
|
||||
'let '
|
||||
);
|
||||
state.str.prependLeft(
|
||||
state.str.prependRight(
|
||||
/** @type {number} */ (node.body.expression.right.start),
|
||||
'$derived('
|
||||
);
|
||||
@ -388,14 +429,14 @@ const instance_script = {
|
||||
');'
|
||||
);
|
||||
} else {
|
||||
state.str.appendRight(/** @type {number} */ (node.end), ');');
|
||||
state.str.appendLeft(/** @type {number} */ (node.end), ');');
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
for (const binding of reassigned_bindings) {
|
||||
if (binding && ids.includes(binding.node)) {
|
||||
// implicitly-declared variable which we need to make explicit
|
||||
state.str.prependLeft(
|
||||
state.str.prependRight(
|
||||
/** @type {number} */ (node.start),
|
||||
`let ${binding.node.name}${binding.kind === 'state' ? ' = $state()' : ''};\n${state.indent}`
|
||||
);
|
||||
@ -428,7 +469,7 @@ const instance_script = {
|
||||
[/** @type {number} */ (node.body.end), state.end]
|
||||
]
|
||||
});
|
||||
state.str.appendRight(/** @type {number} */ (node.end), `\n${state.indent}});`);
|
||||
state.str.appendLeft(/** @type {number} */ (node.end), `\n${state.indent}});`);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -808,6 +849,30 @@ function get_indent(state, ...nodes) {
|
||||
return indent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns start and end of the node. If the start is preceeded with white-space-only before a line break,
|
||||
* the start will be the start of the line.
|
||||
* @param {string} source
|
||||
* @param {Node} node
|
||||
*/
|
||||
function get_node_range(source, node) {
|
||||
let start = /** @type {number} */ (node.start);
|
||||
let end = /** @type {number} */ (node.end);
|
||||
|
||||
let idx = start;
|
||||
while (source[idx - 1] !== '\n' && source[idx - 1] !== '\r') {
|
||||
idx--;
|
||||
if (source[idx] !== ' ' && source[idx] !== '\t') {
|
||||
idx = start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
start = idx;
|
||||
|
||||
return { start, end };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Compiler.OnDirective} last
|
||||
* @param {State} state
|
||||
|
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
$: mobile = width < 640;
|
||||
$: x = !mobile;
|
||||
let width = 0;
|
||||
</script>
|
||||
|
||||
{width / mobile / x}
|
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
let width = 0;
|
||||
let mobile = $derived(width < 640);
|
||||
let x = $derived(!mobile);
|
||||
</script>
|
||||
|
||||
{width / mobile / x}
|
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
let width = 0;
|
||||
$: console.log(mobile);
|
||||
$: mobile = width < 640;
|
||||
</script>
|
||||
|
||||
{width / mobile}
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
let width = 0;
|
||||
let mobile = $derived(width < 640);
|
||||
run(() => {
|
||||
console.log(mobile);
|
||||
});
|
||||
</script>
|
||||
|
||||
{width / mobile}
|
Loading…
Reference in New Issue
Block a user