@tsrx/core 0.1.55 → 0.1.57
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 +73 -50
- package/src/scope.js +1 -1
- package/src/transform/jsx/helpers.js +20 -5
- package/src/transform/jsx/server-module.js +2 -1
- package/types/index.d.ts +2 -2
package/package.json
CHANGED
package/src/plugin.js
CHANGED
|
@@ -108,6 +108,28 @@ function get_argument_clash_reported_names(check_clashes) {
|
|
|
108
108
|
return reported_names;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
* A `<` opens a tag only when the character after it can begin one: `/` for a
|
|
113
|
+
* closing tag, `>` for a fragment, `{` for a dynamic tag, or an element or
|
|
114
|
+
* component name start. Any other character, or end of input, leaves the `<`
|
|
115
|
+
* as literal text.
|
|
116
|
+
* @param {string} input
|
|
117
|
+
* @param {number} index Index of the `<`.
|
|
118
|
+
*/
|
|
119
|
+
function can_start_tag_after_lt(input, index) {
|
|
120
|
+
const next = index + 1 < input.length ? input.charCodeAt(index + 1) : -1;
|
|
121
|
+
return (
|
|
122
|
+
next === CharCode.slash ||
|
|
123
|
+
next === CharCode.greaterThan ||
|
|
124
|
+
next === CharCode.openBrace ||
|
|
125
|
+
next === CharCode.at ||
|
|
126
|
+
next === CharCode.dollar ||
|
|
127
|
+
next === CharCode.underscore ||
|
|
128
|
+
(next >= CharCode.uppercaseA && next <= CharCode.uppercaseZ) ||
|
|
129
|
+
(next >= CharCode.lowercaseA && next <= CharCode.lowercaseZ)
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
111
133
|
/**
|
|
112
134
|
* @param {string} input
|
|
113
135
|
* @param {number} i
|
|
@@ -299,12 +321,32 @@ export function TSRXPlugin(config) {
|
|
|
299
321
|
finishNode(node, type) {
|
|
300
322
|
const finished = super.finishNode(node, type);
|
|
301
323
|
if (type === 'TSModuleDeclaration') {
|
|
302
|
-
const
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
324
|
+
const declaration = /** @type {AST.TSModuleDeclaration} */ (finished);
|
|
325
|
+
const start = /** @type {number} */ (declaration.start);
|
|
326
|
+
// acorn-typescript still exposes the legacy `global` flag without
|
|
327
|
+
// TSESTree's replacement `kind`; replace it at the parser boundary
|
|
328
|
+
// so downstream consumers only receive the current discriminator.
|
|
329
|
+
const legacy = /** @type {{ global?: boolean }} */ (declaration);
|
|
330
|
+
const prefix = this.input
|
|
331
|
+
.slice(start, declaration.id.start)
|
|
332
|
+
.replace(/\/\*[\s\S]*?\*\/|\/\/[^\r\n]*/g, ' ')
|
|
333
|
+
.trim();
|
|
334
|
+
const kind =
|
|
335
|
+
declaration.kind ??
|
|
336
|
+
(legacy.global ? 'global' : prefix.endsWith('namespace') ? 'namespace' : 'module');
|
|
337
|
+
/** @type {AST.TSModuleDeclaration | null} */
|
|
338
|
+
let current = declaration;
|
|
339
|
+
while (current !== null) {
|
|
340
|
+
const current_legacy = /** @type {{ global?: boolean }} */ (current);
|
|
341
|
+
current.kind = kind;
|
|
342
|
+
delete current_legacy.global;
|
|
343
|
+
current.metadata ??= { path: [] };
|
|
344
|
+
current.metadata.module_keyword = kind;
|
|
345
|
+
const body = /** @type {AST.TSModuleBlock | AST.TSModuleDeclaration} */ (
|
|
346
|
+
/** @type {unknown} */ (current.body)
|
|
347
|
+
);
|
|
348
|
+
current = body?.type === 'TSModuleDeclaration' ? body : null;
|
|
349
|
+
}
|
|
308
350
|
}
|
|
309
351
|
return finished;
|
|
310
352
|
}
|
|
@@ -603,7 +645,7 @@ export function TSRXPlugin(config) {
|
|
|
603
645
|
}
|
|
604
646
|
const ch = this.input.charCodeAt(index);
|
|
605
647
|
if (
|
|
606
|
-
ch === CharCode.lessThan ||
|
|
648
|
+
(ch === CharCode.lessThan && can_start_tag_after_lt(this.input, index)) ||
|
|
607
649
|
ch === CharCode.openBrace ||
|
|
608
650
|
ch === CharCode.closeBrace ||
|
|
609
651
|
this.#isCodeBlockStart(index) ||
|
|
@@ -1126,7 +1168,7 @@ export function TSRXPlugin(config) {
|
|
|
1126
1168
|
while (index < this.input.length) {
|
|
1127
1169
|
const ch = this.input.charCodeAt(index);
|
|
1128
1170
|
if (
|
|
1129
|
-
ch === CharCode.lessThan ||
|
|
1171
|
+
(ch === CharCode.lessThan && can_start_tag_after_lt(this.input, index)) ||
|
|
1130
1172
|
ch === CharCode.openBrace ||
|
|
1131
1173
|
ch === CharCode.closeBrace ||
|
|
1132
1174
|
this.#isJSXControlFlowDirectiveAt(index) ||
|
|
@@ -1227,16 +1269,8 @@ export function TSRXPlugin(config) {
|
|
|
1227
1269
|
const index = skip_whitespace_from(this.input, this.start);
|
|
1228
1270
|
const ch = this.input.charCodeAt(index);
|
|
1229
1271
|
if (ch === CharCode.lessThan) {
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
const tagLike =
|
|
1233
|
-
next === CharCode.greaterThan ||
|
|
1234
|
-
next === CharCode.openBrace ||
|
|
1235
|
-
next === CharCode.at ||
|
|
1236
|
-
next === CharCode.dollar ||
|
|
1237
|
-
next === CharCode.underscore ||
|
|
1238
|
-
(next >= CharCode.uppercaseA && next <= CharCode.uppercaseZ) ||
|
|
1239
|
-
(next >= CharCode.lowercaseA && next <= CharCode.lowercaseZ);
|
|
1272
|
+
if (this.input.charCodeAt(index + 1) === CharCode.slash) return false;
|
|
1273
|
+
const tagLike = can_start_tag_after_lt(this.input, index);
|
|
1240
1274
|
const previous = this.#previousNonSpaceTabIndex(index);
|
|
1241
1275
|
const afterSemicolon =
|
|
1242
1276
|
previous >= 0 && this.input.charCodeAt(previous) === CharCode.semicolon;
|
|
@@ -2875,7 +2909,7 @@ export function TSRXPlugin(config) {
|
|
|
2875
2909
|
this.#suppressTemplateRawTextToken = false;
|
|
2876
2910
|
const context = this.curContext();
|
|
2877
2911
|
if (
|
|
2878
|
-
code !== CharCode.lessThan &&
|
|
2912
|
+
(code !== CharCode.lessThan || !can_start_tag_after_lt(this.input, this.pos)) &&
|
|
2879
2913
|
code !== CharCode.greaterThan &&
|
|
2880
2914
|
code !== CharCode.openBrace &&
|
|
2881
2915
|
code !== CharCode.closeBrace &&
|
|
@@ -2913,17 +2947,7 @@ export function TSRXPlugin(config) {
|
|
|
2913
2947
|
return super.readToken(code);
|
|
2914
2948
|
}
|
|
2915
2949
|
if (code === CharCode.lessThan) {
|
|
2916
|
-
|
|
2917
|
-
const isTagLikeAfterLt =
|
|
2918
|
-
next === CharCode.slash ||
|
|
2919
|
-
next === CharCode.greaterThan ||
|
|
2920
|
-
next === CharCode.openBrace ||
|
|
2921
|
-
next === CharCode.at ||
|
|
2922
|
-
next === CharCode.dollar ||
|
|
2923
|
-
next === CharCode.underscore ||
|
|
2924
|
-
(next >= CharCode.uppercaseA && next <= CharCode.uppercaseZ) ||
|
|
2925
|
-
(next >= CharCode.lowercaseA && next <= CharCode.lowercaseZ);
|
|
2926
|
-
if (this.exprAllowed && isTagLikeAfterLt) {
|
|
2950
|
+
if (this.exprAllowed && can_start_tag_after_lt(this.input, this.pos)) {
|
|
2927
2951
|
++this.pos;
|
|
2928
2952
|
return this.finishToken(tstt.jsxTagStart);
|
|
2929
2953
|
}
|
|
@@ -3038,21 +3062,7 @@ export function TSRXPlugin(config) {
|
|
|
3038
3062
|
// <Something>...</Something>\n\n<Child />
|
|
3039
3063
|
// <head><style>...</style></head>
|
|
3040
3064
|
// We only do this when '<' is in a tag-like position.
|
|
3041
|
-
const
|
|
3042
|
-
nextChar === CharCode.space ||
|
|
3043
|
-
nextChar === CharCode.tab ||
|
|
3044
|
-
nextChar === CharCode.lineFeed ||
|
|
3045
|
-
nextChar === CharCode.carriageReturn;
|
|
3046
|
-
const isTagLikeAfterLt =
|
|
3047
|
-
!isWhitespaceAfterLt &&
|
|
3048
|
-
(nextChar === CharCode.slash ||
|
|
3049
|
-
nextChar === CharCode.greaterThan ||
|
|
3050
|
-
nextChar === CharCode.openBrace ||
|
|
3051
|
-
nextChar === CharCode.at ||
|
|
3052
|
-
nextChar === CharCode.dollar ||
|
|
3053
|
-
nextChar === CharCode.underscore ||
|
|
3054
|
-
(nextChar >= CharCode.uppercaseA && nextChar <= CharCode.uppercaseZ) ||
|
|
3055
|
-
(nextChar >= CharCode.lowercaseA && nextChar <= CharCode.lowercaseZ));
|
|
3065
|
+
const isTagLikeAfterLt = can_start_tag_after_lt(this.input, this.pos);
|
|
3056
3066
|
const prevAllowsTagStart =
|
|
3057
3067
|
prevNonWhitespaceChar === null ||
|
|
3058
3068
|
prevNonWhitespaceChar === CharCode.lineFeed || // '\n'
|
|
@@ -3079,7 +3089,7 @@ export function TSRXPlugin(config) {
|
|
|
3079
3089
|
prevNonWhitespaceChar === CharCode.openBrace ||
|
|
3080
3090
|
prevNonWhitespaceChar === CharCode.greaterThan
|
|
3081
3091
|
) {
|
|
3082
|
-
if (
|
|
3092
|
+
if (isTagLikeAfterLt) {
|
|
3083
3093
|
++this.pos;
|
|
3084
3094
|
return this.finishToken(tstt.jsxTagStart);
|
|
3085
3095
|
}
|
|
@@ -4116,6 +4126,16 @@ export function TSRXPlugin(config) {
|
|
|
4116
4126
|
|
|
4117
4127
|
case CharCode.lessThan:
|
|
4118
4128
|
case CharCode.openBrace:
|
|
4129
|
+
// This path reads the children of an element nested in a `{ … }`
|
|
4130
|
+
// expression container, where the raw-text intercept in `readToken`
|
|
4131
|
+
// is off, so the literal-`<` rule has to be applied here too. Keep
|
|
4132
|
+
// scanning rather than finishing the token: the `<` belongs to the
|
|
4133
|
+
// text run, and finishing here would emit an empty text token when
|
|
4134
|
+
// it is the run's first character.
|
|
4135
|
+
if (ch === CharCode.lessThan && !can_start_tag_after_lt(this.input, this.pos)) {
|
|
4136
|
+
++this.pos;
|
|
4137
|
+
break;
|
|
4138
|
+
}
|
|
4119
4139
|
if (out || this.pos > chunkStart) {
|
|
4120
4140
|
return this.finishToken(tstt.jsxText, out + this.input.slice(chunkStart, this.pos));
|
|
4121
4141
|
}
|
|
@@ -4609,10 +4629,13 @@ export function TSRXPlugin(config) {
|
|
|
4609
4629
|
} else if (this.type === tstt.jsxText) {
|
|
4610
4630
|
// A nested element with its own body can leak a JSX expression context,
|
|
4611
4631
|
// so the whitespace after its closing tag is mis-tokenized as a stale
|
|
4612
|
-
// text token whose start was advanced onto the following `<`.
|
|
4613
|
-
//
|
|
4614
|
-
// tag instead of emitting an empty node.
|
|
4615
|
-
if (
|
|
4632
|
+
// text token whose start was advanced onto the following `<`. Text never
|
|
4633
|
+
// starts at a `<` that can open a tag, so drop the leaked context and
|
|
4634
|
+
// re-read the tag instead of emitting an empty node.
|
|
4635
|
+
if (
|
|
4636
|
+
this.input.charCodeAt(this.start) === CharCode.lessThan &&
|
|
4637
|
+
can_start_tag_after_lt(this.input, this.start)
|
|
4638
|
+
) {
|
|
4616
4639
|
if (this.#jsxExpressionContainerDepth > 0) {
|
|
4617
4640
|
// Inside a `{ … }` container the whole-stack counts below are
|
|
4618
4641
|
// blind: the enclosing template's `tc_expr` contexts sit on the
|
package/src/scope.js
CHANGED
|
@@ -130,7 +130,7 @@ export function create_scopes(ast, root, parent, error_options) {
|
|
|
130
130
|
},
|
|
131
131
|
|
|
132
132
|
TSModuleDeclaration(node, { state, next }) {
|
|
133
|
-
const is_submodule = node.
|
|
133
|
+
const is_submodule = node.declare !== true && node.kind === 'module';
|
|
134
134
|
if (is_submodule && node.id?.type === 'Identifier') {
|
|
135
135
|
state.scope.declare(node.id, 'normal', 'module', node);
|
|
136
136
|
}
|
|
@@ -152,6 +152,15 @@ export function tsx_with_ts_locations(boundary_tokens = false, comments = undefi
|
|
|
152
152
|
context.visit(value.body);
|
|
153
153
|
},
|
|
154
154
|
|
|
155
|
+
// TSRX text may contain a literal `<` — one that cannot start a tag
|
|
156
|
+
// (`<span><3</span>`) or a raw-text `<script>` body — but the printed TSX
|
|
157
|
+
// is re-parsed by a JSX toolchain (esbuild, Babel, SWC), and JSX forbids
|
|
158
|
+
// a bare `<` in text. Emit it as `<`, which those parsers decode back
|
|
159
|
+
// to the same string.
|
|
160
|
+
JSXText: (node, context) => {
|
|
161
|
+
context.write(node.value.replace(/</g, '<'), node);
|
|
162
|
+
},
|
|
163
|
+
|
|
155
164
|
// esrap's JSXOpeningElement printer doesn't emit `typeArguments`, so generic
|
|
156
165
|
// component tags like `<RenderProp<User>>` lose the `<User>` in the output.
|
|
157
166
|
JSXOpeningElement: (node, context) => {
|
|
@@ -180,12 +189,18 @@ export function tsx_with_ts_locations(boundary_tokens = false, comments = undefi
|
|
|
180
189
|
}
|
|
181
190
|
},
|
|
182
191
|
TSModuleDeclaration: (node, context) => {
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
-
// `
|
|
192
|
+
// `declare global` is represented as a TSModuleDeclaration whose id is
|
|
193
|
+
// `global`; adding `module` changes it into an unrelated named module.
|
|
194
|
+
// Ambient `declare module '…' { … }` must still keep its `declare` —
|
|
195
|
+
// the typeOnly/volar output is real TS and `module '…' { … }` alone is
|
|
196
|
+
// a syntax error (TS1035).
|
|
187
197
|
if (node.declare) context.write('declare ');
|
|
188
|
-
|
|
198
|
+
if (node.kind === 'global') {
|
|
199
|
+
context.visit(node.id);
|
|
200
|
+
context.visit(node.body);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
context.write(node.kind);
|
|
189
204
|
context.write(' ');
|
|
190
205
|
context.visit(node.id);
|
|
191
206
|
context.visit(node.body);
|
|
@@ -110,7 +110,7 @@ function is_server_module_declaration(node, block_name) {
|
|
|
110
110
|
return (
|
|
111
111
|
node.type === 'TSModuleDeclaration' &&
|
|
112
112
|
node.declare !== true &&
|
|
113
|
-
node.
|
|
113
|
+
node.kind === 'module' &&
|
|
114
114
|
identifier_name(node.id) === block_name
|
|
115
115
|
);
|
|
116
116
|
}
|
|
@@ -465,6 +465,7 @@ function lower_declaration(declaration, outside_names, block_name) {
|
|
|
465
465
|
const namespace = /** @type {AST.TSStatement<AST.TSModuleDeclaration>} */ ({
|
|
466
466
|
...declaration,
|
|
467
467
|
id,
|
|
468
|
+
kind: 'namespace',
|
|
468
469
|
metadata: { ...declaration.metadata, module_keyword: 'namespace' },
|
|
469
470
|
body: { ...declaration.body, body: [...aliases, ...rest] },
|
|
470
471
|
});
|
package/types/index.d.ts
CHANGED
|
@@ -106,7 +106,7 @@ export interface BaseNodeMetaData {
|
|
|
106
106
|
has_template?: boolean;
|
|
107
107
|
source_name?: string;
|
|
108
108
|
source_length?: number;
|
|
109
|
-
module_keyword?: 'module' | 'namespace';
|
|
109
|
+
module_keyword?: 'global' | 'module' | 'namespace';
|
|
110
110
|
/**
|
|
111
111
|
* Generated identifier whose SOURCE span sits inside an authored string
|
|
112
112
|
* literal (e.g. a server-module lowering's namespace reference carrying
|
|
@@ -1302,7 +1302,7 @@ declare module 'estree' {
|
|
|
1302
1302
|
}
|
|
1303
1303
|
interface TSModuleDeclaration extends Omit<
|
|
1304
1304
|
AcornTSNode<TSESTree.TSModuleDeclaration>,
|
|
1305
|
-
'body' | 'id'
|
|
1305
|
+
'body' | 'global' | 'id'
|
|
1306
1306
|
> {
|
|
1307
1307
|
body: TSModuleBlock;
|
|
1308
1308
|
/** A string literal for `declare module '<specifier>'`. */
|