@tsrx/core 0.1.24 → 0.1.26
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/analyze/prune.js +11 -4
- package/src/index.js +1 -0
- package/src/plugin.js +154 -2
- package/src/transform/jsx/ast-builders.js +27 -0
- package/src/transform/jsx/helpers.js +8 -1
- package/src/transform/jsx/index.js +381 -1514
- package/src/transform/jsx-hoist.js +6 -0
- package/src/transform/jsx-interleave.js +4 -0
- package/src/transform/scoping.js +8 -4
- package/src/transform/segments.js +31 -1
- package/types/index.d.ts +4 -4
- package/types/jsx-platform.d.ts +27 -13
- package/types/parse.d.ts +3 -1
|
@@ -7,7 +7,6 @@ import { print } from 'esrap';
|
|
|
7
7
|
import { error } from '../../errors.js';
|
|
8
8
|
import { analyze_css } from '../../analyze/css-analyze.js';
|
|
9
9
|
import { prune_css } from '../../analyze/prune.js';
|
|
10
|
-
import { create_scopes, ScopeRoot } from '../../scope.js';
|
|
11
10
|
import {
|
|
12
11
|
in_jsx_child_context,
|
|
13
12
|
set_node_path_metadata,
|
|
@@ -15,6 +14,7 @@ import {
|
|
|
15
14
|
tsx_with_ts_locations,
|
|
16
15
|
} from './helpers.js';
|
|
17
16
|
import {
|
|
17
|
+
add_extra_source_mappings_from_matching_expression,
|
|
18
18
|
clone_expression_node,
|
|
19
19
|
clone_identifier,
|
|
20
20
|
clone_jsx_name,
|
|
@@ -49,7 +49,6 @@ import {
|
|
|
49
49
|
create_style_ref_setup_statements,
|
|
50
50
|
get_style_element_stylesheet,
|
|
51
51
|
} from '../style-ref.js';
|
|
52
|
-
import { is_function_or_component_node } from '../../utils/ast.js';
|
|
53
52
|
import {
|
|
54
53
|
is_interleaved_body as is_interleaved_body_core,
|
|
55
54
|
is_capturable_jsx_child,
|
|
@@ -57,10 +56,6 @@ import {
|
|
|
57
56
|
} from '../jsx-interleave.js';
|
|
58
57
|
import { is_hoist_safe_jsx_node } from '../jsx-hoist.js';
|
|
59
58
|
|
|
60
|
-
const HOOK_OUTER_ASSIGNMENT_ERROR =
|
|
61
|
-
'Hook calls inside conditional or repeated TSRX scopes must keep their results local to the generated hook component.';
|
|
62
|
-
const HOOK_CALLBACK_OUTER_MUTATION_ERROR =
|
|
63
|
-
'Hook callbacks inside conditional or repeated TSRX scopes must not mutate bindings declared outside the generated hook component.';
|
|
64
59
|
const TEMPLATE_FRAGMENT_ERROR =
|
|
65
60
|
'JSX fragment syntax is not needed in TSRX templates. TSRX renders in immediate mode, so everything is already a fragment. Use `<>...</>` only in expression position.';
|
|
66
61
|
const TSRX_FOR_RETURN_ERROR =
|
|
@@ -74,6 +69,8 @@ const TSRX_IF_RETURN_ERROR =
|
|
|
74
69
|
const TSRX_IF_BREAK_ERROR = 'Break statements are not allowed inside TSRX template @if blocks.';
|
|
75
70
|
const TSRX_IF_CONTINUE_ERROR =
|
|
76
71
|
'Continue statements are not allowed inside TSRX template @if blocks. Filter before rendering or use conditional output instead.';
|
|
72
|
+
const DYNAMIC_IMPORT_LOCAL = 'TsrxDynamic';
|
|
73
|
+
const DYNAMIC_FACTORY_LOCAL = '_tsrx_dynamic';
|
|
77
74
|
|
|
78
75
|
/**
|
|
79
76
|
* @param {AST.Node} node
|
|
@@ -89,44 +86,6 @@ function report_jsx_fragment_in_tsrx_error(node, transform_context) {
|
|
|
89
86
|
);
|
|
90
87
|
}
|
|
91
88
|
|
|
92
|
-
/**
|
|
93
|
-
* @param {AST.Node} node
|
|
94
|
-
* @param {string[]} names
|
|
95
|
-
* @param {string} hook_name
|
|
96
|
-
* @param {TransformContext} transform_context
|
|
97
|
-
* @returns {void}
|
|
98
|
-
*/
|
|
99
|
-
function report_hook_outer_assignment_error(node, names, hook_name, transform_context) {
|
|
100
|
-
const target =
|
|
101
|
-
names.length === 1 ? `\`${names[0]}\`` : names.map((name) => `\`${name}\``).join(', ');
|
|
102
|
-
error(
|
|
103
|
-
`${HOOK_OUTER_ASSIGNMENT_ERROR} The ${hook_name} result is assigned to ${target}, which is declared outside that generated component. Declare the hook result inside the TSRX branch, or move the hook into an explicit child component and pass values with props.`,
|
|
104
|
-
transform_context.filename,
|
|
105
|
-
node,
|
|
106
|
-
transform_context.errors,
|
|
107
|
-
transform_context.comments,
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* @param {AST.Node} node
|
|
113
|
-
* @param {string[]} names
|
|
114
|
-
* @param {string} hook_name
|
|
115
|
-
* @param {TransformContext} transform_context
|
|
116
|
-
* @returns {void}
|
|
117
|
-
*/
|
|
118
|
-
function report_hook_callback_outer_mutation_error(node, names, hook_name, transform_context) {
|
|
119
|
-
const target =
|
|
120
|
-
names.length === 1 ? `\`${names[0]}\`` : names.map((name) => `\`${name}\``).join(', ');
|
|
121
|
-
error(
|
|
122
|
-
`${HOOK_CALLBACK_OUTER_MUTATION_ERROR} The ${hook_name} callback mutates ${target}. Read outer values through props or dependencies, and move mutable state into an explicit child component when it needs to change over time.`,
|
|
123
|
-
transform_context.filename,
|
|
124
|
-
node,
|
|
125
|
-
transform_context.errors,
|
|
126
|
-
transform_context.comments,
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
89
|
/**
|
|
131
90
|
* Local alias for the shared `JsxTransformContext`. Kept as a typedef so the
|
|
132
91
|
* rest of this file's `@param {TransformContext}` annotations don't all have
|
|
@@ -259,15 +218,32 @@ function wrap_in_native_tsrx_fragment(node) {
|
|
|
259
218
|
* (`const x = @switch (…) { … }`, `x = @switch (…) { … }`), or a call/`new`
|
|
260
219
|
* argument (`render(@if (…) { … })`) — in a native TSRX fragment.
|
|
261
220
|
* @param {any} node
|
|
221
|
+
* @param {TransformContext | null} lower_dynamic_context
|
|
262
222
|
* @param {Set<any>} [seen]
|
|
263
223
|
* @returns {void}
|
|
264
224
|
*/
|
|
265
|
-
function wrap_control_flow_expression_values(node, seen = new Set()) {
|
|
225
|
+
function wrap_control_flow_expression_values(node, lower_dynamic_context, seen = new Set()) {
|
|
266
226
|
if (!node || typeof node !== 'object' || seen.has(node)) return;
|
|
267
227
|
seen.add(node);
|
|
268
228
|
|
|
229
|
+
// Dynamic tags on factory platforms must lower before control-flow
|
|
230
|
+
// conversion and static hoisting run. Production output needs the scoped
|
|
231
|
+
// `const TsrxDynamic_N = ...` binding declared in the scope that owns the
|
|
232
|
+
// tag expression (e.g. a `@for` loop variable); type-only output needs
|
|
233
|
+
// `<TsrxDynamic is={expr}>` in place before a reference-free tree (e.g.
|
|
234
|
+
// `<{'div'}>`) is hoisted to a module-level static const while still
|
|
235
|
+
// carrying the raw dynamic tag. Alias lowerings return a replacement
|
|
236
|
+
// fragment, which is swapped into the child's position here.
|
|
237
|
+
const lower_child = (/** @type {any} */ child) => {
|
|
238
|
+
if (!lower_dynamic_context || child?.type !== 'JSXElement') return child;
|
|
239
|
+
return lower_dynamic_jsx_element(child, lower_dynamic_context) ?? child;
|
|
240
|
+
};
|
|
241
|
+
|
|
269
242
|
if (Array.isArray(node)) {
|
|
270
|
-
for (
|
|
243
|
+
for (let i = 0; i < node.length; i++) {
|
|
244
|
+
node[i] = lower_child(node[i]);
|
|
245
|
+
wrap_control_flow_expression_values(node[i], lower_dynamic_context, seen);
|
|
246
|
+
}
|
|
271
247
|
return;
|
|
272
248
|
}
|
|
273
249
|
|
|
@@ -299,7 +275,8 @@ function wrap_control_flow_expression_values(node, seen = new Set()) {
|
|
|
299
275
|
|
|
300
276
|
for (const key of Object.keys(node)) {
|
|
301
277
|
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') continue;
|
|
302
|
-
|
|
278
|
+
node[key] = lower_child(node[key]);
|
|
279
|
+
wrap_control_flow_expression_values(node[key], lower_dynamic_context, seen);
|
|
303
280
|
}
|
|
304
281
|
}
|
|
305
282
|
|
|
@@ -343,6 +320,8 @@ export function createJsxTransform(platform) {
|
|
|
343
320
|
needs_normalize_spread_props: false,
|
|
344
321
|
needs_normalize_spread_props_for_ref_attr: false,
|
|
345
322
|
needs_fragment: false,
|
|
323
|
+
needs_dynamic_element: false,
|
|
324
|
+
needs_dynamic_factory: false,
|
|
346
325
|
needs_for_of_iterable: false,
|
|
347
326
|
needs_iteration_value_type: false,
|
|
348
327
|
stylesheets,
|
|
@@ -353,7 +332,6 @@ export function createJsxTransform(platform) {
|
|
|
353
332
|
hook_helpers_enabled: false,
|
|
354
333
|
available_bindings: new Map(),
|
|
355
334
|
lazy_next_id: 0,
|
|
356
|
-
runtime_dynamic_scopes: null,
|
|
357
335
|
filename: filename ?? null,
|
|
358
336
|
source,
|
|
359
337
|
collect,
|
|
@@ -366,10 +344,9 @@ export function createJsxTransform(platform) {
|
|
|
366
344
|
};
|
|
367
345
|
|
|
368
346
|
expand_child_code_blocks(/** @type {any} */ (ast));
|
|
369
|
-
wrap_control_flow_expression_values(
|
|
370
|
-
transform_context.runtime_dynamic_scopes = create_runtime_dynamic_scopes(
|
|
347
|
+
wrap_control_flow_expression_values(
|
|
371
348
|
/** @type {any} */ (ast),
|
|
372
|
-
transform_context,
|
|
349
|
+
platform.imports.dynamicFactory ? transform_context : null,
|
|
373
350
|
);
|
|
374
351
|
|
|
375
352
|
if (!transform_context.typeOnly) {
|
|
@@ -406,7 +383,15 @@ export function createJsxTransform(platform) {
|
|
|
406
383
|
return /** @type {any} */ (wrap_jsx_setup_declarations(expression, in_jsx_child));
|
|
407
384
|
},
|
|
408
385
|
|
|
409
|
-
JSXElement(node, { next, path, state }) {
|
|
386
|
+
JSXElement(node, { next, path, state, visit }) {
|
|
387
|
+
const lowered = lower_dynamic_jsx_element(node, state);
|
|
388
|
+
if (lowered) {
|
|
389
|
+
// Alias lowerings replace the element with a fragment; factory
|
|
390
|
+
// platforms normally lower in the pre-walk pass, so this only
|
|
391
|
+
// covers elements introduced after it.
|
|
392
|
+
return /** @type {any} */ (visit(lowered, state));
|
|
393
|
+
}
|
|
394
|
+
|
|
410
395
|
if (!node.metadata?.native_tsrx) {
|
|
411
396
|
return next() ?? node;
|
|
412
397
|
}
|
|
@@ -505,6 +490,7 @@ export function createJsxTransform(platform) {
|
|
|
505
490
|
transformed_program.body.unshift(...type_only_style_anchors);
|
|
506
491
|
}
|
|
507
492
|
const expanded = expand_component_helpers(transformed_program);
|
|
493
|
+
inject_dynamic_import(expanded, transform_context);
|
|
508
494
|
if (platform.hooks?.injectImports) {
|
|
509
495
|
platform.hooks.injectImports(expanded, transform_context, suspense_source);
|
|
510
496
|
} else {
|
|
@@ -540,6 +526,179 @@ export function createJsxTransform(platform) {
|
|
|
540
526
|
return transform;
|
|
541
527
|
}
|
|
542
528
|
|
|
529
|
+
/**
|
|
530
|
+
* Lower a single parser-native dynamic tag (`<{expr}>`) into the target runtime
|
|
531
|
+
* `<Dynamic is={expr}>` helper shape while the existing JSXElement walker is
|
|
532
|
+
* already visiting it. The dynamic name container travels by reference through
|
|
533
|
+
* element rebuilds, so checking it covers rebuilt elements too; once lowered,
|
|
534
|
+
* the name is a plain `JSXIdentifier` and the element is skipped on re-visits.
|
|
535
|
+
*
|
|
536
|
+
* The parsed element is never mutated: every lowering builds a fresh
|
|
537
|
+
* replacement node (an element, or a fragment for the alias lowering) that
|
|
538
|
+
* the caller must put in the original element's position.
|
|
539
|
+
*
|
|
540
|
+
* @param {any} node
|
|
541
|
+
* @param {TransformContext} transform_context
|
|
542
|
+
* @returns {ESTreeJSX.JSXElement | ESTreeJSX.JSXFragment | undefined}
|
|
543
|
+
*/
|
|
544
|
+
function lower_dynamic_jsx_element(node, transform_context) {
|
|
545
|
+
const dynamic_name = node.openingElement?.name;
|
|
546
|
+
if (dynamic_name?.type !== 'JSXExpressionContainer' || dynamic_name.isDynamic !== true) return;
|
|
547
|
+
|
|
548
|
+
// Type-only output always uses the `<TsrxDynamic is={expr}>` component
|
|
549
|
+
// shape; production output prefers the platform's runtime factory when one
|
|
550
|
+
// is configured (e.g. Solid's `dynamic`).
|
|
551
|
+
const factory = transform_context.typeOnly
|
|
552
|
+
? undefined
|
|
553
|
+
: transform_context.platform.imports.dynamicFactory;
|
|
554
|
+
if (!factory && !transform_context.platform.imports.dynamic) return;
|
|
555
|
+
|
|
556
|
+
const dynamic_expression = dynamic_name.expression;
|
|
557
|
+
if (!dynamic_expression) return;
|
|
558
|
+
const generated_expression = clone_expression_node(dynamic_expression);
|
|
559
|
+
if (node.closingElement?.name?.expression) {
|
|
560
|
+
// One generated expression stands in for both tags; record the closing
|
|
561
|
+
// tag's positions so editor features keep working on `</{expr}>`.
|
|
562
|
+
add_extra_source_mappings_from_matching_expression(
|
|
563
|
+
generated_expression,
|
|
564
|
+
clone_expression_node(node.closingElement.name.expression),
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Rebuild the element as an ordinary component reference named `name_id`,
|
|
570
|
+
* carrying the original attributes (after any `extra_attributes`) and
|
|
571
|
+
* children over by reference.
|
|
572
|
+
*
|
|
573
|
+
* @param {ESTreeJSX.JSXIdentifier} name_id
|
|
574
|
+
* @param {ESTreeJSX.JSXAttribute[]} [extra_attributes]
|
|
575
|
+
* @returns {ESTreeJSX.JSXElement}
|
|
576
|
+
*/
|
|
577
|
+
const rebuild_element = (name_id, extra_attributes = []) => {
|
|
578
|
+
const element = b.jsx_element_fresh(
|
|
579
|
+
b.jsx_opening_element(
|
|
580
|
+
name_id,
|
|
581
|
+
[...extra_attributes, ...(node.openingElement.attributes || [])],
|
|
582
|
+
node.openingElement.selfClosing,
|
|
583
|
+
node.openingElement.typeArguments,
|
|
584
|
+
node.openingElement,
|
|
585
|
+
),
|
|
586
|
+
node.closingElement
|
|
587
|
+
? b.jsx_closing_element(b.jsx_id(name_id.name), node.closingElement)
|
|
588
|
+
: null,
|
|
589
|
+
node.children,
|
|
590
|
+
node,
|
|
591
|
+
);
|
|
592
|
+
element.metadata = { ...(node.metadata || {}), path: [] };
|
|
593
|
+
return element;
|
|
594
|
+
};
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Scoped-CSS passes treat lowered dynamic tags like the imported `Dynamic`
|
|
598
|
+
* helper: type selectors survive pruning and the scope hash lands on the
|
|
599
|
+
* element's class.
|
|
600
|
+
*
|
|
601
|
+
* @param {ESTreeJSX.JSXElement} element
|
|
602
|
+
* @returns {ESTreeJSX.JSXElement}
|
|
603
|
+
*/
|
|
604
|
+
const mark_dynamic_element = (element) => {
|
|
605
|
+
element.metadata.dynamicElement = true;
|
|
606
|
+
return element;
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
if (factory) {
|
|
610
|
+
// Bind the tag expression to a scoped component const and reference it
|
|
611
|
+
// like an ordinary component.
|
|
612
|
+
transform_context.local_statement_component_index += 1;
|
|
613
|
+
const local = `${DYNAMIC_IMPORT_LOCAL}_${transform_context.local_statement_component_index}`;
|
|
614
|
+
const local_id = b.jsx_id(local);
|
|
615
|
+
transform_context.needs_dynamic_factory = true;
|
|
616
|
+
|
|
617
|
+
if (factory.renderBlock) {
|
|
618
|
+
// Import-free alias inside a reactive render block (Vue): the const
|
|
619
|
+
// is a plain snapshot and Vapor never re-runs setup, so the whole
|
|
620
|
+
// element is rebuilt in a native fragment whose expression-container
|
|
621
|
+
// child holds
|
|
622
|
+
// `(() => { const TsrxDynamic_1 = expr; return <TsrxDynamic_1 ...>; })()`
|
|
623
|
+
// — vue-jsx-vapor compiles expression children into `createNodes(...)`
|
|
624
|
+
// render blocks, which re-run the IIFE when the tag expression
|
|
625
|
+
// changes. The container is marked so downstream lone-child
|
|
626
|
+
// collapsing keeps it in expression-child position instead of
|
|
627
|
+
// unwrapping to a bare call.
|
|
628
|
+
const element = mark_dynamic_element(rebuild_element(local_id));
|
|
629
|
+
const wrapper = b.arrow(
|
|
630
|
+
[],
|
|
631
|
+
b.block([b.const(b.id(local), generated_expression), b.return(element)], node),
|
|
632
|
+
);
|
|
633
|
+
// Lets scoped-CSS collection descend into this generated closure;
|
|
634
|
+
// user function boundaries are otherwise skipped.
|
|
635
|
+
wrapper.metadata = /** @type {any} */ ({
|
|
636
|
+
...(wrapper.metadata || { path: [] }),
|
|
637
|
+
tsrx_dynamic_wrapper: true,
|
|
638
|
+
});
|
|
639
|
+
const container = to_jsx_expression_container(b.call(wrapper), element);
|
|
640
|
+
container.metadata = /** @type {any} */ ({
|
|
641
|
+
...(container.metadata || { path: [] }),
|
|
642
|
+
tsrx_reactive_block: true,
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
return set_loc(wrap_in_native_tsrx_fragment(container), node);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// Statement placement: `const TsrxDynamic_1 = ...;` next to the
|
|
649
|
+
// template. With a factory, the thunk keeps the tag reactive (Solid:
|
|
650
|
+
// `_tsrx_dynamic(() => expr)`); without one, the plain alias is
|
|
651
|
+
// re-evaluated by the host's render cycle (React/Preact re-run the
|
|
652
|
+
// component body). The declaration rides on the name node's metadata:
|
|
653
|
+
// element rebuilds clone names with a shared metadata reference, so
|
|
654
|
+
// setup extraction still finds it afterwards.
|
|
655
|
+
add_jsx_setup_declaration(
|
|
656
|
+
local_id,
|
|
657
|
+
b.const(
|
|
658
|
+
b.id(local),
|
|
659
|
+
factory.name
|
|
660
|
+
? b.call(b.id(DYNAMIC_FACTORY_LOCAL), b.arrow([], generated_expression))
|
|
661
|
+
: generated_expression,
|
|
662
|
+
),
|
|
663
|
+
);
|
|
664
|
+
return mark_dynamic_element(rebuild_element(local_id));
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
transform_context.needs_dynamic_element = true;
|
|
668
|
+
return mark_dynamic_element(
|
|
669
|
+
rebuild_element(b.jsx_id(DYNAMIC_IMPORT_LOCAL), [
|
|
670
|
+
b.jsx_attribute(
|
|
671
|
+
b.jsx_id('is'),
|
|
672
|
+
b.jsx_expression_container(generated_expression, dynamic_name),
|
|
673
|
+
false,
|
|
674
|
+
dynamic_name,
|
|
675
|
+
),
|
|
676
|
+
]),
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* @param {AST.Program} program
|
|
682
|
+
* @param {TransformContext} transform_context
|
|
683
|
+
* @returns {void}
|
|
684
|
+
*/
|
|
685
|
+
function inject_dynamic_import(program, transform_context) {
|
|
686
|
+
const factory = transform_context.platform.imports.dynamicFactory;
|
|
687
|
+
if (transform_context.needs_dynamic_factory && factory?.name && factory.source) {
|
|
688
|
+
program.body.unshift(
|
|
689
|
+
b.import_declaration(
|
|
690
|
+
[b.import_specifier(factory.name, DYNAMIC_FACTORY_LOCAL)],
|
|
691
|
+
factory.source,
|
|
692
|
+
),
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
const source = transform_context.platform.imports.dynamic;
|
|
696
|
+
if (!transform_context.needs_dynamic_element || !source) return;
|
|
697
|
+
program.body.unshift(
|
|
698
|
+
b.import_declaration([b.import_specifier('Dynamic', DYNAMIC_IMPORT_LOCAL)], source),
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
|
|
543
702
|
/**
|
|
544
703
|
* Attach selector-location metadata used by editor definitions/hover before
|
|
545
704
|
* the shared scoping pass mutates class attributes with the component hash.
|
|
@@ -606,15 +765,18 @@ function collect_css_prunable_elements(value, elements = [], transform_context =
|
|
|
606
765
|
}
|
|
607
766
|
|
|
608
767
|
if (
|
|
609
|
-
value.type === 'FunctionDeclaration' ||
|
|
610
|
-
|
|
611
|
-
|
|
768
|
+
(value.type === 'FunctionDeclaration' ||
|
|
769
|
+
value.type === 'FunctionExpression' ||
|
|
770
|
+
value.type === 'ArrowFunctionExpression') &&
|
|
771
|
+
// Generated dynamic-tag wrappers are render-block closures, not user
|
|
772
|
+
// component boundaries — the element inside still belongs to this
|
|
773
|
+
// component's scoped CSS.
|
|
774
|
+
value.metadata?.tsrx_dynamic_wrapper !== true
|
|
612
775
|
) {
|
|
613
776
|
return elements;
|
|
614
777
|
}
|
|
615
778
|
|
|
616
779
|
if (value.type === 'JSXElement' && value.metadata?.native_tsrx) {
|
|
617
|
-
mark_runtime_dynamic_element(value, transform_context);
|
|
618
780
|
if (!is_style_element(value)) {
|
|
619
781
|
elements.push(value);
|
|
620
782
|
}
|
|
@@ -630,241 +792,6 @@ function collect_css_prunable_elements(value, elements = [], transform_context =
|
|
|
630
792
|
return elements;
|
|
631
793
|
}
|
|
632
794
|
|
|
633
|
-
/**
|
|
634
|
-
* @param {AST.Program} ast
|
|
635
|
-
* @param {TransformContext} transform_context
|
|
636
|
-
* @returns {Map<any, any> | null}
|
|
637
|
-
*/
|
|
638
|
-
function create_runtime_dynamic_scopes(ast, transform_context) {
|
|
639
|
-
const dynamic_source = transform_context.platform.imports.dynamic;
|
|
640
|
-
if (!dynamic_source) {
|
|
641
|
-
return null;
|
|
642
|
-
}
|
|
643
|
-
if (!has_runtime_dynamic_import(ast, dynamic_source)) {
|
|
644
|
-
return null;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
const { scopes } = create_scopes(ast, new ScopeRoot(), null, {
|
|
648
|
-
collect: true,
|
|
649
|
-
errors: [],
|
|
650
|
-
filename: transform_context.filename ?? '',
|
|
651
|
-
comments: transform_context.comments,
|
|
652
|
-
});
|
|
653
|
-
|
|
654
|
-
return scopes;
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
/**
|
|
658
|
-
* @param {any} node
|
|
659
|
-
* @param {TransformContext | null} transform_context
|
|
660
|
-
* @returns {void}
|
|
661
|
-
*/
|
|
662
|
-
function mark_runtime_dynamic_element(node, transform_context) {
|
|
663
|
-
const dynamic_source = transform_context?.platform.imports.dynamic;
|
|
664
|
-
const scopes = transform_context?.runtime_dynamic_scopes;
|
|
665
|
-
if (
|
|
666
|
-
!dynamic_source ||
|
|
667
|
-
!scopes ||
|
|
668
|
-
node.metadata?.runtime_dynamic_element === true ||
|
|
669
|
-
!has_jsx_attribute(node, 'is') ||
|
|
670
|
-
!is_runtime_dynamic_jsx_name(node.openingElement?.name, scopes.get(node), dynamic_source)
|
|
671
|
-
) {
|
|
672
|
-
return;
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
node.metadata.runtime_dynamic_element = true;
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
/**
|
|
679
|
-
* @param {AST.Program} ast
|
|
680
|
-
* @param {string} dynamic_source
|
|
681
|
-
* @returns {boolean}
|
|
682
|
-
*/
|
|
683
|
-
function has_runtime_dynamic_import(ast, dynamic_source) {
|
|
684
|
-
return ast.body.some(
|
|
685
|
-
(/** @type {any} */ node) =>
|
|
686
|
-
node.type === 'ImportDeclaration' &&
|
|
687
|
-
node.importKind !== 'type' &&
|
|
688
|
-
node.source?.type === 'Literal' &&
|
|
689
|
-
node.source.value === dynamic_source &&
|
|
690
|
-
node.specifiers.some(
|
|
691
|
-
(/** @type {any} */ specifier) =>
|
|
692
|
-
is_runtime_dynamic_import_specifier(specifier, 'component') ||
|
|
693
|
-
is_runtime_dynamic_import_specifier(specifier, 'namespace'),
|
|
694
|
-
),
|
|
695
|
-
);
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
/**
|
|
699
|
-
* @param {any} node
|
|
700
|
-
* @param {string} name
|
|
701
|
-
* @returns {boolean}
|
|
702
|
-
*/
|
|
703
|
-
function has_jsx_attribute(node, name) {
|
|
704
|
-
return (node.openingElement?.attributes ?? []).some(
|
|
705
|
-
(/** @type {any} */ attr) =>
|
|
706
|
-
attr.type === 'JSXAttribute' &&
|
|
707
|
-
attr.name?.type === 'JSXIdentifier' &&
|
|
708
|
-
attr.name.name === name,
|
|
709
|
-
);
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
/**
|
|
713
|
-
* @param {any} name
|
|
714
|
-
* @param {any} scope
|
|
715
|
-
* @param {string} dynamic_source
|
|
716
|
-
* @returns {boolean}
|
|
717
|
-
*/
|
|
718
|
-
function is_runtime_dynamic_jsx_name(name, scope, dynamic_source) {
|
|
719
|
-
if (!scope || !name) {
|
|
720
|
-
return false;
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
if (name.type === 'JSXIdentifier') {
|
|
724
|
-
return is_runtime_dynamic_binding(scope.get(name.name), dynamic_source, 'component', new Set());
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
if (
|
|
728
|
-
name.type === 'JSXMemberExpression' &&
|
|
729
|
-
name.object?.type === 'JSXIdentifier' &&
|
|
730
|
-
name.property?.type === 'JSXIdentifier' &&
|
|
731
|
-
name.property.name === 'Dynamic'
|
|
732
|
-
) {
|
|
733
|
-
return is_runtime_dynamic_binding(
|
|
734
|
-
scope.get(name.object.name),
|
|
735
|
-
dynamic_source,
|
|
736
|
-
'namespace',
|
|
737
|
-
new Set(),
|
|
738
|
-
);
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
return false;
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
/**
|
|
745
|
-
* @param {any} binding
|
|
746
|
-
* @param {string} dynamic_source
|
|
747
|
-
* @param {'component' | 'namespace'} kind
|
|
748
|
-
* @param {Set<any>} seen
|
|
749
|
-
* @returns {boolean}
|
|
750
|
-
*/
|
|
751
|
-
function is_runtime_dynamic_binding(binding, dynamic_source, kind, seen) {
|
|
752
|
-
if (!binding || seen.has(binding)) {
|
|
753
|
-
return false;
|
|
754
|
-
}
|
|
755
|
-
seen.add(binding);
|
|
756
|
-
|
|
757
|
-
if (is_runtime_dynamic_import_binding(binding, dynamic_source, kind)) {
|
|
758
|
-
return true;
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
if (binding.reassigned) {
|
|
762
|
-
return false;
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
const initial = unwrap_reference_expression(binding.initial);
|
|
766
|
-
if (!initial) {
|
|
767
|
-
return false;
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
if (initial.type === 'Identifier') {
|
|
771
|
-
return is_runtime_dynamic_binding(binding.scope.get(initial.name), dynamic_source, kind, seen);
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
if (
|
|
775
|
-
kind === 'component' &&
|
|
776
|
-
initial.type === 'MemberExpression' &&
|
|
777
|
-
!initial.computed &&
|
|
778
|
-
initial.object?.type === 'Identifier' &&
|
|
779
|
-
initial.property?.type === 'Identifier' &&
|
|
780
|
-
initial.property.name === 'Dynamic'
|
|
781
|
-
) {
|
|
782
|
-
return is_runtime_dynamic_binding(
|
|
783
|
-
binding.scope.get(initial.object.name),
|
|
784
|
-
dynamic_source,
|
|
785
|
-
'namespace',
|
|
786
|
-
new Set(),
|
|
787
|
-
);
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
return false;
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
/**
|
|
794
|
-
* @param {any} binding
|
|
795
|
-
* @param {string} dynamic_source
|
|
796
|
-
* @param {'component' | 'namespace'} kind
|
|
797
|
-
* @returns {boolean}
|
|
798
|
-
*/
|
|
799
|
-
function is_runtime_dynamic_import_binding(binding, dynamic_source, kind) {
|
|
800
|
-
const declaration = binding?.initial;
|
|
801
|
-
if (
|
|
802
|
-
binding?.declaration_kind !== 'import' ||
|
|
803
|
-
declaration?.type !== 'ImportDeclaration' ||
|
|
804
|
-
declaration.importKind === 'type' ||
|
|
805
|
-
declaration.source?.type !== 'Literal' ||
|
|
806
|
-
declaration.source.value !== dynamic_source
|
|
807
|
-
) {
|
|
808
|
-
return false;
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
return declaration.specifiers.some(
|
|
812
|
-
(/** @type {any} */ specifier) =>
|
|
813
|
-
specifier.local?.name === binding.node?.name &&
|
|
814
|
-
is_runtime_dynamic_import_specifier(specifier, kind),
|
|
815
|
-
);
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
/**
|
|
819
|
-
* @param {any} specifier
|
|
820
|
-
* @param {'component' | 'namespace'} kind
|
|
821
|
-
* @returns {boolean}
|
|
822
|
-
*/
|
|
823
|
-
function is_runtime_dynamic_import_specifier(specifier, kind) {
|
|
824
|
-
if (kind === 'namespace') {
|
|
825
|
-
return specifier.type === 'ImportNamespaceSpecifier';
|
|
826
|
-
}
|
|
827
|
-
return (
|
|
828
|
-
specifier.type === 'ImportSpecifier' &&
|
|
829
|
-
specifier.importKind !== 'type' &&
|
|
830
|
-
get_imported_name(specifier) === 'Dynamic'
|
|
831
|
-
);
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
/**
|
|
835
|
-
* @param {any} specifier
|
|
836
|
-
* @returns {string | null}
|
|
837
|
-
*/
|
|
838
|
-
function get_imported_name(specifier) {
|
|
839
|
-
const imported = specifier.imported;
|
|
840
|
-
if (imported?.type === 'Identifier') {
|
|
841
|
-
return imported.name;
|
|
842
|
-
}
|
|
843
|
-
if (imported?.type === 'Literal') {
|
|
844
|
-
return String(imported.value);
|
|
845
|
-
}
|
|
846
|
-
return null;
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
/**
|
|
850
|
-
* @param {any} expression
|
|
851
|
-
* @returns {any}
|
|
852
|
-
*/
|
|
853
|
-
function unwrap_reference_expression(expression) {
|
|
854
|
-
let node = expression;
|
|
855
|
-
while (
|
|
856
|
-
node &&
|
|
857
|
-
(node.type === 'TSAsExpression' ||
|
|
858
|
-
node.type === 'TSTypeAssertion' ||
|
|
859
|
-
node.type === 'TSNonNullExpression' ||
|
|
860
|
-
node.type === 'ParenthesizedExpression' ||
|
|
861
|
-
node.type === 'ChainExpression')
|
|
862
|
-
) {
|
|
863
|
-
node = node.expression;
|
|
864
|
-
}
|
|
865
|
-
return node;
|
|
866
|
-
}
|
|
867
|
-
|
|
868
795
|
/**
|
|
869
796
|
* @param {any[]} body_nodes
|
|
870
797
|
* @param {TransformContext} transform_context
|
|
@@ -1038,424 +965,54 @@ function is_interleaved_body(body_nodes) {
|
|
|
1038
965
|
}
|
|
1039
966
|
|
|
1040
967
|
/**
|
|
1041
|
-
* @param {any}
|
|
968
|
+
* @param {any[]} body_nodes
|
|
1042
969
|
* @param {TransformContext} transform_context
|
|
970
|
+
* @param {boolean} include_platform_setup
|
|
1043
971
|
* @returns {boolean}
|
|
1044
972
|
*/
|
|
1045
|
-
function
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
973
|
+
function body_contains_top_level_hook_call(
|
|
974
|
+
body_nodes,
|
|
975
|
+
transform_context,
|
|
976
|
+
include_platform_setup = false,
|
|
977
|
+
) {
|
|
978
|
+
return body_nodes.some((node) =>
|
|
979
|
+
statement_contains_top_level_hook_call(node, transform_context, include_platform_setup),
|
|
1052
980
|
);
|
|
1053
981
|
}
|
|
1054
982
|
|
|
1055
983
|
/**
|
|
1056
984
|
* @param {any} node
|
|
1057
985
|
* @param {TransformContext} transform_context
|
|
1058
|
-
* @
|
|
1059
|
-
*/
|
|
1060
|
-
function create_hook_split_block(node, transform_context) {
|
|
1061
|
-
if (
|
|
1062
|
-
transform_context.platform.hooks?.componentBodyHookHelpers !== true ||
|
|
1063
|
-
!should_extract_hook_helpers(transform_context) ||
|
|
1064
|
-
node.body?.type !== 'BlockStatement'
|
|
1065
|
-
) {
|
|
1066
|
-
return null;
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
const source_body = node.body.body || [];
|
|
1070
|
-
const branch_rewrite = rewrite_component_body_branch_hook_returns(source_body, transform_context);
|
|
1071
|
-
const body = branch_rewrite.body;
|
|
1072
|
-
const split_index = find_hook_split_index(body, transform_context);
|
|
1073
|
-
if (split_index === -1 && !branch_rewrite.changed) {
|
|
1074
|
-
return null;
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
let block_body;
|
|
1078
|
-
if (split_index === -1) {
|
|
1079
|
-
block_body = expand_native_tsrx_return_statement_list(body, transform_context);
|
|
1080
|
-
} else {
|
|
1081
|
-
const split_statement = body[split_index];
|
|
1082
|
-
const continuation_body = body.slice(split_index + 1);
|
|
1083
|
-
const helper = create_hook_safe_helper(
|
|
1084
|
-
expand_native_tsrx_return_statement_list(continuation_body, transform_context),
|
|
1085
|
-
undefined,
|
|
1086
|
-
get_body_source_node(continuation_body) || split_statement,
|
|
1087
|
-
transform_context,
|
|
1088
|
-
);
|
|
1089
|
-
|
|
1090
|
-
block_body = [
|
|
1091
|
-
...body.slice(0, split_index + 1),
|
|
1092
|
-
...helper.setup_statements,
|
|
1093
|
-
set_loc(b.return(helper.component_element), split_statement),
|
|
1094
|
-
];
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
const block = b.block(block_body, node.body);
|
|
1098
|
-
block.metadata = {
|
|
1099
|
-
...(block.metadata || {}),
|
|
1100
|
-
hook_split_block: true,
|
|
1101
|
-
};
|
|
1102
|
-
return block;
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
/**
|
|
1106
|
-
* @param {any[]} body_nodes
|
|
1107
|
-
* @param {TransformContext} transform_context
|
|
986
|
+
* @param {boolean} include_platform_setup
|
|
1108
987
|
* @returns {boolean}
|
|
1109
988
|
*/
|
|
1110
|
-
function
|
|
1111
|
-
return
|
|
1112
|
-
statement_contains_component_body_branch_hook_return(node, transform_context),
|
|
1113
|
-
);
|
|
989
|
+
function statement_contains_top_level_hook_call(node, transform_context, include_platform_setup) {
|
|
990
|
+
return node_contains_top_level_hook_call(node, false, transform_context, include_platform_setup);
|
|
1114
991
|
}
|
|
1115
992
|
|
|
1116
993
|
/**
|
|
1117
994
|
* @param {any} node
|
|
995
|
+
* @param {boolean} inside_nested_function
|
|
1118
996
|
* @param {TransformContext} transform_context
|
|
997
|
+
* @param {boolean} include_platform_setup
|
|
1119
998
|
* @returns {boolean}
|
|
1120
999
|
*/
|
|
1121
|
-
function
|
|
1000
|
+
function node_contains_top_level_hook_call(
|
|
1001
|
+
node,
|
|
1002
|
+
inside_nested_function,
|
|
1003
|
+
transform_context,
|
|
1004
|
+
include_platform_setup,
|
|
1005
|
+
) {
|
|
1122
1006
|
if (!node || typeof node !== 'object') {
|
|
1123
1007
|
return false;
|
|
1124
1008
|
}
|
|
1125
1009
|
|
|
1126
|
-
if (
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
if (is_plain_if_statement(node)) {
|
|
1135
|
-
return (
|
|
1136
|
-
branch_needs_component_body_hook_helper(node.consequent, transform_context) ||
|
|
1137
|
-
statement_contains_component_body_branch_hook_return(node.consequent, transform_context) ||
|
|
1138
|
-
branch_needs_component_body_hook_helper(node.alternate, transform_context) ||
|
|
1139
|
-
statement_contains_component_body_branch_hook_return(node.alternate, transform_context)
|
|
1140
|
-
);
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
if (node.type === 'BlockStatement') {
|
|
1144
|
-
return body_contains_component_body_branch_hook_return(node.body || [], transform_context);
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
return false;
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
/**
|
|
1151
|
-
* @param {any[]} body_nodes
|
|
1152
|
-
* @param {TransformContext} transform_context
|
|
1153
|
-
* @returns {{ body: any[], changed: boolean }}
|
|
1154
|
-
*/
|
|
1155
|
-
function rewrite_component_body_branch_hook_returns(body_nodes, transform_context) {
|
|
1156
|
-
let changed = false;
|
|
1157
|
-
const body = body_nodes.map((node) => {
|
|
1158
|
-
const next_node = rewrite_component_body_branch_hook_return_statement(node, transform_context);
|
|
1159
|
-
if (next_node !== node) {
|
|
1160
|
-
changed = true;
|
|
1161
|
-
}
|
|
1162
|
-
return next_node;
|
|
1163
|
-
});
|
|
1164
|
-
return changed ? { body, changed } : { body: body_nodes, changed: false };
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
/**
|
|
1168
|
-
* @param {any} node
|
|
1169
|
-
* @param {TransformContext} transform_context
|
|
1170
|
-
* @returns {any}
|
|
1171
|
-
*/
|
|
1172
|
-
function rewrite_component_body_branch_hook_return_statement(node, transform_context) {
|
|
1173
|
-
if (!node || typeof node !== 'object' || is_function_or_class_boundary(node)) {
|
|
1174
|
-
return node;
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
|
-
if (is_plain_if_statement(node)) {
|
|
1178
|
-
const consequent = rewrite_component_body_hook_return_branch(
|
|
1179
|
-
node.consequent,
|
|
1180
|
-
transform_context,
|
|
1181
|
-
);
|
|
1182
|
-
const alternate = node.alternate
|
|
1183
|
-
? rewrite_component_body_hook_return_branch(node.alternate, transform_context)
|
|
1184
|
-
: { node: node.alternate, changed: false };
|
|
1185
|
-
|
|
1186
|
-
if (!consequent.changed && !alternate.changed) {
|
|
1187
|
-
return node;
|
|
1188
|
-
}
|
|
1189
|
-
return set_loc(b.if(node.test, consequent.node, alternate.node), node);
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
if (node.type === 'BlockStatement') {
|
|
1193
|
-
const rewritten = rewrite_component_body_branch_hook_returns(
|
|
1194
|
-
node.body || [],
|
|
1195
|
-
transform_context,
|
|
1196
|
-
);
|
|
1197
|
-
return rewritten.changed ? set_loc(b.block(rewritten.body, node), node) : node;
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
return node;
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
/**
|
|
1204
|
-
* @param {any} branch
|
|
1205
|
-
* @param {TransformContext} transform_context
|
|
1206
|
-
* @returns {{ node: any, changed: boolean }}
|
|
1207
|
-
*/
|
|
1208
|
-
function rewrite_component_body_hook_return_branch(branch, transform_context) {
|
|
1209
|
-
if (!branch || typeof branch !== 'object') {
|
|
1210
|
-
return { node: branch, changed: false };
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
if (is_plain_if_statement(branch)) {
|
|
1214
|
-
const next_node = rewrite_component_body_branch_hook_return_statement(
|
|
1215
|
-
branch,
|
|
1216
|
-
transform_context,
|
|
1217
|
-
);
|
|
1218
|
-
return { node: next_node, changed: next_node !== branch };
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
const branch_body = branch.type === 'BlockStatement' ? branch.body || [] : [branch];
|
|
1222
|
-
const rewritten = rewrite_component_body_branch_hook_returns(branch_body, transform_context);
|
|
1223
|
-
const body = rewritten.body;
|
|
1224
|
-
const needs_helper = branch_needs_component_body_hook_helper_body(body, transform_context);
|
|
1225
|
-
|
|
1226
|
-
if (!needs_helper) {
|
|
1227
|
-
if (!rewritten.changed) {
|
|
1228
|
-
return { node: branch, changed: false };
|
|
1229
|
-
}
|
|
1230
|
-
const node =
|
|
1231
|
-
branch.type === 'BlockStatement'
|
|
1232
|
-
? set_loc(b.block(body, branch), branch)
|
|
1233
|
-
: (body[0] ?? branch);
|
|
1234
|
-
return { node, changed: true };
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
const helper_body = expand_native_tsrx_return_statement_list(body, transform_context);
|
|
1238
|
-
const helper = create_hook_safe_helper(
|
|
1239
|
-
helper_body,
|
|
1240
|
-
undefined,
|
|
1241
|
-
get_body_source_node(body) || branch,
|
|
1242
|
-
transform_context,
|
|
1243
|
-
);
|
|
1244
|
-
const node = set_loc(
|
|
1245
|
-
b.block([...helper.setup_statements, set_loc(b.return(helper.component_element), branch)]),
|
|
1246
|
-
branch,
|
|
1247
|
-
);
|
|
1248
|
-
return { node, changed: true };
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
/**
|
|
1252
|
-
* @param {any} branch
|
|
1253
|
-
* @param {TransformContext} transform_context
|
|
1254
|
-
* @returns {boolean}
|
|
1255
|
-
*/
|
|
1256
|
-
function branch_needs_component_body_hook_helper(branch, transform_context) {
|
|
1257
|
-
if (!branch || typeof branch !== 'object' || is_plain_if_statement(branch)) {
|
|
1258
|
-
return false;
|
|
1259
|
-
}
|
|
1260
|
-
const body = branch.type === 'BlockStatement' ? branch.body || [] : [branch];
|
|
1261
|
-
return branch_needs_component_body_hook_helper_body(body, transform_context);
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
/**
|
|
1265
|
-
* @param {any[]} body
|
|
1266
|
-
* @param {TransformContext} transform_context
|
|
1267
|
-
* @returns {boolean}
|
|
1268
|
-
*/
|
|
1269
|
-
function branch_needs_component_body_hook_helper_body(body, transform_context) {
|
|
1270
|
-
return (
|
|
1271
|
-
body_has_top_level_component_body_return(body) &&
|
|
1272
|
-
body_contains_direct_top_level_hook_call(body, transform_context, true)
|
|
1273
|
-
);
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
/**
|
|
1277
|
-
* @param {any[]} body
|
|
1278
|
-
* @returns {boolean}
|
|
1279
|
-
*/
|
|
1280
|
-
function body_has_top_level_component_body_return(body) {
|
|
1281
|
-
return body.some((node) => node?.type === 'ReturnStatement');
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
/**
|
|
1285
|
-
* @param {any[]} body
|
|
1286
|
-
* @param {TransformContext} transform_context
|
|
1287
|
-
* @param {boolean} include_platform_setup
|
|
1288
|
-
* @returns {boolean}
|
|
1289
|
-
*/
|
|
1290
|
-
function body_contains_direct_top_level_hook_call(body, transform_context, include_platform_setup) {
|
|
1291
|
-
return body.some((node) =>
|
|
1292
|
-
statement_contains_direct_top_level_hook_call(node, transform_context, include_platform_setup),
|
|
1293
|
-
);
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
/**
|
|
1297
|
-
* @param {any} node
|
|
1298
|
-
* @param {TransformContext} transform_context
|
|
1299
|
-
* @param {boolean} include_platform_setup
|
|
1300
|
-
* @returns {boolean}
|
|
1301
|
-
*/
|
|
1302
|
-
function statement_contains_direct_top_level_hook_call(
|
|
1303
|
-
node,
|
|
1304
|
-
transform_context,
|
|
1305
|
-
include_platform_setup,
|
|
1306
|
-
) {
|
|
1307
|
-
if (!node || typeof node !== 'object') {
|
|
1308
|
-
return false;
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
if (is_function_or_class_boundary(node)) {
|
|
1312
|
-
return false;
|
|
1313
|
-
}
|
|
1314
|
-
|
|
1315
|
-
if (
|
|
1316
|
-
is_plain_if_statement(node) ||
|
|
1317
|
-
is_switch_control_node(node) ||
|
|
1318
|
-
is_try_control_node(node) ||
|
|
1319
|
-
is_for_of_control_node(node)
|
|
1320
|
-
) {
|
|
1321
|
-
return false;
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
|
-
return statement_contains_top_level_hook_call(node, transform_context, include_platform_setup);
|
|
1325
|
-
}
|
|
1326
|
-
|
|
1327
|
-
/**
|
|
1328
|
-
* @param {any[]} body_nodes
|
|
1329
|
-
* @param {TransformContext} transform_context
|
|
1330
|
-
* @returns {number}
|
|
1331
|
-
*/
|
|
1332
|
-
function find_hook_split_index(body_nodes, transform_context) {
|
|
1333
|
-
for (let i = 0; i < body_nodes.length; i += 1) {
|
|
1334
|
-
if (!is_component_body_conditional_return_statement(body_nodes[i])) {
|
|
1335
|
-
continue;
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
if (body_contains_top_level_hook_call(body_nodes.slice(i + 1), transform_context, true)) {
|
|
1339
|
-
return i;
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
|
-
return -1;
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
/**
|
|
1347
|
-
* @param {any} node
|
|
1348
|
-
* @returns {boolean}
|
|
1349
|
-
*/
|
|
1350
|
-
function is_component_body_conditional_return_statement(node) {
|
|
1351
|
-
if (!is_if_control_node(node)) {
|
|
1352
|
-
return false;
|
|
1353
|
-
}
|
|
1354
|
-
|
|
1355
|
-
return (
|
|
1356
|
-
statement_contains_component_body_return(node.consequent) ||
|
|
1357
|
-
statement_contains_component_body_return(node.alternate)
|
|
1358
|
-
);
|
|
1359
|
-
}
|
|
1360
|
-
|
|
1361
|
-
/**
|
|
1362
|
-
* @param {any} node
|
|
1363
|
-
* @returns {boolean}
|
|
1364
|
-
*/
|
|
1365
|
-
function statement_contains_component_body_return(node) {
|
|
1366
|
-
if (!node || typeof node !== 'object') {
|
|
1367
|
-
return false;
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
|
-
if (node.type === 'ReturnStatement') {
|
|
1371
|
-
return true;
|
|
1372
|
-
}
|
|
1373
|
-
|
|
1374
|
-
if (is_function_or_class_boundary(node)) {
|
|
1375
|
-
return false;
|
|
1376
|
-
}
|
|
1377
|
-
|
|
1378
|
-
if (Array.isArray(node)) {
|
|
1379
|
-
return node.some(statement_contains_component_body_return);
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
if (node.type === 'BlockStatement') {
|
|
1383
|
-
return (node.body || []).some(statement_contains_component_body_return);
|
|
1384
|
-
}
|
|
1385
|
-
|
|
1386
|
-
if (is_if_control_node(node)) {
|
|
1387
|
-
return (
|
|
1388
|
-
statement_contains_component_body_return(node.consequent) ||
|
|
1389
|
-
statement_contains_component_body_return(node.alternate)
|
|
1390
|
-
);
|
|
1391
|
-
}
|
|
1392
|
-
|
|
1393
|
-
if (is_switch_control_node(node)) {
|
|
1394
|
-
return (node.cases || []).some((/** @type {any} */ switch_case) =>
|
|
1395
|
-
statement_contains_component_body_return(switch_case.consequent || []),
|
|
1396
|
-
);
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
if (is_try_control_node(node)) {
|
|
1400
|
-
return (
|
|
1401
|
-
statement_contains_component_body_return(node.block) ||
|
|
1402
|
-
statement_contains_component_body_return(node.handler?.body) ||
|
|
1403
|
-
statement_contains_component_body_return(node.finalizer)
|
|
1404
|
-
);
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
return false;
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
/**
|
|
1411
|
-
* @param {any[]} body_nodes
|
|
1412
|
-
* @param {TransformContext} transform_context
|
|
1413
|
-
* @param {boolean} include_platform_setup
|
|
1414
|
-
* @returns {boolean}
|
|
1415
|
-
*/
|
|
1416
|
-
function body_contains_top_level_hook_call(
|
|
1417
|
-
body_nodes,
|
|
1418
|
-
transform_context,
|
|
1419
|
-
include_platform_setup = false,
|
|
1420
|
-
) {
|
|
1421
|
-
return body_nodes.some((node) =>
|
|
1422
|
-
statement_contains_top_level_hook_call(node, transform_context, include_platform_setup),
|
|
1423
|
-
);
|
|
1424
|
-
}
|
|
1425
|
-
|
|
1426
|
-
/**
|
|
1427
|
-
* @param {any} node
|
|
1428
|
-
* @param {TransformContext} transform_context
|
|
1429
|
-
* @param {boolean} include_platform_setup
|
|
1430
|
-
* @returns {boolean}
|
|
1431
|
-
*/
|
|
1432
|
-
function statement_contains_top_level_hook_call(node, transform_context, include_platform_setup) {
|
|
1433
|
-
return node_contains_top_level_hook_call(node, false, transform_context, include_platform_setup);
|
|
1434
|
-
}
|
|
1435
|
-
|
|
1436
|
-
/**
|
|
1437
|
-
* @param {any} node
|
|
1438
|
-
* @param {boolean} inside_nested_function
|
|
1439
|
-
* @param {TransformContext} transform_context
|
|
1440
|
-
* @param {boolean} include_platform_setup
|
|
1441
|
-
* @returns {boolean}
|
|
1442
|
-
*/
|
|
1443
|
-
function node_contains_top_level_hook_call(
|
|
1444
|
-
node,
|
|
1445
|
-
inside_nested_function,
|
|
1446
|
-
transform_context,
|
|
1447
|
-
include_platform_setup,
|
|
1448
|
-
) {
|
|
1449
|
-
if (!node || typeof node !== 'object') {
|
|
1450
|
-
return false;
|
|
1451
|
-
}
|
|
1452
|
-
|
|
1453
|
-
if (
|
|
1454
|
-
inside_nested_function &&
|
|
1455
|
-
(node.type === 'FunctionDeclaration' ||
|
|
1456
|
-
node.type === 'FunctionExpression' ||
|
|
1457
|
-
node.type === 'ArrowFunctionExpression')
|
|
1458
|
-
) {
|
|
1010
|
+
if (
|
|
1011
|
+
inside_nested_function &&
|
|
1012
|
+
(node.type === 'FunctionDeclaration' ||
|
|
1013
|
+
node.type === 'FunctionExpression' ||
|
|
1014
|
+
node.type === 'ArrowFunctionExpression')
|
|
1015
|
+
) {
|
|
1459
1016
|
return false;
|
|
1460
1017
|
}
|
|
1461
1018
|
|
|
@@ -1651,7 +1208,7 @@ function create_generated_helper_metadata(helper_state) {
|
|
|
1651
1208
|
* @returns {any}
|
|
1652
1209
|
*/
|
|
1653
1210
|
function strip_function_transform_metadata(metadata) {
|
|
1654
|
-
const { native_tsrx,
|
|
1211
|
+
const { native_tsrx, ...next_metadata } = metadata || {};
|
|
1655
1212
|
return next_metadata;
|
|
1656
1213
|
}
|
|
1657
1214
|
|
|
@@ -1661,18 +1218,10 @@ function strip_function_transform_metadata(metadata) {
|
|
|
1661
1218
|
* @returns {any}
|
|
1662
1219
|
*/
|
|
1663
1220
|
function transform_block_statement(node, { next, visit, state, path }) {
|
|
1664
|
-
if (node.metadata?.
|
|
1221
|
+
if (node.metadata?.native_return_block) {
|
|
1665
1222
|
return next() ?? node;
|
|
1666
1223
|
}
|
|
1667
1224
|
|
|
1668
|
-
const parent = /** @type {any} */ (path.at(-1));
|
|
1669
|
-
if (parent?.metadata?.hook_split && parent.body === node) {
|
|
1670
|
-
const block = create_hook_split_block(parent, state);
|
|
1671
|
-
if (block) {
|
|
1672
|
-
return visit(block, state);
|
|
1673
|
-
}
|
|
1674
|
-
}
|
|
1675
|
-
|
|
1676
1225
|
if (get_active_native_tsrx_function(path)?.metadata?.native_tsrx_body) {
|
|
1677
1226
|
const block = create_native_tsrx_statement_list_block(node, state);
|
|
1678
1227
|
if (block) {
|
|
@@ -1827,7 +1376,6 @@ function transform_native_tsrx_function(node, { next, state }, { nativeBody = fa
|
|
|
1827
1376
|
...(node.metadata || {}),
|
|
1828
1377
|
native_tsrx: true,
|
|
1829
1378
|
...(nativeBody ? { native_tsrx_body: true } : {}),
|
|
1830
|
-
...(nativeBody && needs_hook_split(node, state) ? { hook_split: true } : {}),
|
|
1831
1379
|
};
|
|
1832
1380
|
state.available_bindings = merge_binding_maps(
|
|
1833
1381
|
saved_bindings,
|
|
@@ -1979,6 +1527,10 @@ function find_native_await_in_statement(statement) {
|
|
|
1979
1527
|
* @returns {any}
|
|
1980
1528
|
*/
|
|
1981
1529
|
function transform_function_with_hook_helpers(node, { next, state }) {
|
|
1530
|
+
if (!state.platform.hooks?.moduleScopedHookComponents) {
|
|
1531
|
+
return next() ?? node;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1982
1534
|
const has_hook_bearing_tsrx = function_contains_hook_bearing_tsrx(node, state);
|
|
1983
1535
|
if (state.helper_state || !is_uppercase_function_like(node) || !has_hook_bearing_tsrx) {
|
|
1984
1536
|
return next() ?? node;
|
|
@@ -2844,7 +2396,10 @@ function should_use_module_scoped_hook_components(transform_context) {
|
|
|
2844
2396
|
* @returns {boolean}
|
|
2845
2397
|
*/
|
|
2846
2398
|
function should_extract_hook_helpers(transform_context) {
|
|
2847
|
-
return !!
|
|
2399
|
+
return !!(
|
|
2400
|
+
transform_context.hook_helpers_enabled &&
|
|
2401
|
+
transform_context.platform.hooks?.moduleScopedHookComponents
|
|
2402
|
+
);
|
|
2848
2403
|
}
|
|
2849
2404
|
|
|
2850
2405
|
/**
|
|
@@ -3406,11 +2961,6 @@ function build_hoisted_for_of_with_hooks(node, transform_context) {
|
|
|
3406
2961
|
for (const param of loop_params) {
|
|
3407
2962
|
collect_pattern_bindings(param, transform_context.available_bindings);
|
|
3408
2963
|
}
|
|
3409
|
-
validate_hook_safe_body_does_not_assign_hook_results_to_outer_bindings(
|
|
3410
|
-
original_loop_body,
|
|
3411
|
-
transform_context,
|
|
3412
|
-
loop_scoped_names,
|
|
3413
|
-
);
|
|
3414
2964
|
|
|
3415
2965
|
const all_helper_bindings = get_referenced_helper_bindings(
|
|
3416
2966
|
original_loop_body,
|
|
@@ -3678,676 +3228,82 @@ function to_jsx_element(
|
|
|
3678
3228
|
? opening_element_node
|
|
3679
3229
|
: set_loc(opening_element_node, node.openingElement || node);
|
|
3680
3230
|
|
|
3681
|
-
const closingElement = selfClosing
|
|
3682
|
-
? null
|
|
3683
|
-
: set_loc(
|
|
3684
|
-
b.jsx_closing_element(
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
return element;
|
|
3695
|
-
}
|
|
3696
|
-
|
|
3697
|
-
/**
|
|
3698
|
-
* @param {any[]} children
|
|
3699
|
-
* @param {TransformContext} transform_context
|
|
3700
|
-
* @returns {any[]}
|
|
3701
|
-
*/
|
|
3702
|
-
|
|
3703
|
-
function create_element_children(children, transform_context) {
|
|
3704
|
-
if (children.length === 0) {
|
|
3705
|
-
return [];
|
|
3706
|
-
}
|
|
3707
|
-
|
|
3708
|
-
if (children.every(is_inline_element_child) && !children_contain_return_semantics(children)) {
|
|
3709
|
-
const saved_inside_element_child = transform_context.inside_element_child;
|
|
3710
|
-
transform_context.inside_element_child = true;
|
|
3711
|
-
try {
|
|
3712
|
-
return children.map((/** @type {any} */ child) => to_jsx_child(child, transform_context));
|
|
3713
|
-
} finally {
|
|
3714
|
-
transform_context.inside_element_child = saved_inside_element_child;
|
|
3715
|
-
}
|
|
3716
|
-
}
|
|
3717
|
-
|
|
3718
|
-
const saved_inside_element_child = transform_context.inside_element_child;
|
|
3719
|
-
transform_context.inside_element_child = true;
|
|
3720
|
-
try {
|
|
3721
|
-
return [statement_body_to_jsx_child(children, transform_context)];
|
|
3722
|
-
} finally {
|
|
3723
|
-
transform_context.inside_element_child = saved_inside_element_child;
|
|
3724
|
-
}
|
|
3725
|
-
}
|
|
3726
|
-
|
|
3727
|
-
/**
|
|
3728
|
-
* @param {any[]} children
|
|
3729
|
-
* @returns {boolean}
|
|
3730
|
-
*/
|
|
3731
|
-
function children_contain_return_semantics(children) {
|
|
3732
|
-
return children.some(child_contains_return_semantics);
|
|
3733
|
-
}
|
|
3734
|
-
|
|
3735
|
-
/**
|
|
3736
|
-
* @param {any} node
|
|
3737
|
-
* @returns {boolean}
|
|
3738
|
-
*/
|
|
3739
|
-
function child_contains_return_semantics(node) {
|
|
3740
|
-
if (!node || typeof node !== 'object') {
|
|
3741
|
-
return false;
|
|
3742
|
-
}
|
|
3743
|
-
|
|
3744
|
-
if (node.type === 'ReturnStatement') {
|
|
3745
|
-
return true;
|
|
3746
|
-
}
|
|
3747
|
-
|
|
3748
|
-
if (
|
|
3749
|
-
node.type === 'FunctionDeclaration' ||
|
|
3750
|
-
node.type === 'FunctionExpression' ||
|
|
3751
|
-
node.type === 'ArrowFunctionExpression'
|
|
3752
|
-
) {
|
|
3753
|
-
return false;
|
|
3754
|
-
}
|
|
3755
|
-
|
|
3756
|
-
if (Array.isArray(node)) {
|
|
3757
|
-
return node.some(child_contains_return_semantics);
|
|
3758
|
-
}
|
|
3759
|
-
|
|
3760
|
-
for (const key of Object.keys(node)) {
|
|
3761
|
-
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
3762
|
-
continue;
|
|
3763
|
-
}
|
|
3764
|
-
if (child_contains_return_semantics(node[key])) {
|
|
3765
|
-
return true;
|
|
3766
|
-
}
|
|
3767
|
-
}
|
|
3768
|
-
|
|
3769
|
-
return false;
|
|
3770
|
-
}
|
|
3771
|
-
|
|
3772
|
-
/**
|
|
3773
|
-
* @param {any} node
|
|
3774
|
-
* @returns {boolean}
|
|
3775
|
-
*/
|
|
3776
|
-
function is_inline_element_child(node) {
|
|
3777
|
-
return node && is_render_child_node(node);
|
|
3778
|
-
}
|
|
3779
|
-
|
|
3780
|
-
/**
|
|
3781
|
-
* @param {any[]} body_nodes
|
|
3782
|
-
* @param {TransformContext} transform_context
|
|
3783
|
-
* @returns {ESTreeJSX.JSXExpressionContainer}
|
|
3784
|
-
*/
|
|
3785
|
-
function statement_body_to_jsx_child(body_nodes, transform_context) {
|
|
3786
|
-
if (
|
|
3787
|
-
should_extract_hook_helpers(transform_context) &&
|
|
3788
|
-
body_contains_top_level_hook_call(body_nodes, transform_context, true)
|
|
3789
|
-
) {
|
|
3790
|
-
return hook_safe_statement_body_to_jsx_child(body_nodes, transform_context);
|
|
3791
|
-
}
|
|
3792
|
-
|
|
3793
|
-
return to_jsx_expression_container(
|
|
3794
|
-
b.call(b.arrow([], b.block(build_render_statements(body_nodes, true, transform_context)))),
|
|
3795
|
-
);
|
|
3796
|
-
}
|
|
3797
|
-
|
|
3798
|
-
/**
|
|
3799
|
-
* @param {any[]} body_nodes
|
|
3800
|
-
* @param {TransformContext} transform_context
|
|
3801
|
-
* @returns {ESTreeJSX.JSXExpressionContainer}
|
|
3802
|
-
*/
|
|
3803
|
-
function hook_safe_statement_body_to_jsx_child(body_nodes, transform_context) {
|
|
3804
|
-
const source_node = get_body_source_node(body_nodes);
|
|
3805
|
-
const helper = create_hook_safe_helper(body_nodes, undefined, source_node, transform_context);
|
|
3806
|
-
|
|
3807
|
-
return to_jsx_expression_container(
|
|
3808
|
-
create_hook_safe_helper_iife(helper.setup_statements, helper.component_element),
|
|
3809
|
-
source_node,
|
|
3810
|
-
);
|
|
3811
|
-
}
|
|
3812
|
-
|
|
3813
|
-
/**
|
|
3814
|
-
* @param {TransformContext} transform_context
|
|
3815
|
-
* @returns {string}
|
|
3816
|
-
*/
|
|
3817
|
-
function create_local_statement_component_name(transform_context) {
|
|
3818
|
-
transform_context.local_statement_component_index += 1;
|
|
3819
|
-
return `StatementBodyHook${transform_context.local_statement_component_index}`;
|
|
3820
|
-
}
|
|
3821
|
-
|
|
3822
|
-
/**
|
|
3823
|
-
* Wraps a list of body nodes into a component and returns
|
|
3824
|
-
* statements that return `<ComponentName prop1={prop1} ... />`.
|
|
3825
|
-
* Targets can either emit the helper component at module scope or cache the
|
|
3826
|
-
* component identity in module state while initializing it from the parent.
|
|
3827
|
-
* Used when a control flow branch contains hook calls that must be moved
|
|
3828
|
-
* into their own component boundary to satisfy the Rules of Hooks.
|
|
3829
|
-
*
|
|
3830
|
-
* @param {any[]} body_nodes
|
|
3831
|
-
* @param {any} key_expression - Optional key expression to add to the component element (for for-of loops)
|
|
3832
|
-
* @param {TransformContext} transform_context
|
|
3833
|
-
* @returns {any[]}
|
|
3834
|
-
*/
|
|
3835
|
-
function hook_safe_render_statements(body_nodes, key_expression, transform_context) {
|
|
3836
|
-
const source_node = get_body_source_node(body_nodes);
|
|
3837
|
-
const helper = create_hook_safe_helper(
|
|
3838
|
-
body_nodes,
|
|
3839
|
-
key_expression,
|
|
3840
|
-
source_node,
|
|
3841
|
-
transform_context,
|
|
3842
|
-
);
|
|
3843
|
-
const statements = [...helper.setup_statements];
|
|
3844
|
-
|
|
3845
|
-
statements.push(b.return(helper.component_element));
|
|
3846
|
-
|
|
3847
|
-
return statements;
|
|
3848
|
-
}
|
|
3849
|
-
|
|
3850
|
-
/**
|
|
3851
|
-
* @param {any[]} body_nodes
|
|
3852
|
-
* @param {Map<string, AST.Identifier>} available_bindings
|
|
3853
|
-
* @returns {AST.Identifier[]}
|
|
3854
|
-
*/
|
|
3855
|
-
function get_referenced_helper_bindings(body_nodes, available_bindings) {
|
|
3856
|
-
const helper_bindings = [];
|
|
3857
|
-
const local_bindings = new Map();
|
|
3858
|
-
|
|
3859
|
-
for (const node of body_nodes) {
|
|
3860
|
-
collect_statement_bindings(node, local_bindings);
|
|
3861
|
-
}
|
|
3862
|
-
|
|
3863
|
-
for (const [name, binding] of available_bindings) {
|
|
3864
|
-
if (local_bindings.has(name)) continue;
|
|
3865
|
-
|
|
3866
|
-
if (references_scope_bindings(body_nodes, new Map([[name, binding]]))) {
|
|
3867
|
-
helper_bindings.push(binding);
|
|
3868
|
-
}
|
|
3869
|
-
}
|
|
3870
|
-
|
|
3871
|
-
return helper_bindings;
|
|
3872
|
-
}
|
|
3873
|
-
|
|
3874
|
-
/**
|
|
3875
|
-
* @param {any[]} body_nodes
|
|
3876
|
-
* @param {TransformContext} transform_context
|
|
3877
|
-
* @param {Set<string>} [local_binding_names]
|
|
3878
|
-
* @returns {void}
|
|
3879
|
-
*/
|
|
3880
|
-
function validate_hook_safe_body_does_not_assign_hook_results_to_outer_bindings(
|
|
3881
|
-
body_nodes,
|
|
3882
|
-
transform_context,
|
|
3883
|
-
local_binding_names,
|
|
3884
|
-
) {
|
|
3885
|
-
if (!is_react_like_hook_platform(transform_context)) {
|
|
3886
|
-
return;
|
|
3887
|
-
}
|
|
3888
|
-
if (!body_contains_top_level_hook_call(body_nodes, transform_context, true)) {
|
|
3889
|
-
return;
|
|
3890
|
-
}
|
|
3891
|
-
if (!transform_context.available_bindings || transform_context.available_bindings.size === 0) {
|
|
3892
|
-
return;
|
|
3893
|
-
}
|
|
3894
|
-
|
|
3895
|
-
const shadowed_names = collect_block_binding_names(body_nodes);
|
|
3896
|
-
for (const name of local_binding_names || []) {
|
|
3897
|
-
shadowed_names.add(name);
|
|
3898
|
-
}
|
|
3899
|
-
validate_hook_outer_assignments_in_node(body_nodes, shadowed_names, transform_context, new Set());
|
|
3900
|
-
}
|
|
3901
|
-
|
|
3902
|
-
/**
|
|
3903
|
-
* @param {TransformContext} transform_context
|
|
3904
|
-
* @returns {boolean}
|
|
3905
|
-
*/
|
|
3906
|
-
function is_react_like_hook_platform(transform_context) {
|
|
3907
|
-
return (
|
|
3908
|
-
transform_context.platform.name === 'React' || transform_context.platform.name === 'Preact'
|
|
3909
|
-
);
|
|
3910
|
-
}
|
|
3911
|
-
|
|
3912
|
-
/**
|
|
3913
|
-
* @param {any[]} statements
|
|
3914
|
-
* @returns {Set<string>}
|
|
3915
|
-
*/
|
|
3916
|
-
function collect_block_binding_names(statements) {
|
|
3917
|
-
const names = new Set();
|
|
3918
|
-
for (const statement of statements || []) {
|
|
3919
|
-
collect_block_binding_names_from_statement(statement, names);
|
|
3920
|
-
}
|
|
3921
|
-
return names;
|
|
3922
|
-
}
|
|
3923
|
-
|
|
3924
|
-
/**
|
|
3925
|
-
* @param {any} statement
|
|
3926
|
-
* @param {Set<string>} names
|
|
3927
|
-
* @returns {void}
|
|
3928
|
-
*/
|
|
3929
|
-
function collect_block_binding_names_from_statement(statement, names) {
|
|
3930
|
-
if (!statement || typeof statement !== 'object') {
|
|
3931
|
-
return;
|
|
3932
|
-
}
|
|
3933
|
-
|
|
3934
|
-
if (statement.type === 'VariableDeclaration') {
|
|
3935
|
-
for (const declaration of statement.declarations || []) {
|
|
3936
|
-
collect_pattern_names(declaration.id, names);
|
|
3937
|
-
}
|
|
3938
|
-
return;
|
|
3939
|
-
}
|
|
3940
|
-
|
|
3941
|
-
if (
|
|
3942
|
-
(statement.type === 'FunctionDeclaration' || statement.type === 'ClassDeclaration') &&
|
|
3943
|
-
statement.id?.type === 'Identifier'
|
|
3944
|
-
) {
|
|
3945
|
-
names.add(statement.id.name);
|
|
3946
|
-
return;
|
|
3947
|
-
}
|
|
3948
|
-
|
|
3949
|
-
if (
|
|
3950
|
-
statement.type === 'ForOfStatement' ||
|
|
3951
|
-
statement.type === 'ForInStatement' ||
|
|
3952
|
-
(statement.type === 'JSXForExpression' &&
|
|
3953
|
-
(statement.statementType === 'ForOfStatement' ||
|
|
3954
|
-
statement.statementType === 'ForInStatement'))
|
|
3955
|
-
) {
|
|
3956
|
-
if (statement.left?.type === 'VariableDeclaration' && statement.left.kind === 'var') {
|
|
3957
|
-
for (const declaration of statement.left.declarations || []) {
|
|
3958
|
-
collect_pattern_names(declaration.id, names);
|
|
3959
|
-
}
|
|
3960
|
-
}
|
|
3961
|
-
return;
|
|
3962
|
-
}
|
|
3963
|
-
|
|
3964
|
-
if (
|
|
3965
|
-
statement.type === 'ForStatement' &&
|
|
3966
|
-
statement.init?.type === 'VariableDeclaration' &&
|
|
3967
|
-
statement.init.kind === 'var'
|
|
3968
|
-
) {
|
|
3969
|
-
for (const declaration of statement.init.declarations || []) {
|
|
3970
|
-
collect_pattern_names(declaration.id, names);
|
|
3971
|
-
}
|
|
3972
|
-
}
|
|
3973
|
-
}
|
|
3974
|
-
|
|
3975
|
-
/**
|
|
3976
|
-
* @param {any} pattern
|
|
3977
|
-
* @param {Set<string>} names
|
|
3978
|
-
* @returns {void}
|
|
3979
|
-
*/
|
|
3980
|
-
function collect_pattern_names(pattern, names) {
|
|
3981
|
-
if (!pattern || typeof pattern !== 'object') {
|
|
3982
|
-
return;
|
|
3983
|
-
}
|
|
3984
|
-
|
|
3985
|
-
if (pattern.type === 'Identifier') {
|
|
3986
|
-
names.add(pattern.name);
|
|
3987
|
-
return;
|
|
3988
|
-
}
|
|
3989
|
-
|
|
3990
|
-
if (pattern.type === 'RestElement') {
|
|
3991
|
-
collect_pattern_names(pattern.argument, names);
|
|
3992
|
-
return;
|
|
3993
|
-
}
|
|
3994
|
-
|
|
3995
|
-
if (pattern.type === 'AssignmentPattern') {
|
|
3996
|
-
collect_pattern_names(pattern.left, names);
|
|
3997
|
-
return;
|
|
3998
|
-
}
|
|
3999
|
-
|
|
4000
|
-
if (pattern.type === 'ArrayPattern') {
|
|
4001
|
-
for (const element of pattern.elements || []) {
|
|
4002
|
-
collect_pattern_names(element, names);
|
|
4003
|
-
}
|
|
4004
|
-
return;
|
|
4005
|
-
}
|
|
4006
|
-
|
|
4007
|
-
if (pattern.type === 'ObjectPattern') {
|
|
4008
|
-
for (const property of pattern.properties || []) {
|
|
4009
|
-
if (property.type === 'RestElement') {
|
|
4010
|
-
collect_pattern_names(property.argument, names);
|
|
4011
|
-
} else {
|
|
4012
|
-
collect_pattern_names(property.value, names);
|
|
4013
|
-
}
|
|
4014
|
-
}
|
|
4015
|
-
}
|
|
4016
|
-
}
|
|
4017
|
-
|
|
4018
|
-
/**
|
|
4019
|
-
* @param {any} node
|
|
4020
|
-
* @param {Set<string>} shadowed_names
|
|
4021
|
-
* @param {TransformContext} transform_context
|
|
4022
|
-
* @param {Set<string>} hook_result_names
|
|
4023
|
-
* @returns {void}
|
|
4024
|
-
*/
|
|
4025
|
-
function validate_hook_outer_assignments_in_node(
|
|
4026
|
-
node,
|
|
4027
|
-
shadowed_names,
|
|
4028
|
-
transform_context,
|
|
4029
|
-
hook_result_names,
|
|
4030
|
-
) {
|
|
4031
|
-
if (!node || typeof node !== 'object') {
|
|
4032
|
-
return;
|
|
4033
|
-
}
|
|
4034
|
-
|
|
4035
|
-
if (Array.isArray(node)) {
|
|
4036
|
-
for (const child of node) {
|
|
4037
|
-
validate_hook_outer_assignments_in_node(
|
|
4038
|
-
child,
|
|
4039
|
-
shadowed_names,
|
|
4040
|
-
transform_context,
|
|
4041
|
-
hook_result_names,
|
|
4042
|
-
);
|
|
4043
|
-
}
|
|
4044
|
-
return;
|
|
4045
|
-
}
|
|
4046
|
-
|
|
4047
|
-
if (is_function_or_component_node(node)) {
|
|
4048
|
-
return;
|
|
4049
|
-
}
|
|
4050
|
-
|
|
4051
|
-
if (node.type === 'CallExpression' && is_hook_callee(node.callee)) {
|
|
4052
|
-
validate_hook_callback_outer_mutations(node, shadowed_names, transform_context);
|
|
4053
|
-
}
|
|
4054
|
-
|
|
4055
|
-
if (node.type === 'BlockStatement') {
|
|
4056
|
-
const next_shadowed = new Set(shadowed_names);
|
|
4057
|
-
const next_hook_result_names = new Set(hook_result_names);
|
|
4058
|
-
for (const name of collect_block_binding_names(node.body || [])) {
|
|
4059
|
-
next_shadowed.add(name);
|
|
4060
|
-
}
|
|
4061
|
-
for (const child of node.body || []) {
|
|
4062
|
-
validate_hook_outer_assignments_in_node(
|
|
4063
|
-
child,
|
|
4064
|
-
next_shadowed,
|
|
4065
|
-
transform_context,
|
|
4066
|
-
next_hook_result_names,
|
|
4067
|
-
);
|
|
4068
|
-
}
|
|
4069
|
-
return;
|
|
4070
|
-
}
|
|
4071
|
-
|
|
4072
|
-
if (node.type === 'VariableDeclaration') {
|
|
4073
|
-
for (const declaration of node.declarations || []) {
|
|
4074
|
-
if (
|
|
4075
|
-
declaration.init &&
|
|
4076
|
-
expression_contains_hook_derived_value(
|
|
4077
|
-
declaration.init,
|
|
4078
|
-
transform_context,
|
|
4079
|
-
hook_result_names,
|
|
4080
|
-
)
|
|
4081
|
-
) {
|
|
4082
|
-
collect_pattern_names(declaration.id, hook_result_names);
|
|
4083
|
-
}
|
|
4084
|
-
validate_hook_outer_assignments_in_node(
|
|
4085
|
-
declaration.init,
|
|
4086
|
-
shadowed_names,
|
|
4087
|
-
transform_context,
|
|
4088
|
-
hook_result_names,
|
|
4089
|
-
);
|
|
4090
|
-
}
|
|
4091
|
-
return;
|
|
4092
|
-
}
|
|
4093
|
-
|
|
4094
|
-
if (
|
|
4095
|
-
node.type === 'AssignmentExpression' &&
|
|
4096
|
-
expression_contains_hook_derived_value(node.right, transform_context, hook_result_names)
|
|
4097
|
-
) {
|
|
4098
|
-
const outer_names = get_referenced_outer_binding_names(
|
|
4099
|
-
node.left,
|
|
4100
|
-
transform_context.available_bindings,
|
|
4101
|
-
shadowed_names,
|
|
4102
|
-
);
|
|
4103
|
-
if (outer_names.length > 0) {
|
|
4104
|
-
report_hook_outer_assignment_error(
|
|
4105
|
-
node.left,
|
|
4106
|
-
outer_names,
|
|
4107
|
-
find_first_hook_call_name(node.right) || 'hook',
|
|
4108
|
-
transform_context,
|
|
4109
|
-
);
|
|
4110
|
-
}
|
|
4111
|
-
for (const name of get_referenced_local_binding_names(node.left, shadowed_names)) {
|
|
4112
|
-
hook_result_names.add(name);
|
|
4113
|
-
}
|
|
4114
|
-
}
|
|
4115
|
-
|
|
4116
|
-
if (is_for_of_control_node(node)) {
|
|
4117
|
-
if (
|
|
4118
|
-
node.left &&
|
|
4119
|
-
node.left.type !== 'VariableDeclaration' &&
|
|
4120
|
-
expression_contains_hook_derived_value(node.right, transform_context, hook_result_names)
|
|
4121
|
-
) {
|
|
4122
|
-
const outer_names = get_referenced_outer_binding_names(
|
|
4123
|
-
node.left,
|
|
4124
|
-
transform_context.available_bindings,
|
|
4125
|
-
shadowed_names,
|
|
4126
|
-
);
|
|
4127
|
-
if (outer_names.length > 0) {
|
|
4128
|
-
report_hook_outer_assignment_error(
|
|
4129
|
-
node.left,
|
|
4130
|
-
outer_names,
|
|
4131
|
-
find_first_hook_call_name(node.right) || 'hook',
|
|
4132
|
-
transform_context,
|
|
4133
|
-
);
|
|
4134
|
-
}
|
|
4135
|
-
for (const name of get_referenced_local_binding_names(node.left, shadowed_names)) {
|
|
4136
|
-
hook_result_names.add(name);
|
|
4137
|
-
}
|
|
4138
|
-
}
|
|
4139
|
-
|
|
4140
|
-
validate_hook_outer_assignments_in_node(
|
|
4141
|
-
node.right,
|
|
4142
|
-
shadowed_names,
|
|
4143
|
-
transform_context,
|
|
4144
|
-
hook_result_names,
|
|
4145
|
-
);
|
|
4146
|
-
|
|
4147
|
-
// Loop-declared bindings (`for (const x of …)`, `for (let x of …)`) live
|
|
4148
|
-
// only in the body. They are deliberately NOT in the enclosing block's
|
|
4149
|
-
// shadowed set (see collect_block_binding_names_from_statement), so add
|
|
4150
|
-
// them just for the body recursion to keep references to the loop var
|
|
4151
|
-
// from being flagged as outer-binding mutations.
|
|
4152
|
-
const body_shadowed = new Set(shadowed_names);
|
|
4153
|
-
if (node.left && node.left.type === 'VariableDeclaration') {
|
|
4154
|
-
for (const declaration of node.left.declarations || []) {
|
|
4155
|
-
collect_pattern_names(declaration.id, body_shadowed);
|
|
4156
|
-
}
|
|
4157
|
-
}
|
|
4158
|
-
validate_hook_outer_assignments_in_node(
|
|
4159
|
-
node.body,
|
|
4160
|
-
body_shadowed,
|
|
4161
|
-
transform_context,
|
|
4162
|
-
hook_result_names,
|
|
4163
|
-
);
|
|
4164
|
-
return;
|
|
4165
|
-
}
|
|
4166
|
-
|
|
4167
|
-
for (const key of Object.keys(node)) {
|
|
4168
|
-
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
4169
|
-
continue;
|
|
4170
|
-
}
|
|
4171
|
-
validate_hook_outer_assignments_in_node(
|
|
4172
|
-
node[key],
|
|
4173
|
-
shadowed_names,
|
|
4174
|
-
transform_context,
|
|
4175
|
-
hook_result_names,
|
|
4176
|
-
);
|
|
4177
|
-
}
|
|
4178
|
-
}
|
|
4179
|
-
|
|
4180
|
-
/**
|
|
4181
|
-
* @param {any} call_node
|
|
4182
|
-
* @param {Set<string>} shadowed_names
|
|
4183
|
-
* @param {TransformContext} transform_context
|
|
4184
|
-
* @returns {void}
|
|
4185
|
-
*/
|
|
4186
|
-
function validate_hook_callback_outer_mutations(call_node, shadowed_names, transform_context) {
|
|
4187
|
-
const hook_name = get_hook_callee_name(call_node.callee);
|
|
4188
|
-
for (const argument of call_node.arguments || []) {
|
|
4189
|
-
if (!is_function_or_component_node(argument)) {
|
|
4190
|
-
continue;
|
|
4191
|
-
}
|
|
4192
|
-
const callback_shadowed_names = create_function_like_shadowed_names(argument, shadowed_names);
|
|
4193
|
-
validate_hook_callback_outer_mutations_in_node(
|
|
4194
|
-
argument.body,
|
|
4195
|
-
callback_shadowed_names,
|
|
4196
|
-
transform_context,
|
|
4197
|
-
hook_name,
|
|
4198
|
-
);
|
|
4199
|
-
}
|
|
4200
|
-
}
|
|
4201
|
-
|
|
4202
|
-
/**
|
|
4203
|
-
* @param {any} node
|
|
4204
|
-
* @param {Set<string>} shadowed_names
|
|
4205
|
-
* @returns {Set<string>}
|
|
4206
|
-
*/
|
|
4207
|
-
function create_function_like_shadowed_names(node, shadowed_names) {
|
|
4208
|
-
const next_shadowed_names = new Set(shadowed_names);
|
|
4209
|
-
for (const param of node.params || []) {
|
|
4210
|
-
collect_pattern_names(param, next_shadowed_names);
|
|
4211
|
-
}
|
|
4212
|
-
if (node.body?.type === 'BlockStatement') {
|
|
4213
|
-
for (const name of collect_block_binding_names(node.body.body || [])) {
|
|
4214
|
-
next_shadowed_names.add(name);
|
|
4215
|
-
}
|
|
4216
|
-
}
|
|
4217
|
-
return next_shadowed_names;
|
|
4218
|
-
}
|
|
4219
|
-
|
|
4220
|
-
/**
|
|
4221
|
-
* @param {any} node
|
|
4222
|
-
* @param {Set<string>} shadowed_names
|
|
4223
|
-
* @param {TransformContext} transform_context
|
|
4224
|
-
* @param {string} hook_name
|
|
4225
|
-
* @returns {void}
|
|
4226
|
-
*/
|
|
4227
|
-
function validate_hook_callback_outer_mutations_in_node(
|
|
4228
|
-
node,
|
|
4229
|
-
shadowed_names,
|
|
4230
|
-
transform_context,
|
|
4231
|
-
hook_name,
|
|
4232
|
-
) {
|
|
4233
|
-
if (!node || typeof node !== 'object') {
|
|
4234
|
-
return;
|
|
4235
|
-
}
|
|
4236
|
-
|
|
4237
|
-
if (Array.isArray(node)) {
|
|
4238
|
-
for (const child of node) {
|
|
4239
|
-
validate_hook_callback_outer_mutations_in_node(
|
|
4240
|
-
child,
|
|
4241
|
-
shadowed_names,
|
|
4242
|
-
transform_context,
|
|
4243
|
-
hook_name,
|
|
3231
|
+
const closingElement = selfClosing
|
|
3232
|
+
? null
|
|
3233
|
+
: set_loc(
|
|
3234
|
+
b.jsx_closing_element(
|
|
3235
|
+
// Clone from the actual closing name when there is one: a dynamic
|
|
3236
|
+
// tag's closing expression (`</{Tag}>`) has its own source positions,
|
|
3237
|
+
// which editor mappings need.
|
|
3238
|
+
clone_jsx_name(
|
|
3239
|
+
node.closingElement?.name ?? name,
|
|
3240
|
+
node.closingElement?.name || node.closingElement || node,
|
|
3241
|
+
),
|
|
3242
|
+
),
|
|
3243
|
+
node.closingElement || node,
|
|
4244
3244
|
);
|
|
4245
|
-
}
|
|
4246
|
-
return;
|
|
4247
|
-
}
|
|
4248
3245
|
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
hook_name,
|
|
4255
|
-
);
|
|
4256
|
-
return;
|
|
3246
|
+
const element = set_loc(b.jsx_element_fresh(openingElement, closingElement, children), node);
|
|
3247
|
+
if (node.metadata?.dynamicElement === true) {
|
|
3248
|
+
// Keep lowered dynamic tags recognizable to scoped-CSS passes and the
|
|
3249
|
+
// static-hoist veto after the rebuild.
|
|
3250
|
+
element.metadata.dynamicElement = true;
|
|
4257
3251
|
}
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
const next_shadowed_names = new Set(shadowed_names);
|
|
4261
|
-
for (const name of collect_block_binding_names(node.body || [])) {
|
|
4262
|
-
next_shadowed_names.add(name);
|
|
4263
|
-
}
|
|
4264
|
-
for (const child of node.body || []) {
|
|
4265
|
-
validate_hook_callback_outer_mutations_in_node(
|
|
4266
|
-
child,
|
|
4267
|
-
next_shadowed_names,
|
|
4268
|
-
transform_context,
|
|
4269
|
-
hook_name,
|
|
4270
|
-
);
|
|
4271
|
-
}
|
|
4272
|
-
return;
|
|
3252
|
+
if (transform_context.typeOnly && is_style_element(node)) {
|
|
3253
|
+
disable_style_anchor_verification(element);
|
|
4273
3254
|
}
|
|
3255
|
+
return element;
|
|
3256
|
+
}
|
|
4274
3257
|
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
outer_names,
|
|
4285
|
-
hook_name,
|
|
4286
|
-
transform_context,
|
|
4287
|
-
);
|
|
4288
|
-
}
|
|
3258
|
+
/**
|
|
3259
|
+
* @param {any[]} children
|
|
3260
|
+
* @param {TransformContext} transform_context
|
|
3261
|
+
* @returns {any[]}
|
|
3262
|
+
*/
|
|
3263
|
+
|
|
3264
|
+
function create_element_children(children, transform_context) {
|
|
3265
|
+
if (children.length === 0) {
|
|
3266
|
+
return [];
|
|
4289
3267
|
}
|
|
4290
3268
|
|
|
4291
|
-
if (
|
|
4292
|
-
const
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
report_hook_callback_outer_mutation_error(
|
|
4299
|
-
node.argument,
|
|
4300
|
-
outer_names,
|
|
4301
|
-
hook_name,
|
|
4302
|
-
transform_context,
|
|
4303
|
-
);
|
|
3269
|
+
if (children.every(is_inline_element_child) && !children_contain_return_semantics(children)) {
|
|
3270
|
+
const saved_inside_element_child = transform_context.inside_element_child;
|
|
3271
|
+
transform_context.inside_element_child = true;
|
|
3272
|
+
try {
|
|
3273
|
+
return children.map((/** @type {any} */ child) => to_jsx_child(child, transform_context));
|
|
3274
|
+
} finally {
|
|
3275
|
+
transform_context.inside_element_child = saved_inside_element_child;
|
|
4304
3276
|
}
|
|
4305
3277
|
}
|
|
4306
3278
|
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
}
|
|
4314
|
-
if (key === 'argument' && node.type === 'UpdateExpression') {
|
|
4315
|
-
continue;
|
|
4316
|
-
}
|
|
4317
|
-
validate_hook_callback_outer_mutations_in_node(
|
|
4318
|
-
node[key],
|
|
4319
|
-
shadowed_names,
|
|
4320
|
-
transform_context,
|
|
4321
|
-
hook_name,
|
|
4322
|
-
);
|
|
3279
|
+
const saved_inside_element_child = transform_context.inside_element_child;
|
|
3280
|
+
transform_context.inside_element_child = true;
|
|
3281
|
+
try {
|
|
3282
|
+
return [statement_body_to_jsx_child(children, transform_context)];
|
|
3283
|
+
} finally {
|
|
3284
|
+
transform_context.inside_element_child = saved_inside_element_child;
|
|
4323
3285
|
}
|
|
4324
3286
|
}
|
|
4325
3287
|
|
|
4326
3288
|
/**
|
|
4327
|
-
* @param {any}
|
|
4328
|
-
* @param {TransformContext} transform_context
|
|
4329
|
-
* @param {Set<string>} hook_result_names
|
|
3289
|
+
* @param {any[]} children
|
|
4330
3290
|
* @returns {boolean}
|
|
4331
3291
|
*/
|
|
4332
|
-
function
|
|
4333
|
-
return (
|
|
4334
|
-
node_contains_top_level_hook_call(node, false, transform_context, true) ||
|
|
4335
|
-
references_name_in_set(node, hook_result_names)
|
|
4336
|
-
);
|
|
3292
|
+
function children_contain_return_semantics(children) {
|
|
3293
|
+
return children.some(child_contains_return_semantics);
|
|
4337
3294
|
}
|
|
4338
3295
|
|
|
4339
3296
|
/**
|
|
4340
3297
|
* @param {any} node
|
|
4341
|
-
* @param {Set<string>} names
|
|
4342
3298
|
* @returns {boolean}
|
|
4343
3299
|
*/
|
|
4344
|
-
function
|
|
4345
|
-
if (!node || typeof node !== 'object'
|
|
3300
|
+
function child_contains_return_semantics(node) {
|
|
3301
|
+
if (!node || typeof node !== 'object') {
|
|
4346
3302
|
return false;
|
|
4347
3303
|
}
|
|
4348
3304
|
|
|
4349
|
-
if (node.type === '
|
|
4350
|
-
return
|
|
3305
|
+
if (node.type === 'ReturnStatement') {
|
|
3306
|
+
return true;
|
|
4351
3307
|
}
|
|
4352
3308
|
|
|
4353
3309
|
if (
|
|
@@ -4359,20 +3315,14 @@ function references_name_in_set(node, names) {
|
|
|
4359
3315
|
}
|
|
4360
3316
|
|
|
4361
3317
|
if (Array.isArray(node)) {
|
|
4362
|
-
return node.some(
|
|
3318
|
+
return node.some(child_contains_return_semantics);
|
|
4363
3319
|
}
|
|
4364
3320
|
|
|
4365
3321
|
for (const key of Object.keys(node)) {
|
|
4366
3322
|
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
4367
3323
|
continue;
|
|
4368
3324
|
}
|
|
4369
|
-
if (key
|
|
4370
|
-
continue;
|
|
4371
|
-
}
|
|
4372
|
-
if (key === 'key' && node.type === 'Property' && !node.computed && !node.shorthand) {
|
|
4373
|
-
continue;
|
|
4374
|
-
}
|
|
4375
|
-
if (references_name_in_set(node[key], names)) {
|
|
3325
|
+
if (child_contains_return_semantics(node[key])) {
|
|
4376
3326
|
return true;
|
|
4377
3327
|
}
|
|
4378
3328
|
}
|
|
@@ -4382,162 +3332,104 @@ function references_name_in_set(node, names) {
|
|
|
4382
3332
|
|
|
4383
3333
|
/**
|
|
4384
3334
|
* @param {any} node
|
|
4385
|
-
* @
|
|
4386
|
-
* @returns {string[]}
|
|
3335
|
+
* @returns {boolean}
|
|
4387
3336
|
*/
|
|
4388
|
-
function
|
|
4389
|
-
|
|
4390
|
-
collect_referenced_local_binding_names(node, shadowed_names, names);
|
|
4391
|
-
return [...names];
|
|
3337
|
+
function is_inline_element_child(node) {
|
|
3338
|
+
return node && is_render_child_node(node);
|
|
4392
3339
|
}
|
|
4393
3340
|
|
|
4394
3341
|
/**
|
|
4395
|
-
* @param {any}
|
|
4396
|
-
* @param {
|
|
4397
|
-
* @
|
|
4398
|
-
* @returns {void}
|
|
3342
|
+
* @param {any[]} body_nodes
|
|
3343
|
+
* @param {TransformContext} transform_context
|
|
3344
|
+
* @returns {ESTreeJSX.JSXExpressionContainer}
|
|
4399
3345
|
*/
|
|
4400
|
-
function
|
|
4401
|
-
if (
|
|
4402
|
-
|
|
3346
|
+
function statement_body_to_jsx_child(body_nodes, transform_context) {
|
|
3347
|
+
if (
|
|
3348
|
+
should_extract_hook_helpers(transform_context) &&
|
|
3349
|
+
body_contains_top_level_hook_call(body_nodes, transform_context, true)
|
|
3350
|
+
) {
|
|
3351
|
+
return hook_safe_statement_body_to_jsx_child(body_nodes, transform_context);
|
|
4403
3352
|
}
|
|
4404
3353
|
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
return;
|
|
4410
|
-
}
|
|
3354
|
+
return to_jsx_expression_container(
|
|
3355
|
+
b.call(b.arrow([], b.block(build_render_statements(body_nodes, true, transform_context)))),
|
|
3356
|
+
);
|
|
3357
|
+
}
|
|
4411
3358
|
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
3359
|
+
/**
|
|
3360
|
+
* @param {any[]} body_nodes
|
|
3361
|
+
* @param {TransformContext} transform_context
|
|
3362
|
+
* @returns {ESTreeJSX.JSXExpressionContainer}
|
|
3363
|
+
*/
|
|
3364
|
+
function hook_safe_statement_body_to_jsx_child(body_nodes, transform_context) {
|
|
3365
|
+
const source_node = get_body_source_node(body_nodes);
|
|
3366
|
+
const helper = create_hook_safe_helper(body_nodes, undefined, source_node, transform_context);
|
|
4418
3367
|
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
if (key === 'property' && node.type === 'MemberExpression' && !node.computed) {
|
|
4424
|
-
continue;
|
|
4425
|
-
}
|
|
4426
|
-
if (key === 'key' && node.type === 'Property' && !node.computed && !node.shorthand) {
|
|
4427
|
-
continue;
|
|
4428
|
-
}
|
|
4429
|
-
collect_referenced_local_binding_names(node[key], shadowed_names, names);
|
|
4430
|
-
}
|
|
3368
|
+
return to_jsx_expression_container(
|
|
3369
|
+
create_hook_safe_helper_iife(helper.setup_statements, helper.component_element),
|
|
3370
|
+
source_node,
|
|
3371
|
+
);
|
|
4431
3372
|
}
|
|
4432
3373
|
|
|
4433
3374
|
/**
|
|
4434
|
-
* @param {
|
|
4435
|
-
* @
|
|
4436
|
-
* @param {Set<string>} shadowed_names
|
|
4437
|
-
* @returns {string[]}
|
|
3375
|
+
* @param {TransformContext} transform_context
|
|
3376
|
+
* @returns {string}
|
|
4438
3377
|
*/
|
|
4439
|
-
function
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
return [...names];
|
|
3378
|
+
function create_local_statement_component_name(transform_context) {
|
|
3379
|
+
transform_context.local_statement_component_index += 1;
|
|
3380
|
+
return `StatementBodyHook${transform_context.local_statement_component_index}`;
|
|
4443
3381
|
}
|
|
4444
3382
|
|
|
4445
3383
|
/**
|
|
4446
|
-
*
|
|
4447
|
-
*
|
|
4448
|
-
*
|
|
4449
|
-
*
|
|
4450
|
-
*
|
|
3384
|
+
* Wraps a list of body nodes into a component and returns
|
|
3385
|
+
* statements that return `<ComponentName prop1={prop1} ... />`.
|
|
3386
|
+
* Targets can either emit the helper component at module scope or cache the
|
|
3387
|
+
* component identity in module state while initializing it from the parent.
|
|
3388
|
+
* Used when a control flow branch contains hook calls that must be moved
|
|
3389
|
+
* into their own component boundary to satisfy the Rules of Hooks.
|
|
3390
|
+
*
|
|
3391
|
+
* @param {any[]} body_nodes
|
|
3392
|
+
* @param {any} key_expression - Optional key expression to add to the component element (for for-of loops)
|
|
3393
|
+
* @param {TransformContext} transform_context
|
|
3394
|
+
* @returns {any[]}
|
|
4451
3395
|
*/
|
|
4452
|
-
function
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
return;
|
|
4462
|
-
}
|
|
3396
|
+
function hook_safe_render_statements(body_nodes, key_expression, transform_context) {
|
|
3397
|
+
const source_node = get_body_source_node(body_nodes);
|
|
3398
|
+
const helper = create_hook_safe_helper(
|
|
3399
|
+
body_nodes,
|
|
3400
|
+
key_expression,
|
|
3401
|
+
source_node,
|
|
3402
|
+
transform_context,
|
|
3403
|
+
);
|
|
3404
|
+
const statements = [...helper.setup_statements];
|
|
4463
3405
|
|
|
4464
|
-
|
|
4465
|
-
for (const child of node) {
|
|
4466
|
-
collect_referenced_outer_binding_names(child, available_bindings, shadowed_names, names);
|
|
4467
|
-
}
|
|
4468
|
-
return;
|
|
4469
|
-
}
|
|
3406
|
+
statements.push(b.return(helper.component_element));
|
|
4470
3407
|
|
|
4471
|
-
|
|
4472
|
-
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
4473
|
-
continue;
|
|
4474
|
-
}
|
|
4475
|
-
if (key === 'property' && node.type === 'MemberExpression' && !node.computed) {
|
|
4476
|
-
continue;
|
|
4477
|
-
}
|
|
4478
|
-
if (key === 'key' && node.type === 'Property' && !node.computed && !node.shorthand) {
|
|
4479
|
-
continue;
|
|
4480
|
-
}
|
|
4481
|
-
collect_referenced_outer_binding_names(node[key], available_bindings, shadowed_names, names);
|
|
4482
|
-
}
|
|
3408
|
+
return statements;
|
|
4483
3409
|
}
|
|
4484
3410
|
|
|
4485
3411
|
/**
|
|
4486
|
-
* @param {any}
|
|
4487
|
-
* @
|
|
3412
|
+
* @param {any[]} body_nodes
|
|
3413
|
+
* @param {Map<string, AST.Identifier>} available_bindings
|
|
3414
|
+
* @returns {AST.Identifier[]}
|
|
4488
3415
|
*/
|
|
4489
|
-
function
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
}
|
|
4493
|
-
|
|
4494
|
-
if (node.type === 'CallExpression' && is_hook_callee(node.callee)) {
|
|
4495
|
-
return get_hook_callee_name(node.callee);
|
|
4496
|
-
}
|
|
3416
|
+
function get_referenced_helper_bindings(body_nodes, available_bindings) {
|
|
3417
|
+
const helper_bindings = [];
|
|
3418
|
+
const local_bindings = new Map();
|
|
4497
3419
|
|
|
4498
|
-
|
|
4499
|
-
node
|
|
4500
|
-
node.type === 'FunctionExpression' ||
|
|
4501
|
-
node.type === 'ArrowFunctionExpression'
|
|
4502
|
-
) {
|
|
4503
|
-
return null;
|
|
3420
|
+
for (const node of body_nodes) {
|
|
3421
|
+
collect_statement_bindings(node, local_bindings);
|
|
4504
3422
|
}
|
|
4505
3423
|
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
const name = find_first_hook_call_name(child);
|
|
4509
|
-
if (name) return name;
|
|
4510
|
-
}
|
|
4511
|
-
return null;
|
|
4512
|
-
}
|
|
3424
|
+
for (const [name, binding] of available_bindings) {
|
|
3425
|
+
if (local_bindings.has(name)) continue;
|
|
4513
3426
|
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
continue;
|
|
3427
|
+
if (references_scope_bindings(body_nodes, new Map([[name, binding]]))) {
|
|
3428
|
+
helper_bindings.push(binding);
|
|
4517
3429
|
}
|
|
4518
|
-
const name = find_first_hook_call_name(node[key]);
|
|
4519
|
-
if (name) return name;
|
|
4520
3430
|
}
|
|
4521
3431
|
|
|
4522
|
-
return
|
|
4523
|
-
}
|
|
4524
|
-
|
|
4525
|
-
/**
|
|
4526
|
-
* @param {any} callee
|
|
4527
|
-
* @returns {string}
|
|
4528
|
-
*/
|
|
4529
|
-
function get_hook_callee_name(callee) {
|
|
4530
|
-
if (callee?.type === 'Identifier') {
|
|
4531
|
-
return callee.name;
|
|
4532
|
-
}
|
|
4533
|
-
if (
|
|
4534
|
-
callee?.type === 'MemberExpression' &&
|
|
4535
|
-
!callee.computed &&
|
|
4536
|
-
callee.property?.type === 'Identifier'
|
|
4537
|
-
) {
|
|
4538
|
-
return callee.property.name;
|
|
4539
|
-
}
|
|
4540
|
-
return 'hook';
|
|
3432
|
+
return helper_bindings;
|
|
4541
3433
|
}
|
|
4542
3434
|
|
|
4543
3435
|
/**
|
|
@@ -4558,11 +3450,6 @@ export function create_hook_safe_helper(
|
|
|
4558
3450
|
preallocated_helper_id,
|
|
4559
3451
|
options = {},
|
|
4560
3452
|
) {
|
|
4561
|
-
validate_hook_safe_body_does_not_assign_hook_results_to_outer_bindings(
|
|
4562
|
-
body_nodes,
|
|
4563
|
-
transform_context,
|
|
4564
|
-
);
|
|
4565
|
-
|
|
4566
3453
|
const helper_id =
|
|
4567
3454
|
preallocated_helper_id ??
|
|
4568
3455
|
create_generated_identifier(create_local_statement_component_name(transform_context));
|
|
@@ -4887,14 +3774,6 @@ function is_if_control_node(node) {
|
|
|
4887
3774
|
return node?.type === 'IfStatement' || node?.type === 'JSXIfExpression';
|
|
4888
3775
|
}
|
|
4889
3776
|
|
|
4890
|
-
/**
|
|
4891
|
-
* @param {any} node
|
|
4892
|
-
* @returns {boolean}
|
|
4893
|
-
*/
|
|
4894
|
-
function is_plain_if_statement(node) {
|
|
4895
|
-
return node?.type === 'IfStatement' && !is_template_if_node(node);
|
|
4896
|
-
}
|
|
4897
|
-
|
|
4898
3777
|
/**
|
|
4899
3778
|
* @param {any} node
|
|
4900
3779
|
* @returns {boolean}
|
|
@@ -4941,17 +3820,6 @@ function is_try_control_node(node) {
|
|
|
4941
3820
|
return node?.type === 'TryStatement' || node?.type === 'JSXTryExpression';
|
|
4942
3821
|
}
|
|
4943
3822
|
|
|
4944
|
-
/**
|
|
4945
|
-
* @param {any} node
|
|
4946
|
-
* @returns {boolean}
|
|
4947
|
-
*/
|
|
4948
|
-
function is_for_of_control_node(node) {
|
|
4949
|
-
return (
|
|
4950
|
-
node?.type === 'ForOfStatement' ||
|
|
4951
|
-
(node?.type === 'JSXForExpression' && node.statementType === 'ForOfStatement')
|
|
4952
|
-
);
|
|
4953
|
-
}
|
|
4954
|
-
|
|
4955
3823
|
/**
|
|
4956
3824
|
* @param {any} node
|
|
4957
3825
|
* @param {TransformContext} transform_context
|
|
@@ -5902,16 +4770,9 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
5902
4770
|
// correctly identifies references to err/reset as non-static
|
|
5903
4771
|
const saved_catch_bindings = transform_context.available_bindings;
|
|
5904
4772
|
transform_context.available_bindings = new Map(saved_catch_bindings);
|
|
5905
|
-
const catch_scoped_names = new Set();
|
|
5906
4773
|
for (const param of catch_params) {
|
|
5907
4774
|
collect_pattern_bindings(param, transform_context.available_bindings);
|
|
5908
|
-
collect_pattern_names(param, catch_scoped_names);
|
|
5909
4775
|
}
|
|
5910
|
-
validate_hook_safe_body_does_not_assign_hook_results_to_outer_bindings(
|
|
5911
|
-
catch_body_nodes,
|
|
5912
|
-
transform_context,
|
|
5913
|
-
catch_scoped_names,
|
|
5914
|
-
);
|
|
5915
4776
|
|
|
5916
4777
|
const fallback_fn = b.arrow(
|
|
5917
4778
|
catch_params,
|
|
@@ -6904,6 +5765,12 @@ function build_return_expression(render_nodes) {
|
|
|
6904
5765
|
if (render_nodes.length === 1) {
|
|
6905
5766
|
const only = render_nodes[0];
|
|
6906
5767
|
if (only.type === 'JSXExpressionContainer') {
|
|
5768
|
+
// Reactive-block containers (dynamic tags) must stay expression
|
|
5769
|
+
// children so the host JSX compiler wraps them in a render block;
|
|
5770
|
+
// returning the bare call would evaluate them once.
|
|
5771
|
+
if (only.metadata?.tsrx_reactive_block === true) {
|
|
5772
|
+
return set_loc(b.jsx_fragment([only]), only.loc ? only : undefined);
|
|
5773
|
+
}
|
|
6907
5774
|
return only.expression;
|
|
6908
5775
|
}
|
|
6909
5776
|
if (only.type === 'JSXText') {
|