@tsrx/core 0.1.46 → 0.1.48

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,
@@ -62,6 +63,7 @@ import {
62
63
  import { is_hoist_safe_jsx_node } from '../jsx-hoist.js';
63
64
  import { lower_server_module_for_types } from './server-module.js';
64
65
  import {
66
+ has_location,
65
67
  is_function_or_class_node as is_function_or_class_boundary,
66
68
  is_template_directive as is_jsx_control_flow_expression,
67
69
  } from '../../utils/ast.js';
@@ -106,18 +108,6 @@ function report_jsx_fragment_in_tsrx_error(node, transform_context) {
106
108
  );
107
109
  }
108
110
 
109
- /**
110
- * Local alias for the shared `JsxTransformContext`. Kept as a typedef so the
111
- * rest of this file's `@param {TransformContext}` annotations don't all have
112
- * to spell out the import.
113
- *
114
- * @typedef {JsxTransformContext} TransformContext
115
- */
116
-
117
- /**
118
- * @typedef {{ source_name: string, read: () => any }} LazyBinding
119
- */
120
-
121
111
  /**
122
112
  * @param {any} node
123
113
  * @param {boolean} [inside_function]
@@ -392,7 +382,7 @@ function wrap_lowered_value_in_fragment(expression, source) {
392
382
  expression?.type === 'JSXExpressionContainer'
393
383
  ? expression
394
384
  : to_jsx_expression_container(expression, source);
395
- return set_loc(b.jsx_fragment([child]), source?.loc ? source : undefined);
385
+ return set_loc(b.jsx_fragment([child]), has_location(source) ? source : undefined);
396
386
  }
397
387
 
398
388
  /**
@@ -568,7 +558,7 @@ export function createJsxTransform(platform) {
568
558
  function transform(ast, source, filename, options) {
569
559
  const suspense_source = options?.suspenseSource ?? platform.imports.suspense;
570
560
  const collect = !!(options?.collect || options?.loose);
571
- /** @type {any[]} */
561
+ /** @type {AST.CSS.StyleSheet[]} */
572
562
  const stylesheets = [];
573
563
  /** @type {AST.Statement[]} */
574
564
  const type_only_style_anchors = [];
