svelte 5.43.7 → 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.
Files changed (26) hide show
  1. package/compiler/index.js +1 -1
  2. package/package.json +1 -1
  3. package/src/compiler/phases/1-parse/utils/create.js +1 -2
  4. package/src/compiler/phases/2-analyze/index.js +269 -187
  5. package/src/compiler/phases/2-analyze/visitors/AwaitExpression.js +0 -4
  6. package/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js +29 -15
  7. package/src/compiler/phases/3-transform/client/visitors/ConstTag.js +50 -14
  8. package/src/compiler/phases/3-transform/client/visitors/Fragment.js +5 -14
  9. package/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js +2 -8
  10. package/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js +12 -2
  11. package/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +1 -1
  12. package/src/compiler/phases/3-transform/server/visitors/ConstTag.js +25 -1
  13. package/src/compiler/phases/3-transform/server/visitors/EachBlock.js +2 -6
  14. package/src/compiler/phases/3-transform/server/visitors/Fragment.js +8 -1
  15. package/src/compiler/phases/3-transform/server/visitors/IfBlock.js +1 -5
  16. package/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js +0 -5
  17. package/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js +3 -10
  18. package/src/compiler/phases/3-transform/server/visitors/shared/component.js +1 -6
  19. package/src/compiler/phases/nodes.js +4 -0
  20. package/src/compiler/phases/scope.js +8 -4
  21. package/src/compiler/utils/builders.js +1 -1
  22. package/src/internal/client/dom/blocks/each.js +156 -236
  23. package/src/internal/client/reactivity/deriveds.js +5 -2
  24. package/src/internal/client/reactivity/effects.js +33 -37
  25. package/src/internal/client/reactivity/sources.js +17 -9
  26. 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, { ...context.state, consts: const_tags });
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} */ (context.visit({ ...node.fragment, nodes }));
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
- !(node.body.metadata.has_await || node.metadata.expression.is_async())
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.init.push(b.const(id, init));
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
- node.fallback.metadata.has_await ? create_async_block(fallback) : fallback
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, statement, block_close]))
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
- const statement = node.fragment.metadata.has_await
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 (
@@ -112,6 +112,10 @@ export class ExpressionMetadata {
112
112
  return b.array([...this.#get_blockers()]);
113
113
  }
114
114
 
115
+ has_blockers() {
116
+ return this.#get_blockers().size > 0;
117
+ }
118
+
115
119
  is_async() {
116
120
  return this.has_await || this.#get_blockers().size > 0;
117
121
  }
@@ -1,4 +1,4 @@
1
- /** @import { ArrowFunctionExpression, BinaryOperator, ClassDeclaration, Expression, FunctionDeclaration, FunctionExpression, Identifier, ImportDeclaration, MemberExpression, LogicalOperator, Node, Pattern, UnaryOperator, VariableDeclarator, Super } from 'estree' */
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
- /** @type {Array<{ value: Expression; scope: Scope }>} */
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 {Expression | null}
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 {'const' | 'let' | 'var'} kind
184
+ * @param {ESTree.VariableDeclaration['kind']} kind
185
185
  * @param {ESTree.VariableDeclarator[]} declarations
186
186
  * @returns {ESTree.VariableDeclaration}
187
187
  */