mirror of https://github.com/sveltejs/svelte
breaking: replace `$state.frozen` with `$state.raw` (#12808)
* breaking: replace `$state.frozen` with `$state.raw` * regenerate * rename * rename * rename * rename * rename * rename * rename * rename * rename * typo * add compiler error for existing `$state.frozen` uses * regenerate * rename * tidy up * move proxy logic into props functionpull/12813/head
parent
fa5d3a9002
commit
7cbd188f80
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
breaking: replace `$state.frozen` with `$state.raw`
|
@ -1,40 +0,0 @@
|
|||||||
import { DEV } from 'esm-env';
|
|
||||||
import { define_property, is_array, is_frozen, object_freeze } from '../shared/utils.js';
|
|
||||||
import { STATE_FROZEN_SYMBOL, STATE_SYMBOL } from './constants.js';
|
|
||||||
import * as e from './errors.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Expects a value that was wrapped with `freeze` and makes it frozen in DEV.
|
|
||||||
* @template T
|
|
||||||
* @param {T} value
|
|
||||||
* @returns {Readonly<T>}
|
|
||||||
*/
|
|
||||||
export function freeze(value) {
|
|
||||||
if (
|
|
||||||
typeof value === 'object' &&
|
|
||||||
value !== null &&
|
|
||||||
!is_frozen(value) &&
|
|
||||||
!(STATE_FROZEN_SYMBOL in value)
|
|
||||||
) {
|
|
||||||
var copy = /** @type {T} */ (value);
|
|
||||||
|
|
||||||
if (STATE_SYMBOL in value) {
|
|
||||||
e.state_frozen_invalid_argument();
|
|
||||||
}
|
|
||||||
|
|
||||||
define_property(copy, STATE_FROZEN_SYMBOL, {
|
|
||||||
value: true,
|
|
||||||
writable: true,
|
|
||||||
enumerable: false
|
|
||||||
});
|
|
||||||
|
|
||||||
// Freeze the object in DEV
|
|
||||||
if (DEV) {
|
|
||||||
object_freeze(copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
return /** @type {Readonly<T>} */ (copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
import { freeze } from './freeze.js';
|
|
||||||
import { assert, test } from 'vitest';
|
|
||||||
import { proxy } from './proxy.js';
|
|
||||||
|
|
||||||
test('freezes an object', () => {
|
|
||||||
const frozen = freeze({ a: 1 });
|
|
||||||
|
|
||||||
assert.throws(() => {
|
|
||||||
// @ts-expect-error
|
|
||||||
frozen.a += 1;
|
|
||||||
}, /Cannot assign to read only property/);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('throws if argument is a state proxy', () => {
|
|
||||||
assert.throws(() => freeze(proxy({})), /state_frozen_invalid_argument/);
|
|
||||||
});
|
|
@ -1,14 +0,0 @@
|
|||||||
<script>
|
|
||||||
class Counter {
|
|
||||||
count = $state.frozen({ a: 0 });
|
|
||||||
}
|
|
||||||
const counter = new Counter();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<button on:click={() => {
|
|
||||||
try {
|
|
||||||
counter.count.a++
|
|
||||||
} catch (e) {
|
|
||||||
console.log('read only')
|
|
||||||
}
|
|
||||||
}}>{counter.count.a}</button>
|
|
@ -1,6 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
class Counter {
|
class Counter {
|
||||||
#count = $state.frozen(0);
|
#count = $state.raw(0);
|
||||||
|
|
||||||
constructor(initial_count) {
|
constructor(initial_count) {
|
||||||
this.#count = initial_count;
|
this.#count = initial_count;
|
@ -0,0 +1,12 @@
|
|||||||
|
<script>
|
||||||
|
class Counter {
|
||||||
|
count = $state.raw({ a: 0 });
|
||||||
|
}
|
||||||
|
const counter = new Counter();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
counter.count.a++;
|
||||||
|
}}>{counter.count.a}</button
|
||||||
|
>
|
@ -1,6 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
class Counter {
|
class Counter {
|
||||||
count = $state.frozen(0);
|
count = $state.raw(0);
|
||||||
}
|
}
|
||||||
const counter = new Counter();
|
const counter = new Counter();
|
||||||
</script>
|
</script>
|
@ -1,16 +0,0 @@
|
|||||||
<script>
|
|
||||||
let frozen_items = $state.frozen([
|
|
||||||
{id: 0, text: 'a'},
|
|
||||||
{id: 1, text: 'b'},
|
|
||||||
{id: 2, text: 'c'}
|
|
||||||
])
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#each frozen_items as item (item.id)}
|
|
||||||
{console.log(item.text)}
|
|
||||||
{item.text}
|
|
||||||
{/each}
|
|
||||||
|
|
||||||
<button onclick={() => {
|
|
||||||
frozen_items = [...frozen_items, {id: 3, text: 'd'}]
|
|
||||||
}}></button>
|
|
@ -0,0 +1,18 @@
|
|||||||
|
<script>
|
||||||
|
let raw_items = $state.raw([
|
||||||
|
{ id: 0, text: 'a' },
|
||||||
|
{ id: 1, text: 'b' },
|
||||||
|
{ id: 2, text: 'c' }
|
||||||
|
]);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#each raw_items as item (item.id)}
|
||||||
|
{console.log(item.text)}
|
||||||
|
{item.text}
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
<button
|
||||||
|
onclick={() => {
|
||||||
|
raw_items = [...raw_items, { id: 3, text: 'd' }];
|
||||||
|
}}
|
||||||
|
></button>
|
@ -0,0 +1,6 @@
|
|||||||
|
<script>
|
||||||
|
let { object = $bindable() } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button onclick={() => (object = { count: object.count + 1 })}>reassign</button>
|
||||||
|
<button onclick={() => (object.count += 1)}>mutate</button>
|
@ -0,0 +1,20 @@
|
|||||||
|
import { flushSync } from 'svelte';
|
||||||
|
import { ok, test } from '../../test';
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
html: '<button>reassign</button> <button>mutate</button> <p>0</p>',
|
||||||
|
test({ assert, target }) {
|
||||||
|
const [reassign, mutate] = target.querySelectorAll('button');
|
||||||
|
const output = target.querySelector('p');
|
||||||
|
ok(output);
|
||||||
|
|
||||||
|
flushSync(() => mutate.click());
|
||||||
|
assert.htmlEqual(output.innerHTML, '0');
|
||||||
|
|
||||||
|
flushSync(() => reassign.click());
|
||||||
|
assert.htmlEqual(output.innerHTML, '2');
|
||||||
|
|
||||||
|
flushSync(() => mutate.click());
|
||||||
|
assert.htmlEqual(output.innerHTML, '2');
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,8 @@
|
|||||||
|
<script>
|
||||||
|
import Child from './Child.svelte';
|
||||||
|
|
||||||
|
let object = $state.raw({ count: 0 });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Child bind:object />
|
||||||
|
<p>{object.count}</p>
|
Loading…
Reference in new issue