svelte 5.42.3 → 5.43.1

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 (54) hide show
  1. package/compiler/index.js +1 -1
  2. package/package.json +1 -1
  3. package/src/compiler/phases/2-analyze/index.js +201 -2
  4. package/src/compiler/phases/2-analyze/visitors/AwaitExpression.js +6 -6
  5. package/src/compiler/phases/2-analyze/visitors/BindDirective.js +1 -0
  6. package/src/compiler/phases/2-analyze/visitors/SnippetBlock.js +6 -1
  7. package/src/compiler/phases/3-transform/client/transform-client.js +13 -34
  8. package/src/compiler/phases/3-transform/client/visitors/BindDirective.js +18 -7
  9. package/src/compiler/phases/3-transform/client/visitors/CallExpression.js +2 -2
  10. package/src/compiler/phases/3-transform/client/visitors/EachBlock.js +6 -4
  11. package/src/compiler/phases/3-transform/client/visitors/ExpressionStatement.js +1 -1
  12. package/src/compiler/phases/3-transform/client/visitors/HtmlTag.js +6 -4
  13. package/src/compiler/phases/3-transform/client/visitors/IfBlock.js +6 -4
  14. package/src/compiler/phases/3-transform/client/visitors/KeyBlock.js +5 -4
  15. package/src/compiler/phases/3-transform/client/visitors/Program.js +15 -3
  16. package/src/compiler/phases/3-transform/client/visitors/RenderTag.js +6 -4
  17. package/src/compiler/phases/3-transform/client/visitors/SlotElement.js +3 -1
  18. package/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +5 -4
  19. package/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +13 -2
  20. package/src/compiler/phases/3-transform/client/visitors/shared/component.js +20 -16
  21. package/src/compiler/phases/3-transform/client/visitors/shared/element.js +1 -0
  22. package/src/compiler/phases/3-transform/client/visitors/shared/utils.js +15 -1
  23. package/src/compiler/phases/3-transform/server/transform-server.js +9 -9
  24. package/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js +6 -2
  25. package/src/compiler/phases/3-transform/server/visitors/CallExpression.js +8 -1
  26. package/src/compiler/phases/3-transform/server/visitors/EachBlock.js +14 -3
  27. package/src/compiler/phases/3-transform/server/visitors/HtmlTag.js +3 -5
  28. package/src/compiler/phases/3-transform/server/visitors/IfBlock.js +12 -4
  29. package/src/compiler/phases/3-transform/server/visitors/KeyBlock.js +7 -1
  30. package/src/compiler/phases/3-transform/server/visitors/Program.js +25 -0
  31. package/src/compiler/phases/3-transform/server/visitors/RegularElement.js +14 -7
  32. package/src/compiler/phases/3-transform/server/visitors/RenderTag.js +27 -11
  33. package/src/compiler/phases/3-transform/server/visitors/SlotElement.js +7 -4
  34. package/src/compiler/phases/3-transform/server/visitors/SvelteElement.js +24 -4
  35. package/src/compiler/phases/3-transform/server/visitors/shared/component.js +10 -4
  36. package/src/compiler/phases/3-transform/server/visitors/shared/utils.js +79 -8
  37. package/src/compiler/phases/3-transform/shared/transform-async.js +102 -0
  38. package/src/compiler/phases/nodes.js +24 -0
  39. package/src/compiler/phases/scope.js +27 -8
  40. package/src/compiler/utils/ast.js +1 -1
  41. package/src/compiler/utils/builders.js +15 -4
  42. package/src/internal/client/dom/blocks/async.js +7 -6
  43. package/src/internal/client/dom/blocks/boundary.js +5 -8
  44. package/src/internal/client/dom/elements/attributes.js +3 -1
  45. package/src/internal/client/index.js +1 -0
  46. package/src/internal/client/proxy.js +1 -1
  47. package/src/internal/client/reactivity/async.js +105 -46
  48. package/src/internal/client/reactivity/batch.js +2 -15
  49. package/src/internal/client/reactivity/effects.js +3 -2
  50. package/src/internal/client/runtime.js +12 -11
  51. package/src/internal/server/renderer.js +58 -3
  52. package/src/version.js +1 -1
  53. package/types/index.d.ts.map +1 -1
  54. package/src/compiler/phases/3-transform/client/visitors/ImportDeclaration.js +0 -16
