diff --git a/.changeset/wicked-wasps-allow.md b/.changeset/wicked-wasps-allow.md
new file mode 100644
index 0000000000..bad5157bc1
--- /dev/null
+++ b/.changeset/wicked-wasps-allow.md
@@ -0,0 +1,5 @@
+---
+"svelte": patch
+---
+
+feat: update error message for snippet binding and assignments
diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js
index 6a867ce12c..8281ab50d8 100644
--- a/packages/svelte/src/compiler/errors.js
+++ b/packages/svelte/src/compiler/errors.js
@@ -214,6 +214,7 @@ const runes = {
'duplicate-props-rune': () => `Cannot use $props() more than once`,
'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')`,
+ 'invalid-snippet-assignment': () => `Cannot reassign or bind to snippet parameter`,
'invalid-derived-call': () => `$derived.call(...) has been replaced with $derived.by(...)`,
'conflicting-property-name': () =>
`Cannot have a property and a component export with the same name`
diff --git a/packages/svelte/src/compiler/phases/2-analyze/validation.js b/packages/svelte/src/compiler/phases/2-analyze/validation.js
index 239a885bff..6e27905726 100644
--- a/packages/svelte/src/compiler/phases/2-analyze/validation.js
+++ b/packages/svelte/src/compiler/phases/2-analyze/validation.js
@@ -352,6 +352,10 @@ const validation = {
if (context.state.analysis.runes && binding.kind === 'each') {
error(node, 'invalid-each-assignment');
}
+
+ if (binding.kind === 'snippet') {
+ error(node, 'invalid-snippet-assignment');
+ }
}
if (node.name === 'group') {
@@ -1011,14 +1015,21 @@ function validate_no_const_assignment(node, argument, scope, is_binding) {
function validate_assignment(node, argument, state) {
validate_no_const_assignment(node, argument, state.scope, false);
- if (state.analysis.runes && argument.type === 'Identifier') {
+ if (argument.type === 'Identifier') {
const binding = state.scope.get(argument.name);
- if (binding?.kind === 'derived') {
- error(node, 'invalid-derived-assignment');
+
+ if (state.analysis.runes) {
+ if (binding?.kind === 'derived') {
+ error(node, 'invalid-derived-assignment');
+ }
+
+ if (binding?.kind === 'each') {
+ error(node, 'invalid-each-assignment');
+ }
}
- if (binding?.kind === 'each') {
- error(node, 'invalid-each-assignment');
+ if (binding?.kind === 'snippet') {
+ error(node, 'invalid-snippet-assignment');
}
}
diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js
index ad4fc2ee9e..d1561f741b 100644
--- a/packages/svelte/src/compiler/phases/scope.js
+++ b/packages/svelte/src/compiler/phases/scope.js
@@ -649,7 +649,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) {
for (const param of node.parameters) {
for (const id of extract_identifiers(param)) {
- child_scope.declare(id, 'each', 'let');
+ child_scope.declare(id, 'snippet', 'let');
}
}
diff --git a/packages/svelte/src/compiler/types/index.d.ts b/packages/svelte/src/compiler/types/index.d.ts
index 2d87299898..e244520e9c 100644
--- a/packages/svelte/src/compiler/types/index.d.ts
+++ b/packages/svelte/src/compiler/types/index.d.ts
@@ -253,7 +253,8 @@ export interface Binding {
* - `rest_prop`: A rest prop
* - `state`: A state variable
* - `derived`: A derived variable
- * - `each`: An each block context variable
+ * - `each`: An each block parameter
+ * - `snippet`: A snippet parameter
* - `store_sub`: A $store value
* - `legacy_reactive`: A `$:` declaration
* - `legacy_reactive_import`: An imported binding that is mutated inside the component
@@ -267,6 +268,7 @@ export interface Binding {
| 'frozen_state'
| 'derived'
| 'each'
+ | 'snippet'
| 'store_sub'
| 'legacy_reactive'
| 'legacy_reactive_import';
diff --git a/packages/svelte/tests/compiler-errors/samples/invalid-snippet-binding/_config.js b/packages/svelte/tests/compiler-errors/samples/invalid-snippet-binding/_config.js
new file mode 100644
index 0000000000..a507812928
--- /dev/null
+++ b/packages/svelte/tests/compiler-errors/samples/invalid-snippet-binding/_config.js
@@ -0,0 +1,8 @@
+import { test } from '../../test';
+
+export default test({
+ error: {
+ code: 'invalid-snippet-assignment',
+ message: 'Cannot reassign or bind to snippet parameter'
+ }
+});
diff --git a/packages/svelte/tests/compiler-errors/samples/invalid-snippet-binding/main.svelte b/packages/svelte/tests/compiler-errors/samples/invalid-snippet-binding/main.svelte
new file mode 100644
index 0000000000..9c26dd3da6
--- /dev/null
+++ b/packages/svelte/tests/compiler-errors/samples/invalid-snippet-binding/main.svelte
@@ -0,0 +1,3 @@
+{#snippet foo(value)}
+
+{/snippet}
diff --git a/packages/svelte/tests/compiler-errors/samples/invalid-snippet-mutation/_config.js b/packages/svelte/tests/compiler-errors/samples/invalid-snippet-mutation/_config.js
new file mode 100644
index 0000000000..a507812928
--- /dev/null
+++ b/packages/svelte/tests/compiler-errors/samples/invalid-snippet-mutation/_config.js
@@ -0,0 +1,8 @@
+import { test } from '../../test';
+
+export default test({
+ error: {
+ code: 'invalid-snippet-assignment',
+ message: 'Cannot reassign or bind to snippet parameter'
+ }
+});
diff --git a/packages/svelte/tests/compiler-errors/samples/invalid-snippet-mutation/main.svelte b/packages/svelte/tests/compiler-errors/samples/invalid-snippet-mutation/main.svelte
new file mode 100644
index 0000000000..d349ec8239
--- /dev/null
+++ b/packages/svelte/tests/compiler-errors/samples/invalid-snippet-mutation/main.svelte
@@ -0,0 +1,3 @@
+{#snippet foo(value)}
+
+{/snippet}
diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts
index 38520da3be..2c5be715b6 100644
--- a/packages/svelte/types/index.d.ts
+++ b/packages/svelte/types/index.d.ts
@@ -718,7 +718,8 @@ declare module 'svelte/compiler' {
* - `rest_prop`: A rest prop
* - `state`: A state variable
* - `derived`: A derived variable
- * - `each`: An each block context variable
+ * - `each`: An each block parameter
+ * - `snippet`: A snippet parameter
* - `store_sub`: A $store value
* - `legacy_reactive`: A `$:` declaration
* - `legacy_reactive_import`: An imported binding that is mutated inside the component
@@ -732,6 +733,7 @@ declare module 'svelte/compiler' {
| 'frozen_state'
| 'derived'
| 'each'
+ | 'snippet'
| 'store_sub'
| 'legacy_reactive'
| 'legacy_reactive_import';