@tsrx/core 0.1.19 → 0.1.22
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 +3 -3
- package/src/analyze/prune.js +119 -61
- package/src/analyze/validation.js +122 -5
- package/src/index.js +11 -3
- package/src/parse/index.js +157 -99
- package/src/plugin.js +2417 -956
- package/src/runtime/iterable.js +15 -13
- package/src/scope.js +25 -10
- package/src/source-map-utils.js +1 -1
- package/src/transform/await.js +5 -1
- package/src/transform/jsx/ast-builders.js +46 -62
- package/src/transform/jsx/helpers.js +8 -5
- package/src/transform/jsx/index.js +756 -358
- package/src/transform/jsx-interleave.js +1 -2
- package/src/transform/scoping.js +26 -63
- package/src/transform/segments.js +53 -5
- package/src/transform/style-ref.js +3 -11
- package/src/utils/builders.js +2 -3
- package/types/index.d.ts +181 -125
- package/types/jsx-platform.d.ts +6 -11
- package/types/parse.d.ts +36 -10
- package/types/runtime/ref.d.ts +1 -0
|
@@ -24,8 +24,8 @@ import {
|
|
|
24
24
|
identifier_to_jsx_name,
|
|
25
25
|
is_bare_render_expression,
|
|
26
26
|
is_component_jsx_name,
|
|
27
|
-
is_dynamic_element_id,
|
|
28
27
|
is_jsx_child,
|
|
28
|
+
jsx_name_to_expression,
|
|
29
29
|
set_loc,
|
|
30
30
|
to_text_expression,
|
|
31
31
|
} from './ast-builders.js';
|
|
@@ -62,7 +62,18 @@ const HOOK_OUTER_ASSIGNMENT_ERROR =
|
|
|
62
62
|
const HOOK_CALLBACK_OUTER_MUTATION_ERROR =
|
|
63
63
|
'Hook callbacks inside conditional or repeated TSRX scopes must not mutate bindings declared outside the generated hook component.';
|
|
64
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
|
|
65
|
+
'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
|
+
const TSRX_FOR_RETURN_ERROR =
|
|
67
|
+
'Return statements are not allowed inside TSRX template for...of loops. Filter the iterable before rendering or use an @for empty fallback for empty lists.';
|
|
68
|
+
const TSRX_FOR_BREAK_ERROR =
|
|
69
|
+
'Break statements are not allowed inside TSRX template for...of loops.';
|
|
70
|
+
const TSRX_FOR_CONTINUE_ERROR =
|
|
71
|
+
'Continue statements are not allowed inside TSRX template for...of loops. Filter the iterable before rendering.';
|
|
72
|
+
const TSRX_IF_RETURN_ERROR =
|
|
73
|
+
'Return statements are not allowed inside TSRX template @if blocks. Move the return before the template output or render conditionally instead.';
|
|
74
|
+
const TSRX_IF_BREAK_ERROR = 'Break statements are not allowed inside TSRX template @if blocks.';
|
|
75
|
+
const TSRX_IF_CONTINUE_ERROR =
|
|
76
|
+
'Continue statements are not allowed inside TSRX template @if blocks. Filter before rendering or use conditional output instead.';
|
|
66
77
|
|
|
67
78
|
/**
|
|
68
79
|
* @param {AST.Node} node
|
|
@@ -142,6 +153,76 @@ function is_function_or_class_boundary(node) {
|
|
|
142
153
|
);
|
|
143
154
|
}
|
|
144
155
|
|
|
156
|
+
/**
|
|
157
|
+
* @param {any} node
|
|
158
|
+
* @param {boolean} [inside_function]
|
|
159
|
+
* @param {Set<any>} [seen]
|
|
160
|
+
* @returns {void}
|
|
161
|
+
*/
|
|
162
|
+
function mark_nested_function_return_jsx(node, inside_function = false, seen = new Set()) {
|
|
163
|
+
if (!node || typeof node !== 'object' || seen.has(node)) return;
|
|
164
|
+
seen.add(node);
|
|
165
|
+
|
|
166
|
+
if (Array.isArray(node)) {
|
|
167
|
+
for (const item of node) mark_nested_function_return_jsx(item, inside_function, seen);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const now_inside = inside_function || is_function_or_class_boundary(node);
|
|
172
|
+
|
|
173
|
+
if (
|
|
174
|
+
now_inside &&
|
|
175
|
+
node.type === 'ReturnStatement' &&
|
|
176
|
+
(node.argument?.type === 'JSXFragment' ||
|
|
177
|
+
node.argument?.type === 'JSXElement' ||
|
|
178
|
+
node.argument?.type === 'JSXStyleElement')
|
|
179
|
+
) {
|
|
180
|
+
node.argument.metadata = { ...(node.argument.metadata || {}), native_tsrx: true };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
for (const key of Object.keys(node)) {
|
|
184
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') continue;
|
|
185
|
+
mark_nested_function_return_jsx(node[key], now_inside, seen);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Flatten a `@{ … }` code block that appears as an element/fragment child into
|
|
191
|
+
* the element's children list: its setup statements followed by its single
|
|
192
|
+
* render output. The render pipeline already handles interleaved setup
|
|
193
|
+
* statements and JSX children. This is the element-scoped equivalent of
|
|
194
|
+
* `transform_function`'s body lowering — function and arrow bodies are never
|
|
195
|
+
* element children, so they are untouched here.
|
|
196
|
+
* @param {any} node
|
|
197
|
+
* @param {Set<any>} [seen]
|
|
198
|
+
* @returns {void}
|
|
199
|
+
*/
|
|
200
|
+
function expand_child_code_blocks(node, seen = new Set()) {
|
|
201
|
+
if (!node || typeof node !== 'object' || seen.has(node)) return;
|
|
202
|
+
seen.add(node);
|
|
203
|
+
|
|
204
|
+
if (Array.isArray(node)) {
|
|
205
|
+
for (const item of node) expand_child_code_blocks(item, seen);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (
|
|
210
|
+
Array.isArray(node.children) &&
|
|
211
|
+
node.children.some((/** @type {any} */ c) => c?.type === 'JSXCodeBlock')
|
|
212
|
+
) {
|
|
213
|
+
node.children = node.children.flatMap((/** @type {any} */ child) =>
|
|
214
|
+
child?.type === 'JSXCodeBlock'
|
|
215
|
+
? [...child.body, ...(child.render != null ? [child.render] : [])]
|
|
216
|
+
: [child],
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
for (const key of Object.keys(node)) {
|
|
221
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') continue;
|
|
222
|
+
expand_child_code_blocks(node[key], seen);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
145
226
|
/**
|
|
146
227
|
* Build a `transform()` function for a specific JSX platform (React, Preact,
|
|
147
228
|
* Solid). Given a `JsxPlatform` descriptor, returns a transform that lowers
|
|
@@ -150,7 +231,7 @@ function is_function_or_class_boundary(node) {
|
|
|
150
231
|
* Any `<style>` element declared inside a TSRX fragment is collected, rendered
|
|
151
232
|
* via `@tsrx/core`'s stylesheet renderer, and returned alongside the JS output
|
|
152
233
|
* so a downstream plugin can inject it. The compiler also augments every
|
|
153
|
-
* non-style
|
|
234
|
+
* non-style JSX element in that fragment with the stylesheet's hash class so scoped
|
|
154
235
|
* selectors match correctly.
|
|
155
236
|
*
|
|
156
237
|
* @param {JsxPlatform} platform
|
|
@@ -203,6 +284,8 @@ export function createJsxTransform(platform) {
|
|
|
203
284
|
...(platform.hooks?.initialState?.() ?? {}),
|
|
204
285
|
};
|
|
205
286
|
|
|
287
|
+
expand_child_code_blocks(/** @type {any} */ (ast));
|
|
288
|
+
|
|
206
289
|
if (!transform_context.typeOnly) {
|
|
207
290
|
preallocate_lazy_ids(/** @type {any} */ (ast), transform_context);
|
|
208
291
|
}
|
|
@@ -213,15 +296,11 @@ export function createJsxTransform(platform) {
|
|
|
213
296
|
return next();
|
|
214
297
|
},
|
|
215
298
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
wrap_jsx_setup_declarations(tsx_node_to_jsx_expression(inner, in_jsx_child), in_jsx_child)
|
|
221
|
-
);
|
|
222
|
-
},
|
|
299
|
+
JSXFragment(node, { next, path, state, visit }) {
|
|
300
|
+
if (!node.metadata?.native_tsrx) {
|
|
301
|
+
return next() ?? node;
|
|
302
|
+
}
|
|
223
303
|
|
|
224
|
-
Tsrx(node, { next, path, state, visit }) {
|
|
225
304
|
const parent = /** @type {AST.ArrowFunctionExpression} */ (path.at(-1));
|
|
226
305
|
if (parent?.metadata?.native_tsrx && parent.body === node) {
|
|
227
306
|
return /** @type {any} */ (visit(create_native_tsrx_render_block(node, state), state));
|
|
@@ -241,18 +320,15 @@ export function createJsxTransform(platform) {
|
|
|
241
320
|
return /** @type {any} */ (wrap_jsx_setup_declarations(expression, in_jsx_child));
|
|
242
321
|
},
|
|
243
322
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
);
|
|
253
|
-
},
|
|
323
|
+
JSXElement(node, { next, path, state }) {
|
|
324
|
+
if (!node.metadata?.native_tsrx && is_dynamic_jsx_element(node)) {
|
|
325
|
+
return dynamic_element_to_jsx(node, state, in_jsx_child_context(path));
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (!node.metadata?.native_tsrx) {
|
|
329
|
+
return next() ?? node;
|
|
330
|
+
}
|
|
254
331
|
|
|
255
|
-
Element(node, { next, path, state }) {
|
|
256
332
|
if (is_style_element(node) && is_style_expression_position(path)) {
|
|
257
333
|
const stylesheet = get_style_element_stylesheet(node);
|
|
258
334
|
if (stylesheet) {
|
|
@@ -262,30 +338,57 @@ export function createJsxTransform(platform) {
|
|
|
262
338
|
}
|
|
263
339
|
}
|
|
264
340
|
|
|
265
|
-
// Capture raw children BEFORE the walker transforms them so
|
|
266
|
-
//
|
|
267
|
-
// inspect the original Text / TSRXExpression nodes rather than
|
|
268
|
-
// their walker-lowered JSXExpressionContainer equivalents.
|
|
341
|
+
// Capture raw children BEFORE the walker transforms them so platform
|
|
342
|
+
// hooks can inspect the original JSX child shape.
|
|
269
343
|
const raw_children = /** @type {any} */ (node.children || []).map(
|
|
270
344
|
(/** @type {any} */ child) => (child && typeof child === 'object' ? { ...child } : child),
|
|
271
345
|
);
|
|
272
346
|
const inner = /** @type {any} */ (next() ?? node);
|
|
273
347
|
const hook = platform.hooks?.transformElement;
|
|
274
348
|
if (hook) return /** @type {any} */ (hook(inner, state, raw_children));
|
|
275
|
-
return /** @type {any} */ (
|
|
349
|
+
return /** @type {any} */ (
|
|
350
|
+
to_jsx_element(inner, state, raw_children, in_jsx_child_context(path))
|
|
351
|
+
);
|
|
276
352
|
},
|
|
277
353
|
|
|
278
|
-
|
|
279
|
-
const
|
|
354
|
+
JSXExpressionContainer(node, { next, state }) {
|
|
355
|
+
const result = /** @type {any} */ (next() ?? node);
|
|
356
|
+
const expression = result.expression;
|
|
357
|
+
// `@if`/`@for`/`@switch`/`@try` used as an expression value (e.g. an
|
|
358
|
+
// attribute value `content={@if (…) { … }}` or a `{ … }` child) leaks a
|
|
359
|
+
// JSX*Expression node straight to the printer. Lower it with the same
|
|
360
|
+
// control-flow machinery used for render children and unwrap the value.
|
|
361
|
+
if (
|
|
362
|
+
is_if_control_node(expression) ||
|
|
363
|
+
is_switch_control_node(expression) ||
|
|
364
|
+
is_try_control_node(expression) ||
|
|
365
|
+
expression?.type === 'JSXForExpression'
|
|
366
|
+
) {
|
|
367
|
+
const lowered = /** @type {any} */ (to_jsx_child(expression, state));
|
|
368
|
+
return { ...result, expression: lowered?.expression ?? lowered };
|
|
369
|
+
}
|
|
370
|
+
return result;
|
|
371
|
+
},
|
|
372
|
+
|
|
373
|
+
JSXStyleElement(node, { path, state }) {
|
|
374
|
+
if (is_style_expression_position(path)) {
|
|
375
|
+
const stylesheet = get_style_element_stylesheet(node);
|
|
376
|
+
if (stylesheet) {
|
|
377
|
+
analyze_css(stylesheet);
|
|
378
|
+
state.stylesheets.push(stylesheet);
|
|
379
|
+
return /** @type {any} */ (create_style_expression_value(node, stylesheet, state));
|
|
380
|
+
}
|
|
381
|
+
}
|
|
280
382
|
return /** @type {any} */ (
|
|
281
|
-
|
|
383
|
+
b.jsx_element(
|
|
384
|
+
/** @type {ESTreeJSX.JSXElement} */ ({ ...node, type: 'JSXElement', children: [] }),
|
|
385
|
+
node.openingElement?.attributes ?? [],
|
|
386
|
+
[],
|
|
387
|
+
)
|
|
282
388
|
);
|
|
283
389
|
},
|
|
284
390
|
|
|
285
|
-
|
|
286
|
-
const inner = /** @type {any} */ (next() ?? node);
|
|
287
|
-
return /** @type {any} */ (to_jsx_expression_container(inner.expression, inner));
|
|
288
|
-
},
|
|
391
|
+
JSXCodeBlock: transform_jsx_code_block,
|
|
289
392
|
|
|
290
393
|
BlockStatement: transform_block_statement,
|
|
291
394
|
ReturnStatement: transform_return_statement,
|
|
@@ -338,6 +441,7 @@ export function createJsxTransform(platform) {
|
|
|
338
441
|
? expanded
|
|
339
442
|
: apply_lazy_transforms(/** @type {any} */ (expanded), new Map())
|
|
340
443
|
);
|
|
444
|
+
lower_remaining_jsx_code_blocks(final_program, transform_context);
|
|
341
445
|
|
|
342
446
|
const result = print(/** @type {any} */ (final_program), tsx_with_ts_locations(), {
|
|
343
447
|
sourceMapSource: filename,
|
|
@@ -416,7 +520,7 @@ function collect_css_prunable_elements(value, elements = []) {
|
|
|
416
520
|
return elements;
|
|
417
521
|
}
|
|
418
522
|
|
|
419
|
-
if (value.type === '
|
|
523
|
+
if (value.type === 'JSXElement' && value.metadata?.native_tsrx) {
|
|
420
524
|
if (!is_style_element(value)) {
|
|
421
525
|
elements.push(value);
|
|
422
526
|
}
|
|
@@ -448,6 +552,12 @@ function build_component_statements(body_nodes, transform_context) {
|
|
|
448
552
|
* @returns {any[]}
|
|
449
553
|
*/
|
|
450
554
|
function build_render_statements(body_nodes, return_null_when_empty, transform_context) {
|
|
555
|
+
body_nodes = body_nodes.flatMap((node) =>
|
|
556
|
+
node?.type === 'JSXCodeBlock'
|
|
557
|
+
? [...node.body, ...(node.render != null ? [node.render] : [])]
|
|
558
|
+
: [node],
|
|
559
|
+
);
|
|
560
|
+
|
|
451
561
|
const statements = [];
|
|
452
562
|
const render_nodes = [];
|
|
453
563
|
let has_terminal_return = false;
|
|
@@ -530,7 +640,7 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
530
640
|
}
|
|
531
641
|
|
|
532
642
|
if (
|
|
533
|
-
child
|
|
643
|
+
is_for_of_control_node(child) &&
|
|
534
644
|
!child.await &&
|
|
535
645
|
should_extract_hook_helpers(transform_context) &&
|
|
536
646
|
!transform_context.platform.hooks?.isTopLevelSetupCall &&
|
|
@@ -541,7 +651,10 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
541
651
|
true,
|
|
542
652
|
)
|
|
543
653
|
) {
|
|
544
|
-
const hoisted = build_hoisted_for_of_with_hooks(
|
|
654
|
+
const hoisted = build_hoisted_for_of_with_hooks(
|
|
655
|
+
jsx_control_expression_to_statement(child),
|
|
656
|
+
transform_context,
|
|
657
|
+
);
|
|
545
658
|
if (hoisted) {
|
|
546
659
|
statements.push(...hoisted.hoist_statements);
|
|
547
660
|
if (interleaved && is_capturable_jsx_child(hoisted.jsx_child)) {
|
|
@@ -568,6 +681,7 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
568
681
|
} else if (is_bare_render_expression(child)) {
|
|
569
682
|
render_nodes.push(to_jsx_expression_container(child, child));
|
|
570
683
|
} else {
|
|
684
|
+
mark_nested_function_return_jsx(child);
|
|
571
685
|
statements.push(child);
|
|
572
686
|
collect_statement_bindings(child, transform_context.available_bindings);
|
|
573
687
|
}
|
|
@@ -675,7 +789,7 @@ function find_hook_split_index(body_nodes, transform_context) {
|
|
|
675
789
|
* @returns {boolean}
|
|
676
790
|
*/
|
|
677
791
|
function is_component_body_conditional_return_statement(node) {
|
|
678
|
-
if (node
|
|
792
|
+
if (!is_if_control_node(node)) {
|
|
679
793
|
return false;
|
|
680
794
|
}
|
|
681
795
|
|
|
@@ -710,20 +824,20 @@ function statement_contains_component_body_return(node) {
|
|
|
710
824
|
return (node.body || []).some(statement_contains_component_body_return);
|
|
711
825
|
}
|
|
712
826
|
|
|
713
|
-
if (node
|
|
827
|
+
if (is_if_control_node(node)) {
|
|
714
828
|
return (
|
|
715
829
|
statement_contains_component_body_return(node.consequent) ||
|
|
716
830
|
statement_contains_component_body_return(node.alternate)
|
|
717
831
|
);
|
|
718
832
|
}
|
|
719
833
|
|
|
720
|
-
if (node
|
|
834
|
+
if (is_switch_control_node(node)) {
|
|
721
835
|
return (node.cases || []).some((/** @type {any} */ switch_case) =>
|
|
722
836
|
statement_contains_component_body_return(switch_case.consequent || []),
|
|
723
837
|
);
|
|
724
838
|
}
|
|
725
839
|
|
|
726
|
-
if (node
|
|
840
|
+
if (is_try_control_node(node)) {
|
|
727
841
|
return (
|
|
728
842
|
statement_contains_component_body_return(node.block) ||
|
|
729
843
|
statement_contains_component_body_return(node.handler?.body) ||
|
|
@@ -1016,13 +1130,41 @@ function transform_block_statement(node, { next, visit, state, path }) {
|
|
|
1016
1130
|
* @returns {any}
|
|
1017
1131
|
*/
|
|
1018
1132
|
function transform_return_statement(node, { next, visit, state, path }) {
|
|
1019
|
-
if (get_active_native_tsrx_function(path) && node.argument
|
|
1133
|
+
if (get_active_native_tsrx_function(path) && is_native_tsrx_node(node.argument)) {
|
|
1020
1134
|
return visit(create_native_tsrx_render_block(node.argument, state), state);
|
|
1021
1135
|
}
|
|
1022
1136
|
|
|
1023
1137
|
return next() ?? node;
|
|
1024
1138
|
}
|
|
1025
1139
|
|
|
1140
|
+
/**
|
|
1141
|
+
* @param {any} node
|
|
1142
|
+
* @param {{ state: TransformContext, path: AST.Node[] }} context
|
|
1143
|
+
* @returns {any}
|
|
1144
|
+
*/
|
|
1145
|
+
function transform_jsx_code_block(node, { state, path }) {
|
|
1146
|
+
const body_nodes = get_jsx_code_block_body_nodes(node, state);
|
|
1147
|
+
const parent = /** @type {any} */ (path.at(-1));
|
|
1148
|
+
|
|
1149
|
+
if (parent && parent.body === node && is_function_or_class_boundary(parent)) {
|
|
1150
|
+
const block = b.block(
|
|
1151
|
+
mark_native_pretransformed_jsx(build_render_statements(body_nodes, true, state)),
|
|
1152
|
+
node,
|
|
1153
|
+
);
|
|
1154
|
+
block.metadata = {
|
|
1155
|
+
...(block.metadata || {}),
|
|
1156
|
+
native_return_block: true,
|
|
1157
|
+
};
|
|
1158
|
+
return block;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
const expression = b.call(
|
|
1162
|
+
b.arrow([], b.block(build_render_statements(body_nodes, true, state), node)),
|
|
1163
|
+
);
|
|
1164
|
+
|
|
1165
|
+
return in_jsx_child_context(path) ? to_jsx_expression_container(expression, node) : expression;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1026
1168
|
/**
|
|
1027
1169
|
* @param {AST.Node[]} path
|
|
1028
1170
|
* @returns {any | null}
|
|
@@ -1043,6 +1185,13 @@ function get_active_native_tsrx_function(path) {
|
|
|
1043
1185
|
* @returns {any}
|
|
1044
1186
|
*/
|
|
1045
1187
|
function transform_function(node, context) {
|
|
1188
|
+
// Lower a `@{ … }` function body (JSXCodeBlock) to an ordinary block: the
|
|
1189
|
+
// setup statements followed by `return <render>` when the block produces a
|
|
1190
|
+
// render output. The parser already marks the render JSX as native_tsrx, so
|
|
1191
|
+
// from here it flows through the existing native-component machinery exactly
|
|
1192
|
+
// like the older fenced `{ return <> … </> }` shape.
|
|
1193
|
+
lower_jsx_code_block_function_body(node);
|
|
1194
|
+
|
|
1046
1195
|
if (node.metadata?.native_tsrx_function || function_has_native_tsrx_return(node)) {
|
|
1047
1196
|
return transform_native_tsrx_function(node, context);
|
|
1048
1197
|
}
|
|
@@ -1050,6 +1199,34 @@ function transform_function(node, context) {
|
|
|
1050
1199
|
return transform_function_with_hook_helpers(node, context);
|
|
1051
1200
|
}
|
|
1052
1201
|
|
|
1202
|
+
/**
|
|
1203
|
+
* @param {any} node
|
|
1204
|
+
* @returns {void}
|
|
1205
|
+
*/
|
|
1206
|
+
function lower_jsx_code_block_function_body(node) {
|
|
1207
|
+
if (node.body?.type !== 'JSXCodeBlock') return;
|
|
1208
|
+
|
|
1209
|
+
const code_block = node.body;
|
|
1210
|
+
const statements = [...code_block.body];
|
|
1211
|
+
if (code_block.render != null) {
|
|
1212
|
+
let render = code_block.render;
|
|
1213
|
+
if (!is_native_tsrx_node(render)) {
|
|
1214
|
+
// A control-flow output (@if/@for/@switch/@try) isn't itself a native
|
|
1215
|
+
// template node, so `return @if (…) { … }` wouldn't be recognized as a
|
|
1216
|
+
// component render output. Wrap it in a native fragment so it flows
|
|
1217
|
+
// through the same children-rendering path as a `<> … </>` render.
|
|
1218
|
+
const fragment = b.jsx_fragment([render]);
|
|
1219
|
+
fragment.metadata = { ...fragment.metadata, native_tsrx: true };
|
|
1220
|
+
render = fragment;
|
|
1221
|
+
}
|
|
1222
|
+
statements.push(b.return(render, code_block.render));
|
|
1223
|
+
}
|
|
1224
|
+
node.body = b.block(statements, code_block);
|
|
1225
|
+
if (node.type === 'ArrowFunctionExpression') {
|
|
1226
|
+
node.expression = false;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1053
1230
|
/**
|
|
1054
1231
|
* @param {any} node
|
|
1055
1232
|
* @param {{ next: () => any, state: TransformContext }} context
|
|
@@ -1080,7 +1257,7 @@ function transform_native_tsrx_function(node, { next, state }) {
|
|
|
1080
1257
|
if (
|
|
1081
1258
|
inner !== node &&
|
|
1082
1259
|
node.type === 'ArrowFunctionExpression' &&
|
|
1083
|
-
node.body
|
|
1260
|
+
is_native_tsrx_node(node.body) &&
|
|
1084
1261
|
inner.body?.type === 'BlockStatement'
|
|
1085
1262
|
) {
|
|
1086
1263
|
inner.expression = false;
|
|
@@ -1140,6 +1317,10 @@ function find_native_await(node) {
|
|
|
1140
1317
|
return find_first_top_level_await(node.body, false);
|
|
1141
1318
|
}
|
|
1142
1319
|
|
|
1320
|
+
if (node.body?.type === 'JSXCodeBlock') {
|
|
1321
|
+
return find_native_await_in_list(get_raw_jsx_code_block_body_nodes(node.body));
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1143
1324
|
const body = node.body?.type === 'BlockStatement' ? node.body.body || [] : [];
|
|
1144
1325
|
return find_native_await_in_list(body);
|
|
1145
1326
|
}
|
|
@@ -1163,7 +1344,7 @@ function find_native_await_in_list(statements) {
|
|
|
1163
1344
|
function find_native_await_in_statement(statement) {
|
|
1164
1345
|
if (!statement || typeof statement !== 'object') return null;
|
|
1165
1346
|
|
|
1166
|
-
if (statement.type === 'ReturnStatement' && statement.argument
|
|
1347
|
+
if (statement.type === 'ReturnStatement' && is_native_tsrx_node(statement.argument)) {
|
|
1167
1348
|
return find_first_top_level_await_in_tsrx_function_body(statement.argument.children || []);
|
|
1168
1349
|
}
|
|
1169
1350
|
|
|
@@ -1182,14 +1363,14 @@ function find_native_await_in_statement(statement) {
|
|
|
1182
1363
|
return find_native_await_in_list(statement.body || []);
|
|
1183
1364
|
}
|
|
1184
1365
|
|
|
1185
|
-
if (statement
|
|
1366
|
+
if (is_if_control_node(statement)) {
|
|
1186
1367
|
return (
|
|
1187
1368
|
find_native_await_in_statement(statement.consequent) ||
|
|
1188
1369
|
find_native_await_in_statement(statement.alternate)
|
|
1189
1370
|
);
|
|
1190
1371
|
}
|
|
1191
1372
|
|
|
1192
|
-
if (statement
|
|
1373
|
+
if (is_switch_control_node(statement)) {
|
|
1193
1374
|
for (const switch_case of statement.cases || []) {
|
|
1194
1375
|
const found = find_native_await_in_list(switch_case.consequent || []);
|
|
1195
1376
|
if (found) return found;
|
|
@@ -1197,7 +1378,7 @@ function find_native_await_in_statement(statement) {
|
|
|
1197
1378
|
return null;
|
|
1198
1379
|
}
|
|
1199
1380
|
|
|
1200
|
-
if (statement
|
|
1381
|
+
if (is_try_control_node(statement)) {
|
|
1201
1382
|
return (
|
|
1202
1383
|
find_native_await_in_statement(statement.block) ||
|
|
1203
1384
|
find_native_await_in_statement(statement.handler?.body) ||
|
|
@@ -1205,7 +1386,7 @@ function find_native_await_in_statement(statement) {
|
|
|
1205
1386
|
);
|
|
1206
1387
|
}
|
|
1207
1388
|
|
|
1208
|
-
return
|
|
1389
|
+
return find_first_top_level_await(statement, false);
|
|
1209
1390
|
}
|
|
1210
1391
|
|
|
1211
1392
|
/**
|
|
@@ -1258,7 +1439,7 @@ function transform_function_with_hook_helpers(node, { next, state }) {
|
|
|
1258
1439
|
* @returns {string}
|
|
1259
1440
|
*/
|
|
1260
1441
|
function get_function_helper_base_name(node) {
|
|
1261
|
-
return get_function_like_name(node) || '
|
|
1442
|
+
return get_function_like_name(node) || 'TSRXTemplate';
|
|
1262
1443
|
}
|
|
1263
1444
|
|
|
1264
1445
|
/**
|
|
@@ -1337,7 +1518,7 @@ function collect_function_scope_bindings(node) {
|
|
|
1337
1518
|
const bindings = collect_param_bindings(node.params || []);
|
|
1338
1519
|
if (node.body?.type === 'BlockStatement') {
|
|
1339
1520
|
for (const statement of node.body.body || []) {
|
|
1340
|
-
if (statement.type === 'ReturnStatement' && statement.argument
|
|
1521
|
+
if (statement.type === 'ReturnStatement' && is_native_tsrx_node(statement.argument)) {
|
|
1341
1522
|
for (const child of get_tsrx_render_children(statement.argument)) {
|
|
1342
1523
|
collect_statement_bindings(child, bindings);
|
|
1343
1524
|
}
|
|
@@ -1369,6 +1550,10 @@ function merge_binding_maps(outer, inner) {
|
|
|
1369
1550
|
function function_has_native_tsrx_return(node) {
|
|
1370
1551
|
if (!node) return false;
|
|
1371
1552
|
|
|
1553
|
+
if (node.body?.type === 'JSXCodeBlock') {
|
|
1554
|
+
return true;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1372
1557
|
if (node.type === 'ArrowFunctionExpression' && node.body?.type !== 'BlockStatement') {
|
|
1373
1558
|
return node_contains_native_tsrx_template(node.body);
|
|
1374
1559
|
}
|
|
@@ -1404,22 +1589,23 @@ function statement_contains_native_tsrx_return(statement) {
|
|
|
1404
1589
|
return statements_contain_native_tsrx_return(statement.body || []);
|
|
1405
1590
|
}
|
|
1406
1591
|
|
|
1407
|
-
if (statement
|
|
1592
|
+
if (is_if_control_node(statement)) {
|
|
1408
1593
|
return (
|
|
1409
1594
|
statement_contains_native_tsrx_return(statement.consequent) ||
|
|
1410
1595
|
statement_contains_native_tsrx_return(statement.alternate)
|
|
1411
1596
|
);
|
|
1412
1597
|
}
|
|
1413
1598
|
|
|
1414
|
-
if (statement
|
|
1599
|
+
if (is_switch_control_node(statement)) {
|
|
1415
1600
|
return (statement.cases || []).some((/** @type {any} */ c) =>
|
|
1416
1601
|
statements_contain_native_tsrx_return(c.consequent || []),
|
|
1417
1602
|
);
|
|
1418
1603
|
}
|
|
1419
1604
|
|
|
1420
|
-
if (statement
|
|
1605
|
+
if (is_try_control_node(statement)) {
|
|
1421
1606
|
return (
|
|
1422
1607
|
statement_contains_native_tsrx_return(statement.block) ||
|
|
1608
|
+
statement_contains_native_tsrx_return(statement.pending) ||
|
|
1423
1609
|
statement_contains_native_tsrx_return(statement.handler?.body) ||
|
|
1424
1610
|
statement_contains_native_tsrx_return(statement.finalizer)
|
|
1425
1611
|
);
|
|
@@ -1446,7 +1632,7 @@ function statement_contains_native_tsrx_return(statement) {
|
|
|
1446
1632
|
*/
|
|
1447
1633
|
function node_contains_native_tsrx_template(node) {
|
|
1448
1634
|
if (!node || typeof node !== 'object') return false;
|
|
1449
|
-
if (node
|
|
1635
|
+
if (is_native_tsrx_node(node)) return true;
|
|
1450
1636
|
|
|
1451
1637
|
if (is_function_or_class_boundary(node)) {
|
|
1452
1638
|
return false;
|
|
@@ -1637,11 +1823,11 @@ function collect_style_elements(node, styles) {
|
|
|
1637
1823
|
return;
|
|
1638
1824
|
}
|
|
1639
1825
|
|
|
1640
|
-
if (is_function_or_class_boundary(node)
|
|
1826
|
+
if (is_function_or_class_boundary(node)) {
|
|
1641
1827
|
return;
|
|
1642
1828
|
}
|
|
1643
1829
|
|
|
1644
|
-
if (node.type === '
|
|
1830
|
+
if ((node.type === 'JSXElement' || node.type === 'JSXFragment') && node.metadata?.native_tsrx) {
|
|
1645
1831
|
collect_style_elements(node.children || [], styles);
|
|
1646
1832
|
return;
|
|
1647
1833
|
}
|
|
@@ -1651,20 +1837,20 @@ function collect_style_elements(node, styles) {
|
|
|
1651
1837
|
return;
|
|
1652
1838
|
}
|
|
1653
1839
|
|
|
1654
|
-
if (node
|
|
1840
|
+
if (is_if_control_node(node)) {
|
|
1655
1841
|
collect_style_elements(node.consequent, styles);
|
|
1656
1842
|
collect_style_elements(node.alternate, styles);
|
|
1657
1843
|
return;
|
|
1658
1844
|
}
|
|
1659
1845
|
|
|
1660
|
-
if (node
|
|
1846
|
+
if (is_switch_control_node(node)) {
|
|
1661
1847
|
for (const switch_case of node.cases || []) {
|
|
1662
1848
|
collect_style_elements(switch_case.consequent || [], styles);
|
|
1663
1849
|
}
|
|
1664
1850
|
return;
|
|
1665
1851
|
}
|
|
1666
1852
|
|
|
1667
|
-
if (node
|
|
1853
|
+
if (is_try_control_node(node)) {
|
|
1668
1854
|
collect_style_elements(node.block, styles);
|
|
1669
1855
|
collect_style_elements(node.handler?.body, styles);
|
|
1670
1856
|
collect_style_elements(node.finalizer, styles);
|
|
@@ -1716,7 +1902,7 @@ function strip_style_elements(node) {
|
|
|
1716
1902
|
return node;
|
|
1717
1903
|
}
|
|
1718
1904
|
|
|
1719
|
-
if (node.type === '
|
|
1905
|
+
if ((node.type === 'JSXElement' || node.type === 'JSXFragment') && node.metadata?.native_tsrx) {
|
|
1720
1906
|
node.children = strip_style_elements(node.children || []);
|
|
1721
1907
|
return node;
|
|
1722
1908
|
}
|
|
@@ -1726,20 +1912,20 @@ function strip_style_elements(node) {
|
|
|
1726
1912
|
return node;
|
|
1727
1913
|
}
|
|
1728
1914
|
|
|
1729
|
-
if (node
|
|
1915
|
+
if (is_if_control_node(node)) {
|
|
1730
1916
|
node.consequent = strip_style_elements(node.consequent);
|
|
1731
1917
|
if (node.alternate) node.alternate = strip_style_elements(node.alternate);
|
|
1732
1918
|
return node;
|
|
1733
1919
|
}
|
|
1734
1920
|
|
|
1735
|
-
if (node
|
|
1921
|
+
if (is_switch_control_node(node)) {
|
|
1736
1922
|
for (const switch_case of node.cases || []) {
|
|
1737
1923
|
switch_case.consequent = strip_style_elements(switch_case.consequent || []);
|
|
1738
1924
|
}
|
|
1739
1925
|
return node;
|
|
1740
1926
|
}
|
|
1741
1927
|
|
|
1742
|
-
if (node
|
|
1928
|
+
if (is_try_control_node(node)) {
|
|
1743
1929
|
node.block = strip_style_elements(node.block);
|
|
1744
1930
|
if (node.handler?.body) node.handler.body = strip_style_elements(node.handler.body);
|
|
1745
1931
|
if (node.finalizer) node.finalizer = strip_style_elements(node.finalizer);
|
|
@@ -1755,10 +1941,7 @@ function strip_style_elements(node) {
|
|
|
1755
1941
|
function is_style_expression_position(path) {
|
|
1756
1942
|
const parent = path.at(-1);
|
|
1757
1943
|
return !(
|
|
1758
|
-
parent
|
|
1759
|
-
parent?.type === 'Tsrx' ||
|
|
1760
|
-
parent?.type === 'Tsx' ||
|
|
1761
|
-
parent?.type === 'TsxCompat' ||
|
|
1944
|
+
is_native_tsrx_node(parent) ||
|
|
1762
1945
|
parent?.type === 'BlockStatement' ||
|
|
1763
1946
|
parent?.type === 'Program' ||
|
|
1764
1947
|
parent?.type === 'SwitchCase'
|
|
@@ -1813,9 +1996,11 @@ function create_native_tsrx_statement_list_block(block, transform_context) {
|
|
|
1813
1996
|
function create_native_tsrx_render_statements(fragment, transform_context) {
|
|
1814
1997
|
return with_tsrx_fragment_styles(fragment, transform_context, (style_context) => {
|
|
1815
1998
|
const target = style_context?.fragment ?? fragment;
|
|
1999
|
+
const render_nodes =
|
|
2000
|
+
target.type === 'JSXFragment' ? get_tsrx_render_children(target) : [target];
|
|
1816
2001
|
return [
|
|
1817
2002
|
...create_tsrx_style_ref_setup_statements(target, style_context, transform_context),
|
|
1818
|
-
...build_render_statements(
|
|
2003
|
+
...build_render_statements(render_nodes, true, transform_context),
|
|
1819
2004
|
];
|
|
1820
2005
|
});
|
|
1821
2006
|
}
|
|
@@ -1845,7 +2030,7 @@ function expand_native_tsrx_return_statement_list(statements, transform_context)
|
|
|
1845
2030
|
function expand_native_tsrx_return_statement(statement, transform_context) {
|
|
1846
2031
|
if (!statement || typeof statement !== 'object') return [statement];
|
|
1847
2032
|
|
|
1848
|
-
if (statement.type === 'ReturnStatement' && statement.argument
|
|
2033
|
+
if (statement.type === 'ReturnStatement' && is_native_tsrx_node(statement.argument)) {
|
|
1849
2034
|
return create_native_tsrx_render_statements(statement.argument, transform_context);
|
|
1850
2035
|
}
|
|
1851
2036
|
|
|
@@ -1858,7 +2043,7 @@ function expand_native_tsrx_return_statement(statement, transform_context) {
|
|
|
1858
2043
|
return body === statement.body ? [statement] : [b.block(body, statement)];
|
|
1859
2044
|
}
|
|
1860
2045
|
|
|
1861
|
-
if (statement
|
|
2046
|
+
if (is_if_control_node(statement)) {
|
|
1862
2047
|
const consequent = expand_embedded_native_return_statement(
|
|
1863
2048
|
statement.consequent,
|
|
1864
2049
|
transform_context,
|
|
@@ -1872,7 +2057,7 @@ function expand_native_tsrx_return_statement(statement, transform_context) {
|
|
|
1872
2057
|
return [set_loc(b.if(statement.test, consequent, alternate), statement)];
|
|
1873
2058
|
}
|
|
1874
2059
|
|
|
1875
|
-
if (statement
|
|
2060
|
+
if (is_switch_control_node(statement)) {
|
|
1876
2061
|
let changed = false;
|
|
1877
2062
|
const cases = (statement.cases || []).map((/** @type {any} */ switch_case) => {
|
|
1878
2063
|
const consequent = expand_native_tsrx_return_statement_list(
|
|
@@ -1888,8 +2073,11 @@ function expand_native_tsrx_return_statement(statement, transform_context) {
|
|
|
1888
2073
|
return changed ? [set_loc(b.switch(statement.discriminant, cases), statement)] : [statement];
|
|
1889
2074
|
}
|
|
1890
2075
|
|
|
1891
|
-
if (statement
|
|
2076
|
+
if (is_try_control_node(statement)) {
|
|
1892
2077
|
const block = expand_embedded_native_return_statement(statement.block, transform_context);
|
|
2078
|
+
const pending = statement.pending
|
|
2079
|
+
? expand_embedded_native_return_statement(statement.pending, transform_context)
|
|
2080
|
+
: statement.pending;
|
|
1893
2081
|
const handler_body = statement.handler?.body
|
|
1894
2082
|
? expand_embedded_native_return_statement(statement.handler.body, transform_context)
|
|
1895
2083
|
: statement.handler?.body;
|
|
@@ -1898,6 +2086,7 @@ function expand_native_tsrx_return_statement(statement, transform_context) {
|
|
|
1898
2086
|
: statement.finalizer;
|
|
1899
2087
|
if (
|
|
1900
2088
|
block === statement.block &&
|
|
2089
|
+
pending === statement.pending &&
|
|
1901
2090
|
handler_body === statement.handler?.body &&
|
|
1902
2091
|
finalizer === statement.finalizer
|
|
1903
2092
|
) {
|
|
@@ -1912,7 +2101,7 @@ function expand_native_tsrx_return_statement(statement, transform_context) {
|
|
|
1912
2101
|
statement.handler,
|
|
1913
2102
|
)
|
|
1914
2103
|
: statement.handler;
|
|
1915
|
-
return [set_loc(b.try(block, handler, finalizer,
|
|
2104
|
+
return [set_loc(b.try(block, handler, finalizer, pending ?? null), statement)];
|
|
1916
2105
|
}
|
|
1917
2106
|
|
|
1918
2107
|
return [statement];
|
|
@@ -2045,7 +2234,7 @@ function node_contains_hook_bearing_tsrx(node, transform_context) {
|
|
|
2045
2234
|
return node.some((child) => node_contains_hook_bearing_tsrx(child, transform_context));
|
|
2046
2235
|
}
|
|
2047
2236
|
|
|
2048
|
-
if (node
|
|
2237
|
+
if (is_native_tsrx_node(node)) {
|
|
2049
2238
|
return body_contains_top_level_hook_call(node.children || [], transform_context, true);
|
|
2050
2239
|
}
|
|
2051
2240
|
|
|
@@ -2092,7 +2281,7 @@ function should_extract_hook_helpers(transform_context) {
|
|
|
2092
2281
|
*/
|
|
2093
2282
|
function create_module_scoped_hook_component_id(helper_id, transform_context) {
|
|
2094
2283
|
return create_generated_identifier(
|
|
2095
|
-
`${transform_context.helper_state?.base_name || '
|
|
2284
|
+
`${transform_context.helper_state?.base_name || 'TSRXTemplate'}__${helper_id.name}`,
|
|
2096
2285
|
);
|
|
2097
2286
|
}
|
|
2098
2287
|
|
|
@@ -2310,6 +2499,48 @@ function expand_component_helpers(program) {
|
|
|
2310
2499
|
return program;
|
|
2311
2500
|
}
|
|
2312
2501
|
|
|
2502
|
+
/**
|
|
2503
|
+
* Generated helper metadata can be appended after the main transformer walk.
|
|
2504
|
+
* If one of those helpers contains a statement-container body, lower it before
|
|
2505
|
+
* the printer sees the helper subtree.
|
|
2506
|
+
*
|
|
2507
|
+
* @param {any} node
|
|
2508
|
+
* @param {TransformContext} transform_context
|
|
2509
|
+
* @param {Set<any>} [seen]
|
|
2510
|
+
* @returns {void}
|
|
2511
|
+
*/
|
|
2512
|
+
function lower_remaining_jsx_code_blocks(node, transform_context, seen = new Set()) {
|
|
2513
|
+
if (!node || typeof node !== 'object' || seen.has(node)) return;
|
|
2514
|
+
seen.add(node);
|
|
2515
|
+
|
|
2516
|
+
if (is_function_or_class_boundary(node)) {
|
|
2517
|
+
lower_jsx_code_block_function_body(node);
|
|
2518
|
+
}
|
|
2519
|
+
|
|
2520
|
+
for (const key of Object.keys(node)) {
|
|
2521
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') continue;
|
|
2522
|
+
let value = node[key];
|
|
2523
|
+
if (!value || typeof value !== 'object') continue;
|
|
2524
|
+
|
|
2525
|
+
if (Array.isArray(value)) {
|
|
2526
|
+
if (key === 'body') {
|
|
2527
|
+
value = node[key] = value.flatMap((child) => {
|
|
2528
|
+
if (child?.type !== 'JSXCodeBlock') return [child];
|
|
2529
|
+
const body_nodes = get_jsx_code_block_body_nodes(child, transform_context);
|
|
2530
|
+
return mark_native_pretransformed_jsx(
|
|
2531
|
+
build_render_statements(body_nodes, true, transform_context),
|
|
2532
|
+
);
|
|
2533
|
+
});
|
|
2534
|
+
}
|
|
2535
|
+
for (const child of value) {
|
|
2536
|
+
lower_remaining_jsx_code_blocks(child, transform_context, seen);
|
|
2537
|
+
}
|
|
2538
|
+
} else {
|
|
2539
|
+
lower_remaining_jsx_code_blocks(value, transform_context, seen);
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
|
|
2313
2544
|
/**
|
|
2314
2545
|
* Generated helper/statics metadata can be carried on function declarations,
|
|
2315
2546
|
* variable declarations, object literal members, or export-safe expressions,
|
|
@@ -2392,11 +2623,7 @@ function create_component_return_statement(
|
|
|
2392
2623
|
* @returns {boolean}
|
|
2393
2624
|
*/
|
|
2394
2625
|
function is_loop_skip_return_statement(node) {
|
|
2395
|
-
return
|
|
2396
|
-
node?.type === 'ReturnStatement' &&
|
|
2397
|
-
node.argument == null &&
|
|
2398
|
-
node.metadata?.generated_loop_continue_return === true
|
|
2399
|
-
);
|
|
2626
|
+
return node?.type === 'ReturnStatement' && node.metadata?.generated_loop_continue_return === true;
|
|
2400
2627
|
}
|
|
2401
2628
|
|
|
2402
2629
|
/**
|
|
@@ -2412,7 +2639,7 @@ function is_loop_skip_if_statement(node) {
|
|
|
2412
2639
|
* @returns {any[] | null}
|
|
2413
2640
|
*/
|
|
2414
2641
|
function get_loop_skip_if_consequent_body(node) {
|
|
2415
|
-
if (node
|
|
2642
|
+
if (!is_if_control_node(node) || node.alternate) {
|
|
2416
2643
|
return null;
|
|
2417
2644
|
}
|
|
2418
2645
|
|
|
@@ -2433,7 +2660,15 @@ function create_component_loop_skip_if_statement(node, render_nodes, transform_c
|
|
|
2433
2660
|
const branch_statements = build_render_statements(consequent_body, true, transform_context);
|
|
2434
2661
|
prepend_render_nodes_to_return_statements(branch_statements, render_nodes);
|
|
2435
2662
|
|
|
2436
|
-
|
|
2663
|
+
const statement = set_loc(
|
|
2664
|
+
b.if(node.test, set_loc(b.block(branch_statements), node.consequent), null),
|
|
2665
|
+
node,
|
|
2666
|
+
);
|
|
2667
|
+
statement.metadata = {
|
|
2668
|
+
...(statement.metadata || {}),
|
|
2669
|
+
generated_loop_skip_if: true,
|
|
2670
|
+
};
|
|
2671
|
+
return statement;
|
|
2437
2672
|
}
|
|
2438
2673
|
|
|
2439
2674
|
/**
|
|
@@ -2812,26 +3047,37 @@ function create_helper_props_type_literal_with_typeof_flags(bindings, aliases, u
|
|
|
2812
3047
|
/**
|
|
2813
3048
|
* @param {any} node
|
|
2814
3049
|
* @param {TransformContext} transform_context
|
|
3050
|
+
* @param {boolean} [in_jsx_child]
|
|
2815
3051
|
* @returns {any}
|
|
2816
3052
|
*/
|
|
2817
|
-
function to_jsx_element(
|
|
2818
|
-
|
|
2819
|
-
|
|
3053
|
+
function to_jsx_element(
|
|
3054
|
+
node,
|
|
3055
|
+
transform_context,
|
|
3056
|
+
raw_children = node.children || [],
|
|
3057
|
+
in_jsx_child = false,
|
|
3058
|
+
) {
|
|
3059
|
+
if (node.type === 'JSXElement' && !node.metadata?.native_tsrx && !is_dynamic_jsx_element(node)) {
|
|
3060
|
+
return node;
|
|
3061
|
+
}
|
|
3062
|
+
|
|
3063
|
+
const source_opening = node.openingElement;
|
|
3064
|
+
const source_name = source_opening?.name;
|
|
3065
|
+
if (!source_name) {
|
|
2820
3066
|
report_jsx_fragment_in_tsrx_error(node, transform_context);
|
|
2821
3067
|
return set_loc(b.jsx_fragment(), node);
|
|
2822
3068
|
}
|
|
2823
|
-
if (
|
|
2824
|
-
return
|
|
3069
|
+
if (is_dynamic_jsx_element(node)) {
|
|
3070
|
+
return dynamic_element_to_jsx(node, transform_context, in_jsx_child);
|
|
2825
3071
|
}
|
|
2826
3072
|
|
|
2827
|
-
const name =
|
|
3073
|
+
const name = clone_jsx_name(source_name);
|
|
2828
3074
|
const attributes = transform_element_attributes_dispatch(
|
|
2829
|
-
|
|
3075
|
+
source_opening.attributes || [],
|
|
2830
3076
|
transform_context,
|
|
2831
3077
|
node,
|
|
2832
3078
|
);
|
|
2833
3079
|
const walked_children = node.children || [];
|
|
2834
|
-
let selfClosing = !!
|
|
3080
|
+
let selfClosing = !!source_opening.selfClosing;
|
|
2835
3081
|
let children;
|
|
2836
3082
|
const child_transform = transform_context.platform.hooks?.transformElementChildren?.(
|
|
2837
3083
|
node,
|
|
@@ -2857,7 +3103,7 @@ function to_jsx_element(node, transform_context, raw_children = node.children ||
|
|
|
2857
3103
|
name,
|
|
2858
3104
|
attributes,
|
|
2859
3105
|
selfClosing,
|
|
2860
|
-
|
|
3106
|
+
source_opening.typeArguments,
|
|
2861
3107
|
);
|
|
2862
3108
|
const openingElement = has_unmappable_attribute
|
|
2863
3109
|
? opening_element_node
|
|
@@ -3131,7 +3377,13 @@ function collect_block_binding_names_from_statement(statement, names) {
|
|
|
3131
3377
|
return;
|
|
3132
3378
|
}
|
|
3133
3379
|
|
|
3134
|
-
if (
|
|
3380
|
+
if (
|
|
3381
|
+
statement.type === 'ForOfStatement' ||
|
|
3382
|
+
statement.type === 'ForInStatement' ||
|
|
3383
|
+
(statement.type === 'JSXForExpression' &&
|
|
3384
|
+
(statement.statementType === 'ForOfStatement' ||
|
|
3385
|
+
statement.statementType === 'ForInStatement'))
|
|
3386
|
+
) {
|
|
3135
3387
|
if (statement.left?.type === 'VariableDeclaration' && statement.left.kind === 'var') {
|
|
3136
3388
|
for (const declaration of statement.left.declarations || []) {
|
|
3137
3389
|
collect_pattern_names(declaration.id, names);
|
|
@@ -3292,7 +3544,7 @@ function validate_hook_outer_assignments_in_node(
|
|
|
3292
3544
|
}
|
|
3293
3545
|
}
|
|
3294
3546
|
|
|
3295
|
-
if (node
|
|
3547
|
+
if (is_for_of_control_node(node)) {
|
|
3296
3548
|
if (
|
|
3297
3549
|
node.left &&
|
|
3298
3550
|
node.left.type !== 'VariableDeclaration' &&
|
|
@@ -3725,9 +3977,7 @@ function get_hook_callee_name(callee) {
|
|
|
3725
3977
|
* @param {any} source_node
|
|
3726
3978
|
* @param {TransformContext} transform_context
|
|
3727
3979
|
* @param {AST.Identifier} [preallocated_helper_id] - Optional pre-allocated id.
|
|
3728
|
-
* Used by
|
|
3729
|
-
* source order in a forward pass and then constructs helpers in reverse so
|
|
3730
|
-
* each fall-through case can reference the next case's component element.
|
|
3980
|
+
* Used by switch lifting to keep generated helper ids stable in source order.
|
|
3731
3981
|
* @param {{ transientBindings?: Set<string> }} [options]
|
|
3732
3982
|
* @returns {{ setup_statements: any[], component_element: ESTreeJSX.JSXElement }}
|
|
3733
3983
|
*/
|
|
@@ -4006,6 +4256,118 @@ function get_body_source_node(body_nodes) {
|
|
|
4006
4256
|
return first;
|
|
4007
4257
|
}
|
|
4008
4258
|
|
|
4259
|
+
/**
|
|
4260
|
+
* @param {any} node
|
|
4261
|
+
* @returns {any}
|
|
4262
|
+
*/
|
|
4263
|
+
function jsx_control_expression_to_statement(node) {
|
|
4264
|
+
if (!node?.statementType) return node;
|
|
4265
|
+
return { ...node, type: node.statementType };
|
|
4266
|
+
}
|
|
4267
|
+
|
|
4268
|
+
/**
|
|
4269
|
+
* @param {any} node
|
|
4270
|
+
* @param {TransformContext} transform_context
|
|
4271
|
+
* @returns {any[]}
|
|
4272
|
+
*/
|
|
4273
|
+
function get_jsx_code_block_body_nodes(node, transform_context) {
|
|
4274
|
+
if (!node.render) {
|
|
4275
|
+
return node.body || [];
|
|
4276
|
+
}
|
|
4277
|
+
|
|
4278
|
+
if (is_native_tsrx_node(node.render)) {
|
|
4279
|
+
const style_context = prepare_tsrx_fragment_styles(node.render, transform_context);
|
|
4280
|
+
const render = style_context?.fragment ?? node.render;
|
|
4281
|
+
return [
|
|
4282
|
+
...(node.body || []),
|
|
4283
|
+
...create_tsrx_style_ref_setup_statements(render, style_context, transform_context),
|
|
4284
|
+
render,
|
|
4285
|
+
];
|
|
4286
|
+
}
|
|
4287
|
+
|
|
4288
|
+
return [...(node.body || []), node.render];
|
|
4289
|
+
}
|
|
4290
|
+
|
|
4291
|
+
/**
|
|
4292
|
+
* @param {any} node
|
|
4293
|
+
* @returns {any[]}
|
|
4294
|
+
*/
|
|
4295
|
+
function get_raw_jsx_code_block_body_nodes(node) {
|
|
4296
|
+
return [...(node.body || []), ...(node.render ? [node.render] : [])];
|
|
4297
|
+
}
|
|
4298
|
+
|
|
4299
|
+
/**
|
|
4300
|
+
* @param {any} node
|
|
4301
|
+
* @returns {boolean}
|
|
4302
|
+
*/
|
|
4303
|
+
function is_native_tsrx_node(node) {
|
|
4304
|
+
return (
|
|
4305
|
+
node?.type === 'JSXCodeBlock' ||
|
|
4306
|
+
((node?.type === 'JSXElement' ||
|
|
4307
|
+
node?.type === 'JSXFragment' ||
|
|
4308
|
+
node?.type === 'JSXStyleElement') &&
|
|
4309
|
+
node.metadata?.native_tsrx)
|
|
4310
|
+
);
|
|
4311
|
+
}
|
|
4312
|
+
|
|
4313
|
+
/**
|
|
4314
|
+
* @param {any} node
|
|
4315
|
+
* @returns {boolean}
|
|
4316
|
+
*/
|
|
4317
|
+
function is_dynamic_jsx_element(node) {
|
|
4318
|
+
return !!(
|
|
4319
|
+
node?.type === 'JSXElement' &&
|
|
4320
|
+
(node.dynamic === true ||
|
|
4321
|
+
node.openingElement?.dynamic === true ||
|
|
4322
|
+
is_dynamic_jsx_name(node.openingElement?.name))
|
|
4323
|
+
);
|
|
4324
|
+
}
|
|
4325
|
+
|
|
4326
|
+
/**
|
|
4327
|
+
* @param {any} name
|
|
4328
|
+
* @returns {boolean}
|
|
4329
|
+
*/
|
|
4330
|
+
function is_dynamic_jsx_name(name) {
|
|
4331
|
+
if (!name || typeof name !== 'object') return false;
|
|
4332
|
+
if (name.dynamic === true) return true;
|
|
4333
|
+
return name.type === 'JSXMemberExpression' && is_dynamic_jsx_name(name.object);
|
|
4334
|
+
}
|
|
4335
|
+
|
|
4336
|
+
/**
|
|
4337
|
+
* @param {any} node
|
|
4338
|
+
* @returns {boolean}
|
|
4339
|
+
*/
|
|
4340
|
+
function is_if_control_node(node) {
|
|
4341
|
+
return node?.type === 'IfStatement' || node?.type === 'JSXIfExpression';
|
|
4342
|
+
}
|
|
4343
|
+
|
|
4344
|
+
/**
|
|
4345
|
+
* @param {any} node
|
|
4346
|
+
* @returns {boolean}
|
|
4347
|
+
*/
|
|
4348
|
+
function is_switch_control_node(node) {
|
|
4349
|
+
return node?.type === 'SwitchStatement' || node?.type === 'JSXSwitchExpression';
|
|
4350
|
+
}
|
|
4351
|
+
|
|
4352
|
+
/**
|
|
4353
|
+
* @param {any} node
|
|
4354
|
+
* @returns {boolean}
|
|
4355
|
+
*/
|
|
4356
|
+
function is_try_control_node(node) {
|
|
4357
|
+
return node?.type === 'TryStatement' || node?.type === 'JSXTryExpression';
|
|
4358
|
+
}
|
|
4359
|
+
|
|
4360
|
+
/**
|
|
4361
|
+
* @param {any} node
|
|
4362
|
+
* @returns {boolean}
|
|
4363
|
+
*/
|
|
4364
|
+
function is_for_of_control_node(node) {
|
|
4365
|
+
return (
|
|
4366
|
+
node?.type === 'ForOfStatement' ||
|
|
4367
|
+
(node?.type === 'JSXForExpression' && node.statementType === 'ForOfStatement')
|
|
4368
|
+
);
|
|
4369
|
+
}
|
|
4370
|
+
|
|
4009
4371
|
/**
|
|
4010
4372
|
* @param {any} node
|
|
4011
4373
|
* @param {TransformContext} transform_context
|
|
@@ -4014,37 +4376,56 @@ function get_body_source_node(body_nodes) {
|
|
|
4014
4376
|
function to_jsx_child(node, transform_context) {
|
|
4015
4377
|
if (!node) return node;
|
|
4016
4378
|
switch (node.type) {
|
|
4017
|
-
case '
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
return
|
|
4025
|
-
case '
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4379
|
+
case 'JSXElement':
|
|
4380
|
+
if (is_native_tsrx_node(node)) {
|
|
4381
|
+
return to_jsx_element(node, transform_context, node.children || [], true);
|
|
4382
|
+
}
|
|
4383
|
+
if (is_dynamic_jsx_element(node)) {
|
|
4384
|
+
return dynamic_element_to_jsx(node, transform_context, true);
|
|
4385
|
+
}
|
|
4386
|
+
return node;
|
|
4387
|
+
case 'JSXFragment':
|
|
4388
|
+
if (is_native_tsrx_node(node)) {
|
|
4389
|
+
return tsrx_node_to_jsx_expression(node, transform_context, true);
|
|
4390
|
+
}
|
|
4391
|
+
return node;
|
|
4392
|
+
case 'JSXIfExpression':
|
|
4031
4393
|
case 'IfStatement':
|
|
4394
|
+
if (node.metadata?.generated_loop_skip_if) {
|
|
4395
|
+
return node;
|
|
4396
|
+
}
|
|
4032
4397
|
return (
|
|
4033
4398
|
transform_context.platform.hooks?.controlFlow?.ifStatement ?? if_statement_to_jsx_child
|
|
4034
|
-
)(node, transform_context);
|
|
4399
|
+
)(jsx_control_expression_to_statement(node), transform_context);
|
|
4400
|
+
case 'JSXForExpression':
|
|
4401
|
+
if (node.statementType !== 'ForOfStatement') {
|
|
4402
|
+
error(
|
|
4403
|
+
'TSRX `@for` currently supports `for...of` loops in template output.',
|
|
4404
|
+
transform_context.filename,
|
|
4405
|
+
node,
|
|
4406
|
+
transform_context.errors,
|
|
4407
|
+
transform_context.comments,
|
|
4408
|
+
);
|
|
4409
|
+
return to_jsx_expression_container(create_null_literal(), node);
|
|
4410
|
+
}
|
|
4411
|
+
return (
|
|
4412
|
+
transform_context.platform.hooks?.controlFlow?.forOf ?? for_of_statement_to_jsx_child
|
|
4413
|
+
)(jsx_control_expression_to_statement(node), transform_context);
|
|
4035
4414
|
case 'ForOfStatement':
|
|
4036
4415
|
return (
|
|
4037
4416
|
transform_context.platform.hooks?.controlFlow?.forOf ?? for_of_statement_to_jsx_child
|
|
4038
4417
|
)(node, transform_context);
|
|
4418
|
+
case 'JSXSwitchExpression':
|
|
4039
4419
|
case 'SwitchStatement':
|
|
4040
4420
|
return (
|
|
4041
4421
|
transform_context.platform.hooks?.controlFlow?.switchStatement ??
|
|
4042
4422
|
switch_statement_to_jsx_child
|
|
4043
|
-
)(node, transform_context);
|
|
4423
|
+
)(jsx_control_expression_to_statement(node), transform_context);
|
|
4424
|
+
case 'JSXTryExpression':
|
|
4044
4425
|
case 'TryStatement':
|
|
4045
4426
|
return (
|
|
4046
4427
|
transform_context.platform.hooks?.controlFlow?.tryStatement ?? try_statement_to_jsx_child
|
|
4047
|
-
)(node, transform_context);
|
|
4428
|
+
)(jsx_control_expression_to_statement(node), transform_context);
|
|
4048
4429
|
default:
|
|
4049
4430
|
return node;
|
|
4050
4431
|
}
|
|
@@ -4052,8 +4433,8 @@ function to_jsx_child(node, transform_context) {
|
|
|
4052
4433
|
|
|
4053
4434
|
/**
|
|
4054
4435
|
* Lower a native TSRX fragment body to a JSX expression.
|
|
4055
|
-
*
|
|
4056
|
-
*
|
|
4436
|
+
* Children have already been parsed and transformed through the normal TSRX
|
|
4437
|
+
* JSX element/text/control-flow visitors.
|
|
4057
4438
|
*
|
|
4058
4439
|
* @param {any} node
|
|
4059
4440
|
* @param {TransformContext} transform_context
|
|
@@ -4136,7 +4517,7 @@ function return_value_statement_to_expression(node, transform_context) {
|
|
|
4136
4517
|
return node.argument;
|
|
4137
4518
|
}
|
|
4138
4519
|
|
|
4139
|
-
if (node
|
|
4520
|
+
if (is_if_control_node(node)) {
|
|
4140
4521
|
return return_value_if_statement_to_conditional_expression(node, transform_context);
|
|
4141
4522
|
}
|
|
4142
4523
|
|
|
@@ -4230,7 +4611,7 @@ function return_value_block_to_expression(node, transform_context) {
|
|
|
4230
4611
|
* @returns {any | null}
|
|
4231
4612
|
*/
|
|
4232
4613
|
function return_value_if_statement_to_conditional_expression(node, transform_context) {
|
|
4233
|
-
if (!node
|
|
4614
|
+
if (!is_if_control_node(node)) return null;
|
|
4234
4615
|
|
|
4235
4616
|
const consequent = return_value_block_to_expression(node.consequent, transform_context);
|
|
4236
4617
|
if (!consequent) return null;
|
|
@@ -4270,14 +4651,14 @@ function if_statement_to_jsx_child(node, transform_context) {
|
|
|
4270
4651
|
* @returns {any | null}
|
|
4271
4652
|
*/
|
|
4272
4653
|
function render_if_statement_to_conditional_expression(node) {
|
|
4273
|
-
if (!node
|
|
4654
|
+
if (!is_if_control_node(node)) return null;
|
|
4274
4655
|
|
|
4275
4656
|
const consequent = block_statement_to_return_expression(node.consequent);
|
|
4276
4657
|
if (!consequent) return null;
|
|
4277
4658
|
|
|
4278
4659
|
let alternate = create_null_literal();
|
|
4279
4660
|
if (node.alternate) {
|
|
4280
|
-
if (node.alternate
|
|
4661
|
+
if (is_if_control_node(node.alternate)) {
|
|
4281
4662
|
alternate = render_if_statement_to_conditional_expression(node.alternate);
|
|
4282
4663
|
if (!alternate) return null;
|
|
4283
4664
|
} else {
|
|
@@ -4314,25 +4695,11 @@ function block_statement_to_return_expression(block) {
|
|
|
4314
4695
|
/**
|
|
4315
4696
|
* Find the first `key` attribute expression in the top-level elements of a body.
|
|
4316
4697
|
* Used to propagate keys from loop body elements to wrapper components.
|
|
4317
|
-
* Works on both pre-transform (Ripple Element) and post-transform (JSXElement) nodes.
|
|
4318
|
-
*
|
|
4319
4698
|
* @param {any[]} body_nodes
|
|
4320
4699
|
* @returns {any | undefined}
|
|
4321
4700
|
*/
|
|
4322
4701
|
function find_key_expression_in_body(body_nodes) {
|
|
4323
4702
|
for (const node of body_nodes) {
|
|
4324
|
-
// Pre-transform: Ripple Element node
|
|
4325
|
-
if (node.type === 'Element') {
|
|
4326
|
-
for (const attr of node.attributes || []) {
|
|
4327
|
-
if (attr.type === 'Attribute') {
|
|
4328
|
-
const attr_name = typeof attr.name === 'string' ? attr.name : attr.name?.name;
|
|
4329
|
-
if (attr_name === 'key') {
|
|
4330
|
-
return attr.value?.expression ?? attr.value;
|
|
4331
|
-
}
|
|
4332
|
-
}
|
|
4333
|
-
}
|
|
4334
|
-
}
|
|
4335
|
-
// Post-transform: JSXElement node
|
|
4336
4703
|
if (node.type === 'JSXElement') {
|
|
4337
4704
|
for (const attr of node.openingElement?.attributes || []) {
|
|
4338
4705
|
if (
|
|
@@ -4357,7 +4724,7 @@ function find_key_expression_in_body(body_nodes) {
|
|
|
4357
4724
|
* @returns {any}
|
|
4358
4725
|
*/
|
|
4359
4726
|
function continue_to_bare_return(source_node) {
|
|
4360
|
-
const node = set_loc(b.return(
|
|
4727
|
+
const node = set_loc(b.return(create_null_literal()), source_node);
|
|
4361
4728
|
node.metadata = {
|
|
4362
4729
|
...(node.metadata || {}),
|
|
4363
4730
|
generated_loop_continue_return: true,
|
|
@@ -4367,8 +4734,9 @@ function continue_to_bare_return(source_node) {
|
|
|
4367
4734
|
|
|
4368
4735
|
/**
|
|
4369
4736
|
* `continue` in a component `for...of` body means "skip this item". JSX targets
|
|
4370
|
-
* lower `for...of` to callbacks, so a raw ContinueStatement would be invalid JS
|
|
4371
|
-
*
|
|
4737
|
+
* lower `for...of` to callbacks, so a raw ContinueStatement would be invalid JS.
|
|
4738
|
+
* Returning null from the callback preserves the item-skip behavior while still
|
|
4739
|
+
* producing an explicit "render nothing" value for JSX runtimes.
|
|
4372
4740
|
*
|
|
4373
4741
|
* @param {any[] | any} node
|
|
4374
4742
|
* @param {boolean} [is_root]
|
|
@@ -4376,7 +4744,9 @@ function continue_to_bare_return(source_node) {
|
|
|
4376
4744
|
*/
|
|
4377
4745
|
export function rewrite_loop_continues_to_bare_returns(node, is_root = true) {
|
|
4378
4746
|
if (Array.isArray(node)) {
|
|
4379
|
-
return node.map((child) =>
|
|
4747
|
+
return node.map((child) =>
|
|
4748
|
+
rewrite_loop_continues_to_bare_returns(child, is_root && !is_loop_statement(child)),
|
|
4749
|
+
);
|
|
4380
4750
|
}
|
|
4381
4751
|
|
|
4382
4752
|
if (!node || typeof node !== 'object') {
|
|
@@ -4401,6 +4771,145 @@ export function rewrite_loop_continues_to_bare_returns(node, is_root = true) {
|
|
|
4401
4771
|
return node;
|
|
4402
4772
|
}
|
|
4403
4773
|
|
|
4774
|
+
/**
|
|
4775
|
+
* @param {any[] | any} node
|
|
4776
|
+
* @param {TransformContext} transform_context
|
|
4777
|
+
* @param {boolean} [is_root]
|
|
4778
|
+
*/
|
|
4779
|
+
function validate_for_body_control_flow(node, transform_context, is_root = true) {
|
|
4780
|
+
if (Array.isArray(node)) {
|
|
4781
|
+
for (const child of node) {
|
|
4782
|
+
validate_for_body_control_flow(
|
|
4783
|
+
child,
|
|
4784
|
+
transform_context,
|
|
4785
|
+
is_root && !is_loop_statement(child),
|
|
4786
|
+
);
|
|
4787
|
+
}
|
|
4788
|
+
return;
|
|
4789
|
+
}
|
|
4790
|
+
|
|
4791
|
+
if (!node || typeof node !== 'object') {
|
|
4792
|
+
return;
|
|
4793
|
+
}
|
|
4794
|
+
|
|
4795
|
+
if (is_template_if_node(node)) {
|
|
4796
|
+
return;
|
|
4797
|
+
}
|
|
4798
|
+
|
|
4799
|
+
if (node.type === 'ReturnStatement') {
|
|
4800
|
+
error(
|
|
4801
|
+
TSRX_FOR_RETURN_ERROR,
|
|
4802
|
+
transform_context.filename,
|
|
4803
|
+
node,
|
|
4804
|
+
transform_context.errors,
|
|
4805
|
+
transform_context.comments,
|
|
4806
|
+
);
|
|
4807
|
+
return;
|
|
4808
|
+
}
|
|
4809
|
+
if (node.type === 'BreakStatement') {
|
|
4810
|
+
error(
|
|
4811
|
+
TSRX_FOR_BREAK_ERROR,
|
|
4812
|
+
transform_context.filename,
|
|
4813
|
+
node,
|
|
4814
|
+
transform_context.errors,
|
|
4815
|
+
transform_context.comments,
|
|
4816
|
+
);
|
|
4817
|
+
return;
|
|
4818
|
+
}
|
|
4819
|
+
if (node.type === 'ContinueStatement') {
|
|
4820
|
+
error(
|
|
4821
|
+
TSRX_FOR_CONTINUE_ERROR,
|
|
4822
|
+
transform_context.filename,
|
|
4823
|
+
node,
|
|
4824
|
+
transform_context.errors,
|
|
4825
|
+
transform_context.comments,
|
|
4826
|
+
);
|
|
4827
|
+
return;
|
|
4828
|
+
}
|
|
4829
|
+
|
|
4830
|
+
if (is_function_or_class_boundary(node) || (!is_root && is_loop_statement(node))) {
|
|
4831
|
+
return;
|
|
4832
|
+
}
|
|
4833
|
+
|
|
4834
|
+
for (const key of Object.keys(node)) {
|
|
4835
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
4836
|
+
continue;
|
|
4837
|
+
}
|
|
4838
|
+
validate_for_body_control_flow(node[key], transform_context, false);
|
|
4839
|
+
}
|
|
4840
|
+
}
|
|
4841
|
+
|
|
4842
|
+
/**
|
|
4843
|
+
* @param {any[] | any} node
|
|
4844
|
+
* @param {TransformContext} transform_context
|
|
4845
|
+
*/
|
|
4846
|
+
function validate_if_body_control_flow(node, transform_context) {
|
|
4847
|
+
if (Array.isArray(node)) {
|
|
4848
|
+
for (const child of node) {
|
|
4849
|
+
validate_if_body_control_flow(child, transform_context);
|
|
4850
|
+
}
|
|
4851
|
+
return;
|
|
4852
|
+
}
|
|
4853
|
+
|
|
4854
|
+
if (!node || typeof node !== 'object') {
|
|
4855
|
+
return;
|
|
4856
|
+
}
|
|
4857
|
+
|
|
4858
|
+
if (node.type === 'ReturnStatement') {
|
|
4859
|
+
error(
|
|
4860
|
+
TSRX_IF_RETURN_ERROR,
|
|
4861
|
+
transform_context.filename,
|
|
4862
|
+
node,
|
|
4863
|
+
transform_context.errors,
|
|
4864
|
+
transform_context.comments,
|
|
4865
|
+
);
|
|
4866
|
+
return;
|
|
4867
|
+
}
|
|
4868
|
+
if (node.type === 'BreakStatement') {
|
|
4869
|
+
error(
|
|
4870
|
+
TSRX_IF_BREAK_ERROR,
|
|
4871
|
+
transform_context.filename,
|
|
4872
|
+
node,
|
|
4873
|
+
transform_context.errors,
|
|
4874
|
+
transform_context.comments,
|
|
4875
|
+
);
|
|
4876
|
+
return;
|
|
4877
|
+
}
|
|
4878
|
+
if (node.type === 'ContinueStatement') {
|
|
4879
|
+
error(
|
|
4880
|
+
TSRX_IF_CONTINUE_ERROR,
|
|
4881
|
+
transform_context.filename,
|
|
4882
|
+
node,
|
|
4883
|
+
transform_context.errors,
|
|
4884
|
+
transform_context.comments,
|
|
4885
|
+
);
|
|
4886
|
+
return;
|
|
4887
|
+
}
|
|
4888
|
+
|
|
4889
|
+
if (is_function_or_class_boundary(node)) {
|
|
4890
|
+
return;
|
|
4891
|
+
}
|
|
4892
|
+
|
|
4893
|
+
for (const key of Object.keys(node)) {
|
|
4894
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
4895
|
+
continue;
|
|
4896
|
+
}
|
|
4897
|
+
validate_if_body_control_flow(node[key], transform_context);
|
|
4898
|
+
}
|
|
4899
|
+
}
|
|
4900
|
+
|
|
4901
|
+
/**
|
|
4902
|
+
* @param {any} node
|
|
4903
|
+
* @returns {boolean}
|
|
4904
|
+
*/
|
|
4905
|
+
function is_template_if_node(node) {
|
|
4906
|
+
return (
|
|
4907
|
+
node?.type === 'JSXIfExpression' ||
|
|
4908
|
+
node?.metadata?.tsrxDirective === 'if' ||
|
|
4909
|
+
(node?.type === 'IfStatement' && node?.statementType === 'IfStatement')
|
|
4910
|
+
);
|
|
4911
|
+
}
|
|
4912
|
+
|
|
4404
4913
|
/**
|
|
4405
4914
|
* @param {any} node
|
|
4406
4915
|
* @returns {boolean}
|
|
@@ -4408,8 +4917,11 @@ export function rewrite_loop_continues_to_bare_returns(node, is_root = true) {
|
|
|
4408
4917
|
function is_loop_statement(node) {
|
|
4409
4918
|
return (
|
|
4410
4919
|
node?.type === 'ForOfStatement' ||
|
|
4920
|
+
(node?.type === 'JSXForExpression' && node.statementType === 'ForOfStatement') ||
|
|
4411
4921
|
node?.type === 'ForStatement' ||
|
|
4922
|
+
(node?.type === 'JSXForExpression' && node.statementType === 'ForStatement') ||
|
|
4412
4923
|
node?.type === 'ForInStatement' ||
|
|
4924
|
+
(node?.type === 'JSXForExpression' && node.statementType === 'ForInStatement') ||
|
|
4413
4925
|
node?.type === 'WhileStatement' ||
|
|
4414
4926
|
node?.type === 'DoWhileStatement'
|
|
4415
4927
|
);
|
|
@@ -4433,10 +4945,9 @@ function for_of_statement_to_jsx_child(node, transform_context) {
|
|
|
4433
4945
|
|
|
4434
4946
|
const loop_params = get_for_of_iteration_params(node.left, node.index);
|
|
4435
4947
|
const loop_body = /** @type {any[]} */ (
|
|
4436
|
-
|
|
4437
|
-
node.body.type === 'BlockStatement' ? node.body.body : [node.body],
|
|
4438
|
-
)
|
|
4948
|
+
node.body.type === 'BlockStatement' ? node.body.body : [node.body]
|
|
4439
4949
|
);
|
|
4950
|
+
validate_for_body_control_flow(loop_body, transform_context);
|
|
4440
4951
|
const has_hooks =
|
|
4441
4952
|
should_extract_hook_helpers(transform_context) &&
|
|
4442
4953
|
body_contains_top_level_hook_call(loop_body, transform_context, true);
|
|
@@ -4491,17 +5002,49 @@ function for_of_statement_to_jsx_child(node, transform_context) {
|
|
|
4491
5002
|
transform_context.available_bindings = saved_bindings;
|
|
4492
5003
|
|
|
4493
5004
|
const iter_callback = b.arrow(loop_params, b.block(body_statements));
|
|
5005
|
+
const empty_fallback = node.empty
|
|
5006
|
+
? b.call(
|
|
5007
|
+
b.arrow(
|
|
5008
|
+
[],
|
|
5009
|
+
b.block(
|
|
5010
|
+
build_render_statements(
|
|
5011
|
+
node.empty.type === 'BlockStatement' ? node.empty.body : [node.empty],
|
|
5012
|
+
true,
|
|
5013
|
+
transform_context,
|
|
5014
|
+
),
|
|
5015
|
+
),
|
|
5016
|
+
false,
|
|
5017
|
+
undefined,
|
|
5018
|
+
node.empty,
|
|
5019
|
+
),
|
|
5020
|
+
)
|
|
5021
|
+
: null;
|
|
4494
5022
|
|
|
4495
5023
|
if (transform_context.platform.imports.forOfIterableHelper) {
|
|
4496
5024
|
transform_context.needs_for_of_iterable = true;
|
|
5025
|
+
const args = [node.right, iter_callback];
|
|
5026
|
+
if (empty_fallback) {
|
|
5027
|
+
args.push(b.literal(null), b.arrow([], empty_fallback));
|
|
5028
|
+
}
|
|
5029
|
+
return to_jsx_expression_container(b.call(b.id(MAP_ITERABLE_INTERNAL_NAME), ...args));
|
|
5030
|
+
}
|
|
5031
|
+
|
|
5032
|
+
const map_call = b.call(b.member(node.right, create_generated_identifier('map')), iter_callback);
|
|
5033
|
+
if (empty_fallback) {
|
|
4497
5034
|
return to_jsx_expression_container(
|
|
4498
|
-
b.
|
|
5035
|
+
b.conditional(
|
|
5036
|
+
b.binary(
|
|
5037
|
+
'===',
|
|
5038
|
+
b.member(clone_expression_node(node.right), create_generated_identifier('length')),
|
|
5039
|
+
b.literal(0),
|
|
5040
|
+
),
|
|
5041
|
+
empty_fallback,
|
|
5042
|
+
map_call,
|
|
5043
|
+
),
|
|
4499
5044
|
);
|
|
4500
5045
|
}
|
|
4501
5046
|
|
|
4502
|
-
return to_jsx_expression_container(
|
|
4503
|
-
b.call(b.member(node.right, create_generated_identifier('map')), iter_callback),
|
|
4504
|
-
);
|
|
5047
|
+
return to_jsx_expression_container(map_call);
|
|
4505
5048
|
}
|
|
4506
5049
|
|
|
4507
5050
|
/**
|
|
@@ -4511,25 +5054,6 @@ function for_of_statement_to_jsx_child(node, transform_context) {
|
|
|
4511
5054
|
*/
|
|
4512
5055
|
function apply_key_to_loop_body(body_nodes, key_expression) {
|
|
4513
5056
|
for (const node of body_nodes) {
|
|
4514
|
-
if (node.type === 'Element') {
|
|
4515
|
-
const attributes = node.attributes || (node.attributes = []);
|
|
4516
|
-
const has_key = attributes.some((/** @type {any} */ attr) => {
|
|
4517
|
-
const attr_name = typeof attr.name === 'string' ? attr.name : attr.name?.name;
|
|
4518
|
-
return attr_name === 'key';
|
|
4519
|
-
});
|
|
4520
|
-
|
|
4521
|
-
if (!has_key) {
|
|
4522
|
-
attributes.push({
|
|
4523
|
-
type: 'Attribute',
|
|
4524
|
-
name: b.id('key'),
|
|
4525
|
-
value: clone_expression_node(key_expression),
|
|
4526
|
-
shorthand: false,
|
|
4527
|
-
metadata: { path: [] },
|
|
4528
|
-
});
|
|
4529
|
-
}
|
|
4530
|
-
return;
|
|
4531
|
-
}
|
|
4532
|
-
|
|
4533
5057
|
if (node.type === 'JSXElement') {
|
|
4534
5058
|
const attributes = node.openingElement?.attributes || [];
|
|
4535
5059
|
const has_key = attributes.some(
|
|
@@ -4559,7 +5083,7 @@ function apply_key_to_loop_body(body_nodes, key_expression) {
|
|
|
4559
5083
|
function should_apply_key_to_loop_body(body_nodes) {
|
|
4560
5084
|
let keyable_children = 0;
|
|
4561
5085
|
for (const node of body_nodes) {
|
|
4562
|
-
if (node.type === '
|
|
5086
|
+
if (node.type === 'JSXElement') {
|
|
4563
5087
|
keyable_children += 1;
|
|
4564
5088
|
}
|
|
4565
5089
|
}
|
|
@@ -4649,11 +5173,11 @@ function switch_statement_to_jsx_child(node, transform_context) {
|
|
|
4649
5173
|
}
|
|
4650
5174
|
|
|
4651
5175
|
/**
|
|
4652
|
-
* Transform
|
|
5176
|
+
* Transform an `@try { ... } @pending { ... } @catch (err, reset) { ... }` block
|
|
4653
5177
|
* into React `<TsrxErrorBoundary>` and/or `<Suspense>` JSX elements.
|
|
4654
5178
|
*
|
|
4655
|
-
* -
|
|
4656
|
-
* -
|
|
5179
|
+
* - `@pending` → `<Suspense fallback={...}>`
|
|
5180
|
+
* - `@catch` → `<TsrxErrorBoundary fallback={(err, reset) => ...}>`
|
|
4657
5181
|
* - both → ErrorBoundary wraps Suspense
|
|
4658
5182
|
* - JavaScript `try/finally` is not part of component template control flow
|
|
4659
5183
|
*
|
|
@@ -4697,30 +5221,6 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
4697
5221
|
);
|
|
4698
5222
|
}
|
|
4699
5223
|
|
|
4700
|
-
// Validate that try body contains JSX if pending block is present
|
|
4701
|
-
if (pending) {
|
|
4702
|
-
const try_body = node.block.body || [];
|
|
4703
|
-
if (!try_body.some(is_jsx_child)) {
|
|
4704
|
-
error(
|
|
4705
|
-
'TSRX try statements must contain a template in their main body. Move the try statement into a function if it does not render anything.',
|
|
4706
|
-
transform_context.filename,
|
|
4707
|
-
node.block,
|
|
4708
|
-
transform_context.errors,
|
|
4709
|
-
transform_context.comments,
|
|
4710
|
-
);
|
|
4711
|
-
}
|
|
4712
|
-
const pending_body = pending.body || [];
|
|
4713
|
-
if (pending_body.length > 0 && !pending_body.some(is_jsx_child)) {
|
|
4714
|
-
error(
|
|
4715
|
-
'TSRX try statements must contain a template in their "pending" body. Rendering a pending fallback is required to have a template.',
|
|
4716
|
-
transform_context.filename,
|
|
4717
|
-
pending,
|
|
4718
|
-
transform_context.errors,
|
|
4719
|
-
transform_context.comments,
|
|
4720
|
-
);
|
|
4721
|
-
}
|
|
4722
|
-
}
|
|
4723
|
-
|
|
4724
5224
|
// Build the try body content as JSX children
|
|
4725
5225
|
const try_body_nodes = node.block.body || [];
|
|
4726
5226
|
const try_content = statement_body_to_jsx_child(try_body_nodes, transform_context);
|
|
@@ -4873,8 +5373,8 @@ function create_jsx_element(tag_name, attributes, children) {
|
|
|
4873
5373
|
|
|
4874
5374
|
/**
|
|
4875
5375
|
* Inject runtime-helper import declarations the transform decided it needed
|
|
4876
|
-
* during the walk: `Suspense` for
|
|
4877
|
-
* `TsrxErrorBoundary` for
|
|
5376
|
+
* during the walk: `Suspense` for `@try { ... } @pending { ... }`,
|
|
5377
|
+
* `TsrxErrorBoundary` for `@try { ... } @catch (...)`, and `mergeRefs` for
|
|
4878
5378
|
* elements with multiple `ref` attributes under the `'merge-refs'`
|
|
4879
5379
|
* strategy. Import sources are platform-specific.
|
|
4880
5380
|
*
|
|
@@ -4988,16 +5488,22 @@ function add_ref_import_specifier(imports, source, specifier) {
|
|
|
4988
5488
|
function create_render_if_statement(node, transform_context) {
|
|
4989
5489
|
const consequent_body =
|
|
4990
5490
|
node.consequent.type === 'BlockStatement' ? node.consequent.body : [node.consequent];
|
|
5491
|
+
if (is_template_if_node(node)) {
|
|
5492
|
+
validate_if_body_control_flow(consequent_body, transform_context);
|
|
5493
|
+
}
|
|
4991
5494
|
const consequent_has_hooks =
|
|
4992
5495
|
should_extract_hook_helpers(transform_context) &&
|
|
4993
5496
|
body_contains_top_level_hook_call(consequent_body, transform_context, true);
|
|
4994
5497
|
|
|
4995
5498
|
let alternate = null;
|
|
4996
5499
|
if (node.alternate) {
|
|
4997
|
-
if (node.alternate
|
|
5500
|
+
if (is_if_control_node(node.alternate)) {
|
|
4998
5501
|
alternate = create_render_if_statement(node.alternate, transform_context);
|
|
4999
5502
|
} else {
|
|
5000
5503
|
const alternate_body = node.alternate.body || [node.alternate];
|
|
5504
|
+
if (is_template_if_node(node)) {
|
|
5505
|
+
validate_if_body_control_flow(alternate_body, transform_context);
|
|
5506
|
+
}
|
|
5001
5507
|
const alternate_has_hooks =
|
|
5002
5508
|
should_extract_hook_helpers(transform_context) &&
|
|
5003
5509
|
body_contains_top_level_hook_call(alternate_body, transform_context, true);
|
|
@@ -5034,9 +5540,8 @@ function create_render_if_statement(node, transform_context) {
|
|
|
5034
5540
|
* case body needs to be hoisted into its own helper component or can stay
|
|
5035
5541
|
* inline.
|
|
5036
5542
|
*
|
|
5037
|
-
* `own_body` is
|
|
5038
|
-
*
|
|
5039
|
-
* terminator. `has_terminator` records whether such a terminator was seen.
|
|
5543
|
+
* `own_body` is the case's isolated consequent. JSX `@switch` cases do not
|
|
5544
|
+
* fall through, so `break` is not part of the template switch model.
|
|
5040
5545
|
*
|
|
5041
5546
|
* @param {any[]} consequent
|
|
5042
5547
|
* @returns {{ own_body: any[], has_terminator: boolean }}
|
|
@@ -5045,10 +5550,6 @@ function summarize_switch_case_body(consequent) {
|
|
|
5045
5550
|
const own_body = [];
|
|
5046
5551
|
let has_terminator = false;
|
|
5047
5552
|
for (const child of consequent) {
|
|
5048
|
-
if (child.type === 'BreakStatement') {
|
|
5049
|
-
has_terminator = true;
|
|
5050
|
-
break;
|
|
5051
|
-
}
|
|
5052
5553
|
if (child.type === 'ReturnStatement' && child.argument == null) {
|
|
5053
5554
|
has_terminator = true;
|
|
5054
5555
|
break;
|
|
@@ -5081,11 +5582,10 @@ export function clone_switch_helper_invocation(helper) {
|
|
|
5081
5582
|
|
|
5082
5583
|
/**
|
|
5083
5584
|
* Plan the switch lift: decide which case bodies to hoist into their own
|
|
5084
|
-
* helper components
|
|
5085
|
-
*
|
|
5086
|
-
*
|
|
5087
|
-
*
|
|
5088
|
-
* hook-detection rules, duplication analysis, and helper-id numbering.
|
|
5585
|
+
* helper components and return everything callers need to construct a
|
|
5586
|
+
* target-specific switch shape (a JS `switch` for React/Preact/Vue or
|
|
5587
|
+
* `<Switch>/<Match>` for Solid). JSX `@switch` cases are isolated and do not
|
|
5588
|
+
* fall through.
|
|
5089
5589
|
*
|
|
5090
5590
|
* Returned helpers — when non-null — are already constructed via
|
|
5091
5591
|
* `create_hook_safe_helper`, which is the same path hook-bearing case bodies
|
|
@@ -5099,7 +5599,6 @@ export function clone_switch_helper_invocation(helper) {
|
|
|
5099
5599
|
* @returns {{
|
|
5100
5600
|
* case_info: Array<{ own_body: any[], has_terminator: boolean }>,
|
|
5101
5601
|
* case_helpers: Array<{ setup_statements: any[], component_element: ESTreeJSX.JSXElement } | null>,
|
|
5102
|
-
* find_next_helper_after: (from_index: number) => { component_element: ESTreeJSX.JSXElement } | null,
|
|
5103
5602
|
* setup_statements: any[],
|
|
5104
5603
|
* }}
|
|
5105
5604
|
*/
|
|
@@ -5109,22 +5608,15 @@ export function plan_switch_lift(switch_node, transform_context) {
|
|
|
5109
5608
|
return summarize_switch_case_body(consequent);
|
|
5110
5609
|
});
|
|
5111
5610
|
|
|
5112
|
-
// A case body needs to be lifted iff
|
|
5113
|
-
//
|
|
5114
|
-
// went through the lift pipeline before this change). Duplication happens
|
|
5115
|
-
// exactly when the previous case has no terminator — that's the only way
|
|
5116
|
-
// an earlier arm can reach this body via JS fall-through semantics.
|
|
5611
|
+
// A case body needs to be lifted iff it contains hooks. Cases are isolated,
|
|
5612
|
+
// so downstream case bodies are never duplicated into earlier arms.
|
|
5117
5613
|
const needs_helper = case_info.map(
|
|
5118
|
-
(/** @type {{ own_body: any[], has_terminator: boolean }} */ info
|
|
5614
|
+
(/** @type {{ own_body: any[], has_terminator: boolean }} */ info) => {
|
|
5119
5615
|
if (info.own_body.length === 0) return false;
|
|
5120
|
-
|
|
5616
|
+
return (
|
|
5121
5617
|
should_extract_hook_helpers(transform_context) &&
|
|
5122
5618
|
body_contains_top_level_hook_call(info.own_body, transform_context, true)
|
|
5123
|
-
)
|
|
5124
|
-
return true;
|
|
5125
|
-
}
|
|
5126
|
-
if (k === 0) return false;
|
|
5127
|
-
return !case_info[k - 1].has_terminator;
|
|
5619
|
+
);
|
|
5128
5620
|
},
|
|
5129
5621
|
);
|
|
5130
5622
|
|
|
@@ -5141,37 +5633,12 @@ export function plan_switch_lift(switch_node, transform_context) {
|
|
|
5141
5633
|
/** @type {Array<{ setup_statements: any[], component_element: ESTreeJSX.JSXElement } | null>} */
|
|
5142
5634
|
const case_helpers = new Array(switch_node.cases.length).fill(null);
|
|
5143
5635
|
|
|
5144
|
-
/**
|
|
5145
|
-
* Find the next downstream helper this arm chains into when it has no
|
|
5146
|
-
* terminator: scan forward past any empty cases until we hit either a
|
|
5147
|
-
* helper-bearing case or a case whose body has a terminator (which stops
|
|
5148
|
-
* the chain — JS would have `break`/`return`ed out at that point).
|
|
5149
|
-
*
|
|
5150
|
-
* @param {number} from_index
|
|
5151
|
-
* @returns {{ component_element: ESTreeJSX.JSXElement } | null}
|
|
5152
|
-
*/
|
|
5153
|
-
function find_next_helper_after(from_index) {
|
|
5154
|
-
for (let j = from_index + 1; j < switch_node.cases.length; j++) {
|
|
5155
|
-
if (case_helpers[j]) return case_helpers[j];
|
|
5156
|
-
if (case_info[j].has_terminator) return null;
|
|
5157
|
-
}
|
|
5158
|
-
return null;
|
|
5159
|
-
}
|
|
5160
|
-
|
|
5161
5636
|
for (let i = switch_node.cases.length - 1; i >= 0; i--) {
|
|
5162
5637
|
if (!needs_helper[i]) continue;
|
|
5163
|
-
const { own_body
|
|
5164
|
-
|
|
5165
|
-
let helper_body = own_body;
|
|
5166
|
-
if (!has_terminator) {
|
|
5167
|
-
const next_helper = find_next_helper_after(i);
|
|
5168
|
-
if (next_helper) {
|
|
5169
|
-
helper_body = [...own_body, clone_switch_helper_invocation(next_helper)];
|
|
5170
|
-
}
|
|
5171
|
-
}
|
|
5638
|
+
const { own_body } = case_info[i];
|
|
5172
5639
|
|
|
5173
5640
|
case_helpers[i] = create_hook_safe_helper(
|
|
5174
|
-
|
|
5641
|
+
own_body,
|
|
5175
5642
|
undefined,
|
|
5176
5643
|
switch_node.cases[i],
|
|
5177
5644
|
transform_context,
|
|
@@ -5189,40 +5656,17 @@ export function plan_switch_lift(switch_node, transform_context) {
|
|
|
5189
5656
|
return {
|
|
5190
5657
|
case_info,
|
|
5191
5658
|
case_helpers,
|
|
5192
|
-
find_next_helper_after,
|
|
5193
5659
|
setup_statements,
|
|
5194
5660
|
};
|
|
5195
5661
|
}
|
|
5196
5662
|
|
|
5197
5663
|
/**
|
|
5198
|
-
* Switch lift for fall-through deduplication. Reuses the same `create_hook_safe_helper`
|
|
5199
|
-
* pipeline as hook-bearing case bodies: every case whose body would otherwise
|
|
5200
|
-
* appear in 2+ arms (because the previous case had no `break` / `return`) is
|
|
5201
|
-
* hoisted into its own helper component, and each upstream arm references the
|
|
5202
|
-
* next helper at the end of its own body to materialize JS fall-through at
|
|
5203
|
-
* render time. Cases whose bodies live in exactly one arm stay inline so the
|
|
5204
|
-
* common (break-terminated) shape compiles to the same simple switch as before
|
|
5205
|
-
* the lift was introduced.
|
|
5206
|
-
*
|
|
5207
|
-
* The chain pattern:
|
|
5208
|
-
* helper_idle = () => <><Online/><Helper_active/></>
|
|
5209
|
-
* helper_active = () => <><Away/><Helper_offline/></>
|
|
5210
|
-
* helper_offline = () => <Offline/>
|
|
5211
|
-
*
|
|
5212
|
-
* case "idle": return <Helper_idle/>
|
|
5213
|
-
* case "active": return <Helper_active/>
|
|
5214
|
-
* case "offline": return <Helper_offline/>
|
|
5215
|
-
*
|
|
5216
|
-
* Each case body appears exactly once in the generated module — matching how
|
|
5217
|
-
* we already handle hook-bearing case bodies — which keeps the bundle from
|
|
5218
|
-
* growing quadratically in case count and means editor mappings are 1:1.
|
|
5219
|
-
*
|
|
5220
5664
|
* @param {any} switch_node
|
|
5221
5665
|
* @param {TransformContext} transform_context
|
|
5222
5666
|
* @returns {{ setup_statements: any[], switch_statement: any }}
|
|
5223
5667
|
*/
|
|
5224
5668
|
function build_switch_with_lift(switch_node, transform_context) {
|
|
5225
|
-
const { case_info, case_helpers,
|
|
5669
|
+
const { case_info, case_helpers, setup_statements } = plan_switch_lift(
|
|
5226
5670
|
switch_node,
|
|
5227
5671
|
transform_context,
|
|
5228
5672
|
);
|
|
@@ -5242,10 +5686,10 @@ function build_switch_with_lift(switch_node, transform_context) {
|
|
|
5242
5686
|
const { own_body, has_terminator } = case_info[i];
|
|
5243
5687
|
|
|
5244
5688
|
if (own_body.length === 0 && !has_terminator) {
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5689
|
+
return set_loc(
|
|
5690
|
+
b.switch_case(original_case.test, [create_null_return_statement()]),
|
|
5691
|
+
original_case,
|
|
5692
|
+
);
|
|
5249
5693
|
}
|
|
5250
5694
|
|
|
5251
5695
|
const case_body = [];
|
|
@@ -5272,27 +5716,13 @@ function build_switch_with_lift(switch_node, transform_context) {
|
|
|
5272
5716
|
}
|
|
5273
5717
|
}
|
|
5274
5718
|
|
|
5275
|
-
if (!has_terminal && !has_terminator) {
|
|
5276
|
-
const next_helper = find_next_helper_after(i);
|
|
5277
|
-
if (next_helper) {
|
|
5278
|
-
render_nodes.push(clone_switch_helper_invocation(next_helper));
|
|
5279
|
-
}
|
|
5280
|
-
}
|
|
5281
|
-
|
|
5282
5719
|
if (!has_terminal) {
|
|
5283
5720
|
if (render_nodes.length > 0) {
|
|
5284
5721
|
case_body.push(create_component_return_statement(render_nodes, original_case));
|
|
5285
|
-
} else if (has_terminator) {
|
|
5286
|
-
// Empty body with explicit `break;` / bare `return;` — keep
|
|
5287
|
-
// a `break` so JS doesn't fall through into the next case
|
|
5288
|
-
// (which may now hold the lifted helper invocation).
|
|
5289
|
-
case_body.push(b.break);
|
|
5290
5722
|
} else if (case_body.length > 0) {
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
// from re-running downstream statements via JS fall-through.
|
|
5295
|
-
case_body.push(b.break);
|
|
5723
|
+
case_body.push(create_null_return_statement());
|
|
5724
|
+
} else if (has_terminator) {
|
|
5725
|
+
case_body.push(create_null_return_statement());
|
|
5296
5726
|
}
|
|
5297
5727
|
}
|
|
5298
5728
|
|
|
@@ -5369,12 +5799,12 @@ function transform_element_attributes_dispatch(attrs, transform_context, element
|
|
|
5369
5799
|
* @returns {boolean}
|
|
5370
5800
|
*/
|
|
5371
5801
|
export function is_component_like_element(element) {
|
|
5372
|
-
const
|
|
5373
|
-
if (!
|
|
5374
|
-
if (
|
|
5375
|
-
if (
|
|
5376
|
-
if (
|
|
5377
|
-
if (
|
|
5802
|
+
const name = element?.openingElement?.name;
|
|
5803
|
+
if (!name) return false;
|
|
5804
|
+
if (name.type === 'Identifier') return /^[A-Z]/.test(name.name);
|
|
5805
|
+
if (name.type === 'JSXIdentifier') return /^[A-Z]/.test(name.name);
|
|
5806
|
+
if (name.type === 'MemberExpression') return true;
|
|
5807
|
+
if (name.type === 'JSXMemberExpression') return true;
|
|
5378
5808
|
return false;
|
|
5379
5809
|
}
|
|
5380
5810
|
|
|
@@ -5527,10 +5957,7 @@ function wrap_jsx_setup_declarations(expression, in_jsx_child) {
|
|
|
5527
5957
|
/**
|
|
5528
5958
|
* Reject elements with more than one TSX-style `ref={...}` attribute.
|
|
5529
5959
|
* This validator runs over the raw, pre-lowering attribute list so each
|
|
5530
|
-
* shape is still distinguishable by `type`.
|
|
5531
|
-
* `Identifier` name (the parser normalizes `JSXAttribute`/`JSXIdentifier`
|
|
5532
|
-
* for non-Tsx elements); inside `<tsx:react>` compat blocks they retain
|
|
5533
|
-
* the original `JSXAttribute`/`JSXIdentifier` shape, so we accept both.
|
|
5960
|
+
* shape is still distinguishable by `type`.
|
|
5534
5961
|
*
|
|
5535
5962
|
* @param {any[]} raw_attrs
|
|
5536
5963
|
* @param {TransformContext} [transform_context]
|
|
@@ -5541,14 +5968,10 @@ export function validate_at_most_one_ref_attribute(raw_attrs, transform_context)
|
|
|
5541
5968
|
for (const attr of raw_attrs) {
|
|
5542
5969
|
if (!attr) continue;
|
|
5543
5970
|
const is_ref_attr =
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
(attr.type === 'JSXAttribute' &&
|
|
5549
|
-
attr.name &&
|
|
5550
|
-
attr.name.type === 'JSXIdentifier' &&
|
|
5551
|
-
attr.name.name === 'ref');
|
|
5971
|
+
attr.type === 'JSXAttribute' &&
|
|
5972
|
+
attr.name &&
|
|
5973
|
+
attr.name.type === 'JSXIdentifier' &&
|
|
5974
|
+
attr.name.name === 'ref';
|
|
5552
5975
|
if (!is_ref_attr) continue;
|
|
5553
5976
|
refs.push(attr.name);
|
|
5554
5977
|
}
|
|
@@ -5760,8 +6183,6 @@ function infer_ref_namespace(tag_name) {
|
|
|
5760
6183
|
* @returns {string | null}
|
|
5761
6184
|
*/
|
|
5762
6185
|
function get_element_ref_tag_name(element) {
|
|
5763
|
-
const id = element?.id;
|
|
5764
|
-
if (id?.type === 'Identifier') return id.name;
|
|
5765
6186
|
const name = element?.name;
|
|
5766
6187
|
if (name?.type === 'JSXIdentifier') return name.name;
|
|
5767
6188
|
if (element?.openingElement?.name?.type === 'JSXIdentifier') {
|
|
@@ -5797,15 +6218,6 @@ export function to_jsx_attribute(attr, transform_context) {
|
|
|
5797
6218
|
if (attr.type === 'JSXSpreadAttribute') {
|
|
5798
6219
|
return attr;
|
|
5799
6220
|
}
|
|
5800
|
-
if (attr.type === 'SpreadAttribute') {
|
|
5801
|
-
return set_loc(
|
|
5802
|
-
/** @type {any} */ ({
|
|
5803
|
-
type: 'JSXSpreadAttribute',
|
|
5804
|
-
argument: attr.argument,
|
|
5805
|
-
}),
|
|
5806
|
-
attr,
|
|
5807
|
-
);
|
|
5808
|
-
}
|
|
5809
6221
|
// Keep this legacy hook for targets that need React-style DOM attrs. The
|
|
5810
6222
|
// current first-party targets preserve authored `class`.
|
|
5811
6223
|
let attr_name = attr.name;
|
|
@@ -5857,25 +6269,29 @@ function value_has_unmappable_jsx_loc(value) {
|
|
|
5857
6269
|
/**
|
|
5858
6270
|
* @param {any} node
|
|
5859
6271
|
* @param {TransformContext} transform_context
|
|
5860
|
-
* @
|
|
6272
|
+
* @param {boolean} in_jsx_child
|
|
6273
|
+
* @returns {any}
|
|
5861
6274
|
*/
|
|
5862
|
-
function
|
|
5863
|
-
const
|
|
5864
|
-
const
|
|
6275
|
+
function dynamic_element_to_jsx(node, transform_context, in_jsx_child) {
|
|
6276
|
+
const source_name = node.openingElement?.name;
|
|
6277
|
+
const dynamic_id = set_loc(create_generated_identifier('DynamicElement'), source_name || node);
|
|
6278
|
+
const alias_declaration = set_loc(
|
|
6279
|
+
b.const(dynamic_id, jsx_name_to_expression(source_name)),
|
|
6280
|
+
source_name || node,
|
|
6281
|
+
);
|
|
5865
6282
|
const jsx_element = create_dynamic_jsx_element(dynamic_id, node, transform_context);
|
|
5866
6283
|
|
|
5867
|
-
|
|
5868
|
-
b.
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
]),
|
|
5875
|
-
),
|
|
6284
|
+
const expression = b.call(
|
|
6285
|
+
b.arrow(
|
|
6286
|
+
[],
|
|
6287
|
+
b.block([
|
|
6288
|
+
alias_declaration,
|
|
6289
|
+
b.return(b.conditional(clone_identifier(dynamic_id), jsx_element, create_null_literal())),
|
|
6290
|
+
]),
|
|
5876
6291
|
),
|
|
5877
|
-
node,
|
|
5878
6292
|
);
|
|
6293
|
+
|
|
6294
|
+
return in_jsx_child ? to_jsx_expression_container(expression, node) : set_loc(expression, node);
|
|
5879
6295
|
}
|
|
5880
6296
|
|
|
5881
6297
|
/**
|
|
@@ -5886,11 +6302,11 @@ function dynamic_element_to_jsx_child(node, transform_context) {
|
|
|
5886
6302
|
*/
|
|
5887
6303
|
function create_dynamic_jsx_element(dynamic_id, node, transform_context) {
|
|
5888
6304
|
const attributes = transform_element_attributes_dispatch(
|
|
5889
|
-
node.attributes || [],
|
|
6305
|
+
node.openingElement?.attributes || [],
|
|
5890
6306
|
transform_context,
|
|
5891
6307
|
node,
|
|
5892
6308
|
);
|
|
5893
|
-
const selfClosing = !!node.selfClosing;
|
|
6309
|
+
const selfClosing = !!node.openingElement?.selfClosing;
|
|
5894
6310
|
const children = create_element_children(node.children || [], transform_context);
|
|
5895
6311
|
const name = identifier_to_jsx_name(clone_identifier(dynamic_id));
|
|
5896
6312
|
|
|
@@ -5912,6 +6328,10 @@ function build_return_expression(render_nodes) {
|
|
|
5912
6328
|
if (only.type === 'JSXExpressionContainer') {
|
|
5913
6329
|
return only.expression;
|
|
5914
6330
|
}
|
|
6331
|
+
if (only.type === 'JSXText') {
|
|
6332
|
+
const value = (only.value ?? '').trim();
|
|
6333
|
+
return b.literal(value, JSON.stringify(value), only);
|
|
6334
|
+
}
|
|
5915
6335
|
return only;
|
|
5916
6336
|
}
|
|
5917
6337
|
const first = render_nodes[0];
|
|
@@ -5930,25 +6350,3 @@ function build_return_expression(render_nodes) {
|
|
|
5930
6350
|
: undefined,
|
|
5931
6351
|
);
|
|
5932
6352
|
}
|
|
5933
|
-
|
|
5934
|
-
/**
|
|
5935
|
-
* @param {any} node
|
|
5936
|
-
* @param {TransformContext} transform_context
|
|
5937
|
-
* @param {boolean} [in_jsx_child]
|
|
5938
|
-
* @returns {any}
|
|
5939
|
-
*/
|
|
5940
|
-
function tsx_compat_node_to_jsx_expression(node, transform_context, in_jsx_child = false) {
|
|
5941
|
-
const platform = transform_context.platform;
|
|
5942
|
-
if (!platform.jsx.acceptedTsxKinds.includes(node.kind)) {
|
|
5943
|
-
const accepted = platform.jsx.acceptedTsxKinds.map((k) => `<tsx:${k}>`).join(', ');
|
|
5944
|
-
error(
|
|
5945
|
-
`${platform.name} TSRX does not support <tsx:${node.kind}> blocks. Use <tsx> or one of: ${accepted}.`,
|
|
5946
|
-
transform_context.filename,
|
|
5947
|
-
node,
|
|
5948
|
-
transform_context.errors,
|
|
5949
|
-
transform_context.comments,
|
|
5950
|
-
);
|
|
5951
|
-
}
|
|
5952
|
-
|
|
5953
|
-
return tsx_node_to_jsx_expression(node, in_jsx_child);
|
|
5954
|
-
}
|