@@ -193,18 +193,25 @@ export function VariableDeclaration(node, context) {
193
193
  /** @type {CallExpression} */ (init)
194
194
  );
195
195
 
196
+ // for now, only wrap async derived in $.save if it's not
197
+ // a top-level instance derived. TODO in future maybe we
198
+ // can dewaterfall all of them?
199
+ const should_save = context.state.is_instance && context.state.scope.function_depth > 1;
200
+
196
201
  if (declarator.id.type === 'Identifier') {
197
202
  let expression = /** @type {Expression} */ (context.visit(value));
198
203
 
199
204
  if (is_async) {
200
205
  const location = dev && !is_ignored(init, 'await_waterfall') && locate_node(init);
206
+
207
+ /** @type {Expression} */
201
208
  let call = b.call(
202
209
  '$.async_derived',
203
210
  b.thunk(expression, true),
204
211
  location ? b.literal(location) : undefined
205
212
  );
206
213
 
207
- call = save(call);
214
+ call = should_save ? save(call) : b.await(call);
208
215
  if (dev) call = b.call('$.tag', call, b.literal(declarator.id.name));
209
216
 
210
217
  declarations.push(b.declarator(declarator.id, call));
@@ -224,18 +231,22 @@ export function VariableDeclaration(node, context) {
224
231
 
225
232
  if (rune !== '$derived' || init.arguments[0].type !== 'Identifier') {
226
233
  const id = b.id(context.state.scope.generate('$$d'));
234
+
235
+ /** @type {Expression} */
227
236
  let call = b.call('$.derived', rune === '$derived' ? b.thunk(expression) : expression);
228
237
 
229
238
  rhs = b.call('$.get', id);
230
239
 
231
240
  if (is_async) {
232
241
  const location = dev && !is_ignored(init, 'await_waterfall') && locate_node(init);
242
+
233
243
  call = b.call(
234
244
  '$.async_derived',
235
245
  b.thunk(expression, true),
236
246
  location ? b.literal(location) : undefined
237
247
  );
238
- call = save(call);
248
+
249
+ call = should_save ? save(call) : b.await(call);
239
250
  }
240
251
 
241
252
  if (dev) {
@@ -16,12 +16,12 @@ import { determine_slot } from '../../../../../utils/slot.js';
16
16
  * @returns {Statement}
17
17
  */
18
18
  export function build_component(node, component_name, context) {
19
- /**
20
- * @type {Expression}
21
- */
19
+ /** @type {Expression} */
22
20
  const anchor = context.state.node;
21
+
23
22
  /** @type {Array<Property[] | Expression>} */
24
23
  const props_and_spreads = [];
24
+
25
25
  /** @type {Array<() => void>} */
26
26
  const delayed_props = [];
27
27
 
@@ -129,14 +129,16 @@ export function build_component(node, component_name, context) {
129
129
  (events[attribute.name] ||= []).push(handler);
130
130
  } else if (attribute.type === 'SpreadAttribute') {
131
131
  const expression = /** @type {Expression} */ (context.visit(attribute));
132
+ const memoized_expression = memoizer.add(expression, attribute.metadata.expression);
133
+ const is_memoized = expression !== memoized_expression;
132
134
 
133
- if (attribute.metadata.expression.has_state || attribute.metadata.expression.has_await) {
135
+ if (
136
+ is_memoized ||
137
+ attribute.metadata.expression.has_state ||
138
+ attribute.metadata.expression.has_await
139
+ ) {
134
140
  props_and_spreads.push(
135
- b.thunk(
136
- attribute.metadata.expression.has_await || attribute.metadata.expression.has_call
137
- ? b.call('$.get', memoizer.add(expression, attribute.metadata.expression))
138
- : expression
139
- )
141
+ b.thunk(is_memoized ? b.call('$.get', memoized_expression) : expression)
140
142
  );
141
143
  } else {
142
144
  props_and_spreads.push(expression);
@@ -147,10 +149,10 @@ export function build_component(node, component_name, context) {
147
149
  b.init(
148
150
  attribute.name,
149
151
  build_attribute_value(attribute.value, context, (value, metadata) => {
152
+ const memoized = memoizer.add(value, metadata);
153
+
150
154
  // TODO put the derived in the local block
151
- return metadata.has_call || metadata.has_await
152
- ? b.call('$.get', memoizer.add(value, metadata))
153
- : value;
155
+ return value !== memoized ? b.call('$.get', memoized) : value;
154
156
  }).value
155
157
  )
156
158
  );
@@ -184,9 +186,9 @@ export function build_component(node, component_name, context) {
184
186
  );
185
187
  });
186
188
 
187
- return should_wrap_in_derived
188
- ? b.call('$.get', memoizer.add(value, metadata, true))
189
- : value;
189
+ const memoized = memoizer.add(value, metadata, should_wrap_in_derived);
190
+
191
+ return value !== memoized ? b.call('$.get', memoized) : value;
190
192
  }
191
193
  );
192
194
 
@@ -497,12 +499,14 @@ export function build_component(node, component_name, context) {
497
499
  memoizer.apply();
498
500
 
499
501
  const async_values = memoizer.async_values();
502
+ const blockers = memoizer.blockers();
500
503
 
501
- if (async_values) {
504
+ if (async_values || blockers) {
502
505
  return b.stmt(
503
506
  b.call(
504
507
  '$.async',
505
508
  anchor,
509
+ blockers,
506
510
  async_values,
507
511
  b.arrow([b.id('$$anchor'), ...memoizer.async_ids()], b.block(statements))
508
512
  )
@@ -89,6 +89,7 @@ export function build_attribute_effect(
89
89
  b.arrow(ids, b.object(values)),
90
90
  memoizer.sync_values(),
91
91
  memoizer.async_values(),
92
+ memoizer.blockers(),
92
93
  element.metadata.scoped &&
93
94
  context.state.analysis.css.hash !== '' &&
94
95
  b.literal(context.state.analysis.css.hash),
@@ -22,12 +22,21 @@ export class Memoizer {
22
22
  /** @type {Array<{ id: Identifier, expression: Expression }>} */
23
23
  #async = [];
24
24
 
25
+ /** @type {Set<Expression>} */
26
+ #blockers = new Set();
27
+
25
28
  /**
26
29
  * @param {Expression} expression
27
30
  * @param {ExpressionMetadata} metadata
28
31
  * @param {boolean} memoize_if_state
29
32
  */
30
33
  add(expression, metadata, memoize_if_state = false) {
34
+ for (const binding of metadata.dependencies) {
35
+ if (binding.blocker) {
36
+ this.#blockers.add(binding.blocker);
37
+ }
38
+ }
39
+
31
40
  const should_memoize =
32
41
  metadata.has_call || metadata.has_await || (memoize_if_state && metadata.has_state);
33
42
 
@@ -50,6 +59,10 @@ export class Memoizer {
50
59
  });
51
60
  }
52
61
 
62
+ blockers() {
63
+ return this.#blockers.size > 0 ? b.array([...this.#blockers]) : undefined;
64
+ }
65
+
53
66
  deriveds(runes = true) {
54
67
  return this.#sync.map((memo) =>
55
68
  b.let(memo.id, b.call(runes ? '$.derived' : '$.derived_safe_equal', b.thunk(memo.expression)))
@@ -185,7 +198,8 @@ export function build_render_statement(state) {
185
198
  : b.block(state.update)
186
199
  ),
187
200
  memoizer.sync_values(),
188
- memoizer.async_values()
201
+ memoizer.async_values(),
202
+ memoizer.blockers()
189
203
  )
190
204
  );
191
205
  }
@@ -25,6 +25,7 @@ import { IfBlock } from './visitors/IfBlock.js';
25
25
  import { KeyBlock } from './visitors/KeyBlock.js';
26
26
  import { LabeledStatement } from './visitors/LabeledStatement.js';
27
27
  import { MemberExpression } from './visitors/MemberExpression.js';
28
+ import { Program } from './visitors/Program.js';
28
29
  import { PropertyDefinition } from './visitors/PropertyDefinition.js';
29
30
  import { RegularElement } from './visitors/RegularElement.js';
30
31
  import { RenderTag } from './visitors/RenderTag.js';
@@ -40,7 +41,7 @@ import { TitleElement } from './visitors/TitleElement.js';
40
41
  import { UpdateExpression } from './visitors/UpdateExpression.js';
41
42
  import { VariableDeclaration } from './visitors/VariableDeclaration.js';
42
43
  import { SvelteBoundary } from './visitors/SvelteBoundary.js';
43
- import { call_component_renderer, create_async_block } from './visitors/shared/utils.js';
44
+ import { call_component_renderer } from './visitors/shared/utils.js';
44
45
 
45
46
  /** @type {Visitors} */
46
47
  const global_visitors = {
@@ -53,6 +54,7 @@ const global_visitors = {
53
54
  Identifier,
54
55
  LabeledStatement,
55
56
  MemberExpression,
57
+ Program,
56
58
  PropertyDefinition,
57
59
  UpdateExpression,
58
60
  VariableDeclaration
@@ -95,7 +97,7 @@ export function server_component(analysis, options) {
95
97
  options,
96
98
  scope: analysis.module.scope,
97
99
  scopes: analysis.module.scopes,
98
- hoisted: [b.import_all('$', 'svelte/internal/server')],
100
+ hoisted: [b.import_all('$', 'svelte/internal/server'), ...analysis.instance_body.hoisted],
99
101
  legacy_reactive_statements: new Map(),
100
102
  // these are set inside the `Fragment` visitor, and cannot be used until then
101
103
  init: /** @type {any} */ (null),
@@ -103,7 +105,8 @@ export function server_component(analysis, options) {
103
105
  namespace: options.namespace,
104
106
  preserve_whitespace: options.preserveWhitespace,
105
107
  state_fields: new Map(),
106
- skip_hydration_boundaries: false
108
+ skip_hydration_boundaries: false,
109
+ is_instance: false
107
110
  };
108
111
 
109
112
  const module = /** @type {ESTree.Program} */ (
@@ -113,7 +116,7 @@ export function server_component(analysis, options) {
113
116
  const instance = /** @type {ESTree.Program} */ (
114
117
  walk(
115
118
  /** @type {AST.SvelteNode} */ (analysis.instance.ast),
116
- { ...state, scopes: analysis.instance.scopes },
119
+ { ...state, scopes: analysis.instance.scopes, is_instance: true },
117
120
  {
118
121
  ...global_visitors,
119
122
  ImportDeclaration(node) {
@@ -243,10 +246,6 @@ export function server_component(analysis, options) {
243
246
  .../** @type {ESTree.Statement[]} */ (template.body)
244
247
  ]);
245
248
 
246
- if (analysis.instance.has_await) {
247
- component_block = b.block([create_async_block(component_block)]);
248
- }
249
-
250
249
  // trick esrap into including comments
251
250
  component_block.loc = instance.loc;
252
251
 
@@ -408,7 +407,8 @@ export function server_module(analysis, options) {
408
407
  // to be present for `javascript_visitors_legacy` and so is included in module
409
408
  // transform state as well as component transform state
410
409
  legacy_reactive_statements: new Map(),
411
- state_fields: new Map()
410
+ state_fields: new Map(),
411
+ is_instance: false
412
412
  };
413
413
 
414
414
  const module = /** @type {ESTree.Program} */ (
@@ -25,8 +25,12 @@ export function AwaitBlock(node, context) {
25
25
  )
26
26
  );
27
27
 
28
- if (node.metadata.expression.has_await) {
29
- statement = create_async_block(b.block([statement]));
28
+ if (node.metadata.expression.is_async()) {
29
+ statement = create_async_block(
30
+ b.block([statement]),
31
+ node.metadata.expression.blockers(),
32
+ node.metadata.expression.has_await
33
+ );
30
34
  }
31
35
 
32
36
  context.state.template.push(statement, block_close);
@@ -12,7 +12,14 @@ import { get_inspect_args } from '../../utils.js';
12
12
  export function CallExpression(node, context) {
13
13
  const rune = get_rune(node, context.state.scope);
14
14
 
15
- if (rune === '$host') {
15
+ if (
16
+ rune === '$host' ||
17
+ rune === '$effect' ||
18
+ rune === '$effect.pre' ||
19
+ rune === '$inspect.trace'
20
+ ) {
21
+ // we will only encounter `$effect` etc if they are top-level statements in the <script>
22
+ // following an `await`, otherwise they are removed by the ExpressionStatement visitor
16
23
  return b.void0;
17
24
  }
18
25
 
@@ -34,7 +34,11 @@ export function EachBlock(node, context) {
34
34
 
35
35
  const new_body = /** @type {BlockStatement} */ (context.visit(node.body)).body;
36
36
 
37
- each.push(...(node.body.metadata.has_await ? [create_async_block(b.block(new_body))] : new_body));
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
+ );
38
42
 
39
43
  const for_loop = b.for(
40
44
  b.declaration('let', [
@@ -65,8 +69,15 @@ export function EachBlock(node, context) {
65
69
  block.body.push(for_loop);
66
70
  }
67
71
 
68
- if (node.metadata.expression.has_await) {
69
- state.template.push(create_async_block(block), block_close);
72
+ if (node.metadata.expression.is_async()) {
73
+ state.template.push(
74
+ create_async_block(
75
+ block,
76
+ node.metadata.expression.blockers(),
77
+ node.metadata.expression.has_await
78
+ ),
79
+ block_close
80
+ );
70
81
  } else {
71
82
  state.template.push(...block.body, block_close);
72
83
  }
@@ -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 { create_push } from './shared/utils.js';
5
6
 
6
7
  /**
7
8
  * @param {AST.HtmlTag} node
@@ -10,9 +11,6 @@ import * as b from '#compiler/builders';
10
11
  export function HtmlTag(node, context) {
11
12
  const expression = /** @type {Expression} */ (context.visit(node.expression));
12
13
  const call = b.call('$.html', expression);
13
- context.state.template.push(
14
- node.metadata.expression.has_await
15
- ? b.stmt(b.call('$$renderer.push', b.thunk(call, true)))
16
- : call
17
- );
14
+
15
+ context.state.template.push(create_push(call, node.metadata.expression, true));
18
16
  }
@@ -23,12 +23,20 @@ export function IfBlock(node, context) {
23
23
  /** @type {Statement} */
24
24
  let statement = b.if(test, consequent, alternate);
25
25
 
26
- if (
26
+ const is_async = node.metadata.expression.is_async();
27
+
28
+ const has_await =
27
29
  node.metadata.expression.has_await ||
30
+ // TODO get rid of this stuff
28
31
  node.consequent.metadata.has_await ||
29
- node.alternate?.metadata.has_await
30
- ) {
31
- statement = create_async_block(b.block([statement]));
32
+ node.alternate?.metadata.has_await;
33
+
34
+ if (is_async || has_await) {
35
+ statement = create_async_block(
36
+ b.block([statement]),
37
+ node.metadata.expression.blockers(),
38
+ !!has_await
39
+ );
32
40
  }
33
41
 
34
42
  context.state.template.push(statement, block_close);
@@ -1,16 +1,22 @@
1
1
  /** @import { BlockStatement } from 'estree' */
2
2
  /** @import { AST } from '#compiler' */
3
3
  /** @import { ComponentContext } from '../types.js' */
4
- import { empty_comment } from './shared/utils.js';
4
+ import { block_close, block_open, empty_comment } from './shared/utils.js';
5
5
 
6
6
  /**
7
7
  * @param {AST.KeyBlock} node
8
8
  * @param {ComponentContext} context
9
9
  */
10
10
  export function KeyBlock(node, context) {
11
+ const is_async = node.metadata.expression.is_async();
12
+
13
+ if (is_async) context.state.template.push(block_open);
14
+
11
15
  context.state.template.push(
12
16
  empty_comment,
13
17
  /** @type {BlockStatement} */ (context.visit(node.fragment)),
14
18
  empty_comment
15
19
  );
20
+
21
+ if (is_async) context.state.template.push(block_close);
16
22
  }
@@ -0,0 +1,25 @@
1
+ /** @import { Node, Program } from 'estree' */
2
+ /** @import { Context, ComponentServerTransformState } from '../types' */
3
+ import * as b from '#compiler/builders';
4
+ import { transform_body } from '../../shared/transform-async.js';
5
+
6
+ /**
7
+ * @param {Program} node
8
+ * @param {Context} context
9
+ */
10
+ export function Program(node, context) {
11
+ if (context.state.is_instance) {
12
+ const state = /** @type {ComponentServerTransformState} */ (context.state);
13
+
14
+ return {
15
+ ...node,
16
+ body: transform_body(
17
+ state.analysis.instance_body,
18
+ b.id('$$renderer.run'),
19
+ (node) => /** @type {Node} */ (context.visit(node))
20
+ )
21
+ };
22
+ }
23
+
24
+ context.next();
25
+ }
@@ -12,7 +12,8 @@ import {
12
12
  process_children,
13
13
  build_template,
14
14
  create_child_block,
15
- PromiseOptimiser
15
+ PromiseOptimiser,
16
+ create_async_block
16
17
  } from './shared/utils.js';
17
18
 
18
19
  /**
@@ -202,13 +203,19 @@ export function RegularElement(node, context) {
202
203
  state.template.push(b.stmt(b.call('$.pop_element')));
203
204
  }
204
205
 
205
- if (optimiser.expressions.length > 0) {
206
- context.state.template.push(
207
- create_child_block(
208
- b.block([optimiser.apply(), ...state.init, ...build_template(state.template)]),
209
- true
210
- )
206
+ if (optimiser.is_async()) {
207
+ let statement = create_child_block(
208
+ b.block([optimiser.apply(), ...state.init, ...build_template(state.template)]),
209
+ true
211
210
  );
211
+
212
+ const blockers = optimiser.blockers();
213
+
214
+ if (blockers.elements.length > 0) {
215
+ statement = create_async_block(b.block([statement]), blockers, false, false);
216
+ }
217
+
218
+ context.state.template.push(statement);
212
219
  } else {
213
220
  context.state.init.push(...state.init);
214
221
  context.state.template.push(...state.template);
@@ -3,32 +3,48 @@
3
3
  /** @import { ComponentContext } from '../types.js' */
4
4
  import { unwrap_optional } from '../../../../utils/ast.js';
5
5
  import * as b from '#compiler/builders';
6
- import { empty_comment } from './shared/utils.js';
6
+ import { create_async_block, empty_comment, PromiseOptimiser } from './shared/utils.js';
7
7
 
8
8
  /**
9
9
  * @param {AST.RenderTag} node
10
10
  * @param {ComponentContext} context
11
11
  */
12
12
  export function RenderTag(node, context) {
13
+ const optimiser = new PromiseOptimiser();
14
+
13
15
  const callee = unwrap_optional(node.expression).callee;
14
16
  const raw_args = unwrap_optional(node.expression).arguments;
15
17
 
16
- const snippet_function = /** @type {Expression} */ (context.visit(callee));
18
+ const snippet_function = optimiser.transform(
19
+ /** @type {Expression} */ (context.visit(callee)),
20
+ node.metadata.expression
21
+ );
17
22
 
18
- const snippet_args = raw_args.map((arg) => {
19
- return /** @type {Expression} */ (context.visit(arg));
23
+ const snippet_args = raw_args.map((arg, i) => {
24
+ return optimiser.transform(
25
+ /** @type {Expression} */ (context.visit(arg)),
26
+ node.metadata.arguments[i]
27
+ );
20
28
  });
21
29
 
22
- context.state.template.push(
23
- b.stmt(
24
- (node.expression.type === 'CallExpression' ? b.call : b.maybe_call)(
25
- snippet_function,
26
- b.id('$$renderer'),
27
- ...snippet_args
28
- )
30
+ let statement = b.stmt(
31
+ (node.expression.type === 'CallExpression' ? b.call : b.maybe_call)(
32
+ snippet_function,
33
+ b.id('$$renderer'),
34
+ ...snippet_args
29
35
  )
30
36
  );
31
37
 
38
+ if (optimiser.is_async()) {
39
+ statement = create_async_block(
40
+ b.block([optimiser.apply(), statement]),
41
+ optimiser.blockers(),
42
+ optimiser.has_await
43
+ );
44
+ }
45
+
46
+ context.state.template.push(statement);
47
+
32
48
  if (!context.state.skip_hydration_boundaries) {
33
49
  context.state.template.push(empty_comment);
34
50
  }
@@ -65,10 +65,13 @@ export function SlotElement(node, context) {
65
65
  fallback
66
66
  );
67
67
 
68
- const statement =
69
- optimiser.expressions.length > 0
70
- ? create_async_block(b.block([optimiser.apply(), b.stmt(slot)]))
71
- : b.stmt(slot);
68
+ const statement = optimiser.is_async()
69
+ ? create_async_block(
70
+ b.block([optimiser.apply(), b.stmt(slot)]),
71
+ optimiser.blockers(),
72
+ optimiser.has_await
73
+ )
74
+ : b.stmt(slot);
72
75
 
73
76
  context.state.template.push(block_open, statement, block_close);
74
77
  }
@@ -6,7 +6,12 @@ import { dev, locator } from '../../../../state.js';
6
6
  import * as b from '#compiler/builders';
7
7
  import { determine_namespace_for_children } from '../../utils.js';
8
8
  import { build_element_attributes } from './shared/element.js';
9
- import { build_template, create_child_block, PromiseOptimiser } from './shared/utils.js';
9
+ import {
10
+ build_template,
11
+ create_async_block,
12
+ create_child_block,
13
+ PromiseOptimiser
14
+ } from './shared/utils.js';
10
15
 
11
16
  /**
12
17
  * @param {AST.SvelteElement} node
@@ -39,11 +44,14 @@ export function SvelteElement(node, context) {
39
44
 
40
45
  const optimiser = new PromiseOptimiser();
41
46
 
47
+ /** @type {Statement[]} */
48
+ let statements = [];
49
+
42
50
  build_element_attributes(node, { ...context, state }, optimiser.transform);
43
51
 
44
52
  if (dev) {
45
53
  const location = /** @type {Location} */ (locator(node.start));
46
- context.state.template.push(
54
+ statements.push(
47
55
  b.stmt(
48
56
  b.call(
49
57
  '$.push_element',
@@ -74,9 +82,21 @@ export function SvelteElement(node, context) {
74
82
  statement = create_child_block(b.block([optimiser.apply(), statement]), true);
75
83
  }
76
84
 
77
- context.state.template.push(statement);
85
+ statements.push(statement);
78
86
 
79
87
  if (dev) {
80
- context.state.template.push(b.stmt(b.call('$.pop_element')));
88
+ statements.push(b.stmt(b.call('$.pop_element')));
81
89
  }
90
+
91
+ if (node.metadata.expression.is_async()) {
92
+ statements = [
93
+ create_async_block(
94
+ b.block(statements),
95
+ node.metadata.expression.blockers(),
96
+ node.metadata.expression.has_await
97
+ )
98
+ ];
99
+ }
100
+
101
+ context.state.template.push(...statements);
82
102
  }
@@ -5,8 +5,7 @@ import {
5
5
  empty_comment,
6
6
  build_attribute_value,
7
7
  create_async_block,
8
- PromiseOptimiser,
9
- build_template
8
+ PromiseOptimiser
10
9
  } from './utils.js';
11
10
  import * as b from '#compiler/builders';
12
11
  import { is_element_node } from '../../../../nodes.js';
@@ -323,8 +322,14 @@ export function build_inline_component(node, expression, context) {
323
322
  );
324
323
  }
325
324
 
326
- if (optimiser.expressions.length > 0) {
327
- statement = create_async_block(b.block([optimiser.apply(), statement]));
325
+ const is_async = optimiser.is_async();
326
+
327
+ if (is_async) {
328
+ statement = create_async_block(
329
+ b.block([optimiser.apply(), statement]),
330
+ optimiser.blockers(),
331
+ optimiser.has_await
332
+ );
328
333
  }
329
334
 
330
335
  if (dynamic && custom_css_props.length === 0) {
@@ -334,6 +339,7 @@ export function build_inline_component(node, expression, context) {
334
339
  context.state.template.push(statement);
335
340
 
336
341
  if (
342
+ !is_async &&
337
343
  !context.state.skip_hydration_boundaries &&
338
344
  custom_css_props.length === 0 &&
339
345
  optimiser.expressions.length === 0