@tsrx/solid 0.1.3 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Solid compiler built on @tsrx/core",
4
4
  "license": "MIT",
5
5
  "author": "Dominic Gannaway",
6
- "version": "0.1.3",
6
+ "version": "0.1.6",
7
7
  "type": "module",
8
8
  "publishConfig": {
9
9
  "access": "public"
@@ -24,9 +24,9 @@
24
24
  }
25
25
  },
26
26
  "dependencies": {
27
- "esrap": "^2.1.0",
27
+ "esrap": "^2.2.7",
28
28
  "zimmerframe": "^1.1.2",
29
- "@tsrx/core": "0.1.3"
29
+ "@tsrx/core": "0.1.6"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "solid-js": ">=1.8 || >=2.0.0-beta"
package/src/index.js CHANGED
@@ -65,6 +65,7 @@ export function compile_to_volar_mappings(source, filename, options) {
65
65
  const transformed = transform(ast, source, filename, {
66
66
  collect: true,
67
67
  loose: !!options?.loose,
68
+ moduleScopedHookComponents: false,
68
69
  typeOnly: true,
69
70
  errors,
70
71
  comments,
package/src/transform.js CHANGED
@@ -27,10 +27,14 @@ import {
27
27
  clone_expression_node,
28
28
  clone_identifier,
29
29
  clone_jsx_name,
30
+ cloneSwitchHelperInvocation as clone_switch_helper_invocation,
31
+ collectParamBindings as collect_param_bindings,
32
+ collectStatementBindings as collect_statement_bindings,
33
+ contains_component_jsx,
30
34
  create_generated_identifier,
31
35
  create_null_literal,
32
- flatten_switch_consequent,
33
36
  get_for_of_iteration_params,
37
+ planSwitchLift as plan_switch_lift,
34
38
  identifier_to_jsx_name,
35
39
  is_bare_render_expression,
36
40
  is_dynamic_element_id,
@@ -104,6 +108,12 @@ const solid_platform = {
104
108
  scanUseServerDirectiveForAwaitWithCustomValidator: false,
105
109
  },
106
110
  hooks: {
111
+ // Hoist to module scope in the client transform —
112
+ // same trade-off as React and Vue, where one definition per helper
113
+ // keeps bundles small and source mappings 1:1. The
114
+ // `compile_to_volar_mappings` entry point opts back out so Volar's
115
+ // type-only output keeps helpers inline against the component body.
116
+ moduleScopedHookComponents: true,
107
117
  initialState: () => ({
108
118
  needs_show: false,
109
119
  needs_for: false,
@@ -113,6 +123,17 @@ const solid_platform = {
113
123
  needs_loading: false,
114
124
  needs_normalize_spread_props: false,
115
125
  }),
126
+ canHoistStaticNode(node) {
127
+ // Solid's reactive runtime doesn't reuse JSX-element identity the
128
+ // way React does, so hoisting `<Component />` references to module
129
+ // level pays no runtime cost — it just creates an extra `const`
130
+ // that aliases a helper invocation (e.g. `App__static1 =
131
+ // <App__StatementBodyHook2 />`). Truly-static DOM trees like
132
+ // `<span>'Hello'</span>` still benefit from being hoisted out of
133
+ // the per-render closure, so we only veto hoisting when the
134
+ // subtree contains a *component* JSX element. Same logic Vue uses.
135
+ return !contains_component_jsx(node);
136
+ },
116
137
  validateComponentAwait: (await_expression, _component, ctx, _requires, source) => {
117
138
  const await_start = get_await_keyword_start(await_expression, source);
118
139
  const adjusted_node = /** @type {any} */ ({
@@ -134,8 +155,8 @@ const solid_platform = {
134
155
  switchStatement: switch_statement_to_jsx_child,
135
156
  tryStatement: try_statement_to_jsx_child,
136
157
  },
137
- componentToFunction: (component, ctx) =>
138
- component_to_function_declaration(component, /** @type {any} */ (ctx)),
158
+ componentToFunction: (component, ctx, helper_state) =>
159
+ component_to_function_declaration(component, /** @type {any} */ (ctx), helper_state),
139
160
  injectImports: (program, ctx) => inject_solid_imports(program, /** @type {any} */ (ctx)),
140
161
  // `transformElementAttributes` is intentionally omitted: the
141
162
  // `transformElement` hook below short-circuits core's element walker
@@ -180,12 +201,42 @@ function get_await_keyword_start(await_node, source) {
180
201
  /**
181
202
  * @param {any} component
182
203
  * @param {TransformContext} transform_context
204
+ * @param {any} [walk_helper_state]
205
+ * The helper_state owned by the walker for this component. Solid's local
206
+ * body lowering happens *after* the walker has restored `helper_state` to
207
+ * the outer scope's value, so we re-install the walker's helper_state here
208
+ * for the duration of the body lowering. That way `to_jsx_child` calls into
209
+ * `switch_statement_to_jsx_child` (and any other lift path that goes
210
+ * through `create_hook_safe_helper`) see the same module-scoped helper
211
+ * bucket the React / Vue paths see, and `moduleScopedHookComponents: true`
212
+ * actually hoists Solid's `<StatementBodyHook/>` helpers out of the
213
+ * component body.
183
214
  * @returns {AST.FunctionDeclaration | AST.FunctionExpression | AST.ArrowFunctionExpression}
184
215
  */
185
- function component_to_function_declaration(component, transform_context) {
216
+ function component_to_function_declaration(component, transform_context, walk_helper_state) {
186
217
  const params = component.params || [];
187
218
  const body = /** @type {any[]} */ (component.body || []);
188
219
 
220
+ const saved_helper_state = transform_context.helper_state;
221
+ if (walk_helper_state) {
222
+ transform_context.helper_state = walk_helper_state;
223
+ }
224
+
225
+ // Re-install the component's body bindings before lowering the body. The
226
+ // walker sets `state.available_bindings` to the component-scope bindings
227
+ // during `next()` and restores them before calling the platform's
228
+ // `componentToFunction` hook — but Solid's body lowering happens *here*,
229
+ // after that restore, so `to_jsx_child` (and any `create_hook_safe_helper`
230
+ // it triggers via `switch_statement_to_jsx_child` / the if / try / for-of
231
+ // lifts) wouldn't otherwise see component-scope locals like `const obj =
232
+ // {...}` and would emit helpers that close over undefined references.
233
+ const saved_bindings = transform_context.available_bindings;
234
+ const body_bindings = collect_param_bindings(params);
235
+ for (const node of body) {
236
+ collect_statement_bindings(node, body_bindings);
237
+ }
238
+ transform_context.available_bindings = body_bindings;
239
+
189
240
  // In type-only mode the lazy rewrite is skipped so destructuring patterns
190
241
  // survive into the virtual TSX and TypeScript can flow real types.
191
242
  const lazy_bindings = transform_context.typeOnly
@@ -314,27 +365,12 @@ function component_to_function_declaration(component, transform_context) {
314
365
  }
315
366
 
316
367
  if (render_nodes.length > 0) {
317
- statements.push(
318
- /** @type {any} */ ({
319
- type: 'ReturnStatement',
320
- argument: build_return_expression(render_nodes) || {
321
- type: 'Literal',
322
- value: null,
323
- raw: 'null',
324
- metadata: { path: [] },
325
- },
326
- metadata: { path: [] },
327
- }),
328
- );
368
+ statements.push(b.return(build_return_expression(render_nodes) || b.literal(null)));
329
369
  }
330
370
 
331
371
  const final_params = lazy_bindings.size > 0 ? replace_lazy_params(params) : params;
332
372
 
333
- const body_block = /** @type {any} */ ({
334
- type: 'BlockStatement',
335
- body: statements,
336
- metadata: { path: [] },
337
- });
373
+ const body_block = b.block(statements);
338
374
  const final_body =
339
375
  lazy_bindings.size > 0 ? apply_lazy_transforms(body_block, lazy_bindings) : body_block;
340
376
 
@@ -342,48 +378,19 @@ function component_to_function_declaration(component, transform_context) {
342
378
  let fn;
343
379
 
344
380
  if (component.id) {
345
- fn = /** @type {any} */ ({
346
- type: 'FunctionDeclaration',
347
- id: component.id,
348
- typeParameters: component.typeParameters,
349
- params: final_params,
350
- body: final_body,
351
- async: false,
352
- generator: false,
353
- metadata: {
354
- path: [],
355
- is_component: true,
356
- },
357
- });
381
+ fn = b.function_declaration(
382
+ component.id,
383
+ final_params,
384
+ final_body,
385
+ false,
386
+ component.typeParameters,
387
+ );
358
388
  } else if (component.metadata?.arrow) {
359
- fn = /** @type {any} */ ({
360
- type: 'ArrowFunctionExpression',
361
- typeParameters: component.typeParameters,
362
- params: final_params,
363
- body: final_body,
364
- async: false,
365
- generator: false,
366
- expression: false,
367
- metadata: {
368
- path: [],
369
- is_component: true,
370
- },
371
- });
389
+ fn = b.arrow(final_params, final_body, false, component.typeParameters);
372
390
  } else {
373
- fn = /** @type {any} */ ({
374
- type: 'FunctionExpression',
375
- id: null,
376
- typeParameters: component.typeParameters,
377
- params: final_params,
378
- body: final_body,
379
- async: false,
380
- generator: false,
381
- metadata: {
382
- path: [],
383
- is_component: true,
384
- },
385
- });
391
+ fn = b.function(null, final_params, final_body, false, component.typeParameters);
386
392
  }
393
+ fn.metadata.is_component = true;
387
394
 
388
395
  if (fn.type === 'FunctionDeclaration' && fn.id) {
389
396
  fn.id.metadata = /** @type {AST.Identifier['metadata']} */ ({
@@ -393,6 +400,20 @@ function component_to_function_declaration(component, transform_context) {
393
400
  }
394
401
 
395
402
  setLocation(fn, /** @type {any} */ (component), true);
403
+
404
+ // Restore the outer helper_state and bindings, then surface this
405
+ // component's generated helpers / statics so the downstream Program-level
406
+ // lift can hoist them (`moduleScopedHookComponents: true` registers
407
+ // helpers into `helper_state.helpers`; the React/Vue post-pass reads them
408
+ // off `fn.metadata.generated_helpers`).
409
+ transform_context.helper_state = saved_helper_state;
410
+ transform_context.available_bindings = saved_bindings;
411
+ if (walk_helper_state) {
412
+ const fn_metadata = /** @type {any} */ (fn.metadata);
413
+ fn_metadata.generated_helpers = walk_helper_state.helpers;
414
+ fn_metadata.generated_statics = walk_helper_state.statics;
415
+ }
416
+
396
417
  return fn;
397
418
  }
398
419
 
@@ -528,14 +549,12 @@ function body_to_jsx_child(body_nodes, transform_context) {
528
549
  let capture_index = 0;
529
550
  for (const child of body_nodes) {
530
551
  if (is_bare_return_statement(child)) {
531
- statements.push({
532
- type: 'ReturnStatement',
533
- argument: children.length > 0 ? build_return_expression(children) : create_null_literal(),
534
- metadata: { path: [] },
535
- start: child.start,
536
- end: child.end,
537
- loc: child.loc,
538
- });
552
+ statements.push(
553
+ set_loc(
554
+ b.return(children.length > 0 ? build_return_expression(children) : create_null_literal()),
555
+ child,
556
+ ),
557
+ );
539
558
  children.length = 0;
540
559
  has_terminal_return = true;
541
560
  continue;
@@ -580,27 +599,13 @@ function body_to_jsx_child(body_nodes, transform_context) {
580
599
  const block_body = [...statements];
581
600
  if (children.length > 0 || !has_terminal_return) {
582
601
  block_body.push(
583
- /** @type {any} */ ({
584
- type: 'ReturnStatement',
585
- argument: children.length > 0 ? build_return_expression(children) : create_null_literal(),
586
- metadata: { path: [] },
587
- }),
602
+ b.return(children.length > 0 ? build_return_expression(children) : create_null_literal()),
588
603
  );
589
604
  }
590
605
 
591
- return /** @type {any} */ ({
592
- type: 'ArrowFunctionExpression',
593
- params: [],
594
- body: {
595
- type: 'BlockStatement',
596
- body: block_body,
597
- metadata: { path: [] },
598
- },
599
- async: false,
600
- generator: false,
601
- expression: false,
602
- metadata: { path: [], is_branch_arrow: true },
603
- });
606
+ const arrow = b.arrow([], b.block(block_body));
607
+ /** @type {any} */ (arrow.metadata).is_branch_arrow = true;
608
+ return arrow;
604
609
  }
605
610
 
606
611
  /**
@@ -686,14 +691,7 @@ function loop_body_to_callback_statements(body_nodes, transform_context) {
686
691
  const create_return_statement = (source_node, render_nodes) => {
687
692
  const cloned = render_nodes.map((node) => clone_expression_node(node));
688
693
  const argument = cloned.length > 0 ? build_return_expression(cloned) : create_null_literal();
689
- return {
690
- type: 'ReturnStatement',
691
- argument,
692
- metadata: { path: [] },
693
- start: source_node?.start,
694
- end: source_node?.end,
695
- loc: source_node?.loc,
696
- };
694
+ return set_loc(b.return(argument), source_node);
697
695
  };
698
696
 
699
697
  /** @param {any} source_node */
@@ -719,20 +717,7 @@ function loop_body_to_callback_statements(body_nodes, transform_context) {
719
717
  transform_context,
720
718
  );
721
719
  prepend_render_nodes_to_return_statements(branch_statements, children);
722
- statements.push({
723
- type: 'IfStatement',
724
- test: child.test,
725
- consequent: {
726
- type: 'BlockStatement',
727
- body: branch_statements,
728
- metadata: { path: [] },
729
- },
730
- alternate: null,
731
- metadata: { path: [] },
732
- start: child.start,
733
- end: child.end,
734
- loc: child.loc,
735
- });
720
+ statements.push(set_loc(b.if(child.test, b.block(branch_statements), null), child));
736
721
  continue;
737
722
  }
738
723
 
@@ -953,13 +938,7 @@ function negate_expression(expr) {
953
938
  return clone_expression_node(expr.argument);
954
939
  }
955
940
 
956
- return {
957
- type: 'UnaryExpression',
958
- operator: '!',
959
- prefix: true,
960
- argument: clone_expression_node(expr),
961
- metadata: { path: [] },
962
- };
941
+ return b.unary('!', clone_expression_node(expr));
963
942
  }
964
943
 
965
944
  /**
@@ -971,13 +950,7 @@ function negate_expression(expr) {
971
950
  */
972
951
  function iife_if_arrow(node) {
973
952
  if (!is_branch_arrow(node)) return node;
974
- return {
975
- type: 'CallExpression',
976
- callee: node,
977
- arguments: [],
978
- optional: false,
979
- metadata: { path: [] },
980
- };
953
+ return b.call(node);
981
954
  }
982
955
 
983
956
  /**
@@ -1137,54 +1110,26 @@ function for_of_statement_to_jsx_child(node, transform_context) {
1137
1110
  )
1138
1111
  );
1139
1112
 
1140
- let arrow = /** @type {any} */ ({
1141
- type: 'ArrowFunctionExpression',
1142
- params: loop_params,
1143
- body: null,
1144
- async: false,
1145
- generator: false,
1146
- expression: true,
1147
- metadata: { path: [] },
1148
- });
1113
+ let arrow;
1149
1114
 
1150
1115
  if (body_has_loop_skip(loop_body)) {
1151
- arrow.body = {
1152
- type: 'BlockStatement',
1153
- body: loop_body_to_callback_statements(loop_body, transform_context),
1154
- metadata: { path: [] },
1155
- };
1156
- arrow.expression = false;
1116
+ arrow = b.arrow(
1117
+ loop_params,
1118
+ b.block(loop_body_to_callback_statements(loop_body, transform_context)),
1119
+ );
1157
1120
  } else {
1121
+ // Placeholder body — merge_branch_body_into_arrow replaces it below.
1122
+ arrow = b.arrow(loop_params, b.literal(null));
1158
1123
  arrow = merge_branch_body_into_arrow(arrow, body_to_jsx_child(loop_body, transform_context));
1159
1124
  }
1160
1125
 
1161
- const attributes = [
1162
- {
1163
- type: 'JSXAttribute',
1164
- name: { type: 'JSXIdentifier', name: 'each', metadata: { path: [] } },
1165
- value: to_jsx_expression_container(node.right),
1166
- metadata: { path: [] },
1167
- },
1168
- ];
1126
+ const attributes = [b.jsx_attribute(b.jsx_id('each'), to_jsx_expression_container(node.right))];
1169
1127
 
1170
1128
  if (node.key) {
1171
1129
  const item_param = clone_expression_node(loop_params[0]);
1172
- const keyed_arrow = /** @type {any} */ ({
1173
- type: 'ArrowFunctionExpression',
1174
- params: [item_param],
1175
- body: node.key,
1176
- async: false,
1177
- generator: false,
1178
- expression: true,
1179
- metadata: { path: [] },
1180
- });
1130
+ const keyed_arrow = b.arrow([item_param], node.key);
1181
1131
  attributes.push(
1182
- /** @type {any} */ ({
1183
- type: 'JSXAttribute',
1184
- name: { type: 'JSXIdentifier', name: 'keyed', metadata: { path: [] } },
1185
- value: to_jsx_expression_container(keyed_arrow, node.key),
1186
- metadata: { path: [] },
1187
- }),
1132
+ b.jsx_attribute(b.jsx_id('keyed'), to_jsx_expression_container(keyed_arrow, node.key)),
1188
1133
  );
1189
1134
  }
1190
1135
 
@@ -1197,6 +1142,20 @@ function for_of_statement_to_jsx_child(node, transform_context) {
1197
1142
  * statement with a discriminant `d` and cases `[c1, c2, default]` becomes:
1198
1143
  * <Switch fallback={...default}><Match when={d === c1}>...</Match>...</Switch>
1199
1144
  *
1145
+ * Fall-through across cases reuses the shared `plan_switch_lift` pipeline
1146
+ * from `@tsrx/core`: each duplicated case body is hoisted into a
1147
+ * `StatementBodyHook` helper component that chains into the next helper, and
1148
+ * each `<Match>` body just renders the appropriate helper element. The
1149
+ * client transform hoists those helpers to module scope (Solid's platform
1150
+ * sets `moduleScopedHookComponents: true`); `compile_to_volar_mappings` opts
1151
+ * back out and emits the helpers locally inside the component body so Volar
1152
+ * still sees closure-captured bindings against the component scope.
1153
+ *
1154
+ * When any case is lifted in `typeOnly` mode the helper declarations have to
1155
+ * live somewhere local-scoped — we wrap the whole `<Switch>` in an IIFE that
1156
+ * declares them in order and returns the element. The client transform's
1157
+ * module-scoped helpers leave that IIFE empty, so we skip the wrapper.
1158
+ *
1200
1159
  * @param {any} node
1201
1160
  * @param {TransformContext} transform_context
1202
1161
  * @returns {any}
@@ -1205,20 +1164,51 @@ function switch_statement_to_jsx_child(node, transform_context) {
1205
1164
  transform_context.needs_switch = true;
1206
1165
  transform_context.needs_match = true;
1207
1166
 
1167
+ const { case_info, case_helpers, find_next_helper_after, setup_statements } = plan_switch_lift(
1168
+ node,
1169
+ transform_context,
1170
+ );
1171
+
1208
1172
  /** @type {any} */
1209
1173
  let fallback = null;
1210
- const match_children = [];
1211
-
1212
- for (const switch_case of node.cases) {
1213
- const consequent = flatten_switch_consequent(switch_case.consequent || []);
1214
- const body = [];
1215
- for (const child of consequent) {
1216
- if (child.type === 'BreakStatement') break;
1217
- body.push(child);
1174
+ /** @type {Array<{ test: any, body_jsx: any }>} */
1175
+ const match_entries = [];
1176
+
1177
+ for (let i = 0; i < node.cases.length; i++) {
1178
+ const original_case = node.cases[i];
1179
+ const info = case_info[i];
1180
+ const helper = case_helpers[i];
1181
+
1182
+ /** @type {any} */
1183
+ let body_jsx;
1184
+ if (helper) {
1185
+ // Lifted case: render the helper element directly. Use the
1186
+ // original `component_element` (not a clone) for this — its
1187
+ // definition's `loc` is what the case position should map to.
1188
+ body_jsx = helper.component_element;
1189
+ } else if (info.own_body.length === 0) {
1190
+ // Empty case in the source. If a downstream chain exists (alias
1191
+ // pattern like `case 1: case 2: body; break;`), the `<Match>` for
1192
+ // the empty label still has to render that downstream body —
1193
+ // Solid's `<Match>` arms are exclusive, so JS fall-through can't
1194
+ // rescue us here.
1195
+ const next_helper = find_next_helper_after(i);
1196
+ body_jsx = next_helper ? clone_switch_helper_invocation(next_helper) : create_null_literal();
1197
+ } else {
1198
+ // Inline case body: process JSX/non-JSX statements just like Solid
1199
+ // does for any other branch body, then append the chain helper if
1200
+ // this case falls through with no terminator.
1201
+ const inline_body = [...info.own_body];
1202
+ if (!info.has_terminator) {
1203
+ const next_helper = find_next_helper_after(i);
1204
+ if (next_helper) {
1205
+ inline_body.push(clone_switch_helper_invocation(next_helper));
1206
+ }
1207
+ }
1208
+ body_jsx = body_to_jsx_child(inline_body, transform_context);
1218
1209
  }
1219
1210
 
1220
- const body_jsx = body_to_jsx_child(body, transform_context);
1221
- if (switch_case.test === null) {
1211
+ if (original_case.test === null) {
1222
1212
  fallback = body_jsx;
1223
1213
  continue;
1224
1214
  }
@@ -1226,31 +1216,29 @@ function switch_statement_to_jsx_child(node, transform_context) {
1226
1216
  // Clone the discriminant per-case: every generated `<Match when={d === caseN}>`
1227
1217
  // would otherwise share the same AST node reference, so a downstream pass
1228
1218
  // (lazy transforms, printer metadata, source-map annotation) mutating it on
1229
- // one case would corrupt the others.
1230
- const test = /** @type {any} */ ({
1231
- type: 'BinaryExpression',
1232
- operator: '===',
1233
- left: clone_expression_node(node.discriminant),
1234
- right: switch_case.test,
1235
- metadata: { path: [] },
1236
- });
1219
+ // one case would corrupt the others. The right operand (`caseN`) is the
1220
+ // original source `test` node unique per case, so we keep its real loc
1221
+ // for editor IntelliSense and don't clone it.
1222
+ const test = b.binary('===', clone_expression_node(node.discriminant), original_case.test);
1237
1223
 
1238
- match_children.push(
1239
- create_jsx_element(
1240
- 'Match',
1241
- [
1242
- {
1243
- type: 'JSXAttribute',
1244
- name: { type: 'JSXIdentifier', name: 'when', metadata: { path: [] } },
1245
- value: to_jsx_expression_container(test),
1246
- metadata: { path: [] },
1247
- },
1248
- ],
1249
- [jsx_child_wrap(to_function_child(body_jsx))],
1250
- ),
1251
- );
1224
+ match_entries.push({ test, body_jsx });
1252
1225
  }
1253
1226
 
1227
+ const match_children = match_entries.map(({ test, body_jsx }) =>
1228
+ create_jsx_element(
1229
+ 'Match',
1230
+ [
1231
+ {
1232
+ type: 'JSXAttribute',
1233
+ name: { type: 'JSXIdentifier', name: 'when', metadata: { path: [] } },
1234
+ value: to_jsx_expression_container(test),
1235
+ metadata: { path: [] },
1236
+ },
1237
+ ],
1238
+ [jsx_child_wrap(to_function_child(body_jsx))],
1239
+ ),
1240
+ );
1241
+
1254
1242
  const attributes =
1255
1243
  fallback !== null
1256
1244
  ? [
@@ -1263,7 +1251,17 @@ function switch_statement_to_jsx_child(node, transform_context) {
1263
1251
  ]
1264
1252
  : [];
1265
1253
 
1266
- return create_jsx_element('Switch', attributes, match_children);
1254
+ const switch_element = create_jsx_element('Switch', attributes, match_children);
1255
+
1256
+ if (setup_statements.length === 0) {
1257
+ return switch_element;
1258
+ }
1259
+
1260
+ // Local-scoped helpers (typeOnly mode): wrap the <Switch> in an IIFE that
1261
+ // declares the helpers in source order and returns the element.
1262
+ return to_jsx_expression_container(
1263
+ b.call(b.arrow([], b.block([...setup_statements, b.return(switch_element)]))),
1264
+ );
1267
1265
  }
1268
1266
 
1269
1267
  /**
@@ -1336,28 +1334,13 @@ function try_statement_to_jsx_child(node, transform_context) {
1336
1334
  const catch_jsx = body_to_jsx_child(catch_body_nodes, transform_context);
1337
1335
 
1338
1336
  const fallback_fn = merge_branch_body_into_arrow(
1339
- /** @type {any} */ ({
1340
- type: 'ArrowFunctionExpression',
1341
- params: catch_params,
1342
- body: null,
1343
- async: false,
1344
- generator: false,
1345
- expression: true,
1346
- metadata: { path: [] },
1347
- }),
1337
+ b.arrow(catch_params, b.literal(null)),
1348
1338
  catch_jsx,
1349
1339
  );
1350
1340
 
1351
1341
  result = create_jsx_element(
1352
1342
  'Errored',
1353
- [
1354
- {
1355
- type: 'JSXAttribute',
1356
- name: { type: 'JSXIdentifier', name: 'fallback', metadata: { path: [] } },
1357
- value: to_jsx_expression_container(fallback_fn),
1358
- metadata: { path: [] },
1359
- },
1360
- ],
1343
+ [b.jsx_attribute(b.jsx_id('fallback'), to_jsx_expression_container(fallback_fn))],
1361
1344
  [result],
1362
1345
  );
1363
1346
  }
@@ -1424,47 +1407,16 @@ function inject_solid_imports(program, transform_context) {
1424
1407
  const specifiers = [];
1425
1408
 
1426
1409
  if (transform_context.needs_ref_prop) {
1427
- specifiers.push({
1428
- type: 'ImportSpecifier',
1429
- imported: { type: 'Identifier', name: 'create_ref_prop', metadata: { path: [] } },
1430
- local: {
1431
- type: 'Identifier',
1432
- name: CREATE_REF_PROP_INTERNAL_NAME,
1433
- metadata: { path: [] },
1434
- },
1435
- metadata: { path: [] },
1436
- });
1410
+ specifiers.push(b.import_specifier('create_ref_prop', CREATE_REF_PROP_INTERNAL_NAME));
1437
1411
  }
1438
1412
 
1439
1413
  if (transform_context.needs_normalize_spread_props) {
1440
- specifiers.push({
1441
- type: 'ImportSpecifier',
1442
- imported: {
1443
- type: 'Identifier',
1444
- name: 'normalize_spread_props',
1445
- metadata: { path: [] },
1446
- },
1447
- local: {
1448
- type: 'Identifier',
1449
- name: NORMALIZE_SPREAD_PROPS_INTERNAL_NAME,
1450
- metadata: { path: [] },
1451
- },
1452
- metadata: { path: [] },
1453
- });
1414
+ specifiers.push(
1415
+ b.import_specifier('normalize_spread_props', NORMALIZE_SPREAD_PROPS_INTERNAL_NAME),
1416
+ );
1454
1417
  }
1455
1418
 
1456
- program.body.unshift(
1457
- /** @type {any} */ ({
1458
- type: 'ImportDeclaration',
1459
- specifiers,
1460
- source: {
1461
- type: 'Literal',
1462
- value: '@tsrx/solid/ref',
1463
- raw: "'@tsrx/solid/ref'",
1464
- },
1465
- metadata: { path: [] },
1466
- }),
1467
- );
1419
+ program.body.unshift(b.import_declaration(specifiers, '@tsrx/solid/ref'));
1468
1420
  }
1469
1421
 
1470
1422
  const needed = [];
@@ -1477,20 +1429,11 @@ function inject_solid_imports(program, transform_context) {
1477
1429
 
1478
1430
  if (needed.length === 0) return;
1479
1431
 
1480
- const specifiers = needed.map((name) => ({
1481
- type: 'ImportSpecifier',
1482
- imported: { type: 'Identifier', name, metadata: { path: [] } },
1483
- local: { type: 'Identifier', name, metadata: { path: [] } },
1484
- metadata: { path: [] },
1485
- }));
1486
-
1487
1432
  program.body.unshift(
1488
- /** @type {any} */ ({
1489
- type: 'ImportDeclaration',
1490
- specifiers,
1491
- source: { type: 'Literal', value: 'solid-js', raw: "'solid-js'" },
1492
- metadata: { path: [] },
1493
- }),
1433
+ b.imports(
1434
+ needed.map((name) => [name, name]),
1435
+ 'solid-js',
1436
+ ),
1494
1437
  );
1495
1438
  }
1496
1439
 
@@ -1876,57 +1819,19 @@ function is_solid_jsx_ref_attribute(attr) {
1876
1819
  */
1877
1820
  function dynamic_element_to_jsx_child(node, transform_context) {
1878
1821
  const dynamic_id = set_loc(create_generated_identifier('DynamicElement'), node.id);
1879
- const alias_declaration = set_loc(
1880
- /** @type {any} */ ({
1881
- type: 'VariableDeclaration',
1882
- kind: 'const',
1883
- declarations: [
1884
- {
1885
- type: 'VariableDeclarator',
1886
- id: dynamic_id,
1887
- init: clone_expression_node(node.id),
1888
- metadata: { path: [] },
1889
- },
1890
- ],
1891
- metadata: { path: [] },
1892
- }),
1893
- node,
1894
- );
1822
+ const alias_declaration = set_loc(b.const(dynamic_id, clone_expression_node(node.id)), node);
1895
1823
  const jsx_element = create_dynamic_jsx_element(dynamic_id, node, transform_context);
1896
1824
 
1897
1825
  return to_jsx_expression_container(
1898
- /** @type {any} */ ({
1899
- type: 'CallExpression',
1900
- callee: {
1901
- type: 'ArrowFunctionExpression',
1902
- params: [],
1903
- body: /** @type {any} */ ({
1904
- type: 'BlockStatement',
1905
- body: [
1906
- alias_declaration,
1907
- {
1908
- type: 'ReturnStatement',
1909
- argument: {
1910
- type: 'ConditionalExpression',
1911
- test: clone_identifier(dynamic_id),
1912
- consequent: jsx_element,
1913
- alternate: create_null_literal(),
1914
- metadata: { path: [] },
1915
- },
1916
- metadata: { path: [] },
1917
- },
1918
- ],
1919
- metadata: { path: [] },
1920
- }),
1921
- async: false,
1922
- generator: false,
1923
- expression: false,
1924
- metadata: { path: [] },
1925
- },
1926
- arguments: [],
1927
- optional: false,
1928
- metadata: { path: [] },
1929
- }),
1826
+ b.call(
1827
+ b.arrow(
1828
+ [],
1829
+ b.block([
1830
+ alias_declaration,
1831
+ b.return(b.conditional(clone_identifier(dynamic_id), jsx_element, create_null_literal())),
1832
+ ]),
1833
+ ),
1834
+ ),
1930
1835
  node,
1931
1836
  );
1932
1837
  }