@gi-tcg/gts-transpiler 0.2.0 → 0.3.1
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.js +363 -183
- package/package.json +4 -3
- package/src/config.ts +2 -2
- package/src/index.ts +8 -2
- package/src/parse/gts_plugin.ts +19 -12
- package/src/parse/index.ts +15 -5
- package/src/parse/loose_plugin.ts +2 -2
- package/src/parse/record_call_lparen_plugin.ts +85 -0
- package/src/transform/gts.ts +32 -45
- package/src/transform/volar/collect_tokens.ts +44 -18
- package/src/transform/volar/index.ts +16 -28
- package/src/transform/volar/mappings.ts +45 -26
- package/src/transform/volar/printer.ts +123 -0
- package/src/transform/volar/replacements.ts +89 -11
- package/src/transform/volar/walker.ts +52 -95
- package/src/types.ts +16 -4
|
@@ -192,19 +192,12 @@ export function getGeneratedPosition(
|
|
|
192
192
|
src_line: number,
|
|
193
193
|
src_column: number,
|
|
194
194
|
srcToGenMap: CodeToGeneratedMap,
|
|
195
|
-
): CodePosition
|
|
195
|
+
): CodePosition[] {
|
|
196
196
|
const key = `${src_line}:${src_column}`;
|
|
197
197
|
const positions = srcToGenMap.get(key);
|
|
198
198
|
|
|
199
|
-
if (!positions || positions.length === 0) {
|
|
200
|
-
// No mapping found in source map - this shouldn't happen since all tokens should have mappings
|
|
201
|
-
// throw new Error(
|
|
202
|
-
// `No source map entry for position "${src_line}:${src_column}"`
|
|
203
|
-
// );
|
|
204
|
-
}
|
|
205
|
-
|
|
206
199
|
// If multiple generated positions map to same source, return the first
|
|
207
|
-
return positions
|
|
200
|
+
return positions || [];
|
|
208
201
|
}
|
|
209
202
|
// Helper to create a line-to-offset lookup table
|
|
210
203
|
function createLineOffsets(content: string): number[] {
|
|
@@ -245,7 +238,7 @@ export function convertToVolarMappings(
|
|
|
245
238
|
source: string,
|
|
246
239
|
sourceMap: SourceMap,
|
|
247
240
|
tokens: LeafToken[],
|
|
248
|
-
|
|
241
|
+
extraMappings: Map<string, string>,
|
|
249
242
|
): CodeMapping[] {
|
|
250
243
|
const sourceLineOffsets = createLineOffsets(source);
|
|
251
244
|
const generatedLineOffsets = createLineOffsets(generated);
|
|
@@ -263,13 +256,18 @@ export function convertToVolarMappings(
|
|
|
263
256
|
token.loc.start.column,
|
|
264
257
|
sourceLineOffsets,
|
|
265
258
|
);
|
|
259
|
+
sourceStart += token.sourceStartOffset ?? 0;
|
|
260
|
+
|
|
266
261
|
const sourceEnd = locToOffset(
|
|
267
262
|
token.loc.end.line,
|
|
268
263
|
token.loc.end.column,
|
|
269
264
|
sourceLineOffsets,
|
|
270
265
|
);
|
|
266
|
+
|
|
271
267
|
let sourceLength = token.sourceLength ?? sourceEnd - sourceStart;
|
|
272
|
-
|
|
268
|
+
|
|
269
|
+
sourceLength += token.sourceLengthOffset ?? 0;
|
|
270
|
+
const [genLineCol] = getGeneratedPosition(
|
|
273
271
|
token.loc.start.line,
|
|
274
272
|
token.loc.start.column,
|
|
275
273
|
srcToGenMap,
|
|
@@ -283,19 +281,6 @@ export function convertToVolarMappings(
|
|
|
283
281
|
genLineCol.column,
|
|
284
282
|
generatedLineOffsets,
|
|
285
283
|
);
|
|
286
|
-
if (token.locationAdjustment) {
|
|
287
|
-
// maps verification back to the start of source
|
|
288
|
-
mappings.push({
|
|
289
|
-
sourceOffsets: [sourceStart],
|
|
290
|
-
generatedOffsets: [genStart],
|
|
291
|
-
lengths: [0],
|
|
292
|
-
generatedLengths: [token.locationAdjustment.generatedLength],
|
|
293
|
-
data: {
|
|
294
|
-
verification: true,
|
|
295
|
-
},
|
|
296
|
-
});
|
|
297
|
-
genStart += token.locationAdjustment.startOffset;
|
|
298
|
-
}
|
|
299
284
|
if (token.isDummy) {
|
|
300
285
|
// A dummy token might be generated for a missing property / argument.
|
|
301
286
|
// Notice that when facing this scenario, the parser tries to 'defer' and step through
|
|
@@ -308,9 +293,25 @@ export function convertToVolarMappings(
|
|
|
308
293
|
sourceLength++;
|
|
309
294
|
}
|
|
310
295
|
}
|
|
311
|
-
|
|
312
296
|
const generatedLength = token.generatedLength ?? sourceLength;
|
|
313
297
|
|
|
298
|
+
if (
|
|
299
|
+
typeof token.generatedStartOffset === "number" &&
|
|
300
|
+
token.generatedStartOffset > 0
|
|
301
|
+
) {
|
|
302
|
+
// maps verification back to the start of source
|
|
303
|
+
mappings.push({
|
|
304
|
+
sourceOffsets: [sourceStart],
|
|
305
|
+
generatedOffsets: [genStart],
|
|
306
|
+
lengths: [0],
|
|
307
|
+
generatedLengths: [generatedLength],
|
|
308
|
+
data: {
|
|
309
|
+
verification: true,
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
genStart += token.generatedStartOffset;
|
|
313
|
+
}
|
|
314
|
+
|
|
314
315
|
mappings.push({
|
|
315
316
|
sourceOffsets: [sourceStart],
|
|
316
317
|
generatedOffsets: [genStart],
|
|
@@ -320,7 +321,25 @@ export function convertToVolarMappings(
|
|
|
320
321
|
});
|
|
321
322
|
}
|
|
322
323
|
|
|
323
|
-
|
|
324
|
+
// Handle extra mappings that purely generated and wants a diagnostic
|
|
325
|
+
// that appears on the top of file.
|
|
326
|
+
// We'd add these mappings at walker that they will have a `loc` to `1:0`
|
|
327
|
+
// so find them by looking up a source position of `1:0` will work
|
|
328
|
+
const locMapsToTop = getGeneratedPosition(1, 0, srcToGenMap);
|
|
329
|
+
for (const loc of locMapsToTop) {
|
|
330
|
+
const offset = locToOffset(loc.line, loc.column, generatedLineOffsets);
|
|
331
|
+
mappings.push({
|
|
332
|
+
sourceOffsets: [0],
|
|
333
|
+
generatedOffsets: [offset],
|
|
334
|
+
lengths: [0],
|
|
335
|
+
generatedLengths: [0],
|
|
336
|
+
data: {
|
|
337
|
+
verification: true,
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
for (const [loc, codeSnippet] of extraMappings) {
|
|
324
343
|
const generatedStart = generated.indexOf(codeSnippet);
|
|
325
344
|
if (generatedStart === -1) {
|
|
326
345
|
continue;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import tsPrinter from "esrap/languages/ts";
|
|
2
|
+
import type { AST } from "../../types";
|
|
3
|
+
import type { Context, Visitors } from "esrap";
|
|
4
|
+
import type { Node, SimpleCallExpression } from "estree";
|
|
5
|
+
|
|
6
|
+
const printer = tsPrinter({
|
|
7
|
+
getLeadingComments: (node) => (node as AST.Node).leadingComments,
|
|
8
|
+
getTrailingComments: (node) => (node as AST.Node).trailingComments,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
// Make the print of dummy identifier print nothing.
|
|
12
|
+
// Exception: if GTS attribute list's last argument is dummy, e.g.
|
|
13
|
+
// foo bar, ;
|
|
14
|
+
// ^~ here
|
|
15
|
+
// Then the printed JS will be `foo(bar, )` which WILL NOT be syntax error in ES6.
|
|
16
|
+
// So we mark the lastArg manually and print an additional comma
|
|
17
|
+
// for this dummy identifier, i.e. `foo(bar,,)` and TypeScript will recognize the error.
|
|
18
|
+
|
|
19
|
+
const prevIdentifier = printer.Identifier!;
|
|
20
|
+
printer.Identifier = function (node, context) {
|
|
21
|
+
if (node.isDummy) {
|
|
22
|
+
const text = Reflect.get(node, "lastArg") ? "," : "";
|
|
23
|
+
context.write(text, node);
|
|
24
|
+
} else {
|
|
25
|
+
prevIdentifier(node, context);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const prevCallNewExpression = printer.CallExpression!;
|
|
30
|
+
const newCallNewExpression: typeof prevCallNewExpression = function (
|
|
31
|
+
node: SimpleCallExpression,
|
|
32
|
+
context,
|
|
33
|
+
) {
|
|
34
|
+
const lastArg = node.arguments.at(-1);
|
|
35
|
+
if (lastArg) {
|
|
36
|
+
Reflect.set(lastArg, "lastArg", true);
|
|
37
|
+
}
|
|
38
|
+
if (!node.lParenLoc) {
|
|
39
|
+
return prevCallNewExpression(node, context);
|
|
40
|
+
}
|
|
41
|
+
let hasDeferredWrite = false;
|
|
42
|
+
let interceptionDone = false;
|
|
43
|
+
const lParenFakeNode = {
|
|
44
|
+
loc: node.lParenLoc!,
|
|
45
|
+
} as AST.Node;
|
|
46
|
+
|
|
47
|
+
// Map the print of `(` with the fake lParen node.
|
|
48
|
+
// The print of `(` can be happened in two area:
|
|
49
|
+
// 1. The wrapped parenthesis towards callee, which follows a `context.visit` to the callee;
|
|
50
|
+
// 2. The argument list (what we should map), which follows a `context.append` call
|
|
51
|
+
// so we defer the write of `(` to the next call of `context.visit|append`.
|
|
52
|
+
// If it is `context.append`, then make `context.write("(")` happens with our fake node
|
|
53
|
+
// and mapping will be added to the final code-mapping. Otherwise, keep original write.
|
|
54
|
+
|
|
55
|
+
const patchedWrite = (text: string, node?: AST.Node) => {
|
|
56
|
+
if (text === "(") {
|
|
57
|
+
hasDeferredWrite = true;
|
|
58
|
+
} else {
|
|
59
|
+
context.write(text, node);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const patchedVisit = (node: AST.Node) => {
|
|
63
|
+
if (hasDeferredWrite) {
|
|
64
|
+
context.write("(");
|
|
65
|
+
}
|
|
66
|
+
return context.visit(node);
|
|
67
|
+
};
|
|
68
|
+
const patchedAppend = (subcontext: Context) => {
|
|
69
|
+
if (hasDeferredWrite) {
|
|
70
|
+
context.write("(", lParenFakeNode);
|
|
71
|
+
// console.log("Inserted fake [LPAREN] for node at ", lParenFakeNode.loc);
|
|
72
|
+
interceptionDone = true;
|
|
73
|
+
}
|
|
74
|
+
return context.append(subcontext);
|
|
75
|
+
};
|
|
76
|
+
const proxiedContext = new Proxy(context, {
|
|
77
|
+
get(target, prop) {
|
|
78
|
+
if (!interceptionDone) {
|
|
79
|
+
if (prop === "write") {
|
|
80
|
+
return patchedWrite;
|
|
81
|
+
}
|
|
82
|
+
if (prop === "visit") {
|
|
83
|
+
return patchedVisit;
|
|
84
|
+
}
|
|
85
|
+
if (prop === "append") {
|
|
86
|
+
return patchedAppend;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const value = Reflect.get(target, prop);
|
|
90
|
+
if (typeof value === "function") {
|
|
91
|
+
return value.bind(target);
|
|
92
|
+
}
|
|
93
|
+
return value;
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
return prevCallNewExpression(node, proxiedContext);
|
|
97
|
+
};
|
|
98
|
+
printer.CallExpression = newCallNewExpression;
|
|
99
|
+
printer.NewExpression = newCallNewExpression;
|
|
100
|
+
|
|
101
|
+
// Handle node with `diagnosticsOnTop`. These nodes and their children will be
|
|
102
|
+
// printed with an extra `loc` that point to the beginning of the source file,
|
|
103
|
+
// so the source-mapping will include them with a 0:1 -> generated position entry.
|
|
104
|
+
|
|
105
|
+
const prevWildcard = printer._!;
|
|
106
|
+
printer._ = (node: Node, context, visit) => {
|
|
107
|
+
const contextProto = Object.getPrototypeOf(context);
|
|
108
|
+
const prevContextWrite: typeof context.write = contextProto.write;
|
|
109
|
+
if ("diagnosticsOnTop" in node && node.diagnosticsOnTop) {
|
|
110
|
+
contextProto.write = function (text: string, node?: AST.Node) {
|
|
111
|
+
node ??= {} as AST.Node;
|
|
112
|
+
node.loc ??= {
|
|
113
|
+
start: { line: 1, column: 0 },
|
|
114
|
+
end: { line: 1, column: 0 },
|
|
115
|
+
};
|
|
116
|
+
return prevContextWrite.call(this, text, node);
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
prevWildcard(node, context, visit);
|
|
120
|
+
contextProto.write = prevContextWrite;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const patchedPrinter: Visitors<AST.Node> = printer;
|
|
@@ -2,6 +2,9 @@ import type { ExpressionStatement } from "estree";
|
|
|
2
2
|
import type { TypingTranspileState } from "./walker";
|
|
3
3
|
|
|
4
4
|
type ReplacementPayload =
|
|
5
|
+
| {
|
|
6
|
+
type: "preface";
|
|
7
|
+
}
|
|
5
8
|
| {
|
|
6
9
|
type: "enterVMFromRoot";
|
|
7
10
|
vm: string;
|
|
@@ -27,6 +30,7 @@ type ReplacementPayload =
|
|
|
27
30
|
defType: string;
|
|
28
31
|
metaType: string;
|
|
29
32
|
lhs: string;
|
|
33
|
+
attrName: string;
|
|
30
34
|
}
|
|
31
35
|
| {
|
|
32
36
|
type: "createBindingTyping";
|
|
@@ -76,32 +80,106 @@ export function applyReplacements(
|
|
|
76
80
|
"\\b" + state.replacementTag.name + "`(.*?)`",
|
|
77
81
|
"gm",
|
|
78
82
|
);
|
|
79
|
-
const {
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
const { NamedDefinitionLit, MetaLit } = state;
|
|
84
|
+
const NamedDefinition = JSON.stringify(NamedDefinitionLit.value);
|
|
85
|
+
const Meta = JSON.stringify(MetaLit.value);
|
|
86
|
+
const oneLine = (
|
|
87
|
+
strings: TemplateStringsArray,
|
|
88
|
+
...values: Array<string | number | boolean>
|
|
89
|
+
): string =>
|
|
90
|
+
strings
|
|
91
|
+
.reduce(
|
|
92
|
+
(result, chunk, index) =>
|
|
93
|
+
result + chunk + (index < values.length ? values[index] : ""),
|
|
94
|
+
"",
|
|
95
|
+
)
|
|
96
|
+
.replace(/\n[ \t]*/g, " ")
|
|
97
|
+
.trim();
|
|
82
98
|
// All replacement should be written in one line to avoid messing up source map
|
|
83
99
|
return code.replace(replacementRegex, (_, rawPayload) => {
|
|
84
100
|
const payload: ReplacementPayload = JSON.parse(rawPayload);
|
|
85
|
-
if (payload.type === "
|
|
86
|
-
return `
|
|
101
|
+
if (payload.type === "preface") {
|
|
102
|
+
return oneLine`
|
|
103
|
+
namespace ${state.utilNsId.name} {
|
|
104
|
+
export type UniqueKeyProbSegment = "__gts_unique_prob_seg__";
|
|
105
|
+
export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
|
|
106
|
+
}
|
|
107
|
+
`;
|
|
108
|
+
} else if (payload.type === "enterVMFromRoot") {
|
|
109
|
+
return oneLine`
|
|
110
|
+
type ${payload.defType} = (typeof ${payload.vm})[${NamedDefinition}];
|
|
111
|
+
type ${payload.metaType} = ${payload.defType}[${Meta}];
|
|
112
|
+
`;
|
|
87
113
|
} else if (payload.type === "enterVMFromAttr") {
|
|
88
|
-
return `
|
|
114
|
+
return oneLine`
|
|
115
|
+
type ${payload.defType} = ${payload.returnType} extends { namedDefinition: infer Def } ? Def : { ${Meta}: unknown };
|
|
116
|
+
type ${payload.metaType} = ${payload.defType}[${Meta}];
|
|
117
|
+
`;
|
|
89
118
|
} else if (payload.type === "exitVM") {
|
|
90
119
|
const lhs = `${payload.finalMetaType}_lhs`;
|
|
91
120
|
const requiredAttrsNs = `${payload.finalMetaType}_rans`;
|
|
92
121
|
const collectedAttrsExpr = `${payload.collectedAttrs.join(" | ")}`;
|
|
93
122
|
const needleString = `"${requiredAttrsNs}_NeedleString" as any as "required attributes are missing"`;
|
|
94
123
|
if (payload.errorLoc) {
|
|
95
|
-
state.
|
|
124
|
+
state.extraMappings.set(payload.errorLoc, needleString);
|
|
96
125
|
}
|
|
97
|
-
return `
|
|
126
|
+
return oneLine`
|
|
127
|
+
type ${payload.finalMetaType} = ${payload.metaType};
|
|
128
|
+
let ${lhs}!: { ${Meta}: ${payload.metaType} } & Omit<${payload.defType}, ${Meta}>;
|
|
129
|
+
type ${lhs} = typeof ${lhs};
|
|
130
|
+
namespace ${requiredAttrsNs} {
|
|
131
|
+
export type Collected = ${collectedAttrsExpr};
|
|
132
|
+
export type Expected = { [K in keyof ${payload.defType}]: ${lhs}[K] extends { required(this: ${lhs}): true } ? K : never }[keyof ${payload.defType}];
|
|
133
|
+
};
|
|
134
|
+
((_: ${requiredAttrsNs}.Expected extends ${requiredAttrsNs}.Collected ? string : ${requiredAttrsNs}.Expected) => 0)(${needleString});
|
|
135
|
+
`;
|
|
98
136
|
} else if (payload.type === "enterAttr") {
|
|
99
|
-
|
|
137
|
+
const uniqueKeyLhs = `${payload.lhs}_uniqueKey_lhs`;
|
|
138
|
+
const uniqueKey = `${payload.lhs}_uniqueKey`;
|
|
139
|
+
const uniqueKeyForThis = `${payload.lhs}_uniqueKeyFor_${payload.lhs}`;
|
|
140
|
+
const uniqueKeyHelperIntf = `${payload.defType}_uniqueKeyProbeHelper`;
|
|
141
|
+
const omittedKeys = `${payload.lhs}_omittedKeys`;
|
|
142
|
+
return oneLine`
|
|
143
|
+
type ${uniqueKeyLhs} = {
|
|
144
|
+
${Meta}: ${payload.metaType};
|
|
145
|
+
uniqueKey: ${payload.defType} extends { [${payload.attrName}]: { uniqueKey: infer UniqueKey } } ? UniqueKey : () => 0;
|
|
146
|
+
|
|
147
|
+
let ${uniqueKeyLhs}!: ${uniqueKeyLhs};
|
|
148
|
+
let ${uniqueKey} = ${uniqueKeyLhs}.uniqueKey();
|
|
149
|
+
type ${uniqueKey} = typeof ${uniqueKey};
|
|
150
|
+
let ${uniqueKeyForThis}!: \`\${${uniqueKey}}\${${state.utilNsId.name}.UniqueKeyProbSegment}${payload.lhs}\`;
|
|
151
|
+
interface ${uniqueKeyHelperIntf} {
|
|
152
|
+
[${uniqueKeyForThis}]: 1;
|
|
153
|
+
}
|
|
154
|
+
type ${omittedKeys} = ${Meta} | (
|
|
155
|
+
${uniqueKey} extends 0
|
|
156
|
+
? never /* no unique requirement */
|
|
157
|
+
: string extends keyof ${uniqueKeyHelperIntf}
|
|
158
|
+
? keyof ${payload.defType} /* too loose, disable all */
|
|
159
|
+
: ${state.utilNsId.name}.UnionToIntersection<
|
|
160
|
+
keyof ${uniqueKeyHelperIntf} & \`\${${uniqueKey}}\${${state.utilNsId.name}.UniqueKeyProbSegment}\${string}\`
|
|
161
|
+
> extends never
|
|
162
|
+
? ${payload.attrName} /* have duplicate, disable this */
|
|
163
|
+
: never
|
|
164
|
+
);
|
|
165
|
+
let ${payload.lhs}!: { ${Meta}: ${payload.metaType} } & Omit<${payload.defType}, ${omittedKeys}>;
|
|
166
|
+
`;
|
|
100
167
|
} else if (payload.type === "createBindingTyping") {
|
|
101
168
|
const typingIdLhs = `${payload.typingId}_lhs`;
|
|
102
|
-
return `
|
|
169
|
+
return oneLine`
|
|
170
|
+
type ${typingIdLhs} = {
|
|
171
|
+
${Meta}: ${payload.finalMetaType};
|
|
172
|
+
as: ${payload.defType} extends { [${payload.attrName}]: { as: infer As } } ? As : unknown;
|
|
173
|
+
};
|
|
174
|
+
let ${typingIdLhs}!: ${typingIdLhs};
|
|
175
|
+
let ${payload.typingId} = ${typingIdLhs}.as();
|
|
176
|
+
type ${payload.typingId} = typeof ${payload.typingId};
|
|
177
|
+
`;
|
|
103
178
|
} else if (payload.type === "exitAttr") {
|
|
104
|
-
return `
|
|
179
|
+
return oneLine`
|
|
180
|
+
type ${payload.returnType} = typeof ${payload.returnType};
|
|
181
|
+
type ${payload.newMetaType} = ${payload.returnType} extends { rewriteMeta: infer NewMeta extends {} } ? NewMeta : ${payload.oldMetaType}
|
|
182
|
+
`;
|
|
105
183
|
} else {
|
|
106
184
|
return "";
|
|
107
185
|
}
|
|
@@ -38,10 +38,9 @@ export interface TypingTranspileState extends TranspileState {
|
|
|
38
38
|
externalizedBindings: ExternalizedTypedBinding[];
|
|
39
39
|
idCounter: number;
|
|
40
40
|
rootVmId: Identifier;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
};
|
|
41
|
+
utilNsId: Identifier;
|
|
42
|
+
MetaLit: Literal;
|
|
43
|
+
NamedDefinitionLit: Literal;
|
|
45
44
|
defineLeadingComments: Comment[] | undefined;
|
|
46
45
|
// type of current VM's definition
|
|
47
46
|
vmDefTypeIdStack: Identifier[];
|
|
@@ -53,11 +52,10 @@ export interface TypingTranspileState extends TranspileState {
|
|
|
53
52
|
finalMetaTypeIdStack: Identifier[];
|
|
54
53
|
// `obj` of `obj.attr(...)`
|
|
55
54
|
// attrLhsIdStack: Identifier[];
|
|
56
|
-
prefaceInserted: boolean;
|
|
57
55
|
/** Pending statements to be inserted to the top-level */
|
|
58
56
|
typingPendingStatements: Statement[];
|
|
59
57
|
replacementTag: Identifier;
|
|
60
|
-
|
|
58
|
+
extraMappings: Map<string, string>;
|
|
61
59
|
}
|
|
62
60
|
|
|
63
61
|
const EMPTY: EmptyStatement = { type: "EmptyStatement" };
|
|
@@ -74,68 +72,7 @@ const ANY_INIT = {
|
|
|
74
72
|
typeAnnotation: ANY,
|
|
75
73
|
} as {} as Expression;
|
|
76
74
|
|
|
77
|
-
const emitPreface = (state: TypingTranspileState) => {
|
|
78
|
-
if (state.prefaceInserted) {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
const symbolsLhs = {
|
|
82
|
-
type: "TSQualifiedName",
|
|
83
|
-
left: { type: "Identifier", name: state.rootVmId.name },
|
|
84
|
-
right: { type: "Identifier", name: "_symbols" },
|
|
85
|
-
};
|
|
86
|
-
for (const symbolName of [
|
|
87
|
-
"Meta",
|
|
88
|
-
"Action",
|
|
89
|
-
"NamedDefinition",
|
|
90
|
-
"Prelude",
|
|
91
|
-
] as const) {
|
|
92
|
-
const init = {
|
|
93
|
-
type: "TSTypeQuery",
|
|
94
|
-
exprName: {
|
|
95
|
-
type: "TSQualifiedName",
|
|
96
|
-
left: symbolsLhs,
|
|
97
|
-
right: { type: "Identifier", name: symbolName },
|
|
98
|
-
},
|
|
99
|
-
};
|
|
100
|
-
const symbolId =
|
|
101
|
-
symbolName === "Action"
|
|
102
|
-
? state.ActionId
|
|
103
|
-
: symbolName === "Prelude"
|
|
104
|
-
? state.preludeSymbolId
|
|
105
|
-
: state.symbolsId[symbolName];
|
|
106
|
-
state.typingPendingStatements.push(
|
|
107
|
-
{
|
|
108
|
-
type: "TSTypeAliasDeclaration",
|
|
109
|
-
id: symbolId,
|
|
110
|
-
typeAnnotation: init,
|
|
111
|
-
} as {} as VariableDeclaration,
|
|
112
|
-
{
|
|
113
|
-
type: "VariableDeclaration",
|
|
114
|
-
kind: "const",
|
|
115
|
-
declarations: [
|
|
116
|
-
{
|
|
117
|
-
type: "VariableDeclarator",
|
|
118
|
-
id: {
|
|
119
|
-
...symbolId,
|
|
120
|
-
typeAnnotation: {
|
|
121
|
-
type: "TSTypeAnnotation",
|
|
122
|
-
typeAnnotation: {
|
|
123
|
-
type: "TSTypeReference",
|
|
124
|
-
typeName: symbolId,
|
|
125
|
-
},
|
|
126
|
-
},
|
|
127
|
-
} as Identifier,
|
|
128
|
-
init: ANY_INIT,
|
|
129
|
-
},
|
|
130
|
-
],
|
|
131
|
-
} as VariableDeclaration
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
state.prefaceInserted = true;
|
|
135
|
-
};
|
|
136
|
-
|
|
137
75
|
const enterVMFromRoot = (state: TypingTranspileState) => {
|
|
138
|
-
emitPreface(state);
|
|
139
76
|
let defTypeId: Identifier = {
|
|
140
77
|
type: "Identifier",
|
|
141
78
|
name: `__gts_rootVmDefType_${state.idCounter++}`,
|
|
@@ -154,7 +91,7 @@ const enterVMFromRoot = (state: TypingTranspileState) => {
|
|
|
154
91
|
vm: state.rootVmId.name,
|
|
155
92
|
defType: defTypeId.name,
|
|
156
93
|
metaType: metaTypeId.name,
|
|
157
|
-
})
|
|
94
|
+
}),
|
|
158
95
|
);
|
|
159
96
|
state.vmDefTypeIdStack.push(defTypeId);
|
|
160
97
|
state.metaTypeIdStack.push(metaTypeId);
|
|
@@ -163,7 +100,7 @@ const enterVMFromRoot = (state: TypingTranspileState) => {
|
|
|
163
100
|
};
|
|
164
101
|
const enterVMFromAttr = (
|
|
165
102
|
state: TypingTranspileState,
|
|
166
|
-
returningId: Identifier
|
|
103
|
+
returningId: Identifier,
|
|
167
104
|
) => {
|
|
168
105
|
const defTypeId: Identifier = {
|
|
169
106
|
type: "Identifier",
|
|
@@ -183,7 +120,7 @@ const enterVMFromAttr = (
|
|
|
183
120
|
returnType: returningId.name,
|
|
184
121
|
defType: defTypeId.name,
|
|
185
122
|
metaType: metaTypeId.name,
|
|
186
|
-
})
|
|
123
|
+
}),
|
|
187
124
|
);
|
|
188
125
|
state.vmDefTypeIdStack.push(defTypeId);
|
|
189
126
|
state.metaTypeIdStack.push(metaTypeId);
|
|
@@ -204,13 +141,13 @@ const exitVM = (state: TypingTranspileState, errorLoc?: string) => {
|
|
|
204
141
|
finalMetaType: finalMetaId.name,
|
|
205
142
|
collectedAttrs: collectedAttrNames,
|
|
206
143
|
errorLoc,
|
|
207
|
-
})
|
|
144
|
+
}),
|
|
208
145
|
);
|
|
209
146
|
};
|
|
210
147
|
|
|
211
148
|
const enterAttr = (
|
|
212
149
|
state: TypingTranspileState,
|
|
213
|
-
attrName: string
|
|
150
|
+
attrName: string,
|
|
214
151
|
): { lhsId: Identifier } => {
|
|
215
152
|
const defTypeId = state.vmDefTypeIdStack.at(-1);
|
|
216
153
|
const metaTypeId = state.metaTypeIdStack.at(-1);
|
|
@@ -229,7 +166,8 @@ const enterAttr = (
|
|
|
229
166
|
defType: defTypeId.name,
|
|
230
167
|
metaType: metaTypeId.name,
|
|
231
168
|
lhs: lhsId.name,
|
|
232
|
-
|
|
169
|
+
attrName,
|
|
170
|
+
}),
|
|
233
171
|
);
|
|
234
172
|
return { lhsId: lhsId };
|
|
235
173
|
};
|
|
@@ -239,7 +177,7 @@ const genBindingTyping = (
|
|
|
239
177
|
info: {
|
|
240
178
|
attrName: string;
|
|
241
179
|
typingId: Identifier;
|
|
242
|
-
}
|
|
180
|
+
},
|
|
243
181
|
) => {
|
|
244
182
|
const finalMetaId = state.finalMetaTypeIdStack.at(-1);
|
|
245
183
|
const defTypeId = state.vmDefTypeIdStack.at(-1);
|
|
@@ -253,7 +191,7 @@ const genBindingTyping = (
|
|
|
253
191
|
defType: defTypeId.name,
|
|
254
192
|
attrName: info.attrName,
|
|
255
193
|
typingId: info.typingId.name,
|
|
256
|
-
})
|
|
194
|
+
}),
|
|
257
195
|
);
|
|
258
196
|
};
|
|
259
197
|
|
|
@@ -274,7 +212,7 @@ const exitAttr = (state: TypingTranspileState, returningId: Identifier) => {
|
|
|
274
212
|
oldMetaType: oldMetaTypeId.name,
|
|
275
213
|
newMetaType: newMetaTypeId.name,
|
|
276
214
|
returnType: returningId.name,
|
|
277
|
-
})
|
|
215
|
+
}),
|
|
278
216
|
);
|
|
279
217
|
};
|
|
280
218
|
|
|
@@ -311,7 +249,6 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
311
249
|
},
|
|
312
250
|
} as VariableDeclarator,
|
|
313
251
|
],
|
|
314
|
-
leadingComments: extBinding.leadingComments,
|
|
315
252
|
};
|
|
316
253
|
if (extBinding.export) {
|
|
317
254
|
body.unshift({
|
|
@@ -320,43 +257,50 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
320
257
|
specifiers: [],
|
|
321
258
|
source: null,
|
|
322
259
|
attributes: [],
|
|
260
|
+
leadingComments: extBinding.leadingComments,
|
|
323
261
|
} as ExportNamedDeclaration);
|
|
324
262
|
} else {
|
|
263
|
+
varDecl.leadingComments = extBinding.leadingComments;
|
|
325
264
|
body.unshift(varDecl);
|
|
326
265
|
}
|
|
327
266
|
}
|
|
328
|
-
if (state.
|
|
267
|
+
if (state.hasQueryExpressions) {
|
|
329
268
|
body.unshift({
|
|
330
269
|
type: "ImportDeclaration",
|
|
270
|
+
diagnosticsOnTop: true,
|
|
331
271
|
specifiers: [
|
|
332
272
|
{
|
|
333
273
|
type: "ImportDefaultSpecifier",
|
|
334
|
-
local: state.
|
|
274
|
+
local: state.queryFnId,
|
|
335
275
|
},
|
|
336
276
|
],
|
|
337
277
|
source: {
|
|
338
278
|
type: "Literal",
|
|
339
|
-
value: `${state.providerImportSource}/
|
|
279
|
+
value: `${state.providerImportSource}/query`,
|
|
340
280
|
},
|
|
341
281
|
attributes: [],
|
|
342
282
|
});
|
|
343
283
|
}
|
|
344
|
-
|
|
345
|
-
|
|
284
|
+
body.unshift(
|
|
285
|
+
{
|
|
346
286
|
type: "ImportDeclaration",
|
|
287
|
+
diagnosticsOnTop: true,
|
|
347
288
|
specifiers: [
|
|
348
289
|
{
|
|
349
290
|
type: "ImportDefaultSpecifier",
|
|
350
|
-
local: state.
|
|
291
|
+
local: state.rootVmId,
|
|
351
292
|
},
|
|
352
293
|
],
|
|
353
294
|
source: {
|
|
354
295
|
type: "Literal",
|
|
355
|
-
value: `${state.providerImportSource}/
|
|
296
|
+
value: `${state.providerImportSource}/vm`,
|
|
356
297
|
},
|
|
357
298
|
attributes: [],
|
|
358
|
-
}
|
|
359
|
-
|
|
299
|
+
},
|
|
300
|
+
createReplacementHolder(state, {
|
|
301
|
+
type: "preface",
|
|
302
|
+
}),
|
|
303
|
+
);
|
|
360
304
|
return {
|
|
361
305
|
...node,
|
|
362
306
|
body,
|
|
@@ -371,18 +315,24 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
371
315
|
GTSNamedAttributeDefinition(node, { visit, state }) {
|
|
372
316
|
const { name, body, bindingName } = node;
|
|
373
317
|
const attrName = JSON.stringify(
|
|
374
|
-
name.type === "Literal" ? String(name.value) : name.name
|
|
318
|
+
name.type === "Literal" ? String(name.value) : name.name,
|
|
375
319
|
);
|
|
320
|
+
const attributeNameToken = state.leafTokens.find((t) => t.loc === name.loc);
|
|
321
|
+
if (attributeNameToken) {
|
|
322
|
+
attributeNameToken.sourceLengthOffset = 1;
|
|
323
|
+
}
|
|
376
324
|
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
|
+
);
|
|
377
329
|
const positionals = body.positionalAttributes.attributes.map(
|
|
378
330
|
(attr): Expression => {
|
|
379
331
|
if (attr.type === "Identifier" && /^[a-z_]/.test(attr.name)) {
|
|
380
332
|
const token = state.leafTokens.find((t) => t.loc === attr.loc);
|
|
381
333
|
if (token) {
|
|
382
|
-
token.
|
|
383
|
-
|
|
384
|
-
generatedLength: attr.name.length + 2, // quotation mark
|
|
385
|
-
};
|
|
334
|
+
token.generatedStartOffset = 1;
|
|
335
|
+
token.generatedLength = attr.name.length + 2; // quotation mark
|
|
386
336
|
}
|
|
387
337
|
return {
|
|
388
338
|
type: "Literal",
|
|
@@ -392,7 +342,7 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
392
342
|
} else {
|
|
393
343
|
return visit(attr) as Expression;
|
|
394
344
|
}
|
|
395
|
-
}
|
|
345
|
+
},
|
|
396
346
|
);
|
|
397
347
|
const returnValue: Identifier = {
|
|
398
348
|
type: "Identifier",
|
|
@@ -425,7 +375,7 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
425
375
|
visit(body.namedAttributes);
|
|
426
376
|
exitVM(
|
|
427
377
|
state,
|
|
428
|
-
`${body.namedAttributes.loc?.start.line}:${body.namedAttributes.loc?.start.column}
|
|
378
|
+
`${body.namedAttributes.loc?.start.line}:${body.namedAttributes.loc?.start.column}`,
|
|
429
379
|
);
|
|
430
380
|
}
|
|
431
381
|
if (bindingName) {
|
|
@@ -462,7 +412,14 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
462
412
|
visit(attr);
|
|
463
413
|
}
|
|
464
414
|
if (node.directAction) {
|
|
465
|
-
const
|
|
415
|
+
const attrName = JSON.stringify(state.ActionLit.value);
|
|
416
|
+
const { lhsId } = enterAttr(state, attrName);
|
|
417
|
+
const actionNotExistsReplacementStr = `${lhsId.name}[${attrName}]`;
|
|
418
|
+
const actionNotExistsErrorLoc = `${node.directAction.loc?.start.line}:${node.directAction.loc?.start.column}`;
|
|
419
|
+
state.extraMappings.set(
|
|
420
|
+
actionNotExistsErrorLoc,
|
|
421
|
+
actionNotExistsReplacementStr,
|
|
422
|
+
);
|
|
466
423
|
const fn: ArrowFunctionExpression = {
|
|
467
424
|
type: "ArrowFunctionExpression",
|
|
468
425
|
params: state.shortcutFunctionParameters,
|
|
@@ -489,7 +446,7 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
489
446
|
callee: {
|
|
490
447
|
type: "MemberExpression",
|
|
491
448
|
object: lhsId,
|
|
492
|
-
property: state.
|
|
449
|
+
property: state.ActionLit,
|
|
493
450
|
computed: true,
|
|
494
451
|
optional: false,
|
|
495
452
|
},
|