Skip to content

Commit 1f9554a

Browse files
committed
Merge branch 'main' into compiler-ast-return
2 parents b52fd77 + 7363f87 commit 1f9554a

File tree

41 files changed

+566
-292
lines changed

Some content is hidden

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

41 files changed

+566
-292
lines changed

.changeset/clever-sloths-push.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: remove unstate(), replace with $state.snapshot rune

.changeset/nervous-turkeys-end.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: improve spreading of attributes

.changeset/pre.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"clean-eels-beg",
5353
"clever-chefs-relate",
5454
"clever-rockets-burn",
55+
"clever-sloths-push",
5556
"cold-birds-own",
5657
"cold-masks-learn",
5758
"cool-ants-leave",
@@ -168,6 +169,7 @@
168169
"khaki-ligers-sing",
169170
"khaki-mails-draw",
170171
"khaki-moose-arrive",
172+
"khaki-tomatoes-rule",
171173
"kind-baboons-approve",
172174
"kind-deers-lay",
173175
"kind-dots-sort",
@@ -219,6 +221,7 @@
219221
"neat-dingos-clap",
220222
"neat-files-rescue",
221223
"nervous-spoons-relax",
224+
"nervous-turkeys-end",
222225
"new-boats-wait",
223226
"new-brooms-grin",
224227
"new-rabbits-flow",
@@ -283,6 +286,7 @@
283286
"rotten-rules-invite",
284287
"rude-ghosts-tickle",
285288
"selfish-dragons-knock",
289+
"selfish-socks-smile",
286290
"selfish-spies-help",
287291
"selfish-tools-hide",
288292
"serious-gorillas-eat",

.changeset/selfish-socks-smile.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: more accurate default value handling

packages/svelte/CHANGELOG.md

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

