@gi-tcg/gts-transpiler 0.1.1 → 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 +45 -0
- package/dist/index.js +708 -441
- package/package.json +9 -10
- package/src/config.ts +221 -0
- package/src/index.ts +12 -3
- package/src/keywords.ts +1 -1
- package/src/parse/comment.d.ts +10 -0
- package/src/parse/gts_plugin.ts +18 -11
- package/src/parse/index.ts +16 -6
- package/src/parse/loose_plugin.ts +2 -2
- package/src/parse/record_call_lparen_plugin.ts +85 -0
- package/src/transform/constants.ts +11 -11
- package/src/transform/gts.ts +198 -209
- package/src/transform/volar/collect_tokens.ts +44 -18
- package/src/transform/volar/index.ts +18 -29
- package/src/transform/volar/mappings.ts +57 -38
- package/src/transform/volar/printer.ts +123 -0
- package/src/transform/volar/replacements.ts +96 -15
- package/src/transform/volar/walker.ts +92 -204
- package/src/types.ts +16 -4
- package/src/transform/NOTES.md +0 -112
package/package.json
CHANGED
|
@@ -1,32 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gi-tcg/gts-transpiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"repository": "https://github.com/piovium/gts.git",
|
|
5
|
+
"license": "Apache-2.0",
|
|
4
6
|
"type": "module",
|
|
5
7
|
"exports": {
|
|
6
8
|
".": {
|
|
7
|
-
"types": "./
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
8
10
|
"bun": "./src/index.ts",
|
|
9
|
-
"
|
|
11
|
+
"default": "./dist/index.js"
|
|
10
12
|
}
|
|
11
13
|
},
|
|
12
14
|
"files": [
|
|
13
15
|
"src",
|
|
14
16
|
"dist"
|
|
15
17
|
],
|
|
16
|
-
"scripts": {
|
|
17
|
-
"build": "bun build --outdir=dist --target=node --format=esm --conditions='bun' --packages=external src/index.ts",
|
|
18
|
-
"prepublishOnly": "bun run build"
|
|
19
|
-
},
|
|
18
|
+
"scripts": {},
|
|
20
19
|
"dependencies": {
|
|
21
20
|
"@jridgewell/sourcemap-codec": "^1.5.5",
|
|
22
21
|
"@sveltejs/acorn-typescript": "^1.0.8",
|
|
23
|
-
"acorn": "
|
|
24
|
-
"esrap": "
|
|
22
|
+
"acorn": "8.15.0",
|
|
23
|
+
"esrap": "2.2.1",
|
|
25
24
|
"magic-string": "^0.30.21",
|
|
26
25
|
"zimmerframe": "^1.1.4"
|
|
27
26
|
},
|
|
28
27
|
"devDependencies": {
|
|
29
|
-
"@types/
|
|
28
|
+
"@types/node": "^24.3.0",
|
|
30
29
|
"@types/estree": "^1.0.8",
|
|
31
30
|
"@volar/language-core": "^2.4.27"
|
|
32
31
|
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import type { TranspileOption } from "./transform/gts";
|
|
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
|
+
|
|
88
|
+
export interface GtsConfig extends TranspileOption {}
|
|
89
|
+
|
|
90
|
+
export interface PackageJson {
|
|
91
|
+
gamingTs?: GtsConfig;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
type ReadFileFn = (path: string, encoding: "utf8") => string;
|
|
95
|
+
type ReadFileAsyncFn = (path: string, encoding: "utf8") => Promise<string>;
|
|
96
|
+
|
|
97
|
+
export interface ResolveGtsConfigSyncOptions {
|
|
98
|
+
readFileFn: ReadFileFn;
|
|
99
|
+
cwd?: string;
|
|
100
|
+
stopDir?: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface ResolveGtsConfigAsyncOptions {
|
|
104
|
+
readFileFn: ReadFileAsyncFn;
|
|
105
|
+
cwd?: string;
|
|
106
|
+
stopDir?: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const DEFAULT_GTS_CONFIG: Required<GtsConfig> = {
|
|
110
|
+
runtimeImportSource: "@gi-tcg/gts-runtime",
|
|
111
|
+
providerImportSource: "@gi-tcg/core/gts",
|
|
112
|
+
shortcutFunctionPreludes: [
|
|
113
|
+
"cryo",
|
|
114
|
+
"hydro",
|
|
115
|
+
"pyro",
|
|
116
|
+
"electro",
|
|
117
|
+
"anemo",
|
|
118
|
+
"geo",
|
|
119
|
+
"dendro",
|
|
120
|
+
"omni",
|
|
121
|
+
],
|
|
122
|
+
queryBindings: ["my", "opp"],
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
function* resolveGtsConfigImpl(
|
|
126
|
+
filePath: string,
|
|
127
|
+
inlineConfig: GtsConfig = {},
|
|
128
|
+
options: ResolveGtsConfigAsyncOptions | ResolveGtsConfigSyncOptions,
|
|
129
|
+
): Generator<string | Promise<string>, Required<GtsConfig>, string> {
|
|
130
|
+
const startDir = normalizeStartDir(filePath, options.cwd);
|
|
131
|
+
const stopDir = options.stopDir ? path.resolve(options.stopDir) : void 0;
|
|
132
|
+
const pkgConfig = yield* findNearestPackageConfig(
|
|
133
|
+
options.readFileFn,
|
|
134
|
+
startDir,
|
|
135
|
+
stopDir,
|
|
136
|
+
);
|
|
137
|
+
return {
|
|
138
|
+
...DEFAULT_GTS_CONFIG,
|
|
139
|
+
...pkgConfig,
|
|
140
|
+
...inlineConfig,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export async function resolveGtsConfig(
|
|
145
|
+
filePath: string,
|
|
146
|
+
inlineConfig: GtsConfig,
|
|
147
|
+
options: ResolveGtsConfigAsyncOptions,
|
|
148
|
+
): Promise<Required<GtsConfig>> {
|
|
149
|
+
const generator = resolveGtsConfigImpl(filePath, inlineConfig, options);
|
|
150
|
+
let result = generator.next();
|
|
151
|
+
while (!result.done) {
|
|
152
|
+
const toRead = result.value;
|
|
153
|
+
const content = await toRead;
|
|
154
|
+
result = generator.next(content);
|
|
155
|
+
}
|
|
156
|
+
return result.value;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function resolveGtsConfigSync(
|
|
160
|
+
filePath: string,
|
|
161
|
+
inlineConfig: GtsConfig,
|
|
162
|
+
options: ResolveGtsConfigSyncOptions,
|
|
163
|
+
): Required<GtsConfig> {
|
|
164
|
+
const generator = resolveGtsConfigImpl(filePath, inlineConfig, options);
|
|
165
|
+
let result = generator.next();
|
|
166
|
+
while (!result.done) {
|
|
167
|
+
const toRead = result.value;
|
|
168
|
+
if (toRead instanceof Promise) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
"resolveGtsConfigSync received a Promise. Did you mean to use resolveGtsConfig instead?",
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
const content = toRead;
|
|
174
|
+
result = generator.next(content);
|
|
175
|
+
}
|
|
176
|
+
return result.value;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function normalizeStartDir(sourceFile: string, cwd?: string): string {
|
|
180
|
+
const absolute = path.isAbsolute(sourceFile)
|
|
181
|
+
? sourceFile
|
|
182
|
+
: path.resolve(cwd || ".", sourceFile);
|
|
183
|
+
return path.dirname(absolute);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function* findNearestPackageConfig(
|
|
187
|
+
readFileFn: ReadFileFn | ReadFileAsyncFn,
|
|
188
|
+
startDir: string,
|
|
189
|
+
stopDir?: string,
|
|
190
|
+
): Generator<string | Promise<string>, GtsConfig, string> {
|
|
191
|
+
let currentDir = startDir;
|
|
192
|
+
while (true) {
|
|
193
|
+
const pkgPath = path.resolve(currentDir, "package.json");
|
|
194
|
+
const config = yield* readPackageConfig(readFileFn, pkgPath);
|
|
195
|
+
if (config) {
|
|
196
|
+
return config;
|
|
197
|
+
}
|
|
198
|
+
const parentDir = path.dirname(currentDir);
|
|
199
|
+
if (parentDir === currentDir) {
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
if (stopDir && currentDir === stopDir) {
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
currentDir = parentDir;
|
|
206
|
+
}
|
|
207
|
+
return {};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function* readPackageConfig(
|
|
211
|
+
readFileFn: ReadFileFn | ReadFileAsyncFn,
|
|
212
|
+
pkgPath: string,
|
|
213
|
+
): Generator<string | Promise<string>, GtsConfig | undefined, string> {
|
|
214
|
+
try {
|
|
215
|
+
const content = yield readFileFn(pkgPath, "utf8");
|
|
216
|
+
const parsed = JSON.parse(content) as PackageJson;
|
|
217
|
+
return parsed.gamingTs;
|
|
218
|
+
} catch {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -11,7 +11,7 @@ export { GtsTranspilerError } from "./error";
|
|
|
11
11
|
export function transpile(
|
|
12
12
|
source: string,
|
|
13
13
|
filename: string,
|
|
14
|
-
option: TranspileOption
|
|
14
|
+
option: TranspileOption,
|
|
15
15
|
): TranspileResult {
|
|
16
16
|
const ast = parse(source);
|
|
17
17
|
return transform(ast, option, {
|
|
@@ -23,9 +23,11 @@ export function transpile(
|
|
|
23
23
|
export function transpileForVolar(
|
|
24
24
|
source: string,
|
|
25
25
|
filename: string,
|
|
26
|
-
option: TranspileOption
|
|
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,3 +35,10 @@ export function transpileForVolar(
|
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
export type { TranspileOption, TranspileResult, VolarMappingResult };
|
|
38
|
+
export {
|
|
39
|
+
resolveGtsConfig,
|
|
40
|
+
resolveGtsConfigSync,
|
|
41
|
+
type GtsConfig,
|
|
42
|
+
path,
|
|
43
|
+
type PathPolyfill,
|
|
44
|
+
} from "./config";
|
package/src/keywords.ts
CHANGED
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
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Parser } from "acorn";
|
|
2
|
-
import type { Program } from "estree";
|
|
2
|
+
import type { Position, Program } from "estree";
|
|
3
3
|
import { tsPlugin } from "@sveltejs/acorn-typescript";
|
|
4
4
|
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
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
export const DEFAULT_SHORTCUT_FUNCTION_PRELUDES =
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export const DEFAULT_QUERY_BINDINGS = ["my", "opp"];
|
|
1
|
+
export const DEFAULT_SHORTCUT_FUNCTION_PRELUDES: string[] = [
|
|
2
|
+
"cryo",
|
|
3
|
+
"hydro",
|
|
4
|
+
"pyro",
|
|
5
|
+
"electro",
|
|
6
|
+
"anemo",
|
|
7
|
+
"geo",
|
|
8
|
+
"dendro",
|
|
9
|
+
"omni",
|
|
10
|
+
];
|
|
11
|
+
export const DEFAULT_QUERY_BINDINGS: string[] = ["my", "opp"];
|