ripple 0.3.12 → 0.3.13

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 (190) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/package.json +8 -2
  3. package/src/compiler/phases/1-parse/index.js +73 -30
  4. package/src/compiler/phases/2-analyze/index.js +28 -58
  5. package/src/compiler/phases/3-transform/client/index.js +127 -164
  6. package/src/compiler/phases/3-transform/segments.js +4 -8
  7. package/src/compiler/phases/3-transform/server/index.js +210 -360
  8. package/src/compiler/types/import.d.ts +0 -12
  9. package/src/compiler/types/index.d.ts +12 -5
  10. package/src/compiler/types/parse.d.ts +2 -0
  11. package/src/compiler/utils.js +39 -44
  12. package/src/helpers.d.ts +2 -0
  13. package/src/runtime/index-client.js +15 -13
  14. package/src/runtime/index-server.js +18 -11
  15. package/src/runtime/internal/client/blocks.js +19 -23
  16. package/src/runtime/internal/client/constants.js +20 -9
  17. package/src/runtime/internal/client/index.js +14 -4
  18. package/src/runtime/internal/client/runtime.js +435 -173
  19. package/src/runtime/internal/client/try.js +334 -156
  20. package/src/runtime/internal/client/types.d.ts +26 -0
  21. package/src/runtime/internal/server/blocks.js +183 -0
  22. package/src/runtime/internal/server/constants.js +7 -0
  23. package/src/runtime/internal/server/index.js +780 -148
  24. package/src/runtime/internal/server/types.d.ts +35 -0
  25. package/src/server/index.js +1 -1
  26. package/src/utils/async.js +35 -0
  27. package/src/utils/builders.js +3 -1
  28. package/tests/client/__snapshots__/computed-properties.test.rsrx.snap +49 -0
  29. package/tests/client/__snapshots__/for.test.rsrx.snap +319 -0
  30. package/tests/client/__snapshots__/html.test.rsrx.snap +40 -0
  31. package/tests/client/_etc.test.rsrx +7 -0
  32. package/tests/client/array/{array.static.test.ripple → array.static.test.rsrx} +18 -20
  33. package/tests/client/async-suspend.test.rsrx +662 -0
  34. package/tests/client/basic/__snapshots__/basic.attributes.test.rsrx.snap +60 -0
  35. package/tests/client/basic/__snapshots__/basic.rendering.test.rsrx.snap +59 -0
  36. package/tests/client/basic/{basic.errors.test.ripple → basic.errors.test.rsrx} +2 -2
  37. package/tests/client/compiler/__snapshots__/compiler.assignments.test.rsrx.snap +12 -0
  38. package/tests/client/compiler/__snapshots__/compiler.typescript.test.rsrx.snap +46 -0
  39. package/tests/client/compiler/{compiler.try-in-function.test.ripple → compiler.try-in-function.test.rsrx} +8 -6
  40. package/tests/client/composite/__snapshots__/composite.render.test.rsrx.snap +37 -0
  41. package/tests/client/{function-overload.test.ripple → function-overload.test.rsrx} +1 -1
  42. package/tests/client/try.test.rsrx +1702 -0
  43. package/tests/hydration/build-components.js +5 -3
  44. package/tests/hydration/compiled/client/head.js +11 -11
  45. package/tests/hydration/compiled/client/mixed-control-flow.js +55 -70
  46. package/tests/hydration/compiled/client/nested-control-flow.js +72 -88
  47. package/tests/hydration/compiled/client/try.js +42 -54
  48. package/tests/hydration/compiled/server/basic.js +491 -369
  49. package/tests/hydration/compiled/server/composite.js +153 -128
  50. package/tests/hydration/compiled/server/events.js +166 -145
  51. package/tests/hydration/compiled/server/for.js +821 -677
  52. package/tests/hydration/compiled/server/head.js +200 -165
  53. package/tests/hydration/compiled/server/hmr.js +62 -54
  54. package/tests/hydration/compiled/server/html-in-template.js +64 -55
  55. package/tests/hydration/compiled/server/html.js +1477 -1360
  56. package/tests/hydration/compiled/server/if-children.js +448 -408
  57. package/tests/hydration/compiled/server/if.js +204 -171
  58. package/tests/hydration/compiled/server/mixed-control-flow.js +237 -195
  59. package/tests/hydration/compiled/server/nested-control-flow.js +533 -467
  60. package/tests/hydration/compiled/server/portal.js +94 -107
  61. package/tests/hydration/compiled/server/reactivity.js +87 -64
  62. package/tests/hydration/compiled/server/return.js +1424 -1174
  63. package/tests/hydration/compiled/server/switch.js +268 -238
  64. package/tests/hydration/compiled/server/try.js +98 -87
  65. package/tests/hydration/components/{mixed-control-flow.ripple → mixed-control-flow.rsrx} +2 -2
  66. package/tests/hydration/components/{try.ripple → try.rsrx} +4 -2
  67. package/tests/hydration/mixed-control-flow.test.js +14 -0
  68. package/tests/hydration/nested-control-flow.test.js +50 -48
  69. package/tests/hydration/try.test.js +25 -0
  70. package/tests/server/__snapshots__/compiler.test.ripple.snap +0 -32
  71. package/tests/server/__snapshots__/compiler.test.rsrx.snap +95 -0
  72. package/tests/server/{compiler.test.ripple → compiler.test.rsrx} +0 -17
  73. package/tests/server/{html-nesting-validation.test.ripple → html-nesting-validation.test.rsrx} +3 -3
  74. package/tests/server/streaming-ssr.test.rsrx +115 -0
  75. package/tests/server/try.test.rsrx +503 -0
  76. package/tests/utils/compiler-compat-config.test.js +3 -3
  77. package/tests/utils/vite-plugin-config.test.js +1 -1
  78. package/tests/utils/vite-plugin-hmr.test.js +5 -5
  79. package/tsconfig.json +2 -0
  80. package/types/index.d.ts +13 -23
  81. package/types/server.d.ts +43 -16
  82. package/tests/client/_etc.test.ripple +0 -5
  83. package/tests/client/async-suspend.test.ripple +0 -94
  84. package/tests/client/try.test.ripple +0 -196
  85. package/tests/server/streaming-ssr.test.ripple +0 -68
  86. package/tests/server/try.test.ripple +0 -82
  87. /package/tests/client/array/{array.copy-within.test.ripple → array.copy-within.test.rsrx} +0 -0
  88. /package/tests/client/array/{array.derived.test.ripple → array.derived.test.rsrx} +0 -0
  89. /package/tests/client/array/{array.iteration.test.ripple → array.iteration.test.rsrx} +0 -0
  90. /package/tests/client/array/{array.mutations.test.ripple → array.mutations.test.rsrx} +0 -0
  91. /package/tests/client/array/{array.to-methods.test.ripple → array.to-methods.test.rsrx} +0 -0
  92. /package/tests/client/basic/{basic.attributes.test.ripple → basic.attributes.test.rsrx} +0 -0
  93. /package/tests/client/basic/{basic.collections.test.ripple → basic.collections.test.rsrx} +0 -0
  94. /package/tests/client/basic/{basic.components.test.ripple → basic.components.test.rsrx} +0 -0
  95. /package/tests/client/basic/{basic.events.test.ripple → basic.events.test.rsrx} +0 -0
  96. /package/tests/client/basic/{basic.get-set.test.ripple → basic.get-set.test.rsrx} +0 -0
  97. /package/tests/client/basic/{basic.hmr.test.ripple → basic.hmr.test.rsrx} +0 -0
  98. /package/tests/client/basic/{basic.reactivity.test.ripple → basic.reactivity.test.rsrx} +0 -0
  99. /package/tests/client/basic/{basic.rendering.test.ripple → basic.rendering.test.rsrx} +0 -0
  100. /package/tests/client/basic/{basic.styling.test.ripple → basic.styling.test.rsrx} +0 -0
  101. /package/tests/client/basic/{basic.utilities.test.ripple → basic.utilities.test.rsrx} +0 -0
  102. /package/tests/client/{boundaries.test.ripple → boundaries.test.rsrx} +0 -0
  103. /package/tests/client/compiler/{compiler.assignments.test.ripple → compiler.assignments.test.rsrx} +0 -0
  104. /package/tests/client/compiler/{compiler.attributes.test.ripple → compiler.attributes.test.rsrx} +0 -0
  105. /package/tests/client/compiler/{compiler.basic.test.ripple → compiler.basic.test.rsrx} +0 -0
  106. /package/tests/client/compiler/{compiler.regex.test.ripple → compiler.regex.test.rsrx} +0 -0
  107. /package/tests/client/compiler/{compiler.tracked-access.test.ripple → compiler.tracked-access.test.rsrx} +0 -0
  108. /package/tests/client/compiler/{compiler.typescript.test.ripple → compiler.typescript.test.rsrx} +0 -0
  109. /package/tests/client/composite/{composite.dynamic-components.test.ripple → composite.dynamic-components.test.rsrx} +0 -0
  110. /package/tests/client/composite/{composite.generics.test.ripple → composite.generics.test.rsrx} +0 -0
  111. /package/tests/client/composite/{composite.props.test.ripple → composite.props.test.rsrx} +0 -0
  112. /package/tests/client/composite/{composite.reactivity.test.ripple → composite.reactivity.test.rsrx} +0 -0
  113. /package/tests/client/composite/{composite.render.test.ripple → composite.render.test.rsrx} +0 -0
  114. /package/tests/client/{computed-properties.test.ripple → computed-properties.test.rsrx} +0 -0
  115. /package/tests/client/{context.test.ripple → context.test.rsrx} +0 -0
  116. /package/tests/client/css/{global-additional-cases.test.ripple → global-additional-cases.test.rsrx} +0 -0
  117. /package/tests/client/css/{global-advanced-selectors.test.ripple → global-advanced-selectors.test.rsrx} +0 -0
  118. /package/tests/client/css/{global-at-rules.test.ripple → global-at-rules.test.rsrx} +0 -0
  119. /package/tests/client/css/{global-basic.test.ripple → global-basic.test.rsrx} +0 -0
  120. /package/tests/client/css/{global-classes-ids.test.ripple → global-classes-ids.test.rsrx} +0 -0
  121. /package/tests/client/css/{global-combinators.test.ripple → global-combinators.test.rsrx} +0 -0
  122. /package/tests/client/css/{global-complex-nesting.test.ripple → global-complex-nesting.test.rsrx} +0 -0
  123. /package/tests/client/css/{global-edge-cases.test.ripple → global-edge-cases.test.rsrx} +0 -0
  124. /package/tests/client/css/{global-keyframes.test.ripple → global-keyframes.test.rsrx} +0 -0
  125. /package/tests/client/css/{global-nested.test.ripple → global-nested.test.rsrx} +0 -0
  126. /package/tests/client/css/{global-pseudo.test.ripple → global-pseudo.test.rsrx} +0 -0
  127. /package/tests/client/css/{global-scoping.test.ripple → global-scoping.test.rsrx} +0 -0
  128. /package/tests/client/css/{style-identifier.test.ripple → style-identifier.test.rsrx} +0 -0
  129. /package/tests/client/{date.test.ripple → date.test.rsrx} +0 -0
  130. /package/tests/client/{dynamic-elements.test.ripple → dynamic-elements.test.rsrx} +0 -0
  131. /package/tests/client/{events.test.ripple → events.test.rsrx} +0 -0
  132. /package/tests/client/{for.test.ripple → for.test.rsrx} +0 -0
  133. /package/tests/client/{function-overload-import.ripple → function-overload-import.rsrx} +0 -0
  134. /package/tests/client/{head.test.ripple → head.test.rsrx} +0 -0
  135. /package/tests/client/{html.test.ripple → html.test.rsrx} +0 -0
  136. /package/tests/client/{input-value.test.ripple → input-value.test.rsrx} +0 -0
  137. /package/tests/client/{lazy-destructuring.test.ripple → lazy-destructuring.test.rsrx} +0 -0
  138. /package/tests/client/{map.test.ripple → map.test.rsrx} +0 -0
  139. /package/tests/client/{media-query.test.ripple → media-query.test.rsrx} +0 -0
  140. /package/tests/client/{object.test.ripple → object.test.rsrx} +0 -0
  141. /package/tests/client/{portal.test.ripple → portal.test.rsrx} +0 -0
  142. /package/tests/client/{ref.test.ripple → ref.test.rsrx} +0 -0
  143. /package/tests/client/{return.test.ripple → return.test.rsrx} +0 -0
  144. /package/tests/client/{set.test.ripple → set.test.rsrx} +0 -0
  145. /package/tests/client/{svg.test.ripple → svg.test.rsrx} +0 -0
  146. /package/tests/client/{switch.test.ripple → switch.test.rsrx} +0 -0
  147. /package/tests/client/{tsx.test.ripple → tsx.test.rsrx} +0 -0
  148. /package/tests/client/{typescript-generics.test.ripple → typescript-generics.test.rsrx} +0 -0
  149. /package/tests/client/url/{url.derived.test.ripple → url.derived.test.rsrx} +0 -0
  150. /package/tests/client/url/{url.parsing.test.ripple → url.parsing.test.rsrx} +0 -0
  151. /package/tests/client/url/{url.partial-removal.test.ripple → url.partial-removal.test.rsrx} +0 -0
  152. /package/tests/client/url/{url.reactivity.test.ripple → url.reactivity.test.rsrx} +0 -0
  153. /package/tests/client/url/{url.serialization.test.ripple → url.serialization.test.rsrx} +0 -0
  154. /package/tests/client/url-search-params/{url-search-params.derived.test.ripple → url-search-params.derived.test.rsrx} +0 -0
  155. /package/tests/client/url-search-params/{url-search-params.initialization.test.ripple → url-search-params.initialization.test.rsrx} +0 -0
  156. /package/tests/client/url-search-params/{url-search-params.iteration.test.ripple → url-search-params.iteration.test.rsrx} +0 -0
  157. /package/tests/client/url-search-params/{url-search-params.mutation.test.ripple → url-search-params.mutation.test.rsrx} +0 -0
  158. /package/tests/client/url-search-params/{url-search-params.retrieval.test.ripple → url-search-params.retrieval.test.rsrx} +0 -0
  159. /package/tests/client/url-search-params/{url-search-params.serialization.test.ripple → url-search-params.serialization.test.rsrx} +0 -0
  160. /package/tests/client/url-search-params/{url-search-params.tracked-url.test.ripple → url-search-params.tracked-url.test.rsrx} +0 -0
  161. /package/tests/hydration/components/{basic.ripple → basic.rsrx} +0 -0
  162. /package/tests/hydration/components/{composite.ripple → composite.rsrx} +0 -0
  163. /package/tests/hydration/components/{events.ripple → events.rsrx} +0 -0
  164. /package/tests/hydration/components/{for.ripple → for.rsrx} +0 -0
  165. /package/tests/hydration/components/{head.ripple → head.rsrx} +0 -0
  166. /package/tests/hydration/components/{hmr.ripple → hmr.rsrx} +0 -0
  167. /package/tests/hydration/components/{html-in-template.ripple → html-in-template.rsrx} +0 -0
  168. /package/tests/hydration/components/{html.ripple → html.rsrx} +0 -0
  169. /package/tests/hydration/components/{if-children.ripple → if-children.rsrx} +0 -0
  170. /package/tests/hydration/components/{if.ripple → if.rsrx} +0 -0
  171. /package/tests/hydration/components/{nested-control-flow.ripple → nested-control-flow.rsrx} +0 -0
  172. /package/tests/hydration/components/{portal.ripple → portal.rsrx} +0 -0
  173. /package/tests/hydration/components/{reactivity.ripple → reactivity.rsrx} +0 -0
  174. /package/tests/hydration/components/{return.ripple → return.rsrx} +0 -0
  175. /package/tests/hydration/components/{switch.ripple → switch.rsrx} +0 -0
  176. /package/tests/server/{await.test.ripple → await.test.rsrx} +0 -0
  177. /package/tests/server/{basic.attributes.test.ripple → basic.attributes.test.rsrx} +0 -0
  178. /package/tests/server/{basic.components.test.ripple → basic.components.test.rsrx} +0 -0
  179. /package/tests/server/{basic.test.ripple → basic.test.rsrx} +0 -0
  180. /package/tests/server/{composite.props.test.ripple → composite.props.test.rsrx} +0 -0
  181. /package/tests/server/{composite.test.ripple → composite.test.rsrx} +0 -0
  182. /package/tests/server/{context.test.ripple → context.test.rsrx} +0 -0
  183. /package/tests/server/{dynamic-elements.test.ripple → dynamic-elements.test.rsrx} +0 -0
  184. /package/tests/server/{for.test.ripple → for.test.rsrx} +0 -0
  185. /package/tests/server/{head.test.ripple → head.test.rsrx} +0 -0
  186. /package/tests/server/{if.test.ripple → if.test.rsrx} +0 -0
  187. /package/tests/server/{lazy-destructuring.test.ripple → lazy-destructuring.test.rsrx} +0 -0
  188. /package/tests/server/{return.test.ripple → return.test.rsrx} +0 -0
  189. /package/tests/server/{style-identifier.test.ripple → style-identifier.test.rsrx} +0 -0
  190. /package/tests/server/{switch.test.ripple → switch.test.rsrx} +0 -0
