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.
Files changed (25) 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 +9 -7
  23. package/src/internal/client/reactivity/deriveds.js +5 -2
  24. package/src/internal/client/reactivity/sources.js +17 -9
  25. package/src/version.js +1 -1
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "svelte",
3
3
  "description": "Cybernetically enhanced web apps",
4
4
  "license": "MIT",
5
- "version": "5.43.8",
5
+ "version": "5.43.9",
6
6
  "type": "module",
7
7
  "types": "./types/index.d.ts",
8
8
  "engines": {
@@ -10,8 +10,7 @@ export function create_fragment(transparent = false) {
10
10
  nodes: [],
11
11
  metadata: {
12
12
  transparent,
13
- dynamic: false,
14
- has_await: false
13
+ dynamic: false
15
14
  }
16
15
  };
17
16
  }
@@ -687,193 +687,7 @@ export function analyze_component(root, source, options) {
687
687
  }
688
688
  }
689
689
 
690
- /**
691
- * @param {ESTree.Node} expression
692
- * @param {Scope} scope
693
- * @param {Set<Binding>} touched
694
- * @param {Set<ESTree.Node>} seen
695
- */
696
- const touch = (expression, scope, touched, seen = new Set()) => {
697
- if (seen.has(expression)) return;
698
- seen.add(expression);
699
-
700
- walk(
701
- expression,
702
- { scope },
703
- {
704
- ImportDeclaration(node) {},
705
- Identifier(node, context) {
706
- const parent = /** @type {ESTree.Node} */ (context.path.at(-1));
707
- if (is_reference(node, parent)) {
708
- const binding = context.state.scope.get(node.name);
709
- if (binding) {
710
- touched.add(binding);
711
-
712
- for (const assignment of binding.assignments) {
713
- touch(assignment.value, assignment.scope, touched, seen);
714
- }
715
- }
716
- }
717
- }
718
- }
719
- );
720
- };
721
-
722
- /**
723
- * @param {ESTree.Node} node
724
- * @param {Set<ESTree.Node>} seen
725
- * @param {Set<Binding>} reads
726
- * @param {Set<Binding>} writes
727
- */
728
- const trace_references = (node, reads, writes, seen = new Set()) => {
729
- if (seen.has(node)) return;
730
- seen.add(node);
731
-
732
- /**
733
- * @param {ESTree.Pattern} node
734
- * @param {Scope} scope
735
- */
736
- function update(node, scope) {
737
- for (const pattern of unwrap_pattern(node)) {
738
- const node = object(pattern);
739
- if (!node) return;
740
-
741
- const binding = scope.get(node.name);
742
- if (!binding) return;
743
-
744
- writes.add(binding);
745
- }
746
- }
747
-
748
- walk(
749
- node,
750
- { scope: instance.scope },
751
- {
752
- _(node, context) {
753
- const scope = scopes.get(node);
754
- if (scope) {
755
- context.next({ scope });
756
- } else {
757
- context.next();
758
- }
759
- },
760
- AssignmentExpression(node, context) {
761
- update(node.left, context.state.scope);
762
- },
763
- UpdateExpression(node, context) {
764
- update(
765
- /** @type {ESTree.Identifier | ESTree.MemberExpression} */ (node.argument),
766
- context.state.scope
767
- );
768
- },
769
- CallExpression(node, context) {
770
- // for now, assume everything touched by the callee ends up mutating the object
771
- // TODO optimise this better
772
-
773
- // special case — no need to peek inside effects as they only run once async work has completed
774
- const rune = get_rune(node, context.state.scope);
775
- if (rune === '$effect') return;
776
-
777
- /** @type {Set<Binding>} */
778
- const touched = new Set();
779
- touch(node, context.state.scope, touched);
780
-
781
- for (const b of touched) {
782
- writes.add(b);
783
- }
784
- },
785
- // don't look inside functions until they are called
786
- ArrowFunctionExpression(_, context) {},
787
- FunctionDeclaration(_, context) {},
788
- FunctionExpression(_, context) {},
789
- Identifier(node, context) {
790
- const parent = /** @type {ESTree.Node} */ (context.path.at(-1));
791
- if (is_reference(node, parent)) {
792
- const binding = context.state.scope.get(node.name);
793
- if (binding) {
794
- reads.add(binding);
795
- }
796
- }
797
- }
798
- }
799
- );
800
- };
801
-
802
- let awaited = false;
803
-
804
- // TODO this should probably be attached to the scope?
805
- var promises = b.id('$$promises');
806
-
807
- /**
808
- * @param {ESTree.Identifier} id
809
- * @param {ESTree.Expression} blocker
810
- */
811
- function push_declaration(id, blocker) {
812
- analysis.instance_body.declarations.push(id);
813
-
814
- const binding = /** @type {Binding} */ (instance.scope.get(id.name));
815
- binding.blocker = blocker;
816
- }
817
-
818
- for (let node of instance.ast.body) {
819
- if (node.type === 'ImportDeclaration') {
820
- analysis.instance_body.hoisted.push(node);
821
- continue;
822
- }
823
-
824
- if (node.type === 'ExportDefaultDeclaration' || node.type === 'ExportAllDeclaration') {
825
- // these can't exist inside `<script>` but TypeScript doesn't know that
826
- continue;
827
- }
828
-
829
- if (node.type === 'ExportNamedDeclaration') {
830
- if (node.declaration) {
831
- node = node.declaration;
832
- } else {
833
- continue;
834
- }
835
- }
836
-
837
- const has_await = has_await_expression(node);
838
- awaited ||= has_await;
839
-
840
- if (awaited && node.type !== 'FunctionDeclaration') {
841
- /** @type {Set<Binding>} */
842
- const reads = new Set(); // TODO we're not actually using this yet
843
-
844
- /** @type {Set<Binding>} */
845
- const writes = new Set();
846
-
847
- trace_references(node, reads, writes);
848
-
849
- const blocker = b.member(promises, b.literal(analysis.instance_body.async.length), true);
850
-
851
- for (const binding of writes) {
852
- binding.blocker = blocker;
853
- }
854
-
855
- if (node.type === 'VariableDeclaration') {
856
- for (const declarator of node.declarations) {
857
- for (const id of extract_identifiers(declarator.id)) {
858
- push_declaration(id, blocker);
859
- }
860
-
861
- // one declarator per declaration, makes things simpler
862
- analysis.instance_body.async.push({
863
- node: declarator,
864
- has_await
865
- });
866
- }
867
- } else if (node.type === 'ClassDeclaration') {
868
- push_declaration(node.id, blocker);
869
- analysis.instance_body.async.push({ node, has_await });
870
- } else {
871
- analysis.instance_body.async.push({ node, has_await });
872
- }
873
- } else {
874
- analysis.instance_body.sync.push(node);
875
- }
876
- }
690
+ calculate_blockers(instance, scopes, analysis);
877
691
 
