svelte 5.56.9 → 5.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiler/index.js +1 -1
- package/elements.d.ts +3 -0
- package/package.json +3 -3
- package/src/compiler/migrate/index.js +6 -1
- package/src/compiler/phases/1-parse/acorn.js +1 -1
- package/src/compiler/phases/1-parse/read/style.js +16 -6
- package/src/compiler/phases/1-parse/utils/html.js +12 -7
- package/src/compiler/phases/2-analyze/css/css-prune.js +10 -0
- package/src/compiler/phases/2-analyze/index.js +45 -18
- package/src/compiler/phases/2-analyze/visitors/AwaitExpression.js +10 -4
- package/src/compiler/phases/2-analyze/visitors/CallExpression.js +1 -1
- package/src/compiler/phases/2-analyze/visitors/DeclarationTag.js +2 -1
- package/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js +10 -10
- package/src/compiler/phases/2-analyze/visitors/SvelteBody.js +4 -4
- package/src/compiler/phases/2-analyze/visitors/SvelteDocument.js +4 -4
- package/src/compiler/phases/2-analyze/visitors/SvelteWindow.js +4 -4
- package/src/compiler/phases/2-analyze/visitors/shared/a11y/index.js +14 -2
- package/src/compiler/phases/2-analyze/visitors/shared/element.js +2 -8
- package/src/compiler/phases/2-analyze/visitors/shared/utils.js +23 -1
- package/src/compiler/phases/3-transform/client/transform-client.js +2 -2
- package/src/compiler/phases/3-transform/client/transform-template/index.js +6 -0
- package/src/compiler/phases/3-transform/client/utils.js +35 -8
- package/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js +16 -3
- package/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js +6 -6
- package/src/compiler/phases/3-transform/client/visitors/BindDirective.js +10 -13
- package/src/compiler/phases/3-transform/client/visitors/ConstTag.js +19 -8
- package/src/compiler/phases/3-transform/client/visitors/DeclarationTag.js +3 -2
- package/src/compiler/phases/3-transform/client/visitors/EachBlock.js +4 -1
- package/src/compiler/phases/3-transform/client/visitors/Fragment.js +2 -7
- package/src/compiler/phases/3-transform/client/visitors/HtmlTag.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/IfBlock.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/KeyBlock.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/RegularElement.js +119 -46
- package/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +2 -1
- package/src/compiler/phases/3-transform/client/visitors/UpdateExpression.js +0 -1
- package/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +11 -4
- package/src/compiler/phases/3-transform/client/visitors/shared/element.js +6 -1
- package/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +1 -6
- package/src/compiler/phases/3-transform/client/visitors/shared/utils.js +5 -5
- package/src/compiler/phases/3-transform/css/index.js +5 -1
- package/src/compiler/phases/3-transform/server/transform-server.js +20 -9
- package/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js +1 -1
- package/src/compiler/phases/3-transform/server/visitors/DeclarationTag.js +1 -1
- package/src/compiler/phases/3-transform/server/visitors/EachBlock.js +8 -2
- package/src/compiler/phases/3-transform/server/visitors/IfBlock.js +4 -4
- package/src/compiler/phases/3-transform/server/visitors/LabeledStatement.js +1 -0
- package/src/compiler/phases/3-transform/server/visitors/RegularElement.js +1 -1
- package/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js +9 -4
- package/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js +4 -2
- package/src/compiler/phases/3-transform/server/visitors/shared/element.js +53 -47
- package/src/compiler/phases/3-transform/server/visitors/shared/utils.js +31 -1
- package/src/compiler/phases/bindings.js +3 -1
- package/src/compiler/phases/nodes.js +4 -0
- package/src/compiler/print/index.js +95 -24
- package/src/easing/index.js +62 -0
- package/src/internal/client/constants.js +9 -0
- package/src/internal/client/context.js +33 -56
- package/src/internal/client/dev/hmr.js +15 -4
- package/src/internal/client/dom/blocks/boundary.js +21 -12
- package/src/internal/client/dom/blocks/css-props.js +3 -3
- package/src/internal/client/dom/blocks/slot.js +19 -0
- package/src/internal/client/dom/blocks/svelte-head.js +8 -0
- package/src/internal/client/dom/css.js +6 -1
- package/src/internal/client/dom/elements/attributes.js +37 -35
- package/src/internal/client/dom/elements/bindings/select.js +99 -9
- package/src/internal/client/dom/elements/bindings/this.js +7 -2
- package/src/internal/client/dom/elements/events.js +19 -1
- package/src/internal/client/dom/elements/transitions.js +81 -68
- package/src/internal/client/dom/operations.js +21 -1
- package/src/internal/client/error-handling.js +10 -2
- package/src/internal/client/index.js +9 -2
- package/src/internal/client/proxy.js +8 -3
- package/src/internal/client/reactivity/async.js +23 -3
- package/src/internal/client/reactivity/batch.js +1 -0
- package/src/internal/client/reactivity/effects.js +14 -1
- package/src/internal/client/reactivity/sources.js +7 -1
- package/src/internal/client/render.js +4 -8
- package/src/internal/client/runtime.js +45 -33
- package/src/internal/client/warnings.js +0 -11
- package/src/internal/server/abort-signal.js +8 -9
- package/src/internal/server/context.js +9 -48
- package/src/internal/server/index.js +1 -1
- package/src/internal/server/renderer.js +164 -52
- package/src/internal/shared/attributes.js +4 -2
- package/src/internal/shared/clone.js +2 -0
- package/src/internal/shared/context.js +53 -0
- package/src/internal/shared/errors.js +2 -2
- package/src/legacy/legacy-server.js +1 -1
- package/src/reactivity/map.js +22 -0
- package/src/reactivity/url.js +2 -0
- package/src/transition/index.js +18 -7
- package/src/version.js +1 -1
- package/types/index.d.ts +148 -46
- package/types/index.d.ts.map +2 -2
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/** @import { Binding } from '#compiler' */
|
|
3
3
|
/** @import { ClientTransformState, ComponentClientTransformState } from './types.js' */
|
|
4
4
|
/** @import { Analysis } from '../../types.js' */
|
|
5
|
+
/** @import { ExpressionMetadata } from '../../nodes.js' */
|
|
5
6
|
/** @import { Scope } from '../../scope.js' */
|
|
6
7
|
import * as b from '#compiler/builders';
|
|
7
8
|
import { is_simple_expression, save } from '../../../utils/ast.js';
|
|
@@ -164,20 +165,46 @@ export function should_proxy(node, scope) {
|
|
|
164
165
|
return true;
|
|
165
166
|
}
|
|
166
167
|
|
|
168
|
+
/**
|
|
169
|
+
* An async thunk. If an `await` inside restores the reaction context via `$.save`,
|
|
170
|
+
* the body exits through `$.unsave` so the context cannot leak into foreign microtasks
|
|
171
|
+
* that run before the returned promise settles
|
|
172
|
+
* @param {Expression | BlockStatement} body
|
|
173
|
+
* @param {ExpressionMetadata} metadata
|
|
174
|
+
*/
|
|
175
|
+
export function async_thunk(body, metadata) {
|
|
176
|
+
if (!metadata.has_pickled_await) {
|
|
177
|
+
return b.arrow([], body, true);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const block = body.type === 'BlockStatement' ? body : b.block([b.return(body)]);
|
|
181
|
+
|
|
182
|
+
return b.arrow(
|
|
183
|
+
[],
|
|
184
|
+
b.block([
|
|
185
|
+
{
|
|
186
|
+
type: 'TryStatement',
|
|
187
|
+
block,
|
|
188
|
+
handler: null,
|
|
189
|
+
finalizer: b.block([b.stmt(b.call('$.unsave'))])
|
|
190
|
+
}
|
|
191
|
+
]),
|
|
192
|
+
true
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
167
196
|
/**
|
|
168
197
|
* Svelte legacy mode should use safe equals in most places, runes mode shouldn't
|
|
169
198
|
* @param {ComponentClientTransformState} state
|
|
170
199
|
* @param {Expression | BlockStatement} expression
|
|
171
|
-
* @param {
|
|
200
|
+
* @param {ExpressionMetadata} [metadata]
|
|
172
201
|
*/
|
|
173
|
-
export function create_derived(state, expression,
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (async) {
|
|
177
|
-
return save(b.call('$.async_derived', thunk));
|
|
178
|
-
} else {
|
|
179
|
-
return b.call(state.analysis.runes ? '$.derived' : '$.derived_safe_equal', thunk);
|
|
202
|
+
export function create_derived(state, expression, metadata) {
|
|
203
|
+
if (metadata?.has_await) {
|
|
204
|
+
return save(b.call('$.async_derived', async_thunk(expression, metadata)));
|
|
180
205
|
}
|
|
206
|
+
|
|
207
|
+
return b.call(state.analysis.runes ? '$.derived' : '$.derived_safe_equal', b.thunk(expression));
|
|
181
208
|
}
|
|
182
209
|
|
|
183
210
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @import { AssignmentExpression, AssignmentOperator, Expression, Identifier, Pattern } from 'estree' */
|
|
1
|
+
/** @import { AssignmentExpression, AssignmentOperator, Expression, Identifier, LogicalOperator, Pattern } from 'estree' */
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
3
|
/** @import { Context } from '../types.js' */
|
|
4
4
|
import * as b from '#compiler/builders';
|
|
@@ -79,8 +79,13 @@ function build_assignment(operator, left, right, context) {
|
|
|
79
79
|
|
|
80
80
|
// special case — assignment to private state field
|
|
81
81
|
if (left.property.type === 'PrivateIdentifier') {
|
|
82
|
+
const logical_operator = ['||=', '&&=', '??='].includes(operator)
|
|
83
|
+
? /** @type {LogicalOperator} */ (operator.slice(0, -1))
|
|
84
|
+
: null;
|
|
82
85
|
let value = /** @type {Expression} */ (
|
|
83
|
-
context.visit(
|
|
86
|
+
context.visit(
|
|
87
|
+
logical_operator === null ? build_assignment_value(operator, left, right) : right
|
|
88
|
+
)
|
|
84
89
|
);
|
|
85
90
|
|
|
86
91
|
const needs_proxy =
|
|
@@ -88,7 +93,15 @@ function build_assignment(operator, left, right, context) {
|
|
|
88
93
|
is_non_coercive_operator(operator) &&
|
|
89
94
|
should_proxy(value, context.state.scope);
|
|
90
95
|
|
|
91
|
-
|
|
96
|
+
const assignment = b.call('$.set', left, value, needs_proxy && b.true);
|
|
97
|
+
|
|
98
|
+
return logical_operator === null
|
|
99
|
+
? assignment
|
|
100
|
+
: b.logical(
|
|
101
|
+
logical_operator,
|
|
102
|
+
/** @type {Expression} */ (context.visit(left)),
|
|
103
|
+
assignment
|
|
104
|
+
);
|
|
92
105
|
}
|
|
93
106
|
}
|
|
94
107
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/** @import { BlockStatement, Pattern, Statement } from 'estree' */
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
3
|
/** @import { ComponentClientTransformState, ComponentContext } from '../types' */
|
|
4
|
-
import { extract_identifiers
|
|
4
|
+
import { extract_identifiers } from '../../../../utils/ast.js';
|
|
5
5
|
import * as b from '#compiler/builders';
|
|
6
|
-
import { create_derived } from '../utils.js';
|
|
6
|
+
import { async_thunk, create_derived } from '../utils.js';
|
|
7
7
|
import { get_value } from './shared/declarations.js';
|
|
8
8
|
import { build_expression, add_svelte_meta } from './shared/utils.js';
|
|
9
9
|
|
|
@@ -15,10 +15,10 @@ export function AwaitBlock(node, context) {
|
|
|
15
15
|
context.state.template.push_comment();
|
|
16
16
|
|
|
17
17
|
// Visit {#await <expression>} first to ensure that scopes are in the correct order
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
node.metadata.expression
|
|
21
|
-
|
|
18
|
+
const input = build_expression(context, node.expression, node.metadata.expression);
|
|
19
|
+
const expression = node.metadata.expression.has_await
|
|
20
|
+
? async_thunk(input, node.metadata.expression)
|
|
21
|
+
: b.thunk(input);
|
|
22
22
|
|
|
23
23
|
let then_block;
|
|
24
24
|
let catch_block;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** @import { CallExpression, Expression, Pattern } from 'estree' */
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
3
|
/** @import { ComponentContext } from '../types' */
|
|
4
|
-
import { dev, is_ignored } from '../../../../state.js';
|
|
4
|
+
import { dev, ignore_map, is_ignored } from '../../../../state.js';
|
|
5
5
|
import { is_text_attribute } from '../../../../utils/ast.js';
|
|
6
6
|
import * as b from '#compiler/builders';
|
|
7
7
|
import { binding_properties } from '../../../bindings.js';
|
|
@@ -40,9 +40,15 @@ export function BindDirective(node, context) {
|
|
|
40
40
|
validate_binding(context.state, node, expression);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const
|
|
44
|
-
|
|
43
|
+
const raw_assignment = b.assignment(
|
|
44
|
+
'=',
|
|
45
|
+
/** @type {Pattern} */ (node.expression),
|
|
46
|
+
b.id('$$value')
|
|
45
47
|
);
|
|
48
|
+
// The assignment is generated, so inherit any ignores attached to the binding
|
|
49
|
+
ignore_map.set(raw_assignment, ignore_map.get(node) ?? []);
|
|
50
|
+
|
|
51
|
+
const assignment = /** @type {Expression} */ (context.visit(raw_assignment));
|
|
46
52
|
|
|
47
53
|
if (dev) {
|
|
48
54
|
// in dev, create named functions, so that `$inspect(...)` delivers
|
|
@@ -58,16 +64,7 @@ export function BindDirective(node, context) {
|
|
|
58
64
|
get = b.thunk(expression);
|
|
59
65
|
|
|
60
66
|
/** @type {Expression | undefined} */
|
|
61
|
-
set = b.unthunk(
|
|
62
|
-
b.arrow(
|
|
63
|
-
[b.id('$$value')],
|
|
64
|
-
/** @type {Expression} */ (
|
|
65
|
-
context.visit(
|
|
66
|
-
b.assignment('=', /** @type {Pattern} */ (node.expression), b.id('$$value'))
|
|
67
|
-
)
|
|
68
|
-
)
|
|
69
|
-
)
|
|
70
|
-
);
|
|
67
|
+
set = b.unthunk(b.arrow([b.id('$$value')], assignment));
|
|
71
68
|
|
|
72
69
|
if (get === set) {
|
|
73
70
|
set = undefined;
|
|
@@ -19,7 +19,7 @@ export function ConstTag(node, context) {
|
|
|
19
19
|
if (declaration.id.type === 'Identifier') {
|
|
20
20
|
const init = build_expression(context, declaration.init, node.metadata.expression);
|
|
21
21
|
|
|
22
|
-
let expression = create_derived(context.state, init, node.metadata.expression
|
|
22
|
+
let expression = create_derived(context.state, init, node.metadata.expression);
|
|
23
23
|
|
|
24
24
|
if (dev) {
|
|
25
25
|
expression = b.call('$.tag', expression, b.literal(declaration.id.name));
|
|
@@ -45,20 +45,31 @@ export function ConstTag(node, context) {
|
|
|
45
45
|
transform
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
const is_simple_object_pattern =
|
|
49
|
+
declaration.id.type === 'ObjectPattern' &&
|
|
50
|
+
declaration.id.properties.every(
|
|
51
|
+
(p) =>
|
|
52
|
+
p.type === 'Property' &&
|
|
53
|
+
!p.computed &&
|
|
54
|
+
p.key.type === 'Identifier' &&
|
|
55
|
+
p.value.type === 'Identifier' &&
|
|
56
|
+
p.key.name === p.value.name
|
|
57
|
+
);
|
|
58
|
+
|
|
50
59
|
const init = build_expression(
|
|
51
60
|
{ ...context, state: child_state },
|
|
52
61
|
declaration.init,
|
|
53
62
|
node.metadata.expression
|
|
54
63
|
);
|
|
55
64
|
|
|
56
|
-
const block =
|
|
57
|
-
b.
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
const block = is_simple_object_pattern
|
|
66
|
+
? b.block([b.return(init)])
|
|
67
|
+
: b.block([
|
|
68
|
+
b.const(/** @type {Pattern} */ (context.visit(declaration.id, child_state)), init),
|
|
69
|
+
b.return(b.object(identifiers.map((node) => b.prop('init', node, node))))
|
|
70
|
+
]);
|
|
60
71
|
|
|
61
|
-
let expression = create_derived(context.state, block, node.metadata.expression
|
|
72
|
+
let expression = create_derived(context.state, block, node.metadata.expression);
|
|
62
73
|
|
|
63
74
|
if (dev) {
|
|
64
75
|
expression = b.call('$.tag', expression, b.literal('[@const]'));
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
/** @import { ComponentContext } from '../types' */
|
|
4
4
|
import { extract_identifiers, has_await_expression } from '../../../../utils/ast.js';
|
|
5
5
|
import * as b from '#compiler/builders';
|
|
6
|
+
import { async_thunk } from '../utils.js';
|
|
6
7
|
import { add_state_transformers } from './shared/declarations.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -70,7 +71,7 @@ export function add_async_declaration(context, metadata, ids, assignments, kind
|
|
|
70
71
|
context.state.consts.push(kind === 'var' ? b.var(id.name) : b.let(id.name));
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
const blockers = [...metadata.expression.
|
|
74
|
+
const blockers = [...metadata.expression.references]
|
|
74
75
|
.map((dep) => dep.blocker)
|
|
75
76
|
.filter((b) => b !== null && b.object !== context.state.async_consts?.id);
|
|
76
77
|
|
|
@@ -85,5 +86,5 @@ export function add_async_declaration(context, metadata, ids, assignments, kind
|
|
|
85
86
|
metadata.expression.has_await ||
|
|
86
87
|
assignments.some((assignment) => has_await_expression(assignment));
|
|
87
88
|
const body = assignments.length === 1 ? assignments[0].expression : b.block(assignments);
|
|
88
|
-
run.thunks.push(b.thunk(body
|
|
89
|
+
run.thunks.push(has_await ? async_thunk(body, metadata.expression) : b.thunk(body));
|
|
89
90
|
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { dev } from '../../../../state.js';
|
|
13
13
|
import { extract_paths, object } from '../../../../utils/ast.js';
|
|
14
14
|
import * as b from '#compiler/builders';
|
|
15
|
+
import { async_thunk } from '../utils.js';
|
|
15
16
|
import { get_value } from './shared/declarations.js';
|
|
16
17
|
import { build_expression, add_svelte_meta } from './shared/utils.js';
|
|
17
18
|
|
|
@@ -313,7 +314,9 @@ export function EachBlock(node, context) {
|
|
|
313
314
|
|
|
314
315
|
const has_await = node.metadata.expression.has_await;
|
|
315
316
|
|
|
316
|
-
const get_collection =
|
|
317
|
+
const get_collection = has_await
|
|
318
|
+
? async_thunk(collection, node.metadata.expression)
|
|
319
|
+
: b.thunk(collection);
|
|
317
320
|
const thunk = has_await ? b.thunk(b.call('$.get', b.id('$$collection'))) : get_collection;
|
|
318
321
|
|
|
319
322
|
const render_args = [b.id('$$anchor'), item];
|
|
@@ -141,14 +141,9 @@ export function Fragment(node, context) {
|
|
|
141
141
|
flags |= TEMPLATE_USE_IMPORT_NODE;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
|
|
145
|
-
// special case — we can use `$.comment` instead of creating a unique template
|
|
146
|
-
state.init.unshift(b.var(id, b.call('$.comment')));
|
|
147
|
-
} else {
|
|
148
|
-
const template_name = transform_template(state, 'root', flags);
|
|
144
|
+
const template_name = transform_template(state, 'root', flags);
|
|
149
145
|
|
|
150
|
-
|
|
151
|
-
}
|
|
146
|
+
state.init.unshift(b.var(id, b.call(template_name)));
|
|
152
147
|
|
|
153
148
|
close = b.stmt(b.call('$.append', b.id('$$anchor'), id));
|
|
154
149
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/** @import { ComponentContext } from '../types' */
|
|
3
3
|
import { is_ignored } from '../../../../state.js';
|
|
4
4
|
import * as b from '#compiler/builders';
|
|
5
|
+
import { async_thunk } from '../utils.js';
|
|
5
6
|
import { build_expression } from './shared/utils.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -46,7 +47,7 @@ export function HtmlTag(node, context) {
|
|
|
46
47
|
'$.async',
|
|
47
48
|
context.state.node,
|
|
48
49
|
node.metadata.expression.blockers(),
|
|
49
|
-
has_await ? b.array([
|
|
50
|
+
has_await ? b.array([async_thunk(expression, node.metadata.expression)]) : b.void0,
|
|
50
51
|
b.arrow(
|
|
51
52
|
has_await ? [context.state.node, b.id('$$html')] : [context.state.node],
|
|
52
53
|
b.block([statement])
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
3
|
/** @import { ComponentContext } from '../types' */
|
|
4
4
|
import * as b from '#compiler/builders';
|
|
5
|
+
import { async_thunk } from '../utils.js';
|
|
5
6
|
import { build_expression, add_svelte_meta } from './shared/utils.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -117,7 +118,7 @@ export function IfBlock(node, context) {
|
|
|
117
118
|
'$.async',
|
|
118
119
|
context.state.node,
|
|
119
120
|
node.metadata.expression.blockers(),
|
|
120
|
-
has_await ? b.array([
|
|
121
|
+
has_await ? b.array([async_thunk(expression, node.metadata.expression)]) : b.void0,
|
|
121
122
|
b.arrow(
|
|
122
123
|
has_await ? [context.state.node, b.id('$$condition')] : [context.state.node],
|
|
123
124
|
b.block(statements)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
3
|
/** @import { ComponentContext } from '../types' */
|
|
4
4
|
import * as b from '#compiler/builders';
|
|
5
|
+
import { async_thunk } from '../utils.js';
|
|
5
6
|
import { build_expression, add_svelte_meta } from './shared/utils.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -31,7 +32,7 @@ export function KeyBlock(node, context) {
|
|
|
31
32
|
'$.async',
|
|
32
33
|
context.state.node,
|
|
33
34
|
node.metadata.expression.blockers(),
|
|
34
|
-
has_await ? b.array([
|
|
35
|
+
has_await ? b.array([async_thunk(expression, node.metadata.expression)]) : b.void0,
|
|
35
36
|
b.arrow(
|
|
36
37
|
has_await ? [context.state.node, b.id('$$key')] : [context.state.node],
|
|
37
38
|
b.block([statement])
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
/** @import { ArrayExpression, Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression } from 'estree' */
|
|
1
|
+
/** @import { ArrayExpression, Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression, Statement } from 'estree' */
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
3
|
/** @import { ComponentClientTransformState, ComponentContext } from '../types' */
|
|
4
4
|
/** @import { Scope } from '../../../scope' */
|
|
5
5
|
import {
|
|
6
6
|
cannot_be_set_statically,
|
|
7
|
-
is_boolean_attribute,
|
|
8
7
|
is_dom_property,
|
|
9
8
|
is_load_error_element
|
|
10
9
|
} from '../../../../../utils.js';
|
|
@@ -13,7 +12,6 @@ import { is_event_attribute, is_text_attribute } from '../../../../utils/ast.js'
|
|
|
13
12
|
import * as b from '#compiler/builders';
|
|
14
13
|
import {
|
|
15
14
|
create_attribute,
|
|
16
|
-
ExpressionMetadata,
|
|
17
15
|
is_custom_element_node,
|
|
18
16
|
is_customizable_select_element
|
|
19
17
|
} from '../../../nodes.js';
|
|
@@ -231,11 +229,18 @@ export function RegularElement(node, context) {
|
|
|
231
229
|
continue;
|
|
232
230
|
}
|
|
233
231
|
|
|
232
|
+
// `<select defaultValue>` needs the options to exist before it can mark one
|
|
233
|
+
// as selected, so it is handled after the children, alongside `value`
|
|
234
|
+
if (node.name === 'select' && get_attribute_name(node, attribute) === 'defaultValue') {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
|
|
234
238
|
const name = get_attribute_name(node, attribute);
|
|
235
239
|
|
|
236
240
|
if (
|
|
237
241
|
!is_custom_element &&
|
|
238
242
|
!cannot_be_set_statically(attribute.name) &&
|
|
243
|
+
(name !== 'value' || node.name !== 'textarea') &&
|
|
239
244
|
(attribute.value === true || is_text_attribute(attribute)) &&
|
|
240
245
|
(name !== 'class' || class_directives.length === 0) &&
|
|
241
246
|
(name !== 'style' || style_directives.length === 0)
|
|
@@ -434,7 +439,7 @@ export function RegularElement(node, context) {
|
|
|
434
439
|
state: child_state
|
|
435
440
|
});
|
|
436
441
|
|
|
437
|
-
if (needs_reset) {
|
|
442
|
+
if (needs_reset && !fold_reset_into_child(child_state.init, context.state.node)) {
|
|
438
443
|
child_state.init.push(b.stmt(b.call('$.reset', context.state.node)));
|
|
439
444
|
}
|
|
440
445
|
}
|
|
@@ -513,6 +518,34 @@ export function RegularElement(node, context) {
|
|
|
513
518
|
}
|
|
514
519
|
}
|
|
515
520
|
|
|
521
|
+
// deferred from the attribute loop above, so that the options it selects from
|
|
522
|
+
// have been created and had their values assigned
|
|
523
|
+
if (!has_spread && name === 'select') {
|
|
524
|
+
const default_value = /** @type {AST.Attribute[]} */ (attributes).find(
|
|
525
|
+
(attribute) => get_attribute_name(node, attribute) === 'defaultValue'
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
if (default_value) {
|
|
529
|
+
const { value, has_state } = build_attribute_value(default_value.value, context, (v, m) =>
|
|
530
|
+
context.state.memoizer.add(v, m)
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
(has_state ? context.state.update : context.state.init).push(
|
|
534
|
+
b.stmt(b.call('$.set_default_select_value', node_id, value))
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const value_attribute = lookup.get('value');
|
|
539
|
+
const dynamic_value =
|
|
540
|
+
value_attribute !== undefined &&
|
|
541
|
+
value_attribute.value !== true &&
|
|
542
|
+
!is_text_attribute(value_attribute);
|
|
543
|
+
|
|
544
|
+
if (default_value || dynamic_value || bindings.has('value')) {
|
|
545
|
+
context.state.init.push(b.stmt(b.call('$.init_select', node_id)));
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
516
549
|
context.state.template.pop_element();
|
|
517
550
|
}
|
|
518
551
|
|
|
@@ -528,18 +561,12 @@ export function build_class_directives_object(
|
|
|
528
561
|
) {
|
|
529
562
|
let properties = [];
|
|
530
563
|
|
|
531
|
-
const metadata = new ExpressionMetadata();
|
|
532
|
-
|
|
533
564
|
for (const d of class_directives) {
|
|
534
|
-
metadata.merge(d.metadata.expression);
|
|
535
|
-
|
|
536
565
|
const expression = /** @type Expression */ (context.visit(d.expression));
|
|
537
|
-
properties.push(b.init(d.name, expression));
|
|
566
|
+
properties.push(b.init(d.name, memoizer.add(expression, d.metadata.expression)));
|
|
538
567
|
}
|
|
539
568
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
return memoizer.add(directives, metadata);
|
|
569
|
+
return b.object(properties);
|
|
543
570
|
}
|
|
544
571
|
|
|
545
572
|
/**
|
|
@@ -555,23 +582,17 @@ export function build_style_directives_object(
|
|
|
555
582
|
const normal = b.object([]);
|
|
556
583
|
const important = b.object([]);
|
|
557
584
|
|
|
558
|
-
const metadata = new ExpressionMetadata();
|
|
559
|
-
|
|
560
585
|
for (const d of style_directives) {
|
|
561
|
-
metadata.merge(d.metadata.expression);
|
|
562
|
-
|
|
563
586
|
const expression =
|
|
564
587
|
d.value === true
|
|
565
588
|
? build_getter(b.id(d.name), context.state)
|
|
566
589
|
: build_attribute_value(d.value, context).value;
|
|
567
590
|
|
|
568
591
|
const object = d.modifiers.includes('important') ? important : normal;
|
|
569
|
-
object.properties.push(b.init(d.name, expression));
|
|
592
|
+
object.properties.push(b.init(d.name, memoizer.add(expression, d.metadata.expression)));
|
|
570
593
|
}
|
|
571
594
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
return memoizer.add(directives, metadata);
|
|
595
|
+
return important.properties.length ? b.array([normal, important]) : normal;
|
|
575
596
|
}
|
|
576
597
|
|
|
577
598
|
/**
|
|
@@ -664,14 +685,25 @@ function build_element_attribute_update(element, node_id, name, value, attribute
|
|
|
664
685
|
* @param {ComponentContext} context
|
|
665
686
|
*/
|
|
666
687
|
function build_custom_element_attribute_update_assignment(node_id, attribute, context) {
|
|
667
|
-
const
|
|
688
|
+
const memoizer = new Memoizer();
|
|
689
|
+
const { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) =>
|
|
690
|
+
memoizer.add(value, metadata)
|
|
691
|
+
);
|
|
668
692
|
|
|
669
693
|
// don't lowercase name, as we set the element's property, which might be case sensitive
|
|
670
694
|
const call = b.call('$.set_custom_element_data', node_id, b.literal(attribute.name), value);
|
|
671
695
|
|
|
672
696
|
// this is different from other updates — it doesn't get grouped,
|
|
673
697
|
// because set_custom_element_data may not be idempotent
|
|
674
|
-
const update = has_state
|
|
698
|
+
const update = has_state
|
|
699
|
+
? b.call(
|
|
700
|
+
'$.template_effect',
|
|
701
|
+
b.arrow(memoizer.apply(), call),
|
|
702
|
+
memoizer.sync_values(),
|
|
703
|
+
memoizer.async_values(),
|
|
704
|
+
memoizer.blockers()
|
|
705
|
+
)
|
|
706
|
+
: call;
|
|
675
707
|
|
|
676
708
|
context.state.init.push(b.stmt(update));
|
|
677
709
|
}
|
|
@@ -704,28 +736,32 @@ function build_element_special_value_attribute(
|
|
|
704
736
|
);
|
|
705
737
|
|
|
706
738
|
const evaluated = context.state.scope.evaluate(value);
|
|
707
|
-
const assignment = b.assignment('=', b.member(node_id, '__value'), value);
|
|
708
739
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
b.member(node_id, '
|
|
712
|
-
evaluated.is_defined ? assignment : b.logical('??', assignment, b.literal(''))
|
|
713
|
-
);
|
|
740
|
+
/** @param {Expression} value */
|
|
741
|
+
const build_update = (value) => {
|
|
742
|
+
const assignment = b.assignment('=', b.member(node_id, '__value'), value);
|
|
714
743
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
744
|
+
const set_value_assignment = b.assignment(
|
|
745
|
+
'=',
|
|
746
|
+
b.member(node_id, 'value'),
|
|
747
|
+
evaluated.is_defined ? assignment : b.logical('??', assignment, b.literal(''))
|
|
748
|
+
);
|
|
749
|
+
|
|
750
|
+
return b.stmt(
|
|
751
|
+
is_select_with_value
|
|
752
|
+
? b.sequence([
|
|
753
|
+
set_value_assignment,
|
|
754
|
+
// This ensures a one-way street to the DOM in case it's <select {value}>
|
|
755
|
+
// and not <select bind:value>. We need it in addition to $.init_select
|
|
756
|
+
// because the select value is not reflected as an attribute, so the
|
|
757
|
+
// mutation observer wouldn't notice.
|
|
758
|
+
b.call('$.select_option', node_id, value)
|
|
759
|
+
])
|
|
760
|
+
: synthetic
|
|
761
|
+
? assignment
|
|
762
|
+
: set_value_assignment
|
|
763
|
+
);
|
|
764
|
+
};
|
|
729
765
|
|
|
730
766
|
if (has_state) {
|
|
731
767
|
const id = b.id(state.scope.generate(`${node_id.name}_value`));
|
|
@@ -736,12 +772,49 @@ function build_element_special_value_attribute(
|
|
|
736
772
|
const init = element === 'option' ? b.object([]) : undefined;
|
|
737
773
|
|
|
738
774
|
state.init.push(b.var(id, init));
|
|
739
|
-
|
|
775
|
+
|
|
776
|
+
// the guard already evaluated `value` into `id`, so read that back rather than
|
|
777
|
+
// evaluating the same expression (and its signal reads) a second time
|
|
778
|
+
state.update.push(
|
|
779
|
+
b.if(b.binary('!==', id, b.assignment('=', id, value)), b.block([build_update(id)]))
|
|
780
|
+
);
|
|
740
781
|
} else {
|
|
741
|
-
state.init.push(
|
|
782
|
+
state.init.push(build_update(value));
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* `<p>{text}</p>` and friends produce `var x = $.child(p, true); $.reset(p);`. That pair is
|
|
788
|
+
* by far the most common shape in compiled output, and `$.only_child` does both, so fold the
|
|
789
|
+
* two together when the `$.child(...)` is the last thing we emitted for this element.
|
|
790
|
+
* @param {Statement[]} init
|
|
791
|
+
* @param {Expression} node_id
|
|
792
|
+
* @returns {boolean} whether the reset was folded in
|
|
793
|
+
*/
|
|
794
|
+
function fold_reset_into_child(init, node_id) {
|
|
795
|
+
const last = init.at(-1);
|
|
796
|
+
|
|
797
|
+
if (
|
|
798
|
+
node_id?.type !== 'Identifier' ||
|
|
799
|
+
last?.type !== 'VariableDeclaration' ||
|
|
800
|
+
last.declarations.length !== 1
|
|
801
|
+
) {
|
|
802
|
+
return false;
|
|
742
803
|
}
|
|
743
804
|
|
|
744
|
-
|
|
745
|
-
|
|
805
|
+
const call = last.declarations[0].init;
|
|
806
|
+
|
|
807
|
+
if (
|
|
808
|
+
call?.type !== 'CallExpression' ||
|
|
809
|
+
call.callee.type !== 'Identifier' ||
|
|
810
|
+
call.callee.name !== '$.child' ||
|
|
811
|
+
call.arguments[0]?.type !== 'Identifier' ||
|
|
812
|
+
call.arguments[0].name !== node_id.name
|
|
813
|
+
) {
|
|
814
|
+
return false;
|
|
746
815
|
}
|
|
816
|
+
|
|
817
|
+
call.callee = b.id('$.only_child');
|
|
818
|
+
|
|
819
|
+
return true;
|
|
747
820
|
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { dev, locator } from '../../../../state.js';
|
|
5
5
|
import { is_text_attribute } from '../../../../utils/ast.js';
|
|
6
6
|
import * as b from '#compiler/builders';
|
|
7
|
+
import { async_thunk } from '../utils.js';
|
|
7
8
|
import { determine_namespace_for_children } from '../../utils.js';
|
|
8
9
|
import {
|
|
9
10
|
build_attribute_value,
|
|
@@ -147,7 +148,7 @@ export function SvelteElement(node, context) {
|
|
|
147
148
|
'$.async',
|
|
148
149
|
context.state.node,
|
|
149
150
|
node.metadata.expression.blockers(),
|
|
150
|
-
has_await ? b.array([
|
|
151
|
+
has_await ? b.array([async_thunk(expression, node.metadata.expression)]) : b.void0,
|
|
151
152
|
b.arrow(
|
|
152
153
|
has_await ? [context.state.node, b.id('$$tag')] : [context.state.node],
|
|
153
154
|
b.block(statements)
|
|
@@ -13,7 +13,6 @@ export function UpdateExpression(node, context) {
|
|
|
13
13
|
|
|
14
14
|
if (
|
|
15
15
|
argument.type === 'MemberExpression' &&
|
|
16
|
-
argument.object.type === 'ThisExpression' &&
|
|
17
16
|
argument.property.type === 'PrivateIdentifier' &&
|
|
18
17
|
context.state.state_fields.has('#' + argument.property.name)
|
|
19
18
|
) {
|
|
@@ -6,7 +6,13 @@ import { extract_paths, save } from '../../../../utils/ast.js';
|
|
|
6
6
|
import * as b from '#compiler/builders';
|
|
7
7
|
import * as assert from '../../../../utils/assert.js';
|
|
8
8
|
import { get_rune } from '../../../scope.js';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
async_thunk,
|
|
11
|
+
get_prop_source,
|
|
12
|
+
is_prop_source,
|
|
13
|
+
is_state_source,
|
|
14
|
+
should_proxy
|
|
15
|
+
} from '../utils.js';
|
|
10
16
|
import { get_value } from './shared/declarations.js';
|
|
11
17
|
|
|
12
18
|
/**
|
|
@@ -200,9 +206,10 @@ export function VariableDeclaration(node, context) {
|
|
|
200
206
|
}
|
|
201
207
|
|
|
202
208
|
if (rune === '$derived' || rune === '$derived.by') {
|
|
203
|
-
const
|
|
209
|
+
const metadata = context.state.analysis.async_deriveds.get(
|
|
204
210
|
/** @type {CallExpression} */ (init)
|
|
205
211
|
);
|
|
212
|
+
const is_async = metadata !== undefined;
|
|
206
213
|
|
|
207
214
|
if (declarator.id.type === 'Identifier') {
|
|
208
215
|
let expression = /** @type {Expression} */ (context.visit(value));
|
|
@@ -213,7 +220,7 @@ export function VariableDeclaration(node, context) {
|
|
|
213
220
|
/** @type {Expression} */
|
|
214
221
|
let call = b.call(
|
|
215
222
|
'$.async_derived',
|
|
216
|
-
|
|
223
|
+
async_thunk(expression, metadata),
|
|
217
224
|
dev && b.literal(declarator.id.name),
|
|
218
225
|
location ? b.literal(location) : undefined
|
|
219
226
|
);
|
|
@@ -246,7 +253,7 @@ export function VariableDeclaration(node, context) {
|
|
|
246
253
|
|
|
247
254
|
call = b.call(
|
|
248
255
|
'$.async_derived',
|
|
249
|
-
|
|
256
|
+
async_thunk(expression, metadata),
|
|
250
257
|
dev &&
|
|
251
258
|
b.literal(
|
|
252
259
|
`[$derived ${declarator.id.type === 'ArrayPattern' ? 'iterable' : 'object'}]`
|