@tsrx/core 0.0.14 → 0.0.15
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/README.md +13 -8
- package/package.json +1 -1
- package/src/plugin.js +39 -1
- package/src/transform/segments.js +14 -13
- package/types/parse.d.ts +2 -0
package/README.md
CHANGED
|
@@ -61,15 +61,20 @@ component Button(props: Props) {
|
|
|
61
61
|
### 2. JSX-as-statements
|
|
62
62
|
|
|
63
63
|
Inside a `component` body, JSX elements are valid _statement_ forms. They describe
|
|
64
|
-
rendered output and are not expressions — they have no value.
|
|
64
|
+
rendered output and are not expressions — they have no value. Static text may be
|
|
65
|
+
written as a direct double-quoted child; dynamic values and other JavaScript
|
|
66
|
+
expressions stay inside `{}`.
|
|
65
67
|
|
|
66
68
|
```tsx
|
|
67
69
|
component Greeting() {
|
|
68
|
-
<h1>
|
|
69
|
-
<p>
|
|
70
|
+
<h1>"Hello"</h1>
|
|
71
|
+
<p>"Welcome"</p>
|
|
70
72
|
}
|
|
71
73
|
```
|
|
72
74
|
|
|
75
|
+
Only double quotes have direct-child text meaning. Single-quoted strings and
|
|
76
|
+
template literals remain JavaScript expressions and must be written inside `{}`.
|
|
77
|
+
|
|
73
78
|
Elsewhere (outside a `component` body), JSX remains an expression, as in standard
|
|
74
79
|
JSX.
|
|
75
80
|
|
|
@@ -84,9 +89,9 @@ introduced — but framework compilers treat them as _reactive_ boundaries.
|
|
|
84
89
|
```tsx
|
|
85
90
|
component List(props: { items: Item[]; showHeader: boolean }) {
|
|
86
91
|
if (props.showHeader) {
|
|
87
|
-
<h1>
|
|
92
|
+
<h1>"Items"</h1>
|
|
88
93
|
} else {
|
|
89
|
-
<h2>
|
|
94
|
+
<h2>"(no header)"</h2>
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
for (const item of props.items) {
|
|
@@ -95,10 +100,10 @@ component List(props: { items: Item[]; showHeader: boolean }) {
|
|
|
95
100
|
|
|
96
101
|
switch (props.items.length) {
|
|
97
102
|
case 0:
|
|
98
|
-
<p>
|
|
103
|
+
<p>"empty"</p>
|
|
99
104
|
break;
|
|
100
105
|
default:
|
|
101
|
-
<p>
|
|
106
|
+
<p>"has items"</p>
|
|
102
107
|
}
|
|
103
108
|
|
|
104
109
|
try {
|
|
@@ -169,7 +174,7 @@ children).
|
|
|
169
174
|
|
|
170
175
|
```tsx
|
|
171
176
|
component Page() {
|
|
172
|
-
const header = <tsx><h1>
|
|
177
|
+
const header = <tsx><h1>Hello</h1></tsx>;
|
|
173
178
|
renderSomewhereElse(header);
|
|
174
179
|
}
|
|
175
180
|
```
|
package/package.json
CHANGED
package/src/plugin.js
CHANGED
|
@@ -175,6 +175,7 @@ export function TSRXPlugin(config) {
|
|
|
175
175
|
class TSRXParser extends Parser {
|
|
176
176
|
/** @type {AST.Node[]} */
|
|
177
177
|
#path = [];
|
|
178
|
+
#allowTagStartAfterDoubleQuotedText = false;
|
|
178
179
|
#commentContextId = 0;
|
|
179
180
|
#loose = false;
|
|
180
181
|
/** @type {import('../types/index').CompileError[] | undefined} */
|
|
@@ -558,6 +559,10 @@ export function TSRXPlugin(config) {
|
|
|
558
559
|
* @type {Parse.Parser['getTokenFromCode']}
|
|
559
560
|
*/
|
|
560
561
|
getTokenFromCode(code) {
|
|
562
|
+
if (code !== 60) {
|
|
563
|
+
this.#allowTagStartAfterDoubleQuotedText = false;
|
|
564
|
+
}
|
|
565
|
+
|
|
561
566
|
if (code === 60) {
|
|
562
567
|
// < character
|
|
563
568
|
const inComponent = this.#path.findLast((n) => n.type === 'Component');
|
|
@@ -634,8 +639,14 @@ export function TSRXPlugin(config) {
|
|
|
634
639
|
// Inside component template bodies, allow adjacent tags without requiring
|
|
635
640
|
// a newline/indentation before the next '<'. This is important for inputs
|
|
636
641
|
// like `<div />` and `</div><style>...</style>` which Prettier formats.
|
|
637
|
-
if (
|
|
642
|
+
if (
|
|
643
|
+
(prevNonWhitespaceChar === 34 /* '"' */ &&
|
|
644
|
+
this.#allowTagStartAfterDoubleQuotedText) ||
|
|
645
|
+
prevNonWhitespaceChar === 123 /* '{' */ ||
|
|
646
|
+
prevNonWhitespaceChar === 62 /* '>' */
|
|
647
|
+
) {
|
|
638
648
|
if (!isWhitespaceAfterLt) {
|
|
649
|
+
this.#allowTagStartAfterDoubleQuotedText = false;
|
|
639
650
|
++this.pos;
|
|
640
651
|
return this.finishToken(tstt.jsxTagStart);
|
|
641
652
|
}
|
|
@@ -712,6 +723,7 @@ export function TSRXPlugin(config) {
|
|
|
712
723
|
}
|
|
713
724
|
}
|
|
714
725
|
}
|
|
726
|
+
this.#allowTagStartAfterDoubleQuotedText = false;
|
|
715
727
|
return super.getTokenFromCode(code);
|
|
716
728
|
}
|
|
717
729
|
|
|
@@ -1300,6 +1312,30 @@ export function TSRXPlugin(config) {
|
|
|
1300
1312
|
);
|
|
1301
1313
|
}
|
|
1302
1314
|
|
|
1315
|
+
/**
|
|
1316
|
+
* @returns {AST.TextNode}
|
|
1317
|
+
*/
|
|
1318
|
+
parseDoubleQuotedTextChild() {
|
|
1319
|
+
const node = /** @type {AST.TextNode} */ (this.startNode());
|
|
1320
|
+
const expression = /** @type {AST.Literal} */ (this.startNode());
|
|
1321
|
+
const raw = this.input.slice(this.start, this.end);
|
|
1322
|
+
const end = this.end;
|
|
1323
|
+
const endLoc = this.endLoc;
|
|
1324
|
+
|
|
1325
|
+
expression.value = this.value;
|
|
1326
|
+
expression.raw = raw;
|
|
1327
|
+
node.expression = this.finishNodeAt(expression, 'Literal', end, endLoc);
|
|
1328
|
+
|
|
1329
|
+
this.#allowTagStartAfterDoubleQuotedText = true;
|
|
1330
|
+
try {
|
|
1331
|
+
this.next();
|
|
1332
|
+
} finally {
|
|
1333
|
+
this.#allowTagStartAfterDoubleQuotedText = false;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
return this.finishNodeAt(node, 'Text', end, endLoc);
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1303
1339
|
/**
|
|
1304
1340
|
* @type {Parse.Parser['jsx_parseAttribute']}
|
|
1305
1341
|
*/
|
|
@@ -2293,6 +2329,8 @@ export function TSRXPlugin(config) {
|
|
|
2293
2329
|
delete node.text;
|
|
2294
2330
|
}
|
|
2295
2331
|
body.push(node);
|
|
2332
|
+
} else if (this.type === tt.string && this.input.charCodeAt(this.start) === 34) {
|
|
2333
|
+
body.push(this.parseDoubleQuotedTextChild());
|
|
2296
2334
|
} else if (this.type === tt.braceR) {
|
|
2297
2335
|
// Leaving a component/template body. We may still be in TSX/JSX tokenization
|
|
2298
2336
|
// context (e.g. after parsing markup), but the closing `}` is a JS token.
|
|
@@ -1450,12 +1450,6 @@ export function convert_source_map_to_mappings(
|
|
|
1450
1450
|
}
|
|
1451
1451
|
}
|
|
1452
1452
|
|
|
1453
|
-
if (node.loc) {
|
|
1454
|
-
mappings.push(
|
|
1455
|
-
get_mapping_from_node(node, src_to_gen_map, gen_line_offsets, mapping_data_verify_only),
|
|
1456
|
-
);
|
|
1457
|
-
}
|
|
1458
|
-
|
|
1459
1453
|
return;
|
|
1460
1454
|
} else if (node.type === 'SwitchCase') {
|
|
1461
1455
|
// Visit in source order: test, consequent
|
|
@@ -1705,14 +1699,21 @@ export function convert_source_map_to_mappings(
|
|
|
1705
1699
|
// Generic spans can be emitted by downstream transforms with sparse source-map
|
|
1706
1700
|
// coverage around the angle-bracket delimiters. Skip missing whole-node mappings
|
|
1707
1701
|
// instead of crashing Volar, and rely on child type-node mappings instead.
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1702
|
+
try {
|
|
1703
|
+
const mapping = get_mapping_from_node(
|
|
1704
|
+
node,
|
|
1705
|
+
src_to_gen_map,
|
|
1706
|
+
gen_line_offsets,
|
|
1707
|
+
mapping_data_verify_only,
|
|
1708
|
+
);
|
|
1715
1709
|
mappings.push(mapping);
|
|
1710
|
+
} catch (error) {
|
|
1711
|
+
if (
|
|
1712
|
+
!(error instanceof Error) ||
|
|
1713
|
+
!error.message.startsWith('No source map entry for position')
|
|
1714
|
+
) {
|
|
1715
|
+
throw error;
|
|
1716
|
+
}
|
|
1716
1717
|
}
|
|
1717
1718
|
}
|
|
1718
1719
|
// Generic type parameters - visit to collect type variable names
|
package/types/parse.d.ts
CHANGED
|
@@ -1174,6 +1174,8 @@ export namespace Parse {
|
|
|
1174
1174
|
|
|
1175
1175
|
parseElement(): AST.Element | AST.Tsx | AST.TsxCompat;
|
|
1176
1176
|
|
|
1177
|
+
parseDoubleQuotedTextChild(): AST.TextNode;
|
|
1178
|
+
|
|
1177
1179
|
parseTemplateBody(
|
|
1178
1180
|
body: (AST.Statement | AST.Node | ESTreeJSX.JSXText | ESTreeJSX.JSXElement['children'])[],
|
|
1179
1181
|
): void;
|