breaking: make non-bindable props read-only

readonly-props
Rich Harris 7 months ago
parent 363a5418e6
commit 73a3895dea

@ -0,0 +1,5 @@
---
'svelte': patch
---
breaking: make non-bindable props read-only

@ -6,6 +6,8 @@
> Cannot assign to %thing% > Cannot assign to %thing%
> Cannot assign to %thing%. %suggestion%
## constant_binding ## constant_binding
> Cannot bind to %thing% > Cannot bind to %thing%

@ -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 {null | number | NodeLike} node
* @param {string} thing * @param {string} thing
* @param {string | undefined | null} [suggestion]
* @returns {never} * @returns {never}
*/ */
export function constant_assignment(node, thing) { export function constant_assignment(node, thing, suggestion) {
e(node, "constant_assignment", `Cannot assign to ${thing}`); e(node, "constant_assignment", suggestion ? `Cannot assign to ${thing}. ${suggestion}` : `Cannot assign to ${thing}`);
} }
/** /**

@ -23,6 +23,14 @@ export function validate_assignment(node, argument, state) {
e.constant_assignment(node, 'derived 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') { if (binding?.kind === 'each') {
e.each_item_invalid_assignment(node); e.each_item_invalid_assignment(node);
} }

@ -14,7 +14,3 @@
<button onclick={() => (non_bindable.count += 1)}> <button onclick={() => (non_bindable.count += 1)}>
mutate: {non_bindable.count} mutate: {non_bindable.count}
</button> </button>
<button onclick={() => (non_bindable = { count: non_bindable.count + 1 })}>
reassign: {non_bindable.count}
</button>

@ -6,11 +6,10 @@ export default test({
<button>mutate: 0</button> <button>mutate: 0</button>
<button>reassign: 0</button> <button>reassign: 0</button>
<button>mutate: 0</button> <button>mutate: 0</button>
<button>reassign: 0</button>
`, `,
async test({ assert, target }) { async test({ assert, target }) {
const [btn1, btn2, btn3, btn4] = target.querySelectorAll('button'); const [btn1, btn2, btn3] = target.querySelectorAll('button');
flushSync(() => { flushSync(() => {
btn1?.click(); btn1?.click();
@ -22,7 +21,6 @@ export default test({
<button>mutate: 1</button> <button>mutate: 1</button>
<button>reassign: 1</button> <button>reassign: 1</button>
<button>mutate: 0</button> <button>mutate: 0</button>
<button>reassign: 0</button>
` `
); );
@ -36,7 +34,6 @@ export default test({
<button>mutate: 2</button> <button>mutate: 2</button>
<button>reassign: 2</button> <button>reassign: 2</button>
<button>mutate: 0</button> <button>mutate: 0</button>
<button>reassign: 0</button>
` `
); );
@ -50,7 +47,6 @@ export default test({
<button>mutate: 3</button> <button>mutate: 3</button>
<button>reassign: 3</button> <button>reassign: 3</button>
<button>mutate: 0</button> <button>mutate: 0</button>
<button>reassign: 0</button>
` `
); );
@ -64,35 +60,6 @@ export default test({
<button>mutate: 3</button> <button>mutate: 3</button>
<button>reassign: 3</button> <button>reassign: 3</button>
<button>mutate: 0</button> <button>mutate: 0</button>
<button>reassign: 0</button>
`
);
flushSync(() => {
btn4?.click();
});
assert.htmlEqual(
target.innerHTML,
`
<button>mutate: 3</button>
<button>reassign: 3</button>
<button>mutate: 2</button>
<button>reassign: 2</button>
`
);
flushSync(() => {
btn3?.click();
});
assert.htmlEqual(
target.innerHTML,
`
<button>mutate: 3</button>
<button>reassign: 3</button>
<button>mutate: 2</button>
<button>reassign: 2</button>
` `
); );
} }

Loading…
Cancel
Save