@tsrx/core 0.1.18 → 0.1.20
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 +0 -1
- package/src/plugin.js +59 -171
- package/src/scope.js +3 -3
- package/src/transform/jsx/ast-builders.js +3 -4
- package/src/transform/jsx/helpers.js +21 -23
- package/src/transform/jsx/index.js +403 -292
- package/types/index.d.ts +24 -19
- package/types/jsx-platform.d.ts +1 -7
- package/types/parse.d.ts +1 -1
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -162,7 +162,6 @@ export {
|
|
|
162
162
|
validate_at_most_one_ref_attribute as validateAtMostOneRefAttribute,
|
|
163
163
|
} from './transform/jsx/index.js';
|
|
164
164
|
export {
|
|
165
|
-
ensure_function_metadata as ensureFunctionMetadata,
|
|
166
165
|
in_jsx_child_context as inJsxChildContext,
|
|
167
166
|
tsx_node_to_jsx_expression as tsxNodeToJsxExpression,
|
|
168
167
|
tsx_with_ts_locations as tsxWithTsLocations,
|
package/src/plugin.js
CHANGED
|
@@ -341,10 +341,7 @@ export function TSRXPlugin(config) {
|
|
|
341
341
|
*/
|
|
342
342
|
#isNativeTemplateNode(node) {
|
|
343
343
|
return (
|
|
344
|
-
node?.type === 'Element' ||
|
|
345
|
-
node?.type === 'Tsx' ||
|
|
346
|
-
node?.type === 'Tsrx' ||
|
|
347
|
-
node?.type === 'TsxCompat'
|
|
344
|
+
node?.type === 'Element' || node?.type === 'TsrxFragment' || node?.type === 'TsxCompat'
|
|
348
345
|
);
|
|
349
346
|
}
|
|
350
347
|
|
|
@@ -368,7 +365,7 @@ export function TSRXPlugin(config) {
|
|
|
368
365
|
);
|
|
369
366
|
}
|
|
370
367
|
this.#reportDynamicJsxElementsInTsx(/** @type {AST.Node[]} */ (child.children));
|
|
371
|
-
} else if (child?.type === '
|
|
368
|
+
} else if (child?.type === 'TsxCompat') {
|
|
372
369
|
this.#reportDynamicJsxElementsInTsx(/** @type {AST.Node[]} */ (child.children));
|
|
373
370
|
}
|
|
374
371
|
}
|
|
@@ -396,16 +393,11 @@ export function TSRXPlugin(config) {
|
|
|
396
393
|
}
|
|
397
394
|
|
|
398
395
|
/**
|
|
399
|
-
* @param {AST.
|
|
396
|
+
* @param {AST.TsxCompat} island
|
|
400
397
|
* @param {AST.Node[]} body
|
|
401
398
|
*/
|
|
402
399
|
#parseTsxIslandBody(island, body) {
|
|
403
|
-
const tagName =
|
|
404
|
-
island.type === 'TsxCompat'
|
|
405
|
-
? `tsx:${island.kind}`
|
|
406
|
-
: island.openingElement.name
|
|
407
|
-
? 'tsx'
|
|
408
|
-
: '';
|
|
400
|
+
const tagName = `tsx:${island.kind}`;
|
|
409
401
|
|
|
410
402
|
this.exprAllowed = true;
|
|
411
403
|
|
|
@@ -424,7 +416,7 @@ export function TSRXPlugin(config) {
|
|
|
424
416
|
return;
|
|
425
417
|
}
|
|
426
418
|
|
|
427
|
-
if (this.#isAtTsxIslandClosing(
|
|
419
|
+
if (this.#isAtTsxIslandClosing()) {
|
|
428
420
|
this.exprAllowed = false;
|
|
429
421
|
return;
|
|
430
422
|
}
|
|
@@ -459,7 +451,7 @@ export function TSRXPlugin(config) {
|
|
|
459
451
|
);
|
|
460
452
|
node.expression = expression;
|
|
461
453
|
this.#popTokenContextsAfterTemplateExpressionElement(
|
|
462
|
-
/** @type {AST.
|
|
454
|
+
/** @type {AST.TsrxFragment | AST.TsxCompat} */ (/** @type {unknown} */ (expression)),
|
|
463
455
|
);
|
|
464
456
|
this.expect(tt.braceR);
|
|
465
457
|
return this.finishNode(node, 'JSXExpressionContainer');
|
|
@@ -499,38 +491,13 @@ export function TSRXPlugin(config) {
|
|
|
499
491
|
* @param {number} index
|
|
500
492
|
*/
|
|
501
493
|
#isReservedTemplateTagNameStart(index) {
|
|
502
|
-
|
|
503
|
-
return (
|
|
504
|
-
this.input.startsWith('tsx', index) &&
|
|
505
|
-
(index + 3 >= this.input.length ||
|
|
506
|
-
char_after_tsx === CharCode.greaterThan ||
|
|
507
|
-
char_after_tsx === CharCode.slash ||
|
|
508
|
-
char_after_tsx === CharCode.space ||
|
|
509
|
-
char_after_tsx === CharCode.tab ||
|
|
510
|
-
char_after_tsx === CharCode.lineFeed ||
|
|
511
|
-
char_after_tsx === CharCode.carriageReturn ||
|
|
512
|
-
char_after_tsx === CharCode.colon)
|
|
513
|
-
);
|
|
494
|
+
return this.input.startsWith('tsx:', index);
|
|
514
495
|
}
|
|
515
496
|
|
|
516
497
|
/**
|
|
517
|
-
* @param {AST.Tsx | AST.TsxCompat} island
|
|
518
498
|
*/
|
|
519
|
-
#isAtTsxIslandClosing(
|
|
520
|
-
|
|
521
|
-
return this.input.slice(this.pos, this.pos + 5) === '/tsx:';
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
if (!island.openingElement.name) {
|
|
525
|
-
return this.input.slice(this.pos, this.pos + 2) === '/>';
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (this.input.slice(this.pos, this.pos + 4) !== '/tsx') {
|
|
529
|
-
return false;
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
const after = this.input.charCodeAt(this.pos + 4);
|
|
533
|
-
return after === CharCode.greaterThan;
|
|
499
|
+
#isAtTsxIslandClosing() {
|
|
500
|
+
return this.input.slice(this.pos, this.pos + 5) === '/tsx:';
|
|
534
501
|
}
|
|
535
502
|
|
|
536
503
|
#parseTsxIslandText() {
|
|
@@ -567,7 +534,7 @@ export function TSRXPlugin(config) {
|
|
|
567
534
|
let index = this.pos;
|
|
568
535
|
let has_newline = false;
|
|
569
536
|
|
|
570
|
-
// Text-only
|
|
537
|
+
// Text-only compat islands can leave the tokenizer in JSX text mode.
|
|
571
538
|
// Only unwind it for ASI before a following TSRX `{expr}` child;
|
|
572
539
|
// fragment props like `content={<></>}` still need the JSX context.
|
|
573
540
|
while (index < this.input.length) {
|
|
@@ -664,7 +631,7 @@ export function TSRXPlugin(config) {
|
|
|
664
631
|
}
|
|
665
632
|
|
|
666
633
|
/**
|
|
667
|
-
* @param {AST.
|
|
634
|
+
* @param {AST.TsrxFragment | AST.TsxCompat} node
|
|
668
635
|
* @returns {boolean}
|
|
669
636
|
*/
|
|
670
637
|
#hasDirectStatementChild(node) {
|
|
@@ -674,7 +641,7 @@ export function TSRXPlugin(config) {
|
|
|
674
641
|
}
|
|
675
642
|
|
|
676
643
|
/**
|
|
677
|
-
* @param {AST.
|
|
644
|
+
* @param {AST.TsrxFragment | AST.TsxCompat} node
|
|
678
645
|
*/
|
|
679
646
|
#popTokenContextsAfterTemplateExpressionElement(node) {
|
|
680
647
|
const ctx = this.context;
|
|
@@ -769,15 +736,14 @@ export function TSRXPlugin(config) {
|
|
|
769
736
|
|
|
770
737
|
#isDoubleQuotedTextChildStart() {
|
|
771
738
|
const current_template_node = this.#path.findLast(
|
|
772
|
-
(n) =>
|
|
773
|
-
n.type === 'Element' || n.type === 'Tsx' || n.type === 'Tsrx' || n.type === 'TsxCompat',
|
|
739
|
+
(n) => n.type === 'Element' || n.type === 'TsrxFragment' || n.type === 'TsxCompat',
|
|
774
740
|
);
|
|
775
|
-
if (current_template_node?.type === 'TsxCompat'
|
|
741
|
+
if (current_template_node?.type === 'TsxCompat') {
|
|
776
742
|
return false;
|
|
777
743
|
}
|
|
778
744
|
|
|
779
745
|
const parent = this.#path.at(-1);
|
|
780
|
-
if (!parent || (parent.type !== 'Element' && parent.type !== '
|
|
746
|
+
if (!parent || (parent.type !== 'Element' && parent.type !== 'TsrxFragment')) {
|
|
781
747
|
return false;
|
|
782
748
|
}
|
|
783
749
|
|
|
@@ -1193,7 +1159,7 @@ export function TSRXPlugin(config) {
|
|
|
1193
1159
|
const parent = this.#path.at(-1);
|
|
1194
1160
|
const inNativeTemplate =
|
|
1195
1161
|
this.#functionBodyDepth === 0 &&
|
|
1196
|
-
(parent?.type === 'Element' || parent?.type === '
|
|
1162
|
+
(parent?.type === 'Element' || parent?.type === 'TsrxFragment');
|
|
1197
1163
|
/** @type {number | null} */
|
|
1198
1164
|
let prevNonWhitespaceChar = null;
|
|
1199
1165
|
|
|
@@ -1740,16 +1706,9 @@ export function TSRXPlugin(config) {
|
|
|
1740
1706
|
|
|
1741
1707
|
if (this.eat(tt.braceL)) {
|
|
1742
1708
|
const current_template_node = this.#path.findLast(
|
|
1743
|
-
(n) =>
|
|
1744
|
-
n.type === 'Element' ||
|
|
1745
|
-
n.type === 'Tsx' ||
|
|
1746
|
-
n.type === 'Tsrx' ||
|
|
1747
|
-
n.type === 'TsxCompat',
|
|
1709
|
+
(n) => n.type === 'Element' || n.type === 'TsrxFragment' || n.type === 'TsxCompat',
|
|
1748
1710
|
);
|
|
1749
|
-
if (
|
|
1750
|
-
current_template_node?.type === 'Tsx' ||
|
|
1751
|
-
current_template_node?.type === 'TsxCompat'
|
|
1752
|
-
) {
|
|
1711
|
+
if (current_template_node?.type === 'TsxCompat') {
|
|
1753
1712
|
if (this.type === tt.ellipsis) {
|
|
1754
1713
|
this.expect(tt.ellipsis);
|
|
1755
1714
|
/** @type {ESTreeJSX.JSXSpreadAttribute} */ (node).argument = this.parseMaybeAssign();
|
|
@@ -1981,10 +1940,9 @@ export function TSRXPlugin(config) {
|
|
|
1981
1940
|
/** @type {Parse.Parser['jsx_readToken']} */
|
|
1982
1941
|
jsx_readToken() {
|
|
1983
1942
|
const current_template_node = this.#path.findLast(
|
|
1984
|
-
(n) =>
|
|
1985
|
-
n.type === 'Element' || n.type === 'Tsx' || n.type === 'Tsrx' || n.type === 'TsxCompat',
|
|
1943
|
+
(n) => n.type === 'Element' || n.type === 'TsrxFragment' || n.type === 'TsxCompat',
|
|
1986
1944
|
);
|
|
1987
|
-
if (current_template_node?.type === 'TsxCompat'
|
|
1945
|
+
if (current_template_node?.type === 'TsxCompat') {
|
|
1988
1946
|
return super.jsx_readToken();
|
|
1989
1947
|
}
|
|
1990
1948
|
let out = '',
|
|
@@ -1993,11 +1951,7 @@ export function TSRXPlugin(config) {
|
|
|
1993
1951
|
while (true) {
|
|
1994
1952
|
if (this.pos >= this.input.length) {
|
|
1995
1953
|
const inside_open_template = this.#path.findLast(
|
|
1996
|
-
(n) =>
|
|
1997
|
-
n.type === 'Element' ||
|
|
1998
|
-
n.type === 'Tsrx' ||
|
|
1999
|
-
n.type === 'TsxCompat' ||
|
|
2000
|
-
n.type === 'Tsx',
|
|
1954
|
+
(n) => n.type === 'Element' || n.type === 'TsrxFragment' || n.type === 'TsxCompat',
|
|
2001
1955
|
);
|
|
2002
1956
|
if (!inside_open_template) {
|
|
2003
1957
|
while (this.curContext() === tstc.tc_expr) {
|
|
@@ -2117,7 +2071,7 @@ export function TSRXPlugin(config) {
|
|
|
2117
2071
|
ch === CharCode.closeBrace &&
|
|
2118
2072
|
(this.#path.length === 0 ||
|
|
2119
2073
|
this.#path.at(-1)?.type === 'Element' ||
|
|
2120
|
-
this.#path.at(-1)?.type === '
|
|
2074
|
+
this.#path.at(-1)?.type === 'TsrxFragment')
|
|
2121
2075
|
) {
|
|
2122
2076
|
this.#resetTokenStartToCurrentPosition();
|
|
2123
2077
|
return original.readToken.call(this, ch);
|
|
@@ -2155,19 +2109,17 @@ export function TSRXPlugin(config) {
|
|
|
2155
2109
|
|
|
2156
2110
|
/**
|
|
2157
2111
|
* Override jsx_parseElement to parse tags and bare fragments as native TSRX
|
|
2158
|
-
* by default. Explicit <tsx
|
|
2159
|
-
*
|
|
2112
|
+
* by default. Explicit <tsx:*> islands keep ordinary TSX parsing for
|
|
2113
|
+
* their children.
|
|
2160
2114
|
* @type {Parse.Parser['jsx_parseElement']}
|
|
2161
2115
|
*/
|
|
2162
2116
|
jsx_parseElement() {
|
|
2163
2117
|
// Current token is jsxTagStart, this.end is position after '<'
|
|
2164
2118
|
const tag_name_start = this.end;
|
|
2165
2119
|
const current_template_node = this.#path.findLast(
|
|
2166
|
-
(n) =>
|
|
2167
|
-
n.type === 'Element' || n.type === 'Tsx' || n.type === 'Tsrx' || n.type === 'TsxCompat',
|
|
2120
|
+
(n) => n.type === 'Element' || n.type === 'TsrxFragment' || n.type === 'TsxCompat',
|
|
2168
2121
|
);
|
|
2169
|
-
const inside_tsx_island =
|
|
2170
|
-
current_template_node?.type === 'Tsx' || current_template_node?.type === 'TsxCompat';
|
|
2122
|
+
const inside_tsx_island = current_template_node?.type === 'TsxCompat';
|
|
2171
2123
|
if (inside_tsx_island) {
|
|
2172
2124
|
if (this.input.charCodeAt(tag_name_start) === CharCode.at) {
|
|
2173
2125
|
this.#report_recoverable_error_range(
|
|
@@ -2186,7 +2138,7 @@ export function TSRXPlugin(config) {
|
|
|
2186
2138
|
);
|
|
2187
2139
|
if (!inside_tsx_island) {
|
|
2188
2140
|
this.#popTokenContextsAfterTemplateExpressionElement(
|
|
2189
|
-
/** @type {AST.
|
|
2141
|
+
/** @type {AST.TsrxFragment | AST.TsxCompat} */ (/** @type {unknown} */ (parsed)),
|
|
2190
2142
|
);
|
|
2191
2143
|
} else if (this.type === tt.braceR && this.curContext() === tstc.tc_expr) {
|
|
2192
2144
|
if (this.#tsxIslandExpressionDepth === 0) {
|
|
@@ -2210,7 +2162,7 @@ export function TSRXPlugin(config) {
|
|
|
2210
2162
|
const start = this.start - 1;
|
|
2211
2163
|
const position = new acorn.Position(this.curLine, start - this.lineStart);
|
|
2212
2164
|
|
|
2213
|
-
const element = /** @type {AST.Element | AST.
|
|
2165
|
+
const element = /** @type {AST.Element | AST.TsrxFragment | AST.TsxCompat} */ (
|
|
2214
2166
|
this.startNode()
|
|
2215
2167
|
);
|
|
2216
2168
|
element.start = start;
|
|
@@ -2229,19 +2181,10 @@ export function TSRXPlugin(config) {
|
|
|
2229
2181
|
|
|
2230
2182
|
// Fragments (<>) produce JSXOpeningFragment with no `name` property
|
|
2231
2183
|
const is_fragment = !open.name;
|
|
2232
|
-
const is_tsx_compat =
|
|
2233
|
-
const is_tsx =
|
|
2234
|
-
!is_fragment &&
|
|
2235
|
-
!is_tsx_compat &&
|
|
2236
|
-
open.name.type === 'JSXIdentifier' &&
|
|
2237
|
-
open.name.name === 'tsx';
|
|
2238
|
-
const is_dynamic_name =
|
|
2184
|
+
const is_tsx_compat =
|
|
2239
2185
|
!is_fragment &&
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
open.name.object.type === 'JSXIdentifier' &&
|
|
2243
|
-
open.name.object.tracked));
|
|
2244
|
-
|
|
2186
|
+
open.name.type === 'JSXNamespacedName' &&
|
|
2187
|
+
open.name.namespace.name === 'tsx';
|
|
2245
2188
|
if (is_tsx_compat) {
|
|
2246
2189
|
const namespace_node = /** @type {ESTreeJSX.JSXNamespacedName} */ (open.name);
|
|
2247
2190
|
/** @type {AST.TsxCompat} */ (element).type = 'TsxCompat';
|
|
@@ -2254,29 +2197,12 @@ export function TSRXPlugin(config) {
|
|
|
2254
2197
|
`TSX compatibility elements cannot be self-closing. '<${tagName} />' must have a closing tag '</${tagName}>'.`,
|
|
2255
2198
|
);
|
|
2256
2199
|
}
|
|
2257
|
-
} else if (is_tsx) {
|
|
2258
|
-
/** @type {AST.Tsx} */ (element).type = 'Tsx';
|
|
2259
|
-
|
|
2260
|
-
if (open.selfClosing) {
|
|
2261
|
-
this.raise(
|
|
2262
|
-
open.start,
|
|
2263
|
-
`TSX elements cannot be self-closing. '<tsx />' must have a closing tag '</tsx>'.`,
|
|
2264
|
-
);
|
|
2265
|
-
}
|
|
2266
2200
|
} else if (is_fragment) {
|
|
2267
|
-
/** @type {AST.
|
|
2201
|
+
/** @type {AST.TsrxFragment} */ (element).type = 'TsrxFragment';
|
|
2268
2202
|
} else {
|
|
2269
2203
|
element.type = 'Element';
|
|
2270
2204
|
}
|
|
2271
2205
|
|
|
2272
|
-
if (is_tsx && is_dynamic_name) {
|
|
2273
|
-
this.#report_recoverable_error_range(
|
|
2274
|
-
open.name.start ?? open.start,
|
|
2275
|
-
open.name.end ?? open.end,
|
|
2276
|
-
DYNAMIC_ELEMENT_IN_TSX_ERROR,
|
|
2277
|
-
);
|
|
2278
|
-
}
|
|
2279
|
-
|
|
2280
2206
|
for (const attr of open.attributes) {
|
|
2281
2207
|
if (attr.type === 'JSXAttribute') {
|
|
2282
2208
|
/** @type {AST.Attribute} */ (/** @type {unknown} */ (attr)).type = 'Attribute';
|
|
@@ -2298,7 +2224,7 @@ export function TSRXPlugin(config) {
|
|
|
2298
2224
|
}
|
|
2299
2225
|
}
|
|
2300
2226
|
|
|
2301
|
-
if (!is_tsx_compat && !
|
|
2227
|
+
if (!is_tsx_compat && !is_fragment) {
|
|
2302
2228
|
/** @type {AST.Element} */ (element).id = /** @type {AST.Identifier} */ (
|
|
2303
2229
|
convert_from_jsx(/** @type {ESTreeJSX.JSXIdentifier} */ (open.name))
|
|
2304
2230
|
);
|
|
@@ -2496,35 +2422,8 @@ export function TSRXPlugin(config) {
|
|
|
2496
2422
|
enterScope: true,
|
|
2497
2423
|
resetFunctionBodyDepth: true,
|
|
2498
2424
|
});
|
|
2499
|
-
if (/** @type {AST.
|
|
2425
|
+
if (/** @type {AST.TsxCompat} */ (element).type === 'TsxCompat') {
|
|
2500
2426
|
this.#reportDynamicJsxElementsInTsx(/** @type {AST.Element} */ (element).children);
|
|
2501
|
-
}
|
|
2502
|
-
|
|
2503
|
-
if (/** @type {AST.Tsx} */ (element).type === 'Tsx') {
|
|
2504
|
-
this.#path.pop();
|
|
2505
|
-
|
|
2506
|
-
if (!element.unclosed) {
|
|
2507
|
-
const raise_error = () => {
|
|
2508
|
-
this.raise(this.start, `Expected closing tag '</tsx>'`);
|
|
2509
|
-
};
|
|
2510
|
-
|
|
2511
|
-
this.next();
|
|
2512
|
-
// we should expect to see </tsx>
|
|
2513
|
-
if (this.value !== '/') {
|
|
2514
|
-
raise_error();
|
|
2515
|
-
}
|
|
2516
|
-
this.next();
|
|
2517
|
-
if (this.value !== 'tsx') {
|
|
2518
|
-
raise_error();
|
|
2519
|
-
}
|
|
2520
|
-
this.next();
|
|
2521
|
-
if (this.type !== tstt.jsxTagEnd) {
|
|
2522
|
-
raise_error();
|
|
2523
|
-
}
|
|
2524
|
-
this.#popTsxTokenContextBeforeTemplateExpressionChild();
|
|
2525
|
-
this.next();
|
|
2526
|
-
}
|
|
2527
|
-
} else if (/** @type {AST.TsxCompat} */ (element).type === 'TsxCompat') {
|
|
2528
2427
|
this.#path.pop();
|
|
2529
2428
|
|
|
2530
2429
|
if (!element.unclosed) {
|
|
@@ -2560,7 +2459,7 @@ export function TSRXPlugin(config) {
|
|
|
2560
2459
|
this.next();
|
|
2561
2460
|
}
|
|
2562
2461
|
} else if (
|
|
2563
|
-
/** @type {AST.
|
|
2462
|
+
/** @type {AST.TsrxFragment} */ (element).type === 'TsrxFragment' &&
|
|
2564
2463
|
this.#path[this.#path.length - 1] === element
|
|
2565
2464
|
) {
|
|
2566
2465
|
const displayTag = element.openingElement.name ? 'tsrx' : '';
|
|
@@ -2603,7 +2502,7 @@ export function TSRXPlugin(config) {
|
|
|
2603
2502
|
}
|
|
2604
2503
|
}
|
|
2605
2504
|
|
|
2606
|
-
if (element.closingElement && !is_tsx_compat &&
|
|
2505
|
+
if (element.closingElement && !is_tsx_compat && element.closingElement.name) {
|
|
2607
2506
|
/** @type {unknown} */ (element.closingElement.name) = convert_from_jsx(
|
|
2608
2507
|
element.closingElement.name,
|
|
2609
2508
|
);
|
|
@@ -2620,15 +2519,12 @@ export function TSRXPlugin(config) {
|
|
|
2620
2519
|
const inside_func =
|
|
2621
2520
|
this.context.some((n) => n.token === 'function') || this.scopeStack.length > 1;
|
|
2622
2521
|
const current_template_node = this.#path.findLast(
|
|
2623
|
-
(n) =>
|
|
2624
|
-
n.type === 'Element' || n.type === 'Tsx' || n.type === 'Tsrx' || n.type === 'TsxCompat',
|
|
2522
|
+
(n) => n.type === 'Element' || n.type === 'TsrxFragment' || n.type === 'TsxCompat',
|
|
2625
2523
|
);
|
|
2626
2524
|
const inside_tsx_island =
|
|
2627
|
-
current_template_node?.type === '
|
|
2628
|
-
? current_template_node
|
|
2629
|
-
: null;
|
|
2525
|
+
current_template_node?.type === 'TsxCompat' ? current_template_node : null;
|
|
2630
2526
|
|
|
2631
|
-
if (current_template_node?.type === '
|
|
2527
|
+
if (current_template_node?.type === 'TsrxFragment' && this.type === tstt.jsxText) {
|
|
2632
2528
|
while (this.curContext() === tstc.tc_expr) {
|
|
2633
2529
|
this.context.pop();
|
|
2634
2530
|
}
|
|
@@ -2649,13 +2545,13 @@ export function TSRXPlugin(config) {
|
|
|
2649
2545
|
|
|
2650
2546
|
if (inside_tsx_island) {
|
|
2651
2547
|
this.#parseTsxIslandBody(
|
|
2652
|
-
/** @type {AST.
|
|
2548
|
+
/** @type {AST.TsxCompat} */ (inside_tsx_island),
|
|
2653
2549
|
/** @type {AST.Node[]} */ (/** @type {unknown} */ (body)),
|
|
2654
2550
|
);
|
|
2655
2551
|
return;
|
|
2656
2552
|
}
|
|
2657
2553
|
if (
|
|
2658
|
-
current_template_node?.type === '
|
|
2554
|
+
current_template_node?.type === 'TsrxFragment' &&
|
|
2659
2555
|
!current_template_node.openingElement.name &&
|
|
2660
2556
|
((this.type === tstt.jsxTagStart && this.input.slice(this.pos, this.pos + 2) === '/>') ||
|
|
2661
2557
|
(this.input.charCodeAt(this.start) === CharCode.lessThan &&
|
|
@@ -2715,8 +2611,7 @@ export function TSRXPlugin(config) {
|
|
|
2715
2611
|
if (
|
|
2716
2612
|
!currentElement ||
|
|
2717
2613
|
(currentElement.type !== 'Element' &&
|
|
2718
|
-
currentElement.type !== '
|
|
2719
|
-
currentElement.type !== 'Tsrx' &&
|
|
2614
|
+
currentElement.type !== 'TsrxFragment' &&
|
|
2720
2615
|
currentElement.type !== 'TsxCompat')
|
|
2721
2616
|
) {
|
|
2722
2617
|
this.raise(this.start, 'Unexpected closing tag');
|
|
@@ -2733,13 +2628,7 @@ export function TSRXPlugin(config) {
|
|
|
2733
2628
|
closingElement.name?.type === 'JSXNamespacedName'
|
|
2734
2629
|
? closingElement.name.namespace.name + ':' + closingElement.name.name.name
|
|
2735
2630
|
: this.getElementName(closingElement.name);
|
|
2736
|
-
} else if (currentElement.type === '
|
|
2737
|
-
openingTagName = currentElement.openingElement.name ? 'tsx' : null;
|
|
2738
|
-
closingTagName =
|
|
2739
|
-
closingElement.name?.type === 'JSXNamespacedName'
|
|
2740
|
-
? closingElement.name.namespace.name + ':' + closingElement.name.name.name
|
|
2741
|
-
: this.getElementName(closingElement.name);
|
|
2742
|
-
} else if (currentElement.type === 'Tsrx') {
|
|
2631
|
+
} else if (currentElement.type === 'TsrxFragment') {
|
|
2743
2632
|
openingTagName = '';
|
|
2744
2633
|
closingTagName =
|
|
2745
2634
|
closingElement.name?.type === 'JSXNamespacedName'
|
|
@@ -2769,8 +2658,7 @@ export function TSRXPlugin(config) {
|
|
|
2769
2658
|
// Stop at non-template boundaries.
|
|
2770
2659
|
if (
|
|
2771
2660
|
elem.type !== 'Element' &&
|
|
2772
|
-
elem.type !== '
|
|
2773
|
-
elem.type !== 'Tsrx' &&
|
|
2661
|
+
elem.type !== 'TsrxFragment' &&
|
|
2774
2662
|
elem.type !== 'TsxCompat'
|
|
2775
2663
|
) {
|
|
2776
2664
|
break;
|
|
@@ -2779,15 +2667,11 @@ export function TSRXPlugin(config) {
|
|
|
2779
2667
|
const elemName =
|
|
2780
2668
|
elem.type === 'TsxCompat'
|
|
2781
2669
|
? 'tsx:' + elem.kind
|
|
2782
|
-
: elem.type === '
|
|
2783
|
-
?
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
? ''
|
|
2788
|
-
: elem.id
|
|
2789
|
-
? this.getElementName(elem.id)
|
|
2790
|
-
: null;
|
|
2670
|
+
: elem.type === 'TsrxFragment'
|
|
2671
|
+
? ''
|
|
2672
|
+
: elem.id
|
|
2673
|
+
? this.getElementName(elem.id)
|
|
2674
|
+
: null;
|
|
2791
2675
|
|
|
2792
2676
|
// Found matching opening tag
|
|
2793
2677
|
if (elemName === closingTagName) {
|
|
@@ -2808,16 +2692,16 @@ export function TSRXPlugin(config) {
|
|
|
2808
2692
|
const elementToClose = this.#path[this.#path.length - 1];
|
|
2809
2693
|
if (
|
|
2810
2694
|
elementToClose &&
|
|
2811
|
-
(elementToClose.type === 'Element' || elementToClose.type === '
|
|
2695
|
+
(elementToClose.type === 'Element' || elementToClose.type === 'TsrxFragment')
|
|
2812
2696
|
) {
|
|
2813
2697
|
const elementToCloseName =
|
|
2814
|
-
elementToClose.type === '
|
|
2698
|
+
elementToClose.type === 'TsrxFragment'
|
|
2815
2699
|
? ''
|
|
2816
2700
|
: /** @type {AST.Element} */ (elementToClose).id
|
|
2817
2701
|
? this.getElementName(/** @type {AST.Element} */ (elementToClose).id)
|
|
2818
2702
|
: null;
|
|
2819
2703
|
if (elementToCloseName === closingTagName) {
|
|
2820
|
-
/** @type {AST.Element | AST.
|
|
2704
|
+
/** @type {AST.Element | AST.TsrxFragment} */ (elementToClose).closingElement =
|
|
2821
2705
|
closingElement;
|
|
2822
2706
|
}
|
|
2823
2707
|
}
|
|
@@ -2933,7 +2817,11 @@ export function TSRXPlugin(config) {
|
|
|
2933
2817
|
if (!node) {
|
|
2934
2818
|
this.unexpected();
|
|
2935
2819
|
}
|
|
2936
|
-
if (
|
|
2820
|
+
if (
|
|
2821
|
+
this.#functionBodyDepth > 0 &&
|
|
2822
|
+
node.type === 'TsrxFragment' &&
|
|
2823
|
+
this.curContext() === b_stat
|
|
2824
|
+
) {
|
|
2937
2825
|
this.context.pop();
|
|
2938
2826
|
if (this.curContext() === tstc.tc_expr) {
|
|
2939
2827
|
this.context.pop();
|
|
@@ -2949,7 +2837,7 @@ export function TSRXPlugin(config) {
|
|
|
2949
2837
|
this.#functionBodyDepth === 0 &&
|
|
2950
2838
|
this.type === tt.string &&
|
|
2951
2839
|
this.input.charCodeAt(this.start) === CharCode.doubleQuote &&
|
|
2952
|
-
(this.#path.at(-1)?.type === 'Element' || this.#path.at(-1)?.type === '
|
|
2840
|
+
(this.#path.at(-1)?.type === 'Element' || this.#path.at(-1)?.type === 'TsrxFragment')
|
|
2953
2841
|
) {
|
|
2954
2842
|
this.pos = this.start;
|
|
2955
2843
|
this.#readDoubleQuotedTextChildToken();
|
|
@@ -3003,7 +2891,7 @@ export function TSRXPlugin(config) {
|
|
|
3003
2891
|
// nested function callable, not in a template.
|
|
3004
2892
|
if (
|
|
3005
2893
|
this.#functionBodyDepth === 0 &&
|
|
3006
|
-
(parent?.type === 'Element' || parent?.type === '
|
|
2894
|
+
(parent?.type === 'Element' || parent?.type === 'TsrxFragment')
|
|
3007
2895
|
) {
|
|
3008
2896
|
if (createNewLexicalScope === void 0) createNewLexicalScope = true;
|
|
3009
2897
|
if (node === void 0) node = /** @type {AST.BlockStatement} */ (this.startNode());
|
package/src/scope.js
CHANGED
|
@@ -114,7 +114,7 @@ export function create_scopes(ast, root, parent, error_options) {
|
|
|
114
114
|
next({ scope });
|
|
115
115
|
},
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
TsrxFragment(node, { state, next }) {
|
|
118
118
|
const scope = state.scope.child();
|
|
119
119
|
scopes.set(node, scope);
|
|
120
120
|
|
|
@@ -192,13 +192,13 @@ export function create_scopes(ast, root, parent, error_options) {
|
|
|
192
192
|
for (const declarator of node.declarations) {
|
|
193
193
|
/** @type {Binding[]} */
|
|
194
194
|
const bindings = [];
|
|
195
|
-
const initial = /** @type {AST.Expression | AST.
|
|
195
|
+
const initial = /** @type {AST.Expression | AST.TsrxFragment | null} */ (declarator.init);
|
|
196
196
|
|
|
197
197
|
state.scope.declarators.set(declarator, bindings);
|
|
198
198
|
|
|
199
199
|
for (const id of extract_identifiers(declarator.id)) {
|
|
200
200
|
const binding = state.scope.declare(id, 'normal', node.kind, initial);
|
|
201
|
-
if (initial?.type === '
|
|
201
|
+
if (initial?.type === 'TsrxFragment') {
|
|
202
202
|
binding.metadata = {
|
|
203
203
|
...(binding.metadata ?? {}),
|
|
204
204
|
is_template_value: true,
|
|
@@ -242,8 +242,7 @@ export function is_jsx_child(node) {
|
|
|
242
242
|
t === 'JSXFragment' ||
|
|
243
243
|
t === 'JSXExpressionContainer' ||
|
|
244
244
|
t === 'JSXText' ||
|
|
245
|
-
t === '
|
|
246
|
-
t === 'Tsrx' ||
|
|
245
|
+
t === 'TsrxFragment' ||
|
|
247
246
|
t === 'TsxCompat' ||
|
|
248
247
|
t === 'Element' ||
|
|
249
248
|
t === 'Text' ||
|
|
@@ -256,8 +255,8 @@ export function is_jsx_child(node) {
|
|
|
256
255
|
}
|
|
257
256
|
|
|
258
257
|
/**
|
|
259
|
-
*
|
|
260
|
-
*
|
|
258
|
+
* Expression-position lowering unwraps single-expression native fragments to
|
|
259
|
+
* the inner expression.
|
|
261
260
|
* When such a node appears directly in a component or statement render body,
|
|
262
261
|
* the unwrapped expression is still render output rather than an executable
|
|
263
262
|
* statement.
|
|
@@ -5,9 +5,9 @@ import tsx from 'esrap/languages/tsx';
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Zimmerframe provides `path` as the ancestor chain (in original pre-transform
|
|
8
|
-
* types, since visitors run bottom-up). A
|
|
9
|
-
* `Element` will render as a JSX child of that element; anywhere else
|
|
10
|
-
* renders as a standalone expression (e.g. a return value).
|
|
8
|
+
* types, since visitors run bottom-up). A native template node whose parent is
|
|
9
|
+
* a ripple `Element` will render as a JSX child of that element; anywhere else
|
|
10
|
+
* it renders as a standalone expression (e.g. a return value).
|
|
11
11
|
*
|
|
12
12
|
* @param {any[]} path
|
|
13
13
|
* @returns {boolean}
|
|
@@ -18,7 +18,24 @@ export function in_jsx_child_context(path) {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* Match Ripple's transform path metadata shape: every node seen by the walker
|
|
22
|
+
* carries its current ancestor path for downstream CSS pruning and mapping
|
|
23
|
+
* helpers.
|
|
24
|
+
*
|
|
25
|
+
* @param {any} node
|
|
26
|
+
* @param {any[]} path
|
|
27
|
+
* @returns {void}
|
|
28
|
+
*/
|
|
29
|
+
export function set_node_path_metadata(node, path) {
|
|
30
|
+
if (!node.metadata) {
|
|
31
|
+
node.metadata = { path: [...path] };
|
|
32
|
+
} else {
|
|
33
|
+
node.metadata.path = [...path];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Flatten a JSX-compatible island's children into a single expression. In a
|
|
22
39
|
* JSX-child position, a JSXExpressionContainer `{expr}` is valid and must stay
|
|
23
40
|
* wrapped. In an expression position (e.g. `return ...`), `{expr}` parses as
|
|
24
41
|
* a block/object literal, so unwrap to `expr`.
|
|
@@ -49,25 +66,6 @@ export function tsx_node_to_jsx_expression(node, in_jsx_child = false) {
|
|
|
49
66
|
});
|
|
50
67
|
}
|
|
51
68
|
|
|
52
|
-
/**
|
|
53
|
-
* Default `node.metadata` to `{ path: [] }` if missing, then continue the
|
|
54
|
-
* walk. Use as the `FunctionDeclaration` / `FunctionExpression` /
|
|
55
|
-
* `ArrowFunctionExpression` visitor in a zimmerframe walk so that downstream
|
|
56
|
-
* consumers don't trip on an undefined metadata object.
|
|
57
|
-
*
|
|
58
|
-
* Ripple's analyze phase does this via `visit_function`; the tsrx-* targets
|
|
59
|
-
* have no analyze phase, so we default metadata during the main walk.
|
|
60
|
-
*
|
|
61
|
-
* @param {any} node
|
|
62
|
-
* @param {{ next: () => any }} ctx
|
|
63
|
-
*/
|
|
64
|
-
export function ensure_function_metadata(node, { next }) {
|
|
65
|
-
if (!node.metadata) {
|
|
66
|
-
node.metadata = { path: [] };
|
|
67
|
-
}
|
|
68
|
-
return next();
|
|
69
|
-
}
|
|
70
|
-
|
|
71
69
|
/**
|
|
72
70
|
* Wrap esrap's `tsx()` printer with location markers for nodes whose spans
|
|
73
71
|
* (e.g. the leading `new ` of a NewExpression or the angle-bracket delimiters
|