diff --git a/.changeset/mighty-files-hammer.md b/.changeset/mighty-files-hammer.md
new file mode 100644
index 0000000000..889e3482d3
--- /dev/null
+++ b/.changeset/mighty-files-hammer.md
@@ -0,0 +1,5 @@
+---
+"svelte": patch
+---
+
+fix: throw validation error when binding to each argument in runes mode
diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js
index 6588e71d97..9ecd141416 100644
--- a/packages/svelte/src/compiler/errors.js
+++ b/packages/svelte/src/compiler/errors.js
@@ -207,7 +207,8 @@ const runes = {
/** @param {string} name */
'invalid-runes-mode-import': (name) => `${name} cannot be used in runes mode`,
'duplicate-props-rune': () => `Cannot use $props() more than once`,
- 'invalid-each-assignment': () => `Cannot reassign each block argument in runes mode`
+ 'invalid-each-assignment': () =>
+ `Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. 'array[i] = value' instead of 'entry = value')`
};
/** @satisfies {Errors} */
diff --git a/packages/svelte/src/compiler/phases/2-analyze/validation.js b/packages/svelte/src/compiler/phases/2-analyze/validation.js
index ca6bc00efe..8af0a29402 100644
--- a/packages/svelte/src/compiler/phases/2-analyze/validation.js
+++ b/packages/svelte/src/compiler/phases/2-analyze/validation.js
@@ -1,5 +1,5 @@
import { error } from '../../errors.js';
-import { extract_identifiers, get_parent, is_text_attribute } from '../../utils/ast.js';
+import { extract_identifiers, get_parent, is_text_attribute, object } from '../../utils/ast.js';
import { warn } from '../../warnings.js';
import fuzzymatch from '../1-parse/utils/fuzzymatch.js';
import { disallowed_parapgraph_contents, interactive_elements } from '../1-parse/utils/html.js';
@@ -341,13 +341,9 @@ const validation = {
validate_no_const_assignment(node, node.expression, context.state.scope, true);
const assignee = node.expression;
- let left = assignee;
+ const left = object(assignee);
- while (left.type === 'MemberExpression') {
- left = /** @type {import('estree').MemberExpression} */ (left.object);
- }
-
- if (left.type !== 'Identifier') {
+ if (left === null) {
error(node, 'invalid-binding-expression');
}
@@ -373,6 +369,10 @@ const validation = {
error(node.expression, 'invalid-derived-binding');
}
+ if (context.state.analysis.runes && binding.kind === 'each') {
+ error(node, 'invalid-each-assignment');
+ }
+
// TODO handle mutations of non-state/props in runes mode
}
diff --git a/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-binding/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-binding/_config.js
new file mode 100644
index 0000000000..55eb0280ae
--- /dev/null
+++ b/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-binding/_config.js
@@ -0,0 +1,9 @@
+import { test } from '../../test';
+
+export default test({
+ error: {
+ code: 'invalid-each-assignment',
+ message:
+ "Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. 'array[i] = value' instead of 'entry = value')"
+ }
+});
diff --git a/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-binding/main.svelte b/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-binding/main.svelte
new file mode 100644
index 0000000000..abf6d202a7
--- /dev/null
+++ b/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-binding/main.svelte
@@ -0,0 +1,7 @@
+
+
+{#each arr as value}
+
+{/each}
diff --git a/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-mutation/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-mutation/_config.js
index eee6ca5c8d..55eb0280ae 100644
--- a/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-mutation/_config.js
+++ b/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-mutation/_config.js
@@ -3,6 +3,7 @@ import { test } from '../../test';
export default test({
error: {
code: 'invalid-each-assignment',
- message: 'Cannot reassign each block argument in runes mode'
+ message:
+ "Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. 'array[i] = value' instead of 'entry = value')"
}
});
diff --git a/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-mutation/main.svelte b/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-mutation/main.svelte
index f9744c08de..41999d53bb 100644
--- a/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-mutation/main.svelte
+++ b/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-mutation/main.svelte
@@ -3,5 +3,5 @@
{#each arr as value}
-
+
{/each}