878
692
  if (analysis.runes) {
879
693
  const props_refs = module.scope.references.get('$$props');
@@ -1118,6 +932,274 @@ export function analyze_component(root, source, options) {
1118
932
  return analysis;
1119
933
  }
1120
934
 
935
+ /**
936
+ * Analyzes the instance's top level statements to calculate which bindings need to wait on which
937
+ * top level statements. This includes indirect blockers such as functions referencing async top level statements.
938
+ *
939
+ * @param {Js} instance
940
+ * @param {Map<AST.SvelteNode, Scope>} scopes
941
+ * @param {ComponentAnalysis} analysis
942
+ * @returns {void}
943
+ */
944
+ function calculate_blockers(instance, scopes, analysis) {
945
+ /**
946
+ * @param {ESTree.Node} expression
947
+ * @param {Scope} scope
948
+ * @param {Set<Binding>} touched
949
+ * @param {Set<ESTree.Node>} seen
950
+ */
951
+ const touch = (expression, scope, touched, seen = new Set()) => {
952
+ if (seen.has(expression)) return;
953
+ seen.add(expression);
954
+
955
+ walk(
956
+ expression,
957
+ { scope },
958
+ {
959
+ ImportDeclaration(node) {},
960
+ Identifier(node, context) {
961
+ const parent = /** @type {ESTree.Node} */ (context.path.at(-1));
962
+ if (is_reference(node, parent)) {
963
+ const binding = context.state.scope.get(node.name);
964
+ if (binding) {
965
+ touched.add(binding);
966
+
967
+ for (const assignment of binding.assignments) {
968
+ touch(assignment.value, assignment.scope, touched, seen);
969
+ }
970
+ }
971
+ }
972
+ }
973
+ }
974
+ );
975
+ };
976
+
977
+ /**
978
+ * @param {ESTree.Node} node
979
+ * @param {Set<ESTree.Node>} seen
980
+ * @param {Set<Binding>} reads
981
+ * @param {Set<Binding>} writes
982
+ */
983
+ const trace_references = (node, reads, writes, seen = new Set()) => {
984
+ if (seen.has(node)) return;
985
+ seen.add(node);
986
+
987
+ /**
988
+ * @param {ESTree.Pattern} node
989
+ * @param {Scope} scope
990
+ */
991
+ function update(node, scope) {
992
+ for (const pattern of unwrap_pattern(node)) {
993
+ const node = object(pattern);
994
+ if (!node) return;
995
+
996
+ const binding = scope.get(node.name);
997
+ if (!binding) return;
998
+
999
+ writes.add(binding);
1000
+ }
1001
+ }
1002
+
1003
+ walk(
1004
+ node,
1005
+ { scope: instance.scope },
1006
+ {
1007
+ _(node, context) {
1008
+ const scope = scopes.get(node);
1009
+ if (scope) {
1010
+ context.next({ scope });
1011
+ } else {
1012
+ context.next();
1013
+ }
1014
+ },
1015
+ AssignmentExpression(node, context) {
1016
+ update(node.left, context.state.scope);
1017
+ },
1018
+ UpdateExpression(node, context) {
1019
+ update(
1020
+ /** @type {ESTree.Identifier | ESTree.MemberExpression} */ (node.argument),
1021
+ context.state.scope
1022
+ );
1023
+ },
1024
+ CallExpression(node, context) {
1025
+ // for now, assume everything touched by the callee ends up mutating the object
1026
+ // TODO optimise this better
1027
+
1028
+ // special case — no need to peek inside effects as they only run once async work has completed
1029
+ const rune = get_rune(node, context.state.scope);
1030
+ if (rune === '$effect') return;
1031
+
1032
+ /** @type {Set<Binding>} */
1033
+ const touched = new Set();
1034
+ touch(node, context.state.scope, touched);
1035
+
1036
+ for (const b of touched) {
1037
+ writes.add(b);
1038
+ }
1039
+ },
1040
+ // don't look inside functions until they are called
1041
+ ArrowFunctionExpression(_, context) {},
1042
+ FunctionDeclaration(_, context) {},
1043
+ FunctionExpression(_, context) {},
1044
+ Identifier(node, context) {
1045
+ const parent = /** @type {ESTree.Node} */ (context.path.at(-1));
1046
+ if (is_reference(node, parent)) {
1047
+ const binding = context.state.scope.get(node.name);
1048
+ if (binding) {
1049
+ reads.add(binding);
1050
+ }
1051
+ }
1052
+ }
1053
+ }
1054
+ );
1055
+ };
1056
+
1057
+ let awaited = false;
1058
+
1059
+ // TODO this should probably be attached to the scope?
1060
+ const promises = b.id('$$promises');
1061
+
1062
+ /**
1063
+ * @param {ESTree.Identifier} id
1064
+ * @param {NonNullable<Binding['blocker']>} blocker
1065
+ */
1066
+ function push_declaration(id, blocker) {
1067
+ analysis.instance_body.declarations.push(id);
1068
+
1069
+ const binding = /** @type {Binding} */ (instance.scope.get(id.name));
1070
+ binding.blocker = blocker;
1071
+ }
1072
+
1073
+ /**
1074
+ * Analysis of blockers for functions is deferred until we know which statements are async/blockers
1075
+ * @type {Array<ESTree.FunctionDeclaration | ESTree.VariableDeclarator>}
1076
+ */
1077
+ const functions = [];
1078
+
1079
+ for (let node of instance.ast.body) {
1080
+ if (node.type === 'ImportDeclaration') {
1081
+ analysis.instance_body.hoisted.push(node);
1082
+ continue;
1083
+ }
1084
+
1085
+ if (node.type === 'ExportDefaultDeclaration' || node.type === 'ExportAllDeclaration') {
1086
+ // these can't exist inside `<script>` but TypeScript doesn't know that
1087
+ continue;
1088
+ }
1089
+
1090
+ if (node.type === 'ExportNamedDeclaration') {
1091
+ if (node.declaration) {
1092
+ node = node.declaration;
1093
+ } else {
1094
+ continue;
1095
+ }
1096
+ }
1097
+
1098
+ const has_await = has_await_expression(node);
1099
+ awaited ||= has_await;
1100
+
1101
+ if (node.type === 'FunctionDeclaration') {
1102
+ analysis.instance_body.sync.push(node);
1103
+ functions.push(node);
1104
+ } else if (node.type === 'VariableDeclaration') {
1105
+ for (const declarator of node.declarations) {
1106
+ if (
1107
+ declarator.init?.type === 'ArrowFunctionExpression' ||
1108
+ declarator.init?.type === 'FunctionExpression'
1109
+ ) {
1110
+ // One declarator per declaration, makes things simpler. The ternary ensures more accurate source maps in the common case
1111
+ analysis.instance_body.sync.push(
1112
+ node.declarations.length === 1 ? node : b.declaration(node.kind, [declarator])
1113
+ );
1114
+ functions.push(declarator);
1115
+ } else if (!awaited) {
1116
+ // One declarator per declaration, makes things simpler. The ternary ensures more accurate source maps in the common case
1117
+ analysis.instance_body.sync.push(
1118
+ node.declarations.length === 1 ? node : b.declaration(node.kind, [declarator])
1119
+ );
1120
+ } else {
1121
+ /** @type {Set<Binding>} */
1122
+ const reads = new Set(); // TODO we're not actually using this yet
1123
+
1124
+ /** @type {Set<Binding>} */
1125
+ const writes = new Set();
1126
+
1127
+ trace_references(declarator, reads, writes);
1128
+
1129
+ const blocker = /** @type {NonNullable<Binding['blocker']>} */ (
1130
+ b.member(promises, b.literal(analysis.instance_body.async.length), true)
1131
+ );
1132
+
1133
+ for (const binding of writes) {
1134
+ binding.blocker = blocker;
1135
+ }
1136
+
1137
+ for (const id of extract_identifiers(declarator.id)) {
1138
+ push_declaration(id, blocker);
1139
+ }
1140
+
1141
+ // one declarator per declaration, makes things simpler
1142
+ analysis.instance_body.async.push({
1143
+ node: declarator,
1144
+ has_await
1145
+ });
1146
+ }
1147
+ }
1148
+ } else if (awaited) {
1149
+ /** @type {Set<Binding>} */
1150
+ const reads = new Set(); // TODO we're not actually using this yet
1151
+
1152
+ /** @type {Set<Binding>} */
1153
+ const writes = new Set();
1154
+
1155
+ trace_references(node, reads, writes);
1156
+
1157
+ const blocker = /** @type {NonNullable<Binding['blocker']>} */ (
1158
+ b.member(promises, b.literal(analysis.instance_body.async.length), true)
1159
+ );
1160
+
1161
+ for (const binding of writes) {
1162
+ binding.blocker = blocker;
1163
+ }
1164
+
1165
+ if (node.type === 'ClassDeclaration') {
1166
+ push_declaration(node.id, blocker);
1167
+ analysis.instance_body.async.push({ node, has_await });
1168
+ } else {
1169
+ analysis.instance_body.async.push({ node, has_await });
1170
+ }
1171
+ } else {
1172
+ analysis.instance_body.sync.push(node);
1173
+ }
1174
+ }
1175
+
1176
+ for (const fn of functions) {
1177
+ /** @type {Set<Binding>} */
1178
+ const reads_writes = new Set();
1179
+ const body =
1180
+ fn.type === 'VariableDeclarator'
1181
+ ? /** @type {ESTree.FunctionExpression | ESTree.ArrowFunctionExpression} */ (fn.init).body
1182
+ : fn.body;
1183
+
1184
+ trace_references(body, reads_writes, reads_writes);
1185
+
1186
+ const max = [...reads_writes].reduce((max, binding) => {
1187
+ return binding.blocker ? Math.max(binding.blocker.property.value, max) : max;
1188
+ }, -1);
1189
+
1190
+ if (max === -1) continue;
1191
+
1192
+ const blocker = b.member(promises, b.literal(max), true);
1193
+ const binding = /** @type {Binding} */ (
1194
+ fn.type === 'FunctionDeclaration'
1195
+ ? instance.scope.get(fn.id.name)
1196
+ : instance.scope.get(/** @type {ESTree.Identifier} */ (fn.id).name)
1197
+ );
1198
+
1199
+ binding.blocker = /** @type {typeof binding['blocker']} */ (blocker);
1200
+ }
1201
+ }
1202
+
1121
1203
  /**
1122
1204
  * @param {Map<import('estree').LabeledStatement, ReactiveStatement>} unsorted_reactive_declarations
1123
1205
  */
@@ -26,10 +26,6 @@ export function AwaitExpression(node, context) {
26
26
  if (context.state.expression) {
27
27
  context.state.expression.has_await = true;
28
28
 
29
- if (context.state.fragment && context.path.some((node) => node.type === 'ConstTag')) {
30
- context.state.fragment.metadata.has_await = true;
31
- }
32
-
33
29
  suspend = true;
34
30
  }
35
31
 
@@ -56,22 +56,36 @@ export function AwaitBlock(node, context) {
56
56
  catch_block = b.arrow(args, b.block([...declarations, ...block.body]));
57
57
  }
58
58
 
59
- context.state.init.push(
60
- add_svelte_meta(
61
- b.call(
62
- '$.await',
63
- context.state.node,
64
- expression,
65
- node.pending
66
- ? b.arrow([b.id('$$anchor')], /** @type {BlockStatement} */ (context.visit(node.pending)))
67
- : b.null,
68
- then_block,
69
- catch_block
70
- ),
71
- node,
72
- 'await'
73
- )
59
+ const stmt = add_svelte_meta(
60
+ b.call(
61
+ '$.await',
62
+ context.state.node,
63
+ expression,
64
+ node.pending
65
+ ? b.arrow([b.id('$$anchor')], /** @type {BlockStatement} */ (context.visit(node.pending)))
66
+ : b.null,
67
+ then_block,
68
+ catch_block
69
+ ),
70
+ node,
71
+ 'await'
74
72
  );
73
+
74
+ if (node.metadata.expression.has_blockers()) {
75
+ context.state.init.push(
76
+ b.stmt(
77
+ b.call(
78
+ '$.async',
79
+ context.state.node,
80
+ node.metadata.expression.blockers(),
81
+ b.array([]),
82
+ b.arrow([context.state.node], b.block([stmt]))
83
+ )
84
+ )
85
+ );
86
+ } else {
87
+ context.state.init.push(stmt);
88
+ }
75
89
  }
76
90
 
77
91
  /**
@@ -24,15 +24,15 @@ export function ConstTag(node, context) {
24
24
  expression = b.call('$.tag', expression, b.literal(declaration.id.name));
25
25
  }
26
26
 
27
- context.state.consts.push(b.const(declaration.id, expression));
28
-
29
27
  context.state.transform[declaration.id.name] = { read: get_value };
30
28
 
31
- // we need to eagerly evaluate the expression in order to hit any
32
- // 'Cannot access x before initialization' errors
33
- if (dev) {
34
- context.state.consts.push(b.stmt(b.call('$.get', declaration.id)));
35
- }
29
+ add_const_declaration(
30
+ context.state,
31
+ declaration.id,
32
+ expression,
33
+ node.metadata.expression.has_await,
34
+ context.state.scope.get_bindings(declaration)
35
+ );
36
36
  } else {
37
37
  const identifiers = extract_identifiers(declaration.id);
38
38
  const tmp = b.id(context.state.scope.generate('computed_const'));
@@ -69,13 +69,13 @@ export function ConstTag(node, context) {
69
69
  expression = b.call('$.tag', expression, b.literal('[@const]'));
70
70
  }
71
71
 
72
- context.state.consts.push(b.const(tmp, expression));
73
-
74
- // we need to eagerly evaluate the expression in order to hit any
75
- // 'Cannot access x before initialization' errors
76
- if (dev) {
77
- context.state.consts.push(b.stmt(b.call('$.get', tmp)));
78
- }
72
+ add_const_declaration(
73
+ context.state,
74
+ tmp,
75
+ expression,
76
+ node.metadata.expression.has_await,
77
+ context.state.scope.get_bindings(declaration)
78
+ );
79
79
 
80
80
  for (const node of identifiers) {
81
81
  context.state.transform[node.name] = {
@@ -84,3 +84,39 @@ export function ConstTag(node, context) {
84
84
  }
85
85
  }
86
86
  }
87
+
88
+ /**
89
+ * @param {ComponentContext['state']} state
90
+ * @param {import('estree').Identifier} id
91
+ * @param {import('estree').Expression} expression
92
+ * @param {boolean} has_await
93
+ * @param {import('#compiler').Binding[]} bindings
94
+ */
95
+ function add_const_declaration(state, id, expression, has_await, bindings) {
96
+ // we need to eagerly evaluate the expression in order to hit any
97
+ // 'Cannot access x before initialization' errors
98
+ const after = dev ? [b.stmt(b.call('$.get', id))] : [];
99
+
100
+ if (has_await || state.async_consts) {
101
+ const run = (state.async_consts ??= {
102
+ id: b.id(state.scope.generate('promises')),
103
+ thunks: []
104
+ });
105
+
106
+ state.consts.push(b.let(id));
107
+
108
+ const assignment = b.assignment('=', id, expression);
109
+ const body = after.length === 0 ? assignment : b.block([b.stmt(assignment), ...after]);
110
+
111
+ run.thunks.push(b.thunk(body, has_await));
112
+
113
+ const blocker = b.member(run.id, b.literal(run.thunks.length - 1), true);
114
+
115
+ for (const binding of bindings) {
116
+ binding.blocker = blocker;
117
+ }
118
+ } else {
119
+ state.consts.push(b.const(id, expression));
120
+ state.consts.push(...after);
121
+ }
122
+ }
@@ -48,8 +48,6 @@ export function Fragment(node, context) {
48
48
  const is_single_child_not_needing_template =
49
49
  trimmed.length === 1 &&
50
50
  (trimmed[0].type === 'SvelteFragment' || trimmed[0].type === 'TitleElement');
51
- const has_await = context.state.init !== null && (node.metadata.has_await || false);
52
-
53
51
  const template_name = context.state.scope.root.unique('root'); // TODO infer name from parent
54
52
 
55
53
  /** @type {Statement[]} */
@@ -72,7 +70,8 @@ export function Fragment(node, context) {
72
70
  metadata: {
73
71
  namespace,
74
72
  bound_contenteditable: context.state.metadata.bound_contenteditable
75
- }
73
+ },
74
+ async_consts: undefined
76
75
  };
77
76
 
78
77
  for (const node of hoisted) {
@@ -153,8 +152,8 @@ export function Fragment(node, context) {
153
152
 
154
153
  body.push(...state.let_directives, ...state.consts);
155
154
 
156
- if (has_await) {
157
- body.push(b.if(b.call('$.aborted'), b.return()));
155
+ if (state.async_consts && state.async_consts.thunks.length > 0) {
156
+ body.push(b.var(state.async_consts.id, b.call('$.run', b.array(state.async_consts.thunks))));
158
157
  }
159
158
 
160
159
  if (is_text_first) {
@@ -177,13 +176,5 @@ export function Fragment(node, context) {
177
176
  body.push(close);
178
177
  }
179
178
 
180
- if (has_await) {
181
- return b.block([
182
- b.stmt(
183
- b.call('$.async_body', b.id('$$anchor'), b.arrow([b.id('$$anchor')], b.block(body), true))
184
- )
185
- ]);
186
- } else {
187
- return b.block(body);
188
- }
179
+ return b.block(body);
189
180
  }
@@ -14,8 +14,6 @@ export function SnippetBlock(node, context) {
14
14
  // TODO hoist where possible
15
15
  /** @type {(Identifier | AssignmentPattern)[]} */
16
16
  const args = [b.id('$$anchor')];
17
- const has_await = node.body.metadata.has_await || false;
18
-
19
17
  /** @type {BlockStatement} */
20
18
  let body;
21
19
 
@@ -78,12 +76,8 @@ export function SnippetBlock(node, context) {
78
76
 
79
77
  // in dev we use a FunctionExpression (not arrow function) so we can use `arguments`
80
78
  let snippet = dev
81
- ? b.call(
82
- '$.wrap_snippet',
83
- b.id(context.state.analysis.name),
84
- b.function(null, args, body, has_await)
85
- )
86
- : b.arrow(args, body, has_await);
79
+ ? b.call('$.wrap_snippet', b.id(context.state.analysis.name), b.function(null, args, body))
80
+ : b.arrow(args, body);
87
81
 
88
82
  const declaration = b.const(node.expression, snippet);
89
83