@tsrx/core 0.1.30 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/index.js +5 -0
- package/src/plugin.js +111 -62
- package/src/transform/jsx/helpers.js +48 -0
- package/src/transform/jsx/index.js +91 -61
- package/src/utils/builders.js +20 -0
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -159,11 +159,16 @@ export {
|
|
|
159
159
|
rewrite_loop_continues_to_bare_returns as rewriteLoopContinuesToBareReturns,
|
|
160
160
|
to_jsx_attribute as toJsxAttribute,
|
|
161
161
|
validate_at_most_one_ref_attribute as validateAtMostOneRefAttribute,
|
|
162
|
+
wrap_edge_whitespace as wrapEdgeWhitespace,
|
|
162
163
|
} from './transform/jsx/index.js';
|
|
163
164
|
export {
|
|
164
165
|
in_jsx_child_context as inJsxChildContext,
|
|
165
166
|
tsx_node_to_jsx_expression as tsxNodeToJsxExpression,
|
|
166
167
|
tsx_with_ts_locations as tsxWithTsLocations,
|
|
168
|
+
is_template_if_node as isTemplateIfNode,
|
|
169
|
+
is_template_for_of_node as isTemplateForOfNode,
|
|
170
|
+
is_template_switch_node as isTemplateSwitchNode,
|
|
171
|
+
is_template_try_node as isTemplateTryNode,
|
|
167
172
|
} from './transform/jsx/helpers.js';
|
|
168
173
|
export {
|
|
169
174
|
collect_style_ref_attributes as collectStyleRefAttributes,
|
package/src/plugin.js
CHANGED
|
@@ -5,12 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import * as acorn from 'acorn';
|
|
8
|
-
import {
|
|
9
|
-
skipWhitespace,
|
|
10
|
-
isWhitespaceTextNode,
|
|
11
|
-
BINDING_TYPES,
|
|
12
|
-
DestructuringErrors,
|
|
13
|
-
} from './parse/index.js';
|
|
8
|
+
import { isWhitespaceTextNode, BINDING_TYPES, DestructuringErrors } from './parse/index.js';
|
|
14
9
|
import { parse_style } from './parse/style.js';
|
|
15
10
|
import { regex_newline_characters } from './utils/patterns.js';
|
|
16
11
|
import { error } from './errors.js';
|
|
@@ -549,7 +544,6 @@ export function TSRXPlugin(config) {
|
|
|
549
544
|
while (index < this.input.length) {
|
|
550
545
|
if (this.#isTemplateLineCommentStart(index, start)) {
|
|
551
546
|
const comment_start = index;
|
|
552
|
-
const comment_start_loc = acorn.getLineInfo(this.input, comment_start);
|
|
553
547
|
index += 2;
|
|
554
548
|
while (
|
|
555
549
|
index < this.input.length &&
|
|
@@ -558,20 +552,9 @@ export function TSRXPlugin(config) {
|
|
|
558
552
|
) {
|
|
559
553
|
index++;
|
|
560
554
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
// as a leading comment on the following child (which the JSX printers
|
|
565
|
-
// emit), rather than on the container's `elementLeadingComments`.
|
|
566
|
-
this.options.onComment(
|
|
567
|
-
false,
|
|
568
|
-
this.input.slice(comment_start + 2, index),
|
|
569
|
-
comment_start,
|
|
570
|
-
index,
|
|
571
|
-
new acorn.Position(comment_start_loc.line, comment_start_loc.column),
|
|
572
|
-
new acorn.Position(comment_end_loc.line, comment_end_loc.column),
|
|
573
|
-
/** @type {any} */ (null),
|
|
574
|
-
);
|
|
555
|
+
|
|
556
|
+
if (comment_start >= token_end) {
|
|
557
|
+
this.#emitTemplateLineComment(comment_start, index, null);
|
|
575
558
|
}
|
|
576
559
|
continue;
|
|
577
560
|
}
|
|
@@ -590,7 +573,7 @@ export function TSRXPlugin(config) {
|
|
|
590
573
|
index,
|
|
591
574
|
new acorn.Position(comment_start_loc.line, comment_start_loc.column),
|
|
592
575
|
new acorn.Position(comment_end_loc.line, comment_end_loc.column),
|
|
593
|
-
|
|
576
|
+
null,
|
|
594
577
|
);
|
|
595
578
|
}
|
|
596
579
|
continue;
|
|
@@ -640,6 +623,62 @@ export function TSRXPlugin(config) {
|
|
|
640
623
|
return node.value !== '' && !regex_newline_characters.test(node.value);
|
|
641
624
|
}
|
|
642
625
|
|
|
626
|
+
#skipTrailingLayoutWhitespace() {
|
|
627
|
+
let index = this.start;
|
|
628
|
+
let has_newline = false;
|
|
629
|
+
while (index < this.input.length) {
|
|
630
|
+
const ch = this.input.charCodeAt(index);
|
|
631
|
+
if (ch === CharCode.lineFeed || ch === CharCode.carriageReturn) {
|
|
632
|
+
has_newline = true;
|
|
633
|
+
index++;
|
|
634
|
+
} else if (ch === CharCode.space || ch === CharCode.tab) {
|
|
635
|
+
index++;
|
|
636
|
+
} else if (ch === CharCode.slash && this.input.charCodeAt(index + 1) === CharCode.slash) {
|
|
637
|
+
const comment_start = index;
|
|
638
|
+
while (index < this.input.length && !this.#isNewlineCharCode(index)) {
|
|
639
|
+
index++;
|
|
640
|
+
}
|
|
641
|
+
this.#emitTemplateLineComment(comment_start, index, null);
|
|
642
|
+
} else {
|
|
643
|
+
break;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
if (!has_newline) return;
|
|
647
|
+
const loc = acorn.getLineInfo(this.input, index);
|
|
648
|
+
this.start = index;
|
|
649
|
+
this.startLoc = new acorn.Position(loc.line, loc.column);
|
|
650
|
+
if (this.pos <= index) {
|
|
651
|
+
this.curLine = loc.line;
|
|
652
|
+
this.lineStart = index - loc.column;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* @param {number} index
|
|
658
|
+
*/
|
|
659
|
+
#isNewlineCharCode(index) {
|
|
660
|
+
const ch = this.input.charCodeAt(index);
|
|
661
|
+
return ch === CharCode.lineFeed || ch === CharCode.carriageReturn;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* @param {number} start
|
|
666
|
+
* @param {number} end
|
|
667
|
+
* @param {Parse.CommentMetaData | null} metadata
|
|
668
|
+
*/
|
|
669
|
+
#emitTemplateLineComment(start, end, metadata) {
|
|
670
|
+
if (!this.options.onComment) return;
|
|
671
|
+
this.options.onComment(
|
|
672
|
+
false,
|
|
673
|
+
this.input.slice(start + 2, end),
|
|
674
|
+
start,
|
|
675
|
+
end,
|
|
676
|
+
acorn.getLineInfo(this.input, start),
|
|
677
|
+
acorn.getLineInfo(this.input, end),
|
|
678
|
+
metadata,
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
|
|
643
682
|
#isSwitchCaseScriptStatementStart() {
|
|
644
683
|
let index = skip_whitespace_from(this.input, this.start);
|
|
645
684
|
|
|
@@ -3650,15 +3689,17 @@ export function TSRXPlugin(config) {
|
|
|
3650
3689
|
}
|
|
3651
3690
|
|
|
3652
3691
|
/**
|
|
3653
|
-
* `@try`/`@pending`/`@catch` blocks
|
|
3654
|
-
*
|
|
3655
|
-
*
|
|
3656
|
-
*
|
|
3692
|
+
* `@try`/`@pending`/`@catch` blocks are template control-flow blocks like
|
|
3693
|
+
* `@if`/`@for`/`@switch`: `return` is not allowed inside them. A `return`
|
|
3694
|
+
* is only valid in the JS setup at the top of a `@{ … }` code block, never
|
|
3695
|
+
* inside a `@`-directive block. Report any direct `return` with the same
|
|
3696
|
+
* template-return diagnostic used elsewhere.
|
|
3657
3697
|
* @returns {AST.BlockStatement}
|
|
3658
3698
|
*/
|
|
3659
3699
|
#parseTemplateControlFlowReturnBlock(createNewLexicalScope = true) {
|
|
3660
|
-
|
|
3661
|
-
|
|
3700
|
+
const block = this.#parseTemplateControlFlowBlock(createNewLexicalScope);
|
|
3701
|
+
this.#report_invalid_template_return_statements(block.body);
|
|
3702
|
+
return block;
|
|
3662
3703
|
}
|
|
3663
3704
|
|
|
3664
3705
|
/**
|
|
@@ -4428,50 +4469,58 @@ export function TSRXPlugin(config) {
|
|
|
4428
4469
|
this.context.pop();
|
|
4429
4470
|
}
|
|
4430
4471
|
return;
|
|
4431
|
-
} else if (
|
|
4432
|
-
this.type === tstt.jsxTagStart ||
|
|
4433
|
-
this.input.charCodeAt(this.start) === CharCode.lessThan
|
|
4434
|
-
) {
|
|
4472
|
+
} else if (this.type === tstt.jsxTagStart) {
|
|
4435
4473
|
const startPos = this.start;
|
|
4436
4474
|
const startLoc = this.startLoc;
|
|
4437
|
-
|
|
4438
|
-
this.next();
|
|
4439
|
-
} else {
|
|
4440
|
-
// A control-flow block inside a native template can leave the tokenizer
|
|
4441
|
-
// in normal JS mode, so a closing tag may arrive as a relational
|
|
4442
|
-
// `<` token. Re-enter JSX closing-tag parsing manually.
|
|
4443
|
-
this.pos = startPos + 1;
|
|
4444
|
-
this.type = tstt.jsxTagStart;
|
|
4445
|
-
this.start = startPos;
|
|
4446
|
-
this.startLoc = startLoc;
|
|
4447
|
-
this.exprAllowed = false;
|
|
4448
|
-
// A genuine `jsxTagStart` pushes `tc_expr` + `tc_oTag` in its
|
|
4449
|
-
// `updateContext`; faking the token here skips those pushes. That is
|
|
4450
|
-
// harmless for an opening tag (the next token is the tag name), but a
|
|
4451
|
-
// closing tag (`</`) immediately runs `context.length -= 2` in the
|
|
4452
|
-
// slash `updateContext`, which would underflow the context stack and
|
|
4453
|
-
// throw "Invalid array length" (e.g. `<>@if (a) { … } done</>`). Push
|
|
4454
|
-
// the two contexts a real `jsxTagStart` would have added so the closing
|
|
4455
|
-
// tag pops its own contexts instead of the enclosing template's.
|
|
4456
|
-
if (this.input.charCodeAt(this.pos) === CharCode.slash) {
|
|
4457
|
-
this.context.push(tstc.tc_expr);
|
|
4458
|
-
this.context.push(tstc.tc_oTag);
|
|
4459
|
-
}
|
|
4460
|
-
this.next();
|
|
4461
|
-
}
|
|
4475
|
+
this.next();
|
|
4462
4476
|
if (this.value === '/' || this.type === tt.slash) {
|
|
4463
4477
|
// Consume '/'
|
|
4464
4478
|
this.next();
|
|
4465
4479
|
|
|
4466
|
-
|
|
4480
|
+
const closingNode = this.startNodeAt(startPos, startLoc);
|
|
4481
|
+
const inside_parent_template =
|
|
4482
|
+
this.#jsxExpressionContainerDepth === 0 &&
|
|
4483
|
+
this.#templateScriptParsingDepth === 0 &&
|
|
4484
|
+
this.#path.slice(0, -1).some((node) => this.#isNativeTemplateNode(node));
|
|
4467
4485
|
this.#closingNativeTemplateNode = true;
|
|
4486
|
+
/** @type {ReturnType<Parse.Parser['jsx_parseElementName']>} */
|
|
4487
|
+
let closingName;
|
|
4468
4488
|
try {
|
|
4469
|
-
|
|
4470
|
-
this.jsx_parseClosingElementAt(startPos, startLoc)
|
|
4471
|
-
);
|
|
4489
|
+
closingName = this.jsx_parseElementName();
|
|
4472
4490
|
} finally {
|
|
4473
4491
|
this.#closingNativeTemplateNode = false;
|
|
4474
4492
|
}
|
|
4493
|
+
if (closingName) {
|
|
4494
|
+
/** @type {ESTreeJSX.JSXClosingElement} */ (closingNode).name =
|
|
4495
|
+
/** @type {ESTreeJSX.JSXIdentifier} */ (closingName);
|
|
4496
|
+
}
|
|
4497
|
+
const current = /** @type {ESTreeJSX.JSXFragment | ESTreeJSX.JSXElement} */ (
|
|
4498
|
+
this.#path[this.#path.length - 1]
|
|
4499
|
+
);
|
|
4500
|
+
const current_name = this.#isNativeTemplateNode(current)
|
|
4501
|
+
? current.type === 'JSXFragment'
|
|
4502
|
+
? ''
|
|
4503
|
+
: current.openingElement?.name
|
|
4504
|
+
? this.getElementName(current.openingElement.name)
|
|
4505
|
+
: null
|
|
4506
|
+
: null;
|
|
4507
|
+
const closing_name_str = !closingName
|
|
4508
|
+
? ''
|
|
4509
|
+
: closingName.type === 'JSXNamespacedName'
|
|
4510
|
+
? closingName.namespace.name + ':' + closingName.name.name
|
|
4511
|
+
: this.getElementName(closingName);
|
|
4512
|
+
if (!(inside_parent_template && current_name === closing_name_str)) {
|
|
4513
|
+
this.#closingNativeTemplateNode = true;
|
|
4514
|
+
}
|
|
4515
|
+
this.expect(tstt.jsxTagEnd);
|
|
4516
|
+
this.#closingNativeTemplateNode = false;
|
|
4517
|
+
const closingElement =
|
|
4518
|
+
/** @type {ESTreeJSX.JSXClosingElement & AST.NodeWithLocation} */ (
|
|
4519
|
+
this.finishNode(
|
|
4520
|
+
closingNode,
|
|
4521
|
+
closingName ? 'JSXClosingElement' : 'JSXClosingFragment',
|
|
4522
|
+
)
|
|
4523
|
+
);
|
|
4475
4524
|
if (this.#isDynamicJSXElementName(closingElement.name)) {
|
|
4476
4525
|
/** @type {any} */ (closingElement).isDynamic = true;
|
|
4477
4526
|
}
|
|
@@ -4584,7 +4633,7 @@ export function TSRXPlugin(config) {
|
|
|
4584
4633
|
}
|
|
4585
4634
|
|
|
4586
4635
|
this.#path.pop();
|
|
4587
|
-
|
|
4636
|
+
this.#skipTrailingLayoutWhitespace();
|
|
4588
4637
|
return;
|
|
4589
4638
|
}
|
|
4590
4639
|
const node = this.parseElement();
|
|
@@ -260,3 +260,51 @@ export function tsx_with_ts_locations() {
|
|
|
260
260
|
|
|
261
261
|
return { ...base, ...wrappers };
|
|
262
262
|
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* @param {any} node
|
|
266
|
+
* @returns {boolean}
|
|
267
|
+
*/
|
|
268
|
+
export function is_template_if_node(node) {
|
|
269
|
+
return (
|
|
270
|
+
node?.type === 'JSXIfExpression' ||
|
|
271
|
+
node?.metadata?.tsrxDirective === 'if' ||
|
|
272
|
+
(node?.type === 'IfStatement' && node?.statementType === 'IfStatement')
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* @param {any} node
|
|
278
|
+
* @returns {boolean}
|
|
279
|
+
*/
|
|
280
|
+
export function is_template_for_of_node(node) {
|
|
281
|
+
return (
|
|
282
|
+
node?.type === 'JSXForExpression' ||
|
|
283
|
+
node?.metadata?.tsrxDirective === 'for' ||
|
|
284
|
+
(node?.type === 'ForOfStatement' && node?.statementType === 'ForOfStatement')
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* @param {any} node
|
|
290
|
+
* @returns {boolean}
|
|
291
|
+
*/
|
|
292
|
+
export function is_template_switch_node(node) {
|
|
293
|
+
return (
|
|
294
|
+
node?.type === 'JSXSwitchExpression' ||
|
|
295
|
+
node?.metadata?.tsrxDirective === 'switch' ||
|
|
296
|
+
(node?.type === 'SwitchStatement' && node?.statementType === 'SwitchStatement')
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* @param {any} node
|
|
302
|
+
* @returns {boolean}
|
|
303
|
+
*/
|
|
304
|
+
export function is_template_try_node(node) {
|
|
305
|
+
return (
|
|
306
|
+
node?.type === 'JSXTryExpression' ||
|
|
307
|
+
node?.metadata?.tsrxDirective === 'try' ||
|
|
308
|
+
(node?.type === 'TryStatement' && node?.statementType === 'TryStatement')
|
|
309
|
+
);
|
|
310
|
+
}
|
|
@@ -10,8 +10,11 @@ import { prune_css } from '../../analyze/prune.js';
|
|
|
10
10
|
import {
|
|
11
11
|
in_jsx_child_context,
|
|
12
12
|
set_node_path_metadata,
|
|
13
|
-
tsx_node_to_jsx_expression,
|
|
14
13
|
tsx_with_ts_locations,
|
|
14
|
+
is_template_if_node,
|
|
15
|
+
is_template_for_of_node,
|
|
16
|
+
is_template_switch_node,
|
|
17
|
+
is_template_try_node,
|
|
15
18
|
} from './helpers.js';
|
|
16
19
|
import {
|
|
17
20
|
add_extra_source_mappings_from_matching_expression,
|
|
@@ -71,6 +74,16 @@ const TSRX_IF_CONTINUE_ERROR =
|
|
|
71
74
|
'Continue statements are not allowed inside TSRX template @if blocks. Filter before rendering or use conditional output instead.';
|
|
72
75
|
const DYNAMIC_IMPORT_LOCAL = 'TsrxDynamic';
|
|
73
76
|
const DYNAMIC_FACTORY_LOCAL = '_tsrx_dynamic';
|
|
77
|
+
const LEADING_INLINE_WHITESPACE = /^[ \t]+/;
|
|
78
|
+
const TRAILING_INLINE_WHITESPACE = /[ \t]+$/;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param {string | undefined} ch
|
|
82
|
+
* @returns {boolean}
|
|
83
|
+
*/
|
|
84
|
+
function is_newline_char(ch) {
|
|
85
|
+
return ch === '\n' || ch === '\r';
|
|
86
|
+
}
|
|
74
87
|
|
|
75
88
|
/**
|
|
76
89
|
* @param {AST.Node} node
|
|
@@ -2410,9 +2423,7 @@ function mark_native_pretransformed_jsx(node, seen = new Set()) {
|
|
|
2410
2423
|
function get_tsrx_render_children(node) {
|
|
2411
2424
|
return (node.children || []).filter(
|
|
2412
2425
|
(/** @type {any} */ child) =>
|
|
2413
|
-
child &&
|
|
2414
|
-
child.type !== 'EmptyStatement' &&
|
|
2415
|
-
(child.type !== 'JSXText' || child.value.trim() !== ''),
|
|
2426
|
+
child && child.type !== 'EmptyStatement' && (child.type !== 'JSXText' || child.value !== ''),
|
|
2416
2427
|
);
|
|
2417
2428
|
}
|
|
2418
2429
|
|
|
@@ -3396,7 +3407,9 @@ function create_element_children(children, transform_context) {
|
|
|
3396
3407
|
const saved_inside_element_child = transform_context.inside_element_child;
|
|
3397
3408
|
transform_context.inside_element_child = true;
|
|
3398
3409
|
try {
|
|
3399
|
-
return
|
|
3410
|
+
return wrap_edge_whitespace(
|
|
3411
|
+
children.map((/** @type {any} */ child) => to_jsx_child(child, transform_context)),
|
|
3412
|
+
);
|
|
3400
3413
|
} finally {
|
|
3401
3414
|
transform_context.inside_element_child = saved_inside_element_child;
|
|
3402
3415
|
}
|
|
@@ -3946,6 +3959,67 @@ function is_try_control_node(node) {
|
|
|
3946
3959
|
return node?.type === 'TryStatement' || node?.type === 'JSXTryExpression';
|
|
3947
3960
|
}
|
|
3948
3961
|
|
|
3962
|
+
/**
|
|
3963
|
+
* Wrap the inline whitespace at a fragment/element's content edges in `{' '}`
|
|
3964
|
+
* containers. A bare leading/trailing space is fragile: once the output is
|
|
3965
|
+
* line-wrapped (by prettier or the host JSX compiler) it becomes newline-adjacent
|
|
3966
|
+
* and is trimmed away, dropping a significant space. Whitespace BETWEEN siblings
|
|
3967
|
+
* stays bare text — it is not at an edge and is preserved as-is. Only spaces/tabs
|
|
3968
|
+
* are pulled out; whitespace runs containing a newline are layout indentation and
|
|
3969
|
+
* are left for the host compiler to collapse.
|
|
3970
|
+
*
|
|
3971
|
+
* @param {any[]} nodes
|
|
3972
|
+
* @returns {any[]}
|
|
3973
|
+
*/
|
|
3974
|
+
export function wrap_edge_whitespace(nodes) {
|
|
3975
|
+
const length = nodes.length;
|
|
3976
|
+
if (length === 0) {
|
|
3977
|
+
return nodes;
|
|
3978
|
+
}
|
|
3979
|
+
|
|
3980
|
+
const first = nodes[0];
|
|
3981
|
+
const last = nodes[length - 1];
|
|
3982
|
+
if (first?.type !== 'JSXText' && last?.type !== 'JSXText') {
|
|
3983
|
+
return nodes;
|
|
3984
|
+
}
|
|
3985
|
+
|
|
3986
|
+
/** @type {(ESTreeJSX.JSXExpressionContainer | ESTreeJSX.JSXText)[]} */
|
|
3987
|
+
const out = [];
|
|
3988
|
+
for (let i = 0; i < length; i++) {
|
|
3989
|
+
const node = nodes[i];
|
|
3990
|
+
const at_start = i === 0;
|
|
3991
|
+
const at_end = i === length - 1;
|
|
3992
|
+
if (!node || node.type !== 'JSXText' || (!at_start && !at_end)) {
|
|
3993
|
+
out.push(node);
|
|
3994
|
+
continue;
|
|
3995
|
+
}
|
|
3996
|
+
let value = /** @type {string} */ (node.value);
|
|
3997
|
+
if (at_start) {
|
|
3998
|
+
const lead = LEADING_INLINE_WHITESPACE.exec(value);
|
|
3999
|
+
if (lead && !is_newline_char(value[lead[0].length])) {
|
|
4000
|
+
out.push(to_jsx_expression_container(b.literal(lead[0]), node));
|
|
4001
|
+
value = value.slice(lead[0].length);
|
|
4002
|
+
}
|
|
4003
|
+
}
|
|
4004
|
+
/** @type {ESTreeJSX.JSXExpressionContainer | null} */
|
|
4005
|
+
let trailing = null;
|
|
4006
|
+
if (at_end) {
|
|
4007
|
+
const trail = TRAILING_INLINE_WHITESPACE.exec(value);
|
|
4008
|
+
if (trail && !is_newline_char(value[value.length - trail[0].length - 1])) {
|
|
4009
|
+
trailing = to_jsx_expression_container(b.literal(trail[0]), node);
|
|
4010
|
+
value = value.slice(0, value.length - trail[0].length);
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
4013
|
+
if (value !== '') {
|
|
4014
|
+
out.push(b.jsx_text(value, value));
|
|
4015
|
+
}
|
|
4016
|
+
if (trailing) {
|
|
4017
|
+
out.push(trailing);
|
|
4018
|
+
}
|
|
4019
|
+
}
|
|
4020
|
+
return out;
|
|
4021
|
+
}
|
|
4022
|
+
|
|
3949
4023
|
/**
|
|
3950
4024
|
* @param {any} node
|
|
3951
4025
|
* @param {TransformContext} transform_context
|
|
@@ -4031,15 +4105,15 @@ function to_jsx_child(node, transform_context) {
|
|
|
4031
4105
|
function tsrx_node_to_jsx_expression(node, transform_context, in_jsx_child = false) {
|
|
4032
4106
|
const children = (node.children || []).filter(
|
|
4033
4107
|
(/** @type {any} */ child) =>
|
|
4034
|
-
child &&
|
|
4035
|
-
child.type !== 'EmptyStatement' &&
|
|
4036
|
-
(child.type !== 'JSXText' || child.value.trim() !== ''),
|
|
4108
|
+
child && child.type !== 'EmptyStatement' && (child.type !== 'JSXText' || child.value !== ''),
|
|
4037
4109
|
);
|
|
4038
4110
|
|
|
4039
4111
|
/** @type {any} */
|
|
4040
4112
|
let expression;
|
|
4041
4113
|
if (children.length === 0) {
|
|
4042
|
-
expression =
|
|
4114
|
+
expression = in_jsx_child
|
|
4115
|
+
? set_loc(b.jsx_fragment([]), node.loc ? node : undefined)
|
|
4116
|
+
: create_null_literal();
|
|
4043
4117
|
} else {
|
|
4044
4118
|
expression = return_value_body_to_expression(children, node, transform_context);
|
|
4045
4119
|
}
|
|
@@ -4049,10 +4123,10 @@ function tsrx_node_to_jsx_expression(node, transform_context, in_jsx_child = fal
|
|
|
4049
4123
|
const saved_inside_element_child = transform_context.inside_element_child;
|
|
4050
4124
|
transform_context.inside_element_child = true;
|
|
4051
4125
|
try {
|
|
4052
|
-
const render_nodes =
|
|
4053
|
-
to_jsx_child(child, transform_context),
|
|
4126
|
+
const render_nodes = wrap_edge_whitespace(
|
|
4127
|
+
children.map((/** @type {any} */ child) => to_jsx_child(child, transform_context)),
|
|
4054
4128
|
);
|
|
4055
|
-
expression = build_return_expression(render_nodes) || create_null_literal();
|
|
4129
|
+
expression = build_return_expression(render_nodes, in_jsx_child) || create_null_literal();
|
|
4056
4130
|
} finally {
|
|
4057
4131
|
transform_context.inside_element_child = saved_inside_element_child;
|
|
4058
4132
|
}
|
|
@@ -4485,54 +4559,6 @@ function validate_if_body_control_flow(node, transform_context) {
|
|
|
4485
4559
|
}
|
|
4486
4560
|
}
|
|
4487
4561
|
|
|
4488
|
-
/**
|
|
4489
|
-
* @param {any} node
|
|
4490
|
-
* @returns {boolean}
|
|
4491
|
-
*/
|
|
4492
|
-
function is_template_if_node(node) {
|
|
4493
|
-
return (
|
|
4494
|
-
node?.type === 'JSXIfExpression' ||
|
|
4495
|
-
node?.metadata?.tsrxDirective === 'if' ||
|
|
4496
|
-
(node?.type === 'IfStatement' && node?.statementType === 'IfStatement')
|
|
4497
|
-
);
|
|
4498
|
-
}
|
|
4499
|
-
|
|
4500
|
-
/**
|
|
4501
|
-
* @param {any} node
|
|
4502
|
-
* @returns {boolean}
|
|
4503
|
-
*/
|
|
4504
|
-
function is_template_for_of_node(node) {
|
|
4505
|
-
return (
|
|
4506
|
-
node?.type === 'JSXForExpression' ||
|
|
4507
|
-
node?.metadata?.tsrxDirective === 'for' ||
|
|
4508
|
-
(node?.type === 'ForOfStatement' && node?.statementType === 'ForOfStatement')
|
|
4509
|
-
);
|
|
4510
|
-
}
|
|
4511
|
-
|
|
4512
|
-
/**
|
|
4513
|
-
* @param {any} node
|
|
4514
|
-
* @returns {boolean}
|
|
4515
|
-
*/
|
|
4516
|
-
function is_template_switch_node(node) {
|
|
4517
|
-
return (
|
|
4518
|
-
node?.type === 'JSXSwitchExpression' ||
|
|
4519
|
-
node?.metadata?.tsrxDirective === 'switch' ||
|
|
4520
|
-
(node?.type === 'SwitchStatement' && node?.statementType === 'SwitchStatement')
|
|
4521
|
-
);
|
|
4522
|
-
}
|
|
4523
|
-
|
|
4524
|
-
/**
|
|
4525
|
-
* @param {any} node
|
|
4526
|
-
* @returns {boolean}
|
|
4527
|
-
*/
|
|
4528
|
-
function is_template_try_node(node) {
|
|
4529
|
-
return (
|
|
4530
|
-
node?.type === 'JSXTryExpression' ||
|
|
4531
|
-
node?.metadata?.tsrxDirective === 'try' ||
|
|
4532
|
-
(node?.type === 'TryStatement' && node?.statementType === 'TryStatement')
|
|
4533
|
-
);
|
|
4534
|
-
}
|
|
4535
|
-
|
|
4536
4562
|
/**
|
|
4537
4563
|
* @param {any} node
|
|
4538
4564
|
* @returns {boolean}
|
|
@@ -5884,9 +5910,10 @@ function value_has_unmappable_jsx_loc(value) {
|
|
|
5884
5910
|
|
|
5885
5911
|
/**
|
|
5886
5912
|
* @param {any[]} render_nodes
|
|
5913
|
+
* @param {boolean} [in_jsx_child]
|
|
5887
5914
|
* @returns {any}
|
|
5888
5915
|
*/
|
|
5889
|
-
function build_return_expression(render_nodes) {
|
|
5916
|
+
function build_return_expression(render_nodes, in_jsx_child = false) {
|
|
5890
5917
|
if (render_nodes.length === 0) return null;
|
|
5891
5918
|
if (render_nodes.length === 1) {
|
|
5892
5919
|
const only = render_nodes[0];
|
|
@@ -5900,6 +5927,9 @@ function build_return_expression(render_nodes) {
|
|
|
5900
5927
|
return only.expression;
|
|
5901
5928
|
}
|
|
5902
5929
|
if (only.type === 'JSXText') {
|
|
5930
|
+
if (in_jsx_child) {
|
|
5931
|
+
return set_loc(b.jsx_fragment([only]), only.loc ? only : undefined);
|
|
5932
|
+
}
|
|
5903
5933
|
const value = (only.value ?? '').trim();
|
|
5904
5934
|
return b.literal(value, JSON.stringify(value), only);
|
|
5905
5935
|
}
|
package/src/utils/builders.js
CHANGED
|
@@ -368,6 +368,26 @@ export function literal(value, raw, loc_info) {
|
|
|
368
368
|
return set_location(node, loc_info);
|
|
369
369
|
}
|
|
370
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Ripple template `Text` node wrapping a string literal of `value`. Used by the
|
|
373
|
+
* Ripple normalizer when lowering a JSX text child to a template Text node.
|
|
374
|
+
*
|
|
375
|
+
* Remove once Ripple transformers move to the parser's JSX AST
|
|
376
|
+
*
|
|
377
|
+
* @param {string} value
|
|
378
|
+
* @param {AST.NodeWithLocation} [loc_info]
|
|
379
|
+
* @returns {AST.Text}
|
|
380
|
+
*/
|
|
381
|
+
export function text(value, loc_info) {
|
|
382
|
+
const node = /** @type {AST.Text} */ ({
|
|
383
|
+
type: 'Text',
|
|
384
|
+
expression: literal(value, JSON.stringify(value), loc_info),
|
|
385
|
+
metadata: { path: [] },
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
return set_location(node, loc_info);
|
|
389
|
+
}
|
|
390
|
+
|
|
371
391
|
/**
|
|
372
392
|
* @param {AST.Expression | AST.Super} object
|
|
373
393
|
* @param {string | AST.Expression | AST.PrivateIdentifier} property
|