@tsrx/core 0.1.4 → 0.1.6

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.
@@ -21,6 +21,7 @@ import {
21
21
  get_for_of_iteration_params,
22
22
  identifier_to_jsx_name,
23
23
  is_bare_render_expression,
24
+ is_component_jsx_name,
24
25
  is_dynamic_element_id,
25
26
  is_jsx_child,
26
27
  set_loc,
@@ -245,6 +246,8 @@ export function createJsxTransform(platform) {
245
246
  needs_ref_prop: false,
246
247
  needs_normalize_spread_props: false,
247
248
  needs_fragment: false,
249
+ needs_for_of_iterable: false,
250
+ needs_iteration_value_type: false,
248
251
  module_scoped_hook_components:
249
252
  options?.moduleScopedHookComponents ?? !!platform.hooks?.moduleScopedHookComponents,
250
253
  helper_state: null,
@@ -515,7 +518,7 @@ export function createJsxTransform(platform) {
515
518
  const value = state.current_css_hash
516
519
  ? `${state.current_css_hash} ${class_name}`
517
520
  : class_name;
518
- return /** @type {any} */ (b.literal(value, node));
521
+ return b.literal(value, undefined, node);
519
522
  },
520
523
 
521
524
  // Default .metadata on every function-like node so downstream consumers
@@ -666,11 +669,7 @@ export function component_to_function_declaration(component, transform_context,
666
669
  // Wrap body_statements in a BlockStatement so that apply_lazy_transforms
667
670
  // runs collect_block_shadowed_names and detects body-level declarations
668
671
  // (e.g. `const name = ...`) that shadow lazy binding names.
669
- const body_block = /** @type {any} */ ({
670
- type: 'BlockStatement',
671
- body: body_statements,
672
- metadata: { path: [] },
673
- });
672
+ const body_block = b.block(body_statements);
674
673
  const final_body =
675
674
  lazy_bindings.size > 0 ? apply_lazy_transforms(body_block, lazy_bindings) : body_block;
676
675
 
@@ -678,48 +677,19 @@ export function component_to_function_declaration(component, transform_context,
678
677
  let fn;
679
678
 
680
679
  if (component.id) {
681
- fn = /** @type {any} */ ({
682
- type: 'FunctionDeclaration',
683
- id: component.id,
684
- typeParameters: component.typeParameters,
685
- params: final_params,
686
- body: final_body,
687
- async: is_async_component,
688
- generator: false,
689
- metadata: {
690
- path: [],
691
- is_component: true,
692
- },
693
- });
680
+ fn = b.function_declaration(
681
+ component.id,
682
+ final_params,
683
+ final_body,
684
+ is_async_component,
685
+ component.typeParameters,
686
+ );
694
687
  } else if (component.metadata?.arrow) {
695
- fn = /** @type {any} */ ({
696
- type: 'ArrowFunctionExpression',
697
- typeParameters: component.typeParameters,
698
- params: final_params,
699
- body: final_body,
700
- async: is_async_component,
701
- generator: false,
702
- expression: false,
703
- metadata: {
704
- path: [],
705
- is_component: true,
706
- },
707
- });
688
+ fn = b.arrow(final_params, final_body, is_async_component, component.typeParameters);
708
689
  } else {
709
- fn = /** @type {any} */ ({
710
- type: 'FunctionExpression',
711
- id: null,
712
- typeParameters: component.typeParameters,
713
- params: final_params,
714
- body: final_body,
715
- async: is_async_component,
716
- generator: false,
717
- metadata: {
718
- path: [],
719
- is_component: true,
720
- },
721
- });
690
+ fn = b.function(null, final_params, final_body, is_async_component, component.typeParameters);
722
691
  }
692
+ /** @type {any} */ (fn.metadata).is_component = true;
723
693
 
724
694
  // Restore context
725
695
  transform_context.helper_state = saved_helper_state;
@@ -889,25 +859,16 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
889
859
  if (stmt.type === 'ReturnStatement') {
890
860
  if (stmt.argument) {
891
861
  render_nodes.push(
892
- /** @type {any} */ ({
893
- type: 'JSXExpressionContainer',
894
- expression: set_loc(
895
- /** @type {any} */ ({
896
- type: 'ConditionalExpression',
897
- test: clone_expression_node(child.test),
898
- consequent: {
899
- type: 'Literal',
900
- value: null,
901
- raw: 'null',
902
- metadata: { path: [] },
903
- },
904
- alternate: stmt.argument,
905
- metadata: { path: [] },
906
- }),
862
+ b.jsx_expression_container(
863
+ set_loc(
864
+ b.conditional(
865
+ clone_expression_node(child.test),
866
+ b.literal(null),
867
+ stmt.argument,
868
+ ),
907
869
  child,
908
870
  ),
909
- metadata: { path: [] },
910
- }),
871
+ ),
911
872
  );
912
873
  }
913
874
  } else {
@@ -928,26 +889,6 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
928
889
  continue;
929
890
  }
930
891
 
931
- if (
932
- child.type === 'IfStatement' &&
933
- !child.alternate &&
934
- !is_returning_if_statement(child) &&
935
- !transform_context.platform.hooks?.isTopLevelSetupCall &&
936
- body_contains_top_level_hook_call([child], transform_context, true) &&
937
- i + 1 < body_nodes.length
938
- ) {
939
- statements.push(
940
- ...create_continuation_lift_if_statement(
941
- child,
942
- body_nodes.slice(i + 1),
943
- render_nodes,
944
- transform_context,
945
- ),
946
- );
947
- transform_context.available_bindings = saved_bindings;
948
- return statements;
949
- }
950
-
951
892
  if (
952
893
  child.type === 'ForOfStatement' &&
953
894
  !child.await &&
@@ -959,26 +900,9 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
959
900
  true,
960
901
  )
961
902
  ) {
962
- const for_of_continuation = body_nodes.slice(i + 1);
963
- const hoisted = build_hoisted_for_of_with_hooks(
964
- child,
965
- for_of_continuation,
966
- transform_context,
967
- );
903
+ const hoisted = build_hoisted_for_of_with_hooks(child, transform_context);
968
904
  if (hoisted) {
969
905
  statements.push(...hoisted.hoist_statements);
970
- if (for_of_continuation.length > 0) {
971
- // Tail was lifted into the helper; everything after the for-of
972
- // now lives there. Combine prior render_nodes with the iteration
973
- // JSX and return.
974
- statements.push({
975
- type: 'ReturnStatement',
976
- argument: combine_render_return_argument(render_nodes, hoisted.jsx_child),
977
- metadata: { path: [] },
978
- });
979
- transform_context.available_bindings = saved_bindings;
980
- return statements;
981
- }
982
906
  if (interleaved && is_capturable_jsx_child(hoisted.jsx_child)) {
983
907
  const { declaration, reference } = captureJsxChild(hoisted.jsx_child, capture_index++);
984
908
  statements.push(declaration);
@@ -990,43 +914,6 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
990
914
  }
991
915
  }
992
916
 
993
- if (
994
- child.type === 'TryStatement' &&
995
- !child.finalizer &&
996
- !transform_context.platform.hooks?.isTopLevelSetupCall &&
997
- try_statement_contains_hooks(child, transform_context) &&
998
- i + 1 < body_nodes.length
999
- ) {
1000
- statements.push(
1001
- ...create_continuation_lift_try_statement(
1002
- child,
1003
- body_nodes.slice(i + 1),
1004
- render_nodes,
1005
- transform_context,
1006
- ),
1007
- );
1008
- transform_context.available_bindings = saved_bindings;
1009
- return statements;
1010
- }
1011
-
1012
- if (
1013
- child.type === 'SwitchStatement' &&
1014
- !transform_context.platform.hooks?.isTopLevelSetupCall &&
1015
- body_contains_top_level_hook_call([child], transform_context, true) &&
1016
- i + 1 < body_nodes.length
1017
- ) {
1018
- statements.push(
1019
- ...create_continuation_lift_switch_statement(
1020
- child,
1021
- body_nodes.slice(i + 1),
1022
- render_nodes,
1023
- transform_context,
1024
- ),
1025
- );
1026
- transform_context.available_bindings = saved_bindings;
1027
- return statements;
1028
- }
1029
-
1030
917
  if (is_jsx_child(child)) {
1031
918
  const jsx = to_jsx_child(child, transform_context);
1032
919
  statements.push(...extract_jsx_setup_declarations(jsx));
@@ -1051,10 +938,7 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
1051
938
 
1052
939
  const return_arg = build_return_expression(render_nodes);
1053
940
  if (return_arg || (return_null_when_empty && !has_terminal_return)) {
1054
- statements.push({
1055
- type: 'ReturnStatement',
1056
- argument: return_arg || { type: 'Literal', value: null, raw: 'null' },
1057
- });
941
+ statements.push(b.return(return_arg || b.literal(null)));
1058
942
  }
1059
943
 
1060
944
  transform_context.available_bindings = saved_bindings;
@@ -1254,16 +1138,7 @@ function create_helper_props_property(binding) {
1254
1138
  const key = create_generated_identifier(binding.name);
1255
1139
  const value = create_generated_identifier(binding.name);
1256
1140
 
1257
- return /** @type {any} */ ({
1258
- type: 'Property',
1259
- key,
1260
- value,
1261
- kind: 'init',
1262
- method: false,
1263
- shorthand: true,
1264
- computed: false,
1265
- metadata: { path: [] },
1266
- });
1141
+ return b.prop('init', key, value, false, true);
1267
1142
  }
1268
1143
 
1269
1144
  /**
@@ -1499,7 +1374,7 @@ function create_module_scoped_hook_component_id(helper_id, transform_context) {
1499
1374
  * @param {any[]} params
1500
1375
  * @returns {Map<string, AST.Identifier>}
1501
1376
  */
1502
- function collect_param_bindings(params) {
1377
+ export function collect_param_bindings(params) {
1503
1378
  const bindings = new Map();
1504
1379
  for (const param of params) {
1505
1380
  collect_pattern_bindings(param, bindings);
@@ -1512,7 +1387,7 @@ function collect_param_bindings(params) {
1512
1387
  * @param {Map<string, AST.Identifier>} bindings
1513
1388
  * @returns {void}
1514
1389
  */
1515
- function collect_statement_bindings(statement, bindings) {
1390
+ export function collect_statement_bindings(statement, bindings) {
1516
1391
  if (!statement) return;
1517
1392
 
1518
1393
  if (statement.type === 'VariableDeclaration') {
@@ -1646,6 +1521,16 @@ function hoist_static_render_nodes(render_nodes, transform_context) {
1646
1521
  const node = render_nodes[i];
1647
1522
  if (node.type !== 'JSXElement') continue;
1648
1523
  if (!is_hoist_safe_jsx_node(node)) continue;
1524
+ if (is_bare_component_invocation(node)) {
1525
+ // `<Helper />` with no attributes and no children is just an
1526
+ // invocation reference — most often a generated `StatementBodyHook`
1527
+ // chain element we emitted ourselves. Hoisting it would produce
1528
+ // `const App__staticN = <Helper />` aliases that bloat the output
1529
+ // without enabling React's element-identity fast path (the helper
1530
+ // isn't memoized, so the parent re-invokes it every render either
1531
+ // way). Inline the reference at the call site instead.
1532
+ continue;
1533
+ }
1649
1534
  if (
1650
1535
  transform_context.platform.hooks?.canHoistStaticNode &&
1651
1536
  !transform_context.platform.hooks.canHoistStaticNode(node, transform_context)
@@ -1663,6 +1548,23 @@ function hoist_static_render_nodes(render_nodes, transform_context) {
1663
1548
  }
1664
1549
  }
1665
1550
 
1551
+ /**
1552
+ * `<Helper />` shape with no attributes and no children. The opening element
1553
+ * name must be component-shaped (see `is_component_jsx_name`) — lowercase
1554
+ * identifiers are host DOM tags, which *do* benefit from hoisting because
1555
+ * React diffs them against the previous render.
1556
+ *
1557
+ * @param {any} node
1558
+ * @returns {boolean}
1559
+ */
1560
+ function is_bare_component_invocation(node) {
1561
+ if (!node || node.type !== 'JSXElement') return false;
1562
+ const opening = node.openingElement;
1563
+ if (!opening || opening.attributes.length > 0) return false;
1564
+ if (node.children.length > 0) return false;
1565
+ return is_component_jsx_name(opening.name);
1566
+ }
1567
+
1666
1568
  /**
1667
1569
  * Static JSX that appears before multiple early-return guards is otherwise
1668
1570
  * cloned into every generated return. Capture it once at its source position
@@ -1891,169 +1793,6 @@ function create_component_returning_if_statement(node, render_nodes, transform_c
1891
1793
  return set_loc(b.if(node.test, set_loc(b.block(branch_statements), node.consequent), null), node);
1892
1794
  }
1893
1795
 
1894
- /* ---------------------------------------------------------------------- *
1895
- * Continuation-lift primitives shared across if / switch / try / for-of *
1896
- * ---------------------------------------------------------------------- */
1897
-
1898
- /**
1899
- * Build the helper component that owns the post-control-flow continuation.
1900
- * Same shape as `create_hook_safe_helper`; named for intent at lift call sites.
1901
- *
1902
- * @param {any[]} continuation_body
1903
- * @param {any} source_node
1904
- * @param {TransformContext} transform_context
1905
- * @returns {{ setup_statements: any[], component_element: ESTreeJSX.JSXElement }}
1906
- */
1907
- function build_tail_helper(continuation_body, source_node, transform_context) {
1908
- return create_hook_safe_helper(continuation_body, undefined, source_node, transform_context);
1909
- }
1910
-
1911
- /**
1912
- * Clone the tail helper's component element for embedding inside another
1913
- * branch's body. Loses location info because the same element appears in
1914
- * multiple positions and downstream tooling treats AST nodes as identity-keyed.
1915
- *
1916
- * @param {{ component_element: ESTreeJSX.JSXElement }} tail_helper
1917
- * @returns {any}
1918
- */
1919
- function clone_tail_invocation(tail_helper) {
1920
- return clone_expression_node(tail_helper.component_element, false);
1921
- }
1922
-
1923
- /**
1924
- * Return `[...body, <TailHelper x={x} />]` so the branch's render output
1925
- * includes the tail invocation and the post-hook locals flow forward.
1926
- * Used by if / switch / try (unconditional append). For-of uses a different
1927
- * shape — gating on `_tsrx_isLast_<n>` — so it constructs its own.
1928
- *
1929
- * @param {any[]} body
1930
- * @param {{ component_element: ESTreeJSX.JSXElement }} tail_helper
1931
- * @returns {any[]}
1932
- */
1933
- function append_tail_invocation(body, tail_helper) {
1934
- return [...body, clone_tail_invocation(tail_helper)];
1935
- }
1936
-
1937
- /**
1938
- * @param {AST.Identifier} tail_synthetic_id
1939
- * @param {{ component_element: ESTreeJSX.JSXElement }} tail_helper
1940
- * @returns {any}
1941
- */
1942
- function create_loop_tail_expression(tail_synthetic_id, tail_helper) {
1943
- return b.logical('&&', clone_identifier(tail_synthetic_id), clone_tail_invocation(tail_helper));
1944
- }
1945
-
1946
- /**
1947
- * @param {AST.Identifier} tail_synthetic_id
1948
- * @param {{ component_element: ESTreeJSX.JSXElement }} tail_helper
1949
- * @returns {any}
1950
- */
1951
- function create_loop_tail_conditional(tail_synthetic_id, tail_helper) {
1952
- return b.conditional(
1953
- clone_identifier(tail_synthetic_id),
1954
- clone_tail_invocation(tail_helper),
1955
- create_null_literal(),
1956
- );
1957
- }
1958
-
1959
- /**
1960
- * @param {any[]} statements
1961
- * @param {AST.Identifier} tail_synthetic_id
1962
- * @param {{ component_element: ESTreeJSX.JSXElement }} tail_helper
1963
- * @returns {void}
1964
- */
1965
- function append_loop_tail_to_return_statements(statements, tail_synthetic_id, tail_helper) {
1966
- for (const statement of statements) {
1967
- append_loop_tail_to_return_statement(statement, tail_synthetic_id, tail_helper, false);
1968
- }
1969
- }
1970
-
1971
- /**
1972
- * @param {any} node
1973
- * @param {AST.Identifier} tail_synthetic_id
1974
- * @param {{ component_element: ESTreeJSX.JSXElement }} tail_helper
1975
- * @param {boolean} inside_nested_function
1976
- * @returns {void}
1977
- */
1978
- function append_loop_tail_to_return_statement(
1979
- node,
1980
- tail_synthetic_id,
1981
- tail_helper,
1982
- inside_nested_function,
1983
- ) {
1984
- if (!node || typeof node !== 'object') {
1985
- return;
1986
- }
1987
-
1988
- if (
1989
- node.type === 'FunctionDeclaration' ||
1990
- node.type === 'FunctionExpression' ||
1991
- node.type === 'ArrowFunctionExpression'
1992
- ) {
1993
- inside_nested_function = true;
1994
- }
1995
-
1996
- if (!inside_nested_function && node.type === 'ReturnStatement') {
1997
- if (
1998
- references_scope_bindings(
1999
- node.argument,
2000
- new Map([[tail_synthetic_id.name, tail_synthetic_id]]),
2001
- )
2002
- ) {
2003
- return;
2004
- }
2005
- node.argument = append_loop_tail_to_return_argument(
2006
- node.argument,
2007
- tail_synthetic_id,
2008
- tail_helper,
2009
- );
2010
- return;
2011
- }
2012
-
2013
- if (Array.isArray(node)) {
2014
- for (const child of node) {
2015
- append_loop_tail_to_return_statement(
2016
- child,
2017
- tail_synthetic_id,
2018
- tail_helper,
2019
- inside_nested_function,
2020
- );
2021
- }
2022
- return;
2023
- }
2024
-
2025
- for (const key of Object.keys(node)) {
2026
- if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
2027
- continue;
2028
- }
2029
- append_loop_tail_to_return_statement(
2030
- node[key],
2031
- tail_synthetic_id,
2032
- tail_helper,
2033
- inside_nested_function,
2034
- );
2035
- }
2036
- }
2037
-
2038
- /**
2039
- * @param {any} return_argument
2040
- * @param {AST.Identifier} tail_synthetic_id
2041
- * @param {{ component_element: ESTreeJSX.JSXElement }} tail_helper
2042
- * @returns {any}
2043
- */
2044
- function append_loop_tail_to_return_argument(return_argument, tail_synthetic_id, tail_helper) {
2045
- if (return_argument == null || is_null_literal(return_argument)) {
2046
- return create_loop_tail_conditional(tail_synthetic_id, tail_helper);
2047
- }
2048
-
2049
- return (
2050
- build_return_expression([
2051
- return_argument_to_render_node(return_argument),
2052
- to_jsx_expression_container(create_loop_tail_expression(tail_synthetic_id, tail_helper)),
2053
- ]) || create_null_literal()
2054
- );
2055
- }
2056
-
2057
1796
  /**
2058
1797
  * Build a `return <combined-render-fragment>;` statement, prepending any
2059
1798
  * `render_nodes` collected before the control-flow construct so they don't
@@ -2133,256 +1872,6 @@ function create_component_helper_split_returning_if_statements(
2133
1872
  ];
2134
1873
  }
2135
1874
 
2136
- /**
2137
- * Lift a non-returning `if` whose consequent contains hook calls plus the
2138
- * statements that follow it into helper components.
2139
- *
2140
- * Without this, the consequent's hook would be wrapped into a child component
2141
- * (StatementBodyHook) but any code after the `if` that reads bindings the hook
2142
- * mutates would observe the pre-hook value, because React commits children
2143
- * after their parent has finished rendering. The fix mirrors the early-return
2144
- * splitter: emit a tail helper that owns the post-`if` statements, append a
2145
- * call to it inside the branch helper so the post-hook bindings flow forward,
2146
- * and render the tail helper directly when the `if` is false.
2147
- *
2148
- * @param {any} if_node
2149
- * @param {any[]} continuation_body
2150
- * @param {any[]} render_nodes
2151
- * @param {TransformContext} transform_context
2152
- * @returns {any[]}
2153
- */
2154
- function create_continuation_lift_if_statement(
2155
- if_node,
2156
- continuation_body,
2157
- render_nodes,
2158
- transform_context,
2159
- ) {
2160
- const consequent_body = get_if_consequent_body(if_node);
2161
- const tail_helper = build_tail_helper(continuation_body, if_node, transform_context);
2162
- const branch_helper = create_hook_safe_helper(
2163
- append_tail_invocation(consequent_body, tail_helper),
2164
- undefined,
2165
- if_node.consequent,
2166
- transform_context,
2167
- );
2168
-
2169
- const branch_block = set_loc(
2170
- b.block([
2171
- ...branch_helper.setup_statements,
2172
- combined_return_statement(render_nodes, branch_helper.component_element),
2173
- ]),
2174
- if_node.consequent,
2175
- );
2176
-
2177
- return [
2178
- ...tail_helper.setup_statements,
2179
- set_loc(b.if(if_node.test, branch_block, null), if_node),
2180
- combined_return_statement(render_nodes, tail_helper.component_element),
2181
- ];
2182
- }
2183
-
2184
- /**
2185
- * Continuation lift for `try` / `try / pending / catch` statements. Same
2186
- * shape as if/switch: build a tail helper from the post-`try` statements, and
2187
- * append a clone of its invocation to the try body and the catch body so the
2188
- * post-hook locals inside each branch flow forward into the tail. The pending
2189
- * body is left untouched — when Suspense renders the pending fallback the
2190
- * parent's render is unwound, so the tail wouldn't run in source semantics
2191
- * either. Once augmented, the existing try transform builds the
2192
- * Suspense / TsrxErrorBoundary wrapper as usual.
2193
- *
2194
- * @param {any} node - TryStatement
2195
- * @param {any[]} continuation_body
2196
- * @param {any[]} render_nodes
2197
- * @param {TransformContext} transform_context
2198
- * @returns {any[]}
2199
- */
2200
- function create_continuation_lift_try_statement(
2201
- node,
2202
- continuation_body,
2203
- render_nodes,
2204
- transform_context,
2205
- ) {
2206
- const tail_helper = build_tail_helper(continuation_body, node, transform_context);
2207
-
2208
- const augmented_block = {
2209
- ...node.block,
2210
- body: append_tail_invocation(node.block.body || [], tail_helper),
2211
- };
2212
-
2213
- let augmented_handler = node.handler;
2214
- if (node.handler) {
2215
- augmented_handler = {
2216
- ...node.handler,
2217
- body: {
2218
- ...node.handler.body,
2219
- body: append_tail_invocation(node.handler.body.body || [], tail_helper),
2220
- },
2221
- };
2222
- }
2223
-
2224
- const augmented_try = {
2225
- ...node,
2226
- block: augmented_block,
2227
- handler: augmented_handler,
2228
- };
2229
-
2230
- const try_jsx_child = (
2231
- transform_context.platform.hooks?.controlFlow?.tryStatement ?? try_statement_to_jsx_child
2232
- )(augmented_try, transform_context);
2233
-
2234
- return [...tail_helper.setup_statements, combined_return_statement(render_nodes, try_jsx_child)];
2235
- }
2236
-
2237
- /**
2238
- * @param {any} node - TryStatement
2239
- * @param {TransformContext} transform_context
2240
- * @returns {boolean}
2241
- */
2242
- function try_statement_contains_hooks(node, transform_context) {
2243
- if (body_contains_top_level_hook_call(node.block?.body || [], transform_context, true)) {
2244
- return true;
2245
- }
2246
- if (
2247
- node.handler &&
2248
- body_contains_top_level_hook_call(node.handler.body?.body || [], transform_context, true)
2249
- ) {
2250
- return true;
2251
- }
2252
- if (
2253
- node.pending &&
2254
- body_contains_top_level_hook_call(node.pending.body || [], transform_context, true)
2255
- ) {
2256
- return true;
2257
- }
2258
- return false;
2259
- }
2260
-
2261
- /**
2262
- * Continuation lift for `switch` statements. Same shape as the if-version:
2263
- * each case body is wrapped in its own helper component that ends with a
2264
- * call to a shared tail helper, so post-hook bindings inside any case flow
2265
- * forward to the statements after the switch. The fall-through return at
2266
- * the end renders the tail helper directly, covering the case where no
2267
- * `case` (and no `default`) matched.
2268
- *
2269
- * Empty fall-through cases (`case 'a':` with no body, falling through to
2270
- * the next case) are preserved as-is — they must not get their own helper
2271
- * because that would convert fall-through into early-return.
2272
- *
2273
- * @param {any} switch_node
2274
- * @param {any[]} continuation_body
2275
- * @param {any[]} render_nodes
2276
- * @param {TransformContext} transform_context
2277
- * @returns {any[]}
2278
- */
2279
- function create_continuation_lift_switch_statement(
2280
- switch_node,
2281
- continuation_body,
2282
- render_nodes,
2283
- transform_context,
2284
- ) {
2285
- const tail_helper = build_tail_helper(continuation_body, switch_node, transform_context);
2286
-
2287
- // Per-case info computed once: own body (statements before any
2288
- // terminator) and whether the case has a `break` / `return`.
2289
- const case_info = switch_node.cases.map((/** @type {any} */ c) => {
2290
- const consequent = flatten_switch_consequent(c.consequent || []);
2291
- const own_body = [];
2292
- let own_has_terminator = false;
2293
- for (const node of consequent) {
2294
- if (node.type === 'BreakStatement' || node.type === 'ReturnStatement') {
2295
- own_has_terminator = true;
2296
- break;
2297
- }
2298
- own_body.push(node);
2299
- }
2300
- return { own_body, own_has_terminator };
2301
- });
2302
-
2303
- // Allocate helper ids in source order (forward pass) so the snapshot's
2304
- // `StatementBodyHook<N>` numbering reads top-to-bottom by case position.
2305
- /** @type {Array<AST.Identifier | null>} */
2306
- const helper_ids = case_info.map(
2307
- (/** @type {{ own_body: any[], own_has_terminator: boolean }} */ info) =>
2308
- info.own_body.length === 0
2309
- ? null
2310
- : create_generated_identifier(create_local_statement_component_name(transform_context)),
2311
- );
2312
-
2313
- // Build helpers in reverse order: each fall-through case's helper body
2314
- // invokes the *next* case's helper, so the chain forwards post-mutation
2315
- // locals through the switch. Reverse iteration ensures the next helper's
2316
- // component_element is already constructed when we need to embed it.
2317
- /** @type {Array<{ setup_statements: any[], component_element: any } | null>} */
2318
- const case_helper_by_index = new Array(switch_node.cases.length).fill(null);
2319
- for (let i = switch_node.cases.length - 1; i >= 0; i--) {
2320
- const { own_body, own_has_terminator } = case_info[i];
2321
- if (own_body.length === 0) continue;
2322
-
2323
- // Determine the downstream helper this case invokes after its own body.
2324
- // - With a terminator: invoke the tail helper directly (case exits switch).
2325
- // - Otherwise (fall-through): invoke the next non-empty case's helper,
2326
- // or the tail if nothing else follows.
2327
- let downstream;
2328
- if (own_has_terminator) {
2329
- downstream = tail_helper;
2330
- } else {
2331
- let next_helper = null;
2332
- for (let j = i + 1; j < switch_node.cases.length; j++) {
2333
- if (case_helper_by_index[j]) {
2334
- next_helper = case_helper_by_index[j];
2335
- break;
2336
- }
2337
- }
2338
- downstream = next_helper ?? tail_helper;
2339
- }
2340
-
2341
- case_helper_by_index[i] = create_hook_safe_helper(
2342
- append_tail_invocation(own_body, downstream),
2343
- undefined,
2344
- switch_node.cases[i],
2345
- transform_context,
2346
- /** @type {any} */ (helper_ids[i]),
2347
- );
2348
- }
2349
-
2350
- const new_cases = switch_node.cases.map(
2351
- (/** @type {any} */ original_case, /** @type {number} */ i) => {
2352
- const helper = case_helper_by_index[i];
2353
- if (helper) {
2354
- return b.switch_case(original_case.test, [
2355
- combined_return_statement(render_nodes, helper.component_element),
2356
- ]);
2357
- }
2358
-
2359
- const { own_body, own_has_terminator } = case_info[i];
2360
- if (own_body.length === 0 && own_has_terminator) {
2361
- // `case 'a': break;` — exits the switch, then runs the tail.
2362
- return b.switch_case(original_case.test, [
2363
- combined_return_statement(render_nodes, tail_helper.component_element),
2364
- ]);
2365
- }
2366
- // Genuine empty fall-through (`case 'a': case 'b': ...`).
2367
- return b.switch_case(original_case.test, []);
2368
- },
2369
- );
2370
-
2371
- // Hoist all case helpers' setup statements above the switch in source
2372
- // order so the switch body is purely a dispatcher.
2373
- const case_helper_setup_statements = [];
2374
- for (const helper of case_helper_by_index) {
2375
- if (helper) case_helper_setup_statements.push(...helper.setup_statements);
2376
- }
2377
-
2378
- return [
2379
- ...tail_helper.setup_statements,
2380
- ...case_helper_setup_statements,
2381
- set_loc(b.switch(switch_node.discriminant, new_cases), switch_node),
2382
- combined_return_statement(render_nodes, tail_helper.component_element),
2383
- ];
2384
- }
2385
-
2386
1875
  /**
2387
1876
  * Hoist the helper for a hook-bearing for-of body out of the iteration
2388
1877
  * callback so the helper is declared once per render rather than re-bound on
@@ -2395,65 +1884,35 @@ function create_continuation_lift_switch_statement(
2395
1884
  * works while skipping the copy when the source is already an array. The
2396
1885
  * iteration itself is emitted as `source.map((item, i) => ...)`.
2397
1886
  *
2398
- * If `continuation_body` is non-empty (the for-of has a tail) we also lift
2399
- * the tail into a TailHelper and call it conditionally on the last iteration
2400
- * via an `isLast={i === source.length - 1}` prop on the loop helper. The
2401
- * loop helper's mutated locals (post-`useState`) flow into the TailHelper as
2402
- * its props. When the source is empty, `.map` returns `[]` and the TailHelper
2403
- * never renders — we add a sibling fallback so the source's tail still runs
2404
- * with the original outer values in that case.
2405
- *
2406
1887
  * Bails out (returns null) when the loop pattern is destructured — deriving
2407
1888
  * element types from a tuple/object pattern is more involved and deferred.
2408
1889
  *
2409
1890
  * @param {any} node - ForOfStatement
2410
- * @param {any[]} continuation_body
2411
1891
  * @param {TransformContext} transform_context
2412
1892
  * @returns {{ hoist_statements: any[], jsx_child: any } | null}
2413
1893
  */
2414
- function build_hoisted_for_of_with_hooks(node, continuation_body, transform_context) {
1894
+ function build_hoisted_for_of_with_hooks(node, transform_context) {
2415
1895
  const loop_params = get_for_of_iteration_params(node.left, node.index);
2416
1896
  for (const param of loop_params) {
2417
1897
  if (param.type !== 'Identifier') return null;
2418
1898
  }
2419
1899
 
2420
- const has_tail = continuation_body.length > 0;
2421
1900
  const original_loop_body = /** @type {any[]} */ (
2422
1901
  rewrite_loop_continues_to_bare_returns(
2423
1902
  node.body.type === 'BlockStatement' ? node.body.body : [node.body],
2424
1903
  )
2425
1904
  );
2426
1905
 
2427
- // When there's a tail, build TailHelper first so its component_element can
2428
- // be embedded inside the loop helper's body (gated on isLast). The
2429
- // synthetic isLast prop uses the loop helper's index (which will be the
2430
- // next one assigned, since `create_hook_safe_helper` for the tail just
2431
- // consumed one) so it lines up with `StatementBodyHook<N>` in the output.
2432
- let tail_helper = null;
2433
- /** @type {AST.Identifier} */ let tail_synthetic_id;
2434
- if (has_tail) {
2435
- tail_helper = build_tail_helper(continuation_body, node, transform_context);
2436
- tail_synthetic_id = create_generated_identifier(
2437
- `_tsrx_isLast_${transform_context.local_statement_component_index + 1}`,
2438
- );
2439
- } else {
2440
- tail_synthetic_id = /** @type {any} */ (null);
2441
- }
2442
- const loop_tail_expression = has_tail
2443
- ? create_loop_tail_expression(tail_synthetic_id, /** @type {any} */ (tail_helper))
2444
- : null;
2445
- const loop_body =
2446
- has_tail && loop_tail_expression
2447
- ? [...original_loop_body, b.jsx_expression_container(loop_tail_expression)]
2448
- : original_loop_body;
2449
-
2450
1906
  const source_id = create_generated_identifier(
2451
1907
  `_tsrx_iteration_items_${transform_context.local_statement_component_index + 1}`,
2452
1908
  );
2453
- const { source_decl, source_normalize_decl } = build_array_normalization_decls(
2454
- source_id,
2455
- node.right,
2456
- );
1909
+ const use_iterable_helper = !!transform_context.platform.imports.forOfIterableHelper;
1910
+ const { source_decl, source_normalize_decl } = use_iterable_helper
1911
+ ? {
1912
+ source_decl: b.let(clone_identifier(source_id), clone_expression_node(node.right)),
1913
+ source_normalize_decl: null,
1914
+ }
1915
+ : build_array_normalization_decls(source_id, node.right);
2457
1916
 
2458
1917
  const saved_bindings = transform_context.available_bindings;
2459
1918
  transform_context.available_bindings = new Map(saved_bindings);
@@ -2468,7 +1927,7 @@ function build_hoisted_for_of_with_hooks(node, continuation_body, transform_cont
2468
1927
  );
2469
1928
 
2470
1929
  const all_helper_bindings = get_referenced_helper_bindings(
2471
- loop_body,
1930
+ original_loop_body,
2472
1931
  transform_context.available_bindings,
2473
1932
  );
2474
1933
  const outer_bindings = all_helper_bindings.filter((b) => !loop_scoped_names.has(b.name));
@@ -2488,69 +1947,42 @@ function build_hoisted_for_of_with_hooks(node, continuation_body, transform_cont
2488
1947
  const loop_aliases = use_module_scoped_component
2489
1948
  ? []
2490
1949
  : loop_bindings.map((binding) =>
2491
- create_loop_scoped_type_alias_declaration(helper_id, binding, source_id, loop_params),
1950
+ create_loop_scoped_type_alias_declaration(
1951
+ helper_id,
1952
+ binding,
1953
+ source_id,
1954
+ loop_params,
1955
+ transform_context,
1956
+ ),
2492
1957
  );
2493
1958
 
2494
- // Synthetic `isLast` prop on the loop helper when there's a tail. It's
2495
- // passed from the .map callback as `i === source.length - 1` so every
2496
- // loop-helper return can append the tail helper on the last iteration.
2497
- const tail_isLast_alias = has_tail
2498
- ? use_module_scoped_component
2499
- ? null
2500
- : {
2501
- id: create_generated_identifier(`_tsrx_${helper_id.name}_isLast`),
2502
- declaration: b.ts_type_alias(
2503
- create_generated_identifier(`_tsrx_${helper_id.name}_isLast`),
2504
- b.ts_keyword_type('boolean'),
2505
- ),
2506
- }
2507
- : null;
2508
-
2509
1959
  const ordered_bindings = [...outer_bindings, ...loop_bindings];
2510
1960
  const ordered_aliases = [...outer_aliases, ...loop_aliases];
2511
1961
  const ordered_use_typeof = [...outer_bindings.map(() => true), ...loop_bindings.map(() => false)];
2512
1962
 
2513
- const signature_bindings = has_tail ? [...ordered_bindings, tail_synthetic_id] : ordered_bindings;
2514
- const signature_aliases = has_tail
2515
- ? [...ordered_aliases, /** @type {any} */ (tail_isLast_alias)]
2516
- : ordered_aliases;
2517
- const signature_use_typeof = has_tail ? [...ordered_use_typeof, false] : ordered_use_typeof;
2518
-
2519
1963
  const props_type =
2520
- signature_bindings.length > 0 && !use_module_scoped_component
1964
+ ordered_bindings.length > 0 && !use_module_scoped_component
2521
1965
  ? create_helper_props_type_literal_with_typeof_flags(
2522
- signature_bindings,
2523
- signature_aliases,
2524
- signature_use_typeof,
1966
+ ordered_bindings,
1967
+ ordered_aliases,
1968
+ ordered_use_typeof,
2525
1969
  )
2526
1970
  : null;
2527
1971
  const params =
2528
- signature_bindings.length > 0
1972
+ ordered_bindings.length > 0
2529
1973
  ? [
2530
1974
  props_type !== null
2531
- ? create_typed_helper_props_pattern(signature_bindings, props_type)
2532
- : create_helper_props_pattern(signature_bindings),
1975
+ ? create_typed_helper_props_pattern(ordered_bindings, props_type)
1976
+ : create_helper_props_pattern(ordered_bindings),
2533
1977
  ]
2534
1978
  : [];
2535
1979
 
2536
1980
  const fn_saved_bindings = transform_context.available_bindings;
2537
1981
  transform_context.available_bindings = new Map(fn_saved_bindings);
2538
- if (has_tail) {
2539
- transform_context.available_bindings.set(tail_synthetic_id.name, tail_synthetic_id);
2540
- }
2541
- const fn_body_statements = build_render_statements(loop_body, true, transform_context);
2542
- if (has_tail) {
2543
- append_loop_tail_to_return_statements(
2544
- fn_body_statements,
2545
- tail_synthetic_id,
2546
- /** @type {any} */ (tail_helper),
2547
- );
2548
- }
1982
+ const fn_body_statements = build_render_statements(original_loop_body, true, transform_context);
2549
1983
  transform_context.available_bindings = fn_saved_bindings;
2550
1984
 
2551
- const helper_fn = /** @type {any} */ (
2552
- b.function(clone_identifier(component_id), params, b.block(fn_body_statements))
2553
- );
1985
+ const helper_fn = b.function(clone_identifier(component_id), params, b.block(fn_body_statements));
2554
1986
  helper_fn.metadata = { path: [], is_component: true, is_method: false };
2555
1987
 
2556
1988
  let helper_decl;
@@ -2582,18 +2014,6 @@ function build_hoisted_for_of_with_hooks(node, continuation_body, transform_cont
2582
2014
  { mapWrapper: false, mapBindingNames: false, mapBindingValues: false },
2583
2015
  );
2584
2016
 
2585
- // When there's a tail, the .map callback always needs an index to compute
2586
- // `isLast`. If the user didn't write `index i`, synthesize one. The same
2587
- // identifier is also used as the implicit key fallback below.
2588
- let index_identifier;
2589
- if (loop_params.length >= 2) {
2590
- index_identifier = clone_identifier(loop_params[1]);
2591
- } else if (has_tail) {
2592
- index_identifier = create_generated_identifier('i');
2593
- } else {
2594
- index_identifier = null;
2595
- }
2596
-
2597
2017
  const body_key_expression = find_key_expression_in_body(original_loop_body);
2598
2018
  const explicit_key_expression =
2599
2019
  body_key_expression ?? (node.key ? clone_expression_node(node.key) : undefined);
@@ -2606,57 +2026,24 @@ function build_hoisted_for_of_with_hooks(node, continuation_body, transform_cont
2606
2026
  );
2607
2027
  }
2608
2028
 
2609
- if (has_tail && index_identifier) {
2610
- const length_minus_one = b.binary(
2611
- '-',
2612
- b.member(clone_identifier(source_id), 'length'),
2613
- b.literal(1),
2614
- );
2615
- callback_invocation_element.openingElement.attributes.push(
2616
- b.jsx_attribute(
2617
- b.jsx_id(tail_synthetic_id.name),
2618
- to_jsx_expression_container(
2619
- b.binary('===', clone_identifier(index_identifier), length_minus_one),
2620
- ),
2621
- ),
2622
- );
2623
- }
2624
-
2625
- const callback_params =
2626
- has_tail && loop_params.length < 2 && index_identifier
2627
- ? [
2628
- ...loop_params.map((/** @type {any} */ p) => clone_identifier(p)),
2629
- clone_identifier(index_identifier),
2630
- ]
2631
- : loop_params.map((/** @type {any} */ p) => clone_identifier(p));
2029
+ const callback_params = loop_params.map((/** @type {any} */ p) => clone_identifier(p));
2632
2030
 
2633
2031
  const iter_callback = b.arrow(callback_params, callback_invocation_element);
2634
2032
 
2635
- const map_call = b.call(b.member(clone_identifier(source_id), 'map'), iter_callback);
2636
-
2637
- // jsx_child for the iteration. When there's a tail, also render the tail
2638
- // helper directly when the source is empty (no iterations means the loop
2639
- // helper never fires, so the tail wouldn't run otherwise).
2640
- const jsx_child = has_tail
2641
- ? to_jsx_expression_container(
2642
- b.conditional(
2643
- b.binary('===', b.member(clone_identifier(source_id), 'length'), b.literal(0)),
2644
- clone_tail_invocation(/** @type {any} */ (tail_helper)),
2645
- map_call,
2646
- ),
2647
- node,
2648
- )
2649
- : to_jsx_expression_container(map_call, node);
2650
-
2651
- const hoist_statements = [source_decl, source_normalize_decl];
2652
- if (has_tail) {
2653
- // TailHelper's setup statements (its alias consts and cache decl).
2654
- hoist_statements.push(.../** @type {any} */ (tail_helper).setup_statements);
2033
+ let map_call;
2034
+ if (use_iterable_helper) {
2035
+ transform_context.needs_for_of_iterable = true;
2036
+ map_call = b.call(b.id(MAP_ITERABLE_INTERNAL_NAME), clone_identifier(source_id), iter_callback);
2037
+ } else {
2038
+ map_call = b.call(b.member(clone_identifier(source_id), 'map'), iter_callback);
2655
2039
  }
2040
+
2041
+ const jsx_child = to_jsx_expression_container(map_call, node);
2042
+
2043
+ const hoist_statements = source_normalize_decl
2044
+ ? [source_decl, source_normalize_decl]
2045
+ : [source_decl];
2656
2046
  for (const alias of ordered_aliases) hoist_statements.push(alias.declaration);
2657
- if (has_tail && tail_isLast_alias) {
2658
- hoist_statements.push(tail_isLast_alias.declaration);
2659
- }
2660
2047
  if (helper_decl) {
2661
2048
  hoist_statements.push(helper_decl);
2662
2049
  }
@@ -2669,28 +2056,49 @@ function build_hoisted_for_of_with_hooks(node, continuation_body, transform_cont
2669
2056
 
2670
2057
  /**
2671
2058
  * Build a TS `type` alias for a loop-scoped binding, deriving the type
2672
- * from the iteration source. For the value param we use
2673
- * `(typeof source)[number]`, which gives the right element type for arrays
2674
- * and tuples (the common case in JSX templates). For the index param,
2675
- * the type is always `number`.
2059
+ * from the iteration source. For the index param the type is always
2060
+ * `number`. For the value param the shape depends on whether the platform
2061
+ * uses the `map_iterable` runtime helper:
2062
+ *
2063
+ * - With the helper (React, Preact): `IterationValue<typeof source>` — any
2064
+ * `Iterable<T>` is accepted, so the element type is derived through the
2065
+ * runtime's exported helper type.
2066
+ * - Without the helper: `(typeof source)[number]` — arrays/tuples only,
2067
+ * matching the inline `.map()` lowering.
2676
2068
  *
2677
2069
  * @param {AST.Identifier} helper_id
2678
2070
  * @param {AST.Identifier} binding
2679
2071
  * @param {AST.Identifier} source_id
2680
2072
  * @param {any[]} loop_params
2073
+ * @param {TransformContext} transform_context
2681
2074
  * @returns {{ id: AST.Identifier, declaration: any }}
2682
2075
  */
2683
- function create_loop_scoped_type_alias_declaration(helper_id, binding, source_id, loop_params) {
2076
+ function create_loop_scoped_type_alias_declaration(
2077
+ helper_id,
2078
+ binding,
2079
+ source_id,
2080
+ loop_params,
2081
+ transform_context,
2082
+ ) {
2684
2083
  const alias_id = create_generated_identifier(`_tsrx_${helper_id.name}_${binding.name}`);
2685
2084
  const is_index = loop_params.length > 1 && binding.name === loop_params[1].name;
2085
+ const use_iterable_helper = !!transform_context.platform.imports.forOfIterableHelper;
2686
2086
  const type_annotation = is_index
2687
2087
  ? b.ts_keyword_type('number')
2688
- : /** @type {any} */ ({
2689
- type: 'TSIndexedAccessType',
2690
- objectType: b.ts_type_query(clone_identifier(source_id)),
2691
- indexType: b.ts_keyword_type('number'),
2692
- metadata: { path: [] },
2693
- });
2088
+ : use_iterable_helper
2089
+ ? (() => {
2090
+ transform_context.needs_iteration_value_type = true;
2091
+ return b.ts_type_reference(
2092
+ b.id(ITERATION_VALUE_INTERNAL_NAME),
2093
+ b.ts_type_parameter_instantiation([b.ts_type_query(clone_identifier(source_id))]),
2094
+ );
2095
+ })()
2096
+ : /** @type {any} */ ({
2097
+ type: 'TSIndexedAccessType',
2098
+ objectType: b.ts_type_query(clone_identifier(source_id)),
2099
+ indexType: b.ts_keyword_type('number'),
2100
+ metadata: { path: [] },
2101
+ });
2694
2102
 
2695
2103
  return {
2696
2104
  id: alias_id,
@@ -2750,23 +2158,19 @@ function create_setup_once_helper_split_returning_if_statements(
2750
2158
  return [
2751
2159
  ...branch_helper.setup_statements,
2752
2160
  ...continuation_helper.setup_statements,
2753
- {
2754
- type: 'ReturnStatement',
2755
- argument: combine_render_return_argument(
2161
+ b.return(
2162
+ combine_render_return_argument(
2756
2163
  render_nodes,
2757
2164
  set_loc(
2758
- /** @type {any} */ ({
2759
- type: 'ConditionalExpression',
2760
- test: node.test,
2761
- consequent: branch_helper.component_element,
2762
- alternate: continuation_helper.component_element,
2763
- metadata: { path: [] },
2764
- }),
2165
+ b.conditional(
2166
+ node.test,
2167
+ branch_helper.component_element,
2168
+ continuation_helper.component_element,
2169
+ ),
2765
2170
  node,
2766
2171
  ),
2767
2172
  ),
2768
- metadata: { path: [] },
2769
- },
2173
+ ),
2770
2174
  ];
2771
2175
  }
2772
2176
 
@@ -3038,25 +2442,7 @@ function statement_body_to_jsx_child(body_nodes, transform_context) {
3038
2442
  }
3039
2443
 
3040
2444
  return to_jsx_expression_container(
3041
- /** @type {any} */ ({
3042
- type: 'CallExpression',
3043
- callee: {
3044
- type: 'ArrowFunctionExpression',
3045
- params: [],
3046
- body: /** @type {any} */ ({
3047
- type: 'BlockStatement',
3048
- body: build_render_statements(body_nodes, true, transform_context),
3049
- metadata: { path: [] },
3050
- }),
3051
- async: false,
3052
- generator: false,
3053
- expression: false,
3054
- metadata: { path: [] },
3055
- },
3056
- arguments: [],
3057
- optional: false,
3058
- metadata: { path: [] },
3059
- }),
2445
+ b.call(b.arrow([], b.block(build_render_statements(body_nodes, true, transform_context)))),
3060
2446
  );
3061
2447
  }
3062
2448
 
@@ -3107,11 +2493,7 @@ function hook_safe_render_statements(body_nodes, key_expression, transform_conte
3107
2493
  );
3108
2494
  const statements = [...helper.setup_statements];
3109
2495
 
3110
- statements.push({
3111
- type: 'ReturnStatement',
3112
- argument: helper.component_element,
3113
- metadata: { path: [] },
3114
- });
2496
+ statements.push(b.return(helper.component_element));
3115
2497
 
3116
2498
  return statements;
3117
2499
  }
@@ -3365,7 +2747,7 @@ function validate_hook_outer_assignments_in_node(
3365
2747
  );
3366
2748
  if (outer_names.length > 0) {
3367
2749
  report_hook_outer_assignment_error(
3368
- node,
2750
+ node.left,
3369
2751
  outer_names,
3370
2752
  find_first_hook_call_name(node.right) || 'hook',
3371
2753
  transform_context,
@@ -3389,7 +2771,7 @@ function validate_hook_outer_assignments_in_node(
3389
2771
  );
3390
2772
  if (outer_names.length > 0) {
3391
2773
  report_hook_outer_assignment_error(
3392
- node,
2774
+ node.left,
3393
2775
  outer_names,
3394
2776
  find_first_hook_call_name(node.right) || 'hook',
3395
2777
  transform_context,
@@ -3557,7 +2939,12 @@ function validate_hook_callback_outer_mutations_in_node(
3557
2939
  shadowed_names,
3558
2940
  );
3559
2941
  if (outer_names.length > 0) {
3560
- report_hook_callback_outer_mutation_error(node, outer_names, hook_name, transform_context);
2942
+ report_hook_callback_outer_mutation_error(
2943
+ node.left,
2944
+ outer_names,
2945
+ hook_name,
2946
+ transform_context,
2947
+ );
3561
2948
  }
3562
2949
  }
3563
2950
 
@@ -3568,7 +2955,12 @@ function validate_hook_callback_outer_mutations_in_node(
3568
2955
  shadowed_names,
3569
2956
  );
3570
2957
  if (outer_names.length > 0) {
3571
- report_hook_callback_outer_mutation_error(node, outer_names, hook_name, transform_context);
2958
+ report_hook_callback_outer_mutation_error(
2959
+ node.argument,
2960
+ outer_names,
2961
+ hook_name,
2962
+ transform_context,
2963
+ );
3572
2964
  }
3573
2965
  }
3574
2966
 
@@ -3863,23 +3255,13 @@ function create_hook_safe_helper(
3863
3255
  const saved_bindings = transform_context.available_bindings;
3864
3256
  transform_context.available_bindings = new Map(saved_bindings);
3865
3257
 
3866
- const helper_fn = /** @type {any} */ ({
3867
- type: 'FunctionExpression',
3868
- id: clone_identifier(component_id),
3258
+ const helper_fn = b.function(
3259
+ clone_identifier(component_id),
3869
3260
  params,
3870
- body: {
3871
- type: 'BlockStatement',
3872
- body: build_render_statements(body_nodes, true, transform_context),
3873
- metadata: { path: [] },
3874
- },
3875
- async: false,
3876
- generator: false,
3877
- metadata: {
3878
- path: [],
3879
- is_component: true,
3880
- is_method: false,
3881
- },
3882
- });
3261
+ b.block(build_render_statements(body_nodes, true, transform_context)),
3262
+ );
3263
+ helper_fn.metadata.is_component = true;
3264
+ helper_fn.metadata.is_method = false;
3883
3265
 
3884
3266
  transform_context.available_bindings = saved_bindings;
3885
3267
 
@@ -3945,7 +3327,7 @@ function create_hook_safe_helper(
3945
3327
 
3946
3328
  /**
3947
3329
  * @param {AST.Identifier} helper_id
3948
- * @param {any} helper_fn
3330
+ * @param {AST.FunctionExpression} helper_fn
3949
3331
  * @param {any} source_node
3950
3332
  * @param {TransformContext} transform_context
3951
3333
  * @returns {any}
@@ -3958,7 +3340,7 @@ function create_helper_declaration(helper_id, helper_fn, source_node, transform_
3958
3340
 
3959
3341
  /**
3960
3342
  * @param {AST.Identifier} helper_id
3961
- * @param {any} helper_fn
3343
+ * @param {AST.FunctionExpression} helper_fn
3962
3344
  * @param {any} source_node
3963
3345
  * @param {TransformContext} transform_context
3964
3346
  * @returns {any}
@@ -3987,32 +3369,7 @@ function create_helper_init_expression(helper_id, helper_fn, source_node, transf
3987
3369
  * @returns {any}
3988
3370
  */
3989
3371
  function create_hook_safe_helper_iife(setup_statements, component_element) {
3990
- return /** @type {any} */ ({
3991
- type: 'CallExpression',
3992
- callee: {
3993
- type: 'ArrowFunctionExpression',
3994
- params: [],
3995
- body: /** @type {any} */ ({
3996
- type: 'BlockStatement',
3997
- body: [
3998
- ...setup_statements,
3999
- {
4000
- type: 'ReturnStatement',
4001
- argument: component_element,
4002
- metadata: { path: [] },
4003
- },
4004
- ],
4005
- metadata: { path: [] },
4006
- }),
4007
- async: false,
4008
- generator: false,
4009
- expression: false,
4010
- metadata: { path: [] },
4011
- },
4012
- arguments: [],
4013
- optional: false,
4014
- metadata: { path: [] },
4015
- });
3372
+ return b.call(b.arrow([], b.block([...setup_statements, b.return(component_element)])));
4016
3373
  }
4017
3374
 
4018
3375
  /**
@@ -4025,19 +3382,7 @@ function create_helper_type_alias_declaration(helper_id, binding) {
4025
3382
 
4026
3383
  return {
4027
3384
  id: alias_id,
4028
- declaration: /** @type {any} */ ({
4029
- type: 'VariableDeclaration',
4030
- kind: 'const',
4031
- declarations: [
4032
- {
4033
- type: 'VariableDeclarator',
4034
- id: clone_identifier(alias_id),
4035
- init: create_generated_identifier(binding.name),
4036
- metadata: { path: [] },
4037
- },
4038
- ],
4039
- metadata: { path: [] },
4040
- }),
3385
+ declaration: b.const(clone_identifier(alias_id), create_generated_identifier(binding.name)),
4041
3386
  };
4042
3387
  }
4043
3388
 
@@ -4047,33 +3392,14 @@ function create_helper_type_alias_declaration(helper_id, binding) {
4047
3392
  * @returns {any}
4048
3393
  */
4049
3394
  function create_helper_props_type_literal(bindings, aliases) {
4050
- return /** @type {any} */ ({
4051
- type: 'TSTypeLiteral',
4052
- members: bindings.map(
4053
- (binding, i) =>
4054
- /** @type {any} */ ({
4055
- type: 'TSPropertySignature',
4056
- key: create_generated_identifier(binding.name),
4057
- computed: false,
4058
- optional: false,
4059
- readonly: false,
4060
- static: false,
4061
- kind: 'init',
4062
- typeAnnotation: {
4063
- type: 'TSTypeAnnotation',
4064
- typeAnnotation: {
4065
- type: 'TSTypeQuery',
4066
- exprName: clone_identifier(aliases[i].id),
4067
- typeArguments: null,
4068
- metadata: { path: [] },
4069
- },
4070
- metadata: { path: [] },
4071
- },
4072
- metadata: { path: [] },
4073
- }),
3395
+ return b.ts_type_literal(
3396
+ bindings.map((binding, i) =>
3397
+ b.ts_property_signature(
3398
+ create_generated_identifier(binding.name),
3399
+ b.ts_type_annotation(b.ts_type_query(clone_identifier(aliases[i].id))),
3400
+ ),
4074
3401
  ),
4075
- metadata: { path: [] },
4076
- });
3402
+ );
4077
3403
  }
4078
3404
 
4079
3405
  /**
@@ -4083,11 +3409,7 @@ function create_helper_props_type_literal(bindings, aliases) {
4083
3409
  */
4084
3410
  function create_typed_helper_props_pattern(bindings, props_type) {
4085
3411
  const pattern = create_helper_props_pattern(bindings);
4086
- /** @type {any} */ (pattern).typeAnnotation = {
4087
- type: 'TSTypeAnnotation',
4088
- typeAnnotation: props_type,
4089
- metadata: { path: [] },
4090
- };
3412
+ /** @type {any} */ (pattern).typeAnnotation = b.ts_type_annotation(props_type);
4091
3413
  return pattern;
4092
3414
  }
4093
3415
 
@@ -4096,19 +3418,7 @@ function create_typed_helper_props_pattern(bindings, props_type) {
4096
3418
  * @returns {any}
4097
3419
  */
4098
3420
  function create_helper_cache_declaration(cache_id) {
4099
- return /** @type {any} */ ({
4100
- type: 'VariableDeclaration',
4101
- kind: 'let',
4102
- declarations: [
4103
- {
4104
- type: 'VariableDeclarator',
4105
- id: clone_identifier(cache_id),
4106
- init: null,
4107
- metadata: { path: [] },
4108
- },
4109
- ],
4110
- metadata: { path: [] },
4111
- });
3421
+ return b.let(clone_identifier(cache_id));
4112
3422
  }
4113
3423
 
4114
3424
  /**
@@ -4118,44 +3428,27 @@ function create_helper_cache_declaration(cache_id) {
4118
3428
  * @returns {any}
4119
3429
  */
4120
3430
  function create_cached_helper_declaration(helper_id, cache_id, helper_init) {
4121
- return /** @type {any} */ ({
4122
- type: 'VariableDeclaration',
4123
- kind: 'const',
4124
- declarations: [
4125
- {
4126
- type: 'VariableDeclarator',
4127
- id: clone_identifier(helper_id),
4128
- init: {
4129
- type: 'LogicalExpression',
4130
- operator: '??',
4131
- left: clone_identifier(cache_id),
4132
- right: {
4133
- type: 'AssignmentExpression',
4134
- operator: '=',
4135
- left: clone_identifier(cache_id),
4136
- right: helper_init,
4137
- metadata: { path: [] },
4138
- },
4139
- metadata: { path: [] },
4140
- },
4141
- metadata: { path: [] },
4142
- },
4143
- ],
4144
- metadata: { path: [] },
4145
- });
3431
+ return b.const(
3432
+ clone_identifier(helper_id),
3433
+ b.logical(
3434
+ '??',
3435
+ clone_identifier(cache_id),
3436
+ b.assignment('=', clone_identifier(cache_id), helper_init),
3437
+ ),
3438
+ );
4146
3439
  }
4147
3440
 
4148
3441
  /**
4149
3442
  * @param {AST.Identifier} helper_id
4150
- * @param {any} helper_fn
3443
+ * @param {AST.FunctionExpression} helper_fn
4151
3444
  * @returns {AST.FunctionDeclaration}
4152
3445
  */
4153
3446
  function create_helper_function_declaration_from_expression(helper_id, helper_fn) {
4154
- return /** @type {any} */ ({
3447
+ return {
4155
3448
  ...helper_fn,
4156
3449
  type: 'FunctionDeclaration',
4157
3450
  id: clone_identifier(helper_id),
4158
- });
3451
+ };
4159
3452
  }
4160
3453
 
4161
3454
  /**
@@ -4540,25 +3833,7 @@ function if_statement_to_jsx_child(node, transform_context) {
4540
3833
  }
4541
3834
 
4542
3835
  return to_jsx_expression_container(
4543
- /** @type {any} */ ({
4544
- type: 'CallExpression',
4545
- callee: {
4546
- type: 'ArrowFunctionExpression',
4547
- params: [],
4548
- body: /** @type {any} */ ({
4549
- type: 'BlockStatement',
4550
- body: [render_if_statement, create_null_return_statement()],
4551
- metadata: { path: [] },
4552
- }),
4553
- async: false,
4554
- generator: false,
4555
- expression: false,
4556
- metadata: { path: [] },
4557
- },
4558
- arguments: [],
4559
- optional: false,
4560
- metadata: { path: [] },
4561
- }),
3836
+ b.call(b.arrow([], b.block([render_if_statement, create_null_return_statement()]))),
4562
3837
  );
4563
3838
  }
4564
3839
 
@@ -4583,16 +3858,7 @@ function render_if_statement_to_conditional_expression(node) {
4583
3858
  }
4584
3859
  }
4585
3860
 
4586
- return set_loc(
4587
- /** @type {any} */ ({
4588
- type: 'ConditionalExpression',
4589
- test: node.test,
4590
- consequent,
4591
- alternate,
4592
- metadata: { path: [] },
4593
- }),
4594
- node,
4595
- );
3861
+ return set_loc(b.conditional(node.test, consequent, alternate), node);
4596
3862
  }
4597
3863
 
4598
3864
  /**
@@ -4660,17 +3926,10 @@ function find_key_expression_in_body(body_nodes) {
4660
3926
 
4661
3927
  /**
4662
3928
  * @param {any} source_node
4663
- * @returns {any}
4664
- */
4665
- function continue_to_bare_return(source_node) {
4666
- return set_loc(
4667
- /** @type {any} */ ({
4668
- type: 'ReturnStatement',
4669
- argument: null,
4670
- metadata: { path: [] },
4671
- }),
4672
- source_node,
4673
- );
3929
+ * @returns {any}
3930
+ */
3931
+ function continue_to_bare_return(source_node) {
3932
+ return set_loc(b.return(null), source_node);
4674
3933
  }
4675
3934
 
4676
3935
  /**
@@ -4796,36 +4055,17 @@ function for_of_statement_to_jsx_child(node, transform_context) {
4796
4055
  // Restore bindings
4797
4056
  transform_context.available_bindings = saved_bindings;
4798
4057
 
4058
+ const iter_callback = b.arrow(loop_params, b.block(body_statements));
4059
+
4060
+ if (transform_context.platform.imports.forOfIterableHelper) {
4061
+ transform_context.needs_for_of_iterable = true;
4062
+ return to_jsx_expression_container(
4063
+ b.call(b.id(MAP_ITERABLE_INTERNAL_NAME), node.right, iter_callback),
4064
+ );
4065
+ }
4066
+
4799
4067
  return to_jsx_expression_container(
4800
- /** @type {any} */ ({
4801
- type: 'CallExpression',
4802
- callee: {
4803
- type: 'MemberExpression',
4804
- object: node.right,
4805
- property: create_generated_identifier('map'),
4806
- computed: false,
4807
- optional: false,
4808
- metadata: { path: [] },
4809
- },
4810
- arguments: [
4811
- {
4812
- type: 'ArrowFunctionExpression',
4813
- params: loop_params,
4814
- body: /** @type {any} */ ({
4815
- type: 'BlockStatement',
4816
- body: body_statements,
4817
- metadata: { path: [] },
4818
- }),
4819
- async: false,
4820
- generator: false,
4821
- expression: false,
4822
- metadata: { path: [] },
4823
- },
4824
- ],
4825
- async: false,
4826
- optional: false,
4827
- metadata: { path: [] },
4828
- }),
4068
+ b.call(b.member(node.right, create_generated_identifier('map')), iter_callback),
4829
4069
  );
4830
4070
  }
4831
4071
 
@@ -4846,7 +4086,7 @@ function apply_key_to_loop_body(body_nodes, key_expression) {
4846
4086
  if (!has_key) {
4847
4087
  attributes.push({
4848
4088
  type: 'Attribute',
4849
- name: { type: 'Identifier', name: 'key', metadata: { path: [] } },
4089
+ name: b.id('key'),
4850
4090
  value: clone_expression_node(key_expression),
4851
4091
  shorthand: false,
4852
4092
  metadata: { path: [] },
@@ -4866,15 +4106,10 @@ function apply_key_to_loop_body(body_nodes, key_expression) {
4866
4106
 
4867
4107
  if (!has_key) {
4868
4108
  attributes.push(
4869
- /** @type {any} */ ({
4870
- type: 'JSXAttribute',
4871
- name: { type: 'JSXIdentifier', name: 'key', metadata: { path: [] } },
4872
- value: to_jsx_expression_container(
4873
- clone_expression_node(key_expression),
4874
- key_expression,
4875
- ),
4876
- metadata: { path: [] },
4877
- }),
4109
+ b.jsx_attribute(
4110
+ b.jsx_id('key'),
4111
+ to_jsx_expression_container(clone_expression_node(key_expression), key_expression),
4112
+ ),
4878
4113
  );
4879
4114
  }
4880
4115
  return;
@@ -4969,29 +4204,12 @@ function keyed_fragment_to_jsx_element(fragment, key_expression) {
4969
4204
  * @returns {ESTreeJSX.JSXExpressionContainer}
4970
4205
  */
4971
4206
  function switch_statement_to_jsx_child(node, transform_context) {
4207
+ const { setup_statements, switch_statement } = build_switch_with_lift(node, transform_context);
4208
+
4972
4209
  return to_jsx_expression_container(
4973
- /** @type {any} */ ({
4974
- type: 'CallExpression',
4975
- callee: {
4976
- type: 'ArrowFunctionExpression',
4977
- params: [],
4978
- body: /** @type {any} */ ({
4979
- type: 'BlockStatement',
4980
- body: [
4981
- create_render_switch_statement(node, transform_context),
4982
- create_null_return_statement(),
4983
- ],
4984
- metadata: { path: [] },
4985
- }),
4986
- async: false,
4987
- generator: false,
4988
- expression: false,
4989
- metadata: { path: [] },
4990
- },
4991
- arguments: [],
4992
- optional: false,
4993
- metadata: { path: [] },
4994
- }),
4210
+ b.call(
4211
+ b.arrow([], b.block([...setup_statements, switch_statement, create_null_return_statement()])),
4212
+ ),
4995
4213
  );
4996
4214
  }
4997
4215
 
@@ -5131,19 +4349,10 @@ function try_statement_to_jsx_child(node, transform_context) {
5131
4349
  catch_scoped_names,
5132
4350
  );
5133
4351
 
5134
- const fallback_fn = {
5135
- type: 'ArrowFunctionExpression',
5136
- params: catch_params,
5137
- body: /** @type {any} */ ({
5138
- type: 'BlockStatement',
5139
- body: build_render_statements(catch_body_nodes, true, transform_context),
5140
- metadata: { path: [] },
5141
- }),
5142
- async: false,
5143
- generator: false,
5144
- expression: false,
5145
- metadata: { path: [] },
5146
- };
4352
+ const fallback_fn = b.arrow(
4353
+ catch_params,
4354
+ b.block(build_render_statements(catch_body_nodes, true, transform_context)),
4355
+ );
5147
4356
 
5148
4357
  transform_context.available_bindings = saved_catch_bindings;
5149
4358
 
@@ -5156,40 +4365,10 @@ function try_statement_to_jsx_child(node, transform_context) {
5156
4365
 
5157
4366
  if (boundary_content && transform_context.inside_element_child) {
5158
4367
  result = to_jsx_expression_container(
5159
- /** @type {any} */ ({
5160
- type: 'CallExpression',
5161
- callee: { type: 'Identifier', name: 'TsrxErrorBoundary', metadata: { path: [] } },
5162
- arguments: [
5163
- {
5164
- type: 'ObjectExpression',
5165
- properties: [
5166
- {
5167
- type: 'Property',
5168
- key: { type: 'Identifier', name: 'fallback', metadata: { path: [] } },
5169
- value: fallback_fn,
5170
- kind: 'init',
5171
- method: false,
5172
- shorthand: false,
5173
- computed: false,
5174
- metadata: { path: [] },
5175
- },
5176
- {
5177
- type: 'Property',
5178
- key: { type: 'Identifier', name: 'content', metadata: { path: [] } },
5179
- value: boundary_content,
5180
- kind: 'init',
5181
- method: false,
5182
- shorthand: false,
5183
- computed: false,
5184
- metadata: { path: [] },
5185
- },
5186
- ],
5187
- metadata: { path: [] },
5188
- },
5189
- ],
5190
- optional: false,
5191
- metadata: { path: [] },
5192
- }),
4368
+ b.call(
4369
+ 'TsrxErrorBoundary',
4370
+ b.object([b.init('fallback', fallback_fn), b.init('content', boundary_content)]),
4371
+ ),
5193
4372
  );
5194
4373
 
5195
4374
  return result;
@@ -5198,21 +4377,12 @@ function try_statement_to_jsx_child(node, transform_context) {
5198
4377
  result = create_jsx_element(
5199
4378
  'TsrxErrorBoundary',
5200
4379
  [
5201
- {
5202
- type: 'JSXAttribute',
5203
- name: { type: 'JSXIdentifier', name: 'fallback', metadata: { path: [] } },
5204
- value: to_jsx_expression_container(/** @type {any} */ (fallback_fn)),
5205
- metadata: { path: [] },
5206
- },
4380
+ b.jsx_attribute(
4381
+ b.jsx_id('fallback'),
4382
+ to_jsx_expression_container(/** @type {any} */ (fallback_fn)),
4383
+ ),
5207
4384
  ...(boundary_content
5208
- ? [
5209
- {
5210
- type: 'JSXAttribute',
5211
- name: { type: 'JSXIdentifier', name: 'content', metadata: { path: [] } },
5212
- value: to_jsx_expression_container(boundary_content),
5213
- metadata: { path: [] },
5214
- },
5215
- ]
4385
+ ? [b.jsx_attribute(b.jsx_id('content'), to_jsx_expression_container(boundary_content))]
5216
4386
  : []),
5217
4387
  ],
5218
4388
  boundary_content ? [] : [result],
@@ -5261,73 +4431,29 @@ function inject_try_imports(program, transform_context, platform, suspense_sourc
5261
4431
  const imports = [];
5262
4432
 
5263
4433
  if (transform_context.needs_fragment && platform.imports.fragment) {
5264
- const fragment_source = platform.imports.fragment;
5265
- imports.push({
5266
- type: 'ImportDeclaration',
5267
- specifiers: [
5268
- {
5269
- type: 'ImportSpecifier',
5270
- imported: { type: 'Identifier', name: 'Fragment', metadata: { path: [] } },
5271
- local: { type: 'Identifier', name: 'Fragment', metadata: { path: [] } },
5272
- metadata: { path: [] },
5273
- },
5274
- ],
5275
- source: {
5276
- type: 'Literal',
5277
- value: fragment_source,
5278
- raw: `'${fragment_source}'`,
5279
- },
5280
- metadata: { path: [] },
5281
- });
4434
+ imports.push(b.imports([['Fragment', 'Fragment']], platform.imports.fragment));
5282
4435
  }
5283
4436
 
5284
4437
  if (transform_context.needs_suspense) {
5285
- imports.push({
5286
- type: 'ImportDeclaration',
5287
- specifiers: [
5288
- {
5289
- type: 'ImportSpecifier',
5290
- imported: { type: 'Identifier', name: 'Suspense', metadata: { path: [] } },
5291
- local: { type: 'Identifier', name: 'Suspense', metadata: { path: [] } },
5292
- metadata: { path: [] },
5293
- },
5294
- ],
5295
- source: {
5296
- type: 'Literal',
5297
- value: suspense_source,
5298
- raw: `'${suspense_source}'`,
5299
- },
5300
- metadata: { path: [] },
5301
- });
4438
+ imports.push(b.imports([['Suspense', 'Suspense']], suspense_source));
4439
+ }
4440
+
4441
+ if (transform_context.needs_for_of_iterable && platform.imports.forOfIterableHelper) {
4442
+ const specifiers = [b.import_specifier('map_iterable', MAP_ITERABLE_INTERNAL_NAME)];
4443
+ // The loop-scoped type alias `IterationValue<typeof source>` only
4444
+ // appears in the output when at least one hook-bearing for-of body
4445
+ // was lowered with non-module-scoped helpers (editor tooling sets
4446
+ // this for typeOnly virtual modules).
4447
+ if (transform_context.needs_iteration_value_type) {
4448
+ specifiers.push(b.import_specifier('IterationValue', ITERATION_VALUE_INTERNAL_NAME, 'type'));
4449
+ }
4450
+ imports.push(b.import_declaration(specifiers, platform.imports.forOfIterableHelper));
5302
4451
  }
5303
4452
 
5304
4453
  if (transform_context.needs_error_boundary) {
5305
- const error_boundary_source = platform.imports.errorBoundary;
5306
- imports.push({
5307
- type: 'ImportDeclaration',
5308
- specifiers: [
5309
- {
5310
- type: 'ImportSpecifier',
5311
- imported: {
5312
- type: 'Identifier',
5313
- name: 'TsrxErrorBoundary',
5314
- metadata: { path: [] },
5315
- },
5316
- local: {
5317
- type: 'Identifier',
5318
- name: 'TsrxErrorBoundary',
5319
- metadata: { path: [] },
5320
- },
5321
- metadata: { path: [] },
5322
- },
5323
- ],
5324
- source: {
5325
- type: 'Literal',
5326
- value: error_boundary_source,
5327
- raw: `'${error_boundary_source}'`,
5328
- },
5329
- metadata: { path: [] },
5330
- });
4454
+ imports.push(
4455
+ b.imports([['TsrxErrorBoundary', 'TsrxErrorBoundary']], platform.imports.errorBoundary),
4456
+ );
5331
4457
  }
5332
4458
 
5333
4459
  const merge_refs_source =
@@ -5345,67 +4471,31 @@ function inject_try_imports(program, transform_context, platform, suspense_sourc
5345
4471
  const ref_imports = new Map();
5346
4472
 
5347
4473
  if (merge_refs_source !== null) {
5348
- add_ref_import_specifier(ref_imports, merge_refs_source, {
5349
- type: 'ImportSpecifier',
5350
- imported: {
5351
- type: 'Identifier',
5352
- name: 'mergeRefs',
5353
- metadata: { path: [] },
5354
- },
5355
- local: {
5356
- type: 'Identifier',
5357
- name: MERGE_REFS_INTERNAL_NAME,
5358
- metadata: { path: [] },
5359
- },
5360
- metadata: { path: [] },
5361
- });
4474
+ add_ref_import_specifier(
4475
+ ref_imports,
4476
+ merge_refs_source,
4477
+ b.import_specifier('mergeRefs', MERGE_REFS_INTERNAL_NAME),
4478
+ );
5362
4479
  }
5363
4480
 
5364
4481
  if (ref_prop_source !== null) {
5365
- add_ref_import_specifier(ref_imports, ref_prop_source, {
5366
- type: 'ImportSpecifier',
5367
- imported: {
5368
- type: 'Identifier',
5369
- name: 'create_ref_prop',
5370
- metadata: { path: [] },
5371
- },
5372
- local: {
5373
- type: 'Identifier',
5374
- name: CREATE_REF_PROP_INTERNAL_NAME,
5375
- metadata: { path: [] },
5376
- },
5377
- metadata: { path: [] },
5378
- });
4482
+ add_ref_import_specifier(
4483
+ ref_imports,
4484
+ ref_prop_source,
4485
+ b.import_specifier('create_ref_prop', CREATE_REF_PROP_INTERNAL_NAME),
4486
+ );
5379
4487
  }
5380
4488
 
5381
4489
  if (normalize_spread_props_source !== null) {
5382
- add_ref_import_specifier(ref_imports, normalize_spread_props_source, {
5383
- type: 'ImportSpecifier',
5384
- imported: {
5385
- type: 'Identifier',
5386
- name: 'normalize_spread_props',
5387
- metadata: { path: [] },
5388
- },
5389
- local: {
5390
- type: 'Identifier',
5391
- name: NORMALIZE_SPREAD_PROPS_INTERNAL_NAME,
5392
- metadata: { path: [] },
5393
- },
5394
- metadata: { path: [] },
5395
- });
4490
+ add_ref_import_specifier(
4491
+ ref_imports,
4492
+ normalize_spread_props_source,
4493
+ b.import_specifier('normalize_spread_props', NORMALIZE_SPREAD_PROPS_INTERNAL_NAME),
4494
+ );
5396
4495
  }
5397
4496
 
5398
4497
  for (const [source, ref_specifiers] of ref_imports) {
5399
- imports.push({
5400
- type: 'ImportDeclaration',
5401
- specifiers: ref_specifiers,
5402
- source: {
5403
- type: 'Literal',
5404
- value: source,
5405
- raw: `'${source}'`,
5406
- },
5407
- metadata: { path: [] },
5408
- });
4498
+ imports.push(b.import_declaration(ref_specifiers, source));
5409
4499
  }
5410
4500
 
5411
4501
  if (imports.length > 0) {
@@ -5453,130 +4543,343 @@ function create_render_if_statement(node, transform_context) {
5453
4543
  true,
5454
4544
  );
5455
4545
  alternate = set_loc(
5456
- /** @type {any} */ ({
5457
- type: 'BlockStatement',
5458
- body: alternate_has_hooks
4546
+ b.block(
4547
+ alternate_has_hooks
5459
4548
  ? hook_safe_render_statements(alternate_body, undefined, transform_context)
5460
4549
  : build_render_statements(alternate_body, true, transform_context),
5461
- metadata: { path: [] },
5462
- }),
4550
+ ),
5463
4551
  node.alternate,
5464
4552
  );
5465
4553
  }
5466
4554
  }
5467
4555
 
5468
4556
  return set_loc(
5469
- {
5470
- type: 'IfStatement',
5471
- test: node.test,
5472
- consequent: set_loc(
5473
- /** @type {any} */ ({
5474
- type: 'BlockStatement',
5475
- body: consequent_has_hooks
4557
+ b.if(
4558
+ node.test,
4559
+ set_loc(
4560
+ b.block(
4561
+ consequent_has_hooks
5476
4562
  ? hook_safe_render_statements(consequent_body, undefined, transform_context)
5477
4563
  : build_render_statements(consequent_body, true, transform_context),
5478
- metadata: { path: [] },
5479
- }),
4564
+ ),
5480
4565
  node.consequent,
5481
4566
  ),
5482
4567
  alternate,
5483
- },
4568
+ ),
5484
4569
  node,
5485
4570
  );
5486
4571
  }
5487
4572
 
5488
4573
  /**
5489
- * @param {any} node
5490
- * @param {TransformContext} transform_context
5491
- * @returns {any}
4574
+ * Per-source-case information used by the switch lift to decide whether each
4575
+ * case body needs to be hoisted into its own helper component or can stay
4576
+ * inline.
4577
+ *
4578
+ * `own_body` is everything in the case's `consequent` up to (and including for
4579
+ * `return <expr>`, excluding for `break` / bare `return;`) the first
4580
+ * terminator. `has_terminator` records whether such a terminator was seen.
4581
+ *
4582
+ * @param {any[]} consequent
4583
+ * @returns {{ own_body: any[], has_terminator: boolean }}
5492
4584
  */
5493
- function create_render_switch_statement(node, transform_context) {
5494
- return /** @type {any} */ ({
5495
- type: 'SwitchStatement',
5496
- discriminant: node.discriminant,
5497
- cases: node.cases.map((/** @type {any} */ c) =>
5498
- create_render_switch_case(c, transform_context),
5499
- ),
5500
- metadata: { path: [] },
5501
- });
4585
+ function summarize_switch_case_body(consequent) {
4586
+ const own_body = [];
4587
+ let has_terminator = false;
4588
+ for (const child of consequent) {
4589
+ if (child.type === 'BreakStatement') {
4590
+ has_terminator = true;
4591
+ break;
4592
+ }
4593
+ if (child.type === 'ReturnStatement' && child.argument == null) {
4594
+ has_terminator = true;
4595
+ break;
4596
+ }
4597
+ own_body.push(child);
4598
+ if (child.type === 'ReturnStatement') {
4599
+ // `return <expr>;` — keep it in own_body so build_render_statements
4600
+ // can emit it as the terminal return for this case, then stop
4601
+ // collecting further nodes.
4602
+ has_terminator = true;
4603
+ break;
4604
+ }
4605
+ }
4606
+ return { own_body, has_terminator };
5502
4607
  }
5503
4608
 
5504
4609
  /**
5505
- * @param {any} switch_case
5506
- * @param {TransformContext} transform_context
4610
+ * Clone a helper's `component_element` for embedding in another case arm or
4611
+ * inside another helper's body. Locations are stripped because the same
4612
+ * element appears in multiple positions; only the helper's *definition* (the
4613
+ * lifted function) keeps the source position so editor IntelliSense doesn't
4614
+ * see double/triple hits per source range.
4615
+ *
4616
+ * @param {{ component_element: ESTreeJSX.JSXElement }} helper
5507
4617
  * @returns {any}
5508
4618
  */
5509
- function create_render_switch_case(switch_case, transform_context) {
5510
- const consequent = flatten_switch_consequent(switch_case.consequent || []);
4619
+ export function clone_switch_helper_invocation(helper) {
4620
+ return clone_expression_node(helper.component_element, false);
4621
+ }
5511
4622
 
5512
- // Strip trailing break statements for hook analysis
5513
- const body_without_break = [];
5514
- for (const child of consequent) {
5515
- if (child.type === 'BreakStatement') break;
5516
- body_without_break.push(child);
5517
- }
4623
+ /**
4624
+ * Plan the switch lift: decide which case bodies to hoist into their own
4625
+ * helper components, build them in reverse so each helper can chain into the
4626
+ * next, and return everything callers need to construct a target-specific
4627
+ * switch shape (a JS `switch` for React/Preact/Vue or `<Switch>/<Match>` for
4628
+ * Solid). Centralizes the lift bookkeeping so both consumers see the same
4629
+ * hook-detection rules, duplication analysis, and helper-id numbering.
4630
+ *
4631
+ * Returned helpers — when non-null — are already constructed via
4632
+ * `create_hook_safe_helper`, which is the same path hook-bearing case bodies
4633
+ * have always used. Locally-scoped helpers have their declarations in
4634
+ * `setup_statements`; module-scoped helpers (the client transform default on
4635
+ * React, Vue, and Solid) already pushed their declarations into
4636
+ * `transform_context.helper_state.helpers`, so `setup_statements` is empty.
4637
+ *
4638
+ * @param {any} switch_node
4639
+ * @param {TransformContext} transform_context
4640
+ * @returns {{
4641
+ * case_info: Array<{ own_body: any[], has_terminator: boolean }>,
4642
+ * case_helpers: Array<{ setup_statements: any[], component_element: ESTreeJSX.JSXElement } | null>,
4643
+ * find_next_helper_after: (from_index: number) => { component_element: ESTreeJSX.JSXElement } | null,
4644
+ * setup_statements: any[],
4645
+ * }}
4646
+ */
4647
+ export function plan_switch_lift(switch_node, transform_context) {
4648
+ const case_info = switch_node.cases.map((/** @type {any} */ c) => {
4649
+ const consequent = flatten_switch_consequent(c.consequent || []);
4650
+ return summarize_switch_case_body(consequent);
4651
+ });
5518
4652
 
5519
- if (body_contains_top_level_hook_call(body_without_break, transform_context, true)) {
5520
- return /** @type {any} */ ({
5521
- type: 'SwitchCase',
5522
- test: switch_case.test,
5523
- consequent: hook_safe_render_statements(body_without_break, undefined, transform_context),
5524
- metadata: { path: [] },
5525
- });
5526
- }
4653
+ // A case body needs to be lifted iff (a) it would render in more than one
4654
+ // arm after fall-through expansion, or (b) it contains hooks (which always
4655
+ // went through the lift pipeline before this change). Duplication happens
4656
+ // exactly when the previous case has no terminator — that's the only way
4657
+ // an earlier arm can reach this body via JS fall-through semantics.
4658
+ const needs_helper = case_info.map(
4659
+ (/** @type {{ own_body: any[], has_terminator: boolean }} */ info, /** @type {number} */ k) => {
4660
+ if (info.own_body.length === 0) return false;
4661
+ if (body_contains_top_level_hook_call(info.own_body, transform_context, true)) {
4662
+ return true;
4663
+ }
4664
+ if (k === 0) return false;
4665
+ return !case_info[k - 1].has_terminator;
4666
+ },
4667
+ );
5527
4668
 
5528
- const case_body = [];
5529
- const render_nodes = [];
5530
- let has_terminal = false;
4669
+ // Pre-allocate helper ids in source order so the snapshot's
4670
+ // `StatementBodyHook<N>` numbering reads top-to-bottom by case position
4671
+ // even though we build helpers in reverse below.
4672
+ /** @type {Array<AST.Identifier | null>} */
4673
+ const helper_ids = needs_helper.map((/** @type {boolean} */ needs) =>
4674
+ needs
4675
+ ? create_generated_identifier(create_local_statement_component_name(transform_context))
4676
+ : null,
4677
+ );
5531
4678
 
5532
- for (const child of consequent) {
5533
- if (child.type === 'BreakStatement') {
5534
- if (render_nodes.length > 0 && !has_terminal) {
5535
- case_body.push(create_component_return_statement(render_nodes, switch_case));
5536
- } else if (!has_terminal) {
5537
- case_body.push(child);
5538
- }
5539
- has_terminal = true;
5540
- break;
5541
- }
4679
+ /** @type {Array<{ setup_statements: any[], component_element: ESTreeJSX.JSXElement } | null>} */
4680
+ const case_helpers = new Array(switch_node.cases.length).fill(null);
5542
4681
 
5543
- if (is_bare_return_statement(child)) {
5544
- case_body.push(create_component_return_statement(render_nodes, child));
5545
- has_terminal = true;
5546
- break;
4682
+ /**
4683
+ * Find the next downstream helper this arm chains into when it has no
4684
+ * terminator: scan forward past any empty cases until we hit either a
4685
+ * helper-bearing case or a case whose body has a terminator (which stops
4686
+ * the chain — JS would have `break`/`return`ed out at that point).
4687
+ *
4688
+ * @param {number} from_index
4689
+ * @returns {{ component_element: ESTreeJSX.JSXElement } | null}
4690
+ */
4691
+ function find_next_helper_after(from_index) {
4692
+ for (let j = from_index + 1; j < switch_node.cases.length; j++) {
4693
+ if (case_helpers[j]) return case_helpers[j];
4694
+ if (case_info[j].has_terminator) return null;
5547
4695
  }
4696
+ return null;
4697
+ }
5548
4698
 
5549
- if (is_jsx_child(child)) {
5550
- render_nodes.push(to_jsx_child(child, transform_context));
5551
- } else if (is_bare_render_expression(child)) {
5552
- render_nodes.push(to_jsx_expression_container(child, child));
5553
- } else {
5554
- case_body.push(child);
4699
+ for (let i = switch_node.cases.length - 1; i >= 0; i--) {
4700
+ if (!needs_helper[i]) continue;
4701
+ const { own_body, has_terminator } = case_info[i];
4702
+
4703
+ let helper_body = own_body;
4704
+ if (!has_terminator) {
4705
+ const next_helper = find_next_helper_after(i);
4706
+ if (next_helper) {
4707
+ helper_body = [...own_body, clone_switch_helper_invocation(next_helper)];
4708
+ }
5555
4709
  }
4710
+
4711
+ case_helpers[i] = create_hook_safe_helper(
4712
+ helper_body,
4713
+ undefined,
4714
+ switch_node.cases[i],
4715
+ transform_context,
4716
+ /** @type {any} */ (helper_ids[i]),
4717
+ );
5556
4718
  }
5557
4719
 
5558
- if (!has_terminal && render_nodes.length > 0) {
5559
- case_body.push(create_component_return_statement(render_nodes, switch_case));
4720
+ // Hoist all helpers' setup statements above the switch in source order so
4721
+ // the switch body stays a pure dispatcher.
4722
+ const setup_statements = [];
4723
+ for (const helper of case_helpers) {
4724
+ if (helper) setup_statements.push(...helper.setup_statements);
5560
4725
  }
5561
4726
 
5562
- return /** @type {any} */ ({
5563
- type: 'SwitchCase',
5564
- test: switch_case.test,
5565
- consequent: case_body,
5566
- metadata: { path: [] },
5567
- });
4727
+ return {
4728
+ case_info,
4729
+ case_helpers,
4730
+ find_next_helper_after,
4731
+ setup_statements,
4732
+ };
5568
4733
  }
5569
4734
 
5570
4735
  /**
5571
- * @returns {any}
4736
+ * Switch lift for fall-through deduplication. Reuses the same `create_hook_safe_helper`
4737
+ * pipeline as hook-bearing case bodies: every case whose body would otherwise
4738
+ * appear in 2+ arms (because the previous case had no `break` / `return`) is
4739
+ * hoisted into its own helper component, and each upstream arm references the
4740
+ * next helper at the end of its own body to materialize JS fall-through at
4741
+ * render time. Cases whose bodies live in exactly one arm stay inline so the
4742
+ * common (break-terminated) shape compiles to the same simple switch as before
4743
+ * the lift was introduced.
4744
+ *
4745
+ * The chain pattern:
4746
+ * helper_idle = () => <><Online/><Helper_active/></>
4747
+ * helper_active = () => <><Away/><Helper_offline/></>
4748
+ * helper_offline = () => <Offline/>
4749
+ *
4750
+ * case "idle": return <Helper_idle/>
4751
+ * case "active": return <Helper_active/>
4752
+ * case "offline": return <Helper_offline/>
4753
+ *
4754
+ * Each case body appears exactly once in the generated module — matching how
4755
+ * we already handle hook-bearing case bodies — which keeps the bundle from
4756
+ * growing quadratically in case count and means editor mappings are 1:1.
4757
+ *
4758
+ * @param {any} switch_node
4759
+ * @param {TransformContext} transform_context
4760
+ * @returns {{ setup_statements: any[], switch_statement: any }}
5572
4761
  */
5573
- function create_null_return_statement() {
4762
+ function build_switch_with_lift(switch_node, transform_context) {
4763
+ const { case_info, case_helpers, find_next_helper_after, setup_statements } = plan_switch_lift(
4764
+ switch_node,
4765
+ transform_context,
4766
+ );
4767
+
4768
+ const new_cases = switch_node.cases.map(
4769
+ (/** @type {any} */ original_case, /** @type {number} */ i) => {
4770
+ const helper = case_helpers[i];
4771
+ if (helper) {
4772
+ return /** @type {any} */ ({
4773
+ type: 'SwitchCase',
4774
+ test: original_case.test,
4775
+ consequent: [
4776
+ create_component_return_statement([helper.component_element], original_case),
4777
+ ],
4778
+ metadata: { path: [] },
4779
+ });
4780
+ }
4781
+
4782
+ const { own_body, has_terminator } = case_info[i];
4783
+
4784
+ if (own_body.length === 0 && !has_terminator) {
4785
+ // Alias-pattern empty case (`case 'a': case 'b': ...`) — keep
4786
+ // the arm body empty so JS falls through to the next case at
4787
+ // runtime, where the helper invocation actually lives.
4788
+ return /** @type {any} */ ({
4789
+ type: 'SwitchCase',
4790
+ test: original_case.test,
4791
+ consequent: [],
4792
+ metadata: { path: [] },
4793
+ });
4794
+ }
4795
+
4796
+ const case_body = [];
4797
+ const render_nodes = [];
4798
+ let has_terminal = false;
4799
+
4800
+ for (const child of own_body) {
4801
+ if (is_bare_return_statement(child)) {
4802
+ case_body.push(create_component_return_statement(render_nodes, child));
4803
+ has_terminal = true;
4804
+ break;
4805
+ }
4806
+ if (child.type === 'ReturnStatement') {
4807
+ case_body.push(child);
4808
+ has_terminal = true;
4809
+ break;
4810
+ }
4811
+ if (is_jsx_child(child)) {
4812
+ render_nodes.push(to_jsx_child(child, transform_context));
4813
+ } else if (is_bare_render_expression(child)) {
4814
+ render_nodes.push(to_jsx_expression_container(child, child));
4815
+ } else {
4816
+ case_body.push(child);
4817
+ }
4818
+ }
4819
+
4820
+ if (!has_terminal && !has_terminator) {
4821
+ const next_helper = find_next_helper_after(i);
4822
+ if (next_helper) {
4823
+ render_nodes.push(clone_switch_helper_invocation(next_helper));
4824
+ }
4825
+ }
4826
+
4827
+ if (!has_terminal) {
4828
+ if (render_nodes.length > 0) {
4829
+ case_body.push(create_component_return_statement(render_nodes, original_case));
4830
+ } else if (has_terminator) {
4831
+ // Empty body with explicit `break;` / bare `return;` — keep
4832
+ // a `break` so JS doesn't fall through into the next case
4833
+ // (which may now hold the lifted helper invocation).
4834
+ case_body.push(
4835
+ /** @type {any} */ ({
4836
+ type: 'BreakStatement',
4837
+ label: null,
4838
+ metadata: { path: [] },
4839
+ }),
4840
+ );
4841
+ } else if (case_body.length > 0) {
4842
+ // Statements-only inline case without terminator. We've
4843
+ // already inlined the downstream chain via the helper
4844
+ // reference above, so emit a `break` to stop the runtime
4845
+ // from re-running downstream statements via JS fall-through.
4846
+ case_body.push(
4847
+ /** @type {any} */ ({
4848
+ type: 'BreakStatement',
4849
+ label: null,
4850
+ metadata: { path: [] },
4851
+ }),
4852
+ );
4853
+ }
4854
+ }
4855
+
4856
+ return /** @type {any} */ ({
4857
+ type: 'SwitchCase',
4858
+ test: original_case.test,
4859
+ consequent: case_body,
4860
+ metadata: { path: [] },
4861
+ });
4862
+ },
4863
+ );
4864
+
5574
4865
  return {
5575
- type: 'ReturnStatement',
5576
- argument: { type: 'Literal', value: null, raw: 'null' },
4866
+ setup_statements,
4867
+ switch_statement: /** @type {any} */ ({
4868
+ type: 'SwitchStatement',
4869
+ discriminant: switch_node.discriminant,
4870
+ cases: new_cases,
4871
+ metadata: { path: [] },
4872
+ }),
5577
4873
  };
5578
4874
  }
5579
4875
 
4876
+ /**
4877
+ * @returns {any}
4878
+ */
4879
+ function create_null_return_statement() {
4880
+ return b.return(b.literal(null));
4881
+ }
4882
+
5580
4883
  /**
5581
4884
  * @param {AST.Expression} expression
5582
4885
  * @param {any} [source_node]
@@ -5675,10 +4978,7 @@ function normalize_named_ref_attributes(attrs, is_host, transform_context) {
5675
4978
  return {
5676
4979
  ...attr,
5677
4980
  metadata: { ...(attr.metadata || {}), from_ref_keyword: true },
5678
- name:
5679
- attr.name?.type === 'JSXIdentifier'
5680
- ? { ...attr.name, name: 'ref' }
5681
- : { type: 'Identifier', name: 'ref', metadata: { path: [] } },
4981
+ name: attr.name?.type === 'JSXIdentifier' ? { ...attr.name, name: 'ref' } : b.id('ref'),
5682
4982
  };
5683
4983
  });
5684
4984
  }
@@ -5819,6 +5119,7 @@ function wrap_jsx_setup_declarations(expression, in_jsx_child) {
5819
5119
  [],
5820
5120
  b.block([...declarations, b.return(return_expression)], expression),
5821
5121
  false,
5122
+ undefined,
5822
5123
  expression,
5823
5124
  ),
5824
5125
  );
@@ -5970,22 +5271,8 @@ export function merge_duplicate_refs(jsx_attrs, transform_context) {
5970
5271
 
5971
5272
  const merged_value =
5972
5273
  strategy === 'merge-refs'
5973
- ? /** @type {any} */ ({
5974
- type: 'CallExpression',
5975
- callee: {
5976
- type: 'Identifier',
5977
- name: MERGE_REFS_INTERNAL_NAME,
5978
- metadata: { path: [] },
5979
- },
5980
- arguments: ref_exprs,
5981
- optional: false,
5982
- metadata: { path: [] },
5983
- })
5984
- : /** @type {any} */ ({
5985
- type: 'ArrayExpression',
5986
- elements: ref_exprs,
5987
- metadata: { path: [] },
5988
- });
5274
+ ? b.call(b.id(MERGE_REFS_INTERNAL_NAME), ...ref_exprs)
5275
+ : b.array(ref_exprs);
5989
5276
 
5990
5277
  if (strategy === 'merge-refs') {
5991
5278
  transform_context.needs_merge_refs = true;
@@ -5999,11 +5286,7 @@ export function merge_duplicate_refs(jsx_attrs, transform_context) {
5999
5286
  const merged_name = build_jsx_id('ref', source_attr?.name);
6000
5287
  const merged_attr = build_jsx_attribute(
6001
5288
  merged_name,
6002
- /** @type {any} */ ({
6003
- type: 'JSXExpressionContainer',
6004
- expression: merged_value,
6005
- metadata: { path: [] },
6006
- }),
5289
+ b.jsx_expression_container(merged_value),
6007
5290
  false,
6008
5291
  source_attr,
6009
5292
  );
@@ -6038,6 +5321,8 @@ function is_jsx_ref_attribute(attr) {
6038
5321
  export const MERGE_REFS_INTERNAL_NAME = '__mergeRefs';
6039
5322
  export const CREATE_REF_PROP_INTERNAL_NAME = '__create_ref_prop';
6040
5323
  export const NORMALIZE_SPREAD_PROPS_INTERNAL_NAME = '__normalize_spread_props';
5324
+ export const MAP_ITERABLE_INTERNAL_NAME = '__map_iterable';
5325
+ export const ITERATION_VALUE_INTERNAL_NAME = '__IterationValue';
6041
5326
 
6042
5327
  /**
6043
5328
  * @param {any} attr
@@ -6111,10 +5396,7 @@ export function to_jsx_attribute(attr, transform_context) {
6111
5396
  attr_name.type === 'Identifier' &&
6112
5397
  attr_name.name === 'class'
6113
5398
  ) {
6114
- attr_name = set_loc(
6115
- /** @type {any} */ ({ type: 'Identifier', name: 'className', metadata: { path: [] } }),
6116
- attr.name,
6117
- );
5399
+ attr_name = set_loc(b.id('className'), attr.name);
6118
5400
  }
6119
5401
 
6120
5402
  const name =
@@ -6177,16 +5459,7 @@ function create_ref_prop_call(node, transform_context) {
6177
5459
 
6178
5460
  if (argument.type === 'Identifier' || argument.type === 'MemberExpression') {
6179
5461
  args.push(
6180
- b.arrow(
6181
- [b.id('v')],
6182
- /** @type {any} */ ({
6183
- type: 'AssignmentExpression',
6184
- operator: '=',
6185
- left: clone_expression_node(argument, false),
6186
- right: b.id('v'),
6187
- metadata: { path: [] },
6188
- }),
6189
- ),
5462
+ b.arrow([b.id('v')], b.assignment('=', clone_expression_node(argument, false), b.id('v'))),
6190
5463
  );
6191
5464
  }
6192
5465
 
@@ -6200,57 +5473,19 @@ function create_ref_prop_call(node, transform_context) {
6200
5473
  */
6201
5474
  function dynamic_element_to_jsx_child(node, transform_context) {
6202
5475
  const dynamic_id = set_loc(create_generated_identifier('DynamicElement'), node.id);
6203
- const alias_declaration = set_loc(
6204
- /** @type {any} */ ({
6205
- type: 'VariableDeclaration',
6206
- kind: 'const',
6207
- declarations: [
6208
- {
6209
- type: 'VariableDeclarator',
6210
- id: dynamic_id,
6211
- init: clone_expression_node(node.id),
6212
- metadata: { path: [] },
6213
- },
6214
- ],
6215
- metadata: { path: [] },
6216
- }),
6217
- node,
6218
- );
5476
+ const alias_declaration = set_loc(b.const(dynamic_id, clone_expression_node(node.id)), node);
6219
5477
  const jsx_element = create_dynamic_jsx_element(dynamic_id, node, transform_context);
6220
5478
 
6221
5479
  return to_jsx_expression_container(
6222
- /** @type {any} */ ({
6223
- type: 'CallExpression',
6224
- callee: {
6225
- type: 'ArrowFunctionExpression',
6226
- params: [],
6227
- body: /** @type {any} */ ({
6228
- type: 'BlockStatement',
6229
- body: [
6230
- alias_declaration,
6231
- {
6232
- type: 'ReturnStatement',
6233
- argument: {
6234
- type: 'ConditionalExpression',
6235
- test: clone_identifier(dynamic_id),
6236
- consequent: jsx_element,
6237
- alternate: create_null_literal(),
6238
- metadata: { path: [] },
6239
- },
6240
- metadata: { path: [] },
6241
- },
6242
- ],
6243
- metadata: { path: [] },
6244
- }),
6245
- async: false,
6246
- generator: false,
6247
- expression: false,
6248
- metadata: { path: [] },
6249
- },
6250
- arguments: [],
6251
- optional: false,
6252
- metadata: { path: [] },
6253
- }),
5480
+ b.call(
5481
+ b.arrow(
5482
+ [],
5483
+ b.block([
5484
+ alias_declaration,
5485
+ b.return(b.conditional(clone_identifier(dynamic_id), jsx_element, create_null_literal())),
5486
+ ]),
5487
+ ),
5488
+ ),
6254
5489
  node,
6255
5490
  );
6256
5491
  }