@@ -32,6 +32,7 @@ import {
32
32
  hash,
33
33
  flatten_switch_consequent,
34
34
  get_ripple_namespace_call_name,
35
+ strip_class_typescript_syntax,
35
36
  jsx_to_ripple_node,
36
37
  } from '../../../utils.js';
37
38
  import { escape } from '../../../../utils/escaping.js';
@@ -65,6 +66,22 @@ function is_template_or_control_flow(node) {
65
66
  );
66
67
  }
67
68
 
69
+ /**
70
+ * @param {AST.Node} node
71
+ * @returns {boolean}
72
+ */
73
+ function should_wrap_node_in_regular_block(node) {
74
+ return is_template_or_control_flow(node) && node.type !== 'TryStatement';
75
+ }
76
+
77
+ /**
78
+ * @param {AST.Node} node
79
+ * @returns {boolean}
80
+ */
81
+ function is_head_element(node) {
82
+ return node.type === 'Element' && node.id.type === 'Identifier' && node.id.name === 'head';
83
+ }
84
+
68
85
  /**
69
86
  * Builds a negated AND condition from return flag names: !__r_1 && !__r_2 && ...
70
87
  * @param {string[]} flags
@@ -79,23 +96,6 @@ function build_return_guard(flags) {
79
96
  return condition;
80
97
  }
81
98
 
82
- /**
83
- * @param {AST.ClassDeclaration | AST.ClassExpression} node
84
- * @param {TransformServerContext} context
85
- * @returns {void}
86
- */
87
- function strip_class_typescript_syntax(node, context) {
88
- delete node.typeParameters;
89
- delete node.superTypeParameters;
90
- delete node.implements;
91
-
92
- if (node.superClass?.type === 'TSInstantiationExpression') {
93
- node.superClass = /** @type {AST.Expression} */ (context.visit(node.superClass.expression));
94
- } else if (node.superClass && 'typeArguments' in node.superClass) {
95
- delete node.superClass.typeArguments;
96
- }
97
- }
98
-
99
99
  /**
100
100
  * Collects all unique return statements from the direct children of a body
101
101
  * @param {AST.Node[]} children
@@ -131,6 +131,8 @@ function collect_returns_from_children(children) {
131
131
  function transform_children(children, context) {
132
132
  const { visit, state } = context;
133
133
  const normalized = normalize_children(children, context);
134
+ const should_wrap_in_regular_block =
135
+ state.component !== undefined && !state.skip_regular_blocks && !state.in_regular_block;
134
136
 
135
137
  const all_returns = collect_returns_from_children(normalized);
136
138
  /** @type {Map<AST.ReturnStatement, { name: string, tracked: boolean }>} */
