@tsrx/core 0.1.16 → 0.1.18
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/README.md +4 -3
- package/package.json +1 -1
- package/src/analyze/prune.js +13 -28
- package/src/analyze/validation.js +27 -94
- package/src/diagnostics.js +1 -3
- package/src/index.js +20 -28
- package/src/parse/index.js +37 -4
- package/src/plugin.js +182 -644
- package/src/runtime/ref.js +48 -0
- package/src/scope.js +0 -13
- package/src/transform/await.js +1 -1
- package/src/transform/jsx/ast-builders.js +3 -5
- package/src/transform/jsx/helpers.js +1 -2
- package/src/transform/jsx/index.js +1326 -1495
- package/src/transform/lazy.js +6 -6
- package/src/transform/scoping.js +6 -18
- package/src/transform/segments.js +26 -157
- package/src/transform/style-ref.js +326 -0
- package/src/utils/ast.js +15 -23
- package/src/utils/builders.js +0 -18
- package/types/index.d.ts +34 -74
- package/types/jsx-platform.d.ts +27 -30
- package/types/parse.d.ts +2 -15
- package/types/runtime/ref.d.ts +3 -0
|
@@ -37,21 +37,19 @@ import {
|
|
|
37
37
|
} from '../../utils/builders.js';
|
|
38
38
|
import * as b from '../../utils/builders.js';
|
|
39
39
|
import { apply_lazy_transforms, preallocate_lazy_ids } from '../lazy.js';
|
|
40
|
-
import { find_first_top_level_await_in_component_body } from '../await.js';
|
|
41
40
|
import {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
} from '../scoping.js';
|
|
41
|
+
find_first_top_level_await,
|
|
42
|
+
find_first_top_level_await_in_tsrx_function_body,
|
|
43
|
+
} from '../await.js';
|
|
44
|
+
import { prepare_stylesheet_for_render, annotate_with_hash, is_style_element } from '../scoping.js';
|
|
46
45
|
import {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
} from '../../
|
|
54
|
-
import { get_component_from_path, is_function_or_component_node } from '../../utils/ast.js';
|
|
46
|
+
collect_style_ref_attributes,
|
|
47
|
+
create_style_class_map,
|
|
48
|
+
create_style_class_map_from_stylesheet,
|
|
49
|
+
create_style_ref_setup_statements,
|
|
50
|
+
get_style_element_stylesheet,
|
|
51
|
+
} from '../style-ref.js';
|
|
52
|
+
import { is_function_or_component_node } from '../../utils/ast.js';
|
|
55
53
|
import {
|
|
56
54
|
is_interleaved_body as is_interleaved_body_core,
|
|
57
55
|
is_capturable_jsx_child,
|
|
@@ -66,43 +64,6 @@ const HOOK_CALLBACK_OUTER_MUTATION_ERROR =
|
|
|
66
64
|
const TEMPLATE_FRAGMENT_ERROR =
|
|
67
65
|
'JSX fragment syntax is not needed in TSRX templates. TSRX renders in immediate mode, so everything is already a fragment. Use `<>...</>` only within <tsx>...</tsx>.';
|
|
68
66
|
|
|
69
|
-
/**
|
|
70
|
-
* @param {TransformContext} transform_context
|
|
71
|
-
* @returns {string}
|
|
72
|
-
*/
|
|
73
|
-
export function get_invalid_html_child_error_message(transform_context) {
|
|
74
|
-
return `\`{html ...}\` is only supported as the sole child of an element in ${transform_context.platform.name}.`;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* @param {AST.Node} node
|
|
79
|
-
* @param {TransformContext} transform_context
|
|
80
|
-
*/
|
|
81
|
-
function report_invalid_html_child_error(node, transform_context) {
|
|
82
|
-
error(
|
|
83
|
-
get_invalid_html_child_error_message(transform_context),
|
|
84
|
-
transform_context.filename,
|
|
85
|
-
node,
|
|
86
|
-
transform_context.errors,
|
|
87
|
-
transform_context.comments,
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* In loose/editor mode `error(...)` records the diagnostic and continues, so an
|
|
93
|
-
* invalid standalone `{html ...}` child still needs a valid expression node for
|
|
94
|
-
* the virtual TSX output.
|
|
95
|
-
*
|
|
96
|
-
* @param {any} node
|
|
97
|
-
* @param {TransformContext} transform_context
|
|
98
|
-
* @returns {ESTreeJSX.JSXExpressionContainer}
|
|
99
|
-
*/
|
|
100
|
-
export function recover_invalid_html_child(node, transform_context) {
|
|
101
|
-
report_invalid_html_child_error(node, transform_context);
|
|
102
|
-
const expression = set_loc(clone_expression_node(node.expression), node);
|
|
103
|
-
return to_jsx_expression_container(expression, node);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
67
|
/**
|
|
107
68
|
* @param {AST.Node} node
|
|
108
69
|
* @param {TransformContext} transform_context
|
|
@@ -181,60 +142,16 @@ function is_function_or_class_boundary(node) {
|
|
|
181
142
|
);
|
|
182
143
|
}
|
|
183
144
|
|
|
184
|
-
/**
|
|
185
|
-
* @param {any[]} path
|
|
186
|
-
* @returns {boolean}
|
|
187
|
-
*/
|
|
188
|
-
function is_inside_component_for_of(path) {
|
|
189
|
-
for (let i = path.length - 1; i >= 0; i -= 1) {
|
|
190
|
-
const node = path[i];
|
|
191
|
-
if (is_function_or_class_boundary(node) || node?.type === 'Component') {
|
|
192
|
-
return false;
|
|
193
|
-
}
|
|
194
|
-
if (node?.type === 'ForOfStatement') {
|
|
195
|
-
return true;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
return false;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* @param {any[]} path
|
|
203
|
-
* @returns {boolean}
|
|
204
|
-
*/
|
|
205
|
-
function break_targets_component_loop(path) {
|
|
206
|
-
for (let i = path.length - 1; i >= 0; i -= 1) {
|
|
207
|
-
const node = path[i];
|
|
208
|
-
if (is_function_or_class_boundary(node) || node?.type === 'Component') {
|
|
209
|
-
return false;
|
|
210
|
-
}
|
|
211
|
-
if (node?.type === 'SwitchStatement') {
|
|
212
|
-
return false;
|
|
213
|
-
}
|
|
214
|
-
if (
|
|
215
|
-
node?.type === 'ForOfStatement' ||
|
|
216
|
-
node?.type === 'ForStatement' ||
|
|
217
|
-
node?.type === 'ForInStatement' ||
|
|
218
|
-
node?.type === 'WhileStatement' ||
|
|
219
|
-
node?.type === 'DoWhileStatement'
|
|
220
|
-
) {
|
|
221
|
-
return true;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
return false;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
145
|
/**
|
|
228
146
|
* Build a `transform()` function for a specific JSX platform (React, Preact,
|
|
229
|
-
* Solid). Given a `JsxPlatform` descriptor, returns a transform that
|
|
230
|
-
*
|
|
231
|
-
* TSX module for that platform.
|
|
147
|
+
* Solid). Given a `JsxPlatform` descriptor, returns a transform that lowers
|
|
148
|
+
* native TSRX template nodes into a plain TSX module for that platform.
|
|
232
149
|
*
|
|
233
|
-
* Any `<style>` element declared inside a
|
|
234
|
-
* via `@tsrx/core`'s stylesheet renderer, and returned alongside the JS
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
150
|
+
* Any `<style>` element declared inside a TSRX fragment is collected, rendered
|
|
151
|
+
* via `@tsrx/core`'s stylesheet renderer, and returned alongside the JS output
|
|
152
|
+
* so a downstream plugin can inject it. The compiler also augments every
|
|
153
|
+
* non-style Element in that fragment with the stylesheet's hash class so scoped
|
|
154
|
+
* selectors match correctly.
|
|
238
155
|
*
|
|
239
156
|
* @param {JsxPlatform} platform
|
|
240
157
|
* @returns {(ast: AST.Program, source: string, filename?: string, options?: JsxTransformOptions) => JsxTransformResult}
|
|
@@ -249,13 +166,6 @@ export function createJsxTransform(platform) {
|
|
|
249
166
|
*/
|
|
250
167
|
function transform(ast, source, filename, options) {
|
|
251
168
|
const suspense_source = options?.suspenseSource ?? platform.imports.suspense;
|
|
252
|
-
const should_scan_use_server_directive =
|
|
253
|
-
platform.validation.requireUseServerForAwait &&
|
|
254
|
-
(!platform.hooks?.validateComponentAwait ||
|
|
255
|
-
platform.validation.scanUseServerDirectiveForAwaitWithCustomValidator !== false);
|
|
256
|
-
const module_uses_server_directive = should_scan_use_server_directive
|
|
257
|
-
? has_use_server_directive(ast)
|
|
258
|
-
: true;
|
|
259
169
|
const collect = !!(options?.collect || options?.loose);
|
|
260
170
|
/** @type {any[]} */
|
|
261
171
|
const stylesheets = [];
|
|
@@ -267,18 +177,20 @@ export function createJsxTransform(platform) {
|
|
|
267
177
|
needs_error_boundary: false,
|
|
268
178
|
needs_suspense: false,
|
|
269
179
|
needs_merge_refs: false,
|
|
270
|
-
needs_ref_prop: false,
|
|
271
180
|
needs_normalize_spread_props: false,
|
|
181
|
+
needs_normalize_spread_props_for_ref_attr: false,
|
|
272
182
|
needs_fragment: false,
|
|
273
183
|
needs_for_of_iterable: false,
|
|
274
184
|
needs_iteration_value_type: false,
|
|
185
|
+
stylesheets,
|
|
275
186
|
module_scoped_hook_components:
|
|
276
187
|
options?.moduleScopedHookComponents ?? !!platform.hooks?.moduleScopedHookComponents,
|
|
277
188
|
helper_state: null,
|
|
189
|
+
hook_helpers_enabled: false,
|
|
278
190
|
available_bindings: new Map(),
|
|
279
191
|
lazy_next_id: 0,
|
|
280
|
-
current_css_hash: null,
|
|
281
192
|
filename: filename ?? null,
|
|
193
|
+
source,
|
|
282
194
|
collect,
|
|
283
195
|
errors: collect ? options?.errors : undefined,
|
|
284
196
|
comments: options?.comments,
|
|
@@ -293,197 +205,12 @@ export function createJsxTransform(platform) {
|
|
|
293
205
|
}
|
|
294
206
|
|
|
295
207
|
walk(/** @type {any} */ (ast), transform_context, {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
validate_component_loop_return_statement(
|
|
300
|
-
node,
|
|
301
|
-
filename,
|
|
302
|
-
transform_context.errors,
|
|
303
|
-
transform_context.comments,
|
|
304
|
-
);
|
|
305
|
-
} else {
|
|
306
|
-
validate_component_return_statement(
|
|
307
|
-
node,
|
|
308
|
-
filename,
|
|
309
|
-
transform_context.errors,
|
|
310
|
-
transform_context.comments,
|
|
311
|
-
);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
return next();
|
|
316
|
-
},
|
|
317
|
-
|
|
318
|
-
BreakStatement(node, { next, path }) {
|
|
319
|
-
if (get_component_from_path(path) && break_targets_component_loop(path)) {
|
|
320
|
-
validate_component_loop_break_statement(
|
|
321
|
-
node,
|
|
322
|
-
filename,
|
|
323
|
-
transform_context.errors,
|
|
324
|
-
transform_context.comments,
|
|
325
|
-
);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
return next();
|
|
329
|
-
},
|
|
330
|
-
|
|
331
|
-
ForStatement(node, { next, path }) {
|
|
332
|
-
if (get_component_from_path(path)) {
|
|
333
|
-
validate_component_unsupported_loop_statement(
|
|
334
|
-
node,
|
|
335
|
-
filename,
|
|
336
|
-
transform_context.errors,
|
|
337
|
-
transform_context.comments,
|
|
338
|
-
);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
return next();
|
|
342
|
-
},
|
|
343
|
-
|
|
344
|
-
ForInStatement(node, { next, path }) {
|
|
345
|
-
if (get_component_from_path(path)) {
|
|
346
|
-
validate_component_unsupported_loop_statement(
|
|
347
|
-
node,
|
|
348
|
-
filename,
|
|
349
|
-
transform_context.errors,
|
|
350
|
-
transform_context.comments,
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
return next();
|
|
355
|
-
},
|
|
356
|
-
|
|
357
|
-
WhileStatement(node, { next, path }) {
|
|
358
|
-
if (get_component_from_path(path)) {
|
|
359
|
-
validate_component_unsupported_loop_statement(
|
|
360
|
-
node,
|
|
361
|
-
filename,
|
|
362
|
-
transform_context.errors,
|
|
363
|
-
transform_context.comments,
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
return next();
|
|
368
|
-
},
|
|
369
|
-
|
|
370
|
-
DoWhileStatement(node, { next, path }) {
|
|
371
|
-
if (get_component_from_path(path)) {
|
|
372
|
-
validate_component_unsupported_loop_statement(
|
|
373
|
-
node,
|
|
374
|
-
filename,
|
|
375
|
-
transform_context.errors,
|
|
376
|
-
transform_context.comments,
|
|
377
|
-
);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
return next();
|
|
381
|
-
},
|
|
382
|
-
|
|
383
|
-
ClassBody(node, { next }) {
|
|
384
|
-
validate_class_component_declarations(
|
|
385
|
-
/** @type {any} */ (node),
|
|
386
|
-
filename,
|
|
387
|
-
transform_context.errors,
|
|
388
|
-
transform_context.comments,
|
|
389
|
-
);
|
|
390
|
-
return next();
|
|
391
|
-
},
|
|
392
|
-
|
|
393
|
-
Component(node, { next, state }) {
|
|
394
|
-
const as_any = /** @type {any} */ (node);
|
|
395
|
-
|
|
396
|
-
validate_component_params(
|
|
397
|
-
as_any,
|
|
398
|
-
filename,
|
|
399
|
-
transform_context.errors,
|
|
400
|
-
transform_context.comments,
|
|
401
|
-
);
|
|
402
|
-
|
|
403
|
-
const await_expression = find_first_top_level_await_in_component_body(as_any.body || []);
|
|
404
|
-
|
|
405
|
-
if (await_expression) {
|
|
406
|
-
// Let a platform reject component-level await entirely (solid)
|
|
407
|
-
// or customize the error. Otherwise fall back to the default
|
|
408
|
-
// `requireUseServerForAwait` check.
|
|
409
|
-
if (platform.hooks?.validateComponentAwait) {
|
|
410
|
-
platform.hooks.validateComponentAwait(
|
|
411
|
-
await_expression,
|
|
412
|
-
as_any,
|
|
413
|
-
state,
|
|
414
|
-
module_uses_server_directive,
|
|
415
|
-
source,
|
|
416
|
-
);
|
|
417
|
-
} else if (!module_uses_server_directive) {
|
|
418
|
-
error(
|
|
419
|
-
`${platform.name} components can only use \`await\` when the module has a top-level "use server" directive.`,
|
|
420
|
-
state.filename,
|
|
421
|
-
await_expression,
|
|
422
|
-
state.errors,
|
|
423
|
-
state.comments,
|
|
424
|
-
);
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
as_any.metadata = /** @type {any} */ ({
|
|
428
|
-
...(as_any.metadata || {}),
|
|
429
|
-
contains_top_level_await: true,
|
|
430
|
-
});
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
const css = as_any.css;
|
|
434
|
-
if (css) {
|
|
435
|
-
apply_css_definition_metadata(as_any, css);
|
|
436
|
-
stylesheets.push(css);
|
|
437
|
-
const hash = css.hash;
|
|
438
|
-
annotate_component_with_hash(
|
|
439
|
-
as_any,
|
|
440
|
-
hash,
|
|
441
|
-
platform.jsx.rewriteClassAttr ? 'className' : 'class',
|
|
442
|
-
transform_context.typeOnly,
|
|
443
|
-
);
|
|
444
|
-
}
|
|
445
|
-
return next(state);
|
|
446
|
-
},
|
|
208
|
+
FunctionDeclaration: collect_native_function_tsrx_metadata,
|
|
209
|
+
FunctionExpression: collect_native_function_tsrx_metadata,
|
|
210
|
+
ArrowFunctionExpression: collect_native_function_tsrx_metadata,
|
|
447
211
|
});
|
|
448
212
|
|
|
449
213
|
const transformed = walk(/** @type {any} */ (ast), transform_context, {
|
|
450
|
-
Component(node, { next, state }) {
|
|
451
|
-
const as_any = /** @type {any} */ (node);
|
|
452
|
-
|
|
453
|
-
// Set up helper_state and bindings BEFORE next() so that nested
|
|
454
|
-
// hook_safe_* calls (inside Element children) can register helpers
|
|
455
|
-
// and access available bindings during the bottom-up walk.
|
|
456
|
-
const helper_state = create_helper_state(as_any.id?.name || 'Component');
|
|
457
|
-
const saved_helper_state = state.helper_state;
|
|
458
|
-
const saved_bindings = state.available_bindings;
|
|
459
|
-
const saved_css_hash = state.current_css_hash;
|
|
460
|
-
state.helper_state = helper_state;
|
|
461
|
-
state.current_css_hash = as_any.css ? as_any.css.hash : null;
|
|
462
|
-
|
|
463
|
-
// Pre-collect component body bindings (params + top-level statements)
|
|
464
|
-
// so Element children processed during the bottom-up walk can see
|
|
465
|
-
// component-scope names. Hook-safe helpers filter this set down to
|
|
466
|
-
// the names their body actually references before generating props.
|
|
467
|
-
const body_bindings = collect_param_bindings(as_any.params || []);
|
|
468
|
-
const body = as_any.body || [];
|
|
469
|
-
const split_index = find_hook_safe_split_index(body, state);
|
|
470
|
-
const collect_end = split_index === -1 ? body.length : split_index;
|
|
471
|
-
for (let i = 0; i < collect_end; i += 1) {
|
|
472
|
-
collect_statement_bindings(body[i], body_bindings);
|
|
473
|
-
}
|
|
474
|
-
state.available_bindings = body_bindings;
|
|
475
|
-
|
|
476
|
-
const inner = /** @type {any} */ (next() ?? node);
|
|
477
|
-
|
|
478
|
-
// Restore context
|
|
479
|
-
state.helper_state = saved_helper_state;
|
|
480
|
-
state.available_bindings = saved_bindings;
|
|
481
|
-
state.current_css_hash = saved_css_hash;
|
|
482
|
-
|
|
483
|
-
const convert = platform.hooks?.componentToFunction ?? component_to_function_declaration;
|
|
484
|
-
return /** @type {any} */ (convert(inner, state, helper_state));
|
|
485
|
-
},
|
|
486
|
-
|
|
487
214
|
Tsx(node, { next, path }) {
|
|
488
215
|
const inner = /** @type {any} */ (next() ?? node);
|
|
489
216
|
const in_jsx_child = in_jsx_child_context(path);
|
|
@@ -493,7 +220,19 @@ export function createJsxTransform(platform) {
|
|
|
493
220
|
},
|
|
494
221
|
|
|
495
222
|
Tsrx(node, { next, path, state }) {
|
|
496
|
-
|
|
223
|
+
/** @type {{ css: any, style_refs: any[] } | null} */
|
|
224
|
+
let style_context = null;
|
|
225
|
+
const inner = with_tsrx_fragment_styles(node, state, (context) => {
|
|
226
|
+
style_context = context;
|
|
227
|
+
return next() ?? node;
|
|
228
|
+
});
|
|
229
|
+
for (const statement of create_tsrx_style_ref_setup_statements(
|
|
230
|
+
node,
|
|
231
|
+
style_context,
|
|
232
|
+
state,
|
|
233
|
+
)) {
|
|
234
|
+
add_jsx_setup_declaration(inner, statement);
|
|
235
|
+
}
|
|
497
236
|
const in_jsx_child = in_jsx_child_context(path);
|
|
498
237
|
return /** @type {any} */ (
|
|
499
238
|
wrap_jsx_setup_declarations(
|
|
@@ -514,12 +253,23 @@ export function createJsxTransform(platform) {
|
|
|
514
253
|
);
|
|
515
254
|
},
|
|
516
255
|
|
|
517
|
-
Element(node, { next, state }) {
|
|
256
|
+
Element(node, { next, path, state }) {
|
|
257
|
+
if (is_style_element(node) && is_style_expression_position(path)) {
|
|
258
|
+
const stylesheet = get_style_element_stylesheet(node);
|
|
259
|
+
if (stylesheet) {
|
|
260
|
+
analyze_css(stylesheet);
|
|
261
|
+
state.stylesheets.push(stylesheet);
|
|
262
|
+
return /** @type {any} */ (create_style_class_map_from_stylesheet(stylesheet));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
518
266
|
// Capture raw children BEFORE the walker transforms them so a
|
|
519
267
|
// platform hook (e.g. Solid's textContent optimization) can
|
|
520
268
|
// inspect the original Text / TSRXExpression nodes rather than
|
|
521
269
|
// their walker-lowered JSXExpressionContainer equivalents.
|
|
522
|
-
const raw_children = /** @type {any} */ (node
|
|
270
|
+
const raw_children = /** @type {any} */ (node.children || []).map(
|
|
271
|
+
(/** @type {any} */ child) => (child && typeof child === 'object' ? { ...child } : child),
|
|
272
|
+
);
|
|
523
273
|
const inner = /** @type {any} */ (next() ?? node);
|
|
524
274
|
const hook = platform.hooks?.transformElement;
|
|
525
275
|
if (hook) return /** @type {any} */ (hook(inner, state, raw_children));
|
|
@@ -538,48 +288,25 @@ export function createJsxTransform(platform) {
|
|
|
538
288
|
return /** @type {any} */ (to_jsx_expression_container(inner.expression, inner));
|
|
539
289
|
},
|
|
540
290
|
|
|
541
|
-
Style(node, { state, path }) {
|
|
542
|
-
validate_style_directive(node, state, path);
|
|
543
|
-
const class_name = typeof node.value.value === 'string' ? node.value.value : '';
|
|
544
|
-
const value = state.current_css_hash
|
|
545
|
-
? `${state.current_css_hash} ${class_name}`
|
|
546
|
-
: class_name;
|
|
547
|
-
return b.literal(value, undefined, node);
|
|
548
|
-
},
|
|
549
|
-
|
|
550
291
|
// Default .metadata on every function-like node so downstream consumers
|
|
551
|
-
//
|
|
552
|
-
//
|
|
553
|
-
//
|
|
554
|
-
//
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
FunctionExpression: transform_function_with_hook_helpers,
|
|
559
|
-
ArrowFunctionExpression: transform_function_with_hook_helpers,
|
|
560
|
-
|
|
561
|
-
RefExpression(node) {
|
|
562
|
-
return create_ref_prop_call(node, transform_context);
|
|
563
|
-
},
|
|
292
|
+
// do not trip on an undefined metadata object. Ripple's analyze phase
|
|
293
|
+
// does this via visit_function; tsrx-react has no analyze phase.
|
|
294
|
+
// If an uppercase JS function contains hook-bearing TSRX, give it a
|
|
295
|
+
// temporary helper scope so extracted hook helpers get stable identities.
|
|
296
|
+
FunctionDeclaration: transform_function,
|
|
297
|
+
FunctionExpression: transform_function,
|
|
298
|
+
ArrowFunctionExpression: transform_function,
|
|
564
299
|
|
|
565
300
|
JSXOpeningElement(node, { next }) {
|
|
566
|
-
const visited = next() || node;
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
visited.attributes || [],
|
|
570
|
-
!is_component,
|
|
571
|
-
transform_context,
|
|
572
|
-
);
|
|
573
|
-
if (transform_context.typeOnly) {
|
|
574
|
-
add_ref_target_type_to_ref_prop_attributes(
|
|
575
|
-
attrs,
|
|
576
|
-
!is_component ? create_element_ref_target_type(visited) : null,
|
|
577
|
-
);
|
|
301
|
+
const visited = /** @type {any} */ (next() || node);
|
|
302
|
+
if (visited.metadata?.native_tsrx_pretransformed) {
|
|
303
|
+
return visited;
|
|
578
304
|
}
|
|
305
|
+
const is_component = is_component_like_jsx_name(visited.name);
|
|
579
306
|
return {
|
|
580
307
|
...visited,
|
|
581
308
|
attributes: merge_duplicate_refs(
|
|
582
|
-
normalize_host_ref_spreads(
|
|
309
|
+
normalize_host_ref_spreads(visited.attributes || [], !is_component, transform_context),
|
|
583
310
|
transform_context,
|
|
584
311
|
),
|
|
585
312
|
};
|
|
@@ -594,9 +321,7 @@ export function createJsxTransform(platform) {
|
|
|
594
321
|
}
|
|
595
322
|
|
|
596
323
|
// Apply lazy destructuring transforms to module-level code (top-level function
|
|
597
|
-
// declarations, arrow functions, etc.).
|
|
598
|
-
// transformed inside component_to_function_declaration; this catches plain
|
|
599
|
-
// functions outside components and any lazy patterns in module scope.
|
|
324
|
+
// declarations, arrow functions, etc.).
|
|
600
325
|
// In type-only mode, the lazy patterns survive untouched: esrap ignores the
|
|
601
326
|
// non-standard `lazy` flag, so `&{ a, b }` prints as `{ a, b }`, `let &[a]
|
|
602
327
|
// = expr` prints as `let [a] = expr`, and the bare statement-level form
|
|
@@ -629,18 +354,30 @@ export function createJsxTransform(platform) {
|
|
|
629
354
|
*
|
|
630
355
|
* @param {any} component
|
|
631
356
|
* @param {any} css
|
|
357
|
+
* @param {boolean} [export_top_scoped_classes]
|
|
632
358
|
* @returns {void}
|
|
633
359
|
*/
|
|
634
|
-
function apply_css_definition_metadata(component, css) {
|
|
360
|
+
function apply_css_definition_metadata(component, css, export_top_scoped_classes = false) {
|
|
635
361
|
analyze_css(css);
|
|
636
362
|
|
|
637
363
|
const metadata = component.metadata || (component.metadata = { path: [] });
|
|
638
364
|
const style_classes = metadata.styleClasses || (metadata.styleClasses = new Map());
|
|
639
365
|
const top_scoped_classes = metadata.topScopedClasses || new Map();
|
|
640
|
-
const elements = collect_css_prunable_elements(component.body || []);
|
|
366
|
+
const elements = collect_css_prunable_elements(component.body || component.children || []);
|
|
367
|
+
|
|
368
|
+
const prune = () => {
|
|
369
|
+
for (const element of elements) {
|
|
370
|
+
prune_css(css, element, style_classes, top_scoped_classes);
|
|
371
|
+
}
|
|
372
|
+
};
|
|
641
373
|
|
|
642
|
-
|
|
643
|
-
|
|
374
|
+
prune();
|
|
375
|
+
|
|
376
|
+
if (export_top_scoped_classes) {
|
|
377
|
+
for (const [class_name, class_info] of top_scoped_classes) {
|
|
378
|
+
style_classes.set(class_name, class_info.selector ?? class_info);
|
|
379
|
+
}
|
|
380
|
+
prune();
|
|
644
381
|
}
|
|
645
382
|
|
|
646
383
|
if (top_scoped_classes.size > 0) {
|
|
@@ -668,8 +405,7 @@ function collect_css_prunable_elements(value, elements = []) {
|
|
|
668
405
|
if (
|
|
669
406
|
value.type === 'FunctionDeclaration' ||
|
|
670
407
|
value.type === 'FunctionExpression' ||
|
|
671
|
-
value.type === 'ArrowFunctionExpression'
|
|
672
|
-
value.type === 'Component'
|
|
408
|
+
value.type === 'ArrowFunctionExpression'
|
|
673
409
|
) {
|
|
674
410
|
return elements;
|
|
675
411
|
}
|
|
@@ -690,127 +426,6 @@ function collect_css_prunable_elements(value, elements = []) {
|
|
|
690
426
|
return elements;
|
|
691
427
|
}
|
|
692
428
|
|
|
693
|
-
/**
|
|
694
|
-
* Detect a top-level `"use server"` directive. Used by platforms whose
|
|
695
|
-
* validation rule requires the directive to enable top-level `await`
|
|
696
|
-
* in components (currently: Preact).
|
|
697
|
-
*
|
|
698
|
-
* @param {AST.Program} program
|
|
699
|
-
* @returns {boolean}
|
|
700
|
-
*/
|
|
701
|
-
function has_use_server_directive(program) {
|
|
702
|
-
for (const statement of program.body || []) {
|
|
703
|
-
const directive = /** @type {any} */ (statement).directive;
|
|
704
|
-
|
|
705
|
-
if (directive === 'use server') {
|
|
706
|
-
return true;
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
if (
|
|
710
|
-
statement.type === 'ExpressionStatement' &&
|
|
711
|
-
statement.expression?.type === 'Literal' &&
|
|
712
|
-
statement.expression.value === 'use server'
|
|
713
|
-
) {
|
|
714
|
-
return true;
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
if (directive == null) {
|
|
718
|
-
break;
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
return false;
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
/**
|
|
726
|
-
* Lower a TSRX `Component` node into the shared function-declaration form used
|
|
727
|
-
* by the default JSX targets. Platform hooks can reuse this helper and wrap the
|
|
728
|
-
* resulting function in another declaration shape without reimplementing
|
|
729
|
-
* component body lowering, lazy destructuring, helper generation, or top-level
|
|
730
|
-
* await handling.
|
|
731
|
-
*
|
|
732
|
-
* @param {any} component
|
|
733
|
-
* @param {TransformContext} transform_context
|
|
734
|
-
* @param {{ base_name: string, next_id: number, helpers: AST.FunctionDeclaration[], statics: any[] }} [walk_helper_state]
|
|
735
|
-
* @returns {AST.FunctionDeclaration | AST.FunctionExpression | AST.ArrowFunctionExpression}
|
|
736
|
-
*/
|
|
737
|
-
export function component_to_function_declaration(component, transform_context, walk_helper_state) {
|
|
738
|
-
const helper_state = walk_helper_state || create_helper_state(component.id?.name || 'Component');
|
|
739
|
-
const params = component.params || [];
|
|
740
|
-
const body = /** @type {any[]} */ (component.body || []);
|
|
741
|
-
const is_async_component =
|
|
742
|
-
!!component?.metadata?.contains_top_level_await ||
|
|
743
|
-
find_first_top_level_await_in_component_body(body) !== null;
|
|
744
|
-
|
|
745
|
-
// Collect param bindings from original patterns (lazy patterns still intact).
|
|
746
|
-
const param_bindings = collect_param_bindings(params);
|
|
747
|
-
|
|
748
|
-
// Save and set context for this component scope
|
|
749
|
-
const saved_helper_state = transform_context.helper_state;
|
|
750
|
-
const saved_bindings = transform_context.available_bindings;
|
|
751
|
-
transform_context.helper_state = helper_state;
|
|
752
|
-
transform_context.available_bindings = new Map(param_bindings);
|
|
753
|
-
|
|
754
|
-
const body_statements = build_component_statements(body, transform_context);
|
|
755
|
-
const body_block = b.block(body_statements);
|
|
756
|
-
|
|
757
|
-
/** @type {AST.FunctionDeclaration | AST.FunctionExpression | AST.ArrowFunctionExpression} */
|
|
758
|
-
let fn;
|
|
759
|
-
|
|
760
|
-
if (component.id) {
|
|
761
|
-
fn = b.function_declaration(
|
|
762
|
-
component.id,
|
|
763
|
-
params,
|
|
764
|
-
body_block,
|
|
765
|
-
is_async_component,
|
|
766
|
-
component.typeParameters,
|
|
767
|
-
);
|
|
768
|
-
} else if (component.metadata?.arrow) {
|
|
769
|
-
fn = b.arrow(params, body_block, is_async_component, component.typeParameters);
|
|
770
|
-
} else {
|
|
771
|
-
fn = b.function(null, params, body_block, is_async_component, component.typeParameters);
|
|
772
|
-
}
|
|
773
|
-
/** @type {any} */ (fn.metadata).is_component = true;
|
|
774
|
-
|
|
775
|
-
// `preallocate_lazy_ids` stamped `has_lazy_descendants` on the source
|
|
776
|
-
// `Component` node; the freshly-built `fn` shares the same params/body
|
|
777
|
-
// subtree, so the flag is equally applicable. Propagating it lets
|
|
778
|
-
// `apply_lazy_transforms` honor its constant-time early-return path.
|
|
779
|
-
if (/** @type {any} */ (component).metadata?.has_lazy_descendants) {
|
|
780
|
-
/** @type {any} */ (fn.metadata).has_lazy_descendants = true;
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
// Apply lazy `&{}` / `&[]` rewrites end-to-end: the function-handler in
|
|
784
|
-
// `apply_lazy_transforms` collects param bindings, merges with body bindings
|
|
785
|
-
// discovered by the BlockStatement handler, replaces lazy params with their
|
|
786
|
-
// `__lazyN` ids, and rewrites every reference. Constant-time fast-path for
|
|
787
|
-
// functions whose subtrees contain no lazy patterns (flagged ahead of time
|
|
788
|
-
// by `preallocate_lazy_ids`). In type-only mode the rewrite is skipped so
|
|
789
|
-
// destructuring patterns survive into the virtual TSX and TypeScript can
|
|
790
|
-
// flow real types.
|
|
791
|
-
if (!transform_context.typeOnly) {
|
|
792
|
-
fn = /** @type {typeof fn} */ (apply_lazy_transforms(fn, new Map()));
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
// Restore context
|
|
796
|
-
transform_context.helper_state = saved_helper_state;
|
|
797
|
-
transform_context.available_bindings = saved_bindings;
|
|
798
|
-
|
|
799
|
-
const fn_metadata = /** @type {any} */ (fn.metadata);
|
|
800
|
-
fn_metadata.generated_helpers = helper_state.helpers;
|
|
801
|
-
fn_metadata.generated_statics = helper_state.statics;
|
|
802
|
-
|
|
803
|
-
if (fn.type === 'FunctionDeclaration' && fn.id) {
|
|
804
|
-
fn.id.metadata = /** @type {AST.Identifier['metadata']} */ ({
|
|
805
|
-
...fn.id.metadata,
|
|
806
|
-
is_component: true,
|
|
807
|
-
});
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
setLocation(fn, /** @type {any} */ (component), true);
|
|
811
|
-
return fn;
|
|
812
|
-
}
|
|
813
|
-
|
|
814
429
|
/**
|
|
815
430
|
* @param {any[]} body_nodes
|
|
816
431
|
* @param {TransformContext} transform_context
|
|
@@ -842,16 +457,12 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
842
457
|
// any JSX is constructed, and every JSX child would observe the final
|
|
843
458
|
// state of mutable variables.
|
|
844
459
|
const interleaved = is_interleaved_body(body_nodes);
|
|
845
|
-
const capture_static_early_return_nodes =
|
|
846
|
-
!interleaved &&
|
|
847
|
-
!transform_context.platform.hooks?.isTopLevelSetupCall &&
|
|
848
|
-
body_nodes.filter(is_returning_if_statement).length > 1;
|
|
849
460
|
let capture_index = 0;
|
|
850
461
|
|
|
851
462
|
for (let i = 0; i < body_nodes.length; i += 1) {
|
|
852
463
|
const child = body_nodes[i];
|
|
853
464
|
|
|
854
|
-
if (
|
|
465
|
+
if (is_loop_skip_return_statement(child)) {
|
|
855
466
|
statements.push(create_component_return_statement(render_nodes, child));
|
|
856
467
|
render_nodes.length = 0;
|
|
857
468
|
has_terminal_return = true;
|
|
@@ -864,92 +475,17 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
864
475
|
continue;
|
|
865
476
|
}
|
|
866
477
|
|
|
867
|
-
if (
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
transform_context,
|
|
876
|
-
true,
|
|
877
|
-
);
|
|
878
|
-
|
|
879
|
-
if (capture_static_early_return_nodes) {
|
|
880
|
-
capture_index = capture_static_early_return_render_nodes(
|
|
881
|
-
render_nodes,
|
|
882
|
-
statements,
|
|
883
|
-
capture_index,
|
|
884
|
-
transform_context,
|
|
885
|
-
);
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
if (branch_has_hooks || continuation_has_hooks) {
|
|
889
|
-
if (transform_context.platform.hooks?.isTopLevelSetupCall) {
|
|
890
|
-
statements.push(
|
|
891
|
-
...create_setup_once_helper_split_returning_if_statements(
|
|
892
|
-
child,
|
|
893
|
-
body_nodes.slice(i + 1),
|
|
894
|
-
render_nodes,
|
|
895
|
-
transform_context,
|
|
896
|
-
),
|
|
897
|
-
);
|
|
898
|
-
transform_context.available_bindings = saved_bindings;
|
|
899
|
-
return statements;
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
statements.push(
|
|
903
|
-
...create_component_helper_split_returning_if_statements(
|
|
904
|
-
child,
|
|
905
|
-
body_nodes.slice(i + 1),
|
|
906
|
-
render_nodes,
|
|
907
|
-
transform_context,
|
|
908
|
-
),
|
|
478
|
+
if (is_loop_skip_if_statement(child)) {
|
|
479
|
+
if (transform_context.platform.hooks?.isTopLevelSetupCall) {
|
|
480
|
+
const continuation_body = body_nodes.slice(i + 1);
|
|
481
|
+
const continuation_has_setup_statements = continuation_body.some(
|
|
482
|
+
(node) =>
|
|
483
|
+
!is_loop_skip_return_statement(node) &&
|
|
484
|
+
!is_loop_skip_if_statement(node) &&
|
|
485
|
+
!is_jsx_child(node),
|
|
909
486
|
);
|
|
910
|
-
transform_context.available_bindings = saved_bindings;
|
|
911
|
-
return statements;
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
if (is_lone_return_if_statement(child)) {
|
|
915
|
-
// On platforms where setup runs once (Vue Vapor), an early
|
|
916
|
-
// `if (cond) return;` placed at setup level is non-reactive:
|
|
917
|
-
// `cond` is evaluated only when setup runs and never again.
|
|
918
|
-
// Inline the rest of the body as a render-time ternary so the
|
|
919
|
-
// conditional re-evaluates when `cond` changes after mount.
|
|
920
|
-
// React/Preact/Solid re-run the component body on every render,
|
|
921
|
-
// so the old setup-time early return is already reactive there
|
|
922
|
-
// and we keep it to avoid gratuitous output changes.
|
|
923
|
-
if (transform_context.platform.hooks?.isTopLevelSetupCall) {
|
|
924
|
-
const continuation_body = body_nodes.slice(i + 1);
|
|
925
|
-
|
|
926
|
-
// Render-time inlining unconditionally lifts continuation
|
|
927
|
-
// statements (provide/watch/declarations/etc.) into the
|
|
928
|
-
// parent setup, which would run them regardless of the
|
|
929
|
-
// early-return condition — wrong when the user wrote them
|
|
930
|
-
// after `if (cond) return;`. Fall back to helper-split if
|
|
931
|
-
// the continuation has any non-render statements so they
|
|
932
|
-
// stay scoped to the helper's lifecycle.
|
|
933
|
-
const continuation_has_setup_statements = continuation_body.some(
|
|
934
|
-
(node) =>
|
|
935
|
-
!is_bare_return_statement(node) &&
|
|
936
|
-
!is_returning_if_statement(node) &&
|
|
937
|
-
!is_jsx_child(node),
|
|
938
|
-
);
|
|
939
|
-
|
|
940
|
-
if (continuation_has_setup_statements) {
|
|
941
|
-
statements.push(
|
|
942
|
-
...create_setup_once_helper_split_returning_if_statements(
|
|
943
|
-
child,
|
|
944
|
-
continuation_body,
|
|
945
|
-
render_nodes,
|
|
946
|
-
transform_context,
|
|
947
|
-
),
|
|
948
|
-
);
|
|
949
|
-
transform_context.available_bindings = saved_bindings;
|
|
950
|
-
return statements;
|
|
951
|
-
}
|
|
952
487
|
|
|
488
|
+
if (!continuation_has_setup_statements) {
|
|
953
489
|
const continuation_statements = build_render_statements(
|
|
954
490
|
continuation_body,
|
|
955
491
|
false,
|
|
@@ -979,13 +515,10 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
979
515
|
|
|
980
516
|
break;
|
|
981
517
|
}
|
|
982
|
-
|
|
983
|
-
statements.push(create_component_lone_return_if_statement(child, render_nodes));
|
|
984
|
-
continue;
|
|
985
518
|
}
|
|
986
519
|
|
|
987
520
|
statements.push(
|
|
988
|
-
|
|
521
|
+
create_component_loop_skip_if_statement(child, render_nodes, transform_context),
|
|
989
522
|
);
|
|
990
523
|
continue;
|
|
991
524
|
}
|
|
@@ -993,6 +526,7 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
993
526
|
if (
|
|
994
527
|
child.type === 'ForOfStatement' &&
|
|
995
528
|
!child.await &&
|
|
529
|
+
should_extract_hook_helpers(transform_context) &&
|
|
996
530
|
!transform_context.platform.hooks?.isTopLevelSetupCall &&
|
|
997
531
|
!transform_context.platform.hooks?.controlFlow?.forOf &&
|
|
998
532
|
body_contains_top_level_hook_call(
|
|
@@ -1047,19 +581,60 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
1047
581
|
}
|
|
1048
582
|
|
|
1049
583
|
/**
|
|
1050
|
-
* React-specific wrapper around the core `isInterleavedBody` helper that
|
|
1051
|
-
* ignores bare `return` / lone return-if statements. Those are rewriting
|
|
1052
|
-
* signals rather than user-visible side effects, so JSX children around
|
|
1053
|
-
* them don't need capturing.
|
|
1054
|
-
*
|
|
1055
584
|
* @param {any[]} body_nodes
|
|
1056
585
|
* @returns {boolean}
|
|
1057
586
|
*/
|
|
1058
587
|
function is_interleaved_body(body_nodes) {
|
|
1059
|
-
|
|
1060
|
-
|
|
588
|
+
return is_interleaved_body_core(body_nodes, is_jsx_child);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* @param {any} node
|
|
593
|
+
* @param {TransformContext} transform_context
|
|
594
|
+
* @returns {boolean}
|
|
595
|
+
*/
|
|
596
|
+
function function_needs_component_body_hook_split(node, transform_context) {
|
|
597
|
+
return (
|
|
598
|
+
transform_context.platform.hooks?.componentBodyHookHelpers === true &&
|
|
599
|
+
node.body?.type === 'BlockStatement' &&
|
|
600
|
+
find_component_body_hook_split_index(node.body.body || [], transform_context) !== -1
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* @param {any} node
|
|
606
|
+
* @param {TransformContext} transform_context
|
|
607
|
+
* @returns {void}
|
|
608
|
+
*/
|
|
609
|
+
function rewrite_component_body_conditional_hook_splits(node, transform_context) {
|
|
610
|
+
if (
|
|
611
|
+
transform_context.platform.hooks?.componentBodyHookHelpers !== true ||
|
|
612
|
+
!should_extract_hook_helpers(transform_context) ||
|
|
613
|
+
node.body?.type !== 'BlockStatement'
|
|
614
|
+
) {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const body = node.body.body || [];
|
|
619
|
+
const split_index = find_component_body_hook_split_index(body, transform_context);
|
|
620
|
+
if (split_index === -1) {
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const split_statement = body[split_index];
|
|
625
|
+
const continuation_body = body.slice(split_index + 1);
|
|
626
|
+
const helper = create_hook_safe_helper(
|
|
627
|
+
continuation_body,
|
|
628
|
+
undefined,
|
|
629
|
+
get_body_source_node(continuation_body) || split_statement,
|
|
630
|
+
transform_context,
|
|
1061
631
|
);
|
|
1062
|
-
|
|
632
|
+
|
|
633
|
+
node.body.body = [
|
|
634
|
+
...body.slice(0, split_index + 1),
|
|
635
|
+
...helper.setup_statements,
|
|
636
|
+
set_loc(b.return(helper.component_element), split_statement),
|
|
637
|
+
];
|
|
1063
638
|
}
|
|
1064
639
|
|
|
1065
640
|
/**
|
|
@@ -1067,9 +642,9 @@ function is_interleaved_body(body_nodes) {
|
|
|
1067
642
|
* @param {TransformContext} transform_context
|
|
1068
643
|
* @returns {number}
|
|
1069
644
|
*/
|
|
1070
|
-
function
|
|
645
|
+
function find_component_body_hook_split_index(body_nodes, transform_context) {
|
|
1071
646
|
for (let i = 0; i < body_nodes.length; i += 1) {
|
|
1072
|
-
if (!
|
|
647
|
+
if (!is_component_body_conditional_return_statement(body_nodes[i])) {
|
|
1073
648
|
continue;
|
|
1074
649
|
}
|
|
1075
650
|
|
|
@@ -1081,53 +656,117 @@ function find_hook_safe_split_index(body_nodes, transform_context) {
|
|
|
1081
656
|
return -1;
|
|
1082
657
|
}
|
|
1083
658
|
|
|
1084
|
-
/**
|
|
1085
|
-
* @param {any[]} body_nodes
|
|
1086
|
-
* @param {TransformContext} transform_context
|
|
1087
|
-
* @param {boolean} include_platform_setup
|
|
1088
|
-
* @returns {boolean}
|
|
1089
|
-
*/
|
|
1090
|
-
function body_contains_top_level_hook_call(
|
|
1091
|
-
body_nodes,
|
|
1092
|
-
transform_context,
|
|
1093
|
-
include_platform_setup = false,
|
|
1094
|
-
) {
|
|
1095
|
-
return body_nodes.some((node) =>
|
|
1096
|
-
statement_contains_top_level_hook_call(node, transform_context, include_platform_setup),
|
|
1097
|
-
);
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
659
|
/**
|
|
1101
660
|
* @param {any} node
|
|
1102
|
-
* @param {TransformContext} transform_context
|
|
1103
|
-
* @param {boolean} include_platform_setup
|
|
1104
661
|
* @returns {boolean}
|
|
1105
662
|
*/
|
|
1106
|
-
function
|
|
1107
|
-
|
|
663
|
+
function is_component_body_conditional_return_statement(node) {
|
|
664
|
+
if (node?.type !== 'IfStatement') {
|
|
665
|
+
return false;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
return (
|
|
669
|
+
statement_contains_component_body_return(node.consequent) ||
|
|
670
|
+
statement_contains_component_body_return(node.alternate)
|
|
671
|
+
);
|
|
1108
672
|
}
|
|
1109
673
|
|
|
1110
674
|
/**
|
|
1111
675
|
* @param {any} node
|
|
1112
|
-
* @param {boolean} inside_nested_function
|
|
1113
|
-
* @param {TransformContext} transform_context
|
|
1114
|
-
* @param {boolean} include_platform_setup
|
|
1115
676
|
* @returns {boolean}
|
|
1116
677
|
*/
|
|
1117
|
-
function
|
|
1118
|
-
node,
|
|
1119
|
-
inside_nested_function,
|
|
1120
|
-
transform_context,
|
|
1121
|
-
include_platform_setup,
|
|
1122
|
-
) {
|
|
678
|
+
function statement_contains_component_body_return(node) {
|
|
1123
679
|
if (!node || typeof node !== 'object') {
|
|
1124
680
|
return false;
|
|
1125
681
|
}
|
|
1126
682
|
|
|
1127
|
-
if (
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
683
|
+
if (node.type === 'ReturnStatement') {
|
|
684
|
+
return true;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
if (is_function_or_class_boundary(node)) {
|
|
688
|
+
return false;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (Array.isArray(node)) {
|
|
692
|
+
return node.some(statement_contains_component_body_return);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
if (node.type === 'BlockStatement') {
|
|
696
|
+
return (node.body || []).some(statement_contains_component_body_return);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
if (node.type === 'IfStatement') {
|
|
700
|
+
return (
|
|
701
|
+
statement_contains_component_body_return(node.consequent) ||
|
|
702
|
+
statement_contains_component_body_return(node.alternate)
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
if (node.type === 'SwitchStatement') {
|
|
707
|
+
return (node.cases || []).some((/** @type {any} */ switch_case) =>
|
|
708
|
+
statement_contains_component_body_return(switch_case.consequent || []),
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (node.type === 'TryStatement') {
|
|
713
|
+
return (
|
|
714
|
+
statement_contains_component_body_return(node.block) ||
|
|
715
|
+
statement_contains_component_body_return(node.handler?.body) ||
|
|
716
|
+
statement_contains_component_body_return(node.finalizer)
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
return false;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* @param {any[]} body_nodes
|
|
725
|
+
* @param {TransformContext} transform_context
|
|
726
|
+
* @param {boolean} include_platform_setup
|
|
727
|
+
* @returns {boolean}
|
|
728
|
+
*/
|
|
729
|
+
function body_contains_top_level_hook_call(
|
|
730
|
+
body_nodes,
|
|
731
|
+
transform_context,
|
|
732
|
+
include_platform_setup = false,
|
|
733
|
+
) {
|
|
734
|
+
return body_nodes.some((node) =>
|
|
735
|
+
statement_contains_top_level_hook_call(node, transform_context, include_platform_setup),
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* @param {any} node
|
|
741
|
+
* @param {TransformContext} transform_context
|
|
742
|
+
* @param {boolean} include_platform_setup
|
|
743
|
+
* @returns {boolean}
|
|
744
|
+
*/
|
|
745
|
+
function statement_contains_top_level_hook_call(node, transform_context, include_platform_setup) {
|
|
746
|
+
return node_contains_top_level_hook_call(node, false, transform_context, include_platform_setup);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* @param {any} node
|
|
751
|
+
* @param {boolean} inside_nested_function
|
|
752
|
+
* @param {TransformContext} transform_context
|
|
753
|
+
* @param {boolean} include_platform_setup
|
|
754
|
+
* @returns {boolean}
|
|
755
|
+
*/
|
|
756
|
+
function node_contains_top_level_hook_call(
|
|
757
|
+
node,
|
|
758
|
+
inside_nested_function,
|
|
759
|
+
transform_context,
|
|
760
|
+
include_platform_setup,
|
|
761
|
+
) {
|
|
762
|
+
if (!node || typeof node !== 'object') {
|
|
763
|
+
return false;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
if (
|
|
767
|
+
inside_nested_function &&
|
|
768
|
+
(node.type === 'FunctionDeclaration' ||
|
|
769
|
+
node.type === 'FunctionExpression' ||
|
|
1131
770
|
node.type === 'ArrowFunctionExpression')
|
|
1132
771
|
) {
|
|
1133
772
|
return false;
|
|
@@ -1271,94 +910,897 @@ function create_helper_component_element(helper_id, bindings, source_node, mappi
|
|
|
1271
910
|
),
|
|
1272
911
|
);
|
|
1273
912
|
|
|
1274
|
-
const opening_element = b.jsx_opening_element(
|
|
1275
|
-
identifier_to_jsx_name(clone_identifier(helper_id)),
|
|
1276
|
-
attributes,
|
|
1277
|
-
true,
|
|
1278
|
-
);
|
|
1279
|
-
const element = b.jsx_element_fresh(
|
|
1280
|
-
mapWrapper ? set_loc(opening_element, source_node) : opening_element,
|
|
913
|
+
const opening_element = b.jsx_opening_element(
|
|
914
|
+
identifier_to_jsx_name(clone_identifier(helper_id)),
|
|
915
|
+
attributes,
|
|
916
|
+
true,
|
|
917
|
+
);
|
|
918
|
+
const element = b.jsx_element_fresh(
|
|
919
|
+
mapWrapper ? set_loc(opening_element, source_node) : opening_element,
|
|
920
|
+
);
|
|
921
|
+
|
|
922
|
+
return mapWrapper ? set_loc(element, source_node) : element;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
/**
|
|
926
|
+
* @param {{ base_name: string, next_id: number, helpers: any[], statics: any[] }} helper_state
|
|
927
|
+
* @param {string} suffix
|
|
928
|
+
* @returns {string}
|
|
929
|
+
*/
|
|
930
|
+
function create_helper_name(helper_state, suffix) {
|
|
931
|
+
helper_state.next_id += 1;
|
|
932
|
+
return `${helper_state.base_name}__${suffix}${helper_state.next_id}`;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* @param {string} base_name
|
|
937
|
+
* @returns {{ base_name: string, next_id: number, helpers: any[], statics: any[] }}
|
|
938
|
+
*/
|
|
939
|
+
function create_helper_state(base_name) {
|
|
940
|
+
return {
|
|
941
|
+
base_name,
|
|
942
|
+
next_id: 0,
|
|
943
|
+
helpers: [],
|
|
944
|
+
statics: [],
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* @param {any} node
|
|
950
|
+
* @param {{ next: (state?: TransformContext) => any, state: TransformContext }} context
|
|
951
|
+
* @returns {any}
|
|
952
|
+
*/
|
|
953
|
+
function collect_native_function_tsrx_metadata(node, { next, state }) {
|
|
954
|
+
if (!function_has_native_tsrx_return(node)) {
|
|
955
|
+
return next(state);
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
node.metadata = {
|
|
959
|
+
...(node.metadata || {}),
|
|
960
|
+
native_tsrx_function: true,
|
|
961
|
+
};
|
|
962
|
+
|
|
963
|
+
return next(state);
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
/**
|
|
967
|
+
* @param {any} node
|
|
968
|
+
* @param {{ next: () => any, state: TransformContext, path: AST.Node[] }} context
|
|
969
|
+
* @returns {any}
|
|
970
|
+
*/
|
|
971
|
+
function transform_function(node, context) {
|
|
972
|
+
if (node.metadata?.native_tsrx_function || function_has_native_tsrx_return(node)) {
|
|
973
|
+
return transform_native_tsrx_function(node, context);
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
return transform_function_with_hook_helpers(node, context);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* @param {any} node
|
|
981
|
+
* @param {{ next: () => any, state: TransformContext, path: AST.Node[] }} context
|
|
982
|
+
* @returns {any}
|
|
983
|
+
*/
|
|
984
|
+
function transform_native_tsrx_function(node, { next, state, path }) {
|
|
985
|
+
const helper_state =
|
|
986
|
+
state.helper_state || create_helper_state(get_function_helper_base_name(node, path));
|
|
987
|
+
const saved_helper_state = state.helper_state;
|
|
988
|
+
const saved_bindings = state.available_bindings;
|
|
989
|
+
const saved_hook_helpers_enabled = state.hook_helpers_enabled;
|
|
990
|
+
|
|
991
|
+
state.helper_state = helper_state;
|
|
992
|
+
state.hook_helpers_enabled = is_uppercase_function_like(node, path);
|
|
993
|
+
state.available_bindings = merge_binding_maps(
|
|
994
|
+
saved_bindings,
|
|
995
|
+
collect_function_scope_bindings(node),
|
|
996
|
+
);
|
|
997
|
+
|
|
998
|
+
validate_native_tsrx_function_await(node, state);
|
|
999
|
+
expand_native_tsrx_function_returns(node, state);
|
|
1000
|
+
rewrite_component_body_conditional_hook_splits(node, state);
|
|
1001
|
+
|
|
1002
|
+
const inner = /** @type {any} */ (next() ?? node);
|
|
1003
|
+
|
|
1004
|
+
state.helper_state = saved_helper_state;
|
|
1005
|
+
state.available_bindings = saved_bindings;
|
|
1006
|
+
state.hook_helpers_enabled = saved_hook_helpers_enabled;
|
|
1007
|
+
|
|
1008
|
+
ensure_function_metadata(inner, { next: () => inner });
|
|
1009
|
+
inner.metadata = {
|
|
1010
|
+
...(inner.metadata || {}),
|
|
1011
|
+
native_tsrx_function: true,
|
|
1012
|
+
};
|
|
1013
|
+
if (!saved_helper_state && (helper_state.helpers.length || helper_state.statics.length)) {
|
|
1014
|
+
inner.metadata.generated_helpers = helper_state.helpers;
|
|
1015
|
+
inner.metadata.generated_statics = helper_state.statics;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
const wrapped = state.platform.hooks?.wrapNativeFunctionComponent?.(inner, state, path);
|
|
1019
|
+
if (wrapped) {
|
|
1020
|
+
return wrapped;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
return inner;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/**
|
|
1027
|
+
* @param {any} node
|
|
1028
|
+
* @param {TransformContext} transform_context
|
|
1029
|
+
* @returns {void}
|
|
1030
|
+
*/
|
|
1031
|
+
function validate_native_tsrx_function_await(node, transform_context) {
|
|
1032
|
+
const await_node = find_first_top_level_await_in_native_tsrx_function(node);
|
|
1033
|
+
if (!await_node) {
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
const validator = transform_context.platform.hooks?.validateComponentAwait;
|
|
1038
|
+
if (validator) {
|
|
1039
|
+
validator(await_node, node, transform_context, false, transform_context.source || '');
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
if (transform_context.platform.validation.requireUseServerForAwait) {
|
|
1044
|
+
error(
|
|
1045
|
+
'Top-level `await` in TSRX functions requires a module-level `"use server"` directive.',
|
|
1046
|
+
transform_context.filename,
|
|
1047
|
+
await_node,
|
|
1048
|
+
transform_context.errors,
|
|
1049
|
+
transform_context.comments,
|
|
1050
|
+
);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/**
|
|
1055
|
+
* @param {any} node
|
|
1056
|
+
* @returns {any | null}
|
|
1057
|
+
*/
|
|
1058
|
+
function find_first_top_level_await_in_native_tsrx_function(node) {
|
|
1059
|
+
if (
|
|
1060
|
+
node.type === 'ArrowFunctionExpression' &&
|
|
1061
|
+
node.body?.type !== 'BlockStatement' &&
|
|
1062
|
+
node_contains_native_tsrx_template(node.body)
|
|
1063
|
+
) {
|
|
1064
|
+
return find_first_top_level_await(node.body, false);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
const body = node.body?.type === 'BlockStatement' ? node.body.body || [] : [];
|
|
1068
|
+
return find_first_top_level_await_in_native_tsrx_statements(body);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/**
|
|
1072
|
+
* @param {any[]} statements
|
|
1073
|
+
* @returns {any | null}
|
|
1074
|
+
*/
|
|
1075
|
+
function find_first_top_level_await_in_native_tsrx_statements(statements) {
|
|
1076
|
+
for (const statement of statements) {
|
|
1077
|
+
const found = find_first_top_level_await_in_native_tsrx_statement(statement);
|
|
1078
|
+
if (found) return found;
|
|
1079
|
+
}
|
|
1080
|
+
return null;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
/**
|
|
1084
|
+
* @param {any} statement
|
|
1085
|
+
* @returns {any | null}
|
|
1086
|
+
*/
|
|
1087
|
+
function find_first_top_level_await_in_native_tsrx_statement(statement) {
|
|
1088
|
+
if (!statement || typeof statement !== 'object') return null;
|
|
1089
|
+
|
|
1090
|
+
if (statement.type === 'ReturnStatement' && statement.argument?.type === 'Tsrx') {
|
|
1091
|
+
return find_first_top_level_await_in_tsrx_function_body(statement.argument.children || []);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
if (
|
|
1095
|
+
statement.type === 'ReturnStatement' &&
|
|
1096
|
+
node_contains_native_tsrx_template(statement.argument)
|
|
1097
|
+
) {
|
|
1098
|
+
return find_first_top_level_await(statement.argument, false);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
if (is_function_or_class_boundary(statement)) {
|
|
1102
|
+
return null;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
if (statement.type === 'BlockStatement') {
|
|
1106
|
+
return find_first_top_level_await_in_native_tsrx_statements(statement.body || []);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
if (statement.type === 'IfStatement') {
|
|
1110
|
+
return (
|
|
1111
|
+
find_first_top_level_await_in_native_tsrx_statement(statement.consequent) ||
|
|
1112
|
+
find_first_top_level_await_in_native_tsrx_statement(statement.alternate)
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
if (statement.type === 'SwitchStatement') {
|
|
1117
|
+
for (const switch_case of statement.cases || []) {
|
|
1118
|
+
const found = find_first_top_level_await_in_native_tsrx_statements(
|
|
1119
|
+
switch_case.consequent || [],
|
|
1120
|
+
);
|
|
1121
|
+
if (found) return found;
|
|
1122
|
+
}
|
|
1123
|
+
return null;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
if (statement.type === 'TryStatement') {
|
|
1127
|
+
return (
|
|
1128
|
+
find_first_top_level_await_in_native_tsrx_statement(statement.block) ||
|
|
1129
|
+
find_first_top_level_await_in_native_tsrx_statement(statement.handler?.body) ||
|
|
1130
|
+
find_first_top_level_await_in_native_tsrx_statement(statement.finalizer)
|
|
1131
|
+
);
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
return null;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* @param {any} node
|
|
1139
|
+
* @param {{ next: () => any, state: TransformContext, path: AST.Node[] }} context
|
|
1140
|
+
* @returns {any}
|
|
1141
|
+
*/
|
|
1142
|
+
function transform_function_with_hook_helpers(node, { next, state, path }) {
|
|
1143
|
+
const has_hook_bearing_tsrx = function_contains_hook_bearing_tsrx(node, state);
|
|
1144
|
+
const has_component_body_hook_split = function_needs_component_body_hook_split(node, state);
|
|
1145
|
+
if (
|
|
1146
|
+
state.helper_state ||
|
|
1147
|
+
!is_uppercase_function_like(node, path) ||
|
|
1148
|
+
(!has_hook_bearing_tsrx && !has_component_body_hook_split)
|
|
1149
|
+
) {
|
|
1150
|
+
return ensure_function_metadata(node, { next });
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
const helper_state = create_helper_state(get_function_helper_base_name(node, path));
|
|
1154
|
+
const saved_helper_state = state.helper_state;
|
|
1155
|
+
const saved_bindings = state.available_bindings;
|
|
1156
|
+
const saved_hook_helpers_enabled = state.hook_helpers_enabled;
|
|
1157
|
+
|
|
1158
|
+
state.helper_state = helper_state;
|
|
1159
|
+
state.hook_helpers_enabled = true;
|
|
1160
|
+
state.available_bindings = collect_function_scope_bindings(node);
|
|
1161
|
+
|
|
1162
|
+
if (has_component_body_hook_split) {
|
|
1163
|
+
rewrite_component_body_conditional_hook_splits(node, state);
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
const inner = /** @type {any} */ (next() ?? node);
|
|
1167
|
+
|
|
1168
|
+
state.helper_state = saved_helper_state;
|
|
1169
|
+
state.available_bindings = saved_bindings;
|
|
1170
|
+
state.hook_helpers_enabled = saved_hook_helpers_enabled;
|
|
1171
|
+
|
|
1172
|
+
ensure_function_metadata(inner, { next: () => inner });
|
|
1173
|
+
if (helper_state.helpers.length || helper_state.statics.length) {
|
|
1174
|
+
inner.metadata = {
|
|
1175
|
+
...(inner.metadata || {}),
|
|
1176
|
+
generated_helpers: helper_state.helpers,
|
|
1177
|
+
generated_statics: helper_state.statics,
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
return inner;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
/**
|
|
1185
|
+
* @param {any} node
|
|
1186
|
+
* @param {AST.Node[]} [path]
|
|
1187
|
+
* @returns {string}
|
|
1188
|
+
*/
|
|
1189
|
+
function get_function_helper_base_name(node, path = []) {
|
|
1190
|
+
return get_function_like_name(node, path) || 'Tsrx';
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
/**
|
|
1194
|
+
* @param {any} node
|
|
1195
|
+
* @param {AST.Node[]} path
|
|
1196
|
+
* @returns {boolean}
|
|
1197
|
+
*/
|
|
1198
|
+
function is_uppercase_function_like(node, path) {
|
|
1199
|
+
const name = get_function_like_name(node, path);
|
|
1200
|
+
return !!(name && /^[A-Z]/.test(name));
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
/**
|
|
1204
|
+
* @param {any} node
|
|
1205
|
+
* @param {AST.Node[]} path
|
|
1206
|
+
* @returns {string | null}
|
|
1207
|
+
*/
|
|
1208
|
+
function get_function_like_name(node, path) {
|
|
1209
|
+
if (node.id?.type === 'Identifier') {
|
|
1210
|
+
return node.id.name;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
const parent = /** @type {any} */ (path.at(-1));
|
|
1214
|
+
if (!parent) return null;
|
|
1215
|
+
|
|
1216
|
+
if (parent.type === 'VariableDeclarator' && parent.init === node) {
|
|
1217
|
+
return get_static_binding_name(parent.id);
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
if (parent.type === 'Property' && parent.value === node) {
|
|
1221
|
+
return get_static_property_name(parent.key);
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
if (parent.type === 'MethodDefinition' && parent.value === node) {
|
|
1225
|
+
return get_static_property_name(parent.key);
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
if (parent.type === 'AssignmentExpression' && parent.right === node) {
|
|
1229
|
+
return get_static_binding_name(parent.left);
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
return null;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
/**
|
|
1236
|
+
* @param {any} node
|
|
1237
|
+
* @returns {string | null}
|
|
1238
|
+
*/
|
|
1239
|
+
function get_static_binding_name(node) {
|
|
1240
|
+
if (node?.type === 'Identifier') {
|
|
1241
|
+
return node.name;
|
|
1242
|
+
}
|
|
1243
|
+
if (node?.type === 'MemberExpression' && !node.computed) {
|
|
1244
|
+
return get_static_property_name(node.property);
|
|
1245
|
+
}
|
|
1246
|
+
return null;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
/**
|
|
1250
|
+
* @param {any} key
|
|
1251
|
+
* @returns {string | null}
|
|
1252
|
+
*/
|
|
1253
|
+
function get_static_property_name(key) {
|
|
1254
|
+
if (key?.type === 'Identifier') {
|
|
1255
|
+
return key.name;
|
|
1256
|
+
}
|
|
1257
|
+
if (key?.type === 'Literal' && typeof key.value === 'string') {
|
|
1258
|
+
return key.value;
|
|
1259
|
+
}
|
|
1260
|
+
return null;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
/**
|
|
1264
|
+
* @param {any} node
|
|
1265
|
+
* @returns {Map<string, AST.Identifier>}
|
|
1266
|
+
*/
|
|
1267
|
+
function collect_function_scope_bindings(node) {
|
|
1268
|
+
const bindings = collect_param_bindings(node.params || []);
|
|
1269
|
+
if (node.body?.type === 'BlockStatement') {
|
|
1270
|
+
for (const statement of node.body.body || []) {
|
|
1271
|
+
if (statement.type === 'ReturnStatement' && statement.argument?.type === 'Tsrx') {
|
|
1272
|
+
for (const child of get_tsrx_render_children(statement.argument)) {
|
|
1273
|
+
collect_statement_bindings(child, bindings);
|
|
1274
|
+
}
|
|
1275
|
+
} else {
|
|
1276
|
+
collect_statement_bindings(statement, bindings);
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
return bindings;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
/**
|
|
1284
|
+
* @param {Map<string, AST.Identifier>} outer
|
|
1285
|
+
* @param {Map<string, AST.Identifier>} inner
|
|
1286
|
+
* @returns {Map<string, AST.Identifier>}
|
|
1287
|
+
*/
|
|
1288
|
+
function merge_binding_maps(outer, inner) {
|
|
1289
|
+
const merged = new Map(outer);
|
|
1290
|
+
for (const [name, binding] of inner) {
|
|
1291
|
+
merged.set(name, binding);
|
|
1292
|
+
}
|
|
1293
|
+
return merged;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
/**
|
|
1297
|
+
* @param {any} node
|
|
1298
|
+
* @returns {boolean}
|
|
1299
|
+
*/
|
|
1300
|
+
function function_has_native_tsrx_return(node) {
|
|
1301
|
+
if (!node) return false;
|
|
1302
|
+
|
|
1303
|
+
if (node.type === 'ArrowFunctionExpression' && node.body?.type !== 'BlockStatement') {
|
|
1304
|
+
return node_contains_native_tsrx_template(node.body);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
const body = node.body?.type === 'BlockStatement' ? node.body.body : [];
|
|
1308
|
+
return statements_contain_native_tsrx_return(body);
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
/**
|
|
1312
|
+
* @param {any[]} statements
|
|
1313
|
+
* @returns {boolean}
|
|
1314
|
+
*/
|
|
1315
|
+
function statements_contain_native_tsrx_return(statements) {
|
|
1316
|
+
return statements.some((statement) => statement_contains_native_tsrx_return(statement));
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
/**
|
|
1320
|
+
* @param {any} statement
|
|
1321
|
+
* @returns {boolean}
|
|
1322
|
+
*/
|
|
1323
|
+
function statement_contains_native_tsrx_return(statement) {
|
|
1324
|
+
if (!statement || typeof statement !== 'object') return false;
|
|
1325
|
+
|
|
1326
|
+
if (statement.type === 'ReturnStatement') {
|
|
1327
|
+
return node_contains_native_tsrx_template(statement.argument);
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
if (is_function_or_class_boundary(statement)) {
|
|
1331
|
+
return false;
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
if (statement.type === 'BlockStatement') {
|
|
1335
|
+
return statements_contain_native_tsrx_return(statement.body || []);
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
if (statement.type === 'IfStatement') {
|
|
1339
|
+
return (
|
|
1340
|
+
statement_contains_native_tsrx_return(statement.consequent) ||
|
|
1341
|
+
statement_contains_native_tsrx_return(statement.alternate)
|
|
1342
|
+
);
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
if (statement.type === 'SwitchStatement') {
|
|
1346
|
+
return (statement.cases || []).some((/** @type {any} */ c) =>
|
|
1347
|
+
statements_contain_native_tsrx_return(c.consequent || []),
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
if (statement.type === 'TryStatement') {
|
|
1352
|
+
return (
|
|
1353
|
+
statement_contains_native_tsrx_return(statement.block) ||
|
|
1354
|
+
statement_contains_native_tsrx_return(statement.handler?.body) ||
|
|
1355
|
+
statement_contains_native_tsrx_return(statement.finalizer)
|
|
1356
|
+
);
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
for (const key of Object.keys(statement)) {
|
|
1360
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
1361
|
+
continue;
|
|
1362
|
+
}
|
|
1363
|
+
const value = statement[key];
|
|
1364
|
+
if (Array.isArray(value)) {
|
|
1365
|
+
if (statements_contain_native_tsrx_return(value)) return true;
|
|
1366
|
+
} else if (statement_contains_native_tsrx_return(value)) {
|
|
1367
|
+
return true;
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
return false;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
/**
|
|
1375
|
+
* @param {any} node
|
|
1376
|
+
* @returns {boolean}
|
|
1377
|
+
*/
|
|
1378
|
+
function node_contains_native_tsrx_template(node) {
|
|
1379
|
+
if (!node || typeof node !== 'object') return false;
|
|
1380
|
+
if (node.type === 'Element' || node.type === 'Tsrx') return true;
|
|
1381
|
+
|
|
1382
|
+
if (is_function_or_class_boundary(node)) {
|
|
1383
|
+
return false;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
if (Array.isArray(node)) {
|
|
1387
|
+
return node.some(node_contains_native_tsrx_template);
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
for (const key of Object.keys(node)) {
|
|
1391
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
1392
|
+
continue;
|
|
1393
|
+
}
|
|
1394
|
+
if (node_contains_native_tsrx_template(node[key])) {
|
|
1395
|
+
return true;
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
return false;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
/**
|
|
1403
|
+
* @param {any} node
|
|
1404
|
+
* @returns {any}
|
|
1405
|
+
*/
|
|
1406
|
+
function collect_tsrx_stylesheet(node) {
|
|
1407
|
+
/** @type {any[]} */
|
|
1408
|
+
const styles = [];
|
|
1409
|
+
collect_style_elements(node.children || [], styles);
|
|
1410
|
+
|
|
1411
|
+
if (styles.length === 0) return null;
|
|
1412
|
+
if (styles.length > 1) {
|
|
1413
|
+
throw new Error('TSRX fragments can only have one style tag');
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
return styles[0];
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
/**
|
|
1420
|
+
* @param {any} node
|
|
1421
|
+
* @param {TransformContext} transform_context
|
|
1422
|
+
* @returns {{ css: any, style_refs: any[] } | null}
|
|
1423
|
+
*/
|
|
1424
|
+
function prepare_tsrx_fragment_styles(node, transform_context) {
|
|
1425
|
+
const css = collect_tsrx_stylesheet(node);
|
|
1426
|
+
if (!css) return null;
|
|
1427
|
+
|
|
1428
|
+
const style_refs = collect_style_ref_attributes(node);
|
|
1429
|
+
apply_css_definition_metadata(node, css, style_refs.length > 0);
|
|
1430
|
+
transform_context.stylesheets.push(css);
|
|
1431
|
+
annotate_tsrx_with_hash(
|
|
1432
|
+
node,
|
|
1433
|
+
css.hash,
|
|
1434
|
+
transform_context.platform.jsx.classAttrName ??
|
|
1435
|
+
(transform_context.platform.jsx.rewriteClassAttr ? 'className' : 'class'),
|
|
1436
|
+
transform_context.typeOnly,
|
|
1437
|
+
);
|
|
1438
|
+
return { css, style_refs };
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
/**
|
|
1442
|
+
* @template T
|
|
1443
|
+
* @param {any} node
|
|
1444
|
+
* @param {TransformContext} transform_context
|
|
1445
|
+
* @param {(style_context: { css: any, style_refs: any[] } | null) => T} callback
|
|
1446
|
+
* @returns {T}
|
|
1447
|
+
*/
|
|
1448
|
+
function with_tsrx_fragment_styles(node, transform_context, callback) {
|
|
1449
|
+
const style_context = prepare_tsrx_fragment_styles(node, transform_context);
|
|
1450
|
+
return callback(style_context);
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
/**
|
|
1454
|
+
* @param {any} fragment
|
|
1455
|
+
* @param {{ css: any, style_refs: any[] } | null} style_context
|
|
1456
|
+
* @param {TransformContext} transform_context
|
|
1457
|
+
* @returns {AST.Statement[]}
|
|
1458
|
+
*/
|
|
1459
|
+
function create_tsrx_style_ref_setup_statements(fragment, style_context, transform_context) {
|
|
1460
|
+
if (!style_context || style_context.style_refs.length === 0) {
|
|
1461
|
+
return [];
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
return create_style_ref_setup_statements(
|
|
1465
|
+
style_context.style_refs,
|
|
1466
|
+
create_style_class_map(fragment, style_context.css),
|
|
1467
|
+
{
|
|
1468
|
+
allowMutableRefTarget: transform_context.platform.jsx.multiRefStrategy === 'array',
|
|
1469
|
+
createTempIdentifier: () =>
|
|
1470
|
+
create_generated_identifier(create_style_ref_temp_name(transform_context)),
|
|
1471
|
+
},
|
|
1472
|
+
);
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
/**
|
|
1476
|
+
* @param {TransformContext} transform_context
|
|
1477
|
+
* @returns {string}
|
|
1478
|
+
*/
|
|
1479
|
+
function create_style_ref_temp_name(transform_context) {
|
|
1480
|
+
if (transform_context.helper_state) {
|
|
1481
|
+
return create_helper_name(transform_context.helper_state, 'style_ref');
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
transform_context.local_statement_component_index += 1;
|
|
1485
|
+
return `_tsrx_style_ref_${transform_context.local_statement_component_index}`;
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
/**
|
|
1489
|
+
* @param {any} node
|
|
1490
|
+
* @param {any[]} styles
|
|
1491
|
+
* @returns {void}
|
|
1492
|
+
*/
|
|
1493
|
+
function collect_style_elements(node, styles) {
|
|
1494
|
+
if (!node || typeof node !== 'object') return;
|
|
1495
|
+
|
|
1496
|
+
if (Array.isArray(node)) {
|
|
1497
|
+
for (const child of node) {
|
|
1498
|
+
collect_style_elements(child, styles);
|
|
1499
|
+
}
|
|
1500
|
+
return;
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
if (is_style_element(node)) {
|
|
1504
|
+
const stylesheet = node.children?.find(
|
|
1505
|
+
(/** @type {any} */ child) => child.type === 'StyleSheet',
|
|
1506
|
+
);
|
|
1507
|
+
if (stylesheet) {
|
|
1508
|
+
styles.push(stylesheet);
|
|
1509
|
+
}
|
|
1510
|
+
return;
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
if (is_function_or_class_boundary(node) || node.type === 'Tsrx') {
|
|
1514
|
+
return;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
if (node.type === 'Element') {
|
|
1518
|
+
collect_style_elements(node.children || [], styles);
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
if (node.type === 'BlockStatement') {
|
|
1523
|
+
collect_style_elements(node.body || [], styles);
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
if (node.type === 'IfStatement') {
|
|
1528
|
+
collect_style_elements(node.consequent, styles);
|
|
1529
|
+
collect_style_elements(node.alternate, styles);
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
if (node.type === 'SwitchStatement') {
|
|
1534
|
+
for (const switch_case of node.cases || []) {
|
|
1535
|
+
collect_style_elements(switch_case.consequent || [], styles);
|
|
1536
|
+
}
|
|
1537
|
+
return;
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
if (node.type === 'TryStatement') {
|
|
1541
|
+
collect_style_elements(node.block, styles);
|
|
1542
|
+
collect_style_elements(node.handler?.body, styles);
|
|
1543
|
+
collect_style_elements(node.finalizer, styles);
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
/**
|
|
1548
|
+
* @param {any} node
|
|
1549
|
+
* @param {string} hash
|
|
1550
|
+
* @param {'class' | 'className'} jsx_class_attr_name
|
|
1551
|
+
* @param {boolean} preserve_style_elements
|
|
1552
|
+
* @returns {void}
|
|
1553
|
+
*/
|
|
1554
|
+
function annotate_tsrx_with_hash(node, hash, jsx_class_attr_name, preserve_style_elements) {
|
|
1555
|
+
node.children = (node.children || []).map((/** @type {any} */ statement) =>
|
|
1556
|
+
annotate_with_hash(statement, hash, jsx_class_attr_name, preserve_style_elements),
|
|
1557
|
+
);
|
|
1558
|
+
if (!preserve_style_elements) {
|
|
1559
|
+
node.children = strip_style_elements(node.children);
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
/**
|
|
1564
|
+
* @param {any} node
|
|
1565
|
+
* @returns {any}
|
|
1566
|
+
*/
|
|
1567
|
+
function strip_style_elements(node) {
|
|
1568
|
+
if (!node || typeof node !== 'object') return node;
|
|
1569
|
+
|
|
1570
|
+
if (Array.isArray(node)) {
|
|
1571
|
+
return node
|
|
1572
|
+
.filter((child) => !is_style_element(child))
|
|
1573
|
+
.map((child) => strip_style_elements(child))
|
|
1574
|
+
.filter(Boolean);
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
if (is_style_element(node)) {
|
|
1578
|
+
return null;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
if (is_function_or_class_boundary(node)) {
|
|
1582
|
+
return node;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
if (node.type === 'Element') {
|
|
1586
|
+
node.children = strip_style_elements(node.children || []);
|
|
1587
|
+
return node;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
if (node.type === 'BlockStatement') {
|
|
1591
|
+
node.body = strip_style_elements(node.body || []);
|
|
1592
|
+
return node;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
if (node.type === 'IfStatement') {
|
|
1596
|
+
node.consequent = strip_style_elements(node.consequent);
|
|
1597
|
+
if (node.alternate) node.alternate = strip_style_elements(node.alternate);
|
|
1598
|
+
return node;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
if (node.type === 'SwitchStatement') {
|
|
1602
|
+
for (const switch_case of node.cases || []) {
|
|
1603
|
+
switch_case.consequent = strip_style_elements(switch_case.consequent || []);
|
|
1604
|
+
}
|
|
1605
|
+
return node;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
if (node.type === 'TryStatement') {
|
|
1609
|
+
node.block = strip_style_elements(node.block);
|
|
1610
|
+
if (node.handler?.body) node.handler.body = strip_style_elements(node.handler.body);
|
|
1611
|
+
if (node.finalizer) node.finalizer = strip_style_elements(node.finalizer);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
return node;
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
/**
|
|
1618
|
+
* @param {any[]} path
|
|
1619
|
+
* @returns {boolean}
|
|
1620
|
+
*/
|
|
1621
|
+
function is_style_expression_position(path) {
|
|
1622
|
+
const parent = path.at(-1);
|
|
1623
|
+
return !(
|
|
1624
|
+
parent?.type === 'Element' ||
|
|
1625
|
+
parent?.type === 'Tsrx' ||
|
|
1626
|
+
parent?.type === 'Tsx' ||
|
|
1627
|
+
parent?.type === 'TsxCompat' ||
|
|
1628
|
+
parent?.type === 'BlockStatement' ||
|
|
1629
|
+
parent?.type === 'Program' ||
|
|
1630
|
+
parent?.type === 'SwitchCase'
|
|
1281
1631
|
);
|
|
1282
|
-
|
|
1283
|
-
return mapWrapper ? set_loc(element, source_node) : element;
|
|
1284
1632
|
}
|
|
1285
1633
|
|
|
1286
1634
|
/**
|
|
1287
|
-
* @param {
|
|
1288
|
-
* @param {
|
|
1289
|
-
* @returns {
|
|
1635
|
+
* @param {any} node
|
|
1636
|
+
* @param {TransformContext} transform_context
|
|
1637
|
+
* @returns {void}
|
|
1290
1638
|
*/
|
|
1291
|
-
function
|
|
1292
|
-
|
|
1293
|
-
|
|
1639
|
+
function expand_native_tsrx_function_returns(node, transform_context) {
|
|
1640
|
+
if (node.type === 'ArrowFunctionExpression' && node.body?.type === 'Tsrx') {
|
|
1641
|
+
const body = node.body;
|
|
1642
|
+
const statements = with_tsrx_fragment_styles(body, transform_context, (style_context) => {
|
|
1643
|
+
return [
|
|
1644
|
+
...create_tsrx_style_ref_setup_statements(body, style_context, transform_context),
|
|
1645
|
+
...build_render_statements(get_tsrx_render_children(body), true, transform_context),
|
|
1646
|
+
];
|
|
1647
|
+
});
|
|
1648
|
+
node.body = b.block(mark_native_pretransformed_jsx(statements), body);
|
|
1649
|
+
node.expression = false;
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
if (node.body?.type !== 'BlockStatement') {
|
|
1654
|
+
return;
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
node.body.body = expand_native_tsrx_return_statement_list(
|
|
1658
|
+
node.body.body || [],
|
|
1659
|
+
transform_context,
|
|
1660
|
+
);
|
|
1294
1661
|
}
|
|
1295
1662
|
|
|
1296
1663
|
/**
|
|
1297
|
-
* @param {
|
|
1298
|
-
* @
|
|
1664
|
+
* @param {any[]} statements
|
|
1665
|
+
* @param {TransformContext} transform_context
|
|
1666
|
+
* @returns {any[]}
|
|
1299
1667
|
*/
|
|
1300
|
-
function
|
|
1301
|
-
return
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
helpers: [],
|
|
1305
|
-
statics: [],
|
|
1306
|
-
};
|
|
1668
|
+
function expand_native_tsrx_return_statement_list(statements, transform_context) {
|
|
1669
|
+
return statements.flatMap((statement) =>
|
|
1670
|
+
expand_native_tsrx_return_statement(statement, transform_context),
|
|
1671
|
+
);
|
|
1307
1672
|
}
|
|
1308
1673
|
|
|
1309
1674
|
/**
|
|
1310
|
-
* @param {any}
|
|
1311
|
-
* @param {
|
|
1312
|
-
* @returns {any}
|
|
1675
|
+
* @param {any} statement
|
|
1676
|
+
* @param {TransformContext} transform_context
|
|
1677
|
+
* @returns {any[]}
|
|
1313
1678
|
*/
|
|
1314
|
-
function
|
|
1315
|
-
if (
|
|
1316
|
-
|
|
1679
|
+
function expand_native_tsrx_return_statement(statement, transform_context) {
|
|
1680
|
+
if (!statement || typeof statement !== 'object') return [statement];
|
|
1681
|
+
|
|
1682
|
+
if (statement.type === 'ReturnStatement' && statement.argument?.type === 'Tsrx') {
|
|
1683
|
+
const fragment = statement.argument;
|
|
1684
|
+
return with_tsrx_fragment_styles(fragment, transform_context, (style_context) => {
|
|
1685
|
+
return mark_native_pretransformed_jsx([
|
|
1686
|
+
...create_tsrx_style_ref_setup_statements(fragment, style_context, transform_context),
|
|
1687
|
+
...build_render_statements(get_tsrx_render_children(fragment), true, transform_context),
|
|
1688
|
+
]);
|
|
1689
|
+
});
|
|
1317
1690
|
}
|
|
1318
1691
|
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1692
|
+
if (is_function_or_class_boundary(statement)) {
|
|
1693
|
+
return [statement];
|
|
1694
|
+
}
|
|
1322
1695
|
|
|
1323
|
-
|
|
1324
|
-
|
|
1696
|
+
if (statement.type === 'BlockStatement') {
|
|
1697
|
+
statement.body = expand_native_tsrx_return_statement_list(
|
|
1698
|
+
statement.body || [],
|
|
1699
|
+
transform_context,
|
|
1700
|
+
);
|
|
1701
|
+
return [statement];
|
|
1702
|
+
}
|
|
1325
1703
|
|
|
1326
|
-
|
|
1704
|
+
if (statement.type === 'IfStatement') {
|
|
1705
|
+
statement.consequent = expand_embedded_native_return_statement(
|
|
1706
|
+
statement.consequent,
|
|
1707
|
+
transform_context,
|
|
1708
|
+
);
|
|
1709
|
+
if (statement.alternate) {
|
|
1710
|
+
statement.alternate = expand_embedded_native_return_statement(
|
|
1711
|
+
statement.alternate,
|
|
1712
|
+
transform_context,
|
|
1713
|
+
);
|
|
1714
|
+
}
|
|
1715
|
+
return [statement];
|
|
1716
|
+
}
|
|
1327
1717
|
|
|
1328
|
-
|
|
1329
|
-
|
|
1718
|
+
if (statement.type === 'SwitchStatement') {
|
|
1719
|
+
for (const switch_case of statement.cases || []) {
|
|
1720
|
+
switch_case.consequent = expand_native_tsrx_return_statement_list(
|
|
1721
|
+
switch_case.consequent || [],
|
|
1722
|
+
transform_context,
|
|
1723
|
+
);
|
|
1724
|
+
}
|
|
1725
|
+
return [statement];
|
|
1726
|
+
}
|
|
1330
1727
|
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1728
|
+
if (statement.type === 'TryStatement') {
|
|
1729
|
+
statement.block = expand_embedded_native_return_statement(statement.block, transform_context);
|
|
1730
|
+
if (statement.handler?.body) {
|
|
1731
|
+
statement.handler.body = expand_embedded_native_return_statement(
|
|
1732
|
+
statement.handler.body,
|
|
1733
|
+
transform_context,
|
|
1734
|
+
);
|
|
1735
|
+
}
|
|
1736
|
+
if (statement.finalizer) {
|
|
1737
|
+
statement.finalizer = expand_embedded_native_return_statement(
|
|
1738
|
+
statement.finalizer,
|
|
1739
|
+
transform_context,
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1742
|
+
return [statement];
|
|
1338
1743
|
}
|
|
1339
1744
|
|
|
1340
|
-
return
|
|
1745
|
+
return [statement];
|
|
1341
1746
|
}
|
|
1342
1747
|
|
|
1343
1748
|
/**
|
|
1344
|
-
* @param {any}
|
|
1345
|
-
* @
|
|
1749
|
+
* @param {any} statement
|
|
1750
|
+
* @param {TransformContext} transform_context
|
|
1751
|
+
* @returns {any}
|
|
1346
1752
|
*/
|
|
1347
|
-
function
|
|
1348
|
-
|
|
1349
|
-
|
|
1753
|
+
function expand_embedded_native_return_statement(statement, transform_context) {
|
|
1754
|
+
const expanded = expand_native_tsrx_return_statement(statement, transform_context);
|
|
1755
|
+
return expanded.length === 1 ? expanded[0] : b.block(expanded, statement);
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
/**
|
|
1759
|
+
* @template T
|
|
1760
|
+
* @param {T} node
|
|
1761
|
+
* @param {Set<any>} [seen]
|
|
1762
|
+
* @returns {T}
|
|
1763
|
+
*/
|
|
1764
|
+
function mark_native_pretransformed_jsx(node, seen = new Set()) {
|
|
1765
|
+
if (node == null || typeof node !== 'object' || seen.has(node)) {
|
|
1766
|
+
return node;
|
|
1767
|
+
}
|
|
1768
|
+
seen.add(node);
|
|
1769
|
+
|
|
1770
|
+
if (Array.isArray(node)) {
|
|
1771
|
+
for (const item of node) mark_native_pretransformed_jsx(item, seen);
|
|
1772
|
+
return node;
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
const as_node = /** @type {any} */ (node);
|
|
1776
|
+
if (as_node.type === 'JSXOpeningElement') {
|
|
1777
|
+
as_node.metadata = {
|
|
1778
|
+
...(as_node.metadata || {}),
|
|
1779
|
+
native_tsrx_pretransformed: true,
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
for (const key of Object.keys(as_node)) {
|
|
1784
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
1785
|
+
continue;
|
|
1786
|
+
}
|
|
1787
|
+
mark_native_pretransformed_jsx(as_node[key], seen);
|
|
1350
1788
|
}
|
|
1351
|
-
|
|
1789
|
+
|
|
1790
|
+
return node;
|
|
1352
1791
|
}
|
|
1353
1792
|
|
|
1354
1793
|
/**
|
|
1355
1794
|
* @param {any} node
|
|
1356
|
-
* @returns {
|
|
1795
|
+
* @returns {any[]}
|
|
1357
1796
|
*/
|
|
1358
|
-
function
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1797
|
+
function get_tsrx_render_children(node) {
|
|
1798
|
+
return (node.children || []).filter(
|
|
1799
|
+
(/** @type {any} */ child) =>
|
|
1800
|
+
child &&
|
|
1801
|
+
child.type !== 'EmptyStatement' &&
|
|
1802
|
+
(child.type !== 'JSXText' || child.value.trim() !== ''),
|
|
1803
|
+
);
|
|
1362
1804
|
}
|
|
1363
1805
|
|
|
1364
1806
|
/**
|
|
@@ -1387,8 +1829,7 @@ function collect_descendant_declaration_bindings(node, bindings) {
|
|
|
1387
1829
|
if (
|
|
1388
1830
|
node.type === 'FunctionDeclaration' ||
|
|
1389
1831
|
node.type === 'FunctionExpression' ||
|
|
1390
|
-
node.type === 'ArrowFunctionExpression'
|
|
1391
|
-
node.type === 'Component'
|
|
1832
|
+
node.type === 'ArrowFunctionExpression'
|
|
1392
1833
|
) {
|
|
1393
1834
|
return;
|
|
1394
1835
|
}
|
|
@@ -1438,8 +1879,7 @@ function node_contains_hook_bearing_tsrx(node, transform_context) {
|
|
|
1438
1879
|
if (
|
|
1439
1880
|
node.type === 'FunctionDeclaration' ||
|
|
1440
1881
|
node.type === 'FunctionExpression' ||
|
|
1441
|
-
node.type === 'ArrowFunctionExpression'
|
|
1442
|
-
node.type === 'Component'
|
|
1882
|
+
node.type === 'ArrowFunctionExpression'
|
|
1443
1883
|
) {
|
|
1444
1884
|
return false;
|
|
1445
1885
|
}
|
|
@@ -1464,6 +1904,14 @@ function should_use_module_scoped_hook_components(transform_context) {
|
|
|
1464
1904
|
return !!(transform_context.helper_state && transform_context.module_scoped_hook_components);
|
|
1465
1905
|
}
|
|
1466
1906
|
|
|
1907
|
+
/**
|
|
1908
|
+
* @param {TransformContext} transform_context
|
|
1909
|
+
* @returns {boolean}
|
|
1910
|
+
*/
|
|
1911
|
+
function should_extract_hook_helpers(transform_context) {
|
|
1912
|
+
return !!transform_context.hook_helpers_enabled;
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1467
1915
|
/**
|
|
1468
1916
|
* @param {AST.Identifier} helper_id
|
|
1469
1917
|
* @param {TransformContext} transform_context
|
|
@@ -1471,7 +1919,7 @@ function should_use_module_scoped_hook_components(transform_context) {
|
|
|
1471
1919
|
*/
|
|
1472
1920
|
function create_module_scoped_hook_component_id(helper_id, transform_context) {
|
|
1473
1921
|
return create_generated_identifier(
|
|
1474
|
-
`${transform_context.helper_state?.base_name || '
|
|
1922
|
+
`${transform_context.helper_state?.base_name || 'Tsrx'}__${helper_id.name}`,
|
|
1475
1923
|
);
|
|
1476
1924
|
}
|
|
1477
1925
|
|
|
@@ -1670,59 +2118,6 @@ function is_bare_component_invocation(node) {
|
|
|
1670
2118
|
return is_component_jsx_name(opening.name);
|
|
1671
2119
|
}
|
|
1672
2120
|
|
|
1673
|
-
/**
|
|
1674
|
-
* Static JSX that appears before multiple early-return guards is otherwise
|
|
1675
|
-
* cloned into every generated return. Capture it once at its source position
|
|
1676
|
-
* and reuse the reference, matching the interleaved-statement capture path
|
|
1677
|
-
* without moving dynamic render-time expressions across guards.
|
|
1678
|
-
*
|
|
1679
|
-
* @param {any[]} render_nodes
|
|
1680
|
-
* @param {any[]} statements
|
|
1681
|
-
* @param {number} capture_index
|
|
1682
|
-
* @param {TransformContext} transform_context
|
|
1683
|
-
* @returns {number}
|
|
1684
|
-
*/
|
|
1685
|
-
function capture_static_early_return_render_nodes(
|
|
1686
|
-
render_nodes,
|
|
1687
|
-
statements,
|
|
1688
|
-
capture_index,
|
|
1689
|
-
transform_context,
|
|
1690
|
-
) {
|
|
1691
|
-
for (let i = 0; i < render_nodes.length; i += 1) {
|
|
1692
|
-
const node = render_nodes[i];
|
|
1693
|
-
if (!is_static_early_return_capture_node(node, transform_context)) {
|
|
1694
|
-
continue;
|
|
1695
|
-
}
|
|
1696
|
-
|
|
1697
|
-
const { declaration, reference } = captureJsxChild(node, capture_index++);
|
|
1698
|
-
statements.push(declaration);
|
|
1699
|
-
render_nodes[i] = reference;
|
|
1700
|
-
}
|
|
1701
|
-
|
|
1702
|
-
return capture_index;
|
|
1703
|
-
}
|
|
1704
|
-
|
|
1705
|
-
/**
|
|
1706
|
-
* @param {any} node
|
|
1707
|
-
* @param {TransformContext} transform_context
|
|
1708
|
-
* @returns {boolean}
|
|
1709
|
-
*/
|
|
1710
|
-
function is_static_early_return_capture_node(node, transform_context) {
|
|
1711
|
-
if (node?.type !== 'JSXElement' && node?.type !== 'JSXFragment') {
|
|
1712
|
-
return false;
|
|
1713
|
-
}
|
|
1714
|
-
if (!is_hoist_safe_jsx_node(node)) {
|
|
1715
|
-
return false;
|
|
1716
|
-
}
|
|
1717
|
-
if (
|
|
1718
|
-
transform_context.platform.hooks?.canHoistStaticNode &&
|
|
1719
|
-
!transform_context.platform.hooks.canHoistStaticNode(node, transform_context)
|
|
1720
|
-
) {
|
|
1721
|
-
return false;
|
|
1722
|
-
}
|
|
1723
|
-
return !references_scope_bindings(node, transform_context.available_bindings);
|
|
1724
|
-
}
|
|
1725
|
-
|
|
1726
2121
|
/**
|
|
1727
2122
|
* @param {AST.Program} program
|
|
1728
2123
|
* @returns {AST.Program}
|
|
@@ -1743,11 +2138,9 @@ function expand_component_helpers(program) {
|
|
|
1743
2138
|
}
|
|
1744
2139
|
|
|
1745
2140
|
/**
|
|
1746
|
-
*
|
|
1747
|
-
* variable
|
|
1748
|
-
*
|
|
1749
|
-
* the hook returns, so helper expansion must read metadata from that broader
|
|
1750
|
-
* set.
|
|
2141
|
+
* Generated helper/statics metadata can be carried on function declarations,
|
|
2142
|
+
* variable declarations, object literal members, or export-safe expressions,
|
|
2143
|
+
* so helper expansion reads metadata from that broader set.
|
|
1751
2144
|
*
|
|
1752
2145
|
* @param {any} node
|
|
1753
2146
|
* @returns {{ generated_helpers?: any[], generated_statics?: any[] }[]}
|
|
@@ -1803,112 +2196,164 @@ function get_generated_component_metadata_list(node) {
|
|
|
1803
2196
|
return metas;
|
|
1804
2197
|
}
|
|
1805
2198
|
|
|
2199
|
+
/**
|
|
2200
|
+
* @param {any[]} render_nodes
|
|
2201
|
+
* @param {any} source_node
|
|
2202
|
+
* @param {boolean} [map_render_node_locations]
|
|
2203
|
+
* @returns {any}
|
|
2204
|
+
*/
|
|
2205
|
+
function create_component_return_statement(
|
|
2206
|
+
render_nodes,
|
|
2207
|
+
source_node,
|
|
2208
|
+
map_render_node_locations = true,
|
|
2209
|
+
) {
|
|
2210
|
+
const cloned = render_nodes.map((node) =>
|
|
2211
|
+
map_render_node_locations ? clone_expression_node(node) : clone_expression_node(node, false),
|
|
2212
|
+
);
|
|
2213
|
+
|
|
2214
|
+
return set_loc(b.return(build_return_expression(cloned) || create_null_literal()), source_node);
|
|
2215
|
+
}
|
|
2216
|
+
|
|
1806
2217
|
/**
|
|
1807
2218
|
* @param {any} node
|
|
1808
2219
|
* @returns {boolean}
|
|
1809
2220
|
*/
|
|
1810
|
-
function
|
|
1811
|
-
return
|
|
2221
|
+
function is_loop_skip_return_statement(node) {
|
|
2222
|
+
return (
|
|
2223
|
+
node?.type === 'ReturnStatement' &&
|
|
2224
|
+
node.argument == null &&
|
|
2225
|
+
node.metadata?.generated_loop_continue_return === true
|
|
2226
|
+
);
|
|
1812
2227
|
}
|
|
1813
2228
|
|
|
1814
2229
|
/**
|
|
1815
2230
|
* @param {any} node
|
|
1816
2231
|
* @returns {boolean}
|
|
1817
2232
|
*/
|
|
1818
|
-
function
|
|
2233
|
+
function is_loop_skip_if_statement(node) {
|
|
2234
|
+
return get_loop_skip_if_consequent_body(node) !== null;
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
/**
|
|
2238
|
+
* @param {any} node
|
|
2239
|
+
* @returns {any[] | null}
|
|
2240
|
+
*/
|
|
2241
|
+
function get_loop_skip_if_consequent_body(node) {
|
|
1819
2242
|
if (node?.type !== 'IfStatement' || node.alternate) {
|
|
1820
|
-
return
|
|
2243
|
+
return null;
|
|
1821
2244
|
}
|
|
1822
2245
|
|
|
1823
|
-
const consequent_body =
|
|
2246
|
+
const consequent_body =
|
|
2247
|
+
node.consequent.type === 'BlockStatement' ? node.consequent.body : [node.consequent];
|
|
1824
2248
|
|
|
1825
|
-
return consequent_body.
|
|
2249
|
+
return consequent_body.some(is_loop_skip_return_statement) ? consequent_body : null;
|
|
1826
2250
|
}
|
|
1827
2251
|
|
|
1828
2252
|
/**
|
|
1829
2253
|
* @param {any} node
|
|
1830
|
-
* @
|
|
2254
|
+
* @param {any[]} render_nodes
|
|
2255
|
+
* @param {TransformContext} transform_context
|
|
2256
|
+
* @returns {any}
|
|
1831
2257
|
*/
|
|
1832
|
-
function
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
2258
|
+
function create_component_loop_skip_if_statement(node, render_nodes, transform_context) {
|
|
2259
|
+
const consequent_body = /** @type {any[]} */ (get_loop_skip_if_consequent_body(node));
|
|
2260
|
+
const branch_statements = build_render_statements(consequent_body, true, transform_context);
|
|
2261
|
+
prepend_render_nodes_to_return_statements(branch_statements, render_nodes);
|
|
1836
2262
|
|
|
1837
|
-
return
|
|
2263
|
+
return set_loc(b.if(node.test, set_loc(b.block(branch_statements), node.consequent), null), node);
|
|
1838
2264
|
}
|
|
1839
2265
|
|
|
1840
2266
|
/**
|
|
1841
|
-
* @param {any}
|
|
1842
|
-
* @
|
|
2267
|
+
* @param {any[]} statements
|
|
2268
|
+
* @param {any[]} render_nodes
|
|
2269
|
+
* @returns {void}
|
|
1843
2270
|
*/
|
|
1844
|
-
function
|
|
1845
|
-
|
|
2271
|
+
function prepend_render_nodes_to_return_statements(statements, render_nodes) {
|
|
2272
|
+
if (render_nodes.length === 0) {
|
|
2273
|
+
return;
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
for (const statement of statements) {
|
|
2277
|
+
prepend_render_nodes_to_return_statement(statement, render_nodes, false);
|
|
2278
|
+
}
|
|
1846
2279
|
}
|
|
1847
2280
|
|
|
1848
2281
|
/**
|
|
2282
|
+
* @param {any} node
|
|
1849
2283
|
* @param {any[]} render_nodes
|
|
1850
|
-
* @param {
|
|
1851
|
-
* @
|
|
1852
|
-
* @returns {any}
|
|
2284
|
+
* @param {boolean} inside_nested_function
|
|
2285
|
+
* @returns {void}
|
|
1853
2286
|
*/
|
|
1854
|
-
function
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
2287
|
+
function prepend_render_nodes_to_return_statement(node, render_nodes, inside_nested_function) {
|
|
2288
|
+
if (!node || typeof node !== 'object') {
|
|
2289
|
+
return;
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
if (
|
|
2293
|
+
node.type === 'FunctionDeclaration' ||
|
|
2294
|
+
node.type === 'FunctionExpression' ||
|
|
2295
|
+
node.type === 'ArrowFunctionExpression'
|
|
2296
|
+
) {
|
|
2297
|
+
inside_nested_function = true;
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
if (!inside_nested_function && node.type === 'ReturnStatement') {
|
|
2301
|
+
node.argument = combine_render_return_argument(render_nodes, node.argument);
|
|
2302
|
+
return;
|
|
2303
|
+
}
|
|
1862
2304
|
|
|
1863
|
-
|
|
2305
|
+
if (Array.isArray(node)) {
|
|
2306
|
+
for (const child of node) {
|
|
2307
|
+
prepend_render_nodes_to_return_statement(child, render_nodes, inside_nested_function);
|
|
2308
|
+
}
|
|
2309
|
+
return;
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
for (const key of Object.keys(node)) {
|
|
2313
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
2314
|
+
continue;
|
|
2315
|
+
}
|
|
2316
|
+
prepend_render_nodes_to_return_statement(node[key], render_nodes, inside_nested_function);
|
|
2317
|
+
}
|
|
1864
2318
|
}
|
|
1865
2319
|
|
|
1866
2320
|
/**
|
|
1867
|
-
* @param {any} node
|
|
1868
2321
|
* @param {any[]} render_nodes
|
|
2322
|
+
* @param {any} return_argument
|
|
1869
2323
|
* @returns {any}
|
|
1870
2324
|
*/
|
|
1871
|
-
function
|
|
1872
|
-
const
|
|
2325
|
+
function combine_render_return_argument(render_nodes, return_argument) {
|
|
2326
|
+
const combined = render_nodes.map((node) => clone_expression_node(node, false));
|
|
1873
2327
|
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
node.consequent,
|
|
1880
|
-
),
|
|
1881
|
-
null,
|
|
1882
|
-
),
|
|
1883
|
-
node,
|
|
1884
|
-
);
|
|
2328
|
+
if (return_argument != null && !is_null_literal(return_argument)) {
|
|
2329
|
+
combined.push(return_argument_to_render_node(return_argument));
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
return build_return_expression(combined) || create_null_literal();
|
|
1885
2333
|
}
|
|
1886
2334
|
|
|
1887
2335
|
/**
|
|
1888
|
-
* @param {any}
|
|
1889
|
-
* @param {any[]} render_nodes
|
|
1890
|
-
* @param {TransformContext} transform_context
|
|
2336
|
+
* @param {any} argument
|
|
1891
2337
|
* @returns {any}
|
|
1892
2338
|
*/
|
|
1893
|
-
function
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
2339
|
+
function return_argument_to_render_node(argument) {
|
|
2340
|
+
if (
|
|
2341
|
+
argument?.type === 'JSXElement' ||
|
|
2342
|
+
argument?.type === 'JSXFragment' ||
|
|
2343
|
+
argument?.type === 'JSXExpressionContainer'
|
|
2344
|
+
) {
|
|
2345
|
+
return argument;
|
|
2346
|
+
}
|
|
1897
2347
|
|
|
1898
|
-
return
|
|
2348
|
+
return to_jsx_expression_container(argument);
|
|
1899
2349
|
}
|
|
1900
2350
|
|
|
1901
2351
|
/**
|
|
1902
|
-
*
|
|
1903
|
-
*
|
|
1904
|
-
* get dropped on the lift path.
|
|
1905
|
-
*
|
|
1906
|
-
* @param {any[]} render_nodes
|
|
1907
|
-
* @param {any} jsx_child
|
|
1908
|
-
* @returns {any}
|
|
2352
|
+
* @param {any} node
|
|
2353
|
+
* @returns {boolean}
|
|
1909
2354
|
*/
|
|
1910
|
-
function
|
|
1911
|
-
return
|
|
2355
|
+
function is_null_literal(node) {
|
|
2356
|
+
return node?.type === 'Literal' && node.value == null;
|
|
1912
2357
|
}
|
|
1913
2358
|
|
|
1914
2359
|
/**
|
|
@@ -1932,51 +2377,6 @@ function build_array_normalization_decls(source_id, source_expr) {
|
|
|
1932
2377
|
return { source_decl, source_normalize_decl };
|
|
1933
2378
|
}
|
|
1934
2379
|
|
|
1935
|
-
/**
|
|
1936
|
-
* @param {any} node
|
|
1937
|
-
* @param {any[]} continuation_body
|
|
1938
|
-
* @param {any[]} render_nodes
|
|
1939
|
-
* @param {TransformContext} transform_context
|
|
1940
|
-
* @returns {any[]}
|
|
1941
|
-
*/
|
|
1942
|
-
function create_component_helper_split_returning_if_statements(
|
|
1943
|
-
node,
|
|
1944
|
-
continuation_body,
|
|
1945
|
-
render_nodes,
|
|
1946
|
-
transform_context,
|
|
1947
|
-
) {
|
|
1948
|
-
const consequent_body = get_if_consequent_body(node);
|
|
1949
|
-
const return_index = consequent_body.findIndex(is_bare_return_statement);
|
|
1950
|
-
const branch_body =
|
|
1951
|
-
return_index === -1 ? consequent_body : consequent_body.slice(0, return_index);
|
|
1952
|
-
const branch_helper = create_hook_safe_helper(
|
|
1953
|
-
branch_body,
|
|
1954
|
-
undefined,
|
|
1955
|
-
node.consequent,
|
|
1956
|
-
transform_context,
|
|
1957
|
-
);
|
|
1958
|
-
const continuation_helper = create_hook_safe_helper(
|
|
1959
|
-
continuation_body,
|
|
1960
|
-
undefined,
|
|
1961
|
-
node,
|
|
1962
|
-
transform_context,
|
|
1963
|
-
);
|
|
1964
|
-
|
|
1965
|
-
const branch_block = set_loc(
|
|
1966
|
-
b.block([
|
|
1967
|
-
...branch_helper.setup_statements,
|
|
1968
|
-
combined_return_statement(render_nodes, branch_helper.component_element),
|
|
1969
|
-
]),
|
|
1970
|
-
node.consequent,
|
|
1971
|
-
);
|
|
1972
|
-
|
|
1973
|
-
return [
|
|
1974
|
-
set_loc(b.if(node.test, branch_block, null), node),
|
|
1975
|
-
...continuation_helper.setup_statements,
|
|
1976
|
-
combined_return_statement(render_nodes, continuation_helper.component_element),
|
|
1977
|
-
];
|
|
1978
|
-
}
|
|
1979
|
-
|
|
1980
2380
|
/**
|
|
1981
2381
|
* Hoist the helper for a hook-bearing for-of body out of the iteration
|
|
1982
2382
|
* callback so the helper is declared once per render rather than re-bound on
|
|
@@ -2088,7 +2488,7 @@ function build_hoisted_for_of_with_hooks(node, transform_context) {
|
|
|
2088
2488
|
transform_context.available_bindings = fn_saved_bindings;
|
|
2089
2489
|
|
|
2090
2490
|
const helper_fn = b.function(clone_identifier(component_id), params, b.block(fn_body_statements));
|
|
2091
|
-
helper_fn.metadata = { path: [],
|
|
2491
|
+
helper_fn.metadata = { path: [], is_method: false };
|
|
2092
2492
|
|
|
2093
2493
|
let helper_decl;
|
|
2094
2494
|
if (transform_context.helper_state && use_module_scoped_component) {
|
|
@@ -2236,142 +2636,6 @@ function create_helper_props_type_literal_with_typeof_flags(bindings, aliases, u
|
|
|
2236
2636
|
);
|
|
2237
2637
|
}
|
|
2238
2638
|
|
|
2239
|
-
/**
|
|
2240
|
-
* @param {any} node
|
|
2241
|
-
* @param {any[]} continuation_body
|
|
2242
|
-
* @param {any[]} render_nodes
|
|
2243
|
-
* @param {TransformContext} transform_context
|
|
2244
|
-
* @returns {any[]}
|
|
2245
|
-
*/
|
|
2246
|
-
function create_setup_once_helper_split_returning_if_statements(
|
|
2247
|
-
node,
|
|
2248
|
-
continuation_body,
|
|
2249
|
-
render_nodes,
|
|
2250
|
-
transform_context,
|
|
2251
|
-
) {
|
|
2252
|
-
const consequent_body = get_if_consequent_body(node);
|
|
2253
|
-
const return_index = consequent_body.findIndex(is_bare_return_statement);
|
|
2254
|
-
const branch_body =
|
|
2255
|
-
return_index === -1 ? consequent_body : consequent_body.slice(0, return_index);
|
|
2256
|
-
const branch_helper = branch_body.length
|
|
2257
|
-
? create_hook_safe_helper(branch_body, undefined, node.consequent, transform_context)
|
|
2258
|
-
: { setup_statements: [], component_element: create_null_literal() };
|
|
2259
|
-
const continuation_helper = continuation_body.length
|
|
2260
|
-
? create_hook_safe_helper(continuation_body, undefined, node, transform_context)
|
|
2261
|
-
: { setup_statements: [], component_element: create_null_literal() };
|
|
2262
|
-
|
|
2263
|
-
return [
|
|
2264
|
-
...branch_helper.setup_statements,
|
|
2265
|
-
...continuation_helper.setup_statements,
|
|
2266
|
-
b.return(
|
|
2267
|
-
combine_render_return_argument(
|
|
2268
|
-
render_nodes,
|
|
2269
|
-
set_loc(
|
|
2270
|
-
b.conditional(
|
|
2271
|
-
node.test,
|
|
2272
|
-
branch_helper.component_element,
|
|
2273
|
-
continuation_helper.component_element,
|
|
2274
|
-
),
|
|
2275
|
-
node,
|
|
2276
|
-
),
|
|
2277
|
-
),
|
|
2278
|
-
),
|
|
2279
|
-
];
|
|
2280
|
-
}
|
|
2281
|
-
|
|
2282
|
-
/**
|
|
2283
|
-
* @param {any[]} statements
|
|
2284
|
-
* @param {any[]} render_nodes
|
|
2285
|
-
* @returns {void}
|
|
2286
|
-
*/
|
|
2287
|
-
function prepend_render_nodes_to_return_statements(statements, render_nodes) {
|
|
2288
|
-
if (render_nodes.length === 0) {
|
|
2289
|
-
return;
|
|
2290
|
-
}
|
|
2291
|
-
|
|
2292
|
-
for (const statement of statements) {
|
|
2293
|
-
prepend_render_nodes_to_return_statement(statement, render_nodes, false);
|
|
2294
|
-
}
|
|
2295
|
-
}
|
|
2296
|
-
|
|
2297
|
-
/**
|
|
2298
|
-
* @param {any} node
|
|
2299
|
-
* @param {any[]} render_nodes
|
|
2300
|
-
* @param {boolean} inside_nested_function
|
|
2301
|
-
* @returns {void}
|
|
2302
|
-
*/
|
|
2303
|
-
function prepend_render_nodes_to_return_statement(node, render_nodes, inside_nested_function) {
|
|
2304
|
-
if (!node || typeof node !== 'object') {
|
|
2305
|
-
return;
|
|
2306
|
-
}
|
|
2307
|
-
|
|
2308
|
-
if (
|
|
2309
|
-
node.type === 'FunctionDeclaration' ||
|
|
2310
|
-
node.type === 'FunctionExpression' ||
|
|
2311
|
-
node.type === 'ArrowFunctionExpression'
|
|
2312
|
-
) {
|
|
2313
|
-
inside_nested_function = true;
|
|
2314
|
-
}
|
|
2315
|
-
|
|
2316
|
-
if (!inside_nested_function && node.type === 'ReturnStatement') {
|
|
2317
|
-
node.argument = combine_render_return_argument(render_nodes, node.argument);
|
|
2318
|
-
return;
|
|
2319
|
-
}
|
|
2320
|
-
|
|
2321
|
-
if (Array.isArray(node)) {
|
|
2322
|
-
for (const child of node) {
|
|
2323
|
-
prepend_render_nodes_to_return_statement(child, render_nodes, inside_nested_function);
|
|
2324
|
-
}
|
|
2325
|
-
return;
|
|
2326
|
-
}
|
|
2327
|
-
|
|
2328
|
-
for (const key of Object.keys(node)) {
|
|
2329
|
-
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
2330
|
-
continue;
|
|
2331
|
-
}
|
|
2332
|
-
prepend_render_nodes_to_return_statement(node[key], render_nodes, inside_nested_function);
|
|
2333
|
-
}
|
|
2334
|
-
}
|
|
2335
|
-
|
|
2336
|
-
/**
|
|
2337
|
-
* @param {any[]} render_nodes
|
|
2338
|
-
* @param {any} return_argument
|
|
2339
|
-
* @returns {any}
|
|
2340
|
-
*/
|
|
2341
|
-
function combine_render_return_argument(render_nodes, return_argument) {
|
|
2342
|
-
const combined = render_nodes.map((node) => clone_expression_node(node, false));
|
|
2343
|
-
|
|
2344
|
-
if (return_argument != null && !is_null_literal(return_argument)) {
|
|
2345
|
-
combined.push(return_argument_to_render_node(return_argument));
|
|
2346
|
-
}
|
|
2347
|
-
|
|
2348
|
-
return build_return_expression(combined) || create_null_literal();
|
|
2349
|
-
}
|
|
2350
|
-
|
|
2351
|
-
/**
|
|
2352
|
-
* @param {any} argument
|
|
2353
|
-
* @returns {any}
|
|
2354
|
-
*/
|
|
2355
|
-
function return_argument_to_render_node(argument) {
|
|
2356
|
-
if (
|
|
2357
|
-
argument?.type === 'JSXElement' ||
|
|
2358
|
-
argument?.type === 'JSXFragment' ||
|
|
2359
|
-
argument?.type === 'JSXExpressionContainer'
|
|
2360
|
-
) {
|
|
2361
|
-
return argument;
|
|
2362
|
-
}
|
|
2363
|
-
|
|
2364
|
-
return to_jsx_expression_container(argument);
|
|
2365
|
-
}
|
|
2366
|
-
|
|
2367
|
-
/**
|
|
2368
|
-
* @param {any} node
|
|
2369
|
-
* @returns {boolean}
|
|
2370
|
-
*/
|
|
2371
|
-
function is_null_literal(node) {
|
|
2372
|
-
return node?.type === 'Literal' && node.value == null;
|
|
2373
|
-
}
|
|
2374
|
-
|
|
2375
2639
|
/**
|
|
2376
2640
|
* @param {any} node
|
|
2377
2641
|
* @param {TransformContext} transform_context
|
|
@@ -2414,159 +2678,36 @@ function to_jsx_element(node, transform_context, raw_children = node.children ||
|
|
|
2414
2678
|
|
|
2415
2679
|
if (child_transform) {
|
|
2416
2680
|
children = child_transform.children;
|
|
2417
|
-
if (typeof child_transform.selfClosing === 'boolean') {
|
|
2418
|
-
selfClosing = child_transform.selfClosing;
|
|
2419
|
-
}
|
|
2420
|
-
} else {
|
|
2421
|
-
const html_child_transform = rewrite_host_html_children(
|
|
2422
|
-
node,
|
|
2423
|
-
walked_children,
|
|
2424
|
-
raw_children,
|
|
2425
|
-
attributes,
|
|
2426
|
-
transform_context,
|
|
2427
|
-
);
|
|
2428
|
-
if (html_child_transform) {
|
|
2429
|
-
children = html_child_transform.children;
|
|
2430
|
-
selfClosing = html_child_transform.selfClosing;
|
|
2431
|
-
} else {
|
|
2432
|
-
children = create_element_children(walked_children, transform_context);
|
|
2433
|
-
}
|
|
2434
|
-
}
|
|
2435
|
-
const has_unmappable_attribute = attributes.some(
|
|
2436
|
-
(/** @type {any} */ attribute) => attribute?.metadata?.has_unmappable_value,
|
|
2437
|
-
);
|
|
2438
|
-
|
|
2439
|
-
const opening_element_node = b.jsx_opening_element(
|
|
2440
|
-
name,
|
|
2441
|
-
attributes,
|
|
2442
|
-
selfClosing,
|
|
2443
|
-
node.openingElement?.typeArguments,
|
|
2444
|
-
);
|
|
2445
|
-
const openingElement = has_unmappable_attribute
|
|
2446
|
-
? opening_element_node
|
|
2447
|
-
: set_loc(opening_element_node, node.openingElement || node);
|
|
2448
|
-
|
|
2449
|
-
const closingElement = selfClosing
|
|
2450
|
-
? null
|
|
2451
|
-
: set_loc(
|
|
2452
|
-
b.jsx_closing_element(
|
|
2453
|
-
clone_jsx_name(name, node.closingElement?.name || node.closingElement || node),
|
|
2454
|
-
),
|
|
2455
|
-
node.closingElement || node,
|
|
2456
|
-
);
|
|
2457
|
-
|
|
2458
|
-
return set_loc(b.jsx_element_fresh(openingElement, closingElement, children), node);
|
|
2459
|
-
}
|
|
2460
|
-
|
|
2461
|
-
/**
|
|
2462
|
-
* @param {any} node
|
|
2463
|
-
* @param {any[]} walked_children
|
|
2464
|
-
* @param {any[]} raw_children
|
|
2465
|
-
* @param {any[]} attributes
|
|
2466
|
-
* @param {TransformContext} transform_context
|
|
2467
|
-
* @returns {{ children: any[]; selfClosing: boolean } | null}
|
|
2468
|
-
*/
|
|
2469
|
-
export function rewrite_host_html_children(
|
|
2470
|
-
node,
|
|
2471
|
-
walked_children,
|
|
2472
|
-
raw_children,
|
|
2473
|
-
attributes,
|
|
2474
|
-
transform_context,
|
|
2475
|
-
) {
|
|
2476
|
-
const source_children = raw_children || walked_children;
|
|
2477
|
-
const source_html_index = source_children.findIndex((child) => child?.type === 'Html');
|
|
2478
|
-
if (source_html_index === -1) {
|
|
2479
|
-
return null;
|
|
2480
|
-
}
|
|
2481
|
-
const source_html = source_children[source_html_index];
|
|
2482
|
-
const walked_html =
|
|
2483
|
-
walked_children[source_html_index]?.type === 'Html'
|
|
2484
|
-
? walked_children[source_html_index]
|
|
2485
|
-
: source_html;
|
|
2486
|
-
|
|
2487
|
-
if (is_component_like_element(node) || source_children.length !== 1) {
|
|
2488
|
-
report_invalid_html_child_error(source_html, transform_context);
|
|
2489
|
-
}
|
|
2490
|
-
|
|
2491
|
-
const conflicting_attribute = get_host_html_conflicting_attribute(attributes, transform_context);
|
|
2492
|
-
if (conflicting_attribute !== null) {
|
|
2493
|
-
error(
|
|
2494
|
-
create_host_html_conflict_error(conflicting_attribute, transform_context),
|
|
2495
|
-
transform_context.filename,
|
|
2496
|
-
source_html,
|
|
2497
|
-
transform_context.errors,
|
|
2498
|
-
transform_context.comments,
|
|
2499
|
-
);
|
|
2500
|
-
}
|
|
2501
|
-
|
|
2502
|
-
attributes.push(create_host_html_attribute(walked_html, source_html, transform_context));
|
|
2503
|
-
|
|
2504
|
-
return { children: [], selfClosing: true };
|
|
2505
|
-
}
|
|
2506
|
-
|
|
2507
|
-
/**
|
|
2508
|
-
* @param {any[]} attributes
|
|
2509
|
-
* @param {TransformContext} transform_context
|
|
2510
|
-
* @returns {{ kind: 'attribute'; name: string } | null}
|
|
2511
|
-
*/
|
|
2512
|
-
export function get_host_html_conflicting_attribute(attributes, transform_context) {
|
|
2513
|
-
const conflicting_attributes = get_host_html_conflicting_attribute_names(transform_context);
|
|
2514
|
-
for (const name of conflicting_attributes) {
|
|
2515
|
-
if (has_jsx_attribute(attributes, name)) {
|
|
2516
|
-
return { kind: 'attribute', name };
|
|
2681
|
+
if (typeof child_transform.selfClosing === 'boolean') {
|
|
2682
|
+
selfClosing = child_transform.selfClosing;
|
|
2517
2683
|
}
|
|
2684
|
+
} else {
|
|
2685
|
+
children = create_element_children(walked_children, transform_context);
|
|
2518
2686
|
}
|
|
2687
|
+
const has_unmappable_attribute = attributes.some(
|
|
2688
|
+
(/** @type {any} */ attribute) => attribute?.metadata?.has_unmappable_value,
|
|
2689
|
+
);
|
|
2519
2690
|
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
const html_attribute = get_host_html_attribute_name(transform_context);
|
|
2530
|
-
return `\`{html ...}\` lowers to \`${html_attribute}\` on the ${transform_context.platform.name} target and cannot be combined with an existing \`${conflicting_attribute.name}\` attribute.`;
|
|
2531
|
-
}
|
|
2532
|
-
|
|
2533
|
-
/**
|
|
2534
|
-
* @param {TransformContext} transform_context
|
|
2535
|
-
* @returns {string[]}
|
|
2536
|
-
*/
|
|
2537
|
-
function get_host_html_conflicting_attribute_names(transform_context) {
|
|
2538
|
-
switch (transform_context.platform.name) {
|
|
2539
|
-
case 'Solid':
|
|
2540
|
-
return ['innerHTML', 'textContent'];
|
|
2541
|
-
case 'Vue':
|
|
2542
|
-
return ['innerHTML'];
|
|
2543
|
-
default:
|
|
2544
|
-
return [get_host_html_attribute_name(transform_context)];
|
|
2545
|
-
}
|
|
2546
|
-
}
|
|
2691
|
+
const opening_element_node = b.jsx_opening_element(
|
|
2692
|
+
name,
|
|
2693
|
+
attributes,
|
|
2694
|
+
selfClosing,
|
|
2695
|
+
node.openingElement?.typeArguments,
|
|
2696
|
+
);
|
|
2697
|
+
const openingElement = has_unmappable_attribute
|
|
2698
|
+
? opening_element_node
|
|
2699
|
+
: set_loc(opening_element_node, node.openingElement || node);
|
|
2547
2700
|
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
}
|
|
2701
|
+
const closingElement = selfClosing
|
|
2702
|
+
? null
|
|
2703
|
+
: set_loc(
|
|
2704
|
+
b.jsx_closing_element(
|
|
2705
|
+
clone_jsx_name(name, node.closingElement?.name || node.closingElement || node),
|
|
2706
|
+
),
|
|
2707
|
+
node.closingElement || node,
|
|
2708
|
+
);
|
|
2557
2709
|
|
|
2558
|
-
|
|
2559
|
-
* @param {any[]} attributes
|
|
2560
|
-
* @param {string} name
|
|
2561
|
-
* @returns {boolean}
|
|
2562
|
-
*/
|
|
2563
|
-
function has_jsx_attribute(attributes, name) {
|
|
2564
|
-
return attributes.some(
|
|
2565
|
-
(attr) =>
|
|
2566
|
-
attr?.type === 'JSXAttribute' &&
|
|
2567
|
-
attr.name?.type === 'JSXIdentifier' &&
|
|
2568
|
-
attr.name.name === name,
|
|
2569
|
-
);
|
|
2710
|
+
return set_loc(b.jsx_element_fresh(openingElement, closingElement, children), node);
|
|
2570
2711
|
}
|
|
2571
2712
|
|
|
2572
2713
|
/**
|
|
@@ -2616,18 +2757,14 @@ function child_contains_return_semantics(node) {
|
|
|
2616
2757
|
return false;
|
|
2617
2758
|
}
|
|
2618
2759
|
|
|
2619
|
-
if (
|
|
2620
|
-
(node.type === 'ReturnStatement' && node.argument == null) ||
|
|
2621
|
-
is_lone_return_if_statement(node)
|
|
2622
|
-
) {
|
|
2760
|
+
if (node.type === 'ReturnStatement') {
|
|
2623
2761
|
return true;
|
|
2624
2762
|
}
|
|
2625
2763
|
|
|
2626
2764
|
if (
|
|
2627
2765
|
node.type === 'FunctionDeclaration' ||
|
|
2628
2766
|
node.type === 'FunctionExpression' ||
|
|
2629
|
-
node.type === 'ArrowFunctionExpression'
|
|
2630
|
-
node.type === 'Component'
|
|
2767
|
+
node.type === 'ArrowFunctionExpression'
|
|
2631
2768
|
) {
|
|
2632
2769
|
return false;
|
|
2633
2770
|
}
|
|
@@ -2662,7 +2799,10 @@ function is_inline_element_child(node) {
|
|
|
2662
2799
|
* @returns {ESTreeJSX.JSXExpressionContainer}
|
|
2663
2800
|
*/
|
|
2664
2801
|
function statement_body_to_jsx_child(body_nodes, transform_context) {
|
|
2665
|
-
if (
|
|
2802
|
+
if (
|
|
2803
|
+
should_extract_hook_helpers(transform_context) &&
|
|
2804
|
+
body_contains_top_level_hook_call(body_nodes, transform_context, true)
|
|
2805
|
+
) {
|
|
2666
2806
|
return hook_safe_statement_body_to_jsx_child(body_nodes, transform_context);
|
|
2667
2807
|
}
|
|
2668
2808
|
|
|
@@ -3223,8 +3363,7 @@ function references_name_in_set(node, names) {
|
|
|
3223
3363
|
if (
|
|
3224
3364
|
node.type === 'FunctionDeclaration' ||
|
|
3225
3365
|
node.type === 'FunctionExpression' ||
|
|
3226
|
-
node.type === 'ArrowFunctionExpression'
|
|
3227
|
-
node.type === 'Component'
|
|
3366
|
+
node.type === 'ArrowFunctionExpression'
|
|
3228
3367
|
) {
|
|
3229
3368
|
return false;
|
|
3230
3369
|
}
|
|
@@ -3369,8 +3508,7 @@ function find_first_hook_call_name(node) {
|
|
|
3369
3508
|
if (
|
|
3370
3509
|
node.type === 'FunctionDeclaration' ||
|
|
3371
3510
|
node.type === 'FunctionExpression' ||
|
|
3372
|
-
node.type === 'ArrowFunctionExpression'
|
|
3373
|
-
node.type === 'Component'
|
|
3511
|
+
node.type === 'ArrowFunctionExpression'
|
|
3374
3512
|
) {
|
|
3375
3513
|
return null;
|
|
3376
3514
|
}
|
|
@@ -3477,7 +3615,6 @@ export function create_hook_safe_helper(
|
|
|
3477
3615
|
params,
|
|
3478
3616
|
b.block(build_render_statements(body_nodes, true, transform_context)),
|
|
3479
3617
|
);
|
|
3480
|
-
helper_fn.metadata.is_component = true;
|
|
3481
3618
|
helper_fn.metadata.is_method = false;
|
|
3482
3619
|
|
|
3483
3620
|
transform_context.available_bindings = saved_bindings;
|
|
@@ -3697,108 +3834,6 @@ function get_body_source_node(body_nodes) {
|
|
|
3697
3834
|
return first;
|
|
3698
3835
|
}
|
|
3699
3836
|
|
|
3700
|
-
/**
|
|
3701
|
-
* @param {any} node
|
|
3702
|
-
* @param {TransformContext} transform_context
|
|
3703
|
-
* @param {any[]} path
|
|
3704
|
-
*/
|
|
3705
|
-
function validate_style_directive(node, transform_context, path) {
|
|
3706
|
-
const { attribute, element } = get_style_attribute_context(node, path);
|
|
3707
|
-
|
|
3708
|
-
if (!attribute) {
|
|
3709
|
-
error(
|
|
3710
|
-
'`{style "class_name"}` can only be used as an element attribute value.',
|
|
3711
|
-
transform_context.filename,
|
|
3712
|
-
node,
|
|
3713
|
-
transform_context.errors,
|
|
3714
|
-
transform_context.comments,
|
|
3715
|
-
);
|
|
3716
|
-
}
|
|
3717
|
-
|
|
3718
|
-
if (element && is_dom_style_target(element)) {
|
|
3719
|
-
error(
|
|
3720
|
-
'`{style "class_name"}` cannot be used directly on DOM elements. Pass the class to a child component instead.',
|
|
3721
|
-
transform_context.filename,
|
|
3722
|
-
node,
|
|
3723
|
-
transform_context.errors,
|
|
3724
|
-
transform_context.comments,
|
|
3725
|
-
);
|
|
3726
|
-
}
|
|
3727
|
-
|
|
3728
|
-
if (!transform_context.current_css_hash) {
|
|
3729
|
-
error(
|
|
3730
|
-
'`{style "class_name"}` requires a <style> block in the current component.',
|
|
3731
|
-
transform_context.filename,
|
|
3732
|
-
node,
|
|
3733
|
-
transform_context.errors,
|
|
3734
|
-
transform_context.comments,
|
|
3735
|
-
);
|
|
3736
|
-
}
|
|
3737
|
-
}
|
|
3738
|
-
|
|
3739
|
-
/**
|
|
3740
|
-
* @param {any} node
|
|
3741
|
-
* @param {any[]} path
|
|
3742
|
-
* @returns {{ attribute: any, element: any }}
|
|
3743
|
-
*/
|
|
3744
|
-
function get_style_attribute_context(node, path) {
|
|
3745
|
-
const parent = path.at(-1);
|
|
3746
|
-
const attribute =
|
|
3747
|
-
parent?.type === 'Attribute' && parent.value === node
|
|
3748
|
-
? parent
|
|
3749
|
-
: path
|
|
3750
|
-
.findLast((ancestor) => ancestor?.type === 'Element')
|
|
3751
|
-
?.attributes?.find(
|
|
3752
|
-
(/** @type {any} */ attr) =>
|
|
3753
|
-
attr?.type === 'Attribute' &&
|
|
3754
|
-
(attr.value === node || node_contains(attr.value, node)),
|
|
3755
|
-
);
|
|
3756
|
-
const element = path.findLast(
|
|
3757
|
-
(ancestor) =>
|
|
3758
|
-
ancestor?.type === 'Element' &&
|
|
3759
|
-
(!attribute || ancestor.attributes?.some((/** @type {any} */ attr) => attr === attribute)),
|
|
3760
|
-
);
|
|
3761
|
-
|
|
3762
|
-
return { attribute: attribute ?? null, element: element ?? null };
|
|
3763
|
-
}
|
|
3764
|
-
|
|
3765
|
-
/**
|
|
3766
|
-
* @param {any} root
|
|
3767
|
-
* @param {any} target
|
|
3768
|
-
* @returns {boolean}
|
|
3769
|
-
*/
|
|
3770
|
-
function node_contains(root, target) {
|
|
3771
|
-
if (!root || typeof root !== 'object') {
|
|
3772
|
-
return false;
|
|
3773
|
-
}
|
|
3774
|
-
if (root === target) {
|
|
3775
|
-
return true;
|
|
3776
|
-
}
|
|
3777
|
-
if (Array.isArray(root)) {
|
|
3778
|
-
return root.some((child) => node_contains(child, target));
|
|
3779
|
-
}
|
|
3780
|
-
for (const key of Object.keys(root)) {
|
|
3781
|
-
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
3782
|
-
continue;
|
|
3783
|
-
}
|
|
3784
|
-
if (node_contains(root[key], target)) {
|
|
3785
|
-
return true;
|
|
3786
|
-
}
|
|
3787
|
-
}
|
|
3788
|
-
return false;
|
|
3789
|
-
}
|
|
3790
|
-
|
|
3791
|
-
/**
|
|
3792
|
-
* @param {any} element
|
|
3793
|
-
* @returns {boolean}
|
|
3794
|
-
*/
|
|
3795
|
-
function is_dom_style_target(element) {
|
|
3796
|
-
if (!element?.id || is_dynamic_element_id(element.id)) {
|
|
3797
|
-
return false;
|
|
3798
|
-
}
|
|
3799
|
-
return element.id.type === 'Identifier' && /^[a-z]/.test(element.id.name);
|
|
3800
|
-
}
|
|
3801
|
-
|
|
3802
3837
|
/**
|
|
3803
3838
|
* @param {any} node
|
|
3804
3839
|
* @param {TransformContext} transform_context
|
|
@@ -3821,8 +3856,6 @@ function to_jsx_child(node, transform_context) {
|
|
|
3821
3856
|
return to_jsx_expression_container(to_text_expression(node.expression, node), node);
|
|
3822
3857
|
case 'TSRXExpression':
|
|
3823
3858
|
return to_jsx_expression_container(node.expression, node);
|
|
3824
|
-
case 'Html':
|
|
3825
|
-
return recover_invalid_html_child(node, transform_context);
|
|
3826
3859
|
case 'IfStatement':
|
|
3827
3860
|
return (
|
|
3828
3861
|
transform_context.platform.hooks?.controlFlow?.ifStatement ?? if_statement_to_jsx_child
|
|
@@ -3846,7 +3879,7 @@ function to_jsx_child(node, transform_context) {
|
|
|
3846
3879
|
}
|
|
3847
3880
|
|
|
3848
3881
|
/**
|
|
3849
|
-
* Lower a
|
|
3882
|
+
* Lower a native TSRX fragment body to a JSX expression.
|
|
3850
3883
|
* Unlike `<tsx>`, children have already been parsed and transformed through
|
|
3851
3884
|
* the normal TSRX Element/Text/control-flow visitors.
|
|
3852
3885
|
*
|
|
@@ -3902,7 +3935,7 @@ function tsrx_node_to_jsx_expression(node, transform_context, in_jsx_child = fal
|
|
|
3902
3935
|
}
|
|
3903
3936
|
|
|
3904
3937
|
/**
|
|
3905
|
-
* Explicit return values inside expression-position
|
|
3938
|
+
* Explicit return values inside expression-position native templates are JavaScript
|
|
3906
3939
|
* values, so keep them out of platform render control flow.
|
|
3907
3940
|
*
|
|
3908
3941
|
* @param {any[]} body_nodes
|
|
@@ -3958,8 +3991,7 @@ function body_contains_top_level_return_value(node) {
|
|
|
3958
3991
|
node.type === 'FunctionExpression' ||
|
|
3959
3992
|
node.type === 'ArrowFunctionExpression' ||
|
|
3960
3993
|
node.type === 'ClassDeclaration' ||
|
|
3961
|
-
node.type === 'ClassExpression'
|
|
3962
|
-
node.type === 'Component'
|
|
3994
|
+
node.type === 'ClassExpression'
|
|
3963
3995
|
) {
|
|
3964
3996
|
return false;
|
|
3965
3997
|
}
|
|
@@ -4153,7 +4185,12 @@ function find_key_expression_in_body(body_nodes) {
|
|
|
4153
4185
|
* @returns {any}
|
|
4154
4186
|
*/
|
|
4155
4187
|
function continue_to_bare_return(source_node) {
|
|
4156
|
-
|
|
4188
|
+
const node = set_loc(b.return(null), source_node);
|
|
4189
|
+
node.metadata = {
|
|
4190
|
+
...(node.metadata || {}),
|
|
4191
|
+
generated_loop_continue_return: true,
|
|
4192
|
+
};
|
|
4193
|
+
return node;
|
|
4157
4194
|
}
|
|
4158
4195
|
|
|
4159
4196
|
/**
|
|
@@ -4214,7 +4251,7 @@ function is_loop_statement(node) {
|
|
|
4214
4251
|
function for_of_statement_to_jsx_child(node, transform_context) {
|
|
4215
4252
|
if (node.await) {
|
|
4216
4253
|
error(
|
|
4217
|
-
`${transform_context.platform.name} TSRX does not support \`for await...of\` in
|
|
4254
|
+
`${transform_context.platform.name} TSRX does not support \`for await...of\` in TSRX templates.`,
|
|
4218
4255
|
transform_context.filename,
|
|
4219
4256
|
node,
|
|
4220
4257
|
transform_context.errors,
|
|
@@ -4228,7 +4265,9 @@ function for_of_statement_to_jsx_child(node, transform_context) {
|
|
|
4228
4265
|
node.body.type === 'BlockStatement' ? node.body.body : [node.body],
|
|
4229
4266
|
)
|
|
4230
4267
|
);
|
|
4231
|
-
const has_hooks =
|
|
4268
|
+
const has_hooks =
|
|
4269
|
+
should_extract_hook_helpers(transform_context) &&
|
|
4270
|
+
body_contains_top_level_hook_call(loop_body, transform_context, true);
|
|
4232
4271
|
const body_key_expression = find_key_expression_in_body(loop_body);
|
|
4233
4272
|
const explicit_key_expression =
|
|
4234
4273
|
body_key_expression ?? (node.key ? clone_expression_node(node.key) : undefined);
|
|
@@ -4457,7 +4496,7 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
4457
4496
|
|
|
4458
4497
|
if (finalizer) {
|
|
4459
4498
|
error(
|
|
4460
|
-
`${transform_context.platform.name} TSRX does not support JavaScript \`try/finally\` in
|
|
4499
|
+
`${transform_context.platform.name} TSRX does not support JavaScript \`try/finally\` in TSRX templates. \`finally\` is not part of TSRX control flow; move the try/finally into a function if you need cleanup logic.`,
|
|
4461
4500
|
transform_context.filename,
|
|
4462
4501
|
finalizer,
|
|
4463
4502
|
transform_context.errors,
|
|
@@ -4467,7 +4506,7 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
4467
4506
|
|
|
4468
4507
|
if (!pending && !handler) {
|
|
4469
4508
|
error(
|
|
4470
|
-
'
|
|
4509
|
+
'TSRX try statements must have a `pending` or `catch` block.',
|
|
4471
4510
|
transform_context.filename,
|
|
4472
4511
|
node,
|
|
4473
4512
|
transform_context.errors,
|
|
@@ -4491,7 +4530,7 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
4491
4530
|
const try_body = node.block.body || [];
|
|
4492
4531
|
if (!try_body.some(is_jsx_child)) {
|
|
4493
4532
|
error(
|
|
4494
|
-
'
|
|
4533
|
+
'TSRX try statements must contain a template in their main body. Move the try statement into a function if it does not render anything.',
|
|
4495
4534
|
transform_context.filename,
|
|
4496
4535
|
node.block,
|
|
4497
4536
|
transform_context.errors,
|
|
@@ -4501,7 +4540,7 @@ function try_statement_to_jsx_child(node, transform_context) {
|
|
|
4501
4540
|
const pending_body = pending.body || [];
|
|
4502
4541
|
if (pending_body.length > 0 && !pending_body.some(is_jsx_child)) {
|
|
4503
4542
|
error(
|
|
4504
|
-
'
|
|
4543
|
+
'TSRX try statements must contain a template in their "pending" body. Rendering a pending fallback is required to have a template.',
|
|
4505
4544
|
transform_context.filename,
|
|
4506
4545
|
pending,
|
|
4507
4546
|
transform_context.errors,
|
|
@@ -4714,12 +4753,14 @@ function inject_try_imports(program, transform_context, platform, suspense_sourc
|
|
|
4714
4753
|
transform_context.needs_merge_refs && platform.imports.mergeRefs
|
|
4715
4754
|
? platform.imports.mergeRefs
|
|
4716
4755
|
: null;
|
|
4717
|
-
const ref_prop_source =
|
|
4718
|
-
transform_context.needs_ref_prop && platform.imports.refProp ? platform.imports.refProp : null;
|
|
4719
4756
|
const normalize_spread_props_source =
|
|
4720
4757
|
transform_context.needs_normalize_spread_props && platform.imports.refProp
|
|
4721
4758
|
? platform.imports.refProp
|
|
4722
4759
|
: null;
|
|
4760
|
+
const normalize_spread_props_for_ref_attr_source =
|
|
4761
|
+
transform_context.needs_normalize_spread_props_for_ref_attr && platform.imports.refProp
|
|
4762
|
+
? platform.imports.refProp
|
|
4763
|
+
: null;
|
|
4723
4764
|
|
|
4724
4765
|
/** @type {Map<string, any[]>} */
|
|
4725
4766
|
const ref_imports = new Map();
|
|
@@ -4732,19 +4773,22 @@ function inject_try_imports(program, transform_context, platform, suspense_sourc
|
|
|
4732
4773
|
);
|
|
4733
4774
|
}
|
|
4734
4775
|
|
|
4735
|
-
if (
|
|
4776
|
+
if (normalize_spread_props_source !== null) {
|
|
4736
4777
|
add_ref_import_specifier(
|
|
4737
4778
|
ref_imports,
|
|
4738
|
-
|
|
4739
|
-
b.import_specifier('
|
|
4779
|
+
normalize_spread_props_source,
|
|
4780
|
+
b.import_specifier('normalize_spread_props', NORMALIZE_SPREAD_PROPS_INTERNAL_NAME),
|
|
4740
4781
|
);
|
|
4741
4782
|
}
|
|
4742
4783
|
|
|
4743
|
-
if (
|
|
4784
|
+
if (normalize_spread_props_for_ref_attr_source !== null) {
|
|
4744
4785
|
add_ref_import_specifier(
|
|
4745
4786
|
ref_imports,
|
|
4746
|
-
|
|
4747
|
-
b.import_specifier(
|
|
4787
|
+
normalize_spread_props_for_ref_attr_source,
|
|
4788
|
+
b.import_specifier(
|
|
4789
|
+
'normalize_spread_props_for_ref_attr',
|
|
4790
|
+
NORMALIZE_SPREAD_PROPS_FOR_REF_ATTR_INTERNAL_NAME,
|
|
4791
|
+
),
|
|
4748
4792
|
);
|
|
4749
4793
|
}
|
|
4750
4794
|
|
|
@@ -4779,11 +4823,9 @@ function add_ref_import_specifier(imports, source, specifier) {
|
|
|
4779
4823
|
function create_render_if_statement(node, transform_context) {
|
|
4780
4824
|
const consequent_body =
|
|
4781
4825
|
node.consequent.type === 'BlockStatement' ? node.consequent.body : [node.consequent];
|
|
4782
|
-
const consequent_has_hooks =
|
|
4783
|
-
|
|
4784
|
-
transform_context,
|
|
4785
|
-
true,
|
|
4786
|
-
);
|
|
4826
|
+
const consequent_has_hooks =
|
|
4827
|
+
should_extract_hook_helpers(transform_context) &&
|
|
4828
|
+
body_contains_top_level_hook_call(consequent_body, transform_context, true);
|
|
4787
4829
|
|
|
4788
4830
|
let alternate = null;
|
|
4789
4831
|
if (node.alternate) {
|
|
@@ -4791,11 +4833,9 @@ function create_render_if_statement(node, transform_context) {
|
|
|
4791
4833
|
alternate = create_render_if_statement(node.alternate, transform_context);
|
|
4792
4834
|
} else {
|
|
4793
4835
|
const alternate_body = node.alternate.body || [node.alternate];
|
|
4794
|
-
const alternate_has_hooks =
|
|
4795
|
-
|
|
4796
|
-
transform_context,
|
|
4797
|
-
true,
|
|
4798
|
-
);
|
|
4836
|
+
const alternate_has_hooks =
|
|
4837
|
+
should_extract_hook_helpers(transform_context) &&
|
|
4838
|
+
body_contains_top_level_hook_call(alternate_body, transform_context, true);
|
|
4799
4839
|
alternate = set_loc(
|
|
4800
4840
|
b.block(
|
|
4801
4841
|
alternate_has_hooks
|
|
@@ -4912,7 +4952,10 @@ export function plan_switch_lift(switch_node, transform_context) {
|
|
|
4912
4952
|
const needs_helper = case_info.map(
|
|
4913
4953
|
(/** @type {{ own_body: any[], has_terminator: boolean }} */ info, /** @type {number} */ k) => {
|
|
4914
4954
|
if (info.own_body.length === 0) return false;
|
|
4915
|
-
if (
|
|
4955
|
+
if (
|
|
4956
|
+
should_extract_hook_helpers(transform_context) &&
|
|
4957
|
+
body_contains_top_level_hook_call(info.own_body, transform_context, true)
|
|
4958
|
+
) {
|
|
4916
4959
|
return true;
|
|
4917
4960
|
}
|
|
4918
4961
|
if (k === 0) return false;
|
|
@@ -5052,7 +5095,7 @@ function build_switch_with_lift(switch_node, transform_context) {
|
|
|
5052
5095
|
let has_terminal = false;
|
|
5053
5096
|
|
|
5054
5097
|
for (const child of own_body) {
|
|
5055
|
-
if (
|
|
5098
|
+
if (is_loop_skip_return_statement(child)) {
|
|
5056
5099
|
case_body.push(create_component_return_statement(render_nodes, child));
|
|
5057
5100
|
has_terminal = true;
|
|
5058
5101
|
break;
|
|
@@ -5155,13 +5198,13 @@ function to_jsx_expression_container(expression, source_node = expression) {
|
|
|
5155
5198
|
* the default "map over `to_jsx_attribute`" via
|
|
5156
5199
|
* `hooks.transformElementAttributes`. Whether or not the hook is used,
|
|
5157
5200
|
* the result is run through `merge_duplicate_refs` so platforms with a
|
|
5158
|
-
* `multiRefStrategy`
|
|
5201
|
+
* `multiRefStrategy` can compose an explicit `ref={...}` with compiler-
|
|
5202
|
+
* synthesized refs created for host spreads.
|
|
5159
5203
|
*
|
|
5160
5204
|
* Before lowering, the raw attribute list is validated to reject elements
|
|
5161
5205
|
* with more than one TSX-style `ref={...}` attribute — that shape produces
|
|
5162
5206
|
* duplicate JSX props which the JSX runtime collapses to last-wins (and
|
|
5163
|
-
* which TypeScript can't type cleanly).
|
|
5164
|
-
* keyword-form refs remain valid and merge into a single ref attribute.
|
|
5207
|
+
* which TypeScript can't type cleanly).
|
|
5165
5208
|
*
|
|
5166
5209
|
* @param {any[]} attrs
|
|
5167
5210
|
* @param {TransformContext} transform_context
|
|
@@ -5171,7 +5214,6 @@ function to_jsx_expression_container(expression, source_node = expression) {
|
|
|
5171
5214
|
function transform_element_attributes_dispatch(attrs, transform_context, element) {
|
|
5172
5215
|
validate_at_most_one_ref_attribute(attrs, transform_context);
|
|
5173
5216
|
const is_component = is_component_like_element(element);
|
|
5174
|
-
attrs = normalize_named_ref_attributes(attrs, !is_component, transform_context);
|
|
5175
5217
|
const preprocess = transform_context.platform.hooks?.preprocessElementAttributes;
|
|
5176
5218
|
if (preprocess) {
|
|
5177
5219
|
attrs = preprocess(attrs, transform_context, element);
|
|
@@ -5180,43 +5222,12 @@ function transform_element_attributes_dispatch(attrs, transform_context, element
|
|
|
5180
5222
|
const result = hook
|
|
5181
5223
|
? hook(attrs, transform_context, element)
|
|
5182
5224
|
: attrs.map((/** @type {any} */ a) => to_jsx_attribute(a, transform_context));
|
|
5183
|
-
if (transform_context.typeOnly) {
|
|
5184
|
-
add_ref_target_type_to_ref_prop_attributes(
|
|
5185
|
-
result,
|
|
5186
|
-
!is_component ? create_element_ref_target_type(element) : null,
|
|
5187
|
-
);
|
|
5188
|
-
}
|
|
5189
5225
|
return merge_duplicate_refs(
|
|
5190
5226
|
normalize_host_ref_spreads(result, !is_component, transform_context),
|
|
5191
5227
|
transform_context,
|
|
5192
5228
|
);
|
|
5193
5229
|
}
|
|
5194
5230
|
|
|
5195
|
-
/**
|
|
5196
|
-
* @param {any[]} attrs
|
|
5197
|
-
* @param {AST.TypeNode | null} ref_target_type
|
|
5198
|
-
* @returns {void}
|
|
5199
|
-
*/
|
|
5200
|
-
export function add_ref_target_type_to_ref_prop_attributes(attrs, ref_target_type) {
|
|
5201
|
-
if (!ref_target_type) return;
|
|
5202
|
-
for (const attr of attrs) {
|
|
5203
|
-
const expression =
|
|
5204
|
-
attr?.type === 'JSXAttribute' &&
|
|
5205
|
-
attr.value?.type === 'JSXExpressionContainer' &&
|
|
5206
|
-
attr.value.expression?.type !== 'JSXEmptyExpression'
|
|
5207
|
-
? attr.value.expression
|
|
5208
|
-
: null;
|
|
5209
|
-
if (
|
|
5210
|
-
expression?.type === 'CallExpression' &&
|
|
5211
|
-
expression.callee?.type === 'Identifier' &&
|
|
5212
|
-
expression.callee.name === CREATE_REF_PROP_INTERNAL_NAME &&
|
|
5213
|
-
!expression.typeArguments
|
|
5214
|
-
) {
|
|
5215
|
-
expression.typeArguments = b.ts_type_parameter_instantiation([ref_target_type]);
|
|
5216
|
-
}
|
|
5217
|
-
}
|
|
5218
|
-
}
|
|
5219
|
-
|
|
5220
5231
|
/**
|
|
5221
5232
|
* @param {any} element
|
|
5222
5233
|
* @returns {boolean}
|
|
@@ -5242,48 +5253,6 @@ function is_component_like_jsx_name(name) {
|
|
|
5242
5253
|
return false;
|
|
5243
5254
|
}
|
|
5244
5255
|
|
|
5245
|
-
/**
|
|
5246
|
-
* @param {any[]} attrs
|
|
5247
|
-
* @param {boolean} is_host
|
|
5248
|
-
* @param {TransformContext} transform_context
|
|
5249
|
-
* @returns {any[]}
|
|
5250
|
-
*/
|
|
5251
|
-
function normalize_named_ref_attributes(attrs, is_host, transform_context) {
|
|
5252
|
-
if (!is_host) return attrs;
|
|
5253
|
-
|
|
5254
|
-
return attrs.map((attr) => {
|
|
5255
|
-
if (!is_named_ref_attribute(attr)) {
|
|
5256
|
-
return attr;
|
|
5257
|
-
}
|
|
5258
|
-
|
|
5259
|
-
if (transform_context.typeOnly) {
|
|
5260
|
-
return mark_type_only_named_ref_attribute(attr);
|
|
5261
|
-
}
|
|
5262
|
-
|
|
5263
|
-
return {
|
|
5264
|
-
...attr,
|
|
5265
|
-
metadata: { ...(attr.metadata || {}), from_ref_keyword: true },
|
|
5266
|
-
name: attr.name?.type === 'JSXIdentifier' ? { ...attr.name, name: 'ref' } : b.id('ref'),
|
|
5267
|
-
};
|
|
5268
|
-
});
|
|
5269
|
-
}
|
|
5270
|
-
|
|
5271
|
-
/**
|
|
5272
|
-
* @param {any} attr
|
|
5273
|
-
* @returns {any}
|
|
5274
|
-
*/
|
|
5275
|
-
function mark_type_only_named_ref_attribute(attr) {
|
|
5276
|
-
return {
|
|
5277
|
-
...attr,
|
|
5278
|
-
name: attr.name
|
|
5279
|
-
? {
|
|
5280
|
-
...attr.name,
|
|
5281
|
-
metadata: { ...(attr.name.metadata || {}), disable_verification: true },
|
|
5282
|
-
}
|
|
5283
|
-
: attr.name,
|
|
5284
|
-
};
|
|
5285
|
-
}
|
|
5286
|
-
|
|
5287
5256
|
/**
|
|
5288
5257
|
* @param {any[]} attrs
|
|
5289
5258
|
* @param {boolean} is_host
|
|
@@ -5305,8 +5274,15 @@ function normalize_host_ref_spreads(attrs, is_host, transform_context) {
|
|
|
5305
5274
|
return [attr];
|
|
5306
5275
|
}
|
|
5307
5276
|
|
|
5308
|
-
|
|
5309
|
-
|
|
5277
|
+
const normalize_helper = needs_synthetic_spread_ref
|
|
5278
|
+
? NORMALIZE_SPREAD_PROPS_FOR_REF_ATTR_INTERNAL_NAME
|
|
5279
|
+
: NORMALIZE_SPREAD_PROPS_INTERNAL_NAME;
|
|
5280
|
+
if (needs_synthetic_spread_ref) {
|
|
5281
|
+
transform_context.needs_normalize_spread_props_for_ref_attr = true;
|
|
5282
|
+
} else {
|
|
5283
|
+
transform_context.needs_normalize_spread_props = true;
|
|
5284
|
+
}
|
|
5285
|
+
const normalized = b.call(normalize_helper, attr.argument);
|
|
5310
5286
|
|
|
5311
5287
|
if (needs_synthetic_spread_ref) {
|
|
5312
5288
|
const normalized_id = create_generated_identifier(
|
|
@@ -5323,7 +5299,7 @@ function normalize_host_ref_spreads(attrs, is_host, transform_context) {
|
|
|
5323
5299
|
attr,
|
|
5324
5300
|
);
|
|
5325
5301
|
ref_attr.metadata = { ...(ref_attr.metadata || {}) };
|
|
5326
|
-
/** @type {any} */ (ref_attr.metadata).
|
|
5302
|
+
/** @type {any} */ (ref_attr.metadata).synthetic_ref = true;
|
|
5327
5303
|
add_jsx_setup_declaration(spread, b.let(clone_identifier(normalized_id), normalized));
|
|
5328
5304
|
|
|
5329
5305
|
return [spread, ref_attr];
|
|
@@ -5412,69 +5388,10 @@ function wrap_jsx_setup_declarations(expression, in_jsx_child) {
|
|
|
5412
5388
|
return in_jsx_child ? to_jsx_expression_container(call, expression) : call;
|
|
5413
5389
|
}
|
|
5414
5390
|
|
|
5415
|
-
/**
|
|
5416
|
-
* @param {any} attr
|
|
5417
|
-
* @returns {boolean}
|
|
5418
|
-
*/
|
|
5419
|
-
function is_named_ref_attribute(attr) {
|
|
5420
|
-
return !!(
|
|
5421
|
-
attr &&
|
|
5422
|
-
(attr.type === 'Attribute' || attr.type === 'JSXAttribute') &&
|
|
5423
|
-
attr.name &&
|
|
5424
|
-
((attr.name.type === 'Identifier' && attr.name.name !== 'ref') ||
|
|
5425
|
-
(attr.name.type === 'JSXIdentifier' && attr.name.name !== 'ref')) &&
|
|
5426
|
-
(attr.value?.type === 'RefExpression' ||
|
|
5427
|
-
is_ref_prop_expression(attr.value) ||
|
|
5428
|
-
(attr.value?.type === 'JSXExpressionContainer' &&
|
|
5429
|
-
is_ref_prop_expression(attr.value.expression)))
|
|
5430
|
-
);
|
|
5431
|
-
}
|
|
5432
|
-
|
|
5433
|
-
/**
|
|
5434
|
-
* @param {any} html_expression
|
|
5435
|
-
* @param {any} source_attr
|
|
5436
|
-
* @param {TransformContext} transform_context
|
|
5437
|
-
* @returns {any}
|
|
5438
|
-
*/
|
|
5439
|
-
export function create_host_html_attribute(html_expression, source_attr, transform_context) {
|
|
5440
|
-
const expression =
|
|
5441
|
-
html_expression?.type === 'Html' ? html_expression.expression : html_expression;
|
|
5442
|
-
const name = get_host_html_attribute_name(transform_context);
|
|
5443
|
-
const value =
|
|
5444
|
-
name === 'dangerouslySetInnerHTML'
|
|
5445
|
-
? set_loc(b.object([b.prop('init', b.id('__html'), expression)]), source_attr)
|
|
5446
|
-
: expression;
|
|
5447
|
-
const value_container = to_jsx_expression_container(value, source_attr);
|
|
5448
|
-
if (name !== 'dangerouslySetInnerHTML') {
|
|
5449
|
-
setLocation(value_container, source_attr, true);
|
|
5450
|
-
}
|
|
5451
|
-
|
|
5452
|
-
return set_loc(
|
|
5453
|
-
build_jsx_attribute(b.jsx_id(name), value_container, false, source_attr),
|
|
5454
|
-
source_attr,
|
|
5455
|
-
);
|
|
5456
|
-
}
|
|
5457
|
-
|
|
5458
|
-
/**
|
|
5459
|
-
* @param {any} expression
|
|
5460
|
-
* @returns {boolean}
|
|
5461
|
-
*/
|
|
5462
|
-
export function is_ref_prop_expression(expression) {
|
|
5463
|
-
return (
|
|
5464
|
-
expression?.type === 'RefExpression' ||
|
|
5465
|
-
(expression?.type === 'CallExpression' &&
|
|
5466
|
-
expression.callee?.type === 'Identifier' &&
|
|
5467
|
-
expression.callee.name === CREATE_REF_PROP_INTERNAL_NAME)
|
|
5468
|
-
);
|
|
5469
|
-
}
|
|
5470
|
-
|
|
5471
5391
|
/**
|
|
5472
5392
|
* Reject elements with more than one TSX-style `ref={...}` attribute.
|
|
5473
|
-
*
|
|
5474
|
-
*
|
|
5475
|
-
* feature that compose via the merge pass. This validator runs over the
|
|
5476
|
-
* raw, pre-lowering attribute list so each shape is still distinguishable
|
|
5477
|
-
* by `type`. Ripple `Element` attributes have type `Attribute` with an
|
|
5393
|
+
* This validator runs over the raw, pre-lowering attribute list so each
|
|
5394
|
+
* shape is still distinguishable by `type`. Ripple `Element` attributes have type `Attribute` with an
|
|
5478
5395
|
* `Identifier` name (the parser normalizes `JSXAttribute`/`JSXIdentifier`
|
|
5479
5396
|
* for non-Tsx elements); inside `<tsx:react>` compat blocks they retain
|
|
5480
5397
|
* the original `JSXAttribute`/`JSXIdentifier` shape, so we accept both.
|
|
@@ -5510,7 +5427,7 @@ export function validate_at_most_one_ref_attribute(raw_attrs, transform_context)
|
|
|
5510
5427
|
}
|
|
5511
5428
|
error(
|
|
5512
5429
|
'Element has multiple `ref={...}` attributes; an element may have at most one. ' +
|
|
5513
|
-
|
|
5430
|
+
'Use a single array-valued ref such as `ref={[a, b]}` where the target framework supports multiple refs.',
|
|
5514
5431
|
transform_context?.filename ?? null,
|
|
5515
5432
|
node,
|
|
5516
5433
|
transform_context?.errors,
|
|
@@ -5520,11 +5437,9 @@ export function validate_at_most_one_ref_attribute(raw_attrs, transform_context)
|
|
|
5520
5437
|
}
|
|
5521
5438
|
|
|
5522
5439
|
/**
|
|
5523
|
-
* Collapse
|
|
5524
|
-
*
|
|
5525
|
-
*
|
|
5526
|
-
* by `to_jsx_attribute` (Ripple) or the parser (TSX-style). The shape of
|
|
5527
|
-
* the merged value depends on `platform.jsx.multiRefStrategy`:
|
|
5440
|
+
* Collapse an explicit `ref={...}` plus compiler-synthesized spread refs into
|
|
5441
|
+
* one attribute. The shape of the merged value depends on
|
|
5442
|
+
* `platform.jsx.multiRefStrategy`:
|
|
5528
5443
|
*
|
|
5529
5444
|
* - `'merge-refs'` — emit `ref={__mergeRefs(a, b, ...)}` and flag
|
|
5530
5445
|
* `needs_merge_refs` so an import is injected later. React and Preact
|
|
@@ -5550,7 +5465,7 @@ export function merge_duplicate_refs(jsx_attrs, transform_context) {
|
|
|
5550
5465
|
for (const attr of jsx_attrs) {
|
|
5551
5466
|
if (!is_jsx_ref_attribute(attr)) continue;
|
|
5552
5467
|
count += 1;
|
|
5553
|
-
if (!attr.metadata?.
|
|
5468
|
+
if (!attr.metadata?.synthetic_ref) tsx_form_count += 1;
|
|
5554
5469
|
}
|
|
5555
5470
|
if (count <= 1) return jsx_attrs;
|
|
5556
5471
|
// Two or more genuine `ref={...}` (TSX-form) attributes are already a
|
|
@@ -5571,7 +5486,7 @@ export function merge_duplicate_refs(jsx_attrs, transform_context) {
|
|
|
5571
5486
|
// Inherit loc from the (at most one) `ref={expr}`-form attribute so
|
|
5572
5487
|
// the kept `ref` keyword in the generated `ref={__mergeRefs(...)}`
|
|
5573
5488
|
// retains a source mapping back to its original `ref=` keyword.
|
|
5574
|
-
if (!source_attr && !attr.metadata?.
|
|
5489
|
+
if (!source_attr && !attr.metadata?.synthetic_ref) {
|
|
5575
5490
|
source_attr = attr;
|
|
5576
5491
|
}
|
|
5577
5492
|
} else {
|
|
@@ -5629,8 +5544,9 @@ function is_jsx_ref_attribute(attr) {
|
|
|
5629
5544
|
* identifiers and avoids shadowing user-declared `mergeRefs` symbols.
|
|
5630
5545
|
*/
|
|
5631
5546
|
export const MERGE_REFS_INTERNAL_NAME = '__mergeRefs';
|
|
5632
|
-
export const CREATE_REF_PROP_INTERNAL_NAME = '__create_ref_prop';
|
|
5633
5547
|
export const NORMALIZE_SPREAD_PROPS_INTERNAL_NAME = '__normalize_spread_props';
|
|
5548
|
+
export const NORMALIZE_SPREAD_PROPS_FOR_REF_ATTR_INTERNAL_NAME =
|
|
5549
|
+
'__normalize_spread_props_for_ref_attr';
|
|
5634
5550
|
export const MAP_ITERABLE_INTERNAL_NAME = '__map_iterable';
|
|
5635
5551
|
export const ITERATION_VALUE_INTERNAL_NAME = '__IterationValue';
|
|
5636
5552
|
|
|
@@ -5652,17 +5568,6 @@ const MATHML_REF_TAG_NAMES = new Set(
|
|
|
5652
5568
|
),
|
|
5653
5569
|
);
|
|
5654
5570
|
|
|
5655
|
-
/**
|
|
5656
|
-
* @param {any} value
|
|
5657
|
-
* @returns {boolean}
|
|
5658
|
-
*/
|
|
5659
|
-
export function is_ref_expression_attribute_value(value) {
|
|
5660
|
-
return (
|
|
5661
|
-
value?.type === 'RefExpression' ||
|
|
5662
|
-
(value?.type === 'JSXExpressionContainer' && value.expression?.type === 'RefExpression')
|
|
5663
|
-
);
|
|
5664
|
-
}
|
|
5665
|
-
|
|
5666
5571
|
/**
|
|
5667
5572
|
* @param {any} element
|
|
5668
5573
|
* @param {'html' | 'svg' | 'mathml'} [namespace]
|
|
@@ -5751,27 +5656,6 @@ function create_tag_name_map_ref_type(map_name, tag_name) {
|
|
|
5751
5656
|
export function to_jsx_attribute(attr, transform_context) {
|
|
5752
5657
|
if (!attr) return attr;
|
|
5753
5658
|
if (attr.type === 'JSXAttribute') {
|
|
5754
|
-
if (
|
|
5755
|
-
attr.value?.type === 'JSXExpressionContainer' &&
|
|
5756
|
-
attr.value.expression?.type === 'RefExpression'
|
|
5757
|
-
) {
|
|
5758
|
-
return {
|
|
5759
|
-
...attr,
|
|
5760
|
-
value: to_jsx_expression_container(
|
|
5761
|
-
create_ref_prop_call(attr.value.expression, transform_context),
|
|
5762
|
-
),
|
|
5763
|
-
metadata: { ...(attr.metadata || {}), from_ref_keyword: true },
|
|
5764
|
-
};
|
|
5765
|
-
}
|
|
5766
|
-
if (
|
|
5767
|
-
attr.value?.type === 'JSXExpressionContainer' &&
|
|
5768
|
-
is_ref_prop_expression(attr.value.expression)
|
|
5769
|
-
) {
|
|
5770
|
-
return {
|
|
5771
|
-
...attr,
|
|
5772
|
-
metadata: { ...(attr.metadata || {}), from_ref_keyword: true },
|
|
5773
|
-
};
|
|
5774
|
-
}
|
|
5775
5659
|
return attr;
|
|
5776
5660
|
}
|
|
5777
5661
|
if (attr.type === 'JSXSpreadAttribute') {
|
|
@@ -5786,28 +5670,8 @@ export function to_jsx_attribute(attr, transform_context) {
|
|
|
5786
5670
|
attr,
|
|
5787
5671
|
);
|
|
5788
5672
|
}
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
// so the source-to-generated mapping is imprecise — but pointing
|
|
5792
|
-
// editors at the `{ref expr}` span is still useful for hover/jump,
|
|
5793
|
-
// matching how shorthand `{name}` → `name={name}` carries loc.
|
|
5794
|
-
// `from_ref_keyword` lets `merge_duplicate_refs` tell this form apart
|
|
5795
|
-
// from genuine `ref={...}` attributes without inferring it from
|
|
5796
|
-
// whether `name.loc` happens to be present.
|
|
5797
|
-
return set_loc(
|
|
5798
|
-
/** @type {any} */ ({
|
|
5799
|
-
type: 'JSXAttribute',
|
|
5800
|
-
name: { type: 'JSXIdentifier', name: 'ref', metadata: { path: [] } },
|
|
5801
|
-
value: to_jsx_expression_container(attr.argument),
|
|
5802
|
-
shorthand: false,
|
|
5803
|
-
metadata: { path: [], from_ref_keyword: true },
|
|
5804
|
-
}),
|
|
5805
|
-
attr,
|
|
5806
|
-
);
|
|
5807
|
-
}
|
|
5808
|
-
|
|
5809
|
-
// Platforms that expect React-style DOM attrs (React) rewrite `class` to
|
|
5810
|
-
// `className`; Preact and Solid accept `class` natively and keep it.
|
|
5673
|
+
// Keep this legacy hook for targets that need React-style DOM attrs. The
|
|
5674
|
+
// current first-party targets preserve authored `class`.
|
|
5811
5675
|
let attr_name = attr.name;
|
|
5812
5676
|
if (
|
|
5813
5677
|
transform_context.platform.jsx.rewriteClassAttr &&
|
|
@@ -5824,28 +5688,15 @@ export function to_jsx_attribute(attr, transform_context) {
|
|
|
5824
5688
|
attr_name && attr_name.type === 'Identifier' ? identifier_to_jsx_name(attr_name) : attr_name;
|
|
5825
5689
|
|
|
5826
5690
|
let value = attr.value;
|
|
5827
|
-
const is_ref_expression_value =
|
|
5828
|
-
value?.type === 'RefExpression' ||
|
|
5829
|
-
is_ref_prop_expression(value) ||
|
|
5830
|
-
(value?.type === 'JSXExpressionContainer' && is_ref_prop_expression(value.expression));
|
|
5831
5691
|
if (value) {
|
|
5832
5692
|
if (value.type === 'Literal' && typeof value.value === 'string') {
|
|
5833
5693
|
// Keep string literal as attribute string.
|
|
5834
|
-
} else if (value.type === 'RefExpression') {
|
|
5835
|
-
value = to_jsx_expression_container(create_ref_prop_call(value, transform_context));
|
|
5836
5694
|
} else if (value.type !== 'JSXExpressionContainer') {
|
|
5837
5695
|
value = to_jsx_expression_container(value);
|
|
5838
|
-
} else if (value.expression?.type === 'RefExpression') {
|
|
5839
|
-
value = to_jsx_expression_container(
|
|
5840
|
-
create_ref_prop_call(value.expression, transform_context),
|
|
5841
|
-
);
|
|
5842
5696
|
}
|
|
5843
5697
|
}
|
|
5844
5698
|
|
|
5845
5699
|
const jsx_attribute = build_jsx_attribute(name, value || null, attr.shorthand === true);
|
|
5846
|
-
if (is_ref_expression_value) {
|
|
5847
|
-
/** @type {any} */ (jsx_attribute.metadata).from_ref_keyword = true;
|
|
5848
|
-
}
|
|
5849
5700
|
|
|
5850
5701
|
if (value_has_unmappable_jsx_loc(value)) {
|
|
5851
5702
|
/** @type {any} */ (jsx_attribute.metadata).has_unmappable_value = true;
|
|
@@ -5867,26 +5718,6 @@ function value_has_unmappable_jsx_loc(value) {
|
|
|
5867
5718
|
);
|
|
5868
5719
|
}
|
|
5869
5720
|
|
|
5870
|
-
/**
|
|
5871
|
-
* @param {any} node
|
|
5872
|
-
* @param {TransformContext} transform_context
|
|
5873
|
-
* @returns {any}
|
|
5874
|
-
*/
|
|
5875
|
-
function create_ref_prop_call(node, transform_context) {
|
|
5876
|
-
transform_context.needs_ref_prop = true;
|
|
5877
|
-
|
|
5878
|
-
const argument = node.argument;
|
|
5879
|
-
const args = [b.thunk(argument)];
|
|
5880
|
-
|
|
5881
|
-
if (argument.type === 'Identifier' || argument.type === 'MemberExpression') {
|
|
5882
|
-
args.push(
|
|
5883
|
-
b.arrow([b.id('v')], b.assignment('=', clone_expression_node(argument, false), b.id('v'))),
|
|
5884
|
-
);
|
|
5885
|
-
}
|
|
5886
|
-
|
|
5887
|
-
return b.call(CREATE_REF_PROP_INTERNAL_NAME, ...args);
|
|
5888
|
-
}
|
|
5889
|
-
|
|
5890
5721
|
/**
|
|
5891
5722
|
* @param {any} node
|
|
5892
5723
|
* @param {TransformContext} transform_context
|