@gi-tcg/gts-transpiler 0.2.0 → 0.3.0
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 +7 -1
- package/dist/index.js +424 -182
- package/package.json +3 -3
- package/src/config.ts +86 -2
- package/src/index.ts +10 -2
- package/src/parse/gts_plugin.ts +18 -11
- 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 +23 -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.0",
|
|
4
4
|
"repository": "https://github.com/piovium/gts.git",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -19,8 +19,8 @@
|
|
|
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
25
|
"zimmerframe": "^1.1.4"
|
|
26
26
|
},
|
package/src/config.ts
CHANGED
|
@@ -1,6 +1,90 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
1
|
import type { TranspileOption } from "./transform/gts";
|
|
3
2
|
|
|
3
|
+
export interface PathPolyfill {
|
|
4
|
+
isAbsolute(filePath: string): boolean;
|
|
5
|
+
dirname(filePath: string): string;
|
|
6
|
+
resolve(...segments: string[]): string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const path: PathPolyfill = globalThis.require
|
|
10
|
+
? (globalThis.require("node:path") as typeof import("node:path"))
|
|
11
|
+
: {
|
|
12
|
+
isAbsolute(filePath) {
|
|
13
|
+
return filePath.startsWith("/");
|
|
14
|
+
},
|
|
15
|
+
dirname(filePath) {
|
|
16
|
+
if (!filePath) {
|
|
17
|
+
return ".";
|
|
18
|
+
}
|
|
19
|
+
const normalized = normalizeSlashes(filePath);
|
|
20
|
+
if (normalized === "/") {
|
|
21
|
+
return "/";
|
|
22
|
+
}
|
|
23
|
+
const trimmed =
|
|
24
|
+
normalized.length > 1 && normalized.endsWith("/")
|
|
25
|
+
? normalized.slice(0, -1)
|
|
26
|
+
: normalized;
|
|
27
|
+
const idx = trimmed.lastIndexOf("/");
|
|
28
|
+
if (idx < 0) {
|
|
29
|
+
return ".";
|
|
30
|
+
}
|
|
31
|
+
if (idx === 0) {
|
|
32
|
+
return "/";
|
|
33
|
+
}
|
|
34
|
+
return trimmed.slice(0, idx);
|
|
35
|
+
},
|
|
36
|
+
resolve(...segments) {
|
|
37
|
+
if (segments.length === 0) {
|
|
38
|
+
return ".";
|
|
39
|
+
}
|
|
40
|
+
let resolved = "";
|
|
41
|
+
for (let i = segments.length - 1; i >= 0; i--) {
|
|
42
|
+
const segment = segments[i];
|
|
43
|
+
if (!segment) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (resolved) {
|
|
47
|
+
resolved = `${segment}/${resolved}`;
|
|
48
|
+
} else {
|
|
49
|
+
resolved = segment;
|
|
50
|
+
}
|
|
51
|
+
if (path.isAbsolute(segment)) {
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return normalizeResolvedPath(resolved || ".");
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
function normalizeSlashes(filePath: string): string {
|
|
60
|
+
return filePath.replace(/\\+/g, "/");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function normalizeResolvedPath(filePath: string): string {
|
|
64
|
+
const normalized = normalizeSlashes(filePath);
|
|
65
|
+
const isAbsolute = normalized.startsWith("/");
|
|
66
|
+
const parts = normalized.split("/");
|
|
67
|
+
const stack: string[] = [];
|
|
68
|
+
for (const part of parts) {
|
|
69
|
+
if (!part || part === ".") {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (part === "..") {
|
|
73
|
+
if (stack.length > 0 && stack[stack.length - 1] !== "..") {
|
|
74
|
+
stack.pop();
|
|
75
|
+
} else if (!isAbsolute) {
|
|
76
|
+
stack.push("..");
|
|
77
|
+
}
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
stack.push(part);
|
|
81
|
+
}
|
|
82
|
+
if (isAbsolute) {
|
|
83
|
+
return `/${stack.join("/")}` || "/";
|
|
84
|
+
}
|
|
85
|
+
return stack.join("/") || ".";
|
|
86
|
+
}
|
|
87
|
+
|
|
4
88
|
export interface GtsConfig extends TranspileOption {}
|
|
5
89
|
|
|
6
90
|
export interface PackageJson {
|
|
@@ -106,7 +190,7 @@ function* findNearestPackageConfig(
|
|
|
106
190
|
): Generator<string | Promise<string>, GtsConfig, string> {
|
|
107
191
|
let currentDir = startDir;
|
|
108
192
|
while (true) {
|
|
109
|
-
const pkgPath = path.
|
|
193
|
+
const pkgPath = path.resolve(currentDir, "package.json");
|
|
110
194
|
const config = yield* readPackageConfig(readFileFn, pkgPath);
|
|
111
195
|
if (config) {
|
|
112
196
|
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,10 @@ 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
|
+
path,
|
|
43
|
+
type PathPolyfill,
|
|
44
|
+
} 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,7 +314,7 @@ 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
319
|
if (this.isContextual("query")) {
|
|
313
320
|
const expr = this.gts_parseQueryExpression();
|
|
@@ -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
|
{
|
|
@@ -182,16 +182,12 @@ const gtsVisitor: Visitors<Node, TranspileState> = {
|
|
|
182
182
|
const body: Program["body"] = [];
|
|
183
183
|
for (const stmt of node.body) {
|
|
184
184
|
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
|
-
}
|
|
185
|
+
body.push(visited);
|
|
193
186
|
}
|
|
194
187
|
|
|
188
|
+
body.unshift(...state.bindingStatements);
|
|
189
|
+
state.bindingStatements = [];
|
|
190
|
+
|
|
195
191
|
if (state.hasQueryExpressions) {
|
|
196
192
|
body.unshift({
|
|
197
193
|
type: "ImportDeclaration",
|
|
@@ -222,16 +218,6 @@ const gtsVisitor: Visitors<Node, TranspileState> = {
|
|
|
222
218
|
imported: { type: "Identifier", name: "createBinding" },
|
|
223
219
|
local: state.createBindingFnId,
|
|
224
220
|
},
|
|
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
221
|
],
|
|
236
222
|
source: { type: "Literal", value: state.runtimeImportSource },
|
|
237
223
|
attributes: [],
|
|
@@ -272,9 +258,8 @@ const gtsVisitor: Visitors<Node, TranspileState> = {
|
|
|
272
258
|
|
|
273
259
|
const newBindings = state.externalizedBindings;
|
|
274
260
|
state.externalizedBindings = [];
|
|
275
|
-
const statements: (Statement | ModuleDeclaration)[] = [];
|
|
276
261
|
|
|
277
|
-
|
|
262
|
+
state.bindingStatements.push({
|
|
278
263
|
type: "VariableDeclaration",
|
|
279
264
|
kind: "const",
|
|
280
265
|
declarations: [
|
|
@@ -287,7 +272,7 @@ const gtsVisitor: Visitors<Node, TranspileState> = {
|
|
|
287
272
|
loc: node.loc,
|
|
288
273
|
});
|
|
289
274
|
|
|
290
|
-
|
|
275
|
+
state.bindingStatements.push({
|
|
291
276
|
type: "VariableDeclaration",
|
|
292
277
|
kind: "const",
|
|
293
278
|
declarations: [
|
|
@@ -325,33 +310,26 @@ const gtsVisitor: Visitors<Node, TranspileState> = {
|
|
|
325
310
|
],
|
|
326
311
|
};
|
|
327
312
|
if (binding.export) {
|
|
328
|
-
|
|
313
|
+
state.bindingStatements.push({
|
|
329
314
|
type: "ExportNamedDeclaration",
|
|
330
315
|
declaration: decl,
|
|
331
316
|
specifiers: [],
|
|
332
317
|
attributes: [],
|
|
333
318
|
});
|
|
334
319
|
} else {
|
|
335
|
-
|
|
320
|
+
state.bindingStatements.push(decl);
|
|
336
321
|
}
|
|
337
322
|
}
|
|
338
|
-
|
|
339
|
-
statements.push({
|
|
323
|
+
return {
|
|
340
324
|
type: "ExpressionStatement",
|
|
341
325
|
expression: {
|
|
342
326
|
type: "CallExpression",
|
|
343
327
|
optional: false,
|
|
344
|
-
callee:
|
|
345
|
-
...state.createDefineFnId,
|
|
346
|
-
loc: node.loc,
|
|
347
|
-
},
|
|
328
|
+
callee: state.createDefineFnId,
|
|
348
329
|
arguments: [state.rootVmId, nodeVarId],
|
|
349
330
|
},
|
|
350
331
|
loc: node.loc,
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
state.pendingStatements.push(...statements);
|
|
354
|
-
return { type: "EmptyStatement" };
|
|
332
|
+
};
|
|
355
333
|
},
|
|
356
334
|
GTSNamedAttributeDefinition(node, { visit, state }) {
|
|
357
335
|
const namedBody = visit(node.body) as ObjectExpression;
|
|
@@ -503,9 +481,9 @@ export const initialTranspileState = (
|
|
|
503
481
|
option.shortcutFunctionPreludes ?? DEFAULT_SHORTCUT_FUNCTION_PRELUDES;
|
|
504
482
|
const queryBindings = option.queryBindings ?? DEFAULT_QUERY_BINDINGS;
|
|
505
483
|
const fnArgId: Identifier = { type: "Identifier", name: "__gts_fnArg" };
|
|
506
|
-
const
|
|
507
|
-
type: "
|
|
508
|
-
|
|
484
|
+
const PreludeLit: Literal = {
|
|
485
|
+
type: "Literal",
|
|
486
|
+
value: "~prelude",
|
|
509
487
|
};
|
|
510
488
|
const shortcutFunctionParameters: Pattern[] = [
|
|
511
489
|
fnArgId,
|
|
@@ -526,7 +504,7 @@ export const initialTranspileState = (
|
|
|
526
504
|
right: {
|
|
527
505
|
type: "MemberExpression",
|
|
528
506
|
object: fnArgId,
|
|
529
|
-
property:
|
|
507
|
+
property: PreludeLit,
|
|
530
508
|
computed: true,
|
|
531
509
|
optional: false,
|
|
532
510
|
},
|
|
@@ -549,8 +527,8 @@ export const initialTranspileState = (
|
|
|
549
527
|
return {
|
|
550
528
|
createDefineFnId: { type: "Identifier", name: "__gts_createDefine" },
|
|
551
529
|
createBindingFnId: { type: "Identifier", name: "__gts_createBinding" },
|
|
552
|
-
|
|
553
|
-
|
|
530
|
+
ActionLit: { type: "Literal", value: "~action" },
|
|
531
|
+
PreludeLit,
|
|
554
532
|
fnArgId,
|
|
555
533
|
shortcutFunctionParameters,
|
|
556
534
|
rootVmId: { type: "Identifier", name: "__gts_rootVm" },
|
|
@@ -576,7 +554,7 @@ export const initialTranspileState = (
|
|
|
576
554
|
hasQueryExpressions: false,
|
|
577
555
|
defineIdCounter: 0,
|
|
578
556
|
|
|
579
|
-
|
|
557
|
+
bindingStatements: [],
|
|
580
558
|
};
|
|
581
559
|
};
|
|
582
560
|
|
|
@@ -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
|
}
|