mirror of https://github.com/sveltejs/svelte
The readonly dev time validation results in false-negative object equality checks: The original object is proxified and thus comparisons could be between the unproxified and proxified version, which will always return false. Fixes #10372 There's also the problem that an object could be passed into a component but then passed upwards into shared state. At that point, it should no longer be readonly, but it's not possible to statically analyze these points. Fixes #10372 Lastly, the each block logic mutates an internal array and that also throws errors with the readonly logic. Fixes #10037remove-readonly-check
parent
d08e05bf7f
commit
f0d3740e30
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: remove readonly validation as it results in false-negative object equality checks
|
@ -0,0 +1,15 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
html: `<button>add</button> <p>1</p><p>1</p><p>1</p>`,
|
||||
|
||||
async test({ assert, target }) {
|
||||
const btn = target.querySelector('button');
|
||||
|
||||
await btn?.click();
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`<button>add</button> <p>1</p><p>2</p><p>1</p><p>2</p><p>1</p><p>2</p>`
|
||||
);
|
||||
}
|
||||
});
|
@ -0,0 +1,15 @@
|
||||
<script>
|
||||
let { array } = $props();
|
||||
</script>
|
||||
|
||||
{#each array as number}
|
||||
<p>{number.v}</p>
|
||||
{/each}
|
||||
|
||||
{#each array as number (number)}
|
||||
<p>{number.v}</p>
|
||||
{/each}
|
||||
|
||||
{#each array as number (number.v)}
|
||||
<p>{number.v}</p>
|
||||
{/each}
|
@ -0,0 +1,12 @@
|
||||
<script>
|
||||
import Child from './child.svelte';
|
||||
|
||||
let array = $state([{v: 1}]);
|
||||
|
||||
const addNew = () => {
|
||||
array.push({v: 2})
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick={addNew}>add</button>
|
||||
<Child {array} />
|
@ -0,0 +1,45 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
html: `
|
||||
<button>a true</button><button>b true</button>
|
||||
<button>a true</button><button>b true</button>
|
||||
<button>a true</button><button>b true</button>
|
||||
`,
|
||||
|
||||
async test({ assert, target }) {
|
||||
let [btn1, _btn2, btn3, _btn4, btn5] = target.querySelectorAll('button');
|
||||
|
||||
await btn1.click();
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<button>a+ true</button><button>b true</button>
|
||||
<button>a+ true</button><button>b true</button>
|
||||
<button>a+ true</button><button>b true</button>
|
||||
`
|
||||
);
|
||||
|
||||
[btn1, _btn2, btn3, _btn4, btn5] = target.querySelectorAll('button');
|
||||
await btn3.click();
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<button>a++ true</button><button>b true</button>
|
||||
<button>a++ true</button><button>b true</button>
|
||||
<button>a++ true</button><button>b true</button>
|
||||
`
|
||||
);
|
||||
|
||||
[btn1, _btn2, btn3, _btn4, btn5] = target.querySelectorAll('button');
|
||||
await btn5.click();
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<button>a+++ true</button><button>b true</button>
|
||||
<button>a+++ true</button><button>b true</button>
|
||||
<button>a+++ true</button><button>b true</button>
|
||||
`
|
||||
);
|
||||
}
|
||||
});
|
@ -0,0 +1,7 @@
|
||||
<script>
|
||||
let {item, items, onclick} = $props()
|
||||
</script>
|
||||
|
||||
<button {onclick}>
|
||||
{item.name} {items.includes(item)}
|
||||
</button>
|
@ -0,0 +1,19 @@
|
||||
<script>
|
||||
import Item from './item.svelte'
|
||||
|
||||
let items = $state([{name: 'a'}, {name: 'b'}]);
|
||||
</script>
|
||||
|
||||
<!-- test that each block doesn't mess with item identity -->
|
||||
|
||||
{#each items as item (item)}
|
||||
<Item {item} {items} onclick={() => item.name = item.name + '+'} />
|
||||
{/each}
|
||||
|
||||
{#each items as item (item.name)}
|
||||
<Item {item} {items} onclick={() => {console.log('hello'); item.name = item.name + '+'}} />
|
||||
{/each}
|
||||
|
||||
{#each items as item}
|
||||
<Item {item} {items} onclick={() => item.name = item.name + '+'} />
|
||||
{/each}
|
@ -1,8 +0,0 @@
|
||||
<script>
|
||||
/** @type {{ object: { count: number }}} */
|
||||
let { object } = $props();
|
||||
</script>
|
||||
|
||||
<button onclick={() => object = { count: object.count + 1 } }>
|
||||
clicks: {object.count}
|
||||
</button>
|
@ -1,22 +0,0 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
// Tests that readonly bails on setters/classes
|
||||
export default test({
|
||||
html: `<button>clicks: 0</button><button>clicks: 0</button>`,
|
||||
|
||||
compileOptions: {
|
||||
dev: true
|
||||
},
|
||||
|
||||
async test({ assert, target }) {
|
||||
const [btn1, btn2] = target.querySelectorAll('button');
|
||||
|
||||
await btn1.click();
|
||||
await btn2.click();
|
||||
assert.htmlEqual(target.innerHTML, `<button>clicks: 1</button><button>clicks: 1</button>`);
|
||||
|
||||
await btn1.click();
|
||||
await btn2.click();
|
||||
assert.htmlEqual(target.innerHTML, `<button>clicks: 2</button><button>clicks: 2</button>`);
|
||||
}
|
||||
});
|
@ -1,25 +0,0 @@
|
||||
<script>
|
||||
import Counter from './Counter.svelte';
|
||||
|
||||
function createCounter() {
|
||||
let count = $state(0)
|
||||
return {
|
||||
get count() {
|
||||
return count;
|
||||
},
|
||||
set count(upd) {
|
||||
count = upd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CounterClass {
|
||||
count = $state(0);
|
||||
}
|
||||
|
||||
const counterSetter = createCounter();
|
||||
const counterClass = new CounterClass();
|
||||
</script>
|
||||
|
||||
<Counter object={counterSetter} />
|
||||
<Counter object={counterClass} />
|
@ -1,8 +0,0 @@
|
||||
<script>
|
||||
/** @type {{ object?: { count: number }}} */
|
||||
let { object = { count: 0 } } = $props();
|
||||
</script>
|
||||
|
||||
<button onclick={() => object = { count: object.count + 1 } }>
|
||||
clicks: {object.count}
|
||||
</button>
|
@ -1,19 +0,0 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
html: `<button>clicks: 0</button>`,
|
||||
|
||||
compileOptions: {
|
||||
dev: true
|
||||
},
|
||||
|
||||
async test({ assert, target }) {
|
||||
const btn = target.querySelector('button');
|
||||
|
||||
await btn?.click();
|
||||
assert.htmlEqual(target.innerHTML, `<button>clicks: 1</button>`);
|
||||
|
||||
await btn?.click();
|
||||
assert.htmlEqual(target.innerHTML, `<button>clicks: 2</button>`);
|
||||
}
|
||||
});
|
@ -1,5 +0,0 @@
|
||||
<script>
|
||||
import Counter from './Counter.svelte';
|
||||
</script>
|
||||
|
||||
<Counter />
|
@ -1,8 +0,0 @@
|
||||
<script>
|
||||
/** @type {{ object?: { count: number }}} */
|
||||
let { object = { count: 0 } } = $props();
|
||||
</script>
|
||||
|
||||
<button onclick={() => object.count += 1}>
|
||||
clicks: {object.count}
|
||||
</button>
|
@ -1,19 +0,0 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
html: `<button>clicks: 0</button>`,
|
||||
|
||||
compileOptions: {
|
||||
dev: true
|
||||
},
|
||||
|
||||
async test({ assert, target }) {
|
||||
const btn = target.querySelector('button');
|
||||
await btn?.click();
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `<button>clicks: 0</button>`);
|
||||
},
|
||||
|
||||
runtime_error:
|
||||
'Non-bound props cannot be mutated — to make the `count` settable, ensure the object it is used within is bound as a prop `bind:<prop>={...}`. Fallback values can never be mutated.'
|
||||
});
|
@ -1,5 +0,0 @@
|
||||
<script>
|
||||
import Counter from './Counter.svelte';
|
||||
</script>
|
||||
|
||||
<Counter />
|
@ -1,8 +0,0 @@
|
||||
<script>
|
||||
/** @type {{ object: { count: number }}} */
|
||||
let { object } = $props();
|
||||
</script>
|
||||
|
||||
<button onclick={() => object.count += 1}>
|
||||
clicks: {object.count}
|
||||
</button>
|
@ -1,19 +0,0 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
html: `<button>clicks: 0</button>`,
|
||||
|
||||
compileOptions: {
|
||||
dev: true
|
||||
},
|
||||
|
||||
async test({ assert, target }) {
|
||||
const btn = target.querySelector('button');
|
||||
await btn?.click();
|
||||
|
||||
assert.htmlEqual(target.innerHTML, `<button>clicks: 0</button>`);
|
||||
},
|
||||
|
||||
runtime_error:
|
||||
'Non-bound props cannot be mutated — to make the `count` settable, ensure the object it is used within is bound as a prop `bind:<prop>={...}`. Fallback values can never be mutated.'
|
||||
});
|
@ -1,7 +0,0 @@
|
||||
<script>
|
||||
import Counter from './Counter.svelte';
|
||||
|
||||
const object = $state({ count: 0 });
|
||||
</script>
|
||||
|
||||
<Counter {object} />
|
Loading…
Reference in new issue