@tsrx/core 0.1.45 → 0.1.47

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.
@@ -1,6 +1,6 @@
1
1
  /** @import * as AST from 'estree' */
2
2
  /** @import * as ESTreeJSX from 'estree-jsx' */
3
- /** @import { JsxPlatform, JsxTransformContext, JsxTransformOptions, JsxTransformResult } from '@tsrx/core/types' */
3
+ /** @import { JsxHelperComponent, JsxPlatform, JsxTransformContext as TransformContext, JsxTransformOptions, JsxTransformResult } from '@tsrx/core/types' */
4
4
 
5
5
  import { walk } from 'zimmerframe';
6
6
  import { print } from 'esrap';
@@ -20,13 +20,14 @@ import {
20
20
  } from './helpers.js';
21
21
  import {
22
22
  add_extra_source_mappings_from_matching_expression,
23
- clone_expression_node,
23
+ clone_ast_node,
24
24
  clone_identifier,
25
25
  clone_jsx_name,
26
26
  create_generated_identifier,
27
27
  create_null_literal,
28
28
  flatten_switch_consequent,
29
29
  get_for_of_iteration_params,
30
+ identifier_to_jsx_identifier,
30
31
  identifier_to_jsx_name,
31
32
  is_bare_render_expression,
32
33
  is_component_jsx_name,
@@ -61,6 +62,11 @@ import {
61
62
  } from '../jsx-interleave.js';
62
63
  import { is_hoist_safe_jsx_node } from '../jsx-hoist.js';
63
64
  import { lower_server_module_for_types } from './server-module.js';
65
+ import {
66
+ has_location,
67
+ is_function_or_class_node as is_function_or_class_boundary,
68
+ is_template_directive as is_jsx_control_flow_expression,
69
+ } from '../../utils/ast.js';
64
70
 
65
71
  const TEMPLATE_FRAGMENT_ERROR =
66
72
  '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.';
@@ -102,32 +108,6 @@ function report_jsx_fragment_in_tsrx_error(node, transform_context) {
102
108
  );
103
109
  }
104
110
 
105
- /**
106
- * Local alias for the shared `JsxTransformContext`. Kept as a typedef so the
107
- * rest of this file's `@param {TransformContext}` annotations don't all have
108
- * to spell out the import.
109
- *
110
- * @typedef {JsxTransformContext} TransformContext
111
- */
112
-
113
- /**
114
- * @typedef {{ source_name: string, read: () => any }} LazyBinding
115
- */
116
-
117
- /**
118
- * @param {any} node
119
- * @returns {boolean}
120
- */
121
- function is_function_or_class_boundary(node) {
122
- return (
123
- node?.type === 'FunctionDeclaration' ||
124
- node?.type === 'FunctionExpression' ||
125
- node?.type === 'ArrowFunctionExpression' ||
126
- node?.type === 'ClassDeclaration' ||
127
- node?.type === 'ClassExpression'
128
- );
129
- }
130
-
131
111
  /**
132
112
  * @param {any} node
133
113
  * @param {boolean} [inside_function]
@@ -264,22 +244,6 @@ function expand_child_code_blocks(node, seen = new Set()) {
264
244
  return out;
265
245
  }
266
246
 
267
- /**
268
- * A `@`-prefixed JSX control-flow expression (`@if`/`@for`/`@switch`/`@try`).
269
- * These are the only control-flow nodes that can appear in expression position;
270
- * the plain statement forms (`IfStatement`, `SwitchStatement`, …) never do.
271
- * @param {any} node
272
- * @returns {boolean}
273
- */
274
- function is_jsx_control_flow_expression(node) {
275
- return (
276
- node?.type === 'JSXIfExpression' ||
277
- node?.type === 'JSXForExpression' ||
278
- node?.type === 'JSXSwitchExpression' ||
279
- node?.type === 'JSXTryExpression'
280
- );
281
- }
282
-
283
247
  /**
284
248
  * Wrap a render-output node in a native TSRX fragment so it flows through the
285
249
  * same single-child render path as a `<> … </>` output. This is a compiler
@@ -418,7 +382,7 @@ function wrap_lowered_value_in_fragment(expression, source) {
418
382
  expression?.type === 'JSXExpressionContainer'
419
383
  ? expression
420
384
  : to_jsx_expression_container(expression, source);
421
- return set_loc(b.jsx_fragment([child]), source?.loc ? source : undefined);
385
+ return set_loc(b.jsx_fragment([child]), has_location(source) ? source : undefined);
422
386
  }
423
387
 
424
388
  /**
@@ -594,7 +558,7 @@ export function createJsxTransform(platform) {
594
558
  function transform(ast, source, filename, options) {
595
559
  const suspense_source = options?.suspenseSource ?? platform.imports.suspense;
596
560
  const collect = !!(options?.collect || options?.loose);
597
- /** @type {any[]} */
561
+ /** @type {AST.CSS.StyleSheet[]} */
598
562
  const stylesheets = [];
599
563
  /** @type {AST.Statement[]} */
600
564
  const type_only_style_anchors = [];
