@tsrx/core 0.1.2 → 0.1.4
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 +1 -1
- package/src/index.js +1 -0
- package/src/plugin.js +423 -234
- package/src/transform/jsx/index.js +1166 -108
|
@@ -57,6 +57,79 @@ import {
|
|
|
57
57
|
} from '../jsx-interleave.js';
|
|
58
58
|
import { is_hoist_safe_jsx_node } from '../jsx-hoist.js';
|
|
59
59
|
|
|
60
|
+
const HOOK_OUTER_ASSIGNMENT_ERROR =
|
|
61
|
+
'Hook calls inside conditional or repeated TSRX scopes must keep their results local to the generated hook component.';
|
|
62
|
+
const HOOK_CALLBACK_OUTER_MUTATION_ERROR =
|
|
63
|
+
'Hook callbacks inside conditional or repeated TSRX scopes must not mutate bindings declared outside the generated hook component.';
|
|
64
|
+
const TEMPLATE_FRAGMENT_ERROR =
|
|
65
|
+
'JSX fragment syntax is not needed in TSRX templates. TSRX renders in immediate mode, so everything is already a fragment. Use `<>...</>` only within <tsx>...</tsx>.';
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @param {AST.Node} node
|
|
69
|
+
* @param {TransformContext} transform_context
|
|
70
|
+
*/
|
|
71
|
+
function report_html_template_unsupported_error(node, transform_context) {
|
|
72
|
+
// this should be a fatal error so we don't pass the errors collection,
|
|
73
|
+
// since we don't have a transform for the Html node
|
|
74
|
+
error(
|
|
75
|
+
`\`{html ...}\` is not supported on the ${transform_context.platform.name} target. Use \`dangerouslySetInnerHTML={{ __html: ... }}\` as an element attribute instead.`,
|
|
76
|
+
transform_context.filename,
|
|
77
|
+
node,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {AST.Node} node
|
|
83
|
+
* @param {TransformContext} transform_context
|
|
84
|
+
*/
|
|
85
|
+
function report_jsx_fragment_in_tsrx_error(node, transform_context) {
|
|
86
|
+
error(
|
|
87
|
+
TEMPLATE_FRAGMENT_ERROR,
|
|
88
|
+
transform_context.filename,
|
|
89
|
+
node,
|
|
90
|
+
transform_context.errors,
|
|
91
|
+
transform_context.comments,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @param {AST.Node} node
|
|
97
|
+
* @param {string[]} names
|
|
98
|
+
* @param {string} hook_name
|
|
99
|
+
* @param {TransformContext} transform_context
|
|
100
|
+
* @returns {void}
|
|
101
|
+
*/
|
|
102
|
+
function report_hook_outer_assignment_error(node, names, hook_name, transform_context) {
|
|
103
|
+
const target =
|
|
104
|
+
names.length === 1 ? `\`${names[0]}\`` : names.map((name) => `\`${name}\``).join(', ');
|
|
105
|
+
error(
|
|
106
|
+
`${HOOK_OUTER_ASSIGNMENT_ERROR} The ${hook_name} result is assigned to ${target}, which is declared outside that generated component. Declare the hook result inside the TSRX branch, or move the hook into an explicit child component and pass values with props.`,
|
|
107
|
+
transform_context.filename,
|
|
108
|
+
node,
|
|
109
|
+
transform_context.errors,
|
|
110
|
+
transform_context.comments,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @param {AST.Node} node
|
|
116
|
+
* @param {string[]} names
|
|
117
|
+
* @param {string} hook_name
|
|
118
|
+
* @param {TransformContext} transform_context
|
|
119
|
+
* @returns {void}
|
|
120
|
+
*/
|
|
121
|
+
function report_hook_callback_outer_mutation_error(node, names, hook_name, transform_context) {
|
|
122
|
+
const target =
|
|
123
|
+
names.length === 1 ? `\`${names[0]}\`` : names.map((name) => `\`${name}\``).join(', ');
|
|
124
|
+
error(
|
|
125
|
+
`${HOOK_CALLBACK_OUTER_MUTATION_ERROR} The ${hook_name} callback mutates ${target}. Read outer values through props or dependencies, and move mutable state into an explicit child component when it needs to change over time.`,
|
|
126
|
+
transform_context.filename,
|
|
127
|
+
node,
|
|
128
|
+
transform_context.errors,
|
|
129
|
+
transform_context.comments,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
60
133
|
/**
|
|
61
134
|
* Local alias for the shared `JsxTransformContext`. Kept as a typedef so the
|
|
62
135
|
* rest of this file's `@param {TransformContext}` annotations don't all have
|
|
@@ -449,9 +522,12 @@ export function createJsxTransform(platform) {
|
|
|
449
522
|
// (e.g. segments.js reading node.value.metadata.is_component on class
|
|
450
523
|
// methods) don't trip on an undefined metadata object. Ripple's analyze
|
|
451
524
|
// phase does this via visit_function; tsrx-react has no analyze phase.
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
525
|
+
// If a plain JS function contains a hook-bearing <tsrx> expression,
|
|
526
|
+
// give it a temporary helper scope so extracted hook components can
|
|
527
|
+
// be emitted with stable identities just like component-body helpers.
|
|
528
|
+
FunctionDeclaration: transform_function_with_hook_helpers,
|
|
529
|
+
FunctionExpression: transform_function_with_hook_helpers,
|
|
530
|
+
ArrowFunctionExpression: transform_function_with_hook_helpers,
|
|
455
531
|
|
|
456
532
|
RefExpression(node) {
|
|
457
533
|
return create_ref_prop_call(node, transform_context);
|
|
@@ -682,7 +758,7 @@ function build_component_statements(body_nodes, transform_context) {
|
|
|
682
758
|
function build_render_statements(body_nodes, return_null_when_empty, transform_context) {
|
|
683
759
|
const statements = [];
|
|
684
760
|
const render_nodes = [];
|
|
685
|
-
let
|
|
761
|
+
let has_terminal_return = false;
|
|
686
762
|
|
|
687
763
|
// Create a new bindings map so inner-scope bindings from
|
|
688
764
|
// collect_statement_bindings don't leak to the caller's scope.
|
|
@@ -707,7 +783,13 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
707
783
|
if (is_bare_return_statement(child)) {
|
|
708
784
|
statements.push(create_component_return_statement(render_nodes, child));
|
|
709
785
|
render_nodes.length = 0;
|
|
710
|
-
|
|
786
|
+
has_terminal_return = true;
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
if (child?.type === 'ReturnStatement' && child.argument != null) {
|
|
791
|
+
statements.push(child);
|
|
792
|
+
has_terminal_return = true;
|
|
711
793
|
continue;
|
|
712
794
|
}
|
|
713
795
|
|
|
@@ -968,7 +1050,7 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
968
1050
|
}
|
|
969
1051
|
|
|
970
1052
|
const return_arg = build_return_expression(render_nodes);
|
|
971
|
-
if (return_arg || (return_null_when_empty && !
|
|
1053
|
+
if (return_arg || (return_null_when_empty && !has_terminal_return)) {
|
|
972
1054
|
statements.push({
|
|
973
1055
|
type: 'ReturnStatement',
|
|
974
1056
|
argument: return_arg || { type: 'Literal', value: null, raw: 'null' },
|
|
@@ -1244,6 +1326,156 @@ function create_helper_state(base_name) {
|
|
|
1244
1326
|
};
|
|
1245
1327
|
}
|
|
1246
1328
|
|
|
1329
|
+
/**
|
|
1330
|
+
* @param {any} node
|
|
1331
|
+
* @param {{ next: () => any, state: TransformContext }} context
|
|
1332
|
+
* @returns {any}
|
|
1333
|
+
*/
|
|
1334
|
+
function transform_function_with_hook_helpers(node, { next, state }) {
|
|
1335
|
+
if (state.helper_state || !function_contains_hook_bearing_tsrx(node, state)) {
|
|
1336
|
+
return ensure_function_metadata(node, { next });
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
const helper_state = create_helper_state(get_function_helper_base_name(node));
|
|
1340
|
+
const saved_helper_state = state.helper_state;
|
|
1341
|
+
const saved_bindings = state.available_bindings;
|
|
1342
|
+
|
|
1343
|
+
state.helper_state = helper_state;
|
|
1344
|
+
state.available_bindings = collect_function_scope_bindings(node);
|
|
1345
|
+
|
|
1346
|
+
const inner = /** @type {any} */ (next() ?? node);
|
|
1347
|
+
|
|
1348
|
+
state.helper_state = saved_helper_state;
|
|
1349
|
+
state.available_bindings = saved_bindings;
|
|
1350
|
+
|
|
1351
|
+
ensure_function_metadata(inner, { next: () => inner });
|
|
1352
|
+
if (helper_state.helpers.length || helper_state.statics.length) {
|
|
1353
|
+
inner.metadata = {
|
|
1354
|
+
...(inner.metadata || {}),
|
|
1355
|
+
generated_helpers: helper_state.helpers,
|
|
1356
|
+
generated_statics: helper_state.statics,
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
return inner;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
/**
|
|
1364
|
+
* @param {any} node
|
|
1365
|
+
* @returns {string}
|
|
1366
|
+
*/
|
|
1367
|
+
function get_function_helper_base_name(node) {
|
|
1368
|
+
if (node.id?.type === 'Identifier') {
|
|
1369
|
+
return node.id.name;
|
|
1370
|
+
}
|
|
1371
|
+
return 'Tsrx';
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
/**
|
|
1375
|
+
* @param {any} node
|
|
1376
|
+
* @returns {Map<string, AST.Identifier>}
|
|
1377
|
+
*/
|
|
1378
|
+
function collect_function_scope_bindings(node) {
|
|
1379
|
+
const bindings = collect_param_bindings(node.params || []);
|
|
1380
|
+
collect_descendant_declaration_bindings(node.body, bindings);
|
|
1381
|
+
return bindings;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
/**
|
|
1385
|
+
* @param {any} node
|
|
1386
|
+
* @param {Map<string, AST.Identifier>} bindings
|
|
1387
|
+
* @returns {void}
|
|
1388
|
+
*/
|
|
1389
|
+
function collect_descendant_declaration_bindings(node, bindings) {
|
|
1390
|
+
if (!node || typeof node !== 'object') {
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
if (node.type === 'VariableDeclaration') {
|
|
1395
|
+
for (const declaration of node.declarations || []) {
|
|
1396
|
+
collect_pattern_bindings(declaration.id, bindings);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
if (
|
|
1401
|
+
(node.type === 'FunctionDeclaration' || node.type === 'ClassDeclaration') &&
|
|
1402
|
+
node.id?.type === 'Identifier'
|
|
1403
|
+
) {
|
|
1404
|
+
bindings.set(node.id.name, node.id);
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
if (
|
|
1408
|
+
node.type === 'FunctionDeclaration' ||
|
|
1409
|
+
node.type === 'FunctionExpression' ||
|
|
1410
|
+
node.type === 'ArrowFunctionExpression' ||
|
|
1411
|
+
node.type === 'Component'
|
|
1412
|
+
) {
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
if (Array.isArray(node)) {
|
|
1417
|
+
for (const child of node) {
|
|
1418
|
+
collect_descendant_declaration_bindings(child, bindings);
|
|
1419
|
+
}
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
for (const key of Object.keys(node)) {
|
|
1424
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
1425
|
+
continue;
|
|
1426
|
+
}
|
|
1427
|
+
collect_descendant_declaration_bindings(node[key], bindings);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
/**
|
|
1432
|
+
* @param {any} node
|
|
1433
|
+
* @param {TransformContext} transform_context
|
|
1434
|
+
* @returns {boolean}
|
|
1435
|
+
*/
|
|
1436
|
+
function function_contains_hook_bearing_tsrx(node, transform_context) {
|
|
1437
|
+
return node_contains_hook_bearing_tsrx(node.body, transform_context);
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
/**
|
|
1441
|
+
* @param {any} node
|
|
1442
|
+
* @param {TransformContext} transform_context
|
|
1443
|
+
* @returns {boolean}
|
|
1444
|
+
*/
|
|
1445
|
+
function node_contains_hook_bearing_tsrx(node, transform_context) {
|
|
1446
|
+
if (!node || typeof node !== 'object') {
|
|
1447
|
+
return false;
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
if (Array.isArray(node)) {
|
|
1451
|
+
return node.some((child) => node_contains_hook_bearing_tsrx(child, transform_context));
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
if (node.type === 'Tsrx') {
|
|
1455
|
+
return body_contains_top_level_hook_call(node.children || [], transform_context, true);
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
if (
|
|
1459
|
+
node.type === 'FunctionDeclaration' ||
|
|
1460
|
+
node.type === 'FunctionExpression' ||
|
|
1461
|
+
node.type === 'ArrowFunctionExpression' ||
|
|
1462
|
+
node.type === 'Component'
|
|
1463
|
+
) {
|
|
1464
|
+
return false;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
for (const key of Object.keys(node)) {
|
|
1468
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
1469
|
+
continue;
|
|
1470
|
+
}
|
|
1471
|
+
if (node_contains_hook_bearing_tsrx(node[key], transform_context)) {
|
|
1472
|
+
return true;
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
return false;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1247
1479
|
/**
|
|
1248
1480
|
* @param {TransformContext} transform_context
|
|
1249
1481
|
* @returns {boolean}
|
|
@@ -2225,15 +2457,20 @@ function build_hoisted_for_of_with_hooks(node, continuation_body, transform_cont
|
|
|
2225
2457
|
|
|
2226
2458
|
const saved_bindings = transform_context.available_bindings;
|
|
2227
2459
|
transform_context.available_bindings = new Map(saved_bindings);
|
|
2460
|
+
const loop_scoped_names = new Set(loop_params.map((/** @type {any} */ p) => p.name));
|
|
2228
2461
|
for (const param of loop_params) {
|
|
2229
2462
|
collect_pattern_bindings(param, transform_context.available_bindings);
|
|
2230
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
|
+
);
|
|
2231
2469
|
|
|
2232
2470
|
const all_helper_bindings = get_referenced_helper_bindings(
|
|
2233
2471
|
loop_body,
|
|
2234
2472
|
transform_context.available_bindings,
|
|
2235
2473
|
);
|
|
2236
|
-
const loop_scoped_names = new Set(loop_params.map((/** @type {any} */ p) => p.name));
|
|
2237
2474
|
const outer_bindings = all_helper_bindings.filter((b) => !loop_scoped_names.has(b.name));
|
|
2238
2475
|
const loop_bindings = all_helper_bindings.filter((b) => loop_scoped_names.has(b.name));
|
|
2239
2476
|
|
|
@@ -2626,9 +2863,6 @@ function is_null_literal(node) {
|
|
|
2626
2863
|
return node?.type === 'Literal' && node.value == null;
|
|
2627
2864
|
}
|
|
2628
2865
|
|
|
2629
|
-
const TEMPLATE_FRAGMENT_ERROR =
|
|
2630
|
-
'JSX fragment syntax is not needed in TSRX templates. TSRX renders in immediate mode, so everything is already a fragment. Use `<>...</>` only within <tsx>...</tsx>.';
|
|
2631
|
-
|
|
2632
2866
|
/**
|
|
2633
2867
|
* @param {any} node
|
|
2634
2868
|
* @param {TransformContext} transform_context
|
|
@@ -2637,13 +2871,7 @@ const TEMPLATE_FRAGMENT_ERROR =
|
|
|
2637
2871
|
function to_jsx_element(node, transform_context, raw_children = node.children || []) {
|
|
2638
2872
|
if (node.type === 'JSXElement') return node;
|
|
2639
2873
|
if (!node.id) {
|
|
2640
|
-
|
|
2641
|
-
TEMPLATE_FRAGMENT_ERROR,
|
|
2642
|
-
transform_context.filename,
|
|
2643
|
-
node,
|
|
2644
|
-
transform_context.errors,
|
|
2645
|
-
transform_context.comments,
|
|
2646
|
-
);
|
|
2874
|
+
report_jsx_fragment_in_tsrx_error(node, transform_context);
|
|
2647
2875
|
return set_loc(
|
|
2648
2876
|
/** @type {any} */ ({
|
|
2649
2877
|
type: 'JSXFragment',
|
|
@@ -2682,9 +2910,7 @@ function to_jsx_element(node, transform_context, raw_children = node.children ||
|
|
|
2682
2910
|
}
|
|
2683
2911
|
} else {
|
|
2684
2912
|
if (walked_children.some((/** @type {any} */ c) => c && c.type === 'Html')) {
|
|
2685
|
-
|
|
2686
|
-
`\`{html ...}\` is not supported on the ${transform_context.platform.name} target. Use \`dangerouslySetInnerHTML={{ __html: ... }}\` as an element attribute instead.`,
|
|
2687
|
-
);
|
|
2913
|
+
return report_html_template_unsupported_error(node, transform_context);
|
|
2688
2914
|
}
|
|
2689
2915
|
children = create_element_children(walked_children, transform_context);
|
|
2690
2916
|
}
|
|
@@ -2761,7 +2987,10 @@ function child_contains_return_semantics(node) {
|
|
|
2761
2987
|
return false;
|
|
2762
2988
|
}
|
|
2763
2989
|
|
|
2764
|
-
if (
|
|
2990
|
+
if (
|
|
2991
|
+
(node.type === 'ReturnStatement' && node.argument == null) ||
|
|
2992
|
+
is_lone_return_if_statement(node)
|
|
2993
|
+
) {
|
|
2765
2994
|
return true;
|
|
2766
2995
|
}
|
|
2767
2996
|
|
|
@@ -2913,82 +3142,757 @@ function get_referenced_helper_bindings(body_nodes, available_bindings) {
|
|
|
2913
3142
|
|
|
2914
3143
|
/**
|
|
2915
3144
|
* @param {any[]} body_nodes
|
|
2916
|
-
* @param {any} key_expression
|
|
2917
|
-
* @param {any} source_node
|
|
2918
3145
|
* @param {TransformContext} transform_context
|
|
2919
|
-
* @param {
|
|
2920
|
-
*
|
|
2921
|
-
* source order in a forward pass and then constructs helpers in reverse so
|
|
2922
|
-
* each fall-through case can reference the next case's component element.
|
|
2923
|
-
* @returns {{ setup_statements: any[], component_element: ESTreeJSX.JSXElement }}
|
|
3146
|
+
* @param {Set<string>} [local_binding_names]
|
|
3147
|
+
* @returns {void}
|
|
2924
3148
|
*/
|
|
2925
|
-
function
|
|
3149
|
+
function validate_hook_safe_body_does_not_assign_hook_results_to_outer_bindings(
|
|
2926
3150
|
body_nodes,
|
|
2927
|
-
key_expression,
|
|
2928
|
-
source_node,
|
|
2929
3151
|
transform_context,
|
|
2930
|
-
|
|
3152
|
+
local_binding_names,
|
|
2931
3153
|
) {
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
transform_context.available_bindings,
|
|
2942
|
-
);
|
|
2943
|
-
const aliases = use_module_scoped_component
|
|
2944
|
-
? []
|
|
2945
|
-
: helper_bindings.map((binding) => create_helper_type_alias_declaration(helper_id, binding));
|
|
2946
|
-
const props_type =
|
|
2947
|
-
helper_bindings.length > 0 && !use_module_scoped_component
|
|
2948
|
-
? create_helper_props_type_literal(helper_bindings, aliases)
|
|
2949
|
-
: null;
|
|
2950
|
-
const params =
|
|
2951
|
-
helper_bindings.length > 0
|
|
2952
|
-
? [
|
|
2953
|
-
props_type !== null
|
|
2954
|
-
? create_typed_helper_props_pattern(helper_bindings, props_type)
|
|
2955
|
-
: create_helper_props_pattern(helper_bindings),
|
|
2956
|
-
]
|
|
2957
|
-
: [];
|
|
3154
|
+
if (!is_react_like_hook_platform(transform_context)) {
|
|
3155
|
+
return;
|
|
3156
|
+
}
|
|
3157
|
+
if (!body_contains_top_level_hook_call(body_nodes, transform_context, true)) {
|
|
3158
|
+
return;
|
|
3159
|
+
}
|
|
3160
|
+
if (!transform_context.available_bindings || transform_context.available_bindings.size === 0) {
|
|
3161
|
+
return;
|
|
3162
|
+
}
|
|
2958
3163
|
|
|
2959
|
-
const
|
|
2960
|
-
|
|
3164
|
+
const shadowed_names = collect_block_binding_names(body_nodes);
|
|
3165
|
+
for (const name of local_binding_names || []) {
|
|
3166
|
+
shadowed_names.add(name);
|
|
3167
|
+
}
|
|
3168
|
+
validate_hook_outer_assignments_in_node(body_nodes, shadowed_names, transform_context, new Set());
|
|
3169
|
+
}
|
|
2961
3170
|
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
async: false,
|
|
2972
|
-
generator: false,
|
|
2973
|
-
metadata: {
|
|
2974
|
-
path: [],
|
|
2975
|
-
is_component: true,
|
|
2976
|
-
is_method: false,
|
|
2977
|
-
},
|
|
2978
|
-
});
|
|
3171
|
+
/**
|
|
3172
|
+
* @param {TransformContext} transform_context
|
|
3173
|
+
* @returns {boolean}
|
|
3174
|
+
*/
|
|
3175
|
+
function is_react_like_hook_platform(transform_context) {
|
|
3176
|
+
return (
|
|
3177
|
+
transform_context.platform.name === 'React' || transform_context.platform.name === 'Preact'
|
|
3178
|
+
);
|
|
3179
|
+
}
|
|
2979
3180
|
|
|
2980
|
-
|
|
3181
|
+
/**
|
|
3182
|
+
* @param {any[]} statements
|
|
3183
|
+
* @returns {Set<string>}
|
|
3184
|
+
*/
|
|
3185
|
+
function collect_block_binding_names(statements) {
|
|
3186
|
+
const names = new Set();
|
|
3187
|
+
for (const statement of statements || []) {
|
|
3188
|
+
collect_block_binding_names_from_statement(statement, names);
|
|
3189
|
+
}
|
|
3190
|
+
return names;
|
|
3191
|
+
}
|
|
2981
3192
|
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
3193
|
+
/**
|
|
3194
|
+
* @param {any} statement
|
|
3195
|
+
* @param {Set<string>} names
|
|
3196
|
+
* @returns {void}
|
|
3197
|
+
*/
|
|
3198
|
+
function collect_block_binding_names_from_statement(statement, names) {
|
|
3199
|
+
if (!statement || typeof statement !== 'object') {
|
|
3200
|
+
return;
|
|
3201
|
+
}
|
|
3202
|
+
|
|
3203
|
+
if (statement.type === 'VariableDeclaration') {
|
|
3204
|
+
for (const declaration of statement.declarations || []) {
|
|
3205
|
+
collect_pattern_names(declaration.id, names);
|
|
3206
|
+
}
|
|
3207
|
+
return;
|
|
3208
|
+
}
|
|
3209
|
+
|
|
3210
|
+
if (
|
|
3211
|
+
(statement.type === 'FunctionDeclaration' || statement.type === 'ClassDeclaration') &&
|
|
3212
|
+
statement.id?.type === 'Identifier'
|
|
3213
|
+
) {
|
|
3214
|
+
names.add(statement.id.name);
|
|
3215
|
+
return;
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
if (statement.type === 'ForOfStatement' || statement.type === 'ForInStatement') {
|
|
3219
|
+
if (statement.left?.type === 'VariableDeclaration' && statement.left.kind === 'var') {
|
|
3220
|
+
for (const declaration of statement.left.declarations || []) {
|
|
3221
|
+
collect_pattern_names(declaration.id, names);
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
return;
|
|
3225
|
+
}
|
|
3226
|
+
|
|
3227
|
+
if (
|
|
3228
|
+
statement.type === 'ForStatement' &&
|
|
3229
|
+
statement.init?.type === 'VariableDeclaration' &&
|
|
3230
|
+
statement.init.kind === 'var'
|
|
3231
|
+
) {
|
|
3232
|
+
for (const declaration of statement.init.declarations || []) {
|
|
3233
|
+
collect_pattern_names(declaration.id, names);
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3237
|
+
|
|
3238
|
+
/**
|
|
3239
|
+
* @param {any} pattern
|
|
3240
|
+
* @param {Set<string>} names
|
|
3241
|
+
* @returns {void}
|
|
3242
|
+
*/
|
|
3243
|
+
function collect_pattern_names(pattern, names) {
|
|
3244
|
+
if (!pattern || typeof pattern !== 'object') {
|
|
3245
|
+
return;
|
|
3246
|
+
}
|
|
3247
|
+
|
|
3248
|
+
if (pattern.type === 'Identifier') {
|
|
3249
|
+
names.add(pattern.name);
|
|
3250
|
+
return;
|
|
3251
|
+
}
|
|
3252
|
+
|
|
3253
|
+
if (pattern.type === 'RestElement') {
|
|
3254
|
+
collect_pattern_names(pattern.argument, names);
|
|
3255
|
+
return;
|
|
3256
|
+
}
|
|
3257
|
+
|
|
3258
|
+
if (pattern.type === 'AssignmentPattern') {
|
|
3259
|
+
collect_pattern_names(pattern.left, names);
|
|
3260
|
+
return;
|
|
3261
|
+
}
|
|
3262
|
+
|
|
3263
|
+
if (pattern.type === 'ArrayPattern') {
|
|
3264
|
+
for (const element of pattern.elements || []) {
|
|
3265
|
+
collect_pattern_names(element, names);
|
|
3266
|
+
}
|
|
3267
|
+
return;
|
|
3268
|
+
}
|
|
3269
|
+
|
|
3270
|
+
if (pattern.type === 'ObjectPattern') {
|
|
3271
|
+
for (const property of pattern.properties || []) {
|
|
3272
|
+
if (property.type === 'RestElement') {
|
|
3273
|
+
collect_pattern_names(property.argument, names);
|
|
3274
|
+
} else {
|
|
3275
|
+
collect_pattern_names(property.value, names);
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3280
|
+
|
|
3281
|
+
/**
|
|
3282
|
+
* @param {any} node
|
|
3283
|
+
* @param {Set<string>} shadowed_names
|
|
3284
|
+
* @param {TransformContext} transform_context
|
|
3285
|
+
* @param {Set<string>} hook_result_names
|
|
3286
|
+
* @returns {void}
|
|
3287
|
+
*/
|
|
3288
|
+
function validate_hook_outer_assignments_in_node(
|
|
3289
|
+
node,
|
|
3290
|
+
shadowed_names,
|
|
3291
|
+
transform_context,
|
|
3292
|
+
hook_result_names,
|
|
3293
|
+
) {
|
|
3294
|
+
if (!node || typeof node !== 'object') {
|
|
3295
|
+
return;
|
|
3296
|
+
}
|
|
3297
|
+
|
|
3298
|
+
if (Array.isArray(node)) {
|
|
3299
|
+
for (const child of node) {
|
|
3300
|
+
validate_hook_outer_assignments_in_node(
|
|
3301
|
+
child,
|
|
3302
|
+
shadowed_names,
|
|
3303
|
+
transform_context,
|
|
3304
|
+
hook_result_names,
|
|
3305
|
+
);
|
|
3306
|
+
}
|
|
3307
|
+
return;
|
|
3308
|
+
}
|
|
3309
|
+
|
|
3310
|
+
if (is_function_like_node(node)) {
|
|
3311
|
+
return;
|
|
3312
|
+
}
|
|
3313
|
+
|
|
3314
|
+
if (node.type === 'CallExpression' && is_hook_callee(node.callee)) {
|
|
3315
|
+
validate_hook_callback_outer_mutations(node, shadowed_names, transform_context);
|
|
3316
|
+
}
|
|
3317
|
+
|
|
3318
|
+
if (node.type === 'BlockStatement') {
|
|
3319
|
+
const next_shadowed = new Set(shadowed_names);
|
|
3320
|
+
const next_hook_result_names = new Set(hook_result_names);
|
|
3321
|
+
for (const name of collect_block_binding_names(node.body || [])) {
|
|
3322
|
+
next_shadowed.add(name);
|
|
3323
|
+
}
|
|
3324
|
+
for (const child of node.body || []) {
|
|
3325
|
+
validate_hook_outer_assignments_in_node(
|
|
3326
|
+
child,
|
|
3327
|
+
next_shadowed,
|
|
3328
|
+
transform_context,
|
|
3329
|
+
next_hook_result_names,
|
|
3330
|
+
);
|
|
3331
|
+
}
|
|
3332
|
+
return;
|
|
3333
|
+
}
|
|
3334
|
+
|
|
3335
|
+
if (node.type === 'VariableDeclaration') {
|
|
3336
|
+
for (const declaration of node.declarations || []) {
|
|
3337
|
+
if (
|
|
3338
|
+
declaration.init &&
|
|
3339
|
+
expression_contains_hook_derived_value(
|
|
3340
|
+
declaration.init,
|
|
3341
|
+
transform_context,
|
|
3342
|
+
hook_result_names,
|
|
3343
|
+
)
|
|
3344
|
+
) {
|
|
3345
|
+
collect_pattern_names(declaration.id, hook_result_names);
|
|
3346
|
+
}
|
|
3347
|
+
validate_hook_outer_assignments_in_node(
|
|
3348
|
+
declaration.init,
|
|
3349
|
+
shadowed_names,
|
|
3350
|
+
transform_context,
|
|
3351
|
+
hook_result_names,
|
|
3352
|
+
);
|
|
3353
|
+
}
|
|
3354
|
+
return;
|
|
3355
|
+
}
|
|
3356
|
+
|
|
3357
|
+
if (
|
|
3358
|
+
node.type === 'AssignmentExpression' &&
|
|
3359
|
+
expression_contains_hook_derived_value(node.right, transform_context, hook_result_names)
|
|
3360
|
+
) {
|
|
3361
|
+
const outer_names = get_referenced_outer_binding_names(
|
|
3362
|
+
node.left,
|
|
3363
|
+
transform_context.available_bindings,
|
|
3364
|
+
shadowed_names,
|
|
3365
|
+
);
|
|
3366
|
+
if (outer_names.length > 0) {
|
|
3367
|
+
report_hook_outer_assignment_error(
|
|
3368
|
+
node,
|
|
3369
|
+
outer_names,
|
|
3370
|
+
find_first_hook_call_name(node.right) || 'hook',
|
|
3371
|
+
transform_context,
|
|
3372
|
+
);
|
|
3373
|
+
}
|
|
3374
|
+
for (const name of get_referenced_local_binding_names(node.left, shadowed_names)) {
|
|
3375
|
+
hook_result_names.add(name);
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
|
|
3379
|
+
if (node.type === 'ForOfStatement') {
|
|
3380
|
+
if (
|
|
3381
|
+
node.left &&
|
|
3382
|
+
node.left.type !== 'VariableDeclaration' &&
|
|
3383
|
+
expression_contains_hook_derived_value(node.right, transform_context, hook_result_names)
|
|
3384
|
+
) {
|
|
3385
|
+
const outer_names = get_referenced_outer_binding_names(
|
|
3386
|
+
node.left,
|
|
3387
|
+
transform_context.available_bindings,
|
|
3388
|
+
shadowed_names,
|
|
3389
|
+
);
|
|
3390
|
+
if (outer_names.length > 0) {
|
|
3391
|
+
report_hook_outer_assignment_error(
|
|
3392
|
+
node,
|
|
3393
|
+
outer_names,
|
|
3394
|
+
find_first_hook_call_name(node.right) || 'hook',
|
|
3395
|
+
transform_context,
|
|
3396
|
+
);
|
|
3397
|
+
}
|
|
3398
|
+
for (const name of get_referenced_local_binding_names(node.left, shadowed_names)) {
|
|
3399
|
+
hook_result_names.add(name);
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
|
|
3403
|
+
validate_hook_outer_assignments_in_node(
|
|
3404
|
+
node.right,
|
|
3405
|
+
shadowed_names,
|
|
3406
|
+
transform_context,
|
|
3407
|
+
hook_result_names,
|
|
3408
|
+
);
|
|
3409
|
+
|
|
3410
|
+
// Loop-declared bindings (`for (const x of …)`, `for (let x of …)`) live
|
|
3411
|
+
// only in the body. They are deliberately NOT in the enclosing block's
|
|
3412
|
+
// shadowed set (see collect_block_binding_names_from_statement), so add
|
|
3413
|
+
// them just for the body recursion to keep references to the loop var
|
|
3414
|
+
// from being flagged as outer-binding mutations.
|
|
3415
|
+
const body_shadowed = new Set(shadowed_names);
|
|
3416
|
+
if (node.left && node.left.type === 'VariableDeclaration') {
|
|
3417
|
+
for (const declaration of node.left.declarations || []) {
|
|
3418
|
+
collect_pattern_names(declaration.id, body_shadowed);
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
validate_hook_outer_assignments_in_node(
|
|
3422
|
+
node.body,
|
|
3423
|
+
body_shadowed,
|
|
3424
|
+
transform_context,
|
|
3425
|
+
hook_result_names,
|
|
3426
|
+
);
|
|
3427
|
+
return;
|
|
3428
|
+
}
|
|
3429
|
+
|
|
3430
|
+
for (const key of Object.keys(node)) {
|
|
3431
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
3432
|
+
continue;
|
|
3433
|
+
}
|
|
3434
|
+
validate_hook_outer_assignments_in_node(
|
|
3435
|
+
node[key],
|
|
3436
|
+
shadowed_names,
|
|
3437
|
+
transform_context,
|
|
3438
|
+
hook_result_names,
|
|
3439
|
+
);
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
|
|
3443
|
+
/**
|
|
3444
|
+
* @param {any} call_node
|
|
3445
|
+
* @param {Set<string>} shadowed_names
|
|
3446
|
+
* @param {TransformContext} transform_context
|
|
3447
|
+
* @returns {void}
|
|
3448
|
+
*/
|
|
3449
|
+
function validate_hook_callback_outer_mutations(call_node, shadowed_names, transform_context) {
|
|
3450
|
+
const hook_name = get_hook_callee_name(call_node.callee);
|
|
3451
|
+
for (const argument of call_node.arguments || []) {
|
|
3452
|
+
if (!is_function_like_node(argument)) {
|
|
3453
|
+
continue;
|
|
3454
|
+
}
|
|
3455
|
+
const callback_shadowed_names = create_function_like_shadowed_names(argument, shadowed_names);
|
|
3456
|
+
validate_hook_callback_outer_mutations_in_node(
|
|
3457
|
+
argument.body,
|
|
3458
|
+
callback_shadowed_names,
|
|
3459
|
+
transform_context,
|
|
3460
|
+
hook_name,
|
|
3461
|
+
);
|
|
3462
|
+
}
|
|
3463
|
+
}
|
|
3464
|
+
|
|
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
|
+
/**
|
|
3481
|
+
* @param {any} node
|
|
3482
|
+
* @param {Set<string>} shadowed_names
|
|
3483
|
+
* @returns {Set<string>}
|
|
3484
|
+
*/
|
|
3485
|
+
function create_function_like_shadowed_names(node, shadowed_names) {
|
|
3486
|
+
const next_shadowed_names = new Set(shadowed_names);
|
|
3487
|
+
for (const param of node.params || []) {
|
|
3488
|
+
collect_pattern_names(param, next_shadowed_names);
|
|
3489
|
+
}
|
|
3490
|
+
if (node.body?.type === 'BlockStatement') {
|
|
3491
|
+
for (const name of collect_block_binding_names(node.body.body || [])) {
|
|
3492
|
+
next_shadowed_names.add(name);
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
return next_shadowed_names;
|
|
3496
|
+
}
|
|
3497
|
+
|
|
3498
|
+
/**
|
|
3499
|
+
* @param {any} node
|
|
3500
|
+
* @param {Set<string>} shadowed_names
|
|
3501
|
+
* @param {TransformContext} transform_context
|
|
3502
|
+
* @param {string} hook_name
|
|
3503
|
+
* @returns {void}
|
|
3504
|
+
*/
|
|
3505
|
+
function validate_hook_callback_outer_mutations_in_node(
|
|
3506
|
+
node,
|
|
3507
|
+
shadowed_names,
|
|
3508
|
+
transform_context,
|
|
3509
|
+
hook_name,
|
|
3510
|
+
) {
|
|
3511
|
+
if (!node || typeof node !== 'object') {
|
|
3512
|
+
return;
|
|
3513
|
+
}
|
|
3514
|
+
|
|
3515
|
+
if (Array.isArray(node)) {
|
|
3516
|
+
for (const child of node) {
|
|
3517
|
+
validate_hook_callback_outer_mutations_in_node(
|
|
3518
|
+
child,
|
|
3519
|
+
shadowed_names,
|
|
3520
|
+
transform_context,
|
|
3521
|
+
hook_name,
|
|
3522
|
+
);
|
|
3523
|
+
}
|
|
3524
|
+
return;
|
|
3525
|
+
}
|
|
3526
|
+
|
|
3527
|
+
if (is_function_like_node(node)) {
|
|
3528
|
+
validate_hook_callback_outer_mutations_in_node(
|
|
3529
|
+
node.body,
|
|
3530
|
+
create_function_like_shadowed_names(node, shadowed_names),
|
|
3531
|
+
transform_context,
|
|
3532
|
+
hook_name,
|
|
3533
|
+
);
|
|
3534
|
+
return;
|
|
3535
|
+
}
|
|
3536
|
+
|
|
3537
|
+
if (node.type === 'BlockStatement') {
|
|
3538
|
+
const next_shadowed_names = new Set(shadowed_names);
|
|
3539
|
+
for (const name of collect_block_binding_names(node.body || [])) {
|
|
3540
|
+
next_shadowed_names.add(name);
|
|
3541
|
+
}
|
|
3542
|
+
for (const child of node.body || []) {
|
|
3543
|
+
validate_hook_callback_outer_mutations_in_node(
|
|
3544
|
+
child,
|
|
3545
|
+
next_shadowed_names,
|
|
3546
|
+
transform_context,
|
|
3547
|
+
hook_name,
|
|
3548
|
+
);
|
|
3549
|
+
}
|
|
3550
|
+
return;
|
|
3551
|
+
}
|
|
3552
|
+
|
|
3553
|
+
if (node.type === 'AssignmentExpression') {
|
|
3554
|
+
const outer_names = get_referenced_outer_binding_names(
|
|
3555
|
+
node.left,
|
|
3556
|
+
transform_context.available_bindings,
|
|
3557
|
+
shadowed_names,
|
|
3558
|
+
);
|
|
3559
|
+
if (outer_names.length > 0) {
|
|
3560
|
+
report_hook_callback_outer_mutation_error(node, outer_names, hook_name, transform_context);
|
|
3561
|
+
}
|
|
3562
|
+
}
|
|
3563
|
+
|
|
3564
|
+
if (node.type === 'UpdateExpression') {
|
|
3565
|
+
const outer_names = get_referenced_outer_binding_names(
|
|
3566
|
+
node.argument,
|
|
3567
|
+
transform_context.available_bindings,
|
|
3568
|
+
shadowed_names,
|
|
3569
|
+
);
|
|
3570
|
+
if (outer_names.length > 0) {
|
|
3571
|
+
report_hook_callback_outer_mutation_error(node, outer_names, hook_name, transform_context);
|
|
3572
|
+
}
|
|
3573
|
+
}
|
|
3574
|
+
|
|
3575
|
+
for (const key of Object.keys(node)) {
|
|
3576
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
3577
|
+
continue;
|
|
3578
|
+
}
|
|
3579
|
+
if (key === 'left' && node.type === 'AssignmentExpression') {
|
|
3580
|
+
continue;
|
|
3581
|
+
}
|
|
3582
|
+
if (key === 'argument' && node.type === 'UpdateExpression') {
|
|
3583
|
+
continue;
|
|
3584
|
+
}
|
|
3585
|
+
validate_hook_callback_outer_mutations_in_node(
|
|
3586
|
+
node[key],
|
|
3587
|
+
shadowed_names,
|
|
3588
|
+
transform_context,
|
|
3589
|
+
hook_name,
|
|
3590
|
+
);
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
|
|
3594
|
+
/**
|
|
3595
|
+
* @param {any} node
|
|
3596
|
+
* @param {TransformContext} transform_context
|
|
3597
|
+
* @param {Set<string>} hook_result_names
|
|
3598
|
+
* @returns {boolean}
|
|
3599
|
+
*/
|
|
3600
|
+
function expression_contains_hook_derived_value(node, transform_context, hook_result_names) {
|
|
3601
|
+
return (
|
|
3602
|
+
node_contains_top_level_hook_call(node, false, transform_context, true) ||
|
|
3603
|
+
references_name_in_set(node, hook_result_names)
|
|
3604
|
+
);
|
|
3605
|
+
}
|
|
3606
|
+
|
|
3607
|
+
/**
|
|
3608
|
+
* @param {any} node
|
|
3609
|
+
* @param {Set<string>} names
|
|
3610
|
+
* @returns {boolean}
|
|
3611
|
+
*/
|
|
3612
|
+
function references_name_in_set(node, names) {
|
|
3613
|
+
if (!node || typeof node !== 'object' || names.size === 0) {
|
|
3614
|
+
return false;
|
|
3615
|
+
}
|
|
3616
|
+
|
|
3617
|
+
if (node.type === 'Identifier') {
|
|
3618
|
+
return names.has(node.name);
|
|
3619
|
+
}
|
|
3620
|
+
|
|
3621
|
+
if (
|
|
3622
|
+
node.type === 'FunctionDeclaration' ||
|
|
3623
|
+
node.type === 'FunctionExpression' ||
|
|
3624
|
+
node.type === 'ArrowFunctionExpression' ||
|
|
3625
|
+
node.type === 'Component'
|
|
3626
|
+
) {
|
|
3627
|
+
return false;
|
|
3628
|
+
}
|
|
3629
|
+
|
|
3630
|
+
if (Array.isArray(node)) {
|
|
3631
|
+
return node.some((child) => references_name_in_set(child, names));
|
|
3632
|
+
}
|
|
3633
|
+
|
|
3634
|
+
for (const key of Object.keys(node)) {
|
|
3635
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
3636
|
+
continue;
|
|
3637
|
+
}
|
|
3638
|
+
if (key === 'property' && node.type === 'MemberExpression' && !node.computed) {
|
|
3639
|
+
continue;
|
|
3640
|
+
}
|
|
3641
|
+
if (key === 'key' && node.type === 'Property' && !node.computed && !node.shorthand) {
|
|
3642
|
+
continue;
|
|
3643
|
+
}
|
|
3644
|
+
if (references_name_in_set(node[key], names)) {
|
|
3645
|
+
return true;
|
|
3646
|
+
}
|
|
3647
|
+
}
|
|
3648
|
+
|
|
3649
|
+
return false;
|
|
3650
|
+
}
|
|
3651
|
+
|
|
3652
|
+
/**
|
|
3653
|
+
* @param {any} node
|
|
3654
|
+
* @param {Set<string>} shadowed_names
|
|
3655
|
+
* @returns {string[]}
|
|
3656
|
+
*/
|
|
3657
|
+
function get_referenced_local_binding_names(node, shadowed_names) {
|
|
3658
|
+
const names = new Set();
|
|
3659
|
+
collect_referenced_local_binding_names(node, shadowed_names, names);
|
|
3660
|
+
return [...names];
|
|
3661
|
+
}
|
|
3662
|
+
|
|
3663
|
+
/**
|
|
3664
|
+
* @param {any} node
|
|
3665
|
+
* @param {Set<string>} shadowed_names
|
|
3666
|
+
* @param {Set<string>} names
|
|
3667
|
+
* @returns {void}
|
|
3668
|
+
*/
|
|
3669
|
+
function collect_referenced_local_binding_names(node, shadowed_names, names) {
|
|
3670
|
+
if (!node || typeof node !== 'object') {
|
|
3671
|
+
return;
|
|
3672
|
+
}
|
|
3673
|
+
|
|
3674
|
+
if (node.type === 'Identifier') {
|
|
3675
|
+
if (shadowed_names.has(node.name)) {
|
|
3676
|
+
names.add(node.name);
|
|
3677
|
+
}
|
|
3678
|
+
return;
|
|
3679
|
+
}
|
|
3680
|
+
|
|
3681
|
+
if (Array.isArray(node)) {
|
|
3682
|
+
for (const child of node) {
|
|
3683
|
+
collect_referenced_local_binding_names(child, shadowed_names, names);
|
|
3684
|
+
}
|
|
3685
|
+
return;
|
|
3686
|
+
}
|
|
3687
|
+
|
|
3688
|
+
for (const key of Object.keys(node)) {
|
|
3689
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
3690
|
+
continue;
|
|
3691
|
+
}
|
|
3692
|
+
if (key === 'property' && node.type === 'MemberExpression' && !node.computed) {
|
|
3693
|
+
continue;
|
|
3694
|
+
}
|
|
3695
|
+
if (key === 'key' && node.type === 'Property' && !node.computed && !node.shorthand) {
|
|
3696
|
+
continue;
|
|
3697
|
+
}
|
|
3698
|
+
collect_referenced_local_binding_names(node[key], shadowed_names, names);
|
|
3699
|
+
}
|
|
3700
|
+
}
|
|
3701
|
+
|
|
3702
|
+
/**
|
|
3703
|
+
* @param {any} node
|
|
3704
|
+
* @param {Map<string, AST.Identifier>} available_bindings
|
|
3705
|
+
* @param {Set<string>} shadowed_names
|
|
3706
|
+
* @returns {string[]}
|
|
3707
|
+
*/
|
|
3708
|
+
function get_referenced_outer_binding_names(node, available_bindings, shadowed_names) {
|
|
3709
|
+
const names = new Set();
|
|
3710
|
+
collect_referenced_outer_binding_names(node, available_bindings, shadowed_names, names);
|
|
3711
|
+
return [...names];
|
|
3712
|
+
}
|
|
3713
|
+
|
|
3714
|
+
/**
|
|
3715
|
+
* @param {any} node
|
|
3716
|
+
* @param {Map<string, AST.Identifier>} available_bindings
|
|
3717
|
+
* @param {Set<string>} shadowed_names
|
|
3718
|
+
* @param {Set<string>} names
|
|
3719
|
+
* @returns {void}
|
|
3720
|
+
*/
|
|
3721
|
+
function collect_referenced_outer_binding_names(node, available_bindings, shadowed_names, names) {
|
|
3722
|
+
if (!node || typeof node !== 'object') {
|
|
3723
|
+
return;
|
|
3724
|
+
}
|
|
3725
|
+
|
|
3726
|
+
if (node.type === 'Identifier') {
|
|
3727
|
+
if (available_bindings.has(node.name) && !shadowed_names.has(node.name)) {
|
|
3728
|
+
names.add(node.name);
|
|
3729
|
+
}
|
|
3730
|
+
return;
|
|
3731
|
+
}
|
|
3732
|
+
|
|
3733
|
+
if (Array.isArray(node)) {
|
|
3734
|
+
for (const child of node) {
|
|
3735
|
+
collect_referenced_outer_binding_names(child, available_bindings, shadowed_names, names);
|
|
3736
|
+
}
|
|
3737
|
+
return;
|
|
3738
|
+
}
|
|
3739
|
+
|
|
3740
|
+
for (const key of Object.keys(node)) {
|
|
3741
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
3742
|
+
continue;
|
|
3743
|
+
}
|
|
3744
|
+
if (key === 'property' && node.type === 'MemberExpression' && !node.computed) {
|
|
3745
|
+
continue;
|
|
3746
|
+
}
|
|
3747
|
+
if (key === 'key' && node.type === 'Property' && !node.computed && !node.shorthand) {
|
|
3748
|
+
continue;
|
|
3749
|
+
}
|
|
3750
|
+
collect_referenced_outer_binding_names(node[key], available_bindings, shadowed_names, names);
|
|
3751
|
+
}
|
|
3752
|
+
}
|
|
3753
|
+
|
|
3754
|
+
/**
|
|
3755
|
+
* @param {any} node
|
|
3756
|
+
* @returns {string | null}
|
|
3757
|
+
*/
|
|
3758
|
+
function find_first_hook_call_name(node) {
|
|
3759
|
+
if (!node || typeof node !== 'object') {
|
|
3760
|
+
return null;
|
|
3761
|
+
}
|
|
3762
|
+
|
|
3763
|
+
if (node.type === 'CallExpression' && is_hook_callee(node.callee)) {
|
|
3764
|
+
return get_hook_callee_name(node.callee);
|
|
3765
|
+
}
|
|
3766
|
+
|
|
3767
|
+
if (
|
|
3768
|
+
node.type === 'FunctionDeclaration' ||
|
|
3769
|
+
node.type === 'FunctionExpression' ||
|
|
3770
|
+
node.type === 'ArrowFunctionExpression' ||
|
|
3771
|
+
node.type === 'Component'
|
|
3772
|
+
) {
|
|
3773
|
+
return null;
|
|
3774
|
+
}
|
|
3775
|
+
|
|
3776
|
+
if (Array.isArray(node)) {
|
|
3777
|
+
for (const child of node) {
|
|
3778
|
+
const name = find_first_hook_call_name(child);
|
|
3779
|
+
if (name) return name;
|
|
3780
|
+
}
|
|
3781
|
+
return null;
|
|
3782
|
+
}
|
|
3783
|
+
|
|
3784
|
+
for (const key of Object.keys(node)) {
|
|
3785
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
3786
|
+
continue;
|
|
3787
|
+
}
|
|
3788
|
+
const name = find_first_hook_call_name(node[key]);
|
|
3789
|
+
if (name) return name;
|
|
3790
|
+
}
|
|
3791
|
+
|
|
3792
|
+
return null;
|
|
3793
|
+
}
|
|
3794
|
+
|
|
3795
|
+
/**
|
|
3796
|
+
* @param {any} callee
|
|
3797
|
+
* @returns {string}
|
|
3798
|
+
*/
|
|
3799
|
+
function get_hook_callee_name(callee) {
|
|
3800
|
+
if (callee?.type === 'Identifier') {
|
|
3801
|
+
return callee.name;
|
|
3802
|
+
}
|
|
3803
|
+
if (
|
|
3804
|
+
callee?.type === 'MemberExpression' &&
|
|
3805
|
+
!callee.computed &&
|
|
3806
|
+
callee.property?.type === 'Identifier'
|
|
3807
|
+
) {
|
|
3808
|
+
return callee.property.name;
|
|
3809
|
+
}
|
|
3810
|
+
return 'hook';
|
|
3811
|
+
}
|
|
3812
|
+
|
|
3813
|
+
/**
|
|
3814
|
+
* @param {any[]} body_nodes
|
|
3815
|
+
* @param {any} key_expression
|
|
3816
|
+
* @param {any} source_node
|
|
3817
|
+
* @param {TransformContext} transform_context
|
|
3818
|
+
* @param {AST.Identifier} [preallocated_helper_id] - Optional pre-allocated id.
|
|
3819
|
+
* Used by the switch lift's chained-call build, which allocates ids in
|
|
3820
|
+
* source order in a forward pass and then constructs helpers in reverse so
|
|
3821
|
+
* each fall-through case can reference the next case's component element.
|
|
3822
|
+
* @returns {{ setup_statements: any[], component_element: ESTreeJSX.JSXElement }}
|
|
3823
|
+
*/
|
|
3824
|
+
function create_hook_safe_helper(
|
|
3825
|
+
body_nodes,
|
|
3826
|
+
key_expression,
|
|
3827
|
+
source_node,
|
|
3828
|
+
transform_context,
|
|
3829
|
+
preallocated_helper_id,
|
|
3830
|
+
) {
|
|
3831
|
+
validate_hook_safe_body_does_not_assign_hook_results_to_outer_bindings(
|
|
3832
|
+
body_nodes,
|
|
3833
|
+
transform_context,
|
|
3834
|
+
);
|
|
3835
|
+
|
|
3836
|
+
const helper_id =
|
|
3837
|
+
preallocated_helper_id ??
|
|
3838
|
+
create_generated_identifier(create_local_statement_component_name(transform_context));
|
|
3839
|
+
const use_module_scoped_component = should_use_module_scoped_hook_components(transform_context);
|
|
3840
|
+
const component_id = use_module_scoped_component
|
|
3841
|
+
? create_module_scoped_hook_component_id(helper_id, transform_context)
|
|
3842
|
+
: helper_id;
|
|
3843
|
+
const helper_bindings = get_referenced_helper_bindings(
|
|
3844
|
+
body_nodes,
|
|
3845
|
+
transform_context.available_bindings,
|
|
3846
|
+
);
|
|
3847
|
+
const aliases = use_module_scoped_component
|
|
3848
|
+
? []
|
|
3849
|
+
: helper_bindings.map((binding) => create_helper_type_alias_declaration(helper_id, binding));
|
|
3850
|
+
const props_type =
|
|
3851
|
+
helper_bindings.length > 0 && !use_module_scoped_component
|
|
3852
|
+
? create_helper_props_type_literal(helper_bindings, aliases)
|
|
3853
|
+
: null;
|
|
3854
|
+
const params =
|
|
3855
|
+
helper_bindings.length > 0
|
|
3856
|
+
? [
|
|
3857
|
+
props_type !== null
|
|
3858
|
+
? create_typed_helper_props_pattern(helper_bindings, props_type)
|
|
3859
|
+
: create_helper_props_pattern(helper_bindings),
|
|
3860
|
+
]
|
|
3861
|
+
: [];
|
|
3862
|
+
|
|
3863
|
+
const saved_bindings = transform_context.available_bindings;
|
|
3864
|
+
transform_context.available_bindings = new Map(saved_bindings);
|
|
3865
|
+
|
|
3866
|
+
const helper_fn = /** @type {any} */ ({
|
|
3867
|
+
type: 'FunctionExpression',
|
|
3868
|
+
id: clone_identifier(component_id),
|
|
3869
|
+
params,
|
|
3870
|
+
body: {
|
|
3871
|
+
type: 'BlockStatement',
|
|
3872
|
+
body: build_render_statements(body_nodes, true, transform_context),
|
|
3873
|
+
metadata: { path: [] },
|
|
3874
|
+
},
|
|
3875
|
+
async: false,
|
|
3876
|
+
generator: false,
|
|
3877
|
+
metadata: {
|
|
3878
|
+
path: [],
|
|
3879
|
+
is_component: true,
|
|
3880
|
+
is_method: false,
|
|
3881
|
+
},
|
|
3882
|
+
});
|
|
3883
|
+
|
|
3884
|
+
transform_context.available_bindings = saved_bindings;
|
|
3885
|
+
|
|
3886
|
+
const component_element = create_helper_component_element(
|
|
3887
|
+
component_id,
|
|
3888
|
+
helper_bindings,
|
|
3889
|
+
source_node,
|
|
3890
|
+
{
|
|
3891
|
+
mapWrapper: false,
|
|
3892
|
+
mapBindingNames: false,
|
|
3893
|
+
mapBindingValues: false,
|
|
3894
|
+
},
|
|
3895
|
+
);
|
|
2992
3896
|
|
|
2993
3897
|
if (key_expression) {
|
|
2994
3898
|
component_element.openingElement.attributes.push(
|
|
@@ -3401,9 +4305,7 @@ function to_jsx_child(node, transform_context) {
|
|
|
3401
4305
|
case 'TSRXExpression':
|
|
3402
4306
|
return to_jsx_expression_container(node.expression, node);
|
|
3403
4307
|
case 'Html':
|
|
3404
|
-
|
|
3405
|
-
`\`{html ...}\` is not supported on the ${transform_context.platform.name} target. Use \`dangerouslySetInnerHTML={{ __html: ... }}\` as an element attribute instead.`,
|
|
3406
|
-
);
|
|
4308
|
+
return report_html_template_unsupported_error(node, transform_context);
|
|
3407
4309
|
case 'IfStatement':
|
|
3408
4310
|
return (
|
|
3409
4311
|
transform_context.platform.hooks?.controlFlow?.ifStatement ?? if_statement_to_jsx_child
|
|
@@ -3448,22 +4350,25 @@ function tsrx_node_to_jsx_expression(node, transform_context, in_jsx_child = fal
|
|
|
3448
4350
|
let expression;
|
|
3449
4351
|
if (children.length === 0) {
|
|
3450
4352
|
expression = create_null_literal();
|
|
3451
|
-
} else if (
|
|
3452
|
-
children.every(is_inline_element_child) &&
|
|
3453
|
-
!children_contain_return_semantics(children)
|
|
3454
|
-
) {
|
|
3455
|
-
const saved_inside_element_child = transform_context.inside_element_child;
|
|
3456
|
-
transform_context.inside_element_child = true;
|
|
3457
|
-
try {
|
|
3458
|
-
const render_nodes = children.map((/** @type {any} */ child) =>
|
|
3459
|
-
to_jsx_child(child, transform_context),
|
|
3460
|
-
);
|
|
3461
|
-
expression = build_return_expression(render_nodes) || create_null_literal();
|
|
3462
|
-
} finally {
|
|
3463
|
-
transform_context.inside_element_child = saved_inside_element_child;
|
|
3464
|
-
}
|
|
3465
4353
|
} else {
|
|
3466
|
-
expression =
|
|
4354
|
+
expression = return_value_body_to_expression(children, node, transform_context);
|
|
4355
|
+
}
|
|
4356
|
+
|
|
4357
|
+
if (!expression) {
|
|
4358
|
+
if (children.every(is_inline_element_child) && !children_contain_return_semantics(children)) {
|
|
4359
|
+
const saved_inside_element_child = transform_context.inside_element_child;
|
|
4360
|
+
transform_context.inside_element_child = true;
|
|
4361
|
+
try {
|
|
4362
|
+
const render_nodes = children.map((/** @type {any} */ child) =>
|
|
4363
|
+
to_jsx_child(child, transform_context),
|
|
4364
|
+
);
|
|
4365
|
+
expression = build_return_expression(render_nodes) || create_null_literal();
|
|
4366
|
+
} finally {
|
|
4367
|
+
transform_context.inside_element_child = saved_inside_element_child;
|
|
4368
|
+
}
|
|
4369
|
+
} else {
|
|
4370
|
+
expression = statement_body_to_jsx_child(children, transform_context).expression;
|
|
4371
|
+
}
|
|
3467
4372
|
}
|
|
3468
4373
|
|
|
3469
4374
|
if (
|
|
@@ -3479,6 +4384,149 @@ function tsrx_node_to_jsx_expression(node, transform_context, in_jsx_child = fal
|
|
|
3479
4384
|
return expression;
|
|
3480
4385
|
}
|
|
3481
4386
|
|
|
4387
|
+
/**
|
|
4388
|
+
* Explicit return values inside expression-position `<tsrx>` templates are JavaScript
|
|
4389
|
+
* values, so keep them out of platform render control flow.
|
|
4390
|
+
*
|
|
4391
|
+
* @param {any[]} body_nodes
|
|
4392
|
+
* @param {any} source_node
|
|
4393
|
+
* @param {TransformContext} [transform_context]
|
|
4394
|
+
* @returns {any | null}
|
|
4395
|
+
*/
|
|
4396
|
+
export function return_value_body_to_expression(body_nodes, source_node, transform_context) {
|
|
4397
|
+
if (!body_contains_top_level_return_value(body_nodes)) return null;
|
|
4398
|
+
|
|
4399
|
+
if (body_nodes.length === 1) {
|
|
4400
|
+
const expression = return_value_statement_to_expression(body_nodes[0], transform_context);
|
|
4401
|
+
if (expression) return expression;
|
|
4402
|
+
}
|
|
4403
|
+
|
|
4404
|
+
return create_statement_iife(body_nodes, source_node, transform_context);
|
|
4405
|
+
}
|
|
4406
|
+
|
|
4407
|
+
/**
|
|
4408
|
+
* @param {any} node
|
|
4409
|
+
* @param {TransformContext} [transform_context]
|
|
4410
|
+
* @returns {any | null}
|
|
4411
|
+
*/
|
|
4412
|
+
function return_value_statement_to_expression(node, transform_context) {
|
|
4413
|
+
if (node?.type === 'ReturnStatement' && node.argument != null) {
|
|
4414
|
+
return node.argument;
|
|
4415
|
+
}
|
|
4416
|
+
|
|
4417
|
+
if (node?.type === 'IfStatement') {
|
|
4418
|
+
return return_value_if_statement_to_conditional_expression(node, transform_context);
|
|
4419
|
+
}
|
|
4420
|
+
|
|
4421
|
+
return null;
|
|
4422
|
+
}
|
|
4423
|
+
|
|
4424
|
+
/**
|
|
4425
|
+
* @param {any} node
|
|
4426
|
+
* @returns {boolean}
|
|
4427
|
+
*/
|
|
4428
|
+
function body_contains_top_level_return_value(node) {
|
|
4429
|
+
if (!node || typeof node !== 'object') return false;
|
|
4430
|
+
|
|
4431
|
+
if (Array.isArray(node)) {
|
|
4432
|
+
return node.some(body_contains_top_level_return_value);
|
|
4433
|
+
}
|
|
4434
|
+
|
|
4435
|
+
if (node.type === 'ReturnStatement') {
|
|
4436
|
+
return node.argument != null;
|
|
4437
|
+
}
|
|
4438
|
+
|
|
4439
|
+
if (
|
|
4440
|
+
node.type === 'FunctionDeclaration' ||
|
|
4441
|
+
node.type === 'FunctionExpression' ||
|
|
4442
|
+
node.type === 'ArrowFunctionExpression' ||
|
|
4443
|
+
node.type === 'ClassDeclaration' ||
|
|
4444
|
+
node.type === 'ClassExpression' ||
|
|
4445
|
+
node.type === 'Component'
|
|
4446
|
+
) {
|
|
4447
|
+
return false;
|
|
4448
|
+
}
|
|
4449
|
+
|
|
4450
|
+
for (const key of Object.keys(node)) {
|
|
4451
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
4452
|
+
continue;
|
|
4453
|
+
}
|
|
4454
|
+
if (body_contains_top_level_return_value(node[key])) {
|
|
4455
|
+
return true;
|
|
4456
|
+
}
|
|
4457
|
+
}
|
|
4458
|
+
|
|
4459
|
+
return false;
|
|
4460
|
+
}
|
|
4461
|
+
|
|
4462
|
+
/**
|
|
4463
|
+
* @param {any[]} body_nodes
|
|
4464
|
+
* @param {any} source_node
|
|
4465
|
+
* @param {TransformContext} [transform_context]
|
|
4466
|
+
* @returns {any}
|
|
4467
|
+
*/
|
|
4468
|
+
function create_statement_iife(body_nodes, source_node, transform_context) {
|
|
4469
|
+
return set_generated_expression_loc(
|
|
4470
|
+
b.call(b.arrow([], b.block(body_nodes))),
|
|
4471
|
+
source_node,
|
|
4472
|
+
transform_context,
|
|
4473
|
+
);
|
|
4474
|
+
}
|
|
4475
|
+
|
|
4476
|
+
/**
|
|
4477
|
+
* @param {any} node
|
|
4478
|
+
* @param {any} source_node
|
|
4479
|
+
* @param {TransformContext} [transform_context]
|
|
4480
|
+
* @returns {any}
|
|
4481
|
+
*/
|
|
4482
|
+
function set_generated_expression_loc(node, source_node, transform_context) {
|
|
4483
|
+
if (transform_context?.typeOnly || !source_node?.loc) return node;
|
|
4484
|
+
return setLocation(/** @type {any} */ (node), source_node);
|
|
4485
|
+
}
|
|
4486
|
+
|
|
4487
|
+
/**
|
|
4488
|
+
* @returns {any}
|
|
4489
|
+
*/
|
|
4490
|
+
function create_undefined_expression() {
|
|
4491
|
+
return b.unary('void', b.literal(0));
|
|
4492
|
+
}
|
|
4493
|
+
|
|
4494
|
+
/**
|
|
4495
|
+
* @param {any} node
|
|
4496
|
+
* @param {TransformContext} [transform_context]
|
|
4497
|
+
* @returns {any | null}
|
|
4498
|
+
*/
|
|
4499
|
+
function return_value_block_to_expression(node, transform_context) {
|
|
4500
|
+
const body = node?.type === 'BlockStatement' ? node.body : node ? [node] : [];
|
|
4501
|
+
if (body.length !== 1) return null;
|
|
4502
|
+
|
|
4503
|
+
return return_value_statement_to_expression(body[0], transform_context);
|
|
4504
|
+
}
|
|
4505
|
+
|
|
4506
|
+
/**
|
|
4507
|
+
* @param {any} node
|
|
4508
|
+
* @param {TransformContext} [transform_context]
|
|
4509
|
+
* @returns {any | null}
|
|
4510
|
+
*/
|
|
4511
|
+
function return_value_if_statement_to_conditional_expression(node, transform_context) {
|
|
4512
|
+
if (!node || node.type !== 'IfStatement') return null;
|
|
4513
|
+
|
|
4514
|
+
const consequent = return_value_block_to_expression(node.consequent, transform_context);
|
|
4515
|
+
if (!consequent) return null;
|
|
4516
|
+
|
|
4517
|
+
let alternate = create_undefined_expression();
|
|
4518
|
+
if (node.alternate) {
|
|
4519
|
+
alternate = return_value_block_to_expression(node.alternate, transform_context);
|
|
4520
|
+
if (!alternate) return null;
|
|
4521
|
+
}
|
|
4522
|
+
|
|
4523
|
+
return set_generated_expression_loc(
|
|
4524
|
+
b.conditional(node.test, consequent, alternate),
|
|
4525
|
+
node,
|
|
4526
|
+
transform_context,
|
|
4527
|
+
);
|
|
4528
|
+
}
|
|
4529
|
+
|
|
3482
4530
|
/**
|
|
3483
4531
|
* @param {any} node
|
|
3484
4532
|
* @param {TransformContext} transform_context
|
|
@@ -4009,7 +5057,7 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
4009
5057
|
);
|
|
4010
5058
|
}
|
|
4011
5059
|
const pending_body = pending.body || [];
|
|
4012
|
-
if (!pending_body.some(is_jsx_child)) {
|
|
5060
|
+
if (pending_body.length > 0 && !pending_body.some(is_jsx_child)) {
|
|
4013
5061
|
error(
|
|
4014
5062
|
'Component try statements must contain a template in their "pending" body. Rendering a pending fallback is required to have a template.',
|
|
4015
5063
|
transform_context.filename,
|
|
@@ -4031,7 +5079,10 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
4031
5079
|
if (pending) {
|
|
4032
5080
|
transform_context.needs_suspense = true;
|
|
4033
5081
|
const pending_body_nodes = pending.body || [];
|
|
4034
|
-
const fallback_content =
|
|
5082
|
+
const fallback_content =
|
|
5083
|
+
pending_body_nodes.length === 0
|
|
5084
|
+
? to_jsx_expression_container(create_null_literal())
|
|
5085
|
+
: statement_body_to_jsx_child(pending_body_nodes, transform_context);
|
|
4035
5086
|
|
|
4036
5087
|
result = create_jsx_element(
|
|
4037
5088
|
'Suspense',
|
|
@@ -4069,9 +5120,16 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
4069
5120
|
// correctly identifies references to err/reset as non-static
|
|
4070
5121
|
const saved_catch_bindings = transform_context.available_bindings;
|
|
4071
5122
|
transform_context.available_bindings = new Map(saved_catch_bindings);
|
|
5123
|
+
const catch_scoped_names = new Set();
|
|
4072
5124
|
for (const param of catch_params) {
|
|
4073
5125
|
collect_pattern_bindings(param, transform_context.available_bindings);
|
|
5126
|
+
collect_pattern_names(param, catch_scoped_names);
|
|
4074
5127
|
}
|
|
5128
|
+
validate_hook_safe_body_does_not_assign_hook_results_to_outer_bindings(
|
|
5129
|
+
catch_body_nodes,
|
|
5130
|
+
transform_context,
|
|
5131
|
+
catch_scoped_names,
|
|
5132
|
+
);
|
|
4075
5133
|
|
|
4076
5134
|
const fallback_fn = {
|
|
4077
5135
|
type: 'ArrowFunctionExpression',
|