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';