@tsrx/core 0.1.18 → 0.1.20

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.
@@ -8,8 +8,8 @@ import { error } from '../../errors.js';
8
8
  import { analyze_css } from '../../analyze/css-analyze.js';
9
9
  import { prune_css } from '../../analyze/prune.js';
10
10
  import {
11
- ensure_function_metadata,
12
11
  in_jsx_child_context,
12
+ set_node_path_metadata,
13
13
  tsx_node_to_jsx_expression,
14
14
  tsx_with_ts_locations,
15
15
  } from './helpers.js';
@@ -62,7 +62,7 @@ const HOOK_OUTER_ASSIGNMENT_ERROR =
62
62
  const HOOK_CALLBACK_OUTER_MUTATION_ERROR =
63
63
  'Hook callbacks inside conditional or repeated TSRX scopes must not mutate bindings declared outside the generated hook component.';
64
64
  const TEMPLATE_FRAGMENT_ERROR =
65
- 'JSX fragment syntax is not needed in TSRX templates. TSRX renders in immediate mode, so everything is already a fragment. Use `<>...</>` only within <tsx>...</tsx>.';
65
+ 'JSX fragment syntax is not needed in TSRX templates. TSRX renders in immediate mode, so everything is already a fragment. Use `<>...</>` only in expression position.';
66
66
 
67
67
  /**
68
68
  * @param {AST.Node} node
@@ -169,6 +169,8 @@ export function createJsxTransform(platform) {
169
169
  const collect = !!(options?.collect || options?.loose);
170
170
  /** @type {any[]} */
171
171
  const stylesheets = [];
172
+ /** @type {AST.Statement[]} */
173
+ const type_only_style_anchors = [];
172
174
 
173
175
  /** @type {TransformContext} */
