@tsrx/core 0.1.20 → 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 -844
- 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 +44 -59
- package/src/transform/jsx/helpers.js +8 -5
- package/src/transform/jsx/index.js +755 -344
- 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 +179 -110
- 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';
|
|
@@ -63,6 +63,17 @@ 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
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,7 +296,11 @@ export function createJsxTransform(platform) {
|
|
|
213
296
|
return next();
|
|
214
297
|
},
|
|
215
298
|
|
|
216
|
-
|
|
299
|
+
JSXFragment(node, { next, path, state, visit }) {
|
|
300
|
+
if (!node.metadata?.native_tsrx) {
|
|
301
|
+
return next() ?? node;
|
|
302
|
+
}
|
|
303
|
+
|
|
217
304
|
const parent = /** @type {AST.ArrowFunctionExpression} */ (path.at(-1));
|
|
218
305
|
if (parent?.metadata?.native_tsrx && parent.body === node) {
|
|
219
306
|
return /** @type {any} */ (visit(create_native_tsrx_render_block(node, state), state));
|
|
@@ -233,18 +320,15 @@ export function createJsxTransform(platform) {
|
|
|
233
320
|
return /** @type {any} */ (wrap_jsx_setup_declarations(expression, in_jsx_child));
|
|
234
321
|
},
|
|
235
322
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
);
|
|
245
|
-
},
|
|
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
|
+
}
|
|
246
331
|
|
|
247
|
-
Element(node, { next, path, state }) {
|
|
248
332
|
if (is_style_element(node) && is_style_expression_position(path)) {
|
|
249
333
|
const stylesheet = get_style_element_stylesheet(node);
|
|
250
334
|
if (stylesheet) {
|
|
@@ -254,30 +338,57 @@ export function createJsxTransform(platform) {
|
|
|
254
338
|
}
|
|
255
339
|
}
|
|
256
340
|
|
|
257
|
-
// Capture raw children BEFORE the walker transforms them so
|
|
258
|
-
//
|
|
259
|
-
// inspect the original Text / TSRXExpression nodes rather than
|
|
260
|
-
// 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.
|
|
261
343
|
const raw_children = /** @type {any} */ (node.children || []).map(
|
|
262
344
|
(/** @type {any} */ child) => (child && typeof child === 'object' ? { ...child } : child),
|
|
263
345
|
);
|
|
264
346
|
const inner = /** @type {any} */ (next() ?? node);
|
|
265
347
|
const hook = platform.hooks?.transformElement;
|
|
266
348
|
if (hook) return /** @type {any} */ (hook(inner, state, raw_children));
|
|
267
|
-
return /** @type {any} */ (
|
|
349
|
+
return /** @type {any} */ (
|
|
350
|
+
to_jsx_element(inner, state, raw_children, in_jsx_child_context(path))
|
|
351
|
+
);
|
|
268
352
|
},
|
|
269
353
|
|
|
270
|
-
|
|
271
|
-
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
|
+
}
|
|
272
382
|
return /** @type {any} */ (
|
|
273
|
-
|
|
383
|
+
b.jsx_element(
|
|
384
|
+
/** @type {ESTreeJSX.JSXElement} */ ({ ...node, type: 'JSXElement', children: [] }),
|
|
385
|
+
node.openingElement?.attributes ?? [],
|
|
386
|
+
[],
|
|
387
|
+
)
|
|
274
388
|
);
|
|
275
389
|
},
|
|
276
390
|
|
|
277
|
-
|
|
278
|
-
const inner = /** @type {any} */ (next() ?? node);
|
|
279
|
-
return /** @type {any} */ (to_jsx_expression_container(inner.expression, inner));
|
|
280
|
-
},
|
|
391
|
+
JSXCodeBlock: transform_jsx_code_block,
|
|
281
392
|
|
|
282
393
|
BlockStatement: transform_block_statement,
|
|
283
394
|
ReturnStatement: transform_return_statement,
|
|
@@ -330,6 +441,7 @@ export function createJsxTransform(platform) {
|
|
|
330
441
|
? expanded
|
|
331
442
|
: apply_lazy_transforms(/** @type {any} */ (expanded), new Map())
|
|
332
443
|
);
|
|
444
|
+
lower_remaining_jsx_code_blocks(final_program, transform_context);
|
|
333
445
|
|
|
334
446
|
const result = print(/** @type {any} */ (final_program), tsx_with_ts_locations(), {
|
|
335
447
|
sourceMapSource: filename,
|
|
@@ -408,7 +520,7 @@ function collect_css_prunable_elements(value, elements = []) {
|
|
|
408
520
|
return elements;
|
|
409
521
|
}
|
|
410
522
|
|
|
411
|
-
if (value.type === '
|
|
523
|
+
if (value.type === 'JSXElement' && value.metadata?.native_tsrx) {
|
|
412
524
|
if (!is_style_element(value)) {
|
|
413
525
|
elements.push(value);
|
|
414
526
|
}
|
|
@@ -440,6 +552,12 @@ function build_component_statements(body_nodes, transform_context) {
|
|
|
440
552
|
* @returns {any[]}
|
|
441
553
|
*/
|
|
442
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
|
+
|
|
443
561
|
const statements = [];
|
|
444
562
|
const render_nodes = [];
|
|
445
563
|
let has_terminal_return = false;
|
|
@@ -522,7 +640,7 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
522
640
|
}
|
|
523
641
|
|
|
524
642
|
if (
|
|
525
|
-
child
|
|
643
|
+
is_for_of_control_node(child) &&
|
|
526
644
|
!child.await &&
|
|
527
645
|
should_extract_hook_helpers(transform_context) &&
|
|
528
646
|
!transform_context.platform.hooks?.isTopLevelSetupCall &&
|
|
@@ -533,7 +651,10 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
533
651
|
true,
|
|
534
652
|
)
|
|
535
653
|
) {
|
|
536
|
-
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
|
+
);
|
|
537
658
|
if (hoisted) {
|
|
538
659
|
statements.push(...hoisted.hoist_statements);
|
|
539
660
|
if (interleaved && is_capturable_jsx_child(hoisted.jsx_child)) {
|
|
@@ -560,6 +681,7 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
560
681
|
} else if (is_bare_render_expression(child)) {
|
|
561
682
|
render_nodes.push(to_jsx_expression_container(child, child));
|
|
562
683
|
} else {
|
|
684
|
+
mark_nested_function_return_jsx(child);
|
|
563
685
|
statements.push(child);
|
|
564
686
|
collect_statement_bindings(child, transform_context.available_bindings);
|
|
565
687
|
}
|
|
@@ -667,7 +789,7 @@ function find_hook_split_index(body_nodes, transform_context) {
|
|
|
667
789
|
* @returns {boolean}
|
|
668
790
|
*/
|
|
669
791
|
function is_component_body_conditional_return_statement(node) {
|
|
670
|
-
if (node
|
|
792
|
+
if (!is_if_control_node(node)) {
|
|
671
793
|
return false;
|
|
672
794
|
}
|
|
673
795
|
|
|
@@ -702,20 +824,20 @@ function statement_contains_component_body_return(node) {
|
|
|
702
824
|
return (node.body || []).some(statement_contains_component_body_return);
|
|
703
825
|
}
|
|
704
826
|
|
|
705
|
-
if (node
|
|
827
|
+
if (is_if_control_node(node)) {
|
|
706
828
|
return (
|
|
707
829
|
statement_contains_component_body_return(node.consequent) ||
|
|
708
830
|
statement_contains_component_body_return(node.alternate)
|
|
709
831
|
);
|
|
710
832
|
}
|
|
711
833
|
|
|
712
|
-
if (node
|
|
834
|
+
if (is_switch_control_node(node)) {
|
|
713
835
|
return (node.cases || []).some((/** @type {any} */ switch_case) =>
|
|
714
836
|
statement_contains_component_body_return(switch_case.consequent || []),
|
|
715
837
|
);
|
|
716
838
|
}
|
|
717
839
|
|
|
718
|
-
if (node
|
|
840
|
+
if (is_try_control_node(node)) {
|
|
719
841
|
return (
|
|
720
842
|
statement_contains_component_body_return(node.block) ||
|
|
721
843
|
statement_contains_component_body_return(node.handler?.body) ||
|
|
@@ -1008,13 +1130,41 @@ function transform_block_statement(node, { next, visit, state, path }) {
|
|
|
1008
1130
|
* @returns {any}
|
|
1009
1131
|
*/
|
|
1010
1132
|
function transform_return_statement(node, { next, visit, state, path }) {
|
|
1011
|
-
if (get_active_native_tsrx_function(path) && node.argument
|
|
1133
|
+
if (get_active_native_tsrx_function(path) && is_native_tsrx_node(node.argument)) {
|
|
1012
1134
|
return visit(create_native_tsrx_render_block(node.argument, state), state);
|
|
1013
1135
|
}
|
|
1014
1136
|
|
|
1015
1137
|
return next() ?? node;
|
|
1016
1138
|
}
|
|
1017
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
|
+
|
|
1018
1168
|
/**
|
|
1019
1169
|
* @param {AST.Node[]} path
|
|
1020
1170
|
* @returns {any | null}
|
|
@@ -1035,6 +1185,13 @@ function get_active_native_tsrx_function(path) {
|
|
|
1035
1185
|
* @returns {any}
|
|
1036
1186
|
*/
|
|
1037
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
|
+
|
|
1038
1195
|
if (node.metadata?.native_tsrx_function || function_has_native_tsrx_return(node)) {
|
|
1039
1196
|
return transform_native_tsrx_function(node, context);
|
|
1040
1197
|
}
|
|
@@ -1042,6 +1199,34 @@ function transform_function(node, context) {
|
|
|
1042
1199
|
return transform_function_with_hook_helpers(node, context);
|
|
1043
1200
|
}
|
|
1044
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
|
+
|
|
1045
1230
|
/**
|
|
1046
1231
|
* @param {any} node
|
|
1047
1232
|
* @param {{ next: () => any, state: TransformContext }} context
|
|
@@ -1072,7 +1257,7 @@ function transform_native_tsrx_function(node, { next, state }) {
|
|
|
1072
1257
|
if (
|
|
1073
1258
|
inner !== node &&
|
|
1074
1259
|
node.type === 'ArrowFunctionExpression' &&
|
|
1075
|
-
node.body
|
|
1260
|
+
is_native_tsrx_node(node.body) &&
|
|
1076
1261
|
inner.body?.type === 'BlockStatement'
|
|
1077
1262
|
) {
|
|
1078
1263
|
inner.expression = false;
|
|
@@ -1132,6 +1317,10 @@ function find_native_await(node) {
|
|
|
1132
1317
|
return find_first_top_level_await(node.body, false);
|
|
1133
1318
|
}
|
|
1134
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
|
+
|
|
1135
1324
|
const body = node.body?.type === 'BlockStatement' ? node.body.body || [] : [];
|
|
1136
1325
|
return find_native_await_in_list(body);
|
|
1137
1326
|
}
|
|
@@ -1155,7 +1344,7 @@ function find_native_await_in_list(statements) {
|
|
|
1155
1344
|
function find_native_await_in_statement(statement) {
|
|
1156
1345
|
if (!statement || typeof statement !== 'object') return null;
|
|
1157
1346
|
|
|
1158
|
-
if (statement.type === 'ReturnStatement' && statement.argument
|
|
1347
|
+
if (statement.type === 'ReturnStatement' && is_native_tsrx_node(statement.argument)) {
|
|
1159
1348
|
return find_first_top_level_await_in_tsrx_function_body(statement.argument.children || []);
|
|
1160
1349
|
}
|
|
1161
1350
|
|
|
@@ -1174,14 +1363,14 @@ function find_native_await_in_statement(statement) {
|
|
|
1174
1363
|
return find_native_await_in_list(statement.body || []);
|
|
1175
1364
|
}
|
|
1176
1365
|
|
|
1177
|
-
if (statement
|
|
1366
|
+
if (is_if_control_node(statement)) {
|
|
1178
1367
|
return (
|
|
1179
1368
|
find_native_await_in_statement(statement.consequent) ||
|
|
1180
1369
|
find_native_await_in_statement(statement.alternate)
|
|
1181
1370
|
);
|
|
1182
1371
|
}
|
|
1183
1372
|
|
|
1184
|
-
if (statement
|
|
1373
|
+
if (is_switch_control_node(statement)) {
|
|
1185
1374
|
for (const switch_case of statement.cases || []) {
|
|
1186
1375
|
const found = find_native_await_in_list(switch_case.consequent || []);
|
|
1187
1376
|
if (found) return found;
|
|
@@ -1189,7 +1378,7 @@ function find_native_await_in_statement(statement) {
|
|
|
1189
1378
|
return null;
|
|
1190
1379
|
}
|
|
1191
1380
|
|
|
1192
|
-
if (statement
|
|
1381
|
+
if (is_try_control_node(statement)) {
|
|
1193
1382
|
return (
|
|
1194
1383
|
find_native_await_in_statement(statement.block) ||
|
|
1195
1384
|
find_native_await_in_statement(statement.handler?.body) ||
|
|
@@ -1197,7 +1386,7 @@ function find_native_await_in_statement(statement) {
|
|
|
1197
1386
|
);
|
|
1198
1387
|
}
|
|
1199
1388
|
|
|
1200
|
-
return
|
|
1389
|
+
return find_first_top_level_await(statement, false);
|
|
1201
1390
|
}
|
|
1202
1391
|
|
|
1203
1392
|
/**
|
|
@@ -1250,7 +1439,7 @@ function transform_function_with_hook_helpers(node, { next, state }) {
|
|
|
1250
1439
|
* @returns {string}
|
|
1251
1440
|
*/
|
|
1252
1441
|
function get_function_helper_base_name(node) {
|
|
1253
|
-
return get_function_like_name(node) || '
|
|
1442
|
+
return get_function_like_name(node) || 'TSRXTemplate';
|
|
1254
1443
|
}
|
|
1255
1444
|
|
|
1256
1445
|
/**
|
|
@@ -1329,7 +1518,7 @@ function collect_function_scope_bindings(node) {
|
|
|
1329
1518
|
const bindings = collect_param_bindings(node.params || []);
|
|
1330
1519
|
if (node.body?.type === 'BlockStatement') {
|
|
1331
1520
|
for (const statement of node.body.body || []) {
|
|
1332
|
-
if (statement.type === 'ReturnStatement' && statement.argument
|
|
1521
|
+
if (statement.type === 'ReturnStatement' && is_native_tsrx_node(statement.argument)) {
|
|
1333
1522
|
for (const child of get_tsrx_render_children(statement.argument)) {
|
|
1334
1523
|
collect_statement_bindings(child, bindings);
|
|
1335
1524
|
}
|
|
@@ -1361,6 +1550,10 @@ function merge_binding_maps(outer, inner) {
|
|
|
1361
1550
|
function function_has_native_tsrx_return(node) {
|
|
1362
1551
|
if (!node) return false;
|
|
1363
1552
|
|
|
1553
|
+
if (node.body?.type === 'JSXCodeBlock') {
|
|
1554
|
+
return true;
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1364
1557
|
if (node.type === 'ArrowFunctionExpression' && node.body?.type !== 'BlockStatement') {
|
|
1365
1558
|
return node_contains_native_tsrx_template(node.body);
|
|
1366
1559
|
}
|
|
@@ -1396,22 +1589,23 @@ function statement_contains_native_tsrx_return(statement) {
|
|
|
1396
1589
|
return statements_contain_native_tsrx_return(statement.body || []);
|
|
1397
1590
|
}
|
|
1398
1591
|
|
|
1399
|
-
if (statement
|
|
1592
|
+
if (is_if_control_node(statement)) {
|
|
1400
1593
|
return (
|
|
1401
1594
|
statement_contains_native_tsrx_return(statement.consequent) ||
|
|
1402
1595
|
statement_contains_native_tsrx_return(statement.alternate)
|
|
1403
1596
|
);
|
|
1404
1597
|
}
|
|
1405
1598
|
|
|
1406
|
-
if (statement
|
|
1599
|
+
if (is_switch_control_node(statement)) {
|
|
1407
1600
|
return (statement.cases || []).some((/** @type {any} */ c) =>
|
|
1408
1601
|
statements_contain_native_tsrx_return(c.consequent || []),
|
|
1409
1602
|
);
|
|
1410
1603
|
}
|
|
1411
1604
|
|
|
1412
|
-
if (statement
|
|
1605
|
+
if (is_try_control_node(statement)) {
|
|
1413
1606
|
return (
|
|
1414
1607
|
statement_contains_native_tsrx_return(statement.block) ||
|
|
1608
|
+
statement_contains_native_tsrx_return(statement.pending) ||
|
|
1415
1609
|
statement_contains_native_tsrx_return(statement.handler?.body) ||
|
|
1416
1610
|
statement_contains_native_tsrx_return(statement.finalizer)
|
|
1417
1611
|
);
|
|
@@ -1438,7 +1632,7 @@ function statement_contains_native_tsrx_return(statement) {
|
|
|
1438
1632
|
*/
|
|
1439
1633
|
function node_contains_native_tsrx_template(node) {
|
|
1440
1634
|
if (!node || typeof node !== 'object') return false;
|
|
1441
|
-
if (node
|
|
1635
|
+
if (is_native_tsrx_node(node)) return true;
|
|
1442
1636
|
|
|
1443
1637
|
if (is_function_or_class_boundary(node)) {
|
|
1444
1638
|
return false;
|
|
@@ -1629,11 +1823,11 @@ function collect_style_elements(node, styles) {
|
|
|
1629
1823
|
return;
|
|
1630
1824
|
}
|
|
1631
1825
|
|
|
1632
|
-
if (is_function_or_class_boundary(node)
|
|
1826
|
+
if (is_function_or_class_boundary(node)) {
|
|
1633
1827
|
return;
|
|
1634
1828
|
}
|
|
1635
1829
|
|
|
1636
|
-
if (node.type === '
|
|
1830
|
+
if ((node.type === 'JSXElement' || node.type === 'JSXFragment') && node.metadata?.native_tsrx) {
|
|
1637
1831
|
collect_style_elements(node.children || [], styles);
|
|
1638
1832
|
return;
|
|
1639
1833
|
}
|
|
@@ -1643,20 +1837,20 @@ function collect_style_elements(node, styles) {
|
|
|
1643
1837
|
return;
|
|
1644
1838
|
}
|
|
1645
1839
|
|
|
1646
|
-
if (node
|
|
1840
|
+
if (is_if_control_node(node)) {
|
|
1647
1841
|
collect_style_elements(node.consequent, styles);
|
|
1648
1842
|
collect_style_elements(node.alternate, styles);
|
|
1649
1843
|
return;
|
|
1650
1844
|
}
|
|
1651
1845
|
|
|
1652
|
-
if (node
|
|
1846
|
+
if (is_switch_control_node(node)) {
|
|
1653
1847
|
for (const switch_case of node.cases || []) {
|
|
1654
1848
|
collect_style_elements(switch_case.consequent || [], styles);
|
|
1655
1849
|
}
|
|
1656
1850
|
return;
|
|
1657
1851
|
}
|
|
1658
1852
|
|
|
1659
|
-
if (node
|
|
1853
|
+
if (is_try_control_node(node)) {
|
|
1660
1854
|
collect_style_elements(node.block, styles);
|
|
1661
1855
|
collect_style_elements(node.handler?.body, styles);
|
|
1662
1856
|
collect_style_elements(node.finalizer, styles);
|
|
@@ -1708,7 +1902,7 @@ function strip_style_elements(node) {
|
|
|
1708
1902
|
return node;
|
|
1709
1903
|
}
|
|
1710
1904
|
|
|
1711
|
-
if (node.type === '
|
|
1905
|
+
if ((node.type === 'JSXElement' || node.type === 'JSXFragment') && node.metadata?.native_tsrx) {
|
|
1712
1906
|
node.children = strip_style_elements(node.children || []);
|
|
1713
1907
|
return node;
|
|
1714
1908
|
}
|
|
@@ -1718,20 +1912,20 @@ function strip_style_elements(node) {
|
|
|
1718
1912
|
return node;
|
|
1719
1913
|
}
|
|
1720
1914
|
|
|
1721
|
-
if (node
|
|
1915
|
+
if (is_if_control_node(node)) {
|
|
1722
1916
|
node.consequent = strip_style_elements(node.consequent);
|
|
1723
1917
|
if (node.alternate) node.alternate = strip_style_elements(node.alternate);
|
|
1724
1918
|
return node;
|
|
1725
1919
|
}
|
|
1726
1920
|
|
|
1727
|
-
if (node
|
|
1921
|
+
if (is_switch_control_node(node)) {
|
|
1728
1922
|
for (const switch_case of node.cases || []) {
|
|
1729
1923
|
switch_case.consequent = strip_style_elements(switch_case.consequent || []);
|
|
1730
1924
|
}
|
|
1731
1925
|
return node;
|
|
1732
1926
|
}
|
|
1733
1927
|
|
|
1734
|
-
if (node
|
|
1928
|
+
if (is_try_control_node(node)) {
|
|
1735
1929
|
node.block = strip_style_elements(node.block);
|
|
1736
1930
|
if (node.handler?.body) node.handler.body = strip_style_elements(node.handler.body);
|
|
1737
1931
|
if (node.finalizer) node.finalizer = strip_style_elements(node.finalizer);
|
|
@@ -1747,9 +1941,7 @@ function strip_style_elements(node) {
|
|
|
1747
1941
|
function is_style_expression_position(path) {
|
|
1748
1942
|
const parent = path.at(-1);
|
|
1749
1943
|
return !(
|
|
1750
|
-
parent
|
|
1751
|
-
parent?.type === 'TsrxFragment' ||
|
|
1752
|
-
parent?.type === 'TsxCompat' ||
|
|
1944
|
+
is_native_tsrx_node(parent) ||
|
|
1753
1945
|
parent?.type === 'BlockStatement' ||
|
|
1754
1946
|
parent?.type === 'Program' ||
|
|
1755
1947
|
parent?.type === 'SwitchCase'
|
|
@@ -1804,9 +1996,11 @@ function create_native_tsrx_statement_list_block(block, transform_context) {
|
|
|
1804
1996
|
function create_native_tsrx_render_statements(fragment, transform_context) {
|
|
1805
1997
|
return with_tsrx_fragment_styles(fragment, transform_context, (style_context) => {
|
|
1806
1998
|
const target = style_context?.fragment ?? fragment;
|
|
1999
|
+
const render_nodes =
|
|
2000
|
+
target.type === 'JSXFragment' ? get_tsrx_render_children(target) : [target];
|
|
1807
2001
|
return [
|
|
1808
2002
|
...create_tsrx_style_ref_setup_statements(target, style_context, transform_context),
|
|
1809
|
-
...build_render_statements(
|
|
2003
|
+
...build_render_statements(render_nodes, true, transform_context),
|
|
1810
2004
|
];
|
|
1811
2005
|
});
|
|
1812
2006
|
}
|
|
@@ -1836,7 +2030,7 @@ function expand_native_tsrx_return_statement_list(statements, transform_context)
|
|
|
1836
2030
|
function expand_native_tsrx_return_statement(statement, transform_context) {
|
|
1837
2031
|
if (!statement || typeof statement !== 'object') return [statement];
|
|
1838
2032
|
|
|
1839
|
-
if (statement.type === 'ReturnStatement' && statement.argument
|
|
2033
|
+
if (statement.type === 'ReturnStatement' && is_native_tsrx_node(statement.argument)) {
|
|
1840
2034
|
return create_native_tsrx_render_statements(statement.argument, transform_context);
|
|
1841
2035
|
}
|
|
1842
2036
|
|
|
@@ -1849,7 +2043,7 @@ function expand_native_tsrx_return_statement(statement, transform_context) {
|
|
|
1849
2043
|
return body === statement.body ? [statement] : [b.block(body, statement)];
|
|
1850
2044
|
}
|
|
1851
2045
|
|
|
1852
|
-
if (statement
|
|
2046
|
+
if (is_if_control_node(statement)) {
|
|
1853
2047
|
const consequent = expand_embedded_native_return_statement(
|
|
1854
2048
|
statement.consequent,
|
|
1855
2049
|
transform_context,
|
|
@@ -1863,7 +2057,7 @@ function expand_native_tsrx_return_statement(statement, transform_context) {
|
|
|
1863
2057
|
return [set_loc(b.if(statement.test, consequent, alternate), statement)];
|
|
1864
2058
|
}
|
|
1865
2059
|
|
|
1866
|
-
if (statement
|
|
2060
|
+
if (is_switch_control_node(statement)) {
|
|
1867
2061
|
let changed = false;
|
|
1868
2062
|
const cases = (statement.cases || []).map((/** @type {any} */ switch_case) => {
|
|
1869
2063
|
const consequent = expand_native_tsrx_return_statement_list(
|
|
@@ -1879,8 +2073,11 @@ function expand_native_tsrx_return_statement(statement, transform_context) {
|
|
|
1879
2073
|
return changed ? [set_loc(b.switch(statement.discriminant, cases), statement)] : [statement];
|
|
1880
2074
|
}
|
|
1881
2075
|
|
|
1882
|
-
if (statement
|
|
2076
|
+
if (is_try_control_node(statement)) {
|
|
1883
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;
|
|
1884
2081
|
const handler_body = statement.handler?.body
|
|
1885
2082
|
? expand_embedded_native_return_statement(statement.handler.body, transform_context)
|
|
1886
2083
|
: statement.handler?.body;
|
|
@@ -1889,6 +2086,7 @@ function expand_native_tsrx_return_statement(statement, transform_context) {
|
|
|
1889
2086
|
: statement.finalizer;
|
|
1890
2087
|
if (
|
|
1891
2088
|
block === statement.block &&
|
|
2089
|
+
pending === statement.pending &&
|
|
1892
2090
|
handler_body === statement.handler?.body &&
|
|
1893
2091
|
finalizer === statement.finalizer
|
|
1894
2092
|
) {
|
|
@@ -1903,7 +2101,7 @@ function expand_native_tsrx_return_statement(statement, transform_context) {
|
|
|
1903
2101
|
statement.handler,
|
|
1904
2102
|
)
|
|
1905
2103
|
: statement.handler;
|
|
1906
|
-
return [set_loc(b.try(block, handler, finalizer,
|
|
2104
|
+
return [set_loc(b.try(block, handler, finalizer, pending ?? null), statement)];
|
|
1907
2105
|
}
|
|
1908
2106
|
|
|
1909
2107
|
return [statement];
|
|
@@ -2036,7 +2234,7 @@ function node_contains_hook_bearing_tsrx(node, transform_context) {
|
|
|
2036
2234
|
return node.some((child) => node_contains_hook_bearing_tsrx(child, transform_context));
|
|
2037
2235
|
}
|
|
2038
2236
|
|
|
2039
|
-
if (node
|
|
2237
|
+
if (is_native_tsrx_node(node)) {
|
|
2040
2238
|
return body_contains_top_level_hook_call(node.children || [], transform_context, true);
|
|
2041
2239
|
}
|
|
2042
2240
|
|
|
@@ -2083,7 +2281,7 @@ function should_extract_hook_helpers(transform_context) {
|
|
|
2083
2281
|
*/
|
|
2084
2282
|
function create_module_scoped_hook_component_id(helper_id, transform_context) {
|
|
2085
2283
|
return create_generated_identifier(
|
|
2086
|
-
`${transform_context.helper_state?.base_name || '
|
|
2284
|
+
`${transform_context.helper_state?.base_name || 'TSRXTemplate'}__${helper_id.name}`,
|
|
2087
2285
|
);
|
|
2088
2286
|
}
|
|
2089
2287
|
|
|
@@ -2301,6 +2499,48 @@ function expand_component_helpers(program) {
|
|
|
2301
2499
|
return program;
|
|
2302
2500
|
}
|
|
2303
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
|
+
|
|
2304
2544
|
/**
|
|
2305
2545
|
* Generated helper/statics metadata can be carried on function declarations,
|
|
2306
2546
|
* variable declarations, object literal members, or export-safe expressions,
|
|
@@ -2383,11 +2623,7 @@ function create_component_return_statement(
|
|
|
2383
2623
|
* @returns {boolean}
|
|
2384
2624
|
*/
|
|
2385
2625
|
function is_loop_skip_return_statement(node) {
|
|
2386
|
-
return
|
|
2387
|
-
node?.type === 'ReturnStatement' &&
|
|
2388
|
-
node.argument == null &&
|
|
2389
|
-
node.metadata?.generated_loop_continue_return === true
|
|
2390
|
-
);
|
|
2626
|
+
return node?.type === 'ReturnStatement' && node.metadata?.generated_loop_continue_return === true;
|
|
2391
2627
|
}
|
|
2392
2628
|
|
|
2393
2629
|
/**
|
|
@@ -2403,7 +2639,7 @@ function is_loop_skip_if_statement(node) {
|
|
|
2403
2639
|
* @returns {any[] | null}
|
|
2404
2640
|
*/
|
|
2405
2641
|
function get_loop_skip_if_consequent_body(node) {
|
|
2406
|
-
if (node
|
|
2642
|
+
if (!is_if_control_node(node) || node.alternate) {
|
|
2407
2643
|
return null;
|
|
2408
2644
|
}
|
|
2409
2645
|
|
|
@@ -2424,7 +2660,15 @@ function create_component_loop_skip_if_statement(node, render_nodes, transform_c
|
|
|
2424
2660
|
const branch_statements = build_render_statements(consequent_body, true, transform_context);
|
|
2425
2661
|
prepend_render_nodes_to_return_statements(branch_statements, render_nodes);
|
|
2426
2662
|
|
|
2427
|
-
|
|
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;
|
|
2428
2672
|
}
|
|
2429
2673
|
|
|
2430
2674
|
/**
|
|
@@ -2803,26 +3047,37 @@ function create_helper_props_type_literal_with_typeof_flags(bindings, aliases, u
|
|
|
2803
3047
|
/**
|
|
2804
3048
|
* @param {any} node
|
|
2805
3049
|
* @param {TransformContext} transform_context
|
|
3050
|
+
* @param {boolean} [in_jsx_child]
|
|
2806
3051
|
* @returns {any}
|
|
2807
3052
|
*/
|
|
2808
|
-
function to_jsx_element(
|
|
2809
|
-
|
|
2810
|
-
|
|
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) {
|
|
2811
3066
|
report_jsx_fragment_in_tsrx_error(node, transform_context);
|
|
2812
3067
|
return set_loc(b.jsx_fragment(), node);
|
|
2813
3068
|
}
|
|
2814
|
-
if (
|
|
2815
|
-
return
|
|
3069
|
+
if (is_dynamic_jsx_element(node)) {
|
|
3070
|
+
return dynamic_element_to_jsx(node, transform_context, in_jsx_child);
|
|
2816
3071
|
}
|
|
2817
3072
|
|
|
2818
|
-
const name =
|
|
3073
|
+
const name = clone_jsx_name(source_name);
|
|
2819
3074
|
const attributes = transform_element_attributes_dispatch(
|
|
2820
|
-
|
|
3075
|
+
source_opening.attributes || [],
|
|
2821
3076
|
transform_context,
|
|
2822
3077
|
node,
|
|
2823
3078
|
);
|
|
2824
3079
|
const walked_children = node.children || [];
|
|
2825
|
-
let selfClosing = !!
|
|
3080
|
+
let selfClosing = !!source_opening.selfClosing;
|
|
2826
3081
|
let children;
|
|
2827
3082
|
const child_transform = transform_context.platform.hooks?.transformElementChildren?.(
|
|
2828
3083
|
node,
|
|
@@ -2848,7 +3103,7 @@ function to_jsx_element(node, transform_context, raw_children = node.children ||
|
|
|
2848
3103
|
name,
|
|
2849
3104
|
attributes,
|
|
2850
3105
|
selfClosing,
|
|
2851
|
-
|
|
3106
|
+
source_opening.typeArguments,
|
|
2852
3107
|
);
|
|
2853
3108
|
const openingElement = has_unmappable_attribute
|
|
2854
3109
|
? opening_element_node
|
|
@@ -3122,7 +3377,13 @@ function collect_block_binding_names_from_statement(statement, names) {
|
|
|
3122
3377
|
return;
|
|
3123
3378
|
}
|
|
3124
3379
|
|
|
3125
|
-
if (
|
|
3380
|
+
if (
|
|
3381
|
+
statement.type === 'ForOfStatement' ||
|
|
3382
|
+
statement.type === 'ForInStatement' ||
|
|
3383
|
+
(statement.type === 'JSXForExpression' &&
|
|
3384
|
+
(statement.statementType === 'ForOfStatement' ||
|
|
3385
|
+
statement.statementType === 'ForInStatement'))
|
|
3386
|
+
) {
|
|
3126
3387
|
if (statement.left?.type === 'VariableDeclaration' && statement.left.kind === 'var') {
|
|
3127
3388
|
for (const declaration of statement.left.declarations || []) {
|
|
3128
3389
|
collect_pattern_names(declaration.id, names);
|
|
@@ -3283,7 +3544,7 @@ function validate_hook_outer_assignments_in_node(
|
|
|
3283
3544
|
}
|
|
3284
3545
|
}
|
|
3285
3546
|
|
|
3286
|
-
if (node
|
|
3547
|
+
if (is_for_of_control_node(node)) {
|
|
3287
3548
|
if (
|
|
3288
3549
|
node.left &&
|
|
3289
3550
|
node.left.type !== 'VariableDeclaration' &&
|
|
@@ -3716,9 +3977,7 @@ function get_hook_callee_name(callee) {
|
|
|
3716
3977
|
* @param {any} source_node
|
|
3717
3978
|
* @param {TransformContext} transform_context
|
|
3718
3979
|
* @param {AST.Identifier} [preallocated_helper_id] - Optional pre-allocated id.
|
|
3719
|
-
* Used by
|
|
3720
|
-
* source order in a forward pass and then constructs helpers in reverse so
|
|
3721
|
-
* 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.
|
|
3722
3981
|
* @param {{ transientBindings?: Set<string> }} [options]
|
|
3723
3982
|
* @returns {{ setup_statements: any[], component_element: ESTreeJSX.JSXElement }}
|
|
3724
3983
|
*/
|
|
@@ -3997,6 +4256,118 @@ function get_body_source_node(body_nodes) {
|
|
|
3997
4256
|
return first;
|
|
3998
4257
|
}
|
|
3999
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
|
+
|
|
4000
4371
|
/**
|
|
4001
4372
|
* @param {any} node
|
|
4002
4373
|
* @param {TransformContext} transform_context
|
|
@@ -4005,33 +4376,56 @@ function get_body_source_node(body_nodes) {
|
|
|
4005
4376
|
function to_jsx_child(node, transform_context) {
|
|
4006
4377
|
if (!node) return node;
|
|
4007
4378
|
switch (node.type) {
|
|
4008
|
-
case '
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
return
|
|
4016
|
-
case '
|
|
4017
|
-
|
|
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':
|
|
4018
4393
|
case 'IfStatement':
|
|
4394
|
+
if (node.metadata?.generated_loop_skip_if) {
|
|
4395
|
+
return node;
|
|
4396
|
+
}
|
|
4019
4397
|
return (
|
|
4020
4398
|
transform_context.platform.hooks?.controlFlow?.ifStatement ?? if_statement_to_jsx_child
|
|
4021
|
-
)(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);
|
|
4022
4414
|
case 'ForOfStatement':
|
|
4023
4415
|
return (
|
|
4024
4416
|
transform_context.platform.hooks?.controlFlow?.forOf ?? for_of_statement_to_jsx_child
|
|
4025
4417
|
)(node, transform_context);
|
|
4418
|
+
case 'JSXSwitchExpression':
|
|
4026
4419
|
case 'SwitchStatement':
|
|
4027
4420
|
return (
|
|
4028
4421
|
transform_context.platform.hooks?.controlFlow?.switchStatement ??
|
|
4029
4422
|
switch_statement_to_jsx_child
|
|
4030
|
-
)(node, transform_context);
|
|
4423
|
+
)(jsx_control_expression_to_statement(node), transform_context);
|
|
4424
|
+
case 'JSXTryExpression':
|
|
4031
4425
|
case 'TryStatement':
|
|
4032
4426
|
return (
|
|
4033
4427
|
transform_context.platform.hooks?.controlFlow?.tryStatement ?? try_statement_to_jsx_child
|
|
4034
|
-
)(node, transform_context);
|
|
4428
|
+
)(jsx_control_expression_to_statement(node), transform_context);
|
|
4035
4429
|
default:
|
|
4036
4430
|
return node;
|
|
4037
4431
|
}
|
|
@@ -4040,7 +4434,7 @@ function to_jsx_child(node, transform_context) {
|
|
|
4040
4434
|
/**
|
|
4041
4435
|
* Lower a native TSRX fragment body to a JSX expression.
|
|
4042
4436
|
* Children have already been parsed and transformed through the normal TSRX
|
|
4043
|
-
*
|
|
4437
|
+
* JSX element/text/control-flow visitors.
|
|
4044
4438
|
*
|
|
4045
4439
|
* @param {any} node
|
|
4046
4440
|
* @param {TransformContext} transform_context
|
|
@@ -4123,7 +4517,7 @@ function return_value_statement_to_expression(node, transform_context) {
|
|
|
4123
4517
|
return node.argument;
|
|
4124
4518
|
}
|
|
4125
4519
|
|
|
4126
|
-
if (node
|
|
4520
|
+
if (is_if_control_node(node)) {
|
|
4127
4521
|
return return_value_if_statement_to_conditional_expression(node, transform_context);
|
|
4128
4522
|
}
|
|
4129
4523
|
|
|
@@ -4217,7 +4611,7 @@ function return_value_block_to_expression(node, transform_context) {
|
|
|
4217
4611
|
* @returns {any | null}
|
|
4218
4612
|
*/
|
|
4219
4613
|
function return_value_if_statement_to_conditional_expression(node, transform_context) {
|
|
4220
|
-
if (!node
|
|
4614
|
+
if (!is_if_control_node(node)) return null;
|
|
4221
4615
|
|
|
4222
4616
|
const consequent = return_value_block_to_expression(node.consequent, transform_context);
|
|
4223
4617
|
if (!consequent) return null;
|
|
@@ -4257,14 +4651,14 @@ function if_statement_to_jsx_child(node, transform_context) {
|
|
|
4257
4651
|
* @returns {any | null}
|
|
4258
4652
|
*/
|
|
4259
4653
|
function render_if_statement_to_conditional_expression(node) {
|
|
4260
|
-
if (!node
|
|
4654
|
+
if (!is_if_control_node(node)) return null;
|
|
4261
4655
|
|
|
4262
4656
|
const consequent = block_statement_to_return_expression(node.consequent);
|
|
4263
4657
|
if (!consequent) return null;
|
|
4264
4658
|
|
|
4265
4659
|
let alternate = create_null_literal();
|
|
4266
4660
|
if (node.alternate) {
|
|
4267
|
-
if (node.alternate
|
|
4661
|
+
if (is_if_control_node(node.alternate)) {
|
|
4268
4662
|
alternate = render_if_statement_to_conditional_expression(node.alternate);
|
|
4269
4663
|
if (!alternate) return null;
|
|
4270
4664
|
} else {
|
|
@@ -4301,25 +4695,11 @@ function block_statement_to_return_expression(block) {
|
|
|
4301
4695
|
/**
|
|
4302
4696
|
* Find the first `key` attribute expression in the top-level elements of a body.
|
|
4303
4697
|
* Used to propagate keys from loop body elements to wrapper components.
|
|
4304
|
-
* Works on both pre-transform (Ripple Element) and post-transform (JSXElement) nodes.
|
|
4305
|
-
*
|
|
4306
4698
|
* @param {any[]} body_nodes
|
|
4307
4699
|
* @returns {any | undefined}
|
|
4308
4700
|
*/
|
|
4309
4701
|
function find_key_expression_in_body(body_nodes) {
|
|
4310
4702
|
for (const node of body_nodes) {
|
|
4311
|
-
// Pre-transform: Ripple Element node
|
|
4312
|
-
if (node.type === 'Element') {
|
|
4313
|
-
for (const attr of node.attributes || []) {
|
|
4314
|
-
if (attr.type === 'Attribute') {
|
|
4315
|
-
const attr_name = typeof attr.name === 'string' ? attr.name : attr.name?.name;
|
|
4316
|
-
if (attr_name === 'key') {
|
|
4317
|
-
return attr.value?.expression ?? attr.value;
|
|
4318
|
-
}
|
|
4319
|
-
}
|
|
4320
|
-
}
|
|
4321
|
-
}
|
|
4322
|
-
// Post-transform: JSXElement node
|
|
4323
4703
|
if (node.type === 'JSXElement') {
|
|
4324
4704
|
for (const attr of node.openingElement?.attributes || []) {
|
|
4325
4705
|
if (
|
|
@@ -4344,7 +4724,7 @@ function find_key_expression_in_body(body_nodes) {
|
|
|
4344
4724
|
* @returns {any}
|
|
4345
4725
|
*/
|
|
4346
4726
|
function continue_to_bare_return(source_node) {
|
|
4347
|
-
const node = set_loc(b.return(
|
|
4727
|
+
const node = set_loc(b.return(create_null_literal()), source_node);
|
|
4348
4728
|
node.metadata = {
|
|
4349
4729
|
...(node.metadata || {}),
|
|
4350
4730
|
generated_loop_continue_return: true,
|
|
@@ -4354,8 +4734,9 @@ function continue_to_bare_return(source_node) {
|
|
|
4354
4734
|
|
|
4355
4735
|
/**
|
|
4356
4736
|
* `continue` in a component `for...of` body means "skip this item". JSX targets
|
|
4357
|
-
* lower `for...of` to callbacks, so a raw ContinueStatement would be invalid JS
|
|
4358
|
-
*
|
|
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.
|
|
4359
4740
|
*
|
|
4360
4741
|
* @param {any[] | any} node
|
|
4361
4742
|
* @param {boolean} [is_root]
|
|
@@ -4363,7 +4744,9 @@ function continue_to_bare_return(source_node) {
|
|
|
4363
4744
|
*/
|
|
4364
4745
|
export function rewrite_loop_continues_to_bare_returns(node, is_root = true) {
|
|
4365
4746
|
if (Array.isArray(node)) {
|
|
4366
|
-
return node.map((child) =>
|
|
4747
|
+
return node.map((child) =>
|
|
4748
|
+
rewrite_loop_continues_to_bare_returns(child, is_root && !is_loop_statement(child)),
|
|
4749
|
+
);
|
|
4367
4750
|
}
|
|
4368
4751
|
|
|
4369
4752
|
if (!node || typeof node !== 'object') {
|
|
@@ -4388,6 +4771,145 @@ export function rewrite_loop_continues_to_bare_returns(node, is_root = true) {
|
|
|
4388
4771
|
return node;
|
|
4389
4772
|
}
|
|
4390
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
|
+
|
|
4391
4913
|
/**
|
|
4392
4914
|
* @param {any} node
|
|
4393
4915
|
* @returns {boolean}
|
|
@@ -4395,8 +4917,11 @@ export function rewrite_loop_continues_to_bare_returns(node, is_root = true) {
|
|
|
4395
4917
|
function is_loop_statement(node) {
|
|
4396
4918
|
return (
|
|
4397
4919
|
node?.type === 'ForOfStatement' ||
|
|
4920
|
+
(node?.type === 'JSXForExpression' && node.statementType === 'ForOfStatement') ||
|
|
4398
4921
|
node?.type === 'ForStatement' ||
|
|
4922
|
+
(node?.type === 'JSXForExpression' && node.statementType === 'ForStatement') ||
|
|
4399
4923
|
node?.type === 'ForInStatement' ||
|
|
4924
|
+
(node?.type === 'JSXForExpression' && node.statementType === 'ForInStatement') ||
|
|
4400
4925
|
node?.type === 'WhileStatement' ||
|
|
4401
4926
|
node?.type === 'DoWhileStatement'
|
|
4402
4927
|
);
|
|
@@ -4420,10 +4945,9 @@ function for_of_statement_to_jsx_child(node, transform_context) {
|
|
|
4420
4945
|
|
|
4421
4946
|
const loop_params = get_for_of_iteration_params(node.left, node.index);
|
|
4422
4947
|
const loop_body = /** @type {any[]} */ (
|
|
4423
|
-
|
|
4424
|
-
node.body.type === 'BlockStatement' ? node.body.body : [node.body],
|
|
4425
|
-
)
|
|
4948
|
+
node.body.type === 'BlockStatement' ? node.body.body : [node.body]
|
|
4426
4949
|
);
|
|
4950
|
+
validate_for_body_control_flow(loop_body, transform_context);
|
|
4427
4951
|
const has_hooks =
|
|
4428
4952
|
should_extract_hook_helpers(transform_context) &&
|
|
4429
4953
|
body_contains_top_level_hook_call(loop_body, transform_context, true);
|
|
@@ -4478,17 +5002,49 @@ function for_of_statement_to_jsx_child(node, transform_context) {
|
|
|
4478
5002
|
transform_context.available_bindings = saved_bindings;
|
|
4479
5003
|
|
|
4480
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;
|
|
4481
5022
|
|
|
4482
5023
|
if (transform_context.platform.imports.forOfIterableHelper) {
|
|
4483
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) {
|
|
4484
5034
|
return to_jsx_expression_container(
|
|
4485
|
-
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
|
+
),
|
|
4486
5044
|
);
|
|
4487
5045
|
}
|
|
4488
5046
|
|
|
4489
|
-
return to_jsx_expression_container(
|
|
4490
|
-
b.call(b.member(node.right, create_generated_identifier('map')), iter_callback),
|
|
4491
|
-
);
|
|
5047
|
+
return to_jsx_expression_container(map_call);
|
|
4492
5048
|
}
|
|
4493
5049
|
|
|
4494
5050
|
/**
|
|
@@ -4498,25 +5054,6 @@ function for_of_statement_to_jsx_child(node, transform_context) {
|
|
|
4498
5054
|
*/
|
|
4499
5055
|
function apply_key_to_loop_body(body_nodes, key_expression) {
|
|
4500
5056
|
for (const node of body_nodes) {
|
|
4501
|
-
if (node.type === 'Element') {
|
|
4502
|
-
const attributes = node.attributes || (node.attributes = []);
|
|
4503
|
-
const has_key = attributes.some((/** @type {any} */ attr) => {
|
|
4504
|
-
const attr_name = typeof attr.name === 'string' ? attr.name : attr.name?.name;
|
|
4505
|
-
return attr_name === 'key';
|
|
4506
|
-
});
|
|
4507
|
-
|
|
4508
|
-
if (!has_key) {
|
|
4509
|
-
attributes.push({
|
|
4510
|
-
type: 'Attribute',
|
|
4511
|
-
name: b.id('key'),
|
|
4512
|
-
value: clone_expression_node(key_expression),
|
|
4513
|
-
shorthand: false,
|
|
4514
|
-
metadata: { path: [] },
|
|
4515
|
-
});
|
|
4516
|
-
}
|
|
4517
|
-
return;
|
|
4518
|
-
}
|
|
4519
|
-
|
|
4520
5057
|
if (node.type === 'JSXElement') {
|
|
4521
5058
|
const attributes = node.openingElement?.attributes || [];
|
|
4522
5059
|
const has_key = attributes.some(
|
|
@@ -4546,7 +5083,7 @@ function apply_key_to_loop_body(body_nodes, key_expression) {
|
|
|
4546
5083
|
function should_apply_key_to_loop_body(body_nodes) {
|
|
4547
5084
|
let keyable_children = 0;
|
|
4548
5085
|
for (const node of body_nodes) {
|
|
4549
|
-
if (node.type === '
|
|
5086
|
+
if (node.type === 'JSXElement') {
|
|
4550
5087
|
keyable_children += 1;
|
|
4551
5088
|
}
|
|
4552
5089
|
}
|
|
@@ -4636,11 +5173,11 @@ function switch_statement_to_jsx_child(node, transform_context) {
|
|
|
4636
5173
|
}
|
|
4637
5174
|
|
|
4638
5175
|
/**
|
|
4639
|
-
* Transform
|
|
5176
|
+
* Transform an `@try { ... } @pending { ... } @catch (err, reset) { ... }` block
|
|
4640
5177
|
* into React `<TsrxErrorBoundary>` and/or `<Suspense>` JSX elements.
|
|
4641
5178
|
*
|
|
4642
|
-
* -
|
|
4643
|
-
* -
|
|
5179
|
+
* - `@pending` → `<Suspense fallback={...}>`
|
|
5180
|
+
* - `@catch` → `<TsrxErrorBoundary fallback={(err, reset) => ...}>`
|
|
4644
5181
|
* - both → ErrorBoundary wraps Suspense
|
|
4645
5182
|
* - JavaScript `try/finally` is not part of component template control flow
|
|
4646
5183
|
*
|
|
@@ -4684,30 +5221,6 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
4684
5221
|
);
|
|
4685
5222
|
}
|
|
4686
5223
|
|
|
4687
|
-
// Validate that try body contains JSX if pending block is present
|
|
4688
|
-
if (pending) {
|
|
4689
|
-
const try_body = node.block.body || [];
|
|
4690
|
-
if (!try_body.some(is_jsx_child)) {
|
|
4691
|
-
error(
|
|
4692
|
-
'TSRX try statements must contain a template in their main body. Move the try statement into a function if it does not render anything.',
|
|
4693
|
-
transform_context.filename,
|
|
4694
|
-
node.block,
|
|
4695
|
-
transform_context.errors,
|
|
4696
|
-
transform_context.comments,
|
|
4697
|
-
);
|
|
4698
|
-
}
|
|
4699
|
-
const pending_body = pending.body || [];
|
|
4700
|
-
if (pending_body.length > 0 && !pending_body.some(is_jsx_child)) {
|
|
4701
|
-
error(
|
|
4702
|
-
'TSRX try statements must contain a template in their "pending" body. Rendering a pending fallback is required to have a template.',
|
|
4703
|
-
transform_context.filename,
|
|
4704
|
-
pending,
|
|
4705
|
-
transform_context.errors,
|
|
4706
|
-
transform_context.comments,
|
|
4707
|
-
);
|
|
4708
|
-
}
|
|
4709
|
-
}
|
|
4710
|
-
|
|
4711
5224
|
// Build the try body content as JSX children
|
|
4712
5225
|
const try_body_nodes = node.block.body || [];
|
|
4713
5226
|
const try_content = statement_body_to_jsx_child(try_body_nodes, transform_context);
|
|
@@ -4860,8 +5373,8 @@ function create_jsx_element(tag_name, attributes, children) {
|
|
|
4860
5373
|
|
|
4861
5374
|
/**
|
|
4862
5375
|
* Inject runtime-helper import declarations the transform decided it needed
|
|
4863
|
-
* during the walk: `Suspense` for
|
|
4864
|
-
* `TsrxErrorBoundary` for
|
|
5376
|
+
* during the walk: `Suspense` for `@try { ... } @pending { ... }`,
|
|
5377
|
+
* `TsrxErrorBoundary` for `@try { ... } @catch (...)`, and `mergeRefs` for
|
|
4865
5378
|
* elements with multiple `ref` attributes under the `'merge-refs'`
|
|
4866
5379
|
* strategy. Import sources are platform-specific.
|
|
4867
5380
|
*
|
|
@@ -4975,16 +5488,22 @@ function add_ref_import_specifier(imports, source, specifier) {
|
|
|
4975
5488
|
function create_render_if_statement(node, transform_context) {
|
|
4976
5489
|
const consequent_body =
|
|
4977
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
|
+
}
|
|
4978
5494
|
const consequent_has_hooks =
|
|
4979
5495
|
should_extract_hook_helpers(transform_context) &&
|
|
4980
5496
|
body_contains_top_level_hook_call(consequent_body, transform_context, true);
|
|
4981
5497
|
|
|
4982
5498
|
let alternate = null;
|
|
4983
5499
|
if (node.alternate) {
|
|
4984
|
-
if (node.alternate
|
|
5500
|
+
if (is_if_control_node(node.alternate)) {
|
|
4985
5501
|
alternate = create_render_if_statement(node.alternate, transform_context);
|
|
4986
5502
|
} else {
|
|
4987
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
|
+
}
|
|
4988
5507
|
const alternate_has_hooks =
|
|
4989
5508
|
should_extract_hook_helpers(transform_context) &&
|
|
4990
5509
|
body_contains_top_level_hook_call(alternate_body, transform_context, true);
|
|
@@ -5021,9 +5540,8 @@ function create_render_if_statement(node, transform_context) {
|
|
|
5021
5540
|
* case body needs to be hoisted into its own helper component or can stay
|
|
5022
5541
|
* inline.
|
|
5023
5542
|
*
|
|
5024
|
-
* `own_body` is
|
|
5025
|
-
*
|
|
5026
|
-
* 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.
|
|
5027
5545
|
*
|
|
5028
5546
|
* @param {any[]} consequent
|
|
5029
5547
|
* @returns {{ own_body: any[], has_terminator: boolean }}
|
|
@@ -5032,10 +5550,6 @@ function summarize_switch_case_body(consequent) {
|
|
|
5032
5550
|
const own_body = [];
|
|
5033
5551
|
let has_terminator = false;
|
|
5034
5552
|
for (const child of consequent) {
|
|
5035
|
-
if (child.type === 'BreakStatement') {
|
|
5036
|
-
has_terminator = true;
|
|
5037
|
-
break;
|
|
5038
|
-
}
|
|
5039
5553
|
if (child.type === 'ReturnStatement' && child.argument == null) {
|
|
5040
5554
|
has_terminator = true;
|
|
5041
5555
|
break;
|
|
@@ -5068,11 +5582,10 @@ export function clone_switch_helper_invocation(helper) {
|
|
|
5068
5582
|
|
|
5069
5583
|
/**
|
|
5070
5584
|
* Plan the switch lift: decide which case bodies to hoist into their own
|
|
5071
|
-
* helper components
|
|
5072
|
-
*
|
|
5073
|
-
*
|
|
5074
|
-
*
|
|
5075
|
-
* 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.
|
|
5076
5589
|
*
|
|
5077
5590
|
* Returned helpers — when non-null — are already constructed via
|
|
5078
5591
|
* `create_hook_safe_helper`, which is the same path hook-bearing case bodies
|
|
@@ -5086,7 +5599,6 @@ export function clone_switch_helper_invocation(helper) {
|
|
|
5086
5599
|
* @returns {{
|
|
5087
5600
|
* case_info: Array<{ own_body: any[], has_terminator: boolean }>,
|
|
5088
5601
|
* case_helpers: Array<{ setup_statements: any[], component_element: ESTreeJSX.JSXElement } | null>,
|
|
5089
|
-
* find_next_helper_after: (from_index: number) => { component_element: ESTreeJSX.JSXElement } | null,
|
|
5090
5602
|
* setup_statements: any[],
|
|
5091
5603
|
* }}
|
|
5092
5604
|
*/
|
|
@@ -5096,22 +5608,15 @@ export function plan_switch_lift(switch_node, transform_context) {
|
|
|
5096
5608
|
return summarize_switch_case_body(consequent);
|
|
5097
5609
|
});
|
|
5098
5610
|
|
|
5099
|
-
// A case body needs to be lifted iff
|
|
5100
|
-
//
|
|
5101
|
-
// went through the lift pipeline before this change). Duplication happens
|
|
5102
|
-
// exactly when the previous case has no terminator — that's the only way
|
|
5103
|
-
// 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.
|
|
5104
5613
|
const needs_helper = case_info.map(
|
|
5105
|
-
(/** @type {{ own_body: any[], has_terminator: boolean }} */ info
|
|
5614
|
+
(/** @type {{ own_body: any[], has_terminator: boolean }} */ info) => {
|
|
5106
5615
|
if (info.own_body.length === 0) return false;
|
|
5107
|
-
|
|
5616
|
+
return (
|
|
5108
5617
|
should_extract_hook_helpers(transform_context) &&
|
|
5109
5618
|
body_contains_top_level_hook_call(info.own_body, transform_context, true)
|
|
5110
|
-
)
|
|
5111
|
-
return true;
|
|
5112
|
-
}
|
|
5113
|
-
if (k === 0) return false;
|
|
5114
|
-
return !case_info[k - 1].has_terminator;
|
|
5619
|
+
);
|
|
5115
5620
|
},
|
|
5116
5621
|
);
|
|
5117
5622
|
|
|
@@ -5128,37 +5633,12 @@ export function plan_switch_lift(switch_node, transform_context) {
|
|
|
5128
5633
|
/** @type {Array<{ setup_statements: any[], component_element: ESTreeJSX.JSXElement } | null>} */
|
|
5129
5634
|
const case_helpers = new Array(switch_node.cases.length).fill(null);
|
|
5130
5635
|
|
|
5131
|
-
/**
|
|
5132
|
-
* Find the next downstream helper this arm chains into when it has no
|
|
5133
|
-
* terminator: scan forward past any empty cases until we hit either a
|
|
5134
|
-
* helper-bearing case or a case whose body has a terminator (which stops
|
|
5135
|
-
* the chain — JS would have `break`/`return`ed out at that point).
|
|
5136
|
-
*
|
|
5137
|
-
* @param {number} from_index
|
|
5138
|
-
* @returns {{ component_element: ESTreeJSX.JSXElement } | null}
|
|
5139
|
-
*/
|
|
5140
|
-
function find_next_helper_after(from_index) {
|
|
5141
|
-
for (let j = from_index + 1; j < switch_node.cases.length; j++) {
|
|
5142
|
-
if (case_helpers[j]) return case_helpers[j];
|
|
5143
|
-
if (case_info[j].has_terminator) return null;
|
|
5144
|
-
}
|
|
5145
|
-
return null;
|
|
5146
|
-
}
|
|
5147
|
-
|
|
5148
5636
|
for (let i = switch_node.cases.length - 1; i >= 0; i--) {
|
|
5149
5637
|
if (!needs_helper[i]) continue;
|
|
5150
|
-
const { own_body
|
|
5151
|
-
|
|
5152
|
-
let helper_body = own_body;
|
|
5153
|
-
if (!has_terminator) {
|
|
5154
|
-
const next_helper = find_next_helper_after(i);
|
|
5155
|
-
if (next_helper) {
|
|
5156
|
-
helper_body = [...own_body, clone_switch_helper_invocation(next_helper)];
|
|
5157
|
-
}
|
|
5158
|
-
}
|
|
5638
|
+
const { own_body } = case_info[i];
|
|
5159
5639
|
|
|
5160
5640
|
case_helpers[i] = create_hook_safe_helper(
|
|
5161
|
-
|
|
5641
|
+
own_body,
|
|
5162
5642
|
undefined,
|
|
5163
5643
|
switch_node.cases[i],
|
|
5164
5644
|
transform_context,
|
|
@@ -5176,40 +5656,17 @@ export function plan_switch_lift(switch_node, transform_context) {
|
|
|
5176
5656
|
return {
|
|
5177
5657
|
case_info,
|
|
5178
5658
|
case_helpers,
|
|
5179
|
-
find_next_helper_after,
|
|
5180
5659
|
setup_statements,
|
|
5181
5660
|
};
|
|
5182
5661
|
}
|
|
5183
5662
|
|
|
5184
5663
|
/**
|
|
5185
|
-
* Switch lift for fall-through deduplication. Reuses the same `create_hook_safe_helper`
|
|
5186
|
-
* pipeline as hook-bearing case bodies: every case whose body would otherwise
|
|
5187
|
-
* appear in 2+ arms (because the previous case had no `break` / `return`) is
|
|
5188
|
-
* hoisted into its own helper component, and each upstream arm references the
|
|
5189
|
-
* next helper at the end of its own body to materialize JS fall-through at
|
|
5190
|
-
* render time. Cases whose bodies live in exactly one arm stay inline so the
|
|
5191
|
-
* common (break-terminated) shape compiles to the same simple switch as before
|
|
5192
|
-
* the lift was introduced.
|
|
5193
|
-
*
|
|
5194
|
-
* The chain pattern:
|
|
5195
|
-
* helper_idle = () => <><Online/><Helper_active/></>
|
|
5196
|
-
* helper_active = () => <><Away/><Helper_offline/></>
|
|
5197
|
-
* helper_offline = () => <Offline/>
|
|
5198
|
-
*
|
|
5199
|
-
* case "idle": return <Helper_idle/>
|
|
5200
|
-
* case "active": return <Helper_active/>
|
|
5201
|
-
* case "offline": return <Helper_offline/>
|
|
5202
|
-
*
|
|
5203
|
-
* Each case body appears exactly once in the generated module — matching how
|
|
5204
|
-
* we already handle hook-bearing case bodies — which keeps the bundle from
|
|
5205
|
-
* growing quadratically in case count and means editor mappings are 1:1.
|
|
5206
|
-
*
|
|
5207
5664
|
* @param {any} switch_node
|
|
5208
5665
|
* @param {TransformContext} transform_context
|
|
5209
5666
|
* @returns {{ setup_statements: any[], switch_statement: any }}
|
|
5210
5667
|
*/
|
|
5211
5668
|
function build_switch_with_lift(switch_node, transform_context) {
|
|
5212
|
-
const { case_info, case_helpers,
|
|
5669
|
+
const { case_info, case_helpers, setup_statements } = plan_switch_lift(
|
|
5213
5670
|
switch_node,
|
|
5214
5671
|
transform_context,
|
|
5215
5672
|
);
|
|
@@ -5229,10 +5686,10 @@ function build_switch_with_lift(switch_node, transform_context) {
|
|
|
5229
5686
|
const { own_body, has_terminator } = case_info[i];
|
|
5230
5687
|
|
|
5231
5688
|
if (own_body.length === 0 && !has_terminator) {
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5689
|
+
return set_loc(
|
|
5690
|
+
b.switch_case(original_case.test, [create_null_return_statement()]),
|
|
5691
|
+
original_case,
|
|
5692
|
+
);
|
|
5236
5693
|
}
|
|
5237
5694
|
|
|
5238
5695
|
const case_body = [];
|
|
@@ -5259,27 +5716,13 @@ function build_switch_with_lift(switch_node, transform_context) {
|
|
|
5259
5716
|
}
|
|
5260
5717
|
}
|
|
5261
5718
|
|
|
5262
|
-
if (!has_terminal && !has_terminator) {
|
|
5263
|
-
const next_helper = find_next_helper_after(i);
|
|
5264
|
-
if (next_helper) {
|
|
5265
|
-
render_nodes.push(clone_switch_helper_invocation(next_helper));
|
|
5266
|
-
}
|
|
5267
|
-
}
|
|
5268
|
-
|
|
5269
5719
|
if (!has_terminal) {
|
|
5270
5720
|
if (render_nodes.length > 0) {
|
|
5271
5721
|
case_body.push(create_component_return_statement(render_nodes, original_case));
|
|
5272
|
-
} else if (has_terminator) {
|
|
5273
|
-
// Empty body with explicit `break;` / bare `return;` — keep
|
|
5274
|
-
// a `break` so JS doesn't fall through into the next case
|
|
5275
|
-
// (which may now hold the lifted helper invocation).
|
|
5276
|
-
case_body.push(b.break);
|
|
5277
5722
|
} else if (case_body.length > 0) {
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
// from re-running downstream statements via JS fall-through.
|
|
5282
|
-
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());
|
|
5283
5726
|
}
|
|
5284
5727
|
}
|
|
5285
5728
|
|
|
@@ -5356,12 +5799,12 @@ function transform_element_attributes_dispatch(attrs, transform_context, element
|
|
|
5356
5799
|
* @returns {boolean}
|
|
5357
5800
|
*/
|
|
5358
5801
|
export function is_component_like_element(element) {
|
|
5359
|
-
const
|
|
5360
|
-
if (!
|
|
5361
|
-
if (
|
|
5362
|
-
if (
|
|
5363
|
-
if (
|
|
5364
|
-
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;
|
|
5365
5808
|
return false;
|
|
5366
5809
|
}
|
|
5367
5810
|
|
|
@@ -5514,10 +5957,7 @@ function wrap_jsx_setup_declarations(expression, in_jsx_child) {
|
|
|
5514
5957
|
/**
|
|
5515
5958
|
* Reject elements with more than one TSX-style `ref={...}` attribute.
|
|
5516
5959
|
* This validator runs over the raw, pre-lowering attribute list so each
|
|
5517
|
-
* shape is still distinguishable by `type`.
|
|
5518
|
-
* `Identifier` name (the parser normalizes `JSXAttribute`/`JSXIdentifier`
|
|
5519
|
-
* for native elements); inside `<tsx:react>` compat blocks they retain
|
|
5520
|
-
* the original `JSXAttribute`/`JSXIdentifier` shape, so we accept both.
|
|
5960
|
+
* shape is still distinguishable by `type`.
|
|
5521
5961
|
*
|
|
5522
5962
|
* @param {any[]} raw_attrs
|
|
5523
5963
|
* @param {TransformContext} [transform_context]
|
|
@@ -5528,14 +5968,10 @@ export function validate_at_most_one_ref_attribute(raw_attrs, transform_context)
|
|
|
5528
5968
|
for (const attr of raw_attrs) {
|
|
5529
5969
|
if (!attr) continue;
|
|
5530
5970
|
const is_ref_attr =
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
(attr.type === 'JSXAttribute' &&
|
|
5536
|
-
attr.name &&
|
|
5537
|
-
attr.name.type === 'JSXIdentifier' &&
|
|
5538
|
-
attr.name.name === 'ref');
|
|
5971
|
+
attr.type === 'JSXAttribute' &&
|
|
5972
|
+
attr.name &&
|
|
5973
|
+
attr.name.type === 'JSXIdentifier' &&
|
|
5974
|
+
attr.name.name === 'ref';
|
|
5539
5975
|
if (!is_ref_attr) continue;
|
|
5540
5976
|
refs.push(attr.name);
|
|
5541
5977
|
}
|
|
@@ -5747,8 +6183,6 @@ function infer_ref_namespace(tag_name) {
|
|
|
5747
6183
|
* @returns {string | null}
|
|
5748
6184
|
*/
|
|
5749
6185
|
function get_element_ref_tag_name(element) {
|
|
5750
|
-
const id = element?.id;
|
|
5751
|
-
if (id?.type === 'Identifier') return id.name;
|
|
5752
6186
|
const name = element?.name;
|
|
5753
6187
|
if (name?.type === 'JSXIdentifier') return name.name;
|
|
5754
6188
|
if (element?.openingElement?.name?.type === 'JSXIdentifier') {
|
|
@@ -5784,15 +6218,6 @@ export function to_jsx_attribute(attr, transform_context) {
|
|
|
5784
6218
|
if (attr.type === 'JSXSpreadAttribute') {
|
|
5785
6219
|
return attr;
|
|
5786
6220
|
}
|
|
5787
|
-
if (attr.type === 'SpreadAttribute') {
|
|
5788
|
-
return set_loc(
|
|
5789
|
-
/** @type {any} */ ({
|
|
5790
|
-
type: 'JSXSpreadAttribute',
|
|
5791
|
-
argument: attr.argument,
|
|
5792
|
-
}),
|
|
5793
|
-
attr,
|
|
5794
|
-
);
|
|
5795
|
-
}
|
|
5796
6221
|
// Keep this legacy hook for targets that need React-style DOM attrs. The
|
|
5797
6222
|
// current first-party targets preserve authored `class`.
|
|
5798
6223
|
let attr_name = attr.name;
|
|
@@ -5844,25 +6269,29 @@ function value_has_unmappable_jsx_loc(value) {
|
|
|
5844
6269
|
/**
|
|
5845
6270
|
* @param {any} node
|
|
5846
6271
|
* @param {TransformContext} transform_context
|
|
5847
|
-
* @
|
|
6272
|
+
* @param {boolean} in_jsx_child
|
|
6273
|
+
* @returns {any}
|
|
5848
6274
|
*/
|
|
5849
|
-
function
|
|
5850
|
-
const
|
|
5851
|
-
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
|
+
);
|
|
5852
6282
|
const jsx_element = create_dynamic_jsx_element(dynamic_id, node, transform_context);
|
|
5853
6283
|
|
|
5854
|
-
|
|
5855
|
-
b.
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
]),
|
|
5862
|
-
),
|
|
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
|
+
]),
|
|
5863
6291
|
),
|
|
5864
|
-
node,
|
|
5865
6292
|
);
|
|
6293
|
+
|
|
6294
|
+
return in_jsx_child ? to_jsx_expression_container(expression, node) : set_loc(expression, node);
|
|
5866
6295
|
}
|
|
5867
6296
|
|
|
5868
6297
|
/**
|
|
@@ -5873,11 +6302,11 @@ function dynamic_element_to_jsx_child(node, transform_context) {
|
|
|
5873
6302
|
*/
|
|
5874
6303
|
function create_dynamic_jsx_element(dynamic_id, node, transform_context) {
|
|
5875
6304
|
const attributes = transform_element_attributes_dispatch(
|
|
5876
|
-
node.attributes || [],
|
|
6305
|
+
node.openingElement?.attributes || [],
|
|
5877
6306
|
transform_context,
|
|
5878
6307
|
node,
|
|
5879
6308
|
);
|
|
5880
|
-
const selfClosing = !!node.selfClosing;
|
|
6309
|
+
const selfClosing = !!node.openingElement?.selfClosing;
|
|
5881
6310
|
const children = create_element_children(node.children || [], transform_context);
|
|
5882
6311
|
const name = identifier_to_jsx_name(clone_identifier(dynamic_id));
|
|
5883
6312
|
|
|
@@ -5899,6 +6328,10 @@ function build_return_expression(render_nodes) {
|
|
|
5899
6328
|
if (only.type === 'JSXExpressionContainer') {
|
|
5900
6329
|
return only.expression;
|
|
5901
6330
|
}
|
|
6331
|
+
if (only.type === 'JSXText') {
|
|
6332
|
+
const value = (only.value ?? '').trim();
|
|
6333
|
+
return b.literal(value, JSON.stringify(value), only);
|
|
6334
|
+
}
|
|
5902
6335
|
return only;
|
|
5903
6336
|
}
|
|
5904
6337
|
const first = render_nodes[0];
|
|
@@ -5917,25 +6350,3 @@ function build_return_expression(render_nodes) {
|
|
|
5917
6350
|
: undefined,
|
|
5918
6351
|
);
|
|
5919
6352
|
}
|
|
5920
|
-
|
|
5921
|
-
/**
|
|
5922
|
-
* @param {any} node
|
|
5923
|
-
* @param {TransformContext} transform_context
|
|
5924
|
-
* @param {boolean} [in_jsx_child]
|
|
5925
|
-
* @returns {any}
|
|
5926
|
-
*/
|
|
5927
|
-
function tsx_compat_node_to_jsx_expression(node, transform_context, in_jsx_child = false) {
|
|
5928
|
-
const platform = transform_context.platform;
|
|
5929
|
-
if (!platform.jsx.acceptedTsxKinds.includes(node.kind)) {
|
|
5930
|
-
const accepted = platform.jsx.acceptedTsxKinds.map((k) => `<tsx:${k}>`).join(', ');
|
|
5931
|
-
error(
|
|
5932
|
-
`${platform.name} TSRX does not support <tsx:${node.kind}> blocks. Use one of: ${accepted}.`,
|
|
5933
|
-
transform_context.filename,
|
|
5934
|
-
node,
|
|
5935
|
-
transform_context.errors,
|
|
5936
|
-
transform_context.comments,
|
|
5937
|
-
);
|
|
5938
|
-
}
|
|
5939
|
-
|
|
5940
|
-
return tsx_node_to_jsx_expression(node, in_jsx_child);
|
|
5941
|
-
}
|