@tsrx/core 0.0.12 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/analyze/validation.js +47 -0
- package/src/index.js +11 -1
- package/src/plugin.js +295 -24
- package/src/transform/jsx/helpers.js +25 -1
- package/src/transform/jsx/index.js +666 -425
- package/src/transform/lazy.js +185 -13
- package/src/transform/segments.js +134 -19
- package/src/utils/ast.js +61 -0
- package/types/index.d.ts +17 -0
- package/types/parse.d.ts +1 -1
|
@@ -35,6 +35,8 @@ import {
|
|
|
35
35
|
} from '../lazy.js';
|
|
36
36
|
import { find_first_top_level_await_in_component_body } from '../await.js';
|
|
37
37
|
import { prepare_stylesheet_for_render, annotate_component_with_hash } from '../scoping.js';
|
|
38
|
+
import { validate_component_return_statement } from '../../analyze/validation.js';
|
|
39
|
+
import { get_component_from_path } from '../../utils/ast.js';
|
|
38
40
|
import {
|
|
39
41
|
is_interleaved_body as is_interleaved_body_core,
|
|
40
42
|
is_capturable_jsx_child,
|
|
@@ -48,7 +50,7 @@ import { is_hoist_safe_jsx_node } from '../jsx-hoist.js';
|
|
|
48
50
|
* local_statement_component_index: number,
|
|
49
51
|
* needs_error_boundary: boolean,
|
|
50
52
|
* needs_suspense: boolean,
|
|
51
|
-
* helper_state: { base_name: string, next_id: number, helpers:
|
|
53
|
+
* helper_state: { base_name: string, next_id: number, helpers: any[], statics: any[] } | null,
|
|
52
54
|
* available_bindings: Map<string, AST.Identifier>,
|
|
53
55
|
* lazy_next_id: number,
|
|
54
56
|
* current_css_hash: string | null,
|
|
@@ -112,8 +114,17 @@ export function createJsxTransform(platform) {
|
|
|
112
114
|
preallocate_lazy_ids(/** @type {any} */ (ast), transform_context);
|
|
113
115
|
|
|
114
116
|
walk(/** @type {any} */ (ast), transform_context, {
|
|
117
|
+
ReturnStatement(node, { next, path }) {
|
|
118
|
+
if (get_component_from_path(path)) {
|
|
119
|
+
validate_component_return_statement(node, filename);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return next();
|
|
123
|
+
},
|
|
124
|
+
|
|
115
125
|
Component(node, { next, state }) {
|
|
116
126
|
const as_any = /** @type {any} */ (node);
|
|
127
|
+
|
|
117
128
|
const await_expression = find_first_top_level_await_in_component_body(as_any.body || []);
|
|
118
129
|
|
|
119
130
|
if (await_expression) {
|
|
@@ -170,17 +181,12 @@ export function createJsxTransform(platform) {
|
|
|
170
181
|
state.current_css_hash = as_any.css ? as_any.css.hash : null;
|
|
171
182
|
|
|
172
183
|
// Pre-collect component body bindings (params + top-level statements)
|
|
173
|
-
// so
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
// Only collect up to the split point — bindings declared after a
|
|
177
|
-
// hook-safe split aren't in scope at the return statement and would
|
|
178
|
-
// cause ReferenceErrors if passed as helper props.
|
|
184
|
+
// so Element children processed during the bottom-up walk can see
|
|
185
|
+
// component-scope names. Hook-safe helpers filter this set down to
|
|
186
|
+
// the names their body actually references before generating props.
|
|
179
187
|
const body_bindings = collect_param_bindings(as_any.params || []);
|
|
180
188
|
const body = as_any.body || [];
|
|
181
|
-
|
|
182
|
-
const collect_end = split_index === -1 ? body.length : split_index;
|
|
183
|
-
for (let i = 0; i < collect_end; i += 1) {
|
|
189
|
+
for (let i = 0; i < body.length; i += 1) {
|
|
184
190
|
collect_statement_bindings(body[i], body_bindings);
|
|
185
191
|
}
|
|
186
192
|
state.available_bindings = body_bindings;
|
|
@@ -347,12 +353,7 @@ function component_to_function_declaration(component, transform_context, walk_he
|
|
|
347
353
|
transform_context.helper_state = helper_state;
|
|
348
354
|
transform_context.available_bindings = new Map(param_bindings);
|
|
349
355
|
|
|
350
|
-
const body_statements = build_component_statements(
|
|
351
|
-
body,
|
|
352
|
-
helper_state,
|
|
353
|
-
param_bindings,
|
|
354
|
-
transform_context,
|
|
355
|
-
);
|
|
356
|
+
const body_statements = build_component_statements(body, transform_context);
|
|
356
357
|
|
|
357
358
|
// Replace lazy param patterns with generated identifiers
|
|
358
359
|
const final_params = lazy_bindings.size > 0 ? replace_lazy_params(params) : params;
|
|
@@ -371,6 +372,7 @@ function component_to_function_declaration(component, transform_context, walk_he
|
|
|
371
372
|
const fn = /** @type {any} */ ({
|
|
372
373
|
type: 'FunctionDeclaration',
|
|
373
374
|
id: component.id,
|
|
375
|
+
typeParameters: component.typeParameters,
|
|
374
376
|
params: final_params,
|
|
375
377
|
body: final_body,
|
|
376
378
|
async: is_async_component,
|
|
@@ -401,107 +403,11 @@ function component_to_function_declaration(component, transform_context, walk_he
|
|
|
401
403
|
|
|
402
404
|
/**
|
|
403
405
|
* @param {any[]} body_nodes
|
|
404
|
-
* @param {{ base_name: string, next_id: number, helpers: AST.FunctionDeclaration[], statics: any[] }} helper_state
|
|
405
|
-
* @param {Map<string, AST.Identifier>} available_bindings
|
|
406
406
|
* @param {TransformContext} transform_context
|
|
407
407
|
* @returns {any[]}
|
|
408
408
|
*/
|
|
409
|
-
function build_component_statements(
|
|
410
|
-
body_nodes,
|
|
411
|
-
helper_state,
|
|
412
|
-
available_bindings,
|
|
413
|
-
transform_context,
|
|
414
|
-
) {
|
|
415
|
-
const split_index = find_hook_safe_split_index(body_nodes);
|
|
416
|
-
if (split_index === -1) {
|
|
417
|
-
return build_render_statements(body_nodes, false, transform_context);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
const statements = [];
|
|
421
|
-
const render_nodes = [];
|
|
422
|
-
const bindings = new Map(available_bindings);
|
|
423
|
-
|
|
424
|
-
const pre_split_body = body_nodes.slice(0, split_index);
|
|
425
|
-
const interleaved = is_interleaved_body(pre_split_body);
|
|
426
|
-
let capture_index = 0;
|
|
427
|
-
|
|
428
|
-
for (let i = 0; i < split_index; i += 1) {
|
|
429
|
-
const child = body_nodes[i];
|
|
430
|
-
|
|
431
|
-
if (is_bare_return_statement(child)) {
|
|
432
|
-
statements.push(create_component_return_statement(render_nodes, child));
|
|
433
|
-
return statements;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
if (is_lone_return_if_statement(child)) {
|
|
437
|
-
statements.push(create_component_lone_return_if_statement(child, render_nodes));
|
|
438
|
-
continue;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
if (is_jsx_child(child)) {
|
|
442
|
-
const jsx = to_jsx_child(child, transform_context);
|
|
443
|
-
if (interleaved && is_capturable_jsx_child(jsx)) {
|
|
444
|
-
const { declaration, reference } = captureJsxChild(jsx, capture_index++);
|
|
445
|
-
statements.push(declaration);
|
|
446
|
-
render_nodes.push(reference);
|
|
447
|
-
} else {
|
|
448
|
-
render_nodes.push(jsx);
|
|
449
|
-
}
|
|
450
|
-
} else {
|
|
451
|
-
statements.push(child);
|
|
452
|
-
collect_statement_bindings(child, bindings);
|
|
453
|
-
transform_context.available_bindings = bindings;
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
if (!interleaved) {
|
|
458
|
-
hoist_static_render_nodes(render_nodes, transform_context);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
const split_node = body_nodes[split_index];
|
|
462
|
-
const consequent_body =
|
|
463
|
-
split_node.consequent.type === 'BlockStatement'
|
|
464
|
-
? split_node.consequent.body
|
|
465
|
-
: [split_node.consequent];
|
|
466
|
-
const short_branch_body = consequent_body.filter(
|
|
467
|
-
(/** @type {any} */ child) => !is_bare_return_statement(child),
|
|
468
|
-
);
|
|
469
|
-
const continuation_body = body_nodes.slice(split_index + 1);
|
|
470
|
-
const short_branch = create_helper_component_expression(
|
|
471
|
-
short_branch_body,
|
|
472
|
-
helper_state,
|
|
473
|
-
bindings,
|
|
474
|
-
split_node.consequent,
|
|
475
|
-
'Exit',
|
|
476
|
-
transform_context,
|
|
477
|
-
);
|
|
478
|
-
const continuation = create_helper_component_expression(
|
|
479
|
-
continuation_body,
|
|
480
|
-
helper_state,
|
|
481
|
-
bindings,
|
|
482
|
-
split_node,
|
|
483
|
-
'Continue',
|
|
484
|
-
transform_context,
|
|
485
|
-
);
|
|
486
|
-
|
|
487
|
-
render_nodes.push(
|
|
488
|
-
to_jsx_expression_container(
|
|
489
|
-
set_loc(
|
|
490
|
-
/** @type {any} */ ({
|
|
491
|
-
type: 'ConditionalExpression',
|
|
492
|
-
test: split_node.test,
|
|
493
|
-
consequent: short_branch,
|
|
494
|
-
alternate: continuation,
|
|
495
|
-
metadata: { path: [] },
|
|
496
|
-
}),
|
|
497
|
-
split_node,
|
|
498
|
-
),
|
|
499
|
-
split_node,
|
|
500
|
-
),
|
|
501
|
-
);
|
|
502
|
-
|
|
503
|
-
statements.push(create_component_return_statement(render_nodes, split_node));
|
|
504
|
-
return statements;
|
|
409
|
+
function build_component_statements(body_nodes, transform_context) {
|
|
410
|
+
return build_render_statements(body_nodes, false, transform_context);
|
|
505
411
|
}
|
|
506
412
|
|
|
507
413
|
/**
|
|
@@ -527,15 +433,40 @@ function build_render_statements(body_nodes, return_null_when_empty, transform_c
|
|
|
527
433
|
const interleaved = is_interleaved_body(body_nodes);
|
|
528
434
|
let capture_index = 0;
|
|
529
435
|
|
|
530
|
-
for (
|
|
436
|
+
for (let i = 0; i < body_nodes.length; i += 1) {
|
|
437
|
+
const child = body_nodes[i];
|
|
438
|
+
|
|
531
439
|
if (is_bare_return_statement(child)) {
|
|
532
440
|
statements.push(create_component_return_statement(render_nodes, child));
|
|
533
|
-
|
|
534
|
-
|
|
441
|
+
render_nodes.length = 0;
|
|
442
|
+
continue;
|
|
535
443
|
}
|
|
536
444
|
|
|
537
|
-
if (
|
|
538
|
-
|
|
445
|
+
if (is_returning_if_statement(child)) {
|
|
446
|
+
const branch_has_hooks = body_contains_top_level_hook_call(get_if_consequent_body(child));
|
|
447
|
+
const continuation_has_hooks = body_contains_top_level_hook_call(body_nodes.slice(i + 1));
|
|
448
|
+
|
|
449
|
+
if (branch_has_hooks || continuation_has_hooks) {
|
|
450
|
+
statements.push(
|
|
451
|
+
...create_component_helper_split_returning_if_statements(
|
|
452
|
+
child,
|
|
453
|
+
body_nodes.slice(i + 1),
|
|
454
|
+
render_nodes,
|
|
455
|
+
transform_context,
|
|
456
|
+
),
|
|
457
|
+
);
|
|
458
|
+
transform_context.available_bindings = saved_bindings;
|
|
459
|
+
return statements;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (is_lone_return_if_statement(child)) {
|
|
463
|
+
statements.push(create_component_lone_return_if_statement(child, render_nodes));
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
statements.push(
|
|
468
|
+
create_component_returning_if_statement(child, render_nodes, transform_context),
|
|
469
|
+
);
|
|
539
470
|
continue;
|
|
540
471
|
}
|
|
541
472
|
|
|
@@ -586,24 +517,6 @@ function is_interleaved_body(body_nodes) {
|
|
|
586
517
|
return is_interleaved_body_core(filtered, is_jsx_child);
|
|
587
518
|
}
|
|
588
519
|
|
|
589
|
-
/**
|
|
590
|
-
* @param {any[]} body_nodes
|
|
591
|
-
* @returns {number}
|
|
592
|
-
*/
|
|
593
|
-
function find_hook_safe_split_index(body_nodes) {
|
|
594
|
-
for (let i = 0; i < body_nodes.length; i += 1) {
|
|
595
|
-
if (!is_lone_return_if_statement(body_nodes[i])) {
|
|
596
|
-
continue;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
if (body_contains_top_level_hook_call(body_nodes.slice(i + 1))) {
|
|
600
|
-
return i;
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
return -1;
|
|
605
|
-
}
|
|
606
|
-
|
|
607
520
|
/**
|
|
608
521
|
* @param {any[]} body_nodes
|
|
609
522
|
* @returns {boolean}
|
|
@@ -698,96 +611,6 @@ function is_hook_callee(callee) {
|
|
|
698
611
|
return false;
|
|
699
612
|
}
|
|
700
613
|
|
|
701
|
-
/**
|
|
702
|
-
* @param {any[]} body_nodes
|
|
703
|
-
* @param {{ base_name: string, next_id: number, helpers: AST.FunctionDeclaration[], statics: any[] }} helper_state
|
|
704
|
-
* @param {Map<string, AST.Identifier>} available_bindings
|
|
705
|
-
* @param {any} source_node
|
|
706
|
-
* @param {string} suffix
|
|
707
|
-
* @param {TransformContext} transform_context
|
|
708
|
-
* @returns {any}
|
|
709
|
-
*/
|
|
710
|
-
function create_helper_component_expression(
|
|
711
|
-
body_nodes,
|
|
712
|
-
helper_state,
|
|
713
|
-
available_bindings,
|
|
714
|
-
source_node,
|
|
715
|
-
suffix,
|
|
716
|
-
transform_context,
|
|
717
|
-
) {
|
|
718
|
-
if (body_nodes.length === 0) {
|
|
719
|
-
return create_null_literal();
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
const helper_name = create_helper_name(helper_state, suffix);
|
|
723
|
-
const helper_id = set_loc(create_generated_identifier(helper_name), source_node);
|
|
724
|
-
const helper_bindings = Array.from(available_bindings.values());
|
|
725
|
-
const helper_fn = create_helper_function_declaration(
|
|
726
|
-
helper_id,
|
|
727
|
-
body_nodes,
|
|
728
|
-
helper_state,
|
|
729
|
-
available_bindings,
|
|
730
|
-
helper_bindings,
|
|
731
|
-
source_node,
|
|
732
|
-
transform_context,
|
|
733
|
-
);
|
|
734
|
-
|
|
735
|
-
helper_state.helpers.push(helper_fn);
|
|
736
|
-
|
|
737
|
-
return create_helper_component_element(helper_id, helper_bindings, source_node);
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
/**
|
|
741
|
-
* @param {AST.Identifier} helper_id
|
|
742
|
-
* @param {any[]} body_nodes
|
|
743
|
-
* @param {{ base_name: string, next_id: number, helpers: AST.FunctionDeclaration[], statics: any[] }} helper_state
|
|
744
|
-
* @param {Map<string, AST.Identifier>} available_bindings
|
|
745
|
-
* @param {AST.Identifier[]} helper_bindings
|
|
746
|
-
* @param {any} source_node
|
|
747
|
-
* @param {TransformContext} transform_context
|
|
748
|
-
* @returns {AST.FunctionDeclaration}
|
|
749
|
-
*/
|
|
750
|
-
function create_helper_function_declaration(
|
|
751
|
-
helper_id,
|
|
752
|
-
body_nodes,
|
|
753
|
-
helper_state,
|
|
754
|
-
available_bindings,
|
|
755
|
-
helper_bindings,
|
|
756
|
-
source_node,
|
|
757
|
-
transform_context,
|
|
758
|
-
) {
|
|
759
|
-
const fn = /** @type {any} */ ({
|
|
760
|
-
type: 'FunctionDeclaration',
|
|
761
|
-
id: helper_id,
|
|
762
|
-
params: helper_bindings.length > 0 ? [create_helper_props_pattern(helper_bindings)] : [],
|
|
763
|
-
body: {
|
|
764
|
-
type: 'BlockStatement',
|
|
765
|
-
body: build_component_statements(
|
|
766
|
-
body_nodes,
|
|
767
|
-
helper_state,
|
|
768
|
-
new Map(available_bindings),
|
|
769
|
-
transform_context,
|
|
770
|
-
),
|
|
771
|
-
metadata: { path: [] },
|
|
772
|
-
},
|
|
773
|
-
async: false,
|
|
774
|
-
generator: false,
|
|
775
|
-
metadata: {
|
|
776
|
-
path: [],
|
|
777
|
-
is_component: true,
|
|
778
|
-
},
|
|
779
|
-
});
|
|
780
|
-
|
|
781
|
-
if (fn.id) {
|
|
782
|
-
fn.id.metadata = /** @type {AST.Identifier['metadata']} */ ({
|
|
783
|
-
...fn.id.metadata,
|
|
784
|
-
is_component: true,
|
|
785
|
-
});
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
return set_loc(fn, source_node);
|
|
789
|
-
}
|
|
790
|
-
|
|
791
614
|
/**
|
|
792
615
|
* @param {AST.Identifier[]} bindings
|
|
793
616
|
* @returns {AST.ObjectPattern}
|
|
@@ -805,8 +628,8 @@ function create_helper_props_pattern(bindings) {
|
|
|
805
628
|
* @returns {AST.Property}
|
|
806
629
|
*/
|
|
807
630
|
function create_helper_props_property(binding) {
|
|
808
|
-
const key =
|
|
809
|
-
const value =
|
|
631
|
+
const key = create_generated_identifier(binding.name);
|
|
632
|
+
const value = create_generated_identifier(binding.name);
|
|
810
633
|
|
|
811
634
|
return /** @type {any} */ ({
|
|
812
635
|
type: 'Property',
|
|
@@ -824,42 +647,50 @@ function create_helper_props_property(binding) {
|
|
|
824
647
|
* @param {AST.Identifier} helper_id
|
|
825
648
|
* @param {AST.Identifier[]} bindings
|
|
826
649
|
* @param {any} source_node
|
|
650
|
+
* @param {{
|
|
651
|
+
* mapWrapper?: boolean,
|
|
652
|
+
* mapBindingNames?: boolean,
|
|
653
|
+
* mapBindingValues?: boolean,
|
|
654
|
+
* }} [mapping]
|
|
827
655
|
* @returns {ESTreeJSX.JSXElement}
|
|
828
656
|
*/
|
|
829
|
-
function create_helper_component_element(helper_id, bindings, source_node) {
|
|
657
|
+
function create_helper_component_element(helper_id, bindings, source_node, mapping = {}) {
|
|
658
|
+
const { mapWrapper = true, mapBindingNames = true, mapBindingValues = true } = mapping;
|
|
830
659
|
const attributes = bindings.map(
|
|
831
660
|
(binding) =>
|
|
832
661
|
/** @type {any} */ ({
|
|
833
662
|
type: 'JSXAttribute',
|
|
834
|
-
name: identifier_to_jsx_name(
|
|
835
|
-
|
|
663
|
+
name: identifier_to_jsx_name(
|
|
664
|
+
mapBindingNames ? clone_identifier(binding) : create_generated_identifier(binding.name),
|
|
665
|
+
),
|
|
666
|
+
value: to_jsx_expression_container(
|
|
667
|
+
mapBindingValues ? clone_identifier(binding) : create_generated_identifier(binding.name),
|
|
668
|
+
binding,
|
|
669
|
+
),
|
|
836
670
|
metadata: { path: [] },
|
|
837
671
|
}),
|
|
838
672
|
);
|
|
839
673
|
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
}),
|
|
857
|
-
source_node,
|
|
858
|
-
);
|
|
674
|
+
const openingElement = {
|
|
675
|
+
type: 'JSXOpeningElement',
|
|
676
|
+
name: identifier_to_jsx_name(clone_identifier(helper_id)),
|
|
677
|
+
attributes,
|
|
678
|
+
selfClosing: true,
|
|
679
|
+
metadata: { path: [] },
|
|
680
|
+
};
|
|
681
|
+
const element = /** @type {any} */ ({
|
|
682
|
+
type: 'JSXElement',
|
|
683
|
+
openingElement: mapWrapper ? set_loc(openingElement, source_node) : openingElement,
|
|
684
|
+
closingElement: null,
|
|
685
|
+
children: [],
|
|
686
|
+
metadata: { path: [] },
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
return mapWrapper ? set_loc(element, source_node) : element;
|
|
859
690
|
}
|
|
860
691
|
|
|
861
692
|
/**
|
|
862
|
-
* @param {{ base_name: string, next_id: number, helpers:
|
|
693
|
+
* @param {{ base_name: string, next_id: number, helpers: any[], statics: any[] }} helper_state
|
|
863
694
|
* @param {string} suffix
|
|
864
695
|
* @returns {string}
|
|
865
696
|
*/
|
|
@@ -870,7 +701,7 @@ function create_helper_name(helper_state, suffix) {
|
|
|
870
701
|
|
|
871
702
|
/**
|
|
872
703
|
* @param {string} base_name
|
|
873
|
-
* @returns {{ base_name: string, next_id: number, helpers:
|
|
704
|
+
* @returns {{ base_name: string, next_id: number, helpers: any[], statics: any[] }}
|
|
874
705
|
*/
|
|
875
706
|
function create_helper_state(base_name) {
|
|
876
707
|
return {
|
|
@@ -1108,28 +939,61 @@ function is_lone_return_if_statement(node) {
|
|
|
1108
939
|
return false;
|
|
1109
940
|
}
|
|
1110
941
|
|
|
1111
|
-
const consequent_body =
|
|
1112
|
-
node.consequent.type === 'BlockStatement' ? node.consequent.body : [node.consequent];
|
|
942
|
+
const consequent_body = get_if_consequent_body(node);
|
|
1113
943
|
|
|
1114
944
|
return consequent_body.length === 1 && is_bare_return_statement(consequent_body[0]);
|
|
1115
945
|
}
|
|
1116
946
|
|
|
947
|
+
/**
|
|
948
|
+
* @param {any} node
|
|
949
|
+
* @returns {boolean}
|
|
950
|
+
*/
|
|
951
|
+
function is_returning_if_statement(node) {
|
|
952
|
+
if (node?.type !== 'IfStatement' || node.alternate) {
|
|
953
|
+
return false;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
return get_if_consequent_body(node).some(is_bare_return_statement);
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
/**
|
|
960
|
+
* @param {any} node
|
|
961
|
+
* @returns {any[]}
|
|
962
|
+
*/
|
|
963
|
+
function get_if_consequent_body(node) {
|
|
964
|
+
return node.consequent.type === 'BlockStatement' ? node.consequent.body : [node.consequent];
|
|
965
|
+
}
|
|
966
|
+
|
|
1117
967
|
/**
|
|
1118
968
|
* @param {any[]} render_nodes
|
|
1119
969
|
* @param {any} source_node
|
|
970
|
+
* @param {boolean} [map_render_node_locations]
|
|
1120
971
|
* @returns {any}
|
|
1121
972
|
*/
|
|
1122
|
-
function create_component_return_statement(
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
973
|
+
function create_component_return_statement(
|
|
974
|
+
render_nodes,
|
|
975
|
+
source_node,
|
|
976
|
+
map_render_node_locations = true,
|
|
977
|
+
) {
|
|
978
|
+
return set_loc(
|
|
979
|
+
/** @type {any} */ ({
|
|
980
|
+
type: 'ReturnStatement',
|
|
981
|
+
argument: build_return_expression(
|
|
982
|
+
render_nodes.map((node) =>
|
|
983
|
+
map_render_node_locations
|
|
984
|
+
? clone_expression_node(node)
|
|
985
|
+
: clone_expression_node_without_locations(node),
|
|
986
|
+
),
|
|
987
|
+
) || {
|
|
988
|
+
type: 'Literal',
|
|
989
|
+
value: null,
|
|
990
|
+
raw: 'null',
|
|
991
|
+
metadata: { path: [] },
|
|
992
|
+
},
|
|
1129
993
|
metadata: { path: [] },
|
|
1130
|
-
},
|
|
1131
|
-
|
|
1132
|
-
|
|
994
|
+
}),
|
|
995
|
+
source_node,
|
|
996
|
+
);
|
|
1133
997
|
}
|
|
1134
998
|
|
|
1135
999
|
/**
|
|
@@ -1138,8 +1002,7 @@ function create_component_return_statement(render_nodes, source_node) {
|
|
|
1138
1002
|
* @returns {any}
|
|
1139
1003
|
*/
|
|
1140
1004
|
function create_component_lone_return_if_statement(node, render_nodes) {
|
|
1141
|
-
const consequent_body =
|
|
1142
|
-
node.consequent.type === 'BlockStatement' ? node.consequent.body : [node.consequent];
|
|
1005
|
+
const consequent_body = get_if_consequent_body(node);
|
|
1143
1006
|
|
|
1144
1007
|
return set_loc(
|
|
1145
1008
|
/** @type {any} */ ({
|
|
@@ -1148,7 +1011,7 @@ function create_component_lone_return_if_statement(node, render_nodes) {
|
|
|
1148
1011
|
consequent: set_loc(
|
|
1149
1012
|
/** @type {any} */ ({
|
|
1150
1013
|
type: 'BlockStatement',
|
|
1151
|
-
body: [create_component_return_statement(render_nodes, consequent_body[0])],
|
|
1014
|
+
body: [create_component_return_statement(render_nodes, consequent_body[0], false)],
|
|
1152
1015
|
metadata: { path: [] },
|
|
1153
1016
|
}),
|
|
1154
1017
|
node.consequent,
|
|
@@ -1160,58 +1023,271 @@ function create_component_lone_return_if_statement(node, render_nodes) {
|
|
|
1160
1023
|
);
|
|
1161
1024
|
}
|
|
1162
1025
|
|
|
1163
|
-
const TEMPLATE_FRAGMENT_ERROR =
|
|
1164
|
-
'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>.';
|
|
1165
|
-
|
|
1166
1026
|
/**
|
|
1167
1027
|
* @param {any} node
|
|
1028
|
+
* @param {any[]} render_nodes
|
|
1168
1029
|
* @param {TransformContext} transform_context
|
|
1169
1030
|
* @returns {any}
|
|
1170
1031
|
*/
|
|
1171
|
-
function
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
`\`{html ...}\` is not supported on the ${transform_context.platform.name} target. Use \`dangerouslySetInnerHTML={{ __html: ... }}\` as an element attribute instead.`,
|
|
1176
|
-
);
|
|
1177
|
-
}
|
|
1178
|
-
if (!node.id) {
|
|
1179
|
-
throw create_compile_error(node, TEMPLATE_FRAGMENT_ERROR);
|
|
1180
|
-
}
|
|
1181
|
-
if (is_dynamic_element_id(node.id)) {
|
|
1182
|
-
return dynamic_element_to_jsx_child(node, transform_context);
|
|
1183
|
-
}
|
|
1032
|
+
function create_component_returning_if_statement(node, render_nodes, transform_context) {
|
|
1033
|
+
const consequent_body = get_if_consequent_body(node);
|
|
1034
|
+
const branch_statements = build_render_statements(consequent_body, true, transform_context);
|
|
1035
|
+
prepend_render_nodes_to_return_statements(branch_statements, render_nodes);
|
|
1184
1036
|
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1037
|
+
return set_loc(
|
|
1038
|
+
/** @type {any} */ ({
|
|
1039
|
+
type: 'IfStatement',
|
|
1040
|
+
test: node.test,
|
|
1041
|
+
consequent: set_loc(
|
|
1042
|
+
/** @type {any} */ ({
|
|
1043
|
+
type: 'BlockStatement',
|
|
1044
|
+
body: branch_statements,
|
|
1045
|
+
metadata: { path: [] },
|
|
1046
|
+
}),
|
|
1047
|
+
node.consequent,
|
|
1048
|
+
),
|
|
1049
|
+
alternate: null,
|
|
1050
|
+
metadata: { path: [] },
|
|
1051
|
+
}),
|
|
1189
1052
|
node,
|
|
1190
1053
|
);
|
|
1191
|
-
|
|
1192
|
-
const children = create_element_children(node.children || [], transform_context);
|
|
1193
|
-
const has_unmappable_attribute = attributes.some(
|
|
1194
|
-
(/** @type {any} */ attribute) => attribute?.metadata?.has_unmappable_value,
|
|
1195
|
-
);
|
|
1054
|
+
}
|
|
1196
1055
|
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1056
|
+
/**
|
|
1057
|
+
* @param {any} node
|
|
1058
|
+
* @param {any[]} continuation_body
|
|
1059
|
+
* @param {any[]} render_nodes
|
|
1060
|
+
* @param {TransformContext} transform_context
|
|
1061
|
+
* @returns {any[]}
|
|
1062
|
+
*/
|
|
1063
|
+
function create_component_helper_split_returning_if_statements(
|
|
1064
|
+
node,
|
|
1065
|
+
continuation_body,
|
|
1066
|
+
render_nodes,
|
|
1067
|
+
transform_context,
|
|
1068
|
+
) {
|
|
1069
|
+
const consequent_body = get_if_consequent_body(node);
|
|
1070
|
+
const return_index = consequent_body.findIndex(is_bare_return_statement);
|
|
1071
|
+
const branch_body =
|
|
1072
|
+
return_index === -1 ? consequent_body : consequent_body.slice(0, return_index);
|
|
1073
|
+
const branch_helper = create_hook_safe_helper(
|
|
1074
|
+
branch_body,
|
|
1075
|
+
undefined,
|
|
1076
|
+
node.consequent,
|
|
1077
|
+
transform_context,
|
|
1078
|
+
);
|
|
1079
|
+
const continuation_helper = create_hook_safe_helper(
|
|
1080
|
+
continuation_body,
|
|
1081
|
+
undefined,
|
|
1082
|
+
node,
|
|
1083
|
+
transform_context,
|
|
1084
|
+
);
|
|
1085
|
+
return [
|
|
1086
|
+
set_loc(
|
|
1087
|
+
/** @type {any} */ ({
|
|
1088
|
+
type: 'IfStatement',
|
|
1089
|
+
test: node.test,
|
|
1090
|
+
consequent: set_loc(
|
|
1091
|
+
/** @type {any} */ ({
|
|
1092
|
+
type: 'BlockStatement',
|
|
1093
|
+
body: [
|
|
1094
|
+
...branch_helper.setup_statements,
|
|
1095
|
+
{
|
|
1096
|
+
type: 'ReturnStatement',
|
|
1097
|
+
argument: combine_render_return_argument(
|
|
1098
|
+
render_nodes,
|
|
1099
|
+
branch_helper.component_element,
|
|
1100
|
+
),
|
|
1101
|
+
metadata: { path: [] },
|
|
1102
|
+
},
|
|
1103
|
+
],
|
|
1104
|
+
metadata: { path: [] },
|
|
1105
|
+
}),
|
|
1106
|
+
node.consequent,
|
|
1107
|
+
),
|
|
1108
|
+
alternate: null,
|
|
1109
|
+
metadata: { path: [] },
|
|
1110
|
+
}),
|
|
1111
|
+
node,
|
|
1112
|
+
),
|
|
1113
|
+
...continuation_helper.setup_statements,
|
|
1114
|
+
{
|
|
1115
|
+
type: 'ReturnStatement',
|
|
1116
|
+
argument: combine_render_return_argument(render_nodes, continuation_helper.component_element),
|
|
1117
|
+
metadata: { path: [] },
|
|
1118
|
+
},
|
|
1119
|
+
];
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
* @param {any[]} statements
|
|
1124
|
+
* @param {any[]} render_nodes
|
|
1125
|
+
* @returns {void}
|
|
1126
|
+
*/
|
|
1127
|
+
function prepend_render_nodes_to_return_statements(statements, render_nodes) {
|
|
1128
|
+
if (render_nodes.length === 0) {
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
for (const statement of statements) {
|
|
1133
|
+
prepend_render_nodes_to_return_statement(statement, render_nodes, false);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* @param {any} node
|
|
1139
|
+
* @param {any[]} render_nodes
|
|
1140
|
+
* @param {boolean} inside_nested_function
|
|
1141
|
+
* @returns {void}
|
|
1142
|
+
*/
|
|
1143
|
+
function prepend_render_nodes_to_return_statement(node, render_nodes, inside_nested_function) {
|
|
1144
|
+
if (!node || typeof node !== 'object') {
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
if (
|
|
1149
|
+
node.type === 'FunctionDeclaration' ||
|
|
1150
|
+
node.type === 'FunctionExpression' ||
|
|
1151
|
+
node.type === 'ArrowFunctionExpression'
|
|
1152
|
+
) {
|
|
1153
|
+
inside_nested_function = true;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
if (!inside_nested_function && node.type === 'ReturnStatement') {
|
|
1157
|
+
node.argument = combine_render_return_argument(render_nodes, node.argument);
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
if (Array.isArray(node)) {
|
|
1162
|
+
for (const child of node) {
|
|
1163
|
+
prepend_render_nodes_to_return_statement(child, render_nodes, inside_nested_function);
|
|
1164
|
+
}
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
for (const key of Object.keys(node)) {
|
|
1169
|
+
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') {
|
|
1170
|
+
continue;
|
|
1171
|
+
}
|
|
1172
|
+
prepend_render_nodes_to_return_statement(node[key], render_nodes, inside_nested_function);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
/**
|
|
1177
|
+
* @param {any[]} render_nodes
|
|
1178
|
+
* @param {any} return_argument
|
|
1179
|
+
* @returns {any}
|
|
1180
|
+
*/
|
|
1181
|
+
function combine_render_return_argument(render_nodes, return_argument) {
|
|
1182
|
+
const combined = render_nodes.map((node) => clone_expression_node_without_locations(node));
|
|
1183
|
+
|
|
1184
|
+
if (!is_null_literal(return_argument)) {
|
|
1185
|
+
combined.push(return_argument_to_render_node(return_argument));
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
return build_return_expression(combined) || create_null_literal();
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
/**
|
|
1192
|
+
* @param {any} argument
|
|
1193
|
+
* @returns {any}
|
|
1194
|
+
*/
|
|
1195
|
+
function return_argument_to_render_node(argument) {
|
|
1196
|
+
if (
|
|
1197
|
+
argument?.type === 'JSXElement' ||
|
|
1198
|
+
argument?.type === 'JSXFragment' ||
|
|
1199
|
+
argument?.type === 'JSXExpressionContainer'
|
|
1200
|
+
) {
|
|
1201
|
+
return argument;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
return to_jsx_expression_container(argument);
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
/**
|
|
1208
|
+
* @param {any} node
|
|
1209
|
+
* @returns {boolean}
|
|
1210
|
+
*/
|
|
1211
|
+
function is_null_literal(node) {
|
|
1212
|
+
return node?.type === 'Literal' && node.value == null;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
/**
|
|
1216
|
+
* @param {any} node
|
|
1217
|
+
* @returns {any}
|
|
1218
|
+
*/
|
|
1219
|
+
function clone_expression_node_without_locations(node) {
|
|
1220
|
+
if (!node || typeof node !== 'object') return node;
|
|
1221
|
+
if (Array.isArray(node)) return node.map(clone_expression_node_without_locations);
|
|
1222
|
+
|
|
1223
|
+
const clone = { ...node };
|
|
1224
|
+
delete clone.loc;
|
|
1225
|
+
delete clone.start;
|
|
1226
|
+
delete clone.end;
|
|
1227
|
+
|
|
1228
|
+
for (const key of Object.keys(clone)) {
|
|
1229
|
+
if (key === 'metadata') {
|
|
1230
|
+
clone.metadata = clone.metadata ? { ...clone.metadata } : { path: [] };
|
|
1231
|
+
continue;
|
|
1232
|
+
}
|
|
1233
|
+
clone[key] = clone_expression_node_without_locations(clone[key]);
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
return clone;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
const TEMPLATE_FRAGMENT_ERROR =
|
|
1240
|
+
'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>.';
|
|
1241
|
+
|
|
1242
|
+
/**
|
|
1243
|
+
* @param {any} node
|
|
1244
|
+
* @param {TransformContext} transform_context
|
|
1245
|
+
* @returns {any}
|
|
1246
|
+
*/
|
|
1247
|
+
function to_jsx_element(node, transform_context) {
|
|
1248
|
+
if (node.type === 'JSXElement') return node;
|
|
1249
|
+
if ((node.children || []).some((/** @type {any} */ c) => c && c.type === 'Html')) {
|
|
1250
|
+
throw new Error(
|
|
1251
|
+
`\`{html ...}\` is not supported on the ${transform_context.platform.name} target. Use \`dangerouslySetInnerHTML={{ __html: ... }}\` as an element attribute instead.`,
|
|
1252
|
+
);
|
|
1253
|
+
}
|
|
1254
|
+
if (!node.id) {
|
|
1255
|
+
throw create_compile_error(node, TEMPLATE_FRAGMENT_ERROR);
|
|
1256
|
+
}
|
|
1257
|
+
if (is_dynamic_element_id(node.id)) {
|
|
1258
|
+
return dynamic_element_to_jsx_child(node, transform_context);
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
const name = identifier_to_jsx_name(node.id);
|
|
1262
|
+
const attributes = transform_element_attributes_dispatch(
|
|
1263
|
+
node.attributes || [],
|
|
1264
|
+
transform_context,
|
|
1265
|
+
node,
|
|
1266
|
+
);
|
|
1267
|
+
const selfClosing = !!node.selfClosing;
|
|
1268
|
+
const children = create_element_children(node.children || [], transform_context);
|
|
1269
|
+
const has_unmappable_attribute = attributes.some(
|
|
1270
|
+
(/** @type {any} */ attribute) => attribute?.metadata?.has_unmappable_value,
|
|
1271
|
+
);
|
|
1272
|
+
|
|
1273
|
+
/** @type {ESTreeJSX.JSXOpeningElement} */
|
|
1274
|
+
const openingElement = /** @type {ESTreeJSX.JSXOpeningElement} */ (
|
|
1275
|
+
has_unmappable_attribute
|
|
1276
|
+
? {
|
|
1277
|
+
type: 'JSXOpeningElement',
|
|
1278
|
+
name,
|
|
1279
|
+
attributes,
|
|
1280
|
+
selfClosing,
|
|
1281
|
+
metadata: { path: [] },
|
|
1282
|
+
}
|
|
1283
|
+
: set_loc(
|
|
1284
|
+
/** @type {any} */ ({
|
|
1285
|
+
type: 'JSXOpeningElement',
|
|
1286
|
+
name,
|
|
1287
|
+
attributes,
|
|
1288
|
+
selfClosing,
|
|
1289
|
+
}),
|
|
1290
|
+
node.openingElement || node,
|
|
1215
1291
|
)
|
|
1216
1292
|
);
|
|
1217
1293
|
|
|
@@ -1349,77 +1425,10 @@ function statement_body_to_jsx_child(body_nodes, transform_context) {
|
|
|
1349
1425
|
*/
|
|
1350
1426
|
function hook_safe_statement_body_to_jsx_child(body_nodes, transform_context) {
|
|
1351
1427
|
const source_node = get_body_source_node(body_nodes);
|
|
1352
|
-
const
|
|
1353
|
-
create_generated_identifier(create_local_statement_component_name(transform_context)),
|
|
1354
|
-
source_node,
|
|
1355
|
-
);
|
|
1356
|
-
const helper_bindings = Array.from(transform_context.available_bindings.values());
|
|
1357
|
-
|
|
1358
|
-
// Save and isolate bindings for the helper body
|
|
1359
|
-
const saved_bindings = transform_context.available_bindings;
|
|
1360
|
-
transform_context.available_bindings = new Map(saved_bindings);
|
|
1361
|
-
|
|
1362
|
-
const helper_fn = set_loc(
|
|
1363
|
-
/** @type {any} */ ({
|
|
1364
|
-
type: 'FunctionDeclaration',
|
|
1365
|
-
id: helper_id,
|
|
1366
|
-
params: helper_bindings.length > 0 ? [create_helper_props_pattern(helper_bindings)] : [],
|
|
1367
|
-
body: {
|
|
1368
|
-
type: 'BlockStatement',
|
|
1369
|
-
body: build_render_statements(body_nodes, true, transform_context),
|
|
1370
|
-
metadata: { path: [] },
|
|
1371
|
-
},
|
|
1372
|
-
async: false,
|
|
1373
|
-
generator: false,
|
|
1374
|
-
metadata: {
|
|
1375
|
-
path: [],
|
|
1376
|
-
is_component: true,
|
|
1377
|
-
is_method: false,
|
|
1378
|
-
},
|
|
1379
|
-
}),
|
|
1380
|
-
source_node,
|
|
1381
|
-
);
|
|
1382
|
-
|
|
1383
|
-
// Restore bindings
|
|
1384
|
-
transform_context.available_bindings = saved_bindings;
|
|
1385
|
-
|
|
1386
|
-
// Register helper for hoisting to module level
|
|
1387
|
-
if (transform_context.helper_state) {
|
|
1388
|
-
transform_context.helper_state.helpers.push(helper_fn);
|
|
1389
|
-
|
|
1390
|
-
return to_jsx_expression_container(
|
|
1391
|
-
/** @type {any} */ (create_helper_component_element(helper_id, helper_bindings, source_node)),
|
|
1392
|
-
source_node,
|
|
1393
|
-
);
|
|
1394
|
-
}
|
|
1428
|
+
const helper = create_hook_safe_helper(body_nodes, undefined, source_node, transform_context);
|
|
1395
1429
|
|
|
1396
1430
|
return to_jsx_expression_container(
|
|
1397
|
-
|
|
1398
|
-
type: 'CallExpression',
|
|
1399
|
-
callee: {
|
|
1400
|
-
type: 'ArrowFunctionExpression',
|
|
1401
|
-
params: [],
|
|
1402
|
-
body: /** @type {any} */ ({
|
|
1403
|
-
type: 'BlockStatement',
|
|
1404
|
-
body: [
|
|
1405
|
-
helper_fn,
|
|
1406
|
-
{
|
|
1407
|
-
type: 'ReturnStatement',
|
|
1408
|
-
argument: create_helper_component_element(helper_id, helper_bindings, source_node),
|
|
1409
|
-
metadata: { path: [] },
|
|
1410
|
-
},
|
|
1411
|
-
],
|
|
1412
|
-
metadata: { path: [] },
|
|
1413
|
-
}),
|
|
1414
|
-
async: false,
|
|
1415
|
-
generator: false,
|
|
1416
|
-
expression: false,
|
|
1417
|
-
metadata: { path: [] },
|
|
1418
|
-
},
|
|
1419
|
-
arguments: [],
|
|
1420
|
-
optional: false,
|
|
1421
|
-
metadata: { path: [] },
|
|
1422
|
-
}),
|
|
1431
|
+
create_hook_safe_helper_iife(helper.setup_statements, helper.component_element),
|
|
1423
1432
|
source_node,
|
|
1424
1433
|
);
|
|
1425
1434
|
}
|
|
@@ -1448,49 +1457,102 @@ function create_local_statement_component_name(transform_context) {
|
|
|
1448
1457
|
*/
|
|
1449
1458
|
function hook_safe_render_statements(body_nodes, key_expression, transform_context) {
|
|
1450
1459
|
const source_node = get_body_source_node(body_nodes);
|
|
1451
|
-
const
|
|
1452
|
-
|
|
1460
|
+
const helper = create_hook_safe_helper(
|
|
1461
|
+
body_nodes,
|
|
1462
|
+
key_expression,
|
|
1453
1463
|
source_node,
|
|
1464
|
+
transform_context,
|
|
1465
|
+
);
|
|
1466
|
+
const statements = [...helper.setup_statements];
|
|
1467
|
+
|
|
1468
|
+
statements.push({
|
|
1469
|
+
type: 'ReturnStatement',
|
|
1470
|
+
argument: helper.component_element,
|
|
1471
|
+
metadata: { path: [] },
|
|
1472
|
+
});
|
|
1473
|
+
|
|
1474
|
+
return statements;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
/**
|
|
1478
|
+
* @param {any[]} body_nodes
|
|
1479
|
+
* @param {Map<string, AST.Identifier>} available_bindings
|
|
1480
|
+
* @returns {AST.Identifier[]}
|
|
1481
|
+
*/
|
|
1482
|
+
function get_referenced_helper_bindings(body_nodes, available_bindings) {
|
|
1483
|
+
const helper_bindings = [];
|
|
1484
|
+
const local_bindings = new Map();
|
|
1485
|
+
|
|
1486
|
+
for (const node of body_nodes) {
|
|
1487
|
+
collect_statement_bindings(node, local_bindings);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
for (const [name, binding] of available_bindings) {
|
|
1491
|
+
if (local_bindings.has(name)) continue;
|
|
1492
|
+
|
|
1493
|
+
if (references_scope_bindings(body_nodes, new Map([[name, binding]]))) {
|
|
1494
|
+
helper_bindings.push(binding);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
return helper_bindings;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
/**
|
|
1502
|
+
* @param {any[]} body_nodes
|
|
1503
|
+
* @param {any} key_expression
|
|
1504
|
+
* @param {any} source_node
|
|
1505
|
+
* @param {TransformContext} transform_context
|
|
1506
|
+
* @returns {{ setup_statements: any[], component_element: ESTreeJSX.JSXElement }}
|
|
1507
|
+
*/
|
|
1508
|
+
function create_hook_safe_helper(body_nodes, key_expression, source_node, transform_context) {
|
|
1509
|
+
const helper_id = create_generated_identifier(
|
|
1510
|
+
create_local_statement_component_name(transform_context),
|
|
1454
1511
|
);
|
|
1455
|
-
const helper_bindings =
|
|
1512
|
+
const helper_bindings = get_referenced_helper_bindings(
|
|
1513
|
+
body_nodes,
|
|
1514
|
+
transform_context.available_bindings,
|
|
1515
|
+
);
|
|
1516
|
+
const aliases = helper_bindings.map((binding) =>
|
|
1517
|
+
create_helper_type_alias_declaration(helper_id, binding),
|
|
1518
|
+
);
|
|
1519
|
+
const props_type =
|
|
1520
|
+
helper_bindings.length > 0 ? create_helper_props_type_literal(helper_bindings, aliases) : null;
|
|
1521
|
+
const params =
|
|
1522
|
+
props_type !== null ? [create_typed_helper_props_pattern(helper_bindings, props_type)] : [];
|
|
1456
1523
|
|
|
1457
|
-
// Save and isolate bindings for the helper body
|
|
1458
1524
|
const saved_bindings = transform_context.available_bindings;
|
|
1459
1525
|
transform_context.available_bindings = new Map(saved_bindings);
|
|
1460
1526
|
|
|
1461
|
-
const helper_fn =
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
}),
|
|
1479
|
-
source_node,
|
|
1480
|
-
);
|
|
1527
|
+
const helper_fn = /** @type {any} */ ({
|
|
1528
|
+
type: 'FunctionExpression',
|
|
1529
|
+
id: clone_identifier(helper_id),
|
|
1530
|
+
params,
|
|
1531
|
+
body: {
|
|
1532
|
+
type: 'BlockStatement',
|
|
1533
|
+
body: build_render_statements(body_nodes, true, transform_context),
|
|
1534
|
+
metadata: { path: [] },
|
|
1535
|
+
},
|
|
1536
|
+
async: false,
|
|
1537
|
+
generator: false,
|
|
1538
|
+
metadata: {
|
|
1539
|
+
path: [],
|
|
1540
|
+
is_component: true,
|
|
1541
|
+
is_method: false,
|
|
1542
|
+
},
|
|
1543
|
+
});
|
|
1481
1544
|
|
|
1482
|
-
// Restore bindings
|
|
1483
1545
|
transform_context.available_bindings = saved_bindings;
|
|
1484
1546
|
|
|
1485
|
-
// Register helper for hoisting to module level
|
|
1486
|
-
if (transform_context.helper_state) {
|
|
1487
|
-
transform_context.helper_state.helpers.push(helper_fn);
|
|
1488
|
-
}
|
|
1489
|
-
|
|
1490
1547
|
const component_element = create_helper_component_element(
|
|
1491
1548
|
helper_id,
|
|
1492
1549
|
helper_bindings,
|
|
1493
1550
|
source_node,
|
|
1551
|
+
{
|
|
1552
|
+
mapWrapper: false,
|
|
1553
|
+
mapBindingNames: false,
|
|
1554
|
+
mapBindingValues: false,
|
|
1555
|
+
},
|
|
1494
1556
|
);
|
|
1495
1557
|
|
|
1496
1558
|
if (key_expression) {
|
|
@@ -1504,26 +1566,205 @@ function hook_safe_render_statements(body_nodes, key_expression, transform_conte
|
|
|
1504
1566
|
);
|
|
1505
1567
|
}
|
|
1506
1568
|
|
|
1507
|
-
// When helper_state is null (no enclosing component context), inline the
|
|
1508
|
-
// helper via an IIFE so the function declaration isn't silently dropped.
|
|
1509
1569
|
if (!transform_context.helper_state) {
|
|
1510
|
-
return
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
];
|
|
1570
|
+
return {
|
|
1571
|
+
setup_statements: [
|
|
1572
|
+
...aliases.map((alias) => alias.declaration),
|
|
1573
|
+
create_helper_function_declaration_from_expression(helper_id, helper_fn),
|
|
1574
|
+
],
|
|
1575
|
+
component_element,
|
|
1576
|
+
};
|
|
1518
1577
|
}
|
|
1519
1578
|
|
|
1520
|
-
|
|
1521
|
-
{
|
|
1522
|
-
|
|
1523
|
-
|
|
1579
|
+
const cache_id = create_generated_identifier(
|
|
1580
|
+
`${transform_context.helper_state.base_name}__${helper_id.name}`,
|
|
1581
|
+
);
|
|
1582
|
+
transform_context.helper_state.helpers.push(create_helper_cache_declaration(cache_id));
|
|
1583
|
+
|
|
1584
|
+
return {
|
|
1585
|
+
setup_statements: [
|
|
1586
|
+
...aliases.map((alias) => alias.declaration),
|
|
1587
|
+
create_cached_helper_declaration(helper_id, cache_id, helper_fn),
|
|
1588
|
+
],
|
|
1589
|
+
component_element,
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
/**
|
|
1594
|
+
* @param {any[]} setup_statements
|
|
1595
|
+
* @param {ESTreeJSX.JSXElement} component_element
|
|
1596
|
+
* @returns {any}
|
|
1597
|
+
*/
|
|
1598
|
+
function create_hook_safe_helper_iife(setup_statements, component_element) {
|
|
1599
|
+
return /** @type {any} */ ({
|
|
1600
|
+
type: 'CallExpression',
|
|
1601
|
+
callee: {
|
|
1602
|
+
type: 'ArrowFunctionExpression',
|
|
1603
|
+
params: [],
|
|
1604
|
+
body: /** @type {any} */ ({
|
|
1605
|
+
type: 'BlockStatement',
|
|
1606
|
+
body: [
|
|
1607
|
+
...setup_statements,
|
|
1608
|
+
{
|
|
1609
|
+
type: 'ReturnStatement',
|
|
1610
|
+
argument: component_element,
|
|
1611
|
+
metadata: { path: [] },
|
|
1612
|
+
},
|
|
1613
|
+
],
|
|
1614
|
+
metadata: { path: [] },
|
|
1615
|
+
}),
|
|
1616
|
+
async: false,
|
|
1617
|
+
generator: false,
|
|
1618
|
+
expression: false,
|
|
1524
1619
|
metadata: { path: [] },
|
|
1525
1620
|
},
|
|
1526
|
-
|
|
1621
|
+
arguments: [],
|
|
1622
|
+
optional: false,
|
|
1623
|
+
metadata: { path: [] },
|
|
1624
|
+
});
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
/**
|
|
1628
|
+
* @param {AST.Identifier} helper_id
|
|
1629
|
+
* @param {AST.Identifier} binding
|
|
1630
|
+
* @returns {{ id: AST.Identifier, declaration: any }}
|
|
1631
|
+
*/
|
|
1632
|
+
function create_helper_type_alias_declaration(helper_id, binding) {
|
|
1633
|
+
const alias_id = create_generated_identifier(`_tsrx_${helper_id.name}_${binding.name}`);
|
|
1634
|
+
|
|
1635
|
+
return {
|
|
1636
|
+
id: alias_id,
|
|
1637
|
+
declaration: /** @type {any} */ ({
|
|
1638
|
+
type: 'VariableDeclaration',
|
|
1639
|
+
kind: 'const',
|
|
1640
|
+
declarations: [
|
|
1641
|
+
{
|
|
1642
|
+
type: 'VariableDeclarator',
|
|
1643
|
+
id: clone_identifier(alias_id),
|
|
1644
|
+
init: create_generated_identifier(binding.name),
|
|
1645
|
+
metadata: { path: [] },
|
|
1646
|
+
},
|
|
1647
|
+
],
|
|
1648
|
+
metadata: { path: [] },
|
|
1649
|
+
}),
|
|
1650
|
+
};
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
/**
|
|
1654
|
+
* @param {AST.Identifier[]} bindings
|
|
1655
|
+
* @param {{ id: AST.Identifier }[]} aliases
|
|
1656
|
+
* @returns {any}
|
|
1657
|
+
*/
|
|
1658
|
+
function create_helper_props_type_literal(bindings, aliases) {
|
|
1659
|
+
return /** @type {any} */ ({
|
|
1660
|
+
type: 'TSTypeLiteral',
|
|
1661
|
+
members: bindings.map(
|
|
1662
|
+
(binding, i) =>
|
|
1663
|
+
/** @type {any} */ ({
|
|
1664
|
+
type: 'TSPropertySignature',
|
|
1665
|
+
key: create_generated_identifier(binding.name),
|
|
1666
|
+
computed: false,
|
|
1667
|
+
optional: false,
|
|
1668
|
+
readonly: false,
|
|
1669
|
+
static: false,
|
|
1670
|
+
kind: 'init',
|
|
1671
|
+
typeAnnotation: {
|
|
1672
|
+
type: 'TSTypeAnnotation',
|
|
1673
|
+
typeAnnotation: {
|
|
1674
|
+
type: 'TSTypeQuery',
|
|
1675
|
+
exprName: clone_identifier(aliases[i].id),
|
|
1676
|
+
typeArguments: null,
|
|
1677
|
+
metadata: { path: [] },
|
|
1678
|
+
},
|
|
1679
|
+
metadata: { path: [] },
|
|
1680
|
+
},
|
|
1681
|
+
metadata: { path: [] },
|
|
1682
|
+
}),
|
|
1683
|
+
),
|
|
1684
|
+
metadata: { path: [] },
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
/**
|
|
1689
|
+
* @param {AST.Identifier[]} bindings
|
|
1690
|
+
* @param {any} props_type
|
|
1691
|
+
* @returns {AST.ObjectPattern}
|
|
1692
|
+
*/
|
|
1693
|
+
function create_typed_helper_props_pattern(bindings, props_type) {
|
|
1694
|
+
const pattern = create_helper_props_pattern(bindings);
|
|
1695
|
+
/** @type {any} */ (pattern).typeAnnotation = {
|
|
1696
|
+
type: 'TSTypeAnnotation',
|
|
1697
|
+
typeAnnotation: props_type,
|
|
1698
|
+
metadata: { path: [] },
|
|
1699
|
+
};
|
|
1700
|
+
return pattern;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
/**
|
|
1704
|
+
* @param {AST.Identifier} cache_id
|
|
1705
|
+
* @returns {any}
|
|
1706
|
+
*/
|
|
1707
|
+
function create_helper_cache_declaration(cache_id) {
|
|
1708
|
+
return /** @type {any} */ ({
|
|
1709
|
+
type: 'VariableDeclaration',
|
|
1710
|
+
kind: 'let',
|
|
1711
|
+
declarations: [
|
|
1712
|
+
{
|
|
1713
|
+
type: 'VariableDeclarator',
|
|
1714
|
+
id: clone_identifier(cache_id),
|
|
1715
|
+
init: null,
|
|
1716
|
+
metadata: { path: [] },
|
|
1717
|
+
},
|
|
1718
|
+
],
|
|
1719
|
+
metadata: { path: [] },
|
|
1720
|
+
});
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
/**
|
|
1724
|
+
* @param {AST.Identifier} helper_id
|
|
1725
|
+
* @param {AST.Identifier} cache_id
|
|
1726
|
+
* @param {any} helper_fn
|
|
1727
|
+
* @returns {any}
|
|
1728
|
+
*/
|
|
1729
|
+
function create_cached_helper_declaration(helper_id, cache_id, helper_fn) {
|
|
1730
|
+
return /** @type {any} */ ({
|
|
1731
|
+
type: 'VariableDeclaration',
|
|
1732
|
+
kind: 'const',
|
|
1733
|
+
declarations: [
|
|
1734
|
+
{
|
|
1735
|
+
type: 'VariableDeclarator',
|
|
1736
|
+
id: clone_identifier(helper_id),
|
|
1737
|
+
init: {
|
|
1738
|
+
type: 'LogicalExpression',
|
|
1739
|
+
operator: '??',
|
|
1740
|
+
left: clone_identifier(cache_id),
|
|
1741
|
+
right: {
|
|
1742
|
+
type: 'AssignmentExpression',
|
|
1743
|
+
operator: '=',
|
|
1744
|
+
left: clone_identifier(cache_id),
|
|
1745
|
+
right: helper_fn,
|
|
1746
|
+
metadata: { path: [] },
|
|
1747
|
+
},
|
|
1748
|
+
metadata: { path: [] },
|
|
1749
|
+
},
|
|
1750
|
+
metadata: { path: [] },
|
|
1751
|
+
},
|
|
1752
|
+
],
|
|
1753
|
+
metadata: { path: [] },
|
|
1754
|
+
});
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
/**
|
|
1758
|
+
* @param {AST.Identifier} helper_id
|
|
1759
|
+
* @param {any} helper_fn
|
|
1760
|
+
* @returns {AST.FunctionDeclaration}
|
|
1761
|
+
*/
|
|
1762
|
+
function create_helper_function_declaration_from_expression(helper_id, helper_fn) {
|
|
1763
|
+
return /** @type {any} */ ({
|
|
1764
|
+
...helper_fn,
|
|
1765
|
+
type: 'FunctionDeclaration',
|
|
1766
|
+
id: clone_identifier(helper_id),
|
|
1767
|
+
});
|
|
1527
1768
|
}
|
|
1528
1769
|
|
|
1529
1770
|
/**
|