174
176
  const transform_context = {
@@ -183,6 +185,7 @@ export function createJsxTransform(platform) {
183
185
  needs_for_of_iterable: false,
184
186
  needs_iteration_value_type: false,
185
187
  stylesheets,
188
+ type_only_style_anchors,
186
189
  module_scoped_hook_components:
187
190
  options?.moduleScopedHookComponents ?? !!platform.hooks?.moduleScopedHookComponents,
188
191
  helper_state: null,
@@ -204,42 +207,30 @@ export function createJsxTransform(platform) {
204
207
  preallocate_lazy_ids(/** @type {any} */ (ast), transform_context);
205
208
  }
206
209
 
207
- walk(/** @type {any} */ (ast), transform_context, {
208
- FunctionDeclaration: collect_native_function_tsrx_metadata,
209
- FunctionExpression: collect_native_function_tsrx_metadata,
210
- ArrowFunctionExpression: collect_native_function_tsrx_metadata,
211
- });
212
-
213
210
  const transformed = walk(/** @type {any} */ (ast), transform_context, {
214
- Tsx(node, { next, path }) {
215
- const inner = /** @type {any} */ (next() ?? node);
216
- const in_jsx_child = in_jsx_child_context(path);
217
- return /** @type {any} */ (
218
- wrap_jsx_setup_declarations(tsx_node_to_jsx_expression(inner, in_jsx_child), in_jsx_child)
219
- );
211
+ _(node, { next, path }) {
212
+ set_node_path_metadata(node, path);
213
+ return next();
220
214
  },
221
215
 
222
- Tsrx(node, { next, path, state }) {
223
- /** @type {{ css: any, style_refs: any[] } | null} */
224
- let style_context = null;
225
- const inner = with_tsrx_fragment_styles(node, state, (context) => {
226
- style_context = context;
227
- return next() ?? node;
228
- });
216
+ TsrxFragment(node, { next, path, state, visit }) {
217
+ const parent = /** @type {AST.ArrowFunctionExpression} */ (path.at(-1));
218
+ if (parent?.metadata?.native_tsrx && parent.body === node) {
219
+ return /** @type {any} */ (visit(create_native_tsrx_render_block(node, state), state));
220
+ }
221
+
222
+ const style_context = prepare_tsrx_fragment_styles(node, state);
223
+ const target = style_context?.fragment ?? next() ?? node;
224
+ const in_jsx_child = in_jsx_child_context(path);
225
+ const expression = tsrx_node_to_jsx_expression(target, state, in_jsx_child);
229
226
  for (const statement of create_tsrx_style_ref_setup_statements(
230
- node,
227
+ target,
231
228
  style_context,
232
229
  state,
233
230
  )) {
234
- add_jsx_setup_declaration(inner, statement);
231
+ add_jsx_setup_declaration(expression, statement);
235
232
  }
236
- const in_jsx_child = in_jsx_child_context(path);
237
- return /** @type {any} */ (
238
- wrap_jsx_setup_declarations(
239
- tsrx_node_to_jsx_expression(inner, state, in_jsx_child),
240
- in_jsx_child,
241
- )
242
- );
233
+ return /** @type {any} */ (wrap_jsx_setup_declarations(expression, in_jsx_child));
243
234
  },
244
235
 
245
236
  TsxCompat(node, { next, path, state }) {
@@ -259,7 +250,7 @@ export function createJsxTransform(platform) {
259
250
  if (stylesheet) {
260
251
  analyze_css(stylesheet);
261
252
  state.stylesheets.push(stylesheet);
262
- return /** @type {any} */ (create_style_class_map_from_stylesheet(stylesheet));
253
+ return /** @type {any} */ (create_style_expression_value(node, stylesheet, state));
263
254
  }
264
255
  }
265
256
 
@@ -288,9 +279,9 @@ export function createJsxTransform(platform) {
288
279
  return /** @type {any} */ (to_jsx_expression_container(inner.expression, inner));
289
280
  },
290
281
 
291
- // Default .metadata on every function-like node so downstream consumers
292
- // do not trip on an undefined metadata object. Ripple's analyze phase
293
- // does this via visit_function; tsrx-react has no analyze phase.
282
+ BlockStatement: transform_block_statement,
283
+ ReturnStatement: transform_return_statement,
284
+
294
285
  // If an uppercase JS function contains hook-bearing TSRX, give it a
295
286
  // temporary helper scope so extracted hook helpers get stable identities.
296
287
  FunctionDeclaration: transform_function,
@@ -303,17 +294,24 @@ export function createJsxTransform(platform) {
303
294
  return visited;
304
295
  }
305
296
  const is_component = is_component_like_jsx_name(visited.name);
306
- return {
307
- ...visited,
308
- attributes: merge_duplicate_refs(
297
+ return b.jsx_opening_element(
298
+ visited.name,
299
+ merge_duplicate_refs(
309
300
  normalize_host_ref_spreads(visited.attributes || [], !is_component, transform_context),
310
301
  transform_context,
311
302
  ),
312
- };
303
+ visited.selfClosing,
304
+ visited.typeArguments,
305
+ visited,
306
+ );
313
307
  },
314
308
  });
315
309
 
316
- const expanded = expand_component_helpers(/** @type {AST.Program} */ (transformed));
310
+ const transformed_program = /** @type {AST.Program} */ (transformed);
311
+ if (type_only_style_anchors.length > 0) {
312
+ transformed_program.body.unshift(...type_only_style_anchors);
313
+ }
314
+ const expanded = expand_component_helpers(transformed_program);
317
315
  if (platform.hooks?.injectImports) {
318
316
  platform.hooks.injectImports(expanded, transform_context, suspense_source);
319
317
  } else {
@@ -593,48 +591,56 @@ function is_interleaved_body(body_nodes) {
593
591
  * @param {TransformContext} transform_context
594
592
  * @returns {boolean}
595
593
  */
596
- function function_needs_component_body_hook_split(node, transform_context) {
594
+ function needs_hook_split(node, transform_context) {
597
595
  return (
598
596
  transform_context.platform.hooks?.componentBodyHookHelpers === true &&
599
597
  node.body?.type === 'BlockStatement' &&
600
- find_component_body_hook_split_index(node.body.body || [], transform_context) !== -1
598
+ find_hook_split_index(node.body.body || [], transform_context) !== -1
601
599
  );
602
600
  }
603
601
 
604
602
  /**
605
603
  * @param {any} node
606
604
  * @param {TransformContext} transform_context
607
- * @returns {void}
605
+ * @returns {any}
608
606
  */
609
- function rewrite_component_body_conditional_hook_splits(node, transform_context) {
607
+ function create_hook_split_block(node, transform_context) {
610
608
  if (
611
609
  transform_context.platform.hooks?.componentBodyHookHelpers !== true ||
612
610
  !should_extract_hook_helpers(transform_context) ||
613
611
  node.body?.type !== 'BlockStatement'
614
612
  ) {
615
- return;
613
+ return null;
616
614
  }
617
615
 
618
616
  const body = node.body.body || [];
619
- const split_index = find_component_body_hook_split_index(body, transform_context);
617
+ const split_index = find_hook_split_index(body, transform_context);
620
618
  if (split_index === -1) {
621
- return;
619
+ return null;
622
620
  }
623
621
 
624
622
  const split_statement = body[split_index];
625
623
  const continuation_body = body.slice(split_index + 1);
626
624
  const helper = create_hook_safe_helper(
627
- continuation_body,
625
+ expand_native_tsrx_return_statement_list(continuation_body, transform_context),
628
626
  undefined,
629
627
  get_body_source_node(continuation_body) || split_statement,
630
628
  transform_context,
631
629
  );
632
630
 
633
- node.body.body = [
634
- ...body.slice(0, split_index + 1),
635
- ...helper.setup_statements,
636
- set_loc(b.return(helper.component_element), split_statement),
637
- ];
631
+ const block = b.block(
632
+ [
633
+ ...body.slice(0, split_index + 1),
634
+ ...helper.setup_statements,
635
+ set_loc(b.return(helper.component_element), split_statement),
636
+ ],
637
+ node.body,
638
+ );
639
+ block.metadata = {
640
+ ...(block.metadata || {}),
641
+ hook_split_block: true,
642
+ };
643
+ return block;
638
644
  }
639
645
 
640
646
  /**
@@ -642,7 +648,7 @@ function rewrite_component_body_conditional_hook_splits(node, transform_context)
642
648
  * @param {TransformContext} transform_context
643
649
  * @returns {number}
644
650
  */
645
- function find_component_body_hook_split_index(body_nodes, transform_context) {
651
+ function find_hook_split_index(body_nodes, transform_context) {
646
652
  for (let i = 0; i < body_nodes.length; i += 1) {
647
653
  if (!is_component_body_conditional_return_statement(body_nodes[i])) {
648
654
  continue;
@@ -945,22 +951,82 @@ function create_helper_state(base_name) {
945
951
  };
946
952
  }
947
953
 
954
+ /**
955
+ * @param {{ helpers: any[], statics: any[] }} helper_state
956
+ * @returns {{ generated_helpers: any[], generated_statics: any[] } | null}
957
+ */
958
+ function create_generated_helper_metadata(helper_state) {
959
+ if (helper_state.helpers.length === 0 && helper_state.statics.length === 0) {
960
+ return null;
961
+ }
962
+ return {
963
+ generated_helpers: helper_state.helpers,
964
+ generated_statics: helper_state.statics,
965
+ };
966
+ }
967
+
968
+ /**
969
+ * @param {any} metadata
970
+ * @returns {any}
971
+ */
972
+ function strip_function_transform_metadata(metadata) {
973
+ const { native_tsrx, hook_split, ...next_metadata } = metadata || {};
974
+ return next_metadata;
975
+ }
976
+
977
+ /**
978
+ * @param {AST.BlockStatement} node
979
+ * @param {{ next: () => any, visit: (node: any, state?: TransformContext) => any, state: TransformContext, path: AST.Node[] }} context
980
+ * @returns {any}
981
+ */
982
+ function transform_block_statement(node, { next, visit, state, path }) {
983
+ if (node.metadata?.hook_split_block || node.metadata?.native_return_block) {
984
+ return next() ?? node;
985
+ }
986
+
987
+ const parent = /** @type {any} */ (path.at(-1));
988
+ if (parent?.metadata?.hook_split && parent.body === node) {
989
+ const block = create_hook_split_block(parent, state);
990
+ if (block) {
991
+ return visit(block, state);
992
+ }
993
+ }
994
+
995
+ if (get_active_native_tsrx_function(path)) {
996
+ const block = create_native_tsrx_statement_list_block(node, state);
997
+ if (block) {
998
+ return visit(block, state);
999
+ }
1000
+ }
1001
+
1002
+ return next() ?? node;
1003
+ }
1004
+
948
1005
  /**
949
1006
  * @param {any} node
950
- * @param {{ next: (state?: TransformContext) => any, state: TransformContext }} context
1007
+ * @param {{ next: () => any, visit: (node: any, state?: TransformContext) => any, state: TransformContext, path: AST.Node[] }} context
951
1008
  * @returns {any}
952
1009
  */
953
- function collect_native_function_tsrx_metadata(node, { next, state }) {
954
- if (!function_has_native_tsrx_return(node)) {
955
- return next(state);
1010
+ function transform_return_statement(node, { next, visit, state, path }) {
1011
+ if (get_active_native_tsrx_function(path) && node.argument?.type === 'TsrxFragment') {
1012
+ return visit(create_native_tsrx_render_block(node.argument, state), state);
956
1013
  }
957
1014
 
958
- node.metadata = {
959
- ...(node.metadata || {}),
960
- native_tsrx_function: true,
961
- };
1015
+ return next() ?? node;
1016
+ }
962
1017
 
963
- return next(state);
1018
+ /**
1019
+ * @param {AST.Node[]} path
1020
+ * @returns {any | null}
1021
+ */
1022
+ function get_active_native_tsrx_function(path) {
1023
+ for (let i = path.length - 1; i >= 0; i -= 1) {
1024
+ const node = /** @type {any} */ (path[i]);
1025
+ if (is_function_or_class_boundary(node)) {
1026
+ return node.metadata?.native_tsrx ? node : null;
1027
+ }
1028
+ }
1029
+ return null;
964
1030
  }
965
1031
 
966
1032
  /**
@@ -978,47 +1044,49 @@ function transform_function(node, context) {
978
1044
 
979
1045
  /**
980
1046
  * @param {any} node
981
- * @param {{ next: () => any, state: TransformContext, path: AST.Node[] }} context
1047
+ * @param {{ next: () => any, state: TransformContext }} context
982
1048
  * @returns {any}
983
1049
  */
984
- function transform_native_tsrx_function(node, { next, state, path }) {
1050
+ function transform_native_tsrx_function(node, { next, state }) {
985
1051
  const helper_state =
986
- state.helper_state || create_helper_state(get_function_helper_base_name(node, path));
1052
+ state.helper_state || create_helper_state(get_function_helper_base_name(node));
987
1053
  const saved_helper_state = state.helper_state;
988
1054
  const saved_bindings = state.available_bindings;
989
1055
  const saved_hook_helpers_enabled = state.hook_helpers_enabled;
990
1056
 
991
1057
  state.helper_state = helper_state;
992
- state.hook_helpers_enabled = is_uppercase_function_like(node, path);
1058
+ state.hook_helpers_enabled = is_uppercase_function_like(node);
1059
+ node.metadata = {
1060
+ ...(node.metadata || {}),
1061
+ native_tsrx: true,
1062
+ ...(needs_hook_split(node, state) ? { hook_split: true } : {}),
1063
+ };
993
1064
  state.available_bindings = merge_binding_maps(
994
1065
  saved_bindings,
995
1066
  collect_function_scope_bindings(node),
996
1067
  );
997
1068
 
998
- validate_native_tsrx_function_await(node, state);
999
- expand_native_tsrx_function_returns(node, state);
1000
- rewrite_component_body_conditional_hook_splits(node, state);
1069
+ validate_native_await(node, state);
1001
1070
 
1002
1071
  const inner = /** @type {any} */ (next() ?? node);
1072
+ if (
1073
+ inner !== node &&
1074
+ node.type === 'ArrowFunctionExpression' &&
1075
+ node.body?.type === 'TsrxFragment' &&
1076
+ inner.body?.type === 'BlockStatement'
1077
+ ) {
1078
+ inner.expression = false;
1079
+ }
1003
1080
 
1004
1081
  state.helper_state = saved_helper_state;
1005
1082
  state.available_bindings = saved_bindings;
1006
1083
  state.hook_helpers_enabled = saved_hook_helpers_enabled;
1007
1084
 
1008
- ensure_function_metadata(inner, { next: () => inner });
1009
1085
  inner.metadata = {
1010
- ...(inner.metadata || {}),
1086
+ ...strip_function_transform_metadata(inner.metadata),
1011
1087
  native_tsrx_function: true,
1088
+ ...(!saved_helper_state ? create_generated_helper_metadata(helper_state) || {} : {}),
1012
1089
  };
1013
- if (!saved_helper_state && (helper_state.helpers.length || helper_state.statics.length)) {
1014
- inner.metadata.generated_helpers = helper_state.helpers;
1015
- inner.metadata.generated_statics = helper_state.statics;
1016
- }
1017
-
1018
- const wrapped = state.platform.hooks?.wrapNativeFunctionComponent?.(inner, state, path);
1019
- if (wrapped) {
1020
- return wrapped;
1021
- }
1022
1090
 
1023
1091
  return inner;
1024
1092
  }
@@ -1028,8 +1096,8 @@ function transform_native_tsrx_function(node, { next, state, path }) {
1028
1096
  * @param {TransformContext} transform_context
1029
1097
  * @returns {void}
1030
1098
  */
1031
- function validate_native_tsrx_function_await(node, transform_context) {
1032
- const await_node = find_first_top_level_await_in_native_tsrx_function(node);
1099
+ function validate_native_await(node, transform_context) {
1100
+ const await_node = find_native_await(node);
1033
1101
  if (!await_node) {
1034
1102
  return;
1035
1103
  }
@@ -1055,7 +1123,7 @@ function validate_native_tsrx_function_await(node, transform_context) {
1055
1123
  * @param {any} node
1056
1124
  * @returns {any | null}
1057
1125
  */
1058
- function find_first_top_level_await_in_native_tsrx_function(node) {
1126
+ function find_native_await(node) {
1059
1127
  if (
1060
1128
  node.type === 'ArrowFunctionExpression' &&
1061
1129
  node.body?.type !== 'BlockStatement' &&
@@ -1065,16 +1133,16 @@ function find_first_top_level_await_in_native_tsrx_function(node) {
1065
1133
  }
1066
1134
 
1067
1135
  const body = node.body?.type === 'BlockStatement' ? node.body.body || [] : [];
1068
- return find_first_top_level_await_in_native_tsrx_statements(body);
1136
+ return find_native_await_in_list(body);
1069
1137
  }
1070
1138
 
1071
1139
  /**
1072
1140
  * @param {any[]} statements
1073
1141
  * @returns {any | null}
1074
1142
  */
1075
- function find_first_top_level_await_in_native_tsrx_statements(statements) {
1143
+ function find_native_await_in_list(statements) {
1076
1144
  for (const statement of statements) {
1077
- const found = find_first_top_level_await_in_native_tsrx_statement(statement);
1145
+ const found = find_native_await_in_statement(statement);
1078
1146
  if (found) return found;
1079
1147
  }
1080
1148
  return null;
@@ -1084,10 +1152,10 @@ function find_first_top_level_await_in_native_tsrx_statements(statements) {
1084
1152
  * @param {any} statement
1085
1153
  * @returns {any | null}
1086
1154
  */
1087
- function find_first_top_level_await_in_native_tsrx_statement(statement) {
1155
+ function find_native_await_in_statement(statement) {
1088
1156
  if (!statement || typeof statement !== 'object') return null;
1089
1157
 
1090
- if (statement.type === 'ReturnStatement' && statement.argument?.type === 'Tsrx') {
1158
+ if (statement.type === 'ReturnStatement' && statement.argument?.type === 'TsrxFragment') {
1091
1159
  return find_first_top_level_await_in_tsrx_function_body(statement.argument.children || []);
1092
1160
  }
1093
1161
 
@@ -1103,21 +1171,19 @@ function find_first_top_level_await_in_native_tsrx_statement(statement) {
1103
1171
  }
1104
1172
 
1105
1173
  if (statement.type === 'BlockStatement') {
1106
- return find_first_top_level_await_in_native_tsrx_statements(statement.body || []);
1174
+ return find_native_await_in_list(statement.body || []);
1107
1175
  }
1108
1176
 
1109
1177
  if (statement.type === 'IfStatement') {
1110
1178
  return (
1111
- find_first_top_level_await_in_native_tsrx_statement(statement.consequent) ||
1112
- find_first_top_level_await_in_native_tsrx_statement(statement.alternate)
1179
+ find_native_await_in_statement(statement.consequent) ||
1180
+ find_native_await_in_statement(statement.alternate)
1113
1181
  );
1114
1182
  }
1115
1183
 
1116
1184
  if (statement.type === 'SwitchStatement') {
1117
1185
  for (const switch_case of statement.cases || []) {
1118
- const found = find_first_top_level_await_in_native_tsrx_statements(
1119
- switch_case.consequent || [],
1120
- );
1186
+ const found = find_native_await_in_list(switch_case.consequent || []);
1121
1187
  if (found) return found;
1122
1188
  }
1123
1189
  return null;
@@ -1125,9 +1191,9 @@ function find_first_top_level_await_in_native_tsrx_statement(statement) {
1125
1191
 
1126
1192
  if (statement.type === 'TryStatement') {
1127
1193
  return (
1128
- find_first_top_level_await_in_native_tsrx_statement(statement.block) ||
1129
- find_first_top_level_await_in_native_tsrx_statement(statement.handler?.body) ||
1130
- find_first_top_level_await_in_native_tsrx_statement(statement.finalizer)
1194
+ find_native_await_in_statement(statement.block) ||
1195
+ find_native_await_in_statement(statement.handler?.body) ||
1196
+ find_native_await_in_statement(statement.finalizer)
1131
1197
  );
1132
1198
  }
1133
1199
 
@@ -1136,32 +1202,34 @@ function find_first_top_level_await_in_native_tsrx_statement(statement) {
1136
1202
 
1137
1203
  /**
1138
1204
  * @param {any} node
1139
- * @param {{ next: () => any, state: TransformContext, path: AST.Node[] }} context
1205
+ * @param {{ next: () => any, state: TransformContext }} context
1140
1206
  * @returns {any}
1141
1207
  */
1142
- function transform_function_with_hook_helpers(node, { next, state, path }) {
1208
+ function transform_function_with_hook_helpers(node, { next, state }) {
1143
1209
  const has_hook_bearing_tsrx = function_contains_hook_bearing_tsrx(node, state);
1144
- const has_component_body_hook_split = function_needs_component_body_hook_split(node, state);
1210
+ const has_hook_split = needs_hook_split(node, state);
1145
1211
  if (
1146
1212
  state.helper_state ||
1147
- !is_uppercase_function_like(node, path) ||
1148
- (!has_hook_bearing_tsrx && !has_component_body_hook_split)
1213
+ !is_uppercase_function_like(node) ||
1214
+ (!has_hook_bearing_tsrx && !has_hook_split)
1149
1215
  ) {
1150
- return ensure_function_metadata(node, { next });
1216
+ return next() ?? node;
1151
1217
  }
1152
1218
 
1153
- const helper_state = create_helper_state(get_function_helper_base_name(node, path));
1219
+ const helper_state = create_helper_state(get_function_helper_base_name(node));
1154
1220
  const saved_helper_state = state.helper_state;
1155
1221
  const saved_bindings = state.available_bindings;
1156
1222
  const saved_hook_helpers_enabled = state.hook_helpers_enabled;
1157
1223
 
1158
1224
  state.helper_state = helper_state;
1159
1225
  state.hook_helpers_enabled = true;
1160
- state.available_bindings = collect_function_scope_bindings(node);
1161
-
1162
- if (has_component_body_hook_split) {
1163
- rewrite_component_body_conditional_hook_splits(node, state);
1226
+ if (has_hook_split) {
1227
+ node.metadata = {
1228
+ ...(node.metadata || {}),
1229
+ hook_split: true,
1230
+ };
1164
1231
  }
1232
+ state.available_bindings = collect_function_scope_bindings(node);
1165
1233
 
1166
1234
  const inner = /** @type {any} */ (next() ?? node);
1167
1235
 
@@ -1169,48 +1237,41 @@ function transform_function_with_hook_helpers(node, { next, state, path }) {
1169
1237
  state.available_bindings = saved_bindings;
1170
1238
  state.hook_helpers_enabled = saved_hook_helpers_enabled;
1171
1239
 
1172
- ensure_function_metadata(inner, { next: () => inner });
1173
- if (helper_state.helpers.length || helper_state.statics.length) {
1174
- inner.metadata = {
1175
- ...(inner.metadata || {}),
1176
- generated_helpers: helper_state.helpers,
1177
- generated_statics: helper_state.statics,
1178
- };
1179
- }
1240
+ inner.metadata = {
1241
+ ...strip_function_transform_metadata(inner.metadata),
1242
+ ...(create_generated_helper_metadata(helper_state) || {}),
1243
+ };
1180
1244
 
1181
1245
  return inner;
1182
1246
  }
1183
1247
 
1184
1248
  /**
1185
1249
  * @param {any} node
1186
- * @param {AST.Node[]} [path]
1187
1250
  * @returns {string}
1188
1251
  */
1189
- function get_function_helper_base_name(node, path = []) {
1190
- return get_function_like_name(node, path) || 'Tsrx';
1252
+ function get_function_helper_base_name(node) {
1253
+ return get_function_like_name(node) || 'TsrxFragment';
1191
1254
  }
1192
1255
 
1193
1256
  /**
1194
1257
  * @param {any} node
1195
- * @param {AST.Node[]} path
1196
1258
  * @returns {boolean}
1197
1259
  */
1198
- function is_uppercase_function_like(node, path) {
1199
- const name = get_function_like_name(node, path);
1260
+ function is_uppercase_function_like(node) {
1261
+ const name = get_function_like_name(node);
1200
1262
  return !!(name && /^[A-Z]/.test(name));
1201
1263
  }
1202
1264
 
1203
1265
  /**
1204
1266
  * @param {any} node
1205
- * @param {AST.Node[]} path
1206
1267
  * @returns {string | null}
1207
1268
  */
1208
- function get_function_like_name(node, path) {
1269
+ function get_function_like_name(node) {
1209
1270
  if (node.id?.type === 'Identifier') {
1210
1271
  return node.id.name;
1211
1272
  }
1212
1273
 
1213
- const parent = /** @type {any} */ (path.at(-1));
1274
+ const parent = /** @type {any} */ (node.metadata?.path?.at(-1));
1214
1275
  if (!parent) return null;
1215
1276
 
1216
1277
  if (parent.type === 'VariableDeclarator' && parent.init === node) {
@@ -1268,7 +1329,7 @@ function collect_function_scope_bindings(node) {
1268
1329
  const bindings = collect_param_bindings(node.params || []);
1269
1330
  if (node.body?.type === 'BlockStatement') {
1270
1331
  for (const statement of node.body.body || []) {
1271
- if (statement.type === 'ReturnStatement' && statement.argument?.type === 'Tsrx') {
1332
+ if (statement.type === 'ReturnStatement' && statement.argument?.type === 'TsrxFragment') {
1272
1333
  for (const child of get_tsrx_render_children(statement.argument)) {
1273
1334
  collect_statement_bindings(child, bindings);
1274
1335
  }
@@ -1377,7 +1438,7 @@ function statement_contains_native_tsrx_return(statement) {
1377
1438
  */
1378
1439
  function node_contains_native_tsrx_template(node) {
1379
1440
  if (!node || typeof node !== 'object') return false;
1380
- if (node.type === 'Element' || node.type === 'Tsrx') return true;
1441
+ if (node.type === 'Element' || node.type === 'TsrxFragment') return true;
1381
1442
 
1382
1443
  if (is_function_or_class_boundary(node)) {
1383
1444
  return false;
@@ -1419,7 +1480,7 @@ function collect_tsrx_stylesheet(node) {
1419
1480
  /**
1420
1481
  * @param {any} node
1421
1482
  * @param {TransformContext} transform_context
1422
- * @returns {{ css: any, style_refs: any[] } | null}
1483
+ * @returns {{ css: any, style_refs: any[], fragment: any } | null}
1423
1484
  */
1424
1485
  function prepare_tsrx_fragment_styles(node, transform_context) {
1425
1486
  const css = collect_tsrx_stylesheet(node);
@@ -1428,21 +1489,21 @@ function prepare_tsrx_fragment_styles(node, transform_context) {
1428
1489
  const style_refs = collect_style_ref_attributes(node);
1429
1490
  apply_css_definition_metadata(node, css, style_refs.length > 0);
1430
1491
  transform_context.stylesheets.push(css);
1431
- annotate_tsrx_with_hash(
1492
+ const fragment = annotate_tsrx_with_hash(
1432
1493
  node,
1433
1494
  css.hash,
1434
1495
  transform_context.platform.jsx.classAttrName ??
1435
1496
  (transform_context.platform.jsx.rewriteClassAttr ? 'className' : 'class'),
1436
1497
  transform_context.typeOnly,
1437
1498
  );
1438
- return { css, style_refs };
1499
+ return { css, style_refs, fragment };
1439
1500
  }
1440
1501
 
1441
1502
  /**
1442
1503
  * @template T
1443
1504
  * @param {any} node
1444
1505
  * @param {TransformContext} transform_context
1445
- * @param {(style_context: { css: any, style_refs: any[] } | null) => T} callback
1506
+ * @param {(style_context: { css: any, style_refs: any[], fragment: any } | null) => T} callback
1446
1507
  * @returns {T}
1447
1508
  */
1448
1509
  function with_tsrx_fragment_styles(node, transform_context, callback) {
@@ -1452,7 +1513,7 @@ function with_tsrx_fragment_styles(node, transform_context, callback) {
1452
1513
 
1453
1514
  /**
1454
1515
  * @param {any} fragment
1455
- * @param {{ css: any, style_refs: any[] } | null} style_context
1516
+ * @param {{ css: any, style_refs: any[], fragment: any } | null} style_context
1456
1517
  * @param {TransformContext} transform_context
1457
1518
  * @returns {AST.Statement[]}
1458
1519
  */
@@ -1472,6 +1533,64 @@ function create_tsrx_style_ref_setup_statements(fragment, style_context, transfo
1472
1533
  );
1473
1534
  }
1474
1535
 
1536
+ /**
1537
+ * @param {any} node
1538
+ * @param {any} stylesheet
1539
+ * @param {TransformContext} transform_context
1540
+ * @returns {AST.Expression}
1541
+ */
1542
+ function create_style_expression_value(node, stylesheet, transform_context) {
1543
+ const class_map = create_style_class_map_from_stylesheet(stylesheet);
1544
+ if (!transform_context.typeOnly) {
1545
+ return class_map;
1546
+ }
1547
+
1548
+ add_type_only_style_anchor(node, transform_context);
1549
+ return class_map;
1550
+ }
1551
+
1552
+ /**
1553
+ * @param {any} node
1554
+ * @param {TransformContext} transform_context
1555
+ */
1556
+ function add_type_only_style_anchor(node, transform_context) {
1557
+ const style_anchor = b.jsx_element(clone_expression_node(node, true), [], []);
1558
+ disable_style_anchor_verification(style_anchor);
1559
+
1560
+ const anchor_id = create_generated_identifier(create_style_anchor_name(transform_context));
1561
+ transform_context.type_only_style_anchors.push(
1562
+ b.const(anchor_id, style_anchor),
1563
+ b.stmt(clone_identifier(anchor_id)),
1564
+ );
1565
+ }
1566
+
1567
+ /**
1568
+ * @param {TransformContext} transform_context
1569
+ * @returns {string}
1570
+ */
1571
+ function create_style_anchor_name(transform_context) {
1572
+ transform_context.local_statement_component_index += 1;
1573
+ return `_tsrx_style_anchor_${transform_context.local_statement_component_index}`;
1574
+ }
1575
+
1576
+ /**
1577
+ * @param {ESTreeJSX.JSXElement} element
1578
+ */
1579
+ function disable_style_anchor_verification(element) {
1580
+ if (element.openingElement?.name) {
1581
+ element.openingElement.name.metadata = {
1582
+ ...(element.openingElement.name.metadata || {}),
1583
+ disable_verification: true,
1584
+ };
1585
+ }
1586
+ if (element.closingElement?.name) {
1587
+ element.closingElement.name.metadata = {
1588
+ ...(element.closingElement.name.metadata || {}),
1589
+ disable_verification: true,
1590
+ };
1591
+ }
1592
+ }
1593
+
1475
1594
  /**
1476
1595
  * @param {TransformContext} transform_context
1477
1596
  * @returns {string}
@@ -1510,7 +1629,7 @@ function collect_style_elements(node, styles) {
1510
1629
  return;
1511
1630
  }
1512
1631
 
1513
- if (is_function_or_class_boundary(node) || node.type === 'Tsrx') {
1632
+ if (is_function_or_class_boundary(node) || node.type === 'TsrxFragment') {
1514
1633
  return;
1515
1634
  }
1516
1635
 
@@ -1549,15 +1668,22 @@ function collect_style_elements(node, styles) {
1549
1668
  * @param {string} hash
1550
1669
  * @param {'class' | 'className'} jsx_class_attr_name
1551
1670
  * @param {boolean} preserve_style_elements
1552
- * @returns {void}
1671
+ * @returns {any}
1553
1672
  */
1554
1673
  function annotate_tsrx_with_hash(node, hash, jsx_class_attr_name, preserve_style_elements) {
1555
- node.children = (node.children || []).map((/** @type {any} */ statement) =>
1556
- annotate_with_hash(statement, hash, jsx_class_attr_name, preserve_style_elements),
1674
+ const annotated = { ...node };
1675
+ annotated.children = (node.children || []).map((/** @type {any} */ statement) =>
1676
+ annotate_with_hash(
1677
+ clone_expression_node(statement),
1678
+ hash,
1679
+ jsx_class_attr_name,
1680
+ preserve_style_elements,
1681
+ ),
1557
1682
  );
1558
1683
  if (!preserve_style_elements) {
1559
- node.children = strip_style_elements(node.children);
1684
+ annotated.children = strip_style_elements(annotated.children);
1560
1685
  }
1686
+ return annotated;
1561
1687
  }
1562
1688
 
1563
1689
  /**
@@ -1622,8 +1748,7 @@ function is_style_expression_position(path) {
1622
1748
  const parent = path.at(-1);
1623
1749
  return !(
1624
1750
  parent?.type === 'Element' ||
1625
- parent?.type === 'Tsrx' ||
1626
- parent?.type === 'Tsx' ||
1751
+ parent?.type === 'TsrxFragment' ||
1627
1752
  parent?.type === 'TsxCompat' ||
1628
1753
  parent?.type === 'BlockStatement' ||
1629
1754
  parent?.type === 'Program' ||
@@ -1632,32 +1757,58 @@ function is_style_expression_position(path) {
1632
1757
  }
1633
1758
 
1634
1759
  /**
1635
- * @param {any} node
1760
+ * @param {any} fragment
1636
1761
  * @param {TransformContext} transform_context
1637
- * @returns {void}
1762
+ * @returns {any}
1638
1763
  */
1639
- function expand_native_tsrx_function_returns(node, transform_context) {
1640
- if (node.type === 'ArrowFunctionExpression' && node.body?.type === 'Tsrx') {
1641
- const body = node.body;
1642
- const statements = with_tsrx_fragment_styles(body, transform_context, (style_context) => {
1643
- return [
1644
- ...create_tsrx_style_ref_setup_statements(body, style_context, transform_context),
1645
- ...build_render_statements(get_tsrx_render_children(body), true, transform_context),
1646
- ];
1647
- });
1648
- node.body = b.block(mark_native_pretransformed_jsx(statements), body);
1649
- node.expression = false;
1650
- return;
1651
- }
1764
+ function create_native_tsrx_render_block(fragment, transform_context) {
1765
+ const block = b.block(
1766
+ mark_native_pretransformed_jsx(
1767
+ create_native_tsrx_render_statements(fragment, transform_context),
1768
+ ),
1769
+ fragment,
1770
+ );
1771
+ block.metadata = {
1772
+ ...(block.metadata || {}),
1773
+ native_return_block: true,
1774
+ };
1775
+ return block;
1776
+ }
1652
1777
 
1653
- if (node.body?.type !== 'BlockStatement') {
1654
- return;
1778
+ /**
1779
+ * @param {any} block
1780
+ * @param {TransformContext} transform_context
1781
+ * @returns {any | null}
1782
+ */
1783
+ function create_native_tsrx_statement_list_block(block, transform_context) {
1784
+ const source_body = block.body || [];
1785
+ const body = expand_native_tsrx_return_statement_list(source_body, transform_context);
1786
+
1787
+ if (body === source_body) {
1788
+ return null;
1655
1789
  }
1656
1790
 
1657
- node.body.body = expand_native_tsrx_return_statement_list(
1658
- node.body.body || [],
1659
- transform_context,
1660
- );
1791
+ const next_block = b.block(mark_native_pretransformed_jsx(body), block);
1792
+ next_block.metadata = {
1793
+ ...(next_block.metadata || {}),
1794
+ native_return_block: true,
1795
+ };
1796
+ return next_block;
1797
+ }
1798
+
1799
+ /**
1800
+ * @param {any} fragment
1801
+ * @param {TransformContext} transform_context
1802
+ * @returns {AST.Statement[]}
1803
+ */
1804
+ function create_native_tsrx_render_statements(fragment, transform_context) {
1805
+ return with_tsrx_fragment_styles(fragment, transform_context, (style_context) => {
1806
+ const target = style_context?.fragment ?? fragment;
1807
+ return [
1808
+ ...create_tsrx_style_ref_setup_statements(target, style_context, transform_context),
1809
+ ...build_render_statements(get_tsrx_render_children(target), true, transform_context),
1810
+ ];
1811
+ });
1661
1812
  }
1662
1813
 
1663
1814
  /**
@@ -1666,9 +1817,15 @@ function expand_native_tsrx_function_returns(node, transform_context) {
1666
1817
  * @returns {any[]}
1667
1818
  */
1668
1819
  function expand_native_tsrx_return_statement_list(statements, transform_context) {
1669
- return statements.flatMap((statement) =>
1670
- expand_native_tsrx_return_statement(statement, transform_context),
1671
- );
1820
+ let changed = false;
1821
+ const next_statements = statements.flatMap((statement) => {
1822
+ const result = expand_native_tsrx_return_statement(statement, transform_context);
1823
+ if (result.length !== 1 || result[0] !== statement) {
1824
+ changed = true;
1825
+ }
1826
+ return result;
1827
+ });
1828
+ return changed ? next_statements : statements;
1672
1829
  }
1673
1830
 
1674
1831
  /**
@@ -1679,14 +1836,8 @@ function expand_native_tsrx_return_statement_list(statements, transform_context)
1679
1836
  function expand_native_tsrx_return_statement(statement, transform_context) {
1680
1837
  if (!statement || typeof statement !== 'object') return [statement];
1681
1838
 
1682
- if (statement.type === 'ReturnStatement' && statement.argument?.type === 'Tsrx') {
1683
- const fragment = statement.argument;
1684
- return with_tsrx_fragment_styles(fragment, transform_context, (style_context) => {
1685
- return mark_native_pretransformed_jsx([
1686
- ...create_tsrx_style_ref_setup_statements(fragment, style_context, transform_context),
1687
- ...build_render_statements(get_tsrx_render_children(fragment), true, transform_context),
1688
- ]);
1689
- });
1839
+ if (statement.type === 'ReturnStatement' && statement.argument?.type === 'TsrxFragment') {
1840
+ return create_native_tsrx_render_statements(statement.argument, transform_context);
1690
1841
  }
1691
1842
 
1692
1843
  if (is_function_or_class_boundary(statement)) {
@@ -1694,52 +1845,65 @@ function expand_native_tsrx_return_statement(statement, transform_context) {
1694
1845
  }
1695
1846
 
1696
1847
  if (statement.type === 'BlockStatement') {
1697
- statement.body = expand_native_tsrx_return_statement_list(
1698
- statement.body || [],
1699
- transform_context,
1700
- );
1701
- return [statement];
1848
+ const body = expand_native_tsrx_return_statement_list(statement.body || [], transform_context);
1849
+ return body === statement.body ? [statement] : [b.block(body, statement)];
1702
1850
  }
1703
1851
 
1704
1852
  if (statement.type === 'IfStatement') {
1705
- statement.consequent = expand_embedded_native_return_statement(
1853
+ const consequent = expand_embedded_native_return_statement(
1706
1854
  statement.consequent,
1707
1855
  transform_context,
1708
1856
  );
1709
- if (statement.alternate) {
1710
- statement.alternate = expand_embedded_native_return_statement(
1711
- statement.alternate,
1712
- transform_context,
1713
- );
1857
+ const alternate = statement.alternate
1858
+ ? expand_embedded_native_return_statement(statement.alternate, transform_context)
1859
+ : statement.alternate;
1860
+ if (consequent === statement.consequent && alternate === statement.alternate) {
1861
+ return [statement];
1714
1862
  }
1715
- return [statement];
1863
+ return [set_loc(b.if(statement.test, consequent, alternate), statement)];
1716
1864
  }
1717
1865
 
1718
1866
  if (statement.type === 'SwitchStatement') {
1719
- for (const switch_case of statement.cases || []) {
1720
- switch_case.consequent = expand_native_tsrx_return_statement_list(
1867
+ let changed = false;
1868
+ const cases = (statement.cases || []).map((/** @type {any} */ switch_case) => {
1869
+ const consequent = expand_native_tsrx_return_statement_list(
1721
1870
  switch_case.consequent || [],
1722
1871
  transform_context,
1723
1872
  );
1724
- }
1725
- return [statement];
1873
+ if (consequent === switch_case.consequent) {
1874
+ return switch_case;
1875
+ }
1876
+ changed = true;
1877
+ return set_loc(b.switch_case(switch_case.test, consequent), switch_case);
1878
+ });
1879
+ return changed ? [set_loc(b.switch(statement.discriminant, cases), statement)] : [statement];
1726
1880
  }
1727
1881
 
1728
1882
  if (statement.type === 'TryStatement') {
1729
- statement.block = expand_embedded_native_return_statement(statement.block, transform_context);
1730
- if (statement.handler?.body) {
1731
- statement.handler.body = expand_embedded_native_return_statement(
1732
- statement.handler.body,
1733
- transform_context,
1734
- );
1735
- }
1736
- if (statement.finalizer) {
1737
- statement.finalizer = expand_embedded_native_return_statement(
1738
- statement.finalizer,
1739
- transform_context,
1740
- );
1883
+ const block = expand_embedded_native_return_statement(statement.block, transform_context);
1884
+ const handler_body = statement.handler?.body
1885
+ ? expand_embedded_native_return_statement(statement.handler.body, transform_context)
1886
+ : statement.handler?.body;
1887
+ const finalizer = statement.finalizer
1888
+ ? expand_embedded_native_return_statement(statement.finalizer, transform_context)
1889
+ : statement.finalizer;
1890
+ if (
1891
+ block === statement.block &&
1892
+ handler_body === statement.handler?.body &&
1893
+ finalizer === statement.finalizer
1894
+ ) {
1895
+ return [statement];
1741
1896
  }
1742
- return [statement];
1897
+ const handler =
1898
+ statement.handler && handler_body !== statement.handler.body
1899
+ ? b.catch_clause(
1900
+ statement.handler.param,
1901
+ statement.handler.resetParam,
1902
+ handler_body,
1903
+ statement.handler,
1904
+ )
1905
+ : statement.handler;
1906
+ return [set_loc(b.try(block, handler, finalizer, statement.pending ?? null), statement)];
1743
1907
  }
1744
1908
 
1745
1909
  return [statement];
@@ -1872,7 +2036,7 @@ function node_contains_hook_bearing_tsrx(node, transform_context) {
1872
2036
  return node.some((child) => node_contains_hook_bearing_tsrx(child, transform_context));
1873
2037
  }
1874
2038
 
1875
- if (node.type === 'Tsrx') {
2039
+ if (node.type === 'TsrxFragment') {
1876
2040
  return body_contains_top_level_hook_call(node.children || [], transform_context, true);
1877
2041
  }
1878
2042
 
@@ -1919,7 +2083,7 @@ function should_extract_hook_helpers(transform_context) {
1919
2083
  */
1920
2084
  function create_module_scoped_hook_component_id(helper_id, transform_context) {
1921
2085
  return create_generated_identifier(
1922
- `${transform_context.helper_state?.base_name || 'Tsrx'}__${helper_id.name}`,
2086
+ `${transform_context.helper_state?.base_name || 'TsrxFragment'}__${helper_id.name}`,
1923
2087
  );
1924
2088
  }
1925
2089
 
@@ -2645,15 +2809,7 @@ function to_jsx_element(node, transform_context, raw_children = node.children ||
2645
2809
  if (node.type === 'JSXElement') return node;
2646
2810
  if (!node.id) {
2647
2811
  report_jsx_fragment_in_tsrx_error(node, transform_context);
2648
- return set_loc(
2649
- /** @type {any} */ ({
2650
- type: 'JSXFragment',
2651
- openingFragment: { type: 'JSXOpeningFragment' },
2652
- closingFragment: { type: 'JSXClosingFragment' },
2653
- children: [],
2654
- }),
2655
- node,
2656
- );
2812
+ return set_loc(b.jsx_fragment(), node);
2657
2813
  }
2658
2814
  if (is_dynamic_element_id(node.id)) {
2659
2815
  return dynamic_element_to_jsx_child(node, transform_context);
@@ -2707,7 +2863,11 @@ function to_jsx_element(node, transform_context, raw_children = node.children ||
2707
2863
  node.closingElement || node,
2708
2864
  );
2709
2865
 
2710
- return set_loc(b.jsx_element_fresh(openingElement, closingElement, children), node);
2866
+ const element = set_loc(b.jsx_element_fresh(openingElement, closingElement, children), node);
2867
+ if (transform_context.typeOnly && is_style_element(node)) {
2868
+ disable_style_anchor_verification(element);
2869
+ }
2870
+ return element;
2711
2871
  }
2712
2872
 
2713
2873
  /**
@@ -3632,12 +3792,7 @@ export function create_hook_safe_helper(
3632
3792
 
3633
3793
  if (key_expression) {
3634
3794
  component_element.openingElement.attributes.push(
3635
- /** @type {any} */ ({
3636
- type: 'JSXAttribute',
3637
- name: { type: 'JSXIdentifier', name: 'key', metadata: { path: [] } },
3638
- value: to_jsx_expression_container(key_expression, key_expression),
3639
- metadata: { path: [] },
3640
- }),
3795
+ b.jsx_attribute(b.jsx_id('key'), to_jsx_expression_container(key_expression, key_expression)),
3641
3796
  );
3642
3797
  }
3643
3798
 
@@ -3805,11 +3960,19 @@ function create_cached_helper_declaration(helper_id, cache_id, helper_init) {
3805
3960
  * @returns {AST.FunctionDeclaration}
3806
3961
  */
3807
3962
  function create_helper_function_declaration_from_expression(helper_id, helper_fn) {
3808
- return {
3809
- ...helper_fn,
3810
- type: 'FunctionDeclaration',
3811
- id: clone_identifier(helper_id),
3812
- };
3963
+ const declaration = set_loc(
3964
+ b.function_declaration(
3965
+ clone_identifier(helper_id),
3966
+ helper_fn.params,
3967
+ helper_fn.body,
3968
+ helper_fn.async,
3969
+ helper_fn.typeParameters,
3970
+ ),
3971
+ helper_fn,
3972
+ );
3973
+ declaration.generator = helper_fn.generator;
3974
+ declaration.metadata = { ...(helper_fn.metadata || {}), path: helper_fn.metadata?.path || [] };
3975
+ return declaration;
3813
3976
  }
3814
3977
 
3815
3978
  /**
@@ -3842,11 +4005,7 @@ function get_body_source_node(body_nodes) {
3842
4005
  function to_jsx_child(node, transform_context) {
3843
4006
  if (!node) return node;
3844
4007
  switch (node.type) {
3845
- case 'Tsx':
3846
- // We're inside a JSX child position by construction, so keep a
3847
- // JSXExpressionContainer wrapper for bare `{expr}` children.
3848
- return tsx_node_to_jsx_expression(node, true);
3849
- case 'Tsrx':
4008
+ case 'TsrxFragment':
3850
4009
  return tsrx_node_to_jsx_expression(node, transform_context, true);
3851
4010
  case 'TsxCompat':
3852
4011
  return tsx_compat_node_to_jsx_expression(node, transform_context, true);
@@ -3880,8 +4039,8 @@ function to_jsx_child(node, transform_context) {
3880
4039
 
3881
4040
  /**
3882
4041
  * Lower a native TSRX fragment body to a JSX expression.
3883
- * Unlike `<tsx>`, children have already been parsed and transformed through
3884
- * the normal TSRX Element/Text/control-flow visitors.
4042
+ * Children have already been parsed and transformed through the normal TSRX
4043
+ * Element/Text/control-flow visitors.
3885
4044
  *
3886
4045
  * @param {any} node
3887
4046
  * @param {TransformContext} transform_context
@@ -4574,14 +4733,7 @@ function try_statement_to_jsx_child(node, transform_context) {
4574
4733
  ) ??
4575
4734
  create_jsx_element(
4576
4735
  'Suspense',
4577
- [
4578
- {
4579
- type: 'JSXAttribute',
4580
- name: { type: 'JSXIdentifier', name: 'fallback', metadata: { path: [] } },
4581
- value: fallback_content,
4582
- metadata: { path: [] },
4583
- },
4584
- ],
4736
+ [b.jsx_attribute(b.jsx_id('fallback'), fallback_content)],
4585
4737
  [result],
4586
4738
  );
4587
4739
  }
@@ -5066,14 +5218,12 @@ function build_switch_with_lift(switch_node, transform_context) {
5066
5218
  (/** @type {any} */ original_case, /** @type {number} */ i) => {
5067
5219
  const helper = case_helpers[i];
5068
5220
  if (helper) {
5069
- return /** @type {any} */ ({
5070
- type: 'SwitchCase',
5071
- test: original_case.test,
5072
- consequent: [
5221
+ return set_loc(
5222
+ b.switch_case(original_case.test, [
5073
5223
  create_component_return_statement([helper.component_element], original_case),
5074
- ],
5075
- metadata: { path: [] },
5076
- });
5224
+ ]),
5225
+ original_case,
5226
+ );
5077
5227
  }
5078
5228
 
5079
5229
  const { own_body, has_terminator } = case_info[i];
@@ -5082,12 +5232,7 @@ function build_switch_with_lift(switch_node, transform_context) {
5082
5232
  // Alias-pattern empty case (`case 'a': case 'b': ...`) — keep
5083
5233
  // the arm body empty so JS falls through to the next case at
5084
5234
  // runtime, where the helper invocation actually lives.
5085
- return /** @type {any} */ ({
5086
- type: 'SwitchCase',
5087
- test: original_case.test,
5088
- consequent: [],
5089
- metadata: { path: [] },
5090
- });
5235
+ return set_loc(b.switch_case(original_case.test, []), original_case);
5091
5236
  }
5092
5237
 
5093
5238
  const case_body = [];
@@ -5128,45 +5273,23 @@ function build_switch_with_lift(switch_node, transform_context) {
5128
5273
  // Empty body with explicit `break;` / bare `return;` — keep
5129
5274
  // a `break` so JS doesn't fall through into the next case
5130
5275
  // (which may now hold the lifted helper invocation).
5131
- case_body.push(
5132
- /** @type {any} */ ({
5133
- type: 'BreakStatement',
5134
- label: null,
5135
- metadata: { path: [] },
5136
- }),
5137
- );
5276
+ case_body.push(b.break);
5138
5277
  } else if (case_body.length > 0) {
5139
5278
  // Statements-only inline case without terminator. We've
5140
5279
  // already inlined the downstream chain via the helper
5141
5280
  // reference above, so emit a `break` to stop the runtime
5142
5281
  // from re-running downstream statements via JS fall-through.
5143
- case_body.push(
5144
- /** @type {any} */ ({
5145
- type: 'BreakStatement',
5146
- label: null,
5147
- metadata: { path: [] },
5148
- }),
5149
- );
5282
+ case_body.push(b.break);
5150
5283
  }
5151
5284
  }
5152
5285
 
5153
- return /** @type {any} */ ({
5154
- type: 'SwitchCase',
5155
- test: original_case.test,
5156
- consequent: case_body,
5157
- metadata: { path: [] },
5158
- });
5286
+ return set_loc(b.switch_case(original_case.test, case_body), original_case);
5159
5287
  },
5160
5288
  );
5161
5289
 
5162
5290
  return {
5163
5291
  setup_statements,
5164
- switch_statement: /** @type {any} */ ({
5165
- type: 'SwitchStatement',
5166
- discriminant: switch_node.discriminant,
5167
- cases: new_cases,
5168
- metadata: { path: [] },
5169
- }),
5292
+ switch_statement: b.switch(switch_node.discriminant, new_cases, switch_node),
5170
5293
  };
5171
5294
  }
5172
5295
 
@@ -5393,7 +5516,7 @@ function wrap_jsx_setup_declarations(expression, in_jsx_child) {
5393
5516
  * This validator runs over the raw, pre-lowering attribute list so each
5394
5517
  * shape is still distinguishable by `type`. Ripple `Element` attributes have type `Attribute` with an
5395
5518
  * `Identifier` name (the parser normalizes `JSXAttribute`/`JSXIdentifier`
5396
- * for non-Tsx elements); inside `<tsx:react>` compat blocks they retain
5519
+ * for native elements); inside `<tsx:react>` compat blocks they retain
5397
5520
  * the original `JSXAttribute`/`JSXIdentifier` shape, so we accept both.
5398
5521
  *
5399
5522
  * @param {any[]} raw_attrs
@@ -5781,19 +5904,7 @@ function build_return_expression(render_nodes) {
5781
5904
  const first = render_nodes[0];
5782
5905
  const last = render_nodes[render_nodes.length - 1];
5783
5906
  return set_loc(
5784
- {
5785
- type: 'JSXFragment',
5786
- openingFragment: /** @type {any} */ ({
5787
- type: 'JSXOpeningFragment',
5788
- metadata: { path: [] },
5789
- }),
5790
- closingFragment: /** @type {any} */ ({
5791
- type: 'JSXClosingFragment',
5792
- metadata: { path: [] },
5793
- }),
5794
- children: render_nodes,
5795
- metadata: { path: [] },
5796
- },
5907
+ b.jsx_fragment(render_nodes),
5797
5908
  first?.loc && last?.loc
5798
5909
  ? {
5799
5910
  start: first.start,
@@ -5818,7 +5929,7 @@ function tsx_compat_node_to_jsx_expression(node, transform_context, in_jsx_child
5818
5929
  if (!platform.jsx.acceptedTsxKinds.includes(node.kind)) {
5819
5930
  const accepted = platform.jsx.acceptedTsxKinds.map((k) => `<tsx:${k}>`).join(', ');
5820
5931
  error(
5821
- `${platform.name} TSRX does not support <tsx:${node.kind}> blocks. Use <tsx> or one of: ${accepted}.`,
5932
+ `${platform.name} TSRX does not support <tsx:${node.kind}> blocks. Use one of: ${accepted}.`,
5822
5933
  transform_context.filename,
5823
5934
  node,
5824
5935
  transform_context.errors,