@gi-tcg/gts-transpiler 0.3.2 → 0.3.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/dist/index.d.ts +1 -1
- package/dist/index.js +351 -458
- package/package.json +6 -4
- package/src/parse/gts_plugin.ts +2 -2
- package/src/parse/index.ts +2 -0
- package/src/parse/loose_plugin.ts +1 -1
- package/src/parse/record_call_lparen_plugin.ts +17 -20
- package/src/transform/gts.ts +26 -71
- package/src/transform/index.ts +3 -3
- package/src/transform/volar/collect_tokens.ts +28 -34
- package/src/transform/volar/content_start.ts +69 -0
- package/src/transform/volar/index.ts +70 -45
- package/src/transform/volar/mappings.ts +5 -352
- package/src/transform/volar/printer.ts +139 -117
- package/src/transform/volar/replacements.ts +75 -47
- package/src/transform/volar/walker.ts +123 -81
- package/src/types.ts +3 -9
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import type { Visitors } from "zimmerframe";
|
|
2
2
|
import type {
|
|
3
|
-
ArrayExpression,
|
|
4
3
|
ArrowFunctionExpression,
|
|
5
|
-
BlockStatement,
|
|
6
4
|
Comment,
|
|
7
|
-
Declaration,
|
|
8
5
|
EmptyStatement,
|
|
9
6
|
ExportNamedDeclaration,
|
|
10
7
|
Expression,
|
|
11
8
|
ExpressionStatement,
|
|
12
9
|
GTSDefineStatement,
|
|
13
10
|
Identifier,
|
|
11
|
+
ImportDeclaration,
|
|
14
12
|
Literal,
|
|
15
13
|
MemberExpression,
|
|
16
14
|
Node,
|
|
17
15
|
Program,
|
|
18
|
-
|
|
16
|
+
SimpleLiteral,
|
|
19
17
|
Statement,
|
|
20
18
|
VariableDeclaration,
|
|
21
19
|
VariableDeclarator,
|
|
@@ -25,8 +23,8 @@ import {
|
|
|
25
23
|
type ExternalizedBinding,
|
|
26
24
|
type TranspileState,
|
|
27
25
|
} from "../gts.ts";
|
|
28
|
-
import type { LeafToken } from "./collect_tokens.ts";
|
|
29
26
|
import { createReplacementHolder } from "./replacements.ts";
|
|
27
|
+
import type { SourceRange } from "espolar";
|
|
30
28
|
|
|
31
29
|
interface ExternalizedTypedBinding extends ExternalizedBinding {
|
|
32
30
|
typingId: Identifier;
|
|
@@ -34,7 +32,6 @@ interface ExternalizedTypedBinding extends ExternalizedBinding {
|
|
|
34
32
|
}
|
|
35
33
|
|
|
36
34
|
export interface TypingTranspileState extends TranspileState {
|
|
37
|
-
leafTokens: LeafToken[];
|
|
38
35
|
externalizedBindings: ExternalizedTypedBinding[];
|
|
39
36
|
idCounter: number;
|
|
40
37
|
rootVmId: Identifier;
|
|
@@ -55,23 +52,40 @@ export interface TypingTranspileState extends TranspileState {
|
|
|
55
52
|
/** Pending statements to be inserted to the top-level */
|
|
56
53
|
typingPendingStatements: Statement[];
|
|
57
54
|
replacementTag: Identifier;
|
|
58
|
-
|
|
55
|
+
/** untouched source nodes */
|
|
56
|
+
sourceNodes: WeakSet<Node>;
|
|
57
|
+
/**
|
|
58
|
+
* The callee of typing source of GTS' attribute names. Map the character
|
|
59
|
+
* after the name (typically whitespace) as the lParen of CallExpression
|
|
60
|
+
*/
|
|
61
|
+
namedAttributeCalleeLParenRange: WeakMap<Node, SourceRange>;
|
|
62
|
+
/**
|
|
63
|
+
* String literal nodes that are derived from identifiers.
|
|
64
|
+
* - sourceStart += 1
|
|
65
|
+
* - sourceLength -= 2
|
|
66
|
+
*/
|
|
67
|
+
literalFromIdentifier: WeakSet<Literal>;
|
|
68
|
+
/** Nodes that are last arguments of a CallExpression */
|
|
69
|
+
lastArgNodes: WeakSet<Node>;
|
|
70
|
+
/**
|
|
71
|
+
* The last import declaration, record if it is a generated one.
|
|
72
|
+
* This declaration marks the TS auto-import insertion point.
|
|
73
|
+
*/
|
|
74
|
+
lastImportDeclarationIfGen: ImportDeclaration | null;
|
|
75
|
+
/** Nodes that have a diagnostic mappings to the top of the file */
|
|
76
|
+
diagnosticsOnTopNodes: WeakSet<Node>;
|
|
77
|
+
/** Extra mappings, the generated range will be found by the needle after replacement */
|
|
78
|
+
extraMappings: {
|
|
79
|
+
sourceOffset: number;
|
|
80
|
+
length: number;
|
|
81
|
+
generatedNeedle: string;
|
|
82
|
+
}[];
|
|
83
|
+
/** Character offset after hashbang and file-scope leading comments */
|
|
84
|
+
contentStartOffset: number;
|
|
59
85
|
}
|
|
60
86
|
|
|
61
87
|
const EMPTY: EmptyStatement = { type: "EmptyStatement" };
|
|
62
88
|
|
|
63
|
-
const ANY = {
|
|
64
|
-
type: "TSAnyKeyword",
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
// definite not supported by esrap yet, so we init the binding with an `as any` cast
|
|
68
|
-
// https://github.com/sveltejs/esrap/issues/95
|
|
69
|
-
const ANY_INIT = {
|
|
70
|
-
type: "TSAsExpression",
|
|
71
|
-
expression: { type: "Literal", value: 0 },
|
|
72
|
-
typeAnnotation: ANY,
|
|
73
|
-
} as {} as Expression;
|
|
74
|
-
|
|
75
89
|
const enterVMFromRoot = (state: TypingTranspileState) => {
|
|
76
90
|
let defTypeId: Identifier = {
|
|
77
91
|
type: "Identifier",
|
|
@@ -128,7 +142,7 @@ const enterVMFromAttr = (
|
|
|
128
142
|
state.attrsOfCurrentVm.push([]);
|
|
129
143
|
};
|
|
130
144
|
|
|
131
|
-
const exitVM = (state: TypingTranspileState,
|
|
145
|
+
const exitVM = (state: TypingTranspileState, errorRange?: [number, number]) => {
|
|
132
146
|
const currentDefTypeId = state.vmDefTypeIdStack.pop()!;
|
|
133
147
|
const currentMetaId = state.metaTypeIdStack.pop()!;
|
|
134
148
|
const finalMetaId = state.finalMetaTypeIdStack.pop()!;
|
|
@@ -140,7 +154,7 @@ const exitVM = (state: TypingTranspileState, errorLoc?: string) => {
|
|
|
140
154
|
defType: currentDefTypeId.name,
|
|
141
155
|
finalMetaType: finalMetaId.name,
|
|
142
156
|
collectedAttrs: collectedAttrNames,
|
|
143
|
-
|
|
157
|
+
errorRange,
|
|
144
158
|
}),
|
|
145
159
|
);
|
|
146
160
|
};
|
|
@@ -153,7 +167,7 @@ const enterAttr = (
|
|
|
153
167
|
const metaTypeId = state.metaTypeIdStack.at(-1);
|
|
154
168
|
if (!defTypeId || !metaTypeId) {
|
|
155
169
|
// TODO error handling?
|
|
156
|
-
return { lhsId: { type: "Identifier", name: "
|
|
170
|
+
return { lhsId: { type: "Identifier", name: "__gts_invalid_attr_obj" } };
|
|
157
171
|
}
|
|
158
172
|
state.attrsOfCurrentVm.at(-1)!.push(attrName);
|
|
159
173
|
const lhsId: Identifier = {
|
|
@@ -239,7 +253,7 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
239
253
|
{
|
|
240
254
|
type: "VariableDeclarator",
|
|
241
255
|
id: extBinding.bindingName,
|
|
242
|
-
|
|
256
|
+
// @ts-expect-error TS property not provided in ESTree
|
|
243
257
|
typeAnnotation: {
|
|
244
258
|
type: "TSTypeAnnotation",
|
|
245
259
|
typeAnnotation: {
|
|
@@ -247,7 +261,14 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
247
261
|
typeName: extBinding.typingId,
|
|
248
262
|
},
|
|
249
263
|
},
|
|
250
|
-
|
|
264
|
+
init: {
|
|
265
|
+
type: "TSNonNullExpression",
|
|
266
|
+
expression: {
|
|
267
|
+
type: "Literal",
|
|
268
|
+
value: null,
|
|
269
|
+
} satisfies Expression,
|
|
270
|
+
} as any,
|
|
271
|
+
},
|
|
251
272
|
],
|
|
252
273
|
};
|
|
253
274
|
if (extBinding.export) {
|
|
@@ -264,39 +285,66 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
264
285
|
body.unshift(varDecl);
|
|
265
286
|
}
|
|
266
287
|
}
|
|
267
|
-
|
|
268
|
-
|
|
288
|
+
const importDecls: ImportDeclaration[] = [];
|
|
289
|
+
importDecls.push(
|
|
290
|
+
{
|
|
269
291
|
type: "ImportDeclaration",
|
|
270
|
-
diagnosticsOnTop: true,
|
|
271
292
|
specifiers: [
|
|
272
293
|
{
|
|
273
294
|
type: "ImportDefaultSpecifier",
|
|
274
|
-
local: state.
|
|
295
|
+
local: state.rootVmId,
|
|
275
296
|
},
|
|
276
297
|
],
|
|
277
298
|
source: {
|
|
278
299
|
type: "Literal",
|
|
279
|
-
value: `${state.providerImportSource}/
|
|
300
|
+
value: `${state.providerImportSource}/vm`,
|
|
280
301
|
},
|
|
281
302
|
attributes: [],
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
body.unshift(
|
|
303
|
+
},
|
|
285
304
|
{
|
|
286
305
|
type: "ImportDeclaration",
|
|
287
|
-
diagnosticsOnTop: true,
|
|
288
306
|
specifiers: [
|
|
289
307
|
{
|
|
290
|
-
type: "
|
|
291
|
-
|
|
308
|
+
type: "ImportSpecifier",
|
|
309
|
+
imported: { type: "Identifier", name: "createDefine" },
|
|
310
|
+
local: state.createDefineFnId,
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
type: "ImportSpecifier",
|
|
314
|
+
imported: { type: "Identifier", name: "createBinding" },
|
|
315
|
+
local: state.createBindingFnId,
|
|
292
316
|
},
|
|
293
317
|
],
|
|
294
|
-
source: {
|
|
295
|
-
type: "Literal",
|
|
296
|
-
value: `${state.providerImportSource}/vm`,
|
|
297
|
-
},
|
|
318
|
+
source: { type: "Literal", value: state.runtimeImportSource },
|
|
298
319
|
attributes: [],
|
|
299
320
|
},
|
|
321
|
+
);
|
|
322
|
+
for (const importDecl of importDecls) {
|
|
323
|
+
state.diagnosticsOnTopNodes.add(importDecl.source);
|
|
324
|
+
for (const specifier of importDecl.specifiers) {
|
|
325
|
+
state.diagnosticsOnTopNodes.add(specifier);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
const lastImportDecl = importDecls.pop() ?? {
|
|
329
|
+
type: "ImportDeclaration",
|
|
330
|
+
specifiers: [],
|
|
331
|
+
source: { type: "Literal", value: "" },
|
|
332
|
+
attributes: [],
|
|
333
|
+
} satisfies ImportDeclaration;
|
|
334
|
+
body.unshift(
|
|
335
|
+
...importDecls,
|
|
336
|
+
// Add an unrelated statement between system generated imports to make them unsorted,
|
|
337
|
+
// so that TSServer will always insert auto-imports after the last ImportDeclaration.
|
|
338
|
+
// `lastImportDecl` will be marked as insertion point in printer if no user's
|
|
339
|
+
// ImportDeclaration is provided
|
|
340
|
+
{
|
|
341
|
+
type: "ExpressionStatement",
|
|
342
|
+
expression: {
|
|
343
|
+
type: "Literal",
|
|
344
|
+
value: 0,
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
lastImportDecl,
|
|
300
348
|
createReplacementHolder(state, {
|
|
301
349
|
type: "preface",
|
|
302
350
|
}),
|
|
@@ -317,30 +365,20 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
317
365
|
const attrName = JSON.stringify(
|
|
318
366
|
name.type === "Literal" ? String(name.value) : name.name,
|
|
319
367
|
);
|
|
320
|
-
const attributeNameToken = state.leafTokens.find((t) => t.loc === name.loc);
|
|
321
|
-
if (attributeNameToken) {
|
|
322
|
-
attributeNameToken.sourceLengthOffset = 1;
|
|
323
|
-
}
|
|
324
368
|
const { lhsId } = enterAttr(state, attrName);
|
|
325
|
-
state.extraMappings.set(
|
|
326
|
-
`${name.loc?.start.line}:${name.loc?.start.column}`,
|
|
327
|
-
`${lhsId.name}${name.type === "Literal" ? `[` : `.`}`,
|
|
328
|
-
);
|
|
329
369
|
const positionals = body.positionalAttributes.attributes.map(
|
|
330
370
|
(attr): Expression => {
|
|
331
371
|
if (attr.type === "Identifier" && /^[a-z_]/.test(attr.name)) {
|
|
332
|
-
const
|
|
333
|
-
if (token) {
|
|
334
|
-
token.generatedStartOffset = 1;
|
|
335
|
-
token.generatedLength = attr.name.length + 2; // quotation mark
|
|
336
|
-
}
|
|
337
|
-
return {
|
|
372
|
+
const lit: SimpleLiteral = {
|
|
338
373
|
type: "Literal",
|
|
339
374
|
value: attr.name,
|
|
340
375
|
loc: attr.loc,
|
|
376
|
+
range: attr.range,
|
|
341
377
|
};
|
|
378
|
+
state.literalFromIdentifier.add(lit);
|
|
379
|
+
return lit;
|
|
342
380
|
} else {
|
|
343
|
-
return visit(attr) as Expression;
|
|
381
|
+
return { ...(visit(attr) as Expression) };
|
|
344
382
|
}
|
|
345
383
|
},
|
|
346
384
|
);
|
|
@@ -348,6 +386,27 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
348
386
|
type: "Identifier",
|
|
349
387
|
name: `__gts_attrRet_${state.idCounter++}`,
|
|
350
388
|
};
|
|
389
|
+
const callee: MemberExpression = {
|
|
390
|
+
type: "MemberExpression",
|
|
391
|
+
object: lhsId,
|
|
392
|
+
property: name,
|
|
393
|
+
computed: name.type === "Literal",
|
|
394
|
+
optional: false,
|
|
395
|
+
};
|
|
396
|
+
if (name.range) {
|
|
397
|
+
// If the `obj.name` is not callable, the error squiggle begins from `obj` not `name`
|
|
398
|
+
// Add a diagnostic-only mapping from the `name` to a call starting from `obj.`
|
|
399
|
+
// This should be unique since each attribute decl generates a new obj name
|
|
400
|
+
state.extraMappings.push({
|
|
401
|
+
sourceOffset: name.range[0],
|
|
402
|
+
length: name.range[1] - name.range[0],
|
|
403
|
+
generatedNeedle: `${lhsId.name}${name.type === "Literal" ? `[` : `.`}`,
|
|
404
|
+
});
|
|
405
|
+
state.namedAttributeCalleeLParenRange.set(callee, {
|
|
406
|
+
start: name.range[1],
|
|
407
|
+
end: name.range[1] + 1,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
351
410
|
state.typingPendingStatements.push({
|
|
352
411
|
type: "VariableDeclaration",
|
|
353
412
|
kind: "const",
|
|
@@ -358,13 +417,7 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
358
417
|
init: {
|
|
359
418
|
type: "CallExpression",
|
|
360
419
|
optional: false,
|
|
361
|
-
callee
|
|
362
|
-
type: "MemberExpression",
|
|
363
|
-
object: lhsId,
|
|
364
|
-
property: name,
|
|
365
|
-
computed: name.type === "Literal",
|
|
366
|
-
optional: false,
|
|
367
|
-
},
|
|
420
|
+
callee,
|
|
368
421
|
arguments: positionals,
|
|
369
422
|
},
|
|
370
423
|
},
|
|
@@ -373,16 +426,13 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
373
426
|
if (body.namedAttributes) {
|
|
374
427
|
enterVMFromAttr(state, returnValue);
|
|
375
428
|
visit(body.namedAttributes);
|
|
376
|
-
exitVM(
|
|
377
|
-
state,
|
|
378
|
-
`${body.namedAttributes.loc?.start.line}:${body.namedAttributes.loc?.start.column}`,
|
|
379
|
-
);
|
|
429
|
+
exitVM(state, body.namedAttributes.range);
|
|
380
430
|
}
|
|
381
431
|
if (bindingName) {
|
|
382
432
|
const export_ = node.bindingAccessModifier !== "private";
|
|
383
433
|
const typingId: Identifier = {
|
|
384
434
|
type: "Identifier",
|
|
385
|
-
name: `
|
|
435
|
+
name: `__gts_binding_type_${state.externalizedBindings.length}`,
|
|
386
436
|
};
|
|
387
437
|
genBindingTyping(state, {
|
|
388
438
|
attrName,
|
|
@@ -415,11 +465,13 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
415
465
|
const attrName = JSON.stringify(state.ActionLit.value);
|
|
416
466
|
const { lhsId } = enterAttr(state, attrName);
|
|
417
467
|
const actionNotExistsReplacementStr = `${lhsId.name}[${attrName}]`;
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
468
|
+
if (node.directAction.range) {
|
|
469
|
+
state.extraMappings.push({
|
|
470
|
+
sourceOffset: node.directAction.range[0],
|
|
471
|
+
length: node.directAction.range[1] - node.directAction.range[0],
|
|
472
|
+
generatedNeedle: actionNotExistsReplacementStr,
|
|
473
|
+
});
|
|
474
|
+
}
|
|
423
475
|
const fn: ArrowFunctionExpression = {
|
|
424
476
|
type: "ArrowFunctionExpression",
|
|
425
477
|
params: state.shortcutFunctionParameters,
|
|
@@ -459,14 +511,4 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
459
511
|
return EMPTY;
|
|
460
512
|
},
|
|
461
513
|
...(commonGtsVisitor as Visitors<Node, TypingTranspileState>),
|
|
462
|
-
GTSShortcutArgumentExpression(node, { state, visit }): MemberExpression {
|
|
463
|
-
const lhs = { ...state.fnArgId };
|
|
464
|
-
return {
|
|
465
|
-
type: "MemberExpression",
|
|
466
|
-
object: lhs,
|
|
467
|
-
computed: false,
|
|
468
|
-
optional: false,
|
|
469
|
-
property: visit(node.property) as Identifier,
|
|
470
|
-
};
|
|
471
|
-
},
|
|
472
514
|
};
|
package/src/types.ts
CHANGED
|
@@ -6,7 +6,6 @@ import type * as AST from "estree";
|
|
|
6
6
|
type ForInit = boolean | "await";
|
|
7
7
|
|
|
8
8
|
declare module "estree" {
|
|
9
|
-
|
|
10
9
|
interface Identifier {
|
|
11
10
|
isDummy?: boolean;
|
|
12
11
|
}
|
|
@@ -24,17 +23,12 @@ declare module "estree" {
|
|
|
24
23
|
GTSShortcutFunctionExpression: GTSShortcutFunctionExpression;
|
|
25
24
|
GTSQueryExpression: GTSQueryExpression;
|
|
26
25
|
}
|
|
27
|
-
|
|
28
|
-
interface ImportDeclaration {
|
|
29
|
-
/** Emit inner diagnostics to the top-of-file */
|
|
30
|
-
diagnosticsOnTop?: boolean;
|
|
31
|
-
}
|
|
32
|
-
|
|
26
|
+
|
|
33
27
|
interface SimpleCallExpression {
|
|
34
|
-
|
|
28
|
+
lParenRange?: [number, number];
|
|
35
29
|
}
|
|
36
30
|
interface NewExpression {
|
|
37
|
-
|
|
31
|
+
lParenRange?: [number, number];
|
|
38
32
|
}
|
|
39
33
|
|
|
40
34
|
interface GTSDefineStatement extends BaseStatement {
|