@tsrx/core 0.1.17 → 0.1.19

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';
@@ -45,7 +45,9 @@ import { prepare_stylesheet_for_render, annotate_with_hash, is_style_element } f
45
45
  import {
46
46
  collect_style_ref_attributes,
47
47
  create_style_class_map,
48
+ create_style_class_map_from_stylesheet,
48
49
  create_style_ref_setup_statements,
50
+ get_style_element_stylesheet,
49
51
  } from '../style-ref.js';
50
52
  import { is_function_or_component_node } from '../../utils/ast.js';
51
53
  import {
@@ -167,6 +169,8 @@ export function createJsxTransform(platform) {
167
169
  const collect = !!(options?.collect || options?.loose);
168
170
  /** @type {any[]} */
169
171
  const stylesheets = [];
172
+ /** @type {AST.Statement[]} */
173
+ const type_only_style_anchors = [];
170
174
 
171
175
  /** @type {TransformContext} */
172
176
  const transform_context = {
@@ -181,6 +185,7 @@ export function createJsxTransform(platform) {
181
185
  needs_for_of_iterable: false,
182
186
  needs_iteration_value_type: false,
183
187
  stylesheets,
188
+ type_only_style_anchors,
184
189
  module_scoped_hook_components:
185
190
  options?.moduleScopedHookComponents ?? !!platform.hooks?.moduleScopedHookComponents,
186
191
  helper_state: null,
@@ -202,13 +207,12 @@ export function createJsxTransform(platform) {
202
207
  preallocate_lazy_ids(/** @type {any} */ (ast), transform_context);
203
208
  }
204
209
 
205
- walk(/** @type {any} */ (ast), transform_context, {
206
- FunctionDeclaration: collect_native_function_tsrx_metadata,
207
- FunctionExpression: collect_native_function_tsrx_metadata,
208
- ArrowFunctionExpression: collect_native_function_tsrx_metadata,
209
- });
210
-
211
210
  const transformed = walk(/** @type {any} */ (ast), transform_context, {
211
+ _(node, { next, path }) {
212
+ set_node_path_metadata(node, path);
213
+ return next();
214
+ },
215
+
212
216
  Tsx(node, { next, path }) {
213
217
  const inner = /** @type {any} */ (next() ?? node);
214
218
  const in_jsx_child = in_jsx_child_context(path);
@@ -217,27 +221,24 @@ export function createJsxTransform(platform) {
217
221
  );
218
222
  },
219
223
 
220
- Tsrx(node, { next, path, state }) {
221
- /** @type {{ css: any, style_refs: any[] } | null} */
222
- let style_context = null;
223
- const inner = with_tsrx_fragment_styles(node, state, (context) => {
224
- style_context = context;
225
- return next() ?? node;
226
- });
224
+ Tsrx(node, { next, path, state, visit }) {
225
+ const parent = /** @type {AST.ArrowFunctionExpression} */ (path.at(-1));
226
+ if (parent?.metadata?.native_tsrx && parent.body === node) {
227
+ return /** @type {any} */ (visit(create_native_tsrx_render_block(node, state), state));
228
+ }
229
+
230
+ const style_context = prepare_tsrx_fragment_styles(node, state);
231
+ const target = style_context?.fragment ?? next() ?? node;
232
+ const in_jsx_child = in_jsx_child_context(path);
233
+ const expression = tsrx_node_to_jsx_expression(target, state, in_jsx_child);
227
234
  for (const statement of create_tsrx_style_ref_setup_statements(
228
- node,
235
+ target,
229
236
  style_context,
230
237
  state,
231
238
  )) {
232
- add_jsx_setup_declaration(inner, statement);
239
+ add_jsx_setup_declaration(expression, statement);
233
240
  }
234
- const in_jsx_child = in_jsx_child_context(path);
235
- return /** @type {any} */ (
236
- wrap_jsx_setup_declarations(
237
- tsrx_node_to_jsx_expression(inner, state, in_jsx_child),
238
- in_jsx_child,
239
- )
240
- );
241
+ return /** @type {any} */ (wrap_jsx_setup_declarations(expression, in_jsx_child));
241
242
  },
242
243
 
243
244
  TsxCompat(node, { next, path, state }) {
@@ -251,7 +252,16 @@ export function createJsxTransform(platform) {
251
252
  );
252
253
  },
253
254
 
254
- Element(node, { next, state }) {
255
+ Element(node, { next, path, state }) {
256
+ if (is_style_element(node) && is_style_expression_position(path)) {
257
+ const stylesheet = get_style_element_stylesheet(node);
258
+ if (stylesheet) {
259
+ analyze_css(stylesheet);
260
+ state.stylesheets.push(stylesheet);
261
+ return /** @type {any} */ (create_style_expression_value(node, stylesheet, state));
262
+ }
263
+ }
264
+
255
265
  // Capture raw children BEFORE the walker transforms them so a
256
266
  // platform hook (e.g. Solid's textContent optimization) can
257
267
  // inspect the original Text / TSRXExpression nodes rather than
@@ -277,9 +287,9 @@ export function createJsxTransform(platform) {
277
287
  return /** @type {any} */ (to_jsx_expression_container(inner.expression, inner));
278
288
  },
279
289
 
280
- // Default .metadata on every function-like node so downstream consumers
281
- // do not trip on an undefined metadata object. Ripple's analyze phase
282
- // does this via visit_function; tsrx-react has no analyze phase.
290
+ BlockStatement: transform_block_statement,
291
+ ReturnStatement: transform_return_statement,
292
+
283
293
  // If an uppercase JS function contains hook-bearing TSRX, give it a
284
294
  // temporary helper scope so extracted hook helpers get stable identities.
285
295
  FunctionDeclaration: transform_function,
@@ -292,17 +302,24 @@ export function createJsxTransform(platform) {
292
302
  return visited;
293
303
  }
294
304
  const is_component = is_component_like_jsx_name(visited.name);
295
- return {
296
- ...visited,
297
- attributes: merge_duplicate_refs(
305
+ return b.jsx_opening_element(
306
+ visited.name,
307
+ merge_duplicate_refs(
298
308
  normalize_host_ref_spreads(visited.attributes || [], !is_component, transform_context),
299
309
  transform_context,
300
310
  ),
301
- };
311
+ visited.selfClosing,
312
+ visited.typeArguments,
313
+ visited,
314
+ );
302
315
  },
303
316
  });
304
317
 
305
- const expanded = expand_component_helpers(/** @type {AST.Program} */ (transformed));
318
+ const transformed_program = /** @type {AST.Program} */ (transformed);
319
+ if (type_only_style_anchors.length > 0) {
320
+ transformed_program.body.unshift(...type_only_style_anchors);
321
+ }
322
+ const expanded = expand_component_helpers(transformed_program);
306
323
  if (platform.hooks?.injectImports) {
307
324
  platform.hooks.injectImports(expanded, transform_context, suspense_source);
308
325
  } else {
@@ -582,48 +599,56 @@ function is_interleaved_body(body_nodes) {
582
599
  * @param {TransformContext} transform_context
583
600
  * @returns {boolean}
584
601
  */
585
- function function_needs_component_body_hook_split(node, transform_context) {
602
+ function needs_hook_split(node, transform_context) {
586
603
  return (
587
604
  transform_context.platform.hooks?.componentBodyHookHelpers === true &&
588
605
  node.body?.type === 'BlockStatement' &&
589
- find_component_body_hook_split_index(node.body.body || [], transform_context) !== -1
606
+ find_hook_split_index(node.body.body || [], transform_context) !== -1
590
607
  );
591
608
  }
592
609
 
593
610
  /**
594
611
  * @param {any} node
595
612
  * @param {TransformContext} transform_context
596
- * @returns {void}
613
+ * @returns {any}
597
614
  */
598
- function rewrite_component_body_conditional_hook_splits(node, transform_context) {
615
+ function create_hook_split_block(node, transform_context) {
599
616
  if (
600
617
  transform_context.platform.hooks?.componentBodyHookHelpers !== true ||
601
618
  !should_extract_hook_helpers(transform_context) ||
602
619
  node.body?.type !== 'BlockStatement'
603
620
  ) {
604
- return;
621
+ return null;
605
622
  }
606
623
 
607
624
  const body = node.body.body || [];
608
- const split_index = find_component_body_hook_split_index(body, transform_context);
625
+ const split_index = find_hook_split_index(body, transform_context);
609
626
  if (split_index === -1) {
610
- return;
627
+ return null;
611
628
  }
612
629
 
613
630
  const split_statement = body[split_index];
614
631
  const continuation_body = body.slice(split_index + 1);
615
632
  const helper = create_hook_safe_helper(
616
- continuation_body,
633
+ expand_native_tsrx_return_statement_list(continuation_body, transform_context),
617
634
  undefined,
618
635
  get_body_source_node(continuation_body) || split_statement,
619
636
  transform_context,
620
637
  );
621
638
 
622
- node.body.body = [
623
- ...body.slice(0, split_index + 1),
624
- ...helper.setup_statements,
625
- set_loc(b.return(helper.component_element), split_statement),
626
- ];
639
+ const block = b.block(
640
+ [
641
+ ...body.slice(0, split_index + 1),
642
+ ...helper.setup_statements,
643
+ set_loc(b.return(helper.component_element), split_statement),
644
+ ],
645
+ node.body,
646
+ );
647
+ block.metadata = {
648
+ ...(block.metadata || {}),
649
+ hook_split_block: true,
650
+ };
651
+ return block;
627
652
  }
628
653
 
629
654
  /**
@@ -631,7 +656,7 @@ function rewrite_component_body_conditional_hook_splits(node, transform_context)
631
656
  * @param {TransformContext} transform_context
632
657
  * @returns {number}
633
658
  */
634
- function find_component_body_hook_split_index(body_nodes, transform_context) {
659
+ function find_hook_split_index(body_nodes, transform_context) {
635
660
  for (let i = 0; i < body_nodes.length; i += 1) {
636
661
  if (!is_component_body_conditional_return_statement(body_nodes[i])) {
637
662
  continue;
@@ -934,22 +959,82 @@ function create_helper_state(base_name) {
934
959
  };
935
960
  }
936
961
 
962
+ /**
963
+ * @param {{ helpers: any[], statics: any[] }} helper_state
964
+ * @returns {{ generated_helpers: any[], generated_statics: any[] } | null}
965
+ */
966
+ function create_generated_helper_metadata(helper_state) {
967
+ if (helper_state.helpers.length === 0 && helper_state.statics.length === 0) {
968
+ return null;
969
+ }
970
+ return {
971
+ generated_helpers: helper_state.helpers,
972
+ generated_statics: helper_state.statics,
973
+ };
974
+ }
975
+
976
+ /**
977
+ * @param {any} metadata
978
+ * @returns {any}
979
+ */
980
+ function strip_function_transform_metadata(metadata) {
981
+ const { native_tsrx, hook_split, ...next_metadata } = metadata || {};
982
+ return next_metadata;
983
+ }
984
+
985
+ /**
986
+ * @param {AST.BlockStatement} node
987
+ * @param {{ next: () => any, visit: (node: any, state?: TransformContext) => any, state: TransformContext, path: AST.Node[] }} context
988
+ * @returns {any}
989
+ */
990
+ function transform_block_statement(node, { next, visit, state, path }) {
991
+ if (node.metadata?.hook_split_block || node.metadata?.native_return_block) {
992
+ return next() ?? node;
993
+ }
994
+
995
+ const parent = /** @type {any} */ (path.at(-1));
996
+ if (parent?.metadata?.hook_split && parent.body === node) {
997
+ const block = create_hook_split_block(parent, state);
998
+ if (block) {
999
+ return visit(block, state);
1000
+ }
1001
+ }
1002
+
1003
+ if (get_active_native_tsrx_function(path)) {
1004
+ const block = create_native_tsrx_statement_list_block(node, state);
1005
+ if (block) {
1006
+ return visit(block, state);
1007
+ }
1008
+ }
1009
+
1010
+ return next() ?? node;
1011
+ }
1012
+
937
1013
  /**
938
1014
  * @param {any} node
939
- * @param {{ next: (state?: TransformContext) => any, state: TransformContext }} context
1015
+ * @param {{ next: () => any, visit: (node: any, state?: TransformContext) => any, state: TransformContext, path: AST.Node[] }} context
940
1016
  * @returns {any}
941
1017
  */
942
- function collect_native_function_tsrx_metadata(node, { next, state }) {
943
- if (!function_has_native_tsrx_return(node)) {
944
- return next(state);
1018
+ function transform_return_statement(node, { next, visit, state, path }) {
1019
+ if (get_active_native_tsrx_function(path) && node.argument?.type === 'Tsrx') {
1020
+ return visit(create_native_tsrx_render_block(node.argument, state), state);
945
1021
  }
946
1022
 
947
- node.metadata = {
948
- ...(node.metadata || {}),
949
- native_tsrx_function: true,
950
- };
1023
+ return next() ?? node;
1024
+ }
951
1025
 
952
- return next(state);
1026
+ /**
1027
+ * @param {AST.Node[]} path
1028
+ * @returns {any | null}
1029
+ */
1030
+ function get_active_native_tsrx_function(path) {
1031
+ for (let i = path.length - 1; i >= 0; i -= 1) {
1032
+ const node = /** @type {any} */ (path[i]);
1033
+ if (is_function_or_class_boundary(node)) {
1034
+ return node.metadata?.native_tsrx ? node : null;
1035
+ }
1036
+ }
1037
+ return null;
953
1038
  }
954
1039
 
955
1040
  /**
@@ -967,47 +1052,49 @@ function transform_function(node, context) {
967
1052
 
968
1053
  /**
969
1054
  * @param {any} node
970
- * @param {{ next: () => any, state: TransformContext, path: AST.Node[] }} context
1055
+ * @param {{ next: () => any, state: TransformContext }} context
971
1056
  * @returns {any}
972
1057
  */
973
- function transform_native_tsrx_function(node, { next, state, path }) {
1058
+ function transform_native_tsrx_function(node, { next, state }) {
974
1059
  const helper_state =
975
- state.helper_state || create_helper_state(get_function_helper_base_name(node, path));
1060
+ state.helper_state || create_helper_state(get_function_helper_base_name(node));
976
1061
  const saved_helper_state = state.helper_state;
977
1062
  const saved_bindings = state.available_bindings;
978
1063
  const saved_hook_helpers_enabled = state.hook_helpers_enabled;
979
1064
 
980
1065
  state.helper_state = helper_state;
981
- state.hook_helpers_enabled = is_uppercase_function_like(node, path);
1066
+ state.hook_helpers_enabled = is_uppercase_function_like(node);
1067
+ node.metadata = {
1068
+ ...(node.metadata || {}),
1069
+ native_tsrx: true,
1070
+ ...(needs_hook_split(node, state) ? { hook_split: true } : {}),
1071
+ };
982
1072
  state.available_bindings = merge_binding_maps(
983
1073
  saved_bindings,
984
1074
  collect_function_scope_bindings(node),
985
1075
  );
986
1076
 
987
- validate_native_tsrx_function_await(node, state);
988
- expand_native_tsrx_function_returns(node, state);
989
- rewrite_component_body_conditional_hook_splits(node, state);
1077
+ validate_native_await(node, state);
990
1078
 
991
1079
  const inner = /** @type {any} */ (next() ?? node);
1080
+ if (
1081
+ inner !== node &&
1082
+ node.type === 'ArrowFunctionExpression' &&
1083
+ node.body?.type === 'Tsrx' &&
1084
+ inner.body?.type === 'BlockStatement'
1085
+ ) {
1086
+ inner.expression = false;
1087
+ }
992
1088
 
993
1089
  state.helper_state = saved_helper_state;
994
1090
  state.available_bindings = saved_bindings;
995
1091
  state.hook_helpers_enabled = saved_hook_helpers_enabled;
996
1092
 
997
- ensure_function_metadata(inner, { next: () => inner });
998
1093
  inner.metadata = {
999
- ...(inner.metadata || {}),
1094
+ ...strip_function_transform_metadata(inner.metadata),
1000
1095
  native_tsrx_function: true,
1096
+ ...(!saved_helper_state ? create_generated_helper_metadata(helper_state) || {} : {}),
1001
1097
  };
1002
- if (!saved_helper_state && (helper_state.helpers.length || helper_state.statics.length)) {
1003
- inner.metadata.generated_helpers = helper_state.helpers;
1004
- inner.metadata.generated_statics = helper_state.statics;
1005
- }
1006
-
1007
- const wrapped = state.platform.hooks?.wrapNativeFunctionComponent?.(inner, state, path);
1008
- if (wrapped) {
1009
- return wrapped;
1010
- }
1011
1098
 
1012
1099
  return inner;
1013
1100
  }
@@ -1017,8 +1104,8 @@ function transform_native_tsrx_function(node, { next, state, path }) {
1017
1104
  * @param {TransformContext} transform_context
1018
1105
  * @returns {void}
1019
1106
  */
1020
- function validate_native_tsrx_function_await(node, transform_context) {
1021
- const await_node = find_first_top_level_await_in_native_tsrx_function(node);
1107
+ function validate_native_await(node, transform_context) {
1108
+ const await_node = find_native_await(node);
1022
1109
  if (!await_node) {
1023
1110
  return;
1024
1111
  }
@@ -1044,7 +1131,7 @@ function validate_native_tsrx_function_await(node, transform_context) {
1044
1131
  * @param {any} node
1045
1132
  * @returns {any | null}
1046
1133
  */
1047
- function find_first_top_level_await_in_native_tsrx_function(node) {
1134
+ function find_native_await(node) {
1048
1135
  if (
1049
1136
  node.type === 'ArrowFunctionExpression' &&
1050
1137
  node.body?.type !== 'BlockStatement' &&
@@ -1054,16 +1141,16 @@ function find_first_top_level_await_in_native_tsrx_function(node) {
1054
1141
  }
1055
1142
 
1056
1143
  const body = node.body?.type === 'BlockStatement' ? node.body.body || [] : [];
1057
- return find_first_top_level_await_in_native_tsrx_statements(body);
1144
+ return find_native_await_in_list(body);
1058
1145
  }
1059
1146
 
1060
1147
  /**
1061
1148
  * @param {any[]} statements
1062
1149
  * @returns {any | null}
1063
1150
  */
1064
- function find_first_top_level_await_in_native_tsrx_statements(statements) {
1151
+ function find_native_await_in_list(statements) {
1065
1152
  for (const statement of statements) {
1066
- const found = find_first_top_level_await_in_native_tsrx_statement(statement);
1153
+ const found = find_native_await_in_statement(statement);
1067
1154
  if (found) return found;
1068
1155
  }
1069
1156
  return null;
@@ -1073,7 +1160,7 @@ function find_first_top_level_await_in_native_tsrx_statements(statements) {
1073
1160
  * @param {any} statement
1074
1161
  * @returns {any | null}
1075
1162
  */
1076
- function find_first_top_level_await_in_native_tsrx_statement(statement) {
1163
+ function find_native_await_in_statement(statement) {
1077
1164
  if (!statement || typeof statement !== 'object') return null;
1078
1165
 
1079
1166
  if (statement.type === 'ReturnStatement' && statement.argument?.type === 'Tsrx') {
@@ -1092,21 +1179,19 @@ function find_first_top_level_await_in_native_tsrx_statement(statement) {
1092
1179
  }
1093
1180
 
1094
1181
  if (statement.type === 'BlockStatement') {
1095
- return find_first_top_level_await_in_native_tsrx_statements(statement.body || []);
1182
+ return find_native_await_in_list(statement.body || []);
1096
1183
  }
1097
1184
 
1098
1185
  if (statement.type === 'IfStatement') {
1099
1186
  return (
1100
- find_first_top_level_await_in_native_tsrx_statement(statement.consequent) ||
1101
- find_first_top_level_await_in_native_tsrx_statement(statement.alternate)
1187
+ find_native_await_in_statement(statement.consequent) ||
1188
+ find_native_await_in_statement(statement.alternate)
1102
1189
  );
1103
1190
  }
1104
1191
 
1105
1192
  if (statement.type === 'SwitchStatement') {
1106
1193
  for (const switch_case of statement.cases || []) {
1107
- const found = find_first_top_level_await_in_native_tsrx_statements(
1108
- switch_case.consequent || [],
1109
- );
1194
+ const found = find_native_await_in_list(switch_case.consequent || []);
1110
1195
  if (found) return found;
1111
1196
  }
1112
1197
  return null;
@@ -1114,9 +1199,9 @@ function find_first_top_level_await_in_native_tsrx_statement(statement) {
1114
1199
 
1115
1200
  if (statement.type === 'TryStatement') {
1116
1201
  return (
1117
- find_first_top_level_await_in_native_tsrx_statement(statement.block) ||
1118
- find_first_top_level_await_in_native_tsrx_statement(statement.handler?.body) ||
1119
- find_first_top_level_await_in_native_tsrx_statement(statement.finalizer)
1202
+ find_native_await_in_statement(statement.block) ||
1203
+ find_native_await_in_statement(statement.handler?.body) ||
1204
+ find_native_await_in_statement(statement.finalizer)
1120
1205
  );
1121
1206
  }
1122
1207
 
@@ -1125,32 +1210,34 @@ function find_first_top_level_await_in_native_tsrx_statement(statement) {
1125
1210
 
1126
1211
  /**
1127
1212
  * @param {any} node
1128
- * @param {{ next: () => any, state: TransformContext, path: AST.Node[] }} context
1213
+ * @param {{ next: () => any, state: TransformContext }} context
1129
1214
  * @returns {any}
1130
1215
  */
1131
- function transform_function_with_hook_helpers(node, { next, state, path }) {
1216
+ function transform_function_with_hook_helpers(node, { next, state }) {
1132
1217
  const has_hook_bearing_tsrx = function_contains_hook_bearing_tsrx(node, state);
1133
- const has_component_body_hook_split = function_needs_component_body_hook_split(node, state);
1218
+ const has_hook_split = needs_hook_split(node, state);
1134
1219
  if (
1135
1220
  state.helper_state ||
1136
- !is_uppercase_function_like(node, path) ||
1137
- (!has_hook_bearing_tsrx && !has_component_body_hook_split)
1221
+ !is_uppercase_function_like(node) ||
1222
+ (!has_hook_bearing_tsrx && !has_hook_split)
1138
1223
  ) {
1139
- return ensure_function_metadata(node, { next });
1224
+ return next() ?? node;
1140
1225
  }
1141
1226
 
1142
- const helper_state = create_helper_state(get_function_helper_base_name(node, path));
1227
+ const helper_state = create_helper_state(get_function_helper_base_name(node));
1143
1228
  const saved_helper_state = state.helper_state;
1144
1229
  const saved_bindings = state.available_bindings;
1145
1230
  const saved_hook_helpers_enabled = state.hook_helpers_enabled;
1146
1231
 
1147
1232
  state.helper_state = helper_state;
1148
1233
  state.hook_helpers_enabled = true;
1149
- state.available_bindings = collect_function_scope_bindings(node);
1150
-
1151
- if (has_component_body_hook_split) {
1152
- rewrite_component_body_conditional_hook_splits(node, state);
1234
+ if (has_hook_split) {
1235
+ node.metadata = {
1236
+ ...(node.metadata || {}),
1237
+ hook_split: true,
1238
+ };
1153
1239
  }
1240
+ state.available_bindings = collect_function_scope_bindings(node);
1154
1241
 
1155
1242
  const inner = /** @type {any} */ (next() ?? node);
1156
1243
 
@@ -1158,48 +1245,41 @@ function transform_function_with_hook_helpers(node, { next, state, path }) {
1158
1245
  state.available_bindings = saved_bindings;
1159
1246
  state.hook_helpers_enabled = saved_hook_helpers_enabled;
1160
1247
 
1161
- ensure_function_metadata(inner, { next: () => inner });
1162
- if (helper_state.helpers.length || helper_state.statics.length) {
1163
- inner.metadata = {
1164
- ...(inner.metadata || {}),
1165
- generated_helpers: helper_state.helpers,
1166
- generated_statics: helper_state.statics,
1167
- };
1168
- }
1248
+ inner.metadata = {
1249
+ ...strip_function_transform_metadata(inner.metadata),
1250
+ ...(create_generated_helper_metadata(helper_state) || {}),
1251
+ };
1169
1252
 
1170
1253
  return inner;
1171
1254
  }
1172
1255
 
1173
1256
  /**
1174
1257
  * @param {any} node
1175
- * @param {AST.Node[]} [path]
1176
1258
  * @returns {string}
1177
1259
  */
1178
- function get_function_helper_base_name(node, path = []) {
1179
- return get_function_like_name(node, path) || 'Tsrx';
1260
+ function get_function_helper_base_name(node) {
1261
+ return get_function_like_name(node) || 'Tsrx';
1180
1262
  }
1181
1263
 
1182
1264
  /**
1183
1265
  * @param {any} node
1184
- * @param {AST.Node[]} path
1185
1266
  * @returns {boolean}
1186
1267
  */
1187
- function is_uppercase_function_like(node, path) {
1188
- const name = get_function_like_name(node, path);
1268
+ function is_uppercase_function_like(node) {
1269
+ const name = get_function_like_name(node);
1189
1270
  return !!(name && /^[A-Z]/.test(name));
1190
1271
  }
1191
1272
 
1192
1273
  /**
1193
1274
  * @param {any} node
1194
- * @param {AST.Node[]} path
1195
1275
  * @returns {string | null}
1196
1276
  */
1197
- function get_function_like_name(node, path) {
1277
+ function get_function_like_name(node) {
1198
1278
  if (node.id?.type === 'Identifier') {
1199
1279
  return node.id.name;
1200
1280
  }
1201
1281
 
1202
- const parent = /** @type {any} */ (path.at(-1));
1282
+ const parent = /** @type {any} */ (node.metadata?.path?.at(-1));
1203
1283
  if (!parent) return null;
1204
1284
 
1205
1285
  if (parent.type === 'VariableDeclarator' && parent.init === node) {
@@ -1408,7 +1488,7 @@ function collect_tsrx_stylesheet(node) {
1408
1488
  /**
1409
1489
  * @param {any} node
1410
1490
  * @param {TransformContext} transform_context
1411
- * @returns {{ css: any, style_refs: any[] } | null}
1491
+ * @returns {{ css: any, style_refs: any[], fragment: any } | null}
1412
1492
  */
1413
1493
  function prepare_tsrx_fragment_styles(node, transform_context) {
1414
1494
  const css = collect_tsrx_stylesheet(node);
@@ -1417,20 +1497,21 @@ function prepare_tsrx_fragment_styles(node, transform_context) {
1417
1497
  const style_refs = collect_style_ref_attributes(node);
1418
1498
  apply_css_definition_metadata(node, css, style_refs.length > 0);
1419
1499
  transform_context.stylesheets.push(css);
1420
- annotate_tsrx_with_hash(
1500
+ const fragment = annotate_tsrx_with_hash(
1421
1501
  node,
1422
1502
  css.hash,
1423
- transform_context.platform.jsx.rewriteClassAttr ? 'className' : 'class',
1503
+ transform_context.platform.jsx.classAttrName ??
1504
+ (transform_context.platform.jsx.rewriteClassAttr ? 'className' : 'class'),
1424
1505
  transform_context.typeOnly,
1425
1506
  );
1426
- return { css, style_refs };
1507
+ return { css, style_refs, fragment };
1427
1508
  }
1428
1509
 
1429
1510
  /**
1430
1511
  * @template T
1431
1512
  * @param {any} node
1432
1513
  * @param {TransformContext} transform_context
1433
- * @param {(style_context: { css: any, style_refs: any[] } | null) => T} callback
1514
+ * @param {(style_context: { css: any, style_refs: any[], fragment: any } | null) => T} callback
1434
1515
  * @returns {T}
1435
1516
  */
1436
1517
  function with_tsrx_fragment_styles(node, transform_context, callback) {
@@ -1440,7 +1521,7 @@ function with_tsrx_fragment_styles(node, transform_context, callback) {
1440
1521
 
1441
1522
  /**
1442
1523
  * @param {any} fragment
1443
- * @param {{ css: any, style_refs: any[] } | null} style_context
1524
+ * @param {{ css: any, style_refs: any[], fragment: any } | null} style_context
1444
1525
  * @param {TransformContext} transform_context
1445
1526
  * @returns {AST.Statement[]}
1446
1527
  */
@@ -1460,6 +1541,64 @@ function create_tsrx_style_ref_setup_statements(fragment, style_context, transfo
1460
1541
  );
1461
1542
  }
1462
1543
 
1544
+ /**
1545
+ * @param {any} node
1546
+ * @param {any} stylesheet
1547
+ * @param {TransformContext} transform_context
1548
+ * @returns {AST.Expression}
1549
+ */
1550
+ function create_style_expression_value(node, stylesheet, transform_context) {
1551
+ const class_map = create_style_class_map_from_stylesheet(stylesheet);
1552
+ if (!transform_context.typeOnly) {
1553
+ return class_map;
1554
+ }
1555
+
1556
+ add_type_only_style_anchor(node, transform_context);
1557
+ return class_map;
1558
+ }
1559
+
1560
+ /**
1561
+ * @param {any} node
1562
+ * @param {TransformContext} transform_context
1563
+ */
1564
+ function add_type_only_style_anchor(node, transform_context) {
1565
+ const style_anchor = b.jsx_element(clone_expression_node(node, true), [], []);
1566
+ disable_style_anchor_verification(style_anchor);
1567
+
1568
+ const anchor_id = create_generated_identifier(create_style_anchor_name(transform_context));
1569
+ transform_context.type_only_style_anchors.push(
1570
+ b.const(anchor_id, style_anchor),
1571
+ b.stmt(clone_identifier(anchor_id)),
1572
+ );
1573
+ }
1574
+
1575
+ /**
1576
+ * @param {TransformContext} transform_context
1577
+ * @returns {string}
1578
+ */
1579
+ function create_style_anchor_name(transform_context) {
1580
+ transform_context.local_statement_component_index += 1;
1581
+ return `_tsrx_style_anchor_${transform_context.local_statement_component_index}`;
1582
+ }
1583
+
1584
+ /**
1585
+ * @param {ESTreeJSX.JSXElement} element
1586
+ */
1587
+ function disable_style_anchor_verification(element) {
1588
+ if (element.openingElement?.name) {
1589
+ element.openingElement.name.metadata = {
1590
+ ...(element.openingElement.name.metadata || {}),
1591
+ disable_verification: true,
1592
+ };
1593
+ }
1594
+ if (element.closingElement?.name) {
1595
+ element.closingElement.name.metadata = {
1596
+ ...(element.closingElement.name.metadata || {}),
1597
+ disable_verification: true,
1598
+ };
1599
+ }
1600
+ }
1601
+
1463
1602
  /**
1464
1603
  * @param {TransformContext} transform_context
1465
1604
  * @returns {string}
@@ -1502,11 +1641,33 @@ function collect_style_elements(node, styles) {
1502
1641
  return;
1503
1642
  }
1504
1643
 
1505
- for (const key of Object.keys(node)) {
1506
- if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
1507
- continue;
1644
+ if (node.type === 'Element') {
1645
+ collect_style_elements(node.children || [], styles);
1646
+ return;
1647
+ }
1648
+
1649
+ if (node.type === 'BlockStatement') {
1650
+ collect_style_elements(node.body || [], styles);
1651
+ return;
1652
+ }
1653
+
1654
+ if (node.type === 'IfStatement') {
1655
+ collect_style_elements(node.consequent, styles);
1656
+ collect_style_elements(node.alternate, styles);
1657
+ return;
1658
+ }
1659
+
1660
+ if (node.type === 'SwitchStatement') {
1661
+ for (const switch_case of node.cases || []) {
1662
+ collect_style_elements(switch_case.consequent || [], styles);
1508
1663
  }
1509
- collect_style_elements(node[key], styles);
1664
+ return;
1665
+ }
1666
+
1667
+ if (node.type === 'TryStatement') {
1668
+ collect_style_elements(node.block, styles);
1669
+ collect_style_elements(node.handler?.body, styles);
1670
+ collect_style_elements(node.finalizer, styles);
1510
1671
  }
1511
1672
  }
1512
1673
 
@@ -1515,15 +1676,22 @@ function collect_style_elements(node, styles) {
1515
1676
  * @param {string} hash
1516
1677
  * @param {'class' | 'className'} jsx_class_attr_name
1517
1678
  * @param {boolean} preserve_style_elements
1518
- * @returns {void}
1679
+ * @returns {any}
1519
1680
  */
1520
1681
  function annotate_tsrx_with_hash(node, hash, jsx_class_attr_name, preserve_style_elements) {
1521
- node.children = (node.children || []).map((/** @type {any} */ statement) =>
1522
- annotate_with_hash(statement, hash, jsx_class_attr_name, preserve_style_elements),
1682
+ const annotated = { ...node };
1683
+ annotated.children = (node.children || []).map((/** @type {any} */ statement) =>
1684
+ annotate_with_hash(
1685
+ clone_expression_node(statement),
1686
+ hash,
1687
+ jsx_class_attr_name,
1688
+ preserve_style_elements,
1689
+ ),
1523
1690
  );
1524
1691
  if (!preserve_style_elements) {
1525
- node.children = strip_style_elements(node.children);
1692
+ annotated.children = strip_style_elements(annotated.children);
1526
1693
  }
1694
+ return annotated;
1527
1695
  }
1528
1696
 
1529
1697
  /**
@@ -1548,51 +1716,108 @@ function strip_style_elements(node) {
1548
1716
  return node;
1549
1717
  }
1550
1718
 
1551
- for (const key of Object.keys(node)) {
1552
- if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata' || key === 'css') {
1553
- continue;
1554
- }
1555
- const value = node[key];
1556
- if (Array.isArray(value)) {
1557
- node[key] = strip_style_elements(value);
1558
- } else if (value && typeof value === 'object') {
1559
- const stripped = strip_style_elements(value);
1560
- if (stripped) {
1561
- node[key] = stripped;
1562
- }
1719
+ if (node.type === 'Element') {
1720
+ node.children = strip_style_elements(node.children || []);
1721
+ return node;
1722
+ }
1723
+
1724
+ if (node.type === 'BlockStatement') {
1725
+ node.body = strip_style_elements(node.body || []);
1726
+ return node;
1727
+ }
1728
+
1729
+ if (node.type === 'IfStatement') {
1730
+ node.consequent = strip_style_elements(node.consequent);
1731
+ if (node.alternate) node.alternate = strip_style_elements(node.alternate);
1732
+ return node;
1733
+ }
1734
+
1735
+ if (node.type === 'SwitchStatement') {
1736
+ for (const switch_case of node.cases || []) {
1737
+ switch_case.consequent = strip_style_elements(switch_case.consequent || []);
1563
1738
  }
1739
+ return node;
1740
+ }
1741
+
1742
+ if (node.type === 'TryStatement') {
1743
+ node.block = strip_style_elements(node.block);
1744
+ if (node.handler?.body) node.handler.body = strip_style_elements(node.handler.body);
1745
+ if (node.finalizer) node.finalizer = strip_style_elements(node.finalizer);
1564
1746
  }
1565
1747
 
1566
1748
  return node;
1567
1749
  }
1568
1750
 
1569
1751
  /**
1570
- * @param {any} node
1752
+ * @param {any[]} path
1753
+ * @returns {boolean}
1754
+ */
1755
+ function is_style_expression_position(path) {
1756
+ const parent = path.at(-1);
1757
+ return !(
1758
+ parent?.type === 'Element' ||
1759
+ parent?.type === 'Tsrx' ||
1760
+ parent?.type === 'Tsx' ||
1761
+ parent?.type === 'TsxCompat' ||
1762
+ parent?.type === 'BlockStatement' ||
1763
+ parent?.type === 'Program' ||
1764
+ parent?.type === 'SwitchCase'
1765
+ );
1766
+ }
1767
+
1768
+ /**
1769
+ * @param {any} fragment
1571
1770
  * @param {TransformContext} transform_context
1572
- * @returns {void}
1771
+ * @returns {any}
1573
1772
  */
1574
- function expand_native_tsrx_function_returns(node, transform_context) {
1575
- if (node.type === 'ArrowFunctionExpression' && node.body?.type === 'Tsrx') {
1576
- const body = node.body;
1577
- const statements = with_tsrx_fragment_styles(body, transform_context, (style_context) => {
1578
- return [
1579
- ...create_tsrx_style_ref_setup_statements(body, style_context, transform_context),
1580
- ...build_render_statements(get_tsrx_render_children(body), true, transform_context),
1581
- ];
1582
- });
1583
- node.body = b.block(mark_native_pretransformed_jsx(statements), body);
1584
- node.expression = false;
1585
- return;
1586
- }
1773
+ function create_native_tsrx_render_block(fragment, transform_context) {
1774
+ const block = b.block(
1775
+ mark_native_pretransformed_jsx(
1776
+ create_native_tsrx_render_statements(fragment, transform_context),
1777
+ ),
1778
+ fragment,
1779
+ );
1780
+ block.metadata = {
1781
+ ...(block.metadata || {}),
1782
+ native_return_block: true,
1783
+ };
1784
+ return block;
1785
+ }
1587
1786
 
1588
- if (node.body?.type !== 'BlockStatement') {
1589
- return;
1787
+ /**
1788
+ * @param {any} block
1789
+ * @param {TransformContext} transform_context
1790
+ * @returns {any | null}
1791
+ */
1792
+ function create_native_tsrx_statement_list_block(block, transform_context) {
1793
+ const source_body = block.body || [];
1794
+ const body = expand_native_tsrx_return_statement_list(source_body, transform_context);
1795
+
1796
+ if (body === source_body) {
1797
+ return null;
1590
1798
  }
1591
1799
 
1592
- node.body.body = expand_native_tsrx_return_statement_list(
1593
- node.body.body || [],
1594
- transform_context,
1595
- );
1800
+ const next_block = b.block(mark_native_pretransformed_jsx(body), block);
1801
+ next_block.metadata = {
1802
+ ...(next_block.metadata || {}),
1803
+ native_return_block: true,
1804
+ };
1805
+ return next_block;
1806
+ }
1807
+
1808
+ /**
1809
+ * @param {any} fragment
1810
+ * @param {TransformContext} transform_context
1811
+ * @returns {AST.Statement[]}
1812
+ */
1813
+ function create_native_tsrx_render_statements(fragment, transform_context) {
1814
+ return with_tsrx_fragment_styles(fragment, transform_context, (style_context) => {
1815
+ const target = style_context?.fragment ?? fragment;
1816
+ return [
1817
+ ...create_tsrx_style_ref_setup_statements(target, style_context, transform_context),
1818
+ ...build_render_statements(get_tsrx_render_children(target), true, transform_context),
1819
+ ];
1820
+ });
1596
1821
  }
1597
1822
 
1598
1823
  /**
@@ -1601,9 +1826,15 @@ function expand_native_tsrx_function_returns(node, transform_context) {
1601
1826
  * @returns {any[]}
1602
1827
  */
1603
1828
  function expand_native_tsrx_return_statement_list(statements, transform_context) {
1604
- return statements.flatMap((statement) =>
1605
- expand_native_tsrx_return_statement(statement, transform_context),
1606
- );
1829
+ let changed = false;
1830
+ const next_statements = statements.flatMap((statement) => {
1831
+ const result = expand_native_tsrx_return_statement(statement, transform_context);
1832
+ if (result.length !== 1 || result[0] !== statement) {
1833
+ changed = true;
1834
+ }
1835
+ return result;
1836
+ });
1837
+ return changed ? next_statements : statements;
1607
1838
  }
1608
1839
 
1609
1840
  /**
@@ -1615,13 +1846,7 @@ function expand_native_tsrx_return_statement(statement, transform_context) {
1615
1846
  if (!statement || typeof statement !== 'object') return [statement];
1616
1847
 
1617
1848
  if (statement.type === 'ReturnStatement' && statement.argument?.type === 'Tsrx') {
1618
- const fragment = statement.argument;
1619
- return with_tsrx_fragment_styles(fragment, transform_context, (style_context) => {
1620
- return mark_native_pretransformed_jsx([
1621
- ...create_tsrx_style_ref_setup_statements(fragment, style_context, transform_context),
1622
- ...build_render_statements(get_tsrx_render_children(fragment), true, transform_context),
1623
- ]);
1624
- });
1849
+ return create_native_tsrx_render_statements(statement.argument, transform_context);
1625
1850
  }
1626
1851
 
1627
1852
  if (is_function_or_class_boundary(statement)) {
@@ -1629,52 +1854,65 @@ function expand_native_tsrx_return_statement(statement, transform_context) {
1629
1854
  }
1630
1855
 
1631
1856
  if (statement.type === 'BlockStatement') {
1632
- statement.body = expand_native_tsrx_return_statement_list(
1633
- statement.body || [],
1634
- transform_context,
1635
- );
1636
- return [statement];
1857
+ const body = expand_native_tsrx_return_statement_list(statement.body || [], transform_context);
1858
+ return body === statement.body ? [statement] : [b.block(body, statement)];
1637
1859
  }
1638
1860
 
1639
1861
  if (statement.type === 'IfStatement') {
1640
- statement.consequent = expand_embedded_native_return_statement(
1862
+ const consequent = expand_embedded_native_return_statement(
1641
1863
  statement.consequent,
1642
1864
  transform_context,
1643
1865
  );
1644
- if (statement.alternate) {
1645
- statement.alternate = expand_embedded_native_return_statement(
1646
- statement.alternate,
1647
- transform_context,
1648
- );
1866
+ const alternate = statement.alternate
1867
+ ? expand_embedded_native_return_statement(statement.alternate, transform_context)
1868
+ : statement.alternate;
1869
+ if (consequent === statement.consequent && alternate === statement.alternate) {
1870
+ return [statement];
1649
1871
  }
1650
- return [statement];
1872
+ return [set_loc(b.if(statement.test, consequent, alternate), statement)];
1651
1873
  }
1652
1874
 
1653
1875
  if (statement.type === 'SwitchStatement') {
1654
- for (const switch_case of statement.cases || []) {
1655
- switch_case.consequent = expand_native_tsrx_return_statement_list(
1876
+ let changed = false;
1877
+ const cases = (statement.cases || []).map((/** @type {any} */ switch_case) => {
1878
+ const consequent = expand_native_tsrx_return_statement_list(
1656
1879
  switch_case.consequent || [],
1657
1880
  transform_context,
1658
1881
  );
1659
- }
1660
- return [statement];
1882
+ if (consequent === switch_case.consequent) {
1883
+ return switch_case;
1884
+ }
1885
+ changed = true;
1886
+ return set_loc(b.switch_case(switch_case.test, consequent), switch_case);
1887
+ });
1888
+ return changed ? [set_loc(b.switch(statement.discriminant, cases), statement)] : [statement];
1661
1889
  }
1662
1890
 
1663
1891
  if (statement.type === 'TryStatement') {
1664
- statement.block = expand_embedded_native_return_statement(statement.block, transform_context);
1665
- if (statement.handler?.body) {
1666
- statement.handler.body = expand_embedded_native_return_statement(
1667
- statement.handler.body,
1668
- transform_context,
1669
- );
1670
- }
1671
- if (statement.finalizer) {
1672
- statement.finalizer = expand_embedded_native_return_statement(
1673
- statement.finalizer,
1674
- transform_context,
1675
- );
1676
- }
1677
- return [statement];
1892
+ const block = expand_embedded_native_return_statement(statement.block, transform_context);
1893
+ const handler_body = statement.handler?.body
1894
+ ? expand_embedded_native_return_statement(statement.handler.body, transform_context)
1895
+ : statement.handler?.body;
1896
+ const finalizer = statement.finalizer
1897
+ ? expand_embedded_native_return_statement(statement.finalizer, transform_context)
1898
+ : statement.finalizer;
1899
+ if (
1900
+ block === statement.block &&
1901
+ handler_body === statement.handler?.body &&
1902
+ finalizer === statement.finalizer
1903
+ ) {
1904
+ return [statement];
1905
+ }
1906
+ const handler =
1907
+ statement.handler && handler_body !== statement.handler.body
1908
+ ? b.catch_clause(
1909
+ statement.handler.param,
1910
+ statement.handler.resetParam,
1911
+ handler_body,
1912
+ statement.handler,
1913
+ )
1914
+ : statement.handler;
1915
+ return [set_loc(b.try(block, handler, finalizer, statement.pending ?? null), statement)];
1678
1916
  }
1679
1917
 
1680
1918
  return [statement];
@@ -2580,15 +2818,7 @@ function to_jsx_element(node, transform_context, raw_children = node.children ||
2580
2818
  if (node.type === 'JSXElement') return node;
2581
2819
  if (!node.id) {
2582
2820
  report_jsx_fragment_in_tsrx_error(node, transform_context);
2583
- return set_loc(
2584
- /** @type {any} */ ({
2585
- type: 'JSXFragment',
2586
- openingFragment: { type: 'JSXOpeningFragment' },
2587
- closingFragment: { type: 'JSXClosingFragment' },
2588
- children: [],
2589
- }),
2590
- node,
2591
- );
2821
+ return set_loc(b.jsx_fragment(), node);
2592
2822
  }
2593
2823
  if (is_dynamic_element_id(node.id)) {
2594
2824
  return dynamic_element_to_jsx_child(node, transform_context);
@@ -2642,7 +2872,11 @@ function to_jsx_element(node, transform_context, raw_children = node.children ||
2642
2872
  node.closingElement || node,
2643
2873
  );
2644
2874
 
2645
- return set_loc(b.jsx_element_fresh(openingElement, closingElement, children), node);
2875
+ const element = set_loc(b.jsx_element_fresh(openingElement, closingElement, children), node);
2876
+ if (transform_context.typeOnly && is_style_element(node)) {
2877
+ disable_style_anchor_verification(element);
2878
+ }
2879
+ return element;
2646
2880
  }
2647
2881
 
2648
2882
  /**
@@ -3567,12 +3801,7 @@ export function create_hook_safe_helper(
3567
3801
 
3568
3802
  if (key_expression) {
3569
3803
  component_element.openingElement.attributes.push(
3570
- /** @type {any} */ ({
3571
- type: 'JSXAttribute',
3572
- name: { type: 'JSXIdentifier', name: 'key', metadata: { path: [] } },
3573
- value: to_jsx_expression_container(key_expression, key_expression),
3574
- metadata: { path: [] },
3575
- }),
3804
+ b.jsx_attribute(b.jsx_id('key'), to_jsx_expression_container(key_expression, key_expression)),
3576
3805
  );
3577
3806
  }
3578
3807
 
@@ -3740,11 +3969,19 @@ function create_cached_helper_declaration(helper_id, cache_id, helper_init) {
3740
3969
  * @returns {AST.FunctionDeclaration}
3741
3970
  */
3742
3971
  function create_helper_function_declaration_from_expression(helper_id, helper_fn) {
3743
- return {
3744
- ...helper_fn,
3745
- type: 'FunctionDeclaration',
3746
- id: clone_identifier(helper_id),
3747
- };
3972
+ const declaration = set_loc(
3973
+ b.function_declaration(
3974
+ clone_identifier(helper_id),
3975
+ helper_fn.params,
3976
+ helper_fn.body,
3977
+ helper_fn.async,
3978
+ helper_fn.typeParameters,
3979
+ ),
3980
+ helper_fn,
3981
+ );
3982
+ declaration.generator = helper_fn.generator;
3983
+ declaration.metadata = { ...(helper_fn.metadata || {}), path: helper_fn.metadata?.path || [] };
3984
+ return declaration;
3748
3985
  }
3749
3986
 
3750
3987
  /**
@@ -4509,14 +4746,7 @@ function try_statement_to_jsx_child(node, transform_context) {
4509
4746
  ) ??
4510
4747
  create_jsx_element(
4511
4748
  'Suspense',
4512
- [
4513
- {
4514
- type: 'JSXAttribute',
4515
- name: { type: 'JSXIdentifier', name: 'fallback', metadata: { path: [] } },
4516
- value: fallback_content,
4517
- metadata: { path: [] },
4518
- },
4519
- ],
4749
+ [b.jsx_attribute(b.jsx_id('fallback'), fallback_content)],
4520
4750
  [result],
4521
4751
  );
4522
4752
  }
@@ -5001,14 +5231,12 @@ function build_switch_with_lift(switch_node, transform_context) {
5001
5231
  (/** @type {any} */ original_case, /** @type {number} */ i) => {
5002
5232
  const helper = case_helpers[i];
5003
5233
  if (helper) {
5004
- return /** @type {any} */ ({
5005
- type: 'SwitchCase',
5006
- test: original_case.test,
5007
- consequent: [
5234
+ return set_loc(
5235
+ b.switch_case(original_case.test, [
5008
5236
  create_component_return_statement([helper.component_element], original_case),
5009
- ],
5010
- metadata: { path: [] },
5011
- });
5237
+ ]),
5238
+ original_case,
5239
+ );
5012
5240
  }
5013
5241
 
5014
5242
  const { own_body, has_terminator } = case_info[i];
@@ -5017,12 +5245,7 @@ function build_switch_with_lift(switch_node, transform_context) {
5017
5245
  // Alias-pattern empty case (`case 'a': case 'b': ...`) — keep
5018
5246
  // the arm body empty so JS falls through to the next case at
5019
5247
  // runtime, where the helper invocation actually lives.
5020
- return /** @type {any} */ ({
5021
- type: 'SwitchCase',
5022
- test: original_case.test,
5023
- consequent: [],
5024
- metadata: { path: [] },
5025
- });
5248
+ return set_loc(b.switch_case(original_case.test, []), original_case);
5026
5249
  }
5027
5250
 
5028
5251
  const case_body = [];
@@ -5063,45 +5286,23 @@ function build_switch_with_lift(switch_node, transform_context) {
5063
5286
  // Empty body with explicit `break;` / bare `return;` — keep
5064
5287
  // a `break` so JS doesn't fall through into the next case
5065
5288
  // (which may now hold the lifted helper invocation).
5066
- case_body.push(
5067
- /** @type {any} */ ({
5068
- type: 'BreakStatement',
5069
- label: null,
5070
- metadata: { path: [] },
5071
- }),
5072
- );
5289
+ case_body.push(b.break);
5073
5290
  } else if (case_body.length > 0) {
5074
5291
  // Statements-only inline case without terminator. We've
5075
5292
  // already inlined the downstream chain via the helper
5076
5293
  // reference above, so emit a `break` to stop the runtime
5077
5294
  // from re-running downstream statements via JS fall-through.
5078
- case_body.push(
5079
- /** @type {any} */ ({
5080
- type: 'BreakStatement',
5081
- label: null,
5082
- metadata: { path: [] },
5083
- }),
5084
- );
5295
+ case_body.push(b.break);
5085
5296
  }
5086
5297
  }
5087
5298
 
5088
- return /** @type {any} */ ({
5089
- type: 'SwitchCase',
5090
- test: original_case.test,
5091
- consequent: case_body,
5092
- metadata: { path: [] },
5093
- });
5299
+ return set_loc(b.switch_case(original_case.test, case_body), original_case);
5094
5300
  },
5095
5301
  );
5096
5302
 
5097
5303
  return {
5098
5304
  setup_statements,
5099
- switch_statement: /** @type {any} */ ({
5100
- type: 'SwitchStatement',
5101
- discriminant: switch_node.discriminant,
5102
- cases: new_cases,
5103
- metadata: { path: [] },
5104
- }),
5305
+ switch_statement: b.switch(switch_node.discriminant, new_cases, switch_node),
5105
5306
  };
5106
5307
  }
5107
5308
 
@@ -5605,8 +5806,8 @@ export function to_jsx_attribute(attr, transform_context) {
5605
5806
  attr,
5606
5807
  );
5607
5808
  }
5608
- // Platforms that expect React-style DOM attrs (React) rewrite `class` to
5609
- // `className`; Preact and Solid accept `class` natively and keep it.
5809
+ // Keep this legacy hook for targets that need React-style DOM attrs. The
5810
+ // current first-party targets preserve authored `class`.
5610
5811
  let attr_name = attr.name;
5611
5812
  if (
5612
5813
  transform_context.platform.jsx.rewriteClassAttr &&
@@ -5716,19 +5917,7 @@ function build_return_expression(render_nodes) {
5716
5917
  const first = render_nodes[0];
5717
5918
  const last = render_nodes[render_nodes.length - 1];
5718
5919
  return set_loc(
5719
- {
5720
- type: 'JSXFragment',
5721
- openingFragment: /** @type {any} */ ({
5722
- type: 'JSXOpeningFragment',
5723
- metadata: { path: [] },
5724
- }),
5725
- closingFragment: /** @type {any} */ ({
5726
- type: 'JSXClosingFragment',
5727
- metadata: { path: [] },
5728
- }),
5729
- children: render_nodes,
5730
- metadata: { path: [] },
5731
- },
5920
+ b.jsx_fragment(render_nodes),
5732
5921
  first?.loc && last?.loc
5733
5922
  ? {
5734
5923
  start: first.start,