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>
|
||||
class Counter {
|
||||
#count = $state.frozen(0);
|
||||
#count = $state.raw(0);
|
||||
|
||||
constructor(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>
|
||||
class Counter {
|
||||
count = $state.frozen(0);
|
||||
count = $state.raw(0);
|
||||
}
|
||||
const counter = new Counter();
|
||||
</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