diff --git a/.changeset/chatty-gorillas-poke.md b/.changeset/chatty-gorillas-poke.md
new file mode 100644
index 0000000000..744adcf214
--- /dev/null
+++ b/.changeset/chatty-gorillas-poke.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: add migration task when there's a variable named that would conflict with a rune
diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js
index 387b6a5485..b7687af09f 100644
--- a/packages/svelte/src/compiler/migrate/index.js
+++ b/packages/svelte/src/compiler/migrate/index.js
@@ -239,6 +239,18 @@ export function migrate(source, { filename, use_ts } = {}) {
insertion_point = state.props_insertion_point;
+ /**
+ * @param {"derived"|"props"|"bindable"} rune
+ */
+ function check_rune_binding(rune) {
+ const has_rune_binding = !!state.scope.get(rune);
+ if (has_rune_binding) {
+ throw new MigrationError(
+ `migrating this component would require adding a \`$${rune}\` rune but there's already a variable named ${rune}.\n Rename the variable and try again or migrate by hand.`
+ );
+ }
+ }
+
if (state.props.length > 0 || analysis.uses_rest_props || analysis.uses_props) {
const has_many_props = state.props.length > 3;
const newline_separator = `\n${indent}${indent}`;
@@ -253,6 +265,7 @@ export function migrate(source, { filename, use_ts } = {}) {
let prop_str =
prop.local === prop.exported ? prop.local : `${prop.exported}: ${prop.local}`;
if (prop.bindable) {
+ check_rune_binding('bindable');
prop_str += ` = $bindable(${prop.init})`;
} else if (prop.init) {
prop_str += ` = ${prop.init}`;
@@ -300,11 +313,13 @@ export function migrate(source, { filename, use_ts } = {}) {
if (type) {
props_declaration = `${type}\n\n${indent}${props_declaration}`;
}
+ check_rune_binding('props');
props_declaration = `${props_declaration}${type ? `: ${type_name}` : ''} = $props();`;
} else {
if (type) {
props_declaration = `${state.props.length > 0 ? `${type}\n\n${indent}` : ''}/** @type {${state.props.length > 0 ? type_name : ''}${analysis.uses_props || analysis.uses_rest_props ? `${state.props.length > 0 ? ' & ' : ''}{ [key: string]: any }` : ''}} */\n${indent}${props_declaration}`;
}
+ check_rune_binding('props');
props_declaration = `${props_declaration} = $props();`;
}
@@ -361,6 +376,7 @@ export function migrate(source, { filename, use_ts } = {}) {
: insertion_point;
if (state.derived_components.size > 0) {
+ check_rune_binding('derived');
str.appendRight(
insertion_point,
`\n${indent}${[...state.derived_components.entries()].map(([init, name]) => `const ${name} = $derived(${init});`).join(`\n${indent}`)}\n`
@@ -368,6 +384,7 @@ export function migrate(source, { filename, use_ts } = {}) {
}
if (state.derived_conflicting_slots.size > 0) {
+ check_rune_binding('derived');
str.appendRight(
insertion_point,
`\n${indent}${[...state.derived_conflicting_slots.entries()].map(([name, init]) => `const ${name} = $derived(${init});`).join(`\n${indent}`)}\n`
@@ -652,6 +669,18 @@ const instance_script = {
continue;
}
+ /**
+ * @param {"state"|"derived"} rune
+ */
+ function check_rune_binding(rune) {
+ const has_rune_binding = !!state.scope.get(rune);
+ if (has_rune_binding) {
+ throw new MigrationError(
+ `can't migrate \`${state.str.original.substring(/** @type {number} */ (node.start), node.end)}\` to \`$${rune}\` because there's a variable named ${rune}.\n Rename the variable and try again or migrate by hand.`
+ );
+ }
+ }
+
// state
if (declarator.init) {
let { start, end } = /** @type {{ start: number, end: number }} */ (declarator.init);
@@ -661,6 +690,8 @@ const instance_script = {
while (state.str.original[end - 1] !== ')') end += 1;
}
+ check_rune_binding('state');
+
state.str.prependLeft(start, '$state(');
state.str.appendRight(end, ')');
} else {
@@ -755,6 +786,8 @@ const instance_script = {
}
}
+ check_rune_binding('derived');
+
// Someone wrote a `$: { ... }` statement which we can turn into a `$derived`
state.str.appendRight(
/** @type {number} */ (declarator.id.typeAnnotation?.end ?? declarator.id.end),
@@ -795,6 +828,8 @@ const instance_script = {
}
}
} else {
+ check_rune_binding('state');
+
state.str.prependLeft(
/** @type {number} */ (declarator.id.typeAnnotation?.end ?? declarator.id.end),
' = $state('
@@ -858,6 +893,18 @@ const instance_script = {
next();
+ /**
+ * @param {"state"|"derived"} rune
+ */
+ function check_rune_binding(rune) {
+ const has_rune_binding = state.scope.get(rune);
+ if (has_rune_binding) {
+ throw new MigrationError(
+ `can't migrate \`$: ${state.str.original.substring(/** @type {number} */ (node.body.start), node.body.end)}\` to \`$${rune}\` because there's a variable named ${rune}.\n Rename the variable and try again or migrate by hand.`
+ );
+ }
+ }
+
if (
node.body.type === 'ExpressionStatement' &&
node.body.expression.type === 'AssignmentExpression'
@@ -878,6 +925,8 @@ const instance_script = {
node.body.expression.right
);
+ check_rune_binding('derived');
+
// $derived
state.str.update(
/** @type {number} */ (node.start),
@@ -902,6 +951,7 @@ const instance_script = {
} else {
for (const binding of reassigned_bindings) {
if (binding && (ids.includes(binding.node) || expression_ids.length === 0)) {
+ check_rune_binding('state');
const init =
binding.kind === 'state'
? ' = $state()'
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/_config.js
new file mode 100644
index 0000000000..aceae9ce69
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/_config.js
@@ -0,0 +1,8 @@
+import { test } from '../../test';
+
+export default test({
+ logs: [
+ 'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.'
+ ],
+ errors: []
+});
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/input.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/input.svelte
new file mode 100644
index 0000000000..e66dfea9f7
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/input.svelte
@@ -0,0 +1,6 @@
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/output.svelte
new file mode 100644
index 0000000000..f956c2cd75
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$bindable-bindable-var-1/output.svelte
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/_config.js
new file mode 100644
index 0000000000..aceae9ce69
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/_config.js
@@ -0,0 +1,8 @@
+import { test } from '../../test';
+
+export default test({
+ logs: [
+ 'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.'
+ ],
+ errors: []
+});
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/input.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/input.svelte
new file mode 100644
index 0000000000..f8bde54909
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/input.svelte
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/output.svelte
new file mode 100644
index 0000000000..64c61be00c
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-1/output.svelte
@@ -0,0 +1,11 @@
+
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/_config.js
new file mode 100644
index 0000000000..aceae9ce69
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/_config.js
@@ -0,0 +1,8 @@
+import { test } from '../../test';
+
+export default test({
+ logs: [
+ 'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.'
+ ],
+ errors: []
+});
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/input.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/input.svelte
new file mode 100644
index 0000000000..0b298cc7d2
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/input.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/output.svelte
new file mode 100644
index 0000000000..01864be058
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-2/output.svelte
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/_config.js
new file mode 100644
index 0000000000..aceae9ce69
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/_config.js
@@ -0,0 +1,8 @@
+import { test } from '../../test';
+
+export default test({
+ logs: [
+ 'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.'
+ ],
+ errors: []
+});
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/input.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/input.svelte
new file mode 100644
index 0000000000..9203fdf30c
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/input.svelte
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/output.svelte
new file mode 100644
index 0000000000..9e4f086aed
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/output.svelte
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/_config.js
new file mode 100644
index 0000000000..aceae9ce69
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/_config.js
@@ -0,0 +1,8 @@
+import { test } from '../../test';
+
+export default test({
+ logs: [
+ 'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.'
+ ],
+ errors: []
+});
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/input.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/input.svelte
new file mode 100644
index 0000000000..c4179c7185
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/input.svelte
@@ -0,0 +1,10 @@
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/output.svelte
new file mode 100644
index 0000000000..05cd363682
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-4/output.svelte
@@ -0,0 +1,12 @@
+
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/_config.js
new file mode 100644
index 0000000000..aceae9ce69
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/_config.js
@@ -0,0 +1,8 @@
+import { test } from '../../test';
+
+export default test({
+ logs: [
+ 'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.'
+ ],
+ errors: []
+});
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/input.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/input.svelte
new file mode 100644
index 0000000000..bc50d58776
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/input.svelte
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/output.svelte
new file mode 100644
index 0000000000..33532dbaf5
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$props-props-var-1/output.svelte
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/_config.js
new file mode 100644
index 0000000000..aceae9ce69
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/_config.js
@@ -0,0 +1,8 @@
+import { test } from '../../test';
+
+export default test({
+ logs: [
+ 'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.'
+ ],
+ errors: []
+});
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/input.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/input.svelte
new file mode 100644
index 0000000000..8999063751
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/input.svelte
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/output.svelte
new file mode 100644
index 0000000000..d795f588ec
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-1/output.svelte
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/_config.js
new file mode 100644
index 0000000000..aceae9ce69
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/_config.js
@@ -0,0 +1,8 @@
+import { test } from '../../test';
+
+export default test({
+ logs: [
+ 'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.'
+ ],
+ errors: []
+});
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/input.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/input.svelte
new file mode 100644
index 0000000000..354110f521
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/input.svelte
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/output.svelte
new file mode 100644
index 0000000000..cf084c0364
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-2/output.svelte
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/_config.js b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/_config.js
new file mode 100644
index 0000000000..aceae9ce69
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/_config.js
@@ -0,0 +1,8 @@
+import { test } from '../../test';
+
+export default test({
+ logs: [
+ 'One or more `@migration-task` comments were added to `output.svelte`, please check them and complete the migration manually.'
+ ],
+ errors: []
+});
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/input.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/input.svelte
new file mode 100644
index 0000000000..ddf64172fe
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/input.svelte
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/output.svelte
new file mode 100644
index 0000000000..3e41b632d1
--- /dev/null
+++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$state-state-var-3/output.svelte
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file