Skip to content

breaking: change $state.frozen -> $state.raw #12511

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions packages/svelte/messages/client-errors/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,6 @@

> The `%rune%` rune is only available inside `.svelte` and `.svelte.js/ts` files

## state_frozen_invalid_argument

> The argument to `$state.frozen(...)` cannot be an object created with `$state(...)`. You should create a copy of it first, for example with `$state.snapshot`

## state_prototype_fixed

> Cannot set prototype of `$state` object
Expand Down
9 changes: 5 additions & 4 deletions packages/svelte/src/ambient.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,13 @@ declare namespace $state {
: never;

/**
* Declares reactive read-only state that is shallowly immutable.
* Declares state that is _not_ made deeply reactive — instead of mutating it,
* you must reassign it.
*
* Example:
* ```ts
* <script>
* let items = $state.frozen([0]);
* let items = $state.raw([0]);
*
* const addItem = () => {
* items = [...items, items.length];
Expand All @@ -123,8 +124,8 @@ declare namespace $state {
*
* @param initial The initial value
*/
export function frozen<T>(initial: T): Readonly<T>;
export function frozen<T>(): Readonly<T> | undefined;
export function raw<T>(initial: T): T;
export function raw<T>(): T | undefined;
/**
* To take a static snapshot of a deeply reactive `$state` proxy, use `$state.snapshot`:
*
Expand Down
13 changes: 4 additions & 9 deletions packages/svelte/src/compiler/phases/2-analyze/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -909,19 +909,14 @@ const runes_scope_js_tweaker = {
const callee = node.init.callee;
if (callee.type !== 'Identifier' && callee.type !== 'MemberExpression') return;

if (
rune !== '$state' &&
rune !== '$state.frozen' &&
rune !== '$derived' &&
rune !== '$derived.by'
)
if (rune !== '$state' && rune !== '$state.raw' && rune !== '$derived' && rune !== '$derived.by')
return;

for (const path of extract_paths(node.id)) {
// @ts-ignore this fails in CI for some insane reason
const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(path.node.name));
binding.kind =
rune === '$state' ? 'state' : rune === '$state.frozen' ? 'frozen_state' : 'derived';
rune === '$state' ? 'state' : rune === '$state.raw' ? 'frozen_state' : 'derived';
}
}
};
Expand All @@ -947,7 +942,7 @@ const runes_scope_tweaker = {

if (
rune !== '$state' &&
rune !== '$state.frozen' &&
rune !== '$state.raw' &&
rune !== '$derived' &&
rune !== '$derived.by' &&
rune !== '$props'
Expand All @@ -960,7 +955,7 @@ const runes_scope_tweaker = {
binding.kind =
rune === '$state'
? 'state'
: rune === '$state.frozen'
: rune === '$state.raw'
? 'frozen_state'
: rune === '$derived' || rune === '$derived.by'
? 'derived'
Expand Down
7 changes: 1 addition & 6 deletions packages/svelte/src/compiler/phases/2-analyze/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -903,12 +903,7 @@ function validate_call_expression(node, scope, path) {
e.bindable_invalid_location(node);
}

if (
rune === '$state' ||
rune === '$state.frozen' ||
rune === '$derived' ||
rune === '$derived.by'
) {
if (rune === '$state' || rune === '$state.raw' || rune === '$derived' || rune === '$derived.by') {
if (parent.type === 'VariableDeclarator') return;
if (parent.type === 'PropertyDefinition' && !parent.static && !parent.computed) return;
e.state_invalid_placement(node, rune);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,18 +215,8 @@ export function client_component(source, analysis, options) {
}

if (binding?.kind === 'state' || binding?.kind === 'frozen_state') {
return [
getter,
b.set(alias ?? name, [
b.stmt(
b.call(
'$.set',
b.id(name),
b.call(binding.kind === 'state' ? '$.proxy' : '$.freeze', b.id('$$value'))
)
)
])
];
const value = binding.kind === 'state' ? b.call('$.proxy', b.id('$$value')) : b.id('$$value');
return [getter, b.set(alias ?? name, [b.stmt(b.call('$.set', b.id(name), value))])];
}

return getter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ export function serialize_set_binding(node, context, fallback, prefix, options)
if (assignment.type === 'AssignmentExpression') {
assignment.right =
private_state.kind === 'frozen_state'
? b.call('$.freeze', value)
? value
: serialize_proxy_reassignment(value, private_state.id, state);
return assignment;
}
Expand All @@ -219,7 +219,7 @@ export function serialize_set_binding(node, context, fallback, prefix, options)
!options?.skip_proxy_and_freeze &&
should_proxy_or_freeze(value, context.state.scope)
? private_state.kind === 'frozen_state'
? b.call('$.freeze', value)
? value
: serialize_proxy_reassignment(value, private_state.id, state)
: value
);
Expand All @@ -243,7 +243,7 @@ export function serialize_set_binding(node, context, fallback, prefix, options)
if (assignment.type === 'AssignmentExpression') {
assignment.right =
public_state.kind === 'frozen_state'
? b.call('$.freeze', value)
? value
: serialize_proxy_reassignment(value, public_state.id, state);
return assignment;
}
Expand Down Expand Up @@ -325,7 +325,7 @@ export function serialize_set_binding(node, context, fallback, prefix, options)
context.state.analysis.runes &&
!options?.skip_proxy_and_freeze &&
should_proxy_or_freeze(value, context.state.scope)
? b.call('$.freeze', value)
? value
: value
);
} else if (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const javascript_visitors_runes = {
const rune = get_rune(definition.value, state.scope);
if (
rune === '$state' ||
rune === '$state.frozen' ||
rune === '$state.raw' ||
rune === '$derived' ||
rune === '$derived.by'
) {
Expand All @@ -51,7 +51,7 @@ export const javascript_visitors_runes = {
kind:
rune === '$state'
? 'state'
: rune === '$state.frozen'
: rune === '$state.raw'
? 'frozen_state'
: rune === '$derived.by'
? 'derived_call'
Expand Down Expand Up @@ -115,10 +115,7 @@ export const javascript_visitors_runes = {
should_proxy_or_freeze(init, state.scope) ? b.call('$.proxy', init) : init
)
: field.kind === 'frozen_state'
? b.call(
'$.source',
should_proxy_or_freeze(init, state.scope) ? b.call('$.freeze', init) : init
)
? b.call('$.source', init)
: field.kind === 'derived_call'
? b.call('$.derived', init)
: b.call('$.derived', b.thunk(init));
Expand Down Expand Up @@ -158,12 +155,7 @@ export const javascript_visitors_runes = {
// set foo(value) { this.#foo = value; }
const value = b.id('value');
body.push(
b.method(
'set',
definition.key,
[value],
[b.stmt(b.call('$.set', member, b.call('$.freeze', value)))]
)
b.method('set', definition.key, [value], [b.stmt(b.call('$.set', member, value))])
);
}

Expand Down Expand Up @@ -314,15 +306,15 @@ export const javascript_visitors_runes = {
? b.id('undefined')
: /** @type {import('estree').Expression} */ (visit(args[0]));

if (rune === '$state' || rune === '$state.frozen') {
if (rune === '$state' || rune === '$state.raw') {
/**
* @param {import('estree').Identifier} id
* @param {import('estree').Expression} value
*/
const create_state_declarator = (id, value) => {
const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(id.name));
if (should_proxy_or_freeze(value, state.scope)) {
value = b.call(rune === '$state' ? '$.proxy' : '$.freeze', value);
value = rune === '$state' ? b.call('$.proxy', value) : value;
}
if (is_state_source(binding, state)) {
value = b.call('$.source', value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,7 @@ const javascript_visitors_runes = {
if (node.value != null && node.value.type === 'CallExpression') {
const rune = get_rune(node.value, state.scope);

if (rune === '$state' || rune === '$state.frozen' || rune === '$derived') {
if (rune === '$state' || rune === '$state.raw' || rune === '$derived') {
return {
...node,
value:
Expand Down
2 changes: 1 addition & 1 deletion packages/svelte/src/compiler/phases/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const PassiveEvents = ['wheel', 'touchstart', 'touchmove', 'touchend', 't

export const Runes = /** @type {const} */ ([
'$state',
'$state.frozen',
'$state.raw',
'$state.snapshot',
'$state.is',
'$props',
Expand Down
1 change: 0 additions & 1 deletion packages/svelte/src/internal/client/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,4 @@ export const INSPECT_EFFECT = 1 << 17;
export const HEAD_EFFECT = 1 << 18;

export const STATE_SYMBOL = Symbol('$state');
export const STATE_FROZEN_SYMBOL = Symbol('$state.frozen');
export const LOADING_ATTR_SYMBOL = Symbol('');
9 changes: 2 additions & 7 deletions packages/svelte/src/internal/client/dom/blocks/each.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
} from '../../reactivity/effects.js';
import { source, mutable_source, set } from '../../reactivity/sources.js';
import { is_array, is_frozen } from '../../../shared/utils.js';
import { INERT, STATE_FROZEN_SYMBOL, STATE_SYMBOL } from '../../constants.js';
import { INERT, STATE_SYMBOL } from '../../constants.js';
import { queue_micro_task } from '../task.js';
import { current_effect } from '../../runtime.js';

Expand Down Expand Up @@ -142,12 +142,7 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
// If we are working with an array that isn't proxied or frozen, then remove strict equality and ensure the items
// are treated as reactive, so they get wrapped in a signal.
var flags = state.flags;
if (
(flags & EACH_IS_STRICT_EQUALS) !== 0 &&
!is_frozen(array) &&
!(STATE_FROZEN_SYMBOL in array) &&
!(STATE_SYMBOL in array)
) {
if ((flags & EACH_IS_STRICT_EQUALS) !== 0 && !is_frozen(array) && !(STATE_SYMBOL in array)) {
flags ^= EACH_IS_STRICT_EQUALS;

// Additionally if we're in an keyed each block, we'll need ensure the items are all wrapped in signals.
Expand Down
16 changes: 0 additions & 16 deletions packages/svelte/src/internal/client/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,22 +262,6 @@ export function rune_outside_svelte(rune) {
}
}

/**
* The argument to `$state.frozen(...)` cannot be an object created with `$state(...)`. You should create a copy of it first, for example with `$state.snapshot`
* @returns {never}
*/
export function state_frozen_invalid_argument() {
if (DEV) {
const error = new Error(`state_frozen_invalid_argument\nThe argument to \`$state.frozen(...)\` cannot be an object created with \`$state(...)\`. You should create a copy of it first, for example with \`$state.snapshot\``);

error.name = 'Svelte error';
throw error;
} else {
// TODO print a link to the documentation
throw new Error("state_frozen_invalid_argument");
}
}

/**
* Cannot set prototype of `$state` object
* @returns {never}
Expand Down
40 changes: 0 additions & 40 deletions packages/svelte/src/internal/client/freeze.js

This file was deleted.

16 changes: 0 additions & 16 deletions packages/svelte/src/internal/client/freeze.test.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/svelte/src/internal/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ export {
template_with_script,
text
} from './dom/template.js';
export { freeze } from './freeze.js';
export { derived, derived_safe_equal } from './reactivity/deriveds.js';
export {
effect_tracking,
Expand Down
9 changes: 2 additions & 7 deletions packages/svelte/src/internal/client/proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from '../shared/utils.js';
import { check_ownership, widen_ownership } from './dev/ownership.js';
import { source, set } from './reactivity/sources.js';
import { STATE_FROZEN_SYMBOL, STATE_SYMBOL } from './constants.js';
import { STATE_SYMBOL } from './constants.js';
import { UNINITIALIZED } from '../../constants.js';
import * as e from './errors.js';

Expand All @@ -23,12 +23,7 @@ import * as e from './errors.js';
* @returns {import('#client').ProxyStateObject<T> | T}
*/
export function proxy(value, parent = null, prev) {
if (
typeof value === 'object' &&
value != null &&
!is_frozen(value) &&
!(STATE_FROZEN_SYMBOL in value)
) {
if (typeof value === 'object' && value != null && !is_frozen(value)) {
// If we have an existing proxy, return it...
if (STATE_SYMBOL in value) {
const metadata = /** @type {import('#client').ProxyMetadata<T>} */ (value[STATE_SYMBOL]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import ComponentB from './ComponentB.svelte';

let type = $state(ComponentA);
let elem = $state.frozen();
let elem = $state.raw();

$effect(() => {
console.log(elem);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ export default test({
async test({ assert, warnings }) {
assert.deepEqual(warnings, [
`\`bind:value={pojo.value}\` (main.svelte:50:7) is binding to a non-reactive property`,
`\`bind:value={frozen.value}\` (main.svelte:51:7) is binding to a non-reactive property`,
`\`bind:value={raw.value}\` (main.svelte:51:7) is binding to a non-reactive property`,
`\`bind:value={pojo.value}\` (main.svelte:52:7) is binding to a non-reactive property`,
`\`bind:value={frozen.value}\` (main.svelte:53:7) is binding to a non-reactive property`
`\`bind:value={raw.value}\` (main.svelte:53:7) is binding to a non-reactive property`
]);
}
});
Loading
Loading