Skip to content

Commit ad20f48

Browse files
authored
Merge branch 'main' into tweak-snapshot-example
2 parents ba1e2fa + b27113d commit ad20f48

File tree

44 files changed

+504
-224
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+504
-224
lines changed

.changeset/hip-months-breathe.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: show correct errors for invalid runes in `.svelte.js` files

.changeset/perfect-actors-bake.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
breaking: use structuredClone inside `$state.snapshot`

.changeset/pre.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@
252252
"heavy-ears-rule",
253253
"hip-balloons-begin",
254254
"hip-garlics-tap",
255+
"hip-months-breathe",
255256
"hip-pumpkins-boil",
256257
"honest-buses-add",
257258
"honest-dragons-turn",
@@ -408,6 +409,7 @@
408409
"orange-masks-exercise",
409410
"orange-yaks-protect",
410411
"orange-zoos-heal",
412+
"perfect-actors-bake",
411413
"pink-bikes-agree",
412414
"pink-goats-promise",
413415
"pink-mayflies-tie",

documentation/docs/03-runes/01-state.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ This can improve performance with large arrays and objects that you weren't plan
9292

9393
In development mode, the argument to `$state.frozen` will be shallowly frozen with `Object.freeze()`, to make it obvious if you accidentally mutate it.
9494

95-
> Objects and arrays passed to `$state.frozen` will have a `Symbol` property added to them to signal to Svelte that they are frozen. If you don't want this, pass in a clone of the object or array instead.
95+
> Objects and arrays passed to `$state.frozen` will have a `Symbol` property added to them to signal to Svelte that they are frozen. If you don't want this, pass in a clone of the object or array instead. The argument cannot be an existing state proxy created with `$state(...)`.
9696
9797
## `$state.snapshot`
9898

packages/svelte/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# svelte
22

