@tsrx/core 0.1.22 → 0.1.25

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.
@@ -7,6 +7,7 @@ import { print } from 'esrap';
7
7
  import { error } from '../../errors.js';
8
8
  import { analyze_css } from '../../analyze/css-analyze.js';
9
9
  import { prune_css } from '../../analyze/prune.js';
10
+ import { create_scopes, ScopeRoot } from '../../scope.js';
10
11
  import {
11
12
  in_jsx_child_context,
12
13
  set_node_path_metadata,
@@ -25,7 +26,6 @@ import {
25
26
  is_bare_render_expression,
26
27
  is_component_jsx_name,
27
28
  is_jsx_child,
28
- jsx_name_to_expression,
29
29
  set_loc,
30
30
  to_text_expression,
31
31
  } from './ast-builders.js';
@@ -49,7 +49,6 @@ import {
49
49
  create_style_ref_setup_statements,
50
50
  get_style_element_stylesheet,
51
51
  } from '../style-ref.js';
52
- import { is_function_or_component_node } from '../../utils/ast.js';
53
52
  import {
54
53
  is_interleaved_body as is_interleaved_body_core,
55
54
  is_capturable_jsx_child,
@@ -57,14 +56,10 @@ import {
57
56
  } from '../jsx-interleave.js';
58
57
  import { is_hoist_safe_jsx_node } from '../jsx-hoist.js';
59
58
 
60
- const HOOK_OUTER_ASSIGNMENT_ERROR =
61
- 'Hook calls inside conditional or repeated TSRX scopes must keep their results local to the generated hook component.';
62
- const HOOK_CALLBACK_OUTER_MUTATION_ERROR =
63
- 'Hook callbacks inside conditional or repeated TSRX scopes must not mutate bindings declared outside the generated hook component.';
64
59
  const TEMPLATE_FRAGMENT_ERROR =
65
60
  'JSX fragment syntax is not needed in TSRX templates. TSRX renders in immediate mode, so everything is already a fragment. Use `<>...</>` only in expression position.';
66
61
  const TSRX_FOR_RETURN_ERROR =
67
- 'Return statements are not allowed inside TSRX template for...of loops. Filter the iterable before rendering or use an @for empty fallback for empty lists.';
62
+ 'Return statements are not allowed inside TSRX template for...of loops. Filter the iterable before rendering or use an @empty fallback for empty lists.';
68
63
  const TSRX_FOR_BREAK_ERROR =
69
64
  'Break statements are not allowed inside TSRX template for...of loops.';
70
65
  const TSRX_FOR_CONTINUE_ERROR =
@@ -89,44 +84,6 @@ function report_jsx_fragment_in_tsrx_error(node, transform_context) {
89
84
  );
90
85
  }
91
86
 
92
- /**
93
- * @param {AST.Node} node
94
- * @param {string[]} names
95
- * @param {string} hook_name
96
- * @param {TransformContext} transform_context
97
- * @returns {void}
98
- */
99
- function report_hook_outer_assignment_error(node, names, hook_name, transform_context) {
100
- const target =
101
- names.length === 1 ? `\`${names[0]}\`` : names.map((name) => `\`${name}\``).join(', ');
102
- error(
103
- `${HOOK_OUTER_ASSIGNMENT_ERROR} The ${hook_name} result is assigned to ${target}, which is declared outside that generated component. Declare the hook result inside the TSRX branch, or move the hook into an explicit child component and pass values with props.`,
104
- transform_context.filename,
105
- node,
106
- transform_context.errors,
107
- transform_context.comments,
108
- );
109
- }
110
-
111
- /**
112
- * @param {AST.Node} node
113
- * @param {string[]} names
114
- * @param {string} hook_name
115
- * @param {TransformContext} transform_context
116
- * @returns {void}
117
- */
118
- function report_hook_callback_outer_mutation_error(node, names, hook_name, transform_context) {
119
- const target =
120
- names.length === 1 ? `\`${names[0]}\`` : names.map((name) => `\`${name}\``).join(', ');
121
- error(
122
- `${HOOK_CALLBACK_OUTER_MUTATION_ERROR} The ${hook_name} callback mutates ${target}. Read outer values through props or dependencies, and move mutable state into an explicit child component when it needs to change over time.`,
123
- transform_context.filename,
124
- node,
125
- transform_context.errors,
126
- transform_context.comments,
127
- );
128
- }
129
-
130
87
  /**
131
88
  * Local alias for the shared `JsxTransformContext`. Kept as a typedef so the
132
89
  * rest of this file's `@param {TransformContext}` annotations don't all have
@@ -223,6 +180,86 @@ function expand_child_code_blocks(node, seen = new Set()) {
223
180
  }
224
181
  }
225
182
 
183
+ /**
184
+ * A `@`-prefixed JSX control-flow expression (`@if`/`@for`/`@switch`/`@try`).
185
+ * These are the only control-flow nodes that can appear in expression position;
186
+ * the plain statement forms (`IfStatement`, `SwitchStatement`, …) never do.
187
+ * @param {any} node
188
+ * @returns {boolean}
189
+ */
190
+ function is_jsx_control_flow_expression(node) {
191
+ return (
192
+ node?.type === 'JSXIfExpression' ||
193
+ node?.type === 'JSXForExpression' ||
194
+ node?.type === 'JSXSwitchExpression' ||
195
+ node?.type === 'JSXTryExpression'
196
+ );
197
+ }
198
+
199
+ /**
200
+ * Wrap a render-output node in a native TSRX fragment so it flows through the
201
+ * same single-child render path as a `<> … </>` output.
202
+ * @param {any} node
203
+ * @returns {any}
204
+ */
205
+ function wrap_in_native_tsrx_fragment(node) {
206
+ const fragment = b.jsx_fragment([node]);
207
+ fragment.metadata = { ...(fragment.metadata || {}), native_tsrx: true };
208
+ return fragment;
209
+ }
210
+
211
+ /**
212
+ * Wrap a bare JSX control-flow directive that sits directly in an expression
213
+ * position — an expression-bodied arrow (`() => @switch (…) { … }`), a
214
+ * `return @switch (…) { … }`, an unused expression statement,
215
+ * assignment to a variable
216
+ * (`const x = @switch (…) { … }`, `x = @switch (…) { … }`), or a call/`new`
217
+ * argument (`render(@if (…) { … })`) — in a native TSRX fragment.
218
+ * @param {any} node
219
+ * @param {Set<any>} [seen]
220
+ * @returns {void}
221
+ */
222
+ function wrap_control_flow_expression_values(node, seen = new Set()) {
223
+ if (!node || typeof node !== 'object' || seen.has(node)) return;
224
+ seen.add(node);
225
+
226
+ if (Array.isArray(node)) {
227
+ for (const item of node) wrap_control_flow_expression_values(item, seen);
228
+ return;
229
+ }
230
+
231
+ if (
232
+ node.type === 'ArrowFunctionExpression' &&
233
+ node.body?.type !== 'BlockStatement' &&
234
+ is_jsx_control_flow_expression(node.body)
235
+ ) {
236
+ node.body = wrap_in_native_tsrx_fragment(node.body);
237
+ } else if (node.type === 'ReturnStatement' && is_jsx_control_flow_expression(node.argument)) {
238
+ node.argument = wrap_in_native_tsrx_fragment(node.argument);
239
+ } else if (
240
+ node.type === 'ExpressionStatement' &&
241
+ is_jsx_control_flow_expression(node.expression)
242
+ ) {
243
+ node.expression = wrap_in_native_tsrx_fragment(node.expression);
244
+ } else if (node.type === 'VariableDeclarator' && is_jsx_control_flow_expression(node.init)) {
245
+ node.init = wrap_in_native_tsrx_fragment(node.init);
246
+ } else if (node.type === 'AssignmentExpression' && is_jsx_control_flow_expression(node.right)) {
247
+ node.right = wrap_in_native_tsrx_fragment(node.right);
248
+ } else if (
249
+ (node.type === 'CallExpression' || node.type === 'NewExpression') &&
250
+ Array.isArray(node.arguments)
251
+ ) {
252
+ node.arguments = node.arguments.map((/** @type {any} */ arg) =>
253
+ is_jsx_control_flow_expression(arg) ? wrap_in_native_tsrx_fragment(arg) : arg,
254
+ );
255
+ }
256
+
257
+ for (const key of Object.keys(node)) {
258
+ if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') continue;
259
+ wrap_control_flow_expression_values(node[key], seen);
260
+ }
261
+ }
262
+
226
263
  /**
227
264
  * Build a `transform()` function for a specific JSX platform (React, Preact,
228
265
  * Solid). Given a `JsxPlatform` descriptor, returns a transform that lowers
@@ -273,6 +310,7 @@ export function createJsxTransform(platform) {
273
310
  hook_helpers_enabled: false,
274
311
  available_bindings: new Map(),
275
312
  lazy_next_id: 0,
313
+ runtime_dynamic_scopes: null,
276
314
  filename: filename ?? null,
277
315
  source,
278
316
  collect,
@@ -285,6 +323,11 @@ export function createJsxTransform(platform) {
285
323
  };
286
324
 
287
325
  expand_child_code_blocks(/** @type {any} */ (ast));
326
+ wrap_control_flow_expression_values(/** @type {any} */ (ast));
327
+ transform_context.runtime_dynamic_scopes = create_runtime_dynamic_scopes(
328
+ /** @type {any} */ (ast),
329
+ transform_context,
330
+ );
288
331
 
