@tsrx/core 0.1.4 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +39 -6
- package/src/diagnostics.js +1 -0
- package/src/index.js +8 -2
- package/src/plugin.js +31 -1
- package/src/runtime/events.js +10 -0
- package/src/runtime/hash.js +1 -0
- package/src/runtime/html.js +3 -0
- package/src/runtime/iterable.js +110 -0
- package/src/source-map-utils.js +19 -2
- package/src/transform/jsx/ast-builders.js +120 -0
- package/src/transform/jsx/index.js +632 -1415
- package/src/transform/lazy.js +301 -205
- package/src/transform/scoping.js +9 -45
- package/src/transform/segments.js +164 -11
- package/src/utils/ast.js +13 -0
- package/src/utils/builders.js +51 -13
- package/src/utils/dom.js +158 -0
- package/src/utils.js +6 -159
- package/types/index.d.ts +1 -0
- package/types/jsx-platform.d.ts +12 -0
- package/types/runtime/events.d.ts +11 -0
- package/types/runtime/hash.d.ts +2 -0
- package/types/runtime/html.d.ts +4 -0
- package/types/runtime/iterable.d.ts +13 -0
- package/types/runtime/language-helpers.d.ts +17 -0
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
get_for_of_iteration_params,
|
|
22
22
|
identifier_to_jsx_name,
|
|
23
23
|
is_bare_render_expression,
|
|
24
|
+
is_component_jsx_name,
|
|
24
25
|
is_dynamic_element_id,
|
|
25
26
|
is_jsx_child,
|
|
26
27
|
set_loc,
|
|
@@ -33,12 +34,7 @@ import {
|
|
|
33
34
|
jsx_id as build_jsx_id,
|
|
34
35
|
} from '../../utils/builders.js';
|
|
35
36
|
import * as b from '../../utils/builders.js';
|
|
36
|
-
import {
|
|
37
|
-
apply_lazy_transforms,
|
|
38
|
-
collect_lazy_bindings_from_component,
|
|
39
|
-
preallocate_lazy_ids,
|
|
40
|
-
replace_lazy_params,
|
|
41
|
-
} from '../lazy.js';
|
|
37
|
+
import { apply_lazy_transforms, preallocate_lazy_ids } from '../lazy.js';
|
|
42
38
|
import { find_first_top_level_await_in_component_body } from '../await.js';
|
|
43
39
|
import { prepare_stylesheet_for_render, annotate_component_with_hash } from '../scoping.js';
|
|
44
40
|
import {
|
|
@@ -49,7 +45,7 @@ import {
|
|
|
49
45
|
validate_component_return_statement,
|
|
50
46
|
validate_component_unsupported_loop_statement,
|
|
51
47
|
} from '../../analyze/validation.js';
|
|
52
|
-
import { get_component_from_path } from '../../utils/ast.js';
|
|
48
|
+
import { get_component_from_path, is_function_or_component_node } from '../../utils/ast.js';
|
|
53
49
|
import {
|
|
54
50
|
is_interleaved_body as is_interleaved_body_core,
|
|
55
51
|
is_capturable_jsx_child,
|
|
@@ -245,6 +241,8 @@ export function createJsxTransform(platform) {
|
|
|
245
241
|
needs_ref_prop: false,
|
|
246
242
|
needs_normalize_spread_props: false,
|
|
247
243
|
needs_fragment: false,
|
|
244
|
+
needs_for_of_iterable: false,
|
|
245
|
+
needs_iteration_value_type: false,
|
|
248
246
|
module_scoped_hook_components:
|
|
249
247
|
options?.moduleScopedHookComponents ?? !!platform.hooks?.moduleScopedHookComponents,
|
|
250
248
|
helper_state: null,
|
|
@@ -515,7 +513,7 @@ export function createJsxTransform(platform) {
|
|
|
515
513
|
const value = state.current_css_hash
|
|
516
514
|
? `${state.current_css_hash} ${class_name}`
|
|
517
515
|
: class_name;
|
|
518
|
-
return
|
|
516
|
+
return b.literal(value, undefined, node);
|
|
519
517
|
},
|
|
520
518
|
|
|
521
519
|
// Default .metadata on every function-like node so downstream consumers
|
|
@@ -643,15 +641,6 @@ export function component_to_function_declaration(component, transform_context,
|
|
|
643
641
|
// Collect param bindings from original patterns (lazy patterns still intact).
|
|
644
642
|
const param_bindings = collect_param_bindings(params);
|
|
645
643
|
|
|
646
|
-
// Collect lazy binding info WITHOUT mutating patterns. Stores lazy_id on metadata
|
|
647
|
-
// for later replacement. Body bindings (count, setCount, etc.) are still in the
|
|
648
|
-
// original patterns, so collect_statement_bindings during build will find them.
|
|
649
|
-
// In type-only mode the lazy rewrite is skipped entirely so destructuring
|
|
650
|
-
// patterns survive into the virtual TSX and TypeScript can flow real types.
|
|
651
|
-
const lazy_bindings = transform_context.typeOnly
|
|
652
|
-
? new Map()
|
|
653
|
-
: collect_lazy_bindings_from_component(params, body, transform_context);
|
|
654
|
-
|
|
655
644
|
// Save and set context for this component scope
|
|
656
645
|
const saved_helper_state = transform_context.helper_state;
|
|
657
646
|
const saved_bindings = transform_context.available_bindings;
|
|
@@ -659,66 +648,44 @@ export function component_to_function_declaration(component, transform_context,
|
|
|
659
648
|
transform_context.available_bindings = new Map(param_bindings);
|
|
660
649
|
|
|
661
650
|
const body_statements = build_component_statements(body, transform_context);
|
|
662
|
-
|
|
663
|
-
// Replace lazy param patterns with generated identifiers
|
|
664
|
-
const final_params = lazy_bindings.size > 0 ? replace_lazy_params(params) : params;
|
|
665
|
-
|
|
666
|
-
// Wrap body_statements in a BlockStatement so that apply_lazy_transforms
|
|
667
|
-
// runs collect_block_shadowed_names and detects body-level declarations
|
|
668
|
-
// (e.g. `const name = ...`) that shadow lazy binding names.
|
|
669
|
-
const body_block = /** @type {any} */ ({
|
|
670
|
-
type: 'BlockStatement',
|
|
671
|
-
body: body_statements,
|
|
672
|
-
metadata: { path: [] },
|
|
673
|
-
});
|
|
674
|
-
const final_body =
|
|
675
|
-
lazy_bindings.size > 0 ? apply_lazy_transforms(body_block, lazy_bindings) : body_block;
|
|
651
|
+
const body_block = b.block(body_statements);
|
|
676
652
|
|
|
677
653
|
/** @type {AST.FunctionDeclaration | AST.FunctionExpression | AST.ArrowFunctionExpression} */
|
|
678
654
|
let fn;
|
|
679
655
|
|
|
680
656
|
if (component.id) {
|
|
681
|
-
fn =
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
generator: false,
|
|
689
|
-
metadata: {
|
|
690
|
-
path: [],
|
|
691
|
-
is_component: true,
|
|
692
|
-
},
|
|
693
|
-
});
|
|
657
|
+
fn = b.function_declaration(
|
|
658
|
+
component.id,
|
|
659
|
+
params,
|
|
660
|
+
body_block,
|
|
661
|
+
is_async_component,
|
|
662
|
+
component.typeParameters,
|
|
663
|
+
);
|
|
694
664
|
} else if (component.metadata?.arrow) {
|
|
695
|
-
fn =
|
|
696
|
-
type: 'ArrowFunctionExpression',
|
|
697
|
-
typeParameters: component.typeParameters,
|
|
698
|
-
params: final_params,
|
|
699
|
-
body: final_body,
|
|
700
|
-
async: is_async_component,
|
|
701
|
-
generator: false,
|
|
702
|
-
expression: false,
|
|
703
|
-
metadata: {
|
|
704
|
-
path: [],
|
|
705
|
-
is_component: true,
|
|
706
|
-
},
|
|
707
|
-
});
|
|
665
|
+
fn = b.arrow(params, body_block, is_async_component, component.typeParameters);
|
|
708
666
|
} else {
|
|
709
|
-
fn =
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
667
|
+
fn = b.function(null, params, body_block, is_async_component, component.typeParameters);
|
|
668
|
+
}
|
|
669
|
+
/** @type {any} */ (fn.metadata).is_component = true;
|
|
670
|
+
|
|
671
|
+
// `preallocate_lazy_ids` stamped `has_lazy_descendants` on the source
|
|
672
|
+
// `Component` node; the freshly-built `fn` shares the same params/body
|
|
673
|
+
// subtree, so the flag is equally applicable. Propagating it lets
|
|
674
|
+
// `apply_lazy_transforms` honor its constant-time early-return path.
|
|
675
|
+
if (/** @type {any} */ (component).metadata?.has_lazy_descendants) {
|
|
676
|
+
/** @type {any} */ (fn.metadata).has_lazy_descendants = true;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Apply lazy `&{}` / `&[]` rewrites end-to-end: the function-handler in
|
|
680
|
+
// `apply_lazy_transforms` collects param bindings, merges with body bindings
|
|
681
|
+
// discovered by the BlockStatement handler, replaces lazy params with their
|
|
682
|
+
// `__lazyN` ids, and rewrites every reference. Constant-time fast-path for
|
|
683
|
+
// functions whose subtrees contain no lazy patterns (flagged ahead of time
|
|
684
|
+
// by `preallocate_lazy_ids`). In type-only mode the rewrite is skipped so
|
|
685
|
+
// destructuring patterns survive into the virtual TSX and TypeScript can
|
|
686
|
+
// flow real types.
|
|
687
|
+
if (!transform_context.typeOnly) {
|
|
688
|
+
fn = /** @type {typeof fn} */ (apply_lazy_transforms(fn, new Map()));
|
|
722
689
|
}
|
|
723
690
|
|
|
724
691
|
// Restore context
|
|
@@ -889,25 +856,16 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
889
856
|
if (stmt.type === 'ReturnStatement') {
|
|
890
857
|
if (stmt.argument) {
|
|
891
858
|
render_nodes.push(
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
type: 'Literal',
|
|
900
|
-
value: null,
|
|
901
|
-
raw: 'null',
|
|
902
|
-
metadata: { path: [] },
|
|
903
|
-
},
|
|
904
|
-
alternate: stmt.argument,
|
|
905
|
-
metadata: { path: [] },
|
|
906
|
-
}),
|
|
859
|
+
b.jsx_expression_container(
|
|
860
|
+
set_loc(
|
|
861
|
+
b.conditional(
|
|
862
|
+
clone_expression_node(child.test),
|
|
863
|
+
b.literal(null),
|
|
864
|
+
stmt.argument,
|
|
865
|
+
),
|
|
907
866
|
child,
|
|
908
867
|
),
|
|
909
|
-
|
|
910
|
-
}),
|
|
868
|
+
),
|
|
911
869
|
);
|
|
912
870
|
}
|
|
913
871
|
} else {
|
|
@@ -928,26 +886,6 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
928
886
|
continue;
|
|
929
887
|
}
|
|
930
888
|
|
|
931
|
-
if (
|
|
932
|
-
child.type === 'IfStatement' &&
|
|
933
|
-
!child.alternate &&
|
|
934
|
-
!is_returning_if_statement(child) &&
|
|
935
|
-
!transform_context.platform.hooks?.isTopLevelSetupCall &&
|
|
936
|
-
body_contains_top_level_hook_call([child], transform_context, true) &&
|
|
937
|
-
i + 1 < body_nodes.length
|
|
938
|
-
) {
|
|
939
|
-
statements.push(
|
|
940
|
-
...create_continuation_lift_if_statement(
|
|
941
|
-
child,
|
|
942
|
-
body_nodes.slice(i + 1),
|
|
943
|
-
render_nodes,
|
|
944
|
-
transform_context,
|
|
945
|
-
),
|
|
946
|
-
);
|
|
947
|
-
transform_context.available_bindings = saved_bindings;
|
|
948
|
-
return statements;
|
|
949
|
-
}
|
|
950
|
-
|
|
951
889
|
if (
|
|
952
890
|
child.type === 'ForOfStatement' &&
|
|
953
891
|
!child.await &&
|
|
@@ -959,26 +897,9 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
959
897
|
true,
|
|
960
898
|
)
|
|
961
899
|
) {
|
|
962
|
-
const
|
|
963
|
-
const hoisted = build_hoisted_for_of_with_hooks(
|
|
964
|
-
child,
|
|
965
|
-
for_of_continuation,
|
|
966
|
-
transform_context,
|
|
967
|
-
);
|
|
900
|
+
const hoisted = build_hoisted_for_of_with_hooks(child, transform_context);
|
|
968
901
|
if (hoisted) {
|
|
969
902
|
statements.push(...hoisted.hoist_statements);
|
|
970
|
-
if (for_of_continuation.length > 0) {
|
|
971
|
-
// Tail was lifted into the helper; everything after the for-of
|
|
972
|
-
// now lives there. Combine prior render_nodes with the iteration
|
|
973
|
-
// JSX and return.
|
|
974
|
-
statements.push({
|
|
975
|
-
type: 'ReturnStatement',
|
|
976
|
-
argument: combine_render_return_argument(render_nodes, hoisted.jsx_child),
|
|
977
|
-
metadata: { path: [] },
|
|
978
|
-
});
|
|
979
|
-
transform_context.available_bindings = saved_bindings;
|
|
980
|
-
return statements;
|
|
981
|
-
}
|
|
982
903
|
if (interleaved && is_capturable_jsx_child(hoisted.jsx_child)) {
|
|
983
904
|
const { declaration, reference } = captureJsxChild(hoisted.jsx_child, capture_index++);
|
|
984
905
|
statements.push(declaration);
|
|
@@ -990,43 +911,6 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
990
911
|
}
|
|
991
912
|
}
|
|
992
913
|
|
|
993
|
-
if (
|
|
994
|
-
child.type === 'TryStatement' &&
|
|
995
|
-
!child.finalizer &&
|
|
996
|
-
!transform_context.platform.hooks?.isTopLevelSetupCall &&
|
|
997
|
-
try_statement_contains_hooks(child, transform_context) &&
|
|
998
|
-
i + 1 < body_nodes.length
|
|
999
|
-
) {
|
|
1000
|
-
statements.push(
|
|
1001
|
-
...create_continuation_lift_try_statement(
|
|
1002
|
-
child,
|
|
1003
|
-
body_nodes.slice(i + 1),
|
|
1004
|
-
render_nodes,
|
|
1005
|
-
transform_context,
|
|
1006
|
-
),
|
|
1007
|
-
);
|
|
1008
|
-
transform_context.available_bindings = saved_bindings;
|
|
1009
|
-
return statements;
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
if (
|
|
1013
|
-
child.type === 'SwitchStatement' &&
|
|
1014
|
-
!transform_context.platform.hooks?.isTopLevelSetupCall &&
|
|
1015
|
-
body_contains_top_level_hook_call([child], transform_context, true) &&
|
|
1016
|
-
i + 1 < body_nodes.length
|
|
1017
|
-
) {
|
|
1018
|
-
statements.push(
|
|
1019
|
-
...create_continuation_lift_switch_statement(
|
|
1020
|
-
child,
|
|
1021
|
-
body_nodes.slice(i + 1),
|
|
1022
|
-
render_nodes,
|
|
1023
|
-
transform_context,
|
|
1024
|
-
),
|
|
1025
|
-
);
|
|
1026
|
-
transform_context.available_bindings = saved_bindings;
|
|
1027
|
-
return statements;
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
914
|
if (is_jsx_child(child)) {
|
|
1031
915
|
const jsx = to_jsx_child(child, transform_context);
|
|
1032
916
|
statements.push(...extract_jsx_setup_declarations(jsx));
|
|
@@ -1051,10 +935,7 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
1051
935
|
|
|
1052
936
|
const return_arg = build_return_expression(render_nodes);
|
|
1053
937
|
if (return_arg || (return_null_when_empty && !has_terminal_return)) {
|
|
1054
|
-
statements.push(
|
|
1055
|
-
type: 'ReturnStatement',
|
|
1056
|
-
argument: return_arg || { type: 'Literal', value: null, raw: 'null' },
|
|
1057
|
-
});
|
|
938
|
+
statements.push(b.return(return_arg || b.literal(null)));
|
|
1058
939
|
}
|
|
1059
940
|
|
|
1060
941
|
transform_context.available_bindings = saved_bindings;
|
|
@@ -1254,16 +1135,7 @@ function create_helper_props_property(binding) {
|
|
|
1254
1135
|
const key = create_generated_identifier(binding.name);
|
|
1255
1136
|
const value = create_generated_identifier(binding.name);
|
|
1256
1137
|
|
|
1257
|
-
return
|
|
1258
|
-
type: 'Property',
|
|
1259
|
-
key,
|
|
1260
|
-
value,
|
|
1261
|
-
kind: 'init',
|
|
1262
|
-
method: false,
|
|
1263
|
-
shorthand: true,
|
|
1264
|
-
computed: false,
|
|
1265
|
-
metadata: { path: [] },
|
|
1266
|
-
});
|
|
1138
|
+
return b.prop('init', key, value, false, true);
|
|
1267
1139
|
}
|
|
1268
1140
|
|
|
1269
1141
|
/**
|
|
@@ -1499,7 +1371,7 @@ function create_module_scoped_hook_component_id(helper_id, transform_context) {
|
|
|
1499
1371
|
* @param {any[]} params
|
|
1500
1372
|
* @returns {Map<string, AST.Identifier>}
|
|
1501
1373
|
*/
|
|
1502
|
-
function collect_param_bindings(params) {
|
|
1374
|
+
export function collect_param_bindings(params) {
|
|
1503
1375
|
const bindings = new Map();
|
|
1504
1376
|
for (const param of params) {
|
|
1505
1377
|
collect_pattern_bindings(param, bindings);
|
|
@@ -1512,7 +1384,7 @@ function collect_param_bindings(params) {
|
|
|
1512
1384
|
* @param {Map<string, AST.Identifier>} bindings
|
|
1513
1385
|
* @returns {void}
|
|
1514
1386
|
*/
|
|
1515
|
-
function collect_statement_bindings(statement, bindings) {
|
|
1387
|
+
export function collect_statement_bindings(statement, bindings) {
|
|
1516
1388
|
if (!statement) return;
|
|
1517
1389
|
|
|
1518
1390
|
if (statement.type === 'VariableDeclaration') {
|
|
@@ -1646,6 +1518,16 @@ function hoist_static_render_nodes(render_nodes, transform_context) {
|
|
|
1646
1518
|
const node = render_nodes[i];
|
|
1647
1519
|
if (node.type !== 'JSXElement') continue;
|
|
1648
1520
|
if (!is_hoist_safe_jsx_node(node)) continue;
|
|
1521
|
+
if (is_bare_component_invocation(node)) {
|
|
1522
|
+
// `<Helper />` with no attributes and no children is just an
|
|
1523
|
+
// invocation reference — most often a generated `StatementBodyHook`
|
|
1524
|
+
// chain element we emitted ourselves. Hoisting it would produce
|
|
1525
|
+
// `const App__staticN = <Helper />` aliases that bloat the output
|
|
1526
|
+
// without enabling React's element-identity fast path (the helper
|
|
1527
|
+
// isn't memoized, so the parent re-invokes it every render either
|
|
1528
|
+
// way). Inline the reference at the call site instead.
|
|
1529
|
+
continue;
|
|
1530
|
+
}
|
|
1649
1531
|
if (
|
|
1650
1532
|
transform_context.platform.hooks?.canHoistStaticNode &&
|
|
1651
1533
|
!transform_context.platform.hooks.canHoistStaticNode(node, transform_context)
|
|
@@ -1663,6 +1545,23 @@ function hoist_static_render_nodes(render_nodes, transform_context) {
|
|
|
1663
1545
|
}
|
|
1664
1546
|
}
|
|
1665
1547
|
|
|
1548
|
+
/**
|
|
1549
|
+
* `<Helper />` shape with no attributes and no children. The opening element
|
|
1550
|
+
* name must be component-shaped (see `is_component_jsx_name`) — lowercase
|
|
1551
|
+
* identifiers are host DOM tags, which *do* benefit from hoisting because
|
|
1552
|
+
* React diffs them against the previous render.
|
|
1553
|
+
*
|
|
1554
|
+
* @param {any} node
|
|
1555
|
+
* @returns {boolean}
|
|
1556
|
+
*/
|
|
1557
|
+
function is_bare_component_invocation(node) {
|
|
1558
|
+
if (!node || node.type !== 'JSXElement') return false;
|
|
1559
|
+
const opening = node.openingElement;
|
|
1560
|
+
if (!opening || opening.attributes.length > 0) return false;
|
|
1561
|
+
if (node.children.length > 0) return false;
|
|
1562
|
+
return is_component_jsx_name(opening.name);
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1666
1565
|
/**
|
|
1667
1566
|
* Static JSX that appears before multiple early-return guards is otherwise
|
|
1668
1567
|
* cloned into every generated return. Capture it once at its source position
|
|
@@ -1891,169 +1790,6 @@ function create_component_returning_if_statement(node, render_nodes, transform_c
|
|
|
1891
1790
|
return set_loc(b.if(node.test, set_loc(b.block(branch_statements), node.consequent), null), node);
|
|
1892
1791
|
}
|
|
1893
1792
|
|
|
1894
|
-
/* ---------------------------------------------------------------------- *
|
|
1895
|
-
* Continuation-lift primitives shared across if / switch / try / for-of *
|
|
1896
|
-
* ---------------------------------------------------------------------- */
|
|
1897
|
-
|
|
1898
|
-
/**
|
|
1899
|
-
* Build the helper component that owns the post-control-flow continuation.
|
|
1900
|
-
* Same shape as `create_hook_safe_helper`; named for intent at lift call sites.
|
|
1901
|
-
*
|
|
1902
|
-
* @param {any[]} continuation_body
|
|
1903
|
-
* @param {any} source_node
|
|
1904
|
-
* @param {TransformContext} transform_context
|
|
1905
|
-
* @returns {{ setup_statements: any[], component_element: ESTreeJSX.JSXElement }}
|
|
1906
|
-
*/
|
|
1907
|
-
function build_tail_helper(continuation_body, source_node, transform_context) {
|
|
1908
|
-
return create_hook_safe_helper(continuation_body, undefined, source_node, transform_context);
|
|
1909
|
-
}
|
|
1910
|
-
|
|
1911
|
-
/**
|
|
1912
|
-
* Clone the tail helper's component element for embedding inside another
|
|
1913
|
-
* branch's body. Loses location info because the same element appears in
|
|
1914
|
-
* multiple positions and downstream tooling treats AST nodes as identity-keyed.
|
|
1915
|
-
*
|
|
1916
|
-
* @param {{ component_element: ESTreeJSX.JSXElement }} tail_helper
|
|
1917
|
-
* @returns {any}
|
|
1918
|
-
*/
|
|
1919
|
-
function clone_tail_invocation(tail_helper) {
|
|
1920
|
-
return clone_expression_node(tail_helper.component_element, false);
|
|
1921
|
-
}
|
|
1922
|
-
|
|
1923
|
-
/**
|
|
1924
|
-
* Return `[...body, <TailHelper x={x} />]` so the branch's render output
|
|
1925
|
-
* includes the tail invocation and the post-hook locals flow forward.
|
|
1926
|
-
* Used by if / switch / try (unconditional append). For-of uses a different
|
|
1927
|
-
* shape — gating on `_tsrx_isLast_<n>` — so it constructs its own.
|
|
1928
|
-
*
|
|
1929
|
-
* @param {any[]} body
|
|
1930
|
-
* @param {{ component_element: ESTreeJSX.JSXElement }} tail_helper
|
|
1931
|
-
* @returns {any[]}
|
|
1932
|
-
*/
|
|
1933
|
-
function append_tail_invocation(body, tail_helper) {
|
|
1934
|
-
return [...body, clone_tail_invocation(tail_helper)];
|
|
1935
|
-
}
|
|
1936
|
-
|
|
1937
|
-
/**
|
|
1938
|
-
* @param {AST.Identifier} tail_synthetic_id
|
|
1939
|
-
* @param {{ component_element: ESTreeJSX.JSXElement }} tail_helper
|
|
1940
|
-
* @returns {any}
|
|
1941
|
-
*/
|
|
1942
|
-
function create_loop_tail_expression(tail_synthetic_id, tail_helper) {
|
|
1943
|
-
return b.logical('&&', clone_identifier(tail_synthetic_id), clone_tail_invocation(tail_helper));
|
|
1944
|
-
}
|
|
1945
|
-
|
|
1946
|
-
/**
|
|
1947
|
-
* @param {AST.Identifier} tail_synthetic_id
|
|
1948
|
-
* @param {{ component_element: ESTreeJSX.JSXElement }} tail_helper
|
|
1949
|
-
* @returns {any}
|
|
1950
|
-
*/
|
|
1951
|
-
function create_loop_tail_conditional(tail_synthetic_id, tail_helper) {
|
|
1952
|
-
return b.conditional(
|
|
1953
|
-
clone_identifier(tail_synthetic_id),
|
|
1954
|
-
clone_tail_invocation(tail_helper),
|
|
1955
|
-
create_null_literal(),
|
|
1956
|
-
);
|
|
1957
|
-
}
|
|
1958
|
-
|
|
1959
|
-
/**
|
|
1960
|
-
* @param {any[]} statements
|
|
1961
|
-
* @param {AST.Identifier} tail_synthetic_id
|
|
1962
|
-
* @param {{ component_element: ESTreeJSX.JSXElement }} tail_helper
|
|
1963
|
-
* @returns {void}
|
|
1964
|
-
*/
|
|
1965
|
-
function append_loop_tail_to_return_statements(statements, tail_synthetic_id, tail_helper) {
|
|
1966
|
-
for (const statement of statements) {
|
|
1967
|
-
append_loop_tail_to_return_statement(statement, tail_synthetic_id, tail_helper, false);
|
|
1968
|
-
}
|
|
1969
|
-
}
|
|
1970
|
-
|
|
1971
|
-
/**
|
|
1972
|
-
* @param {any} node
|
|
1973
|
-
* @param {AST.Identifier} tail_synthetic_id
|
|
1974
|
-
* @param {{ component_element: ESTreeJSX.JSXElement }} tail_helper
|
|
1975
|
-
* @param {boolean} inside_nested_function
|
|
1976
|
-
* @returns {void}
|
|
1977
|
-
*/
|
|
1978
|
-
function append_loop_tail_to_return_statement(
|
|
1979
|
-
node,
|
|
1980
|
-
tail_synthetic_id,
|
|
1981
|
-
tail_helper,
|
|
1982
|
-
inside_nested_function,
|
|
1983
|
-
) {
|
|
1984
|
-
if (!node || typeof node !== 'object') {
|
|
1985
|
-
return;
|
|
1986
|
-
}
|
|
1987
|
-
|
|
1988
|
-
if (
|
|
1989
|
-
node.type === 'FunctionDeclaration' ||
|
|
1990
|
-
node.type === 'FunctionExpression' ||
|
|
1991
|
-
node.type === 'ArrowFunctionExpression'
|
|
1992
|
-
) {
|
|
1993
|
-
inside_nested_function = true;
|
|
1994
|
-
}
|
|
1995
|
-
|
|
1996
|
-
if (!inside_nested_function && node.type === 'ReturnStatement') {
|
|
1997
|
-
if (
|
|
1998
|
-
references_scope_bindings(
|
|
1999
|
-
node.argument,
|
|
2000
|
-
new Map([[tail_synthetic_id.name, tail_synthetic_id]]),
|
|
2001
|
-
)
|
|
2002
|
-
) {
|
|
2003
|
-
return;
|
|
2004
|
-
}
|
|
2005
|
-
node.argument = append_loop_tail_to_return_argument(
|
|
2006
|
-
node.argument,
|
|
2007
|
-
tail_synthetic_id,
|
|
2008
|
-
tail_helper,
|
|
2009
|
-
);
|
|
2010
|
-
return;
|
|
2011
|
-
}
|
|
2012
|
-
|
|
2013
|
-
if (Array.isArray(node)) {
|
|
2014
|
-
for (const child of node) {
|
|
2015
|
-
append_loop_tail_to_return_statement(
|
|
2016
|
-
child,
|
|
2017
|
-
tail_synthetic_id,
|
|
2018
|
-
tail_helper,
|
|
2019
|
-
inside_nested_function,
|
|
2020
|
-
);
|
|
2021
|
-
}
|
|
2022
|
-
return;
|
|
2023
|
-
}
|
|
2024
|
-
|
|
2025
|
-
for (const key of Object.keys(node)) {
|
|
2026
|
-
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
2027
|
-
continue;
|
|
2028
|
-
}
|
|
2029
|
-
append_loop_tail_to_return_statement(
|
|
2030
|
-
node[key],
|
|
2031
|
-
tail_synthetic_id,
|
|
2032
|
-
tail_helper,
|
|
2033
|
-
inside_nested_function,
|
|
2034
|
-
);
|
|
2035
|
-
}
|
|
2036
|
-
}
|
|
2037
|
-
|
|
2038
|
-
/**
|
|
2039
|
-
* @param {any} return_argument
|
|
2040
|
-
* @param {AST.Identifier} tail_synthetic_id
|
|
2041
|
-
* @param {{ component_element: ESTreeJSX.JSXElement }} tail_helper
|
|
2042
|
-
* @returns {any}
|
|
2043
|
-
*/
|
|
2044
|
-
function append_loop_tail_to_return_argument(return_argument, tail_synthetic_id, tail_helper) {
|
|
2045
|
-
if (return_argument == null || is_null_literal(return_argument)) {
|
|
2046
|
-
return create_loop_tail_conditional(tail_synthetic_id, tail_helper);
|
|
2047
|
-
}
|
|
2048
|
-
|
|
2049
|
-
return (
|
|
2050
|
-
build_return_expression([
|
|
2051
|
-
return_argument_to_render_node(return_argument),
|
|
2052
|
-
to_jsx_expression_container(create_loop_tail_expression(tail_synthetic_id, tail_helper)),
|
|
2053
|
-
]) || create_null_literal()
|
|
2054
|
-
);
|
|
2055
|
-
}
|
|
2056
|
-
|
|
2057
1793
|
/**
|
|
2058
1794
|
* Build a `return <combined-render-fragment>;` statement, prepending any
|
|
2059
1795
|
* `render_nodes` collected before the control-flow construct so they don't
|
|
@@ -2134,353 +1870,73 @@ function create_component_helper_split_returning_if_statements(
|
|
|
2134
1870
|
}
|
|
2135
1871
|
|
|
2136
1872
|
/**
|
|
2137
|
-
*
|
|
2138
|
-
*
|
|
1873
|
+
* Hoist the helper for a hook-bearing for-of body out of the iteration
|
|
1874
|
+
* callback so the helper is declared once per render rather than re-bound on
|
|
1875
|
+
* every iteration. Loop-scoped param types are derived from the iteration
|
|
1876
|
+
* source via a TS `type` alias (rather than the const+typeof pattern used
|
|
1877
|
+
* for outer bindings, which would require the loop var to be in scope).
|
|
1878
|
+
*
|
|
1879
|
+
* The iteration source is hoisted into a generated `let` and normalized via
|
|
1880
|
+
* `Array.isArray(src) ? src : Array.from(src)` so any Iterable / ArrayLike
|
|
1881
|
+
* works while skipping the copy when the source is already an array. The
|
|
1882
|
+
* iteration itself is emitted as `source.map((item, i) => ...)`.
|
|
2139
1883
|
*
|
|
2140
|
-
*
|
|
2141
|
-
*
|
|
2142
|
-
* mutates would observe the pre-hook value, because React commits children
|
|
2143
|
-
* after their parent has finished rendering. The fix mirrors the early-return
|
|
2144
|
-
* splitter: emit a tail helper that owns the post-`if` statements, append a
|
|
2145
|
-
* call to it inside the branch helper so the post-hook bindings flow forward,
|
|
2146
|
-
* and render the tail helper directly when the `if` is false.
|
|
1884
|
+
* Bails out (returns null) when the loop pattern is destructured — deriving
|
|
1885
|
+
* element types from a tuple/object pattern is more involved and deferred.
|
|
2147
1886
|
*
|
|
2148
|
-
* @param {any}
|
|
2149
|
-
* @param {any[]} continuation_body
|
|
2150
|
-
* @param {any[]} render_nodes
|
|
1887
|
+
* @param {any} node - ForOfStatement
|
|
2151
1888
|
* @param {TransformContext} transform_context
|
|
2152
|
-
* @returns {any[]}
|
|
1889
|
+
* @returns {{ hoist_statements: any[], jsx_child: any } | null}
|
|
2153
1890
|
*/
|
|
2154
|
-
function
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
) {
|
|
2160
|
-
const consequent_body = get_if_consequent_body(if_node);
|
|
2161
|
-
const tail_helper = build_tail_helper(continuation_body, if_node, transform_context);
|
|
2162
|
-
const branch_helper = create_hook_safe_helper(
|
|
2163
|
-
append_tail_invocation(consequent_body, tail_helper),
|
|
2164
|
-
undefined,
|
|
2165
|
-
if_node.consequent,
|
|
2166
|
-
transform_context,
|
|
2167
|
-
);
|
|
1891
|
+
function build_hoisted_for_of_with_hooks(node, transform_context) {
|
|
1892
|
+
const loop_params = get_for_of_iteration_params(node.left, node.index);
|
|
1893
|
+
for (const param of loop_params) {
|
|
1894
|
+
if (param.type !== 'Identifier') return null;
|
|
1895
|
+
}
|
|
2168
1896
|
|
|
2169
|
-
const
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
]),
|
|
2174
|
-
if_node.consequent,
|
|
1897
|
+
const original_loop_body = /** @type {any[]} */ (
|
|
1898
|
+
rewrite_loop_continues_to_bare_returns(
|
|
1899
|
+
node.body.type === 'BlockStatement' ? node.body.body : [node.body],
|
|
1900
|
+
)
|
|
2175
1901
|
);
|
|
2176
1902
|
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
* append a clone of its invocation to the try body and the catch body so the
|
|
2188
|
-
* post-hook locals inside each branch flow forward into the tail. The pending
|
|
2189
|
-
* body is left untouched — when Suspense renders the pending fallback the
|
|
2190
|
-
* parent's render is unwound, so the tail wouldn't run in source semantics
|
|
2191
|
-
* either. Once augmented, the existing try transform builds the
|
|
2192
|
-
* Suspense / TsrxErrorBoundary wrapper as usual.
|
|
2193
|
-
*
|
|
2194
|
-
* @param {any} node - TryStatement
|
|
2195
|
-
* @param {any[]} continuation_body
|
|
2196
|
-
* @param {any[]} render_nodes
|
|
2197
|
-
* @param {TransformContext} transform_context
|
|
2198
|
-
* @returns {any[]}
|
|
2199
|
-
*/
|
|
2200
|
-
function create_continuation_lift_try_statement(
|
|
2201
|
-
node,
|
|
2202
|
-
continuation_body,
|
|
2203
|
-
render_nodes,
|
|
2204
|
-
transform_context,
|
|
2205
|
-
) {
|
|
2206
|
-
const tail_helper = build_tail_helper(continuation_body, node, transform_context);
|
|
2207
|
-
|
|
2208
|
-
const augmented_block = {
|
|
2209
|
-
...node.block,
|
|
2210
|
-
body: append_tail_invocation(node.block.body || [], tail_helper),
|
|
2211
|
-
};
|
|
1903
|
+
const source_id = create_generated_identifier(
|
|
1904
|
+
`_tsrx_iteration_items_${transform_context.local_statement_component_index + 1}`,
|
|
1905
|
+
);
|
|
1906
|
+
const use_iterable_helper = !!transform_context.platform.imports.forOfIterableHelper;
|
|
1907
|
+
const { source_decl, source_normalize_decl } = use_iterable_helper
|
|
1908
|
+
? {
|
|
1909
|
+
source_decl: b.let(clone_identifier(source_id), clone_expression_node(node.right)),
|
|
1910
|
+
source_normalize_decl: null,
|
|
1911
|
+
}
|
|
1912
|
+
: build_array_normalization_decls(source_id, node.right);
|
|
2212
1913
|
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
...node.handler.body,
|
|
2219
|
-
body: append_tail_invocation(node.handler.body.body || [], tail_helper),
|
|
2220
|
-
},
|
|
2221
|
-
};
|
|
1914
|
+
const saved_bindings = transform_context.available_bindings;
|
|
1915
|
+
transform_context.available_bindings = new Map(saved_bindings);
|
|
1916
|
+
const loop_scoped_names = new Set(loop_params.map((/** @type {any} */ p) => p.name));
|
|
1917
|
+
for (const param of loop_params) {
|
|
1918
|
+
collect_pattern_bindings(param, transform_context.available_bindings);
|
|
2222
1919
|
}
|
|
1920
|
+
validate_hook_safe_body_does_not_assign_hook_results_to_outer_bindings(
|
|
1921
|
+
original_loop_body,
|
|
1922
|
+
transform_context,
|
|
1923
|
+
loop_scoped_names,
|
|
1924
|
+
);
|
|
2223
1925
|
|
|
2224
|
-
const
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
const try_jsx_child = (
|
|
2231
|
-
transform_context.platform.hooks?.controlFlow?.tryStatement ?? try_statement_to_jsx_child
|
|
2232
|
-
)(augmented_try, transform_context);
|
|
1926
|
+
const all_helper_bindings = get_referenced_helper_bindings(
|
|
1927
|
+
original_loop_body,
|
|
1928
|
+
transform_context.available_bindings,
|
|
1929
|
+
);
|
|
1930
|
+
const outer_bindings = all_helper_bindings.filter((b) => !loop_scoped_names.has(b.name));
|
|
1931
|
+
const loop_bindings = all_helper_bindings.filter((b) => loop_scoped_names.has(b.name));
|
|
2233
1932
|
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
*/
|
|
2242
|
-
function try_statement_contains_hooks(node, transform_context) {
|
|
2243
|
-
if (body_contains_top_level_hook_call(node.block?.body || [], transform_context, true)) {
|
|
2244
|
-
return true;
|
|
2245
|
-
}
|
|
2246
|
-
if (
|
|
2247
|
-
node.handler &&
|
|
2248
|
-
body_contains_top_level_hook_call(node.handler.body?.body || [], transform_context, true)
|
|
2249
|
-
) {
|
|
2250
|
-
return true;
|
|
2251
|
-
}
|
|
2252
|
-
if (
|
|
2253
|
-
node.pending &&
|
|
2254
|
-
body_contains_top_level_hook_call(node.pending.body || [], transform_context, true)
|
|
2255
|
-
) {
|
|
2256
|
-
return true;
|
|
2257
|
-
}
|
|
2258
|
-
return false;
|
|
2259
|
-
}
|
|
2260
|
-
|
|
2261
|
-
/**
|
|
2262
|
-
* Continuation lift for `switch` statements. Same shape as the if-version:
|
|
2263
|
-
* each case body is wrapped in its own helper component that ends with a
|
|
2264
|
-
* call to a shared tail helper, so post-hook bindings inside any case flow
|
|
2265
|
-
* forward to the statements after the switch. The fall-through return at
|
|
2266
|
-
* the end renders the tail helper directly, covering the case where no
|
|
2267
|
-
* `case` (and no `default`) matched.
|
|
2268
|
-
*
|
|
2269
|
-
* Empty fall-through cases (`case 'a':` with no body, falling through to
|
|
2270
|
-
* the next case) are preserved as-is — they must not get their own helper
|
|
2271
|
-
* because that would convert fall-through into early-return.
|
|
2272
|
-
*
|
|
2273
|
-
* @param {any} switch_node
|
|
2274
|
-
* @param {any[]} continuation_body
|
|
2275
|
-
* @param {any[]} render_nodes
|
|
2276
|
-
* @param {TransformContext} transform_context
|
|
2277
|
-
* @returns {any[]}
|
|
2278
|
-
*/
|
|
2279
|
-
function create_continuation_lift_switch_statement(
|
|
2280
|
-
switch_node,
|
|
2281
|
-
continuation_body,
|
|
2282
|
-
render_nodes,
|
|
2283
|
-
transform_context,
|
|
2284
|
-
) {
|
|
2285
|
-
const tail_helper = build_tail_helper(continuation_body, switch_node, transform_context);
|
|
2286
|
-
|
|
2287
|
-
// Per-case info computed once: own body (statements before any
|
|
2288
|
-
// terminator) and whether the case has a `break` / `return`.
|
|
2289
|
-
const case_info = switch_node.cases.map((/** @type {any} */ c) => {
|
|
2290
|
-
const consequent = flatten_switch_consequent(c.consequent || []);
|
|
2291
|
-
const own_body = [];
|
|
2292
|
-
let own_has_terminator = false;
|
|
2293
|
-
for (const node of consequent) {
|
|
2294
|
-
if (node.type === 'BreakStatement' || node.type === 'ReturnStatement') {
|
|
2295
|
-
own_has_terminator = true;
|
|
2296
|
-
break;
|
|
2297
|
-
}
|
|
2298
|
-
own_body.push(node);
|
|
2299
|
-
}
|
|
2300
|
-
return { own_body, own_has_terminator };
|
|
2301
|
-
});
|
|
2302
|
-
|
|
2303
|
-
// Allocate helper ids in source order (forward pass) so the snapshot's
|
|
2304
|
-
// `StatementBodyHook<N>` numbering reads top-to-bottom by case position.
|
|
2305
|
-
/** @type {Array<AST.Identifier | null>} */
|
|
2306
|
-
const helper_ids = case_info.map(
|
|
2307
|
-
(/** @type {{ own_body: any[], own_has_terminator: boolean }} */ info) =>
|
|
2308
|
-
info.own_body.length === 0
|
|
2309
|
-
? null
|
|
2310
|
-
: create_generated_identifier(create_local_statement_component_name(transform_context)),
|
|
2311
|
-
);
|
|
2312
|
-
|
|
2313
|
-
// Build helpers in reverse order: each fall-through case's helper body
|
|
2314
|
-
// invokes the *next* case's helper, so the chain forwards post-mutation
|
|
2315
|
-
// locals through the switch. Reverse iteration ensures the next helper's
|
|
2316
|
-
// component_element is already constructed when we need to embed it.
|
|
2317
|
-
/** @type {Array<{ setup_statements: any[], component_element: any } | null>} */
|
|
2318
|
-
const case_helper_by_index = new Array(switch_node.cases.length).fill(null);
|
|
2319
|
-
for (let i = switch_node.cases.length - 1; i >= 0; i--) {
|
|
2320
|
-
const { own_body, own_has_terminator } = case_info[i];
|
|
2321
|
-
if (own_body.length === 0) continue;
|
|
2322
|
-
|
|
2323
|
-
// Determine the downstream helper this case invokes after its own body.
|
|
2324
|
-
// - With a terminator: invoke the tail helper directly (case exits switch).
|
|
2325
|
-
// - Otherwise (fall-through): invoke the next non-empty case's helper,
|
|
2326
|
-
// or the tail if nothing else follows.
|
|
2327
|
-
let downstream;
|
|
2328
|
-
if (own_has_terminator) {
|
|
2329
|
-
downstream = tail_helper;
|
|
2330
|
-
} else {
|
|
2331
|
-
let next_helper = null;
|
|
2332
|
-
for (let j = i + 1; j < switch_node.cases.length; j++) {
|
|
2333
|
-
if (case_helper_by_index[j]) {
|
|
2334
|
-
next_helper = case_helper_by_index[j];
|
|
2335
|
-
break;
|
|
2336
|
-
}
|
|
2337
|
-
}
|
|
2338
|
-
downstream = next_helper ?? tail_helper;
|
|
2339
|
-
}
|
|
2340
|
-
|
|
2341
|
-
case_helper_by_index[i] = create_hook_safe_helper(
|
|
2342
|
-
append_tail_invocation(own_body, downstream),
|
|
2343
|
-
undefined,
|
|
2344
|
-
switch_node.cases[i],
|
|
2345
|
-
transform_context,
|
|
2346
|
-
/** @type {any} */ (helper_ids[i]),
|
|
2347
|
-
);
|
|
2348
|
-
}
|
|
2349
|
-
|
|
2350
|
-
const new_cases = switch_node.cases.map(
|
|
2351
|
-
(/** @type {any} */ original_case, /** @type {number} */ i) => {
|
|
2352
|
-
const helper = case_helper_by_index[i];
|
|
2353
|
-
if (helper) {
|
|
2354
|
-
return b.switch_case(original_case.test, [
|
|
2355
|
-
combined_return_statement(render_nodes, helper.component_element),
|
|
2356
|
-
]);
|
|
2357
|
-
}
|
|
2358
|
-
|
|
2359
|
-
const { own_body, own_has_terminator } = case_info[i];
|
|
2360
|
-
if (own_body.length === 0 && own_has_terminator) {
|
|
2361
|
-
// `case 'a': break;` — exits the switch, then runs the tail.
|
|
2362
|
-
return b.switch_case(original_case.test, [
|
|
2363
|
-
combined_return_statement(render_nodes, tail_helper.component_element),
|
|
2364
|
-
]);
|
|
2365
|
-
}
|
|
2366
|
-
// Genuine empty fall-through (`case 'a': case 'b': ...`).
|
|
2367
|
-
return b.switch_case(original_case.test, []);
|
|
2368
|
-
},
|
|
2369
|
-
);
|
|
2370
|
-
|
|
2371
|
-
// Hoist all case helpers' setup statements above the switch in source
|
|
2372
|
-
// order so the switch body is purely a dispatcher.
|
|
2373
|
-
const case_helper_setup_statements = [];
|
|
2374
|
-
for (const helper of case_helper_by_index) {
|
|
2375
|
-
if (helper) case_helper_setup_statements.push(...helper.setup_statements);
|
|
2376
|
-
}
|
|
2377
|
-
|
|
2378
|
-
return [
|
|
2379
|
-
...tail_helper.setup_statements,
|
|
2380
|
-
...case_helper_setup_statements,
|
|
2381
|
-
set_loc(b.switch(switch_node.discriminant, new_cases), switch_node),
|
|
2382
|
-
combined_return_statement(render_nodes, tail_helper.component_element),
|
|
2383
|
-
];
|
|
2384
|
-
}
|
|
2385
|
-
|
|
2386
|
-
/**
|
|
2387
|
-
* Hoist the helper for a hook-bearing for-of body out of the iteration
|
|
2388
|
-
* callback so the helper is declared once per render rather than re-bound on
|
|
2389
|
-
* every iteration. Loop-scoped param types are derived from the iteration
|
|
2390
|
-
* source via a TS `type` alias (rather than the const+typeof pattern used
|
|
2391
|
-
* for outer bindings, which would require the loop var to be in scope).
|
|
2392
|
-
*
|
|
2393
|
-
* The iteration source is hoisted into a generated `let` and normalized via
|
|
2394
|
-
* `Array.isArray(src) ? src : Array.from(src)` so any Iterable / ArrayLike
|
|
2395
|
-
* works while skipping the copy when the source is already an array. The
|
|
2396
|
-
* iteration itself is emitted as `source.map((item, i) => ...)`.
|
|
2397
|
-
*
|
|
2398
|
-
* If `continuation_body` is non-empty (the for-of has a tail) we also lift
|
|
2399
|
-
* the tail into a TailHelper and call it conditionally on the last iteration
|
|
2400
|
-
* via an `isLast={i === source.length - 1}` prop on the loop helper. The
|
|
2401
|
-
* loop helper's mutated locals (post-`useState`) flow into the TailHelper as
|
|
2402
|
-
* its props. When the source is empty, `.map` returns `[]` and the TailHelper
|
|
2403
|
-
* never renders — we add a sibling fallback so the source's tail still runs
|
|
2404
|
-
* with the original outer values in that case.
|
|
2405
|
-
*
|
|
2406
|
-
* Bails out (returns null) when the loop pattern is destructured — deriving
|
|
2407
|
-
* element types from a tuple/object pattern is more involved and deferred.
|
|
2408
|
-
*
|
|
2409
|
-
* @param {any} node - ForOfStatement
|
|
2410
|
-
* @param {any[]} continuation_body
|
|
2411
|
-
* @param {TransformContext} transform_context
|
|
2412
|
-
* @returns {{ hoist_statements: any[], jsx_child: any } | null}
|
|
2413
|
-
*/
|
|
2414
|
-
function build_hoisted_for_of_with_hooks(node, continuation_body, transform_context) {
|
|
2415
|
-
const loop_params = get_for_of_iteration_params(node.left, node.index);
|
|
2416
|
-
for (const param of loop_params) {
|
|
2417
|
-
if (param.type !== 'Identifier') return null;
|
|
2418
|
-
}
|
|
2419
|
-
|
|
2420
|
-
const has_tail = continuation_body.length > 0;
|
|
2421
|
-
const original_loop_body = /** @type {any[]} */ (
|
|
2422
|
-
rewrite_loop_continues_to_bare_returns(
|
|
2423
|
-
node.body.type === 'BlockStatement' ? node.body.body : [node.body],
|
|
2424
|
-
)
|
|
2425
|
-
);
|
|
2426
|
-
|
|
2427
|
-
// When there's a tail, build TailHelper first so its component_element can
|
|
2428
|
-
// be embedded inside the loop helper's body (gated on isLast). The
|
|
2429
|
-
// synthetic isLast prop uses the loop helper's index (which will be the
|
|
2430
|
-
// next one assigned, since `create_hook_safe_helper` for the tail just
|
|
2431
|
-
// consumed one) so it lines up with `StatementBodyHook<N>` in the output.
|
|
2432
|
-
let tail_helper = null;
|
|
2433
|
-
/** @type {AST.Identifier} */ let tail_synthetic_id;
|
|
2434
|
-
if (has_tail) {
|
|
2435
|
-
tail_helper = build_tail_helper(continuation_body, node, transform_context);
|
|
2436
|
-
tail_synthetic_id = create_generated_identifier(
|
|
2437
|
-
`_tsrx_isLast_${transform_context.local_statement_component_index + 1}`,
|
|
2438
|
-
);
|
|
2439
|
-
} else {
|
|
2440
|
-
tail_synthetic_id = /** @type {any} */ (null);
|
|
2441
|
-
}
|
|
2442
|
-
const loop_tail_expression = has_tail
|
|
2443
|
-
? create_loop_tail_expression(tail_synthetic_id, /** @type {any} */ (tail_helper))
|
|
2444
|
-
: null;
|
|
2445
|
-
const loop_body =
|
|
2446
|
-
has_tail && loop_tail_expression
|
|
2447
|
-
? [...original_loop_body, b.jsx_expression_container(loop_tail_expression)]
|
|
2448
|
-
: original_loop_body;
|
|
2449
|
-
|
|
2450
|
-
const source_id = create_generated_identifier(
|
|
2451
|
-
`_tsrx_iteration_items_${transform_context.local_statement_component_index + 1}`,
|
|
2452
|
-
);
|
|
2453
|
-
const { source_decl, source_normalize_decl } = build_array_normalization_decls(
|
|
2454
|
-
source_id,
|
|
2455
|
-
node.right,
|
|
2456
|
-
);
|
|
2457
|
-
|
|
2458
|
-
const saved_bindings = transform_context.available_bindings;
|
|
2459
|
-
transform_context.available_bindings = new Map(saved_bindings);
|
|
2460
|
-
const loop_scoped_names = new Set(loop_params.map((/** @type {any} */ p) => p.name));
|
|
2461
|
-
for (const param of loop_params) {
|
|
2462
|
-
collect_pattern_bindings(param, transform_context.available_bindings);
|
|
2463
|
-
}
|
|
2464
|
-
validate_hook_safe_body_does_not_assign_hook_results_to_outer_bindings(
|
|
2465
|
-
original_loop_body,
|
|
2466
|
-
transform_context,
|
|
2467
|
-
loop_scoped_names,
|
|
2468
|
-
);
|
|
2469
|
-
|
|
2470
|
-
const all_helper_bindings = get_referenced_helper_bindings(
|
|
2471
|
-
loop_body,
|
|
2472
|
-
transform_context.available_bindings,
|
|
2473
|
-
);
|
|
2474
|
-
const outer_bindings = all_helper_bindings.filter((b) => !loop_scoped_names.has(b.name));
|
|
2475
|
-
const loop_bindings = all_helper_bindings.filter((b) => loop_scoped_names.has(b.name));
|
|
2476
|
-
|
|
2477
|
-
const helper_id = create_generated_identifier(
|
|
2478
|
-
create_local_statement_component_name(transform_context),
|
|
2479
|
-
);
|
|
2480
|
-
const use_module_scoped_component = should_use_module_scoped_hook_components(transform_context);
|
|
2481
|
-
const component_id = use_module_scoped_component
|
|
2482
|
-
? create_module_scoped_hook_component_id(helper_id, transform_context)
|
|
2483
|
-
: helper_id;
|
|
1933
|
+
const helper_id = create_generated_identifier(
|
|
1934
|
+
create_local_statement_component_name(transform_context),
|
|
1935
|
+
);
|
|
1936
|
+
const use_module_scoped_component = should_use_module_scoped_hook_components(transform_context);
|
|
1937
|
+
const component_id = use_module_scoped_component
|
|
1938
|
+
? create_module_scoped_hook_component_id(helper_id, transform_context)
|
|
1939
|
+
: helper_id;
|
|
2484
1940
|
|
|
2485
1941
|
const outer_aliases = use_module_scoped_component
|
|
2486
1942
|
? []
|
|
@@ -2488,69 +1944,42 @@ function build_hoisted_for_of_with_hooks(node, continuation_body, transform_cont
|
|
|
2488
1944
|
const loop_aliases = use_module_scoped_component
|
|
2489
1945
|
? []
|
|
2490
1946
|
: loop_bindings.map((binding) =>
|
|
2491
|
-
create_loop_scoped_type_alias_declaration(
|
|
1947
|
+
create_loop_scoped_type_alias_declaration(
|
|
1948
|
+
helper_id,
|
|
1949
|
+
binding,
|
|
1950
|
+
source_id,
|
|
1951
|
+
loop_params,
|
|
1952
|
+
transform_context,
|
|
1953
|
+
),
|
|
2492
1954
|
);
|
|
2493
1955
|
|
|
2494
|
-
// Synthetic `isLast` prop on the loop helper when there's a tail. It's
|
|
2495
|
-
// passed from the .map callback as `i === source.length - 1` so every
|
|
2496
|
-
// loop-helper return can append the tail helper on the last iteration.
|
|
2497
|
-
const tail_isLast_alias = has_tail
|
|
2498
|
-
? use_module_scoped_component
|
|
2499
|
-
? null
|
|
2500
|
-
: {
|
|
2501
|
-
id: create_generated_identifier(`_tsrx_${helper_id.name}_isLast`),
|
|
2502
|
-
declaration: b.ts_type_alias(
|
|
2503
|
-
create_generated_identifier(`_tsrx_${helper_id.name}_isLast`),
|
|
2504
|
-
b.ts_keyword_type('boolean'),
|
|
2505
|
-
),
|
|
2506
|
-
}
|
|
2507
|
-
: null;
|
|
2508
|
-
|
|
2509
1956
|
const ordered_bindings = [...outer_bindings, ...loop_bindings];
|
|
2510
1957
|
const ordered_aliases = [...outer_aliases, ...loop_aliases];
|
|
2511
1958
|
const ordered_use_typeof = [...outer_bindings.map(() => true), ...loop_bindings.map(() => false)];
|
|
2512
1959
|
|
|
2513
|
-
const signature_bindings = has_tail ? [...ordered_bindings, tail_synthetic_id] : ordered_bindings;
|
|
2514
|
-
const signature_aliases = has_tail
|
|
2515
|
-
? [...ordered_aliases, /** @type {any} */ (tail_isLast_alias)]
|
|
2516
|
-
: ordered_aliases;
|
|
2517
|
-
const signature_use_typeof = has_tail ? [...ordered_use_typeof, false] : ordered_use_typeof;
|
|
2518
|
-
|
|
2519
1960
|
const props_type =
|
|
2520
|
-
|
|
1961
|
+
ordered_bindings.length > 0 && !use_module_scoped_component
|
|
2521
1962
|
? create_helper_props_type_literal_with_typeof_flags(
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
1963
|
+
ordered_bindings,
|
|
1964
|
+
ordered_aliases,
|
|
1965
|
+
ordered_use_typeof,
|
|
2525
1966
|
)
|
|
2526
1967
|
: null;
|
|
2527
1968
|
const params =
|
|
2528
|
-
|
|
1969
|
+
ordered_bindings.length > 0
|
|
2529
1970
|
? [
|
|
2530
1971
|
props_type !== null
|
|
2531
|
-
? create_typed_helper_props_pattern(
|
|
2532
|
-
: create_helper_props_pattern(
|
|
1972
|
+
? create_typed_helper_props_pattern(ordered_bindings, props_type)
|
|
1973
|
+
: create_helper_props_pattern(ordered_bindings),
|
|
2533
1974
|
]
|
|
2534
1975
|
: [];
|
|
2535
1976
|
|
|
2536
1977
|
const fn_saved_bindings = transform_context.available_bindings;
|
|
2537
1978
|
transform_context.available_bindings = new Map(fn_saved_bindings);
|
|
2538
|
-
|
|
2539
|
-
transform_context.available_bindings.set(tail_synthetic_id.name, tail_synthetic_id);
|
|
2540
|
-
}
|
|
2541
|
-
const fn_body_statements = build_render_statements(loop_body, true, transform_context);
|
|
2542
|
-
if (has_tail) {
|
|
2543
|
-
append_loop_tail_to_return_statements(
|
|
2544
|
-
fn_body_statements,
|
|
2545
|
-
tail_synthetic_id,
|
|
2546
|
-
/** @type {any} */ (tail_helper),
|
|
2547
|
-
);
|
|
2548
|
-
}
|
|
1979
|
+
const fn_body_statements = build_render_statements(original_loop_body, true, transform_context);
|
|
2549
1980
|
transform_context.available_bindings = fn_saved_bindings;
|
|
2550
1981
|
|
|
2551
|
-
const helper_fn =
|
|
2552
|
-
b.function(clone_identifier(component_id), params, b.block(fn_body_statements))
|
|
2553
|
-
);
|
|
1982
|
+
const helper_fn = b.function(clone_identifier(component_id), params, b.block(fn_body_statements));
|
|
2554
1983
|
helper_fn.metadata = { path: [], is_component: true, is_method: false };
|
|
2555
1984
|
|
|
2556
1985
|
let helper_decl;
|
|
@@ -2582,18 +2011,6 @@ function build_hoisted_for_of_with_hooks(node, continuation_body, transform_cont
|
|
|
2582
2011
|
{ mapWrapper: false, mapBindingNames: false, mapBindingValues: false },
|
|
2583
2012
|
);
|
|
2584
2013
|
|
|
2585
|
-
// When there's a tail, the .map callback always needs an index to compute
|
|
2586
|
-
// `isLast`. If the user didn't write `index i`, synthesize one. The same
|
|
2587
|
-
// identifier is also used as the implicit key fallback below.
|
|
2588
|
-
let index_identifier;
|
|
2589
|
-
if (loop_params.length >= 2) {
|
|
2590
|
-
index_identifier = clone_identifier(loop_params[1]);
|
|
2591
|
-
} else if (has_tail) {
|
|
2592
|
-
index_identifier = create_generated_identifier('i');
|
|
2593
|
-
} else {
|
|
2594
|
-
index_identifier = null;
|
|
2595
|
-
}
|
|
2596
|
-
|
|
2597
2014
|
const body_key_expression = find_key_expression_in_body(original_loop_body);
|
|
2598
2015
|
const explicit_key_expression =
|
|
2599
2016
|
body_key_expression ?? (node.key ? clone_expression_node(node.key) : undefined);
|
|
@@ -2606,57 +2023,24 @@ function build_hoisted_for_of_with_hooks(node, continuation_body, transform_cont
|
|
|
2606
2023
|
);
|
|
2607
2024
|
}
|
|
2608
2025
|
|
|
2609
|
-
|
|
2610
|
-
const length_minus_one = b.binary(
|
|
2611
|
-
'-',
|
|
2612
|
-
b.member(clone_identifier(source_id), 'length'),
|
|
2613
|
-
b.literal(1),
|
|
2614
|
-
);
|
|
2615
|
-
callback_invocation_element.openingElement.attributes.push(
|
|
2616
|
-
b.jsx_attribute(
|
|
2617
|
-
b.jsx_id(tail_synthetic_id.name),
|
|
2618
|
-
to_jsx_expression_container(
|
|
2619
|
-
b.binary('===', clone_identifier(index_identifier), length_minus_one),
|
|
2620
|
-
),
|
|
2621
|
-
),
|
|
2622
|
-
);
|
|
2623
|
-
}
|
|
2624
|
-
|
|
2625
|
-
const callback_params =
|
|
2626
|
-
has_tail && loop_params.length < 2 && index_identifier
|
|
2627
|
-
? [
|
|
2628
|
-
...loop_params.map((/** @type {any} */ p) => clone_identifier(p)),
|
|
2629
|
-
clone_identifier(index_identifier),
|
|
2630
|
-
]
|
|
2631
|
-
: loop_params.map((/** @type {any} */ p) => clone_identifier(p));
|
|
2026
|
+
const callback_params = loop_params.map((/** @type {any} */ p) => clone_identifier(p));
|
|
2632
2027
|
|
|
2633
2028
|
const iter_callback = b.arrow(callback_params, callback_invocation_element);
|
|
2634
2029
|
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
? to_jsx_expression_container(
|
|
2642
|
-
b.conditional(
|
|
2643
|
-
b.binary('===', b.member(clone_identifier(source_id), 'length'), b.literal(0)),
|
|
2644
|
-
clone_tail_invocation(/** @type {any} */ (tail_helper)),
|
|
2645
|
-
map_call,
|
|
2646
|
-
),
|
|
2647
|
-
node,
|
|
2648
|
-
)
|
|
2649
|
-
: to_jsx_expression_container(map_call, node);
|
|
2650
|
-
|
|
2651
|
-
const hoist_statements = [source_decl, source_normalize_decl];
|
|
2652
|
-
if (has_tail) {
|
|
2653
|
-
// TailHelper's setup statements (its alias consts and cache decl).
|
|
2654
|
-
hoist_statements.push(.../** @type {any} */ (tail_helper).setup_statements);
|
|
2030
|
+
let map_call;
|
|
2031
|
+
if (use_iterable_helper) {
|
|
2032
|
+
transform_context.needs_for_of_iterable = true;
|
|
2033
|
+
map_call = b.call(b.id(MAP_ITERABLE_INTERNAL_NAME), clone_identifier(source_id), iter_callback);
|
|
2034
|
+
} else {
|
|
2035
|
+
map_call = b.call(b.member(clone_identifier(source_id), 'map'), iter_callback);
|
|
2655
2036
|
}
|
|
2037
|
+
|
|
2038
|
+
const jsx_child = to_jsx_expression_container(map_call, node);
|
|
2039
|
+
|
|
2040
|
+
const hoist_statements = source_normalize_decl
|
|
2041
|
+
? [source_decl, source_normalize_decl]
|
|
2042
|
+
: [source_decl];
|
|
2656
2043
|
for (const alias of ordered_aliases) hoist_statements.push(alias.declaration);
|
|
2657
|
-
if (has_tail && tail_isLast_alias) {
|
|
2658
|
-
hoist_statements.push(tail_isLast_alias.declaration);
|
|
2659
|
-
}
|
|
2660
2044
|
if (helper_decl) {
|
|
2661
2045
|
hoist_statements.push(helper_decl);
|
|
2662
2046
|
}
|
|
@@ -2669,28 +2053,49 @@ function build_hoisted_for_of_with_hooks(node, continuation_body, transform_cont
|
|
|
2669
2053
|
|
|
2670
2054
|
/**
|
|
2671
2055
|
* Build a TS `type` alias for a loop-scoped binding, deriving the type
|
|
2672
|
-
* from the iteration source. For the
|
|
2673
|
-
* `
|
|
2674
|
-
*
|
|
2675
|
-
*
|
|
2056
|
+
* from the iteration source. For the index param the type is always
|
|
2057
|
+
* `number`. For the value param the shape depends on whether the platform
|
|
2058
|
+
* uses the `map_iterable` runtime helper:
|
|
2059
|
+
*
|
|
2060
|
+
* - With the helper (React, Preact): `IterationValue<typeof source>` — any
|
|
2061
|
+
* `Iterable<T>` is accepted, so the element type is derived through the
|
|
2062
|
+
* runtime's exported helper type.
|
|
2063
|
+
* - Without the helper: `(typeof source)[number]` — arrays/tuples only,
|
|
2064
|
+
* matching the inline `.map()` lowering.
|
|
2676
2065
|
*
|
|
2677
2066
|
* @param {AST.Identifier} helper_id
|
|
2678
2067
|
* @param {AST.Identifier} binding
|
|
2679
2068
|
* @param {AST.Identifier} source_id
|
|
2680
2069
|
* @param {any[]} loop_params
|
|
2070
|
+
* @param {TransformContext} transform_context
|
|
2681
2071
|
* @returns {{ id: AST.Identifier, declaration: any }}
|
|
2682
2072
|
*/
|
|
2683
|
-
function create_loop_scoped_type_alias_declaration(
|
|
2073
|
+
function create_loop_scoped_type_alias_declaration(
|
|
2074
|
+
helper_id,
|
|
2075
|
+
binding,
|
|
2076
|
+
source_id,
|
|
2077
|
+
loop_params,
|
|
2078
|
+
transform_context,
|
|
2079
|
+
) {
|
|
2684
2080
|
const alias_id = create_generated_identifier(`_tsrx_${helper_id.name}_${binding.name}`);
|
|
2685
2081
|
const is_index = loop_params.length > 1 && binding.name === loop_params[1].name;
|
|
2082
|
+
const use_iterable_helper = !!transform_context.platform.imports.forOfIterableHelper;
|
|
2686
2083
|
const type_annotation = is_index
|
|
2687
2084
|
? b.ts_keyword_type('number')
|
|
2688
|
-
:
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2085
|
+
: use_iterable_helper
|
|
2086
|
+
? (() => {
|
|
2087
|
+
transform_context.needs_iteration_value_type = true;
|
|
2088
|
+
return b.ts_type_reference(
|
|
2089
|
+
b.id(ITERATION_VALUE_INTERNAL_NAME),
|
|
2090
|
+
b.ts_type_parameter_instantiation([b.ts_type_query(clone_identifier(source_id))]),
|
|
2091
|
+
);
|
|
2092
|
+
})()
|
|
2093
|
+
: /** @type {any} */ ({
|
|
2094
|
+
type: 'TSIndexedAccessType',
|
|
2095
|
+
objectType: b.ts_type_query(clone_identifier(source_id)),
|
|
2096
|
+
indexType: b.ts_keyword_type('number'),
|
|
2097
|
+
metadata: { path: [] },
|
|
2098
|
+
});
|
|
2694
2099
|
|
|
2695
2100
|
return {
|
|
2696
2101
|
id: alias_id,
|
|
@@ -2750,23 +2155,19 @@ function create_setup_once_helper_split_returning_if_statements(
|
|
|
2750
2155
|
return [
|
|
2751
2156
|
...branch_helper.setup_statements,
|
|
2752
2157
|
...continuation_helper.setup_statements,
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
argument: combine_render_return_argument(
|
|
2158
|
+
b.return(
|
|
2159
|
+
combine_render_return_argument(
|
|
2756
2160
|
render_nodes,
|
|
2757
2161
|
set_loc(
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
metadata: { path: [] },
|
|
2764
|
-
}),
|
|
2162
|
+
b.conditional(
|
|
2163
|
+
node.test,
|
|
2164
|
+
branch_helper.component_element,
|
|
2165
|
+
continuation_helper.component_element,
|
|
2166
|
+
),
|
|
2765
2167
|
node,
|
|
2766
2168
|
),
|
|
2767
2169
|
),
|
|
2768
|
-
|
|
2769
|
-
},
|
|
2170
|
+
),
|
|
2770
2171
|
];
|
|
2771
2172
|
}
|
|
2772
2173
|
|
|
@@ -3038,25 +2439,7 @@ function statement_body_to_jsx_child(body_nodes, transform_context) {
|
|
|
3038
2439
|
}
|
|
3039
2440
|
|
|
3040
2441
|
return to_jsx_expression_container(
|
|
3041
|
-
|
|
3042
|
-
type: 'CallExpression',
|
|
3043
|
-
callee: {
|
|
3044
|
-
type: 'ArrowFunctionExpression',
|
|
3045
|
-
params: [],
|
|
3046
|
-
body: /** @type {any} */ ({
|
|
3047
|
-
type: 'BlockStatement',
|
|
3048
|
-
body: build_render_statements(body_nodes, true, transform_context),
|
|
3049
|
-
metadata: { path: [] },
|
|
3050
|
-
}),
|
|
3051
|
-
async: false,
|
|
3052
|
-
generator: false,
|
|
3053
|
-
expression: false,
|
|
3054
|
-
metadata: { path: [] },
|
|
3055
|
-
},
|
|
3056
|
-
arguments: [],
|
|
3057
|
-
optional: false,
|
|
3058
|
-
metadata: { path: [] },
|
|
3059
|
-
}),
|
|
2442
|
+
b.call(b.arrow([], b.block(build_render_statements(body_nodes, true, transform_context)))),
|
|
3060
2443
|
);
|
|
3061
2444
|
}
|
|
3062
2445
|
|
|
@@ -3107,11 +2490,7 @@ function hook_safe_render_statements(body_nodes, key_expression, transform_conte
|
|
|
3107
2490
|
);
|
|
3108
2491
|
const statements = [...helper.setup_statements];
|
|
3109
2492
|
|
|
3110
|
-
statements.push(
|
|
3111
|
-
type: 'ReturnStatement',
|
|
3112
|
-
argument: helper.component_element,
|
|
3113
|
-
metadata: { path: [] },
|
|
3114
|
-
});
|
|
2493
|
+
statements.push(b.return(helper.component_element));
|
|
3115
2494
|
|
|
3116
2495
|
return statements;
|
|
3117
2496
|
}
|
|
@@ -3307,7 +2686,7 @@ function validate_hook_outer_assignments_in_node(
|
|
|
3307
2686
|
return;
|
|
3308
2687
|
}
|
|
3309
2688
|
|
|
3310
|
-
if (
|
|
2689
|
+
if (is_function_or_component_node(node)) {
|
|
3311
2690
|
return;
|
|
3312
2691
|
}
|
|
3313
2692
|
|
|
@@ -3365,7 +2744,7 @@ function validate_hook_outer_assignments_in_node(
|
|
|
3365
2744
|
);
|
|
3366
2745
|
if (outer_names.length > 0) {
|
|
3367
2746
|
report_hook_outer_assignment_error(
|
|
3368
|
-
node,
|
|
2747
|
+
node.left,
|
|
3369
2748
|
outer_names,
|
|
3370
2749
|
find_first_hook_call_name(node.right) || 'hook',
|
|
3371
2750
|
transform_context,
|
|
@@ -3389,7 +2768,7 @@ function validate_hook_outer_assignments_in_node(
|
|
|
3389
2768
|
);
|
|
3390
2769
|
if (outer_names.length > 0) {
|
|
3391
2770
|
report_hook_outer_assignment_error(
|
|
3392
|
-
node,
|
|
2771
|
+
node.left,
|
|
3393
2772
|
outer_names,
|
|
3394
2773
|
find_first_hook_call_name(node.right) || 'hook',
|
|
3395
2774
|
transform_context,
|
|
@@ -3449,7 +2828,7 @@ function validate_hook_outer_assignments_in_node(
|
|
|
3449
2828
|
function validate_hook_callback_outer_mutations(call_node, shadowed_names, transform_context) {
|
|
3450
2829
|
const hook_name = get_hook_callee_name(call_node.callee);
|
|
3451
2830
|
for (const argument of call_node.arguments || []) {
|
|
3452
|
-
if (!
|
|
2831
|
+
if (!is_function_or_component_node(argument)) {
|
|
3453
2832
|
continue;
|
|
3454
2833
|
}
|
|
3455
2834
|
const callback_shadowed_names = create_function_like_shadowed_names(argument, shadowed_names);
|
|
@@ -3462,21 +2841,6 @@ function validate_hook_callback_outer_mutations(call_node, shadowed_names, trans
|
|
|
3462
2841
|
}
|
|
3463
2842
|
}
|
|
3464
2843
|
|
|
3465
|
-
/**
|
|
3466
|
-
* @param {any} node
|
|
3467
|
-
* @returns {boolean}
|
|
3468
|
-
*/
|
|
3469
|
-
function is_function_like_node(node) {
|
|
3470
|
-
return (
|
|
3471
|
-
node.type === 'FunctionDeclaration' ||
|
|
3472
|
-
node.type === 'FunctionExpression' ||
|
|
3473
|
-
node.type === 'ArrowFunctionExpression' ||
|
|
3474
|
-
// this is just in case but we should already
|
|
3475
|
-
// have a component replaced with a function node
|
|
3476
|
-
node.type === 'Component'
|
|
3477
|
-
);
|
|
3478
|
-
}
|
|
3479
|
-
|
|
3480
2844
|
/**
|
|
3481
2845
|
* @param {any} node
|
|
3482
2846
|
* @param {Set<string>} shadowed_names
|
|
@@ -3524,7 +2888,7 @@ function validate_hook_callback_outer_mutations_in_node(
|
|
|
3524
2888
|
return;
|
|
3525
2889
|
}
|
|
3526
2890
|
|
|
3527
|
-
if (
|
|
2891
|
+
if (is_function_or_component_node(node)) {
|
|
3528
2892
|
validate_hook_callback_outer_mutations_in_node(
|
|
3529
2893
|
node.body,
|
|
3530
2894
|
create_function_like_shadowed_names(node, shadowed_names),
|
|
@@ -3557,7 +2921,12 @@ function validate_hook_callback_outer_mutations_in_node(
|
|
|
3557
2921
|
shadowed_names,
|
|
3558
2922
|
);
|
|
3559
2923
|
if (outer_names.length > 0) {
|
|
3560
|
-
report_hook_callback_outer_mutation_error(
|
|
2924
|
+
report_hook_callback_outer_mutation_error(
|
|
2925
|
+
node.left,
|
|
2926
|
+
outer_names,
|
|
2927
|
+
hook_name,
|
|
2928
|
+
transform_context,
|
|
2929
|
+
);
|
|
3561
2930
|
}
|
|
3562
2931
|
}
|
|
3563
2932
|
|
|
@@ -3568,7 +2937,12 @@ function validate_hook_callback_outer_mutations_in_node(
|
|
|
3568
2937
|
shadowed_names,
|
|
3569
2938
|
);
|
|
3570
2939
|
if (outer_names.length > 0) {
|
|
3571
|
-
report_hook_callback_outer_mutation_error(
|
|
2940
|
+
report_hook_callback_outer_mutation_error(
|
|
2941
|
+
node.argument,
|
|
2942
|
+
outer_names,
|
|
2943
|
+
hook_name,
|
|
2944
|
+
transform_context,
|
|
2945
|
+
);
|
|
3572
2946
|
}
|
|
3573
2947
|
}
|
|
3574
2948
|
|
|
@@ -3863,23 +3237,13 @@ function create_hook_safe_helper(
|
|
|
3863
3237
|
const saved_bindings = transform_context.available_bindings;
|
|
3864
3238
|
transform_context.available_bindings = new Map(saved_bindings);
|
|
3865
3239
|
|
|
3866
|
-
const helper_fn =
|
|
3867
|
-
|
|
3868
|
-
id: clone_identifier(component_id),
|
|
3240
|
+
const helper_fn = b.function(
|
|
3241
|
+
clone_identifier(component_id),
|
|
3869
3242
|
params,
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
},
|
|
3875
|
-
async: false,
|
|
3876
|
-
generator: false,
|
|
3877
|
-
metadata: {
|
|
3878
|
-
path: [],
|
|
3879
|
-
is_component: true,
|
|
3880
|
-
is_method: false,
|
|
3881
|
-
},
|
|
3882
|
-
});
|
|
3243
|
+
b.block(build_render_statements(body_nodes, true, transform_context)),
|
|
3244
|
+
);
|
|
3245
|
+
helper_fn.metadata.is_component = true;
|
|
3246
|
+
helper_fn.metadata.is_method = false;
|
|
3883
3247
|
|
|
3884
3248
|
transform_context.available_bindings = saved_bindings;
|
|
3885
3249
|
|
|
@@ -3945,7 +3309,7 @@ function create_hook_safe_helper(
|
|
|
3945
3309
|
|
|
3946
3310
|
/**
|
|
3947
3311
|
* @param {AST.Identifier} helper_id
|
|
3948
|
-
* @param {
|
|
3312
|
+
* @param {AST.FunctionExpression} helper_fn
|
|
3949
3313
|
* @param {any} source_node
|
|
3950
3314
|
* @param {TransformContext} transform_context
|
|
3951
3315
|
* @returns {any}
|
|
@@ -3958,7 +3322,7 @@ function create_helper_declaration(helper_id, helper_fn, source_node, transform_
|
|
|
3958
3322
|
|
|
3959
3323
|
/**
|
|
3960
3324
|
* @param {AST.Identifier} helper_id
|
|
3961
|
-
* @param {
|
|
3325
|
+
* @param {AST.FunctionExpression} helper_fn
|
|
3962
3326
|
* @param {any} source_node
|
|
3963
3327
|
* @param {TransformContext} transform_context
|
|
3964
3328
|
* @returns {any}
|
|
@@ -3987,32 +3351,7 @@ function create_helper_init_expression(helper_id, helper_fn, source_node, transf
|
|
|
3987
3351
|
* @returns {any}
|
|
3988
3352
|
*/
|
|
3989
3353
|
function create_hook_safe_helper_iife(setup_statements, component_element) {
|
|
3990
|
-
return
|
|
3991
|
-
type: 'CallExpression',
|
|
3992
|
-
callee: {
|
|
3993
|
-
type: 'ArrowFunctionExpression',
|
|
3994
|
-
params: [],
|
|
3995
|
-
body: /** @type {any} */ ({
|
|
3996
|
-
type: 'BlockStatement',
|
|
3997
|
-
body: [
|
|
3998
|
-
...setup_statements,
|
|
3999
|
-
{
|
|
4000
|
-
type: 'ReturnStatement',
|
|
4001
|
-
argument: component_element,
|
|
4002
|
-
metadata: { path: [] },
|
|
4003
|
-
},
|
|
4004
|
-
],
|
|
4005
|
-
metadata: { path: [] },
|
|
4006
|
-
}),
|
|
4007
|
-
async: false,
|
|
4008
|
-
generator: false,
|
|
4009
|
-
expression: false,
|
|
4010
|
-
metadata: { path: [] },
|
|
4011
|
-
},
|
|
4012
|
-
arguments: [],
|
|
4013
|
-
optional: false,
|
|
4014
|
-
metadata: { path: [] },
|
|
4015
|
-
});
|
|
3354
|
+
return b.call(b.arrow([], b.block([...setup_statements, b.return(component_element)])));
|
|
4016
3355
|
}
|
|
4017
3356
|
|
|
4018
3357
|
/**
|
|
@@ -4025,19 +3364,7 @@ function create_helper_type_alias_declaration(helper_id, binding) {
|
|
|
4025
3364
|
|
|
4026
3365
|
return {
|
|
4027
3366
|
id: alias_id,
|
|
4028
|
-
declaration:
|
|
4029
|
-
type: 'VariableDeclaration',
|
|
4030
|
-
kind: 'const',
|
|
4031
|
-
declarations: [
|
|
4032
|
-
{
|
|
4033
|
-
type: 'VariableDeclarator',
|
|
4034
|
-
id: clone_identifier(alias_id),
|
|
4035
|
-
init: create_generated_identifier(binding.name),
|
|
4036
|
-
metadata: { path: [] },
|
|
4037
|
-
},
|
|
4038
|
-
],
|
|
4039
|
-
metadata: { path: [] },
|
|
4040
|
-
}),
|
|
3367
|
+
declaration: b.const(clone_identifier(alias_id), create_generated_identifier(binding.name)),
|
|
4041
3368
|
};
|
|
4042
3369
|
}
|
|
4043
3370
|
|
|
@@ -4047,33 +3374,14 @@ function create_helper_type_alias_declaration(helper_id, binding) {
|
|
|
4047
3374
|
* @returns {any}
|
|
4048
3375
|
*/
|
|
4049
3376
|
function create_helper_props_type_literal(bindings, aliases) {
|
|
4050
|
-
return
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
key: create_generated_identifier(binding.name),
|
|
4057
|
-
computed: false,
|
|
4058
|
-
optional: false,
|
|
4059
|
-
readonly: false,
|
|
4060
|
-
static: false,
|
|
4061
|
-
kind: 'init',
|
|
4062
|
-
typeAnnotation: {
|
|
4063
|
-
type: 'TSTypeAnnotation',
|
|
4064
|
-
typeAnnotation: {
|
|
4065
|
-
type: 'TSTypeQuery',
|
|
4066
|
-
exprName: clone_identifier(aliases[i].id),
|
|
4067
|
-
typeArguments: null,
|
|
4068
|
-
metadata: { path: [] },
|
|
4069
|
-
},
|
|
4070
|
-
metadata: { path: [] },
|
|
4071
|
-
},
|
|
4072
|
-
metadata: { path: [] },
|
|
4073
|
-
}),
|
|
3377
|
+
return b.ts_type_literal(
|
|
3378
|
+
bindings.map((binding, i) =>
|
|
3379
|
+
b.ts_property_signature(
|
|
3380
|
+
create_generated_identifier(binding.name),
|
|
3381
|
+
b.ts_type_annotation(b.ts_type_query(clone_identifier(aliases[i].id))),
|
|
3382
|
+
),
|
|
4074
3383
|
),
|
|
4075
|
-
|
|
4076
|
-
});
|
|
3384
|
+
);
|
|
4077
3385
|
}
|
|
4078
3386
|
|
|
4079
3387
|
/**
|
|
@@ -4083,11 +3391,7 @@ function create_helper_props_type_literal(bindings, aliases) {
|
|
|
4083
3391
|
*/
|
|
4084
3392
|
function create_typed_helper_props_pattern(bindings, props_type) {
|
|
4085
3393
|
const pattern = create_helper_props_pattern(bindings);
|
|
4086
|
-
/** @type {any} */ (pattern).typeAnnotation =
|
|
4087
|
-
type: 'TSTypeAnnotation',
|
|
4088
|
-
typeAnnotation: props_type,
|
|
4089
|
-
metadata: { path: [] },
|
|
4090
|
-
};
|
|
3394
|
+
/** @type {any} */ (pattern).typeAnnotation = b.ts_type_annotation(props_type);
|
|
4091
3395
|
return pattern;
|
|
4092
3396
|
}
|
|
4093
3397
|
|
|
@@ -4096,19 +3400,7 @@ function create_typed_helper_props_pattern(bindings, props_type) {
|
|
|
4096
3400
|
* @returns {any}
|
|
4097
3401
|
*/
|
|
4098
3402
|
function create_helper_cache_declaration(cache_id) {
|
|
4099
|
-
return
|
|
4100
|
-
type: 'VariableDeclaration',
|
|
4101
|
-
kind: 'let',
|
|
4102
|
-
declarations: [
|
|
4103
|
-
{
|
|
4104
|
-
type: 'VariableDeclarator',
|
|
4105
|
-
id: clone_identifier(cache_id),
|
|
4106
|
-
init: null,
|
|
4107
|
-
metadata: { path: [] },
|
|
4108
|
-
},
|
|
4109
|
-
],
|
|
4110
|
-
metadata: { path: [] },
|
|
4111
|
-
});
|
|
3403
|
+
return b.let(clone_identifier(cache_id));
|
|
4112
3404
|
}
|
|
4113
3405
|
|
|
4114
3406
|
/**
|
|
@@ -4118,44 +3410,27 @@ function create_helper_cache_declaration(cache_id) {
|
|
|
4118
3410
|
* @returns {any}
|
|
4119
3411
|
*/
|
|
4120
3412
|
function create_cached_helper_declaration(helper_id, cache_id, helper_init) {
|
|
4121
|
-
return
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
type: 'LogicalExpression',
|
|
4130
|
-
operator: '??',
|
|
4131
|
-
left: clone_identifier(cache_id),
|
|
4132
|
-
right: {
|
|
4133
|
-
type: 'AssignmentExpression',
|
|
4134
|
-
operator: '=',
|
|
4135
|
-
left: clone_identifier(cache_id),
|
|
4136
|
-
right: helper_init,
|
|
4137
|
-
metadata: { path: [] },
|
|
4138
|
-
},
|
|
4139
|
-
metadata: { path: [] },
|
|
4140
|
-
},
|
|
4141
|
-
metadata: { path: [] },
|
|
4142
|
-
},
|
|
4143
|
-
],
|
|
4144
|
-
metadata: { path: [] },
|
|
4145
|
-
});
|
|
3413
|
+
return b.const(
|
|
3414
|
+
clone_identifier(helper_id),
|
|
3415
|
+
b.logical(
|
|
3416
|
+
'??',
|
|
3417
|
+
clone_identifier(cache_id),
|
|
3418
|
+
b.assignment('=', clone_identifier(cache_id), helper_init),
|
|
3419
|
+
),
|
|
3420
|
+
);
|
|
4146
3421
|
}
|
|
4147
3422
|
|
|
4148
3423
|
/**
|
|
4149
3424
|
* @param {AST.Identifier} helper_id
|
|
4150
|
-
* @param {
|
|
3425
|
+
* @param {AST.FunctionExpression} helper_fn
|
|
4151
3426
|
* @returns {AST.FunctionDeclaration}
|
|
4152
3427
|
*/
|
|
4153
3428
|
function create_helper_function_declaration_from_expression(helper_id, helper_fn) {
|
|
4154
|
-
return
|
|
3429
|
+
return {
|
|
4155
3430
|
...helper_fn,
|
|
4156
3431
|
type: 'FunctionDeclaration',
|
|
4157
3432
|
id: clone_identifier(helper_id),
|
|
4158
|
-
}
|
|
3433
|
+
};
|
|
4159
3434
|
}
|
|
4160
3435
|
|
|
4161
3436
|
/**
|
|
@@ -4540,25 +3815,7 @@ function if_statement_to_jsx_child(node, transform_context) {
|
|
|
4540
3815
|
}
|
|
4541
3816
|
|
|
4542
3817
|
return to_jsx_expression_container(
|
|
4543
|
-
|
|
4544
|
-
type: 'CallExpression',
|
|
4545
|
-
callee: {
|
|
4546
|
-
type: 'ArrowFunctionExpression',
|
|
4547
|
-
params: [],
|
|
4548
|
-
body: /** @type {any} */ ({
|
|
4549
|
-
type: 'BlockStatement',
|
|
4550
|
-
body: [render_if_statement, create_null_return_statement()],
|
|
4551
|
-
metadata: { path: [] },
|
|
4552
|
-
}),
|
|
4553
|
-
async: false,
|
|
4554
|
-
generator: false,
|
|
4555
|
-
expression: false,
|
|
4556
|
-
metadata: { path: [] },
|
|
4557
|
-
},
|
|
4558
|
-
arguments: [],
|
|
4559
|
-
optional: false,
|
|
4560
|
-
metadata: { path: [] },
|
|
4561
|
-
}),
|
|
3818
|
+
b.call(b.arrow([], b.block([render_if_statement, create_null_return_statement()]))),
|
|
4562
3819
|
);
|
|
4563
3820
|
}
|
|
4564
3821
|
|
|
@@ -4583,16 +3840,7 @@ function render_if_statement_to_conditional_expression(node) {
|
|
|
4583
3840
|
}
|
|
4584
3841
|
}
|
|
4585
3842
|
|
|
4586
|
-
return set_loc(
|
|
4587
|
-
/** @type {any} */ ({
|
|
4588
|
-
type: 'ConditionalExpression',
|
|
4589
|
-
test: node.test,
|
|
4590
|
-
consequent,
|
|
4591
|
-
alternate,
|
|
4592
|
-
metadata: { path: [] },
|
|
4593
|
-
}),
|
|
4594
|
-
node,
|
|
4595
|
-
);
|
|
3843
|
+
return set_loc(b.conditional(node.test, consequent, alternate), node);
|
|
4596
3844
|
}
|
|
4597
3845
|
|
|
4598
3846
|
/**
|
|
@@ -4663,14 +3911,7 @@ function find_key_expression_in_body(body_nodes) {
|
|
|
4663
3911
|
* @returns {any}
|
|
4664
3912
|
*/
|
|
4665
3913
|
function continue_to_bare_return(source_node) {
|
|
4666
|
-
return set_loc(
|
|
4667
|
-
/** @type {any} */ ({
|
|
4668
|
-
type: 'ReturnStatement',
|
|
4669
|
-
argument: null,
|
|
4670
|
-
metadata: { path: [] },
|
|
4671
|
-
}),
|
|
4672
|
-
source_node,
|
|
4673
|
-
);
|
|
3914
|
+
return set_loc(b.return(null), source_node);
|
|
4674
3915
|
}
|
|
4675
3916
|
|
|
4676
3917
|
/**
|
|
@@ -4796,36 +4037,17 @@ function for_of_statement_to_jsx_child(node, transform_context) {
|
|
|
4796
4037
|
// Restore bindings
|
|
4797
4038
|
transform_context.available_bindings = saved_bindings;
|
|
4798
4039
|
|
|
4040
|
+
const iter_callback = b.arrow(loop_params, b.block(body_statements));
|
|
4041
|
+
|
|
4042
|
+
if (transform_context.platform.imports.forOfIterableHelper) {
|
|
4043
|
+
transform_context.needs_for_of_iterable = true;
|
|
4044
|
+
return to_jsx_expression_container(
|
|
4045
|
+
b.call(b.id(MAP_ITERABLE_INTERNAL_NAME), node.right, iter_callback),
|
|
4046
|
+
);
|
|
4047
|
+
}
|
|
4048
|
+
|
|
4799
4049
|
return to_jsx_expression_container(
|
|
4800
|
-
|
|
4801
|
-
type: 'CallExpression',
|
|
4802
|
-
callee: {
|
|
4803
|
-
type: 'MemberExpression',
|
|
4804
|
-
object: node.right,
|
|
4805
|
-
property: create_generated_identifier('map'),
|
|
4806
|
-
computed: false,
|
|
4807
|
-
optional: false,
|
|
4808
|
-
metadata: { path: [] },
|
|
4809
|
-
},
|
|
4810
|
-
arguments: [
|
|
4811
|
-
{
|
|
4812
|
-
type: 'ArrowFunctionExpression',
|
|
4813
|
-
params: loop_params,
|
|
4814
|
-
body: /** @type {any} */ ({
|
|
4815
|
-
type: 'BlockStatement',
|
|
4816
|
-
body: body_statements,
|
|
4817
|
-
metadata: { path: [] },
|
|
4818
|
-
}),
|
|
4819
|
-
async: false,
|
|
4820
|
-
generator: false,
|
|
4821
|
-
expression: false,
|
|
4822
|
-
metadata: { path: [] },
|
|
4823
|
-
},
|
|
4824
|
-
],
|
|
4825
|
-
async: false,
|
|
4826
|
-
optional: false,
|
|
4827
|
-
metadata: { path: [] },
|
|
4828
|
-
}),
|
|
4050
|
+
b.call(b.member(node.right, create_generated_identifier('map')), iter_callback),
|
|
4829
4051
|
);
|
|
4830
4052
|
}
|
|
4831
4053
|
|
|
@@ -4846,7 +4068,7 @@ function apply_key_to_loop_body(body_nodes, key_expression) {
|
|
|
4846
4068
|
if (!has_key) {
|
|
4847
4069
|
attributes.push({
|
|
4848
4070
|
type: 'Attribute',
|
|
4849
|
-
name:
|
|
4071
|
+
name: b.id('key'),
|
|
4850
4072
|
value: clone_expression_node(key_expression),
|
|
4851
4073
|
shorthand: false,
|
|
4852
4074
|
metadata: { path: [] },
|
|
@@ -4866,15 +4088,10 @@ function apply_key_to_loop_body(body_nodes, key_expression) {
|
|
|
4866
4088
|
|
|
4867
4089
|
if (!has_key) {
|
|
4868
4090
|
attributes.push(
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
clone_expression_node(key_expression),
|
|
4874
|
-
key_expression,
|
|
4875
|
-
),
|
|
4876
|
-
metadata: { path: [] },
|
|
4877
|
-
}),
|
|
4091
|
+
b.jsx_attribute(
|
|
4092
|
+
b.jsx_id('key'),
|
|
4093
|
+
to_jsx_expression_container(clone_expression_node(key_expression), key_expression),
|
|
4094
|
+
),
|
|
4878
4095
|
);
|
|
4879
4096
|
}
|
|
4880
4097
|
return;
|
|
@@ -4969,29 +4186,12 @@ function keyed_fragment_to_jsx_element(fragment, key_expression) {
|
|
|
4969
4186
|
* @returns {ESTreeJSX.JSXExpressionContainer}
|
|
4970
4187
|
*/
|
|
4971
4188
|
function switch_statement_to_jsx_child(node, transform_context) {
|
|
4189
|
+
const { setup_statements, switch_statement } = build_switch_with_lift(node, transform_context);
|
|
4190
|
+
|
|
4972
4191
|
return to_jsx_expression_container(
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
type: 'ArrowFunctionExpression',
|
|
4977
|
-
params: [],
|
|
4978
|
-
body: /** @type {any} */ ({
|
|
4979
|
-
type: 'BlockStatement',
|
|
4980
|
-
body: [
|
|
4981
|
-
create_render_switch_statement(node, transform_context),
|
|
4982
|
-
create_null_return_statement(),
|
|
4983
|
-
],
|
|
4984
|
-
metadata: { path: [] },
|
|
4985
|
-
}),
|
|
4986
|
-
async: false,
|
|
4987
|
-
generator: false,
|
|
4988
|
-
expression: false,
|
|
4989
|
-
metadata: { path: [] },
|
|
4990
|
-
},
|
|
4991
|
-
arguments: [],
|
|
4992
|
-
optional: false,
|
|
4993
|
-
metadata: { path: [] },
|
|
4994
|
-
}),
|
|
4192
|
+
b.call(
|
|
4193
|
+
b.arrow([], b.block([...setup_statements, switch_statement, create_null_return_statement()])),
|
|
4194
|
+
),
|
|
4995
4195
|
);
|
|
4996
4196
|
}
|
|
4997
4197
|
|
|
@@ -5131,19 +4331,10 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
5131
4331
|
catch_scoped_names,
|
|
5132
4332
|
);
|
|
5133
4333
|
|
|
5134
|
-
const fallback_fn =
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
type: 'BlockStatement',
|
|
5139
|
-
body: build_render_statements(catch_body_nodes, true, transform_context),
|
|
5140
|
-
metadata: { path: [] },
|
|
5141
|
-
}),
|
|
5142
|
-
async: false,
|
|
5143
|
-
generator: false,
|
|
5144
|
-
expression: false,
|
|
5145
|
-
metadata: { path: [] },
|
|
5146
|
-
};
|
|
4334
|
+
const fallback_fn = b.arrow(
|
|
4335
|
+
catch_params,
|
|
4336
|
+
b.block(build_render_statements(catch_body_nodes, true, transform_context)),
|
|
4337
|
+
);
|
|
5147
4338
|
|
|
5148
4339
|
transform_context.available_bindings = saved_catch_bindings;
|
|
5149
4340
|
|
|
@@ -5156,40 +4347,10 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
5156
4347
|
|
|
5157
4348
|
if (boundary_content && transform_context.inside_element_child) {
|
|
5158
4349
|
result = to_jsx_expression_container(
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
{
|
|
5164
|
-
type: 'ObjectExpression',
|
|
5165
|
-
properties: [
|
|
5166
|
-
{
|
|
5167
|
-
type: 'Property',
|
|
5168
|
-
key: { type: 'Identifier', name: 'fallback', metadata: { path: [] } },
|
|
5169
|
-
value: fallback_fn,
|
|
5170
|
-
kind: 'init',
|
|
5171
|
-
method: false,
|
|
5172
|
-
shorthand: false,
|
|
5173
|
-
computed: false,
|
|
5174
|
-
metadata: { path: [] },
|
|
5175
|
-
},
|
|
5176
|
-
{
|
|
5177
|
-
type: 'Property',
|
|
5178
|
-
key: { type: 'Identifier', name: 'content', metadata: { path: [] } },
|
|
5179
|
-
value: boundary_content,
|
|
5180
|
-
kind: 'init',
|
|
5181
|
-
method: false,
|
|
5182
|
-
shorthand: false,
|
|
5183
|
-
computed: false,
|
|
5184
|
-
metadata: { path: [] },
|
|
5185
|
-
},
|
|
5186
|
-
],
|
|
5187
|
-
metadata: { path: [] },
|
|
5188
|
-
},
|
|
5189
|
-
],
|
|
5190
|
-
optional: false,
|
|
5191
|
-
metadata: { path: [] },
|
|
5192
|
-
}),
|
|
4350
|
+
b.call(
|
|
4351
|
+
'TsrxErrorBoundary',
|
|
4352
|
+
b.object([b.init('fallback', fallback_fn), b.init('content', boundary_content)]),
|
|
4353
|
+
),
|
|
5193
4354
|
);
|
|
5194
4355
|
|
|
5195
4356
|
return result;
|
|
@@ -5198,21 +4359,12 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
5198
4359
|
result = create_jsx_element(
|
|
5199
4360
|
'TsrxErrorBoundary',
|
|
5200
4361
|
[
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
metadata: { path: [] },
|
|
5206
|
-
},
|
|
4362
|
+
b.jsx_attribute(
|
|
4363
|
+
b.jsx_id('fallback'),
|
|
4364
|
+
to_jsx_expression_container(/** @type {any} */ (fallback_fn)),
|
|
4365
|
+
),
|
|
5207
4366
|
...(boundary_content
|
|
5208
|
-
? [
|
|
5209
|
-
{
|
|
5210
|
-
type: 'JSXAttribute',
|
|
5211
|
-
name: { type: 'JSXIdentifier', name: 'content', metadata: { path: [] } },
|
|
5212
|
-
value: to_jsx_expression_container(boundary_content),
|
|
5213
|
-
metadata: { path: [] },
|
|
5214
|
-
},
|
|
5215
|
-
]
|
|
4367
|
+
? [b.jsx_attribute(b.jsx_id('content'), to_jsx_expression_container(boundary_content))]
|
|
5216
4368
|
: []),
|
|
5217
4369
|
],
|
|
5218
4370
|
boundary_content ? [] : [result],
|
|
@@ -5261,73 +4413,29 @@ function inject_try_imports(program, transform_context, platform, suspense_sourc
|
|
|
5261
4413
|
const imports = [];
|
|
5262
4414
|
|
|
5263
4415
|
if (transform_context.needs_fragment && platform.imports.fragment) {
|
|
5264
|
-
|
|
5265
|
-
imports.push({
|
|
5266
|
-
type: 'ImportDeclaration',
|
|
5267
|
-
specifiers: [
|
|
5268
|
-
{
|
|
5269
|
-
type: 'ImportSpecifier',
|
|
5270
|
-
imported: { type: 'Identifier', name: 'Fragment', metadata: { path: [] } },
|
|
5271
|
-
local: { type: 'Identifier', name: 'Fragment', metadata: { path: [] } },
|
|
5272
|
-
metadata: { path: [] },
|
|
5273
|
-
},
|
|
5274
|
-
],
|
|
5275
|
-
source: {
|
|
5276
|
-
type: 'Literal',
|
|
5277
|
-
value: fragment_source,
|
|
5278
|
-
raw: `'${fragment_source}'`,
|
|
5279
|
-
},
|
|
5280
|
-
metadata: { path: [] },
|
|
5281
|
-
});
|
|
4416
|
+
imports.push(b.imports([['Fragment', 'Fragment']], platform.imports.fragment));
|
|
5282
4417
|
}
|
|
5283
4418
|
|
|
5284
4419
|
if (transform_context.needs_suspense) {
|
|
5285
|
-
imports.push(
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
raw: `'${suspense_source}'`,
|
|
5299
|
-
},
|
|
5300
|
-
metadata: { path: [] },
|
|
5301
|
-
});
|
|
4420
|
+
imports.push(b.imports([['Suspense', 'Suspense']], suspense_source));
|
|
4421
|
+
}
|
|
4422
|
+
|
|
4423
|
+
if (transform_context.needs_for_of_iterable && platform.imports.forOfIterableHelper) {
|
|
4424
|
+
const specifiers = [b.import_specifier('map_iterable', MAP_ITERABLE_INTERNAL_NAME)];
|
|
4425
|
+
// The loop-scoped type alias `IterationValue<typeof source>` only
|
|
4426
|
+
// appears in the output when at least one hook-bearing for-of body
|
|
4427
|
+
// was lowered with non-module-scoped helpers (editor tooling sets
|
|
4428
|
+
// this for typeOnly virtual modules).
|
|
4429
|
+
if (transform_context.needs_iteration_value_type) {
|
|
4430
|
+
specifiers.push(b.import_specifier('IterationValue', ITERATION_VALUE_INTERNAL_NAME, 'type'));
|
|
4431
|
+
}
|
|
4432
|
+
imports.push(b.import_declaration(specifiers, platform.imports.forOfIterableHelper));
|
|
5302
4433
|
}
|
|
5303
4434
|
|
|
5304
4435
|
if (transform_context.needs_error_boundary) {
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
specifiers: [
|
|
5309
|
-
{
|
|
5310
|
-
type: 'ImportSpecifier',
|
|
5311
|
-
imported: {
|
|
5312
|
-
type: 'Identifier',
|
|
5313
|
-
name: 'TsrxErrorBoundary',
|
|
5314
|
-
metadata: { path: [] },
|
|
5315
|
-
},
|
|
5316
|
-
local: {
|
|
5317
|
-
type: 'Identifier',
|
|
5318
|
-
name: 'TsrxErrorBoundary',
|
|
5319
|
-
metadata: { path: [] },
|
|
5320
|
-
},
|
|
5321
|
-
metadata: { path: [] },
|
|
5322
|
-
},
|
|
5323
|
-
],
|
|
5324
|
-
source: {
|
|
5325
|
-
type: 'Literal',
|
|
5326
|
-
value: error_boundary_source,
|
|
5327
|
-
raw: `'${error_boundary_source}'`,
|
|
5328
|
-
},
|
|
5329
|
-
metadata: { path: [] },
|
|
5330
|
-
});
|
|
4436
|
+
imports.push(
|
|
4437
|
+
b.imports([['TsrxErrorBoundary', 'TsrxErrorBoundary']], platform.imports.errorBoundary),
|
|
4438
|
+
);
|
|
5331
4439
|
}
|
|
5332
4440
|
|
|
5333
4441
|
const merge_refs_source =
|
|
@@ -5345,67 +4453,31 @@ function inject_try_imports(program, transform_context, platform, suspense_sourc
|
|
|
5345
4453
|
const ref_imports = new Map();
|
|
5346
4454
|
|
|
5347
4455
|
if (merge_refs_source !== null) {
|
|
5348
|
-
add_ref_import_specifier(
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
metadata: { path: [] },
|
|
5354
|
-
},
|
|
5355
|
-
local: {
|
|
5356
|
-
type: 'Identifier',
|
|
5357
|
-
name: MERGE_REFS_INTERNAL_NAME,
|
|
5358
|
-
metadata: { path: [] },
|
|
5359
|
-
},
|
|
5360
|
-
metadata: { path: [] },
|
|
5361
|
-
});
|
|
4456
|
+
add_ref_import_specifier(
|
|
4457
|
+
ref_imports,
|
|
4458
|
+
merge_refs_source,
|
|
4459
|
+
b.import_specifier('mergeRefs', MERGE_REFS_INTERNAL_NAME),
|
|
4460
|
+
);
|
|
5362
4461
|
}
|
|
5363
4462
|
|
|
5364
4463
|
if (ref_prop_source !== null) {
|
|
5365
|
-
add_ref_import_specifier(
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
metadata: { path: [] },
|
|
5371
|
-
},
|
|
5372
|
-
local: {
|
|
5373
|
-
type: 'Identifier',
|
|
5374
|
-
name: CREATE_REF_PROP_INTERNAL_NAME,
|
|
5375
|
-
metadata: { path: [] },
|
|
5376
|
-
},
|
|
5377
|
-
metadata: { path: [] },
|
|
5378
|
-
});
|
|
4464
|
+
add_ref_import_specifier(
|
|
4465
|
+
ref_imports,
|
|
4466
|
+
ref_prop_source,
|
|
4467
|
+
b.import_specifier('create_ref_prop', CREATE_REF_PROP_INTERNAL_NAME),
|
|
4468
|
+
);
|
|
5379
4469
|
}
|
|
5380
4470
|
|
|
5381
4471
|
if (normalize_spread_props_source !== null) {
|
|
5382
|
-
add_ref_import_specifier(
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
metadata: { path: [] },
|
|
5388
|
-
},
|
|
5389
|
-
local: {
|
|
5390
|
-
type: 'Identifier',
|
|
5391
|
-
name: NORMALIZE_SPREAD_PROPS_INTERNAL_NAME,
|
|
5392
|
-
metadata: { path: [] },
|
|
5393
|
-
},
|
|
5394
|
-
metadata: { path: [] },
|
|
5395
|
-
});
|
|
4472
|
+
add_ref_import_specifier(
|
|
4473
|
+
ref_imports,
|
|
4474
|
+
normalize_spread_props_source,
|
|
4475
|
+
b.import_specifier('normalize_spread_props', NORMALIZE_SPREAD_PROPS_INTERNAL_NAME),
|
|
4476
|
+
);
|
|
5396
4477
|
}
|
|
5397
4478
|
|
|
5398
4479
|
for (const [source, ref_specifiers] of ref_imports) {
|
|
5399
|
-
imports.push(
|
|
5400
|
-
type: 'ImportDeclaration',
|
|
5401
|
-
specifiers: ref_specifiers,
|
|
5402
|
-
source: {
|
|
5403
|
-
type: 'Literal',
|
|
5404
|
-
value: source,
|
|
5405
|
-
raw: `'${source}'`,
|
|
5406
|
-
},
|
|
5407
|
-
metadata: { path: [] },
|
|
5408
|
-
});
|
|
4480
|
+
imports.push(b.import_declaration(ref_specifiers, source));
|
|
5409
4481
|
}
|
|
5410
4482
|
|
|
5411
4483
|
if (imports.length > 0) {
|
|
@@ -5453,130 +4525,343 @@ function create_render_if_statement(node, transform_context) {
|
|
|
5453
4525
|
true,
|
|
5454
4526
|
);
|
|
5455
4527
|
alternate = set_loc(
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
body: alternate_has_hooks
|
|
4528
|
+
b.block(
|
|
4529
|
+
alternate_has_hooks
|
|
5459
4530
|
? hook_safe_render_statements(alternate_body, undefined, transform_context)
|
|
5460
4531
|
: build_render_statements(alternate_body, true, transform_context),
|
|
5461
|
-
|
|
5462
|
-
}),
|
|
4532
|
+
),
|
|
5463
4533
|
node.alternate,
|
|
5464
4534
|
);
|
|
5465
4535
|
}
|
|
5466
4536
|
}
|
|
5467
4537
|
|
|
5468
4538
|
return set_loc(
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
type: 'BlockStatement',
|
|
5475
|
-
body: consequent_has_hooks
|
|
4539
|
+
b.if(
|
|
4540
|
+
node.test,
|
|
4541
|
+
set_loc(
|
|
4542
|
+
b.block(
|
|
4543
|
+
consequent_has_hooks
|
|
5476
4544
|
? hook_safe_render_statements(consequent_body, undefined, transform_context)
|
|
5477
4545
|
: build_render_statements(consequent_body, true, transform_context),
|
|
5478
|
-
|
|
5479
|
-
}),
|
|
4546
|
+
),
|
|
5480
4547
|
node.consequent,
|
|
5481
4548
|
),
|
|
5482
4549
|
alternate,
|
|
5483
|
-
|
|
4550
|
+
),
|
|
5484
4551
|
node,
|
|
5485
4552
|
);
|
|
5486
4553
|
}
|
|
5487
4554
|
|
|
5488
4555
|
/**
|
|
5489
|
-
*
|
|
5490
|
-
*
|
|
5491
|
-
*
|
|
4556
|
+
* Per-source-case information used by the switch lift to decide whether each
|
|
4557
|
+
* case body needs to be hoisted into its own helper component or can stay
|
|
4558
|
+
* inline.
|
|
4559
|
+
*
|
|
4560
|
+
* `own_body` is everything in the case's `consequent` up to (and including for
|
|
4561
|
+
* `return <expr>`, excluding for `break` / bare `return;`) the first
|
|
4562
|
+
* terminator. `has_terminator` records whether such a terminator was seen.
|
|
4563
|
+
*
|
|
4564
|
+
* @param {any[]} consequent
|
|
4565
|
+
* @returns {{ own_body: any[], has_terminator: boolean }}
|
|
5492
4566
|
*/
|
|
5493
|
-
function
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
4567
|
+
function summarize_switch_case_body(consequent) {
|
|
4568
|
+
const own_body = [];
|
|
4569
|
+
let has_terminator = false;
|
|
4570
|
+
for (const child of consequent) {
|
|
4571
|
+
if (child.type === 'BreakStatement') {
|
|
4572
|
+
has_terminator = true;
|
|
4573
|
+
break;
|
|
4574
|
+
}
|
|
4575
|
+
if (child.type === 'ReturnStatement' && child.argument == null) {
|
|
4576
|
+
has_terminator = true;
|
|
4577
|
+
break;
|
|
4578
|
+
}
|
|
4579
|
+
own_body.push(child);
|
|
4580
|
+
if (child.type === 'ReturnStatement') {
|
|
4581
|
+
// `return <expr>;` — keep it in own_body so build_render_statements
|
|
4582
|
+
// can emit it as the terminal return for this case, then stop
|
|
4583
|
+
// collecting further nodes.
|
|
4584
|
+
has_terminator = true;
|
|
4585
|
+
break;
|
|
4586
|
+
}
|
|
4587
|
+
}
|
|
4588
|
+
return { own_body, has_terminator };
|
|
5502
4589
|
}
|
|
5503
4590
|
|
|
5504
4591
|
/**
|
|
5505
|
-
*
|
|
5506
|
-
*
|
|
4592
|
+
* Clone a helper's `component_element` for embedding in another case arm or
|
|
4593
|
+
* inside another helper's body. Locations are stripped because the same
|
|
4594
|
+
* element appears in multiple positions; only the helper's *definition* (the
|
|
4595
|
+
* lifted function) keeps the source position so editor IntelliSense doesn't
|
|
4596
|
+
* see double/triple hits per source range.
|
|
4597
|
+
*
|
|
4598
|
+
* @param {{ component_element: ESTreeJSX.JSXElement }} helper
|
|
5507
4599
|
* @returns {any}
|
|
5508
4600
|
*/
|
|
5509
|
-
function
|
|
5510
|
-
|
|
4601
|
+
export function clone_switch_helper_invocation(helper) {
|
|
4602
|
+
return clone_expression_node(helper.component_element, false);
|
|
4603
|
+
}
|
|
5511
4604
|
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
4605
|
+
/**
|
|
4606
|
+
* Plan the switch lift: decide which case bodies to hoist into their own
|
|
4607
|
+
* helper components, build them in reverse so each helper can chain into the
|
|
4608
|
+
* next, and return everything callers need to construct a target-specific
|
|
4609
|
+
* switch shape (a JS `switch` for React/Preact/Vue or `<Switch>/<Match>` for
|
|
4610
|
+
* Solid). Centralizes the lift bookkeeping so both consumers see the same
|
|
4611
|
+
* hook-detection rules, duplication analysis, and helper-id numbering.
|
|
4612
|
+
*
|
|
4613
|
+
* Returned helpers — when non-null — are already constructed via
|
|
4614
|
+
* `create_hook_safe_helper`, which is the same path hook-bearing case bodies
|
|
4615
|
+
* have always used. Locally-scoped helpers have their declarations in
|
|
4616
|
+
* `setup_statements`; module-scoped helpers (the client transform default on
|
|
4617
|
+
* React, Vue, and Solid) already pushed their declarations into
|
|
4618
|
+
* `transform_context.helper_state.helpers`, so `setup_statements` is empty.
|
|
4619
|
+
*
|
|
4620
|
+
* @param {any} switch_node
|
|
4621
|
+
* @param {TransformContext} transform_context
|
|
4622
|
+
* @returns {{
|
|
4623
|
+
* case_info: Array<{ own_body: any[], has_terminator: boolean }>,
|
|
4624
|
+
* case_helpers: Array<{ setup_statements: any[], component_element: ESTreeJSX.JSXElement } | null>,
|
|
4625
|
+
* find_next_helper_after: (from_index: number) => { component_element: ESTreeJSX.JSXElement } | null,
|
|
4626
|
+
* setup_statements: any[],
|
|
4627
|
+
* }}
|
|
4628
|
+
*/
|
|
4629
|
+
export function plan_switch_lift(switch_node, transform_context) {
|
|
4630
|
+
const case_info = switch_node.cases.map((/** @type {any} */ c) => {
|
|
4631
|
+
const consequent = flatten_switch_consequent(c.consequent || []);
|
|
4632
|
+
return summarize_switch_case_body(consequent);
|
|
4633
|
+
});
|
|
5518
4634
|
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
})
|
|
5526
|
-
|
|
4635
|
+
// A case body needs to be lifted iff (a) it would render in more than one
|
|
4636
|
+
// arm after fall-through expansion, or (b) it contains hooks (which always
|
|
4637
|
+
// went through the lift pipeline before this change). Duplication happens
|
|
4638
|
+
// exactly when the previous case has no terminator — that's the only way
|
|
4639
|
+
// an earlier arm can reach this body via JS fall-through semantics.
|
|
4640
|
+
const needs_helper = case_info.map(
|
|
4641
|
+
(/** @type {{ own_body: any[], has_terminator: boolean }} */ info, /** @type {number} */ k) => {
|
|
4642
|
+
if (info.own_body.length === 0) return false;
|
|
4643
|
+
if (body_contains_top_level_hook_call(info.own_body, transform_context, true)) {
|
|
4644
|
+
return true;
|
|
4645
|
+
}
|
|
4646
|
+
if (k === 0) return false;
|
|
4647
|
+
return !case_info[k - 1].has_terminator;
|
|
4648
|
+
},
|
|
4649
|
+
);
|
|
5527
4650
|
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
4651
|
+
// Pre-allocate helper ids in source order so the snapshot's
|
|
4652
|
+
// `StatementBodyHook<N>` numbering reads top-to-bottom by case position
|
|
4653
|
+
// even though we build helpers in reverse below.
|
|
4654
|
+
/** @type {Array<AST.Identifier | null>} */
|
|
4655
|
+
const helper_ids = needs_helper.map((/** @type {boolean} */ needs) =>
|
|
4656
|
+
needs
|
|
4657
|
+
? create_generated_identifier(create_local_statement_component_name(transform_context))
|
|
4658
|
+
: null,
|
|
4659
|
+
);
|
|
5531
4660
|
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
if (render_nodes.length > 0 && !has_terminal) {
|
|
5535
|
-
case_body.push(create_component_return_statement(render_nodes, switch_case));
|
|
5536
|
-
} else if (!has_terminal) {
|
|
5537
|
-
case_body.push(child);
|
|
5538
|
-
}
|
|
5539
|
-
has_terminal = true;
|
|
5540
|
-
break;
|
|
5541
|
-
}
|
|
4661
|
+
/** @type {Array<{ setup_statements: any[], component_element: ESTreeJSX.JSXElement } | null>} */
|
|
4662
|
+
const case_helpers = new Array(switch_node.cases.length).fill(null);
|
|
5542
4663
|
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
4664
|
+
/**
|
|
4665
|
+
* Find the next downstream helper this arm chains into when it has no
|
|
4666
|
+
* terminator: scan forward past any empty cases until we hit either a
|
|
4667
|
+
* helper-bearing case or a case whose body has a terminator (which stops
|
|
4668
|
+
* the chain — JS would have `break`/`return`ed out at that point).
|
|
4669
|
+
*
|
|
4670
|
+
* @param {number} from_index
|
|
4671
|
+
* @returns {{ component_element: ESTreeJSX.JSXElement } | null}
|
|
4672
|
+
*/
|
|
4673
|
+
function find_next_helper_after(from_index) {
|
|
4674
|
+
for (let j = from_index + 1; j < switch_node.cases.length; j++) {
|
|
4675
|
+
if (case_helpers[j]) return case_helpers[j];
|
|
4676
|
+
if (case_info[j].has_terminator) return null;
|
|
5547
4677
|
}
|
|
4678
|
+
return null;
|
|
4679
|
+
}
|
|
5548
4680
|
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
4681
|
+
for (let i = switch_node.cases.length - 1; i >= 0; i--) {
|
|
4682
|
+
if (!needs_helper[i]) continue;
|
|
4683
|
+
const { own_body, has_terminator } = case_info[i];
|
|
4684
|
+
|
|
4685
|
+
let helper_body = own_body;
|
|
4686
|
+
if (!has_terminator) {
|
|
4687
|
+
const next_helper = find_next_helper_after(i);
|
|
4688
|
+
if (next_helper) {
|
|
4689
|
+
helper_body = [...own_body, clone_switch_helper_invocation(next_helper)];
|
|
4690
|
+
}
|
|
5555
4691
|
}
|
|
4692
|
+
|
|
4693
|
+
case_helpers[i] = create_hook_safe_helper(
|
|
4694
|
+
helper_body,
|
|
4695
|
+
undefined,
|
|
4696
|
+
switch_node.cases[i],
|
|
4697
|
+
transform_context,
|
|
4698
|
+
/** @type {any} */ (helper_ids[i]),
|
|
4699
|
+
);
|
|
5556
4700
|
}
|
|
5557
4701
|
|
|
5558
|
-
|
|
5559
|
-
|
|
4702
|
+
// Hoist all helpers' setup statements above the switch in source order so
|
|
4703
|
+
// the switch body stays a pure dispatcher.
|
|
4704
|
+
const setup_statements = [];
|
|
4705
|
+
for (const helper of case_helpers) {
|
|
4706
|
+
if (helper) setup_statements.push(...helper.setup_statements);
|
|
5560
4707
|
}
|
|
5561
4708
|
|
|
5562
|
-
return
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
}
|
|
4709
|
+
return {
|
|
4710
|
+
case_info,
|
|
4711
|
+
case_helpers,
|
|
4712
|
+
find_next_helper_after,
|
|
4713
|
+
setup_statements,
|
|
4714
|
+
};
|
|
5568
4715
|
}
|
|
5569
4716
|
|
|
5570
4717
|
/**
|
|
5571
|
-
*
|
|
4718
|
+
* Switch lift for fall-through deduplication. Reuses the same `create_hook_safe_helper`
|
|
4719
|
+
* pipeline as hook-bearing case bodies: every case whose body would otherwise
|
|
4720
|
+
* appear in 2+ arms (because the previous case had no `break` / `return`) is
|
|
4721
|
+
* hoisted into its own helper component, and each upstream arm references the
|
|
4722
|
+
* next helper at the end of its own body to materialize JS fall-through at
|
|
4723
|
+
* render time. Cases whose bodies live in exactly one arm stay inline so the
|
|
4724
|
+
* common (break-terminated) shape compiles to the same simple switch as before
|
|
4725
|
+
* the lift was introduced.
|
|
4726
|
+
*
|
|
4727
|
+
* The chain pattern:
|
|
4728
|
+
* helper_idle = () => <><Online/><Helper_active/></>
|
|
4729
|
+
* helper_active = () => <><Away/><Helper_offline/></>
|
|
4730
|
+
* helper_offline = () => <Offline/>
|
|
4731
|
+
*
|
|
4732
|
+
* case "idle": return <Helper_idle/>
|
|
4733
|
+
* case "active": return <Helper_active/>
|
|
4734
|
+
* case "offline": return <Helper_offline/>
|
|
4735
|
+
*
|
|
4736
|
+
* Each case body appears exactly once in the generated module — matching how
|
|
4737
|
+
* we already handle hook-bearing case bodies — which keeps the bundle from
|
|
4738
|
+
* growing quadratically in case count and means editor mappings are 1:1.
|
|
4739
|
+
*
|
|
4740
|
+
* @param {any} switch_node
|
|
4741
|
+
* @param {TransformContext} transform_context
|
|
4742
|
+
* @returns {{ setup_statements: any[], switch_statement: any }}
|
|
5572
4743
|
*/
|
|
5573
|
-
function
|
|
4744
|
+
function build_switch_with_lift(switch_node, transform_context) {
|
|
4745
|
+
const { case_info, case_helpers, find_next_helper_after, setup_statements } = plan_switch_lift(
|
|
4746
|
+
switch_node,
|
|
4747
|
+
transform_context,
|
|
4748
|
+
);
|
|
4749
|
+
|
|
4750
|
+
const new_cases = switch_node.cases.map(
|
|
4751
|
+
(/** @type {any} */ original_case, /** @type {number} */ i) => {
|
|
4752
|
+
const helper = case_helpers[i];
|
|
4753
|
+
if (helper) {
|
|
4754
|
+
return /** @type {any} */ ({
|
|
4755
|
+
type: 'SwitchCase',
|
|
4756
|
+
test: original_case.test,
|
|
4757
|
+
consequent: [
|
|
4758
|
+
create_component_return_statement([helper.component_element], original_case),
|
|
4759
|
+
],
|
|
4760
|
+
metadata: { path: [] },
|
|
4761
|
+
});
|
|
4762
|
+
}
|
|
4763
|
+
|
|
4764
|
+
const { own_body, has_terminator } = case_info[i];
|
|
4765
|
+
|
|
4766
|
+
if (own_body.length === 0 && !has_terminator) {
|
|
4767
|
+
// Alias-pattern empty case (`case 'a': case 'b': ...`) — keep
|
|
4768
|
+
// the arm body empty so JS falls through to the next case at
|
|
4769
|
+
// runtime, where the helper invocation actually lives.
|
|
4770
|
+
return /** @type {any} */ ({
|
|
4771
|
+
type: 'SwitchCase',
|
|
4772
|
+
test: original_case.test,
|
|
4773
|
+
consequent: [],
|
|
4774
|
+
metadata: { path: [] },
|
|
4775
|
+
});
|
|
4776
|
+
}
|
|
4777
|
+
|
|
4778
|
+
const case_body = [];
|
|
4779
|
+
const render_nodes = [];
|
|
4780
|
+
let has_terminal = false;
|
|
4781
|
+
|
|
4782
|
+
for (const child of own_body) {
|
|
4783
|
+
if (is_bare_return_statement(child)) {
|
|
4784
|
+
case_body.push(create_component_return_statement(render_nodes, child));
|
|
4785
|
+
has_terminal = true;
|
|
4786
|
+
break;
|
|
4787
|
+
}
|
|
4788
|
+
if (child.type === 'ReturnStatement') {
|
|
4789
|
+
case_body.push(child);
|
|
4790
|
+
has_terminal = true;
|
|
4791
|
+
break;
|
|
4792
|
+
}
|
|
4793
|
+
if (is_jsx_child(child)) {
|
|
4794
|
+
render_nodes.push(to_jsx_child(child, transform_context));
|
|
4795
|
+
} else if (is_bare_render_expression(child)) {
|
|
4796
|
+
render_nodes.push(to_jsx_expression_container(child, child));
|
|
4797
|
+
} else {
|
|
4798
|
+
case_body.push(child);
|
|
4799
|
+
}
|
|
4800
|
+
}
|
|
4801
|
+
|
|
4802
|
+
if (!has_terminal && !has_terminator) {
|
|
4803
|
+
const next_helper = find_next_helper_after(i);
|
|
4804
|
+
if (next_helper) {
|
|
4805
|
+
render_nodes.push(clone_switch_helper_invocation(next_helper));
|
|
4806
|
+
}
|
|
4807
|
+
}
|
|
4808
|
+
|
|
4809
|
+
if (!has_terminal) {
|
|
4810
|
+
if (render_nodes.length > 0) {
|
|
4811
|
+
case_body.push(create_component_return_statement(render_nodes, original_case));
|
|
4812
|
+
} else if (has_terminator) {
|
|
4813
|
+
// Empty body with explicit `break;` / bare `return;` — keep
|
|
4814
|
+
// a `break` so JS doesn't fall through into the next case
|
|
4815
|
+
// (which may now hold the lifted helper invocation).
|
|
4816
|
+
case_body.push(
|
|
4817
|
+
/** @type {any} */ ({
|
|
4818
|
+
type: 'BreakStatement',
|
|
4819
|
+
label: null,
|
|
4820
|
+
metadata: { path: [] },
|
|
4821
|
+
}),
|
|
4822
|
+
);
|
|
4823
|
+
} else if (case_body.length > 0) {
|
|
4824
|
+
// Statements-only inline case without terminator. We've
|
|
4825
|
+
// already inlined the downstream chain via the helper
|
|
4826
|
+
// reference above, so emit a `break` to stop the runtime
|
|
4827
|
+
// from re-running downstream statements via JS fall-through.
|
|
4828
|
+
case_body.push(
|
|
4829
|
+
/** @type {any} */ ({
|
|
4830
|
+
type: 'BreakStatement',
|
|
4831
|
+
label: null,
|
|
4832
|
+
metadata: { path: [] },
|
|
4833
|
+
}),
|
|
4834
|
+
);
|
|
4835
|
+
}
|
|
4836
|
+
}
|
|
4837
|
+
|
|
4838
|
+
return /** @type {any} */ ({
|
|
4839
|
+
type: 'SwitchCase',
|
|
4840
|
+
test: original_case.test,
|
|
4841
|
+
consequent: case_body,
|
|
4842
|
+
metadata: { path: [] },
|
|
4843
|
+
});
|
|
4844
|
+
},
|
|
4845
|
+
);
|
|
4846
|
+
|
|
5574
4847
|
return {
|
|
5575
|
-
|
|
5576
|
-
|
|
4848
|
+
setup_statements,
|
|
4849
|
+
switch_statement: /** @type {any} */ ({
|
|
4850
|
+
type: 'SwitchStatement',
|
|
4851
|
+
discriminant: switch_node.discriminant,
|
|
4852
|
+
cases: new_cases,
|
|
4853
|
+
metadata: { path: [] },
|
|
4854
|
+
}),
|
|
5577
4855
|
};
|
|
5578
4856
|
}
|
|
5579
4857
|
|
|
4858
|
+
/**
|
|
4859
|
+
* @returns {any}
|
|
4860
|
+
*/
|
|
4861
|
+
function create_null_return_statement() {
|
|
4862
|
+
return b.return(b.literal(null));
|
|
4863
|
+
}
|
|
4864
|
+
|
|
5580
4865
|
/**
|
|
5581
4866
|
* @param {AST.Expression} expression
|
|
5582
4867
|
* @param {any} [source_node]
|
|
@@ -5675,10 +4960,7 @@ function normalize_named_ref_attributes(attrs, is_host, transform_context) {
|
|
|
5675
4960
|
return {
|
|
5676
4961
|
...attr,
|
|
5677
4962
|
metadata: { ...(attr.metadata || {}), from_ref_keyword: true },
|
|
5678
|
-
name:
|
|
5679
|
-
attr.name?.type === 'JSXIdentifier'
|
|
5680
|
-
? { ...attr.name, name: 'ref' }
|
|
5681
|
-
: { type: 'Identifier', name: 'ref', metadata: { path: [] } },
|
|
4963
|
+
name: attr.name?.type === 'JSXIdentifier' ? { ...attr.name, name: 'ref' } : b.id('ref'),
|
|
5682
4964
|
};
|
|
5683
4965
|
});
|
|
5684
4966
|
}
|
|
@@ -5819,6 +5101,7 @@ function wrap_jsx_setup_declarations(expression, in_jsx_child) {
|
|
|
5819
5101
|
[],
|
|
5820
5102
|
b.block([...declarations, b.return(return_expression)], expression),
|
|
5821
5103
|
false,
|
|
5104
|
+
undefined,
|
|
5822
5105
|
expression,
|
|
5823
5106
|
),
|
|
5824
5107
|
);
|
|
@@ -5970,22 +5253,8 @@ export function merge_duplicate_refs(jsx_attrs, transform_context) {
|
|
|
5970
5253
|
|
|
5971
5254
|
const merged_value =
|
|
5972
5255
|
strategy === 'merge-refs'
|
|
5973
|
-
?
|
|
5974
|
-
|
|
5975
|
-
callee: {
|
|
5976
|
-
type: 'Identifier',
|
|
5977
|
-
name: MERGE_REFS_INTERNAL_NAME,
|
|
5978
|
-
metadata: { path: [] },
|
|
5979
|
-
},
|
|
5980
|
-
arguments: ref_exprs,
|
|
5981
|
-
optional: false,
|
|
5982
|
-
metadata: { path: [] },
|
|
5983
|
-
})
|
|
5984
|
-
: /** @type {any} */ ({
|
|
5985
|
-
type: 'ArrayExpression',
|
|
5986
|
-
elements: ref_exprs,
|
|
5987
|
-
metadata: { path: [] },
|
|
5988
|
-
});
|
|
5256
|
+
? b.call(b.id(MERGE_REFS_INTERNAL_NAME), ...ref_exprs)
|
|
5257
|
+
: b.array(ref_exprs);
|
|
5989
5258
|
|
|
5990
5259
|
if (strategy === 'merge-refs') {
|
|
5991
5260
|
transform_context.needs_merge_refs = true;
|
|
@@ -5999,11 +5268,7 @@ export function merge_duplicate_refs(jsx_attrs, transform_context) {
|
|
|
5999
5268
|
const merged_name = build_jsx_id('ref', source_attr?.name);
|
|
6000
5269
|
const merged_attr = build_jsx_attribute(
|
|
6001
5270
|
merged_name,
|
|
6002
|
-
|
|
6003
|
-
type: 'JSXExpressionContainer',
|
|
6004
|
-
expression: merged_value,
|
|
6005
|
-
metadata: { path: [] },
|
|
6006
|
-
}),
|
|
5271
|
+
b.jsx_expression_container(merged_value),
|
|
6007
5272
|
false,
|
|
6008
5273
|
source_attr,
|
|
6009
5274
|
);
|
|
@@ -6038,6 +5303,8 @@ function is_jsx_ref_attribute(attr) {
|
|
|
6038
5303
|
export const MERGE_REFS_INTERNAL_NAME = '__mergeRefs';
|
|
6039
5304
|
export const CREATE_REF_PROP_INTERNAL_NAME = '__create_ref_prop';
|
|
6040
5305
|
export const NORMALIZE_SPREAD_PROPS_INTERNAL_NAME = '__normalize_spread_props';
|
|
5306
|
+
export const MAP_ITERABLE_INTERNAL_NAME = '__map_iterable';
|
|
5307
|
+
export const ITERATION_VALUE_INTERNAL_NAME = '__IterationValue';
|
|
6041
5308
|
|
|
6042
5309
|
/**
|
|
6043
5310
|
* @param {any} attr
|
|
@@ -6111,10 +5378,7 @@ export function to_jsx_attribute(attr, transform_context) {
|
|
|
6111
5378
|
attr_name.type === 'Identifier' &&
|
|
6112
5379
|
attr_name.name === 'class'
|
|
6113
5380
|
) {
|
|
6114
|
-
attr_name = set_loc(
|
|
6115
|
-
/** @type {any} */ ({ type: 'Identifier', name: 'className', metadata: { path: [] } }),
|
|
6116
|
-
attr.name,
|
|
6117
|
-
);
|
|
5381
|
+
attr_name = set_loc(b.id('className'), attr.name);
|
|
6118
5382
|
}
|
|
6119
5383
|
|
|
6120
5384
|
const name =
|
|
@@ -6177,16 +5441,7 @@ function create_ref_prop_call(node, transform_context) {
|
|
|
6177
5441
|
|
|
6178
5442
|
if (argument.type === 'Identifier' || argument.type === 'MemberExpression') {
|
|
6179
5443
|
args.push(
|
|
6180
|
-
b.arrow(
|
|
6181
|
-
[b.id('v')],
|
|
6182
|
-
/** @type {any} */ ({
|
|
6183
|
-
type: 'AssignmentExpression',
|
|
6184
|
-
operator: '=',
|
|
6185
|
-
left: clone_expression_node(argument, false),
|
|
6186
|
-
right: b.id('v'),
|
|
6187
|
-
metadata: { path: [] },
|
|
6188
|
-
}),
|
|
6189
|
-
),
|
|
5444
|
+
b.arrow([b.id('v')], b.assignment('=', clone_expression_node(argument, false), b.id('v'))),
|
|
6190
5445
|
);
|
|
6191
5446
|
}
|
|
6192
5447
|
|
|
@@ -6200,57 +5455,19 @@ function create_ref_prop_call(node, transform_context) {
|
|
|
6200
5455
|
*/
|
|
6201
5456
|
function dynamic_element_to_jsx_child(node, transform_context) {
|
|
6202
5457
|
const dynamic_id = set_loc(create_generated_identifier('DynamicElement'), node.id);
|
|
6203
|
-
const alias_declaration = set_loc(
|
|
6204
|
-
/** @type {any} */ ({
|
|
6205
|
-
type: 'VariableDeclaration',
|
|
6206
|
-
kind: 'const',
|
|
6207
|
-
declarations: [
|
|
6208
|
-
{
|
|
6209
|
-
type: 'VariableDeclarator',
|
|
6210
|
-
id: dynamic_id,
|
|
6211
|
-
init: clone_expression_node(node.id),
|
|
6212
|
-
metadata: { path: [] },
|
|
6213
|
-
},
|
|
6214
|
-
],
|
|
6215
|
-
metadata: { path: [] },
|
|
6216
|
-
}),
|
|
6217
|
-
node,
|
|
6218
|
-
);
|
|
5458
|
+
const alias_declaration = set_loc(b.const(dynamic_id, clone_expression_node(node.id)), node);
|
|
6219
5459
|
const jsx_element = create_dynamic_jsx_element(dynamic_id, node, transform_context);
|
|
6220
5460
|
|
|
6221
5461
|
return to_jsx_expression_container(
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
{
|
|
6232
|
-
type: 'ReturnStatement',
|
|
6233
|
-
argument: {
|
|
6234
|
-
type: 'ConditionalExpression',
|
|
6235
|
-
test: clone_identifier(dynamic_id),
|
|
6236
|
-
consequent: jsx_element,
|
|
6237
|
-
alternate: create_null_literal(),
|
|
6238
|
-
metadata: { path: [] },
|
|
6239
|
-
},
|
|
6240
|
-
metadata: { path: [] },
|
|
6241
|
-
},
|
|
6242
|
-
],
|
|
6243
|
-
metadata: { path: [] },
|
|
6244
|
-
}),
|
|
6245
|
-
async: false,
|
|
6246
|
-
generator: false,
|
|
6247
|
-
expression: false,
|
|
6248
|
-
metadata: { path: [] },
|
|
6249
|
-
},
|
|
6250
|
-
arguments: [],
|
|
6251
|
-
optional: false,
|
|
6252
|
-
metadata: { path: [] },
|
|
6253
|
-
}),
|
|
5462
|
+
b.call(
|
|
5463
|
+
b.arrow(
|
|
5464
|
+
[],
|
|
5465
|
+
b.block([
|
|
5466
|
+
alias_declaration,
|
|
5467
|
+
b.return(b.conditional(clone_identifier(dynamic_id), jsx_element, create_null_literal())),
|
|
5468
|
+
]),
|
|
5469
|
+
),
|
|
5470
|
+
),
|
|
6254
5471
|
node,
|
|
6255
5472
|
);
|
|
6256
5473
|
}
|