@tsrx/core 0.1.3 → 0.1.4
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/plugin.js +264 -230
- package/src/transform/jsx/index.js +1001 -101
package/package.json
CHANGED
package/src/plugin.js
CHANGED
|
@@ -216,6 +216,8 @@ 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} */
|
|
@@ -279,6 +281,204 @@ export function TSRXPlugin(config) {
|
|
|
279
281
|
return this.#isInsideComponent() && this.#functionBodyDepth === 0;
|
|
280
282
|
}
|
|
281
283
|
|
|
284
|
+
/**
|
|
285
|
+
* Component bodies and native TSRX element bodies share the same grammar.
|
|
286
|
+
* This helper keeps the parser-state setup in one place while callers keep
|
|
287
|
+
* ownership of their distinct closing delimiter handling (`}` vs `</tag>`).
|
|
288
|
+
*
|
|
289
|
+
* @param {AST.Node} node
|
|
290
|
+
* @param {AST.Node[]} body
|
|
291
|
+
* @param {{
|
|
292
|
+
* enterScope?: boolean,
|
|
293
|
+
* pushPath?: boolean,
|
|
294
|
+
* trackComponentDepth?: boolean,
|
|
295
|
+
* resetFunctionBodyDepth?: boolean,
|
|
296
|
+
* }} [options]
|
|
297
|
+
*/
|
|
298
|
+
#parseNativeTemplateBody(
|
|
299
|
+
node,
|
|
300
|
+
body,
|
|
301
|
+
{
|
|
302
|
+
enterScope = false,
|
|
303
|
+
pushPath = false,
|
|
304
|
+
trackComponentDepth = false,
|
|
305
|
+
resetFunctionBodyDepth = false,
|
|
306
|
+
} = {},
|
|
307
|
+
) {
|
|
308
|
+
const parent_function_body_depth = this.#functionBodyDepth;
|
|
309
|
+
|
|
310
|
+
if (resetFunctionBodyDepth) {
|
|
311
|
+
this.#functionBodyDepth = 0;
|
|
312
|
+
}
|
|
313
|
+
if (enterScope) {
|
|
314
|
+
this.enterScope(0);
|
|
315
|
+
}
|
|
316
|
+
if (pushPath) {
|
|
317
|
+
this.#path.push(node);
|
|
318
|
+
}
|
|
319
|
+
if (trackComponentDepth) {
|
|
320
|
+
this.#componentDepth++;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
try {
|
|
324
|
+
this.parseTemplateBody(body);
|
|
325
|
+
} finally {
|
|
326
|
+
if (trackComponentDepth) {
|
|
327
|
+
this.#componentDepth--;
|
|
328
|
+
}
|
|
329
|
+
if (pushPath) {
|
|
330
|
+
this.#path.pop();
|
|
331
|
+
}
|
|
332
|
+
if (enterScope) {
|
|
333
|
+
this.exitScope();
|
|
334
|
+
}
|
|
335
|
+
if (resetFunctionBodyDepth) {
|
|
336
|
+
this.#functionBodyDepth = parent_function_body_depth;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* @param {AST.Node | undefined} node
|
|
343
|
+
*/
|
|
344
|
+
#isNativeTemplateNode(node) {
|
|
345
|
+
return (
|
|
346
|
+
node?.type === 'Component' ||
|
|
347
|
+
node?.type === 'Element' ||
|
|
348
|
+
node?.type === 'Tsx' ||
|
|
349
|
+
node?.type === 'Tsrx' ||
|
|
350
|
+
node?.type === 'TsxCompat'
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
#parseNativeTemplateExpressionContainer() {
|
|
355
|
+
const node = this.jsx_parseExpressionContainer();
|
|
356
|
+
// Keep JSXEmptyExpression as-is (for prettier to handle comments)
|
|
357
|
+
// but convert other expressions to native TSRX child nodes.
|
|
358
|
+
if (node.expression.type !== 'JSXEmptyExpression') {
|
|
359
|
+
/** @type {AST.TSRXExpression | AST.Html | AST.TextNode | AST.Style} */ (
|
|
360
|
+
/** @type {unknown} */ (node)
|
|
361
|
+
).type = node.html
|
|
362
|
+
? 'Html'
|
|
363
|
+
: node.text
|
|
364
|
+
? 'Text'
|
|
365
|
+
: node.style
|
|
366
|
+
? 'Style'
|
|
367
|
+
: 'TSRXExpression';
|
|
368
|
+
if (node.style) {
|
|
369
|
+
/** @type {AST.Style} */ (/** @type {unknown} */ (node)).value =
|
|
370
|
+
/** @type {AST.Literal} */ (node.expression);
|
|
371
|
+
delete (/** @type {any} */ (node).expression);
|
|
372
|
+
}
|
|
373
|
+
delete node.html;
|
|
374
|
+
delete node.text;
|
|
375
|
+
delete node.style;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return /** @type {ESTreeJSX.JSXEmptyExpression | AST.TSRXExpression | AST.Html | AST.TextNode | AST.Style | ESTreeJSX.JSXExpressionContainer} */ (
|
|
379
|
+
/** @type {unknown} */ (node)
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* @param {AST.Tsx | AST.TsxCompat} island
|
|
385
|
+
* @param {AST.Node[]} body
|
|
386
|
+
*/
|
|
387
|
+
#parseTsxIslandBody(island, body) {
|
|
388
|
+
const tagName =
|
|
389
|
+
island.type === 'TsxCompat'
|
|
390
|
+
? `tsx:${island.kind}`
|
|
391
|
+
: island.openingElement.name
|
|
392
|
+
? 'tsx'
|
|
393
|
+
: '';
|
|
394
|
+
|
|
395
|
+
this.exprAllowed = true;
|
|
396
|
+
|
|
397
|
+
while (true) {
|
|
398
|
+
if (this.type === tt.eof || this.pos >= this.input.length || this.type === tt.braceR) {
|
|
399
|
+
const displayTag = tagName || '';
|
|
400
|
+
this.#report_broken_markup_error(
|
|
401
|
+
this.start,
|
|
402
|
+
`Unclosed tag '<${displayTag}>'. Expected '</${displayTag}>' before end of component.`,
|
|
403
|
+
);
|
|
404
|
+
island.unclosed = true;
|
|
405
|
+
/** @type {AST.NodeWithLocation} */ (island).loc.end = {
|
|
406
|
+
.../** @type {AST.SourceLocation} */ (island.openingElement.loc).end,
|
|
407
|
+
};
|
|
408
|
+
island.end = island.openingElement.end;
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (this.#isAtTsxIslandClosing(island)) {
|
|
413
|
+
this.exprAllowed = false;
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (this.type === tt.braceL) {
|
|
418
|
+
body.push(this.jsx_parseExpressionContainer());
|
|
419
|
+
} else if (this.type === tstt.jsxTagStart) {
|
|
420
|
+
body.push(super.jsx_parseElement());
|
|
421
|
+
} else {
|
|
422
|
+
const node = this.#parseTsxIslandText();
|
|
423
|
+
if (node) {
|
|
424
|
+
body.push(node);
|
|
425
|
+
}
|
|
426
|
+
this.#popTemplateLiteralTokenContext();
|
|
427
|
+
this.next();
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* @param {AST.Tsx | AST.TsxCompat} island
|
|
434
|
+
*/
|
|
435
|
+
#isAtTsxIslandClosing(island) {
|
|
436
|
+
if (island.type === 'TsxCompat') {
|
|
437
|
+
return this.input.slice(this.pos, this.pos + 5) === '/tsx:';
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (!island.openingElement.name) {
|
|
441
|
+
return this.input.slice(this.pos, this.pos + 2) === '/>';
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (this.input.slice(this.pos, this.pos + 4) !== '/tsx') {
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const after = this.input.charCodeAt(this.pos + 4);
|
|
449
|
+
return after === 62 /* > */;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
#parseTsxIslandText() {
|
|
453
|
+
const start = this.start;
|
|
454
|
+
this.pos = start;
|
|
455
|
+
let text = '';
|
|
456
|
+
|
|
457
|
+
while (this.pos < this.input.length) {
|
|
458
|
+
const ch = this.input.charCodeAt(this.pos);
|
|
459
|
+
|
|
460
|
+
// Stop at opening tag, expression, or the component-closing brace
|
|
461
|
+
if (ch === 60 || ch === 123 || ch === 125) {
|
|
462
|
+
break;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
text += this.input[this.pos];
|
|
466
|
+
this.pos++;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (!text) {
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return /** @type {ESTreeJSX.JSXText} */ ({
|
|
474
|
+
type: 'JSXText',
|
|
475
|
+
value: text,
|
|
476
|
+
raw: text,
|
|
477
|
+
start,
|
|
478
|
+
end: this.pos,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
|
|
282
482
|
#popTsxTokenContextBeforeTemplateExpressionChild() {
|
|
283
483
|
let index = this.pos;
|
|
284
484
|
let has_newline = false;
|
|
@@ -1151,6 +1351,11 @@ export function TSRXPlugin(config) {
|
|
|
1151
1351
|
skipName = false,
|
|
1152
1352
|
} = {}) {
|
|
1153
1353
|
const node = /** @type {AST.Component} */ (this.startNode());
|
|
1354
|
+
const parent_context = [...this.context];
|
|
1355
|
+
const restore_parent_context =
|
|
1356
|
+
!requireName &&
|
|
1357
|
+
this.#isInsideComponent() &&
|
|
1358
|
+
this.context.some((context) => context === tstc.tc_oTag || context === tstc.tc_cTag);
|
|
1154
1359
|
node.type = 'Component';
|
|
1155
1360
|
node.css = null;
|
|
1156
1361
|
node.default = isDefault;
|
|
@@ -1201,32 +1406,24 @@ export function TSRXPlugin(config) {
|
|
|
1201
1406
|
this.next();
|
|
1202
1407
|
}
|
|
1203
1408
|
|
|
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
1409
|
if (this.type === tt.braceL) {
|
|
1212
1410
|
this.#allowDoubleQuotedTextChildAfterBrace = true;
|
|
1213
1411
|
}
|
|
1214
1412
|
this.eat(tt.braceL);
|
|
1215
1413
|
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();
|
|
1414
|
+
this.#parseNativeTemplateBody(node, node.body, {
|
|
1415
|
+
pushPath: true,
|
|
1416
|
+
trackComponentDepth: true,
|
|
1417
|
+
resetFunctionBodyDepth: true,
|
|
1418
|
+
});
|
|
1226
1419
|
this.exitScope();
|
|
1227
1420
|
|
|
1228
1421
|
this.next();
|
|
1229
1422
|
skipWhitespace(this);
|
|
1423
|
+
if (restore_parent_context) {
|
|
1424
|
+
this.context = this.type === tt.braceR ? parent_context.slice(0, -1) : parent_context;
|
|
1425
|
+
this.exprAllowed = false;
|
|
1426
|
+
}
|
|
1230
1427
|
this.finishNode(node, 'Component');
|
|
1231
1428
|
this.awaitPos = 0;
|
|
1232
1429
|
|
|
@@ -1444,6 +1641,14 @@ export function TSRXPlugin(config) {
|
|
|
1444
1641
|
parseFunctionBody(node, isArrowFunction, isMethod, forInit, ...args) {
|
|
1445
1642
|
this.#functionBodyDepth++;
|
|
1446
1643
|
this.#functionStack.push(node);
|
|
1644
|
+
const context_restore = {
|
|
1645
|
+
parentContext: [...this.context],
|
|
1646
|
+
canRestore:
|
|
1647
|
+
this.#isInsideComponent() &&
|
|
1648
|
+
this.context.some((context) => context === tstc.tc_oTag || context === tstc.tc_cTag),
|
|
1649
|
+
restore: false,
|
|
1650
|
+
};
|
|
1651
|
+
this.#functionBodyContextRestoreStack.push(context_restore);
|
|
1447
1652
|
// Inside a component, nested JS function bodies should parse like
|
|
1448
1653
|
// ordinary functions, not component template bodies.
|
|
1449
1654
|
if (
|
|
@@ -1452,9 +1657,9 @@ export function TSRXPlugin(config) {
|
|
|
1452
1657
|
// A stale JSX expression context means the surrounding template
|
|
1453
1658
|
// tokenizer can still treat `<` as template markup.
|
|
1454
1659
|
this.context.some((context) => context === tstc.tc_expr) &&
|
|
1455
|
-
// Keep
|
|
1456
|
-
//
|
|
1457
|
-
!
|
|
1660
|
+
// Keep callback props on their surrounding JSX attribute path until
|
|
1661
|
+
// statement-position TSRX needs to suspend it.
|
|
1662
|
+
!context_restore.canRestore &&
|
|
1458
1663
|
// Only reset statement-level function bodies, not expression
|
|
1459
1664
|
// contexts that are actively parsing JSX.
|
|
1460
1665
|
this.curContext() === b_stat
|
|
@@ -1465,6 +1670,11 @@ export function TSRXPlugin(config) {
|
|
|
1465
1670
|
try {
|
|
1466
1671
|
return super.parseFunctionBody(node, isArrowFunction, isMethod, forInit, ...args);
|
|
1467
1672
|
} finally {
|
|
1673
|
+
if (context_restore.restore) {
|
|
1674
|
+
this.context = context_restore.parentContext.slice(0, -1);
|
|
1675
|
+
this.exprAllowed = false;
|
|
1676
|
+
}
|
|
1677
|
+
this.#functionBodyContextRestoreStack.pop();
|
|
1468
1678
|
this.#functionStack.pop();
|
|
1469
1679
|
this.#functionBodyDepth--;
|
|
1470
1680
|
}
|
|
@@ -2226,9 +2436,9 @@ export function TSRXPlugin(config) {
|
|
|
2226
2436
|
this.next();
|
|
2227
2437
|
}
|
|
2228
2438
|
} else if (is_fragment) {
|
|
2229
|
-
this.
|
|
2230
|
-
|
|
2231
|
-
|
|
2439
|
+
this.#parseNativeTemplateBody(element, /** @type {AST.Element} */ (element).children, {
|
|
2440
|
+
enterScope: true,
|
|
2441
|
+
});
|
|
2232
2442
|
|
|
2233
2443
|
if (element.type === 'Tsx') {
|
|
2234
2444
|
this.#path.pop();
|
|
@@ -2395,12 +2605,7 @@ export function TSRXPlugin(config) {
|
|
|
2395
2605
|
// Ensure we escape JSX <tag></tag> context
|
|
2396
2606
|
const curContext = this.curContext();
|
|
2397
2607
|
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';
|
|
2608
|
+
const insideTemplate = this.#isNativeTemplateNode(parent);
|
|
2404
2609
|
|
|
2405
2610
|
if (curContext === tstc.tc_expr && !insideTemplate) {
|
|
2406
2611
|
this.context.pop();
|
|
@@ -2408,9 +2613,9 @@ export function TSRXPlugin(config) {
|
|
|
2408
2613
|
|
|
2409
2614
|
/** @type {AST.Element} */ (element).css = content;
|
|
2410
2615
|
} else {
|
|
2411
|
-
this.
|
|
2412
|
-
|
|
2413
|
-
|
|
2616
|
+
this.#parseNativeTemplateBody(element, /** @type {AST.Element} */ (element).children, {
|
|
2617
|
+
enterScope: true,
|
|
2618
|
+
});
|
|
2414
2619
|
|
|
2415
2620
|
if (element.type === 'Tsx') {
|
|
2416
2621
|
this.#path.pop();
|
|
@@ -2501,12 +2706,7 @@ export function TSRXPlugin(config) {
|
|
|
2501
2706
|
// Ensure we escape JSX <tag></tag> context
|
|
2502
2707
|
const curContext = this.curContext();
|
|
2503
2708
|
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';
|
|
2709
|
+
const insideTemplate = this.#isNativeTemplateNode(parent);
|
|
2510
2710
|
|
|
2511
2711
|
if (curContext === tstc.tc_expr && !insideTemplate) {
|
|
2512
2712
|
this.context.pop();
|
|
@@ -2535,8 +2735,9 @@ export function TSRXPlugin(config) {
|
|
|
2535
2735
|
parseTemplateBody(body) {
|
|
2536
2736
|
const inside_func =
|
|
2537
2737
|
this.context.some((n) => n.token === 'function') || this.scopeStack.length > 1;
|
|
2538
|
-
const
|
|
2539
|
-
|
|
2738
|
+
const inside_tsx_island = this.#path.findLast(
|
|
2739
|
+
(n) => n.type === 'Tsx' || n.type === 'TsxCompat',
|
|
2740
|
+
);
|
|
2540
2741
|
|
|
2541
2742
|
if (!inside_func) {
|
|
2542
2743
|
if (this.type.label === 'continue') {
|
|
@@ -2547,168 +2748,15 @@ export function TSRXPlugin(config) {
|
|
|
2547
2748
|
}
|
|
2548
2749
|
}
|
|
2549
2750
|
|
|
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
|
-
}
|
|
2751
|
+
if (inside_tsx_island) {
|
|
2752
|
+
this.#parseTsxIslandBody(
|
|
2753
|
+
/** @type {AST.Tsx | AST.TsxCompat} */ (inside_tsx_island),
|
|
2754
|
+
/** @type {AST.Node[]} */ (/** @type {unknown} */ (body)),
|
|
2755
|
+
);
|
|
2756
|
+
return;
|
|
2687
2757
|
}
|
|
2688
2758
|
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);
|
|
2759
|
+
body.push(this.#parseNativeTemplateExpressionContainer());
|
|
2712
2760
|
} else if (this.type === tt.string && this.input.charCodeAt(this.start) === 34) {
|
|
2713
2761
|
body.push(this.parseDoubleQuotedTextChild());
|
|
2714
2762
|
} else if (this.type === tt.braceR) {
|
|
@@ -2957,30 +3005,8 @@ export function TSRXPlugin(config) {
|
|
|
2957
3005
|
this.type === tt.braceL &&
|
|
2958
3006
|
this.context.some((c) => c === tstc.tc_expr)
|
|
2959
3007
|
) {
|
|
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
3008
|
return /** @type {ESTreeJSX.JSXEmptyExpression | AST.TSRXExpression | AST.Html | AST.TextNode | ESTreeJSX.JSXExpressionContainer} */ (
|
|
2983
|
-
/** @type {unknown} */ (
|
|
3009
|
+
/** @type {unknown} */ (this.#parseNativeTemplateExpressionContainer())
|
|
2984
3010
|
);
|
|
2985
3011
|
}
|
|
2986
3012
|
|
|
@@ -3008,6 +3034,18 @@ export function TSRXPlugin(config) {
|
|
|
3008
3034
|
this.context.pop();
|
|
3009
3035
|
}
|
|
3010
3036
|
}
|
|
3037
|
+
const context_restore = this.#functionBodyContextRestoreStack.at(-1);
|
|
3038
|
+
if (
|
|
3039
|
+
this.#functionBodyDepth > 0 &&
|
|
3040
|
+
node.type === 'Tsrx' &&
|
|
3041
|
+
context_restore?.canRestore &&
|
|
3042
|
+
this.type !== tt.braceR &&
|
|
3043
|
+
this.type !== tt.comma
|
|
3044
|
+
) {
|
|
3045
|
+
context_restore.restore = true;
|
|
3046
|
+
this.context = [b_stat];
|
|
3047
|
+
this.exprAllowed = true;
|
|
3048
|
+
}
|
|
3011
3049
|
return node;
|
|
3012
3050
|
}
|
|
3013
3051
|
|
|
@@ -3077,10 +3115,9 @@ export function TSRXPlugin(config) {
|
|
|
3077
3115
|
node.body = [];
|
|
3078
3116
|
this.#allowDoubleQuotedTextChildAfterBrace = true;
|
|
3079
3117
|
this.expect(tt.braceL);
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
}
|
|
3083
|
-
this.parseTemplateBody(node.body);
|
|
3118
|
+
this.#parseNativeTemplateBody(node, node.body, {
|
|
3119
|
+
enterScope: createNewLexicalScope,
|
|
3120
|
+
});
|
|
3084
3121
|
|
|
3085
3122
|
if (exitStrict) {
|
|
3086
3123
|
this.strict = false;
|
|
@@ -3088,9 +3125,6 @@ export function TSRXPlugin(config) {
|
|
|
3088
3125
|
this.exprAllowed = true;
|
|
3089
3126
|
|
|
3090
3127
|
this.next();
|
|
3091
|
-
if (createNewLexicalScope) {
|
|
3092
|
-
this.exitScope();
|
|
3093
|
-
}
|
|
3094
3128
|
return this.finishNode(node, 'BlockStatement');
|
|
3095
3129
|
}
|
|
3096
3130
|
|