3+
## 5.0.0-next.184
4+
5+
### Patch Changes
6+
7+
- fix: show correct errors for invalid runes in `.svelte.js` files ([#12432](https://github.com/sveltejs/svelte/pull/12432))
8+
9+
- breaking: use structuredClone inside `$state.snapshot` ([#12413](https://github.com/sveltejs/svelte/pull/12413))
10+
311
## 5.0.0-next.183
412

513
### Patch Changes

packages/svelte/messages/client-errors/errors.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@
6060

6161
> The `%rune%` rune is only available inside `.svelte` and `.svelte.js/ts` files
6262
63+
## state_frozen_invalid_argument
64+
65+
> 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`
66+
6367
## state_prototype_fixed
6468

6569
> Cannot set prototype of `$state` object

packages/svelte/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "svelte",
33
"description": "Cybernetically enhanced web apps",
44
"license": "MIT",
5-
"version": "5.0.0-next.183",
5+
"version": "5.0.0-next.184",
66
"type": "module",
77
"types": "./types/index.d.ts",
88
"engines": {

packages/svelte/src/ambient.d.ts

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,75 @@ declare function $state<T>(initial: T): T;
3232
declare function $state<T>(): T | undefined;
3333

3434
declare namespace $state {
35+
type Primitive = string | number | boolean | null | undefined;
36+
37+
type TypedArray =
38+
| Int8Array
39+
| Uint8Array
40+
| Uint8ClampedArray
41+
| Int16Array
42+
| Uint16Array
43+
| Int32Array
44+
| Uint32Array
45+
| Float32Array
46+
| Float64Array
47+
| BigInt64Array
48+
| BigUint64Array;
49+
50+
/** The things that `structuredClone` can handle — https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm */
51+
export type Cloneable =
52+
| ArrayBuffer
53+
| DataView
54+
| Date
55+
| Error
56+
| Map<any, any>
57+
| RegExp
58+
| Set<any>
59+
| TypedArray
60+
// web APIs
61+
| Blob
62+
| CryptoKey
63+
| DOMException
64+
| DOMMatrix
65+
| DOMMatrixReadOnly
66+
| DOMPoint
67+
| DOMPointReadOnly
68+
| DOMQuad
69+
| DOMRect
70+
| DOMRectReadOnly
71+
| File
72+
| FileList
73+
| FileSystemDirectoryHandle
74+
| FileSystemFileHandle
75+
| FileSystemHandle
76+
| ImageBitmap
77+
| ImageData
78+
| RTCCertificate
79+
| VideoFrame;
80+
81+
/** Turn `SvelteDate`, `SvelteMap` and `SvelteSet` into their non-reactive counterparts. (`URL` is uncloneable.) */
82+
type NonReactive<T> = T extends Date
83+
? Date
84+
: T extends Map<infer K, infer V>
85+
? Map<K, V>
86+
: T extends Set<infer K>
87+
? Set<K>
88+
: T;
89+
90+
type Snapshot<T> = T extends Primitive
91+
? T
92+
: T extends Cloneable
93+
? NonReactive<T>
94+
: T extends { toJSON(): infer R }
95+
? R
96+
: T extends Array<infer U>
97+
? Array<Snapshot<U>>
98+
: T extends object
99+
? T extends { [key: string]: any }
100+
? { [K in keyof T]: Snapshot<T[K]> }
101+
: never
102+
: never;
103+
35104
/**
36105
* Declares reactive read-only state that is shallowly immutable.
37106
*
@@ -75,7 +144,7 @@ declare namespace $state {
75144
*
76145
* @param state The value to snapshot
77146
*/
78-
export function snapshot<T>(state: T): T;
147+
export function snapshot<T>(state: T): Snapshot<T>;
79148

80149
/**
81150
* Compare two values, one or both of which is a reactive `$state(...)` proxy.

packages/svelte/src/compiler/phases/2-analyze/validation.js

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,41 @@ export const validation_runes_js = {
10431043
if (node.callee.type === 'ClassExpression' && context.state.scope.function_depth > 0) {
10441044
w.perf_avoid_inline_class(node);
10451045
}
1046+
},
1047+
Identifier(node, { path, state }) {
1048+
let i = path.length;
1049+
let parent = /** @type {import('estree').Expression} */ (path[--i]);
1050+
1051+
if (
1052+
Runes.includes(/** @type {Runes[number]} */ (node.name)) &&
1053+
is_reference(node, parent) &&
1054+
state.scope.get(node.name) === null &&
1055+
state.scope.get(node.name.slice(1)) === null
1056+
) {
1057+
/** @type {import('estree').Expression} */
1058+
let current = node;
1059+
let name = node.name;
1060+
1061+
while (parent.type === 'MemberExpression') {
1062+
if (parent.computed) e.rune_invalid_computed_property(parent);
1063+
name += `.${/** @type {import('estree').Identifier} */ (parent.property).name}`;
1064+
1065+
current = parent;
1066+
parent = /** @type {import('estree').Expression} */ (path[--i]);
1067+
1068+
if (!Runes.includes(/** @type {Runes[number]} */ (name))) {
1069+
if (name === '$effect.active') {
1070+
e.rune_renamed(parent, '$effect.active', '$effect.tracking');
1071+
}
1072+
1073+
e.rune_invalid_name(parent, name);
1074+
}
1075+
}
1076+
1077+
if (parent.type !== 'CallExpression') {
1078+
e.rune_missing_parentheses(current);
1079+
}
1080+
}
10461081
}
10471082
};
10481083

@@ -1153,41 +1188,6 @@ export const validation_runes = merge(validation, a11y_validators, {
11531188
e.import_svelte_internal_forbidden(node);
11541189
}
11551190
},
1156-
Identifier(node, { path, state }) {
1157-
let i = path.length;
1158-
let parent = /** @type {import('estree').Expression} */ (path[--i]);
1159-
1160-
if (
1161-
Runes.includes(/** @type {Runes[number]} */ (node.name)) &&
1162-
is_reference(node, parent) &&
1163-
state.scope.get(node.name) === null &&
1164-
state.scope.get(node.name.slice(1)) === null
1165-
) {
1166-
/** @type {import('estree').Expression} */
1167-
let current = node;
1168-
let name = node.name;
1169-
1170-
while (parent.type === 'MemberExpression') {
1171-
if (parent.computed) e.rune_invalid_computed_property(parent);
1172-
name += `.${/** @type {import('estree').Identifier} */ (parent.property).name}`;
1173-
1174-
current = parent;
1175-
parent = /** @type {import('estree').Expression} */ (path[--i]);
1176-
1177-
if (!Runes.includes(/** @type {Runes[number]} */ (name))) {
1178-
if (name === '$effect.active') {
1179-
e.rune_renamed(parent, '$effect.active', '$effect.tracking');
1180-
}
1181-
1182-
e.rune_invalid_name(parent, name);
1183-
}
1184-
}
1185-
1186-
if (parent.type !== 'CallExpression') {
1187-
e.rune_missing_parentheses(current);
1188-
}
1189-
}
1190-
},
11911191
LabeledStatement(node, { path }) {
11921192
if (node.label.name !== '$' || path.at(-1)?.type !== 'Program') return;
11931193
e.legacy_reactive_statement_invalid(node);
@@ -1368,5 +1368,6 @@ export const validation_runes = merge(validation, a11y_validators, {
13681368
// TODO this is a code smell. need to refactor this stuff
13691369
ClassBody: validation_runes_js.ClassBody,
13701370
ClassDeclaration: validation_runes_js.ClassDeclaration,
1371+
Identifier: validation_runes_js.Identifier,
13711372
NewExpression: validation_runes_js.NewExpression
13721373
});

packages/svelte/src/compiler/phases/3-transform/server/transform-server.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,10 @@ const global_visitors = {
423423
}
424424

425425
if (rune === '$state.snapshot') {
426-
return /** @type {import('estree').Expression} */ (context.visit(node.arguments[0]));
426+
return b.call(
427+
'$.snapshot',
428+
/** @type {import('estree').Expression} */ (context.visit(node.arguments[0]))
429+
);
427430
}
428431

429432
if (rune === '$state.is') {

0 commit comments

Comments
 (0)