@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gi-tcg/gts-transpiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"repository": "https://github.com/piovium/gts.git",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@jridgewell/sourcemap-codec": "^1.5.5",
|
|
21
21
|
"@sveltejs/acorn-typescript": "^1.0.8",
|
|
22
|
-
"acorn": "
|
|
23
|
-
"esrap": "
|
|
22
|
+
"acorn": "8.15.0",
|
|
23
|
+
"esrap": "2.2.1",
|
|
24
24
|
"magic-string": "^0.30.21",
|
|
25
|
+
"path-browserify-esm": "^1.0.6",
|
|
25
26
|
"zimmerframe": "^1.1.4"
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
package/src/config.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
1
|
import type { TranspileOption } from "./transform/gts";
|
|
2
|
+
import path from "path-browserify-esm";
|
|
3
3
|
|
|
4
4
|
export interface GtsConfig extends TranspileOption {}
|
|
5
5
|
|
|
@@ -106,7 +106,7 @@ function* findNearestPackageConfig(
|
|
|
106
106
|
): Generator<string | Promise<string>, GtsConfig, string> {
|
|
107
107
|
let currentDir = startDir;
|
|
108
108
|
while (true) {
|
|
109
|
-
const pkgPath = path.
|
|
109
|
+
const pkgPath = path.resolve(currentDir, "package.json");
|
|
110
110
|
const config = yield* readPackageConfig(readFileFn, pkgPath);
|
|
111
111
|
if (config) {
|
|
112
112
|
return config;
|
package/src/index.ts
CHANGED
|
@@ -25,7 +25,9 @@ export function transpileForVolar(
|
|
|
25
25
|
filename: string,
|
|
26
26
|
option: TranspileOption,
|
|
27
27
|
): VolarMappingResult {
|
|
28
|
-
const ast = parseLoose(source
|
|
28
|
+
const ast = parseLoose(source, {
|
|
29
|
+
recordCallLParens: true,
|
|
30
|
+
});
|
|
29
31
|
return transformForVolar(ast, option, {
|
|
30
32
|
content: source,
|
|
31
33
|
filename,
|
|
@@ -33,4 +35,8 @@ export function transpileForVolar(
|
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
export type { TranspileOption, TranspileResult, VolarMappingResult };
|
|
36
|
-
export {
|
|
38
|
+
export {
|
|
39
|
+
resolveGtsConfig,
|
|
40
|
+
resolveGtsConfigSync,
|
|
41
|
+
type GtsConfig,
|
|
42
|
+
} from "./config";
|
package/src/parse/gts_plugin.ts
CHANGED
|
@@ -75,7 +75,7 @@ export interface GtsPluginOption {
|
|
|
75
75
|
|
|
76
76
|
export function gtsPlugin(options: GtsPluginOption = {}) {
|
|
77
77
|
return function gtsPluginTransformer(
|
|
78
|
-
Parser: typeof ParserClass
|
|
78
|
+
Parser: typeof ParserClass,
|
|
79
79
|
): typeof ParserClass {
|
|
80
80
|
const skipWhiteSpace = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g;
|
|
81
81
|
const lineBreak = /\r\n?|\n|\u2028|\u2029/;
|
|
@@ -95,7 +95,7 @@ export function gtsPlugin(options: GtsPluginOption = {}) {
|
|
|
95
95
|
override parseStatement(
|
|
96
96
|
context?: string | null,
|
|
97
97
|
topLevel?: boolean,
|
|
98
|
-
exports?: AST.ExportSpecifier
|
|
98
|
+
exports?: AST.ExportSpecifier,
|
|
99
99
|
) {
|
|
100
100
|
if (topLevel && this.gts_isDefineStatement()) {
|
|
101
101
|
const node = this.startNode() as AST.GTSDefineStatement;
|
|
@@ -119,13 +119,20 @@ export function gtsPlugin(options: GtsPluginOption = {}) {
|
|
|
119
119
|
gts_parseNamedAttributeDefinition() {
|
|
120
120
|
const node = this.startNode() as AST.GTSNamedAttributeDefinition;
|
|
121
121
|
// AttributeName
|
|
122
|
-
|
|
122
|
+
const start = this.start;
|
|
123
|
+
let name: AST.Identifier | AST.SimpleLiteral;
|
|
123
124
|
if (this.type.label === "string") {
|
|
124
|
-
name = this.parseExprAtom();
|
|
125
|
+
name = this.parseExprAtom() as AST.SimpleLiteral;
|
|
126
|
+
if (typeof name.value === "string" && name.value.startsWith("~")) {
|
|
127
|
+
this.raise(
|
|
128
|
+
start,
|
|
129
|
+
`Attribute name starts with '~' is reserved for internal use.`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
125
132
|
} else if (this.type.label === "name") {
|
|
126
133
|
name = this.parseIdent();
|
|
127
134
|
} else {
|
|
128
|
-
this.raise(
|
|
135
|
+
this.raise(start, "Expected attribute name");
|
|
129
136
|
}
|
|
130
137
|
node.name = name;
|
|
131
138
|
// AttributeBody
|
|
@@ -215,7 +222,7 @@ export function gtsPlugin(options: GtsPluginOption = {}) {
|
|
|
215
222
|
if (stmt.type === "GTSDefineStatement") {
|
|
216
223
|
this.raise(
|
|
217
224
|
startPos,
|
|
218
|
-
"DefineStatement is not allowed in direct function."
|
|
225
|
+
"DefineStatement is not allowed in direct function.",
|
|
219
226
|
);
|
|
220
227
|
}
|
|
221
228
|
node.body.push(stmt);
|
|
@@ -274,13 +281,13 @@ export function gtsPlugin(options: GtsPluginOption = {}) {
|
|
|
274
281
|
override parseExprAtom(
|
|
275
282
|
refDestructuringErrors?: Parse.DestructuringErrors,
|
|
276
283
|
forInit?: boolean | "await",
|
|
277
|
-
forNew?: boolean
|
|
284
|
+
forNew?: boolean,
|
|
278
285
|
): AST.Expression {
|
|
279
286
|
if (this.type === tokTypes.colon) {
|
|
280
287
|
if (!this.isShortcutContext) {
|
|
281
288
|
this.raise(
|
|
282
289
|
this.start,
|
|
283
|
-
"ShortcutArgumentExpression ':' must be inside ShortcutFunction or DirectShortcutFunction."
|
|
290
|
+
"ShortcutArgumentExpression ':' must be inside ShortcutFunction or DirectShortcutFunction.",
|
|
284
291
|
);
|
|
285
292
|
}
|
|
286
293
|
const node = this.startNode() as AST.GTSShortcutArgumentExpression;
|
|
@@ -307,9 +314,9 @@ export function gtsPlugin(options: GtsPluginOption = {}) {
|
|
|
307
314
|
refDestructuringErrors?: Parse.DestructuringErrors | null,
|
|
308
315
|
sawUnary?: boolean,
|
|
309
316
|
incDec?: boolean,
|
|
310
|
-
forInit?: boolean | "await"
|
|
317
|
+
forInit?: boolean | "await",
|
|
311
318
|
): AST.Expression {
|
|
312
|
-
if (this.isContextual("query")) {
|
|
319
|
+
if (this.isShortcutContext && this.isContextual("query")) {
|
|
313
320
|
const expr = this.gts_parseQueryExpression();
|
|
314
321
|
if (!incDec && this.eat(tokTypes.starstar)) {
|
|
315
322
|
this.unexpected(this.lastTokStart);
|
|
@@ -320,12 +327,12 @@ export function gtsPlugin(options: GtsPluginOption = {}) {
|
|
|
320
327
|
refDestructuringErrors,
|
|
321
328
|
sawUnary,
|
|
322
329
|
incDec,
|
|
323
|
-
forInit
|
|
330
|
+
forInit,
|
|
324
331
|
);
|
|
325
332
|
}
|
|
326
333
|
|
|
327
334
|
gts_parseQueryExpression(
|
|
328
|
-
forInit?: boolean | "await"
|
|
335
|
+
forInit?: boolean | "await",
|
|
329
336
|
): AST.GTSQueryExpression {
|
|
330
337
|
const node = this.startNode() as AST.GTSQueryExpression;
|
|
331
338
|
this.next(); // consume 'query'
|
package/src/parse/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { gtsPlugin, type GtsPluginOption } from "./gts_plugin.js";
|
|
|
5
5
|
import { loosePlugin } from "./loose_plugin.js";
|
|
6
6
|
import { getCommentHandlers } from "./comment.js";
|
|
7
7
|
import { GtsTranspilerError } from "../error.js";
|
|
8
|
+
import { recordCallLParenPlugin } from "./record_call_lparen_plugin.js";
|
|
8
9
|
|
|
9
10
|
const TsParser = Parser.extend(tsPlugin());
|
|
10
11
|
|
|
@@ -29,15 +30,24 @@ export function parse(input: string, options?: GtsPluginOption): Program {
|
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
export
|
|
33
|
+
export interface ParseLooseOptions extends GtsPluginOption {
|
|
34
|
+
recordCallLParens?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function parseLoose(
|
|
38
|
+
input: string,
|
|
39
|
+
options?: ParseLooseOptions,
|
|
40
|
+
): Program {
|
|
33
41
|
try {
|
|
34
42
|
const GtsParser = TsParser.extend(
|
|
35
43
|
loosePlugin(),
|
|
44
|
+
...(options?.recordCallLParens ? [recordCallLParenPlugin()] : []),
|
|
36
45
|
gtsPlugin({
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
allowEmptyPositionalAttribute:
|
|
40
|
-
|
|
46
|
+
allowEmptyShortcutMember:
|
|
47
|
+
options?.allowEmptyPositionalAttribute || true,
|
|
48
|
+
allowEmptyPositionalAttribute:
|
|
49
|
+
options?.allowEmptyPositionalAttribute || true,
|
|
50
|
+
}),
|
|
41
51
|
);
|
|
42
52
|
const { onComment, addComments } = getCommentHandlers(input, []);
|
|
43
53
|
const ast = GtsParser.parse(input, {
|
|
@@ -15,7 +15,7 @@ export function loosePlugin() {
|
|
|
15
15
|
return super.parseIdent(liberal);
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
|
-
|
|
18
|
+
readonly #proxiedThis = new Proxy(this, {
|
|
19
19
|
get: (target, prop) => {
|
|
20
20
|
if (prop === "parseIdent") {
|
|
21
21
|
return this._patchedParseIdent;
|
|
@@ -45,7 +45,7 @@ export function loosePlugin() {
|
|
|
45
45
|
forInit?: boolean | "await",
|
|
46
46
|
): AST.Expression {
|
|
47
47
|
return super.parseSubscript.call(
|
|
48
|
-
this
|
|
48
|
+
this.#proxiedThis,
|
|
49
49
|
base,
|
|
50
50
|
startPos,
|
|
51
51
|
startLoc,
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { tokTypes, type Parser } from "acorn";
|
|
2
|
+
import type { AST, Parse } from "../types.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A plugin that records the location of the left parenthesis in CallExpression and
|
|
6
|
+
* NewExpression.
|
|
7
|
+
*
|
|
8
|
+
* This is useful for Language tooling, that we can maps the '(' token from its location,
|
|
9
|
+
* to provide * a signature help for user when they press `(` after a function name.
|
|
10
|
+
*
|
|
11
|
+
* The recorded location will be stored in `lParenLoc` property of the
|
|
12
|
+
* CallExpression/NewExpression node.
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
15
|
+
export function recordCallLParenPlugin() {
|
|
16
|
+
return function recordCallLParenPluginTransformer(
|
|
17
|
+
parser: typeof Parser,
|
|
18
|
+
): typeof Parser {
|
|
19
|
+
return class RecordCallLParenParser extends (parser as typeof Parse.Parser) {
|
|
20
|
+
override parseSubscript(
|
|
21
|
+
base: AST.Expression,
|
|
22
|
+
startPos: number,
|
|
23
|
+
startLoc: AST.Position,
|
|
24
|
+
noCalls?: boolean,
|
|
25
|
+
maybeAsyncArrow?: boolean,
|
|
26
|
+
optionalChained?: boolean,
|
|
27
|
+
forInit?: boolean | "await",
|
|
28
|
+
): AST.Expression {
|
|
29
|
+
let recordedLParenLoc: AST.SourceLocation | null = null;
|
|
30
|
+
if (!noCalls && this.type === tokTypes.parenL) {
|
|
31
|
+
recordedLParenLoc = {
|
|
32
|
+
start: this.startLoc,
|
|
33
|
+
end: this.endLoc,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const result = super.parseSubscript(
|
|
37
|
+
base,
|
|
38
|
+
startPos,
|
|
39
|
+
startLoc,
|
|
40
|
+
noCalls,
|
|
41
|
+
maybeAsyncArrow,
|
|
42
|
+
optionalChained,
|
|
43
|
+
forInit,
|
|
44
|
+
);
|
|
45
|
+
if (recordedLParenLoc && result.type === "CallExpression") {
|
|
46
|
+
result.lParenLoc = recordedLParenLoc;
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private _capturedLParenLocFromNew: AST.SourceLocation | null = null;
|
|
52
|
+
private readonly _patchedEat = (type: any) => {
|
|
53
|
+
if (type === tokTypes.parenL) {
|
|
54
|
+
this._capturedLParenLocFromNew = {
|
|
55
|
+
start: this.startLoc,
|
|
56
|
+
end: this.endLoc,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return this.eat(type);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
readonly #proxiedThis = new Proxy(this, {
|
|
63
|
+
get: (target, prop) => {
|
|
64
|
+
if (prop === "eat") {
|
|
65
|
+
return this._patchedEat;
|
|
66
|
+
}
|
|
67
|
+
const value = Reflect.get(target, prop);
|
|
68
|
+
if (typeof value === "function") {
|
|
69
|
+
return value.bind(target);
|
|
70
|
+
}
|
|
71
|
+
return value;
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
override parseNew() {
|
|
76
|
+
const result = super.parseNew.apply(this.#proxiedThis);
|
|
77
|
+
if (this._capturedLParenLocFromNew && result.type === "NewExpression") {
|
|
78
|
+
result.lParenLoc = this._capturedLParenLocFromNew;
|
|
79
|
+
this._capturedLParenLocFromNew = null;
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
}
|
package/src/transform/gts.ts
CHANGED
|
@@ -30,8 +30,8 @@ export interface ExternalizedBinding {
|
|
|
30
30
|
export interface TranspileState {
|
|
31
31
|
readonly createDefineFnId: Identifier;
|
|
32
32
|
readonly createBindingFnId: Identifier;
|
|
33
|
-
readonly
|
|
34
|
-
readonly
|
|
33
|
+
readonly ActionLit: Literal;
|
|
34
|
+
readonly PreludeLit: Literal;
|
|
35
35
|
readonly fnArgId: Identifier;
|
|
36
36
|
readonly shortcutFunctionParameters: Pattern[];
|
|
37
37
|
readonly rootVmId: Identifier;
|
|
@@ -48,8 +48,8 @@ export interface TranspileState {
|
|
|
48
48
|
/** Internal counters / state for emitting per-define nodes & bindings */
|
|
49
49
|
defineIdCounter: number;
|
|
50
50
|
|
|
51
|
-
/**
|
|
52
|
-
|
|
51
|
+
/** Binding statements to be inserted before all define statement */
|
|
52
|
+
bindingStatements: (Statement | ModuleDeclaration)[];
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export const commonGtsVisitor: Visitors<Node, TranspileState> = {
|
|
@@ -64,7 +64,7 @@ export const commonGtsVisitor: Visitors<Node, TranspileState> = {
|
|
|
64
64
|
kind: "init",
|
|
65
65
|
method: false,
|
|
66
66
|
shorthand: false,
|
|
67
|
-
value: state.
|
|
67
|
+
value: state.ActionLit,
|
|
68
68
|
loc: node.loc,
|
|
69
69
|
},
|
|
70
70
|
{
|
|
@@ -162,6 +162,15 @@ export const commonGtsVisitor: Visitors<Node, TranspileState> = {
|
|
|
162
162
|
value: !!node.star,
|
|
163
163
|
},
|
|
164
164
|
},
|
|
165
|
+
{
|
|
166
|
+
type: "Property",
|
|
167
|
+
key: { type: "Identifier", name: "context" },
|
|
168
|
+
computed: false,
|
|
169
|
+
kind: "init",
|
|
170
|
+
method: false,
|
|
171
|
+
shorthand: false,
|
|
172
|
+
value: state.fnArgId,
|
|
173
|
+
}
|
|
165
174
|
],
|
|
166
175
|
},
|
|
167
176
|
],
|
|
@@ -182,16 +191,12 @@ const gtsVisitor: Visitors<Node, TranspileState> = {
|
|
|
182
191
|
const body: Program["body"] = [];
|
|
183
192
|
for (const stmt of node.body) {
|
|
184
193
|
const visited = visit(stmt) as Statement;
|
|
185
|
-
|
|
186
|
-
if (visited.type !== "EmptyStatement") {
|
|
187
|
-
body.push(visited);
|
|
188
|
-
}
|
|
189
|
-
if (state.pendingStatements.length > 0) {
|
|
190
|
-
body.push(...(state.pendingStatements));
|
|
191
|
-
state.pendingStatements = [];
|
|
192
|
-
}
|
|
194
|
+
body.push(visited);
|
|
193
195
|
}
|
|
194
196
|
|
|
197
|
+
body.unshift(...state.bindingStatements);
|
|
198
|
+
state.bindingStatements = [];
|
|
199
|
+
|
|
195
200
|
if (state.hasQueryExpressions) {
|
|
196
201
|
body.unshift({
|
|
197
202
|
type: "ImportDeclaration",
|
|
@@ -222,16 +227,6 @@ const gtsVisitor: Visitors<Node, TranspileState> = {
|
|
|
222
227
|
imported: { type: "Identifier", name: "createBinding" },
|
|
223
228
|
local: state.createBindingFnId,
|
|
224
229
|
},
|
|
225
|
-
{
|
|
226
|
-
type: "ImportSpecifier",
|
|
227
|
-
imported: { type: "Identifier", name: "Action" },
|
|
228
|
-
local: state.ActionId,
|
|
229
|
-
},
|
|
230
|
-
{
|
|
231
|
-
type: "ImportSpecifier",
|
|
232
|
-
imported: { type: "Identifier", name: "Prelude" },
|
|
233
|
-
local: state.preludeSymbolId,
|
|
234
|
-
},
|
|
235
230
|
],
|
|
236
231
|
source: { type: "Literal", value: state.runtimeImportSource },
|
|
237
232
|
attributes: [],
|
|
@@ -272,9 +267,8 @@ const gtsVisitor: Visitors<Node, TranspileState> = {
|
|
|
272
267
|
|
|
273
268
|
const newBindings = state.externalizedBindings;
|
|
274
269
|
state.externalizedBindings = [];
|
|
275
|
-
const statements: (Statement | ModuleDeclaration)[] = [];
|
|
276
270
|
|
|
277
|
-
|
|
271
|
+
state.bindingStatements.push({
|
|
278
272
|
type: "VariableDeclaration",
|
|
279
273
|
kind: "const",
|
|
280
274
|
declarations: [
|
|
@@ -287,7 +281,7 @@ const gtsVisitor: Visitors<Node, TranspileState> = {
|
|
|
287
281
|
loc: node.loc,
|
|
288
282
|
});
|
|
289
283
|
|
|
290
|
-
|
|
284
|
+
state.bindingStatements.push({
|
|
291
285
|
type: "VariableDeclaration",
|
|
292
286
|
kind: "const",
|
|
293
287
|
declarations: [
|
|
@@ -325,33 +319,26 @@ const gtsVisitor: Visitors<Node, TranspileState> = {
|
|
|
325
319
|
],
|
|
326
320
|
};
|
|
327
321
|
if (binding.export) {
|
|
328
|
-
|
|
322
|
+
state.bindingStatements.push({
|
|
329
323
|
type: "ExportNamedDeclaration",
|
|
330
324
|
declaration: decl,
|
|
331
325
|
specifiers: [],
|
|
332
326
|
attributes: [],
|
|
333
327
|
});
|
|
334
328
|
} else {
|
|
335
|
-
|
|
329
|
+
state.bindingStatements.push(decl);
|
|
336
330
|
}
|
|
337
331
|
}
|
|
338
|
-
|
|
339
|
-
statements.push({
|
|
332
|
+
return {
|
|
340
333
|
type: "ExpressionStatement",
|
|
341
334
|
expression: {
|
|
342
335
|
type: "CallExpression",
|
|
343
336
|
optional: false,
|
|
344
|
-
callee:
|
|
345
|
-
...state.createDefineFnId,
|
|
346
|
-
loc: node.loc,
|
|
347
|
-
},
|
|
337
|
+
callee: state.createDefineFnId,
|
|
348
338
|
arguments: [state.rootVmId, nodeVarId],
|
|
349
339
|
},
|
|
350
340
|
loc: node.loc,
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
state.pendingStatements.push(...statements);
|
|
354
|
-
return { type: "EmptyStatement" };
|
|
341
|
+
};
|
|
355
342
|
},
|
|
356
343
|
GTSNamedAttributeDefinition(node, { visit, state }) {
|
|
357
344
|
const namedBody = visit(node.body) as ObjectExpression;
|
|
@@ -503,9 +490,9 @@ export const initialTranspileState = (
|
|
|
503
490
|
option.shortcutFunctionPreludes ?? DEFAULT_SHORTCUT_FUNCTION_PRELUDES;
|
|
504
491
|
const queryBindings = option.queryBindings ?? DEFAULT_QUERY_BINDINGS;
|
|
505
492
|
const fnArgId: Identifier = { type: "Identifier", name: "__gts_fnArg" };
|
|
506
|
-
const
|
|
507
|
-
type: "
|
|
508
|
-
|
|
493
|
+
const PreludeLit: Literal = {
|
|
494
|
+
type: "Literal",
|
|
495
|
+
value: "~prelude",
|
|
509
496
|
};
|
|
510
497
|
const shortcutFunctionParameters: Pattern[] = [
|
|
511
498
|
fnArgId,
|
|
@@ -526,7 +513,7 @@ export const initialTranspileState = (
|
|
|
526
513
|
right: {
|
|
527
514
|
type: "MemberExpression",
|
|
528
515
|
object: fnArgId,
|
|
529
|
-
property:
|
|
516
|
+
property: PreludeLit,
|
|
530
517
|
computed: true,
|
|
531
518
|
optional: false,
|
|
532
519
|
},
|
|
@@ -549,8 +536,8 @@ export const initialTranspileState = (
|
|
|
549
536
|
return {
|
|
550
537
|
createDefineFnId: { type: "Identifier", name: "__gts_createDefine" },
|
|
551
538
|
createBindingFnId: { type: "Identifier", name: "__gts_createBinding" },
|
|
552
|
-
|
|
553
|
-
|
|
539
|
+
ActionLit: { type: "Literal", value: "~action" },
|
|
540
|
+
PreludeLit,
|
|
554
541
|
fnArgId,
|
|
555
542
|
shortcutFunctionParameters,
|
|
556
543
|
rootVmId: { type: "Identifier", name: "__gts_rootVm" },
|
|
@@ -576,7 +563,7 @@ export const initialTranspileState = (
|
|
|
576
563
|
hasQueryExpressions: false,
|
|
577
564
|
defineIdCounter: 0,
|
|
578
565
|
|
|
579
|
-
|
|
566
|
+
bindingStatements: [],
|
|
580
567
|
};
|
|
581
568
|
};
|
|
582
569
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SourceLocation } from "estree";
|
|
1
|
+
import type { Node, SourceLocation } from "estree";
|
|
2
2
|
import { walk } from "zimmerframe";
|
|
3
3
|
|
|
4
4
|
function isLeafNode(node: any): boolean {
|
|
@@ -28,43 +28,69 @@ function isLeafNode(node: any): boolean {
|
|
|
28
28
|
return true;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export interface
|
|
31
|
+
export interface LeafToken {
|
|
32
|
+
loc: SourceLocation;
|
|
33
|
+
isDummy?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Override source length (instead of loc.end - loc.start)
|
|
36
|
+
*/
|
|
37
|
+
sourceLength?: number;
|
|
38
|
+
/**
|
|
39
|
+
* Adjust the start position of source code
|
|
40
|
+
*/
|
|
41
|
+
sourceStartOffset?: number;
|
|
32
42
|
/**
|
|
33
43
|
* Adjust the start position of generated code
|
|
34
44
|
*/
|
|
35
|
-
|
|
45
|
+
generatedStartOffset?: number;
|
|
46
|
+
/**
|
|
47
|
+
* Make the source length longer
|
|
48
|
+
*/
|
|
49
|
+
sourceLengthOffset?: number;
|
|
36
50
|
/**
|
|
37
51
|
* The original length of generated code, used for mapping diagnostics
|
|
38
52
|
*/
|
|
39
|
-
generatedLength: number;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface LeafToken {
|
|
43
|
-
loc: SourceLocation;
|
|
44
|
-
isDummy?: boolean;
|
|
45
|
-
sourceLength?: number;
|
|
46
53
|
generatedLength?: number;
|
|
47
|
-
locationAdjustment?: LocationAdjustment;
|
|
48
54
|
}
|
|
49
55
|
|
|
50
56
|
export function collectLeafTokens(ast: any): LeafToken[] {
|
|
51
|
-
const
|
|
52
|
-
|
|
57
|
+
const state = {
|
|
58
|
+
tokens: [] as LeafToken[],
|
|
59
|
+
};
|
|
60
|
+
walk(ast as Node, state, {
|
|
53
61
|
_(node, { state, next }) {
|
|
54
62
|
if (isLeafNode(node) && node.loc) {
|
|
55
63
|
const token: LeafToken = {
|
|
56
|
-
loc: node.loc
|
|
64
|
+
loc: node.loc,
|
|
57
65
|
};
|
|
58
|
-
if (node.isDummy) {
|
|
66
|
+
if ("isDummy" in node && node.isDummy) {
|
|
59
67
|
token.isDummy = true;
|
|
60
68
|
token.sourceLength = 0;
|
|
61
|
-
//
|
|
69
|
+
// add 1 for squiggle on next character
|
|
62
70
|
token.generatedLength = 1;
|
|
63
71
|
}
|
|
64
|
-
state.push(token);
|
|
72
|
+
state.tokens.push(token);
|
|
73
|
+
}
|
|
74
|
+
next();
|
|
75
|
+
},
|
|
76
|
+
NewExpression(node, { state, next }) {
|
|
77
|
+
const lParenLoc = node.lParenLoc;
|
|
78
|
+
if (lParenLoc) {
|
|
79
|
+
state.tokens.push({
|
|
80
|
+
loc: lParenLoc,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
next();
|
|
84
|
+
},
|
|
85
|
+
CallExpression(node, { state, next }) {
|
|
86
|
+
const lParenLoc = node.lParenLoc;
|
|
87
|
+
if (lParenLoc) {
|
|
88
|
+
state.tokens.push({
|
|
89
|
+
loc: lParenLoc,
|
|
90
|
+
});
|
|
65
91
|
}
|
|
66
92
|
next();
|
|
67
93
|
},
|
|
68
94
|
});
|
|
69
|
-
return tokens;
|
|
95
|
+
return state.tokens;
|
|
70
96
|
}
|
|
@@ -2,7 +2,6 @@ import type { AST } from "../../types";
|
|
|
2
2
|
import { walk } from "zimmerframe";
|
|
3
3
|
import type { SourceInfo, TranspileResult } from "..";
|
|
4
4
|
import { print } from "esrap";
|
|
5
|
-
import tsPrinter from "esrap/languages/ts";
|
|
6
5
|
import {
|
|
7
6
|
initialTranspileState,
|
|
8
7
|
type TranspileOption,
|
|
@@ -13,16 +12,19 @@ import { applyReplacements } from "./replacements";
|
|
|
13
12
|
import type { Program } from "estree";
|
|
14
13
|
import { convertToVolarMappings, type VolarMappingResult } from "./mappings";
|
|
15
14
|
import { collectLeafTokens, type LeafToken } from "./collect_tokens";
|
|
15
|
+
import { patchedPrinter } from "./printer";
|
|
16
16
|
|
|
17
17
|
interface TypingTranspileOption extends TranspileOption {
|
|
18
18
|
leafTokens: LeafToken[];
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
/**
|
|
20
|
+
* "row:col" -> "replacement string"
|
|
21
|
+
*/
|
|
22
|
+
extraMappings: Map<string, string>;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
function gtsToTypings(
|
|
24
26
|
ast: AST.Program,
|
|
25
|
-
option: TypingTranspileOption
|
|
27
|
+
option: TypingTranspileOption,
|
|
26
28
|
): TranspileResult {
|
|
27
29
|
const state: TypingTranspileState = {
|
|
28
30
|
...(initialTranspileState(option) as Pick<
|
|
@@ -32,34 +34,20 @@ function gtsToTypings(
|
|
|
32
34
|
leafTokens: option.leafTokens,
|
|
33
35
|
idCounter: 0,
|
|
34
36
|
typingPendingStatements: [],
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
rootVmId: { type: "Identifier", name: "__gts_root_vm" },
|
|
38
|
+
utilNsId: { type: "Identifier", name: "__gts_util" },
|
|
37
39
|
replacementTag: { type: "Identifier", name: "__gts_replacement_tag" },
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
NamedDefinition: { type: "Identifier", name: "__gts_symbols_namedDef" },
|
|
41
|
-
},
|
|
40
|
+
MetaLit: { type: "Literal", value: "~meta" },
|
|
41
|
+
NamedDefinitionLit: { type: "Literal", value: "~namedDefinition" },
|
|
42
42
|
defineLeadingComments: [],
|
|
43
43
|
vmDefTypeIdStack: [],
|
|
44
44
|
metaTypeIdStack: [],
|
|
45
45
|
finalMetaTypeIdStack: [],
|
|
46
46
|
attrsOfCurrentVm: [],
|
|
47
|
-
|
|
47
|
+
extraMappings: option.extraMappings,
|
|
48
48
|
};
|
|
49
49
|
const newAst = walk(ast as AST.Node, state, gtsToTypingsWalker);
|
|
50
|
-
const
|
|
51
|
-
getLeadingComments: (node) => (node as AST.Node).leadingComments,
|
|
52
|
-
getTrailingComments: (node) => (node as AST.Node).trailingComments,
|
|
53
|
-
});
|
|
54
|
-
const prevIdentifier = printer.Identifier!;
|
|
55
|
-
printer.Identifier = function (node, context) {
|
|
56
|
-
if (node.isDummy) {
|
|
57
|
-
context.write("", node);
|
|
58
|
-
} else {
|
|
59
|
-
prevIdentifier(node, context);
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
const { code, map } = print(newAst, printer, {
|
|
50
|
+
const { code, map } = print(newAst, patchedPrinter, {
|
|
63
51
|
indent: " ",
|
|
64
52
|
});
|
|
65
53
|
return {
|
|
@@ -71,21 +59,21 @@ function gtsToTypings(
|
|
|
71
59
|
export function transformForVolar(
|
|
72
60
|
ast: Program,
|
|
73
61
|
option: TranspileOption,
|
|
74
|
-
sourceInfo: Required<SourceInfo
|
|
62
|
+
sourceInfo: Required<SourceInfo>,
|
|
75
63
|
): VolarMappingResult {
|
|
76
64
|
const tokens = collectLeafTokens(ast);
|
|
77
|
-
const
|
|
65
|
+
const extraMappings = new Map<string, string>();
|
|
78
66
|
const { code, sourceMap } = gtsToTypings(ast, {
|
|
79
67
|
...option,
|
|
80
68
|
leafTokens: tokens,
|
|
81
|
-
|
|
69
|
+
extraMappings,
|
|
82
70
|
});
|
|
83
71
|
const volarMappings = convertToVolarMappings(
|
|
84
72
|
code,
|
|
85
73
|
sourceInfo.content,
|
|
86
74
|
sourceMap,
|
|
87
75
|
tokens,
|
|
88
|
-
|
|
76
|
+
extraMappings,
|
|
89
77
|
);
|
|
90
78
|
return {
|
|
91
79
|
code,
|