3+
## 5.0.0-next.105
4+
5+
### Patch Changes
6+
7+
- breaking: remove unstate(), replace with $state.snapshot rune ([#11180](https://github.com/sveltejs/svelte/pull/11180))
8+
9+
- fix: more accurate default value handling ([#11183](https://github.com/sveltejs/svelte/pull/11183))
10+
11+
## 5.0.0-next.104
12+
13+
### Patch Changes
14+
15+
- fix: ssr comments in head elements that require raw content ([#10936](https://github.com/sveltejs/svelte/pull/10936))
16+
17+
- fix: improve spreading of attributes ([#11177](https://github.com/sveltejs/svelte/pull/11177))
18+
319
## 5.0.0-next.103
420

521
### Patch Changes

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.103",
5+
"version": "5.0.0-next.105",
66
"type": "module",
77
"types": "./types/index.d.ts",
88
"engines": {

packages/svelte/src/ambient.d.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,26 @@ declare namespace $state {
4242
*/
4343
export function frozen<T>(initial: T): Readonly<T>;
4444
export function frozen<T>(): Readonly<T> | undefined;
45+
/**
46+
* To take a static snapshot of a deeply reactive `$state` proxy, use `$state.snapshot`:
47+
*
48+
* Example:
49+
* ```ts
50+
* <script>
51+
* let counter = $state({ count: 0 });
52+
*
53+
* function onclick() {
54+
* // Will log `{ count: ... }` rather than `Proxy { ... }`
55+
* console.log($state.snapshot(counter));
56+
* };
57+
* </script>
58+
* ```
59+
*
60+
* https://svelte-5-preview.vercel.app/docs/runes#$state.snapshot
61+
*
62+
* @param state The value to snapshot
63+
*/
64+
export function snapshot<T>(state: T): T;
4565
}
4666

4767
/**

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,12 @@ function validate_call_expression(node, scope, path) {
865865
error(node, 'invalid-rune-args-length', rune, [1]);
866866
}
867867
}
868+
869+
if (rune === '$state.snapshot') {
870+
if (node.arguments.length !== 1) {
871+
error(node, 'invalid-rune-args-length', rune, [1]);
872+
}
873+
}
868874
}
869875

870876
/**

packages/svelte/src/compiler/phases/3-transform/client/utils.js

Lines changed: 6 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import * as b from '../../../utils/builders.js';
2-
import { extract_paths, is_simple_expression, object } from '../../../utils/ast.js';
2+
import {
3+
extract_paths,
4+
is_expression_async,
5+
is_simple_expression,
6+
object
7+
} from '../../../utils/ast.js';
38
import { error } from '../../../errors.js';
49
import {
510
PROPS_IS_LAZY_INITIAL,
@@ -115,103 +120,6 @@ export function serialize_get_binding(node, state) {
115120
return node;
116121
}
117122

118-
/**
119-
* @param {import('estree').Expression | import('estree').Pattern} expression
120-
* @returns {boolean}
121-
*/
122-
function is_expression_async(expression) {
123-
switch (expression.type) {
124-
case 'AwaitExpression': {
125-
return true;
126-
}
127-
case 'ArrayPattern': {
128-
return expression.elements.some((element) => element && is_expression_async(element));
129-
}
130-
case 'ArrayExpression': {
131-
return expression.elements.some((element) => {
132-
if (!element) {
133-
return false;
134-
} else if (element.type === 'SpreadElement') {
135-
return is_expression_async(element.argument);
136-
} else {
137-
return is_expression_async(element);
138-
}
139-
});
140-
}
141-
case 'AssignmentPattern':
142-
case 'AssignmentExpression':
143-
case 'BinaryExpression':
144-
case 'LogicalExpression': {
145-
return is_expression_async(expression.left) || is_expression_async(expression.right);
146-
}
147-
case 'CallExpression':
148-
case 'NewExpression': {
149-
return (
150-
(expression.callee.type !== 'Super' && is_expression_async(expression.callee)) ||
151-
expression.arguments.some((element) => {
152-
if (element.type === 'SpreadElement') {
153-
return is_expression_async(element.argument);
154-
} else {
155-
return is_expression_async(element);
156-
}
157-
})
158-
);
159-
}
160-
case 'ChainExpression': {
161-
return is_expression_async(expression.expression);
162-
}
163-
case 'ConditionalExpression': {
164-
return (
165-
is_expression_async(expression.test) ||
166-
is_expression_async(expression.alternate) ||
167-
is_expression_async(expression.consequent)
168-
);
169-
}
170-
case 'ImportExpression': {
171-
return is_expression_async(expression.source);
172-
}
173-
case 'MemberExpression': {
174-
return (
175-
(expression.object.type !== 'Super' && is_expression_async(expression.object)) ||
176-
(expression.property.type !== 'PrivateIdentifier' &&
177-
is_expression_async(expression.property))
178-
);
179-
}
180-
case 'ObjectPattern':
181-
case 'ObjectExpression': {
182-
return expression.properties.some((property) => {
183-
if (property.type === 'SpreadElement') {
184-
return is_expression_async(property.argument);
185-
} else if (property.type === 'Property') {
186-
return (
187-
(property.key.type !== 'PrivateIdentifier' && is_expression_async(property.key)) ||
188-
is_expression_async(property.value)
189-
);
190-
}
191-
});
192-
}
193-
case 'RestElement': {
194-
return is_expression_async(expression.argument);
195-
}
196-
case 'SequenceExpression':
197-
case 'TemplateLiteral': {
198-
return expression.expressions.some((subexpression) => is_expression_async(subexpression));
199-
}
200-
case 'TaggedTemplateExpression': {
201-
return is_expression_async(expression.tag) || is_expression_async(expression.quasi);
202-
}
203-
case 'UnaryExpression':
204-
case 'UpdateExpression': {
205-
return is_expression_async(expression.argument);
206-
}
207-
case 'YieldExpression': {
208-
return expression.argument ? is_expression_async(expression.argument) : false;
209-
}
210-
default:
211-
return false;
212-
}
213-
}
214-
215123
/**
216124
* @template {import('./types').ClientTransformState} State
217125
* @param {import('estree').AssignmentExpression} node

packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,13 @@ export const javascript_visitors_runes = {
388388
return b.call('$.effect_active');
389389
}
390390

391+
if (rune === '$state.snapshot') {
392+
return b.call(
393+
'$.snapshot',
394+
/** @type {import('estree').Expression} */ (context.visit(node.arguments[0]))
395+
);
396+
}
397+
391398
if (rune === '$effect.root') {
392399
const args = /** @type {import('estree').Expression[]} */ (
393400
node.arguments.map((arg) => context.visit(arg))

packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2277,27 +2277,24 @@ export const template_visitors = {
22772277
for (const path of paths) {
22782278
const name = /** @type {import('estree').Identifier} */ (path.node).name;
22792279
const binding = /** @type {import('#compiler').Binding} */ (context.state.scope.get(name));
2280+
const needs_derived = path.has_default_value; // to ensure that default value is only called once
2281+
const fn = b.thunk(
2282+
/** @type {import('estree').Expression} */ (context.visit(path.expression?.(unwrapped)))
2283+
);
2284+
22802285
declarations.push(
2281-
b.let(
2282-
path.node,
2283-
b.thunk(
2284-
/** @type {import('estree').Expression} */ (
2285-
context.visit(path.expression?.(unwrapped))
2286-
)
2287-
)
2288-
)
2286+
b.let(path.node, needs_derived ? b.call('$.derived_safe_equal', fn) : fn)
2287+
);
2288+
binding.expression = needs_derived ? b.call('$.get', b.id(name)) : b.call(name);
2289+
binding.mutation = create_mutation(
2290+
/** @type {import('estree').Pattern} */ (path.update_expression(unwrapped))
22892291
);
22902292

22912293
// we need to eagerly evaluate the expression in order to hit any
22922294
// 'Cannot access x before initialization' errors
22932295
if (context.state.options.dev) {
2294-
declarations.push(b.stmt(b.call(name)));
2296+
declarations.push(b.stmt(binding.expression));
22952297
}
2296-
2297-
binding.expression = b.call(name);
2298-
binding.mutation = create_mutation(
2299-
/** @type {import('estree').Pattern} */ (path.update_expression(unwrapped))
2300-
);
23012298
}
23022299
}
23032300

@@ -2486,24 +2483,23 @@ export const template_visitors = {
24862483
for (const path of paths) {
24872484
const name = /** @type {import('estree').Identifier} */ (path.node).name;
24882485
const binding = /** @type {import('#compiler').Binding} */ (context.state.scope.get(name));
2489-
declarations.push(
2490-
b.let(
2491-
path.node,
2492-
b.thunk(
2493-
/** @type {import('estree').Expression} */ (
2494-
context.visit(path.expression?.(b.maybe_call(b.id(arg_alias))))
2495-
)
2496-
)
2486+
const needs_derived = path.has_default_value; // to ensure that default value is only called once
2487+
const fn = b.thunk(
2488+
/** @type {import('estree').Expression} */ (
2489+
context.visit(path.expression?.(b.maybe_call(b.id(arg_alias))))
24972490
)
24982491
);
24992492

2493+
declarations.push(
2494+
b.let(path.node, needs_derived ? b.call('$.derived_safe_equal', fn) : fn)
2495+
);
2496+
binding.expression = needs_derived ? b.call('$.get', b.id(name)) : b.call(name);
2497+
25002498
// we need to eagerly evaluate the expression in order to hit any
25012499
// 'Cannot access x before initialization' errors
25022500
if (context.state.options.dev) {
2503-
declarations.push(b.stmt(b.call(name)));
2501+
declarations.push(b.stmt(binding.expression));
25042502
}
2505-
2506-
binding.expression = b.call(name);
25072503
}
25082504
}
25092505

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

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
extract_identifiers,
55
extract_paths,
66
is_event_attribute,
7+
is_expression_async,
78
unwrap_optional
89
} from '../../../utils/ast.js';
910
import * as b from '../../../utils/builders.js';
@@ -793,6 +794,10 @@ const javascript_visitors_runes = {
793794
return b.literal(false);
794795
}
795796

797+
if (rune === '$state.snapshot') {
798+
return /** @type {import('estree').Expression} */ (context.visit(node.arguments[0]));
799+
}
800+
796801
if (rune === '$inspect' || rune === '$inspect().with') {
797802
return transform_inspect_rune(node, context);
798803
}
@@ -1191,7 +1196,9 @@ const javascript_visitors_legacy = {
11911196
const name = /** @type {import('estree').Identifier} */ (path.node).name;
11921197
const binding = /** @type {import('#compiler').Binding} */ (state.scope.get(name));
11931198
const prop = b.member(b.id('$$props'), b.literal(binding.prop_alias ?? name), true);
1194-
declarations.push(b.declarator(path.node, b.call('$.value_or_fallback', prop, value)));
1199+
declarations.push(
1200+
b.declarator(path.node, b.call('$.value_or_fallback', prop, b.thunk(value)))
1201+
);
11951202
}
11961203
continue;
11971204
}
@@ -1204,13 +1211,15 @@ const javascript_visitors_legacy = {
12041211
b.literal(binding.prop_alias ?? declarator.id.name),
12051212
true
12061213
);
1207-
const init = declarator.init
1208-
? b.call(
1209-
'$.value_or_fallback',
1210-
prop,
1211-
/** @type {import('estree').Expression} */ (visit(declarator.init))
1212-
)
1213-
: prop;
1214+
1215+
/** @type {import('estree').Expression} */
1216+
let init = prop;
1217+
if (declarator.init) {
1218+
const default_value = /** @type {import('estree').Expression} */ (visit(declarator.init));
1219+
init = is_expression_async(default_value)
1220+
? b.await(b.call('$.value_or_fallback_async', prop, b.thunk(default_value, true)))
1221+
: b.call('$.value_or_fallback', prop, b.thunk(default_value));
1222+
}
12141223

12151224
declarations.push(b.declarator(declarator.id, init));
12161225

packages/svelte/src/compiler/phases/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export const PassiveEvents = ['wheel', 'touchstart', 'touchmove', 'touchend', 't
3131
export const Runes = /** @type {const} */ ([
3232
'$state',
3333
'$state.frozen',
34+
'$state.snapshot',
3435
'$props',
3536
'$bindable',
3637
'$derived',

0 commit comments

Comments
 (0)