@@ -166,8 +168,20 @@ function transform_children(children, context) {
166
168
  }
167
169
  };
168
170
 
171
+ /**
172
+ * @param {AST.Statement[]} statements
173
+ * @returns {AST.Statement[]}
174
+ */
175
+ const wrap_regular_block = (statements) => {
176
+ if (!should_wrap_in_regular_block || statements.length === 0) {
177
+ return statements;
178
+ }
179
+
180
+ return [b.stmt(b.call('_$_.regular_block', b.arrow([], b.block(statements))))];
181
+ };
182
+
169
183
  /** @param {AST.Node} node */
170
- const process_node = (node) => {
184
+ const process_node = (node, local_state = state) => {
171
185
  if (node.type === 'BreakStatement') {
172
186
  state.init?.push(b.break);
173
187
  return;
@@ -184,16 +198,9 @@ function transform_children(children, context) {
184
198
  node.type === 'ReturnStatement' ||
185
199
  node.type === 'Component'
186
200
  ) {
187
- const metadata = { await: false };
188
201
  state.init?.push(
189
- /** @type {AST.Statement} */ (visit(node, { ...state, return_flags, metadata })),
202
+ /** @type {AST.Statement} */ (visit(node, { ...local_state, return_flags })),
190
203
  );
191
- if (metadata.await) {
192
- state.init?.push(b.if(b.call('_$_.aborted'), b.return(null)));
193
- if (state.metadata?.await === false) {
194
- state.metadata.await = true;
195
- }
196
- }
197
204
  if (node.type === 'ReturnStatement') {
198
205
  const info = return_flags.get(node);
199
206
  if (info && !accumulated_flags.includes(info.name)) {
@@ -201,7 +208,7 @@ function transform_children(children, context) {
201
208
  }
202
209
  }
203
210
  } else {
204
- visit(node, { ...state, return_flags, template_child: true });
211
+ visit(node, { ...local_state, return_flags, template_child: true });
205
212
  }
206
213
  };
207
214
 
@@ -224,7 +231,7 @@ function transform_children(children, context) {
224
231
  state.init = wrapped;
225
232
 
226
233
  for (const group_node of group) {
227
- process_node(group_node);
234
+ process_node(group_node, { ...state, init: wrapped, in_regular_block: true });
228
235
  }
229
236
 
230
237
  state.init = saved_init;
@@ -232,18 +239,42 @@ function transform_children(children, context) {
232
239
 
233
240
  const guard = build_return_guard(guard_flags);
234
241
  state.init?.push(
235
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(BLOCK_OPEN))),
236
- );
237
- state.init?.push(b.if(guard, b.block(wrapped)));
238
- state.init?.push(
239
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(BLOCK_CLOSE))),
242
+ ...wrap_regular_block([
243
+ b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_OPEN))),
244
+ b.if(guard, b.block(wrapped)),
245
+ b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_CLOSE))),
246
+ ]),
240
247
  );