289
332
  if (!transform_context.typeOnly) {
290
333
  preallocate_lazy_ids(/** @type {any} */ (ast), transform_context);
@@ -321,10 +364,6 @@ export function createJsxTransform(platform) {
321
364
  },
322
365
 
323
366
  JSXElement(node, { next, path, state }) {
324
- if (!node.metadata?.native_tsrx && is_dynamic_jsx_element(node)) {
325
- return dynamic_element_to_jsx(node, state, in_jsx_child_context(path));
326
- }
327
-
328
367
  if (!node.metadata?.native_tsrx) {
329
368
  return next() ?? node;
330
369
  }
@@ -464,16 +503,26 @@ export function createJsxTransform(platform) {
464
503
  *
465
504
  * @param {any} component
466
505
  * @param {any} css
506
+ * @param {TransformContext} transform_context
467
507
  * @param {boolean} [export_top_scoped_classes]
468
508
  * @returns {void}
469
509
  */
470
- function apply_css_definition_metadata(component, css, export_top_scoped_classes = false) {
510
+ function apply_css_definition_metadata(
511
+ component,
512
+ css,
513
+ transform_context,
514
+ export_top_scoped_classes = false,
515
+ ) {
471
516
  analyze_css(css);
472
517
 
473
518
  const metadata = component.metadata || (component.metadata = { path: [] });
474
519
  const style_classes = metadata.styleClasses || (metadata.styleClasses = new Map());
475
520
  const top_scoped_classes = metadata.topScopedClasses || new Map();
476
- const elements = collect_css_prunable_elements(component.body || component.children || []);
521
+ const elements = collect_css_prunable_elements(
522
+ component.body || component.children || [],
523
+ [],
524
+ transform_context,
525
+ );
477
526
 
478
527
  const prune = () => {
479
528
  for (const element of elements) {
@@ -498,16 +547,17 @@ function apply_css_definition_metadata(component, css, export_top_scoped_classes
498
547
  /**
499
548
  * @param {any} value
500
549
  * @param {any[]} [elements]
550
+ * @param {TransformContext | null} [transform_context]
501
551
  * @returns {any[]}
502
552
  */
503
- function collect_css_prunable_elements(value, elements = []) {
553
+ function collect_css_prunable_elements(value, elements = [], transform_context = null) {
504
554
  if (!value || typeof value !== 'object') {
505
555
  return elements;
506
556
  }
507
557
 
508
558
  if (Array.isArray(value)) {
509
559
  for (const child of value) {
510
- collect_css_prunable_elements(child, elements);
560
+ collect_css_prunable_elements(child, elements, transform_context);
511
561
  }
512
562
  return elements;
513
563
  }
@@ -521,6 +571,7 @@ function collect_css_prunable_elements(value, elements = []) {
521
571
  }
522
572
 
523
573
  if (value.type === 'JSXElement' && value.metadata?.native_tsrx) {
574
+ mark_runtime_dynamic_element(value, transform_context);
524
575
  if (!is_style_element(value)) {
525
576
  elements.push(value);
526
577
  }
@@ -530,12 +581,247 @@ function collect_css_prunable_elements(value, elements = []) {
530
581
  if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata' || key === 'css') {
531
582
  continue;
532
583
  }
533
- collect_css_prunable_elements(value[key], elements);
584
+ collect_css_prunable_elements(value[key], elements, transform_context);
534
585
  }
535
586
 
536
587
  return elements;
537
588
  }
538
589
 
590
+ /**
591
+ * @param {AST.Program} ast
592
+ * @param {TransformContext} transform_context
593
+ * @returns {Map<any, any> | null}
594
+ */
595
+ function create_runtime_dynamic_scopes(ast, transform_context) {
596
+ const dynamic_source = transform_context.platform.imports.dynamic;
597
+ if (!dynamic_source) {
598
+ return null;
599
+ }
600
+ if (!has_runtime_dynamic_import(ast, dynamic_source)) {
601
+ return null;
602
+ }
603
+
604
+ const { scopes } = create_scopes(ast, new ScopeRoot(), null, {
605
+ collect: true,
606
+ errors: [],
607
+ filename: transform_context.filename ?? '',
608
+ comments: transform_context.comments,
609
+ });
610
+
611
+ return scopes;
612
+ }
613
+
614
+ /**
615
+ * @param {any} node
616
+ * @param {TransformContext | null} transform_context
617
+ * @returns {void}
618
+ */
619
+ function mark_runtime_dynamic_element(node, transform_context) {
620
+ const dynamic_source = transform_context?.platform.imports.dynamic;
621
+ const scopes = transform_context?.runtime_dynamic_scopes;
622
+ if (
623
+ !dynamic_source ||
624
+ !scopes ||
625
+ node.metadata?.runtime_dynamic_element === true ||
626
+ !has_jsx_attribute(node, 'is') ||
627
+ !is_runtime_dynamic_jsx_name(node.openingElement?.name, scopes.get(node), dynamic_source)
628
+ ) {
629
+ return;
630
+ }
631
+
632
+ node.metadata.runtime_dynamic_element = true;
633
+ }
634
+
635
+ /**
636
+ * @param {AST.Program} ast
637
+ * @param {string} dynamic_source
638
+ * @returns {boolean}
639
+ */
640
+ function has_runtime_dynamic_import(ast, dynamic_source) {
641
+ return ast.body.some(
642
+ (/** @type {any} */ node) =>
643
+ node.type === 'ImportDeclaration' &&
644
+ node.importKind !== 'type' &&
645
+ node.source?.type === 'Literal' &&
646
+ node.source.value === dynamic_source &&
647
+ node.specifiers.some(
648
+ (/** @type {any} */ specifier) =>
649
+ is_runtime_dynamic_import_specifier(specifier, 'component') ||
650
+ is_runtime_dynamic_import_specifier(specifier, 'namespace'),
651
+ ),
652
+ );
653
+ }
654
+
655
+ /**
656
+ * @param {any} node
657
+ * @param {string} name
658
+ * @returns {boolean}
659
+ */
660
+ function has_jsx_attribute(node, name) {
661
+ return (node.openingElement?.attributes ?? []).some(
662
+ (/** @type {any} */ attr) =>
663
+ attr.type === 'JSXAttribute' &&
664
+ attr.name?.type === 'JSXIdentifier' &&
665
+ attr.name.name === name,
666
+ );
667
+ }
668
+
669
+ /**
670
+ * @param {any} name
671
+ * @param {any} scope
672
+ * @param {string} dynamic_source
673
+ * @returns {boolean}
674
+ */
675
+ function is_runtime_dynamic_jsx_name(name, scope, dynamic_source) {
676
+ if (!scope || !name) {
677
+ return false;
678
+ }
679
+
680
+ if (name.type === 'JSXIdentifier') {
681
+ return is_runtime_dynamic_binding(scope.get(name.name), dynamic_source, 'component', new Set());
682
+ }
683
+
684
+ if (
685
+ name.type === 'JSXMemberExpression' &&
686
+ name.object?.type === 'JSXIdentifier' &&
687
+ name.property?.type === 'JSXIdentifier' &&
688
+ name.property.name === 'Dynamic'
689
+ ) {
690
+ return is_runtime_dynamic_binding(
691
+ scope.get(name.object.name),
692
+ dynamic_source,
693
+ 'namespace',
694
+ new Set(),
695
+ );
696
+ }
697
+
698
+ return false;
699
+ }
700
+
701
+ /**
702
+ * @param {any} binding
703
+ * @param {string} dynamic_source
704
+ * @param {'component' | 'namespace'} kind
705
+ * @param {Set<any>} seen
706
+ * @returns {boolean}
707
+ */
708
+ function is_runtime_dynamic_binding(binding, dynamic_source, kind, seen) {
709
+ if (!binding || seen.has(binding)) {
710
+ return false;
711
+ }
712
+ seen.add(binding);
713
+
714
+ if (is_runtime_dynamic_import_binding(binding, dynamic_source, kind)) {
715
+ return true;
716
+ }
717
+
718
+ if (binding.reassigned) {
719
+ return false;
720
+ }
721
+
722
+ const initial = unwrap_reference_expression(binding.initial);
723
+ if (!initial) {
724
+ return false;
725
+ }
726
+
727
+ if (initial.type === 'Identifier') {
728
+ return is_runtime_dynamic_binding(binding.scope.get(initial.name), dynamic_source, kind, seen);
729
+ }
730
+
731
+ if (
732
+ kind === 'component' &&
733
+ initial.type === 'MemberExpression' &&
734
+ !initial.computed &&
735
+ initial.object?.type === 'Identifier' &&
736
+ initial.property?.type === 'Identifier' &&
737
+ initial.property.name === 'Dynamic'
738
+ ) {
739
+ return is_runtime_dynamic_binding(
740
+ binding.scope.get(initial.object.name),
741
+ dynamic_source,
742
+ 'namespace',
743
+ new Set(),
744
+ );
745
+ }
746
+
747
+ return false;
748
+ }
749
+
750
+ /**
751
+ * @param {any} binding
752
+ * @param {string} dynamic_source
753
+ * @param {'component' | 'namespace'} kind
754
+ * @returns {boolean}
755
+ */
756
+ function is_runtime_dynamic_import_binding(binding, dynamic_source, kind) {
757
+ const declaration = binding?.initial;
758
+ if (
759
+ binding?.declaration_kind !== 'import' ||
760
+ declaration?.type !== 'ImportDeclaration' ||
761
+ declaration.importKind === 'type' ||
762
+ declaration.source?.type !== 'Literal' ||
763
+ declaration.source.value !== dynamic_source
764
+ ) {
765
+ return false;
766
+ }
767
+
768
+ return declaration.specifiers.some(
769
+ (/** @type {any} */ specifier) =>
770
+ specifier.local?.name === binding.node?.name &&
771
+ is_runtime_dynamic_import_specifier(specifier, kind),
772
+ );
773
+ }
774
+
775
+ /**
776
+ * @param {any} specifier
777
+ * @param {'component' | 'namespace'} kind
778
+ * @returns {boolean}
779
+ */
780
+ function is_runtime_dynamic_import_specifier(specifier, kind) {
781
+ if (kind === 'namespace') {
782
+ return specifier.type === 'ImportNamespaceSpecifier';
783
+ }
784
+ return (
785
+ specifier.type === 'ImportSpecifier' &&
786
+ specifier.importKind !== 'type' &&
787
+ get_imported_name(specifier) === 'Dynamic'
788
+ );
789
+ }
790
+
791
+ /**
792
+ * @param {any} specifier
793
+ * @returns {string | null}
794
+ */
795
+ function get_imported_name(specifier) {
796
+ const imported = specifier.imported;
797
+ if (imported?.type === 'Identifier') {
798
+ return imported.name;
799
+ }
800
+ if (imported?.type === 'Literal') {
801
+ return String(imported.value);
802
+ }
803
+ return null;
804
+ }
805
+
806
+ /**
807
+ * @param {any} expression
808
+ * @returns {any}
809
+ */
810
+ function unwrap_reference_expression(expression) {
811
+ let node = expression;
812
+ while (
813
+ node &&
814
+ (node.type === 'TSAsExpression' ||
815
+ node.type === 'TSTypeAssertion' ||
816
+ node.type === 'TSNonNullExpression' ||
817
+ node.type === 'ParenthesizedExpression' ||
818
+ node.type === 'ChainExpression')
819
+ ) {
820
+ node = node.expression;
821
+ }
822
+ return node;
823
+ }
824
+
539
825
  /**
540
826
  * @param {any[]} body_nodes
541
827
  * @param {TransformContext} transform_context
@@ -598,7 +884,7 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
598
884
  (node) =>
599
885
  !is_loop_skip_return_statement(node) &&
600
886
  !is_loop_skip_if_statement(node) &&
601
- !is_jsx_child(node),
887
+ !is_render_child_node(node),
602
888
  );
603
889
 
604
890
  if (!continuation_has_setup_statements) {
@@ -640,7 +926,7 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
640
926
  }
641
927
 
642
928
  if (
643
- is_for_of_control_node(child) &&
929
+ is_template_for_of_node(child) &&
644
930
  !child.await &&
645
931
  should_extract_hook_helpers(transform_context) &&
646
932
  !transform_context.platform.hooks?.isTopLevelSetupCall &&
@@ -668,7 +954,7 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
668
954
  }
669
955
  }
670
956
 
671
- if (is_jsx_child(child)) {
957
+ if (is_render_child_node(child)) {
672
958
  const jsx = to_jsx_child(child, transform_context);
673
959
  statements.push(...extract_jsx_setup_declarations(jsx));
674
960
  if (interleaved && is_capturable_jsx_child(jsx)) {
@@ -705,189 +991,49 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
705
991
  * @returns {boolean}
706
992
  */
707
993
  function is_interleaved_body(body_nodes) {
708
- return is_interleaved_body_core(body_nodes, is_jsx_child);
994
+ return is_interleaved_body_core(body_nodes, is_render_child_node);
709
995
  }
710
996
 
711
997
  /**
712
- * @param {any} node
998
+ * @param {any[]} body_nodes
713
999
  * @param {TransformContext} transform_context
1000
+ * @param {boolean} include_platform_setup
714
1001
  * @returns {boolean}
715
1002
  */
716
- function needs_hook_split(node, transform_context) {
717
- return (
718
- transform_context.platform.hooks?.componentBodyHookHelpers === true &&
719
- node.body?.type === 'BlockStatement' &&
720
- find_hook_split_index(node.body.body || [], transform_context) !== -1
1003
+ function body_contains_top_level_hook_call(
1004
+ body_nodes,
1005
+ transform_context,
1006
+ include_platform_setup = false,
1007
+ ) {
1008
+ return body_nodes.some((node) =>
1009
+ statement_contains_top_level_hook_call(node, transform_context, include_platform_setup),
721
1010
  );
722
1011
  }
723
1012
 
724
1013
  /**
725
1014
  * @param {any} node
726
1015
  * @param {TransformContext} transform_context
727
- * @returns {any}
728
- */
729
- function create_hook_split_block(node, transform_context) {
730
- if (
731
- transform_context.platform.hooks?.componentBodyHookHelpers !== true ||
732
- !should_extract_hook_helpers(transform_context) ||
733
- node.body?.type !== 'BlockStatement'
734
- ) {
735
- return null;
736
- }
737
-
738
- const body = node.body.body || [];
739
- const split_index = find_hook_split_index(body, transform_context);
740
- if (split_index === -1) {
741
- return null;
742
- }
743
-
744
- const split_statement = body[split_index];
745
- const continuation_body = body.slice(split_index + 1);
746
- const helper = create_hook_safe_helper(
747
- expand_native_tsrx_return_statement_list(continuation_body, transform_context),
748
- undefined,
749
- get_body_source_node(continuation_body) || split_statement,
750
- transform_context,
751
- );
752
-
753
- const block = b.block(
754
- [
755
- ...body.slice(0, split_index + 1),
756
- ...helper.setup_statements,
757
- set_loc(b.return(helper.component_element), split_statement),
758
- ],
759
- node.body,
760
- );
761
- block.metadata = {
762
- ...(block.metadata || {}),
763
- hook_split_block: true,
764
- };
765
- return block;
766
- }
767
-
768
- /**
769
- * @param {any[]} body_nodes
770
- * @param {TransformContext} transform_context
771
- * @returns {number}
1016
+ * @param {boolean} include_platform_setup
1017
+ * @returns {boolean}
772
1018
  */
773
- function find_hook_split_index(body_nodes, transform_context) {
774
- for (let i = 0; i < body_nodes.length; i += 1) {
775
- if (!is_component_body_conditional_return_statement(body_nodes[i])) {
776
- continue;
777
- }
778
-
779
- if (body_contains_top_level_hook_call(body_nodes.slice(i + 1), transform_context, true)) {
780
- return i;
781
- }
782
- }
783
-
784
- return -1;
1019
+ function statement_contains_top_level_hook_call(node, transform_context, include_platform_setup) {
1020
+ return node_contains_top_level_hook_call(node, false, transform_context, include_platform_setup);
785
1021
  }
786
1022
 
787
1023
  /**
788
1024
  * @param {any} node
1025
+ * @param {boolean} inside_nested_function
1026
+ * @param {TransformContext} transform_context
1027
+ * @param {boolean} include_platform_setup
789
1028
  * @returns {boolean}
790
1029
  */
791
- function is_component_body_conditional_return_statement(node) {
792
- if (!is_if_control_node(node)) {
793
- return false;
794
- }
795
-
796
- return (
797
- statement_contains_component_body_return(node.consequent) ||
798
- statement_contains_component_body_return(node.alternate)
799
- );
800
- }
801
-
802
- /**
803
- * @param {any} node
804
- * @returns {boolean}
805
- */
806
- function statement_contains_component_body_return(node) {
807
- if (!node || typeof node !== 'object') {
808
- return false;
809
- }
810
-
811
- if (node.type === 'ReturnStatement') {
812
- return true;
813
- }
814
-
815
- if (is_function_or_class_boundary(node)) {
816
- return false;
817
- }
818
-
819
- if (Array.isArray(node)) {
820
- return node.some(statement_contains_component_body_return);
821
- }
822
-
823
- if (node.type === 'BlockStatement') {
824
- return (node.body || []).some(statement_contains_component_body_return);
825
- }
826
-
827
- if (is_if_control_node(node)) {
828
- return (
829
- statement_contains_component_body_return(node.consequent) ||
830
- statement_contains_component_body_return(node.alternate)
831
- );
832
- }
833
-
834
- if (is_switch_control_node(node)) {
835
- return (node.cases || []).some((/** @type {any} */ switch_case) =>
836
- statement_contains_component_body_return(switch_case.consequent || []),
837
- );
838
- }
839
-
840
- if (is_try_control_node(node)) {
841
- return (
842
- statement_contains_component_body_return(node.block) ||
843
- statement_contains_component_body_return(node.handler?.body) ||
844
- statement_contains_component_body_return(node.finalizer)
845
- );
846
- }
847
-
848
- return false;
849
- }
850
-
851
- /**
852
- * @param {any[]} body_nodes
853
- * @param {TransformContext} transform_context
854
- * @param {boolean} include_platform_setup
855
- * @returns {boolean}
856
- */
857
- function body_contains_top_level_hook_call(
858
- body_nodes,
859
- transform_context,
860
- include_platform_setup = false,
861
- ) {
862
- return body_nodes.some((node) =>
863
- statement_contains_top_level_hook_call(node, transform_context, include_platform_setup),
864
- );
865
- }
866
-
867
- /**
868
- * @param {any} node
869
- * @param {TransformContext} transform_context
870
- * @param {boolean} include_platform_setup
871
- * @returns {boolean}
872
- */
873
- function statement_contains_top_level_hook_call(node, transform_context, include_platform_setup) {
874
- return node_contains_top_level_hook_call(node, false, transform_context, include_platform_setup);
875
- }
876
-
877
- /**
878
- * @param {any} node
879
- * @param {boolean} inside_nested_function
880
- * @param {TransformContext} transform_context
881
- * @param {boolean} include_platform_setup
882
- * @returns {boolean}
883
- */
884
- function node_contains_top_level_hook_call(
885
- node,
886
- inside_nested_function,
887
- transform_context,
888
- include_platform_setup,
889
- ) {
890
- if (!node || typeof node !== 'object') {
1030
+ function node_contains_top_level_hook_call(
1031
+ node,
1032
+ inside_nested_function,
1033
+ transform_context,
1034
+ include_platform_setup,
1035
+ ) {
1036
+ if (!node || typeof node !== 'object') {
891
1037
  return false;
892
1038
  }
893
1039
 
@@ -1092,7 +1238,7 @@ function create_generated_helper_metadata(helper_state) {
1092
1238
  * @returns {any}
1093
1239
  */
1094
1240
  function strip_function_transform_metadata(metadata) {
1095
- const { native_tsrx, hook_split, ...next_metadata } = metadata || {};
1241
+ const { native_tsrx, ...next_metadata } = metadata || {};
1096
1242
  return next_metadata;
1097
1243
  }
1098
1244
 
@@ -1102,19 +1248,11 @@ function strip_function_transform_metadata(metadata) {
1102
1248
  * @returns {any}
1103
1249
  */
1104
1250
  function transform_block_statement(node, { next, visit, state, path }) {
1105
- if (node.metadata?.hook_split_block || node.metadata?.native_return_block) {
1251
+ if (node.metadata?.native_return_block) {
1106
1252
  return next() ?? node;
1107
1253
  }
1108
1254
 
1109
- const parent = /** @type {any} */ (path.at(-1));
1110
- if (parent?.metadata?.hook_split && parent.body === node) {
1111
- const block = create_hook_split_block(parent, state);
1112
- if (block) {
1113
- return visit(block, state);
1114
- }
1115
- }
1116
-
1117
- if (get_active_native_tsrx_function(path)) {
1255
+ if (get_active_native_tsrx_function(path)?.metadata?.native_tsrx_body) {
1118
1256
  const block = create_native_tsrx_statement_list_block(node, state);
1119
1257
  if (block) {
1120
1258
  return visit(block, state);
@@ -1130,7 +1268,22 @@ function transform_block_statement(node, { next, visit, state, path }) {
1130
1268
  * @returns {any}
1131
1269
  */
1132
1270
  function transform_return_statement(node, { next, visit, state, path }) {
1133
- if (get_active_native_tsrx_function(path) && is_native_tsrx_node(node.argument)) {
1271
+ const active_native_tsrx_function = get_active_native_tsrx_function(path);
1272
+ if (active_native_tsrx_function && is_native_tsrx_node(node.argument)) {
1273
+ if (!active_native_tsrx_function.metadata?.native_tsrx_body) {
1274
+ const statements = mark_native_pretransformed_jsx(
1275
+ create_native_tsrx_render_statements(node.argument, state),
1276
+ );
1277
+ if (statements.length === 1) {
1278
+ return visit(statements[0], state);
1279
+ }
1280
+ const block = b.block(statements, node.argument);
1281
+ block.metadata = {
1282
+ ...(block.metadata || {}),
1283
+ native_return_block: true,
1284
+ };
1285
+ return visit(block, state);
1286
+ }
1134
1287
  return visit(create_native_tsrx_render_block(node.argument, state), state);
1135
1288
  }
1136
1289
 
@@ -1190,10 +1343,17 @@ function transform_function(node, context) {
1190
1343
  // render output. The parser already marks the render JSX as native_tsrx, so
1191
1344
  // from here it flows through the existing native-component machinery exactly
1192
1345
  // like the older fenced `{ return <> … </> }` shape.
1346
+ const has_jsx_code_block_body = node.body?.type === 'JSXCodeBlock';
1193
1347
  lower_jsx_code_block_function_body(node);
1194
1348
 
1195
- if (node.metadata?.native_tsrx_function || function_has_native_tsrx_return(node)) {
1196
- return transform_native_tsrx_function(node, context);
1349
+ if (
1350
+ has_jsx_code_block_body ||
1351
+ node.metadata?.native_tsrx_function ||
1352
+ function_has_native_tsrx_return(node)
1353
+ ) {
1354
+ return transform_native_tsrx_function(node, context, {
1355
+ nativeBody: has_jsx_code_block_body || !!node.metadata?.native_tsrx_function,
1356
+ });
1197
1357
  }
1198
1358
 
1199
1359
  return transform_function_with_hook_helpers(node, context);
@@ -1230,9 +1390,10 @@ function lower_jsx_code_block_function_body(node) {
1230
1390
  /**
1231
1391
  * @param {any} node
1232
1392
  * @param {{ next: () => any, state: TransformContext }} context
1393
+ * @param {{ nativeBody?: boolean }} [options]
1233
1394
  * @returns {any}
1234
1395
  */
1235
- function transform_native_tsrx_function(node, { next, state }) {
1396
+ function transform_native_tsrx_function(node, { next, state }, { nativeBody = false } = {}) {
1236
1397
  const helper_state =
1237
1398
  state.helper_state || create_helper_state(get_function_helper_base_name(node));
1238
1399
  const saved_helper_state = state.helper_state;
@@ -1244,7 +1405,7 @@ function transform_native_tsrx_function(node, { next, state }) {
1244
1405
  node.metadata = {
1245
1406
  ...(node.metadata || {}),
1246
1407
  native_tsrx: true,
1247
- ...(needs_hook_split(node, state) ? { hook_split: true } : {}),
1408
+ ...(nativeBody ? { native_tsrx_body: true } : {}),
1248
1409
  };
1249
1410
  state.available_bindings = merge_binding_maps(
1250
1411
  saved_bindings,
@@ -1270,6 +1431,7 @@ function transform_native_tsrx_function(node, { next, state }) {
1270
1431
  inner.metadata = {
1271
1432
  ...strip_function_transform_metadata(inner.metadata),
1272
1433
  native_tsrx_function: true,
1434
+ ...(nativeBody ? { native_tsrx_body: true } : {}),
1273
1435
  ...(!saved_helper_state ? create_generated_helper_metadata(helper_state) || {} : {}),
1274
1436
  };
1275
1437
 
@@ -1395,13 +1557,12 @@ function find_native_await_in_statement(statement) {
1395
1557
  * @returns {any}
1396
1558
  */
1397
1559
  function transform_function_with_hook_helpers(node, { next, state }) {
1560
+ if (!state.platform.hooks?.moduleScopedHookComponents) {
1561
+ return next() ?? node;
1562
+ }
1563
+
1398
1564
  const has_hook_bearing_tsrx = function_contains_hook_bearing_tsrx(node, state);
1399
- const has_hook_split = needs_hook_split(node, state);
1400
- if (
1401
- state.helper_state ||
1402
- !is_uppercase_function_like(node) ||
1403
- (!has_hook_bearing_tsrx && !has_hook_split)
1404
- ) {
1565
+ if (state.helper_state || !is_uppercase_function_like(node) || !has_hook_bearing_tsrx) {
1405
1566
  return next() ?? node;
1406
1567
  }
1407
1568
 
@@ -1412,12 +1573,6 @@ function transform_function_with_hook_helpers(node, { next, state }) {
1412
1573
 
1413
1574
  state.helper_state = helper_state;
1414
1575
  state.hook_helpers_enabled = true;
1415
- if (has_hook_split) {
1416
- node.metadata = {
1417
- ...(node.metadata || {}),
1418
- hook_split: true,
1419
- };
1420
- }
1421
1576
  state.available_bindings = collect_function_scope_bindings(node);
1422
1577
 
1423
1578
  const inner = /** @type {any} */ (next() ?? node);
@@ -1681,7 +1836,7 @@ function prepare_tsrx_fragment_styles(node, transform_context) {
1681
1836
  if (!css) return null;
1682
1837
 
1683
1838
  const style_refs = collect_style_ref_attributes(node);
1684
- apply_css_definition_metadata(node, css, style_refs.length > 0);
1839
+ apply_css_definition_metadata(node, css, transform_context, style_refs.length > 0);
1685
1840
  transform_context.stylesheets.push(css);
1686
1841
  const fragment = annotate_tsrx_with_hash(
1687
1842
  node,
@@ -2271,7 +2426,10 @@ function should_use_module_scoped_hook_components(transform_context) {
2271
2426
  * @returns {boolean}
2272
2427
  */
2273
2428
  function should_extract_hook_helpers(transform_context) {
2274
- return !!transform_context.hook_helpers_enabled;
2429
+ return !!(
2430
+ transform_context.hook_helpers_enabled &&
2431
+ transform_context.platform.hooks?.moduleScopedHookComponents
2432
+ );
2275
2433
  }
2276
2434
 
2277
2435
  /**
@@ -2833,11 +2991,6 @@ function build_hoisted_for_of_with_hooks(node, transform_context) {
2833
2991
  for (const param of loop_params) {
2834
2992
  collect_pattern_bindings(param, transform_context.available_bindings);
2835
2993
  }
2836
- validate_hook_safe_body_does_not_assign_hook_results_to_outer_bindings(
2837
- original_loop_body,
2838
- transform_context,
2839
- loop_scoped_names,
2840
- );
2841
2994
 
2842
2995
  const all_helper_bindings = get_referenced_helper_bindings(
2843
2996
  original_loop_body,
@@ -3056,7 +3209,7 @@ function to_jsx_element(
3056
3209
  raw_children = node.children || [],
3057
3210
  in_jsx_child = false,
3058
3211
  ) {
3059
- if (node.type === 'JSXElement' && !node.metadata?.native_tsrx && !is_dynamic_jsx_element(node)) {
3212
+ if (node.type === 'JSXElement' && !node.metadata?.native_tsrx) {
3060
3213
  return node;
3061
3214
  }
3062
3215
 
@@ -3066,10 +3219,6 @@ function to_jsx_element(
3066
3219
  report_jsx_fragment_in_tsrx_error(node, transform_context);
3067
3220
  return set_loc(b.jsx_fragment(), node);
3068
3221
  }
3069
- if (is_dynamic_jsx_element(node)) {
3070
- return dynamic_element_to_jsx(node, transform_context, in_jsx_child);
3071
- }
3072
-
3073
3222
  const name = clone_jsx_name(source_name);
3074
3223
  const attributes = transform_element_attributes_dispatch(
3075
3224
  source_opening.attributes || [],
@@ -3146,829 +3295,160 @@ function create_element_children(children, transform_context) {
3146
3295
  }
3147
3296
  }
3148
3297
 
3149
- const saved_inside_element_child = transform_context.inside_element_child;
3150
- transform_context.inside_element_child = true;
3151
- try {
3152
- return [statement_body_to_jsx_child(children, transform_context)];
3153
- } finally {
3154
- transform_context.inside_element_child = saved_inside_element_child;
3155
- }
3156
- }
3157
-
3158
- /**
3159
- * @param {any[]} children
3160
- * @returns {boolean}
3161
- */
3162
- function children_contain_return_semantics(children) {
3163
- return children.some(child_contains_return_semantics);
3164
- }
3165
-
3166
- /**
3167
- * @param {any} node
3168
- * @returns {boolean}
3169
- */
3170
- function child_contains_return_semantics(node) {
3171
- if (!node || typeof node !== 'object') {
3172
- return false;
3173
- }
3174
-
3175
- if (node.type === 'ReturnStatement') {
3176
- return true;
3177
- }
3178
-
3179
- if (
3180
- node.type === 'FunctionDeclaration' ||
3181
- node.type === 'FunctionExpression' ||
3182
- node.type === 'ArrowFunctionExpression'
3183
- ) {
3184
- return false;
3185
- }
3186
-
3187
- if (Array.isArray(node)) {
3188
- return node.some(child_contains_return_semantics);
3189
- }
3190
-
3191
- for (const key of Object.keys(node)) {
3192
- if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
3193
- continue;
3194
- }
3195
- if (child_contains_return_semantics(node[key])) {
3196
- return true;
3197
- }
3198
- }
3199
-
3200
- return false;
3201
- }
3202
-
3203
- /**
3204
- * @param {any} node
3205
- * @returns {boolean}
3206
- */
3207
- function is_inline_element_child(node) {
3208
- return node && is_jsx_child(node);
3209
- }
3210
-
3211
- /**
3212
- * @param {any[]} body_nodes
3213
- * @param {TransformContext} transform_context
3214
- * @returns {ESTreeJSX.JSXExpressionContainer}
3215
- */
3216
- function statement_body_to_jsx_child(body_nodes, transform_context) {
3217
- if (
3218
- should_extract_hook_helpers(transform_context) &&
3219
- body_contains_top_level_hook_call(body_nodes, transform_context, true)
3220
- ) {
3221
- return hook_safe_statement_body_to_jsx_child(body_nodes, transform_context);
3222
- }
3223
-
3224
- return to_jsx_expression_container(
3225
- b.call(b.arrow([], b.block(build_render_statements(body_nodes, true, transform_context)))),
3226
- );
3227
- }
3228
-
3229
- /**
3230
- * @param {any[]} body_nodes
3231
- * @param {TransformContext} transform_context
3232
- * @returns {ESTreeJSX.JSXExpressionContainer}
3233
- */
3234
- function hook_safe_statement_body_to_jsx_child(body_nodes, transform_context) {
3235
- const source_node = get_body_source_node(body_nodes);
3236
- const helper = create_hook_safe_helper(body_nodes, undefined, source_node, transform_context);
3237
-
3238
- return to_jsx_expression_container(
3239
- create_hook_safe_helper_iife(helper.setup_statements, helper.component_element),
3240
- source_node,
3241
- );
3242
- }
3243
-
3244
- /**
3245
- * @param {TransformContext} transform_context
3246
- * @returns {string}
3247
- */
3248
- function create_local_statement_component_name(transform_context) {
3249
- transform_context.local_statement_component_index += 1;
3250
- return `StatementBodyHook${transform_context.local_statement_component_index}`;
3251
- }
3252
-
3253
- /**
3254
- * Wraps a list of body nodes into a component and returns
3255
- * statements that return `<ComponentName prop1={prop1} ... />`.
3256
- * Targets can either emit the helper component at module scope or cache the
3257
- * component identity in module state while initializing it from the parent.
3258
- * Used when a control flow branch contains hook calls that must be moved
3259
- * into their own component boundary to satisfy the Rules of Hooks.
3260
- *
3261
- * @param {any[]} body_nodes
3262
- * @param {any} key_expression - Optional key expression to add to the component element (for for-of loops)
3263
- * @param {TransformContext} transform_context
3264
- * @returns {any[]}
3265
- */
3266
- function hook_safe_render_statements(body_nodes, key_expression, transform_context) {
3267
- const source_node = get_body_source_node(body_nodes);
3268
- const helper = create_hook_safe_helper(
3269
- body_nodes,
3270
- key_expression,
3271
- source_node,
3272
- transform_context,
3273
- );
3274
- const statements = [...helper.setup_statements];
3275
-
3276
- statements.push(b.return(helper.component_element));
3277
-
3278
- return statements;
3279
- }
3280
-
3281
- /**
3282
- * @param {any[]} body_nodes
3283
- * @param {Map<string, AST.Identifier>} available_bindings
3284
- * @returns {AST.Identifier[]}
3285
- */
3286
- function get_referenced_helper_bindings(body_nodes, available_bindings) {
3287
- const helper_bindings = [];
3288
- const local_bindings = new Map();
3289
-
3290
- for (const node of body_nodes) {
3291
- collect_statement_bindings(node, local_bindings);
3292
- }
3293
-
3294
- for (const [name, binding] of available_bindings) {
3295
- if (local_bindings.has(name)) continue;
3296
-
3297
- if (references_scope_bindings(body_nodes, new Map([[name, binding]]))) {
3298
- helper_bindings.push(binding);
3299
- }
3300
- }
3301
-
3302
- return helper_bindings;
3303
- }
3304
-
3305
- /**
3306
- * @param {any[]} body_nodes
3307
- * @param {TransformContext} transform_context
3308
- * @param {Set<string>} [local_binding_names]
3309
- * @returns {void}
3310
- */
3311
- function validate_hook_safe_body_does_not_assign_hook_results_to_outer_bindings(
3312
- body_nodes,
3313
- transform_context,
3314
- local_binding_names,
3315
- ) {
3316
- if (!is_react_like_hook_platform(transform_context)) {
3317
- return;
3318
- }
3319
- if (!body_contains_top_level_hook_call(body_nodes, transform_context, true)) {
3320
- return;
3321
- }
3322
- if (!transform_context.available_bindings || transform_context.available_bindings.size === 0) {
3323
- return;
3324
- }
3325
-
3326
- const shadowed_names = collect_block_binding_names(body_nodes);
3327
- for (const name of local_binding_names || []) {
3328
- shadowed_names.add(name);
3329
- }
3330
- validate_hook_outer_assignments_in_node(body_nodes, shadowed_names, transform_context, new Set());
3331
- }
3332
-
3333
- /**
3334
- * @param {TransformContext} transform_context
3335
- * @returns {boolean}
3336
- */
3337
- function is_react_like_hook_platform(transform_context) {
3338
- return (
3339
- transform_context.platform.name === 'React' || transform_context.platform.name === 'Preact'
3340
- );
3341
- }
3342
-
3343
- /**
3344
- * @param {any[]} statements
3345
- * @returns {Set<string>}
3346
- */
3347
- function collect_block_binding_names(statements) {
3348
- const names = new Set();
3349
- for (const statement of statements || []) {
3350
- collect_block_binding_names_from_statement(statement, names);
3351
- }
3352
- return names;
3353
- }
3354
-
3355
- /**
3356
- * @param {any} statement
3357
- * @param {Set<string>} names
3358
- * @returns {void}
3359
- */
3360
- function collect_block_binding_names_from_statement(statement, names) {
3361
- if (!statement || typeof statement !== 'object') {
3362
- return;
3363
- }
3364
-
3365
- if (statement.type === 'VariableDeclaration') {
3366
- for (const declaration of statement.declarations || []) {
3367
- collect_pattern_names(declaration.id, names);
3368
- }
3369
- return;
3370
- }
3371
-
3372
- if (
3373
- (statement.type === 'FunctionDeclaration' || statement.type === 'ClassDeclaration') &&
3374
- statement.id?.type === 'Identifier'
3375
- ) {
3376
- names.add(statement.id.name);
3377
- return;
3378
- }
3379
-
3380
- if (
3381
- statement.type === 'ForOfStatement' ||
3382
- statement.type === 'ForInStatement' ||
3383
- (statement.type === 'JSXForExpression' &&
3384
- (statement.statementType === 'ForOfStatement' ||
3385
- statement.statementType === 'ForInStatement'))
3386
- ) {
3387
- if (statement.left?.type === 'VariableDeclaration' && statement.left.kind === 'var') {
3388
- for (const declaration of statement.left.declarations || []) {
3389
- collect_pattern_names(declaration.id, names);
3390
- }
3391
- }
3392
- return;
3393
- }
3394
-
3395
- if (
3396
- statement.type === 'ForStatement' &&
3397
- statement.init?.type === 'VariableDeclaration' &&
3398
- statement.init.kind === 'var'
3399
- ) {
3400
- for (const declaration of statement.init.declarations || []) {
3401
- collect_pattern_names(declaration.id, names);
3402
- }
3403
- }
3404
- }
3405
-
3406
- /**
3407
- * @param {any} pattern
3408
- * @param {Set<string>} names
3409
- * @returns {void}
3410
- */
3411
- function collect_pattern_names(pattern, names) {
3412
- if (!pattern || typeof pattern !== 'object') {
3413
- return;
3414
- }
3415
-
3416
- if (pattern.type === 'Identifier') {
3417
- names.add(pattern.name);
3418
- return;
3419
- }
3420
-
3421
- if (pattern.type === 'RestElement') {
3422
- collect_pattern_names(pattern.argument, names);
3423
- return;
3424
- }
3425
-
3426
- if (pattern.type === 'AssignmentPattern') {
3427
- collect_pattern_names(pattern.left, names);
3428
- return;
3429
- }
3430
-
3431
- if (pattern.type === 'ArrayPattern') {
3432
- for (const element of pattern.elements || []) {
3433
- collect_pattern_names(element, names);
3434
- }
3435
- return;
3436
- }
3437
-
3438
- if (pattern.type === 'ObjectPattern') {
3439
- for (const property of pattern.properties || []) {
3440
- if (property.type === 'RestElement') {
3441
- collect_pattern_names(property.argument, names);
3442
- } else {
3443
- collect_pattern_names(property.value, names);
3444
- }
3445
- }
3446
- }
3447
- }
3448
-
3449
- /**
3450
- * @param {any} node
3451
- * @param {Set<string>} shadowed_names
3452
- * @param {TransformContext} transform_context
3453
- * @param {Set<string>} hook_result_names
3454
- * @returns {void}
3455
- */
3456
- function validate_hook_outer_assignments_in_node(
3457
- node,
3458
- shadowed_names,
3459
- transform_context,
3460
- hook_result_names,
3461
- ) {
3462
- if (!node || typeof node !== 'object') {
3463
- return;
3464
- }
3465
-
3466
- if (Array.isArray(node)) {
3467
- for (const child of node) {
3468
- validate_hook_outer_assignments_in_node(
3469
- child,
3470
- shadowed_names,
3471
- transform_context,
3472
- hook_result_names,
3473
- );
3474
- }
3475
- return;
3476
- }
3477
-
3478
- if (is_function_or_component_node(node)) {
3479
- return;
3480
- }
3481
-
3482
- if (node.type === 'CallExpression' && is_hook_callee(node.callee)) {
3483
- validate_hook_callback_outer_mutations(node, shadowed_names, transform_context);
3484
- }
3485
-
3486
- if (node.type === 'BlockStatement') {
3487
- const next_shadowed = new Set(shadowed_names);
3488
- const next_hook_result_names = new Set(hook_result_names);
3489
- for (const name of collect_block_binding_names(node.body || [])) {
3490
- next_shadowed.add(name);
3491
- }
3492
- for (const child of node.body || []) {
3493
- validate_hook_outer_assignments_in_node(
3494
- child,
3495
- next_shadowed,
3496
- transform_context,
3497
- next_hook_result_names,
3498
- );
3499
- }
3500
- return;
3501
- }
3502
-
3503
- if (node.type === 'VariableDeclaration') {
3504
- for (const declaration of node.declarations || []) {
3505
- if (
3506
- declaration.init &&
3507
- expression_contains_hook_derived_value(
3508
- declaration.init,
3509
- transform_context,
3510
- hook_result_names,
3511
- )
3512
- ) {
3513
- collect_pattern_names(declaration.id, hook_result_names);
3514
- }
3515
- validate_hook_outer_assignments_in_node(
3516
- declaration.init,
3517
- shadowed_names,
3518
- transform_context,
3519
- hook_result_names,
3520
- );
3521
- }
3522
- return;
3523
- }
3524
-
3525
- if (
3526
- node.type === 'AssignmentExpression' &&
3527
- expression_contains_hook_derived_value(node.right, transform_context, hook_result_names)
3528
- ) {
3529
- const outer_names = get_referenced_outer_binding_names(
3530
- node.left,
3531
- transform_context.available_bindings,
3532
- shadowed_names,
3533
- );
3534
- if (outer_names.length > 0) {
3535
- report_hook_outer_assignment_error(
3536
- node.left,
3537
- outer_names,
3538
- find_first_hook_call_name(node.right) || 'hook',
3539
- transform_context,
3540
- );
3541
- }
3542
- for (const name of get_referenced_local_binding_names(node.left, shadowed_names)) {
3543
- hook_result_names.add(name);
3544
- }
3545
- }
3546
-
3547
- if (is_for_of_control_node(node)) {
3548
- if (
3549
- node.left &&
3550
- node.left.type !== 'VariableDeclaration' &&
3551
- expression_contains_hook_derived_value(node.right, transform_context, hook_result_names)
3552
- ) {
3553
- const outer_names = get_referenced_outer_binding_names(
3554
- node.left,
3555
- transform_context.available_bindings,
3556
- shadowed_names,
3557
- );
3558
- if (outer_names.length > 0) {
3559
- report_hook_outer_assignment_error(
3560
- node.left,
3561
- outer_names,
3562
- find_first_hook_call_name(node.right) || 'hook',
3563
- transform_context,
3564
- );
3565
- }
3566
- for (const name of get_referenced_local_binding_names(node.left, shadowed_names)) {
3567
- hook_result_names.add(name);
3568
- }
3569
- }
3570
-
3571
- validate_hook_outer_assignments_in_node(
3572
- node.right,
3573
- shadowed_names,
3574
- transform_context,
3575
- hook_result_names,
3576
- );
3577
-
3578
- // Loop-declared bindings (`for (const x of …)`, `for (let x of …)`) live
3579
- // only in the body. They are deliberately NOT in the enclosing block's
3580
- // shadowed set (see collect_block_binding_names_from_statement), so add
3581
- // them just for the body recursion to keep references to the loop var
3582
- // from being flagged as outer-binding mutations.
3583
- const body_shadowed = new Set(shadowed_names);
3584
- if (node.left && node.left.type === 'VariableDeclaration') {
3585
- for (const declaration of node.left.declarations || []) {
3586
- collect_pattern_names(declaration.id, body_shadowed);
3587
- }
3588
- }
3589
- validate_hook_outer_assignments_in_node(
3590
- node.body,
3591
- body_shadowed,
3592
- transform_context,
3593
- hook_result_names,
3594
- );
3595
- return;
3596
- }
3597
-
3598
- for (const key of Object.keys(node)) {
3599
- if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
3600
- continue;
3601
- }
3602
- validate_hook_outer_assignments_in_node(
3603
- node[key],
3604
- shadowed_names,
3605
- transform_context,
3606
- hook_result_names,
3607
- );
3608
- }
3609
- }
3610
-
3611
- /**
3612
- * @param {any} call_node
3613
- * @param {Set<string>} shadowed_names
3614
- * @param {TransformContext} transform_context
3615
- * @returns {void}
3616
- */
3617
- function validate_hook_callback_outer_mutations(call_node, shadowed_names, transform_context) {
3618
- const hook_name = get_hook_callee_name(call_node.callee);
3619
- for (const argument of call_node.arguments || []) {
3620
- if (!is_function_or_component_node(argument)) {
3621
- continue;
3622
- }
3623
- const callback_shadowed_names = create_function_like_shadowed_names(argument, shadowed_names);
3624
- validate_hook_callback_outer_mutations_in_node(
3625
- argument.body,
3626
- callback_shadowed_names,
3627
- transform_context,
3628
- hook_name,
3629
- );
3630
- }
3631
- }
3632
-
3633
- /**
3634
- * @param {any} node
3635
- * @param {Set<string>} shadowed_names
3636
- * @returns {Set<string>}
3637
- */
3638
- function create_function_like_shadowed_names(node, shadowed_names) {
3639
- const next_shadowed_names = new Set(shadowed_names);
3640
- for (const param of node.params || []) {
3641
- collect_pattern_names(param, next_shadowed_names);
3642
- }
3643
- if (node.body?.type === 'BlockStatement') {
3644
- for (const name of collect_block_binding_names(node.body.body || [])) {
3645
- next_shadowed_names.add(name);
3646
- }
3647
- }
3648
- return next_shadowed_names;
3649
- }
3650
-
3651
- /**
3652
- * @param {any} node
3653
- * @param {Set<string>} shadowed_names
3654
- * @param {TransformContext} transform_context
3655
- * @param {string} hook_name
3656
- * @returns {void}
3657
- */
3658
- function validate_hook_callback_outer_mutations_in_node(
3659
- node,
3660
- shadowed_names,
3661
- transform_context,
3662
- hook_name,
3663
- ) {
3664
- if (!node || typeof node !== 'object') {
3665
- return;
3666
- }
3667
-
3668
- if (Array.isArray(node)) {
3669
- for (const child of node) {
3670
- validate_hook_callback_outer_mutations_in_node(
3671
- child,
3672
- shadowed_names,
3673
- transform_context,
3674
- hook_name,
3675
- );
3676
- }
3677
- return;
3678
- }
3679
-
3680
- if (is_function_or_component_node(node)) {
3681
- validate_hook_callback_outer_mutations_in_node(
3682
- node.body,
3683
- create_function_like_shadowed_names(node, shadowed_names),
3684
- transform_context,
3685
- hook_name,
3686
- );
3687
- return;
3688
- }
3689
-
3690
- if (node.type === 'BlockStatement') {
3691
- const next_shadowed_names = new Set(shadowed_names);
3692
- for (const name of collect_block_binding_names(node.body || [])) {
3693
- next_shadowed_names.add(name);
3694
- }
3695
- for (const child of node.body || []) {
3696
- validate_hook_callback_outer_mutations_in_node(
3697
- child,
3698
- next_shadowed_names,
3699
- transform_context,
3700
- hook_name,
3701
- );
3702
- }
3703
- return;
3704
- }
3705
-
3706
- if (node.type === 'AssignmentExpression') {
3707
- const outer_names = get_referenced_outer_binding_names(
3708
- node.left,
3709
- transform_context.available_bindings,
3710
- shadowed_names,
3711
- );
3712
- if (outer_names.length > 0) {
3713
- report_hook_callback_outer_mutation_error(
3714
- node.left,
3715
- outer_names,
3716
- hook_name,
3717
- transform_context,
3718
- );
3719
- }
3720
- }
3721
-
3722
- if (node.type === 'UpdateExpression') {
3723
- const outer_names = get_referenced_outer_binding_names(
3724
- node.argument,
3725
- transform_context.available_bindings,
3726
- shadowed_names,
3727
- );
3728
- if (outer_names.length > 0) {
3729
- report_hook_callback_outer_mutation_error(
3730
- node.argument,
3731
- outer_names,
3732
- hook_name,
3733
- transform_context,
3734
- );
3735
- }
3736
- }
3737
-
3738
- for (const key of Object.keys(node)) {
3739
- if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
3740
- continue;
3741
- }
3742
- if (key === 'left' && node.type === 'AssignmentExpression') {
3743
- continue;
3744
- }
3745
- if (key === 'argument' && node.type === 'UpdateExpression') {
3746
- continue;
3747
- }
3748
- validate_hook_callback_outer_mutations_in_node(
3749
- node[key],
3750
- shadowed_names,
3751
- transform_context,
3752
- hook_name,
3753
- );
3754
- }
3755
- }
3756
-
3757
- /**
3758
- * @param {any} node
3759
- * @param {TransformContext} transform_context
3760
- * @param {Set<string>} hook_result_names
3761
- * @returns {boolean}
3762
- */
3763
- function expression_contains_hook_derived_value(node, transform_context, hook_result_names) {
3764
- return (
3765
- node_contains_top_level_hook_call(node, false, transform_context, true) ||
3766
- references_name_in_set(node, hook_result_names)
3767
- );
3768
- }
3769
-
3770
- /**
3771
- * @param {any} node
3772
- * @param {Set<string>} names
3773
- * @returns {boolean}
3774
- */
3775
- function references_name_in_set(node, names) {
3776
- if (!node || typeof node !== 'object' || names.size === 0) {
3777
- return false;
3778
- }
3779
-
3780
- if (node.type === 'Identifier') {
3781
- return names.has(node.name);
3782
- }
3783
-
3784
- if (
3785
- node.type === 'FunctionDeclaration' ||
3786
- node.type === 'FunctionExpression' ||
3787
- node.type === 'ArrowFunctionExpression'
3788
- ) {
3789
- return false;
3790
- }
3791
-
3792
- if (Array.isArray(node)) {
3793
- return node.some((child) => references_name_in_set(child, names));
3794
- }
3795
-
3796
- for (const key of Object.keys(node)) {
3797
- if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
3798
- continue;
3799
- }
3800
- if (key === 'property' && node.type === 'MemberExpression' && !node.computed) {
3801
- continue;
3802
- }
3803
- if (key === 'key' && node.type === 'Property' && !node.computed && !node.shorthand) {
3804
- continue;
3805
- }
3806
- if (references_name_in_set(node[key], names)) {
3807
- return true;
3808
- }
3809
- }
3810
-
3811
- return false;
3812
- }
3813
-
3814
- /**
3815
- * @param {any} node
3816
- * @param {Set<string>} shadowed_names
3817
- * @returns {string[]}
3818
- */
3819
- function get_referenced_local_binding_names(node, shadowed_names) {
3820
- const names = new Set();
3821
- collect_referenced_local_binding_names(node, shadowed_names, names);
3822
- return [...names];
3823
- }
3824
-
3825
- /**
3826
- * @param {any} node
3827
- * @param {Set<string>} shadowed_names
3828
- * @param {Set<string>} names
3829
- * @returns {void}
3830
- */
3831
- function collect_referenced_local_binding_names(node, shadowed_names, names) {
3832
- if (!node || typeof node !== 'object') {
3833
- return;
3834
- }
3835
-
3836
- if (node.type === 'Identifier') {
3837
- if (shadowed_names.has(node.name)) {
3838
- names.add(node.name);
3839
- }
3840
- return;
3841
- }
3842
-
3843
- if (Array.isArray(node)) {
3844
- for (const child of node) {
3845
- collect_referenced_local_binding_names(child, shadowed_names, names);
3846
- }
3847
- return;
3848
- }
3849
-
3850
- for (const key of Object.keys(node)) {
3851
- if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
3852
- continue;
3853
- }
3854
- if (key === 'property' && node.type === 'MemberExpression' && !node.computed) {
3855
- continue;
3856
- }
3857
- if (key === 'key' && node.type === 'Property' && !node.computed && !node.shorthand) {
3858
- continue;
3859
- }
3860
- collect_referenced_local_binding_names(node[key], shadowed_names, names);
3298
+ const saved_inside_element_child = transform_context.inside_element_child;
3299
+ transform_context.inside_element_child = true;
3300
+ try {
3301
+ return [statement_body_to_jsx_child(children, transform_context)];
3302
+ } finally {
3303
+ transform_context.inside_element_child = saved_inside_element_child;
3861
3304
  }
3862
3305
  }
3863
3306
 
3864
3307
  /**
3865
- * @param {any} node
3866
- * @param {Map<string, AST.Identifier>} available_bindings
3867
- * @param {Set<string>} shadowed_names
3868
- * @returns {string[]}
3308
+ * @param {any[]} children
3309
+ * @returns {boolean}
3869
3310
  */
3870
- function get_referenced_outer_binding_names(node, available_bindings, shadowed_names) {
3871
- const names = new Set();
3872
- collect_referenced_outer_binding_names(node, available_bindings, shadowed_names, names);
3873
- return [...names];
3311
+ function children_contain_return_semantics(children) {
3312
+ return children.some(child_contains_return_semantics);
3874
3313
  }
3875
3314
 
3876
3315
  /**
3877
3316
  * @param {any} node
3878
- * @param {Map<string, AST.Identifier>} available_bindings
3879
- * @param {Set<string>} shadowed_names
3880
- * @param {Set<string>} names
3881
- * @returns {void}
3317
+ * @returns {boolean}
3882
3318
  */
3883
- function collect_referenced_outer_binding_names(node, available_bindings, shadowed_names, names) {
3319
+ function child_contains_return_semantics(node) {
3884
3320
  if (!node || typeof node !== 'object') {
3885
- return;
3321
+ return false;
3886
3322
  }
3887
3323
 
3888
- if (node.type === 'Identifier') {
3889
- if (available_bindings.has(node.name) && !shadowed_names.has(node.name)) {
3890
- names.add(node.name);
3891
- }
3892
- return;
3324
+ if (node.type === 'ReturnStatement') {
3325
+ return true;
3326
+ }
3327
+
3328
+ if (
3329
+ node.type === 'FunctionDeclaration' ||
3330
+ node.type === 'FunctionExpression' ||
3331
+ node.type === 'ArrowFunctionExpression'
3332
+ ) {
3333
+ return false;
3893
3334
  }
3894
3335
 
3895
3336
  if (Array.isArray(node)) {
3896
- for (const child of node) {
3897
- collect_referenced_outer_binding_names(child, available_bindings, shadowed_names, names);
3898
- }
3899
- return;
3337
+ return node.some(child_contains_return_semantics);
3900
3338
  }
3901
3339
 
3902
3340
  for (const key of Object.keys(node)) {
3903
3341
  if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
3904
3342
  continue;
3905
3343
  }
3906
- if (key === 'property' && node.type === 'MemberExpression' && !node.computed) {
3907
- continue;
3908
- }
3909
- if (key === 'key' && node.type === 'Property' && !node.computed && !node.shorthand) {
3910
- continue;
3344
+ if (child_contains_return_semantics(node[key])) {
3345
+ return true;
3911
3346
  }
3912
- collect_referenced_outer_binding_names(node[key], available_bindings, shadowed_names, names);
3913
3347
  }
3348
+
3349
+ return false;
3914
3350
  }
3915
3351
 
3916
3352
  /**
3917
3353
  * @param {any} node
3918
- * @returns {string | null}
3354
+ * @returns {boolean}
3919
3355
  */
3920
- function find_first_hook_call_name(node) {
3921
- if (!node || typeof node !== 'object') {
3922
- return null;
3923
- }
3924
-
3925
- if (node.type === 'CallExpression' && is_hook_callee(node.callee)) {
3926
- return get_hook_callee_name(node.callee);
3927
- }
3356
+ function is_inline_element_child(node) {
3357
+ return node && is_render_child_node(node);
3358
+ }
3928
3359
 
3360
+ /**
3361
+ * @param {any[]} body_nodes
3362
+ * @param {TransformContext} transform_context
3363
+ * @returns {ESTreeJSX.JSXExpressionContainer}
3364
+ */
3365
+ function statement_body_to_jsx_child(body_nodes, transform_context) {
3929
3366
  if (
3930
- node.type === 'FunctionDeclaration' ||
3931
- node.type === 'FunctionExpression' ||
3932
- node.type === 'ArrowFunctionExpression'
3367
+ should_extract_hook_helpers(transform_context) &&
3368
+ body_contains_top_level_hook_call(body_nodes, transform_context, true)
3933
3369
  ) {
3934
- return null;
3370
+ return hook_safe_statement_body_to_jsx_child(body_nodes, transform_context);
3935
3371
  }
3936
3372
 
3937
- if (Array.isArray(node)) {
3938
- for (const child of node) {
3939
- const name = find_first_hook_call_name(child);
3940
- if (name) return name;
3941
- }
3942
- return null;
3943
- }
3373
+ return to_jsx_expression_container(
3374
+ b.call(b.arrow([], b.block(build_render_statements(body_nodes, true, transform_context)))),
3375
+ );
3376
+ }
3944
3377
 
3945
- for (const key of Object.keys(node)) {
3946
- if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
3947
- continue;
3948
- }
3949
- const name = find_first_hook_call_name(node[key]);
3950
- if (name) return name;
3951
- }
3378
+ /**
3379
+ * @param {any[]} body_nodes
3380
+ * @param {TransformContext} transform_context
3381
+ * @returns {ESTreeJSX.JSXExpressionContainer}
3382
+ */
3383
+ function hook_safe_statement_body_to_jsx_child(body_nodes, transform_context) {
3384
+ const source_node = get_body_source_node(body_nodes);
3385
+ const helper = create_hook_safe_helper(body_nodes, undefined, source_node, transform_context);
3952
3386
 
3953
- return null;
3387
+ return to_jsx_expression_container(
3388
+ create_hook_safe_helper_iife(helper.setup_statements, helper.component_element),
3389
+ source_node,
3390
+ );
3954
3391
  }
3955
3392
 
3956
3393
  /**
3957
- * @param {any} callee
3394
+ * @param {TransformContext} transform_context
3958
3395
  * @returns {string}
3959
3396
  */
3960
- function get_hook_callee_name(callee) {
3961
- if (callee?.type === 'Identifier') {
3962
- return callee.name;
3397
+ function create_local_statement_component_name(transform_context) {
3398
+ transform_context.local_statement_component_index += 1;
3399
+ return `StatementBodyHook${transform_context.local_statement_component_index}`;
3400
+ }
3401
+
3402
+ /**
3403
+ * Wraps a list of body nodes into a component and returns
3404
+ * statements that return `<ComponentName prop1={prop1} ... />`.
3405
+ * Targets can either emit the helper component at module scope or cache the
3406
+ * component identity in module state while initializing it from the parent.
3407
+ * Used when a control flow branch contains hook calls that must be moved
3408
+ * into their own component boundary to satisfy the Rules of Hooks.
3409
+ *
3410
+ * @param {any[]} body_nodes
3411
+ * @param {any} key_expression - Optional key expression to add to the component element (for for-of loops)
3412
+ * @param {TransformContext} transform_context
3413
+ * @returns {any[]}
3414
+ */
3415
+ function hook_safe_render_statements(body_nodes, key_expression, transform_context) {
3416
+ const source_node = get_body_source_node(body_nodes);
3417
+ const helper = create_hook_safe_helper(
3418
+ body_nodes,
3419
+ key_expression,
3420
+ source_node,
3421
+ transform_context,
3422
+ );
3423
+ const statements = [...helper.setup_statements];
3424
+
3425
+ statements.push(b.return(helper.component_element));
3426
+
3427
+ return statements;
3428
+ }
3429
+
3430
+ /**
3431
+ * @param {any[]} body_nodes
3432
+ * @param {Map<string, AST.Identifier>} available_bindings
3433
+ * @returns {AST.Identifier[]}
3434
+ */
3435
+ function get_referenced_helper_bindings(body_nodes, available_bindings) {
3436
+ const helper_bindings = [];
3437
+ const local_bindings = new Map();
3438
+
3439
+ for (const node of body_nodes) {
3440
+ collect_statement_bindings(node, local_bindings);
3963
3441
  }
3964
- if (
3965
- callee?.type === 'MemberExpression' &&
3966
- !callee.computed &&
3967
- callee.property?.type === 'Identifier'
3968
- ) {
3969
- return callee.property.name;
3442
+
3443
+ for (const [name, binding] of available_bindings) {
3444
+ if (local_bindings.has(name)) continue;
3445
+
3446
+ if (references_scope_bindings(body_nodes, new Map([[name, binding]]))) {
3447
+ helper_bindings.push(binding);
3448
+ }
3970
3449
  }
3971
- return 'hook';
3450
+
3451
+ return helper_bindings;
3972
3452
  }
3973
3453
 
3974
3454
  /**
@@ -3989,11 +3469,6 @@ export function create_hook_safe_helper(
3989
3469
  preallocated_helper_id,
3990
3470
  options = {},
3991
3471
  ) {
3992
- validate_hook_safe_body_does_not_assign_hook_results_to_outer_bindings(
3993
- body_nodes,
3994
- transform_context,
3995
- );
3996
-
3997
3472
  const helper_id =
3998
3473
  preallocated_helper_id ??
3999
3474
  create_generated_identifier(create_local_statement_component_name(transform_context));
@@ -4314,31 +3789,38 @@ function is_native_tsrx_node(node) {
4314
3789
  * @param {any} node
4315
3790
  * @returns {boolean}
4316
3791
  */
4317
- function is_dynamic_jsx_element(node) {
4318
- return !!(
4319
- node?.type === 'JSXElement' &&
4320
- (node.dynamic === true ||
4321
- node.openingElement?.dynamic === true ||
4322
- is_dynamic_jsx_name(node.openingElement?.name))
4323
- );
4324
- }
4325
-
4326
- /**
4327
- * @param {any} name
4328
- * @returns {boolean}
4329
- */
4330
- function is_dynamic_jsx_name(name) {
4331
- if (!name || typeof name !== 'object') return false;
4332
- if (name.dynamic === true) return true;
4333
- return name.type === 'JSXMemberExpression' && is_dynamic_jsx_name(name.object);
3792
+ function is_if_control_node(node) {
3793
+ return node?.type === 'IfStatement' || node?.type === 'JSXIfExpression';
4334
3794
  }
4335
3795
 
4336
3796
  /**
4337
3797
  * @param {any} node
4338
3798
  * @returns {boolean}
4339
3799
  */
4340
- function is_if_control_node(node) {
4341
- return node?.type === 'IfStatement' || node?.type === 'JSXIfExpression';
3800
+ function is_render_child_node(node) {
3801
+ if (!node) return false;
3802
+
3803
+ switch (node.type) {
3804
+ case 'JSXElement':
3805
+ case 'JSXFragment':
3806
+ case 'JSXExpressionContainer':
3807
+ case 'JSXText':
3808
+ case 'JSXIfExpression':
3809
+ case 'JSXForExpression':
3810
+ case 'JSXSwitchExpression':
3811
+ case 'JSXTryExpression':
3812
+ return true;
3813
+ case 'IfStatement':
3814
+ return is_template_if_node(node);
3815
+ case 'ForOfStatement':
3816
+ return is_template_for_of_node(node);
3817
+ case 'SwitchStatement':
3818
+ return is_template_switch_node(node);
3819
+ case 'TryStatement':
3820
+ return is_template_try_node(node);
3821
+ default:
3822
+ return false;
3823
+ }
4342
3824
  }
4343
3825
 
4344
3826
  /**
@@ -4357,17 +3839,6 @@ function is_try_control_node(node) {
4357
3839
  return node?.type === 'TryStatement' || node?.type === 'JSXTryExpression';
4358
3840
  }
4359
3841
 
4360
- /**
4361
- * @param {any} node
4362
- * @returns {boolean}
4363
- */
4364
- function is_for_of_control_node(node) {
4365
- return (
4366
- node?.type === 'ForOfStatement' ||
4367
- (node?.type === 'JSXForExpression' && node.statementType === 'ForOfStatement')
4368
- );
4369
- }
4370
-
4371
3842
  /**
4372
3843
  * @param {any} node
4373
3844
  * @param {TransformContext} transform_context
@@ -4380,9 +3851,6 @@ function to_jsx_child(node, transform_context) {
4380
3851
  if (is_native_tsrx_node(node)) {
4381
3852
  return to_jsx_element(node, transform_context, node.children || [], true);
4382
3853
  }
4383
- if (is_dynamic_jsx_element(node)) {
4384
- return dynamic_element_to_jsx(node, transform_context, true);
4385
- }
4386
3854
  return node;
4387
3855
  case 'JSXFragment':
4388
3856
  if (is_native_tsrx_node(node)) {
@@ -4391,6 +3859,9 @@ function to_jsx_child(node, transform_context) {
4391
3859
  return node;
4392
3860
  case 'JSXIfExpression':
4393
3861
  case 'IfStatement':
3862
+ if (node.type === 'IfStatement' && !is_template_if_node(node)) {
3863
+ return node;
3864
+ }
4394
3865
  if (node.metadata?.generated_loop_skip_if) {
4395
3866
  return node;
4396
3867
  }
@@ -4412,17 +3883,26 @@ function to_jsx_child(node, transform_context) {
4412
3883
  transform_context.platform.hooks?.controlFlow?.forOf ?? for_of_statement_to_jsx_child
4413
3884
  )(jsx_control_expression_to_statement(node), transform_context);
4414
3885
  case 'ForOfStatement':
3886
+ if (!is_template_for_of_node(node)) {
3887
+ return node;
3888
+ }
4415
3889
  return (
4416
3890
  transform_context.platform.hooks?.controlFlow?.forOf ?? for_of_statement_to_jsx_child
4417
3891
  )(node, transform_context);
4418
3892
  case 'JSXSwitchExpression':
4419
3893
  case 'SwitchStatement':
3894
+ if (node.type === 'SwitchStatement' && !is_template_switch_node(node)) {
3895
+ return node;
3896
+ }
4420
3897
  return (
4421
3898
  transform_context.platform.hooks?.controlFlow?.switchStatement ??
4422
3899
  switch_statement_to_jsx_child
4423
3900
  )(jsx_control_expression_to_statement(node), transform_context);
4424
3901
  case 'JSXTryExpression':
4425
3902
  case 'TryStatement':
3903
+ if (node.type === 'TryStatement' && !is_template_try_node(node)) {
3904
+ return node;
3905
+ }
4426
3906
  return (
4427
3907
  transform_context.platform.hooks?.controlFlow?.tryStatement ?? try_statement_to_jsx_child
4428
3908
  )(jsx_control_expression_to_statement(node), transform_context);
@@ -4910,6 +4390,42 @@ function is_template_if_node(node) {
4910
4390
  );
4911
4391
  }
4912
4392
 
4393
+ /**
4394
+ * @param {any} node
4395
+ * @returns {boolean}
4396
+ */
4397
+ function is_template_for_of_node(node) {
4398
+ return (
4399
+ node?.type === 'JSXForExpression' ||
4400
+ node?.metadata?.tsrxDirective === 'for' ||
4401
+ (node?.type === 'ForOfStatement' && node?.statementType === 'ForOfStatement')
4402
+ );
4403
+ }
4404
+
4405
+ /**
4406
+ * @param {any} node
4407
+ * @returns {boolean}
4408
+ */
4409
+ function is_template_switch_node(node) {
4410
+ return (
4411
+ node?.type === 'JSXSwitchExpression' ||
4412
+ node?.metadata?.tsrxDirective === 'switch' ||
4413
+ (node?.type === 'SwitchStatement' && node?.statementType === 'SwitchStatement')
4414
+ );
4415
+ }
4416
+
4417
+ /**
4418
+ * @param {any} node
4419
+ * @returns {boolean}
4420
+ */
4421
+ function is_template_try_node(node) {
4422
+ return (
4423
+ node?.type === 'JSXTryExpression' ||
4424
+ node?.metadata?.tsrxDirective === 'try' ||
4425
+ (node?.type === 'TryStatement' && node?.statementType === 'TryStatement')
4426
+ );
4427
+ }
4428
+
4913
4429
  /**
4914
4430
  * @param {any} node
4915
4431
  * @returns {boolean}
@@ -5273,16 +4789,9 @@ function try_statement_to_jsx_child(node, transform_context) {
5273
4789
  // correctly identifies references to err/reset as non-static
5274
4790
  const saved_catch_bindings = transform_context.available_bindings;
5275
4791
  transform_context.available_bindings = new Map(saved_catch_bindings);
5276
- const catch_scoped_names = new Set();
5277
4792
  for (const param of catch_params) {
5278
4793
  collect_pattern_bindings(param, transform_context.available_bindings);
5279
- collect_pattern_names(param, catch_scoped_names);
5280
4794
  }
5281
- validate_hook_safe_body_does_not_assign_hook_results_to_outer_bindings(
5282
- catch_body_nodes,
5283
- transform_context,
5284
- catch_scoped_names,
5285
- );
5286
4795
 
5287
4796
  const fallback_fn = b.arrow(
5288
4797
  catch_params,
@@ -5707,7 +5216,7 @@ function build_switch_with_lift(switch_node, transform_context) {
5707
5216
  has_terminal = true;
5708
5217
  break;
5709
5218
  }
5710
- if (is_jsx_child(child)) {
5219
+ if (is_render_child_node(child)) {
5711
5220
  render_nodes.push(to_jsx_child(child, transform_context));
5712
5221
  } else if (is_bare_render_expression(child)) {
5713
5222
  render_nodes.push(to_jsx_expression_container(child, child));
@@ -6266,57 +5775,6 @@ function value_has_unmappable_jsx_loc(value) {
6266
5775
  );
6267
5776
  }
6268
5777
 
6269
- /**
6270
- * @param {any} node
6271
- * @param {TransformContext} transform_context
6272
- * @param {boolean} in_jsx_child
6273
- * @returns {any}
6274
- */
6275
- function dynamic_element_to_jsx(node, transform_context, in_jsx_child) {
6276
- const source_name = node.openingElement?.name;
6277
- const dynamic_id = set_loc(create_generated_identifier('DynamicElement'), source_name || node);
6278
- const alias_declaration = set_loc(
6279
- b.const(dynamic_id, jsx_name_to_expression(source_name)),
6280
- source_name || node,
6281
- );
6282
- const jsx_element = create_dynamic_jsx_element(dynamic_id, node, transform_context);
6283
-
6284
- const expression = b.call(
6285
- b.arrow(
6286
- [],
6287
- b.block([
6288
- alias_declaration,
6289
- b.return(b.conditional(clone_identifier(dynamic_id), jsx_element, create_null_literal())),
6290
- ]),
6291
- ),
6292
- );
6293
-
6294
- return in_jsx_child ? to_jsx_expression_container(expression, node) : set_loc(expression, node);
6295
- }
6296
-
6297
- /**
6298
- * @param {AST.Identifier} dynamic_id
6299
- * @param {any} node
6300
- * @param {TransformContext} transform_context
6301
- * @returns {ESTreeJSX.JSXElement}
6302
- */
6303
- function create_dynamic_jsx_element(dynamic_id, node, transform_context) {
6304
- const attributes = transform_element_attributes_dispatch(
6305
- node.openingElement?.attributes || [],
6306
- transform_context,
6307
- node,
6308
- );
6309
- const selfClosing = !!node.openingElement?.selfClosing;
6310
- const children = create_element_children(node.children || [], transform_context);
6311
- const name = identifier_to_jsx_name(clone_identifier(dynamic_id));
6312
-
6313
- return b.jsx_element_fresh(
6314
- b.jsx_opening_element(name, attributes, selfClosing),
6315
- selfClosing ? null : b.jsx_closing_element(clone_jsx_name(name)),
6316
- children,
6317
- );
6318
- }
6319
-
6320
5778
  /**
6321
5779
  * @param {any[]} render_nodes
6322
5780
  * @returns {any}