@@ -613,6 +577,14 @@ export function createJsxTransform(platform) {
613
577
  needs_dynamic_factory: false,
614
578
  needs_for_of_iterable: false,
615
579
  needs_iteration_value_type: false,
580
+ needs_show: false,
581
+ needs_for: false,
582
+ needs_switch: false,
583
+ needs_match: false,
584
+ needs_errored: false,
585
+ needs_loading: false,
586
+ needs_define_vapor_component: false,
587
+ needs_vapor_for: false,
616
588
  stylesheets,
617
589
  type_only_style_anchors,
618
590
  module_scoped_hook_components:
@@ -907,13 +879,13 @@ function lower_dynamic_jsx_element(node, transform_context) {
907
879
 
908
880
  const dynamic_expression = dynamic_name.expression;
909
881
  if (!dynamic_expression) return;
910
- const generated_expression = clone_expression_node(dynamic_expression);
882
+ const generated_expression = clone_ast_node(dynamic_expression);
911
883
  if (node.closingElement?.name?.expression) {
912
884
  // One generated expression stands in for both tags; record the closing
913
885
  // tag's positions so editor features keep working on `</{expr}>`.
914
886
  add_extra_source_mappings_from_matching_expression(
915
887
  generated_expression,
916
- clone_expression_node(node.closingElement.name.expression),
888
+ clone_ast_node(node.closingElement.name.expression),
917
889
  );
918
890
  }
919
891
 
@@ -1324,11 +1296,7 @@ function build_render_statements(
1324
1296
  render_nodes.push(
1325
1297
  b.jsx_expression_container(
1326
1298
  set_loc(
1327
- b.conditional(
1328
- clone_expression_node(child.test),
1329
- b.literal(null),
1330
- stmt.argument,
1331
- ),
1299
+ b.conditional(clone_ast_node(child.test), b.literal(null), stmt.argument),
1332
1300
  child,
1333
1301
  ),
1334
1302
  ),
@@ -1411,7 +1379,7 @@ function build_render_statements(
1411
1379
  if (return_arg === null) {
1412
1380
  return_arg = set_loc(
1413
1381
  b.jsx_fragment([]),
1414
- source_authored_fragment.loc ? source_authored_fragment : undefined,
1382
+ has_location(source_authored_fragment) ? source_authored_fragment : undefined,
1415
1383
  );
1416
1384
  } else if (return_arg.type !== 'JSXFragment') {
1417
1385
  return_arg = wrap_lowered_value_in_fragment(return_arg, source_authored_fragment);
@@ -1613,7 +1581,7 @@ function create_helper_component_element(helper_id, bindings, source_node, mappi
1613
1581
  const { mapWrapper = true, mapBindingNames = true, mapBindingValues = true } = mapping;
1614
1582
  const attributes = bindings.map((binding) =>
1615
1583
  b.jsx_attribute(
1616
- identifier_to_jsx_name(
1584
+ identifier_to_jsx_identifier(
1617
1585
  mapBindingNames ? clone_identifier(binding) : create_generated_identifier(binding.name),
1618
1586
  ),
1619
1587
  to_jsx_expression_container(
@@ -1624,7 +1592,7 @@ function create_helper_component_element(helper_id, bindings, source_node, mappi
1624
1592
  );
1625
1593
 
1626
1594
  const opening_element = b.jsx_opening_element(
1627
- identifier_to_jsx_name(clone_identifier(helper_id)),
1595
+ identifier_to_jsx_identifier(clone_identifier(helper_id)),
1628
1596
  attributes,
1629
1597
  true,
1630
1598
  );
@@ -2382,7 +2350,7 @@ function create_style_expression_value(node, stylesheet, transform_context) {
2382
2350
  * @param {TransformContext} transform_context
2383
2351
  */
2384
2352
  function add_type_only_style_anchor(node, transform_context) {
2385
- const style_anchor = b.jsx_element(clone_expression_node(node, true), [], []);
2353
+ const style_anchor = b.jsx_element(clone_ast_node(node, true), [], []);
2386
2354
  disable_style_anchor_verification(style_anchor);
2387
2355
 
2388
2356
  const anchor_id = create_generated_identifier(create_style_anchor_name(transform_context));
@@ -2502,7 +2470,7 @@ function annotate_tsrx_with_hash(node, hash, jsx_class_attr_name, preserve_style
2502
2470
  const annotated = { ...node };
2503
2471
  annotated.children = (node.children || []).map((/** @type {any} */ statement) =>
2504
2472
  annotate_with_hash(
2505
- clone_expression_node(statement),
2473
+ clone_ast_node(statement),
2506
2474
  hash,
2507
2475
  jsx_class_attr_name,
2508
2476
  preserve_style_elements,
@@ -2867,7 +2835,11 @@ function node_contains_hook_bearing_tsrx(node, transform_context) {
2867
2835
  }
2868
2836
 
2869
2837
  if (is_native_tsrx_node(node)) {
2870
- return body_contains_top_level_hook_call(node.children || [], transform_context, true);
2838
+ return body_contains_top_level_hook_call(
2839
+ 'children' in node ? node.children : [],
2840
+ transform_context,
2841
+ true,
2842
+ );
2871
2843
  }
2872
2844
 
2873
2845
  if (
@@ -3270,7 +3242,7 @@ function create_component_return_statement(
3270
3242
  type_only = false,
3271
3243
  ) {
3272
3244
  const cloned = render_nodes.map((node) =>
3273
- map_render_node_locations ? clone_expression_node(node) : clone_expression_node(node, false),
3245
+ map_render_node_locations ? clone_ast_node(node) : clone_ast_node(node, false),
3274
3246
  );
3275
3247
 
3276
3248
  return set_loc(
@@ -3428,7 +3400,7 @@ function prepend_render_nodes_to_return_statement(
3428
3400
  * @returns {any}
3429
3401
  */
3430
3402
  function combine_render_return_argument(render_nodes, return_argument, type_only = false) {
3431
- const combined = render_nodes.map((node) => clone_expression_node(node, false));
3403
+ const combined = render_nodes.map((node) => clone_ast_node(node, false));
3432
3404
 
3433
3405
  if (return_argument != null && !is_null_literal(return_argument)) {
3434
3406
  combined.push(return_argument_to_render_node(return_argument));
@@ -3473,7 +3445,7 @@ function is_null_literal(node) {
3473
3445
  * @returns {{ source_decl: any, source_normalize_decl: any }}
3474
3446
  */
3475
3447
  function build_array_normalization_decls(source_id, source_expr) {
3476
- const source_decl = b.let(clone_identifier(source_id), clone_expression_node(source_expr));
3448
+ const source_decl = b.let(clone_identifier(source_id), clone_ast_node(source_expr));
3477
3449
  const is_array_call = b.call(b.member(b.id('Array'), 'isArray'), clone_identifier(source_id));
3478
3450
  const from_call = b.call(b.member(b.id('Array'), 'from'), clone_identifier(source_id));
3479
3451
  const normalized = b.conditional(is_array_call, clone_identifier(source_id), from_call);
@@ -3519,7 +3491,7 @@ function build_hoisted_for_of_with_hooks(node, transform_context) {
3519
3491
  const use_iterable_helper = !!transform_context.platform.imports.forOfIterableHelper;
3520
3492
  const { source_decl, source_normalize_decl } = use_iterable_helper
3521
3493
  ? {
3522
- source_decl: b.let(clone_identifier(source_id), clone_expression_node(node.right)),
3494
+ source_decl: b.let(clone_identifier(source_id), clone_ast_node(node.right)),
3523
3495
  source_normalize_decl: null,
3524
3496
  }
3525
3497
  : build_array_normalization_decls(source_id, node.right);
@@ -3621,10 +3593,12 @@ function build_hoisted_for_of_with_hooks(node, transform_context) {
3621
3593
 
3622
3594
  const body_key_expression = find_key_expression_in_body(original_loop_body);
3623
3595
  const explicit_key_expression =
3624
- body_key_expression ?? (node.key ? clone_expression_node(node.key) : undefined);
3596
+ body_key_expression ?? (node.key ? clone_ast_node(node.key) : undefined);
3625
3597
  const key_expression =
3626
3598
  explicit_key_expression ??
3627
- (loop_params.length >= 2 ? clone_identifier(loop_params[1]) : undefined);
3599
+ (loop_params.length >= 2
3600
+ ? clone_identifier(/** @type {AST.Identifier} */ (loop_params[1]))
3601
+ : undefined);
3628
3602
  if (key_expression) {
3629
3603
  callback_invocation_element.openingElement.attributes.push(
3630
3604
  b.jsx_attribute(b.jsx_id('key'), to_jsx_expression_container(key_expression, key_expression)),
@@ -3834,9 +3808,9 @@ function to_jsx_element(
3834
3808
  }
3835
3809
 
3836
3810
  /**
3837
- * @param {any[]} children
3811
+ * @param {AST.Node[]} children
3838
3812
  * @param {TransformContext} transform_context
3839
- * @returns {any[]}
3813
+ * @returns {ESTreeJSX.JSXElement['children']}
3840
3814
  */
3841
3815
 
3842
3816
  function create_element_children(children, transform_context) {
@@ -3848,9 +3822,7 @@ function create_element_children(children, transform_context) {
3848
3822
  const saved_inside_element_child = transform_context.inside_element_child;
3849
3823
  transform_context.inside_element_child = true;
3850
3824
  try {
3851
- return wrap_edge_whitespace(
3852
- children.map((/** @type {any} */ child) => to_jsx_child(child, transform_context)),
3853
- );
3825
+ return wrap_edge_whitespace(children.map((child) => to_jsx_child(child, transform_context)));
3854
3826
  } finally {
3855
3827
  transform_context.inside_element_child = saved_inside_element_child;
3856
3828
  }
@@ -3866,7 +3838,7 @@ function create_element_children(children, transform_context) {
3866
3838
  }
3867
3839
 
3868
3840
  /**
3869
- * @param {any[]} children
3841
+ * @param {AST.Node[]} children
3870
3842
  * @returns {boolean}
3871
3843
  */
3872
3844
  function children_contain_return_semantics(children) {
@@ -3874,35 +3846,35 @@ function children_contain_return_semantics(children) {
3874
3846
  }
3875
3847
 
3876
3848
  /**
3877
- * @param {any} node
3849
+ * @param {unknown} node
3878
3850
  * @returns {boolean}
3879
3851
  */
3880
3852
  function child_contains_return_semantics(node) {
3853
+ if (Array.isArray(node)) {
3854
+ return node.some(child_contains_return_semantics);
3855
+ }
3881
3856
  if (!node || typeof node !== 'object') {
3882
3857
  return false;
3883
3858
  }
3859
+ const ast_node = /** @type {Record<string, unknown>} */ (node);
3884
3860
 
3885
- if (node.type === 'ReturnStatement') {
3861
+ if (ast_node.type === 'ReturnStatement') {
3886
3862
  return true;
3887
3863
  }
3888
3864
 
3889
3865
  if (
3890
- node.type === 'FunctionDeclaration' ||
3891
- node.type === 'FunctionExpression' ||
3892
- node.type === 'ArrowFunctionExpression'
3866
+ ast_node.type === 'FunctionDeclaration' ||
3867
+ ast_node.type === 'FunctionExpression' ||
3868
+ ast_node.type === 'ArrowFunctionExpression'
3893
3869
  ) {
3894
3870
  return false;
3895
3871
  }
3896
3872
 
3897
- if (Array.isArray(node)) {
3898
- return node.some(child_contains_return_semantics);
3899
- }
3900
-
3901
- for (const key of Object.keys(node)) {
3873
+ for (const key of Object.keys(ast_node)) {
3902
3874
  if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
3903
3875
  continue;
3904
3876
  }
3905
- if (child_contains_return_semantics(node[key])) {
3877
+ if (child_contains_return_semantics(ast_node[key])) {
3906
3878
  return true;
3907
3879
  }
3908
3880
  }
@@ -3911,11 +3883,11 @@ function child_contains_return_semantics(node) {
3911
3883
  }
3912
3884
 
3913
3885
  /**
3914
- * @param {any} node
3915
- * @returns {boolean}
3886
+ * @param {AST.Node | null | undefined} node
3887
+ * @returns {node is AST.TSRXRenderChild}
3916
3888
  */
3917
3889
  function is_inline_element_child(node) {
3918
- return node && is_render_child_node(node);
3890
+ return !!node && is_render_child_node(node);
3919
3891
  }
3920
3892
 
3921
3893
  /**
@@ -4278,7 +4250,7 @@ function get_body_source_node(body_nodes) {
4278
4250
  const first = body_nodes[0];
4279
4251
  const last = body_nodes[body_nodes.length - 1];
4280
4252
 
4281
- if (first?.loc && last?.loc) {
4253
+ if (has_location(first) && has_location(last)) {
4282
4254
  return {
4283
4255
  start: first.start,
4284
4256
  end: last.end,
@@ -4333,8 +4305,8 @@ function get_raw_jsx_code_block_body_nodes(node) {
4333
4305
  }
4334
4306
 
4335
4307
  /**
4336
- * @param {any} node
4337
- * @returns {boolean}
4308
+ * @param {AST.Node | null | undefined} node
4309
+ * @returns {node is AST.NativeTSRXNode}
4338
4310
  */
4339
4311
  function is_native_tsrx_node(node) {
4340
4312
  return (
@@ -4342,21 +4314,21 @@ function is_native_tsrx_node(node) {
4342
4314
  ((node?.type === 'JSXElement' ||
4343
4315
  node?.type === 'JSXFragment' ||
4344
4316
  node?.type === 'JSXStyleElement') &&
4345
- node.metadata?.native_tsrx)
4317
+ !!node.metadata?.native_tsrx)
4346
4318
  );
4347
4319
  }
4348
4320
 
4349
4321
  /**
4350
- * @param {any} node
4351
- * @returns {boolean}
4322
+ * @param {AST.Node | null | undefined} node
4323
+ * @returns {node is AST.IfStatement | AST.JSXIfExpression}
4352
4324
  */
4353
4325
  function is_if_control_node(node) {
4354
4326
  return node?.type === 'IfStatement' || node?.type === 'JSXIfExpression';
4355
4327
  }
4356
4328
 
4357
4329
  /**
4358
- * @param {any} node
4359
- * @returns {boolean}
4330
+ * @param {AST.Node | null | undefined} node
4331
+ * @returns {node is AST.TSRXRenderChild}
4360
4332
  */
4361
4333
  function is_render_child_node(node) {
4362
4334
  if (!node) return false;
@@ -4409,8 +4381,25 @@ function is_try_control_node(node) {
4409
4381
  * are pulled out; whitespace runs containing a newline are layout indentation and
4410
4382
  * are left for the host compiler to collapse.
4411
4383
  *
4412
- * @param {any[]} nodes
4413
- * @returns {any[]}
4384
+ */
4385
+ /**
4386
+ * @overload
4387
+ * @param {ESTreeJSX.JSXRenderChild[]} nodes
4388
+ * @returns {ESTreeJSX.JSXRenderChild[]}
4389
+ */
4390
+ /**
4391
+ * @overload
4392
+ * @param {ESTreeJSX.JSXElement['children']} nodes
4393
+ * @returns {ESTreeJSX.JSXElement['children']}
4394
+ */
4395
+ /**
4396
+ * @overload
4397
+ * @param {ESTreeJSX.JSXTransformChild[]} nodes
4398
+ * @returns {ESTreeJSX.JSXTransformChild[]}
4399
+ */
4400
+ /**
4401
+ * @param {ESTreeJSX.JSXTransformChild[]} nodes
4402
+ * @returns {ESTreeJSX.JSXTransformChild[]}
4414
4403
  */
4415
4404
  export function wrap_edge_whitespace(nodes) {
4416
4405
  const length = nodes.length;
@@ -4424,7 +4413,7 @@ export function wrap_edge_whitespace(nodes) {
4424
4413
  return nodes;
4425
4414
  }
4426
4415
 
4427
- /** @type {(ESTreeJSX.JSXExpressionContainer | ESTreeJSX.JSXText)[]} */
4416
+ /** @type {ESTreeJSX.JSXTransformChild[]} */
4428
4417
  const out = [];
4429
4418
  for (let i = 0; i < length; i++) {
4430
4419
  const node = nodes[i];
@@ -4454,7 +4443,7 @@ export function wrap_edge_whitespace(nodes) {
4454
4443
  if (value !== '') {
4455
4444
  // keep the location as we need it for @ autocomplete
4456
4445
  // and perhaps other things in the future
4457
- out.push(b.jsx_text(value, value, node));
4446
+ out.push(b.jsx_text(value, value, has_location(node) ? node : undefined));
4458
4447
  }
4459
4448
  if (trailing) {
4460
4449
  out.push(trailing);
@@ -4464,9 +4453,21 @@ export function wrap_edge_whitespace(nodes) {
4464
4453
  }
4465
4454
 
4466
4455
  /**
4467
- * @param {any} node
4456
+ * @overload
4457
+ * @param {AST.TSRXRenderChild} node
4468
4458
  * @param {TransformContext} transform_context
4469
- * @returns {any}
4459
+ * @returns {ESTreeJSX.JSXRenderChild}
4460
+ */
4461
+ /**
4462
+ * @overload
4463
+ * @param {AST.Node} node
4464
+ * @param {TransformContext} transform_context
4465
+ * @returns {AST.Node}
4466
+ */
4467
+ /**
4468
+ * @param {AST.Node} node
4469
+ * @param {TransformContext} transform_context
4470
+ * @returns {AST.Node}
4470
4471
  */
4471
4472
  function to_jsx_child(node, transform_context) {
4472
4473
  if (!node) return node;
@@ -4540,25 +4541,25 @@ function to_jsx_child(node, transform_context) {
4540
4541
  * Children have already been parsed and transformed through the normal TSRX
4541
4542
  * JSX element/text/control-flow visitors.
4542
4543
  *
4543
- * @param {any} node
4544
+ * @param {AST.TSRXJSXElement | AST.TSRXJSXFragment} node
4544
4545
  * @param {TransformContext} transform_context
4545
4546
  * @param {boolean} [in_jsx_child]
4546
- * @returns {any}
4547
+ * @returns {AST.Expression | ESTreeJSX.JSXExpressionContainer}
4547
4548
  */
4548
4549
  function tsrx_node_to_jsx_expression(node, transform_context, in_jsx_child = false) {
4549
4550
  const children = (node.children || []).filter(
4550
- (/** @type {any} */ child) =>
4551
+ (child) =>
4551
4552
  child && child.type !== 'EmptyStatement' && (child.type !== 'JSXText' || child.value !== ''),
4552
4553
  );
4553
4554
 
4554
- /** @type {any} */
4555
- let expression;
4555
+ /** @type {AST.Expression | null} */
4556
+ let expression = null;
4556
4557
  if (children.length === 0) {
4557
4558
  // An empty fragment is a real value: keep it as `<></>` in BOTH child and
4558
4559
  // expression position. Lowering it to a bare `null` in expression position
4559
4560
  // (e.g. `let b = <></>`) drops the author's fragment and changes its type;
4560
4561
  // `<></>` is a valid value and keeps the to_ts/runtime view faithful.
4561
- expression = set_loc(b.jsx_fragment([]), node.loc ? node : undefined);
4562
+ expression = set_loc(b.jsx_fragment([]), has_location(node) ? node : undefined);
4562
4563
  } else if (
4563
4564
  children.length === 1 &&
4564
4565
  (is_empty_jsx_fragment(children[0]) ||
@@ -4569,7 +4570,7 @@ function tsrx_node_to_jsx_expression(node, transform_context, in_jsx_child = fal
4569
4570
  // dropping the outer fragment the author wrote. Keep both levels. (`<><></></>`
4570
4571
  // is kept regardless; a non-empty inner is only kept for an authored outer, so
4571
4572
  // a generated wrapper still collapses.)
4572
- expression = set_loc(b.jsx_fragment(children), node.loc ? node : undefined);
4573
+ expression = set_loc(b.jsx_fragment(children), has_location(node) ? node : undefined);
4573
4574
  } else {
4574
4575
  expression = return_value_body_to_expression(children, node, transform_context);
4575
4576
  }
@@ -4580,7 +4581,7 @@ function tsrx_node_to_jsx_expression(node, transform_context, in_jsx_child = fal
4580
4581
  transform_context.inside_element_child = true;
4581
4582
  try {
4582
4583
  const render_nodes = wrap_edge_whitespace(
4583
- children.map((/** @type {any} */ child) => to_jsx_child(child, transform_context)),
4584
+ children.map((child) => to_jsx_child(child, transform_context)),
4584
4585
  );
4585
4586
  expression =
4586
4587
  build_return_expression(render_nodes, in_jsx_child, transform_context.typeOnly) ||
@@ -4593,13 +4594,7 @@ function tsrx_node_to_jsx_expression(node, transform_context, in_jsx_child = fal
4593
4594
  }
4594
4595
  }
4595
4596
 
4596
- if (
4597
- in_jsx_child &&
4598
- expression.type !== 'JSXElement' &&
4599
- expression.type !== 'JSXFragment' &&
4600
- expression.type !== 'JSXText' &&
4601
- expression.type !== 'JSXExpressionContainer'
4602
- ) {
4597
+ if (in_jsx_child && expression.type !== 'JSXElement' && expression.type !== 'JSXFragment') {
4603
4598
  return to_jsx_expression_container(expression, node);
4604
4599
  }
4605
4600
 
@@ -4695,14 +4690,14 @@ function create_statement_iife(body_nodes, source_node, transform_context) {
4695
4690
  }
4696
4691
 
4697
4692
  /**
4698
- * @param {any} node
4699
- * @param {any} source_node
4693
+ * @param {AST.Expression} node
4694
+ * @param {AST.Node | null | undefined} source_node
4700
4695
  * @param {TransformContext} [transform_context]
4701
- * @returns {any}
4696
+ * @returns {AST.Expression}
4702
4697
  */
4703
4698
  function set_generated_expression_loc(node, source_node, transform_context) {
4704
- if (transform_context?.typeOnly || !source_node?.loc) return node;
4705
- return setLocation(/** @type {any} */ (node), source_node);
4699
+ if (transform_context?.typeOnly || !has_location(source_node)) return node;
4700
+ return setLocation(node, source_node);
4706
4701
  }
4707
4702
 
4708
4703
  /**
@@ -5072,17 +5067,17 @@ function for_of_statement_to_jsx_child(node, transform_context) {
5072
5067
  body_contains_top_level_hook_call(loop_body, transform_context, true);
5073
5068
  const body_key_expression = find_key_expression_in_body(loop_body);
5074
5069
  const explicit_key_expression =
5075
- body_key_expression ?? (node.key ? clone_expression_node(node.key) : undefined);
5070
+ body_key_expression ?? (node.key ? clone_ast_node(node.key) : undefined);
5076
5071
  const key_expression =
5077
5072
  has_hooks && explicit_key_expression == null && node.index
5078
- ? clone_expression_node(node.index)
5073
+ ? clone_ast_node(node.index)
5079
5074
  : explicit_key_expression;
5080
5075
  const implicit_non_hook_key_expression =
5081
5076
  !has_hooks && body_key_expression == null
5082
5077
  ? node.key
5083
- ? clone_expression_node(node.key)
5078
+ ? clone_ast_node(node.key)
5084
5079
  : node.index
5085
- ? clone_expression_node(node.index)
5080
+ ? clone_ast_node(node.index)
5086
5081
  : undefined
5087
5082
  : undefined;
5088
5083
 
@@ -5158,7 +5153,7 @@ function for_of_statement_to_jsx_child(node, transform_context) {
5158
5153
  b.conditional(
5159
5154
  b.binary(
5160
5155
  '===',
5161
- b.member(clone_expression_node(node.right), create_generated_identifier('length')),
5156
+ b.member(clone_ast_node(node.right), create_generated_identifier('length')),
5162
5157
  b.literal(0),
5163
5158
  ),
5164
5159
  empty_fallback,
@@ -5199,7 +5194,7 @@ function apply_key_to_loop_body(body_nodes, key_expression) {
5199
5194
  ...attributes,
5200
5195
  b.jsx_attribute(
5201
5196
  b.jsx_id('key'),
5202
- to_jsx_expression_container(clone_expression_node(key_expression), key_expression),
5197
+ to_jsx_expression_container(clone_ast_node(key_expression), key_expression),
5203
5198
  ),
5204
5199
  ],
5205
5200
  },
@@ -5280,7 +5275,7 @@ function apply_key_to_jsx_element(element, key_expression) {
5280
5275
  ...attributes,
5281
5276
  b.jsx_attribute(
5282
5277
  b.jsx_id('key'),
5283
- to_jsx_expression_container(clone_expression_node(key_expression), key_expression),
5278
+ to_jsx_expression_container(clone_ast_node(key_expression), key_expression),
5284
5279
  ),
5285
5280
  ],
5286
5281
  },
@@ -5296,7 +5291,7 @@ function keyed_fragment_to_jsx_element(fragment, key_expression) {
5296
5291
  const name = b.jsx_id('Fragment');
5297
5292
  const key_attribute = b.jsx_attribute(
5298
5293
  b.jsx_id('key'),
5299
- to_jsx_expression_container(clone_expression_node(key_expression), key_expression),
5294
+ to_jsx_expression_container(clone_ast_node(key_expression), key_expression),
5300
5295
  );
5301
5296
 
5302
5297
  return b.jsx_element_fresh(
@@ -5502,9 +5497,9 @@ function try_statement_to_jsx_child(node, transform_context) {
5502
5497
  * Create a simple JSX element AST node.
5503
5498
  *
5504
5499
  * @param {string} tag_name
5505
- * @param {any[]} attributes
5506
- * @param {any[]} children
5507
- * @returns {any}
5500
+ * @param {ESTreeJSX.JSXAttributeNode[]} attributes
5501
+ * @param {ESTreeJSX.JSXElement['children']} children
5502
+ * @returns {ESTreeJSX.JSXElement}
5508
5503
  */
5509
5504
  function create_jsx_element(tag_name, attributes, children) {
5510
5505
  const self_closing = children.length === 0;
@@ -5522,12 +5517,12 @@ function create_jsx_element(tag_name, attributes, children) {
5522
5517
  *
5523
5518
  * @param {AST.Program} program
5524
5519
  * @param {TransformContext} transform_context
5525
- * @param {JsxPlatform} platform
5520
+ * @param {Pick<JsxPlatform, 'imports'>} platform
5526
5521
  * @param {string} suspense_source - effective suspense import source after
5527
5522
  * applying any per-call override from JsxTransformOptions.suspenseSource.
5528
5523
  */
5529
5524
  function inject_try_imports(program, transform_context, platform, suspense_source) {
5530
- /** @type {any[]} */
5525
+ /** @type {AST.ImportDeclaration[]} */
5531
5526
  const imports = [];
5532
5527
 
5533
5528
  if (transform_context.needs_fragment && platform.imports.fragment) {
@@ -5569,7 +5564,7 @@ function inject_try_imports(program, transform_context, platform, suspense_sourc
5569
5564
  ? platform.imports.refProp
5570
5565
  : null;
5571
5566
 
5572
- /** @type {Map<string, any[]>} */
5567
+ /** @type {Map<string, AST.ImportSpecifier[]>} */
5573
5568
  const ref_imports = new Map();
5574
5569
 
5575
5570
  if (merge_refs_source !== null) {
@@ -5609,9 +5604,9 @@ function inject_try_imports(program, transform_context, platform, suspense_sourc
5609
5604
  }
5610
5605
 
5611
5606
  /**
5612
- * @param {Map<string, any[]>} imports
5607
+ * @param {Map<string, AST.ImportSpecifier[]>} imports
5613
5608
  * @param {string} source
5614
- * @param {any} specifier
5609
+ * @param {AST.ImportSpecifier} specifier
5615
5610
  */
5616
5611
  function add_ref_import_specifier(imports, source, specifier) {
5617
5612
  const specifiers = imports.get(source);
@@ -5623,9 +5618,9 @@ function add_ref_import_specifier(imports, source, specifier) {
5623
5618
  }
5624
5619
 
5625
5620
  /**
5626
- * @param {any} node
5621
+ * @param {AST.IfStatement | AST.JSXIfExpression} node
5627
5622
  * @param {TransformContext} transform_context
5628
- * @returns {any}
5623
+ * @returns {AST.IfStatement}
5629
5624
  */
5630
5625
  function create_render_if_statement(node, transform_context) {
5631
5626
  const consequent_body =
@@ -5642,7 +5637,8 @@ function create_render_if_statement(node, transform_context) {
5642
5637
  if (is_if_control_node(node.alternate)) {
5643
5638
  alternate = create_render_if_statement(node.alternate, transform_context);
5644
5639
  } else {
5645
- const alternate_body = node.alternate.body || [node.alternate];
5640
+ const alternate_body =
5641
+ node.alternate.type === 'BlockStatement' ? node.alternate.body : [node.alternate];
5646
5642
  if (is_template_if_node(node)) {
5647
5643
  validate_if_body_control_flow(alternate_body, transform_context);
5648
5644
  }
@@ -5685,8 +5681,8 @@ function create_render_if_statement(node, transform_context) {
5685
5681
  * `own_body` is the case's isolated consequent. JSX `@switch` cases do not
5686
5682
  * fall through, so `break` is not part of the template switch model.
5687
5683
  *
5688
- * @param {any[]} consequent
5689
- * @returns {{ own_body: any[], has_terminator: boolean }}
5684
+ * @param {AST.Statement[]} consequent
5685
+ * @returns {{ own_body: AST.Statement[], has_terminator: boolean }}
5690
5686
  */
5691
5687
  function summarize_switch_case_body(consequent) {
5692
5688
  const own_body = [];
@@ -5716,10 +5712,10 @@ function summarize_switch_case_body(consequent) {
5716
5712
  * see double/triple hits per source range.
5717
5713
  *
5718
5714
  * @param {{ component_element: ESTreeJSX.JSXElement }} helper
5719
- * @returns {any}
5715
+ * @returns {ESTreeJSX.JSXElement}
5720
5716
  */
5721
5717
  export function clone_switch_helper_invocation(helper) {
5722
- return clone_expression_node(helper.component_element, false);
5718
+ return clone_ast_node(helper.component_element, false);
5723
5719
  }
5724
5720
 
5725
5721
  /**
@@ -5736,31 +5732,29 @@ export function clone_switch_helper_invocation(helper) {
5736
5732
  * React, Vue, and Solid) already pushed their declarations into
5737
5733
  * `transform_context.helper_state.helpers`, so `setup_statements` is empty.
5738
5734
  *
5739
- * @param {any} switch_node
5735
+ * @param {AST.SwitchStatement} switch_node
5740
5736
  * @param {TransformContext} transform_context
5741
5737
  * @returns {{
5742
- * case_info: Array<{ own_body: any[], has_terminator: boolean }>,
5743
- * case_helpers: Array<{ setup_statements: any[], component_element: ESTreeJSX.JSXElement } | null>,
5744
- * setup_statements: any[],
5738
+ * case_info: Array<{ own_body: AST.Statement[], has_terminator: boolean }>,
5739
+ * case_helpers: Array<JsxHelperComponent | null>,
5740
+ * setup_statements: AST.Statement[],
5745
5741
  * }}
5746
5742
  */
5747
5743
  export function plan_switch_lift(switch_node, transform_context) {
5748
- const case_info = switch_node.cases.map((/** @type {any} */ c) => {
5744
+ const case_info = switch_node.cases.map((c) => {
5749
5745
  const consequent = flatten_switch_consequent(c.consequent || []);
5750
5746
  return summarize_switch_case_body(consequent);
5751
5747
  });
5752
5748
 
5753
5749
  // A case body needs to be lifted iff it contains hooks. Cases are isolated,
5754
5750
  // so downstream case bodies are never duplicated into earlier arms.
5755
- const needs_helper = case_info.map(
5756
- (/** @type {{ own_body: any[], has_terminator: boolean }} */ info) => {
5757
- if (info.own_body.length === 0) return false;
5758
- return (
5759
- should_extract_hook_helpers(transform_context) &&
5760
- body_contains_top_level_hook_call(info.own_body, transform_context, true)
5761
- );
5762
- },
5763
- );
5751
+ const needs_helper = case_info.map((info) => {
5752
+ if (info.own_body.length === 0) return false;
5753
+ return (
5754
+ should_extract_hook_helpers(transform_context) &&
5755
+ body_contains_top_level_hook_call(info.own_body, transform_context, true)
5756
+ );
5757
+ });
5764
5758
 
5765
5759
  // Pre-allocate helper ids in source order so the snapshot's
5766
5760
  // `StatementBodyHook<N>` numbering reads top-to-bottom by case position
@@ -5772,7 +5766,7 @@ export function plan_switch_lift(switch_node, transform_context) {
5772
5766
  : null,
5773
5767
  );
5774
5768
 
5775
- /** @type {Array<{ setup_statements: any[], component_element: ESTreeJSX.JSXElement } | null>} */
5769
+ /** @type {Array<JsxHelperComponent | null>} */
5776
5770
  const case_helpers = new Array(switch_node.cases.length).fill(null);
5777
5771
 
5778
5772
  for (let i = switch_node.cases.length - 1; i >= 0; i--) {
@@ -5784,12 +5778,13 @@ export function plan_switch_lift(switch_node, transform_context) {
5784
5778
  undefined,
5785
5779
  switch_node.cases[i],
5786
5780
  transform_context,
5787
- /** @type {any} */ (helper_ids[i]),
5781
+ /** @type {AST.Identifier} */ (helper_ids[i]),
5788
5782
  );
5789
5783
  }
5790
5784
 
5791
5785
  // Hoist all helpers' setup statements above the switch in source order so
5792
5786
  // the switch body stays a pure dispatcher.
5787
+ /** @type {AST.Statement[]} */
5793
5788
  const setup_statements = [];
5794
5789
  for (const helper of case_helpers) {
5795
5790
  if (helper) setup_statements.push(...helper.setup_statements);
@@ -5803,9 +5798,9 @@ export function plan_switch_lift(switch_node, transform_context) {
5803
5798
  }
5804
5799
 
5805
5800
  /**
5806
- * @param {any} switch_node
5801
+ * @param {AST.SwitchStatement} switch_node
5807
5802
  * @param {TransformContext} transform_context
5808
- * @returns {{ setup_statements: any[], switch_statement: any }}
5803
+ * @returns {{ setup_statements: AST.Statement[], switch_statement: AST.SwitchStatement }}
5809
5804
  */
5810
5805
  function build_switch_with_lift(switch_node, transform_context) {
5811
5806
  const { case_info, case_helpers, setup_statements } = plan_switch_lift(
@@ -5813,92 +5808,95 @@ function build_switch_with_lift(switch_node, transform_context) {
5813
5808
  transform_context,
5814
5809
  );
5815
5810
 
5816
- const new_cases = switch_node.cases.map(
5817
- (/** @type {any} */ original_case, /** @type {number} */ i) => {
5818
- const helper = case_helpers[i];
5819
- if (helper) {
5820
- return set_loc(
5821
- b.switch_case(original_case.test, [
5822
- create_component_return_statement(
5823
- [helper.component_element],
5824
- original_case,
5825
- true,
5826
- transform_context.typeOnly,
5827
- ),
5828
- ]),
5829
- original_case,
5830
- );
5831
- }
5811
+ const new_cases = switch_node.cases.map((original_case, /** @type {number} */ i) => {
5812
+ const helper = case_helpers[i];
5813
+ if (helper) {
5814
+ return set_loc(
5815
+ b.switch_case(original_case.test, [
5816
+ create_component_return_statement(
5817
+ [helper.component_element],
5818
+ original_case,
5819
+ true,
5820
+ transform_context.typeOnly,
5821
+ ),
5822
+ ]),
5823
+ original_case,
5824
+ );
5825
+ }
5832
5826
 
5833
- const { own_body, has_terminator } = case_info[i];
5827
+ const { own_body, has_terminator } = case_info[i];
5834
5828
 
5835
- if (own_body.length === 0 && !has_terminator) {
5836
- return set_loc(
5837
- b.switch_case(original_case.test, [create_null_return_statement()]),
5838
- original_case,
5829
+ if (own_body.length === 0 && !has_terminator) {
5830
+ return set_loc(
5831
+ b.switch_case(original_case.test, [create_null_return_statement()]),
5832
+ original_case,
5833
+ );
5834
+ }
5835
+
5836
+ /** @type {AST.Statement[]} */
5837
+ const case_body = [];
5838
+ /** @type {ESTreeJSX.JSXElement['children']} */
5839
+ const render_nodes = [];
5840
+ let has_terminal = false;
5841
+
5842
+ for (const child of own_body) {
5843
+ if (is_loop_skip_return_statement(child)) {
5844
+ case_body.push(
5845
+ create_component_return_statement(render_nodes, child, true, transform_context.typeOnly),
5839
5846
  );
5847
+ has_terminal = true;
5848
+ break;
5840
5849
  }
5841
-
5842
- const case_body = [];
5843
- const render_nodes = [];
5844
- let has_terminal = false;
5845
-
5846
- for (const child of own_body) {
5847
- if (is_loop_skip_return_statement(child)) {
5848
- case_body.push(
5849
- create_component_return_statement(
5850
- render_nodes,
5851
- child,
5852
- true,
5853
- transform_context.typeOnly,
5854
- ),
5855
- );
5856
- has_terminal = true;
5857
- break;
5858
- }
5859
- if (child.type === 'ReturnStatement') {
5860
- case_body.push(child);
5861
- has_terminal = true;
5862
- break;
5863
- }
5864
- if (is_render_child_node(child)) {
5865
- render_nodes.push(to_jsx_child(child, transform_context));
5866
- } else if (is_bare_render_expression(child)) {
5867
- render_nodes.push(to_jsx_expression_container(child, child));
5868
- } else {
5869
- case_body.push(child);
5870
- }
5850
+ if (child.type === 'ReturnStatement') {
5851
+ case_body.push(child);
5852
+ has_terminal = true;
5853
+ break;
5854
+ }
5855
+ if (is_render_child_node(child)) {
5856
+ render_nodes.push(
5857
+ /** @type {ESTreeJSX.JSXElement['children'][number]} */ (
5858
+ to_jsx_child(child, transform_context)
5859
+ ),
5860
+ );
5861
+ } else if (is_bare_render_expression(child)) {
5862
+ render_nodes.push(to_jsx_expression_container(child, child));
5863
+ } else {
5864
+ case_body.push(child);
5871
5865
  }
5866
+ }
5872
5867
 
5873
- if (!has_terminal) {
5874
- if (render_nodes.length > 0) {
5875
- case_body.push(
5876
- create_component_return_statement(
5877
- render_nodes,
5878
- original_case,
5879
- true,
5880
- transform_context.typeOnly,
5881
- ),
5882
- );
5883
- } else if (case_body.length > 0) {
5884
- case_body.push(create_null_return_statement());
5885
- } else if (has_terminator) {
5886
- case_body.push(create_null_return_statement());
5887
- }
5868
+ if (!has_terminal) {
5869
+ if (render_nodes.length > 0) {
5870
+ case_body.push(
5871
+ create_component_return_statement(
5872
+ render_nodes,
5873
+ original_case,
5874
+ true,
5875
+ transform_context.typeOnly,
5876
+ ),
5877
+ );
5878
+ } else if (case_body.length > 0) {
5879
+ case_body.push(create_null_return_statement());
5880
+ } else if (has_terminator) {
5881
+ case_body.push(create_null_return_statement());
5888
5882
  }
5883
+ }
5889
5884
 
5890
- return set_loc(b.switch_case(original_case.test, case_body), original_case);
5891
- },
5892
- );
5885
+ return set_loc(b.switch_case(original_case.test, case_body), original_case);
5886
+ });
5893
5887
 
5894
5888
  return {
5895
5889
  setup_statements,
5896
- switch_statement: b.switch(switch_node.discriminant, new_cases, switch_node),
5890
+ switch_statement: b.switch(
5891
+ switch_node.discriminant,
5892
+ new_cases,
5893
+ has_location(switch_node) ? switch_node : undefined,
5894
+ ),
5897
5895
  };
5898
5896
  }
5899
5897
 
5900
5898
  /**
5901
- * @returns {any}
5899
+ * @returns {AST.ReturnStatement}
5902
5900
  */
5903
5901
  function create_null_return_statement() {
5904
5902
  return b.return(b.literal(null));
@@ -5906,25 +5904,25 @@ function create_null_return_statement() {
5906
5904
 
5907
5905
  /**
5908
5906
  * @param {AST.Expression} expression
5909
- * @param {any} [source_node]
5907
+ * @param {AST.Node} [source_node]
5910
5908
  * @returns {ESTreeJSX.JSXExpressionContainer}
5911
5909
  */
5912
5910
  function to_jsx_expression_container(expression, source_node = expression) {
5913
5911
  // NOTE: JSXExpressionContainer nodes are intentionally created without loc.
5914
5912
  // They are synthetic wrappers whose source positions do not correspond to
5915
5913
  // entries in the generated source map, so adding loc causes Volar mapping failures.
5916
- return /** @type {any} */ ({
5914
+ return {
5917
5915
  type: 'JSXExpressionContainer',
5918
- expression: /** @type {any} */ (expression),
5916
+ expression,
5919
5917
  metadata: { path: [] },
5920
- });
5918
+ };
5921
5919
  }
5922
5920
 
5923
5921
  /**
5924
- * Dispatch point for element attribute transformation. Platforms can replace
5925
- * the default "map over `to_jsx_attribute`" via
5926
- * `hooks.transformElementAttributes`. Whether or not the hook is used,
5927
- * the result is run through `merge_duplicate_refs` so platforms with a
5922
+ * Dispatch point for element attribute transformation. Platforms can transform
5923
+ * the parser-native JSX attributes via `hooks.transformElementAttributes`.
5924
+ * Whether or not the hook is used, the result is run through
5925
+ * `merge_duplicate_refs` so platforms with a
5928
5926
  * `multiRefStrategy` can compose an explicit `ref={...}` with compiler-
5929
5927
  * synthesized refs created for host spreads.
5930
5928
  *
@@ -5933,10 +5931,10 @@ function to_jsx_expression_container(expression, source_node = expression) {
5933
5931
  * duplicate JSX props which the JSX runtime collapses to last-wins (and
5934
5932
  * which TypeScript can't type cleanly).
5935
5933
  *
5936
- * @param {any[]} attrs
5934
+ * @param {ESTreeJSX.JSXAttributeNode[]} attrs
5937
5935
  * @param {TransformContext} transform_context
5938
- * @param {any} element
5939
- * @returns {any[]}
5936
+ * @param {AST.TSRXJSXElement} element
5937
+ * @returns {ESTreeJSX.JSXAttributeNode[]}
5940
5938
  */
5941
5939
  function transform_element_attributes_dispatch(attrs, transform_context, element) {
5942
5940
  validate_at_most_one_ref_attribute(attrs, transform_context);
@@ -5946,9 +5944,7 @@ function transform_element_attributes_dispatch(attrs, transform_context, element
5946
5944
  attrs = preprocess(attrs, transform_context, element);
5947
5945
  }
5948
5946
  const hook = transform_context.platform.hooks?.transformElementAttributes;
5949
- const result = hook
5950
- ? hook(attrs, transform_context, element)
5951
- : attrs.map((/** @type {any} */ a) => to_jsx_attribute(a, transform_context));
5947
+ const result = hook ? hook(attrs, transform_context, element) : attrs;
5952
5948
  return merge_duplicate_refs(
5953
5949
  normalize_host_ref_spreads(result, !is_component, transform_context),
5954
5950
  transform_context,
@@ -5956,7 +5952,7 @@ function transform_element_attributes_dispatch(attrs, transform_context, element
5956
5952
  }
5957
5953
 
5958
5954
  /**
5959
- * @param {any} element
5955
+ * @param {AST.TSRXJSXElement | ESTreeJSX.JSXElement} element
5960
5956
  * @returns {boolean}
5961
5957
  */
5962
5958
  export function is_component_like_element(element) {
@@ -5970,7 +5966,7 @@ export function is_component_like_element(element) {
5970
5966
  }
5971
5967
 
5972
5968
  /**
5973
- * @param {any} name
5969
+ * @param {ESTreeJSX.TSRXJSXOpeningElement['name']} name
5974
5970
  * @returns {boolean}
5975
5971
  */
5976
5972
  function is_component_like_jsx_name(name) {
@@ -5981,10 +5977,10 @@ function is_component_like_jsx_name(name) {
5981
5977
  }
5982
5978
 
5983
5979
  /**
5984
- * @param {any[]} attrs
5980
+ * @param {ESTreeJSX.JSXAttributeNode[]} attrs
5985
5981
  * @param {boolean} is_host
5986
5982
  * @param {TransformContext} transform_context
5987
- * @returns {any[]}
5983
+ * @returns {ESTreeJSX.JSXAttributeNode[]}
5988
5984
  */
5989
5985
  function normalize_host_ref_spreads(attrs, is_host, transform_context) {
5990
5986
  if (!is_host) return attrs;
@@ -5996,49 +5992,55 @@ function normalize_host_ref_spreads(attrs, is_host, transform_context) {
5996
5992
  .map((attr) => attr.value.expression);
5997
5993
  const needs_synthetic_spread_ref = needs_explicit_spread_ref || ref_exprs.length > 0;
5998
5994
 
5999
- return attrs.flatMap((attr) => {
6000
- if (!attr || attr.type !== 'JSXSpreadAttribute') {
6001
- return [attr];
6002
- }
5995
+ return attrs.flatMap(
5996
+ /**
5997
+ * @param {ESTreeJSX.JSXAttributeNode} attr
5998
+ * @returns {ESTreeJSX.JSXAttributeNode[]}
5999
+ */
6000
+ (attr) => {
6001
+ if (attr.type !== 'JSXSpreadAttribute') {
6002
+ return [attr];
6003
+ }
6003
6004
 
6004
- const normalize_helper = needs_synthetic_spread_ref
6005
- ? NORMALIZE_SPREAD_PROPS_FOR_REF_ATTR_INTERNAL_NAME
6006
- : NORMALIZE_SPREAD_PROPS_INTERNAL_NAME;
6007
- if (needs_synthetic_spread_ref) {
6008
- transform_context.needs_normalize_spread_props_for_ref_attr = true;
6009
- } else {
6010
- transform_context.needs_normalize_spread_props = true;
6011
- }
6012
- const normalized = b.call(normalize_helper, attr.argument);
6005
+ const normalize_helper = needs_synthetic_spread_ref
6006
+ ? NORMALIZE_SPREAD_PROPS_FOR_REF_ATTR_INTERNAL_NAME
6007
+ : NORMALIZE_SPREAD_PROPS_INTERNAL_NAME;
6008
+ if (needs_synthetic_spread_ref) {
6009
+ transform_context.needs_normalize_spread_props_for_ref_attr = true;
6010
+ } else {
6011
+ transform_context.needs_normalize_spread_props = true;
6012
+ }
6013
+ const normalized = b.call(normalize_helper, attr.argument);
6013
6014
 
6014
- if (needs_synthetic_spread_ref) {
6015
- const normalized_id = create_generated_identifier(
6016
- create_spread_props_name(transform_context),
6017
- );
6018
- const spread = {
6019
- ...attr,
6020
- argument: clone_identifier(normalized_id),
6021
- };
6022
- const ref_attr = b.jsx_attribute(
6023
- b.jsx_id('ref'),
6024
- to_jsx_expression_container(b.member(clone_identifier(normalized_id), 'ref'), attr),
6025
- false,
6026
- attr,
6027
- );
6028
- ref_attr.metadata = { ...(ref_attr.metadata || {}) };
6029
- /** @type {any} */ (ref_attr.metadata).synthetic_ref = true;
6030
- add_jsx_setup_declaration(spread, b.let(clone_identifier(normalized_id), normalized));
6015
+ if (needs_synthetic_spread_ref) {
6016
+ const normalized_id = create_generated_identifier(
6017
+ create_spread_props_name(transform_context),
6018
+ );
6019
+ const spread = {
6020
+ ...attr,
6021
+ argument: clone_identifier(normalized_id),
6022
+ };
6023
+ const ref_attr = b.jsx_attribute(
6024
+ b.jsx_id('ref'),
6025
+ to_jsx_expression_container(b.member(clone_identifier(normalized_id), 'ref'), attr),
6026
+ false,
6027
+ has_location(attr) ? attr : undefined,
6028
+ );
6029
+ ref_attr.metadata = { ...(ref_attr.metadata || {}) };
6030
+ ref_attr.metadata.synthetic_ref = true;
6031
+ add_jsx_setup_declaration(spread, b.let(clone_identifier(normalized_id), normalized));
6031
6032
 
6032
- return [spread, ref_attr];
6033
- }
6033
+ return [spread, ref_attr];
6034
+ }
6034
6035
 
6035
- return [
6036
- {
6037
- ...attr,
6038
- argument: normalized,
6039
- },
6040
- ];
6041
- });
6036
+ return [
6037
+ {
6038
+ ...attr,
6039
+ argument: normalized,
6040
+ },
6041
+ ];
6042
+ },
6043
+ );
6042
6044
  }
6043
6045
 
6044
6046
  /**
@@ -6055,8 +6057,8 @@ function create_spread_props_name(transform_context) {
6055
6057
  }
6056
6058
 
6057
6059
  /**
6058
- * @param {any} node
6059
- * @param {any} declaration
6060
+ * @param {AST.Node} node
6061
+ * @param {AST.Statement} declaration
6060
6062
  */
6061
6063
  export function add_jsx_setup_declaration(node, declaration) {
6062
6064
  node.metadata ??= { path: [] };
@@ -6064,29 +6066,40 @@ export function add_jsx_setup_declaration(node, declaration) {
6064
6066
  }
6065
6067
 
6066
6068
  /**
6067
- * @param {any} node
6068
- * @param {Set<any>} [seen]
6069
- * @returns {any[]}
6069
+ * @param {AST.Node} node
6070
+ * @param {Set<object>} [seen]
6071
+ * @returns {AST.Statement[]}
6070
6072
  */
6071
6073
  export function extract_jsx_setup_declarations(node, seen = new Set()) {
6072
- if (node == null || typeof node !== 'object' || seen.has(node)) {
6073
- return [];
6074
- }
6075
- seen.add(node);
6074
+ /** @type {AST.Statement[]} */
6075
+ const declarations = [];
6076
+ collect_jsx_setup_declarations(node, seen, declarations);
6077
+ return declarations;
6078
+ }
6076
6079
 
6077
- const declarations = node.metadata?.generated_setup_declarations ?? [];
6078
- if (node.metadata?.generated_setup_declarations) {
6079
- delete node.metadata.generated_setup_declarations;
6080
- }
6080
+ /**
6081
+ * @param {unknown} value
6082
+ * @param {Set<object>} seen
6083
+ * @param {AST.Statement[]} declarations
6084
+ * @returns {void}
6085
+ */
6086
+ function collect_jsx_setup_declarations(value, seen, declarations) {
6087
+ if (value == null || typeof value !== 'object' || seen.has(value)) return;
6088
+ seen.add(value);
6081
6089
 
6082
- for (const key of Object.keys(node)) {
6083
- if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
6084
- continue;
6090
+ if ('type' in value && typeof value.type === 'string') {
6091
+ const node = /** @type {AST.Node} */ (value);
6092
+ if (node.metadata?.generated_setup_declarations) {
6093
+ declarations.push(...node.metadata.generated_setup_declarations);
6094
+ delete node.metadata.generated_setup_declarations;
6085
6095
  }
6086
- declarations.push(...extract_jsx_setup_declarations(node[key], seen));
6087
6096
  }
6088
6097
 
6089
- return declarations;
6098
+ const record = /** @type {Record<string, unknown>} */ (value);
6099
+ for (const key of Object.keys(record)) {
6100
+ if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') continue;
6101
+ collect_jsx_setup_declarations(record[key], seen, declarations);
6102
+ }
6090
6103
  }
6091
6104
 
6092
6105
  /**
@@ -6120,11 +6133,11 @@ function wrap_jsx_setup_declarations(expression, in_jsx_child) {
6120
6133
  * This validator runs over the raw, pre-lowering attribute list so each
6121
6134
  * shape is still distinguishable by `type`.
6122
6135
  *
6123
- * @param {any[]} raw_attrs
6136
+ * @param {ESTreeJSX.JSXAttributeNode[]} raw_attrs
6124
6137
  * @param {TransformContext} [transform_context]
6125
6138
  */
6126
6139
  export function validate_at_most_one_ref_attribute(raw_attrs, transform_context) {
6127
- /** @type {any[]} */
6140
+ /** @type {ESTreeJSX.JSXIdentifier[]} */
6128
6141
  const refs = [];
6129
6142
  for (const attr of raw_attrs) {
6130
6143
  if (!attr) continue;
@@ -6134,7 +6147,7 @@ export function validate_at_most_one_ref_attribute(raw_attrs, transform_context)
6134
6147
  attr.name.type === 'JSXIdentifier' &&
6135
6148
  attr.name.name === 'ref';
6136
6149
  if (!is_ref_attr) continue;
6137
- refs.push(attr.name);
6150
+ refs.push(/** @type {ESTreeJSX.JSXIdentifier} */ (attr.name));
6138
6151
  }
6139
6152
  if (refs.length < 2) {
6140
6153
  return;
@@ -6172,9 +6185,9 @@ export function validate_at_most_one_ref_attribute(raw_attrs, transform_context)
6172
6185
  * Single-ref elements are always left unchanged so trivial cases stay
6173
6186
  * import-free and produce no helper call.
6174
6187
  *
6175
- * @param {any[]} jsx_attrs
6188
+ * @param {ESTreeJSX.JSXAttributeNode[]} jsx_attrs
6176
6189
  * @param {TransformContext} transform_context
6177
- * @returns {any[]}
6190
+ * @returns {ESTreeJSX.JSXAttributeNode[]}
6178
6191
  */
6179
6192
  export function merge_duplicate_refs(jsx_attrs, transform_context) {
6180
6193
  const strategy = transform_context.platform.jsx.multiRefStrategy;
@@ -6194,11 +6207,11 @@ export function merge_duplicate_refs(jsx_attrs, transform_context) {
6194
6207
  // instead of silently composing them into `__mergeRefs(...)`.
6195
6208
  if (tsx_form_count >= 2) return jsx_attrs;
6196
6209
 
6197
- /** @type {any[]} */
6210
+ /** @type {AST.Expression[]} */
6198
6211
  const ref_exprs = [];
6199
- /** @type {any[]} */
6212
+ /** @type {ESTreeJSX.JSXAttributeNode[]} */
6200
6213
  const result = [];
6201
- /** @type {any} */
6214
+ /** @type {ESTreeJSX.JSXRefAttribute | null} */
6202
6215
  let source_attr = null;
6203
6216
  for (const attr of jsx_attrs) {
6204
6217
  if (is_jsx_ref_attribute(attr)) {
@@ -6228,12 +6241,15 @@ export function merge_duplicate_refs(jsx_attrs, transform_context) {
6228
6241
  // merged attribute and its name. Without this the kept `ref` keyword in
6229
6242
  // `ref={__mergeRefs(...)}` has no source mapping back to the user's `ref=`
6230
6243
  // keyword.
6231
- const merged_name = build_jsx_id('ref', source_attr?.name);
6244
+ const source_name = source_attr?.name;
6245
+ const source_name_location = has_location(source_name) ? source_name : undefined;
6246
+ const source_attr_location = has_location(source_attr) ? source_attr : undefined;
6247
+ const merged_name = build_jsx_id('ref', source_name_location);
6232
6248
  const merged_attr = build_jsx_attribute(
6233
6249
  merged_name,
6234
6250
  b.jsx_expression_container(merged_value),
6235
6251
  false,
6236
- source_attr,
6252
+ source_attr_location,
6237
6253
  );
6238
6254
  result.push(merged_attr);
6239
6255
 
@@ -6241,8 +6257,8 @@ export function merge_duplicate_refs(jsx_attrs, transform_context) {
6241
6257
  }
6242
6258
 
6243
6259
  /**
6244
- * @param {any} attr
6245
- * @returns {boolean}
6260
+ * @param {ESTreeJSX.JSXAttributeNode} attr
6261
+ * @returns {attr is ESTreeJSX.JSXRefAttribute}
6246
6262
  */
6247
6263
  function is_jsx_ref_attribute(attr) {
6248
6264
  return (
@@ -6289,7 +6305,7 @@ const MATHML_REF_TAG_NAMES = new Set(
6289
6305
  );
6290
6306
 
6291
6307
  /**
6292
- * @param {any} element
6308
+ * @param {ESTreeJSX.JSXElement | ESTreeJSX.JSXOpeningElement} element
6293
6309
  * @param {'html' | 'svg' | 'mathml'} [namespace]
6294
6310
  * @returns {AST.TypeNode | null}
6295
6311
  */
@@ -6340,13 +6356,14 @@ function infer_ref_namespace(tag_name) {
6340
6356
  }
6341
6357
 
6342
6358
  /**
6343
- * @param {any} element
6359
+ * @param {ESTreeJSX.JSXElement | ESTreeJSX.JSXOpeningElement} element
6344
6360
  * @returns {string | null}
6345
6361
  */
6346
6362
  function get_element_ref_tag_name(element) {
6347
- const name = element?.name;
6348
- if (name?.type === 'JSXIdentifier') return name.name;
6349
- if (element?.openingElement?.name?.type === 'JSXIdentifier') {
6363
+ if ('name' in element && element.name.type === 'JSXIdentifier') {
6364
+ return element.name.name;
6365
+ }
6366
+ if ('openingElement' in element && element.openingElement.name.type === 'JSXIdentifier') {
6350
6367
  return element.openingElement.name.name;
6351
6368
  }
6352
6369
  return null;
@@ -6367,70 +6384,9 @@ function create_tag_name_map_ref_type(map_name, tag_name) {
6367
6384
  }
6368
6385
 
6369
6386
  /**
6370
- * @param {any} attr
6371
- * @param {TransformContext} transform_context
6372
- * @returns {ESTreeJSX.JSXAttribute | ESTreeJSX.JSXSpreadAttribute}
6373
- */
6374
- export function to_jsx_attribute(attr, transform_context) {
6375
- if (!attr) return attr;
6376
- if (attr.type === 'JSXAttribute') {
6377
- return attr;
6378
- }
6379
- if (attr.type === 'JSXSpreadAttribute') {
6380
- return attr;
6381
- }
6382
- // Keep this legacy hook for targets that need React-style DOM attrs. The
6383
- // current first-party targets preserve authored `class`.
6384
- let attr_name = attr.name;
6385
- if (
6386
- transform_context.platform.jsx.rewriteClassAttr &&
6387
- attr_name &&
6388
- attr_name.type === 'Identifier' &&
6389
- attr_name.name === 'class'
6390
- ) {
6391
- attr_name = set_loc(b.id('className'), attr.name);
6392
- attr_name.metadata.source_name = 'class';
6393
- attr_name.metadata.source_length = 'class'.length;
6394
- }
6395
-
6396
- const name =
6397
- attr_name && attr_name.type === 'Identifier' ? identifier_to_jsx_name(attr_name) : attr_name;
6398
-
6399
- let value = attr.value;
6400
- if (value) {
6401
- if (value.type === 'Literal' && typeof value.value === 'string') {
6402
- // Keep string literal as attribute string.
6403
- } else if (value.type !== 'JSXExpressionContainer') {
6404
- value = to_jsx_expression_container(value);
6405
- }
6406
- }
6407
-
6408
- const jsx_attribute = build_jsx_attribute(name, value || null, attr.shorthand === true);
6409
-
6410
- if (value_has_unmappable_jsx_loc(value)) {
6411
- /** @type {any} */ (jsx_attribute.metadata).has_unmappable_value = true;
6412
- return jsx_attribute;
6413
- }
6414
-
6415
- return set_loc(jsx_attribute, attr);
6416
- }
6417
-
6418
- /**
6419
- * @param {any} value
6420
- * @returns {boolean}
6421
- */
6422
- function value_has_unmappable_jsx_loc(value) {
6423
- return !!(
6424
- value?.type === 'JSXExpressionContainer' &&
6425
- (value.expression?.type === 'JSXElement' || value.expression?.type === 'JSXFragment') &&
6426
- !value.expression.loc
6427
- );
6428
- }
6429
-
6430
- /**
6431
- * @param {any[]} render_nodes
6387
+ * @param {ESTreeJSX.JSXRenderChild[]} render_nodes
6432
6388
  * @param {boolean} [in_jsx_child]
6433
- * @returns {any}
6389
+ * @returns {AST.Expression | null}
6434
6390
  */
6435
6391
  export function build_return_expression(render_nodes, in_jsx_child = false, type_only = false) {
6436
6392
  if (render_nodes.length === 0) return null;
@@ -6438,10 +6394,10 @@ export function build_return_expression(render_nodes, in_jsx_child = false, type
6438
6394
  const only = render_nodes[0];
6439
6395
  if (only.type === 'JSXExpressionContainer') {
6440
6396
  if (only.metadata?.tsrx_reactive_block === true) {
6441
- return set_loc(b.jsx_fragment([only]), only.loc ? only : undefined);
6397
+ return set_loc(b.jsx_fragment([only]), has_location(only) ? only : undefined);
6442
6398
  }
6443
6399
  if (only.expression?.type === 'JSXEmptyExpression') {
6444
- return set_loc(b.jsx_fragment([]), only.loc ? only : undefined);
6400
+ return set_loc(b.jsx_fragment([]), has_location(only) ? only : undefined);
6445
6401
  }
6446
6402
  return only.expression;
6447
6403
  }
@@ -6454,7 +6410,7 @@ export function build_return_expression(render_nodes, in_jsx_child = false, type
6454
6410
  if (!type_only && !in_jsx_child && (only.value ?? '').trim() === '') {
6455
6411
  return null;
6456
6412
  }
6457
- return set_loc(b.jsx_fragment([only]), only.loc ? only : undefined);
6413
+ return set_loc(b.jsx_fragment([only]), has_location(only) ? only : undefined);
6458
6414
  }
6459
6415
  return only;
6460
6416
  }
@@ -6462,7 +6418,7 @@ export function build_return_expression(render_nodes, in_jsx_child = false, type
6462
6418
  const last = render_nodes[render_nodes.length - 1];
6463
6419
  return set_loc(
6464
6420
  b.jsx_fragment(render_nodes),
6465
- first?.loc && last?.loc
6421
+ has_location(first) && has_location(last)
6466
6422
  ? {
6467
6423
  start: first.start,
6468
6424
  end: last.end,