@tsrx/core 0.1.47 → 0.1.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.js +1 -1
- package/src/plugin.js +177 -42
- package/src/transform/await.js +14 -12
- package/src/transform/imports.js +96 -0
- package/src/transform/jsx/ast-builders.js +2 -81
- package/src/transform/jsx/helpers.js +13 -7
- package/src/transform/jsx/index.js +951 -727
- package/src/transform/jsx-hoist.js +2 -1
- package/src/transform/jsx-interleave.js +22 -36
- package/src/transform/scoping.js +4 -2
- package/src/transform/segments.js +20 -6
- package/src/utils/ast.js +37 -0
- package/src/utils/builders.js +22 -15
- package/types/helpers.d.ts +1 -2
- package/types/index.d.ts +88 -17
- package/types/jsx-platform.d.ts +18 -8
- package/types/parse.d.ts +66 -8
- package/types/runtime/ref.d.ts +1 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @import * as AST from 'estree' */
|
|
2
2
|
/** @import * as ESTreeJSX from 'estree-jsx' */
|
|
3
|
-
/** @import { JsxHelperComponent, JsxPlatform, JsxTransformContext as TransformContext, JsxTransformOptions, JsxTransformResult } from '@tsrx/core/types' */
|
|
3
|
+
/** @import { BaseNodeMetaData, FunctionMetaData, JsxHelperComponent, JsxHelperState, JsxPlatform, JsxStyleContext, JsxTransformContext as TransformContext, JsxTransformOptions, JsxTransformResult, JsxVisitorContext } from '@tsrx/core/types' */
|
|
4
4
|
|
|
5
5
|
import { walk } from 'zimmerframe';
|
|
6
6
|
import { print } from 'esrap';
|
|
@@ -28,12 +28,9 @@ import {
|
|
|
28
28
|
flatten_switch_consequent,
|
|
29
29
|
get_for_of_iteration_params,
|
|
30
30
|
identifier_to_jsx_identifier,
|
|
31
|
-
identifier_to_jsx_name,
|
|
32
31
|
is_bare_render_expression,
|
|
33
32
|
is_component_jsx_name,
|
|
34
|
-
is_jsx_child,
|
|
35
33
|
set_loc,
|
|
36
|
-
to_text_expression,
|
|
37
34
|
} from './ast-builders.js';
|
|
38
35
|
import { render_css_result } from '../stylesheet.js';
|
|
39
36
|
import {
|
|
@@ -63,7 +60,10 @@ import {
|
|
|
63
60
|
import { is_hoist_safe_jsx_node } from '../jsx-hoist.js';
|
|
64
61
|
import { lower_server_module_for_types } from './server-module.js';
|
|
65
62
|
import {
|
|
63
|
+
child_nodes,
|
|
66
64
|
has_location,
|
|
65
|
+
is_ast_node,
|
|
66
|
+
is_function_node,
|
|
67
67
|
is_function_or_class_node as is_function_or_class_boundary,
|
|
68
68
|
is_template_directive as is_jsx_control_flow_expression,
|
|
69
69
|
} from '../../utils/ast.js';
|
|
@@ -109,20 +109,15 @@ function report_jsx_fragment_in_tsrx_error(node, transform_context) {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
|
-
* @param {
|
|
112
|
+
* @param {AST.Node} node
|
|
113
113
|
* @param {boolean} [inside_function]
|
|
114
|
-
* @param {Set<
|
|
114
|
+
* @param {Set<AST.Node>} [seen]
|
|
115
115
|
* @returns {void}
|
|
116
116
|
*/
|
|
117
117
|
function mark_nested_function_return_jsx(node, inside_function = false, seen = new Set()) {
|
|
118
|
-
if (
|
|
118
|
+
if (seen.has(node)) return;
|
|
119
119
|
seen.add(node);
|
|
120
120
|
|
|
121
|
-
if (Array.isArray(node)) {
|
|
122
|
-
for (const item of node) mark_nested_function_return_jsx(item, inside_function, seen);
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
121
|
const now_inside = inside_function || is_function_or_class_boundary(node);
|
|
127
122
|
|
|
128
123
|
if (
|
|
@@ -132,12 +127,11 @@ function mark_nested_function_return_jsx(node, inside_function = false, seen = n
|
|
|
132
127
|
node.argument?.type === 'JSXElement' ||
|
|
133
128
|
node.argument?.type === 'JSXStyleElement')
|
|
134
129
|
) {
|
|
135
|
-
node.argument.metadata = { ...
|
|
130
|
+
node.argument.metadata = { ...node.argument.metadata, native_tsrx: true };
|
|
136
131
|
}
|
|
137
132
|
|
|
138
|
-
for (const
|
|
139
|
-
|
|
140
|
-
mark_nested_function_return_jsx(node[key], now_inside, seen);
|
|
133
|
+
for (const child of child_nodes(node)) {
|
|
134
|
+
mark_nested_function_return_jsx(child, now_inside, seen);
|
|
141
135
|
}
|
|
142
136
|
}
|
|
143
137
|
|
|
@@ -157,12 +151,13 @@ function mark_nested_function_return_jsx(node, inside_function = false, seen = n
|
|
|
157
151
|
* (`transform_jsx_code_block` / `build_render_statements`).
|
|
158
152
|
*
|
|
159
153
|
* Always returns zero or one node.
|
|
160
|
-
* @param {
|
|
161
|
-
* @returns {
|
|
154
|
+
* @param {AST.JSXCodeBlock} block
|
|
155
|
+
* @returns {AST.Node[]}
|
|
162
156
|
*/
|
|
163
157
|
function lower_code_block_child(block) {
|
|
164
158
|
const body = block.body || [];
|
|
165
159
|
const render = block.render ?? null;
|
|
160
|
+
const block_loc = has_location(block) ? block : undefined;
|
|
166
161
|
|
|
167
162
|
if (body.length === 0) {
|
|
168
163
|
if (render == null) return [];
|
|
@@ -173,10 +168,10 @@ function lower_code_block_child(block) {
|
|
|
173
168
|
if (render?.type === 'JSXCodeBlock') {
|
|
174
169
|
const inner = lower_code_block_child(render);
|
|
175
170
|
if (inner.length === 0) {
|
|
176
|
-
return [b.block(body,
|
|
171
|
+
return [b.block(body, block_loc)];
|
|
177
172
|
}
|
|
178
173
|
if (inner[0].type === 'BlockStatement') {
|
|
179
|
-
return [b.block([...body, inner[0]],
|
|
174
|
+
return [b.block([...body, inner[0]], block_loc)];
|
|
180
175
|
}
|
|
181
176
|
// The chain still renders — simplify the render to the lowered inner
|
|
182
177
|
// node and leave the block for the context-aware lowering.
|
|
@@ -184,7 +179,7 @@ function lower_code_block_child(block) {
|
|
|
184
179
|
}
|
|
185
180
|
|
|
186
181
|
if (render == null) {
|
|
187
|
-
return [b.block(body,
|
|
182
|
+
return [b.block(body, block_loc)];
|
|
188
183
|
}
|
|
189
184
|
|
|
190
185
|
return [block];
|
|
@@ -199,49 +194,57 @@ function lower_code_block_child(block) {
|
|
|
199
194
|
* The input tree is never mutated: replacements land on a shallow copy of the
|
|
200
195
|
* owning node (or array), so the return value must be used in place of the
|
|
201
196
|
* argument. Untouched subtrees are shared by reference with the input.
|
|
202
|
-
*
|
|
203
|
-
* @
|
|
204
|
-
* @
|
|
197
|
+
*
|
|
198
|
+
* @template {AST.Node} T
|
|
199
|
+
* @param {T} node
|
|
200
|
+
* @param {Set<AST.Node>} [seen]
|
|
201
|
+
* @returns {T}
|
|
205
202
|
*/
|
|
206
203
|
function expand_child_code_blocks(node, seen = new Set()) {
|
|
207
|
-
if (
|
|
204
|
+
if (seen.has(node)) return node;
|
|
208
205
|
seen.add(node);
|
|
209
206
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const walked = expand_child_code_blocks(item, seen);
|
|
214
|
-
if (walked !== item) changed = true;
|
|
215
|
-
return walked;
|
|
216
|
-
});
|
|
217
|
-
return changed ? result : node;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
let out = node;
|
|
221
|
-
const set = (/** @type {string} */ key, /** @type {any} */ value) => {
|
|
207
|
+
const source = /** @type {AST.TraversableAstNode} */ (node);
|
|
208
|
+
let out = source;
|
|
209
|
+
const set = (/** @type {string} */ key, /** @type {unknown} */ value) => {
|
|
222
210
|
if (out[key] === value) return;
|
|
223
|
-
if (out ===
|
|
211
|
+
if (out === source) out = { ...source };
|
|
224
212
|
out[key] = value;
|
|
225
213
|
};
|
|
226
214
|
|
|
215
|
+
const children = source.children;
|
|
227
216
|
if (
|
|
228
|
-
Array.isArray(
|
|
229
|
-
|
|
217
|
+
Array.isArray(children) &&
|
|
218
|
+
children.some((c) => is_ast_node(c) && c.type === 'JSXCodeBlock')
|
|
230
219
|
) {
|
|
231
220
|
set(
|
|
232
221
|
'children',
|
|
233
|
-
|
|
234
|
-
child
|
|
222
|
+
children.flatMap((child) =>
|
|
223
|
+
is_ast_node(child) && child.type === 'JSXCodeBlock'
|
|
224
|
+
? lower_code_block_child(child)
|
|
225
|
+
: [child],
|
|
235
226
|
),
|
|
236
227
|
);
|
|
237
228
|
}
|
|
238
229
|
|
|
239
230
|
for (const key of Object.keys(out)) {
|
|
240
231
|
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') continue;
|
|
241
|
-
|
|
232
|
+
const value = out[key];
|
|
233
|
+
if (Array.isArray(value)) {
|
|
234
|
+
let changed = false;
|
|
235
|
+
const result = value.map((entry) => {
|
|
236
|
+
if (!is_ast_node(entry)) return entry;
|
|
237
|
+
const walked = expand_child_code_blocks(entry, seen);
|
|
238
|
+
if (walked !== entry) changed = true;
|
|
239
|
+
return walked;
|
|
240
|
+
});
|
|
241
|
+
if (changed) set(key, result);
|
|
242
|
+
} else if (is_ast_node(value)) {
|
|
243
|
+
set(key, expand_child_code_blocks(value, seen));
|
|
244
|
+
}
|
|
242
245
|
}
|
|
243
246
|
|
|
244
|
-
return out;
|
|
247
|
+
return /** @type {T} */ (out);
|
|
245
248
|
}
|
|
246
249
|
|
|
247
250
|
/**
|
|
@@ -250,8 +253,8 @@ function expand_child_code_blocks(node, seen = new Set()) {
|
|
|
250
253
|
* GENERATED wrapper (it wraps a control-flow directive / render output so it
|
|
251
254
|
* lowers to a value) — it is marked `tsrx_generated_wrapper` so the single-child
|
|
252
255
|
* collapse keeps unwrapping it, unlike an AUTHORED `<> … </>` which is kept.
|
|
253
|
-
* @param {
|
|
254
|
-
* @returns {
|
|
256
|
+
* @param {AST.Node} node
|
|
257
|
+
* @returns {AST.TSRXJSXFragment}
|
|
255
258
|
*/
|
|
256
259
|
function wrap_in_native_tsrx_fragment(node) {
|
|
257
260
|
const fragment = b.jsx_fragment([node]);
|
|
@@ -267,8 +270,8 @@ function wrap_in_native_tsrx_fragment(node) {
|
|
|
267
270
|
* An AUTHORED `<> … </>` fragment (not a compiler-generated wrapper, nor a Ripple
|
|
268
271
|
* code-block-chain wrapper). These are kept verbatim in the output instead of
|
|
269
272
|
* being unwrapped to their single child.
|
|
270
|
-
* @param {
|
|
271
|
-
* @returns {
|
|
273
|
+
* @param {AST.Node | null | undefined} node
|
|
274
|
+
* @returns {node is AST.TSRXJSXFragment}
|
|
272
275
|
*/
|
|
273
276
|
function is_authored_native_fragment(node) {
|
|
274
277
|
return (
|
|
@@ -285,7 +288,7 @@ function is_authored_native_fragment(node) {
|
|
|
285
288
|
* stray "control flow used as a value". Everything else is a value position:
|
|
286
289
|
* an unhandled control-flow directive there is the raw-value error case
|
|
287
290
|
* (a `@for` iterable, an `@if`/`@switch` test, etc.).
|
|
288
|
-
* @param {
|
|
291
|
+
* @param {AST.Node | null | undefined} parent
|
|
289
292
|
* @param {string} key
|
|
290
293
|
* @returns {boolean}
|
|
291
294
|
*/
|
|
@@ -312,7 +315,7 @@ function is_statement_or_template_slot(parent, key) {
|
|
|
312
315
|
* (`const x = @switch …`, `() => @if …`, `return @if …`, `render(@for …)`),
|
|
313
316
|
* distinct from combining a directive INTO an expression (an operator operand, a
|
|
314
317
|
* `@for` iterable, an `@if`/`@switch` test), which is an error.
|
|
315
|
-
* @param {
|
|
318
|
+
* @param {AST.Node | null | undefined} parent
|
|
316
319
|
* @param {string} key
|
|
317
320
|
* @returns {boolean}
|
|
318
321
|
*/
|
|
@@ -344,8 +347,8 @@ function is_render_output_value_slot(parent, key) {
|
|
|
344
347
|
* collapse is NOT invisible: a fragment is always a truthy element, but its
|
|
345
348
|
* collapsed content may be falsy, so `<>{0}</> || 'x'` (renders `0`) must not turn
|
|
346
349
|
* into `0 || 'x'` (renders `'x'`). Keep the fragment in these positions.
|
|
347
|
-
* @param {
|
|
348
|
-
* @param {
|
|
350
|
+
* @param {AST.Node | null | undefined} parent
|
|
351
|
+
* @param {AST.Node} child
|
|
349
352
|
* @returns {boolean}
|
|
350
353
|
*/
|
|
351
354
|
function is_combined_expression_position(parent, child) {
|
|
@@ -358,7 +361,7 @@ function is_combined_expression_position(parent, child) {
|
|
|
358
361
|
return parent.right !== child;
|
|
359
362
|
case 'CallExpression':
|
|
360
363
|
case 'NewExpression':
|
|
361
|
-
return !
|
|
364
|
+
return !parent.arguments.some((argument) => argument === child);
|
|
362
365
|
default:
|
|
363
366
|
return true;
|
|
364
367
|
}
|
|
@@ -368,18 +371,16 @@ function is_combined_expression_position(parent, child) {
|
|
|
368
371
|
* Re-wrap an already-lowered render value in a `<> … </>` fragment so a fragment
|
|
369
372
|
* combined into an expression keeps its fragment identity (see
|
|
370
373
|
* `is_combined_expression_position`). A value that is already a fragment is left
|
|
371
|
-
* as-is; a JSX element
|
|
372
|
-
*
|
|
373
|
-
* @param {
|
|
374
|
-
* @param {
|
|
375
|
-
* @returns {
|
|
374
|
+
* as-is; a JSX element nests directly (`<><span /></>`); any other expression
|
|
375
|
+
* goes in a `{ … }` container (`<>{0}</>`).
|
|
376
|
+
* @param {AST.Expression | ESTreeJSX.JSXExpressionContainer} expression
|
|
377
|
+
* @param {AST.Node} source
|
|
378
|
+
* @returns {AST.TSRXJSXFragment}
|
|
376
379
|
*/
|
|
377
380
|
function wrap_lowered_value_in_fragment(expression, source) {
|
|
378
|
-
if (expression
|
|
381
|
+
if (expression.type === 'JSXFragment') return expression;
|
|
379
382
|
const child =
|
|
380
|
-
expression
|
|
381
|
-
expression?.type === 'JSXText' ||
|
|
382
|
-
expression?.type === 'JSXExpressionContainer'
|
|
383
|
+
expression.type === 'JSXElement' || expression.type === 'JSXExpressionContainer'
|
|
383
384
|
? expression
|
|
384
385
|
: to_jsx_expression_container(expression, source);
|
|
385
386
|
return set_loc(b.jsx_fragment([child]), has_location(source) ? source : undefined);
|
|
@@ -408,13 +409,14 @@ function wrap_lowered_value_in_fragment(expression, source) {
|
|
|
408
409
|
* the owning node (or array), so the return value must be used in place of the
|
|
409
410
|
* argument. Untouched subtrees are shared by reference with the input.
|
|
410
411
|
*
|
|
411
|
-
* @
|
|
412
|
+
* @template {AST.Node} T
|
|
413
|
+
* @param {T} node
|
|
412
414
|
* @param {TransformContext} transform_context
|
|
413
|
-
* @param {Set<
|
|
414
|
-
* @returns {
|
|
415
|
+
* @param {Set<AST.Node>} [seen]
|
|
416
|
+
* @returns {T}
|
|
415
417
|
*/
|
|
416
418
|
function wrap_control_flow_expression_values(node, transform_context, seen = new Set()) {
|
|
417
|
-
if (
|
|
419
|
+
if (seen.has(node)) return node;
|
|
418
420
|
seen.add(node);
|
|
419
421
|
|
|
420
422
|
// Dynamic tags on factory platforms must lower before control-flow
|
|
@@ -426,8 +428,13 @@ function wrap_control_flow_expression_values(node, transform_context, seen = new
|
|
|
426
428
|
// carrying the raw dynamic tag. Alias lowerings return a replacement
|
|
427
429
|
// fragment, which is swapped into the child's position here.
|
|
428
430
|
const lower_dynamic = !!transform_context?.platform?.imports?.dynamicFactory;
|
|
429
|
-
|
|
430
|
-
|
|
431
|
+
/**
|
|
432
|
+
* @template {AST.Node} C
|
|
433
|
+
* @param {C} child
|
|
434
|
+
* @returns {C | AST.TSRXJSXElement | AST.TSRXJSXFragment}
|
|
435
|
+
*/
|
|
436
|
+
const lower_child = (child) => {
|
|
437
|
+
if (!lower_dynamic || child.type !== 'JSXElement') return child;
|
|
431
438
|
return lower_dynamic_jsx_element(child, transform_context) ?? child;
|
|
432
439
|
};
|
|
433
440
|
|
|
@@ -438,39 +445,36 @@ function wrap_control_flow_expression_values(node, transform_context, seen = new
|
|
|
438
445
|
// position the fragment is then KEPT (a fragment is a truthy value); in a
|
|
439
446
|
// "raw value" slot like a `@for` iterable it collapses to its rendered value
|
|
440
447
|
// (see the JSXFragment visitor and `is_combined_expression_position`).
|
|
441
|
-
|
|
442
|
-
|
|
448
|
+
/**
|
|
449
|
+
* @template {AST.Node} V
|
|
450
|
+
* @param {V} value
|
|
451
|
+
* @returns {V | AST.TSRXJSXFragment}
|
|
452
|
+
*/
|
|
453
|
+
const wrap_directive_in_expression = (value) =>
|
|
454
|
+
is_jsx_control_flow_expression(value) || value.type === 'JSXCodeBlock'
|
|
443
455
|
? wrap_in_native_tsrx_fragment(value)
|
|
444
456
|
: value;
|
|
445
457
|
|
|
446
|
-
if (Array.isArray(node)) {
|
|
447
|
-
let changed = false;
|
|
448
|
-
const result = node.map((entry) => {
|
|
449
|
-
const walked = wrap_control_flow_expression_values(
|
|
450
|
-
lower_child(entry),
|
|
451
|
-
transform_context,
|
|
452
|
-
seen,
|
|
453
|
-
);
|
|
454
|
-
if (walked !== entry) changed = true;
|
|
455
|
-
return walked;
|
|
456
|
-
});
|
|
457
|
-
return changed ? result : node;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
458
|
// Wrap a bare control-flow directive that is the sole value of a render-output
|
|
461
459
|
// slot in a native TSRX fragment, collapsing to its rendered value. (A `@{ … }`
|
|
462
460
|
// code block in the same slots already self-lowers to an IIFE, so it is left
|
|
463
461
|
// as-is.) These render-output slots are the only value positions a directive is
|
|
464
462
|
// allowed in; see `is_render_output_value_slot`.
|
|
465
|
-
|
|
463
|
+
/**
|
|
464
|
+
* @template {AST.Node} V
|
|
465
|
+
* @param {V} value
|
|
466
|
+
* @returns {V | AST.TSRXJSXFragment}
|
|
467
|
+
*/
|
|
468
|
+
const wrap_value = (value) =>
|
|
466
469
|
is_jsx_control_flow_expression(value) ? wrap_in_native_tsrx_fragment(value) : value;
|
|
467
470
|
|
|
468
471
|
// All replacements land on `out`, a shallow copy made on first write; the
|
|
469
472
|
// input node's fields are never reassigned.
|
|
470
|
-
|
|
471
|
-
|
|
473
|
+
const source = /** @type {AST.TraversableAstNode} */ (node);
|
|
474
|
+
let out = source;
|
|
475
|
+
const set = (/** @type {string} */ key, /** @type {unknown} */ value) => {
|
|
472
476
|
if (out[key] === value) return;
|
|
473
|
-
if (out ===
|
|
477
|
+
if (out === source) out = { ...source };
|
|
474
478
|
out[key] = value;
|
|
475
479
|
};
|
|
476
480
|
|
|
@@ -495,12 +499,9 @@ function wrap_control_flow_expression_values(node, transform_context, seen = new
|
|
|
495
499
|
(node.type === 'CallExpression' || node.type === 'NewExpression') &&
|
|
496
500
|
Array.isArray(node.arguments)
|
|
497
501
|
) {
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
(/** @type {any} */ argument, /** @type {number} */ i) => argument !== node.arguments[i],
|
|
502
|
-
)
|
|
503
|
-
) {
|
|
502
|
+
const args = node.arguments;
|
|
503
|
+
const wrapped = args.map(wrap_value);
|
|
504
|
+
if (wrapped.some((argument, i) => argument !== args[i])) {
|
|
504
505
|
set('arguments', wrapped);
|
|
505
506
|
}
|
|
506
507
|
}
|
|
@@ -516,6 +517,8 @@ function wrap_control_flow_expression_values(node, transform_context, seen = new
|
|
|
516
517
|
if (Array.isArray(value)) {
|
|
517
518
|
let changed = false;
|
|
518
519
|
const result = value.map((entry) => {
|
|
520
|
+
if (!is_ast_node(entry)) return entry;
|
|
521
|
+
/** @type {AST.Node} */
|
|
519
522
|
let next = lower_child(entry);
|
|
520
523
|
if (!allowed_slot) next = wrap_directive_in_expression(next);
|
|
521
524
|
next = wrap_control_flow_expression_values(next, transform_context, seen);
|
|
@@ -523,14 +526,15 @@ function wrap_control_flow_expression_values(node, transform_context, seen = new
|
|
|
523
526
|
return next;
|
|
524
527
|
});
|
|
525
528
|
if (changed) set(key, result);
|
|
526
|
-
} else {
|
|
529
|
+
} else if (is_ast_node(value)) {
|
|
530
|
+
/** @type {AST.Node} */
|
|
527
531
|
let next = lower_child(value);
|
|
528
532
|
if (!allowed_slot) next = wrap_directive_in_expression(next);
|
|
529
533
|
set(key, wrap_control_flow_expression_values(next, transform_context, seen));
|
|
530
534
|
}
|
|
531
535
|
}
|
|
532
536
|
|
|
533
|
-
return out;
|
|
537
|
+
return /** @type {T} */ (out);
|
|
534
538
|
}
|
|
535
539
|
|
|
536
540
|
/**
|
|
@@ -599,6 +603,11 @@ export function createJsxTransform(platform) {
|
|
|
599
603
|
errors: collect ? options?.errors : undefined,
|
|
600
604
|
comments: options?.comments,
|
|
601
605
|
typeOnly: !!options?.typeOnly,
|
|
606
|
+
// Opt-in navigation origins (see stamp_directive_origin). OFF by
|
|
607
|
+
// default, and the editor pipeline never asks for it: with the flag
|
|
608
|
+
// clear the emitted code, the source map and therefore every Volar
|
|
609
|
+
// mapping are byte-identical to before.
|
|
610
|
+
inspect: !!options?.inspect,
|
|
602
611
|
// Platforms can seed their own tracking state (e.g. solid's
|
|
603
612
|
// needs_show / needs_for flags) via `hooks.initialState`.
|
|
604
613
|
...(platform.hooks?.initialState?.() ?? {}),
|
|
@@ -612,19 +621,22 @@ export function createJsxTransform(platform) {
|
|
|
612
621
|
// Copy-on-write: a program without a server block passes through as the
|
|
613
622
|
// same object.
|
|
614
623
|
if (transform_context.typeOnly && platform.serverModule) {
|
|
615
|
-
ast =
|
|
616
|
-
lower_server_module_for_types(/** @type {any} */ (ast), platform.serverModule)
|
|
617
|
-
);
|
|
624
|
+
ast = lower_server_module_for_types(ast, platform.serverModule);
|
|
618
625
|
}
|
|
619
626
|
|
|
620
|
-
|
|
621
|
-
|
|
627
|
+
// Both passes are copy-on-write over arbitrary property values, so they
|
|
628
|
+
// hand back the same shape they were given.
|
|
629
|
+
ast = /** @type {AST.Program} */ (expand_child_code_blocks(ast));
|
|
630
|
+
ast = /** @type {AST.Program} */ (wrap_control_flow_expression_values(ast, transform_context));
|
|
622
631
|
|
|
623
632
|
if (!transform_context.typeOnly) {
|
|
624
|
-
preallocate_lazy_ids(
|
|
633
|
+
preallocate_lazy_ids(ast, transform_context);
|
|
625
634
|
}
|
|
626
635
|
|
|
627
|
-
|
|
636
|
+
// Walked as `AST.Node` rather than `AST.Program`: zimmerframe keys its
|
|
637
|
+
// visitor map off the node type it is given, and only the `Node` union
|
|
638
|
+
// admits a visitor per node type.
|
|
639
|
+
const transformed = walk(/** @type {AST.Node} */ (ast), transform_context, {
|
|
628
640
|
_(node, { next, path }) {
|
|
629
641
|
set_node_path_metadata(node, path);
|
|
630
642
|
return next();
|
|
@@ -635,28 +647,33 @@ export function createJsxTransform(platform) {
|
|
|
635
647
|
return next() ?? node;
|
|
636
648
|
}
|
|
637
649
|
|
|
638
|
-
const parent =
|
|
639
|
-
if (
|
|
640
|
-
|
|
650
|
+
const parent = path.at(-1);
|
|
651
|
+
if (
|
|
652
|
+
parent &&
|
|
653
|
+
is_function_node(parent) &&
|
|
654
|
+
parent.metadata?.native_tsrx &&
|
|
655
|
+
parent.body === node
|
|
656
|
+
) {
|
|
657
|
+
return visit(create_native_tsrx_render_block(node, state), state);
|
|
641
658
|
}
|
|
642
659
|
|
|
643
660
|
const style_context = prepare_tsrx_fragment_styles(node, state);
|
|
644
|
-
const target =
|
|
661
|
+
const target = /** @type {AST.TSRXJSXElement | AST.TSRXJSXFragment} */ (
|
|
662
|
+
style_context?.fragment ?? next() ?? node
|
|
663
|
+
);
|
|
645
664
|
// An EMPTY fragment that is the sole expression of a `{ … }` container in a
|
|
646
665
|
// JSX child slot (`<b>{<></>}</b>`) must stay `<></>`: the container already
|
|
647
666
|
// supplies the `{}` wrapper, so lowering it to a bare `null` (the default
|
|
648
667
|
// expression-position behavior) drops the source fragment. This matches how
|
|
649
668
|
// the same fragment is preserved in an attribute value (`a={<></>}`).
|
|
650
669
|
// Non-empty fragments keep their existing lowering.
|
|
651
|
-
const immediate_parent =
|
|
670
|
+
const immediate_parent = path[path.length - 1];
|
|
652
671
|
const is_empty_container_child =
|
|
653
672
|
immediate_parent?.type === 'JSXExpressionContainer' &&
|
|
654
673
|
in_jsx_child_context(path.slice(0, -1)) &&
|
|
655
|
-
!(target
|
|
656
|
-
(
|
|
657
|
-
child &&
|
|
658
|
-
child.type !== 'EmptyStatement' &&
|
|
659
|
-
(child.type !== 'JSXText' || child.value !== ''),
|
|
674
|
+
!node_children(target).some(
|
|
675
|
+
(child) =>
|
|
676
|
+
child.type !== 'EmptyStatement' && (child.type !== 'JSXText' || child.value !== ''),
|
|
660
677
|
);
|
|
661
678
|
const in_jsx_child = in_jsx_child_context(path) || is_empty_container_child;
|
|
662
679
|
let expression = tsrx_node_to_jsx_expression(target, state, in_jsx_child);
|
|
@@ -680,7 +697,7 @@ export function createJsxTransform(platform) {
|
|
|
680
697
|
)) {
|
|
681
698
|
add_jsx_setup_declaration(expression, statement);
|
|
682
699
|
}
|
|
683
|
-
return
|
|
700
|
+
return wrap_jsx_setup_declarations(expression, in_jsx_child);
|
|
684
701
|
},
|
|
685
702
|
|
|
686
703
|
JSXElement(node, { next, path, state, visit }) {
|
|
@@ -689,7 +706,7 @@ export function createJsxTransform(platform) {
|
|
|
689
706
|
// Alias lowerings replace the element with a fragment; factory
|
|
690
707
|
// platforms normally lower in the pre-walk pass, so this only
|
|
691
708
|
// covers elements introduced after it.
|
|
692
|
-
return
|
|
709
|
+
return visit(lowered, state);
|
|
693
710
|
}
|
|
694
711
|
|
|
695
712
|
if (!node.metadata?.native_tsrx) {
|
|
@@ -698,19 +715,15 @@ export function createJsxTransform(platform) {
|
|
|
698
715
|
|
|
699
716
|
// Capture raw children BEFORE the walker transforms them so platform
|
|
700
717
|
// hooks can inspect the original JSX child shape.
|
|
701
|
-
const raw_children =
|
|
702
|
-
|
|
703
|
-
);
|
|
704
|
-
const inner = /** @type {any} */ (next() ?? node);
|
|
718
|
+
const raw_children = node_children(node).map((child) => ({ ...child }));
|
|
719
|
+
const inner = /** @type {AST.TSRXJSXElement} */ (next() ?? node);
|
|
705
720
|
const hook = platform.hooks?.transformElement;
|
|
706
|
-
if (hook) return
|
|
707
|
-
return
|
|
708
|
-
to_jsx_element(inner, state, raw_children, in_jsx_child_context(path))
|
|
709
|
-
);
|
|
721
|
+
if (hook) return hook(inner, state, raw_children);
|
|
722
|
+
return to_jsx_element(inner, state, raw_children, in_jsx_child_context(path));
|
|
710
723
|
},
|
|
711
724
|
|
|
712
725
|
JSXExpressionContainer(node, { next, state }) {
|
|
713
|
-
const result = /** @type {
|
|
726
|
+
const result = /** @type {ESTreeJSX.JSXExpressionContainer} */ (next() ?? node);
|
|
714
727
|
const expression = result.expression;
|
|
715
728
|
// `@if`/`@for`/`@switch`/`@try` used as an expression value (e.g. an
|
|
716
729
|
// attribute value `content={@if (…) { … }}` or a `{ … }` child) leaks a
|
|
@@ -722,8 +735,14 @@ export function createJsxTransform(platform) {
|
|
|
722
735
|
is_try_control_node(expression) ||
|
|
723
736
|
expression?.type === 'JSXForExpression'
|
|
724
737
|
) {
|
|
725
|
-
const lowered =
|
|
726
|
-
return {
|
|
738
|
+
const lowered = to_jsx_child(expression, state);
|
|
739
|
+
return {
|
|
740
|
+
...result,
|
|
741
|
+
expression:
|
|
742
|
+
lowered.type === 'JSXExpressionContainer'
|
|
743
|
+
? lowered.expression
|
|
744
|
+
: /** @type {AST.Expression} */ (lowered),
|
|
745
|
+
};
|
|
727
746
|
}
|
|
728
747
|
return result;
|
|
729
748
|
},
|
|
@@ -734,7 +753,7 @@ export function createJsxTransform(platform) {
|
|
|
734
753
|
if (stylesheet) {
|
|
735
754
|
analyze_css(stylesheet);
|
|
736
755
|
state.stylesheets.push(prepare_stylesheet_for_render(stylesheet, true));
|
|
737
|
-
return
|
|
756
|
+
return create_style_expression_value(node, stylesheet, state);
|
|
738
757
|
}
|
|
739
758
|
}
|
|
740
759
|
return b.jsx_element(
|
|
@@ -756,7 +775,7 @@ export function createJsxTransform(platform) {
|
|
|
756
775
|
ArrowFunctionExpression: transform_function,
|
|
757
776
|
|
|
758
777
|
JSXOpeningElement(node, { next }) {
|
|
759
|
-
const visited = /** @type {
|
|
778
|
+
const visited = /** @type {ESTreeJSX.TSRXJSXOpeningElement} */ (next() || node);
|
|
760
779
|
if (visited.metadata?.native_tsrx_pretransformed) {
|
|
761
780
|
return visited;
|
|
762
781
|
}
|
|
@@ -769,7 +788,7 @@ export function createJsxTransform(platform) {
|
|
|
769
788
|
),
|
|
770
789
|
visited.selfClosing,
|
|
771
790
|
visited.typeArguments,
|
|
772
|
-
visited,
|
|
791
|
+
has_location(visited) ? visited : undefined,
|
|
773
792
|
);
|
|
774
793
|
},
|
|
775
794
|
});
|
|
@@ -780,7 +799,7 @@ export function createJsxTransform(platform) {
|
|
|
780
799
|
// expansion, import injection) write into the program's `body`, so
|
|
781
800
|
// detach from the caller's AST first; a changed program is already a
|
|
782
801
|
// fresh walk-owned node with a fresh `body` array.
|
|
783
|
-
if (
|
|
802
|
+
if (transformed_program === ast) {
|
|
784
803
|
transformed_program = { ...transformed_program, body: [...transformed_program.body] };
|
|
785
804
|
}
|
|
786
805
|
if (type_only_style_anchors.length > 0) {
|
|
@@ -819,13 +838,11 @@ export function createJsxTransform(platform) {
|
|
|
819
838
|
// so lazy bindings declared inside a nested block or directive body are
|
|
820
839
|
// rewritten just like a flat function body.
|
|
821
840
|
if (!transform_context.typeOnly) {
|
|
822
|
-
preallocate_lazy_ids(
|
|
841
|
+
preallocate_lazy_ids(lowered_program, transform_context);
|
|
823
842
|
}
|
|
824
|
-
const final_program =
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
: apply_lazy_transforms(/** @type {any} */ (lowered_program), new Map())
|
|
828
|
-
);
|
|
843
|
+
const final_program = transform_context.typeOnly
|
|
844
|
+
? lowered_program
|
|
845
|
+
: /** @type {AST.Program} */ (apply_lazy_transforms(lowered_program, new Map()));
|
|
829
846
|
|
|
830
847
|
const result = print(
|
|
831
848
|
final_program,
|
|
@@ -842,7 +859,7 @@ export function createJsxTransform(platform) {
|
|
|
842
859
|
},
|
|
843
860
|
);
|
|
844
861
|
|
|
845
|
-
const { css, cssHash } = render_css_result(
|
|
862
|
+
const { css, cssHash } = render_css_result(stylesheets);
|
|
846
863
|
|
|
847
864
|
return { ast: final_program, code: result.code, map: result.map, css, cssHash };
|
|
848
865
|
}
|
|
@@ -861,9 +878,9 @@ export function createJsxTransform(platform) {
|
|
|
861
878
|
* replacement node (an element, or a fragment for the alias lowering) that
|
|
862
879
|
* the caller must put in the original element's position.
|
|
863
880
|
*
|
|
864
|
-
* @param {
|
|
881
|
+
* @param {AST.TSRXJSXElement} node
|
|
865
882
|
* @param {TransformContext} transform_context
|
|
866
|
-
* @returns {
|
|
883
|
+
* @returns {AST.TSRXJSXElement | AST.TSRXJSXFragment | undefined}
|
|
867
884
|
*/
|
|
868
885
|
function lower_dynamic_jsx_element(node, transform_context) {
|
|
869
886
|
const dynamic_name = node.openingElement?.name;
|
|
@@ -880,12 +897,13 @@ function lower_dynamic_jsx_element(node, transform_context) {
|
|
|
880
897
|
const dynamic_expression = dynamic_name.expression;
|
|
881
898
|
if (!dynamic_expression) return;
|
|
882
899
|
const generated_expression = clone_ast_node(dynamic_expression);
|
|
883
|
-
|
|
900
|
+
const closing_name = node.closingElement?.name;
|
|
901
|
+
if (closing_name?.type === 'JSXExpressionContainer' && closing_name.expression) {
|
|
884
902
|
// One generated expression stands in for both tags; record the closing
|
|
885
903
|
// tag's positions so editor features keep working on `</{expr}>`.
|
|
886
904
|
add_extra_source_mappings_from_matching_expression(
|
|
887
905
|
generated_expression,
|
|
888
|
-
clone_ast_node(
|
|
906
|
+
clone_ast_node(closing_name.expression),
|
|
889
907
|
);
|
|
890
908
|
}
|
|
891
909
|
|
|
@@ -896,22 +914,23 @@ function lower_dynamic_jsx_element(node, transform_context) {
|
|
|
896
914
|
*
|
|
897
915
|
* @param {ESTreeJSX.JSXIdentifier} name_id
|
|
898
916
|
* @param {ESTreeJSX.JSXAttribute[]} [extra_attributes]
|
|
899
|
-
* @returns {
|
|
917
|
+
* @returns {AST.TSRXJSXElement}
|
|
900
918
|
*/
|
|
901
919
|
const rebuild_element = (name_id, extra_attributes = []) => {
|
|
920
|
+
const closing = node.closingElement;
|
|
902
921
|
const element = b.jsx_element_fresh(
|
|
903
922
|
b.jsx_opening_element(
|
|
904
923
|
name_id,
|
|
905
924
|
[...extra_attributes, ...(node.openingElement.attributes || [])],
|
|
906
925
|
node.openingElement.selfClosing,
|
|
907
926
|
node.openingElement.typeArguments,
|
|
908
|
-
node.openingElement,
|
|
927
|
+
has_location(node.openingElement) ? node.openingElement : undefined,
|
|
909
928
|
),
|
|
910
|
-
|
|
911
|
-
? b.jsx_closing_element(b.jsx_id(name_id.name),
|
|
929
|
+
closing
|
|
930
|
+
? b.jsx_closing_element(b.jsx_id(name_id.name), has_location(closing) ? closing : undefined)
|
|
912
931
|
: null,
|
|
913
932
|
node.children,
|
|
914
|
-
node,
|
|
933
|
+
has_location(node) ? node : undefined,
|
|
915
934
|
);
|
|
916
935
|
element.metadata = { ...(node.metadata || {}), path: [] };
|
|
917
936
|
return element;
|
|
@@ -922,8 +941,8 @@ function lower_dynamic_jsx_element(node, transform_context) {
|
|
|
922
941
|
* helper: type selectors survive pruning and the scope hash lands on the
|
|
923
942
|
* element's class.
|
|
924
943
|
*
|
|
925
|
-
* @param {
|
|
926
|
-
* @returns {
|
|
944
|
+
* @param {AST.TSRXJSXElement} element
|
|
945
|
+
* @returns {AST.TSRXJSXElement}
|
|
927
946
|
*/
|
|
928
947
|
const mark_dynamic_element = (element) => {
|
|
929
948
|
element.metadata.dynamicElement = true;
|
|
@@ -952,19 +971,22 @@ function lower_dynamic_jsx_element(node, transform_context) {
|
|
|
952
971
|
const element = mark_dynamic_element(rebuild_element(local_id));
|
|
953
972
|
const wrapper = b.arrow(
|
|
954
973
|
[],
|
|
955
|
-
b.block(
|
|
974
|
+
b.block(
|
|
975
|
+
[b.const(b.id(local), generated_expression), b.return(element)],
|
|
976
|
+
has_location(node) ? node : undefined,
|
|
977
|
+
),
|
|
956
978
|
);
|
|
957
979
|
// Lets scoped-CSS collection descend into this generated closure;
|
|
958
980
|
// user function boundaries are otherwise skipped.
|
|
959
|
-
wrapper.metadata =
|
|
981
|
+
wrapper.metadata = {
|
|
960
982
|
...(wrapper.metadata || { path: [] }),
|
|
961
983
|
tsrx_dynamic_wrapper: true,
|
|
962
|
-
}
|
|
984
|
+
};
|
|
963
985
|
const container = to_jsx_expression_container(b.call(wrapper), element);
|
|
964
|
-
container.metadata =
|
|
986
|
+
container.metadata = {
|
|
965
987
|
...(container.metadata || { path: [] }),
|
|
966
988
|
tsrx_reactive_block: true,
|
|
967
|
-
}
|
|
989
|
+
};
|
|
968
990
|
|
|
969
991
|
return set_loc(wrap_in_native_tsrx_fragment(container), node);
|
|
970
992
|
}
|
|
@@ -989,13 +1011,14 @@ function lower_dynamic_jsx_element(node, transform_context) {
|
|
|
989
1011
|
}
|
|
990
1012
|
|
|
991
1013
|
transform_context.needs_dynamic_element = true;
|
|
1014
|
+
const name_loc = has_location(dynamic_name) ? dynamic_name : undefined;
|
|
992
1015
|
return mark_dynamic_element(
|
|
993
1016
|
rebuild_element(b.jsx_id(DYNAMIC_IMPORT_LOCAL), [
|
|
994
1017
|
b.jsx_attribute(
|
|
995
1018
|
b.jsx_id('is'),
|
|
996
|
-
b.jsx_expression_container(generated_expression,
|
|
1019
|
+
b.jsx_expression_container(generated_expression, name_loc),
|
|
997
1020
|
false,
|
|
998
|
-
|
|
1021
|
+
name_loc,
|
|
999
1022
|
),
|
|
1000
1023
|
]),
|
|
1001
1024
|
);
|
|
@@ -1023,12 +1046,25 @@ function inject_dynamic_import(program, transform_context) {
|
|
|
1023
1046
|
);
|
|
1024
1047
|
}
|
|
1025
1048
|
|
|
1049
|
+
/**
|
|
1050
|
+
* The children a node carries, as nodes. Node types differ in whether they
|
|
1051
|
+
* have a `children` slot at all (`JSXCodeBlock` does not) and in what it may
|
|
1052
|
+
* hold, so this reads it uniformly instead of forcing every caller to narrow.
|
|
1053
|
+
*
|
|
1054
|
+
* @param {AST.Node} node
|
|
1055
|
+
* @returns {AST.Node[]}
|
|
1056
|
+
*/
|
|
1057
|
+
function node_children(node) {
|
|
1058
|
+
const children = /** @type {AST.TraversableAstNode} */ (node).children;
|
|
1059
|
+
return Array.isArray(children) ? children.filter(is_ast_node) : [];
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1026
1062
|
/**
|
|
1027
1063
|
* Attach selector-location metadata used by editor definitions/hover before
|
|
1028
1064
|
* the shared scoping pass mutates class attributes with the component hash.
|
|
1029
1065
|
*
|
|
1030
|
-
* @param {
|
|
1031
|
-
* @param {
|
|
1066
|
+
* @param {AST.NativeTSRXNode} component
|
|
1067
|
+
* @param {AST.CSS.StyleSheet} css
|
|
1032
1068
|
* @param {TransformContext} transform_context
|
|
1033
1069
|
* @param {boolean} [export_top_scoped_classes]
|
|
1034
1070
|
* @returns {void}
|
|
@@ -1044,11 +1080,7 @@ function apply_css_definition_metadata(
|
|
|
1044
1080
|
const metadata = component.metadata || (component.metadata = { path: [] });
|
|
1045
1081
|
const style_classes = metadata.styleClasses || (metadata.styleClasses = new Map());
|
|
1046
1082
|
const top_scoped_classes = metadata.topScopedClasses || new Map();
|
|
1047
|
-
const elements = collect_css_prunable_elements(
|
|
1048
|
-
component.body || component.children || [],
|
|
1049
|
-
[],
|
|
1050
|
-
transform_context,
|
|
1051
|
-
);
|
|
1083
|
+
const elements = collect_css_prunable_elements(node_children(component), [], transform_context);
|
|
1052
1084
|
|
|
1053
1085
|
const prune = () => {
|
|
1054
1086
|
for (const element of elements) {
|
|
@@ -1076,17 +1108,13 @@ function apply_css_definition_metadata(
|
|
|
1076
1108
|
* ancestor chain (`metadata.path`) here — descendant/sibling selector matching
|
|
1077
1109
|
* in `prune_css` reads it.
|
|
1078
1110
|
*
|
|
1079
|
-
* @param {
|
|
1080
|
-
* @param {
|
|
1111
|
+
* @param {AST.Node | AST.Node[]} value
|
|
1112
|
+
* @param {AST.TSRXJSXElement[]} [elements]
|
|
1081
1113
|
* @param {TransformContext | null} [transform_context]
|
|
1082
|
-
* @param {
|
|
1083
|
-
* @returns {
|
|
1114
|
+
* @param {AST.Node[]} [path]
|
|
1115
|
+
* @returns {AST.TSRXJSXElement[]}
|
|
1084
1116
|
*/
|
|
1085
1117
|
function collect_css_prunable_elements(value, elements = [], transform_context = null, path = []) {
|
|
1086
|
-
if (!value || typeof value !== 'object') {
|
|
1087
|
-
return elements;
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
1118
|
if (Array.isArray(value)) {
|
|
1091
1119
|
for (const child of value) {
|
|
1092
1120
|
collect_css_prunable_elements(child, elements, transform_context, path);
|
|
@@ -1113,22 +1141,19 @@ function collect_css_prunable_elements(value, elements = [], transform_context =
|
|
|
1113
1141
|
}
|
|
1114
1142
|
}
|
|
1115
1143
|
|
|
1116
|
-
const child_path =
|
|
1144
|
+
const child_path = [...path, value];
|
|
1117
1145
|
|
|
1118
|
-
for (const
|
|
1119
|
-
|
|
1120
|
-
continue;
|
|
1121
|
-
}
|
|
1122
|
-
collect_css_prunable_elements(value[key], elements, transform_context, child_path);
|
|
1146
|
+
for (const child of child_nodes(value, 'css')) {
|
|
1147
|
+
collect_css_prunable_elements(child, elements, transform_context, child_path);
|
|
1123
1148
|
}
|
|
1124
1149
|
|
|
1125
1150
|
return elements;
|
|
1126
1151
|
}
|
|
1127
1152
|
|
|
1128
1153
|
/**
|
|
1129
|
-
* @param {
|
|
1154
|
+
* @param {AST.Node[]} body_nodes
|
|
1130
1155
|
* @param {TransformContext} transform_context
|
|
1131
|
-
* @returns {
|
|
1156
|
+
* @returns {AST.Statement[]}
|
|
1132
1157
|
*/
|
|
1133
1158
|
function build_component_statements(body_nodes, transform_context) {
|
|
1134
1159
|
return build_render_statements(body_nodes, false, transform_context);
|
|
@@ -1140,9 +1165,9 @@ function build_component_statements(body_nodes, transform_context) {
|
|
|
1140
1165
|
* kept in a nested plain `{ … }` block, so a whole chain shares a single
|
|
1141
1166
|
* closure while still scoping each level; the generated `return` exits that
|
|
1142
1167
|
* closure.
|
|
1143
|
-
* @param {
|
|
1168
|
+
* @param {AST.JSXCodeBlock} block
|
|
1144
1169
|
* @param {TransformContext} transform_context
|
|
1145
|
-
* @returns {{ statements:
|
|
1170
|
+
* @returns {{ statements: AST.Statement[], has_render: boolean }}
|
|
1146
1171
|
*/
|
|
1147
1172
|
function code_block_scope_statements(block, transform_context) {
|
|
1148
1173
|
const statements = [...(block.body || [])];
|
|
@@ -1156,7 +1181,7 @@ function code_block_scope_statements(block, transform_context) {
|
|
|
1156
1181
|
const inner = code_block_scope_statements(render, transform_context);
|
|
1157
1182
|
if (inner.statements.length > 0) {
|
|
1158
1183
|
if ((render.body || []).length > 0) {
|
|
1159
|
-
statements.push(b.block(inner.statements, render));
|
|
1184
|
+
statements.push(b.block(inner.statements, has_location(render) ? render : undefined));
|
|
1160
1185
|
} else {
|
|
1161
1186
|
statements.push(...inner.statements);
|
|
1162
1187
|
}
|
|
@@ -1181,9 +1206,9 @@ function code_block_scope_statements(block, transform_context) {
|
|
|
1181
1206
|
* the render output, with nested chains folded into the one closure.
|
|
1182
1207
|
*
|
|
1183
1208
|
* Always returns zero or one node.
|
|
1184
|
-
* @param {
|
|
1209
|
+
* @param {AST.JSXCodeBlock} block
|
|
1185
1210
|
* @param {TransformContext} transform_context
|
|
1186
|
-
* @returns {
|
|
1211
|
+
* @returns {AST.Node[]}
|
|
1187
1212
|
*/
|
|
1188
1213
|
function lower_code_block_stream_node(block, transform_context) {
|
|
1189
1214
|
const body = block.body || [];
|
|
@@ -1198,24 +1223,25 @@ function lower_code_block_stream_node(block, transform_context) {
|
|
|
1198
1223
|
}
|
|
1199
1224
|
|
|
1200
1225
|
const { statements, has_render } = code_block_scope_statements(block, transform_context);
|
|
1226
|
+
const block_loc = has_location(block) ? block : undefined;
|
|
1201
1227
|
|
|
1202
1228
|
if (!has_render) {
|
|
1203
|
-
return [b.block(statements,
|
|
1229
|
+
return [b.block(statements, block_loc)];
|
|
1204
1230
|
}
|
|
1205
1231
|
|
|
1206
|
-
const iife = b.call(b.arrow([], b.block(statements,
|
|
1232
|
+
const iife = b.call(b.arrow([], b.block(statements, block_loc)));
|
|
1207
1233
|
return [to_jsx_expression_container(iife, block)];
|
|
1208
1234
|
}
|
|
1209
1235
|
|
|
1210
1236
|
/**
|
|
1211
|
-
* @param {
|
|
1237
|
+
* @param {AST.Node[]} body_nodes
|
|
1212
1238
|
* @param {boolean} return_null_when_empty
|
|
1213
1239
|
* @param {TransformContext} transform_context
|
|
1214
|
-
* @param {
|
|
1240
|
+
* @param {AST.Node | null} [source_authored_fragment] When the render output is an AUTHORED
|
|
1215
1241
|
* `<> … </>` (`is_authored_native_fragment`), the built return value is re-wrapped
|
|
1216
1242
|
* in a fragment so the author's fragment is kept verbatim (not collapsed to its
|
|
1217
1243
|
* single child), matching value positions. A generated wrapper passes nothing.
|
|
1218
|
-
* @returns {
|
|
1244
|
+
* @returns {AST.Statement[]}
|
|
1219
1245
|
*/
|
|
1220
1246
|
function build_render_statements(
|
|
1221
1247
|
body_nodes,
|
|
@@ -1238,7 +1264,9 @@ function build_render_statements(
|
|
|
1238
1264
|
}
|
|
1239
1265
|
}
|
|
1240
1266
|
|
|
1267
|
+
/** @type {AST.Statement[]} */
|
|
1241
1268
|
const statements = [];
|
|
1269
|
+
/** @type {ESTreeJSX.JSXRenderChild[]} */
|
|
1242
1270
|
const render_nodes = [];
|
|
1243
1271
|
let has_terminal_return = false;
|
|
1244
1272
|
|
|
@@ -1254,6 +1282,15 @@ function build_render_statements(
|
|
|
1254
1282
|
// state of mutable variables.
|
|
1255
1283
|
const interleaved = is_interleaved_body(body_nodes);
|
|
1256
1284
|
let capture_index = 0;
|
|
1285
|
+
// When this pass hoists a JSX child into `const _tsrx_child_N = …`, that
|
|
1286
|
+
// NAME is the one anchorable token of the whole expression: a `@if` whose
|
|
1287
|
+
// branch carries hooks lowers to `cond ? (() => { … })() : …`, and neither
|
|
1288
|
+
// the ternary (it starts where its test does) nor an IIFE arm (it starts on
|
|
1289
|
+
// a paren) yields a map segment of its own. `stamp_directive_origin`
|
|
1290
|
+
// confirms the authored spelling, so a hoist of anything else is untouched.
|
|
1291
|
+
/** @type {(id: AST.Identifier, init: AST.Expression) => AST.Identifier} */
|
|
1292
|
+
const anchor_capture_name = (id, init) =>
|
|
1293
|
+
stamp_directive_origin(id, init, '@if', transform_context);
|
|
1257
1294
|
|
|
1258
1295
|
for (let i = 0; i < body_nodes.length; i += 1) {
|
|
1259
1296
|
const child = body_nodes[i];
|
|
@@ -1336,7 +1373,11 @@ function build_render_statements(
|
|
|
1336
1373
|
if (hoisted) {
|
|
1337
1374
|
statements.push(...hoisted.hoist_statements);
|
|
1338
1375
|
if (interleaved && is_capturable_jsx_child(hoisted.jsx_child)) {
|
|
1339
|
-
const { declaration, reference } = captureJsxChild(
|
|
1376
|
+
const { declaration, reference } = captureJsxChild(
|
|
1377
|
+
hoisted.jsx_child,
|
|
1378
|
+
capture_index++,
|
|
1379
|
+
anchor_capture_name,
|
|
1380
|
+
);
|
|
1340
1381
|
statements.push(declaration);
|
|
1341
1382
|
render_nodes.push(reference);
|
|
1342
1383
|
} else {
|
|
@@ -1350,7 +1391,11 @@ function build_render_statements(
|
|
|
1350
1391
|
const jsx = to_jsx_child(child, transform_context);
|
|
1351
1392
|
statements.push(...extract_jsx_setup_declarations(jsx));
|
|
1352
1393
|
if (interleaved && is_capturable_jsx_child(jsx)) {
|
|
1353
|
-
const { declaration, reference } = captureJsxChild(
|
|
1394
|
+
const { declaration, reference } = captureJsxChild(
|
|
1395
|
+
jsx,
|
|
1396
|
+
capture_index++,
|
|
1397
|
+
anchor_capture_name,
|
|
1398
|
+
);
|
|
1354
1399
|
statements.push(declaration);
|
|
1355
1400
|
render_nodes.push(reference);
|
|
1356
1401
|
} else {
|
|
@@ -1360,7 +1405,8 @@ function build_render_statements(
|
|
|
1360
1405
|
render_nodes.push(to_jsx_expression_container(child, child));
|
|
1361
1406
|
} else {
|
|
1362
1407
|
mark_nested_function_return_jsx(child);
|
|
1363
|
-
|
|
1408
|
+
// Anything left after the render-child cases is ordinary setup code.
|
|
1409
|
+
statements.push(/** @type {AST.Statement} */ (child));
|
|
1364
1410
|
collect_statement_bindings(child, transform_context.available_bindings);
|
|
1365
1411
|
}
|
|
1366
1412
|
}
|
|
@@ -1394,7 +1440,7 @@ function build_render_statements(
|
|
|
1394
1440
|
}
|
|
1395
1441
|
|
|
1396
1442
|
/**
|
|
1397
|
-
* @param {
|
|
1443
|
+
* @param {AST.Node[]} body_nodes
|
|
1398
1444
|
* @returns {boolean}
|
|
1399
1445
|
*/
|
|
1400
1446
|
function is_interleaved_body(body_nodes) {
|
|
@@ -1402,7 +1448,7 @@ function is_interleaved_body(body_nodes) {
|
|
|
1402
1448
|
}
|
|
1403
1449
|
|
|
1404
1450
|
/**
|
|
1405
|
-
* @param {
|
|
1451
|
+
* @param {AST.Node[]} body_nodes
|
|
1406
1452
|
* @param {TransformContext} transform_context
|
|
1407
1453
|
* @param {boolean} include_platform_setup
|
|
1408
1454
|
* @returns {boolean}
|
|
@@ -1418,7 +1464,7 @@ function body_contains_top_level_hook_call(
|
|
|
1418
1464
|
}
|
|
1419
1465
|
|
|
1420
1466
|
/**
|
|
1421
|
-
* @param {
|
|
1467
|
+
* @param {AST.Node | null | undefined} node
|
|
1422
1468
|
* @param {TransformContext} transform_context
|
|
1423
1469
|
* @param {boolean} include_platform_setup
|
|
1424
1470
|
* @returns {boolean}
|
|
@@ -1428,7 +1474,7 @@ function statement_contains_top_level_hook_call(node, transform_context, include
|
|
|
1428
1474
|
}
|
|
1429
1475
|
|
|
1430
1476
|
/**
|
|
1431
|
-
* @param {
|
|
1477
|
+
* @param {AST.Node | null | undefined} node
|
|
1432
1478
|
* @param {boolean} inside_nested_function
|
|
1433
1479
|
* @param {TransformContext} transform_context
|
|
1434
1480
|
* @param {boolean} include_platform_setup
|
|
@@ -1440,10 +1486,12 @@ function node_contains_top_level_hook_call(
|
|
|
1440
1486
|
transform_context,
|
|
1441
1487
|
include_platform_setup,
|
|
1442
1488
|
) {
|
|
1443
|
-
if (!node
|
|
1489
|
+
if (!node) {
|
|
1444
1490
|
return false;
|
|
1445
1491
|
}
|
|
1446
1492
|
|
|
1493
|
+
const entries = /** @type {AST.TraversableAstNode} */ (node);
|
|
1494
|
+
|
|
1447
1495
|
if (
|
|
1448
1496
|
inside_nested_function &&
|
|
1449
1497
|
(node.type === 'FunctionDeclaration' ||
|
|
@@ -1459,13 +1507,10 @@ function node_contains_top_level_hook_call(
|
|
|
1459
1507
|
node.type === 'ArrowFunctionExpression'
|
|
1460
1508
|
) {
|
|
1461
1509
|
const next_inside_nested_function = true;
|
|
1462
|
-
for (const
|
|
1463
|
-
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
1464
|
-
continue;
|
|
1465
|
-
}
|
|
1510
|
+
for (const child of child_nodes(node)) {
|
|
1466
1511
|
if (
|
|
1467
1512
|
node_contains_top_level_hook_call(
|
|
1468
|
-
|
|
1513
|
+
child,
|
|
1469
1514
|
next_inside_nested_function,
|
|
1470
1515
|
transform_context,
|
|
1471
1516
|
include_platform_setup,
|
|
@@ -1498,13 +1543,10 @@ function node_contains_top_level_hook_call(
|
|
|
1498
1543
|
);
|
|
1499
1544
|
}
|
|
1500
1545
|
|
|
1501
|
-
for (const
|
|
1502
|
-
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
1503
|
-
continue;
|
|
1504
|
-
}
|
|
1546
|
+
for (const child of child_nodes(node)) {
|
|
1505
1547
|
if (
|
|
1506
1548
|
node_contains_top_level_hook_call(
|
|
1507
|
-
|
|
1549
|
+
child,
|
|
1508
1550
|
inside_nested_function,
|
|
1509
1551
|
transform_context,
|
|
1510
1552
|
include_platform_setup,
|
|
@@ -1518,7 +1560,7 @@ function node_contains_top_level_hook_call(
|
|
|
1518
1560
|
}
|
|
1519
1561
|
|
|
1520
1562
|
/**
|
|
1521
|
-
* @param {
|
|
1563
|
+
* @param {AST.Node | null | undefined} callee
|
|
1522
1564
|
* @returns {boolean}
|
|
1523
1565
|
*/
|
|
1524
1566
|
function is_hook_callee(callee) {
|
|
@@ -1529,8 +1571,8 @@ function is_hook_callee(callee) {
|
|
|
1529
1571
|
}
|
|
1530
1572
|
|
|
1531
1573
|
if (
|
|
1532
|
-
!callee.computed &&
|
|
1533
1574
|
callee.type === 'MemberExpression' &&
|
|
1575
|
+
!callee.computed &&
|
|
1534
1576
|
callee.property?.type === 'Identifier'
|
|
1535
1577
|
) {
|
|
1536
1578
|
return /^use[A-Z0-9]/.test(callee.property.name);
|
|
@@ -1545,37 +1587,35 @@ function is_hook_callee(callee) {
|
|
|
1545
1587
|
* @returns {AST.ObjectPattern}
|
|
1546
1588
|
*/
|
|
1547
1589
|
function create_helper_props_pattern(bindings, mapped_bindings = new Set()) {
|
|
1548
|
-
return
|
|
1549
|
-
|
|
1550
|
-
properties: bindings.map((binding) =>
|
|
1590
|
+
return b.object_pattern(
|
|
1591
|
+
bindings.map((binding) =>
|
|
1551
1592
|
create_helper_props_property(binding, mapped_bindings.has(binding.name)),
|
|
1552
1593
|
),
|
|
1553
|
-
|
|
1554
|
-
});
|
|
1594
|
+
);
|
|
1555
1595
|
}
|
|
1556
1596
|
|
|
1557
1597
|
/**
|
|
1558
1598
|
* @param {AST.Identifier} binding
|
|
1559
1599
|
* @param {boolean} [map_binding]
|
|
1560
|
-
* @returns {AST.
|
|
1600
|
+
* @returns {AST.AssignmentProperty}
|
|
1561
1601
|
*/
|
|
1562
1602
|
function create_helper_props_property(binding, map_binding = false) {
|
|
1563
1603
|
const key = map_binding ? clone_identifier(binding) : create_generated_identifier(binding.name);
|
|
1564
1604
|
const value = map_binding ? clone_identifier(binding) : create_generated_identifier(binding.name);
|
|
1565
1605
|
|
|
1566
|
-
return b.prop('init', key, value, false, true);
|
|
1606
|
+
return /** @type {AST.AssignmentProperty} */ (b.prop('init', key, value, false, true));
|
|
1567
1607
|
}
|
|
1568
1608
|
|
|
1569
1609
|
/**
|
|
1570
1610
|
* @param {AST.Identifier} helper_id
|
|
1571
1611
|
* @param {AST.Identifier[]} bindings
|
|
1572
|
-
* @param {
|
|
1612
|
+
* @param {AST.Node | AST.NodeWithLocation | null | undefined} source_node
|
|
1573
1613
|
* @param {{
|
|
1574
1614
|
* mapWrapper?: boolean,
|
|
1575
1615
|
* mapBindingNames?: boolean,
|
|
1576
1616
|
* mapBindingValues?: boolean,
|
|
1577
1617
|
* }} [mapping]
|
|
1578
|
-
* @returns {
|
|
1618
|
+
* @returns {AST.TSRXJSXElement}
|
|
1579
1619
|
*/
|
|
1580
1620
|
function create_helper_component_element(helper_id, bindings, source_node, mapping = {}) {
|
|
1581
1621
|
const { mapWrapper = true, mapBindingNames = true, mapBindingValues = true } = mapping;
|
|
@@ -1604,7 +1644,7 @@ function create_helper_component_element(helper_id, bindings, source_node, mappi
|
|
|
1604
1644
|
}
|
|
1605
1645
|
|
|
1606
1646
|
/**
|
|
1607
|
-
* @param {
|
|
1647
|
+
* @param {JsxHelperState} helper_state
|
|
1608
1648
|
* @param {string} suffix
|
|
1609
1649
|
* @returns {string}
|
|
1610
1650
|
*/
|
|
@@ -1615,7 +1655,7 @@ function create_helper_name(helper_state, suffix) {
|
|
|
1615
1655
|
|
|
1616
1656
|
/**
|
|
1617
1657
|
* @param {string} base_name
|
|
1618
|
-
* @returns {
|
|
1658
|
+
* @returns {JsxHelperState}
|
|
1619
1659
|
*/
|
|
1620
1660
|
function create_helper_state(base_name) {
|
|
1621
1661
|
return {
|
|
@@ -1627,8 +1667,8 @@ function create_helper_state(base_name) {
|
|
|
1627
1667
|
}
|
|
1628
1668
|
|
|
1629
1669
|
/**
|
|
1630
|
-
* @param {
|
|
1631
|
-
* @returns {{ generated_helpers:
|
|
1670
|
+
* @param {JsxHelperState} helper_state
|
|
1671
|
+
* @returns {{ generated_helpers: AST.Statement[], generated_statics: AST.Statement[] } | null}
|
|
1632
1672
|
*/
|
|
1633
1673
|
function create_generated_helper_metadata(helper_state) {
|
|
1634
1674
|
if (helper_state.helpers.length === 0 && helper_state.statics.length === 0) {
|
|
@@ -1641,18 +1681,18 @@ function create_generated_helper_metadata(helper_state) {
|
|
|
1641
1681
|
}
|
|
1642
1682
|
|
|
1643
1683
|
/**
|
|
1644
|
-
* @param {
|
|
1645
|
-
* @returns {
|
|
1684
|
+
* @param {FunctionMetaData | undefined} metadata
|
|
1685
|
+
* @returns {FunctionMetaData}
|
|
1646
1686
|
*/
|
|
1647
1687
|
function strip_function_transform_metadata(metadata) {
|
|
1648
|
-
const { native_tsrx, ...next_metadata } = metadata
|
|
1688
|
+
const { native_tsrx, ...next_metadata } = metadata ?? { path: [] };
|
|
1649
1689
|
return next_metadata;
|
|
1650
1690
|
}
|
|
1651
1691
|
|
|
1652
1692
|
/**
|
|
1653
1693
|
* @param {AST.BlockStatement} node
|
|
1654
|
-
* @param {
|
|
1655
|
-
* @returns {
|
|
1694
|
+
* @param {JsxVisitorContext} context
|
|
1695
|
+
* @returns {AST.Node}
|
|
1656
1696
|
*/
|
|
1657
1697
|
function transform_block_statement(node, { next, visit, state, path }) {
|
|
1658
1698
|
if (node.metadata?.native_return_block) {
|
|
@@ -1670,9 +1710,9 @@ function transform_block_statement(node, { next, visit, state, path }) {
|
|
|
1670
1710
|
}
|
|
1671
1711
|
|
|
1672
1712
|
/**
|
|
1673
|
-
* @param {
|
|
1674
|
-
* @param {
|
|
1675
|
-
* @returns {
|
|
1713
|
+
* @param {AST.ReturnStatement} node
|
|
1714
|
+
* @param {JsxVisitorContext} context
|
|
1715
|
+
* @returns {AST.Node}
|
|
1676
1716
|
*/
|
|
1677
1717
|
function transform_return_statement(node, { next, visit, state, path }) {
|
|
1678
1718
|
const active_native_tsrx_function = get_active_native_tsrx_function(path);
|
|
@@ -1684,7 +1724,7 @@ function transform_return_statement(node, { next, visit, state, path }) {
|
|
|
1684
1724
|
if (statements.length === 1) {
|
|
1685
1725
|
return visit(statements[0], state);
|
|
1686
1726
|
}
|
|
1687
|
-
const block = b.block(statements, node.argument);
|
|
1727
|
+
const block = b.block(statements, has_location(node.argument) ? node.argument : undefined);
|
|
1688
1728
|
block.metadata = {
|
|
1689
1729
|
...(block.metadata || {}),
|
|
1690
1730
|
native_return_block: true,
|
|
@@ -1698,23 +1738,24 @@ function transform_return_statement(node, { next, visit, state, path }) {
|
|
|
1698
1738
|
}
|
|
1699
1739
|
|
|
1700
1740
|
/**
|
|
1701
|
-
* @param {
|
|
1702
|
-
* @param {
|
|
1703
|
-
* @returns {
|
|
1741
|
+
* @param {AST.JSXCodeBlock} node
|
|
1742
|
+
* @param {JsxVisitorContext} context
|
|
1743
|
+
* @returns {AST.Node}
|
|
1704
1744
|
*/
|
|
1705
1745
|
function transform_jsx_code_block(node, { state, path, visit }) {
|
|
1706
1746
|
const body_nodes = get_jsx_code_block_body_nodes(node, state);
|
|
1707
|
-
const parent =
|
|
1747
|
+
const parent = path.at(-1);
|
|
1708
1748
|
// Keep an authored `<> … </>` trailing render output verbatim (a generated
|
|
1709
1749
|
// control-flow wrapper carries `tsrx_generated_wrapper`, so it stays null).
|
|
1710
1750
|
const render_authored_fragment = is_authored_native_fragment(node.render) ? node.render : null;
|
|
1751
|
+
const node_loc = has_location(node) ? node : undefined;
|
|
1711
1752
|
|
|
1712
|
-
if (parent && parent.body === node
|
|
1753
|
+
if (parent && is_function_or_class_boundary(parent) && parent.body === node) {
|
|
1713
1754
|
const block = b.block(
|
|
1714
1755
|
mark_native_pretransformed_jsx(
|
|
1715
1756
|
build_render_statements(body_nodes, true, state, render_authored_fragment),
|
|
1716
1757
|
),
|
|
1717
|
-
|
|
1758
|
+
node_loc,
|
|
1718
1759
|
);
|
|
1719
1760
|
block.metadata = {
|
|
1720
1761
|
...(block.metadata || {}),
|
|
@@ -1730,7 +1771,7 @@ function transform_jsx_code_block(node, { state, path, visit }) {
|
|
|
1730
1771
|
mark_native_pretransformed_jsx(
|
|
1731
1772
|
build_render_statements(body_nodes, true, state, render_authored_fragment),
|
|
1732
1773
|
),
|
|
1733
|
-
|
|
1774
|
+
node_loc,
|
|
1734
1775
|
),
|
|
1735
1776
|
),
|
|
1736
1777
|
);
|
|
@@ -1746,11 +1787,11 @@ function transform_jsx_code_block(node, { state, path, visit }) {
|
|
|
1746
1787
|
|
|
1747
1788
|
/**
|
|
1748
1789
|
* @param {AST.Node[]} path
|
|
1749
|
-
* @returns {
|
|
1790
|
+
* @returns {AST.Function | AST.ClassDeclaration | AST.ClassExpression | null}
|
|
1750
1791
|
*/
|
|
1751
1792
|
function get_active_native_tsrx_function(path) {
|
|
1752
1793
|
for (let i = path.length - 1; i >= 0; i -= 1) {
|
|
1753
|
-
const node =
|
|
1794
|
+
const node = path[i];
|
|
1754
1795
|
if (is_function_or_class_boundary(node)) {
|
|
1755
1796
|
return node.metadata?.native_tsrx ? node : null;
|
|
1756
1797
|
}
|
|
@@ -1759,9 +1800,9 @@ function get_active_native_tsrx_function(path) {
|
|
|
1759
1800
|
}
|
|
1760
1801
|
|
|
1761
1802
|
/**
|
|
1762
|
-
* @param {
|
|
1763
|
-
* @param {
|
|
1764
|
-
* @returns {
|
|
1803
|
+
* @param {AST.Function} node
|
|
1804
|
+
* @param {JsxVisitorContext} context
|
|
1805
|
+
* @returns {AST.Node}
|
|
1765
1806
|
*/
|
|
1766
1807
|
function transform_function(node, context) {
|
|
1767
1808
|
// Lower a `@{ … }` function body (JSXCodeBlock) to an ordinary block: the
|
|
@@ -1800,8 +1841,9 @@ function transform_function(node, context) {
|
|
|
1800
1841
|
* Lower a `@{ … }` body (JSXCodeBlock) to an ordinary block on a COPY built
|
|
1801
1842
|
* with the AST builders — the source function node is never mutated. Returns
|
|
1802
1843
|
* the input node unchanged when there is nothing to lower.
|
|
1803
|
-
* @
|
|
1804
|
-
* @
|
|
1844
|
+
* @template {AST.Function} T
|
|
1845
|
+
* @param {T} node
|
|
1846
|
+
* @returns {T}
|
|
1805
1847
|
*/
|
|
1806
1848
|
function lower_jsx_code_block_function_body(node) {
|
|
1807
1849
|
if (node.body?.type !== 'JSXCodeBlock') return node;
|
|
@@ -1823,20 +1865,22 @@ function lower_jsx_code_block_function_body(node) {
|
|
|
1823
1865
|
};
|
|
1824
1866
|
render = fragment;
|
|
1825
1867
|
}
|
|
1826
|
-
statements.push(
|
|
1868
|
+
statements.push(
|
|
1869
|
+
b.return(render, has_location(code_block.render) ? code_block.render : undefined),
|
|
1870
|
+
);
|
|
1827
1871
|
}
|
|
1828
1872
|
return {
|
|
1829
1873
|
...node,
|
|
1830
|
-
body: b.block(statements, code_block),
|
|
1874
|
+
body: b.block(statements, has_location(code_block) ? code_block : undefined),
|
|
1831
1875
|
...(node.type === 'ArrowFunctionExpression' ? { expression: false } : null),
|
|
1832
1876
|
};
|
|
1833
1877
|
}
|
|
1834
1878
|
|
|
1835
1879
|
/**
|
|
1836
|
-
* @param {
|
|
1837
|
-
* @param {
|
|
1880
|
+
* @param {AST.Function} node
|
|
1881
|
+
* @param {JsxVisitorContext} context
|
|
1838
1882
|
* @param {{ nativeBody?: boolean }} [options]
|
|
1839
|
-
* @returns {
|
|
1883
|
+
* @returns {AST.Node}
|
|
1840
1884
|
*/
|
|
1841
1885
|
function transform_native_tsrx_function(node, { next, state }, { nativeBody = false } = {}) {
|
|
1842
1886
|
const helper_state =
|
|
@@ -1859,11 +1903,12 @@ function transform_native_tsrx_function(node, { next, state }, { nativeBody = fa
|
|
|
1859
1903
|
|
|
1860
1904
|
validate_native_await(node, state);
|
|
1861
1905
|
|
|
1862
|
-
const inner = /** @type {
|
|
1906
|
+
const inner = /** @type {AST.Function} */ (next() ?? node);
|
|
1863
1907
|
if (
|
|
1864
1908
|
inner !== node &&
|
|
1865
1909
|
node.type === 'ArrowFunctionExpression' &&
|
|
1866
1910
|
is_native_tsrx_node(node.body) &&
|
|
1911
|
+
inner.type === 'ArrowFunctionExpression' &&
|
|
1867
1912
|
inner.body?.type === 'BlockStatement'
|
|
1868
1913
|
) {
|
|
1869
1914
|
inner.expression = false;
|
|
@@ -1884,7 +1929,7 @@ function transform_native_tsrx_function(node, { next, state }, { nativeBody = fa
|
|
|
1884
1929
|
}
|
|
1885
1930
|
|
|
1886
1931
|
/**
|
|
1887
|
-
* @param {
|
|
1932
|
+
* @param {AST.Function} node
|
|
1888
1933
|
* @param {TransformContext} transform_context
|
|
1889
1934
|
* @returns {void}
|
|
1890
1935
|
*/
|
|
@@ -1912,8 +1957,8 @@ function validate_native_await(node, transform_context) {
|
|
|
1912
1957
|
}
|
|
1913
1958
|
|
|
1914
1959
|
/**
|
|
1915
|
-
* @param {
|
|
1916
|
-
* @returns {
|
|
1960
|
+
* @param {AST.Function} node
|
|
1961
|
+
* @returns {AST.TSRXAwaitNode | null}
|
|
1917
1962
|
*/
|
|
1918
1963
|
function find_native_await(node) {
|
|
1919
1964
|
if (
|
|
@@ -1933,8 +1978,8 @@ function find_native_await(node) {
|
|
|
1933
1978
|
}
|
|
1934
1979
|
|
|
1935
1980
|
/**
|
|
1936
|
-
* @param {
|
|
1937
|
-
* @returns {
|
|
1981
|
+
* @param {AST.Node[]} statements
|
|
1982
|
+
* @returns {AST.TSRXAwaitNode | null}
|
|
1938
1983
|
*/
|
|
1939
1984
|
function find_native_await_in_list(statements) {
|
|
1940
1985
|
for (const statement of statements) {
|
|
@@ -1945,14 +1990,14 @@ function find_native_await_in_list(statements) {
|
|
|
1945
1990
|
}
|
|
1946
1991
|
|
|
1947
1992
|
/**
|
|
1948
|
-
* @param {
|
|
1949
|
-
* @returns {
|
|
1993
|
+
* @param {AST.Node | null | undefined} statement
|
|
1994
|
+
* @returns {AST.TSRXAwaitNode | null}
|
|
1950
1995
|
*/
|
|
1951
1996
|
function find_native_await_in_statement(statement) {
|
|
1952
|
-
if (!statement
|
|
1997
|
+
if (!statement) return null;
|
|
1953
1998
|
|
|
1954
1999
|
if (statement.type === 'ReturnStatement' && is_native_tsrx_node(statement.argument)) {
|
|
1955
|
-
return find_first_top_level_await_in_tsrx_function_body(statement.argument
|
|
2000
|
+
return find_first_top_level_await_in_tsrx_function_body(node_children(statement.argument));
|
|
1956
2001
|
}
|
|
1957
2002
|
|
|
1958
2003
|
if (
|
|
@@ -1997,9 +2042,9 @@ function find_native_await_in_statement(statement) {
|
|
|
1997
2042
|
}
|
|
1998
2043
|
|
|
1999
2044
|
/**
|
|
2000
|
-
* @param {
|
|
2001
|
-
* @param {
|
|
2002
|
-
* @returns {
|
|
2045
|
+
* @param {AST.Function} node
|
|
2046
|
+
* @param {JsxVisitorContext} context
|
|
2047
|
+
* @returns {AST.Node}
|
|
2003
2048
|
*/
|
|
2004
2049
|
function transform_function_with_hook_helpers(node, { next, state }) {
|
|
2005
2050
|
if (!state.platform.hooks?.moduleScopedHookComponents) {
|
|
@@ -2020,7 +2065,7 @@ function transform_function_with_hook_helpers(node, { next, state }) {
|
|
|
2020
2065
|
state.hook_helpers_enabled = true;
|
|
2021
2066
|
state.available_bindings = collect_function_scope_bindings(node);
|
|
2022
2067
|
|
|
2023
|
-
const inner = /** @type {
|
|
2068
|
+
const inner = /** @type {AST.Function} */ (next() ?? node);
|
|
2024
2069
|
|
|
2025
2070
|
state.helper_state = saved_helper_state;
|
|
2026
2071
|
state.available_bindings = saved_bindings;
|
|
@@ -2035,7 +2080,7 @@ function transform_function_with_hook_helpers(node, { next, state }) {
|
|
|
2035
2080
|
}
|
|
2036
2081
|
|
|
2037
2082
|
/**
|
|
2038
|
-
* @param {
|
|
2083
|
+
* @param {AST.Function} node
|
|
2039
2084
|
* @returns {string}
|
|
2040
2085
|
*/
|
|
2041
2086
|
function get_function_helper_base_name(node) {
|
|
@@ -2043,7 +2088,7 @@ function get_function_helper_base_name(node) {
|
|
|
2043
2088
|
}
|
|
2044
2089
|
|
|
2045
2090
|
/**
|
|
2046
|
-
* @param {
|
|
2091
|
+
* @param {AST.Function} node
|
|
2047
2092
|
* @returns {boolean}
|
|
2048
2093
|
*/
|
|
2049
2094
|
function is_uppercase_function_like(node) {
|
|
@@ -2052,15 +2097,15 @@ function is_uppercase_function_like(node) {
|
|
|
2052
2097
|
}
|
|
2053
2098
|
|
|
2054
2099
|
/**
|
|
2055
|
-
* @param {
|
|
2100
|
+
* @param {AST.Function} node
|
|
2056
2101
|
* @returns {string | null}
|
|
2057
2102
|
*/
|
|
2058
2103
|
function get_function_like_name(node) {
|
|
2059
|
-
if (node.id?.type === 'Identifier') {
|
|
2104
|
+
if (node.type !== 'ArrowFunctionExpression' && node.id?.type === 'Identifier') {
|
|
2060
2105
|
return node.id.name;
|
|
2061
2106
|
}
|
|
2062
2107
|
|
|
2063
|
-
const parent =
|
|
2108
|
+
const parent = node.metadata?.path?.at(-1);
|
|
2064
2109
|
if (!parent) return null;
|
|
2065
2110
|
|
|
2066
2111
|
if (parent.type === 'VariableDeclarator' && parent.init === node) {
|
|
@@ -2083,7 +2128,7 @@ function get_function_like_name(node) {
|
|
|
2083
2128
|
}
|
|
2084
2129
|
|
|
2085
2130
|
/**
|
|
2086
|
-
* @param {
|
|
2131
|
+
* @param {AST.Node | null | undefined} node
|
|
2087
2132
|
* @returns {string | null}
|
|
2088
2133
|
*/
|
|
2089
2134
|
function get_static_binding_name(node) {
|
|
@@ -2097,7 +2142,7 @@ function get_static_binding_name(node) {
|
|
|
2097
2142
|
}
|
|
2098
2143
|
|
|
2099
2144
|
/**
|
|
2100
|
-
* @param {
|
|
2145
|
+
* @param {AST.Node | null | undefined} key
|
|
2101
2146
|
* @returns {string | null}
|
|
2102
2147
|
*/
|
|
2103
2148
|
function get_static_property_name(key) {
|
|
@@ -2111,7 +2156,7 @@ function get_static_property_name(key) {
|
|
|
2111
2156
|
}
|
|
2112
2157
|
|
|
2113
2158
|
/**
|
|
2114
|
-
* @param {
|
|
2159
|
+
* @param {AST.Function} node
|
|
2115
2160
|
* @returns {Map<string, AST.Identifier>}
|
|
2116
2161
|
*/
|
|
2117
2162
|
function collect_function_scope_bindings(node) {
|
|
@@ -2144,7 +2189,7 @@ function merge_binding_maps(outer, inner) {
|
|
|
2144
2189
|
}
|
|
2145
2190
|
|
|
2146
2191
|
/**
|
|
2147
|
-
* @param {
|
|
2192
|
+
* @param {AST.Function | null | undefined} node
|
|
2148
2193
|
* @returns {boolean}
|
|
2149
2194
|
*/
|
|
2150
2195
|
function function_has_native_tsrx_return(node) {
|
|
@@ -2163,7 +2208,7 @@ function function_has_native_tsrx_return(node) {
|
|
|
2163
2208
|
}
|
|
2164
2209
|
|
|
2165
2210
|
/**
|
|
2166
|
-
* @param {
|
|
2211
|
+
* @param {AST.Node[]} statements
|
|
2167
2212
|
* @returns {boolean}
|
|
2168
2213
|
*/
|
|
2169
2214
|
function statements_contain_native_tsrx_return(statements) {
|
|
@@ -2171,11 +2216,11 @@ function statements_contain_native_tsrx_return(statements) {
|
|
|
2171
2216
|
}
|
|
2172
2217
|
|
|
2173
2218
|
/**
|
|
2174
|
-
* @param {
|
|
2219
|
+
* @param {AST.Node | null | undefined} statement
|
|
2175
2220
|
* @returns {boolean}
|
|
2176
2221
|
*/
|
|
2177
2222
|
function statement_contains_native_tsrx_return(statement) {
|
|
2178
|
-
if (!statement
|
|
2223
|
+
if (!statement) return false;
|
|
2179
2224
|
|
|
2180
2225
|
if (statement.type === 'ReturnStatement') {
|
|
2181
2226
|
return node_contains_native_tsrx_template(statement.argument);
|
|
@@ -2197,7 +2242,7 @@ function statement_contains_native_tsrx_return(statement) {
|
|
|
2197
2242
|
}
|
|
2198
2243
|
|
|
2199
2244
|
if (is_switch_control_node(statement)) {
|
|
2200
|
-
return (statement.cases || []).some((
|
|
2245
|
+
return (statement.cases || []).some((c) =>
|
|
2201
2246
|
statements_contain_native_tsrx_return(c.consequent || []),
|
|
2202
2247
|
);
|
|
2203
2248
|
}
|
|
@@ -2211,14 +2256,8 @@ function statement_contains_native_tsrx_return(statement) {
|
|
|
2211
2256
|
);
|
|
2212
2257
|
}
|
|
2213
2258
|
|
|
2214
|
-
for (const
|
|
2215
|
-
if (
|
|
2216
|
-
continue;
|
|
2217
|
-
}
|
|
2218
|
-
const value = statement[key];
|
|
2219
|
-
if (Array.isArray(value)) {
|
|
2220
|
-
if (statements_contain_native_tsrx_return(value)) return true;
|
|
2221
|
-
} else if (statement_contains_native_tsrx_return(value)) {
|
|
2259
|
+
for (const child of child_nodes(statement)) {
|
|
2260
|
+
if (statement_contains_native_tsrx_return(child)) {
|
|
2222
2261
|
return true;
|
|
2223
2262
|
}
|
|
2224
2263
|
}
|
|
@@ -2227,26 +2266,24 @@ function statement_contains_native_tsrx_return(statement) {
|
|
|
2227
2266
|
}
|
|
2228
2267
|
|
|
2229
2268
|
/**
|
|
2230
|
-
* @param {
|
|
2269
|
+
* @param {AST.Node | AST.Node[] | null | undefined} node
|
|
2231
2270
|
* @returns {boolean}
|
|
2232
2271
|
*/
|
|
2233
2272
|
function node_contains_native_tsrx_template(node) {
|
|
2234
|
-
if (!node
|
|
2273
|
+
if (!node) return false;
|
|
2274
|
+
|
|
2275
|
+
if (Array.isArray(node)) {
|
|
2276
|
+
return node.some((child) => node_contains_native_tsrx_template(child));
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2235
2279
|
if (is_native_tsrx_node(node)) return true;
|
|
2236
2280
|
|
|
2237
2281
|
if (is_function_or_class_boundary(node)) {
|
|
2238
2282
|
return false;
|
|
2239
2283
|
}
|
|
2240
2284
|
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
}
|
|
2244
|
-
|
|
2245
|
-
for (const key of Object.keys(node)) {
|
|
2246
|
-
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
2247
|
-
continue;
|
|
2248
|
-
}
|
|
2249
|
-
if (node_contains_native_tsrx_template(node[key])) {
|
|
2285
|
+
for (const child of child_nodes(node)) {
|
|
2286
|
+
if (node_contains_native_tsrx_template(child)) {
|
|
2250
2287
|
return true;
|
|
2251
2288
|
}
|
|
2252
2289
|
}
|
|
@@ -2255,13 +2292,13 @@ function node_contains_native_tsrx_template(node) {
|
|
|
2255
2292
|
}
|
|
2256
2293
|
|
|
2257
2294
|
/**
|
|
2258
|
-
* @param {
|
|
2259
|
-
* @returns {
|
|
2295
|
+
* @param {AST.NativeTSRXNode} node
|
|
2296
|
+
* @returns {AST.CSS.StyleSheet | null}
|
|
2260
2297
|
*/
|
|
2261
2298
|
function collect_tsrx_stylesheet(node) {
|
|
2262
|
-
/** @type {
|
|
2299
|
+
/** @type {AST.CSS.StyleSheet[]} */
|
|
2263
2300
|
const styles = [];
|
|
2264
|
-
collect_style_elements(node
|
|
2301
|
+
collect_style_elements(node_children(node), styles);
|
|
2265
2302
|
|
|
2266
2303
|
if (styles.length === 0) return null;
|
|
2267
2304
|
if (styles.length > 1) {
|
|
@@ -2272,9 +2309,9 @@ function collect_tsrx_stylesheet(node) {
|
|
|
2272
2309
|
}
|
|
2273
2310
|
|
|
2274
2311
|
/**
|
|
2275
|
-
* @param {
|
|
2312
|
+
* @param {AST.NativeTSRXNode} node
|
|
2276
2313
|
* @param {TransformContext} transform_context
|
|
2277
|
-
* @returns {
|
|
2314
|
+
* @returns {JsxStyleContext | null}
|
|
2278
2315
|
*/
|
|
2279
2316
|
function prepare_tsrx_fragment_styles(node, transform_context) {
|
|
2280
2317
|
const css = collect_tsrx_stylesheet(node);
|
|
@@ -2297,9 +2334,9 @@ function prepare_tsrx_fragment_styles(node, transform_context) {
|
|
|
2297
2334
|
|
|
2298
2335
|
/**
|
|
2299
2336
|
* @template T
|
|
2300
|
-
* @param {
|
|
2337
|
+
* @param {AST.NativeTSRXNode} node
|
|
2301
2338
|
* @param {TransformContext} transform_context
|
|
2302
|
-
* @param {(style_context:
|
|
2339
|
+
* @param {(style_context: JsxStyleContext | null) => T} callback
|
|
2303
2340
|
* @returns {T}
|
|
2304
2341
|
*/
|
|
2305
2342
|
function with_tsrx_fragment_styles(node, transform_context, callback) {
|
|
@@ -2308,8 +2345,8 @@ function with_tsrx_fragment_styles(node, transform_context, callback) {
|
|
|
2308
2345
|
}
|
|
2309
2346
|
|
|
2310
2347
|
/**
|
|
2311
|
-
* @param {
|
|
2312
|
-
* @param {
|
|
2348
|
+
* @param {AST.Node} fragment
|
|
2349
|
+
* @param {JsxStyleContext | null} style_context
|
|
2313
2350
|
* @param {TransformContext} transform_context
|
|
2314
2351
|
* @returns {AST.Statement[]}
|
|
2315
2352
|
*/
|
|
@@ -2330,8 +2367,8 @@ function create_tsrx_style_ref_setup_statements(fragment, style_context, transfo
|
|
|
2330
2367
|
}
|
|
2331
2368
|
|
|
2332
2369
|
/**
|
|
2333
|
-
* @param {
|
|
2334
|
-
* @param {
|
|
2370
|
+
* @param {AST.JSXStyleElement} node
|
|
2371
|
+
* @param {AST.CSS.StyleSheet} stylesheet
|
|
2335
2372
|
* @param {TransformContext} transform_context
|
|
2336
2373
|
* @returns {AST.Expression}
|
|
2337
2374
|
*/
|
|
@@ -2346,7 +2383,7 @@ function create_style_expression_value(node, stylesheet, transform_context) {
|
|
|
2346
2383
|
}
|
|
2347
2384
|
|
|
2348
2385
|
/**
|
|
2349
|
-
* @param {
|
|
2386
|
+
* @param {AST.JSXStyleElement} node
|
|
2350
2387
|
* @param {TransformContext} transform_context
|
|
2351
2388
|
*/
|
|
2352
2389
|
function add_type_only_style_anchor(node, transform_context) {
|
|
@@ -2401,12 +2438,12 @@ function create_style_ref_temp_name(transform_context) {
|
|
|
2401
2438
|
}
|
|
2402
2439
|
|
|
2403
2440
|
/**
|
|
2404
|
-
* @param {
|
|
2405
|
-
* @param {
|
|
2441
|
+
* @param {AST.Node | AST.Node[] | null | undefined} node
|
|
2442
|
+
* @param {AST.CSS.StyleSheet[]} styles
|
|
2406
2443
|
* @returns {void}
|
|
2407
2444
|
*/
|
|
2408
2445
|
function collect_style_elements(node, styles) {
|
|
2409
|
-
if (!node
|
|
2446
|
+
if (!node) return;
|
|
2410
2447
|
|
|
2411
2448
|
if (Array.isArray(node)) {
|
|
2412
2449
|
for (const child of node) {
|
|
@@ -2416,9 +2453,7 @@ function collect_style_elements(node, styles) {
|
|
|
2416
2453
|
}
|
|
2417
2454
|
|
|
2418
2455
|
if (is_style_element(node)) {
|
|
2419
|
-
const stylesheet = node.children?.find(
|
|
2420
|
-
(/** @type {any} */ child) => child.type === 'StyleSheet',
|
|
2421
|
-
);
|
|
2456
|
+
const stylesheet = node.children?.find((child) => child.type === 'StyleSheet');
|
|
2422
2457
|
if (stylesheet) {
|
|
2423
2458
|
styles.push(stylesheet);
|
|
2424
2459
|
}
|
|
@@ -2460,43 +2495,61 @@ function collect_style_elements(node, styles) {
|
|
|
2460
2495
|
}
|
|
2461
2496
|
|
|
2462
2497
|
/**
|
|
2463
|
-
* @
|
|
2498
|
+
* @template {AST.NativeTSRXNode} T
|
|
2499
|
+
* @param {T} node
|
|
2464
2500
|
* @param {string} hash
|
|
2465
2501
|
* @param {'class' | 'className'} jsx_class_attr_name
|
|
2466
2502
|
* @param {boolean} preserve_style_elements
|
|
2467
|
-
* @returns {
|
|
2503
|
+
* @returns {T}
|
|
2468
2504
|
*/
|
|
2469
2505
|
function annotate_tsrx_with_hash(node, hash, jsx_class_attr_name, preserve_style_elements) {
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2506
|
+
// `annotate_with_hash` returns null for a `<style>` element it drops, so
|
|
2507
|
+
// filter before the children go back on the node.
|
|
2508
|
+
let children = node_children(node)
|
|
2509
|
+
.map((statement) =>
|
|
2510
|
+
annotate_with_hash(
|
|
2511
|
+
clone_ast_node(statement),
|
|
2512
|
+
hash,
|
|
2513
|
+
jsx_class_attr_name,
|
|
2514
|
+
preserve_style_elements,
|
|
2515
|
+
),
|
|
2516
|
+
)
|
|
2517
|
+
.filter(is_ast_node);
|
|
2479
2518
|
if (!preserve_style_elements) {
|
|
2480
|
-
|
|
2519
|
+
children = strip_style_elements_from_list(children);
|
|
2481
2520
|
}
|
|
2482
|
-
|
|
2521
|
+
// Shallow copy with rewritten children; the spread preserves `node`'s type.
|
|
2522
|
+
return /** @type {T} */ ({ ...node, children });
|
|
2483
2523
|
}
|
|
2484
2524
|
|
|
2485
2525
|
/**
|
|
2486
|
-
*
|
|
2487
|
-
*
|
|
2526
|
+
* Drop `<style>` elements from a child list, recursing into the nodes that
|
|
2527
|
+
* survive.
|
|
2528
|
+
*
|
|
2529
|
+
* @param {Array<AST.Node | null | undefined>} nodes
|
|
2530
|
+
* @returns {AST.Node[]}
|
|
2488
2531
|
*/
|
|
2489
|
-
function
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
.filter(Boolean);
|
|
2532
|
+
function strip_style_elements_from_list(nodes) {
|
|
2533
|
+
/** @type {AST.Node[]} */
|
|
2534
|
+
const kept = [];
|
|
2535
|
+
for (const child of nodes) {
|
|
2536
|
+
if (is_style_element(child)) continue;
|
|
2537
|
+
const stripped = strip_style_elements(child);
|
|
2538
|
+
if (stripped) kept.push(stripped);
|
|
2497
2539
|
}
|
|
2540
|
+
return kept;
|
|
2541
|
+
}
|
|
2498
2542
|
|
|
2499
|
-
|
|
2543
|
+
/**
|
|
2544
|
+
* Strip `<style>` elements out of one node's render subtree, in place. Returns
|
|
2545
|
+
* `null` when the node itself is a `<style>` element and should be dropped, or
|
|
2546
|
+
* when there is no node — child lists can hold holes.
|
|
2547
|
+
*
|
|
2548
|
+
* @param {AST.Node | null | undefined} node
|
|
2549
|
+
* @returns {AST.Node | null}
|
|
2550
|
+
*/
|
|
2551
|
+
function strip_style_elements(node) {
|
|
2552
|
+
if (!node || is_style_element(node)) {
|
|
2500
2553
|
return null;
|
|
2501
2554
|
}
|
|
2502
2555
|
|
|
@@ -2505,39 +2558,45 @@ function strip_style_elements(node) {
|
|
|
2505
2558
|
}
|
|
2506
2559
|
|
|
2507
2560
|
if ((node.type === 'JSXElement' || node.type === 'JSXFragment') && node.metadata?.native_tsrx) {
|
|
2508
|
-
node.children =
|
|
2561
|
+
node.children = strip_style_elements_from_list(node.children || []);
|
|
2509
2562
|
return node;
|
|
2510
2563
|
}
|
|
2511
2564
|
|
|
2512
2565
|
if (node.type === 'BlockStatement') {
|
|
2513
|
-
node.body =
|
|
2566
|
+
node.body = /** @type {AST.Statement[]} */ (strip_style_elements_from_list(node.body || []));
|
|
2514
2567
|
return node;
|
|
2515
2568
|
}
|
|
2516
2569
|
|
|
2517
2570
|
if (is_if_control_node(node)) {
|
|
2518
|
-
|
|
2519
|
-
if (
|
|
2571
|
+
const consequent = strip_style_elements(node.consequent);
|
|
2572
|
+
if (consequent) node.consequent = /** @type {AST.Statement} */ (consequent);
|
|
2573
|
+
if (node.alternate) {
|
|
2574
|
+
const alternate = strip_style_elements(node.alternate);
|
|
2575
|
+
if (alternate) node.alternate = /** @type {AST.Statement} */ (alternate);
|
|
2576
|
+
}
|
|
2520
2577
|
return node;
|
|
2521
2578
|
}
|
|
2522
2579
|
|
|
2523
2580
|
if (is_switch_control_node(node)) {
|
|
2524
2581
|
for (const switch_case of node.cases || []) {
|
|
2525
|
-
switch_case.consequent =
|
|
2582
|
+
switch_case.consequent = /** @type {AST.Statement[]} */ (
|
|
2583
|
+
strip_style_elements_from_list(switch_case.consequent || [])
|
|
2584
|
+
);
|
|
2526
2585
|
}
|
|
2527
2586
|
return node;
|
|
2528
2587
|
}
|
|
2529
2588
|
|
|
2530
2589
|
if (is_try_control_node(node)) {
|
|
2531
|
-
|
|
2532
|
-
if (node.handler?.body)
|
|
2533
|
-
if (node.finalizer)
|
|
2590
|
+
strip_style_elements(node.block);
|
|
2591
|
+
if (node.handler?.body) strip_style_elements(node.handler.body);
|
|
2592
|
+
if (node.finalizer) strip_style_elements(node.finalizer);
|
|
2534
2593
|
}
|
|
2535
2594
|
|
|
2536
2595
|
return node;
|
|
2537
2596
|
}
|
|
2538
2597
|
|
|
2539
2598
|
/**
|
|
2540
|
-
* @param {
|
|
2599
|
+
* @param {AST.Node[]} path
|
|
2541
2600
|
* @returns {boolean}
|
|
2542
2601
|
*/
|
|
2543
2602
|
function is_style_expression_position(path) {
|
|
@@ -2551,16 +2610,16 @@ function is_style_expression_position(path) {
|
|
|
2551
2610
|
}
|
|
2552
2611
|
|
|
2553
2612
|
/**
|
|
2554
|
-
* @param {
|
|
2613
|
+
* @param {AST.NativeTSRXNode} fragment
|
|
2555
2614
|
* @param {TransformContext} transform_context
|
|
2556
|
-
* @returns {
|
|
2615
|
+
* @returns {AST.BlockStatement}
|
|
2557
2616
|
*/
|
|
2558
2617
|
function create_native_tsrx_render_block(fragment, transform_context) {
|
|
2559
2618
|
const block = b.block(
|
|
2560
2619
|
mark_native_pretransformed_jsx(
|
|
2561
2620
|
create_native_tsrx_render_statements(fragment, transform_context),
|
|
2562
2621
|
),
|
|
2563
|
-
fragment,
|
|
2622
|
+
has_location(fragment) ? fragment : undefined,
|
|
2564
2623
|
);
|
|
2565
2624
|
block.metadata = {
|
|
2566
2625
|
...(block.metadata || {}),
|
|
@@ -2570,9 +2629,9 @@ function create_native_tsrx_render_block(fragment, transform_context) {
|
|
|
2570
2629
|
}
|
|
2571
2630
|
|
|
2572
2631
|
/**
|
|
2573
|
-
* @param {
|
|
2632
|
+
* @param {AST.BlockStatement} block
|
|
2574
2633
|
* @param {TransformContext} transform_context
|
|
2575
|
-
* @returns {
|
|
2634
|
+
* @returns {AST.BlockStatement | null}
|
|
2576
2635
|
*/
|
|
2577
2636
|
function create_native_tsrx_statement_list_block(block, transform_context) {
|
|
2578
2637
|
const source_body = block.body || [];
|
|
@@ -2582,7 +2641,10 @@ function create_native_tsrx_statement_list_block(block, transform_context) {
|
|
|
2582
2641
|
return null;
|
|
2583
2642
|
}
|
|
2584
2643
|
|
|
2585
|
-
const next_block = b.block(
|
|
2644
|
+
const next_block = b.block(
|
|
2645
|
+
mark_native_pretransformed_jsx(body),
|
|
2646
|
+
has_location(block) ? block : undefined,
|
|
2647
|
+
);
|
|
2586
2648
|
next_block.metadata = {
|
|
2587
2649
|
...(next_block.metadata || {}),
|
|
2588
2650
|
native_return_block: true,
|
|
@@ -2591,7 +2653,7 @@ function create_native_tsrx_statement_list_block(block, transform_context) {
|
|
|
2591
2653
|
}
|
|
2592
2654
|
|
|
2593
2655
|
/**
|
|
2594
|
-
* @param {
|
|
2656
|
+
* @param {AST.NativeTSRXNode} fragment
|
|
2595
2657
|
* @param {TransformContext} transform_context
|
|
2596
2658
|
* @returns {AST.Statement[]}
|
|
2597
2659
|
*/
|
|
@@ -2608,9 +2670,9 @@ function create_native_tsrx_render_statements(fragment, transform_context) {
|
|
|
2608
2670
|
}
|
|
2609
2671
|
|
|
2610
2672
|
/**
|
|
2611
|
-
* @param {
|
|
2673
|
+
* @param {AST.Statement[]} statements
|
|
2612
2674
|
* @param {TransformContext} transform_context
|
|
2613
|
-
* @returns {
|
|
2675
|
+
* @returns {AST.Statement[]}
|
|
2614
2676
|
*/
|
|
2615
2677
|
function expand_native_tsrx_return_statement_list(statements, transform_context) {
|
|
2616
2678
|
let changed = false;
|
|
@@ -2625,13 +2687,11 @@ function expand_native_tsrx_return_statement_list(statements, transform_context)
|
|
|
2625
2687
|
}
|
|
2626
2688
|
|
|
2627
2689
|
/**
|
|
2628
|
-
* @param {
|
|
2690
|
+
* @param {AST.Statement} statement
|
|
2629
2691
|
* @param {TransformContext} transform_context
|
|
2630
|
-
* @returns {
|
|
2692
|
+
* @returns {AST.Statement[]}
|
|
2631
2693
|
*/
|
|
2632
2694
|
function expand_native_tsrx_return_statement(statement, transform_context) {
|
|
2633
|
-
if (!statement || typeof statement !== 'object') return [statement];
|
|
2634
|
-
|
|
2635
2695
|
if (statement.type === 'ReturnStatement' && is_native_tsrx_node(statement.argument)) {
|
|
2636
2696
|
return create_native_tsrx_render_statements(statement.argument, transform_context);
|
|
2637
2697
|
}
|
|
@@ -2642,7 +2702,9 @@ function expand_native_tsrx_return_statement(statement, transform_context) {
|
|
|
2642
2702
|
|
|
2643
2703
|
if (statement.type === 'BlockStatement') {
|
|
2644
2704
|
const body = expand_native_tsrx_return_statement_list(statement.body || [], transform_context);
|
|
2645
|
-
return body === statement.body
|
|
2705
|
+
return body === statement.body
|
|
2706
|
+
? [statement]
|
|
2707
|
+
: [b.block(body, has_location(statement) ? statement : undefined)];
|
|
2646
2708
|
}
|
|
2647
2709
|
|
|
2648
2710
|
if (is_if_control_node(statement)) {
|
|
@@ -2661,7 +2723,7 @@ function expand_native_tsrx_return_statement(statement, transform_context) {
|
|
|
2661
2723
|
|
|
2662
2724
|
if (is_switch_control_node(statement)) {
|
|
2663
2725
|
let changed = false;
|
|
2664
|
-
const cases = (statement.cases || []).map((
|
|
2726
|
+
const cases = (statement.cases || []).map((switch_case) => {
|
|
2665
2727
|
const consequent = expand_native_tsrx_return_statement_list(
|
|
2666
2728
|
switch_case.consequent || [],
|
|
2667
2729
|
transform_context,
|
|
@@ -2695,12 +2757,12 @@ function expand_native_tsrx_return_statement(statement, transform_context) {
|
|
|
2695
2757
|
return [statement];
|
|
2696
2758
|
}
|
|
2697
2759
|
const handler =
|
|
2698
|
-
statement.handler && handler_body !== statement.handler.body
|
|
2760
|
+
statement.handler && handler_body && handler_body !== statement.handler.body
|
|
2699
2761
|
? b.catch_clause(
|
|
2700
2762
|
statement.handler.param,
|
|
2701
2763
|
statement.handler.resetParam,
|
|
2702
2764
|
handler_body,
|
|
2703
|
-
statement.handler,
|
|
2765
|
+
has_location(statement.handler) ? statement.handler : undefined,
|
|
2704
2766
|
)
|
|
2705
2767
|
: statement.handler;
|
|
2706
2768
|
return [set_loc(b.try(block, handler, finalizer, pending ?? null), statement)];
|
|
@@ -2710,68 +2772,78 @@ function expand_native_tsrx_return_statement(statement, transform_context) {
|
|
|
2710
2772
|
}
|
|
2711
2773
|
|
|
2712
2774
|
/**
|
|
2713
|
-
*
|
|
2775
|
+
* A block always expands back to a block, so try/catch slots keep their shape.
|
|
2776
|
+
* @overload
|
|
2777
|
+
* @param {AST.BlockStatement} statement
|
|
2778
|
+
* @param {TransformContext} transform_context
|
|
2779
|
+
* @returns {AST.BlockStatement}
|
|
2780
|
+
*/
|
|
2781
|
+
/**
|
|
2782
|
+
* @overload
|
|
2783
|
+
* @param {AST.Statement} statement
|
|
2714
2784
|
* @param {TransformContext} transform_context
|
|
2715
|
-
* @returns {
|
|
2785
|
+
* @returns {AST.Statement}
|
|
2786
|
+
*/
|
|
2787
|
+
/**
|
|
2788
|
+
* @param {AST.Statement} statement
|
|
2789
|
+
* @param {TransformContext} transform_context
|
|
2790
|
+
* @returns {AST.Statement}
|
|
2716
2791
|
*/
|
|
2717
2792
|
function expand_embedded_native_return_statement(statement, transform_context) {
|
|
2718
2793
|
const expanded = expand_native_tsrx_return_statement(statement, transform_context);
|
|
2719
|
-
return expanded.length === 1
|
|
2794
|
+
return expanded.length === 1
|
|
2795
|
+
? expanded[0]
|
|
2796
|
+
: b.block(expanded, has_location(statement) ? statement : undefined);
|
|
2720
2797
|
}
|
|
2721
2798
|
|
|
2722
2799
|
/**
|
|
2723
|
-
* @template T
|
|
2800
|
+
* @template {AST.Node | AST.Node[]} T
|
|
2724
2801
|
* @param {T} node
|
|
2725
|
-
* @param {Set<
|
|
2802
|
+
* @param {Set<AST.Node>} [seen]
|
|
2726
2803
|
* @returns {T}
|
|
2727
2804
|
*/
|
|
2728
2805
|
function mark_native_pretransformed_jsx(node, seen = new Set()) {
|
|
2729
|
-
if (
|
|
2806
|
+
if (Array.isArray(node)) {
|
|
2807
|
+
for (const item of node) mark_native_pretransformed_jsx(item, seen);
|
|
2730
2808
|
return node;
|
|
2731
2809
|
}
|
|
2732
|
-
seen.add(node);
|
|
2733
2810
|
|
|
2734
|
-
if (
|
|
2735
|
-
for (const item of node) mark_native_pretransformed_jsx(item, seen);
|
|
2811
|
+
if (seen.has(node)) {
|
|
2736
2812
|
return node;
|
|
2737
2813
|
}
|
|
2814
|
+
seen.add(node);
|
|
2738
2815
|
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
...(as_node.metadata || {}),
|
|
2816
|
+
if (node.type === 'JSXOpeningElement') {
|
|
2817
|
+
node.metadata = {
|
|
2818
|
+
...node.metadata,
|
|
2743
2819
|
native_tsrx_pretransformed: true,
|
|
2744
2820
|
};
|
|
2745
2821
|
}
|
|
2746
2822
|
|
|
2747
|
-
for (const
|
|
2748
|
-
|
|
2749
|
-
continue;
|
|
2750
|
-
}
|
|
2751
|
-
mark_native_pretransformed_jsx(as_node[key], seen);
|
|
2823
|
+
for (const child of child_nodes(node)) {
|
|
2824
|
+
mark_native_pretransformed_jsx(child, seen);
|
|
2752
2825
|
}
|
|
2753
2826
|
|
|
2754
2827
|
return node;
|
|
2755
2828
|
}
|
|
2756
2829
|
|
|
2757
2830
|
/**
|
|
2758
|
-
* @param {
|
|
2759
|
-
* @returns {
|
|
2831
|
+
* @param {AST.NativeTSRXNode} node
|
|
2832
|
+
* @returns {AST.Node[]}
|
|
2760
2833
|
*/
|
|
2761
2834
|
function get_tsrx_render_children(node) {
|
|
2762
|
-
return (node
|
|
2763
|
-
(
|
|
2764
|
-
child && child.type !== 'EmptyStatement' && (child.type !== 'JSXText' || child.value !== ''),
|
|
2835
|
+
return node_children(node).filter(
|
|
2836
|
+
(child) => child.type !== 'EmptyStatement' && (child.type !== 'JSXText' || child.value !== ''),
|
|
2765
2837
|
);
|
|
2766
2838
|
}
|
|
2767
2839
|
|
|
2768
2840
|
/**
|
|
2769
|
-
* @param {
|
|
2841
|
+
* @param {AST.Node | null | undefined} node
|
|
2770
2842
|
* @param {Map<string, AST.Identifier>} bindings
|
|
2771
2843
|
* @returns {void}
|
|
2772
2844
|
*/
|
|
2773
2845
|
function collect_descendant_declaration_bindings(node, bindings) {
|
|
2774
|
-
if (!node
|
|
2846
|
+
if (!node) {
|
|
2775
2847
|
return;
|
|
2776
2848
|
}
|
|
2777
2849
|
|
|
@@ -2796,23 +2868,13 @@ function collect_descendant_declaration_bindings(node, bindings) {
|
|
|
2796
2868
|
return;
|
|
2797
2869
|
}
|
|
2798
2870
|
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
collect_descendant_declaration_bindings(child, bindings);
|
|
2802
|
-
}
|
|
2803
|
-
return;
|
|
2804
|
-
}
|
|
2805
|
-
|
|
2806
|
-
for (const key of Object.keys(node)) {
|
|
2807
|
-
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
2808
|
-
continue;
|
|
2809
|
-
}
|
|
2810
|
-
collect_descendant_declaration_bindings(node[key], bindings);
|
|
2871
|
+
for (const child of child_nodes(node)) {
|
|
2872
|
+
collect_descendant_declaration_bindings(child, bindings);
|
|
2811
2873
|
}
|
|
2812
2874
|
}
|
|
2813
2875
|
|
|
2814
2876
|
/**
|
|
2815
|
-
* @param {
|
|
2877
|
+
* @param {AST.Function} node
|
|
2816
2878
|
* @param {TransformContext} transform_context
|
|
2817
2879
|
* @returns {boolean}
|
|
2818
2880
|
*/
|
|
@@ -2821,25 +2883,17 @@ function function_contains_hook_bearing_tsrx(node, transform_context) {
|
|
|
2821
2883
|
}
|
|
2822
2884
|
|
|
2823
2885
|
/**
|
|
2824
|
-
* @param {
|
|
2886
|
+
* @param {AST.Node | null | undefined} node
|
|
2825
2887
|
* @param {TransformContext} transform_context
|
|
2826
2888
|
* @returns {boolean}
|
|
2827
2889
|
*/
|
|
2828
2890
|
function node_contains_hook_bearing_tsrx(node, transform_context) {
|
|
2829
|
-
if (!node
|
|
2891
|
+
if (!node) {
|
|
2830
2892
|
return false;
|
|
2831
2893
|
}
|
|
2832
2894
|
|
|
2833
|
-
if (Array.isArray(node)) {
|
|
2834
|
-
return node.some((child) => node_contains_hook_bearing_tsrx(child, transform_context));
|
|
2835
|
-
}
|
|
2836
|
-
|
|
2837
2895
|
if (is_native_tsrx_node(node)) {
|
|
2838
|
-
return body_contains_top_level_hook_call(
|
|
2839
|
-
'children' in node ? node.children : [],
|
|
2840
|
-
transform_context,
|
|
2841
|
-
true,
|
|
2842
|
-
);
|
|
2896
|
+
return body_contains_top_level_hook_call(node_children(node), transform_context, true);
|
|
2843
2897
|
}
|
|
2844
2898
|
|
|
2845
2899
|
if (
|
|
@@ -2850,11 +2904,8 @@ function node_contains_hook_bearing_tsrx(node, transform_context) {
|
|
|
2850
2904
|
return false;
|
|
2851
2905
|
}
|
|
2852
2906
|
|
|
2853
|
-
for (const
|
|
2854
|
-
if (
|
|
2855
|
-
continue;
|
|
2856
|
-
}
|
|
2857
|
-
if (node_contains_hook_bearing_tsrx(node[key], transform_context)) {
|
|
2907
|
+
for (const child of child_nodes(node)) {
|
|
2908
|
+
if (node_contains_hook_bearing_tsrx(child, transform_context)) {
|
|
2858
2909
|
return true;
|
|
2859
2910
|
}
|
|
2860
2911
|
}
|
|
@@ -2893,7 +2944,7 @@ function create_module_scoped_hook_component_id(helper_id, transform_context) {
|
|
|
2893
2944
|
}
|
|
2894
2945
|
|
|
2895
2946
|
/**
|
|
2896
|
-
* @param {
|
|
2947
|
+
* @param {AST.Pattern[]} params
|
|
2897
2948
|
* @returns {Map<string, AST.Identifier>}
|
|
2898
2949
|
*/
|
|
2899
2950
|
export function collect_param_bindings(params) {
|
|
@@ -2905,7 +2956,7 @@ export function collect_param_bindings(params) {
|
|
|
2905
2956
|
}
|
|
2906
2957
|
|
|
2907
2958
|
/**
|
|
2908
|
-
* @param {
|
|
2959
|
+
* @param {AST.Node | null | undefined} statement
|
|
2909
2960
|
* @param {Map<string, AST.Identifier>} bindings
|
|
2910
2961
|
* @returns {void}
|
|
2911
2962
|
*/
|
|
@@ -2940,12 +2991,12 @@ export function collect_statement_bindings(statement, bindings) {
|
|
|
2940
2991
|
}
|
|
2941
2992
|
|
|
2942
2993
|
/**
|
|
2943
|
-
* @param {
|
|
2994
|
+
* @param {AST.Node | null | undefined} pattern
|
|
2944
2995
|
* @param {Map<string, AST.Identifier>} bindings
|
|
2945
2996
|
* @returns {void}
|
|
2946
2997
|
*/
|
|
2947
2998
|
function collect_pattern_bindings(pattern, bindings) {
|
|
2948
|
-
if (!pattern
|
|
2999
|
+
if (!pattern) return;
|
|
2949
3000
|
|
|
2950
3001
|
if (pattern.type === 'Identifier') {
|
|
2951
3002
|
bindings.set(pattern.name, pattern);
|
|
@@ -2984,12 +3035,12 @@ function collect_pattern_bindings(pattern, bindings) {
|
|
|
2984
3035
|
* Check if a node references any of the given scope bindings.
|
|
2985
3036
|
* Used to determine if a JSX element is static and can be hoisted to module level.
|
|
2986
3037
|
*
|
|
2987
|
-
* @param {
|
|
3038
|
+
* @param {AST.Node | null | undefined} node
|
|
2988
3039
|
* @param {Map<string, AST.Identifier>} scope_bindings
|
|
2989
3040
|
* @returns {boolean}
|
|
2990
3041
|
*/
|
|
2991
3042
|
function references_scope_bindings(node, scope_bindings) {
|
|
2992
|
-
if (!node
|
|
3043
|
+
if (!node) return false;
|
|
2993
3044
|
if (scope_bindings.size === 0) return false;
|
|
2994
3045
|
|
|
2995
3046
|
if (node.type === 'Identifier') {
|
|
@@ -3002,11 +3053,10 @@ function references_scope_bindings(node, scope_bindings) {
|
|
|
3002
3053
|
return scope_bindings.has(node.name);
|
|
3003
3054
|
}
|
|
3004
3055
|
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
}
|
|
3008
|
-
|
|
3009
|
-
for (const key of Object.keys(node)) {
|
|
3056
|
+
// Not `child_nodes`: several keys are labels rather than references and must
|
|
3057
|
+
// be skipped based on the owning node's type.
|
|
3058
|
+
const entries = /** @type {AST.TraversableAstNode} */ (node);
|
|
3059
|
+
for (const key of Object.keys(entries)) {
|
|
3010
3060
|
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') continue;
|
|
3011
3061
|
|
|
3012
3062
|
// Skip non-computed, non-shorthand property keys (they are labels, not references)
|
|
@@ -3021,7 +3071,15 @@ function references_scope_bindings(node, scope_bindings) {
|
|
|
3021
3071
|
// Skip JSXAttribute names — they are attribute labels, not variable references
|
|
3022
3072
|
if (key === 'name' && node.type === 'JSXAttribute') continue;
|
|
3023
3073
|
|
|
3024
|
-
|
|
3074
|
+
const value = entries[key];
|
|
3075
|
+
if (Array.isArray(value)) {
|
|
3076
|
+
if (
|
|
3077
|
+
value.some((item) => is_ast_node(item) && references_scope_bindings(item, scope_bindings))
|
|
3078
|
+
)
|
|
3079
|
+
return true;
|
|
3080
|
+
} else if (is_ast_node(value) && references_scope_bindings(value, scope_bindings)) {
|
|
3081
|
+
return true;
|
|
3082
|
+
}
|
|
3025
3083
|
}
|
|
3026
3084
|
|
|
3027
3085
|
return false;
|
|
@@ -3033,7 +3091,7 @@ function references_scope_bindings(node, scope_bindings) {
|
|
|
3033
3091
|
* Hoisting prevents React from recreating the element on every render, allowing
|
|
3034
3092
|
* the reconciler to skip diffing when it sees the same element identity.
|
|
3035
3093
|
*
|
|
3036
|
-
* @param {
|
|
3094
|
+
* @param {ESTreeJSX.JSXRenderChild[]} render_nodes
|
|
3037
3095
|
* @param {TransformContext} transform_context
|
|
3038
3096
|
*/
|
|
3039
3097
|
function hoist_static_render_nodes(render_nodes, transform_context) {
|
|
@@ -3076,7 +3134,7 @@ function hoist_static_render_nodes(render_nodes, transform_context) {
|
|
|
3076
3134
|
* identifiers are host DOM tags, which *do* benefit from hoisting because
|
|
3077
3135
|
* React diffs them against the previous render.
|
|
3078
3136
|
*
|
|
3079
|
-
* @param {
|
|
3137
|
+
* @param {AST.Node | null | undefined} node
|
|
3080
3138
|
* @returns {boolean}
|
|
3081
3139
|
*/
|
|
3082
3140
|
function is_bare_component_invocation(node) {
|
|
@@ -3115,34 +3173,37 @@ function expand_component_helpers(program) {
|
|
|
3115
3173
|
* node (or array), so the return value must be used in place of the argument.
|
|
3116
3174
|
* Untouched subtrees are shared by reference with the input.
|
|
3117
3175
|
*
|
|
3118
|
-
* @
|
|
3176
|
+
* @template {AST.Node} T
|
|
3177
|
+
* @param {T} node
|
|
3119
3178
|
* @param {TransformContext} transform_context
|
|
3120
|
-
* @param {Set<
|
|
3121
|
-
* @returns {
|
|
3179
|
+
* @param {Set<AST.Node>} [seen]
|
|
3180
|
+
* @returns {T}
|
|
3122
3181
|
*/
|
|
3123
3182
|
function lower_remaining_jsx_code_blocks(node, transform_context, seen = new Set()) {
|
|
3124
|
-
if (
|
|
3183
|
+
if (seen.has(node)) return node;
|
|
3125
3184
|
seen.add(node);
|
|
3126
3185
|
|
|
3127
3186
|
// A code-block function body lowers to a fresh copy of the function node;
|
|
3128
3187
|
// its children are then walked below like any other node's.
|
|
3129
|
-
|
|
3130
|
-
|
|
3188
|
+
const source = /** @type {AST.TraversableAstNode} */ (
|
|
3189
|
+
is_function_node(node) ? lower_jsx_code_block_function_body(node) : node
|
|
3190
|
+
);
|
|
3191
|
+
let out = source;
|
|
3192
|
+
const set = (/** @type {string} */ key, /** @type {unknown} */ value) => {
|
|
3131
3193
|
if (out[key] === value) return;
|
|
3132
|
-
if (out ===
|
|
3194
|
+
if (out === source) out = { ...source };
|
|
3133
3195
|
out[key] = value;
|
|
3134
3196
|
};
|
|
3135
3197
|
|
|
3136
3198
|
for (const key of Object.keys(out)) {
|
|
3137
3199
|
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') continue;
|
|
3138
3200
|
const value = out[key];
|
|
3139
|
-
if (!value || typeof value !== 'object') continue;
|
|
3140
3201
|
|
|
3141
3202
|
if (Array.isArray(value)) {
|
|
3142
3203
|
const expanded =
|
|
3143
|
-
key === 'body' && value.some((child) => child
|
|
3204
|
+
key === 'body' && value.some((child) => is_ast_node(child) && child.type === 'JSXCodeBlock')
|
|
3144
3205
|
? value.flatMap((child) => {
|
|
3145
|
-
if (child
|
|
3206
|
+
if (!is_ast_node(child) || child.type !== 'JSXCodeBlock') return [child];
|
|
3146
3207
|
const body_nodes = get_jsx_code_block_body_nodes(child, transform_context);
|
|
3147
3208
|
return mark_native_pretransformed_jsx(
|
|
3148
3209
|
build_render_statements(
|
|
@@ -3156,17 +3217,18 @@ function lower_remaining_jsx_code_blocks(node, transform_context, seen = new Set
|
|
|
3156
3217
|
: value;
|
|
3157
3218
|
let changed = expanded !== value;
|
|
3158
3219
|
const result = expanded.map((child) => {
|
|
3220
|
+
if (!is_ast_node(child)) return child;
|
|
3159
3221
|
const walked = lower_remaining_jsx_code_blocks(child, transform_context, seen);
|
|
3160
3222
|
if (walked !== child) changed = true;
|
|
3161
3223
|
return walked;
|
|
3162
3224
|
});
|
|
3163
3225
|
if (changed) set(key, result);
|
|
3164
|
-
} else {
|
|
3226
|
+
} else if (is_ast_node(value)) {
|
|
3165
3227
|
set(key, lower_remaining_jsx_code_blocks(value, transform_context, seen));
|
|
3166
3228
|
}
|
|
3167
3229
|
}
|
|
3168
3230
|
|
|
3169
|
-
return out;
|
|
3231
|
+
return /** @type {T} */ (out);
|
|
3170
3232
|
}
|
|
3171
3233
|
|
|
3172
3234
|
/**
|
|
@@ -3174,18 +3236,20 @@ function lower_remaining_jsx_code_blocks(node, transform_context, seen = new Set
|
|
|
3174
3236
|
* variable declarations, object literal members, or export-safe expressions,
|
|
3175
3237
|
* so helper expansion reads metadata from that broader set.
|
|
3176
3238
|
*
|
|
3177
|
-
* @param {
|
|
3178
|
-
* @returns {
|
|
3239
|
+
* @param {AST.Node} node
|
|
3240
|
+
* @returns {BaseNodeMetaData[]}
|
|
3179
3241
|
*/
|
|
3180
3242
|
function get_generated_component_metadata_list(node) {
|
|
3181
|
-
/** @type {
|
|
3243
|
+
/** @type {BaseNodeMetaData[]} */
|
|
3182
3244
|
const metas = [];
|
|
3245
|
+
/** @type {Set<AST.Node>} */
|
|
3183
3246
|
const seen_nodes = new Set();
|
|
3247
|
+
/** @type {Set<BaseNodeMetaData>} */
|
|
3184
3248
|
const seen_metas = new Set();
|
|
3185
3249
|
|
|
3186
|
-
/** @param {
|
|
3250
|
+
/** @param {AST.Node} current */
|
|
3187
3251
|
const visit = (current) => {
|
|
3188
|
-
if (
|
|
3252
|
+
if (seen_nodes.has(current)) {
|
|
3189
3253
|
return;
|
|
3190
3254
|
}
|
|
3191
3255
|
|
|
@@ -3207,19 +3271,8 @@ function get_generated_component_metadata_list(node) {
|
|
|
3207
3271
|
return;
|
|
3208
3272
|
}
|
|
3209
3273
|
|
|
3210
|
-
for (const
|
|
3211
|
-
|
|
3212
|
-
continue;
|
|
3213
|
-
}
|
|
3214
|
-
|
|
3215
|
-
const value = current[key];
|
|
3216
|
-
if (Array.isArray(value)) {
|
|
3217
|
-
for (const child of value) {
|
|
3218
|
-
visit(child);
|
|
3219
|
-
}
|
|
3220
|
-
} else {
|
|
3221
|
-
visit(value);
|
|
3222
|
-
}
|
|
3274
|
+
for (const child of child_nodes(current)) {
|
|
3275
|
+
visit(child);
|
|
3223
3276
|
}
|
|
3224
3277
|
};
|
|
3225
3278
|
|
|
@@ -3229,11 +3282,11 @@ function get_generated_component_metadata_list(node) {
|
|
|
3229
3282
|
}
|
|
3230
3283
|
|
|
3231
3284
|
/**
|
|
3232
|
-
* @param {
|
|
3233
|
-
* @param {
|
|
3285
|
+
* @param {ESTreeJSX.JSXRenderChild[]} render_nodes
|
|
3286
|
+
* @param {AST.Node | null | undefined} source_node
|
|
3234
3287
|
* @param {boolean} [map_render_node_locations]
|
|
3235
3288
|
* @param {boolean} [type_only]
|
|
3236
|
-
* @returns {
|
|
3289
|
+
* @returns {AST.ReturnStatement}
|
|
3237
3290
|
*/
|
|
3238
3291
|
function create_component_return_statement(
|
|
3239
3292
|
render_nodes,
|
|
@@ -3252,24 +3305,24 @@ function create_component_return_statement(
|
|
|
3252
3305
|
}
|
|
3253
3306
|
|
|
3254
3307
|
/**
|
|
3255
|
-
* @param {
|
|
3256
|
-
* @returns {
|
|
3308
|
+
* @param {AST.Node | null | undefined} node
|
|
3309
|
+
* @returns {node is AST.ReturnStatement & { metadata: { generated_loop_continue_return: true } }}
|
|
3257
3310
|
*/
|
|
3258
3311
|
function is_loop_skip_return_statement(node) {
|
|
3259
3312
|
return node?.type === 'ReturnStatement' && node.metadata?.generated_loop_continue_return === true;
|
|
3260
3313
|
}
|
|
3261
3314
|
|
|
3262
3315
|
/**
|
|
3263
|
-
* @param {
|
|
3264
|
-
* @returns {
|
|
3316
|
+
* @param {AST.Node | null | undefined} node
|
|
3317
|
+
* @returns {node is AST.IfStatement | AST.JSXIfExpression}
|
|
3265
3318
|
*/
|
|
3266
3319
|
function is_loop_skip_if_statement(node) {
|
|
3267
3320
|
return get_loop_skip_if_consequent_body(node) !== null;
|
|
3268
3321
|
}
|
|
3269
3322
|
|
|
3270
3323
|
/**
|
|
3271
|
-
* @param {
|
|
3272
|
-
* @returns {
|
|
3324
|
+
* @param {AST.Node | null | undefined} node
|
|
3325
|
+
* @returns {AST.Statement[] | null}
|
|
3273
3326
|
*/
|
|
3274
3327
|
function get_loop_skip_if_consequent_body(node) {
|
|
3275
3328
|
if (!is_if_control_node(node) || node.alternate) {
|
|
@@ -3283,13 +3336,14 @@ function get_loop_skip_if_consequent_body(node) {
|
|
|
3283
3336
|
}
|
|
3284
3337
|
|
|
3285
3338
|
/**
|
|
3286
|
-
* @param {
|
|
3287
|
-
* @param {
|
|
3339
|
+
* @param {AST.IfStatement | AST.JSXIfExpression} node
|
|
3340
|
+
* @param {ESTreeJSX.JSXRenderChild[]} render_nodes
|
|
3288
3341
|
* @param {TransformContext} transform_context
|
|
3289
|
-
* @returns {
|
|
3342
|
+
* @returns {AST.IfStatement}
|
|
3290
3343
|
*/
|
|
3291
3344
|
function create_component_loop_skip_if_statement(node, render_nodes, transform_context) {
|
|
3292
|
-
|
|
3345
|
+
// `is_loop_skip_if_statement` already proved this is non-null.
|
|
3346
|
+
const consequent_body = /** @type {AST.Statement[]} */ (get_loop_skip_if_consequent_body(node));
|
|
3293
3347
|
const branch_statements = prepend_render_nodes_to_return_statements(
|
|
3294
3348
|
build_render_statements(consequent_body, true, transform_context),
|
|
3295
3349
|
render_nodes,
|
|
@@ -3312,27 +3366,28 @@ function create_component_loop_skip_if_statement(node, render_nodes, transform_c
|
|
|
3312
3366
|
* rewritten returns land on shallow copies; the returned array must be used in
|
|
3313
3367
|
* place of the argument.
|
|
3314
3368
|
*
|
|
3315
|
-
* @param {
|
|
3316
|
-
* @param {
|
|
3369
|
+
* @param {AST.Statement[]} statements
|
|
3370
|
+
* @param {ESTreeJSX.JSXRenderChild[]} render_nodes
|
|
3317
3371
|
* @param {boolean} [type_only]
|
|
3318
|
-
* @returns {
|
|
3372
|
+
* @returns {AST.Statement[]}
|
|
3319
3373
|
*/
|
|
3320
3374
|
function prepend_render_nodes_to_return_statements(statements, render_nodes, type_only = false) {
|
|
3321
3375
|
if (render_nodes.length === 0) {
|
|
3322
3376
|
return statements;
|
|
3323
3377
|
}
|
|
3324
3378
|
|
|
3325
|
-
return
|
|
3326
|
-
prepend_render_nodes_to_return_statement(
|
|
3379
|
+
return statements.map((statement) =>
|
|
3380
|
+
prepend_render_nodes_to_return_statement(statement, render_nodes, false, type_only),
|
|
3327
3381
|
);
|
|
3328
3382
|
}
|
|
3329
3383
|
|
|
3330
3384
|
/**
|
|
3331
|
-
* @
|
|
3332
|
-
* @param {
|
|
3385
|
+
* @template {AST.Node} T
|
|
3386
|
+
* @param {T} node
|
|
3387
|
+
* @param {ESTreeJSX.JSXRenderChild[]} render_nodes
|
|
3333
3388
|
* @param {boolean} inside_nested_function
|
|
3334
3389
|
* @param {boolean} [type_only]
|
|
3335
|
-
* @returns {
|
|
3390
|
+
* @returns {T}
|
|
3336
3391
|
*/
|
|
3337
3392
|
function prepend_render_nodes_to_return_statement(
|
|
3338
3393
|
node,
|
|
@@ -3340,10 +3395,6 @@ function prepend_render_nodes_to_return_statement(
|
|
|
3340
3395
|
inside_nested_function,
|
|
3341
3396
|
type_only = false,
|
|
3342
3397
|
) {
|
|
3343
|
-
if (!node || typeof node !== 'object') {
|
|
3344
|
-
return node;
|
|
3345
|
-
}
|
|
3346
|
-
|
|
3347
3398
|
if (
|
|
3348
3399
|
node.type === 'FunctionDeclaration' ||
|
|
3349
3400
|
node.type === 'FunctionExpression' ||
|
|
@@ -3359,45 +3410,50 @@ function prepend_render_nodes_to_return_statement(
|
|
|
3359
3410
|
};
|
|
3360
3411
|
}
|
|
3361
3412
|
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3413
|
+
const source = /** @type {AST.TraversableAstNode} */ (node);
|
|
3414
|
+
let out = source;
|
|
3415
|
+
for (const key of Object.keys(source)) {
|
|
3416
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
3417
|
+
continue;
|
|
3418
|
+
}
|
|
3419
|
+
const value = source[key];
|
|
3420
|
+
/** @type {unknown} */
|
|
3421
|
+
let walked = value;
|
|
3422
|
+
if (Array.isArray(value)) {
|
|
3423
|
+
let changed = false;
|
|
3424
|
+
const result = value.map((child) => {
|
|
3425
|
+
if (!is_ast_node(child)) return child;
|
|
3426
|
+
const next = prepend_render_nodes_to_return_statement(
|
|
3427
|
+
child,
|
|
3428
|
+
render_nodes,
|
|
3429
|
+
inside_nested_function,
|
|
3430
|
+
type_only,
|
|
3431
|
+
);
|
|
3432
|
+
if (next !== child) changed = true;
|
|
3433
|
+
return next;
|
|
3434
|
+
});
|
|
3435
|
+
if (changed) walked = result;
|
|
3436
|
+
} else if (is_ast_node(value)) {
|
|
3437
|
+
walked = prepend_render_nodes_to_return_statement(
|
|
3438
|
+
value,
|
|
3367
3439
|
render_nodes,
|
|
3368
3440
|
inside_nested_function,
|
|
3369
3441
|
type_only,
|
|
3370
3442
|
);
|
|
3371
|
-
if (walked !== child) changed = true;
|
|
3372
|
-
return walked;
|
|
3373
|
-
});
|
|
3374
|
-
return changed ? result : node;
|
|
3375
|
-
}
|
|
3376
|
-
|
|
3377
|
-
let out = node;
|
|
3378
|
-
for (const key of Object.keys(node)) {
|
|
3379
|
-
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
3380
|
-
continue;
|
|
3381
3443
|
}
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
render_nodes,
|
|
3385
|
-
inside_nested_function,
|
|
3386
|
-
type_only,
|
|
3387
|
-
);
|
|
3388
|
-
if (walked !== node[key]) {
|
|
3389
|
-
if (out === node) out = { ...node };
|
|
3444
|
+
if (walked !== value) {
|
|
3445
|
+
if (out === source) out = { ...source };
|
|
3390
3446
|
out[key] = walked;
|
|
3391
3447
|
}
|
|
3392
3448
|
}
|
|
3393
|
-
return out;
|
|
3449
|
+
return /** @type {T} */ (out);
|
|
3394
3450
|
}
|
|
3395
3451
|
|
|
3396
3452
|
/**
|
|
3397
|
-
* @param {
|
|
3398
|
-
* @param {
|
|
3453
|
+
* @param {ESTreeJSX.JSXRenderChild[]} render_nodes
|
|
3454
|
+
* @param {AST.Expression | null | undefined} return_argument
|
|
3399
3455
|
* @param {boolean} [type_only]
|
|
3400
|
-
* @returns {
|
|
3456
|
+
* @returns {AST.Expression}
|
|
3401
3457
|
*/
|
|
3402
3458
|
function combine_render_return_argument(render_nodes, return_argument, type_only = false) {
|
|
3403
3459
|
const combined = render_nodes.map((node) => clone_ast_node(node, false));
|
|
@@ -3410,8 +3466,8 @@ function combine_render_return_argument(render_nodes, return_argument, type_only
|
|
|
3410
3466
|
}
|
|
3411
3467
|
|
|
3412
3468
|
/**
|
|
3413
|
-
* @param {
|
|
3414
|
-
* @returns {
|
|
3469
|
+
* @param {AST.Expression | ESTreeJSX.JSXExpressionContainer} argument
|
|
3470
|
+
* @returns {ESTreeJSX.JSXRenderChild}
|
|
3415
3471
|
*/
|
|
3416
3472
|
function return_argument_to_render_node(argument) {
|
|
3417
3473
|
if (
|
|
@@ -3426,7 +3482,7 @@ function return_argument_to_render_node(argument) {
|
|
|
3426
3482
|
}
|
|
3427
3483
|
|
|
3428
3484
|
/**
|
|
3429
|
-
* @param {
|
|
3485
|
+
* @param {AST.Node | null | undefined} node
|
|
3430
3486
|
* @returns {boolean}
|
|
3431
3487
|
*/
|
|
3432
3488
|
function is_null_literal(node) {
|
|
@@ -3441,8 +3497,8 @@ function is_null_literal(node) {
|
|
|
3441
3497
|
* runtime check skips the copy when the value is already an array.
|
|
3442
3498
|
*
|
|
3443
3499
|
* @param {AST.Identifier} source_id
|
|
3444
|
-
* @param {
|
|
3445
|
-
* @returns {{ source_decl:
|
|
3500
|
+
* @param {AST.Expression} source_expr
|
|
3501
|
+
* @returns {{ source_decl: AST.VariableDeclaration, source_normalize_decl: AST.ExpressionStatement }}
|
|
3446
3502
|
*/
|
|
3447
3503
|
function build_array_normalization_decls(source_id, source_expr) {
|
|
3448
3504
|
const source_decl = b.let(clone_identifier(source_id), clone_ast_node(source_expr));
|
|
@@ -3469,20 +3525,21 @@ function build_array_normalization_decls(source_id, source_expr) {
|
|
|
3469
3525
|
* Bails out (returns null) when the loop pattern is destructured — deriving
|
|
3470
3526
|
* element types from a tuple/object pattern is more involved and deferred.
|
|
3471
3527
|
*
|
|
3472
|
-
* @param {
|
|
3528
|
+
* @param {AST.ForOfStatement} node
|
|
3473
3529
|
* @param {TransformContext} transform_context
|
|
3474
|
-
* @returns {{ hoist_statements:
|
|
3530
|
+
* @returns {{ hoist_statements: AST.Statement[], jsx_child: ESTreeJSX.JSXExpressionContainer } | null}
|
|
3475
3531
|
*/
|
|
3476
3532
|
function build_hoisted_for_of_with_hooks(node, transform_context) {
|
|
3477
|
-
|
|
3478
|
-
|
|
3533
|
+
/** @type {AST.Identifier[]} */
|
|
3534
|
+
const loop_params = [];
|
|
3535
|
+
for (const param of get_for_of_iteration_params(node.left, node.index)) {
|
|
3536
|
+
// Deriving element types from a destructured pattern is deferred.
|
|
3479
3537
|
if (param.type !== 'Identifier') return null;
|
|
3538
|
+
loop_params.push(param);
|
|
3480
3539
|
}
|
|
3481
3540
|
|
|
3482
|
-
const original_loop_body =
|
|
3483
|
-
|
|
3484
|
-
node.body.type === 'BlockStatement' ? node.body.body : [node.body],
|
|
3485
|
-
)
|
|
3541
|
+
const original_loop_body = rewrite_loop_continues_to_bare_returns(
|
|
3542
|
+
node.body.type === 'BlockStatement' ? node.body.body : [node.body],
|
|
3486
3543
|
);
|
|
3487
3544
|
|
|
3488
3545
|
const source_id = create_generated_identifier(
|
|
@@ -3498,7 +3555,7 @@ function build_hoisted_for_of_with_hooks(node, transform_context) {
|
|
|
3498
3555
|
|
|
3499
3556
|
const saved_bindings = transform_context.available_bindings;
|
|
3500
3557
|
transform_context.available_bindings = new Map(saved_bindings);
|
|
3501
|
-
const loop_scoped_names = new Set(loop_params.map((
|
|
3558
|
+
const loop_scoped_names = new Set(loop_params.map((p) => p.name));
|
|
3502
3559
|
for (const param of loop_params) {
|
|
3503
3560
|
collect_pattern_bindings(param, transform_context.available_bindings);
|
|
3504
3561
|
}
|
|
@@ -3562,10 +3619,12 @@ function build_hoisted_for_of_with_hooks(node, transform_context) {
|
|
|
3562
3619
|
const helper_fn = b.function(clone_identifier(component_id), params, b.block(fn_body_statements));
|
|
3563
3620
|
helper_fn.metadata = { path: [], is_method: false };
|
|
3564
3621
|
|
|
3622
|
+
const node_loc = has_location(node) ? node : undefined;
|
|
3623
|
+
/** @type {AST.Statement | null} */
|
|
3565
3624
|
let helper_decl;
|
|
3566
3625
|
if (transform_context.helper_state && use_module_scoped_component) {
|
|
3567
3626
|
transform_context.helper_state.helpers.push(
|
|
3568
|
-
create_helper_declaration(component_id, helper_fn,
|
|
3627
|
+
create_helper_declaration(component_id, helper_fn, node_loc, transform_context),
|
|
3569
3628
|
);
|
|
3570
3629
|
helper_decl = null;
|
|
3571
3630
|
} else if (transform_context.helper_state) {
|
|
@@ -3576,10 +3635,10 @@ function build_hoisted_for_of_with_hooks(node, transform_context) {
|
|
|
3576
3635
|
helper_decl = create_cached_helper_declaration(
|
|
3577
3636
|
helper_id,
|
|
3578
3637
|
cache_id,
|
|
3579
|
-
create_helper_init_expression(helper_id, helper_fn,
|
|
3638
|
+
create_helper_init_expression(helper_id, helper_fn, node_loc, transform_context),
|
|
3580
3639
|
);
|
|
3581
3640
|
} else {
|
|
3582
|
-
helper_decl = create_helper_declaration(helper_id, helper_fn,
|
|
3641
|
+
helper_decl = create_helper_declaration(helper_id, helper_fn, node_loc, transform_context);
|
|
3583
3642
|
}
|
|
3584
3643
|
|
|
3585
3644
|
transform_context.available_bindings = saved_bindings;
|
|
@@ -3605,7 +3664,7 @@ function build_hoisted_for_of_with_hooks(node, transform_context) {
|
|
|
3605
3664
|
);
|
|
3606
3665
|
}
|
|
3607
3666
|
|
|
3608
|
-
const callback_params = loop_params.map((
|
|
3667
|
+
const callback_params = loop_params.map((p) => clone_identifier(p));
|
|
3609
3668
|
|
|
3610
3669
|
const iter_callback = b.arrow(callback_params, callback_invocation_element);
|
|
3611
3670
|
|
|
@@ -3619,6 +3678,7 @@ function build_hoisted_for_of_with_hooks(node, transform_context) {
|
|
|
3619
3678
|
|
|
3620
3679
|
const jsx_child = to_jsx_expression_container(map_call, node);
|
|
3621
3680
|
|
|
3681
|
+
/** @type {AST.Statement[]} */
|
|
3622
3682
|
const hoist_statements = source_normalize_decl
|
|
3623
3683
|
? [source_decl, source_normalize_decl]
|
|
3624
3684
|
: [source_decl];
|
|
@@ -3648,9 +3708,9 @@ function build_hoisted_for_of_with_hooks(node, transform_context) {
|
|
|
3648
3708
|
* @param {AST.Identifier} helper_id
|
|
3649
3709
|
* @param {AST.Identifier} binding
|
|
3650
3710
|
* @param {AST.Identifier} source_id
|
|
3651
|
-
* @param {
|
|
3711
|
+
* @param {AST.Identifier[]} loop_params
|
|
3652
3712
|
* @param {TransformContext} transform_context
|
|
3653
|
-
* @returns {{ id: AST.Identifier, declaration:
|
|
3713
|
+
* @returns {{ id: AST.Identifier, declaration: AST.TSTypeAliasDeclaration & AST.Statement }}
|
|
3654
3714
|
*/
|
|
3655
3715
|
function create_loop_scoped_type_alias_declaration(
|
|
3656
3716
|
helper_id,
|
|
@@ -3672,7 +3732,7 @@ function create_loop_scoped_type_alias_declaration(
|
|
|
3672
3732
|
b.ts_type_parameter_instantiation([b.ts_type_query(clone_identifier(source_id))]),
|
|
3673
3733
|
);
|
|
3674
3734
|
})()
|
|
3675
|
-
: /** @type {
|
|
3735
|
+
: /** @type {AST.TypeNode} */ ({
|
|
3676
3736
|
type: 'TSIndexedAccessType',
|
|
3677
3737
|
objectType: b.ts_type_query(clone_identifier(source_id)),
|
|
3678
3738
|
indexType: b.ts_keyword_type('number'),
|
|
@@ -3694,7 +3754,7 @@ function create_loop_scoped_type_alias_declaration(
|
|
|
3694
3754
|
* @param {AST.Identifier[]} bindings
|
|
3695
3755
|
* @param {{ id: AST.Identifier }[]} aliases
|
|
3696
3756
|
* @param {boolean[]} use_typeof
|
|
3697
|
-
* @returns {
|
|
3757
|
+
* @returns {AST.TSTypeLiteral}
|
|
3698
3758
|
*/
|
|
3699
3759
|
function create_helper_props_type_literal_with_typeof_flags(bindings, aliases, use_typeof) {
|
|
3700
3760
|
return b.ts_type_literal(
|
|
@@ -3711,34 +3771,36 @@ function create_helper_props_type_literal_with_typeof_flags(bindings, aliases, u
|
|
|
3711
3771
|
}
|
|
3712
3772
|
|
|
3713
3773
|
/**
|
|
3714
|
-
* @param {
|
|
3774
|
+
* @param {AST.TSRXJSXElement | AST.TSRXJSXFragment | AST.JSXStyleElement} node
|
|
3715
3775
|
* @param {TransformContext} transform_context
|
|
3776
|
+
* @param {AST.Node[]} [raw_children]
|
|
3716
3777
|
* @param {boolean} [in_jsx_child]
|
|
3717
|
-
* @returns {
|
|
3778
|
+
* @returns {AST.TSRXJSXElement | AST.TSRXJSXFragment}
|
|
3718
3779
|
*/
|
|
3719
3780
|
function to_jsx_element(
|
|
3720
3781
|
node,
|
|
3721
3782
|
transform_context,
|
|
3722
|
-
raw_children = node
|
|
3783
|
+
raw_children = node_children(node),
|
|
3723
3784
|
in_jsx_child = false,
|
|
3724
3785
|
) {
|
|
3725
3786
|
if (node.type === 'JSXElement' && !node.metadata?.native_tsrx) {
|
|
3726
3787
|
return node;
|
|
3727
3788
|
}
|
|
3728
3789
|
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
if (!
|
|
3790
|
+
// A fragment has no opening element to take a name from; in a TSRX template
|
|
3791
|
+
// that is the "fragments are not needed here" error case.
|
|
3792
|
+
if (node.type === 'JSXFragment' || !node.openingElement?.name) {
|
|
3732
3793
|
report_jsx_fragment_in_tsrx_error(node, transform_context);
|
|
3733
3794
|
return set_loc(b.jsx_fragment(), node);
|
|
3734
3795
|
}
|
|
3735
|
-
const
|
|
3796
|
+
const source_opening = node.openingElement;
|
|
3797
|
+
const name = clone_jsx_name(source_opening.name);
|
|
3736
3798
|
const attributes = transform_element_attributes_dispatch(
|
|
3737
3799
|
source_opening.attributes || [],
|
|
3738
3800
|
transform_context,
|
|
3739
|
-
node,
|
|
3801
|
+
/** @type {AST.TSRXJSXElement} */ (node),
|
|
3740
3802
|
);
|
|
3741
|
-
let walked_children = node
|
|
3803
|
+
let walked_children = node_children(node);
|
|
3742
3804
|
// A raw-text `<script>` body (mirrored by the parser as a JSXText child of
|
|
3743
3805
|
// `node.content`) must not appear in the type-only editor TSX: raw JS/TS
|
|
3744
3806
|
// (`{`, `<`) doesn't lex as JSX text there and would surface bogus syntactic
|
|
@@ -3751,7 +3813,7 @@ function to_jsx_element(
|
|
|
3751
3813
|
let selfClosing = !!source_opening.selfClosing;
|
|
3752
3814
|
let children;
|
|
3753
3815
|
const child_transform = transform_context.platform.hooks?.transformElementChildren?.(
|
|
3754
|
-
node,
|
|
3816
|
+
/** @type {AST.TSRXJSXElement} */ (node),
|
|
3755
3817
|
walked_children,
|
|
3756
3818
|
raw_children,
|
|
3757
3819
|
attributes,
|
|
@@ -3767,7 +3829,7 @@ function to_jsx_element(
|
|
|
3767
3829
|
children = create_element_children(walked_children, transform_context);
|
|
3768
3830
|
}
|
|
3769
3831
|
const has_unmappable_attribute = attributes.some(
|
|
3770
|
-
(
|
|
3832
|
+
(attribute) => attribute?.metadata?.has_unmappable_value,
|
|
3771
3833
|
);
|
|
3772
3834
|
|
|
3773
3835
|
const opening_element_node = b.jsx_opening_element(
|
|
@@ -3810,9 +3872,8 @@ function to_jsx_element(
|
|
|
3810
3872
|
/**
|
|
3811
3873
|
* @param {AST.Node[]} children
|
|
3812
3874
|
* @param {TransformContext} transform_context
|
|
3813
|
-
* @returns {
|
|
3875
|
+
* @returns {AST.TSRXJSXElement['children']}
|
|
3814
3876
|
*/
|
|
3815
|
-
|
|
3816
3877
|
function create_element_children(children, transform_context) {
|
|
3817
3878
|
if (children.length === 0) {
|
|
3818
3879
|
return [];
|
|
@@ -3891,7 +3952,7 @@ function is_inline_element_child(node) {
|
|
|
3891
3952
|
}
|
|
3892
3953
|
|
|
3893
3954
|
/**
|
|
3894
|
-
* @param {
|
|
3955
|
+
* @param {AST.Node[]} body_nodes
|
|
3895
3956
|
* @param {TransformContext} transform_context
|
|
3896
3957
|
* @returns {ESTreeJSX.JSXExpressionContainer}
|
|
3897
3958
|
*/
|
|
@@ -3909,7 +3970,7 @@ function statement_body_to_jsx_child(body_nodes, transform_context) {
|
|
|
3909
3970
|
}
|
|
3910
3971
|
|
|
3911
3972
|
/**
|
|
3912
|
-
* @param {
|
|
3973
|
+
* @param {AST.Node[]} body_nodes
|
|
3913
3974
|
* @param {TransformContext} transform_context
|
|
3914
3975
|
* @returns {ESTreeJSX.JSXExpressionContainer}
|
|
3915
3976
|
*/
|
|
@@ -3940,10 +4001,10 @@ function create_local_statement_component_name(transform_context) {
|
|
|
3940
4001
|
* Used when a control flow branch contains hook calls that must be moved
|
|
3941
4002
|
* into their own component boundary to satisfy the Rules of Hooks.
|
|
3942
4003
|
*
|
|
3943
|
-
* @param {
|
|
3944
|
-
* @param {
|
|
4004
|
+
* @param {AST.Node[]} body_nodes
|
|
4005
|
+
* @param {AST.Expression | undefined} key_expression Optional key expression to add to the component element (for `for-of` loops)
|
|
3945
4006
|
* @param {TransformContext} transform_context
|
|
3946
|
-
* @returns {
|
|
4007
|
+
* @returns {AST.Statement[]}
|
|
3947
4008
|
*/
|
|
3948
4009
|
function hook_safe_render_statements(body_nodes, key_expression, transform_context) {
|
|
3949
4010
|
const source_node = get_body_source_node(body_nodes);
|
|
@@ -3961,12 +4022,14 @@ function hook_safe_render_statements(body_nodes, key_expression, transform_conte
|
|
|
3961
4022
|
}
|
|
3962
4023
|
|
|
3963
4024
|
/**
|
|
3964
|
-
* @param {
|
|
4025
|
+
* @param {AST.Node[]} body_nodes
|
|
3965
4026
|
* @param {Map<string, AST.Identifier>} available_bindings
|
|
3966
4027
|
* @returns {AST.Identifier[]}
|
|
3967
4028
|
*/
|
|
3968
4029
|
function get_referenced_helper_bindings(body_nodes, available_bindings) {
|
|
4030
|
+
/** @type {AST.Identifier[]} */
|
|
3969
4031
|
const helper_bindings = [];
|
|
4032
|
+
/** @type {Map<string, AST.Identifier>} */
|
|
3970
4033
|
const local_bindings = new Map();
|
|
3971
4034
|
|
|
3972
4035
|
for (const node of body_nodes) {
|
|
@@ -3976,7 +4039,8 @@ function get_referenced_helper_bindings(body_nodes, available_bindings) {
|
|
|
3976
4039
|
for (const [name, binding] of available_bindings) {
|
|
3977
4040
|
if (local_bindings.has(name)) continue;
|
|
3978
4041
|
|
|
3979
|
-
|
|
4042
|
+
const scope = new Map([[name, binding]]);
|
|
4043
|
+
if (body_nodes.some((node) => references_scope_bindings(node, scope))) {
|
|
3980
4044
|
helper_bindings.push(binding);
|
|
3981
4045
|
}
|
|
3982
4046
|
}
|
|
@@ -3985,14 +4049,14 @@ function get_referenced_helper_bindings(body_nodes, available_bindings) {
|
|
|
3985
4049
|
}
|
|
3986
4050
|
|
|
3987
4051
|
/**
|
|
3988
|
-
* @param {
|
|
3989
|
-
* @param {
|
|
3990
|
-
* @param {
|
|
4052
|
+
* @param {AST.Node[]} body_nodes
|
|
4053
|
+
* @param {AST.Expression | undefined} key_expression
|
|
4054
|
+
* @param {AST.NodeWithLocation | undefined} source_node
|
|
3991
4055
|
* @param {TransformContext} transform_context
|
|
3992
4056
|
* @param {AST.Identifier} [preallocated_helper_id] - Optional pre-allocated id.
|
|
3993
4057
|
* Used by switch lifting to keep generated helper ids stable in source order.
|
|
3994
4058
|
* @param {{ transientBindings?: Set<string> }} [options]
|
|
3995
|
-
* @returns {{ setup_statements:
|
|
4059
|
+
* @returns {{ setup_statements: AST.Statement[], component_element: AST.TSRXJSXElement }}
|
|
3996
4060
|
*/
|
|
3997
4061
|
export function create_hook_safe_helper(
|
|
3998
4062
|
body_nodes,
|
|
@@ -4104,9 +4168,9 @@ export function create_hook_safe_helper(
|
|
|
4104
4168
|
/**
|
|
4105
4169
|
* @param {AST.Identifier} helper_id
|
|
4106
4170
|
* @param {AST.FunctionExpression} helper_fn
|
|
4107
|
-
* @param {
|
|
4171
|
+
* @param {AST.NodeWithLocation | undefined} source_node
|
|
4108
4172
|
* @param {TransformContext} transform_context
|
|
4109
|
-
* @returns {
|
|
4173
|
+
* @returns {AST.Statement}
|
|
4110
4174
|
*/
|
|
4111
4175
|
function create_helper_declaration(helper_id, helper_fn, source_node, transform_context) {
|
|
4112
4176
|
const declaration = create_helper_function_declaration_from_expression(helper_id, helper_fn);
|
|
@@ -4117,9 +4181,9 @@ function create_helper_declaration(helper_id, helper_fn, source_node, transform_
|
|
|
4117
4181
|
/**
|
|
4118
4182
|
* @param {AST.Identifier} helper_id
|
|
4119
4183
|
* @param {AST.FunctionExpression} helper_fn
|
|
4120
|
-
* @param {
|
|
4184
|
+
* @param {AST.NodeWithLocation | undefined} source_node
|
|
4121
4185
|
* @param {TransformContext} transform_context
|
|
4122
|
-
* @returns {
|
|
4186
|
+
* @returns {AST.Expression}
|
|
4123
4187
|
*/
|
|
4124
4188
|
function create_helper_init_expression(helper_id, helper_fn, source_node, transform_context) {
|
|
4125
4189
|
const hook = transform_context.platform.hooks?.wrapHelperComponent;
|
|
@@ -4140,9 +4204,9 @@ function create_helper_init_expression(helper_id, helper_fn, source_node, transf
|
|
|
4140
4204
|
}
|
|
4141
4205
|
|
|
4142
4206
|
/**
|
|
4143
|
-
* @param {
|
|
4144
|
-
* @param {
|
|
4145
|
-
* @returns {
|
|
4207
|
+
* @param {AST.Statement[]} setup_statements
|
|
4208
|
+
* @param {AST.TSRXJSXElement} component_element
|
|
4209
|
+
* @returns {AST.CallExpression}
|
|
4146
4210
|
*/
|
|
4147
4211
|
function create_hook_safe_helper_iife(setup_statements, component_element) {
|
|
4148
4212
|
return b.call(b.arrow([], b.block([...setup_statements, b.return(component_element)])));
|
|
@@ -4151,7 +4215,7 @@ function create_hook_safe_helper_iife(setup_statements, component_element) {
|
|
|
4151
4215
|
/**
|
|
4152
4216
|
* @param {AST.Identifier} helper_id
|
|
4153
4217
|
* @param {AST.Identifier} binding
|
|
4154
|
-
* @returns {{ id: AST.Identifier, declaration:
|
|
4218
|
+
* @returns {{ id: AST.Identifier, declaration: AST.VariableDeclaration }}
|
|
4155
4219
|
*/
|
|
4156
4220
|
function create_helper_type_alias_declaration(helper_id, binding) {
|
|
4157
4221
|
const alias_id = create_generated_identifier(`_tsrx_${helper_id.name}_${binding.name}`);
|
|
@@ -4165,7 +4229,7 @@ function create_helper_type_alias_declaration(helper_id, binding) {
|
|
|
4165
4229
|
/**
|
|
4166
4230
|
* @param {AST.Identifier[]} bindings
|
|
4167
4231
|
* @param {({ id: AST.Identifier } | null)[]} aliases
|
|
4168
|
-
* @returns {
|
|
4232
|
+
* @returns {AST.TSTypeLiteral}
|
|
4169
4233
|
*/
|
|
4170
4234
|
function create_helper_props_type_literal(bindings, aliases) {
|
|
4171
4235
|
return b.ts_type_literal(
|
|
@@ -4186,19 +4250,19 @@ function create_helper_props_type_literal(bindings, aliases) {
|
|
|
4186
4250
|
|
|
4187
4251
|
/**
|
|
4188
4252
|
* @param {AST.Identifier[]} bindings
|
|
4189
|
-
* @param {
|
|
4253
|
+
* @param {AST.TSTypeLiteral} props_type
|
|
4190
4254
|
* @param {Set<string>} [mapped_bindings]
|
|
4191
4255
|
* @returns {AST.ObjectPattern}
|
|
4192
4256
|
*/
|
|
4193
4257
|
function create_typed_helper_props_pattern(bindings, props_type, mapped_bindings = new Set()) {
|
|
4194
4258
|
const pattern = create_helper_props_pattern(bindings, mapped_bindings);
|
|
4195
|
-
|
|
4259
|
+
pattern.typeAnnotation = b.ts_type_annotation(props_type);
|
|
4196
4260
|
return pattern;
|
|
4197
4261
|
}
|
|
4198
4262
|
|
|
4199
4263
|
/**
|
|
4200
4264
|
* @param {AST.Identifier} cache_id
|
|
4201
|
-
* @returns {
|
|
4265
|
+
* @returns {AST.VariableDeclaration}
|
|
4202
4266
|
*/
|
|
4203
4267
|
function create_helper_cache_declaration(cache_id) {
|
|
4204
4268
|
return b.let(clone_identifier(cache_id));
|
|
@@ -4207,8 +4271,8 @@ function create_helper_cache_declaration(cache_id) {
|
|
|
4207
4271
|
/**
|
|
4208
4272
|
* @param {AST.Identifier} helper_id
|
|
4209
4273
|
* @param {AST.Identifier} cache_id
|
|
4210
|
-
* @param {
|
|
4211
|
-
* @returns {
|
|
4274
|
+
* @param {AST.Expression} helper_init
|
|
4275
|
+
* @returns {AST.VariableDeclaration}
|
|
4212
4276
|
*/
|
|
4213
4277
|
function create_cached_helper_declaration(helper_id, cache_id, helper_init) {
|
|
4214
4278
|
return b.const(
|
|
@@ -4243,8 +4307,11 @@ function create_helper_function_declaration_from_expression(helper_id, helper_fn
|
|
|
4243
4307
|
}
|
|
4244
4308
|
|
|
4245
4309
|
/**
|
|
4246
|
-
*
|
|
4247
|
-
*
|
|
4310
|
+
* The source span covering a body, for stamping onto the nodes generated from
|
|
4311
|
+
* it. Undefined when the body carries no usable positions.
|
|
4312
|
+
*
|
|
4313
|
+
* @param {AST.Node[]} body_nodes
|
|
4314
|
+
* @returns {AST.NodeWithLocation | undefined}
|
|
4248
4315
|
*/
|
|
4249
4316
|
function get_body_source_node(body_nodes) {
|
|
4250
4317
|
const first = body_nodes[0];
|
|
@@ -4261,22 +4328,46 @@ function get_body_source_node(body_nodes) {
|
|
|
4261
4328
|
};
|
|
4262
4329
|
}
|
|
4263
4330
|
|
|
4264
|
-
return first;
|
|
4331
|
+
return has_location(first) ? first : undefined;
|
|
4265
4332
|
}
|
|
4266
4333
|
|
|
4267
4334
|
/**
|
|
4268
|
-
*
|
|
4269
|
-
*
|
|
4335
|
+
* Retype a directive expression node (`JSXIfExpression`, …) to the statement
|
|
4336
|
+
* form its `statementType` names, so statement-shaped code paths can consume it.
|
|
4337
|
+
*
|
|
4338
|
+
* @overload
|
|
4339
|
+
* @param {AST.JSXIfExpression | AST.IfStatement} node
|
|
4340
|
+
* @returns {AST.IfStatement}
|
|
4341
|
+
*/
|
|
4342
|
+
/**
|
|
4343
|
+
* @overload
|
|
4344
|
+
* @param {AST.JSXForExpression | AST.ForOfStatement} node
|
|
4345
|
+
* @returns {AST.ForOfStatement}
|
|
4346
|
+
*/
|
|
4347
|
+
/**
|
|
4348
|
+
* @overload
|
|
4349
|
+
* @param {AST.JSXSwitchExpression | AST.SwitchStatement} node
|
|
4350
|
+
* @returns {AST.SwitchStatement}
|
|
4351
|
+
*/
|
|
4352
|
+
/**
|
|
4353
|
+
* @overload
|
|
4354
|
+
* @param {AST.JSXTryExpression | AST.TryStatement} node
|
|
4355
|
+
* @returns {AST.TryStatement}
|
|
4356
|
+
*/
|
|
4357
|
+
/**
|
|
4358
|
+
* @param {AST.Node} node
|
|
4359
|
+
* @returns {AST.Node}
|
|
4270
4360
|
*/
|
|
4271
4361
|
function jsx_control_expression_to_statement(node) {
|
|
4272
|
-
|
|
4273
|
-
|
|
4362
|
+
const statement_type = /** @type {AST.JSXTemplateDirective} */ (node).statementType;
|
|
4363
|
+
if (!statement_type) return node;
|
|
4364
|
+
return /** @type {AST.Node} */ ({ ...node, type: statement_type });
|
|
4274
4365
|
}
|
|
4275
4366
|
|
|
4276
4367
|
/**
|
|
4277
|
-
* @param {
|
|
4368
|
+
* @param {AST.JSXCodeBlock} node
|
|
4278
4369
|
* @param {TransformContext} transform_context
|
|
4279
|
-
* @returns {
|
|
4370
|
+
* @returns {AST.Node[]}
|
|
4280
4371
|
*/
|
|
4281
4372
|
function get_jsx_code_block_body_nodes(node, transform_context) {
|
|
4282
4373
|
if (!node.render) {
|
|
@@ -4297,8 +4388,8 @@ function get_jsx_code_block_body_nodes(node, transform_context) {
|
|
|
4297
4388
|
}
|
|
4298
4389
|
|
|
4299
4390
|
/**
|
|
4300
|
-
* @param {
|
|
4301
|
-
* @returns {
|
|
4391
|
+
* @param {AST.JSXCodeBlock} node
|
|
4392
|
+
* @returns {AST.Node[]}
|
|
4302
4393
|
*/
|
|
4303
4394
|
function get_raw_jsx_code_block_body_nodes(node) {
|
|
4304
4395
|
return [...(node.body || []), ...(node.render ? [node.render] : [])];
|
|
@@ -4357,16 +4448,16 @@ function is_render_child_node(node) {
|
|
|
4357
4448
|
}
|
|
4358
4449
|
|
|
4359
4450
|
/**
|
|
4360
|
-
* @param {
|
|
4361
|
-
* @returns {
|
|
4451
|
+
* @param {AST.Node | null | undefined} node
|
|
4452
|
+
* @returns {node is AST.SwitchStatement | AST.JSXSwitchExpression}
|
|
4362
4453
|
*/
|
|
4363
4454
|
function is_switch_control_node(node) {
|
|
4364
4455
|
return node?.type === 'SwitchStatement' || node?.type === 'JSXSwitchExpression';
|
|
4365
4456
|
}
|
|
4366
4457
|
|
|
4367
4458
|
/**
|
|
4368
|
-
* @param {
|
|
4369
|
-
* @returns {
|
|
4459
|
+
* @param {AST.Node | null | undefined} node
|
|
4460
|
+
* @returns {node is AST.TryStatement | AST.JSXTryExpression}
|
|
4370
4461
|
*/
|
|
4371
4462
|
function is_try_control_node(node) {
|
|
4372
4463
|
return node?.type === 'TryStatement' || node?.type === 'JSXTryExpression';
|
|
@@ -4605,10 +4696,10 @@ function tsrx_node_to_jsx_expression(node, transform_context, in_jsx_child = fal
|
|
|
4605
4696
|
* Explicit return values inside expression-position native templates are JavaScript
|
|
4606
4697
|
* values, so keep them out of platform render control flow.
|
|
4607
4698
|
*
|
|
4608
|
-
* @param {
|
|
4609
|
-
* @param {
|
|
4699
|
+
* @param {AST.Node[]} body_nodes
|
|
4700
|
+
* @param {AST.Node | null | undefined} source_node
|
|
4610
4701
|
* @param {TransformContext} [transform_context]
|
|
4611
|
-
* @returns {
|
|
4702
|
+
* @returns {AST.Expression | null}
|
|
4612
4703
|
*/
|
|
4613
4704
|
export function return_value_body_to_expression(body_nodes, source_node, transform_context) {
|
|
4614
4705
|
if (!body_contains_top_level_return_value(body_nodes)) return null;
|
|
@@ -4618,13 +4709,17 @@ export function return_value_body_to_expression(body_nodes, source_node, transfo
|
|
|
4618
4709
|
if (expression) return expression;
|
|
4619
4710
|
}
|
|
4620
4711
|
|
|
4621
|
-
return create_statement_iife(
|
|
4712
|
+
return create_statement_iife(
|
|
4713
|
+
/** @type {AST.Statement[]} */ (body_nodes),
|
|
4714
|
+
source_node,
|
|
4715
|
+
transform_context,
|
|
4716
|
+
);
|
|
4622
4717
|
}
|
|
4623
4718
|
|
|
4624
4719
|
/**
|
|
4625
|
-
* @param {
|
|
4720
|
+
* @param {AST.Node | null | undefined} node
|
|
4626
4721
|
* @param {TransformContext} [transform_context]
|
|
4627
|
-
* @returns {
|
|
4722
|
+
* @returns {AST.Expression | null}
|
|
4628
4723
|
*/
|
|
4629
4724
|
function return_value_statement_to_expression(node, transform_context) {
|
|
4630
4725
|
if (node?.type === 'ReturnStatement' && node.argument != null) {
|
|
@@ -4639,11 +4734,11 @@ function return_value_statement_to_expression(node, transform_context) {
|
|
|
4639
4734
|
}
|
|
4640
4735
|
|
|
4641
4736
|
/**
|
|
4642
|
-
* @param {
|
|
4737
|
+
* @param {AST.Node | AST.Node[] | null | undefined} node
|
|
4643
4738
|
* @returns {boolean}
|
|
4644
4739
|
*/
|
|
4645
4740
|
function body_contains_top_level_return_value(node) {
|
|
4646
|
-
if (!node
|
|
4741
|
+
if (!node) return false;
|
|
4647
4742
|
|
|
4648
4743
|
if (Array.isArray(node)) {
|
|
4649
4744
|
return node.some(body_contains_top_level_return_value);
|
|
@@ -4663,11 +4758,8 @@ function body_contains_top_level_return_value(node) {
|
|
|
4663
4758
|
return false;
|
|
4664
4759
|
}
|
|
4665
4760
|
|
|
4666
|
-
for (const
|
|
4667
|
-
if (
|
|
4668
|
-
continue;
|
|
4669
|
-
}
|
|
4670
|
-
if (body_contains_top_level_return_value(node[key])) {
|
|
4761
|
+
for (const child of child_nodes(node)) {
|
|
4762
|
+
if (body_contains_top_level_return_value(child)) {
|
|
4671
4763
|
return true;
|
|
4672
4764
|
}
|
|
4673
4765
|
}
|
|
@@ -4676,10 +4768,10 @@ function body_contains_top_level_return_value(node) {
|
|
|
4676
4768
|
}
|
|
4677
4769
|
|
|
4678
4770
|
/**
|
|
4679
|
-
* @param {
|
|
4680
|
-
* @param {
|
|
4771
|
+
* @param {AST.Statement[]} body_nodes
|
|
4772
|
+
* @param {AST.Node | null | undefined} source_node
|
|
4681
4773
|
* @param {TransformContext} [transform_context]
|
|
4682
|
-
* @returns {
|
|
4774
|
+
* @returns {AST.Expression}
|
|
4683
4775
|
*/
|
|
4684
4776
|
function create_statement_iife(body_nodes, source_node, transform_context) {
|
|
4685
4777
|
return set_generated_expression_loc(
|
|
@@ -4701,16 +4793,16 @@ function set_generated_expression_loc(node, source_node, transform_context) {
|
|
|
4701
4793
|
}
|
|
4702
4794
|
|
|
4703
4795
|
/**
|
|
4704
|
-
* @returns {
|
|
4796
|
+
* @returns {AST.UnaryExpression}
|
|
4705
4797
|
*/
|
|
4706
4798
|
function create_undefined_expression() {
|
|
4707
4799
|
return b.unary('void', b.literal(0));
|
|
4708
4800
|
}
|
|
4709
4801
|
|
|
4710
4802
|
/**
|
|
4711
|
-
* @param {
|
|
4803
|
+
* @param {AST.Node | null | undefined} node
|
|
4712
4804
|
* @param {TransformContext} [transform_context]
|
|
4713
|
-
* @returns {
|
|
4805
|
+
* @returns {AST.Expression | null}
|
|
4714
4806
|
*/
|
|
4715
4807
|
function return_value_block_to_expression(node, transform_context) {
|
|
4716
4808
|
const body = node?.type === 'BlockStatement' ? node.body : node ? [node] : [];
|
|
@@ -4720,9 +4812,9 @@ function return_value_block_to_expression(node, transform_context) {
|
|
|
4720
4812
|
}
|
|
4721
4813
|
|
|
4722
4814
|
/**
|
|
4723
|
-
* @param {
|
|
4815
|
+
* @param {AST.Node | null | undefined} node
|
|
4724
4816
|
* @param {TransformContext} [transform_context]
|
|
4725
|
-
* @returns {
|
|
4817
|
+
* @returns {AST.Expression | null}
|
|
4726
4818
|
*/
|
|
4727
4819
|
function return_value_if_statement_to_conditional_expression(node, transform_context) {
|
|
4728
4820
|
if (!is_if_control_node(node)) return null;
|
|
@@ -4730,10 +4822,12 @@ function return_value_if_statement_to_conditional_expression(node, transform_con
|
|
|
4730
4822
|
const consequent = return_value_block_to_expression(node.consequent, transform_context);
|
|
4731
4823
|
if (!consequent) return null;
|
|
4732
4824
|
|
|
4825
|
+
/** @type {AST.Expression} */
|
|
4733
4826
|
let alternate = create_undefined_expression();
|
|
4734
4827
|
if (node.alternate) {
|
|
4735
|
-
|
|
4736
|
-
if (!
|
|
4828
|
+
const lowered = return_value_block_to_expression(node.alternate, transform_context);
|
|
4829
|
+
if (!lowered) return null;
|
|
4830
|
+
alternate = lowered;
|
|
4737
4831
|
}
|
|
4738
4832
|
|
|
4739
4833
|
return set_generated_expression_loc(
|
|
@@ -4744,7 +4838,7 @@ function return_value_if_statement_to_conditional_expression(node, transform_con
|
|
|
4744
4838
|
}
|
|
4745
4839
|
|
|
4746
4840
|
/**
|
|
4747
|
-
* @param {
|
|
4841
|
+
* @param {AST.IfStatement | AST.JSXIfExpression} node
|
|
4748
4842
|
* @param {TransformContext} transform_context
|
|
4749
4843
|
* @returns {ESTreeJSX.JSXExpressionContainer}
|
|
4750
4844
|
*/
|
|
@@ -4752,6 +4846,29 @@ function if_statement_to_jsx_child(node, transform_context) {
|
|
|
4752
4846
|
const render_if_statement = create_render_if_statement(node, transform_context);
|
|
4753
4847
|
const conditional_expression = render_if_statement_to_conditional_expression(render_if_statement);
|
|
4754
4848
|
if (conditional_expression) {
|
|
4849
|
+
// `@if` / `@else` are lowered away: the directive becomes a ternary and
|
|
4850
|
+
// the clause becomes its alternate, so neither keyword has a counterpart
|
|
4851
|
+
// in the output. Anchor the ternary on `@if` and its alternate on
|
|
4852
|
+
// `@else` — `node` is the AUTHORED directive here, before
|
|
4853
|
+
// `create_render_if_statement` rebuilt it.
|
|
4854
|
+
if (conditional_expression.alternate) {
|
|
4855
|
+
conditional_expression.alternate = stamp_directive_origin(
|
|
4856
|
+
conditional_expression.alternate,
|
|
4857
|
+
node.alternateKeyword,
|
|
4858
|
+
'@else',
|
|
4859
|
+
transform_context,
|
|
4860
|
+
);
|
|
4861
|
+
}
|
|
4862
|
+
// The ternary itself cannot carry the anchor: it begins at the same
|
|
4863
|
+
// generated position as its test, so the test's own mapping wins there.
|
|
4864
|
+
// Each ARM is a distinct position, and pointing a directive at the branch
|
|
4865
|
+
// it renders is what the runtime targets already do.
|
|
4866
|
+
conditional_expression.consequent = stamp_directive_origin(
|
|
4867
|
+
conditional_expression.consequent,
|
|
4868
|
+
node,
|
|
4869
|
+
'@if',
|
|
4870
|
+
transform_context,
|
|
4871
|
+
);
|
|
4755
4872
|
return to_jsx_expression_container(conditional_expression, node);
|
|
4756
4873
|
}
|
|
4757
4874
|
|
|
@@ -4761,8 +4878,8 @@ function if_statement_to_jsx_child(node, transform_context) {
|
|
|
4761
4878
|
}
|
|
4762
4879
|
|
|
4763
4880
|
/**
|
|
4764
|
-
* @param {
|
|
4765
|
-
* @returns {
|
|
4881
|
+
* @param {AST.Node | null | undefined} node
|
|
4882
|
+
* @returns {AST.ConditionalExpression | null}
|
|
4766
4883
|
*/
|
|
4767
4884
|
function render_if_statement_to_conditional_expression(node) {
|
|
4768
4885
|
if (!is_if_control_node(node)) return null;
|
|
@@ -4770,23 +4887,22 @@ function render_if_statement_to_conditional_expression(node) {
|
|
|
4770
4887
|
const consequent = block_statement_to_return_expression(node.consequent);
|
|
4771
4888
|
if (!consequent) return null;
|
|
4772
4889
|
|
|
4890
|
+
/** @type {AST.Expression} */
|
|
4773
4891
|
let alternate = create_null_literal();
|
|
4774
4892
|
if (node.alternate) {
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
if (!alternate) return null;
|
|
4781
|
-
}
|
|
4893
|
+
const lowered = is_if_control_node(node.alternate)
|
|
4894
|
+
? render_if_statement_to_conditional_expression(node.alternate)
|
|
4895
|
+
: block_statement_to_return_expression(node.alternate);
|
|
4896
|
+
if (!lowered) return null;
|
|
4897
|
+
alternate = lowered;
|
|
4782
4898
|
}
|
|
4783
4899
|
|
|
4784
4900
|
return set_loc(b.conditional(node.test, consequent, alternate), node);
|
|
4785
4901
|
}
|
|
4786
4902
|
|
|
4787
4903
|
/**
|
|
4788
|
-
* @param {
|
|
4789
|
-
* @returns {
|
|
4904
|
+
* @param {AST.Node | null | undefined} block
|
|
4905
|
+
* @returns {AST.Expression | null}
|
|
4790
4906
|
*/
|
|
4791
4907
|
function block_statement_to_return_expression(block) {
|
|
4792
4908
|
if (!block || block.type !== 'BlockStatement' || block.body.length === 0) {
|
|
@@ -4803,14 +4919,15 @@ function block_statement_to_return_expression(block) {
|
|
|
4803
4919
|
return argument;
|
|
4804
4920
|
}
|
|
4805
4921
|
|
|
4806
|
-
|
|
4922
|
+
// The IIFE's trailing value is a render expression here, not an element.
|
|
4923
|
+
return b.call(b.arrow([], b.block([...block.body.slice(0, -1), b.return(argument)])));
|
|
4807
4924
|
}
|
|
4808
4925
|
|
|
4809
4926
|
/**
|
|
4810
4927
|
* Find the first `key` attribute expression in the top-level elements of a body.
|
|
4811
4928
|
* Used to propagate keys from loop body elements to wrapper components.
|
|
4812
|
-
* @param {
|
|
4813
|
-
* @returns {
|
|
4929
|
+
* @param {AST.Node[]} body_nodes
|
|
4930
|
+
* @returns {AST.Expression | undefined}
|
|
4814
4931
|
*/
|
|
4815
4932
|
function find_key_expression_in_body(body_nodes) {
|
|
4816
4933
|
for (const node of body_nodes) {
|
|
@@ -4821,11 +4938,13 @@ function find_key_expression_in_body(body_nodes) {
|
|
|
4821
4938
|
attr.name?.type === 'JSXIdentifier' &&
|
|
4822
4939
|
attr.name.name === 'key'
|
|
4823
4940
|
) {
|
|
4824
|
-
// Value is a JSXExpressionContainer
|
|
4941
|
+
// Value is a JSXExpressionContainer; a comment-only one has no key
|
|
4942
|
+
// expression to propagate.
|
|
4825
4943
|
if (attr.value?.type === 'JSXExpressionContainer') {
|
|
4826
|
-
|
|
4944
|
+
const { expression } = attr.value;
|
|
4945
|
+
return expression.type === 'JSXEmptyExpression' ? undefined : expression;
|
|
4827
4946
|
}
|
|
4828
|
-
return attr.value;
|
|
4947
|
+
return attr.value ?? undefined;
|
|
4829
4948
|
}
|
|
4830
4949
|
}
|
|
4831
4950
|
}
|
|
@@ -4834,8 +4953,8 @@ function find_key_expression_in_body(body_nodes) {
|
|
|
4834
4953
|
}
|
|
4835
4954
|
|
|
4836
4955
|
/**
|
|
4837
|
-
* @param {
|
|
4838
|
-
* @returns {
|
|
4956
|
+
* @param {AST.Node} source_node
|
|
4957
|
+
* @returns {AST.ReturnStatement}
|
|
4839
4958
|
*/
|
|
4840
4959
|
function continue_to_bare_return(source_node) {
|
|
4841
4960
|
const node = set_loc(b.return(create_null_literal()), source_node);
|
|
@@ -4852,9 +4971,21 @@ function continue_to_bare_return(source_node) {
|
|
|
4852
4971
|
* Returning null from the callback preserves the item-skip behavior while still
|
|
4853
4972
|
* producing an explicit "render nothing" value for JSX runtimes.
|
|
4854
4973
|
*
|
|
4855
|
-
* @
|
|
4974
|
+
* @overload
|
|
4975
|
+
* @param {AST.Statement[]} node
|
|
4856
4976
|
* @param {boolean} [is_root]
|
|
4857
|
-
* @returns {
|
|
4977
|
+
* @returns {AST.Statement[]}
|
|
4978
|
+
*/
|
|
4979
|
+
/**
|
|
4980
|
+
* @overload
|
|
4981
|
+
* @param {AST.Node} node
|
|
4982
|
+
* @param {boolean} [is_root]
|
|
4983
|
+
* @returns {AST.Node}
|
|
4984
|
+
*/
|
|
4985
|
+
/**
|
|
4986
|
+
* @param {AST.Node | AST.Node[]} node
|
|
4987
|
+
* @param {boolean} [is_root]
|
|
4988
|
+
* @returns {AST.Node | AST.Node[]}
|
|
4858
4989
|
*/
|
|
4859
4990
|
export function rewrite_loop_continues_to_bare_returns(node, is_root = true) {
|
|
4860
4991
|
if (Array.isArray(node)) {
|
|
@@ -4870,10 +5001,6 @@ export function rewrite_loop_continues_to_bare_returns(node, is_root = true) {
|
|
|
4870
5001
|
return changed ? result : node;
|
|
4871
5002
|
}
|
|
4872
5003
|
|
|
4873
|
-
if (!node || typeof node !== 'object') {
|
|
4874
|
-
return node;
|
|
4875
|
-
}
|
|
4876
|
-
|
|
4877
5004
|
if (node.type === 'ContinueStatement') {
|
|
4878
5005
|
return continue_to_bare_return(node);
|
|
4879
5006
|
}
|
|
@@ -4882,14 +5009,29 @@ export function rewrite_loop_continues_to_bare_returns(node, is_root = true) {
|
|
|
4882
5009
|
return node;
|
|
4883
5010
|
}
|
|
4884
5011
|
|
|
4885
|
-
|
|
4886
|
-
|
|
5012
|
+
const source = /** @type {AST.TraversableAstNode} */ (node);
|
|
5013
|
+
let out = source;
|
|
5014
|
+
for (const key of Object.keys(source)) {
|
|
4887
5015
|
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
4888
5016
|
continue;
|
|
4889
5017
|
}
|
|
4890
|
-
const
|
|
4891
|
-
|
|
4892
|
-
|
|
5018
|
+
const value = source[key];
|
|
5019
|
+
/** @type {unknown} */
|
|
5020
|
+
let walked = value;
|
|
5021
|
+
if (Array.isArray(value)) {
|
|
5022
|
+
let changed = false;
|
|
5023
|
+
const result = value.map((child) => {
|
|
5024
|
+
if (!is_ast_node(child)) return child;
|
|
5025
|
+
const next = rewrite_loop_continues_to_bare_returns(child, false);
|
|
5026
|
+
if (next !== child) changed = true;
|
|
5027
|
+
return next;
|
|
5028
|
+
});
|
|
5029
|
+
if (changed) walked = result;
|
|
5030
|
+
} else if (is_ast_node(value)) {
|
|
5031
|
+
walked = rewrite_loop_continues_to_bare_returns(value, false);
|
|
5032
|
+
}
|
|
5033
|
+
if (walked !== value) {
|
|
5034
|
+
if (out === source) out = { ...source };
|
|
4893
5035
|
out[key] = walked;
|
|
4894
5036
|
}
|
|
4895
5037
|
}
|
|
@@ -4898,7 +5040,7 @@ export function rewrite_loop_continues_to_bare_returns(node, is_root = true) {
|
|
|
4898
5040
|
}
|
|
4899
5041
|
|
|
4900
5042
|
/**
|
|
4901
|
-
* @param {
|
|
5043
|
+
* @param {AST.Node | AST.Node[] | null | undefined} node
|
|
4902
5044
|
* @param {TransformContext} transform_context
|
|
4903
5045
|
* @param {boolean} [is_root]
|
|
4904
5046
|
*/
|
|
@@ -4914,7 +5056,7 @@ function validate_for_body_control_flow(node, transform_context, is_root = true)
|
|
|
4914
5056
|
return;
|
|
4915
5057
|
}
|
|
4916
5058
|
|
|
4917
|
-
if (!node
|
|
5059
|
+
if (!node) {
|
|
4918
5060
|
return;
|
|
4919
5061
|
}
|
|
4920
5062
|
|
|
@@ -4957,16 +5099,13 @@ function validate_for_body_control_flow(node, transform_context, is_root = true)
|
|
|
4957
5099
|
return;
|
|
4958
5100
|
}
|
|
4959
5101
|
|
|
4960
|
-
for (const
|
|
4961
|
-
|
|
4962
|
-
continue;
|
|
4963
|
-
}
|
|
4964
|
-
validate_for_body_control_flow(node[key], transform_context, false);
|
|
5102
|
+
for (const child of child_nodes(node)) {
|
|
5103
|
+
validate_for_body_control_flow(child, transform_context, false);
|
|
4965
5104
|
}
|
|
4966
5105
|
}
|
|
4967
5106
|
|
|
4968
5107
|
/**
|
|
4969
|
-
* @param {
|
|
5108
|
+
* @param {AST.Node | AST.Node[] | null | undefined} node
|
|
4970
5109
|
* @param {TransformContext} transform_context
|
|
4971
5110
|
*/
|
|
4972
5111
|
function validate_if_body_control_flow(node, transform_context) {
|
|
@@ -4977,7 +5116,7 @@ function validate_if_body_control_flow(node, transform_context) {
|
|
|
4977
5116
|
return;
|
|
4978
5117
|
}
|
|
4979
5118
|
|
|
4980
|
-
if (!node
|
|
5119
|
+
if (!node) {
|
|
4981
5120
|
return;
|
|
4982
5121
|
}
|
|
4983
5122
|
|
|
@@ -5016,16 +5155,13 @@ function validate_if_body_control_flow(node, transform_context) {
|
|
|
5016
5155
|
return;
|
|
5017
5156
|
}
|
|
5018
5157
|
|
|
5019
|
-
for (const
|
|
5020
|
-
|
|
5021
|
-
continue;
|
|
5022
|
-
}
|
|
5023
|
-
validate_if_body_control_flow(node[key], transform_context);
|
|
5158
|
+
for (const child of child_nodes(node)) {
|
|
5159
|
+
validate_if_body_control_flow(child, transform_context);
|
|
5024
5160
|
}
|
|
5025
5161
|
}
|
|
5026
5162
|
|
|
5027
5163
|
/**
|
|
5028
|
-
* @param {
|
|
5164
|
+
* @param {AST.Node | null | undefined} node
|
|
5029
5165
|
* @returns {boolean}
|
|
5030
5166
|
*/
|
|
5031
5167
|
function is_loop_statement(node) {
|
|
@@ -5042,7 +5178,7 @@ function is_loop_statement(node) {
|
|
|
5042
5178
|
}
|
|
5043
5179
|
|
|
5044
5180
|
/**
|
|
5045
|
-
* @param {
|
|
5181
|
+
* @param {AST.ForOfStatement} node
|
|
5046
5182
|
* @param {TransformContext} transform_context
|
|
5047
5183
|
* @returns {ESTreeJSX.JSXExpressionContainer}
|
|
5048
5184
|
*/
|
|
@@ -5058,9 +5194,8 @@ function for_of_statement_to_jsx_child(node, transform_context) {
|
|
|
5058
5194
|
}
|
|
5059
5195
|
|
|
5060
5196
|
const loop_params = get_for_of_iteration_params(node.left, node.index);
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
);
|
|
5197
|
+
/** @type {AST.Node[]} */
|
|
5198
|
+
let loop_body = node.body.type === 'BlockStatement' ? node.body.body : [node.body];
|
|
5064
5199
|
validate_for_body_control_flow(loop_body, transform_context);
|
|
5065
5200
|
const has_hooks =
|
|
5066
5201
|
should_extract_hook_helpers(transform_context) &&
|
|
@@ -5133,7 +5268,7 @@ function for_of_statement_to_jsx_child(node, transform_context) {
|
|
|
5133
5268
|
),
|
|
5134
5269
|
false,
|
|
5135
5270
|
undefined,
|
|
5136
|
-
node.empty,
|
|
5271
|
+
has_location(node.empty) ? node.empty : undefined,
|
|
5137
5272
|
),
|
|
5138
5273
|
)
|
|
5139
5274
|
: null;
|
|
@@ -5142,9 +5277,28 @@ function for_of_statement_to_jsx_child(node, transform_context) {
|
|
|
5142
5277
|
transform_context.needs_for_of_iterable = true;
|
|
5143
5278
|
const args = [node.right, iter_callback];
|
|
5144
5279
|
if (empty_fallback) {
|
|
5145
|
-
|
|
5280
|
+
// `@empty` is a clause: its block starts at `{`, so the parser records
|
|
5281
|
+
// the keyword's own span. Anchor the arm the clause became on it.
|
|
5282
|
+
args.push(
|
|
5283
|
+
b.literal(null),
|
|
5284
|
+
stamp_directive_origin(
|
|
5285
|
+
b.arrow([], empty_fallback),
|
|
5286
|
+
node.emptyKeyword,
|
|
5287
|
+
'@empty',
|
|
5288
|
+
transform_context,
|
|
5289
|
+
),
|
|
5290
|
+
);
|
|
5146
5291
|
}
|
|
5147
|
-
|
|
5292
|
+
// `@for` itself has no counterpart in the output — the directive is
|
|
5293
|
+
// lowered away. Point its keyword at the helper this became, so tooling
|
|
5294
|
+
// can navigate from the authored construct to the code it produced.
|
|
5295
|
+
const helper_id = stamp_directive_origin(
|
|
5296
|
+
b.id(MAP_ITERABLE_INTERNAL_NAME),
|
|
5297
|
+
node,
|
|
5298
|
+
'@for',
|
|
5299
|
+
transform_context,
|
|
5300
|
+
);
|
|
5301
|
+
return to_jsx_expression_container(b.call(helper_id, ...args));
|
|
5148
5302
|
}
|
|
5149
5303
|
|
|
5150
5304
|
const map_call = b.call(b.member(node.right, create_generated_identifier('map')), iter_callback);
|
|
@@ -5169,9 +5323,9 @@ function for_of_statement_to_jsx_child(node, transform_context) {
|
|
|
5169
5323
|
* Returns a copy of `body_nodes` where the first keyable element carries the
|
|
5170
5324
|
* key attribute on a rebuilt opening element — the source nodes are never
|
|
5171
5325
|
* mutated (they may belong to the caller's parsed AST).
|
|
5172
|
-
* @param {
|
|
5173
|
-
* @param {
|
|
5174
|
-
* @returns {
|
|
5326
|
+
* @param {AST.Node[]} body_nodes
|
|
5327
|
+
* @param {AST.Expression} key_expression
|
|
5328
|
+
* @returns {AST.Node[]}
|
|
5175
5329
|
*/
|
|
5176
5330
|
function apply_key_to_loop_body(body_nodes, key_expression) {
|
|
5177
5331
|
let applied = false;
|
|
@@ -5179,13 +5333,7 @@ function apply_key_to_loop_body(body_nodes, key_expression) {
|
|
|
5179
5333
|
if (applied || node.type !== 'JSXElement') return node;
|
|
5180
5334
|
applied = true;
|
|
5181
5335
|
const attributes = node.openingElement?.attributes || [];
|
|
5182
|
-
|
|
5183
|
-
(/** @type {any} */ attr) =>
|
|
5184
|
-
attr.type === 'JSXAttribute' &&
|
|
5185
|
-
attr.name?.type === 'JSXIdentifier' &&
|
|
5186
|
-
attr.name.name === 'key',
|
|
5187
|
-
);
|
|
5188
|
-
if (has_key) return node;
|
|
5336
|
+
if (attributes.some(is_key_attribute)) return node;
|
|
5189
5337
|
return {
|
|
5190
5338
|
...node,
|
|
5191
5339
|
openingElement: {
|
|
@@ -5203,7 +5351,7 @@ function apply_key_to_loop_body(body_nodes, key_expression) {
|
|
|
5203
5351
|
}
|
|
5204
5352
|
|
|
5205
5353
|
/**
|
|
5206
|
-
* @param {
|
|
5354
|
+
* @param {AST.Node[]} body_nodes
|
|
5207
5355
|
* @returns {boolean}
|
|
5208
5356
|
*/
|
|
5209
5357
|
function should_apply_key_to_loop_body(body_nodes) {
|
|
@@ -5221,10 +5369,10 @@ function should_apply_key_to_loop_body(body_nodes) {
|
|
|
5221
5369
|
* lands on shallow copies; the returned array must be used in place of the
|
|
5222
5370
|
* argument.
|
|
5223
5371
|
*
|
|
5224
|
-
* @param {
|
|
5225
|
-
* @param {
|
|
5372
|
+
* @param {AST.Statement[]} statements
|
|
5373
|
+
* @param {AST.Expression} key_expression
|
|
5226
5374
|
* @param {TransformContext} transform_context
|
|
5227
|
-
* @returns {
|
|
5375
|
+
* @returns {AST.Statement[]}
|
|
5228
5376
|
*/
|
|
5229
5377
|
function apply_key_to_render_statements(statements, key_expression, transform_context) {
|
|
5230
5378
|
for (let i = statements.length - 1; i >= 0; i -= 1) {
|
|
@@ -5252,20 +5400,14 @@ function apply_key_to_render_statements(statements, key_expression, transform_co
|
|
|
5252
5400
|
}
|
|
5253
5401
|
|
|
5254
5402
|
/**
|
|
5255
|
-
* @param {
|
|
5256
|
-
* @param {
|
|
5257
|
-
* @returns {
|
|
5258
|
-
* shallow copy with the key attribute appended.
|
|
5403
|
+
* @param {AST.TSRXJSXElement} element
|
|
5404
|
+
* @param {AST.Expression} key_expression
|
|
5405
|
+
* @returns {AST.TSRXJSXElement} the element itself when it already has a `key`,
|
|
5406
|
+
* otherwise a shallow copy with the key attribute appended.
|
|
5259
5407
|
*/
|
|
5260
5408
|
function apply_key_to_jsx_element(element, key_expression) {
|
|
5261
5409
|
const attributes = element.openingElement?.attributes || [];
|
|
5262
|
-
|
|
5263
|
-
(/** @type {any} */ attr) =>
|
|
5264
|
-
attr.type === 'JSXAttribute' &&
|
|
5265
|
-
attr.name?.type === 'JSXIdentifier' &&
|
|
5266
|
-
attr.name.name === 'key',
|
|
5267
|
-
);
|
|
5268
|
-
if (has_key) return element;
|
|
5410
|
+
if (attributes.some(is_key_attribute)) return element;
|
|
5269
5411
|
|
|
5270
5412
|
return {
|
|
5271
5413
|
...element,
|
|
@@ -5283,9 +5425,19 @@ function apply_key_to_jsx_element(element, key_expression) {
|
|
|
5283
5425
|
}
|
|
5284
5426
|
|
|
5285
5427
|
/**
|
|
5286
|
-
* @param {
|
|
5287
|
-
* @
|
|
5288
|
-
|
|
5428
|
+
* @param {ESTreeJSX.JSXAttributeNode} attr
|
|
5429
|
+
* @returns {boolean}
|
|
5430
|
+
*/
|
|
5431
|
+
function is_key_attribute(attr) {
|
|
5432
|
+
return (
|
|
5433
|
+
attr.type === 'JSXAttribute' && attr.name?.type === 'JSXIdentifier' && attr.name.name === 'key'
|
|
5434
|
+
);
|
|
5435
|
+
}
|
|
5436
|
+
|
|
5437
|
+
/**
|
|
5438
|
+
* @param {AST.TSRXJSXFragment} fragment
|
|
5439
|
+
* @param {AST.Expression} key_expression
|
|
5440
|
+
* @returns {AST.TSRXJSXElement}
|
|
5289
5441
|
*/
|
|
5290
5442
|
function keyed_fragment_to_jsx_element(fragment, key_expression) {
|
|
5291
5443
|
const name = b.jsx_id('Fragment');
|
|
@@ -5302,7 +5454,7 @@ function keyed_fragment_to_jsx_element(fragment, key_expression) {
|
|
|
5302
5454
|
}
|
|
5303
5455
|
|
|
5304
5456
|
/**
|
|
5305
|
-
* @param {
|
|
5457
|
+
* @param {AST.SwitchStatement} node
|
|
5306
5458
|
* @param {TransformContext} transform_context
|
|
5307
5459
|
* @returns {ESTreeJSX.JSXExpressionContainer}
|
|
5308
5460
|
*/
|
|
@@ -5325,7 +5477,7 @@ function switch_statement_to_jsx_child(node, transform_context) {
|
|
|
5325
5477
|
* - both → ErrorBoundary wraps Suspense
|
|
5326
5478
|
* - JavaScript `try/finally` is not part of component template control flow
|
|
5327
5479
|
*
|
|
5328
|
-
* @param {
|
|
5480
|
+
* @param {AST.TryStatement} node
|
|
5329
5481
|
* @param {TransformContext} transform_context
|
|
5330
5482
|
* @returns {ESTreeJSX.JSXExpressionContainer}
|
|
5331
5483
|
*/
|
|
@@ -5369,7 +5521,7 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
5369
5521
|
const try_body_nodes = node.block.body || [];
|
|
5370
5522
|
const try_content = statement_body_to_jsx_child(try_body_nodes, transform_context);
|
|
5371
5523
|
|
|
5372
|
-
/** @type {
|
|
5524
|
+
/** @type {ESTreeJSX.JSXRenderNode} */
|
|
5373
5525
|
let result = try_content;
|
|
5374
5526
|
|
|
5375
5527
|
// Wrap in <Suspense> if pending block exists
|
|
@@ -5381,18 +5533,31 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
5381
5533
|
? to_jsx_expression_container(create_null_literal())
|
|
5382
5534
|
: statement_body_to_jsx_child(pending_body_nodes, transform_context);
|
|
5383
5535
|
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5536
|
+
const custom_pending = transform_context.platform.hooks?.createPendingBoundary?.(
|
|
5537
|
+
result,
|
|
5538
|
+
fallback_content,
|
|
5539
|
+
transform_context,
|
|
5540
|
+
node,
|
|
5541
|
+
);
|
|
5542
|
+
if (custom_pending != null) {
|
|
5543
|
+
result = custom_pending;
|
|
5544
|
+
} else {
|
|
5545
|
+
// The keyword the fallback came from, and — since `<Suspense>` is the
|
|
5546
|
+
// outermost thing `@try` produced unless a `@catch` wraps it below —
|
|
5547
|
+
// the keyword the boundary itself came from.
|
|
5548
|
+
const fallback_name = stamp_directive_origin(
|
|
5549
|
+
b.jsx_id('fallback'),
|
|
5550
|
+
node.pendingKeyword,
|
|
5551
|
+
'@pending',
|
|
5388
5552
|
transform_context,
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
create_jsx_element(
|
|
5553
|
+
);
|
|
5554
|
+
result = create_jsx_element(
|
|
5392
5555
|
'Suspense',
|
|
5393
|
-
[b.jsx_attribute(
|
|
5556
|
+
[b.jsx_attribute(fallback_name, fallback_content)],
|
|
5394
5557
|
[result],
|
|
5395
5558
|
);
|
|
5559
|
+
stamp_directive_origin(result.openingElement.name, node, '@try', transform_context);
|
|
5560
|
+
}
|
|
5396
5561
|
}
|
|
5397
5562
|
|
|
5398
5563
|
// Wrap in <TsrxErrorBoundary> if catch block exists
|
|
@@ -5423,10 +5588,13 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
5423
5588
|
|
|
5424
5589
|
const fallback_fn = b.arrow(
|
|
5425
5590
|
catch_params,
|
|
5426
|
-
b.block(
|
|
5591
|
+
b.block(
|
|
5592
|
+
build_render_statements(catch_body_nodes, true, transform_context),
|
|
5593
|
+
has_location(handler.body) ? handler.body : undefined,
|
|
5594
|
+
),
|
|
5427
5595
|
false,
|
|
5428
5596
|
undefined,
|
|
5429
|
-
handler,
|
|
5597
|
+
has_location(handler) ? handler : undefined,
|
|
5430
5598
|
);
|
|
5431
5599
|
|
|
5432
5600
|
const fallback_component =
|
|
@@ -5468,19 +5636,28 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
5468
5636
|
|
|
5469
5637
|
return result;
|
|
5470
5638
|
} else {
|
|
5639
|
+
const fallback_name = stamp_directive_origin(
|
|
5640
|
+
b.jsx_id('fallback'),
|
|
5641
|
+
node.handlerKeyword,
|
|
5642
|
+
'@catch',
|
|
5643
|
+
transform_context,
|
|
5644
|
+
);
|
|
5471
5645
|
result = create_jsx_element(
|
|
5472
5646
|
'TsrxErrorBoundary',
|
|
5473
5647
|
[
|
|
5474
|
-
b.jsx_attribute(
|
|
5475
|
-
b.jsx_id('fallback'),
|
|
5476
|
-
to_jsx_expression_container(/** @type {any} */ (fallback_fn)),
|
|
5477
|
-
),
|
|
5648
|
+
b.jsx_attribute(fallback_name, to_jsx_expression_container(fallback_fn)),
|
|
5478
5649
|
...(boundary_content
|
|
5479
5650
|
? [b.jsx_attribute(b.jsx_id('content'), to_jsx_expression_container(boundary_content))]
|
|
5480
5651
|
: []),
|
|
5481
5652
|
],
|
|
5482
5653
|
boundary_content ? [] : [result],
|
|
5483
5654
|
);
|
|
5655
|
+
// `@try` names the OUTERMOST boundary it produced. With a `@pending`
|
|
5656
|
+
// that is the `<Suspense>` stamped above, and this element merely
|
|
5657
|
+
// wraps it; without one, the error boundary is what `@try` became.
|
|
5658
|
+
if (!pending) {
|
|
5659
|
+
stamp_directive_origin(result.openingElement.name, node, '@try', transform_context);
|
|
5660
|
+
}
|
|
5484
5661
|
}
|
|
5485
5662
|
}
|
|
5486
5663
|
|
|
@@ -5490,7 +5667,9 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
5490
5667
|
return to_jsx_expression_container(result);
|
|
5491
5668
|
}
|
|
5492
5669
|
|
|
5493
|
-
return result
|
|
5670
|
+
return result.type === 'JSXExpressionContainer'
|
|
5671
|
+
? result
|
|
5672
|
+
: to_jsx_expression_container(/** @type {AST.Expression} */ (result));
|
|
5494
5673
|
}
|
|
5495
5674
|
|
|
5496
5675
|
/**
|
|
@@ -5498,8 +5677,8 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
5498
5677
|
*
|
|
5499
5678
|
* @param {string} tag_name
|
|
5500
5679
|
* @param {ESTreeJSX.JSXAttributeNode[]} attributes
|
|
5501
|
-
* @param {
|
|
5502
|
-
* @returns {
|
|
5680
|
+
* @param {AST.TSRXJSXElement['children']} children
|
|
5681
|
+
* @returns {AST.TSRXJSXElement}
|
|
5503
5682
|
*/
|
|
5504
5683
|
function create_jsx_element(tag_name, attributes, children) {
|
|
5505
5684
|
const self_closing = children.length === 0;
|
|
@@ -5711,8 +5890,8 @@ function summarize_switch_case_body(consequent) {
|
|
|
5711
5890
|
* lifted function) keeps the source position so editor IntelliSense doesn't
|
|
5712
5891
|
* see double/triple hits per source range.
|
|
5713
5892
|
*
|
|
5714
|
-
* @param {{ component_element:
|
|
5715
|
-
* @returns {
|
|
5893
|
+
* @param {{ component_element: AST.TSRXJSXElement }} helper
|
|
5894
|
+
* @returns {AST.TSRXJSXElement}
|
|
5716
5895
|
*/
|
|
5717
5896
|
export function clone_switch_helper_invocation(helper) {
|
|
5718
5897
|
return clone_ast_node(helper.component_element, false);
|
|
@@ -5773,10 +5952,11 @@ export function plan_switch_lift(switch_node, transform_context) {
|
|
|
5773
5952
|
if (!needs_helper[i]) continue;
|
|
5774
5953
|
const { own_body } = case_info[i];
|
|
5775
5954
|
|
|
5955
|
+
const source_case = switch_node.cases[i];
|
|
5776
5956
|
case_helpers[i] = create_hook_safe_helper(
|
|
5777
5957
|
own_body,
|
|
5778
5958
|
undefined,
|
|
5779
|
-
|
|
5959
|
+
has_location(source_case) ? source_case : undefined,
|
|
5780
5960
|
transform_context,
|
|
5781
5961
|
/** @type {AST.Identifier} */ (helper_ids[i]),
|
|
5782
5962
|
);
|
|
@@ -5836,6 +6016,7 @@ function build_switch_with_lift(switch_node, transform_context) {
|
|
|
5836
6016
|
/** @type {AST.Statement[]} */
|
|
5837
6017
|
const case_body = [];
|
|
5838
6018
|
/** @type {ESTreeJSX.JSXElement['children']} */
|
|
6019
|
+
/** @type {ESTreeJSX.JSXRenderChild[]} */
|
|
5839
6020
|
const render_nodes = [];
|
|
5840
6021
|
let has_terminal = false;
|
|
5841
6022
|
|
|
@@ -5853,11 +6034,7 @@ function build_switch_with_lift(switch_node, transform_context) {
|
|
|
5853
6034
|
break;
|
|
5854
6035
|
}
|
|
5855
6036
|
if (is_render_child_node(child)) {
|
|
5856
|
-
render_nodes.push(
|
|
5857
|
-
/** @type {ESTreeJSX.JSXElement['children'][number]} */ (
|
|
5858
|
-
to_jsx_child(child, transform_context)
|
|
5859
|
-
),
|
|
5860
|
-
);
|
|
6037
|
+
render_nodes.push(to_jsx_child(child, transform_context));
|
|
5861
6038
|
} else if (is_bare_render_expression(child)) {
|
|
5862
6039
|
render_nodes.push(to_jsx_expression_container(child, child));
|
|
5863
6040
|
} else {
|
|
@@ -5904,7 +6081,7 @@ function create_null_return_statement() {
|
|
|
5904
6081
|
|
|
5905
6082
|
/**
|
|
5906
6083
|
* @param {AST.Expression} expression
|
|
5907
|
-
* @param {AST.Node} [source_node]
|
|
6084
|
+
* @param {AST.Node | AST.NodeWithLocation} [source_node]
|
|
5908
6085
|
* @returns {ESTreeJSX.JSXExpressionContainer}
|
|
5909
6086
|
*/
|
|
5910
6087
|
function to_jsx_expression_container(expression, source_node = expression) {
|
|
@@ -6103,9 +6280,9 @@ function collect_jsx_setup_declarations(value, seen, declarations) {
|
|
|
6103
6280
|
}
|
|
6104
6281
|
|
|
6105
6282
|
/**
|
|
6106
|
-
* @param {
|
|
6283
|
+
* @param {AST.Expression | ESTreeJSX.JSXExpressionContainer} expression
|
|
6107
6284
|
* @param {boolean} in_jsx_child
|
|
6108
|
-
* @returns {
|
|
6285
|
+
* @returns {AST.Expression | ESTreeJSX.JSXExpressionContainer}
|
|
6109
6286
|
*/
|
|
6110
6287
|
function wrap_jsx_setup_declarations(expression, in_jsx_child) {
|
|
6111
6288
|
const declarations = extract_jsx_setup_declarations(expression);
|
|
@@ -6114,14 +6291,15 @@ function wrap_jsx_setup_declarations(expression, in_jsx_child) {
|
|
|
6114
6291
|
}
|
|
6115
6292
|
|
|
6116
6293
|
const return_expression =
|
|
6117
|
-
expression
|
|
6294
|
+
expression.type === 'JSXExpressionContainer' ? expression.expression : expression;
|
|
6295
|
+
const expression_loc = has_location(expression) ? expression : undefined;
|
|
6118
6296
|
const call = b.call(
|
|
6119
6297
|
b.arrow(
|
|
6120
6298
|
[],
|
|
6121
|
-
b.block([...declarations, b.return(return_expression)],
|
|
6299
|
+
b.block([...declarations, b.return(return_expression)], expression_loc),
|
|
6122
6300
|
false,
|
|
6123
6301
|
undefined,
|
|
6124
|
-
|
|
6302
|
+
expression_loc,
|
|
6125
6303
|
),
|
|
6126
6304
|
);
|
|
6127
6305
|
|
|
@@ -6283,6 +6461,52 @@ export const MERGE_REFS_INTERNAL_NAME = '__mergeRefs';
|
|
|
6283
6461
|
export const NORMALIZE_SPREAD_PROPS_INTERNAL_NAME = '__normalize_spread_props';
|
|
6284
6462
|
export const NORMALIZE_SPREAD_PROPS_FOR_REF_ATTR_INTERNAL_NAME =
|
|
6285
6463
|
'__normalize_spread_props_for_ref_attr';
|
|
6464
|
+
/**
|
|
6465
|
+
* Point a generated node at the DIRECTIVE KEYWORD that produced it.
|
|
6466
|
+
*
|
|
6467
|
+
* A template directive is lowered away entirely — `@for` becomes a
|
|
6468
|
+
* `map_iterable` call, `@if` a conditional — so the keyword the author actually
|
|
6469
|
+
* wrote has no counterpart in the output and nothing in the source map reaches
|
|
6470
|
+
* it. Navigation tooling (the playground's compiled-output pane; anything else
|
|
6471
|
+
* tracing authored syntax to emitted code) therefore cannot resolve a cursor
|
|
6472
|
+
* placed on it.
|
|
6473
|
+
*
|
|
6474
|
+
* Opt-in via `options.inspect`. With the flag clear this returns the node
|
|
6475
|
+
* untouched, so the emitted bytes, the print's source map and every Volar
|
|
6476
|
+
* mapping derived from it are exactly what they were — the editor pipeline is
|
|
6477
|
+
* unaffected by construction.
|
|
6478
|
+
*
|
|
6479
|
+
* Only ONE identifying token per construct is stamped (the helper's callee, an
|
|
6480
|
+
* arm's identifier). Stamping a whole subtree would give the keyword a span
|
|
6481
|
+
* wide enough to shadow the finer mappings inside it, which is precisely the
|
|
6482
|
+
* failure the synthesized-`return` clamp used to produce.
|
|
6483
|
+
*
|
|
6484
|
+
* @template {AST.Node} T
|
|
6485
|
+
* @param {T} node the generated node to anchor
|
|
6486
|
+
* @param {AST.Node | AST.NodeWithLocation | null | undefined} directive the authored
|
|
6487
|
+
* directive node; its `start`/`loc.start` IS the keyword
|
|
6488
|
+
* @param {string} keyword the authored spelling, `@` included
|
|
6489
|
+
* @param {TransformContext} transform_context
|
|
6490
|
+
* @returns {T}
|
|
6491
|
+
*/
|
|
6492
|
+
function stamp_directive_origin(node, directive, keyword, transform_context) {
|
|
6493
|
+
if (!transform_context.inspect) return node;
|
|
6494
|
+
const start = directive?.start;
|
|
6495
|
+
const loc_start = directive?.loc?.start;
|
|
6496
|
+
if (typeof start !== 'number' || !loc_start) return node;
|
|
6497
|
+
// Confirm the authored spelling before claiming those bytes: the same
|
|
6498
|
+
// lowering also runs for constructs the author never wrote as a directive.
|
|
6499
|
+
if (transform_context.source?.startsWith(keyword, start) !== true) return node;
|
|
6500
|
+
|
|
6501
|
+
node.start = start;
|
|
6502
|
+
node.end = start + keyword.length;
|
|
6503
|
+
node.loc = {
|
|
6504
|
+
start: { ...loc_start },
|
|
6505
|
+
end: { ...loc_start, column: loc_start.column + keyword.length },
|
|
6506
|
+
};
|
|
6507
|
+
return node;
|
|
6508
|
+
}
|
|
6509
|
+
|
|
6286
6510
|
export const MAP_ITERABLE_INTERNAL_NAME = '__map_iterable';
|
|
6287
6511
|
export const ITERATION_VALUE_INTERNAL_NAME = '__IterationValue';
|
|
6288
6512
|
|