svelte 5.43.8 → 5.43.9
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/package.json +1 -1
- package/src/compiler/phases/1-parse/utils/create.js +1 -2
- package/src/compiler/phases/2-analyze/index.js +269 -187
- package/src/compiler/phases/2-analyze/visitors/AwaitExpression.js +0 -4
- package/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js +29 -15
- package/src/compiler/phases/3-transform/client/visitors/ConstTag.js +50 -14
- package/src/compiler/phases/3-transform/client/visitors/Fragment.js +5 -14
- package/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js +2 -8
- package/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js +12 -2
- package/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +1 -1
- package/src/compiler/phases/3-transform/server/visitors/ConstTag.js +25 -1
- package/src/compiler/phases/3-transform/server/visitors/EachBlock.js +2 -6
- package/src/compiler/phases/3-transform/server/visitors/Fragment.js +8 -1
- package/src/compiler/phases/3-transform/server/visitors/IfBlock.js +1 -5
- package/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js +0 -5
- package/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js +3 -10
- package/src/compiler/phases/3-transform/server/visitors/shared/component.js +1 -6
- package/src/compiler/phases/nodes.js +4 -0
- package/src/compiler/phases/scope.js +8 -4
- package/src/compiler/utils/builders.js +1 -1
- package/src/internal/client/dom/blocks/each.js +9 -7
- package/src/internal/client/reactivity/deriveds.js +5 -2
- package/src/internal/client/reactivity/sources.js +17 -9
- package/src/version.js +1 -1
|
@@ -48,7 +48,11 @@ export function SvelteBoundary(node, context) {
|
|
|
48
48
|
if (child.type === 'ConstTag') {
|
|
49
49
|
has_const = true;
|
|
50
50
|
if (!context.state.options.experimental.async) {
|
|
51
|
-
context.visit(child, {
|
|
51
|
+
context.visit(child, {
|
|
52
|
+
...context.state,
|
|
53
|
+
consts: const_tags,
|
|
54
|
+
scope: context.state.scopes.get(node.fragment) ?? context.state.scope
|
|
55
|
+
});
|
|
52
56
|
}
|
|
53
57
|
}
|
|
54
58
|
}
|
|
@@ -101,7 +105,13 @@ export function SvelteBoundary(node, context) {
|
|
|
101
105
|
nodes.push(child);
|
|
102
106
|
}
|
|
103
107
|
|
|
104
|
-
const block = /** @type {BlockStatement} */ (
|
|
108
|
+
const block = /** @type {BlockStatement} */ (
|
|
109
|
+
context.visit(
|
|
110
|
+
{ ...node.fragment, nodes },
|
|
111
|
+
// Since we're creating a new fragment the reference in scopes can't match, so we gotta attach the right scope manually
|
|
112
|
+
{ ...context.state, scope: context.state.scopes.get(node.fragment) ?? context.state.scope }
|
|
113
|
+
)
|
|
114
|
+
);
|
|
105
115
|
|
|
106
116
|
if (!context.state.options.experimental.async) {
|
|
107
117
|
block.body.unshift(...const_tags);
|
|
@@ -105,7 +105,7 @@ export function process_children(nodes, initial, is_element, context) {
|
|
|
105
105
|
is_element &&
|
|
106
106
|
// In case it's wrapped in async the async logic will want to skip sibling nodes up until the end, hence we cannot make this controlled
|
|
107
107
|
// TODO switch this around and instead optimize for elements with a single block child and not require extra comments (neither for async nor normally)
|
|
108
|
-
!
|
|
108
|
+
!node.metadata.expression.is_async()
|
|
109
109
|
) {
|
|
110
110
|
node.metadata.is_controlled = true;
|
|
111
111
|
} else {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/** @import { AST } from '#compiler' */
|
|
3
3
|
/** @import { ComponentContext } from '../types.js' */
|
|
4
4
|
import * as b from '#compiler/builders';
|
|
5
|
+
import { extract_identifiers } from '../../../../utils/ast.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @param {AST.ConstTag} node
|
|
@@ -11,6 +12,29 @@ export function ConstTag(node, context) {
|
|
|
11
12
|
const declaration = node.declaration.declarations[0];
|
|
12
13
|
const id = /** @type {Pattern} */ (context.visit(declaration.id));
|
|
13
14
|
const init = /** @type {Expression} */ (context.visit(declaration.init));
|
|
15
|
+
const has_await = node.metadata.expression.has_await;
|
|
14
16
|
|
|
15
|
-
context.state.
|
|
17
|
+
if (has_await || context.state.async_consts) {
|
|
18
|
+
const run = (context.state.async_consts ??= {
|
|
19
|
+
id: b.id(context.state.scope.generate('promises')),
|
|
20
|
+
thunks: []
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const identifiers = extract_identifiers(declaration.id);
|
|
24
|
+
const bindings = context.state.scope.get_bindings(declaration);
|
|
25
|
+
|
|
26
|
+
for (const identifier of identifiers) {
|
|
27
|
+
context.state.init.push(b.let(identifier.name));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const assignment = b.assignment('=', id, init);
|
|
31
|
+
run.thunks.push(b.thunk(b.block([b.stmt(assignment)]), has_await));
|
|
32
|
+
|
|
33
|
+
const blocker = b.member(run.id, b.literal(run.thunks.length - 1), true);
|
|
34
|
+
for (const binding of bindings) {
|
|
35
|
+
binding.blocker = blocker;
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
context.state.init.push(b.const(id, init));
|
|
39
|
+
}
|
|
16
40
|
}
|
|
@@ -34,11 +34,7 @@ export function EachBlock(node, context) {
|
|
|
34
34
|
|
|
35
35
|
const new_body = /** @type {BlockStatement} */ (context.visit(node.body)).body;
|
|
36
36
|
|
|
37
|
-
if (node.body)
|
|
38
|
-
each.push(
|
|
39
|
-
// TODO get rid of fragment.has_await
|
|
40
|
-
...(node.body.metadata.has_await ? [create_async_block(b.block(new_body))] : new_body)
|
|
41
|
-
);
|
|
37
|
+
if (node.body) each.push(...new_body);
|
|
42
38
|
|
|
43
39
|
const for_loop = b.for(
|
|
44
40
|
b.declaration('let', [
|
|
@@ -61,7 +57,7 @@ export function EachBlock(node, context) {
|
|
|
61
57
|
b.if(
|
|
62
58
|
b.binary('!==', b.member(array_id, 'length'), b.literal(0)),
|
|
63
59
|
b.block([open, for_loop]),
|
|
64
|
-
|
|
60
|
+
fallback
|
|
65
61
|
)
|
|
66
62
|
);
|
|
67
63
|
} else {
|
|
@@ -28,7 +28,8 @@ export function Fragment(node, context) {
|
|
|
28
28
|
init: [],
|
|
29
29
|
template: [],
|
|
30
30
|
namespace,
|
|
31
|
-
skip_hydration_boundaries: is_standalone
|
|
31
|
+
skip_hydration_boundaries: is_standalone,
|
|
32
|
+
async_consts: undefined
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
for (const node of hoisted) {
|
|
@@ -42,5 +43,11 @@ export function Fragment(node, context) {
|
|
|
42
43
|
|
|
43
44
|
process_children(trimmed, { ...context, state });
|
|
44
45
|
|
|
46
|
+
if (state.async_consts && state.async_consts.thunks.length > 0) {
|
|
47
|
+
state.init.push(
|
|
48
|
+
b.var(state.async_consts.id, b.call('$$renderer.run', b.array(state.async_consts.thunks)))
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
45
52
|
return b.block([...state.init, ...build_template(state.template)]);
|
|
46
53
|
}
|
|
@@ -25,11 +25,7 @@ export function IfBlock(node, context) {
|
|
|
25
25
|
|
|
26
26
|
const is_async = node.metadata.expression.is_async();
|
|
27
27
|
|
|
28
|
-
const has_await =
|
|
29
|
-
node.metadata.expression.has_await ||
|
|
30
|
-
// TODO get rid of this stuff
|
|
31
|
-
node.consequent.metadata.has_await ||
|
|
32
|
-
node.alternate?.metadata.has_await;
|
|
28
|
+
const has_await = node.metadata.expression.has_await;
|
|
33
29
|
|
|
34
30
|
if (is_async || has_await) {
|
|
35
31
|
statement = create_async_block(
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
/** @import { ComponentContext } from '../types.js' */
|
|
4
4
|
import { dev } from '../../../../state.js';
|
|
5
5
|
import * as b from '#compiler/builders';
|
|
6
|
-
import { create_async_block } from './shared/utils.js';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* @param {AST.SnippetBlock} node
|
|
@@ -16,10 +15,6 @@ export function SnippetBlock(node, context) {
|
|
|
16
15
|
/** @type {BlockStatement} */ (context.visit(node.body))
|
|
17
16
|
);
|
|
18
17
|
|
|
19
|
-
if (node.body.metadata.has_await) {
|
|
20
|
-
fn.body = b.block([create_async_block(fn.body)]);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
18
|
// @ts-expect-error - TODO remove this hack once $$render_inner for legacy bindings is gone
|
|
24
19
|
fn.___snippet = true;
|
|
25
20
|
|
|
@@ -7,8 +7,7 @@ import {
|
|
|
7
7
|
block_open,
|
|
8
8
|
block_open_else,
|
|
9
9
|
build_attribute_value,
|
|
10
|
-
build_template
|
|
11
|
-
create_async_block
|
|
10
|
+
build_template
|
|
12
11
|
} from './shared/utils.js';
|
|
13
12
|
|
|
14
13
|
/**
|
|
@@ -43,14 +42,11 @@ export function SvelteBoundary(node, context) {
|
|
|
43
42
|
);
|
|
44
43
|
const pending = b.call(callee, b.id('$$renderer'));
|
|
45
44
|
const block = /** @type {BlockStatement} */ (context.visit(node.fragment));
|
|
46
|
-
const statement = node.fragment.metadata.has_await
|
|
47
|
-
? create_async_block(b.block([block]))
|
|
48
|
-
: block;
|
|
49
45
|
context.state.template.push(
|
|
50
46
|
b.if(
|
|
51
47
|
callee,
|
|
52
48
|
b.block(build_template([block_open_else, b.stmt(pending), block_close])),
|
|
53
|
-
b.block(build_template([block_open,
|
|
49
|
+
b.block(build_template([block_open, block, block_close]))
|
|
54
50
|
)
|
|
55
51
|
);
|
|
56
52
|
} else {
|
|
@@ -70,9 +66,6 @@ export function SvelteBoundary(node, context) {
|
|
|
70
66
|
}
|
|
71
67
|
} else {
|
|
72
68
|
const block = /** @type {BlockStatement} */ (context.visit(node.fragment));
|
|
73
|
-
|
|
74
|
-
? create_async_block(b.block([block]))
|
|
75
|
-
: block;
|
|
76
|
-
context.state.template.push(block_open, statement, block_close);
|
|
69
|
+
context.state.template.push(block_open, block, block_close);
|
|
77
70
|
}
|
|
78
71
|
}
|
|
@@ -244,12 +244,7 @@ export function build_inline_component(node, expression, context) {
|
|
|
244
244
|
params.push(pattern);
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
const slot_fn = b.arrow(
|
|
248
|
-
params,
|
|
249
|
-
node.fragment.metadata.has_await
|
|
250
|
-
? b.block([create_async_block(b.block(block.body))])
|
|
251
|
-
: b.block(block.body)
|
|
252
|
-
);
|
|
247
|
+
const slot_fn = b.arrow(params, b.block(block.body));
|
|
253
248
|
|
|
254
249
|
if (slot_name === 'default' && !has_children_prop) {
|
|
255
250
|
if (
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @import {
|
|
1
|
+
/** @import { BinaryOperator, ClassDeclaration, Expression, FunctionDeclaration, Identifier, ImportDeclaration, MemberExpression, LogicalOperator, Node, Pattern, UnaryOperator, VariableDeclarator, Super, SimpleLiteral, FunctionExpression, ArrowFunctionExpression } from 'estree' */
|
|
2
2
|
/** @import { Context, Visitor } from 'zimmerframe' */
|
|
3
3
|
/** @import { AST, BindingKind, DeclarationKind } from '#compiler' */
|
|
4
4
|
import is_reference from 'is-reference';
|
|
@@ -108,7 +108,10 @@ export class Binding {
|
|
|
108
108
|
/** @type {Array<{ node: Identifier; path: AST.SvelteNode[] }>} */
|
|
109
109
|
references = [];
|
|
110
110
|
|
|
111
|
-
/**
|
|
111
|
+
/**
|
|
112
|
+
* (Re)assignments of this binding. Includes declarations such as `function x() {}`.
|
|
113
|
+
* @type {Array<{ value: Expression; scope: Scope }>}
|
|
114
|
+
*/
|
|
112
115
|
assignments = [];
|
|
113
116
|
|
|
114
117
|
/**
|
|
@@ -135,9 +138,10 @@ export class Binding {
|
|
|
135
138
|
/**
|
|
136
139
|
* Instance-level declarations may follow (or contain) a top-level `await`. In these cases,
|
|
137
140
|
* any reads that occur in the template must wait for the corresponding promise to resolve
|
|
138
|
-
* otherwise the initial value will not have been assigned
|
|
141
|
+
* otherwise the initial value will not have been assigned.
|
|
142
|
+
* It is a member expression of the form `$$blockers[n]`.
|
|
139
143
|
* TODO the blocker is set during transform which feels a bit grubby
|
|
140
|
-
* @type {
|
|
144
|
+
* @type {MemberExpression & { object: Identifier, property: SimpleLiteral & { value: number } } | null}
|
|
141
145
|
*/
|
|
142
146
|
blocker = null;
|
|
143
147
|
|
|
@@ -181,7 +181,7 @@ export function logical(operator, left, right) {
|
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
/**
|
|
184
|
-
* @param {'
|
|
184
|
+
* @param {ESTree.VariableDeclaration['kind']} kind
|
|
185
185
|
* @param {ESTree.VariableDeclarator[]} declarations
|
|
186
186
|
* @returns {ESTree.VariableDeclaration}
|
|
187
187
|
*/
|
|
@@ -244,7 +244,9 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
|
|
|
244
244
|
item.i = i;
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
|
|
247
|
+
if (defer) {
|
|
248
|
+
batch.skipped_effects.delete(item.e);
|
|
249
|
+
}
|
|
248
250
|
} else {
|
|
249
251
|
item = create_item(
|
|
250
252
|
first_run ? anchor : null,
|
|
@@ -298,14 +300,14 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
|
|
|
298
300
|
set_hydrate_node(skip_nodes());
|
|
299
301
|
}
|
|
300
302
|
|
|
301
|
-
for (const [key, item] of state.items) {
|
|
302
|
-
if (!keys.has(key)) {
|
|
303
|
-
batch.skipped_effects.add(item.e);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
303
|
if (!first_run) {
|
|
308
304
|
if (defer) {
|
|
305
|
+
for (const [key, item] of state.items) {
|
|
306
|
+
if (!keys.has(key)) {
|
|
307
|
+
batch.skipped_effects.add(item.e);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
309
311
|
batch.oncommit(commit);
|
|
310
312
|
batch.ondiscard(() => {
|
|
311
313
|
// TODO presumably we need to do something here?
|
|
@@ -11,7 +11,8 @@ import {
|
|
|
11
11
|
STALE_REACTION,
|
|
12
12
|
ASYNC,
|
|
13
13
|
WAS_MARKED,
|
|
14
|
-
CONNECTED
|
|
14
|
+
CONNECTED,
|
|
15
|
+
DESTROYED
|
|
15
16
|
} from '#client/constants';
|
|
16
17
|
import {
|
|
17
18
|
active_reaction,
|
|
@@ -296,7 +297,9 @@ function get_derived_parent_effect(derived) {
|
|
|
296
297
|
var parent = derived.parent;
|
|
297
298
|
while (parent !== null) {
|
|
298
299
|
if ((parent.f & DERIVED) === 0) {
|
|
299
|
-
|
|
300
|
+
// The original parent effect might've been destroyed but the derived
|
|
301
|
+
// is used elsewhere now - do not return the destroyed effect in that case
|
|
302
|
+
return (parent.f & DESTROYED) === 0 ? /** @type {Effect} */ (parent) : null;
|
|
300
303
|
}
|
|
301
304
|
parent = parent.parent;
|
|
302
305
|
}
|
|
@@ -14,7 +14,9 @@ import {
|
|
|
14
14
|
is_dirty,
|
|
15
15
|
untracking,
|
|
16
16
|
is_destroying_effect,
|
|
17
|
-
push_reaction_value
|
|
17
|
+
push_reaction_value,
|
|
18
|
+
set_is_updating_effect,
|
|
19
|
+
is_updating_effect
|
|
18
20
|
} from '../runtime.js';
|
|
19
21
|
import { equals, safe_equals } from './equality.js';
|
|
20
22
|
import {
|
|
@@ -246,19 +248,25 @@ export function internal_set(source, value) {
|
|
|
246
248
|
|
|
247
249
|
export function flush_eager_effects() {
|
|
248
250
|
eager_effects_deferred = false;
|
|
251
|
+
var prev_is_updating_effect = is_updating_effect;
|
|
252
|
+
set_is_updating_effect(true);
|
|
249
253
|
|
|
250
254
|
const inspects = Array.from(eager_effects);
|
|
251
255
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
256
|
+
try {
|
|
257
|
+
for (const effect of inspects) {
|
|
258
|
+
// Mark clean inspect-effects as maybe dirty and then check their dirtiness
|
|
259
|
+
// instead of just updating the effects - this way we avoid overfiring.
|
|
260
|
+
if ((effect.f & CLEAN) !== 0) {
|
|
261
|
+
set_signal_status(effect, MAYBE_DIRTY);
|
|
262
|
+
}
|
|
258
263
|
|
|
259
|
-
|
|
260
|
-
|
|
264
|
+
if (is_dirty(effect)) {
|
|
265
|
+
update_effect(effect);
|
|
266
|
+
}
|
|
261
267
|
}
|
|
268
|
+
} finally {
|
|
269
|
+
set_is_updating_effect(prev_is_updating_effect);
|
|
262
270
|
}
|
|
263
271
|
|
|
264
272
|
eager_effects.clear();
|
package/src/version.js
CHANGED