diff --git a/.changeset/two-ties-guess.md b/.changeset/two-ties-guess.md new file mode 100644 index 000000000000..71fb25fd016b --- /dev/null +++ b/.changeset/two-ties-guess.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +breaking: make non-bindable props read-only diff --git a/packages/svelte/messages/compile-errors/script.md b/packages/svelte/messages/compile-errors/script.md index e84174e42a7d..aeda7f286497 100644 --- a/packages/svelte/messages/compile-errors/script.md +++ b/packages/svelte/messages/compile-errors/script.md @@ -6,6 +6,8 @@ > Cannot assign to %thing% +> Cannot assign to %thing%. %suggestion% + ## constant_binding > Cannot bind to %thing% diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index f0fca61e9ac7..e2a69369d1ab 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -69,13 +69,14 @@ export function bindable_invalid_location(node) { } /** - * Cannot assign to %thing% + * Cannot assign to %thing%. %suggestion% * @param {null | number | NodeLike} node * @param {string} thing + * @param {string | undefined | null} [suggestion] * @returns {never} */ -export function constant_assignment(node, thing) { - e(node, "constant_assignment", `Cannot assign to ${thing}`); +export function constant_assignment(node, thing, suggestion) { + e(node, "constant_assignment", suggestion ? `Cannot assign to ${thing}. ${suggestion}` : `Cannot assign to ${thing}`); } /** diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js index 0d3165e35592..cd2d270f03af 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js @@ -23,6 +23,14 @@ export function validate_assignment(node, argument, state) { e.constant_assignment(node, 'derived state'); } + if (binding?.kind === 'prop') { + e.constant_assignment( + node, + 'a non-bindable prop', + 'Use `$state.link(...)` to create a local copy of the value' + ); + } + if (binding?.kind === 'each') { e.each_item_invalid_assignment(node); } diff --git a/packages/svelte/tests/runtime-runes/samples/props-default-reactivity/Counter.svelte b/packages/svelte/tests/runtime-runes/samples/props-default-reactivity/Counter.svelte index 7b0f28b2139b..e900f1ca7f72 100644 --- a/packages/svelte/tests/runtime-runes/samples/props-default-reactivity/Counter.svelte +++ b/packages/svelte/tests/runtime-runes/samples/props-default-reactivity/Counter.svelte @@ -14,7 +14,3 @@ - - diff --git a/packages/svelte/tests/runtime-runes/samples/props-default-reactivity/_config.js b/packages/svelte/tests/runtime-runes/samples/props-default-reactivity/_config.js index 5eff145cb7f5..d32de5a70d4e 100644 --- a/packages/svelte/tests/runtime-runes/samples/props-default-reactivity/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/props-default-reactivity/_config.js @@ -6,11 +6,10 @@ export default test({ - `, async test({ assert, target }) { - const [btn1, btn2, btn3, btn4] = target.querySelectorAll('button'); + const [btn1, btn2, btn3] = target.querySelectorAll('button'); flushSync(() => { btn1?.click(); @@ -22,7 +21,6 @@ export default test({ - ` ); @@ -36,7 +34,6 @@ export default test({ - ` ); @@ -50,7 +47,6 @@ export default test({ - ` ); @@ -64,35 +60,6 @@ export default test({ - - ` - ); - - flushSync(() => { - btn4?.click(); - }); - - assert.htmlEqual( - target.innerHTML, - ` - - - - - ` - ); - - flushSync(() => { - btn3?.click(); - }); - - assert.htmlEqual( - target.innerHTML, - ` - - - - ` ); }