@@ -587,6 +577,14 @@ export function createJsxTransform(platform) {
587
577
  needs_dynamic_factory: false,
588
578
  needs_for_of_iterable: false,
589
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,
590
588
  stylesheets,
591
589
  type_only_style_anchors,
592
590
  module_scoped_hook_components:
@@ -881,13 +879,13 @@ function lower_dynamic_jsx_element(node, transform_context) {
881
879
 
882
880
  const dynamic_expression = dynamic_name.expression;
883
881
  if (!dynamic_expression) return;
884
- const generated_expression = clone_expression_node(dynamic_expression);
882
+ const generated_expression = clone_ast_node(dynamic_expression);
885
883
  if (node.closingElement?.name?.expression) {
886
884
  // One generated expression stands in for both tags; record the closing
887
885
  // tag's positions so editor features keep working on `</{expr}>`.
888
886
  add_extra_source_mappings_from_matching_expression(
889
887
  generated_expression,
890
- clone_expression_node(node.closingElement.name.expression),
888
+ clone_ast_node(node.closingElement.name.expression),
891
889
  );
892
890
  }
893
891
 
@@ -1298,11 +1296,7 @@ function build_render_statements(
1298
1296
  render_nodes.push(
1299
1297
  b.jsx_expression_container(
1300
1298
  set_loc(
1301
- b.conditional(
1302
- clone_expression_node(child.test),
1303
- b.literal(null),
1304
- stmt.argument,
1305
- ),
1299
+ b.conditional(clone_ast_node(child.test), b.literal(null), stmt.argument),
1306
1300
  child,
1307
1301
  ),
1308
1302
  ),
@@ -1385,7 +1379,7 @@ function build_render_statements(
1385
1379
  if (return_arg === null) {
1386
1380
  return_arg = set_loc(
1387
1381
  b.jsx_fragment([]),
1388
- source_authored_fragment.loc ? source_authored_fragment : undefined,
1382
+ has_location(source_authored_fragment) ? source_authored_fragment : undefined,
1389
1383
  );
1390
1384
  } else if (return_arg.type !== 'JSXFragment') {
1391
1385
  return_arg = wrap_lowered_value_in_fragment(return_arg, source_authored_fragment);
@@ -1587,7 +1581,7 @@ function create_helper_component_element(helper_id, bindings, source_node, mappi
1587
1581
  const { mapWrapper = true, mapBindingNames = true, mapBindingValues = true } = mapping;
1588
1582
  const attributes = bindings.map((binding) =>
1589
1583
  b.jsx_attribute(
1590
- identifier_to_jsx_name(
1584
+ identifier_to_jsx_identifier(
1591
1585
  mapBindingNames ? clone_identifier(binding) : create_generated_identifier(binding.name),
1592
1586
  ),
1593
1587
  to_jsx_expression_container(
@@ -1598,7 +1592,7 @@ function create_helper_component_element(helper_id, bindings, source_node, mappi
1598
1592
  );
1599
1593
 
1600
1594
  const opening_element = b.jsx_opening_element(
1601
- identifier_to_jsx_name(clone_identifier(helper_id)),
1595
+ identifier_to_jsx_identifier(clone_identifier(helper_id)),
1602
1596
  attributes,
1603
1597
  true,
1604
1598
  );
@@ -2356,7 +2350,7 @@ function create_style_expression_value(node, stylesheet, transform_context) {
2356
2350
  * @param {TransformContext} transform_context
2357
2351
  */
2358
2352
  function add_type_only_style_anchor(node, transform_context) {
2359
- const style_anchor = b.jsx_element(clone_expression_node(node, true), [], []);
2353
+ const style_anchor = b.jsx_element(clone_ast_node(node, true), [], []);
2360
2354
  disable_style_anchor_verification(style_anchor);
2361
2355
 
2362
2356
  const anchor_id = create_generated_identifier(create_style_anchor_name(transform_context));
@@ -2476,7 +2470,7 @@ function annotate_tsrx_with_hash(node, hash, jsx_class_attr_name, preserve_style
2476
2470
  const annotated = { ...node };
2477
2471
  annotated.children = (node.children || []).map((/** @type {any} */ statement) =>
2478
2472
  annotate_with_hash(
2479
- clone_expression_node(statement),
2473
+ clone_ast_node(statement),
2480
2474
  hash,
2481
2475
  jsx_class_attr_name,
2482
2476
  preserve_style_elements,
@@ -2841,7 +2835,11 @@ function node_contains_hook_bearing_tsrx(node, transform_context) {
2841
2835
  }
2842
2836
 
2843
2837
  if (is_native_tsrx_node(node)) {
2844
- 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
+ );
2845
2843
  }
2846
2844
 
2847
2845
  if (
@@ -3244,7 +3242,7 @@ function create_component_return_statement(
3244
3242
  type_only = false,
3245
3243
  ) {
3246
3244
  const cloned = render_nodes.map((node) =>
3247
- 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),
3248
3246
  );
3249
3247
 
3250
3248
  return set_loc(
@@ -3402,7 +3400,7 @@ function prepend_render_nodes_to_return_statement(
3402
3400
  * @returns {any}
3403
3401
  */
3404
3402
  function combine_render_return_argument(render_nodes, return_argument, type_only = false) {
3405
- const combined = render_nodes.map((node) => clone_expression_node(node, false));
3403
+ const combined = render_nodes.map((node) => clone_ast_node(node, false));
3406
3404
 
3407
3405
  if (return_argument != null && !is_null_literal(return_argument)) {
3408
3406
  combined.push(return_argument_to_render_node(return_argument));
@@ -3447,7 +3445,7 @@ function is_null_literal(node) {
3447
3445
  * @returns {{ source_decl: any, source_normalize_decl: any }}
3448
3446
  */
3449
3447
  function build_array_normalization_decls(source_id, source_expr) {
3450
- 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));
3451
3449
  const is_array_call = b.call(b.member(b.id('Array'), 'isArray'), clone_identifier(source_id));
3452
3450
  const from_call = b.call(b.member(b.id('Array'), 'from'), clone_identifier(source_id));
3453
3451
  const normalized = b.conditional(is_array_call, clone_identifier(source_id), from_call);
@@ -3493,7 +3491,7 @@ function build_hoisted_for_of_with_hooks(node, transform_context) {
3493
3491
  const use_iterable_helper = !!transform_context.platform.imports.forOfIterableHelper;
3494
3492
  const { source_decl, source_normalize_decl } = use_iterable_helper
3495
3493
  ? {
3496
- 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)),
3497
3495
  source_normalize_decl: null,
3498
3496
  }
3499
3497
  : build_array_normalization_decls(source_id, node.right);
@@ -3595,10 +3593,12 @@ function build_hoisted_for_of_with_hooks(node, transform_context) {
3595
3593
 
3596
3594
  const body_key_expression = find_key_expression_in_body(original_loop_body);
3597
3595
  const explicit_key_expression =
3598
- body_key_expression ?? (node.key ? clone_expression_node(node.key) : undefined);
3596
+ body_key_expression ?? (node.key ? clone_ast_node(node.key) : undefined);
3599
3597
  const key_expression =
3600
3598
  explicit_key_expression ??
3601
- (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);
3602
3602
  if (key_expression) {
3603
3603
  callback_invocation_element.openingElement.attributes.push(
3604
3604
  b.jsx_attribute(b.jsx_id('key'), to_jsx_expression_container(key_expression, key_expression)),
@@ -3808,9 +3808,9 @@ function to_jsx_element(
3808
3808
  }
3809
3809
 
3810
3810
  /**
3811
- * @param {any[]} children
3811
+ * @param {AST.Node[]} children
3812
3812
  * @param {TransformContext} transform_context
3813
- * @returns {any[]}
3813
+ * @returns {ESTreeJSX.JSXElement['children']}
3814
3814
  */
3815
3815
 
3816
3816
  function create_element_children(children, transform_context) {
@@ -3822,9 +3822,7 @@ function create_element_children(children, transform_context) {
3822
3822
  const saved_inside_element_child = transform_context.inside_element_child;
3823
3823
  transform_context.inside_element_child = true;
3824
3824
  try {
3825
- return wrap_edge_whitespace(
3826
- children.map((/** @type {any} */ child) => to_jsx_child(child, transform_context)),
3827
- );
3825
+ return wrap_edge_whitespace(children.map((child) => to_jsx_child(child, transform_context)));
3828
3826
  } finally {
3829
3827
  transform_context.inside_element_child = saved_inside_element_child;
3830
3828
  }
@@ -3840,7 +3838,7 @@ function create_element_children(children, transform_context) {
3840
3838
  }
3841
3839
 
3842
3840
  /**
3843
- * @param {any[]} children
3841
+ * @param {AST.Node[]} children
3844
3842
  * @returns {boolean}
3845
3843
  */
3846
3844
  function children_contain_return_semantics(children) {
@@ -3848,35 +3846,35 @@ function children_contain_return_semantics(children) {
3848
3846
  }
3849
3847
 
3850
3848
  /**
3851
- * @param {any} node
3849
+ * @param {unknown} node
3852
3850
  * @returns {boolean}
3853
3851
  */
3854
3852
  function child_contains_return_semantics(node) {
3853
+ if (Array.isArray(node)) {
3854
+ return node.some(child_contains_return_semantics);
3855
+ }
3855
3856
  if (!node || typeof node !== 'object') {
3856
3857
  return false;
3857
3858
  }
3859
+ const ast_node = /** @type {Record<string, unknown>} */ (node);
3858
3860
 
3859
- if (node.type === 'ReturnStatement') {
3861
+ if (ast_node.type === 'ReturnStatement') {
3860
3862
  return true;
3861
3863
  }
3862
3864
 
3863
3865
  if (
3864
- node.type === 'FunctionDeclaration' ||
3865
- node.type === 'FunctionExpression' ||
3866
- node.type === 'ArrowFunctionExpression'
3866
+ ast_node.type === 'FunctionDeclaration' ||
3867
+ ast_node.type === 'FunctionExpression' ||
3868
+ ast_node.type === 'ArrowFunctionExpression'
3867
3869
  ) {
3868
3870
  return false;
3869
3871
  }
3870
3872
 
3871
- if (Array.isArray(node)) {
3872
- return node.some(child_contains_return_semantics);
3873
- }
3874
-
3875
- for (const key of Object.keys(node)) {
3873
+ for (const key of Object.keys(ast_node)) {
3876
3874
  if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
3877
3875
  continue;
3878
3876
  }
3879
- if (child_contains_return_semantics(node[key])) {
3877
+ if (child_contains_return_semantics(ast_node[key])) {
3880
3878
  return true;
3881
3879
  }
3882
3880
  }
@@ -3885,11 +3883,11 @@ function child_contains_return_semantics(node) {
3885
3883
  }
3886
3884
 
3887
3885
  /**
3888
- * @param {any} node
3889
- * @returns {boolean}
3886
+ * @param {AST.Node | null | undefined} node
3887
+ * @returns {node is AST.TSRXRenderChild}
3890
3888
  */
3891
3889
  function is_inline_element_child(node) {
3892
- return node && is_render_child_node(node);
3890
+ return !!node && is_render_child_node(node);
3893
3891
  }
3894
3892
 
3895
3893
  /**
@@ -4252,7 +4250,7 @@ function get_body_source_node(body_nodes) {
4252
4250
  const first = body_nodes[0];
4253
4251
  const last = body_nodes[body_nodes.length - 1];
4254
4252
 
4255
- if (first?.loc && last?.loc) {
4253
+ if (has_location(first) && has_location(last)) {
4256
4254
  return {
4257
4255
  start: first.start,
4258
4256
  end: last.end,
@@ -4307,8 +4305,8 @@ function get_raw_jsx_code_block_body_nodes(node) {
4307
4305
  }
4308
4306
 
4309
4307
  /**
4310
- * @param {any} node
4311
- * @returns {boolean}
4308
+ * @param {AST.Node | null | undefined} node
4309
+ * @returns {node is AST.NativeTSRXNode}
4312
4310
  */
4313
4311
  function is_native_tsrx_node(node) {
4314
4312
  return (
@@ -4316,21 +4314,21 @@ function is_native_tsrx_node(node) {
4316
4314
  ((node?.type === 'JSXElement' ||
4317
4315
  node?.type === 'JSXFragment' ||
4318
4316
  node?.type === 'JSXStyleElement') &&
4319
- node.metadata?.native_tsrx)
4317
+ !!node.metadata?.native_tsrx)
4320
4318
  );
4321
4319
  }
4322
4320
 
4323
4321
  /**
4324
- * @param {any} node
4325
- * @returns {boolean}
4322
+ * @param {AST.Node | null | undefined} node
4323
+ * @returns {node is AST.IfStatement | AST.JSXIfExpression}
4326
4324
  */
4327
4325
  function is_if_control_node(node) {
4328
4326
  return node?.type === 'IfStatement' || node?.type === 'JSXIfExpression';
4329
4327
  }
4330
4328
 
4331
4329
  /**
4332
- * @param {any} node
4333
- * @returns {boolean}
4330
+ * @param {AST.Node | null | undefined} node
4331
+ * @returns {node is AST.TSRXRenderChild}
4334
4332
  */
4335
4333
  function is_render_child_node(node) {
4336
4334
  if (!node) return false;
@@ -4383,8 +4381,25 @@ function is_try_control_node(node) {
4383
4381
  * are pulled out; whitespace runs containing a newline are layout indentation and
4384
4382
  * are left for the host compiler to collapse.
4385
4383
  *
4386
- * @param {any[]} nodes
4387
- * @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[]}
4388
4403
  */
4389
4404
  export function wrap_edge_whitespace(nodes) {
4390
4405
  const length = nodes.length;
@@ -4398,7 +4413,7 @@ export function wrap_edge_whitespace(nodes) {
4398
4413
  return nodes;
4399
4414
  }
4400
4415
 
4401
- /** @type {(ESTreeJSX.JSXExpressionContainer | ESTreeJSX.JSXText)[]} */
4416
+ /** @type {ESTreeJSX.JSXTransformChild[]} */
4402
4417
  const out = [];
4403
4418
  for (let i = 0; i < length; i++) {
4404
4419
  const node = nodes[i];
@@ -4428,7 +4443,7 @@ export function wrap_edge_whitespace(nodes) {
4428
4443
  if (value !== '') {
4429
4444
  // keep the location as we need it for @ autocomplete
4430
4445
  // and perhaps other things in the future
4431
- out.push(b.jsx_text(value, value, node));
4446
+ out.push(b.jsx_text(value, value, has_location(node) ? node : undefined));
4432
4447
  }
4433
4448
  if (trailing) {
4434
4449
  out.push(trailing);
@@ -4438,9 +4453,21 @@ export function wrap_edge_whitespace(nodes) {
4438
4453
  }
4439
4454
 
4440
4455
  /**
4441
- * @param {any} node
4456
+ * @overload
4457
+ * @param {AST.TSRXRenderChild} node
4442
4458
  * @param {TransformContext} transform_context
4443
- * @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}
4444
4471
  */
4445
4472
  function to_jsx_child(node, transform_context) {
4446
4473
  if (!node) return node;
@@ -4514,25 +4541,25 @@ function to_jsx_child(node, transform_context) {
4514
4541
  * Children have already been parsed and transformed through the normal TSRX
4515
4542
  * JSX element/text/control-flow visitors.
4516
4543
  *
4517
- * @param {any} node
4544
+ * @param {AST.TSRXJSXElement | AST.TSRXJSXFragment} node
4518
4545
  * @param {TransformContext} transform_context
4519
4546
  * @param {boolean} [in_jsx_child]
4520
- * @returns {any}
4547
+ * @returns {AST.Expression | ESTreeJSX.JSXExpressionContainer}
4521
4548
  */
4522
4549
  function tsrx_node_to_jsx_expression(node, transform_context, in_jsx_child = false) {
4523
4550
  const children = (node.children || []).filter(
4524
- (/** @type {any} */ child) =>
4551
+ (child) =>
4525
4552
  child && child.type !== 'EmptyStatement' && (child.type !== 'JSXText' || child.value !== ''),
4526
4553
  );
4527
4554
 
4528
- /** @type {any} */
4529
- let expression;
4555
+ /** @type {AST.Expression | null} */
4556
+ let expression = null;
4530
4557
  if (children.length === 0) {
4531
4558
  // An empty fragment is a real value: keep it as `<></>` in BOTH child and
4532
4559
  // expression position. Lowering it to a bare `null` in expression position
4533
4560
  // (e.g. `let b = <></>`) drops the author's fragment and changes its type;
4534
4561
  // `<></>` is a valid value and keeps the to_ts/runtime view faithful.
4535
- expression = set_loc(b.jsx_fragment([]), node.loc ? node : undefined);
4562
+ expression = set_loc(b.jsx_fragment([]), has_location(node) ? node : undefined);
4536
4563
  } else if (
4537
4564
  children.length === 1 &&
4538
4565
  (is_empty_jsx_fragment(children[0]) ||
@@ -4543,7 +4570,7 @@ function tsrx_node_to_jsx_expression(node, transform_context, in_jsx_child = fal
4543
4570
  // dropping the outer fragment the author wrote. Keep both levels. (`<><></></>`
4544
4571
  // is kept regardless; a non-empty inner is only kept for an authored outer, so
4545
4572
  // a generated wrapper still collapses.)
4546
- expression = set_loc(b.jsx_fragment(children), node.loc ? node : undefined);
4573
+ expression = set_loc(b.jsx_fragment(children), has_location(node) ? node : undefined);
4547
4574
  } else {
4548
4575
  expression = return_value_body_to_expression(children, node, transform_context);
4549
4576
  }
@@ -4554,7 +4581,7 @@ function tsrx_node_to_jsx_expression(node, transform_context, in_jsx_child = fal
4554
4581
  transform_context.inside_element_child = true;
4555
4582
  try {
4556
4583
  const render_nodes = wrap_edge_whitespace(
4557
- children.map((/** @type {any} */ child) => to_jsx_child(child, transform_context)),
4584
+ children.map((child) => to_jsx_child(child, transform_context)),
4558
4585
  );
4559
4586
  expression =
4560
4587
  build_return_expression(render_nodes, in_jsx_child, transform_context.typeOnly) ||
@@ -4567,13 +4594,7 @@ function tsrx_node_to_jsx_expression(node, transform_context, in_jsx_child = fal
4567
4594
  }
4568
4595
  }
4569
4596
 
4570
- if (
4571
- in_jsx_child &&
4572
- expression.type !== 'JSXElement' &&
4573
- expression.type !== 'JSXFragment' &&
4574
- expression.type !== 'JSXText' &&
4575
- expression.type !== 'JSXExpressionContainer'
4576
- ) {
4597
+ if (in_jsx_child && expression.type !== 'JSXElement' && expression.type !== 'JSXFragment') {
4577
4598
  return to_jsx_expression_container(expression, node);
4578
4599
  }
4579
4600
 
@@ -4669,14 +4690,14 @@ function create_statement_iife(body_nodes, source_node, transform_context) {
4669
4690
  }
4670
4691
 
4671
4692
  /**
4672
- * @param {any} node
4673
- * @param {any} source_node
4693
+ * @param {AST.Expression} node
4694
+ * @param {AST.Node | null | undefined} source_node
4674
4695
  * @param {TransformContext} [transform_context]
4675
- * @returns {any}
4696
+ * @returns {AST.Expression}
4676
4697
  */
4677
4698
  function set_generated_expression_loc(node, source_node, transform_context) {
4678
- if (transform_context?.typeOnly || !source_node?.loc) return node;
4679
- return setLocation(/** @type {any} */ (node), source_node);
4699
+ if (transform_context?.typeOnly || !has_location(source_node)) return node;
4700
+ return setLocation(node, source_node);
4680
4701
  }
4681
4702
 
4682
4703
  /**
@@ -5046,17 +5067,17 @@ function for_of_statement_to_jsx_child(node, transform_context) {
5046
5067
  body_contains_top_level_hook_call(loop_body, transform_context, true);
5047
5068
  const body_key_expression = find_key_expression_in_body(loop_body);
5048
5069
  const explicit_key_expression =
5049
- body_key_expression ?? (node.key ? clone_expression_node(node.key) : undefined);
5070
+ body_key_expression ?? (node.key ? clone_ast_node(node.key) : undefined);
5050
5071
  const key_expression =
5051
5072
  has_hooks && explicit_key_expression == null && node.index
5052
- ? clone_expression_node(node.index)
5073
+ ? clone_ast_node(node.index)
5053
5074
  : explicit_key_expression;
5054
5075
  const implicit_non_hook_key_expression =
5055
5076
  !has_hooks && body_key_expression == null
5056
5077
  ? node.key
5057
- ? clone_expression_node(node.key)
5078
+ ? clone_ast_node(node.key)
5058
5079
  : node.index
5059
- ? clone_expression_node(node.index)
5080
+ ? clone_ast_node(node.index)
5060
5081
  : undefined
5061
5082
  : undefined;
5062
5083
 
@@ -5132,7 +5153,7 @@ function for_of_statement_to_jsx_child(node, transform_context) {
5132
5153
  b.conditional(
5133
5154
  b.binary(
5134
5155
  '===',
5135
- b.member(clone_expression_node(node.right), create_generated_identifier('length')),
5156
+ b.member(clone_ast_node(node.right), create_generated_identifier('length')),
5136
5157
  b.literal(0),
5137
5158
  ),
5138
5159
  empty_fallback,
@@ -5173,7 +5194,7 @@ function apply_key_to_loop_body(body_nodes, key_expression) {
5173
5194
  ...attributes,
5174
5195
  b.jsx_attribute(
5175
5196
  b.jsx_id('key'),
5176
- to_jsx_expression_container(clone_expression_node(key_expression), key_expression),
5197
+ to_jsx_expression_container(clone_ast_node(key_expression), key_expression),
5177
5198
  ),
5178
5199
  ],
5179
5200
  },
@@ -5254,7 +5275,7 @@ function apply_key_to_jsx_element(element, key_expression) {
5254
5275
  ...attributes,
5255
5276
  b.jsx_attribute(
5256
5277
  b.jsx_id('key'),
5257
- to_jsx_expression_container(clone_expression_node(key_expression), key_expression),
5278
+ to_jsx_expression_container(clone_ast_node(key_expression), key_expression),
5258
5279
  ),
5259
5280
  ],
5260
5281
  },
@@ -5270,7 +5291,7 @@ function keyed_fragment_to_jsx_element(fragment, key_expression) {
5270
5291
  const name = b.jsx_id('Fragment');
5271
5292
  const key_attribute = b.jsx_attribute(
5272
5293
  b.jsx_id('key'),
5273
- to_jsx_expression_container(clone_expression_node(key_expression), key_expression),
5294
+ to_jsx_expression_container(clone_ast_node(key_expression), key_expression),
5274
5295
  );
5275
5296
 
5276
5297
  return b.jsx_element_fresh(
@@ -5476,9 +5497,9 @@ function try_statement_to_jsx_child(node, transform_context) {
5476
5497
  * Create a simple JSX element AST node.
5477
5498
  *
5478
5499
  * @param {string} tag_name
5479
- * @param {any[]} attributes
5480
- * @param {any[]} children
5481
- * @returns {any}
5500
+ * @param {ESTreeJSX.JSXAttributeNode[]} attributes
5501
+ * @param {ESTreeJSX.JSXElement['children']} children
5502
+ * @returns {ESTreeJSX.JSXElement}
5482
5503
  */
5483
5504
  function create_jsx_element(tag_name, attributes, children) {
5484
5505
  const self_closing = children.length === 0;
@@ -5496,12 +5517,12 @@ function create_jsx_element(tag_name, attributes, children) {
5496
5517
  *
5497
5518
  * @param {AST.Program} program
5498
5519
  * @param {TransformContext} transform_context
5499
- * @param {JsxPlatform} platform
5520
+ * @param {Pick<JsxPlatform, 'imports'>} platform
5500
5521
  * @param {string} suspense_source - effective suspense import source after
5501
5522
  * applying any per-call override from JsxTransformOptions.suspenseSource.
5502
5523
  */
5503
5524
  function inject_try_imports(program, transform_context, platform, suspense_source) {
5504
- /** @type {any[]} */
5525
+ /** @type {AST.ImportDeclaration[]} */
5505
5526
  const imports = [];
5506
5527
 
5507
5528
  if (transform_context.needs_fragment && platform.imports.fragment) {
@@ -5543,7 +5564,7 @@ function inject_try_imports(program, transform_context, platform, suspense_sourc
5543
5564
  ? platform.imports.refProp
5544
5565
  : null;
5545
5566
 
5546
- /** @type {Map<string, any[]>} */
5567
+ /** @type {Map<string, AST.ImportSpecifier[]>} */
5547
5568
  const ref_imports = new Map();
5548
5569
 
5549
5570
  if (merge_refs_source !== null) {
@@ -5583,9 +5604,9 @@ function inject_try_imports(program, transform_context, platform, suspense_sourc
5583
5604
  }
5584
5605
 
5585
5606
  /**
5586
- * @param {Map<string, any[]>} imports
5607
+ * @param {Map<string, AST.ImportSpecifier[]>} imports
5587
5608
  * @param {string} source
5588
- * @param {any} specifier
5609
+ * @param {AST.ImportSpecifier} specifier
5589
5610
  */
5590
5611
  function add_ref_import_specifier(imports, source, specifier) {
5591
5612
  const specifiers = imports.get(source);
@@ -5597,9 +5618,9 @@ function add_ref_import_specifier(imports, source, specifier) {
5597
5618
  }
5598
5619
 
5599
5620
  /**
5600
- * @param {any} node
5621
+ * @param {AST.IfStatement | AST.JSXIfExpression} node
5601
5622
  * @param {TransformContext} transform_context
5602
- * @returns {any}
5623
+ * @returns {AST.IfStatement}
5603
5624
  */
5604
5625
  function create_render_if_statement(node, transform_context) {
5605
5626
  const consequent_body =
@@ -5616,7 +5637,8 @@ function create_render_if_statement(node, transform_context) {
5616
5637
  if (is_if_control_node(node.alternate)) {
5617
5638
  alternate = create_render_if_statement(node.alternate, transform_context);
5618
5639
  } else {
5619
- const alternate_body = node.alternate.body || [node.alternate];
5640
+ const alternate_body =
5641
+ node.alternate.type === 'BlockStatement' ? node.alternate.body : [node.alternate];
5620
5642
  if (is_template_if_node(node)) {
5621
5643
  validate_if_body_control_flow(alternate_body, transform_context);
5622
5644
  }
@@ -5659,8 +5681,8 @@ function create_render_if_statement(node, transform_context) {
5659
5681
  * `own_body` is the case's isolated consequent. JSX `@switch` cases do not
5660
5682
  * fall through, so `break` is not part of the template switch model.
5661
5683
  *
5662
- * @param {any[]} consequent
5663
- * @returns {{ own_body: any[], has_terminator: boolean }}
5684
+ * @param {AST.Statement[]} consequent
5685
+ * @returns {{ own_body: AST.Statement[], has_terminator: boolean }}
5664
5686
  */
5665
5687
  function summarize_switch_case_body(consequent) {
5666
5688
  const own_body = [];
@@ -5690,10 +5712,10 @@ function summarize_switch_case_body(consequent) {
5690
5712
  * see double/triple hits per source range.
5691
5713
  *
5692
5714
  * @param {{ component_element: ESTreeJSX.JSXElement }} helper
5693
- * @returns {any}
5715
+ * @returns {ESTreeJSX.JSXElement}
5694
5716
  */
5695
5717
  export function clone_switch_helper_invocation(helper) {
5696
- return clone_expression_node(helper.component_element, false);
5718
+ return clone_ast_node(helper.component_element, false);
5697
5719
  }
5698
5720
 
5699
5721
  /**
@@ -5710,31 +5732,29 @@ export function clone_switch_helper_invocation(helper) {
5710
5732
  * React, Vue, and Solid) already pushed their declarations into
5711
5733
  * `transform_context.helper_state.helpers`, so `setup_statements` is empty.
5712
5734
  *
5713
- * @param {any} switch_node
5735
+ * @param {AST.SwitchStatement} switch_node
5714
5736
  * @param {TransformContext} transform_context
5715
5737
  * @returns {{
5716
- * case_info: Array<{ own_body: any[], has_terminator: boolean }>,
5717
- * case_helpers: Array<{ setup_statements: any[], component_element: ESTreeJSX.JSXElement } | null>,
5718
- * 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[],
5719
5741
  * }}
5720
5742
  */
5721
5743
  export function plan_switch_lift(switch_node, transform_context) {
5722
- const case_info = switch_node.cases.map((/** @type {any} */ c) => {
5744
+ const case_info = switch_node.cases.map((c) => {
5723
5745
  const consequent = flatten_switch_consequent(c.consequent || []);
5724
5746
  return summarize_switch_case_body(consequent);
5725
5747
  });
5726
5748
 
5727
5749
  // A case body needs to be lifted iff it contains hooks. Cases are isolated,
5728
5750
  // so downstream case bodies are never duplicated into earlier arms.
5729
- const needs_helper = case_info.map(
5730
- (/** @type {{ own_body: any[], has_terminator: boolean }} */ info) => {
5731
- if (info.own_body.length === 0) return false;
5732
- return (
5733
- should_extract_hook_helpers(transform_context) &&
5734
- body_contains_top_level_hook_call(info.own_body, transform_context, true)
5735
- );
5736
- },
5737
- );
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
+ });
5738
5758
 
5739
5759
  // Pre-allocate helper ids in source order so the snapshot's
5740
5760
  // `StatementBodyHook<N>` numbering reads top-to-bottom by case position
@@ -5746,7 +5766,7 @@ export function plan_switch_lift(switch_node, transform_context) {
5746
5766
  : null,
5747
5767
  );
5748
5768
 
5749
- /** @type {Array<{ setup_statements: any[], component_element: ESTreeJSX.JSXElement } | null>} */
5769
+ /** @type {Array<JsxHelperComponent | null>} */
5750
5770
  const case_helpers = new Array(switch_node.cases.length).fill(null);
5751
5771
 
5752
5772
  for (let i = switch_node.cases.length - 1; i >= 0; i--) {
@@ -5758,12 +5778,13 @@ export function plan_switch_lift(switch_node, transform_context) {
5758
5778
  undefined,
5759
5779
  switch_node.cases[i],
5760
5780
  transform_context,
5761
- /** @type {any} */ (helper_ids[i]),
5781
+ /** @type {AST.Identifier} */ (helper_ids[i]),
5762
5782
  );
5763
5783
  }
5764
5784
 
5765
5785
  // Hoist all helpers' setup statements above the switch in source order so
5766
5786
  // the switch body stays a pure dispatcher.
5787
+ /** @type {AST.Statement[]} */
5767
5788
  const setup_statements = [];
5768
5789
  for (const helper of case_helpers) {
5769
5790
  if (helper) setup_statements.push(...helper.setup_statements);
@@ -5777,9 +5798,9 @@ export function plan_switch_lift(switch_node, transform_context) {
5777
5798
  }
5778
5799
 
5779
5800
  /**
5780
- * @param {any} switch_node
5801
+ * @param {AST.SwitchStatement} switch_node
5781
5802
  * @param {TransformContext} transform_context
5782
- * @returns {{ setup_statements: any[], switch_statement: any }}
5803
+ * @returns {{ setup_statements: AST.Statement[], switch_statement: AST.SwitchStatement }}
5783
5804
  */
5784
5805
  function build_switch_with_lift(switch_node, transform_context) {
5785
5806
  const { case_info, case_helpers, setup_statements } = plan_switch_lift(
@@ -5787,92 +5808,95 @@ function build_switch_with_lift(switch_node, transform_context) {
5787
5808
  transform_context,
5788
5809
  );
5789
5810
 
5790
- const new_cases = switch_node.cases.map(
5791
- (/** @type {any} */ original_case, /** @type {number} */ i) => {
5792
- const helper = case_helpers[i];
5793
- if (helper) {
5794
- return set_loc(
5795
- b.switch_case(original_case.test, [
5796
- create_component_return_statement(
5797
- [helper.component_element],
5798
- original_case,
5799
- true,
5800
- transform_context.typeOnly,
5801
- ),
5802
- ]),
5803
- original_case,
5804
- );
5805
- }
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
+ }
5806
5826
 
5807
- const { own_body, has_terminator } = case_info[i];
5827
+ const { own_body, has_terminator } = case_info[i];
5808
5828
 
5809
- if (own_body.length === 0 && !has_terminator) {
5810
- return set_loc(
5811
- b.switch_case(original_case.test, [create_null_return_statement()]),
5812
- 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),
5813
5846
  );
5847
+ has_terminal = true;
5848
+ break;
5814
5849
  }
5815
-
5816
- const case_body = [];
5817
- const render_nodes = [];
5818
- let has_terminal = false;
5819
-
5820
- for (const child of own_body) {
5821
- if (is_loop_skip_return_statement(child)) {
5822
- case_body.push(
5823
- create_component_return_statement(
5824
- render_nodes,
5825
- child,
5826
- true,
5827
- transform_context.typeOnly,
5828
- ),
5829
- );
5830
- has_terminal = true;
5831
- break;
5832
- }
5833
- if (child.type === 'ReturnStatement') {
5834
- case_body.push(child);
5835
- has_terminal = true;
5836
- break;
5837
- }
5838
- if (is_render_child_node(child)) {
5839
- render_nodes.push(to_jsx_child(child, transform_context));
5840
- } else if (is_bare_render_expression(child)) {
5841
- render_nodes.push(to_jsx_expression_container(child, child));
5842
- } else {
5843
- case_body.push(child);
5844
- }
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);
5845
5865
  }
5866
+ }
5846
5867
 
5847
- if (!has_terminal) {
5848
- if (render_nodes.length > 0) {
5849
- case_body.push(
5850
- create_component_return_statement(
5851
- render_nodes,
5852
- original_case,
5853
- true,
5854
- transform_context.typeOnly,
5855
- ),
5856
- );
5857
- } else if (case_body.length > 0) {
5858
- case_body.push(create_null_return_statement());
5859
- } else if (has_terminator) {
5860
- case_body.push(create_null_return_statement());
5861
- }
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());
5862
5882
  }
5883
+ }
5863
5884
 
5864
- return set_loc(b.switch_case(original_case.test, case_body), original_case);
5865
- },
5866
- );
5885
+ return set_loc(b.switch_case(original_case.test, case_body), original_case);
5886
+ });
5867
5887
 
5868
5888
  return {
5869
5889
  setup_statements,
5870
- 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
+ ),
5871
5895
  };
5872
5896
  }
5873
5897
 
5874
5898
  /**
5875
- * @returns {any}
5899
+ * @returns {AST.ReturnStatement}
5876
5900
  */
5877
5901
  function create_null_return_statement() {
5878
5902
  return b.return(b.literal(null));
@@ -5880,25 +5904,25 @@ function create_null_return_statement() {
5880
5904
 
5881
5905
  /**
5882
5906
  * @param {AST.Expression} expression
5883
- * @param {any} [source_node]
5907
+ * @param {AST.Node} [source_node]
5884
5908
  * @returns {ESTreeJSX.JSXExpressionContainer}
5885
5909
  */
5886
5910
  function to_jsx_expression_container(expression, source_node = expression) {
5887
5911
  // NOTE: JSXExpressionContainer nodes are intentionally created without loc.
5888
5912
  // They are synthetic wrappers whose source positions do not correspond to
5889
5913
  // entries in the generated source map, so adding loc causes Volar mapping failures.
5890
- return /** @type {any} */ ({
5914
+ return {
5891
5915
  type: 'JSXExpressionContainer',
5892
- expression: /** @type {any} */ (expression),
5916
+ expression,
5893
5917
  metadata: { path: [] },
5894
- });
5918
+ };
5895
5919
  }
5896
5920
 
5897
5921
  /**
5898
- * Dispatch point for element attribute transformation. Platforms can replace
5899
- * the default "map over `to_jsx_attribute`" via
5900
- * `hooks.transformElementAttributes`. Whether or not the hook is used,
5901
- * 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
5902
5926
  * `multiRefStrategy` can compose an explicit `ref={...}` with compiler-
5903
5927
  * synthesized refs created for host spreads.
5904
5928
  *
@@ -5907,10 +5931,10 @@ function to_jsx_expression_container(expression, source_node = expression) {
5907
5931
  * duplicate JSX props which the JSX runtime collapses to last-wins (and
5908
5932
  * which TypeScript can't type cleanly).
5909
5933
  *
5910
- * @param {any[]} attrs
5934
+ * @param {ESTreeJSX.JSXAttributeNode[]} attrs
5911
5935
  * @param {TransformContext} transform_context
5912
- * @param {any} element
5913
- * @returns {any[]}
5936
+ * @param {AST.TSRXJSXElement} element
5937
+ * @returns {ESTreeJSX.JSXAttributeNode[]}
5914
5938
  */
5915
5939
  function transform_element_attributes_dispatch(attrs, transform_context, element) {
5916
5940
  validate_at_most_one_ref_attribute(attrs, transform_context);
@@ -5920,9 +5944,7 @@ function transform_element_attributes_dispatch(attrs, transform_context, element
5920
5944
  attrs = preprocess(attrs, transform_context, element);
5921
5945
  }
5922
5946
  const hook = transform_context.platform.hooks?.transformElementAttributes;
5923
- const result = hook
5924
- ? hook(attrs, transform_context, element)
5925
- : attrs.map((/** @type {any} */ a) => to_jsx_attribute(a, transform_context));
5947
+ const result = hook ? hook(attrs, transform_context, element) : attrs;
5926
5948
  return merge_duplicate_refs(
5927
5949
  normalize_host_ref_spreads(result, !is_component, transform_context),
5928
5950
  transform_context,
@@ -5930,7 +5952,7 @@ function transform_element_attributes_dispatch(attrs, transform_context, element
5930
5952
  }
5931
5953
 
5932
5954
  /**
5933
- * @param {any} element
5955
+ * @param {AST.TSRXJSXElement | ESTreeJSX.JSXElement} element
5934
5956
  * @returns {boolean}
5935
5957
  */
5936
5958
  export function is_component_like_element(element) {
@@ -5944,7 +5966,7 @@ export function is_component_like_element(element) {
5944
5966
  }
5945
5967
 
5946
5968
  /**
5947
- * @param {any} name
5969
+ * @param {ESTreeJSX.TSRXJSXOpeningElement['name']} name
5948
5970
  * @returns {boolean}
5949
5971
  */
5950
5972
  function is_component_like_jsx_name(name) {
@@ -5955,10 +5977,10 @@ function is_component_like_jsx_name(name) {
5955
5977
  }
5956
5978
 
5957
5979
  /**
5958
- * @param {any[]} attrs
5980
+ * @param {ESTreeJSX.JSXAttributeNode[]} attrs
5959
5981
  * @param {boolean} is_host
5960
5982
  * @param {TransformContext} transform_context
5961
- * @returns {any[]}
5983
+ * @returns {ESTreeJSX.JSXAttributeNode[]}
5962
5984
  */
5963
5985
  function normalize_host_ref_spreads(attrs, is_host, transform_context) {
5964
5986
  if (!is_host) return attrs;
@@ -5970,49 +5992,55 @@ function normalize_host_ref_spreads(attrs, is_host, transform_context) {
5970
5992
  .map((attr) => attr.value.expression);
5971
5993
  const needs_synthetic_spread_ref = needs_explicit_spread_ref || ref_exprs.length > 0;
5972
5994
 
5973
- return attrs.flatMap((attr) => {
5974
- if (!attr || attr.type !== 'JSXSpreadAttribute') {
5975
- return [attr];
5976
- }
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
+ }
5977
6004
 
5978
- const normalize_helper = needs_synthetic_spread_ref
5979
- ? NORMALIZE_SPREAD_PROPS_FOR_REF_ATTR_INTERNAL_NAME
5980
- : NORMALIZE_SPREAD_PROPS_INTERNAL_NAME;
5981
- if (needs_synthetic_spread_ref) {
5982
- transform_context.needs_normalize_spread_props_for_ref_attr = true;
5983
- } else {
5984
- transform_context.needs_normalize_spread_props = true;
5985
- }
5986
- 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);
5987
6014
 
5988
- if (needs_synthetic_spread_ref) {
5989
- const normalized_id = create_generated_identifier(
5990
- create_spread_props_name(transform_context),
5991
- );
5992
- const spread = {
5993
- ...attr,
5994
- argument: clone_identifier(normalized_id),
5995
- };
5996
- const ref_attr = b.jsx_attribute(
5997
- b.jsx_id('ref'),
5998
- to_jsx_expression_container(b.member(clone_identifier(normalized_id), 'ref'), attr),
5999
- false,
6000
- attr,
6001
- );
6002
- ref_attr.metadata = { ...(ref_attr.metadata || {}) };
6003
- /** @type {any} */ (ref_attr.metadata).synthetic_ref = true;
6004
- 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));
6005
6032
 
6006
- return [spread, ref_attr];
6007
- }
6033
+ return [spread, ref_attr];
6034
+ }
6008
6035
 
6009
- return [
6010
- {
6011
- ...attr,
6012
- argument: normalized,
6013
- },
6014
- ];
6015
- });
6036
+ return [
6037
+ {
6038
+ ...attr,
6039
+ argument: normalized,
6040
+ },
6041
+ ];
6042
+ },
6043
+ );
6016
6044
  }
6017
6045
 
6018
6046
  /**
@@ -6029,8 +6057,8 @@ function create_spread_props_name(transform_context) {
6029
6057
  }
6030
6058
 
6031
6059
  /**
6032
- * @param {any} node
6033
- * @param {any} declaration
6060
+ * @param {AST.Node} node
6061
+ * @param {AST.Statement} declaration
6034
6062
  */
6035
6063
  export function add_jsx_setup_declaration(node, declaration) {
6036
6064
  node.metadata ??= { path: [] };
@@ -6038,29 +6066,40 @@ export function add_jsx_setup_declaration(node, declaration) {
6038
6066
  }
6039
6067
 
6040
6068
  /**
6041
- * @param {any} node
6042
- * @param {Set<any>} [seen]
6043
- * @returns {any[]}
6069
+ * @param {AST.Node} node
6070
+ * @param {Set<object>} [seen]
6071
+ * @returns {AST.Statement[]}
6044
6072
  */
6045
6073
  export function extract_jsx_setup_declarations(node, seen = new Set()) {
6046
- if (node == null || typeof node !== 'object' || seen.has(node)) {
6047
- return [];
6048
- }
6049
- seen.add(node);
6074
+ /** @type {AST.Statement[]} */
6075
+ const declarations = [];
6076
+ collect_jsx_setup_declarations(node, seen, declarations);
6077
+ return declarations;
6078
+ }
6050
6079
 
6051
- const declarations = node.metadata?.generated_setup_declarations ?? [];
6052
- if (node.metadata?.generated_setup_declarations) {
6053
- delete node.metadata.generated_setup_declarations;
6054
- }
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);
6055
6089
 
6056
- for (const key of Object.keys(node)) {
6057
- if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
6058
- 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;
6059
6095
  }
6060
- declarations.push(...extract_jsx_setup_declarations(node[key], seen));
6061
6096
  }
6062
6097
 
6063
- 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
+ }
6064
6103
  }
6065
6104
 
6066
6105
  /**
@@ -6094,11 +6133,11 @@ function wrap_jsx_setup_declarations(expression, in_jsx_child) {
6094
6133
  * This validator runs over the raw, pre-lowering attribute list so each
6095
6134
  * shape is still distinguishable by `type`.
6096
6135
  *
6097
- * @param {any[]} raw_attrs
6136
+ * @param {ESTreeJSX.JSXAttributeNode[]} raw_attrs
6098
6137
  * @param {TransformContext} [transform_context]
6099
6138
  */
6100
6139
  export function validate_at_most_one_ref_attribute(raw_attrs, transform_context) {
6101
- /** @type {any[]} */
6140
+ /** @type {ESTreeJSX.JSXIdentifier[]} */
6102
6141
  const refs = [];
6103
6142
  for (const attr of raw_attrs) {
6104
6143
  if (!attr) continue;
@@ -6108,7 +6147,7 @@ export function validate_at_most_one_ref_attribute(raw_attrs, transform_context)
6108
6147
  attr.name.type === 'JSXIdentifier' &&
6109
6148
  attr.name.name === 'ref';
6110
6149
  if (!is_ref_attr) continue;
6111
- refs.push(attr.name);
6150
+ refs.push(/** @type {ESTreeJSX.JSXIdentifier} */ (attr.name));
6112
6151
  }
6113
6152
  if (refs.length < 2) {
6114
6153
  return;
@@ -6146,9 +6185,9 @@ export function validate_at_most_one_ref_attribute(raw_attrs, transform_context)
6146
6185
  * Single-ref elements are always left unchanged so trivial cases stay
6147
6186
  * import-free and produce no helper call.
6148
6187
  *
6149
- * @param {any[]} jsx_attrs
6188
+ * @param {ESTreeJSX.JSXAttributeNode[]} jsx_attrs
6150
6189
  * @param {TransformContext} transform_context
6151
- * @returns {any[]}
6190
+ * @returns {ESTreeJSX.JSXAttributeNode[]}
6152
6191
  */
6153
6192
  export function merge_duplicate_refs(jsx_attrs, transform_context) {
6154
6193
  const strategy = transform_context.platform.jsx.multiRefStrategy;
@@ -6168,11 +6207,11 @@ export function merge_duplicate_refs(jsx_attrs, transform_context) {
6168
6207
  // instead of silently composing them into `__mergeRefs(...)`.
6169
6208
  if (tsx_form_count >= 2) return jsx_attrs;
6170
6209
 
6171
- /** @type {any[]} */
6210
+ /** @type {AST.Expression[]} */
6172
6211
  const ref_exprs = [];
6173
- /** @type {any[]} */
6212
+ /** @type {ESTreeJSX.JSXAttributeNode[]} */
6174
6213
  const result = [];
6175
- /** @type {any} */
6214
+ /** @type {ESTreeJSX.JSXRefAttribute | null} */
6176
6215
  let source_attr = null;
6177
6216
  for (const attr of jsx_attrs) {
6178
6217
  if (is_jsx_ref_attribute(attr)) {
@@ -6202,12 +6241,15 @@ export function merge_duplicate_refs(jsx_attrs, transform_context) {
6202
6241
  // merged attribute and its name. Without this the kept `ref` keyword in
6203
6242
  // `ref={__mergeRefs(...)}` has no source mapping back to the user's `ref=`
6204
6243
  // keyword.
6205
- 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);
6206
6248
  const merged_attr = build_jsx_attribute(
6207
6249
  merged_name,
6208
6250
  b.jsx_expression_container(merged_value),
6209
6251
  false,
6210
- source_attr,
6252
+ source_attr_location,
6211
6253
  );
6212
6254
  result.push(merged_attr);
6213
6255
 
@@ -6215,8 +6257,8 @@ export function merge_duplicate_refs(jsx_attrs, transform_context) {
6215
6257
  }
6216
6258
 
6217
6259
  /**
6218
- * @param {any} attr
6219
- * @returns {boolean}
6260
+ * @param {ESTreeJSX.JSXAttributeNode} attr
6261
+ * @returns {attr is ESTreeJSX.JSXRefAttribute}
6220
6262
  */
6221
6263
  function is_jsx_ref_attribute(attr) {
6222
6264
  return (
@@ -6263,7 +6305,7 @@ const MATHML_REF_TAG_NAMES = new Set(
6263
6305
  );
6264
6306
 
6265
6307
  /**
6266
- * @param {any} element
6308
+ * @param {ESTreeJSX.JSXElement | ESTreeJSX.JSXOpeningElement} element
6267
6309
  * @param {'html' | 'svg' | 'mathml'} [namespace]
6268
6310
  * @returns {AST.TypeNode | null}
6269
6311
  */
@@ -6314,13 +6356,14 @@ function infer_ref_namespace(tag_name) {
6314
6356
  }
6315
6357
 
6316
6358
  /**
6317
- * @param {any} element
6359
+ * @param {ESTreeJSX.JSXElement | ESTreeJSX.JSXOpeningElement} element
6318
6360
  * @returns {string | null}
6319
6361
  */
6320
6362
  function get_element_ref_tag_name(element) {
6321
- const name = element?.name;
6322
- if (name?.type === 'JSXIdentifier') return name.name;
6323
- 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') {
6324
6367
  return element.openingElement.name.name;
6325
6368
  }
6326
6369
  return null;
@@ -6341,70 +6384,9 @@ function create_tag_name_map_ref_type(map_name, tag_name) {
6341
6384
  }
6342
6385
 
6343
6386
  /**
6344
- * @param {any} attr
6345
- * @param {TransformContext} transform_context
6346
- * @returns {ESTreeJSX.JSXAttribute | ESTreeJSX.JSXSpreadAttribute}
6347
- */
6348
- export function to_jsx_attribute(attr, transform_context) {
6349
- if (!attr) return attr;
6350
- if (attr.type === 'JSXAttribute') {
6351
- return attr;
6352
- }
6353
- if (attr.type === 'JSXSpreadAttribute') {
6354
- return attr;
6355
- }
6356
- // Keep this legacy hook for targets that need React-style DOM attrs. The
6357
- // current first-party targets preserve authored `class`.
6358
- let attr_name = attr.name;
6359
- if (
6360
- transform_context.platform.jsx.rewriteClassAttr &&
6361
- attr_name &&
6362
- attr_name.type === 'Identifier' &&
6363
- attr_name.name === 'class'
6364
- ) {
6365
- attr_name = set_loc(b.id('className'), attr.name);
6366
- attr_name.metadata.source_name = 'class';
6367
- attr_name.metadata.source_length = 'class'.length;
6368
- }
6369
-
6370
- const name =
6371
- attr_name && attr_name.type === 'Identifier' ? identifier_to_jsx_name(attr_name) : attr_name;
6372
-
6373
- let value = attr.value;
6374
- if (value) {
6375
- if (value.type === 'Literal' && typeof value.value === 'string') {
6376
- // Keep string literal as attribute string.
6377
- } else if (value.type !== 'JSXExpressionContainer') {
6378
- value = to_jsx_expression_container(value);
6379
- }
6380
- }
6381
-
6382
- const jsx_attribute = build_jsx_attribute(name, value || null, attr.shorthand === true);
6383
-
6384
- if (value_has_unmappable_jsx_loc(value)) {
6385
- /** @type {any} */ (jsx_attribute.metadata).has_unmappable_value = true;
6386
- return jsx_attribute;
6387
- }
6388
-
6389
- return set_loc(jsx_attribute, attr);
6390
- }
6391
-
6392
- /**
6393
- * @param {any} value
6394
- * @returns {boolean}
6395
- */
6396
- function value_has_unmappable_jsx_loc(value) {
6397
- return !!(
6398
- value?.type === 'JSXExpressionContainer' &&
6399
- (value.expression?.type === 'JSXElement' || value.expression?.type === 'JSXFragment') &&
6400
- !value.expression.loc
6401
- );
6402
- }
6403
-
6404
- /**
6405
- * @param {any[]} render_nodes
6387
+ * @param {ESTreeJSX.JSXRenderChild[]} render_nodes
6406
6388
  * @param {boolean} [in_jsx_child]
6407
- * @returns {any}
6389
+ * @returns {AST.Expression | null}
6408
6390
  */
6409
6391
  export function build_return_expression(render_nodes, in_jsx_child = false, type_only = false) {
6410
6392
  if (render_nodes.length === 0) return null;
@@ -6412,10 +6394,10 @@ export function build_return_expression(render_nodes, in_jsx_child = false, type
6412
6394
  const only = render_nodes[0];
6413
6395
  if (only.type === 'JSXExpressionContainer') {
6414
6396
  if (only.metadata?.tsrx_reactive_block === true) {
6415
- return set_loc(b.jsx_fragment([only]), only.loc ? only : undefined);
6397
+ return set_loc(b.jsx_fragment([only]), has_location(only) ? only : undefined);
6416
6398
  }
6417
6399
  if (only.expression?.type === 'JSXEmptyExpression') {
6418
- return set_loc(b.jsx_fragment([]), only.loc ? only : undefined);
6400
+ return set_loc(b.jsx_fragment([]), has_location(only) ? only : undefined);
6419
6401
  }
6420
6402
  return only.expression;
6421
6403
  }
@@ -6428,7 +6410,7 @@ export function build_return_expression(render_nodes, in_jsx_child = false, type
6428
6410
  if (!type_only && !in_jsx_child && (only.value ?? '').trim() === '') {
6429
6411
  return null;
6430
6412
  }
6431
- return set_loc(b.jsx_fragment([only]), only.loc ? only : undefined);
6413
+ return set_loc(b.jsx_fragment([only]), has_location(only) ? only : undefined);
6432
6414
  }
6433
6415
  return only;
6434
6416
  }
@@ -6436,7 +6418,7 @@ export function build_return_expression(render_nodes, in_jsx_child = false, type
6436
6418
  const last = render_nodes[render_nodes.length - 1];
6437
6419
  return set_loc(
6438
6420
  b.jsx_fragment(render_nodes),
6439
- first?.loc && last?.loc
6421
+ has_location(first) && has_location(last)
6440
6422
  ? {
6441
6423
  start: first.start,
6442
6424
  end: last.end,