From 5f67750693a8d0f21a37a881fa644e7a4cd64eb8 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 15 Apr 2024 16:24:43 +0100 Subject: [PATCH 01/13] breaking: remove untrack(), replace with $state.clean rune --- .changeset/clever-sloths-push.md | 5 ++ .../client/visitors/javascript-runes.js | 8 ++ .../3-transform/server/transform-server.js | 8 ++ .../svelte/src/compiler/phases/constants.js | 1 + packages/svelte/src/index-client.js | 2 - packages/svelte/src/index-server.js | 10 --- .../src/internal/client/dom/blocks/each.js | 2 +- .../client/dom/elements/transitions.js | 3 +- packages/svelte/src/internal/client/index.js | 2 +- packages/svelte/src/internal/client/proxy.js | 2 +- .../svelte/src/internal/client/runtime.js | 6 +- .../{unstate => state-clean}/_config.js | 0 .../{unstate => state-clean}/main.svelte | 4 +- packages/svelte/types/index.d.ts | 75 ++++++++++++------- .../src/lib/CodeMirror.svelte | 19 ++--- .../routes/docs/content/01-api/02-runes.md | 19 +++++ .../docs/content/01-api/05-functions.md | 21 ------ 17 files changed, 107 insertions(+), 80 deletions(-) create mode 100644 .changeset/clever-sloths-push.md rename packages/svelte/tests/runtime-runes/samples/{unstate => state-clean}/_config.js (100%) rename packages/svelte/tests/runtime-runes/samples/{unstate => state-clean}/main.svelte (57%) diff --git a/.changeset/clever-sloths-push.md b/.changeset/clever-sloths-push.md new file mode 100644 index 000000000000..892695641afb --- /dev/null +++ b/.changeset/clever-sloths-push.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +breaking: remove untrack(), replace with $state.clean rune diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js index 5c4169b409ee..670025a61a09 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js @@ -388,6 +388,14 @@ export const javascript_visitors_runes = { return b.call('$.effect_active'); } + if (rune === '$state.clean') { + const arg = + node.arguments.length > 0 + ? /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])) + : b.id('undefined'); + return b.call('$.clean', arg); + } + if (rune === '$effect.root') { const args = /** @type {import('estree').Expression[]} */ ( node.arguments.map((arg) => context.visit(arg)) diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index 8c502449d41e..e66bbc7a8421 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -793,6 +793,14 @@ const javascript_visitors_runes = { return b.literal(false); } + if (rune === '$state.clean') { + const arg = + node.arguments.length > 0 + ? /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])) + : b.id('undefined'); + return context.visit(arg); + } + if (rune === '$inspect' || rune === '$inspect().with') { return transform_inspect_rune(node, context); } diff --git a/packages/svelte/src/compiler/phases/constants.js b/packages/svelte/src/compiler/phases/constants.js index c61015e92a40..2a924a0d8af1 100644 --- a/packages/svelte/src/compiler/phases/constants.js +++ b/packages/svelte/src/compiler/phases/constants.js @@ -31,6 +31,7 @@ export const PassiveEvents = ['wheel', 'touchstart', 'touchmove', 'touchend', 't export const Runes = /** @type {const} */ ([ '$state', '$state.frozen', + '$state.clean', '$props', '$bindable', '$derived', diff --git a/packages/svelte/src/index-client.js b/packages/svelte/src/index-client.js index 215a3379ff90..ba810969257e 100644 --- a/packages/svelte/src/index-client.js +++ b/packages/svelte/src/index-client.js @@ -177,8 +177,6 @@ export function flushSync(fn) { flush_sync(fn); } -export { unstate } from './internal/client/proxy.js'; - export { hydrate, mount, unmount } from './internal/client/render.js'; export { diff --git a/packages/svelte/src/index-server.js b/packages/svelte/src/index-server.js index aacf4dd711e8..bf03521c9658 100644 --- a/packages/svelte/src/index-server.js +++ b/packages/svelte/src/index-server.js @@ -33,14 +33,4 @@ export function unmount() { export async function tick() {} -/** - * @template T - * @param {T} value - * @returns {T} - */ -export function unstate(value) { - // There's no signals/proxies on the server, so just return the value - return value; -} - export { getAllContexts, getContext, hasContext, setContext } from './internal/server/context.js'; diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index 71a8c50c3e1f..db529a38b560 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -23,7 +23,7 @@ import { resume_effect } from '../../reactivity/effects.js'; import { source, mutable_source, set } from '../../reactivity/sources.js'; -import { is_array, is_frozen, map_get, map_set } from '../../utils.js'; +import { is_array, is_frozen } from '../../utils.js'; import { STATE_SYMBOL } from '../../constants.js'; /** diff --git a/packages/svelte/src/internal/client/dom/elements/transitions.js b/packages/svelte/src/internal/client/dom/elements/transitions.js index 0c0b4332e88e..451ff62c487f 100644 --- a/packages/svelte/src/internal/client/dom/elements/transitions.js +++ b/packages/svelte/src/internal/client/dom/elements/transitions.js @@ -294,7 +294,8 @@ function animate(element, options, counterpart, t2, callback) { delay, duration, easing: 'linear', - fill: 'forwards' + fill: 'none', + composite: 'accumulate' }); animation.finished diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 86c8fbc7da6d..f9316a404bf7 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -127,7 +127,7 @@ export { validate_store } from './validate.js'; export { raf } from './timing.js'; -export { proxy, unstate } from './proxy.js'; +export { proxy, clean } from './proxy.js'; export { create_custom_element } from './dom/elements/custom-element.js'; export { child, diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index a6297559ab08..279264d3bb58 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -140,7 +140,7 @@ function unwrap(value, already_unwrapped) { * @param {T} value * @returns {T} */ -export function unstate(value) { +export function clean(value) { return /** @type {T} */ ( unwrap(/** @type {import('#client').ProxyStateObject} */ (value), new Map()) ); diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index c1b8ee3e441a..c11cc180bc56 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -7,7 +7,7 @@ import { object_freeze, object_prototype } from './utils.js'; -import { unstate } from './proxy.js'; +import { clean } from './proxy.js'; import { destroy_effect, effect, user_pre_effect } from './reactivity/effects.js'; import { EFFECT, @@ -1173,7 +1173,7 @@ export function deep_read(value, visited = new Set()) { */ function deep_unstate(value, visited = new Map()) { if (typeof value === 'object' && value !== null && !visited.has(value)) { - const unstated = unstate(value); + const unstated = clean(value); if (unstated !== value) { visited.set(value, unstated); return unstated; @@ -1303,7 +1303,7 @@ export function freeze(value) { if (typeof value === 'object' && value != null && !is_frozen(value)) { // If the object is already proxified, then unstate the value if (STATE_SYMBOL in value) { - return object_freeze(unstate(value)); + return object_freeze(clean(value)); } // Otherwise freeze the object object_freeze(value); diff --git a/packages/svelte/tests/runtime-runes/samples/unstate/_config.js b/packages/svelte/tests/runtime-runes/samples/state-clean/_config.js similarity index 100% rename from packages/svelte/tests/runtime-runes/samples/unstate/_config.js rename to packages/svelte/tests/runtime-runes/samples/state-clean/_config.js diff --git a/packages/svelte/tests/runtime-runes/samples/unstate/main.svelte b/packages/svelte/tests/runtime-runes/samples/state-clean/main.svelte similarity index 57% rename from packages/svelte/tests/runtime-runes/samples/unstate/main.svelte rename to packages/svelte/tests/runtime-runes/samples/state-clean/main.svelte index cb014acff38b..3e3c107a2f00 100644 --- a/packages/svelte/tests/runtime-runes/samples/unstate/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/state-clean/main.svelte @@ -1,7 +1,5 @@ - + diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 0342dc5a24fe..2c8d7445c87b 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -1011,15 +1011,15 @@ declare module 'svelte/compiler' { filename?: string | undefined; } | undefined): Promise; export class CompileError extends Error { - + constructor(code: string, message: string, position: [number, number] | undefined); - + filename: CompileError_1['filename']; - + position: CompileError_1['position']; - + start: CompileError_1['start']; - + end: CompileError_1['end']; code: string; } @@ -1030,9 +1030,9 @@ declare module 'svelte/compiler' { * */ export const VERSION: string; class Scope { - + constructor(root: ScopeRoot, parent: Scope | null, porous: boolean); - + root: ScopeRoot; /** * The immediate parent scope @@ -1060,25 +1060,25 @@ declare module 'svelte/compiler' { * which is usually an error. Block statements do not increase this value */ function_depth: number; - + declare(node: import('estree').Identifier, kind: Binding['kind'], declaration_kind: DeclarationKind, initial?: null | import('estree').Expression | import('estree').FunctionDeclaration | import('estree').ClassDeclaration | import('estree').ImportDeclaration | EachBlock): Binding; child(porous?: boolean): Scope; - + generate(preferred_name: string): string; - + get(name: string): Binding | null; - + get_bindings(node: import('estree').VariableDeclarator | LetDirective): Binding[]; - + owner(name: string): Scope | null; - + reference(node: import('estree').Identifier, path: SvelteNode[]): void; #private; } class ScopeRoot { - + conflicts: Set; - + unique(preferred_name: string): import("estree").Identifier; } namespace Css { @@ -1970,18 +1970,18 @@ declare module 'svelte/motion' { declare module 'svelte/reactivity' { class ReactiveDate extends Date { - + constructor(...values: any[]); #private; } class ReactiveSet extends Set { - + constructor(value?: Iterable | null | undefined); - + has(value: T): boolean; - + add(value: T): this; - + delete(value: T): boolean; keys(): IterableIterator; values(): IterableIterator; @@ -1990,17 +1990,17 @@ declare module 'svelte/reactivity' { #private; } class ReactiveMap extends Map { - + constructor(value?: Iterable | null | undefined); - + has(key: K): boolean; - + forEach(callbackfn: (value: V, key: K, map: Map) => void, this_arg?: any): void; - + get(key: K): V | undefined; - + set(key: K, value: V): this; - + delete(key: K): boolean; keys(): IterableIterator; values(): IterableIterator; @@ -2013,7 +2013,7 @@ declare module 'svelte/reactivity' { #private; } class ReactiveURLSearchParams extends URLSearchParams { - + [REPLACE](params: URLSearchParams): void; #private; } @@ -2551,6 +2551,25 @@ declare namespace $state { */ export function frozen(initial: T): Readonly; export function frozen(): Readonly | undefined; + /** + * To remove reactivity from objects and arrays created with `$state`, use `$state.clean`: + * + * Example: + * ```ts + * + * ``` + * + * https://svelte-5-preview.vercel.app/docs/runes#$state.clean + * + * @param state The state object to clean + */ + export function clean(state: T): T; } /** @@ -2742,4 +2761,4 @@ declare function $inspect( */ declare function $host(): El; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file +//# sourceMappingURL=index.d.ts.map diff --git a/sites/svelte-5-preview/src/lib/CodeMirror.svelte b/sites/svelte-5-preview/src/lib/CodeMirror.svelte index 530e1e59c2bd..a2f2e849717d 100644 --- a/sites/svelte-5-preview/src/lib/CodeMirror.svelte +++ b/sites/svelte-5-preview/src/lib/CodeMirror.svelte @@ -205,27 +205,28 @@ return { from: word.from - 1, options: [ - { label: '$state', type: 'keyword', boost: 10 }, - { label: '$props', type: 'keyword', boost: 9 }, - { label: '$derived', type: 'keyword', boost: 8 }, + { label: '$state', type: 'keyword', boost: 12 }, + { label: '$props', type: 'keyword', boost: 11 }, + { label: '$derived', type: 'keyword', boost: 10 }, snip('$derived.by(() => {\n\t${}\n});', { label: '$derived.by', type: 'keyword', - boost: 7 + boost: 9 }), - snip('$effect(() => {\n\t${}\n});', { label: '$effect', type: 'keyword', boost: 6 }), + snip('$effect(() => {\n\t${}\n});', { label: '$effect', type: 'keyword', boost: 8 }), snip('$effect.pre(() => {\n\t${}\n});', { label: '$effect.pre', type: 'keyword', - boost: 5 + boost: 7 }), - { label: '$state.frozen', type: 'keyword', boost: 4 }, - { label: '$bindable', type: 'keyword', boost: 4 }, + { label: '$state.frozen', type: 'keyword', boost: 6 }, + { label: '$bindable', type: 'keyword', boost: 5 }, snip('$effect.root(() => {\n\t${}\n});', { label: '$effect.root', type: 'keyword', - boost: 3 + boost: 4 }), + { label: '$state.clean', type: 'keyword', boost: 3 }, snip('$effect.active()', { label: '$effect.active', type: 'keyword', diff --git a/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md b/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md index 135404ec5b4e..82b2254aa13c 100644 --- a/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md +++ b/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md @@ -112,6 +112,25 @@ Svelte provides reactive `Map`, `Set` and `Date` classes. These can be imported

{map.get('message')}

``` +## `$state.clean` + +To remove reactivity from objects and arrays created with `$state`, use `$state.clean`: + +```svelte + +``` + +This is handy when you want to pass some state to an external library or API that doesn't expect a reactive object – such as `structuredClone`. + +> Note that `$state.clean` will return a new object from the input when removing reactivity. If the object passed isn't reactive, it will be returned as is. + ## `$derived` Derived state is declared with the `$derived` rune: diff --git a/sites/svelte-5-preview/src/routes/docs/content/01-api/05-functions.md b/sites/svelte-5-preview/src/routes/docs/content/01-api/05-functions.md index a0b1651109e7..6e39ce4848b8 100644 --- a/sites/svelte-5-preview/src/routes/docs/content/01-api/05-functions.md +++ b/sites/svelte-5-preview/src/routes/docs/content/01-api/05-functions.md @@ -23,27 +23,6 @@ To prevent something from being treated as an `$effect`/`$derived` dependency, u ``` -## `unstate` - -To remove reactivity from objects and arrays created with `$state`, use `unstate`: - -```svelte - -``` - -This is handy when you want to pass some state to an external library or API that doesn't expect a reactive object – such as `structuredClone`. - -> Note that `unstate` will return a new object from the input when removing reactivity. If the object passed isn't reactive, it will be returned as is. - ## `mount` Instantiates a component and mounts it to the given target: From 51a853e16c530715ed1e5c77a0db0b20071b497c Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 15 Apr 2024 16:26:02 +0100 Subject: [PATCH 02/13] lol --- .changeset/clever-sloths-push.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/clever-sloths-push.md b/.changeset/clever-sloths-push.md index 892695641afb..c4420b79eed8 100644 --- a/.changeset/clever-sloths-push.md +++ b/.changeset/clever-sloths-push.md @@ -2,4 +2,4 @@ "svelte": patch --- -breaking: remove untrack(), replace with $state.clean rune +breaking: remove unstate(), replace with $state.clean rune From ffd062fffee58c668f8915e6651f3e69a71acb51 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 15 Apr 2024 16:28:40 +0100 Subject: [PATCH 03/13] update types --- packages/svelte/types/index.d.ts | 92 ++++++++++++-------------------- 1 file changed, 34 insertions(+), 58 deletions(-) diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 2c8d7445c87b..5d449611ea7b 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -297,7 +297,6 @@ declare module 'svelte' { export function flushSync(fn?: (() => void) | undefined): void; /** Anything except a function */ type NotFunction = T extends Function ? never : T; - export function unstate(value: T): T; /** * Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component * @@ -1011,15 +1010,15 @@ declare module 'svelte/compiler' { filename?: string | undefined; } | undefined): Promise; export class CompileError extends Error { - + constructor(code: string, message: string, position: [number, number] | undefined); - + filename: CompileError_1['filename']; - + position: CompileError_1['position']; - + start: CompileError_1['start']; - + end: CompileError_1['end']; code: string; } @@ -1030,9 +1029,9 @@ declare module 'svelte/compiler' { * */ export const VERSION: string; class Scope { - + constructor(root: ScopeRoot, parent: Scope | null, porous: boolean); - + root: ScopeRoot; /** * The immediate parent scope @@ -1060,25 +1059,25 @@ declare module 'svelte/compiler' { * which is usually an error. Block statements do not increase this value */ function_depth: number; - + declare(node: import('estree').Identifier, kind: Binding['kind'], declaration_kind: DeclarationKind, initial?: null | import('estree').Expression | import('estree').FunctionDeclaration | import('estree').ClassDeclaration | import('estree').ImportDeclaration | EachBlock): Binding; child(porous?: boolean): Scope; - + generate(preferred_name: string): string; - + get(name: string): Binding | null; - + get_bindings(node: import('estree').VariableDeclarator | LetDirective): Binding[]; - + owner(name: string): Scope | null; - + reference(node: import('estree').Identifier, path: SvelteNode[]): void; #private; } class ScopeRoot { - + conflicts: Set; - + unique(preferred_name: string): import("estree").Identifier; } namespace Css { @@ -1752,8 +1751,6 @@ declare module 'svelte/compiler' { style?: Preprocessor; script?: Preprocessor; } - - export { walk }; } declare module 'svelte/easing' { @@ -1969,19 +1966,19 @@ declare module 'svelte/motion' { } declare module 'svelte/reactivity' { - class ReactiveDate extends Date { - + export class Date extends Date { + constructor(...values: any[]); #private; } - class ReactiveSet extends Set { - + export class Set extends Set { + constructor(value?: Iterable | null | undefined); - + has(value: T): boolean; - + add(value: T): this; - + delete(value: T): boolean; keys(): IterableIterator; values(): IterableIterator; @@ -1989,18 +1986,18 @@ declare module 'svelte/reactivity' { [Symbol.iterator](): IterableIterator; #private; } - class ReactiveMap extends Map { - + export class Map extends Map { + constructor(value?: Iterable | null | undefined); - + has(key: K): boolean; - + forEach(callbackfn: (value: V, key: K, map: Map) => void, this_arg?: any): void; - + get(key: K): V | undefined; - + set(key: K, value: V): this; - + delete(key: K): boolean; keys(): IterableIterator; values(): IterableIterator; @@ -2008,18 +2005,16 @@ declare module 'svelte/reactivity' { [Symbol.iterator](): IterableIterator<[K, V]>; #private; } - class ReactiveURL extends URL { - get searchParams(): ReactiveURLSearchParams; + export class URL extends URL { + get searchParams(): URLSearchParams; #private; } - class ReactiveURLSearchParams extends URLSearchParams { - + export class URLSearchParams extends URLSearchParams { + [REPLACE](params: URLSearchParams): void; #private; } const REPLACE: unique symbol; - - export { ReactiveDate as Date, ReactiveSet as Set, ReactiveMap as Map, ReactiveURL as URL, ReactiveURLSearchParams as URLSearchParams }; } declare module 'svelte/server' { @@ -2551,25 +2546,6 @@ declare namespace $state { */ export function frozen(initial: T): Readonly; export function frozen(): Readonly | undefined; - /** - * To remove reactivity from objects and arrays created with `$state`, use `$state.clean`: - * - * Example: - * ```ts - * - * ``` - * - * https://svelte-5-preview.vercel.app/docs/runes#$state.clean - * - * @param state The state object to clean - */ - export function clean(state: T): T; } /** @@ -2761,4 +2737,4 @@ declare function $inspect( */ declare function $host(): El; -//# sourceMappingURL=index.d.ts.map +//# sourceMappingURL=index.d.ts.map \ No newline at end of file From 7b9f2653ee0ddcc8fef9d829484922c9b8442c55 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 15 Apr 2024 16:33:12 +0100 Subject: [PATCH 04/13] update types --- packages/svelte/types/index.d.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 5d449611ea7b..b141f74cf772 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -1751,6 +1751,8 @@ declare module 'svelte/compiler' { style?: Preprocessor; script?: Preprocessor; } + + export { walk }; } declare module 'svelte/easing' { @@ -1966,12 +1968,12 @@ declare module 'svelte/motion' { } declare module 'svelte/reactivity' { - export class Date extends Date { + class ReactiveDate extends Date { constructor(...values: any[]); #private; } - export class Set extends Set { + class ReactiveSet extends Set { constructor(value?: Iterable | null | undefined); @@ -1986,7 +1988,7 @@ declare module 'svelte/reactivity' { [Symbol.iterator](): IterableIterator; #private; } - export class Map extends Map { + class ReactiveMap extends Map { constructor(value?: Iterable | null | undefined); @@ -2005,16 +2007,18 @@ declare module 'svelte/reactivity' { [Symbol.iterator](): IterableIterator<[K, V]>; #private; } - export class URL extends URL { - get searchParams(): URLSearchParams; + class ReactiveURL extends URL { + get searchParams(): ReactiveURLSearchParams; #private; } - export class URLSearchParams extends URLSearchParams { + class ReactiveURLSearchParams extends URLSearchParams { [REPLACE](params: URLSearchParams): void; #private; } const REPLACE: unique symbol; + + export { ReactiveDate as Date, ReactiveSet as Set, ReactiveMap as Map, ReactiveURL as URL, ReactiveURLSearchParams as URLSearchParams }; } declare module 'svelte/server' { From f7e04923013f33686287146c1a6925ec879b50f9 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 15 Apr 2024 16:50:32 +0100 Subject: [PATCH 05/13] undo --- .../svelte/src/internal/client/dom/elements/transitions.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/svelte/src/internal/client/dom/elements/transitions.js b/packages/svelte/src/internal/client/dom/elements/transitions.js index 451ff62c487f..5a6be614f9cf 100644 --- a/packages/svelte/src/internal/client/dom/elements/transitions.js +++ b/packages/svelte/src/internal/client/dom/elements/transitions.js @@ -294,8 +294,7 @@ function animate(element, options, counterpart, t2, callback) { delay, duration, easing: 'linear', - fill: 'none', - composite: 'accumulate' + fill: 'forwards', }); animation.finished From 8a23228312ea353494c22038b1b195b039eda00b Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 15 Apr 2024 16:50:48 +0100 Subject: [PATCH 06/13] undo --- packages/svelte/src/internal/client/dom/elements/transitions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/internal/client/dom/elements/transitions.js b/packages/svelte/src/internal/client/dom/elements/transitions.js index 5a6be614f9cf..0c0b4332e88e 100644 --- a/packages/svelte/src/internal/client/dom/elements/transitions.js +++ b/packages/svelte/src/internal/client/dom/elements/transitions.js @@ -294,7 +294,7 @@ function animate(element, options, counterpart, t2, callback) { delay, duration, easing: 'linear', - fill: 'forwards', + fill: 'forwards' }); animation.finished From 2ef6fdcc8bf8e204261e4c1f8440b8406bf135b7 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 15 Apr 2024 22:08:50 +0100 Subject: [PATCH 07/13] rename to raw --- .changeset/clever-sloths-push.md | 2 +- .../3-transform/client/visitors/javascript-runes.js | 4 ++-- .../phases/3-transform/server/transform-server.js | 2 +- packages/svelte/src/compiler/phases/constants.js | 2 +- packages/svelte/src/internal/client/index.js | 2 +- packages/svelte/src/internal/client/proxy.js | 2 +- packages/svelte/src/internal/client/runtime.js | 6 +++--- .../samples/{state-clean => state-raw}/_config.js | 0 .../samples/{state-clean => state-raw}/main.svelte | 2 +- sites/svelte-5-preview/src/lib/CodeMirror.svelte | 2 +- .../src/routes/docs/content/01-api/02-runes.md | 8 ++++---- 11 files changed, 16 insertions(+), 16 deletions(-) rename packages/svelte/tests/runtime-runes/samples/{state-clean => state-raw}/_config.js (100%) rename packages/svelte/tests/runtime-runes/samples/{state-clean => state-raw}/main.svelte (68%) diff --git a/.changeset/clever-sloths-push.md b/.changeset/clever-sloths-push.md index c4420b79eed8..54af03b547cc 100644 --- a/.changeset/clever-sloths-push.md +++ b/.changeset/clever-sloths-push.md @@ -2,4 +2,4 @@ "svelte": patch --- -breaking: remove unstate(), replace with $state.clean rune +breaking: remove unstate(), replace with $state.raw rune diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js index 670025a61a09..b5a0d2b00e84 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js @@ -388,12 +388,12 @@ export const javascript_visitors_runes = { return b.call('$.effect_active'); } - if (rune === '$state.clean') { + if (rune === '$state.raw') { const arg = node.arguments.length > 0 ? /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])) : b.id('undefined'); - return b.call('$.clean', arg); + return b.call('$.raw', arg); } if (rune === '$effect.root') { diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index e66bbc7a8421..f19ff2b5618d 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -793,7 +793,7 @@ const javascript_visitors_runes = { return b.literal(false); } - if (rune === '$state.clean') { + if (rune === '$state.raw') { const arg = node.arguments.length > 0 ? /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])) diff --git a/packages/svelte/src/compiler/phases/constants.js b/packages/svelte/src/compiler/phases/constants.js index 2a924a0d8af1..493c67d317b1 100644 --- a/packages/svelte/src/compiler/phases/constants.js +++ b/packages/svelte/src/compiler/phases/constants.js @@ -31,7 +31,7 @@ export const PassiveEvents = ['wheel', 'touchstart', 'touchmove', 'touchend', 't export const Runes = /** @type {const} */ ([ '$state', '$state.frozen', - '$state.clean', + '$state.raw', '$props', '$bindable', '$derived', diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index f9316a404bf7..03bf8e7b52ff 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -127,7 +127,7 @@ export { validate_store } from './validate.js'; export { raf } from './timing.js'; -export { proxy, clean } from './proxy.js'; +export { proxy, raw } from './proxy.js'; export { create_custom_element } from './dom/elements/custom-element.js'; export { child, diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index 279264d3bb58..d0918761fdea 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -140,7 +140,7 @@ function unwrap(value, already_unwrapped) { * @param {T} value * @returns {T} */ -export function clean(value) { +export function raw(value) { return /** @type {T} */ ( unwrap(/** @type {import('#client').ProxyStateObject} */ (value), new Map()) ); diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index c11cc180bc56..b0463132e3d0 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -7,7 +7,7 @@ import { object_freeze, object_prototype } from './utils.js'; -import { clean } from './proxy.js'; +import { raw } from './proxy.js'; import { destroy_effect, effect, user_pre_effect } from './reactivity/effects.js'; import { EFFECT, @@ -1173,7 +1173,7 @@ export function deep_read(value, visited = new Set()) { */ function deep_unstate(value, visited = new Map()) { if (typeof value === 'object' && value !== null && !visited.has(value)) { - const unstated = clean(value); + const unstated = raw(value); if (unstated !== value) { visited.set(value, unstated); return unstated; @@ -1303,7 +1303,7 @@ export function freeze(value) { if (typeof value === 'object' && value != null && !is_frozen(value)) { // If the object is already proxified, then unstate the value if (STATE_SYMBOL in value) { - return object_freeze(clean(value)); + return object_freeze(raw(value)); } // Otherwise freeze the object object_freeze(value); diff --git a/packages/svelte/tests/runtime-runes/samples/state-clean/_config.js b/packages/svelte/tests/runtime-runes/samples/state-raw/_config.js similarity index 100% rename from packages/svelte/tests/runtime-runes/samples/state-clean/_config.js rename to packages/svelte/tests/runtime-runes/samples/state-raw/_config.js diff --git a/packages/svelte/tests/runtime-runes/samples/state-clean/main.svelte b/packages/svelte/tests/runtime-runes/samples/state-raw/main.svelte similarity index 68% rename from packages/svelte/tests/runtime-runes/samples/state-clean/main.svelte rename to packages/svelte/tests/runtime-runes/samples/state-raw/main.svelte index 3e3c107a2f00..a7bf450fab2a 100644 --- a/packages/svelte/tests/runtime-runes/samples/state-clean/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/state-raw/main.svelte @@ -2,4 +2,4 @@ let items = $state([{a: 0}]); - + diff --git a/sites/svelte-5-preview/src/lib/CodeMirror.svelte b/sites/svelte-5-preview/src/lib/CodeMirror.svelte index a2f2e849717d..9e8547d77ac9 100644 --- a/sites/svelte-5-preview/src/lib/CodeMirror.svelte +++ b/sites/svelte-5-preview/src/lib/CodeMirror.svelte @@ -226,7 +226,7 @@ type: 'keyword', boost: 4 }), - { label: '$state.clean', type: 'keyword', boost: 3 }, + { label: '$state.raw', type: 'keyword', boost: 3 }, snip('$effect.active()', { label: '$effect.active', type: 'keyword', diff --git a/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md b/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md index 82b2254aa13c..bb93b4ead79f 100644 --- a/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md +++ b/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md @@ -112,9 +112,9 @@ Svelte provides reactive `Map`, `Set` and `Date` classes. These can be imported

{map.get('message')}

``` -## `$state.clean` +## `$state.raw` -To remove reactivity from objects and arrays created with `$state`, use `$state.clean`: +To remove reactivity from objects and arrays created with `$state`, use `$state.raw`: ```svelte ``` This is handy when you want to pass some state to an external library or API that doesn't expect a reactive object – such as `structuredClone`. -> Note that `$state.clean` will return a new object from the input when removing reactivity. If the object passed isn't reactive, it will be returned as is. +> Note that `$state.raw` will return a new object from the input when removing reactivity. If the object passed isn't reactive, it will be returned as is. ## `$derived` From 92c7e2557aef73a12958c992ca0fb95949144dcd Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 15 Apr 2024 23:18:49 +0100 Subject: [PATCH 08/13] rename to snapshot --- .changeset/clever-sloths-push.md | 2 +- packages/svelte/src/ambient.d.ts | 19 +++++++++++++++++++ .../client/visitors/javascript-runes.js | 4 ++-- .../3-transform/server/transform-server.js | 2 +- .../svelte/src/compiler/phases/constants.js | 2 +- packages/svelte/src/internal/client/proxy.js | 2 +- .../svelte/src/internal/client/runtime.js | 6 +++--- .../{state-raw => state-snapshot}/_config.js | 0 .../{state-raw => state-snapshot}/main.svelte | 2 +- packages/svelte/types/index.d.ts | 19 +++++++++++++++++++ .../src/lib/CodeMirror.svelte | 2 +- .../routes/docs/content/01-api/02-runes.md | 8 ++++---- 12 files changed, 53 insertions(+), 15 deletions(-) rename packages/svelte/tests/runtime-runes/samples/{state-raw => state-snapshot}/_config.js (100%) rename packages/svelte/tests/runtime-runes/samples/{state-raw => state-snapshot}/main.svelte (67%) diff --git a/.changeset/clever-sloths-push.md b/.changeset/clever-sloths-push.md index 54af03b547cc..91e2612c4d76 100644 --- a/.changeset/clever-sloths-push.md +++ b/.changeset/clever-sloths-push.md @@ -2,4 +2,4 @@ "svelte": patch --- -breaking: remove unstate(), replace with $state.raw rune +breaking: remove unstate(), replace with $state.snapshot rune diff --git a/packages/svelte/src/ambient.d.ts b/packages/svelte/src/ambient.d.ts index f923aace5ab1..b09f68cde090 100644 --- a/packages/svelte/src/ambient.d.ts +++ b/packages/svelte/src/ambient.d.ts @@ -42,6 +42,25 @@ declare namespace $state { */ export function frozen(initial: T): Readonly; export function frozen(): Readonly | undefined; + /** + * To remove reactivity from objects and arrays created with `$state`, use `$state.snapshot`: + * + * Example: + * ```ts + * + * ``` + * + * https://svelte-5-preview.vercel.app/docs/runes#$state.snapshot + * + * @param state The state object to snapshot + */ + export function snapshot(state: T): T; } /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js index b5a0d2b00e84..5e4d9f5fbec5 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js @@ -388,12 +388,12 @@ export const javascript_visitors_runes = { return b.call('$.effect_active'); } - if (rune === '$state.raw') { + if (rune === '$state.snapshot') { const arg = node.arguments.length > 0 ? /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])) : b.id('undefined'); - return b.call('$.raw', arg); + return b.call('$.snapshot', arg); } if (rune === '$effect.root') { diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index f19ff2b5618d..3b8fb9094267 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -793,7 +793,7 @@ const javascript_visitors_runes = { return b.literal(false); } - if (rune === '$state.raw') { + if (rune === '$state.snapshot') { const arg = node.arguments.length > 0 ? /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])) diff --git a/packages/svelte/src/compiler/phases/constants.js b/packages/svelte/src/compiler/phases/constants.js index 493c67d317b1..0fec2e894838 100644 --- a/packages/svelte/src/compiler/phases/constants.js +++ b/packages/svelte/src/compiler/phases/constants.js @@ -31,7 +31,7 @@ export const PassiveEvents = ['wheel', 'touchstart', 'touchmove', 'touchend', 't export const Runes = /** @type {const} */ ([ '$state', '$state.frozen', - '$state.raw', + '$state.snapshot', '$props', '$bindable', '$derived', diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index d0918761fdea..24d4567debc4 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -140,7 +140,7 @@ function unwrap(value, already_unwrapped) { * @param {T} value * @returns {T} */ -export function raw(value) { +export function snapshot(value) { return /** @type {T} */ ( unwrap(/** @type {import('#client').ProxyStateObject} */ (value), new Map()) ); diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index b0463132e3d0..f6d5d5973381 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -7,7 +7,7 @@ import { object_freeze, object_prototype } from './utils.js'; -import { raw } from './proxy.js'; +import { snapshot } from './proxy.js'; import { destroy_effect, effect, user_pre_effect } from './reactivity/effects.js'; import { EFFECT, @@ -1173,7 +1173,7 @@ export function deep_read(value, visited = new Set()) { */ function deep_unstate(value, visited = new Map()) { if (typeof value === 'object' && value !== null && !visited.has(value)) { - const unstated = raw(value); + const unstated = snapshot(value); if (unstated !== value) { visited.set(value, unstated); return unstated; @@ -1303,7 +1303,7 @@ export function freeze(value) { if (typeof value === 'object' && value != null && !is_frozen(value)) { // If the object is already proxified, then unstate the value if (STATE_SYMBOL in value) { - return object_freeze(raw(value)); + return object_freeze(snapshot(value)); } // Otherwise freeze the object object_freeze(value); diff --git a/packages/svelte/tests/runtime-runes/samples/state-raw/_config.js b/packages/svelte/tests/runtime-runes/samples/state-snapshot/_config.js similarity index 100% rename from packages/svelte/tests/runtime-runes/samples/state-raw/_config.js rename to packages/svelte/tests/runtime-runes/samples/state-snapshot/_config.js diff --git a/packages/svelte/tests/runtime-runes/samples/state-raw/main.svelte b/packages/svelte/tests/runtime-runes/samples/state-snapshot/main.svelte similarity index 67% rename from packages/svelte/tests/runtime-runes/samples/state-raw/main.svelte rename to packages/svelte/tests/runtime-runes/samples/state-snapshot/main.svelte index a7bf450fab2a..ac438dacc49d 100644 --- a/packages/svelte/tests/runtime-runes/samples/state-raw/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/state-snapshot/main.svelte @@ -2,4 +2,4 @@ let items = $state([{a: 0}]); - + diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index b141f74cf772..8b0a6f5ee96f 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -2550,6 +2550,25 @@ declare namespace $state { */ export function frozen(initial: T): Readonly; export function frozen(): Readonly | undefined; + /** + * To remove reactivity from objects and arrays created with `$state`, use `$state.snapshot`: + * + * Example: + * ```ts + * + * ``` + * + * https://svelte-5-preview.vercel.app/docs/runes#$state.snapshot + * + * @param state The state object to snapshot + */ + export function snapshot(state: T): T; } /** diff --git a/sites/svelte-5-preview/src/lib/CodeMirror.svelte b/sites/svelte-5-preview/src/lib/CodeMirror.svelte index 9e8547d77ac9..347cf7d93882 100644 --- a/sites/svelte-5-preview/src/lib/CodeMirror.svelte +++ b/sites/svelte-5-preview/src/lib/CodeMirror.svelte @@ -226,7 +226,7 @@ type: 'keyword', boost: 4 }), - { label: '$state.raw', type: 'keyword', boost: 3 }, + { label: '$state.snapshot', type: 'keyword', boost: 3 }, snip('$effect.active()', { label: '$effect.active', type: 'keyword', diff --git a/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md b/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md index bb93b4ead79f..f9102feff0db 100644 --- a/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md +++ b/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md @@ -112,9 +112,9 @@ Svelte provides reactive `Map`, `Set` and `Date` classes. These can be imported

{map.get('message')}

``` -## `$state.raw` +## `$state.snapshot` -To remove reactivity from objects and arrays created with `$state`, use `$state.raw`: +To remove reactivity from objects and arrays created with `$state`, use `$state.snapshot`: ```svelte ``` This is handy when you want to pass some state to an external library or API that doesn't expect a reactive object – such as `structuredClone`. -> Note that `$state.raw` will return a new object from the input when removing reactivity. If the object passed isn't reactive, it will be returned as is. +> Note that `$state.snapshot` will return a new object from the input when removing reactivity. If the object passed isn't reactive, it will be returned as is. ## `$derived` From af7d5c3c0cc2caa36c5e897af925f58db22d87fe Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 15 Apr 2024 23:22:06 +0100 Subject: [PATCH 09/13] fix --- packages/svelte/src/internal/client/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 03bf8e7b52ff..278e4c1dcd0f 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -127,7 +127,7 @@ export { validate_store } from './validate.js'; export { raf } from './timing.js'; -export { proxy, raw } from './proxy.js'; +export { proxy, snapshot } from './proxy.js'; export { create_custom_element } from './dom/elements/custom-element.js'; export { child, From 9afc5dcc902b484b1f35b9736bf33b396d993f1c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 15 Apr 2024 22:12:15 -0400 Subject: [PATCH 10/13] tweak docs, to make it explicitly that we're converting to and from proxies --- packages/svelte/src/ambient.d.ts | 5 +++-- packages/svelte/types/index.d.ts | 5 +++-- .../src/routes/docs/content/01-api/02-runes.md | 14 +++++++------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/svelte/src/ambient.d.ts b/packages/svelte/src/ambient.d.ts index b09f68cde090..855d1b88a05d 100644 --- a/packages/svelte/src/ambient.d.ts +++ b/packages/svelte/src/ambient.d.ts @@ -43,14 +43,15 @@ declare namespace $state { export function frozen(initial: T): Readonly; export function frozen(): Readonly | undefined; /** - * To remove reactivity from objects and arrays created with `$state`, use `$state.snapshot`: + * To take a static snapshot of a deeply reactive `$state` proxy, use `$state.snapshot`: * * Example: * ```ts * diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 8b0a6f5ee96f..07823b65347c 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -2551,14 +2551,15 @@ declare namespace $state { export function frozen(initial: T): Readonly; export function frozen(): Readonly | undefined; /** - * To remove reactivity from objects and arrays created with `$state`, use `$state.snapshot`: + * To take a static snapshot of a deeply reactive `$state` proxy, use `$state.snapshot`: * * Example: * ```ts * diff --git a/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md b/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md index f9102feff0db..27e2cbbf6aad 100644 --- a/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md +++ b/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md @@ -40,7 +40,7 @@ class Todo { > In this example, the compiler transforms `done` and `text` into `get`/`set` methods on the class prototype referencing private fields -Objects and arrays [are made deeply reactive](/#H4sIAAAAAAAAE42QwWrDMBBEf2URhUhUNEl7c21DviPOwZY3jVpZEtIqUBz9e-UUt9BTj7M784bdmZ21wciq48xsPyGr2MF7Jhl9-kXEKxrCoqNLQS2TOqqgPbWd7cgggU3TgCFCAw-RekJ-3Et4lvByEq-drbe_dlsPichZcFYZrT6amQto2pXw5FO88FUYtG90gUfYi3zvWrYL75vxL57zfA07_zfr23k1vjtt-aZ0bQTcbrDL5ZifZcAxKeS8lzDc8X0xDhJ2ItdbX1jlOZMb9VnjyCoKCfMpfwG975NFVwEAAA==): +Objects and arrays [are made deeply reactive](/#H4sIAAAAAAAAE42QwWrDMBBEf2URhUhUNEl7c21DviPOwZY3jVpZEtIqUBz9e-UUt9BTj7M784bdmZ21wciq48xsPyGr2MF7Jhl9-kXEKxrCoqNLQS2TOqqgPbWd7cgggU3TgCFCAw-RekJ-3Et4lvByEq-drbe_dlsPichZcFYZrT6amQto2pXw5FO88FUYtG90gUfYi3zvWrYL75vxL57zfA07_zfr23k1vjtt-aZ0bQTcbrDL5ZifZcAxKeS8lzDc8X0xDhJ2ItdbX1jlOZMb9VnjyCoKCfMpfwG975NFVwEAAA==) by wrapping with with [`Proxies`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy): ```svelte ``` -This is handy when you want to pass some state to an external library or API that doesn't expect a reactive object – such as `structuredClone`. +This is handy when you want to pass some state to an external library or API that doesn't expect a proxy, such as `structuredClone`. -> Note that `$state.snapshot` will return a new object from the input when removing reactivity. If the object passed isn't reactive, it will be returned as is. +> Note that `$state.snapshot` will clone the data when removing reactivity. If the value passed isn't a `$state` proxy, it will be returned as-is. ## `$derived` From 9e5f99240aa774b00f6c5ac5893cc2385683219e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 15 Apr 2024 22:16:19 -0400 Subject: [PATCH 11/13] remove vestiges --- packages/svelte/src/internal/client/runtime.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index f6d5d5973381..4082d90fa088 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -1166,12 +1166,12 @@ export function deep_read(value, visited = new Set()) { } /** - * Like `unstate`, but recursively traverses into normal arrays/objects to find potential states in them. + * Like `snapshot`, but recursively traverses into normal arrays/objects to find potential states in them. * @param {any} value * @param {Map} visited * @returns {any} */ -function deep_unstate(value, visited = new Map()) { +function deep_snapshot(value, visited = new Map()) { if (typeof value === 'object' && value !== null && !visited.has(value)) { const unstated = snapshot(value); if (unstated !== value) { @@ -1179,13 +1179,13 @@ function deep_unstate(value, visited = new Map()) { return unstated; } const prototype = get_prototype_of(value); - // Only deeply unstate plain objects and arrays + // Only deeply snapshot plain objects and arrays if (prototype === object_prototype || prototype === array_prototype) { let contains_unstated = false; /** @type {any} */ const nested_unstated = Array.isArray(value) ? [] : {}; for (let key in value) { - const result = deep_unstate(value[key], visited); + const result = deep_snapshot(value[key], visited); nested_unstated[key] = result; if (result !== value[key]) { contains_unstated = true; @@ -1213,7 +1213,7 @@ export function inspect(get_value, inspect = console.log) { user_pre_effect(() => { const fn = () => { - const value = untrack(() => get_value().map((v) => deep_unstate(v))); + const value = untrack(() => get_value().map((v) => deep_snapshot(v))); if (value.length === 2 && typeof value[1] === 'function' && !warned_inspect_changed) { // eslint-disable-next-line no-console console.warn( @@ -1301,7 +1301,7 @@ if (DEV) { */ export function freeze(value) { if (typeof value === 'object' && value != null && !is_frozen(value)) { - // If the object is already proxified, then unstate the value + // If the object is already proxified, then snapshot the value if (STATE_SYMBOL in value) { return object_freeze(snapshot(value)); } From 16cb26587871c8f56c5de2487f24b8c7f9f54d96 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 15 Apr 2024 22:29:29 -0400 Subject: [PATCH 12/13] validation --- .../svelte/src/compiler/phases/2-analyze/validation.js | 6 ++++++ .../3-transform/client/visitors/javascript-runes.js | 9 ++++----- .../phases/3-transform/server/transform-server.js | 6 +----- .../samples/runes-wrong-state-snapshot-args/_config.js | 8 ++++++++ .../samples/runes-wrong-state-snapshot-args/main.svelte | 3 +++ .../src/routes/docs/content/01-api/02-runes.md | 2 +- 6 files changed, 23 insertions(+), 11 deletions(-) create mode 100644 packages/svelte/tests/compiler-errors/samples/runes-wrong-state-snapshot-args/_config.js create mode 100644 packages/svelte/tests/compiler-errors/samples/runes-wrong-state-snapshot-args/main.svelte diff --git a/packages/svelte/src/compiler/phases/2-analyze/validation.js b/packages/svelte/src/compiler/phases/2-analyze/validation.js index 6e279057268c..7d77003c9d1f 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/validation.js +++ b/packages/svelte/src/compiler/phases/2-analyze/validation.js @@ -865,6 +865,12 @@ function validate_call_expression(node, scope, path) { error(node, 'invalid-rune-args-length', rune, [1]); } } + + if (rune === '$state.snapshot') { + if (node.arguments.length !== 1) { + error(node, 'invalid-rune-args-length', rune, [1]); + } + } } /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js index 5e4d9f5fbec5..c4b87e8d58e1 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js @@ -389,11 +389,10 @@ export const javascript_visitors_runes = { } if (rune === '$state.snapshot') { - const arg = - node.arguments.length > 0 - ? /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])) - : b.id('undefined'); - return b.call('$.snapshot', arg); + return b.call( + '$.snapshot', + /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])) + ); } if (rune === '$effect.root') { diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index 3b8fb9094267..f8bbeafbb7f8 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -794,11 +794,7 @@ const javascript_visitors_runes = { } if (rune === '$state.snapshot') { - const arg = - node.arguments.length > 0 - ? /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])) - : b.id('undefined'); - return context.visit(arg); + return /** @type {import('estree').Expression} */ (context.visit(node.arguments[0])); } if (rune === '$inspect' || rune === '$inspect().with') { diff --git a/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-snapshot-args/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-snapshot-args/_config.js new file mode 100644 index 000000000000..c701cfac7eb8 --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-snapshot-args/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + error: { + code: 'invalid-rune-args-length', + message: '$state.snapshot can only be called with 1 argument' + } +}); diff --git a/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-snapshot-args/main.svelte b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-snapshot-args/main.svelte new file mode 100644 index 000000000000..5ff573f4748d --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-snapshot-args/main.svelte @@ -0,0 +1,3 @@ + diff --git a/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md b/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md index 27e2cbbf6aad..64dc5d02e944 100644 --- a/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md +++ b/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md @@ -40,7 +40,7 @@ class Todo { > In this example, the compiler transforms `done` and `text` into `get`/`set` methods on the class prototype referencing private fields -Objects and arrays [are made deeply reactive](/#H4sIAAAAAAAAE42QwWrDMBBEf2URhUhUNEl7c21DviPOwZY3jVpZEtIqUBz9e-UUt9BTj7M784bdmZ21wciq48xsPyGr2MF7Jhl9-kXEKxrCoqNLQS2TOqqgPbWd7cgggU3TgCFCAw-RekJ-3Et4lvByEq-drbe_dlsPichZcFYZrT6amQto2pXw5FO88FUYtG90gUfYi3zvWrYL75vxL57zfA07_zfr23k1vjtt-aZ0bQTcbrDL5ZifZcAxKeS8lzDc8X0xDhJ2ItdbX1jlOZMb9VnjyCoKCfMpfwG975NFVwEAAA==) by wrapping with with [`Proxies`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy): +Objects and arrays [are made deeply reactive](/#H4sIAAAAAAAAE42QwWrDMBBEf2URhUhUNEl7c21DviPOwZY3jVpZEtIqUBz9e-UUt9BTj7M784bdmZ21wciq48xsPyGr2MF7Jhl9-kXEKxrCoqNLQS2TOqqgPbWd7cgggU3TgCFCAw-RekJ-3Et4lvByEq-drbe_dlsPichZcFYZrT6amQto2pXw5FO88FUYtG90gUfYi3zvWrYL75vxL57zfA07_zfr23k1vjtt-aZ0bQTcbrDL5ZifZcAxKeS8lzDc8X0xDhJ2ItdbX1jlOZMb9VnjyCoKCfMpfwG975NFVwEAAA==) by wrapping them with [`Proxies`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy): ```svelte