241
248
  };
242
249
 
250
+ /**
251
+ * @param {AST.Node} node
252
+ * @returns {void}
253
+ */
254
+ const process_wrapped_template_or_control_flow = (node) => {
255
+ /** @type {AST.Statement[]} */
256
+ const wrapped = [];
257
+ const saved_init = state.init;
258
+ state.init = wrapped;
259
+ process_node(node, { ...state, init: wrapped, in_regular_block: true });
260
+ state.init = saved_init;
261
+
262
+ if (wrapped.length === 0) {
263
+ return;
264
+ }
265
+
266
+ state.init?.push(...wrap_regular_block(wrapped));
267
+ };
268
+
243
269
  for (let idx = 0; idx < normalized.length; idx++) {
244
270
  const node = normalized[idx];
245
271
 
246
- if (accumulated_flags.length > 0 && is_template_or_control_flow(node)) {
272
+ if (is_head_element(node)) {
273
+ flush_pending_group();
274
+ continue;
275
+ }
276
+
277
+ if (accumulated_flags.length > 0 && should_wrap_node_in_regular_block(node)) {
247
278
  if (pending_group.length === 0) {
248
279
  pending_guard_flags = [...accumulated_flags];
249
280
  }
@@ -257,22 +288,23 @@ function transform_children(children, context) {
257
288
  }
258
289
 
259
290
  flush_pending_group();
260
- process_node(node);
291
+
292
+ if (should_wrap_node_in_regular_block(node)) {
293
+ process_wrapped_template_or_control_flow(node);
294
+ } else {
295
+ process_node(node);
296
+ }
261
297
  push_return_flags(node.metadata?.has_return ? node.metadata.returns : undefined);
262
298
  }
263
299
 
264
300
  flush_pending_group();
265
301
 
266
302
  const head_elements = /** @type {AST.Element[]} */ (
267
- children.filter(
268
- (node) => node.type === 'Element' && node.id.type === 'Identifier' && node.id.name === 'head',
269
- )
303
+ children.filter((node) => is_head_element(node))
270
304
  );
271
305
 
272
306
  if (head_elements.length) {
273
- state.init?.push(
274
- b.stmt(b.assignment('=', b.member(b.id('__output'), b.id('target')), b.literal('head'))),
275
- );
307
+ state.init?.push(b.stmt(b.call(b.id('_$_.set_output_target'), b.literal('head'))));
276
308
  for (let i = 0; i < head_elements.length; i++) {
277
309
  const head_element = head_elements[i];
278
310
  // Generate a hash for this head element to match client-side hydration
@@ -281,17 +313,17 @@ function transform_children(children, context) {
281
313
  const hash_value = hash(hash_source);
282
314
 
283
315
  // Emit hydration marker comment with hash
284
- state.init?.push(
285
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(`<!--${hash_value}-->`))),
286
- );
316
+ state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(`<!--${hash_value}-->`))));
287
317
 
288
- transform_children(head_element.children, context);
318
+ transform_children(head_element.children, {
319
+ ...context,
320
+ state: { ...state, skip_regular_blocks: true },
321
+ });
289
322
 
290
323
  // No closing marker needed for head elements - the hash is sufficient
291
324
  }
292
- state.init?.push(
293
- b.stmt(b.assignment('=', b.member(b.id('__output'), b.id('target')), b.literal(null))),
294
- );
325
+
326
+ state.init?.push(b.stmt(b.call(b.id('_$_.set_output_target'), b.literal(null))));
295
327
  }
296
328
  }
297
329
 
@@ -345,7 +377,8 @@ const visitors = {
345
377
  },
346
378
 
