@tsrx/core 0.1.3 → 0.1.6
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 +23 -5
- package/src/diagnostics.js +1 -0
- package/src/index.js +7 -0
- package/src/plugin.js +294 -230
- package/src/runtime/index.js +110 -0
- package/src/source-map-utils.js +19 -2
- package/src/transform/jsx/ast-builders.js +120 -0
- package/src/transform/jsx/index.js +1575 -1440
- package/src/transform/lazy.js +19 -60
- package/src/transform/scoping.js +9 -45
- package/src/transform/segments.js +164 -11
- package/src/utils/builders.js +51 -13
- package/types/jsx-platform.d.ts +10 -0
- package/types/runtime/index.d.ts +13 -0
package/src/plugin.js
CHANGED
|
@@ -216,12 +216,15 @@ export function TSRXPlugin(config) {
|
|
|
216
216
|
#loose = false;
|
|
217
217
|
/** @type {AST.Node[]} */
|
|
218
218
|
#functionStack = [];
|
|
219
|
+
/** @type {Array<{ parentContext: any[], canRestore: boolean, restore: boolean }>} */
|
|
220
|
+
#functionBodyContextRestoreStack = [];
|
|
219
221
|
/** @type {import('../types/index').CompileError[] | undefined} */
|
|
220
222
|
#errors = undefined;
|
|
221
223
|
/** @type {string | null} */
|
|
222
224
|
#filename = null;
|
|
223
225
|
#componentDepth = 0;
|
|
224
226
|
#functionBodyDepth = 0;
|
|
227
|
+
#allowExpressionContainerTrailingSemicolon = false;
|
|
225
228
|
|
|
226
229
|
/**
|
|
227
230
|
* @type {Parse.Parser['finishNode']}
|
|
@@ -279,6 +282,211 @@ export function TSRXPlugin(config) {
|
|
|
279
282
|
return this.#isInsideComponent() && this.#functionBodyDepth === 0;
|
|
280
283
|
}
|
|
281
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Component bodies and native TSRX element bodies share the same grammar.
|
|
287
|
+
* This helper keeps the parser-state setup in one place while callers keep
|
|
288
|
+
* ownership of their distinct closing delimiter handling (`}` vs `</tag>`).
|
|
289
|
+
*
|
|
290
|
+
* @param {AST.Node} node
|
|
291
|
+
* @param {AST.Node[]} body
|
|
292
|
+
* @param {{
|
|
293
|
+
* enterScope?: boolean,
|
|
294
|
+
* pushPath?: boolean,
|
|
295
|
+
* trackComponentDepth?: boolean,
|
|
296
|
+
* resetFunctionBodyDepth?: boolean,
|
|
297
|
+
* }} [options]
|
|
298
|
+
*/
|
|
299
|
+
#parseNativeTemplateBody(
|
|
300
|
+
node,
|
|
301
|
+
body,
|
|
302
|
+
{
|
|
303
|
+
enterScope = false,
|
|
304
|
+
pushPath = false,
|
|
305
|
+
trackComponentDepth = false,
|
|
306
|
+
resetFunctionBodyDepth = false,
|
|
307
|
+
} = {},
|
|
308
|
+
) {
|
|
309
|
+
const parent_function_body_depth = this.#functionBodyDepth;
|
|
310
|
+
|
|
311
|
+
if (resetFunctionBodyDepth) {
|
|
312
|
+
this.#functionBodyDepth = 0;
|
|
313
|
+
}
|
|
314
|
+
if (enterScope) {
|
|
315
|
+
this.enterScope(0);
|
|
316
|
+
}
|
|
317
|
+
if (pushPath) {
|
|
318
|
+
this.#path.push(node);
|
|
319
|
+
}
|
|
320
|
+
if (trackComponentDepth) {
|
|
321
|
+
this.#componentDepth++;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
try {
|
|
325
|
+
this.parseTemplateBody(body);
|
|
326
|
+
} finally {
|
|
327
|
+
if (trackComponentDepth) {
|
|
328
|
+
this.#componentDepth--;
|
|
329
|
+
}
|
|
330
|
+
if (pushPath) {
|
|
331
|
+
this.#path.pop();
|
|
332
|
+
}
|
|
333
|
+
if (enterScope) {
|
|
334
|
+
this.exitScope();
|
|
335
|
+
}
|
|
336
|
+
if (resetFunctionBodyDepth) {
|
|
337
|
+
this.#functionBodyDepth = parent_function_body_depth;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* @param {AST.Node | undefined} node
|
|
344
|
+
*/
|
|
345
|
+
#isNativeTemplateNode(node) {
|
|
346
|
+
return (
|
|
347
|
+
node?.type === 'Component' ||
|
|
348
|
+
node?.type === 'Element' ||
|
|
349
|
+
node?.type === 'Tsx' ||
|
|
350
|
+
node?.type === 'Tsrx' ||
|
|
351
|
+
node?.type === 'TsxCompat'
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
#parseNativeTemplateExpressionContainer() {
|
|
356
|
+
const allow_trailing_semicolon = this.#allowExpressionContainerTrailingSemicolon;
|
|
357
|
+
this.#allowExpressionContainerTrailingSemicolon = true;
|
|
358
|
+
let node;
|
|
359
|
+
try {
|
|
360
|
+
node = this.jsx_parseExpressionContainer();
|
|
361
|
+
} finally {
|
|
362
|
+
this.#allowExpressionContainerTrailingSemicolon = allow_trailing_semicolon;
|
|
363
|
+
}
|
|
364
|
+
// Keep JSXEmptyExpression as-is (for prettier to handle comments)
|
|
365
|
+
// but convert other expressions to native TSRX child nodes.
|
|
366
|
+
if (node.expression.type !== 'JSXEmptyExpression') {
|
|
367
|
+
/** @type {AST.TSRXExpression | AST.Html | AST.TextNode | AST.Style} */ (
|
|
368
|
+
/** @type {unknown} */ (node)
|
|
369
|
+
).type = node.html
|
|
370
|
+
? 'Html'
|
|
371
|
+
: node.text
|
|
372
|
+
? 'Text'
|
|
373
|
+
: node.style
|
|
374
|
+
? 'Style'
|
|
375
|
+
: 'TSRXExpression';
|
|
376
|
+
if (node.style) {
|
|
377
|
+
/** @type {AST.Style} */ (/** @type {unknown} */ (node)).value =
|
|
378
|
+
/** @type {AST.Literal} */ (node.expression);
|
|
379
|
+
delete (/** @type {any} */ (node).expression);
|
|
380
|
+
}
|
|
381
|
+
delete node.html;
|
|
382
|
+
delete node.text;
|
|
383
|
+
delete node.style;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return /** @type {ESTreeJSX.JSXEmptyExpression | AST.TSRXExpression | AST.Html | AST.TextNode | AST.Style | ESTreeJSX.JSXExpressionContainer} */ (
|
|
387
|
+
/** @type {unknown} */ (node)
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* @param {AST.Tsx | AST.TsxCompat} island
|
|
393
|
+
* @param {AST.Node[]} body
|
|
394
|
+
*/
|
|
395
|
+
#parseTsxIslandBody(island, body) {
|
|
396
|
+
const tagName =
|
|
397
|
+
island.type === 'TsxCompat'
|
|
398
|
+
? `tsx:${island.kind}`
|
|
399
|
+
: island.openingElement.name
|
|
400
|
+
? 'tsx'
|
|
401
|
+
: '';
|
|
402
|
+
|
|
403
|
+
this.exprAllowed = true;
|
|
404
|
+
|
|
405
|
+
while (true) {
|
|
406
|
+
if (this.type === tt.eof || this.pos >= this.input.length || this.type === tt.braceR) {
|
|
407
|
+
const displayTag = tagName || '';
|
|
408
|
+
this.#report_broken_markup_error(
|
|
409
|
+
this.start,
|
|
410
|
+
`Unclosed tag '<${displayTag}>'. Expected '</${displayTag}>' before end of component.`,
|
|
411
|
+
);
|
|
412
|
+
island.unclosed = true;
|
|
413
|
+
/** @type {AST.NodeWithLocation} */ (island).loc.end = {
|
|
414
|
+
.../** @type {AST.SourceLocation} */ (island.openingElement.loc).end,
|
|
415
|
+
};
|
|
416
|
+
island.end = island.openingElement.end;
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (this.#isAtTsxIslandClosing(island)) {
|
|
421
|
+
this.exprAllowed = false;
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (this.type === tt.braceL) {
|
|
426
|
+
body.push(this.jsx_parseExpressionContainer());
|
|
427
|
+
} else if (this.type === tstt.jsxTagStart) {
|
|
428
|
+
body.push(super.jsx_parseElement());
|
|
429
|
+
} else {
|
|
430
|
+
const node = this.#parseTsxIslandText();
|
|
431
|
+
if (node) {
|
|
432
|
+
body.push(node);
|
|
433
|
+
}
|
|
434
|
+
this.#popTemplateLiteralTokenContext();
|
|
435
|
+
this.next();
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* @param {AST.Tsx | AST.TsxCompat} island
|
|
442
|
+
*/
|
|
443
|
+
#isAtTsxIslandClosing(island) {
|
|
444
|
+
if (island.type === 'TsxCompat') {
|
|
445
|
+
return this.input.slice(this.pos, this.pos + 5) === '/tsx:';
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (!island.openingElement.name) {
|
|
449
|
+
return this.input.slice(this.pos, this.pos + 2) === '/>';
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (this.input.slice(this.pos, this.pos + 4) !== '/tsx') {
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const after = this.input.charCodeAt(this.pos + 4);
|
|
457
|
+
return after === 62 /* > */;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
#parseTsxIslandText() {
|
|
461
|
+
const start = this.start;
|
|
462
|
+
this.pos = start;
|
|
463
|
+
let text = '';
|
|
464
|
+
|
|
465
|
+
while (this.pos < this.input.length) {
|
|
466
|
+
const ch = this.input.charCodeAt(this.pos);
|
|
467
|
+
|
|
468
|
+
// Stop at opening tag, expression, or the component-closing brace
|
|
469
|
+
if (ch === 60 || ch === 123 || ch === 125) {
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
text += this.input[this.pos];
|
|
474
|
+
this.pos++;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (!text) {
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return /** @type {ESTreeJSX.JSXText} */ ({
|
|
482
|
+
type: 'JSXText',
|
|
483
|
+
value: text,
|
|
484
|
+
raw: text,
|
|
485
|
+
start,
|
|
486
|
+
end: this.pos,
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
|
|
282
490
|
#popTsxTokenContextBeforeTemplateExpressionChild() {
|
|
283
491
|
let index = this.pos;
|
|
284
492
|
let has_newline = false;
|
|
@@ -444,6 +652,18 @@ export function TSRXPlugin(config) {
|
|
|
444
652
|
ctx.length = ci - 1;
|
|
445
653
|
return;
|
|
446
654
|
}
|
|
655
|
+
// Statement-bodied `<tsrx>` attributes can leave the attribute's
|
|
656
|
+
// expression contexts above the still-open JSX tag context. Strip
|
|
657
|
+
// those so a following `/>` stays in JSX opening-tag mode.
|
|
658
|
+
if (
|
|
659
|
+
this.type === tt.braceR &&
|
|
660
|
+
top === tstc.tc_expr &&
|
|
661
|
+
second === b_expr &&
|
|
662
|
+
ctx[ci - 2] === tstc.tc_oTag
|
|
663
|
+
) {
|
|
664
|
+
ctx.length = ci - 1;
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
447
667
|
// Closing token after the template at expression position. For `}`
|
|
448
668
|
// only pop if it actually closes this `b_expr` — otherwise the
|
|
449
669
|
// brace targets an inner callback/object body that should pop it
|
|
@@ -1151,6 +1371,11 @@ export function TSRXPlugin(config) {
|
|
|
1151
1371
|
skipName = false,
|
|
1152
1372
|
} = {}) {
|
|
1153
1373
|
const node = /** @type {AST.Component} */ (this.startNode());
|
|
1374
|
+
const parent_context = [...this.context];
|
|
1375
|
+
const restore_parent_context =
|
|
1376
|
+
!requireName &&
|
|
1377
|
+
this.#isInsideComponent() &&
|
|
1378
|
+
this.context.some((context) => context === tstc.tc_oTag || context === tstc.tc_cTag);
|
|
1154
1379
|
node.type = 'Component';
|
|
1155
1380
|
node.css = null;
|
|
1156
1381
|
node.default = isDefault;
|
|
@@ -1201,32 +1426,24 @@ export function TSRXPlugin(config) {
|
|
|
1201
1426
|
this.next();
|
|
1202
1427
|
}
|
|
1203
1428
|
|
|
1204
|
-
// Reset before `eat(braceL)` so the lookahead `next()` it triggers reads
|
|
1205
|
-
// the component body's first token as if we'd entered fresh — no
|
|
1206
|
-
// surrounding function body should affect our parseStatement/parseBlock
|
|
1207
|
-
// branching while inside the template.
|
|
1208
|
-
const parent_function_body_depth = this.#functionBodyDepth;
|
|
1209
|
-
this.#functionBodyDepth = 0;
|
|
1210
|
-
|
|
1211
1429
|
if (this.type === tt.braceL) {
|
|
1212
1430
|
this.#allowDoubleQuotedTextChildAfterBrace = true;
|
|
1213
1431
|
}
|
|
1214
1432
|
this.eat(tt.braceL);
|
|
1215
1433
|
node.body = [];
|
|
1216
|
-
this.#
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
} finally {
|
|
1222
|
-
this.#functionBodyDepth = parent_function_body_depth;
|
|
1223
|
-
this.#componentDepth--;
|
|
1224
|
-
}
|
|
1225
|
-
this.#path.pop();
|
|
1434
|
+
this.#parseNativeTemplateBody(node, node.body, {
|
|
1435
|
+
pushPath: true,
|
|
1436
|
+
trackComponentDepth: true,
|
|
1437
|
+
resetFunctionBodyDepth: true,
|
|
1438
|
+
});
|
|
1226
1439
|
this.exitScope();
|
|
1227
1440
|
|
|
1228
1441
|
this.next();
|
|
1229
1442
|
skipWhitespace(this);
|
|
1443
|
+
if (restore_parent_context) {
|
|
1444
|
+
this.context = this.type === tt.braceR ? parent_context.slice(0, -1) : parent_context;
|
|
1445
|
+
this.exprAllowed = false;
|
|
1446
|
+
}
|
|
1230
1447
|
this.finishNode(node, 'Component');
|
|
1231
1448
|
this.awaitPos = 0;
|
|
1232
1449
|
|
|
@@ -1444,6 +1661,14 @@ export function TSRXPlugin(config) {
|
|
|
1444
1661
|
parseFunctionBody(node, isArrowFunction, isMethod, forInit, ...args) {
|
|
1445
1662
|
this.#functionBodyDepth++;
|
|
1446
1663
|
this.#functionStack.push(node);
|
|
1664
|
+
const context_restore = {
|
|
1665
|
+
parentContext: [...this.context],
|
|
1666
|
+
canRestore:
|
|
1667
|
+
this.#isInsideComponent() &&
|
|
1668
|
+
this.context.some((context) => context === tstc.tc_oTag || context === tstc.tc_cTag),
|
|
1669
|
+
restore: false,
|
|
1670
|
+
};
|
|
1671
|
+
this.#functionBodyContextRestoreStack.push(context_restore);
|
|
1447
1672
|
// Inside a component, nested JS function bodies should parse like
|
|
1448
1673
|
// ordinary functions, not component template bodies.
|
|
1449
1674
|
if (
|
|
@@ -1452,9 +1677,9 @@ export function TSRXPlugin(config) {
|
|
|
1452
1677
|
// A stale JSX expression context means the surrounding template
|
|
1453
1678
|
// tokenizer can still treat `<` as template markup.
|
|
1454
1679
|
this.context.some((context) => context === tstc.tc_expr) &&
|
|
1455
|
-
// Keep
|
|
1456
|
-
//
|
|
1457
|
-
!
|
|
1680
|
+
// Keep callback props on their surrounding JSX attribute path until
|
|
1681
|
+
// statement-position TSRX needs to suspend it.
|
|
1682
|
+
!context_restore.canRestore &&
|
|
1458
1683
|
// Only reset statement-level function bodies, not expression
|
|
1459
1684
|
// contexts that are actively parsing JSX.
|
|
1460
1685
|
this.curContext() === b_stat
|
|
@@ -1465,6 +1690,11 @@ export function TSRXPlugin(config) {
|
|
|
1465
1690
|
try {
|
|
1466
1691
|
return super.parseFunctionBody(node, isArrowFunction, isMethod, forInit, ...args);
|
|
1467
1692
|
} finally {
|
|
1693
|
+
if (context_restore.restore) {
|
|
1694
|
+
this.context = context_restore.parentContext.slice(0, -1);
|
|
1695
|
+
this.exprAllowed = false;
|
|
1696
|
+
}
|
|
1697
|
+
this.#functionBodyContextRestoreStack.pop();
|
|
1468
1698
|
this.#functionStack.pop();
|
|
1469
1699
|
this.#functionBodyDepth--;
|
|
1470
1700
|
}
|
|
@@ -1561,6 +1791,16 @@ export function TSRXPlugin(config) {
|
|
|
1561
1791
|
'"style" is a TSRX keyword and must be used in the form {style "class_name"}',
|
|
1562
1792
|
);
|
|
1563
1793
|
}
|
|
1794
|
+
if (this.#allowExpressionContainerTrailingSemicolon && this.type === tt.semi) {
|
|
1795
|
+
if (this.#collect) {
|
|
1796
|
+
this.#report_recoverable_error(
|
|
1797
|
+
this.start,
|
|
1798
|
+
'TSRX expression containers do not use semicolons. Remove this semicolon.',
|
|
1799
|
+
DIAGNOSTIC_CODES.TEMPLATE_EXPRESSION_TRAILING_SEMICOLON,
|
|
1800
|
+
);
|
|
1801
|
+
}
|
|
1802
|
+
this.next();
|
|
1803
|
+
}
|
|
1564
1804
|
this.expect(tt.braceR);
|
|
1565
1805
|
|
|
1566
1806
|
return this.finishNode(node, 'JSXExpressionContainer');
|
|
@@ -2226,9 +2466,9 @@ export function TSRXPlugin(config) {
|
|
|
2226
2466
|
this.next();
|
|
2227
2467
|
}
|
|
2228
2468
|
} else if (is_fragment) {
|
|
2229
|
-
this.
|
|
2230
|
-
|
|
2231
|
-
|
|
2469
|
+
this.#parseNativeTemplateBody(element, /** @type {AST.Element} */ (element).children, {
|
|
2470
|
+
enterScope: true,
|
|
2471
|
+
});
|
|
2232
2472
|
|
|
2233
2473
|
if (element.type === 'Tsx') {
|
|
2234
2474
|
this.#path.pop();
|
|
@@ -2395,12 +2635,7 @@ export function TSRXPlugin(config) {
|
|
|
2395
2635
|
// Ensure we escape JSX <tag></tag> context
|
|
2396
2636
|
const curContext = this.curContext();
|
|
2397
2637
|
const parent = this.#path.at(-1);
|
|
2398
|
-
const insideTemplate =
|
|
2399
|
-
parent?.type === 'Component' ||
|
|
2400
|
-
parent?.type === 'Element' ||
|
|
2401
|
-
parent?.type === 'Tsx' ||
|
|
2402
|
-
parent?.type === 'Tsrx' ||
|
|
2403
|
-
parent?.type === 'TsxCompat';
|
|
2638
|
+
const insideTemplate = this.#isNativeTemplateNode(parent);
|
|
2404
2639
|
|
|
2405
2640
|
if (curContext === tstc.tc_expr && !insideTemplate) {
|
|
2406
2641
|
this.context.pop();
|
|
@@ -2408,9 +2643,9 @@ export function TSRXPlugin(config) {
|
|
|
2408
2643
|
|
|
2409
2644
|
/** @type {AST.Element} */ (element).css = content;
|
|
2410
2645
|
} else {
|
|
2411
|
-
this.
|
|
2412
|
-
|
|
2413
|
-
|
|
2646
|
+
this.#parseNativeTemplateBody(element, /** @type {AST.Element} */ (element).children, {
|
|
2647
|
+
enterScope: true,
|
|
2648
|
+
});
|
|
2414
2649
|
|
|
2415
2650
|
if (element.type === 'Tsx') {
|
|
2416
2651
|
this.#path.pop();
|
|
@@ -2501,12 +2736,7 @@ export function TSRXPlugin(config) {
|
|
|
2501
2736
|
// Ensure we escape JSX <tag></tag> context
|
|
2502
2737
|
const curContext = this.curContext();
|
|
2503
2738
|
const parent = this.#path.at(-1);
|
|
2504
|
-
const insideTemplate =
|
|
2505
|
-
parent?.type === 'Component' ||
|
|
2506
|
-
parent?.type === 'Element' ||
|
|
2507
|
-
parent?.type === 'Tsx' ||
|
|
2508
|
-
parent?.type === 'Tsrx' ||
|
|
2509
|
-
parent?.type === 'TsxCompat';
|
|
2739
|
+
const insideTemplate = this.#isNativeTemplateNode(parent);
|
|
2510
2740
|
|
|
2511
2741
|
if (curContext === tstc.tc_expr && !insideTemplate) {
|
|
2512
2742
|
this.context.pop();
|
|
@@ -2535,8 +2765,9 @@ export function TSRXPlugin(config) {
|
|
|
2535
2765
|
parseTemplateBody(body) {
|
|
2536
2766
|
const inside_func =
|
|
2537
2767
|
this.context.some((n) => n.token === 'function') || this.scopeStack.length > 1;
|
|
2538
|
-
const
|
|
2539
|
-
|
|
2768
|
+
const inside_tsx_island = this.#path.findLast(
|
|
2769
|
+
(n) => n.type === 'Tsx' || n.type === 'TsxCompat',
|
|
2770
|
+
);
|
|
2540
2771
|
|
|
2541
2772
|
if (!inside_func) {
|
|
2542
2773
|
if (this.type.label === 'continue') {
|
|
@@ -2547,168 +2778,15 @@ export function TSRXPlugin(config) {
|
|
|
2547
2778
|
}
|
|
2548
2779
|
}
|
|
2549
2780
|
|
|
2550
|
-
if (
|
|
2551
|
-
this
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
this.start,
|
|
2557
|
-
`Unclosed tag '<tsx>'. Expected '</tsx>' before end of component.`,
|
|
2558
|
-
);
|
|
2559
|
-
inside_tsx.unclosed = true;
|
|
2560
|
-
/** @type {AST.NodeWithLocation} */ (inside_tsx).loc.end = {
|
|
2561
|
-
.../** @type {AST.SourceLocation} */ (inside_tsx.openingElement.loc).end,
|
|
2562
|
-
};
|
|
2563
|
-
inside_tsx.end = inside_tsx.openingElement.end;
|
|
2564
|
-
return;
|
|
2565
|
-
}
|
|
2566
|
-
|
|
2567
|
-
if (!inside_tsx.openingElement.name) {
|
|
2568
|
-
if (this.input.slice(this.pos, this.pos + 2) === '/>') {
|
|
2569
|
-
// Reset exprAllowed so the trailing `/` of `</>` is tokenized
|
|
2570
|
-
// as a slash rather than as the start of a regex literal.
|
|
2571
|
-
this.exprAllowed = false;
|
|
2572
|
-
return;
|
|
2573
|
-
}
|
|
2574
|
-
} else if (this.input.slice(this.pos, this.pos + 4) === '/tsx') {
|
|
2575
|
-
const after = this.input.charCodeAt(this.pos + 4);
|
|
2576
|
-
// Make sure it's </tsx> and not </tsx:...>
|
|
2577
|
-
if (after === 62 /* > */) {
|
|
2578
|
-
this.exprAllowed = false;
|
|
2579
|
-
return;
|
|
2580
|
-
}
|
|
2581
|
-
}
|
|
2582
|
-
|
|
2583
|
-
if (this.type === tt.braceL) {
|
|
2584
|
-
const node = this.jsx_parseExpressionContainer();
|
|
2585
|
-
body.push(node);
|
|
2586
|
-
} else if (this.type === tstt.jsxTagStart) {
|
|
2587
|
-
// Parse JSX element
|
|
2588
|
-
const node = super.jsx_parseElement();
|
|
2589
|
-
body.push(node);
|
|
2590
|
-
} else {
|
|
2591
|
-
const start = this.start;
|
|
2592
|
-
this.pos = start;
|
|
2593
|
-
let text = '';
|
|
2594
|
-
|
|
2595
|
-
while (this.pos < this.input.length) {
|
|
2596
|
-
const ch = this.input.charCodeAt(this.pos);
|
|
2597
|
-
|
|
2598
|
-
// Stop at opening tag, expression, or the component-closing brace
|
|
2599
|
-
if (ch === 60 || ch === 123 || ch === 125) {
|
|
2600
|
-
// < or { or }
|
|
2601
|
-
break;
|
|
2602
|
-
}
|
|
2603
|
-
|
|
2604
|
-
text += this.input[this.pos];
|
|
2605
|
-
this.pos++;
|
|
2606
|
-
}
|
|
2607
|
-
|
|
2608
|
-
if (text) {
|
|
2609
|
-
const node = /** @type {ESTreeJSX.JSXText} */ ({
|
|
2610
|
-
type: 'JSXText',
|
|
2611
|
-
value: text,
|
|
2612
|
-
raw: text,
|
|
2613
|
-
start,
|
|
2614
|
-
end: this.pos,
|
|
2615
|
-
});
|
|
2616
|
-
body.push(node);
|
|
2617
|
-
}
|
|
2618
|
-
|
|
2619
|
-
this.#popTemplateLiteralTokenContext();
|
|
2620
|
-
// Always call next() to ensure parser makes progress
|
|
2621
|
-
this.next();
|
|
2622
|
-
}
|
|
2623
|
-
}
|
|
2624
|
-
}
|
|
2625
|
-
if (inside_tsx_compat) {
|
|
2626
|
-
this.exprAllowed = true;
|
|
2627
|
-
|
|
2628
|
-
while (true) {
|
|
2629
|
-
if (this.type === tt.eof || this.pos >= this.input.length || this.type === tt.braceR) {
|
|
2630
|
-
this.#report_broken_markup_error(
|
|
2631
|
-
this.start,
|
|
2632
|
-
`Unclosed tag '<tsx:${inside_tsx_compat.kind}>'. Expected '</tsx:${inside_tsx_compat.kind}>' before end of component.`,
|
|
2633
|
-
);
|
|
2634
|
-
inside_tsx_compat.unclosed = true;
|
|
2635
|
-
/** @type {AST.NodeWithLocation} */ (inside_tsx_compat).loc.end = {
|
|
2636
|
-
.../** @type {AST.SourceLocation} */ (inside_tsx_compat.openingElement.loc).end,
|
|
2637
|
-
};
|
|
2638
|
-
inside_tsx_compat.end = inside_tsx_compat.openingElement.end;
|
|
2639
|
-
return;
|
|
2640
|
-
}
|
|
2641
|
-
|
|
2642
|
-
if (this.input.slice(this.pos, this.pos + 5) === '/tsx:') {
|
|
2643
|
-
this.exprAllowed = false;
|
|
2644
|
-
return;
|
|
2645
|
-
}
|
|
2646
|
-
|
|
2647
|
-
if (this.type === tt.braceL) {
|
|
2648
|
-
const node = this.jsx_parseExpressionContainer();
|
|
2649
|
-
body.push(node);
|
|
2650
|
-
} else if (this.type === tstt.jsxTagStart) {
|
|
2651
|
-
// Parse JSX element
|
|
2652
|
-
const node = super.jsx_parseElement();
|
|
2653
|
-
body.push(node);
|
|
2654
|
-
} else {
|
|
2655
|
-
const start = this.start;
|
|
2656
|
-
this.pos = start;
|
|
2657
|
-
let text = '';
|
|
2658
|
-
|
|
2659
|
-
while (this.pos < this.input.length) {
|
|
2660
|
-
const ch = this.input.charCodeAt(this.pos);
|
|
2661
|
-
|
|
2662
|
-
// Stop at opening tag, expression, or the component-closing brace
|
|
2663
|
-
if (ch === 60 || ch === 123 || ch === 125) {
|
|
2664
|
-
// < or { or }
|
|
2665
|
-
break;
|
|
2666
|
-
}
|
|
2667
|
-
|
|
2668
|
-
text += this.input[this.pos];
|
|
2669
|
-
this.pos++;
|
|
2670
|
-
}
|
|
2671
|
-
|
|
2672
|
-
if (text) {
|
|
2673
|
-
const node = /** @type {ESTreeJSX.JSXText} */ ({
|
|
2674
|
-
type: 'JSXText',
|
|
2675
|
-
value: text,
|
|
2676
|
-
raw: text,
|
|
2677
|
-
start,
|
|
2678
|
-
end: this.pos,
|
|
2679
|
-
});
|
|
2680
|
-
body.push(node);
|
|
2681
|
-
}
|
|
2682
|
-
|
|
2683
|
-
this.#popTemplateLiteralTokenContext();
|
|
2684
|
-
this.next();
|
|
2685
|
-
}
|
|
2686
|
-
}
|
|
2781
|
+
if (inside_tsx_island) {
|
|
2782
|
+
this.#parseTsxIslandBody(
|
|
2783
|
+
/** @type {AST.Tsx | AST.TsxCompat} */ (inside_tsx_island),
|
|
2784
|
+
/** @type {AST.Node[]} */ (/** @type {unknown} */ (body)),
|
|
2785
|
+
);
|
|
2786
|
+
return;
|
|
2687
2787
|
}
|
|
2688
2788
|
if (this.type === tt.braceL) {
|
|
2689
|
-
|
|
2690
|
-
// Keep JSXEmptyExpression as-is (for prettier to handle comments)
|
|
2691
|
-
// but convert other expressions to Html/TSRXExpression/Text nodes
|
|
2692
|
-
if (node.expression.type !== 'JSXEmptyExpression') {
|
|
2693
|
-
/** @type {AST.TSRXExpression | AST.Html | AST.TextNode | AST.Style} */ (
|
|
2694
|
-
/** @type {unknown} */ (node)
|
|
2695
|
-
).type = node.html
|
|
2696
|
-
? 'Html'
|
|
2697
|
-
: node.text
|
|
2698
|
-
? 'Text'
|
|
2699
|
-
: node.style
|
|
2700
|
-
? 'Style'
|
|
2701
|
-
: 'TSRXExpression';
|
|
2702
|
-
if (node.style) {
|
|
2703
|
-
/** @type {AST.Style} */ (/** @type {unknown} */ (node)).value =
|
|
2704
|
-
/** @type {AST.Literal} */ (node.expression);
|
|
2705
|
-
delete (/** @type {any} */ (node).expression);
|
|
2706
|
-
}
|
|
2707
|
-
delete node.html;
|
|
2708
|
-
delete node.text;
|
|
2709
|
-
delete node.style;
|
|
2710
|
-
}
|
|
2711
|
-
body.push(node);
|
|
2789
|
+
body.push(this.#parseNativeTemplateExpressionContainer());
|
|
2712
2790
|
} else if (this.type === tt.string && this.input.charCodeAt(this.start) === 34) {
|
|
2713
2791
|
body.push(this.parseDoubleQuotedTextChild());
|
|
2714
2792
|
} else if (this.type === tt.braceR) {
|
|
@@ -2957,30 +3035,8 @@ export function TSRXPlugin(config) {
|
|
|
2957
3035
|
this.type === tt.braceL &&
|
|
2958
3036
|
this.context.some((c) => c === tstc.tc_expr)
|
|
2959
3037
|
) {
|
|
2960
|
-
const node = this.jsx_parseExpressionContainer();
|
|
2961
|
-
// Keep JSXEmptyExpression as-is (don't convert to TSRXExpression/Text/Html)
|
|
2962
|
-
if (node.expression.type !== 'JSXEmptyExpression') {
|
|
2963
|
-
/** @type {AST.TSRXExpression | AST.Html | AST.TextNode | AST.Style} */ (
|
|
2964
|
-
/** @type {unknown} */ (node)
|
|
2965
|
-
).type = node.html
|
|
2966
|
-
? 'Html'
|
|
2967
|
-
: node.text
|
|
2968
|
-
? 'Text'
|
|
2969
|
-
: node.style
|
|
2970
|
-
? 'Style'
|
|
2971
|
-
: 'TSRXExpression';
|
|
2972
|
-
if (node.style) {
|
|
2973
|
-
/** @type {AST.Style} */ (/** @type {unknown} */ (node)).value =
|
|
2974
|
-
/** @type {AST.Literal} */ (node.expression);
|
|
2975
|
-
delete (/** @type {any} */ (node).expression);
|
|
2976
|
-
}
|
|
2977
|
-
delete node.html;
|
|
2978
|
-
delete node.text;
|
|
2979
|
-
delete node.style;
|
|
2980
|
-
}
|
|
2981
|
-
|
|
2982
3038
|
return /** @type {ESTreeJSX.JSXEmptyExpression | AST.TSRXExpression | AST.Html | AST.TextNode | ESTreeJSX.JSXExpressionContainer} */ (
|
|
2983
|
-
/** @type {unknown} */ (
|
|
3039
|
+
/** @type {unknown} */ (this.#parseNativeTemplateExpressionContainer())
|
|
2984
3040
|
);
|
|
2985
3041
|
}
|
|
2986
3042
|
|
|
@@ -3008,6 +3064,18 @@ export function TSRXPlugin(config) {
|
|
|
3008
3064
|
this.context.pop();
|
|
3009
3065
|
}
|
|
3010
3066
|
}
|
|
3067
|
+
const context_restore = this.#functionBodyContextRestoreStack.at(-1);
|
|
3068
|
+
if (
|
|
3069
|
+
this.#functionBodyDepth > 0 &&
|
|
3070
|
+
node.type === 'Tsrx' &&
|
|
3071
|
+
context_restore?.canRestore &&
|
|
3072
|
+
this.type !== tt.braceR &&
|
|
3073
|
+
this.type !== tt.comma
|
|
3074
|
+
) {
|
|
3075
|
+
context_restore.restore = true;
|
|
3076
|
+
this.context = [b_stat];
|
|
3077
|
+
this.exprAllowed = true;
|
|
3078
|
+
}
|
|
3011
3079
|
return node;
|
|
3012
3080
|
}
|
|
3013
3081
|
|
|
@@ -3077,10 +3145,9 @@ export function TSRXPlugin(config) {
|
|
|
3077
3145
|
node.body = [];
|
|
3078
3146
|
this.#allowDoubleQuotedTextChildAfterBrace = true;
|
|
3079
3147
|
this.expect(tt.braceL);
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
}
|
|
3083
|
-
this.parseTemplateBody(node.body);
|
|
3148
|
+
this.#parseNativeTemplateBody(node, node.body, {
|
|
3149
|
+
enterScope: createNewLexicalScope,
|
|
3150
|
+
});
|
|
3084
3151
|
|
|
3085
3152
|
if (exitStrict) {
|
|
3086
3153
|
this.strict = false;
|
|
@@ -3088,9 +3155,6 @@ export function TSRXPlugin(config) {
|
|
|
3088
3155
|
this.exprAllowed = true;
|
|
3089
3156
|
|
|
3090
3157
|
this.next();
|
|
3091
|
-
if (createNewLexicalScope) {
|
|
3092
|
-
this.exitScope();
|
|
3093
|
-
}
|
|
3094
3158
|
return this.finishNode(node, 'BlockStatement');
|
|
3095
3159
|
}
|
|
3096
3160
|
|