347
379
  Component(node, context) {
348
- let props_param_output;
380
+ /** @type {AST.Pattern | null} */
381
+ let props_param_output = null;
349
382
 
350
383
  if (node.params.length > 0) {
351
384
  let props_param = node.params[0];
@@ -366,8 +399,6 @@ const visitors = {
366
399
  }
367
400
  }
368
401
 
369
- const metadata = { await: false };
370
-
371
402
  /** @type {AST.Statement[]} */
372
403
  const body_statements = [];
373
404
 
@@ -377,10 +408,7 @@ const visitors = {
377
408
  context.state.stylesheets.push(node.css);
378
409
 
379
410
  // Register CSS hash during rendering
380
- body_statements.push(
381
- hash,
382
- b.stmt(b.call(b.member(b.id('__output'), b.id('register_css')), hash_id)),
383
- );
411
+ body_statements.push(hash, b.stmt(b.call(b.id('_$_.output_register_css'), hash_id)));
384
412
 
385
413
  if (node.metadata.styleIdentifierPresent) {
386
414
  /** @type {AST.Property[]} */
@@ -407,7 +435,6 @@ const visitors = {
407
435
  state: {
408
436
  ...context.state,
409
437
  component: node,
410
- metadata,
411
438
  applyParentCssScope:
412
439
  node.id?.name === 'render_children' ? context.state.applyParentCssScope : undefined,
413
440
  },
@@ -417,80 +444,25 @@ const visitors = {
417
444
 
418
445
  let component_fn = b.function(
419
446
  node.id,
420
- node.params.length > 0
421
- ? [b.id('__output'), /** @type {AST.Pattern} */ (props_param_output)]
422
- : [b.id('__output')],
423
- b.block([
424
- ...(metadata.await
425
- ? [b.return(b.call('_$_.async', b.thunk(b.block(body_statements), true)))]
426
- : body_statements),
427
- ]),
447
+ props_param_output ? [props_param_output] : [],
448
+ b.block(body_statements),
428
449
  );
429
450
 
430
- // Mark function as async if needed
431
- if (metadata.await) {
432
- component_fn = b.async(component_fn);
433
- }
434
-
435
451
  // Anonymous components return a FunctionExpression
436
452
  if (!node.id) {
437
- // For async anonymous components, we need to set .async on the function
438
- if (metadata.await) {
439
- // Use IIFE pattern: (fn => (fn.async = true, fn))(function() { ... })
440
- return b.call(
441
- b.arrow(
442
- [b.id('fn')],
443
- b.sequence([
444
- b.assignment('=', b.member(b.id('fn'), b.id('async')), b.true),
445
- b.id('fn'),
446
- ]),
447
- ),
448
- component_fn,
449
- );
450
- }
451
453
  return component_fn;
452
454
  }
453
455
 
454
456
  // Named components return a FunctionDeclaration
455
- const declaration = b.function_declaration(
456
- node.id,
457
- component_fn.params,
458
- component_fn.body,
459
- component_fn.async,
460
- );
461
-
462
- if (metadata.await) {
463
- const parent = context.path.at(-1);
464
- /** @type {AST.RippleProgram['body'] | null} */
465
- let body = null;
466
- /** @type {AST.Component | AST.ExportNamedDeclaration} */
467
- let target_node = node;
468
- if (parent?.type === 'Program' || parent?.type === 'BlockStatement') {
469
- body = parent.body;
470
- } else if (parent?.type === 'ExportNamedDeclaration') {
471
- const grandparent = context.path.at(-2);
472
- if (grandparent?.type === 'Program' || grandparent?.type === 'BlockStatement') {
473
- body = grandparent.body;
474
- target_node = parent;
475
- }
476
- }
477
- if (body !== null) {
478
- const index = body.indexOf(target_node);
479
- if (index >= 0) {
480
- body.splice(
481
- index + 1,
482
- 0,
483
- b.stmt(b.assignment('=', b.member(node.id, b.id('async')), b.true)),
484
- );
485
- }
486
- }
487
- }
457
+ const declaration = b.function_declaration(node.id, component_fn.params, component_fn.body);
488
458
 
489
459
  return declaration;
490
460
  },
491
461
 
492
462
  CallExpression(node, context) {
493
- if (!context.state.to_ts) {
463
+ const { state } = context;
464
+
465
+ if (!state.to_ts) {
494
466
  delete node.typeArguments;
495
467
  }
496
468
 
@@ -512,7 +484,7 @@ const visitors = {
512
484
 
513
485
  const track_call_name = is_ripple_track_call(callee, context);
514
486
  if (track_call_name) {
515
- const track_method_name = 'track';
487
+ const track_method_name = track_call_name === 'trackAsync' ? 'track_async' : 'track';
516
488
 
517
489
  return {
518
490
  ...node,
@@ -885,7 +857,7 @@ const visitors = {
885
857
  state.init?.push(
886
858
  b.stmt(
887
859
  b.call(
888
- b.member(b.id('__output'), b.id('push')),
860
+ b.id('_$_.output_push'),
889
861
  dynamic_name
890
862
  ? b.template([b.quasi('<', false), b.quasi('', false)], [tag_name])
891
863
  : b.literal('<' + /** @type {AST.Literal} */ (tag_name).value),
@@ -918,9 +890,7 @@ const visitors = {
918
890
  : `="${value === true ? '' : escape_html(value, true)}"`
919
891
  }`;
920
892
 
921
- state.init?.push(
922
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(attr_str))),
923
- );
893
+ state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(attr_str))));
924
894
  }
925
895
  };
926
896
 
@@ -948,7 +918,7 @@ const visitors = {
948
918
  if (is_event_attribute(name)) {
949
919
  continue;
950
920
  }
951
- const metadata = { tracking: false, await: false };
921
+ const metadata = { tracking: false };
952
922
  const expression = /** @type {AST.Expression} */ (
953
923
  visit(attr.value, { ...state, metadata })
954
924
  );
@@ -956,7 +926,7 @@ const visitors = {
956
926
  state.init?.push(
957
927
  b.stmt(
958
928
  b.call(
959
- b.member(b.id('__output'), b.id('push')),
929
+ b.id('_$_.output_push'),
960
930
  b.call(
961
931
  '_$_.attr',
962
932
  b.literal(name),
@@ -985,7 +955,7 @@ const visitors = {
985
955
 
986
956
  handle_static_attr(class_attribute.name.name, value);
987
957
  } else {
988
- const metadata = { tracking: false, await: false };
958
+ const metadata = { tracking: false };
989
959
  let expression = /** @type {AST.Expression} */ (
990
960
  visit(attr_value, { ...state, metadata })
991
961
  );
@@ -997,10 +967,7 @@ const visitors = {
997
967
 
998
968
  state.init?.push(
999
969
  b.stmt(
1000
- b.call(
1001
- b.member(b.id('__output'), b.id('push')),
1002
- b.call('_$_.attr', b.literal('class'), expression),
1003
- ),
970
+ b.call(b.id('_$_.output_push'), b.call('_$_.attr', b.literal('class'), expression)),
1004
971
  ),
1005
972
  );
1006
973
  }
@@ -1012,7 +979,7 @@ const visitors = {
1012
979
  state.init?.push(
1013
980
  b.stmt(
1014
981
  b.call(
1015
- b.member(b.id('__output'), b.id('push')),
982
+ b.id('_$_.output_push'),
1016
983
  b.call(
1017
984
  '_$_.spread_attrs',
1018
985
  b.object(spread_attributes),
@@ -1024,12 +991,7 @@ const visitors = {
1024
991
  }
1025
992
 
1026
993
  state.init?.push(
1027
- b.stmt(
1028
- b.call(
1029
- b.member(b.id('__output'), b.id('push')),
1030
- b.literal(use_self_closing_syntax ? ' />' : '>'),
1031
- ),
1032
- ),
994
+ b.stmt(b.call(b.id('_$_.output_push'), b.literal(use_self_closing_syntax ? ' />' : '>'))),
1033
995
  );
1034
996
 
1035
997
  // In dev mode, emit push_element for runtime nesting validation
@@ -1079,7 +1041,7 @@ const visitors = {
1079
1041
  state.init?.push(
1080
1042
  b.stmt(
1081
1043
  b.call(
1082
- b.member(b.id('__output'), b.id('push')),
1044
+ b.id('_$_.output_push'),
1083
1045
  dynamic_name
1084
1046
  ? b.template([b.quasi('</', false), b.quasi('>', false)], [tag_name])
1085
1047
  : b.literal('</' + /** @type {AST.Literal} */ (tag_name).value + '>'),
@@ -1104,7 +1066,7 @@ const visitors = {
1104
1066
  for (const attr of node.attributes) {
1105
1067
  if (attr.type === 'Attribute') {
1106
1068
  if (attr.name.type === 'Identifier') {
1107
- const metadata = { tracking: false, await: false };
1069
+ const metadata = { tracking: false };
1108
1070
  let property =
1109
1071
  attr.value === null
1110
1072
  ? b.literal(true)
@@ -1177,10 +1139,9 @@ const visitors = {
1177
1139
  props.push(children_prop);
1178
1140
  }
1179
1141
 
1180
- // For SSR, determine if we should await based on component metadata
1181
- const args = [b.id('__output'), b.object(props)];
1142
+ const args = [b.object(props)];
1182
1143
 
1183
- // Check if this is a locally defined component and if it's async
1144
+ // Check if this is a locally defined component
1184
1145
  const component_name = node.id.type === 'Identifier' ? node.id.name : null;
1185
1146
  const local_metadata = component_name
1186
1147
  ? state.component_metadata.find((m) => m.id === component_name)
@@ -1188,8 +1149,7 @@ const visitors = {
1188
1149
  const comp_id = b.id('comp');
1189
1150
  const args_id = b.id('args');
1190
1151
  const comp_call = b.call(comp_id, b.spread(args_id));
1191
- const comp_call_regular = b.stmt(comp_call);
1192
- const comp_call_await = b.stmt(b.await(comp_call));
1152
+ const comp_call_statement = b.stmt(comp_call);
1193
1153
 
1194
1154
  /** @type {AST.Statement[]} */
1195
1155
  const init = [];
@@ -1201,28 +1161,10 @@ const visitors = {
1201
1161
  ];
1202
1162
 
1203
1163
  if (local_metadata) {
1204
- if (local_metadata?.async) {
1205
- statements.push(comp_call_await);
1206
-
1207
- if (state.metadata?.await === false) {
1208
- state.metadata.await = true;
1209
- }
1210
- } else {
1211
- statements.push(comp_call_regular);
1212
- }
1164
+ statements.push(comp_call_statement);
1213
1165
  } else if (!is_element_dynamic(node)) {
1214
- // it's imported element, so it could be async
1215
- statements.push(
1216
- b.if(
1217
- b.member(comp_id, b.id('async'), false, true),
1218
- b.block([comp_call_await]),
1219
- b.if(comp_id, b.block([comp_call_regular])),
1220
- ),
1221
- );
1222
-
1223
- if (state.metadata?.await === false) {
1224
- state.metadata.await = true;
1225
- }
1166
+ // imported or children
1167
+ statements.push(b.if(comp_id, b.block([comp_call_statement])));
1226
1168
  } else {
1227
1169
  // if it's a dynamic element, build the element output
1228
1170
  // and store the results in the `init` array
@@ -1235,33 +1177,18 @@ const visitors = {
1235
1177
  }),
1236
1178
  );
1237
1179
 
1238
- statements.push(
1239
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(BLOCK_OPEN))),
1240
- );
1180
+ statements.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_OPEN))));
1241
1181
 
1242
1182
  statements.push(
1243
1183
  b.if(
1244
1184
  b.binary('===', b.unary('typeof', comp_id), b.literal('function')),
1245
- b.block([
1246
- b.if(
1247
- b.member(comp_id, b.id('async')),
1248
- b.block([comp_call_await]),
1249
- b.block([comp_call_regular]),
1250
- ),
1251
- ]),
1185
+ b.block([comp_call_statement]),
1252
1186
  // make sure that falsy values for dynamic element or component don't get rendered
1253
1187
  b.if(comp_id, b.block(init)),
1254
1188
  ),
1255
1189
  );
1256
1190
 
1257
- statements.push(
1258
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(BLOCK_CLOSE))),
1259
- );
1260
-
1261
- // Mark parent component as async since this child component could potentially be async
1262
- if (state.metadata?.await === false) {
1263
- state.metadata.await = true;
1264
- }
1191
+ statements.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_CLOSE))));
1265
1192
  }
1266
1193
 
1267
1194
  state.init?.push(b.block(statements));
@@ -1299,17 +1226,13 @@ const visitors = {
1299
1226
  );
1300
1227
  }
1301
1228
 
1302
- context.state.init?.push(
1303
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(BLOCK_OPEN))),
1304
- );
1229
+ context.state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_OPEN))));
1305
1230
 
1306
1231
  context.state.init?.push(
1307
1232
  b.switch(/** @type {AST.Expression} */ (context.visit(node.discriminant)), cases),
1308
1233
  );
1309
1234
 
1310
- context.state.init?.push(
1311
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(BLOCK_CLOSE))),
1312
- );
1235
+ context.state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_CLOSE))));
1313
1236
  },
1314
1237
 
1315
1238
  ForOfStatement(node, context) {
@@ -1319,9 +1242,7 @@ const visitors = {
1319
1242
  }
1320
1243
  const body_scope = context.state.scopes.get(node.body);
1321
1244
 
1322
- context.state.init?.push(
1323
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(BLOCK_OPEN))),
1324
- );
1245
+ context.state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_OPEN))));
1325
1246
 
1326
1247
  const body = transform_body(/** @type {AST.BlockStatement} */ (node.body).body, {
1327
1248
  ...context,
@@ -1342,9 +1263,7 @@ const visitors = {
1342
1263
  ),
1343
1264
  );
1344
1265
 
1345
- context.state.init?.push(
1346
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(BLOCK_CLOSE))),
1347
- );
1266
+ context.state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_CLOSE))));
1348
1267
  },
1349
1268
 
1350
1269
  IfStatement(node, context) {
@@ -1368,9 +1287,7 @@ const visitors = {
1368
1287
  }),
1369
1288
  );
1370
1289
 
1371
- context.state.init?.push(
1372
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(BLOCK_OPEN))),
1373
- );
1290
+ context.state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_OPEN))));
1374
1291
 
1375
1292
  /** @type {AST.BlockStatement | AST.IfStatement | null} */
1376
1293
  let alternate = null;
@@ -1393,9 +1310,7 @@ const visitors = {
1393
1310
  b.if(/** @type {AST.Expression} */ (context.visit(node.test)), consequent, alternate),
1394
1311
  );
1395
1312
 
1396
- context.state.init?.push(
1397
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(BLOCK_CLOSE))),
1398
- );
1313
+ context.state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_CLOSE))));
1399
1314
  },
1400
1315
 
1401
1316
  ReturnStatement(node, context) {
@@ -1489,180 +1404,120 @@ const visitors = {
1489
1404
  return context.next();
1490
1405
  }
1491
1406
 
1492
- // If there's a pending block, this is an async operation
1493
1407
  const has_pending = node.pending !== null;
1494
- if (has_pending && context.state.metadata?.await === false) {
1495
- context.state.metadata.await = true;
1496
- }
1408
+ const has_catch = node.handler !== null;
1497
1409
 
1498
- const metadata = { await: false };
1499
1410
  const body = transform_body(node.block.body, {
1500
1411
  ...context,
1501
- state: { ...context.state, metadata },
1412
+ state: {
1413
+ ...context.state,
1414
+ scope: /** @type {ScopeInterface} */ (context.state.scopes.get(node.block)),
1415
+ },
1502
1416
  });
1503
1417
 
1504
- // Check if the try block itself contains async operations
1505
- const is_async = metadata.await || has_pending;
1506
-
1507
- if (is_async) {
1508
- if (context.state.metadata?.await === false) {
1509
- context.state.metadata.await = true;
1510
- }
1418
+ // Wrap try_fn body with hydration markers when pending or catch is present
1419
+ const try_fn = b.arrow(
1420
+ [],
1421
+ b.block(
1422
+ has_pending || has_catch
1423
+ ? [
1424
+ b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_OPEN))),
1425
+ ...body,
1426
+ b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_CLOSE))),
1427
+ ]
1428
+ : body,
1429
+ ),
1430
+ );
1511
1431
 
1512
- const pending_position_name = node.pending
1513
- ? context.state.scope.generate('__pending_pos')
1514
- : null;
1432
+ /** @type {AST.Expression} */
1433
+ let catch_fn = b.literal(null);
1515
1434
 
1516
- // Render pending block first, saving position so we can remove it after async resolves
1517
- if (node.pending) {
1518
- context.state.init?.push(
1519
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(BLOCK_OPEN))),
1520
- );
1521
- const pending_body = transform_body(node.pending.body, {
1522
- ...context,
1523
- state: {
1524
- ...context.state,
1525
- scope: /** @type {ScopeInterface} */ (context.state.scopes.get(node.pending)),
1526
- },
1527
- });
1528
- context.state.init?.push(
1529
- b.var(
1530
- b.id(/** @type {string} */ (pending_position_name)),
1531
- b.member(b.member(b.id('__output'), b.id('body')), b.id('length')),
1532
- ),
1533
- );
1534
- context.state.init?.push(...pending_body);
1435
+ const handler = node.handler;
1436
+ if (handler) {
1437
+ if (handler.param) {
1438
+ delete handler.param.typeAnnotation;
1535
1439
  }
1536
1440
 
1537
- // For SSR with pending block: render the resolved content wrapped in async
1538
- // In a streaming SSR implementation, we'd render pending first, then stream resolved
1539
- const handler = node.handler;
1540
- /** @type {AST.Statement[]} */
1541
- let try_statements = body;
1542
- if (handler != null) {
1543
- try_statements = [
1544
- b.try(
1545
- b.block(body),
1546
- b.catch_clause(
1547
- handler.param || b.id('error'),
1548
- b.block(
1549
- transform_body(handler.body.body, {
1550
- ...context,
1551
- state: {
1552
- ...context.state,
1553
- scope: /** @type {ScopeInterface} */ (context.state.scopes.get(handler.body)),
1554
- },
1555
- }),
1556
- ),
1557
- ),
1558
- ),
1559
- ];
1560
- }
1441
+ /** @type {AST.Statement | null} */
1442
+ let reset = null;
1443
+ if (handler.resetParam) {
1444
+ delete handler.resetParam.typeAnnotation;
1561
1445
 
1562
- // Remove pending content before rendering resolved/catch content
1563
- if (node.pending) {
1564
- try_statements = [
1565
- b.stmt(
1566
- b.assignment(
1567
- '=',
1568
- b.member(b.id('__output'), b.id('body')),
1569
- b.call(
1570
- b.member(b.member(b.id('__output'), b.id('body')), b.id('slice')),
1571
- b.literal(0),
1572
- b.id(/** @type {string} */ (pending_position_name)),
1573
- ),
1574
- ),
1575
- ),
1576
- ...try_statements,
1577
- ];
1446
+ reset = b.const(
1447
+ handler.resetParam.type === 'AssignmentPattern'
1448
+ ? /** @type {AST.Identifier} */ (handler.resetParam.left).name
1449
+ : /** @type {AST.Identifier} */ (handler.resetParam).name,
1450
+ b.id('_$_.noop'),
1451
+ );
1578
1452
  }
1579
1453
 
1580
- context.state.init?.push(
1581
- b.stmt(b.await(b.call('_$_.async', b.thunk(b.block(try_statements), true)))),
1454
+ catch_fn = b.arrow(
1455
+ [handler.param || b.id('error')],
1456
+ b.block([
1457
+ b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_OPEN))),
1458
+ ...(reset ? [reset] : []),
1459
+ ...transform_body(handler.body.body, {
1460
+ ...context,
1461
+ state: {
1462
+ ...context.state,
1463
+ scope: /** @type {ScopeInterface} */ (context.state.scopes.get(handler.body)),
1464
+ },
1465
+ }),
1466
+ b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_CLOSE))),
1467
+ ]),
1582
1468
  );
1469
+ }
1583
1470
 
1584
- if (node.pending) {
1585
- context.state.init?.push(
1586
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(BLOCK_CLOSE))),
1587
- );
1588
- }
1589
- } else {
1590
- // No async, just regular try/catch
1591
- if (node.handler != null) {
1592
- const handler_body = transform_body(node.handler.body.body, {
1471
+ const pending_body = node.pending
1472
+ ? transform_body(node.pending.body, {
1593
1473
  ...context,
1594
1474
  state: {
1595
1475
  ...context.state,
1596
- scope: /** @type {ScopeInterface} */ (context.state.scopes.get(node.handler.body)),
1476
+ scope: /** @type {ScopeInterface} */ (context.state.scopes.get(node.pending)),
1597
1477
  },
1598
- });
1599
-
1600
- context.state.init?.push(
1601
- b.try(
1602
- b.block(body),
1603
- b.catch_clause(node.handler.param || b.id('error'), b.block(handler_body)),
1604
- ),
1605
- );
1606
- } else {
1607
- context.state.init?.push(...body);
1608
- }
1609
- }
1610
- },
1611
-
1612
- AwaitExpression(node, context) {
1613
- const { state } = context;
1614
-
1615
- if (state.to_ts) {
1616
- return context.next();
1617
- }
1618
-
1619
- if (state.metadata?.await === false) {
1620
- state.metadata.await = true;
1621
- }
1622
-
1623
- return b.await(/** @type {AST.AwaitExpression} */ (context.visit(node.argument)));
1624
- },
1478
+ })
1479
+ : null;
1480
+
1481
+ // Wrap pending_fn body with hydration markers
1482
+ const pending_fn =
1483
+ pending_body !== null
1484
+ ? b.arrow(
1485
+ [],
1486
+ b.block([
1487
+ b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_OPEN))),
1488
+ ...pending_body,
1489
+ b.stmt(b.call(b.id('_$_.output_push'), b.literal(BLOCK_CLOSE))),
1490
+ ]),
1491
+ )
1492
+ : b.literal(null);
1625
1493
 
1626
- MemberExpression(node, context) {
1627
- return context.next();
1494
+ context.state.init?.push(b.stmt(b.call('_$_.try_block', try_fn, catch_fn, pending_fn)));
1628
1495
  },
1629
1496
 
1630
1497
  RippleExpression(node, { visit, state }) {
1631
- const metadata = { await: false };
1632
- let expression = /** @type {AST.Expression} */ (visit(node.expression, { ...state, metadata }));
1498
+ let expression = /** @type {AST.Expression} */ (visit(node.expression, state));
1633
1499
  const is_children_expression = is_children_template_expression(node.expression, state.scope);
1634
1500
 
1635
1501
  if (expression.type === 'Literal') {
1636
1502
  state.init?.push(
1637
- b.stmt(
1638
- b.call(b.member(b.id('__output'), b.id('push')), b.literal(escape(expression.value))),
1639
- ),
1503
+ b.stmt(b.call(b.id('_$_.output_push'), b.literal(escape(expression.value)))),
1640
1504
  );
1641
1505
  } else if (is_children_expression) {
1642
- state.init?.push(b.stmt(b.call('_$_.render_expression', b.id('__output'), expression)));
1506
+ state.init?.push(b.stmt(b.call('_$_.render_expression', expression)));
1643
1507
  } else {
1644
- state.init?.push(
1645
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.call('_$_.escape', expression))),
1646
- );
1508
+ state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.call('_$_.escape', expression))));
1647
1509
  }
1648
1510
  },
1649
1511
 
1650
- Text(node, context) {
1651
- const metadata = { await: false };
1652
- let expression = /** @type {AST.Expression} */ (
1653
- context.visit(node.expression, { ...context.state, metadata })
1654
- );
1512
+ Text(node, { visit, state }) {
1513
+ let expression = /** @type {AST.Expression} */ (visit(node.expression, state));
1655
1514
 
1656
1515
  if (expression.type === 'Literal') {
1657
- context.state.init?.push(
1658
- b.stmt(
1659
- b.call(b.member(b.id('__output'), b.id('push')), b.literal(escape(expression.value))),
1660
- ),
1516
+ state.init?.push(
1517
+ b.stmt(b.call(b.id('_$_.output_push'), b.literal(escape(expression.value)))),
1661
1518
  );
1662
1519
  } else {
1663
- context.state.init?.push(
1664
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.call('_$_.escape', expression))),
1665
- );
1520
+ state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.call('_$_.escape', expression))));
1666
1521
  }
1667
1522
  },
1668
1523
 
@@ -1692,31 +1547,24 @@ const visitors = {
1692
1547
  }
1693
1548
  } else {
1694
1549
  // Expression context: return ripple_element(render_fn)
1695
- const render_fn = b.function(b.id('render_children'), [b.id('__output')], b.block(init));
1550
+ const render_fn = b.function(b.id('render_children'), [], b.block(init));
1696
1551
  return b.call('_$_.ripple_element', render_fn);
1697
1552
  }
1698
1553
  },
1699
1554
 
1700
1555
  Html(node, { visit, state }) {
1701
- const metadata = { await: false };
1702
- const expression = /** @type {AST.Expression} */ (
1703
- visit(node.expression, { ...state, metadata })
1704
- );
1556
+ const expression = /** @type {AST.Expression} */ (visit(node.expression, state));
1705
1557
 
1706
1558
  // For literal values, compute hash at build time
1707
1559
  if (expression.type === 'Literal') {
1708
1560
  const value = String(expression.value ?? '');
1709
1561
  const hash_value = hash(value);
1710
1562
  // Push hash comment
1711
- state.init?.push(
1712
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(`<!--${hash_value}-->`))),
1713
- );
1563
+ state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(`<!--${hash_value}-->`))));
1714
1564
  // Push the HTML content
1715
- state.init?.push(b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(value))));
1565
+ state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(value))));
1716
1566
  // Push empty comment as end marker
1717
- state.init?.push(
1718
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal('<!---->'))),
1719
- );
1567
+ state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal('<!---->'))));
1720
1568
  } else {
1721
1569
  // For dynamic values, compute hash at runtime
1722
1570
  // Create a variable to store the value
@@ -1729,7 +1577,7 @@ const visitors = {
1729
1577
  state.init?.push(
1730
1578
  b.stmt(
1731
1579
  b.call(
1732
- b.member(b.id('__output'), b.id('push')),
1580
+ b.id('_$_.output_push'),
1733
1581
  b.binary(
1734
1582
  '+',
1735
1583
  b.binary('+', b.literal('<!--'), b.call('_$_.hash', b.id(value_id))),
@@ -1739,19 +1587,15 @@ const visitors = {
1739
1587
  ),
1740
1588
  );
1741
1589
  // Push the HTML content
1742
- state.init?.push(b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.id(value_id))));
1590
+ state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.id(value_id))));
1743
1591
  // Push empty comment as end marker
1744
- state.init?.push(
1745
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal('<!---->'))),
1746
- );
1592
+ state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal('<!---->'))));
1747
1593
  }
1748
1594
  }
1749
1595
  },
1750
1596
 
1751
1597
  ScriptContent(node, context) {
1752
- context.state.init?.push(
1753
- b.stmt(b.call(b.member(b.id('__output'), b.id('push')), b.literal(node.content))),
1754
- );
1598
+ context.state.init?.push(b.stmt(b.call(b.id('_$_.output_push'), b.literal(node.content))));
1755
1599
  },
1756
1600
 
1757
1601
  ServerBlock(node, context) {
@@ -1877,15 +1721,21 @@ export function transform_server(filename, source, analysis, minify_css, dev = f
1877
1721
  }
1878
1722
  }
1879
1723
 
1880
- // Add async property to component functions
1724
+ /** @type {AST.Program['body']} */
1725
+ let body = [];
1726
+
1881
1727
  for (const import_node of state.imports) {
1882
1728
  if (typeof import_node === 'string') {
1883
- program.body.unshift(b.stmt(b.id(import_node)));
1729
+ body.push(b.stmt(b.id(import_node)));
1884
1730
  } else {
1885
- program.body.unshift(import_node);
1731
+ body.push(import_node);
1886
1732
  }
1887
1733
  }
1888
1734
 
1735
+ body.push(...program.body);
1736
+
1737
+ program.body = body;
1738
+
1889
1739
  const js = print(program, /** @type {Visitors<AST.Node, TransformServerState>} */ (ts()), {
1890
1740
  sourceMapContent: source,
1891
1741
  sourceMapSource: path.basename(filename),