@scaffscript/core 0.2.1 → 0.2.5
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.cjs +2511 -0
- package/dist/index.d.ts +95 -0
- package/package.json +1 -1
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2511 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
12
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
20
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
21
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
+
for (let key of __getOwnPropNames(mod))
|
|
23
|
+
if (!__hasOwnProp.call(to, key))
|
|
24
|
+
__defProp(to, key, {
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
26
|
+
enumerable: true
|
|
27
|
+
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
30
|
+
return to;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// src/utils/array.ts
|
|
34
|
+
function swapAndPop(arr, idx) {
|
|
35
|
+
const lastIdx = arr.length - 1;
|
|
36
|
+
const last = arr[lastIdx];
|
|
37
|
+
arr[lastIdx] = arr[idx];
|
|
38
|
+
arr[idx] = last;
|
|
39
|
+
return arr.pop();
|
|
40
|
+
}
|
|
41
|
+
// src/utils/path.ts
|
|
42
|
+
var import_path = __toESM(require("path"));
|
|
43
|
+
function resolvePath(p) {
|
|
44
|
+
return import_path.default.resolve(p);
|
|
45
|
+
}
|
|
46
|
+
function normalizePath(p) {
|
|
47
|
+
return p.replace(/\\/g, "/");
|
|
48
|
+
}
|
|
49
|
+
function relativePath(p, root) {
|
|
50
|
+
return import_path.default.relative(root, p);
|
|
51
|
+
}
|
|
52
|
+
function isAbsolute(p) {
|
|
53
|
+
return import_path.default.isAbsolute(p);
|
|
54
|
+
}
|
|
55
|
+
function commonPath(paths) {
|
|
56
|
+
const split = paths.map((p) => p.split(/[\\/]/));
|
|
57
|
+
return !split.length ? null : split[0].filter((_, i) => split.every((p) => p[i] === split[0][i])).join("/");
|
|
58
|
+
}
|
|
59
|
+
// src/utils/log.ts
|
|
60
|
+
var log = {
|
|
61
|
+
debug: (msg) => console.log("\x1B[90m[DEBUG]\x1B[0m ", msg),
|
|
62
|
+
info: (msg) => console.log("\x1B[36m[INFO]\x1B[0m ", msg),
|
|
63
|
+
warn: (msg) => console.log("\x1B[33m[WARN]\x1B[0m ", msg),
|
|
64
|
+
error: (msg) => console.log("\x1B[31m[ERROR]\x1B[0m ", msg)
|
|
65
|
+
};
|
|
66
|
+
// src/utils/fs.ts
|
|
67
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
68
|
+
var import_fs = require("fs");
|
|
69
|
+
async function clearOutDir() {
|
|
70
|
+
await import_promises.default.rm(".out", { recursive: true, force: true });
|
|
71
|
+
}
|
|
72
|
+
async function fileExists(path2) {
|
|
73
|
+
if (typeof Bun !== "undefined") {
|
|
74
|
+
return Bun.file(path2).exists();
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
await import_promises.default.access(path2, import_fs.constants.R_OK);
|
|
78
|
+
return true;
|
|
79
|
+
} catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function deleteDir(target, root) {
|
|
84
|
+
const resolvedTarget = resolvePath(target);
|
|
85
|
+
const resolvedRoot = resolvePath(root);
|
|
86
|
+
const rel = relativePath(resolvedTarget, resolvedRoot);
|
|
87
|
+
if (!rel || rel === ".") {
|
|
88
|
+
log.error(`Failed to delete directory \x1B[32m${resolvedTarget}\x1B[0m: Cannot delete root directory.`);
|
|
89
|
+
return;
|
|
90
|
+
} else if (resolvedTarget === resolvedRoot) {
|
|
91
|
+
log.error(`Failed to delete directory \x1B[32m${resolvedTarget}\x1B[0m: Cannot delete root directory.`);
|
|
92
|
+
return;
|
|
93
|
+
} else if (rel.startsWith("..") || isAbsolute(rel)) {
|
|
94
|
+
log.error(`Failed to delete directory \x1B[32m${resolvedTarget}\x1B[0m: Outside root \x1B[32m${resolvedRoot}\x1B[0m.`);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
await import_promises.default.rm(resolvedTarget, { recursive: true, force: true });
|
|
98
|
+
}
|
|
99
|
+
// src/cli/args.ts
|
|
100
|
+
async function parseArgs(...args) {
|
|
101
|
+
console.log("");
|
|
102
|
+
console.log("===========================================================================");
|
|
103
|
+
console.log("ScaffScript - A superset language of GML with TypeScript-like module system");
|
|
104
|
+
console.log("===========================================================================");
|
|
105
|
+
if (!args.length) {
|
|
106
|
+
log.error("No command specified. Add \x1B[32m--help\x1B[0m argument for more information. Aborting...");
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
const cmd = args[0];
|
|
110
|
+
switch (cmd) {
|
|
111
|
+
case "gen":
|
|
112
|
+
case "generate":
|
|
113
|
+
const yypPath = args[1];
|
|
114
|
+
if (!yypPath) {
|
|
115
|
+
log.error("No project path specified. Please specify a valid project path. Aborting...");
|
|
116
|
+
return null;
|
|
117
|
+
} else if (!yypPath.endsWith(".yyp")) {
|
|
118
|
+
log.error(`Invalid project path: \x1B[33m${yypPath}\x1B[0m. Please specify a valid \x1B[32m.yyp\x1B[0m file. Aborting...`);
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
const optionList = [...args].slice(2);
|
|
122
|
+
const options = {
|
|
123
|
+
integrate: optionList.includes("-i") || optionList.includes("--integrate"),
|
|
124
|
+
noIntegration: optionList.includes("-!i") || optionList.includes("--no-integration")
|
|
125
|
+
};
|
|
126
|
+
if (options.integrate && options.noIntegration) {
|
|
127
|
+
log.error("Cannot specify both \x1B[33m--integrate\x1B[0m and \x1B[33m--no-integration\x1B[0m. Aborting...");
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const exists = await fileExists(normalizePath(resolvePath(yypPath)));
|
|
131
|
+
if (!exists) {
|
|
132
|
+
log.error(`Project path \x1B[33m${yypPath}\x1B[0m not found. Aborting...`);
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
cmd: "generate",
|
|
137
|
+
options,
|
|
138
|
+
projectPath: normalizePath(resolvePath(yypPath))
|
|
139
|
+
};
|
|
140
|
+
case "help":
|
|
141
|
+
case "-h":
|
|
142
|
+
case "--help":
|
|
143
|
+
console.log(`\x1B[33mscaff \x1B[32m<command> \x1B[34m[args]\x1B[0m`);
|
|
144
|
+
console.log(`\x1B[32m<command>\x1B[0m: The command to execute.`);
|
|
145
|
+
console.log(`\x1B[34m[args]\x1B[0m: Optional arguments.`);
|
|
146
|
+
console.log("");
|
|
147
|
+
console.log("Commands:");
|
|
148
|
+
console.log(" generate <source_path> <project_path> Generate source code from the given path to the given project");
|
|
149
|
+
console.log(" aliases: gen");
|
|
150
|
+
console.log(" example: generate ./src ./my-game.yyp");
|
|
151
|
+
console.log(" help Show this help message");
|
|
152
|
+
console.log(" aliases: -h, --help");
|
|
153
|
+
console.log("");
|
|
154
|
+
return {
|
|
155
|
+
cmd: "help"
|
|
156
|
+
};
|
|
157
|
+
default:
|
|
158
|
+
log.error(`Invalid command: \x1B[33m${cmd}\x1B[0m. Aborting...`);
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// src/fs/scan.ts
|
|
163
|
+
var import_promises2 = require("node:fs/promises");
|
|
164
|
+
var import_node_path = require("node:path");
|
|
165
|
+
var import_node_url = require("node:url");
|
|
166
|
+
|
|
167
|
+
// src/runtime/detect.ts
|
|
168
|
+
var runtime = typeof Bun !== "undefined" ? "bun" : "node";
|
|
169
|
+
|
|
170
|
+
// src/runtime/bun.ts
|
|
171
|
+
var BunRuntime = {
|
|
172
|
+
async readText(path3) {
|
|
173
|
+
return Bun.file(path3).text();
|
|
174
|
+
},
|
|
175
|
+
async writeText(path3, data) {
|
|
176
|
+
await Bun.write(path3, data);
|
|
177
|
+
},
|
|
178
|
+
async delete(path3) {
|
|
179
|
+
await Bun.file(path3).delete();
|
|
180
|
+
},
|
|
181
|
+
async prompt(message) {
|
|
182
|
+
const input = prompt(message);
|
|
183
|
+
return input?.trim() ?? "";
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// src/runtime/node.ts
|
|
188
|
+
var fs3;
|
|
189
|
+
var readline;
|
|
190
|
+
async function ensureModules() {
|
|
191
|
+
if (!fs3)
|
|
192
|
+
fs3 = await import("fs/promises");
|
|
193
|
+
if (!readline)
|
|
194
|
+
readline = await import("readline/promises");
|
|
195
|
+
}
|
|
196
|
+
var NodeRuntime = {
|
|
197
|
+
async readText(path3) {
|
|
198
|
+
await ensureModules();
|
|
199
|
+
return fs3.readFile(path3, "utf8");
|
|
200
|
+
},
|
|
201
|
+
async writeText(path3, data) {
|
|
202
|
+
await ensureModules();
|
|
203
|
+
const { dirname } = await import("node:path");
|
|
204
|
+
await fs3.mkdir(dirname(path3), { recursive: true });
|
|
205
|
+
await fs3.writeFile(path3, data, "utf8");
|
|
206
|
+
},
|
|
207
|
+
async delete(path3) {
|
|
208
|
+
await ensureModules();
|
|
209
|
+
await fs3.unlink(path3);
|
|
210
|
+
},
|
|
211
|
+
async prompt(message) {
|
|
212
|
+
await ensureModules();
|
|
213
|
+
const { stdin, stdout } = await import("process");
|
|
214
|
+
const rl = readline.createInterface({
|
|
215
|
+
input: stdin,
|
|
216
|
+
output: stdout
|
|
217
|
+
});
|
|
218
|
+
const answer = await rl.question(message + " ");
|
|
219
|
+
rl.close();
|
|
220
|
+
return answer.trim();
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// src/runtime/adapter.ts
|
|
225
|
+
var fsRuntime = runtime === "bun" ? BunRuntime : NodeRuntime;
|
|
226
|
+
// src/fs/scan.ts
|
|
227
|
+
var DEFAULT_PATH = resolvePath("src");
|
|
228
|
+
var CONFIG_FILES = [
|
|
229
|
+
"scaff.config.ts",
|
|
230
|
+
"scaff.config.mjs",
|
|
231
|
+
"scaff.config.js",
|
|
232
|
+
"scaff.config.cjs",
|
|
233
|
+
"scaff.config.json"
|
|
234
|
+
];
|
|
235
|
+
function dirIsIgnored(dir) {
|
|
236
|
+
const slugs = normalizePath(dir).split("/");
|
|
237
|
+
return slugs.find((slug) => slug.startsWith("_")) ? true : false;
|
|
238
|
+
}
|
|
239
|
+
async function findConfig(filename) {
|
|
240
|
+
let dir = process.cwd();
|
|
241
|
+
while (true) {
|
|
242
|
+
const full = import_node_path.join(dir, filename);
|
|
243
|
+
if (await fileExists(full)) {
|
|
244
|
+
return full;
|
|
245
|
+
}
|
|
246
|
+
const parent = import_node_path.dirname(dir);
|
|
247
|
+
if (parent === dir) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
dir = parent;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
async function getScaffFiles(path3) {
|
|
254
|
+
log.debug(`Scanning for \x1B[34m*.ss\x1B[0m and \x1B[34m*.gml\x1B[0m files in \x1B[32m${path3}\x1B[0m...`);
|
|
255
|
+
const files = await import_promises2.readdir(path3, { withFileTypes: true, recursive: true });
|
|
256
|
+
const vFiles = files.filter((file) => file.isFile() && !file.name.startsWith("_") && !dirIsIgnored(file.parentPath) && (file.name.endsWith(".ss") || file.name.endsWith(".gml"))).map((file) => {
|
|
257
|
+
return {
|
|
258
|
+
name: file.name,
|
|
259
|
+
path: normalizePath(resolvePath(file.parentPath)),
|
|
260
|
+
isScaff: file.name.endsWith(".ss"),
|
|
261
|
+
isIndex: file.name === "index.ss",
|
|
262
|
+
toGenerate: false,
|
|
263
|
+
content: "",
|
|
264
|
+
childs: []
|
|
265
|
+
};
|
|
266
|
+
});
|
|
267
|
+
log.info(`Found \x1B[32m${vFiles.filter((file) => file.isScaff).length} \x1B[34m*.ss\x1B[0m file(s) and \x1B[32m${vFiles.filter((file) => !file.isScaff).length}\x1B[0m \x1B[34m*.gml\x1B[0m file(s).`);
|
|
268
|
+
return vFiles;
|
|
269
|
+
}
|
|
270
|
+
async function getScaffConfig() {
|
|
271
|
+
let confPath = null;
|
|
272
|
+
for (const name of CONFIG_FILES) {
|
|
273
|
+
confPath = await findConfig(name);
|
|
274
|
+
if (confPath)
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
let conf = {};
|
|
278
|
+
if (confPath) {
|
|
279
|
+
const ext = confPath.split(".").pop();
|
|
280
|
+
if (ext === "cjs" || ext === "json") {
|
|
281
|
+
conf = require(confPath);
|
|
282
|
+
} else {
|
|
283
|
+
const mod = await import(import_node_url.pathToFileURL(confPath).href);
|
|
284
|
+
conf = mod.default ?? mod;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (!confPath) {
|
|
288
|
+
const pkgPath = await findConfig("package.json");
|
|
289
|
+
if (pkgPath) {
|
|
290
|
+
try {
|
|
291
|
+
const raw = await fsRuntime.readText(pkgPath);
|
|
292
|
+
const pkg = JSON.parse(raw);
|
|
293
|
+
if (pkg.scaff) {
|
|
294
|
+
conf = pkg.scaff;
|
|
295
|
+
}
|
|
296
|
+
} catch {}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
acceptAllIntegrations: conf.acceptAllIntegrations ?? false,
|
|
301
|
+
clearOutputDir: conf.clearOutputDir ?? false,
|
|
302
|
+
counterStart: conf.counterStart ?? 1,
|
|
303
|
+
debugLevel: conf.debugLevel ?? 0,
|
|
304
|
+
integrationOption: conf.integrationOption ?? {},
|
|
305
|
+
noBackup: conf.noBackup ?? false,
|
|
306
|
+
noIntegration: conf.noIntegration ?? true,
|
|
307
|
+
onNotFound: conf.onNotFound ?? "error",
|
|
308
|
+
path: conf.path ?? {},
|
|
309
|
+
production: conf.production ?? false,
|
|
310
|
+
source: conf.source ?? "./src",
|
|
311
|
+
tabType: conf.tabType ?? "1t",
|
|
312
|
+
targetPlatform: conf.targetPlatform ?? "all",
|
|
313
|
+
useGmAssetPath: conf.useGmAssetPath ?? false
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
// src/parser/regex.ts
|
|
317
|
+
var commentRegex = /(?<!\/)\/\/(?!\/)[^\n]*|\/\*(?!\*)[\s\S]*?\*\//g;
|
|
318
|
+
var implRegex = /impl\s+(?<name>[\w+]+)\s+\{\s+(?<body>[.\s\S]+?)\}/g;
|
|
319
|
+
var implHeaderRegex = /impl\s+(?<name>\w+)\s*\{/g;
|
|
320
|
+
var fnParamsRegex = /\((?<params>[^)]*)\)/g;
|
|
321
|
+
var arrowFnHeaderRegex = /\b(?:(?:const|let|var)\s+)?(?<name>\w+)\s*=\s*(?<params>\([^)]*\)|\w+)\s*=>/g;
|
|
322
|
+
var modControlRegex = /(?<cmd>export|import|include)\s+(?<mod>\*|\{[^}]+\}|[A-Za-z0-9_]+)\s+(?<src>from)\s+(?<path>["'][^"']+["'])\s*;?/g;
|
|
323
|
+
var contentModRegex = /@(?<cmd>content|valueof|typeof|nameof)\s+(?<mod>[A-Za-z0-9_]+)/g;
|
|
324
|
+
var useModRegex = /@use\s+(?<mod>[A-Za-z0-9_]+)\s+(?<body>\{[.\s\S]+?\})/g;
|
|
325
|
+
var contentModShortRegex = /@:(?<mod>[A-Za-z0-9_]+)\s*/g;
|
|
326
|
+
var specialValueRegex = /@(?<cmd>now|today|version|file|line|counter)/g;
|
|
327
|
+
var tabRegex = {
|
|
328
|
+
oneTab: /^(\t*)/gm,
|
|
329
|
+
twoSpaces: /^(\ {2})*/gm,
|
|
330
|
+
fourSpaces: /^(\ {4})*/gm
|
|
331
|
+
};
|
|
332
|
+
var intgRegex = /intg\s+(?<targets>\*|\{[^}]+\}|[A-Za-z0-9_]+)\s+to\s+(?<path>["'][^"']+["'])\s*;?/g;
|
|
333
|
+
var intgBlockRegex = /#\[(?<name>[^\]]+)\](?<body>[\s\S]*?(?=\n?#\[[^\]]+\]|$))/g;
|
|
334
|
+
function countSubstring(str, sub) {
|
|
335
|
+
const regex = new RegExp(sub, "g");
|
|
336
|
+
const matches = str.match(regex);
|
|
337
|
+
return matches ? matches.length : 0;
|
|
338
|
+
}
|
|
339
|
+
function getTabLevels(str, tabType) {
|
|
340
|
+
const regex = tabType === "1t" ? tabRegex.oneTab : tabType === "2s" ? tabRegex.twoSpaces : tabRegex.fourSpaces;
|
|
341
|
+
const match = [...str.matchAll(regex)].map((m) => m[0]);
|
|
342
|
+
return match.map((m) => countSubstring(m, tabType === "1t" ? "\t" : tabType === "2s" ? " " : " "));
|
|
343
|
+
}
|
|
344
|
+
// package.json
|
|
345
|
+
var package_default = {
|
|
346
|
+
name: "@scaffscript/core",
|
|
347
|
+
version: "0.2.3",
|
|
348
|
+
repository: {
|
|
349
|
+
type: "git",
|
|
350
|
+
url: "https://github.com/undervolta/scaffscript"
|
|
351
|
+
},
|
|
352
|
+
main: "dist/index.cjs",
|
|
353
|
+
devDependencies: {
|
|
354
|
+
"@types/bun": "latest"
|
|
355
|
+
},
|
|
356
|
+
peerDependencies: {
|
|
357
|
+
typescript: "^5"
|
|
358
|
+
},
|
|
359
|
+
bin: {
|
|
360
|
+
scaff: "./dist/index.cjs"
|
|
361
|
+
},
|
|
362
|
+
description: "A minimal superset language of GML with TypeScript-like module system",
|
|
363
|
+
files: [
|
|
364
|
+
"dist"
|
|
365
|
+
],
|
|
366
|
+
keywords: [
|
|
367
|
+
"gamemaker",
|
|
368
|
+
"gml",
|
|
369
|
+
"scaff",
|
|
370
|
+
"script",
|
|
371
|
+
"superset",
|
|
372
|
+
"module",
|
|
373
|
+
"cli"
|
|
374
|
+
],
|
|
375
|
+
license: "MIT",
|
|
376
|
+
scripts: {
|
|
377
|
+
build: "bun run build:all",
|
|
378
|
+
"build:node": "bun build src/index-node.ts --outfile dist/index.cjs --target node --format cjs",
|
|
379
|
+
"build:bun": "bun build src/index-bun.ts --outfile build/index.mjs --target bun --format esm",
|
|
380
|
+
"build:all": "bun run build:node && bun run build:bun",
|
|
381
|
+
dev: "bun run src/index-node.ts",
|
|
382
|
+
prelink: "bun run build"
|
|
383
|
+
},
|
|
384
|
+
type: "module"
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
// src/parser/special-value.ts
|
|
388
|
+
function parseSpecialValues(file, counter) {
|
|
389
|
+
let res = file.content;
|
|
390
|
+
const matchComment = [...res.matchAll(commentRegex)];
|
|
391
|
+
for (const match of matchComment) {
|
|
392
|
+
res = res.replace(match[0], "");
|
|
393
|
+
}
|
|
394
|
+
const matchSpecial = [...res.matchAll(specialValueRegex)];
|
|
395
|
+
for (const match of matchSpecial) {
|
|
396
|
+
const { cmd } = match.groups;
|
|
397
|
+
switch (cmd) {
|
|
398
|
+
case "now":
|
|
399
|
+
res = res.replace(match[0], new Date().toISOString());
|
|
400
|
+
break;
|
|
401
|
+
case "today":
|
|
402
|
+
res = res.replace(match[0], new Date().toISOString().split("T")[0]);
|
|
403
|
+
break;
|
|
404
|
+
case "version":
|
|
405
|
+
res = res.replace(match[0], package_default.version);
|
|
406
|
+
break;
|
|
407
|
+
case "file":
|
|
408
|
+
res = res.replace(match[0], file.name);
|
|
409
|
+
break;
|
|
410
|
+
case "line":
|
|
411
|
+
res = res.replace(match[0], String(countSubstring(res.slice(0, match.index), `
|
|
412
|
+
`) + 1));
|
|
413
|
+
break;
|
|
414
|
+
case "counter":
|
|
415
|
+
res = res.replace(match[0], String(counter.count));
|
|
416
|
+
counter.count++;
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return {
|
|
421
|
+
content: res,
|
|
422
|
+
counter: counter.count
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// src/fs/grouping.ts
|
|
427
|
+
async function readAndSplitFiles(files, config) {
|
|
428
|
+
const res = {
|
|
429
|
+
generate: [],
|
|
430
|
+
scaff: [],
|
|
431
|
+
normal: []
|
|
432
|
+
};
|
|
433
|
+
const intgRegex2 = /intg (\{[A-Za-z0-9,*\s]+\}|[A-Za-z0-9,*]+) to/;
|
|
434
|
+
const implFiles = [];
|
|
435
|
+
const exports2 = [];
|
|
436
|
+
const indexes = [];
|
|
437
|
+
const counter = { count: config.counterStart };
|
|
438
|
+
for (const file of files) {
|
|
439
|
+
file.content = (await fsRuntime.readText(`${file.path}/${file.name}`)).replace(/\r\n/g, `
|
|
440
|
+
`);
|
|
441
|
+
file.toGenerate = intgRegex2.test(file.content);
|
|
442
|
+
file.name = file.name.replace(".ss", "");
|
|
443
|
+
if (file.isScaff) {
|
|
444
|
+
const { content: parsedContent, counter: newCounter } = parseSpecialValues(file, counter);
|
|
445
|
+
file.content = parsedContent;
|
|
446
|
+
counter.count = newCounter;
|
|
447
|
+
}
|
|
448
|
+
if (file.isIndex) {
|
|
449
|
+
file.name = "";
|
|
450
|
+
indexes.push({ file, depth: file.path.split("/").filter(Boolean).length });
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
const matchExport = [...file.content.matchAll(modControlRegex)];
|
|
454
|
+
implRegex.lastIndex = 0;
|
|
455
|
+
if (matchExport.length)
|
|
456
|
+
exports2.push({ file, depth: file.path.split("/").filter(Boolean).length });
|
|
457
|
+
else if (implRegex.test(file.content)) {
|
|
458
|
+
implRegex.lastIndex = 0;
|
|
459
|
+
const implMatches = [...file.content.matchAll(implRegex)];
|
|
460
|
+
if (implMatches.length) {
|
|
461
|
+
for (const match of implMatches) {
|
|
462
|
+
const className = match.groups.name;
|
|
463
|
+
if (className && file.content.includes(`class ${className} {`)) {
|
|
464
|
+
file.childs.push(file);
|
|
465
|
+
res.scaff.push(file);
|
|
466
|
+
} else
|
|
467
|
+
implFiles.push(file);
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
} else
|
|
471
|
+
implFiles.push(file);
|
|
472
|
+
} else if (file.isScaff && file.toGenerate)
|
|
473
|
+
res.generate.push(file);
|
|
474
|
+
else if (file.isScaff)
|
|
475
|
+
res.scaff.push(file);
|
|
476
|
+
else
|
|
477
|
+
res.normal.push(file);
|
|
478
|
+
}
|
|
479
|
+
exports2.sort((a, b) => b.depth - a.depth);
|
|
480
|
+
indexes.sort((a, b) => b.depth - a.depth);
|
|
481
|
+
for (const fileHandle of exports2) {
|
|
482
|
+
implRegex.lastIndex = 0;
|
|
483
|
+
if (implRegex.test(fileHandle.file.content))
|
|
484
|
+
implFiles.push(fileHandle.file);
|
|
485
|
+
else if (fileHandle.file.isScaff && fileHandle.file.toGenerate)
|
|
486
|
+
res.generate.push(fileHandle.file);
|
|
487
|
+
else
|
|
488
|
+
res.scaff.push(fileHandle.file);
|
|
489
|
+
}
|
|
490
|
+
for (const fileHandle of indexes) {
|
|
491
|
+
implRegex.lastIndex = 0;
|
|
492
|
+
if (implRegex.test(fileHandle.file.content))
|
|
493
|
+
implFiles.push(fileHandle.file);
|
|
494
|
+
else if (fileHandle.file.isScaff && fileHandle.file.toGenerate)
|
|
495
|
+
res.generate.push(fileHandle.file);
|
|
496
|
+
else
|
|
497
|
+
res.scaff.push(fileHandle.file);
|
|
498
|
+
}
|
|
499
|
+
for (const file of implFiles) {
|
|
500
|
+
const classMatch = file.content.match(implRegex);
|
|
501
|
+
if (!classMatch)
|
|
502
|
+
continue;
|
|
503
|
+
const className = classMatch[0].split(" ")[1];
|
|
504
|
+
let classFile = res.scaff.find((f) => f.content.includes(`class ${className} {`));
|
|
505
|
+
if (!classFile)
|
|
506
|
+
classFile = res.generate.find((f) => f.content.includes(`class ${className} {`));
|
|
507
|
+
if (classFile)
|
|
508
|
+
classFile.childs.push(file);
|
|
509
|
+
else {
|
|
510
|
+
if (config.onNotFound === "error") {
|
|
511
|
+
log.error(`Class \x1B[33m${className}\x1B[0m not found for file \x1B[34m${file.name}\x1B[0m. Aborting...`);
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
log.warn(`Class \x1B[33m${className}\x1B[0m not found for file \x1B[34m${file.name}\x1B[0m. Skipping this file...`);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return res;
|
|
518
|
+
}
|
|
519
|
+
// src/parser/import-module.ts
|
|
520
|
+
function countTabsBeforeSubstring(str, sub, tabChar) {
|
|
521
|
+
const idx = str.indexOf(sub);
|
|
522
|
+
if (idx === -1)
|
|
523
|
+
return -1;
|
|
524
|
+
const lineStart = str.lastIndexOf(`
|
|
525
|
+
`, idx - 1) + 1;
|
|
526
|
+
const segment = str.slice(lineStart, idx);
|
|
527
|
+
let count = 0;
|
|
528
|
+
for (const ch of segment) {
|
|
529
|
+
if (ch === tabChar)
|
|
530
|
+
count++;
|
|
531
|
+
else
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
534
|
+
return count;
|
|
535
|
+
}
|
|
536
|
+
function resolveImportPath(filePath, importPath, config) {
|
|
537
|
+
if (!config.path)
|
|
538
|
+
return resolvePath(`${filePath}/${importPath}`);
|
|
539
|
+
const useWildcard = Object.keys(config.path).filter((k) => k.endsWith("*")).find((k) => importPath.startsWith(k.slice(0, -1)));
|
|
540
|
+
if (useWildcard) {
|
|
541
|
+
const pathAlias = config.path[useWildcard]?.replace("*", "");
|
|
542
|
+
const dynPath = importPath.replace(useWildcard.slice(0, -2), "");
|
|
543
|
+
if (!pathAlias) {
|
|
544
|
+
log.error(`Path \x1B[33m${importPath}\x1B[0m not found in path aliases. Aborting...`);
|
|
545
|
+
return "";
|
|
546
|
+
}
|
|
547
|
+
if (pathAlias.startsWith("~")) {
|
|
548
|
+
return resolvePath(`${config.source}/${pathAlias.replace("~/", "").replace("~", "")}${dynPath}`);
|
|
549
|
+
}
|
|
550
|
+
return resolvePath(`${filePath}/${pathAlias}${dynPath}`);
|
|
551
|
+
} else if (config.path[importPath]) {
|
|
552
|
+
const pathAlias = config.path[importPath];
|
|
553
|
+
if (pathAlias.startsWith("~")) {
|
|
554
|
+
return resolvePath(`${config.source}/${pathAlias.replace("~/", "").replace("~", "")}`);
|
|
555
|
+
}
|
|
556
|
+
return resolvePath(`${filePath}/${pathAlias}`);
|
|
557
|
+
}
|
|
558
|
+
return resolvePath(`${filePath}/${importPath}`);
|
|
559
|
+
}
|
|
560
|
+
async function getModuleUsage(module2, fileGroup, file, config) {
|
|
561
|
+
const matches = [...file.content.matchAll(modControlRegex)].filter((match) => match.groups.cmd !== "export");
|
|
562
|
+
const limit = 10;
|
|
563
|
+
const results = [];
|
|
564
|
+
let isInvalid = false;
|
|
565
|
+
for (let i = 0;i < matches.length; i += limit) {
|
|
566
|
+
const batch = matches.slice(i, i + limit);
|
|
567
|
+
const res = await Promise.all(batch.map(async (match) => {
|
|
568
|
+
const { cmd, mod, path: path3 } = match.groups;
|
|
569
|
+
const res2 = {
|
|
570
|
+
cmd: null,
|
|
571
|
+
files: null,
|
|
572
|
+
modList: null,
|
|
573
|
+
targetPath: null,
|
|
574
|
+
targetStr: ""
|
|
575
|
+
};
|
|
576
|
+
if (!(cmd && mod && path3)) {
|
|
577
|
+
log.error(`Invalid module control statement: \x1B[34m${cmd} ${mod} from ${path3}\x1B[0m in \x1B[33m${file.name}\x1B[0m from \x1B[32m${file.path}\x1B[0m. Aborting...`);
|
|
578
|
+
isInvalid = true;
|
|
579
|
+
return null;
|
|
580
|
+
}
|
|
581
|
+
const fromPath = normalizePath(resolveImportPath(file.path, path3.slice(1, -1), config));
|
|
582
|
+
const modList = {};
|
|
583
|
+
const alias = {};
|
|
584
|
+
if (!module2[fromPath]) {
|
|
585
|
+
if (cmd === "include" && mod.startsWith("{") && (mod.includes('"') || mod.includes("'"))) {
|
|
586
|
+
const files = mod.slice(1, -1).split(",").map((m) => m.trim());
|
|
587
|
+
const filePaths = [];
|
|
588
|
+
for (const f of files) {
|
|
589
|
+
const filePath = normalizePath(resolvePath(`${file.path}/${f.slice(1, -1)}`));
|
|
590
|
+
const targetFile = fileGroup.normal.find((fl) => {
|
|
591
|
+
let targetFile2 = `${fl.path}/${fl.name}`;
|
|
592
|
+
let currFile = filePath;
|
|
593
|
+
if (!targetFile2.endsWith(".gml"))
|
|
594
|
+
targetFile2 += ".gml";
|
|
595
|
+
if (!currFile.endsWith(".gml"))
|
|
596
|
+
currFile += ".gml";
|
|
597
|
+
return targetFile2 === currFile;
|
|
598
|
+
});
|
|
599
|
+
if (targetFile)
|
|
600
|
+
filePaths.push(targetFile);
|
|
601
|
+
else if (await fileExists(filePath))
|
|
602
|
+
filePaths.push(filePath);
|
|
603
|
+
else {
|
|
604
|
+
if (config.onNotFound === "error") {
|
|
605
|
+
log.error(`File \x1B[33m${f.slice(1, -1)}\x1B[0m from \x1B[32m${file.path}\x1B[0m not found. Aborting...`);
|
|
606
|
+
isInvalid = true;
|
|
607
|
+
return null;
|
|
608
|
+
} else
|
|
609
|
+
log.warn(`File \x1B[33m${f.slice(1, -1)}\x1B[0m from \x1B[32m${file.path}\x1B[0m not found. Skipping this file...`);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
return {
|
|
613
|
+
cmd,
|
|
614
|
+
files: filePaths,
|
|
615
|
+
modList,
|
|
616
|
+
targetPath: fromPath,
|
|
617
|
+
targetStr: match[0]
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
log.error(`Path \x1B[33m${fromPath}\x1B[0m doesn't have any exported modules. Aborting...`);
|
|
621
|
+
isInvalid = true;
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
if (mod === "*" || mod.startsWith("{") && mod.endsWith("}")) {
|
|
625
|
+
const targetMods = mod === "*" ? null : mod.slice(1, -1).split(",").map((m) => {
|
|
626
|
+
const split = m.split(":");
|
|
627
|
+
const key = split[0].trim();
|
|
628
|
+
if (split.length === 1)
|
|
629
|
+
alias[key] = key;
|
|
630
|
+
else
|
|
631
|
+
alias[key] = split[1].trim();
|
|
632
|
+
if (!module2[fromPath]) {
|
|
633
|
+
if (config.onNotFound === "error") {
|
|
634
|
+
log.error(`Path \x1B[33m${fromPath}\x1B[0m doesn't have any exported modules. Aborting...`);
|
|
635
|
+
isInvalid = true;
|
|
636
|
+
} else
|
|
637
|
+
log.warn(`Path \x1B[33m${fromPath}\x1B[0m doesn't have any exported modules. Skipping this module...`);
|
|
638
|
+
return null;
|
|
639
|
+
}
|
|
640
|
+
if (!module2[fromPath][key]) {
|
|
641
|
+
if (config.onNotFound === "error") {
|
|
642
|
+
log.error(`Module \x1B[33m${key}\x1B[0m from \x1B[32m${fromPath}\x1B[0m not found. Aborting...`);
|
|
643
|
+
isInvalid = true;
|
|
644
|
+
} else
|
|
645
|
+
log.warn(`Module \x1B[33m${key}\x1B[0m from \x1B[32m${fromPath}\x1B[0m not found. Skipping this module...`);
|
|
646
|
+
return null;
|
|
647
|
+
}
|
|
648
|
+
return key;
|
|
649
|
+
}).filter(Boolean);
|
|
650
|
+
Object.entries(module2[fromPath]).forEach(([key, value]) => {
|
|
651
|
+
if (mod === "*")
|
|
652
|
+
modList[key] = { name: key, as: key, value, usingAlias: false };
|
|
653
|
+
else if (targetMods.includes(key)) {
|
|
654
|
+
const usingAlias = key in alias && alias[key] !== key;
|
|
655
|
+
modList[alias[key] ?? key] = {
|
|
656
|
+
name: key,
|
|
657
|
+
as: alias[key] ?? key,
|
|
658
|
+
value,
|
|
659
|
+
usingAlias
|
|
660
|
+
};
|
|
661
|
+
if (usingAlias) {
|
|
662
|
+
if (!module2[fromPath])
|
|
663
|
+
module2[fromPath] = {};
|
|
664
|
+
module2[fromPath][`@${alias[key]}`] = value;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
} else {
|
|
669
|
+
if (!module2[fromPath][mod]) {
|
|
670
|
+
if (config.onNotFound === "error") {
|
|
671
|
+
log.error(`Module \x1B[33m${mod}\x1B[0m from \x1B[32m${fromPath}\x1B[0m not found. Aborting...`);
|
|
672
|
+
isInvalid = true;
|
|
673
|
+
} else
|
|
674
|
+
log.warn(`Module \x1B[33m${mod}\x1B[0m from \x1B[32m${fromPath}\x1B[0m not found. Skipping this module...`);
|
|
675
|
+
return res2;
|
|
676
|
+
}
|
|
677
|
+
modList[mod] = {
|
|
678
|
+
name: mod,
|
|
679
|
+
as: mod,
|
|
680
|
+
value: module2[fromPath][mod],
|
|
681
|
+
usingAlias: false
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
return {
|
|
685
|
+
cmd,
|
|
686
|
+
files: null,
|
|
687
|
+
modList,
|
|
688
|
+
targetPath: fromPath,
|
|
689
|
+
targetStr: match[0]
|
|
690
|
+
};
|
|
691
|
+
}));
|
|
692
|
+
if (isInvalid)
|
|
693
|
+
break;
|
|
694
|
+
results.push(...res);
|
|
695
|
+
}
|
|
696
|
+
return !isInvalid && results.every((r) => r) ? results : null;
|
|
697
|
+
}
|
|
698
|
+
async function implementModules(module2, fileGroup, file, config, mods) {
|
|
699
|
+
if (!mods)
|
|
700
|
+
mods = await getModuleUsage(module2, fileGroup, file, config);
|
|
701
|
+
if (!mods)
|
|
702
|
+
return null;
|
|
703
|
+
const toRemove = [...file.content.matchAll(modControlRegex)];
|
|
704
|
+
for (const rm of toRemove) {
|
|
705
|
+
if (rm.groups?.cmd !== "include")
|
|
706
|
+
file.content = file.content.replace(rm[0], "");
|
|
707
|
+
}
|
|
708
|
+
for (const mod of mods) {
|
|
709
|
+
if (!mod)
|
|
710
|
+
return null;
|
|
711
|
+
if (!mod.cmd)
|
|
712
|
+
return null;
|
|
713
|
+
switch (mod.cmd) {
|
|
714
|
+
case "include":
|
|
715
|
+
let toReplace = "";
|
|
716
|
+
if (!mod.files) {
|
|
717
|
+
if (!mod.modList)
|
|
718
|
+
return null;
|
|
719
|
+
const modEntries = Object.entries(mod.modList);
|
|
720
|
+
const modLen = modEntries.length;
|
|
721
|
+
const modIterator = modEntries.entries();
|
|
722
|
+
for (const [idx, [_, include]] of modIterator) {
|
|
723
|
+
if (idx > 0)
|
|
724
|
+
switch (include.value.type) {
|
|
725
|
+
case "object":
|
|
726
|
+
case "method":
|
|
727
|
+
case "array":
|
|
728
|
+
case "enum":
|
|
729
|
+
toReplace += `
|
|
730
|
+
`;
|
|
731
|
+
break;
|
|
732
|
+
}
|
|
733
|
+
toReplace += include.value.parsedStr + `
|
|
734
|
+
`;
|
|
735
|
+
if (idx === modLen - 1)
|
|
736
|
+
toReplace += `
|
|
737
|
+
`;
|
|
738
|
+
}
|
|
739
|
+
file.content = file.content.replace(mod.targetStr, toReplace);
|
|
740
|
+
} else {
|
|
741
|
+
for (const fileOrPath of mod.files) {
|
|
742
|
+
if (typeof fileOrPath === "string") {
|
|
743
|
+
const content = await fsRuntime.readText(fileOrPath);
|
|
744
|
+
toReplace += content + `
|
|
745
|
+
|
|
746
|
+
`;
|
|
747
|
+
} else
|
|
748
|
+
toReplace += fileOrPath.content + `
|
|
749
|
+
|
|
750
|
+
`;
|
|
751
|
+
}
|
|
752
|
+
file.content = file.content.replace(mod.targetStr, toReplace);
|
|
753
|
+
}
|
|
754
|
+
break;
|
|
755
|
+
case "import":
|
|
756
|
+
const cmdMatches = [...file.content.matchAll(contentModRegex)];
|
|
757
|
+
const shortCmdMatches = [
|
|
758
|
+
...file.content.matchAll(contentModShortRegex)
|
|
759
|
+
];
|
|
760
|
+
const useMatches = [...file.content.matchAll(useModRegex)];
|
|
761
|
+
for (const match of cmdMatches) {
|
|
762
|
+
const { cmd: contentCmd, mod: contentMod } = match.groups;
|
|
763
|
+
if (!contentCmd || !contentMod) {
|
|
764
|
+
log.error(`Invalid content module statement: \x1B[34m${contentCmd} ${contentMod}\x1B[0m in \x1B[33m${file.name}\x1B[0m from \x1B[32m${file.path}\x1B[0m. Aborting...`);
|
|
765
|
+
return null;
|
|
766
|
+
}
|
|
767
|
+
if (!(`@${contentMod}` in mod.modList) && !(contentMod in mod.modList))
|
|
768
|
+
continue;
|
|
769
|
+
if (config.debugLevel >= 1)
|
|
770
|
+
log.debug(`Content module statement found: \x1B[34m${contentCmd} ${contentMod}\x1B[0m in \x1B[33m${file.name === "" ? "index" : file.name}\x1B[0m from \x1B[32m${file.path}\x1B[0m.`);
|
|
771
|
+
let parsedStr = "";
|
|
772
|
+
switch (contentCmd) {
|
|
773
|
+
case "content":
|
|
774
|
+
const tabChar = config.tabType === "1t" ? "\t" : config.tabType === "2s" ? " " : " ";
|
|
775
|
+
const tabCnt = countTabsBeforeSubstring(file.content, match[0], tabChar);
|
|
776
|
+
parsedStr = module2[mod.targetPath][`@${contentMod}` in module2[mod.targetPath] ? `@${contentMod}` : contentMod].parsedStr;
|
|
777
|
+
if (tabCnt > 0) {
|
|
778
|
+
const tabLevels = getTabLevels(parsedStr, config.tabType).map((l) => l + tabCnt);
|
|
779
|
+
file.content = file.content.replace(match[0], parsedStr.split(`
|
|
780
|
+
`).map((l, i) => tabLevels[i] ? insertTabs(tabLevels[i], config.tabType) + l : l).join(`
|
|
781
|
+
`));
|
|
782
|
+
} else
|
|
783
|
+
file.content = file.content.replace(match[0], parsedStr);
|
|
784
|
+
break;
|
|
785
|
+
case "nameof":
|
|
786
|
+
parsedStr = module2[mod.targetPath][`@${contentMod}` in module2[mod.targetPath] ? `@${contentMod}` : contentMod].name;
|
|
787
|
+
file.content = file.content.replace(`@nameof ${contentMod}`, parsedStr);
|
|
788
|
+
break;
|
|
789
|
+
case "typeof":
|
|
790
|
+
parsedStr = module2[mod.targetPath][`@${contentMod}` in module2[mod.targetPath] ? `@${contentMod}` : contentMod].type;
|
|
791
|
+
file.content = file.content.replace(`@typeof ${contentMod}`, `"${parsedStr}"`);
|
|
792
|
+
break;
|
|
793
|
+
case "valueof":
|
|
794
|
+
parsedStr = module2[mod.targetPath][`@${contentMod}` in module2[mod.targetPath] ? `@${contentMod}` : contentMod].value;
|
|
795
|
+
file.content = file.content.replace(`@valueof ${contentMod}`, parsedStr);
|
|
796
|
+
break;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
for (const match of shortCmdMatches) {
|
|
800
|
+
const { mod: contentMod } = match.groups;
|
|
801
|
+
if (!contentMod) {
|
|
802
|
+
log.error(`Invalid content module statement: \x1B[34m@:${contentMod}\x1B[0m in \x1B[33m${file.name}\x1B[0m from \x1B[32m${file.path}\x1B[0m. Aborting...`);
|
|
803
|
+
return null;
|
|
804
|
+
}
|
|
805
|
+
if (config.debugLevel >= 1)
|
|
806
|
+
log.debug(`Valueof module statement found: \x1B[34m${contentMod}\x1B[0m in \x1B[33m${file.name}\x1B[0m from \x1B[32m${file.path}\x1B[0m.`);
|
|
807
|
+
if (!(`@${contentMod}` in mod.modList) && !(contentMod in mod.modList))
|
|
808
|
+
continue;
|
|
809
|
+
const parsedStr = module2[mod.targetPath][`@${contentMod}` in module2[mod.targetPath] ? `@${contentMod}` : contentMod].value;
|
|
810
|
+
file.content = file.content.replace(`@:${contentMod}`, parsedStr);
|
|
811
|
+
}
|
|
812
|
+
for (const match of useMatches) {
|
|
813
|
+
const { mod: contentMod, body } = match.groups;
|
|
814
|
+
if (!contentMod || !body) {
|
|
815
|
+
log.error(`Invalid content module statement: \x1B[34m@use ${contentMod} ${body}\x1B[0m in \x1B[33m${file.name}\x1B[0m from \x1B[32m${file.path}\x1B[0m. Aborting...`);
|
|
816
|
+
return null;
|
|
817
|
+
}
|
|
818
|
+
if (!(`@${contentMod}` in mod.modList) && !(contentMod in mod.modList))
|
|
819
|
+
continue;
|
|
820
|
+
if (config.debugLevel >= 1)
|
|
821
|
+
log.debug(`Use module statement found: \x1B[34m@use ${contentMod} ${body}\x1B[0m in \x1B[33m${file.name}\x1B[0m from \x1B[32m${file.path}\x1B[0m.`);
|
|
822
|
+
const lines = body.slice(1, -1).split(`
|
|
823
|
+
`).filter(Boolean);
|
|
824
|
+
const pairs = lines.map((l) => l.split(":").map((p) => {
|
|
825
|
+
const delimIdx = p.lastIndexOf(",");
|
|
826
|
+
return p.slice(0, delimIdx === -1 ? undefined : delimIdx).trim();
|
|
827
|
+
}));
|
|
828
|
+
const tabChar = config.tabType === "1t" ? "\t" : config.tabType === "2s" ? " " : " ";
|
|
829
|
+
const tabCnt = countTabsBeforeSubstring(file.content.slice(file.content.lastIndexOf(`
|
|
830
|
+
`, match.index), match.index + match[0].length), "@", tabChar);
|
|
831
|
+
const tabLevels = getTabLevels(body, config.tabType).map((l) => l + tabCnt);
|
|
832
|
+
const currMod = module2[mod.targetPath][`@${contentMod}` in module2[mod.targetPath] ? `@${contentMod}` : contentMod];
|
|
833
|
+
const modMember = Object.entries(currMod.member);
|
|
834
|
+
const tabLevel = tabCnt + tabLevels[Math.min(1, tabLevels.length - 1)];
|
|
835
|
+
let res = `{
|
|
836
|
+
`;
|
|
837
|
+
for (const [idx, [mName, mVal]] of modMember.entries()) {
|
|
838
|
+
const pairIdx = pairs.findIndex((p) => p[0] === mName);
|
|
839
|
+
const pair = pairIdx > -1 ? pairs[pairIdx] : null;
|
|
840
|
+
const value = pair ? pair.length === 2 ? pair[1] : mVal.value : mVal.value;
|
|
841
|
+
if (value) {
|
|
842
|
+
res += `${insertTabs(tabLevel, config.tabType)}${mName}: ${value}${idx < modMember.length - 2 ? "," : ""}
|
|
843
|
+
`;
|
|
844
|
+
}
|
|
845
|
+
if (pairIdx > -1)
|
|
846
|
+
swapAndPop(pairs, pairIdx);
|
|
847
|
+
}
|
|
848
|
+
for (const [idx, pair] of pairs.entries()) {
|
|
849
|
+
if (idx === 0)
|
|
850
|
+
res = res.slice(0, -1) + `,
|
|
851
|
+
`;
|
|
852
|
+
res += `${insertTabs(tabLevel, config.tabType)}${pair[0]}: ${pair[1]}${idx < pairs.length - 1 ? "," : ""}
|
|
853
|
+
`;
|
|
854
|
+
}
|
|
855
|
+
res += insertTabs(tabCnt, config.tabType) + "}";
|
|
856
|
+
file.content = file.content.replace(match[0], res);
|
|
857
|
+
}
|
|
858
|
+
break;
|
|
859
|
+
}
|
|
860
|
+
file.content = file.content.trim() + `
|
|
861
|
+
`;
|
|
862
|
+
}
|
|
863
|
+
return mods;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// src/parser/export-module.ts
|
|
867
|
+
function inferType(value) {
|
|
868
|
+
value = value.trim();
|
|
869
|
+
if (value.startsWith('"') || value.startsWith("'"))
|
|
870
|
+
return "string";
|
|
871
|
+
if (/^\d/.test(value))
|
|
872
|
+
return "number";
|
|
873
|
+
if (value === "true" || value === "false")
|
|
874
|
+
return "boolean";
|
|
875
|
+
if (value.startsWith("{") || value.includes("=>"))
|
|
876
|
+
return "method";
|
|
877
|
+
if (value.startsWith("["))
|
|
878
|
+
return "array";
|
|
879
|
+
if (value.startsWith("{"))
|
|
880
|
+
return "object";
|
|
881
|
+
return "any";
|
|
882
|
+
}
|
|
883
|
+
function inferInlineType(value) {
|
|
884
|
+
value = value.trim();
|
|
885
|
+
if (value.toLowerCase() === "string")
|
|
886
|
+
return "string";
|
|
887
|
+
if (value.toLowerCase() === "number")
|
|
888
|
+
return "number";
|
|
889
|
+
if (value.toLowerCase() === "boolean")
|
|
890
|
+
return "boolean";
|
|
891
|
+
if (value.toLowerCase() === "object")
|
|
892
|
+
return "object";
|
|
893
|
+
if (value.toLowerCase() === "array")
|
|
894
|
+
return "array";
|
|
895
|
+
if (value.toLowerCase() === "enum")
|
|
896
|
+
return "enum";
|
|
897
|
+
return "any";
|
|
898
|
+
}
|
|
899
|
+
function getDefaultValue(type) {
|
|
900
|
+
switch (type) {
|
|
901
|
+
case "string":
|
|
902
|
+
return '""';
|
|
903
|
+
case "number":
|
|
904
|
+
return "0";
|
|
905
|
+
case "boolean":
|
|
906
|
+
return "false";
|
|
907
|
+
case "object":
|
|
908
|
+
return "{}";
|
|
909
|
+
case "array":
|
|
910
|
+
return "[]";
|
|
911
|
+
default:
|
|
912
|
+
return null;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
function getObjectMembers(module2, retryList, filePath, name, objCode, isType = false) {
|
|
916
|
+
const deleteCommentRegex = /\/\/[^\n]*|\/\*[\s\S]*?\*\//g;
|
|
917
|
+
const cleanObjCode = objCode.replace(deleteCommentRegex, "").replace(/\s+/g, "");
|
|
918
|
+
const shapes = isType ? cleanObjCode.split("=")[1].trim() : null;
|
|
919
|
+
const members = cleanObjCode.split("{")[1].replace("}", "").split(",");
|
|
920
|
+
const memberObj = {};
|
|
921
|
+
if (objCode.includes("extends")) {
|
|
922
|
+
const extendsMatch = objCode.match(/extends (\w+)/);
|
|
923
|
+
if (extendsMatch) {
|
|
924
|
+
const extendsName = extendsMatch[1];
|
|
925
|
+
if (!module2[filePath])
|
|
926
|
+
module2[filePath] = {};
|
|
927
|
+
if (!module2[filePath][extendsName])
|
|
928
|
+
retryList.push({ filePath, name, targetName: extendsName });
|
|
929
|
+
else {
|
|
930
|
+
const extendsInterface = module2[filePath][extendsName];
|
|
931
|
+
for (const [mName, m] of Object.entries(extendsInterface.member)) {
|
|
932
|
+
memberObj[mName] = { type: m.type, value: m.value };
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
} else if (isType && objCode.includes("&")) {
|
|
937
|
+
const andSplit = shapes.split("&").map((s) => s.trim().replace(";", ""));
|
|
938
|
+
for (const and of andSplit) {
|
|
939
|
+
if (and.startsWith("{"))
|
|
940
|
+
continue;
|
|
941
|
+
if (!module2[filePath])
|
|
942
|
+
module2[filePath] = {};
|
|
943
|
+
if (!module2[filePath][and])
|
|
944
|
+
retryList.push({ filePath, name, targetName: and });
|
|
945
|
+
else {
|
|
946
|
+
const extendsType = module2[filePath][and];
|
|
947
|
+
for (const [mName, m] of Object.entries(extendsType.member)) {
|
|
948
|
+
memberObj[mName] = { type: m.type, value: m.value };
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
for (const member of members) {
|
|
954
|
+
const parts = member.split(":").map((p) => p.replace(";", ""));
|
|
955
|
+
const defParts = parts.length === 1 ? parts[0].split("=") : parts[1].split("=");
|
|
956
|
+
const partFirst = defParts[0].trim().replace(";", "");
|
|
957
|
+
const memName = /\w+/.exec(parts[0].replace("?", ""))[0];
|
|
958
|
+
const defValue = defParts.length === 1 ? parts[0].endsWith("?") ? "undefined" : getDefaultValue(inferInlineType(partFirst)) : defParts[1].trim();
|
|
959
|
+
memberObj[memName] = { type: parts.length === 1 ? "any" : inferInlineType(partFirst), value: defValue };
|
|
960
|
+
}
|
|
961
|
+
return memberObj;
|
|
962
|
+
}
|
|
963
|
+
function countBraces(line) {
|
|
964
|
+
let count = 0;
|
|
965
|
+
let inString = false;
|
|
966
|
+
let stringChar = "";
|
|
967
|
+
for (let i = 0;i < line.length; i++) {
|
|
968
|
+
const char = line[i];
|
|
969
|
+
if (inString) {
|
|
970
|
+
if (char === stringChar && (i === 0 || line[i - 1] !== "\\")) {
|
|
971
|
+
inString = false;
|
|
972
|
+
}
|
|
973
|
+
} else {
|
|
974
|
+
if (char === '"' || char === "'") {
|
|
975
|
+
inString = true;
|
|
976
|
+
stringChar = char;
|
|
977
|
+
} else if (char === "{") {
|
|
978
|
+
count++;
|
|
979
|
+
} else if (char === "}") {
|
|
980
|
+
count--;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
return count;
|
|
985
|
+
}
|
|
986
|
+
function parseFnParams(str) {
|
|
987
|
+
const match = str.match(fnParamsRegex);
|
|
988
|
+
let names = [];
|
|
989
|
+
let defaults = [];
|
|
990
|
+
let combined = [];
|
|
991
|
+
if (!match)
|
|
992
|
+
return {
|
|
993
|
+
names,
|
|
994
|
+
defaults,
|
|
995
|
+
combined
|
|
996
|
+
};
|
|
997
|
+
const params = match[0].slice(1, -1).split(",");
|
|
998
|
+
names = params.map((p) => p.trim().split("=")[0].trim());
|
|
999
|
+
defaults = params.map((p) => {
|
|
1000
|
+
const def = p.trim().split("=");
|
|
1001
|
+
return def.length > 1 ? def[1]?.trim() : def[0]?.trim().endsWith("?") ? "undefined" : null;
|
|
1002
|
+
});
|
|
1003
|
+
combined = names.map((n, i) => defaults[i] ? `${n.replaceAll("?", "")} = ${defaults[i]}` : n.replaceAll("?", ""));
|
|
1004
|
+
return { names, defaults, combined };
|
|
1005
|
+
}
|
|
1006
|
+
function convertClassMethods(classBody) {
|
|
1007
|
+
const methodRegex = /(\w+)\s*\(([^)]*)\)\s*{([\s\S]*?)}/g;
|
|
1008
|
+
return classBody.replace(methodRegex, (match, methodName, params, body) => {
|
|
1009
|
+
if (methodName === "function")
|
|
1010
|
+
return match;
|
|
1011
|
+
return `${methodName} = function(${params.replaceAll("?", " = undefined")}) {${body}}`;
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
function insertTabs(count, type) {
|
|
1015
|
+
let str = "";
|
|
1016
|
+
for (let i = 0;i < count; i++) {
|
|
1017
|
+
str += type === "1t" ? "\t" : type === "2s" ? " " : " ";
|
|
1018
|
+
}
|
|
1019
|
+
return str;
|
|
1020
|
+
}
|
|
1021
|
+
function getExportedModules(files, config) {
|
|
1022
|
+
const module2 = {};
|
|
1023
|
+
if (files.generate.length == 0 && files.scaff.length == 0) {
|
|
1024
|
+
log.warn("No files to get exported modules from.");
|
|
1025
|
+
return module2;
|
|
1026
|
+
}
|
|
1027
|
+
const retryList = [];
|
|
1028
|
+
let retryCount = 0;
|
|
1029
|
+
let retryLastLen = 0;
|
|
1030
|
+
let retryLen = 0;
|
|
1031
|
+
for (const file of files.scaff) {
|
|
1032
|
+
const filePath = file.isIndex ? file.path : `${file.path}/${file.name}`;
|
|
1033
|
+
const lines = file.content.split(`
|
|
1034
|
+
`);
|
|
1035
|
+
let i = 0;
|
|
1036
|
+
while (i < lines.length) {
|
|
1037
|
+
if (!lines[i]) {
|
|
1038
|
+
i++;
|
|
1039
|
+
continue;
|
|
1040
|
+
}
|
|
1041
|
+
const line = lines[i].trim();
|
|
1042
|
+
if (!line.startsWith("export ")) {
|
|
1043
|
+
i++;
|
|
1044
|
+
continue;
|
|
1045
|
+
}
|
|
1046
|
+
if (line.startsWith("export function ")) {
|
|
1047
|
+
const funcLines = [];
|
|
1048
|
+
let braceCount = 0;
|
|
1049
|
+
let j = i;
|
|
1050
|
+
for (;j < lines.length; j++) {
|
|
1051
|
+
const l = lines[j];
|
|
1052
|
+
funcLines.push(l);
|
|
1053
|
+
braceCount += countBraces(l);
|
|
1054
|
+
if (braceCount <= 0 && (l?.trim().endsWith("}") || l?.trim().includes(";")))
|
|
1055
|
+
break;
|
|
1056
|
+
}
|
|
1057
|
+
i = j + 1;
|
|
1058
|
+
const funcCode = funcLines.join(`
|
|
1059
|
+
`);
|
|
1060
|
+
const match = funcCode.match(/export function (\w+)/);
|
|
1061
|
+
if (match) {
|
|
1062
|
+
const name = match[1];
|
|
1063
|
+
if (!module2[filePath])
|
|
1064
|
+
module2[filePath] = {};
|
|
1065
|
+
const body = funcCode.slice(funcCode.indexOf("{")).trim();
|
|
1066
|
+
const params = parseFnParams(funcCode);
|
|
1067
|
+
const parsedStr = `function ${name}(${params.combined.join(", ")}) ${body}`;
|
|
1068
|
+
module2[filePath][name] = { name, value: body.slice(1, -1), type: "function", parsedStr };
|
|
1069
|
+
}
|
|
1070
|
+
} else if (line.startsWith("export class ")) {
|
|
1071
|
+
const classLines = [];
|
|
1072
|
+
let braceCount = 0;
|
|
1073
|
+
let j = i;
|
|
1074
|
+
for (;j < lines.length; j++) {
|
|
1075
|
+
const l = lines[j];
|
|
1076
|
+
classLines.push(l);
|
|
1077
|
+
braceCount += countBraces(l);
|
|
1078
|
+
if (braceCount <= 0 && (l?.trim().endsWith("}") || l?.trim().includes(";")))
|
|
1079
|
+
break;
|
|
1080
|
+
}
|
|
1081
|
+
i = j + 1;
|
|
1082
|
+
const classCode = classLines.join(`
|
|
1083
|
+
`);
|
|
1084
|
+
const match = classCode.match(/export class (\w+)/);
|
|
1085
|
+
if (match) {
|
|
1086
|
+
const name = match[1];
|
|
1087
|
+
const constructor = classCode.match(/constructor\s*\(([^)]*)\)/)?.[1] ?? "()";
|
|
1088
|
+
if (!module2[filePath])
|
|
1089
|
+
module2[filePath] = {};
|
|
1090
|
+
const bodyMatch = classCode.match(/{([\s\S]*)}/);
|
|
1091
|
+
let classBody = bodyMatch ? bodyMatch[1] : "";
|
|
1092
|
+
if (classBody) {
|
|
1093
|
+
classBody = classBody.replace(/^\s*constructor\s*\([^)]*\)\s*\n?/m, "");
|
|
1094
|
+
classBody = convertClassMethods(classBody);
|
|
1095
|
+
if (classBody.includes("=>")) {
|
|
1096
|
+
const argsMatch = classBody.match(/\([^)]*\)\s*=>/)?.[0];
|
|
1097
|
+
if (argsMatch)
|
|
1098
|
+
classBody = classBody.replace(argsMatch, `function${argsMatch.replace("=>", "").replaceAll("?", " = undefined").trimEnd()}`);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
const parsedStr = `function ${name}(${constructor.replaceAll("?", " = undefined")}) constructor {
|
|
1102
|
+
${insertTabs(1, config.tabType)}${classBody}
|
|
1103
|
+
}`;
|
|
1104
|
+
module2[filePath][name] = { name, value: classCode.replace("export ", ""), type: "class", parsedStr };
|
|
1105
|
+
}
|
|
1106
|
+
} else if (line.startsWith("export interface ")) {
|
|
1107
|
+
const interfaceLines = [];
|
|
1108
|
+
let braceCount = 0;
|
|
1109
|
+
let j = i;
|
|
1110
|
+
for (;j < lines.length; j++) {
|
|
1111
|
+
const l = lines[j];
|
|
1112
|
+
interfaceLines.push(l);
|
|
1113
|
+
braceCount += countBraces(l);
|
|
1114
|
+
if (braceCount <= 0 && (l?.trim().endsWith("}") || l?.trim().includes(";")))
|
|
1115
|
+
break;
|
|
1116
|
+
}
|
|
1117
|
+
i = j + 1;
|
|
1118
|
+
const interfaceCode = interfaceLines.join(`
|
|
1119
|
+
`);
|
|
1120
|
+
const match = interfaceCode.match(/export interface (\w+)/);
|
|
1121
|
+
if (match) {
|
|
1122
|
+
const name = match[1];
|
|
1123
|
+
if (!module2[filePath])
|
|
1124
|
+
module2[filePath] = {};
|
|
1125
|
+
const member = getObjectMembers(module2, retryList, filePath, name, interfaceCode);
|
|
1126
|
+
const parsedStr = "{" + interfaceCode.split("{")[1].split("}")[0] + "}";
|
|
1127
|
+
module2[filePath][name] = { name, value: interfaceCode.replace("export ", ""), type: "interface", member, parsedStr };
|
|
1128
|
+
}
|
|
1129
|
+
} else if (line.startsWith("export enum ") || line.startsWith("export const enum ")) {
|
|
1130
|
+
const enumLines = [];
|
|
1131
|
+
let braceCount = 0;
|
|
1132
|
+
let j = i;
|
|
1133
|
+
for (;j < lines.length; j++) {
|
|
1134
|
+
const l = lines[j];
|
|
1135
|
+
enumLines.push(l);
|
|
1136
|
+
braceCount += countBraces(l);
|
|
1137
|
+
if (braceCount <= 0 && (l?.trim().endsWith("}") || l?.trim().includes(";")))
|
|
1138
|
+
break;
|
|
1139
|
+
}
|
|
1140
|
+
i = j + 1;
|
|
1141
|
+
const constEnum = line.startsWith("export const enum ");
|
|
1142
|
+
const enumCode = enumLines.join(`
|
|
1143
|
+
`);
|
|
1144
|
+
const match = !constEnum ? enumCode.match(/export enum (\w+)/) : enumCode.match(/export const enum (\w+)/);
|
|
1145
|
+
if (match) {
|
|
1146
|
+
const name = match[1];
|
|
1147
|
+
if (!module2[filePath])
|
|
1148
|
+
module2[filePath] = {};
|
|
1149
|
+
const parsedStr = enumCode.replace(!constEnum ? "export " : "export const ", "").replace("?", " = undefined");
|
|
1150
|
+
module2[filePath][name] = { name, value: parsedStr, type: "enum", parsedStr };
|
|
1151
|
+
}
|
|
1152
|
+
} else if (line.startsWith("export type ")) {
|
|
1153
|
+
const typeLines = [];
|
|
1154
|
+
let j = i;
|
|
1155
|
+
let hasBrace = false;
|
|
1156
|
+
let braceCount = 0;
|
|
1157
|
+
for (;j < lines.length; j++) {
|
|
1158
|
+
const l = lines[j];
|
|
1159
|
+
typeLines.push(l);
|
|
1160
|
+
if (l?.includes("{"))
|
|
1161
|
+
hasBrace = true;
|
|
1162
|
+
braceCount += countBraces(l);
|
|
1163
|
+
if (!hasBrace && l?.includes(";"))
|
|
1164
|
+
break;
|
|
1165
|
+
if (hasBrace && braceCount <= 0 && (l?.trim().endsWith("}") || l?.trim().includes(";")))
|
|
1166
|
+
break;
|
|
1167
|
+
}
|
|
1168
|
+
i = j + 1;
|
|
1169
|
+
const typeCode = typeLines.join(`
|
|
1170
|
+
`);
|
|
1171
|
+
const match = typeCode.match(/export type (\w+)/);
|
|
1172
|
+
if (match) {
|
|
1173
|
+
const name = match[1];
|
|
1174
|
+
if (!module2[filePath])
|
|
1175
|
+
module2[filePath] = {};
|
|
1176
|
+
const member = getObjectMembers(module2, retryList, filePath, name, typeCode, true);
|
|
1177
|
+
const parsedStr = "{" + typeCode.split("{")[1].split("}")[0] + "}";
|
|
1178
|
+
module2[filePath][name] = { name, value: typeCode.replace("export ", ""), type: "type", member, parsedStr };
|
|
1179
|
+
}
|
|
1180
|
+
} else if (line.includes(" = ")) {
|
|
1181
|
+
const parts = line.split(" = ");
|
|
1182
|
+
if (parts.length >= 2) {
|
|
1183
|
+
const decl = parts[0].trim();
|
|
1184
|
+
const valuePart = parts.slice(1).join(" = ").trim().replace(/;$/, "");
|
|
1185
|
+
const valueType = inferType(valuePart);
|
|
1186
|
+
let name = decl?.replace(/^export\s+(const|let|var)?\s*/, "").trim();
|
|
1187
|
+
if (valuePart.includes("=>")) {
|
|
1188
|
+
if (valuePart.trim().endsWith("{")) {
|
|
1189
|
+
const arrowLines = [];
|
|
1190
|
+
let braceCount = 0;
|
|
1191
|
+
let j = i;
|
|
1192
|
+
for (;j < lines.length; j++) {
|
|
1193
|
+
const l = lines[j];
|
|
1194
|
+
arrowLines.push(l);
|
|
1195
|
+
braceCount += countBraces(l);
|
|
1196
|
+
if (braceCount <= 0 && (l?.trim().endsWith("}") || l?.trim().includes(";")))
|
|
1197
|
+
break;
|
|
1198
|
+
}
|
|
1199
|
+
i = j + 1;
|
|
1200
|
+
const arrowCode = arrowLines.join(`
|
|
1201
|
+
`);
|
|
1202
|
+
const arrowBlock = "{" + arrowCode.split("{")[1];
|
|
1203
|
+
const params = parseFnParams(valuePart);
|
|
1204
|
+
const parsedStr = `${name} = function(${params.combined.join(", ")}) ${arrowBlock}`;
|
|
1205
|
+
if (!module2[filePath])
|
|
1206
|
+
module2[filePath] = {};
|
|
1207
|
+
module2[filePath][name] = { name, value: arrowBlock.slice(1, -1), type: "arrow-fn", parsedStr };
|
|
1208
|
+
} else {
|
|
1209
|
+
const params = parseFnParams(valuePart);
|
|
1210
|
+
const body = valuePart.split("=>")[1].trim();
|
|
1211
|
+
const parsedStr = `${name} = function(${params.combined.join(", ")}) { return ${body}; }`;
|
|
1212
|
+
if (!module2[filePath])
|
|
1213
|
+
module2[filePath] = {};
|
|
1214
|
+
module2[filePath][name] = { name, value: body, type: "arrow-fn", parsedStr };
|
|
1215
|
+
}
|
|
1216
|
+
} else if (valuePart.startsWith("function")) {
|
|
1217
|
+
if (valuePart.trim().endsWith("{")) {
|
|
1218
|
+
const funcLines = [];
|
|
1219
|
+
let braceCount = 0;
|
|
1220
|
+
let j = i;
|
|
1221
|
+
for (;j < lines.length; j++) {
|
|
1222
|
+
const l = lines[j];
|
|
1223
|
+
funcLines.push(l);
|
|
1224
|
+
braceCount += countBraces(l);
|
|
1225
|
+
if (braceCount <= 0 && (l?.trim().endsWith("}") || l?.trim().includes(";")))
|
|
1226
|
+
break;
|
|
1227
|
+
}
|
|
1228
|
+
i = j + 1;
|
|
1229
|
+
const funcCode = funcLines.join(`
|
|
1230
|
+
`);
|
|
1231
|
+
const funcBlock = "{" + funcCode.split("{")[1];
|
|
1232
|
+
const params = parseFnParams(valuePart);
|
|
1233
|
+
const parsedStr = `${name} = function(${params.combined.join(", ")}) ${funcBlock}`;
|
|
1234
|
+
if (!module2[filePath])
|
|
1235
|
+
module2[filePath] = {};
|
|
1236
|
+
module2[filePath][name] = { name, value: funcBlock.slice(1, -1), type: "method", parsedStr };
|
|
1237
|
+
} else {
|
|
1238
|
+
const parsedStr = `${name} = ${valuePart}`;
|
|
1239
|
+
if (!module2[filePath])
|
|
1240
|
+
module2[filePath] = {};
|
|
1241
|
+
module2[filePath][name] = { name, value: line.replace("export ", ""), type: "method", parsedStr };
|
|
1242
|
+
}
|
|
1243
|
+
} else {
|
|
1244
|
+
let varType = "var";
|
|
1245
|
+
if (decl?.includes("const"))
|
|
1246
|
+
varType = "const";
|
|
1247
|
+
else if (decl?.includes("let"))
|
|
1248
|
+
varType = "let";
|
|
1249
|
+
else if (decl?.includes("var"))
|
|
1250
|
+
varType = "var";
|
|
1251
|
+
const noVarKeyword = varType === "var" && !parts[0].includes("var");
|
|
1252
|
+
if (!module2[filePath])
|
|
1253
|
+
module2[filePath] = {};
|
|
1254
|
+
const parsedStr = varType !== "const" ? `${varType === "let" ? "" : "var "}${name} = ${valuePart};` : `#macro ${name} ${valuePart}`;
|
|
1255
|
+
module2[filePath][name] = { name, value: valuePart, type: varType === "const" ? "constant" : "variable", parsedStr };
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
i++;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
retryLen = retryList.length;
|
|
1263
|
+
while (retryCount < 10 && retryLastLen !== retryLen) {
|
|
1264
|
+
for (const retry of retryList) {
|
|
1265
|
+
if (!module2[retry.filePath])
|
|
1266
|
+
continue;
|
|
1267
|
+
if (!module2[retry.filePath][retry.targetName])
|
|
1268
|
+
continue;
|
|
1269
|
+
switch (module2[retry.filePath][retry.name].type) {
|
|
1270
|
+
case "interface":
|
|
1271
|
+
const currInterface = module2[retry.filePath][retry.name];
|
|
1272
|
+
const extendsInterface = module2[retry.filePath][retry.targetName];
|
|
1273
|
+
for (const [mName, m] of Object.entries(extendsInterface.member)) {
|
|
1274
|
+
currInterface.member[mName] = { type: m.type, value: m.value };
|
|
1275
|
+
}
|
|
1276
|
+
break;
|
|
1277
|
+
case "type":
|
|
1278
|
+
const currType = module2[retry.filePath][retry.name];
|
|
1279
|
+
const extendsType = module2[retry.filePath][retry.targetName];
|
|
1280
|
+
for (const [mName, m] of Object.entries(extendsType.member)) {
|
|
1281
|
+
currType.member[mName] = { type: m.type, value: m.value };
|
|
1282
|
+
}
|
|
1283
|
+
break;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
retryLastLen = retryLen;
|
|
1287
|
+
retryLen = retryList.length;
|
|
1288
|
+
retryCount++;
|
|
1289
|
+
}
|
|
1290
|
+
return module2;
|
|
1291
|
+
}
|
|
1292
|
+
function reexportModule(module2, file, config) {
|
|
1293
|
+
const reexportMatches = [...file.content.matchAll(modControlRegex)].filter((match) => match.groups?.cmd === "export");
|
|
1294
|
+
if (!reexportMatches.length)
|
|
1295
|
+
return false;
|
|
1296
|
+
for (const match of reexportMatches) {
|
|
1297
|
+
const { cmd, mod, path: path3 } = match.groups;
|
|
1298
|
+
if (!(cmd && mod && path3)) {
|
|
1299
|
+
log.error(`Invalid module control statement: \x1B[34m${cmd} ${mod} from ${path3}\x1B[0m in \x1B[33m${file.name}\x1B[0m from \x1B[32m${file.path}\x1B[0m. Aborting...`);
|
|
1300
|
+
return false;
|
|
1301
|
+
}
|
|
1302
|
+
const fromPath = normalizePath(resolveImportPath(file.path, path3.slice(1, -1), config));
|
|
1303
|
+
const thisPath = file.isIndex ? file.path : `${file.path}/${file.name}`;
|
|
1304
|
+
const alias = {};
|
|
1305
|
+
if (mod === "*" || mod.startsWith("{") && mod.endsWith("}")) {
|
|
1306
|
+
const targetMods = mod === "*" ? null : mod.slice(1, -1).split(",").map((m) => {
|
|
1307
|
+
const split = m.split(":");
|
|
1308
|
+
const key = split[0].trim();
|
|
1309
|
+
if (split.length === 1)
|
|
1310
|
+
alias[key] = key;
|
|
1311
|
+
else
|
|
1312
|
+
alias[key] = split[1].trim();
|
|
1313
|
+
if (!module2[fromPath]) {
|
|
1314
|
+
if (config.onNotFound === "error") {
|
|
1315
|
+
log.error(`Path \x1B[33m${fromPath}\x1B[0m doesn't have any exported modules. Aborting...`);
|
|
1316
|
+
return false;
|
|
1317
|
+
} else
|
|
1318
|
+
log.warn(`Path \x1B[33m${fromPath}\x1B[0m doesn't have any exported modules. Skipping this module...`);
|
|
1319
|
+
return false;
|
|
1320
|
+
}
|
|
1321
|
+
if (!module2[fromPath][key]) {
|
|
1322
|
+
if (config.onNotFound === "error") {
|
|
1323
|
+
log.error(`Module \x1B[33m${key}\x1B[0m from \x1B[32m${fromPath}\x1B[0m not found. Aborting...`);
|
|
1324
|
+
return false;
|
|
1325
|
+
} else
|
|
1326
|
+
log.warn(`Module \x1B[33m${key}\x1B[0m from \x1B[32m${fromPath}\x1B[0m not found. Skipping this module...`);
|
|
1327
|
+
return false;
|
|
1328
|
+
}
|
|
1329
|
+
return key;
|
|
1330
|
+
}).filter(Boolean);
|
|
1331
|
+
if (!module2[fromPath]) {
|
|
1332
|
+
if (config.onNotFound === "error") {
|
|
1333
|
+
log.error(`Path \x1B[33m${fromPath}\x1B[0m doesn't have any exported modules. Aborting...`);
|
|
1334
|
+
return false;
|
|
1335
|
+
} else
|
|
1336
|
+
log.warn(`Path \x1B[33m${fromPath}\x1B[0m doesn't have any exported modules. Skipping this module...`);
|
|
1337
|
+
return false;
|
|
1338
|
+
}
|
|
1339
|
+
Object.entries(module2[fromPath]).forEach(([key, value]) => {
|
|
1340
|
+
if (!module2[thisPath])
|
|
1341
|
+
module2[thisPath] = {};
|
|
1342
|
+
if (mod === "*")
|
|
1343
|
+
module2[thisPath][key] = value;
|
|
1344
|
+
else if (targetMods.includes(key)) {
|
|
1345
|
+
const usingAlias = key in alias && alias[key] !== key;
|
|
1346
|
+
if (usingAlias)
|
|
1347
|
+
module2[thisPath][`@${alias[key]}`] = value;
|
|
1348
|
+
else
|
|
1349
|
+
module2[thisPath][key] = value;
|
|
1350
|
+
}
|
|
1351
|
+
});
|
|
1352
|
+
} else {
|
|
1353
|
+
if (!module2[fromPath]) {
|
|
1354
|
+
if (config.onNotFound === "error") {
|
|
1355
|
+
log.error(`Path \x1B[33m${fromPath}\x1B[0m doesn't have any exported modules. Aborting...`);
|
|
1356
|
+
return false;
|
|
1357
|
+
} else
|
|
1358
|
+
log.warn(`Path \x1B[33m${fromPath}\x1B[0m doesn't have any exported modules. Skipping this module...`);
|
|
1359
|
+
return false;
|
|
1360
|
+
}
|
|
1361
|
+
if (!module2[fromPath][mod]) {
|
|
1362
|
+
if (config.onNotFound === "error") {
|
|
1363
|
+
log.error(`Module \x1B[33m${mod}\x1B[0m from \x1B[32m${fromPath}\x1B[0m not found. Aborting...`);
|
|
1364
|
+
return false;
|
|
1365
|
+
} else
|
|
1366
|
+
log.warn(`Module \x1B[33m${mod}\x1B[0m from \x1B[32m${fromPath}\x1B[0m not found. Skipping this module...`);
|
|
1367
|
+
return false;
|
|
1368
|
+
}
|
|
1369
|
+
if (!module2[thisPath])
|
|
1370
|
+
module2[thisPath] = {};
|
|
1371
|
+
module2[thisPath][mod] = module2[fromPath][mod];
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
return true;
|
|
1375
|
+
}
|
|
1376
|
+
// src/parser/class-implement.ts
|
|
1377
|
+
function convertArrowFn(str) {
|
|
1378
|
+
return str.replace(arrowFnHeaderRegex, (match, _p1, _p2, _offset, _input, groups) => {
|
|
1379
|
+
if (!groups?.name)
|
|
1380
|
+
return match;
|
|
1381
|
+
const rawParams = groups.params?.trim() ?? "";
|
|
1382
|
+
const paramsSource = rawParams.startsWith("(") ? rawParams : `(${rawParams})`;
|
|
1383
|
+
const params = parseFnParams(paramsSource);
|
|
1384
|
+
return `${groups.name} = function(${params.combined.join(", ")})`;
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
function parseHeader(str, regex = implHeaderRegex) {
|
|
1388
|
+
const results = [];
|
|
1389
|
+
let match;
|
|
1390
|
+
while ((match = regex.exec(str)) !== null) {
|
|
1391
|
+
const name = match.groups.name;
|
|
1392
|
+
const start = regex.lastIndex;
|
|
1393
|
+
let braceCount = 1;
|
|
1394
|
+
let inString = false;
|
|
1395
|
+
let stringChar = "";
|
|
1396
|
+
let i = start;
|
|
1397
|
+
for (;i < str.length; i++) {
|
|
1398
|
+
const char = str[i];
|
|
1399
|
+
if (inString) {
|
|
1400
|
+
if (char === stringChar && (i === 0 || str[i - 1] !== "\\")) {
|
|
1401
|
+
inString = false;
|
|
1402
|
+
}
|
|
1403
|
+
} else {
|
|
1404
|
+
if (char === '"' || char === "'") {
|
|
1405
|
+
inString = true;
|
|
1406
|
+
stringChar = char;
|
|
1407
|
+
} else if (char === "{") {
|
|
1408
|
+
braceCount++;
|
|
1409
|
+
} else if (char === "}") {
|
|
1410
|
+
braceCount--;
|
|
1411
|
+
if (braceCount === 0)
|
|
1412
|
+
break;
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
const body = str.slice(start, i);
|
|
1417
|
+
results.push({ name, body });
|
|
1418
|
+
regex.lastIndex = i + 1;
|
|
1419
|
+
}
|
|
1420
|
+
return results;
|
|
1421
|
+
}
|
|
1422
|
+
function implementClass(module2, fileGroup, config) {
|
|
1423
|
+
if (fileGroup.generate.length == 0 && fileGroup.scaff.length == 0) {
|
|
1424
|
+
log.warn("No files to implement classes from.");
|
|
1425
|
+
return false;
|
|
1426
|
+
}
|
|
1427
|
+
const toImpl = [];
|
|
1428
|
+
for (const file of fileGroup.scaff) {
|
|
1429
|
+
if (file.childs.length > 0)
|
|
1430
|
+
file.childs.forEach((child) => toImpl.push({ parent: file, file: child }));
|
|
1431
|
+
}
|
|
1432
|
+
for (const file of fileGroup.generate) {
|
|
1433
|
+
if (file.childs.length > 0)
|
|
1434
|
+
file.childs.forEach((child) => toImpl.push({ parent: file, file: child }));
|
|
1435
|
+
}
|
|
1436
|
+
for (const fileImpl of toImpl) {
|
|
1437
|
+
const filePath = fileImpl.parent.isIndex ? fileImpl.parent.path : `${fileImpl.parent.path}/${fileImpl.parent.name}`;
|
|
1438
|
+
const match = parseHeader(fileImpl.file.content);
|
|
1439
|
+
const classNames = [];
|
|
1440
|
+
for (const [mIdx, m] of match.entries()) {
|
|
1441
|
+
const { name: className } = m;
|
|
1442
|
+
let { body } = m;
|
|
1443
|
+
body = convertClassMethods(body);
|
|
1444
|
+
body = convertArrowFn(body);
|
|
1445
|
+
if (!className || !body)
|
|
1446
|
+
continue;
|
|
1447
|
+
classNames.push(className);
|
|
1448
|
+
if (!module2[filePath] || !module2[filePath][className]) {
|
|
1449
|
+
let newFilePath = null;
|
|
1450
|
+
for (const file of fileGroup.scaff) {
|
|
1451
|
+
if (file.content.includes(`class ${className} {`)) {
|
|
1452
|
+
newFilePath = file.isIndex ? file.path : `${file.path}/${file.name}`;
|
|
1453
|
+
break;
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
if (!newFilePath) {
|
|
1457
|
+
for (const file of fileGroup.generate) {
|
|
1458
|
+
if (file.content.includes(`class ${className} {`)) {
|
|
1459
|
+
newFilePath = file.isIndex ? file.path : `${file.path}/${file.name}`;
|
|
1460
|
+
break;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
if (!newFilePath) {
|
|
1465
|
+
if (config.onNotFound === "error") {
|
|
1466
|
+
log.error(`Class \x1B[33m${className}\x1B[0m not found for file \x1B[34m${fileImpl.file.name}\x1B[0m. Aborting...`);
|
|
1467
|
+
return false;
|
|
1468
|
+
} else {
|
|
1469
|
+
log.warn(`Class \x1B[33m${className}\x1B[0m not found for file \x1B[34m${fileImpl.file.name}\x1B[0m. Skipping this class...`);
|
|
1470
|
+
continue;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
const classCloseBracket = module2[newFilePath][className].parsedStr.lastIndexOf("}");
|
|
1474
|
+
module2[newFilePath][className].parsedStr = module2[newFilePath][className].parsedStr.slice(0, classCloseBracket) + `${body.replace(`
|
|
1475
|
+
`, "")}` + (mIdx < match.length - 1 ? `
|
|
1476
|
+
|
|
1477
|
+
` : `
|
|
1478
|
+
}
|
|
1479
|
+
`);
|
|
1480
|
+
if (mIdx === match.length - 1) {
|
|
1481
|
+
for (const name of classNames) {
|
|
1482
|
+
if (!module2[filePath] || !module2[filePath][name])
|
|
1483
|
+
continue;
|
|
1484
|
+
module2[filePath][name].parsedStr += `}
|
|
1485
|
+
`;
|
|
1486
|
+
}
|
|
1487
|
+
} else
|
|
1488
|
+
classNames.splice(classNames.indexOf(className), 1);
|
|
1489
|
+
continue;
|
|
1490
|
+
} else {
|
|
1491
|
+
const classCloseBracket = module2[filePath][className].parsedStr.lastIndexOf("}");
|
|
1492
|
+
module2[filePath][className].parsedStr = module2[filePath][className].parsedStr.slice(0, classCloseBracket) + `${body.replace(`
|
|
1493
|
+
`, "")}` + (mIdx < match.length - 1 ? `
|
|
1494
|
+
|
|
1495
|
+
` : `
|
|
1496
|
+
}
|
|
1497
|
+
`);
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
return true;
|
|
1502
|
+
}
|
|
1503
|
+
// types/gm-event.ts
|
|
1504
|
+
var EVENT = {
|
|
1505
|
+
ALARM_0: 0,
|
|
1506
|
+
ALARM_1: 1,
|
|
1507
|
+
ALARM_2: 2,
|
|
1508
|
+
ALARM_3: 3,
|
|
1509
|
+
ALARM_4: 4,
|
|
1510
|
+
ALARM_5: 5,
|
|
1511
|
+
ALARM_6: 6,
|
|
1512
|
+
ALARM_7: 7,
|
|
1513
|
+
ALARM_8: 8,
|
|
1514
|
+
ALARM_9: 9,
|
|
1515
|
+
ALARM_10: 10,
|
|
1516
|
+
ALARM_11: 11,
|
|
1517
|
+
CLEAN_UP: 0,
|
|
1518
|
+
CREATE: 0,
|
|
1519
|
+
DESTROY: 0,
|
|
1520
|
+
DRAW: 0,
|
|
1521
|
+
DRAW_GUI: 64,
|
|
1522
|
+
WINDOW_RESIZE: 65,
|
|
1523
|
+
DRAW_BEGIN: 72,
|
|
1524
|
+
DRAW_END: 73,
|
|
1525
|
+
DRAW_GUI_BEGIN: 74,
|
|
1526
|
+
DRAW_GUI_END: 75,
|
|
1527
|
+
PREDRAW: 76,
|
|
1528
|
+
POSTDRAW: 77,
|
|
1529
|
+
GESTURE_TAP: 0,
|
|
1530
|
+
GESTURE_DOUBLE_TAP: 1,
|
|
1531
|
+
GESTURE_DRAG_START: 2,
|
|
1532
|
+
GESTURE_DRAGGING: 3,
|
|
1533
|
+
GESTURE_DRAG_END: 4,
|
|
1534
|
+
GESTURE_FLICK: 5,
|
|
1535
|
+
GESTURE_PINCH_START: 6,
|
|
1536
|
+
GESTURE_PINCH_IN: 7,
|
|
1537
|
+
GESTURE_PINCH_OUT: 8,
|
|
1538
|
+
GESTURE_PINCH_END: 9,
|
|
1539
|
+
GESTURE_ROTATE_START: 10,
|
|
1540
|
+
GESTURE_ROTATING: 11,
|
|
1541
|
+
GESTURE_ROTATE_END: 12,
|
|
1542
|
+
GLOBAL_GESTURE_TAP: 64,
|
|
1543
|
+
GLOBAL_GESTURE_DOUBLE_TAP: 65,
|
|
1544
|
+
GLOBAL_GESTURE_DRAG_START: 66,
|
|
1545
|
+
GLOBAL_GESTURE_DRAGGING: 67,
|
|
1546
|
+
GLOBAL_GESTURE_DRAG_END: 68,
|
|
1547
|
+
GLOBAL_GESTURE_FLICK: 69,
|
|
1548
|
+
GLOBAL_GESTURE_PINCH_START: 70,
|
|
1549
|
+
GLOBAL_GESTURE_PINCH_IN: 71,
|
|
1550
|
+
GLOBAL_GESTURE_PINCH_OUT: 72,
|
|
1551
|
+
GLOBAL_GESTURE_PINCH_END: 73,
|
|
1552
|
+
GLOBAL_GESTURE_ROTATE_START: 74,
|
|
1553
|
+
GLOBAL_GESTURE_ROTATING: 75,
|
|
1554
|
+
GLOBAL_GESTURE_ROTATE_END: 76,
|
|
1555
|
+
KEYBOARD_NO_KEY: 0,
|
|
1556
|
+
KEYBOARD_ANY_KEY: 1,
|
|
1557
|
+
KEYBOARD_BACKSPACE: 8,
|
|
1558
|
+
KEYBOARD_TAB: 9,
|
|
1559
|
+
KEYBOARD_ENTER: 13,
|
|
1560
|
+
KEYBOARD_SHIFT: 16,
|
|
1561
|
+
KEYBOARD_CONTROL: 17,
|
|
1562
|
+
KEYBOARD_ALT: 18,
|
|
1563
|
+
KEYBOARD_ESCAPE: 27,
|
|
1564
|
+
KEYBOARD_SPACE: 32,
|
|
1565
|
+
KEYBOARD_PAGE_UP: 33,
|
|
1566
|
+
KEYBOARD_PAGE_DOWN: 34,
|
|
1567
|
+
KEYBOARD_END: 35,
|
|
1568
|
+
KEYBOARD_HOME: 36,
|
|
1569
|
+
KEYBOARD_LEFT: 37,
|
|
1570
|
+
KEYBOARD_UP: 38,
|
|
1571
|
+
KEYBOARD_RIGHT: 39,
|
|
1572
|
+
KEYBOARD_DOWN: 40,
|
|
1573
|
+
KEYBOARD_INSERT: 45,
|
|
1574
|
+
KEYBOARD_DELETE: 46,
|
|
1575
|
+
KEYBOARD_0: 48,
|
|
1576
|
+
KEYBOARD_1: 49,
|
|
1577
|
+
KEYBOARD_2: 50,
|
|
1578
|
+
KEYBOARD_3: 51,
|
|
1579
|
+
KEYBOARD_4: 52,
|
|
1580
|
+
KEYBOARD_5: 53,
|
|
1581
|
+
KEYBOARD_6: 54,
|
|
1582
|
+
KEYBOARD_7: 55,
|
|
1583
|
+
KEYBOARD_8: 56,
|
|
1584
|
+
KEYBOARD_9: 57,
|
|
1585
|
+
KEYBOARD_A: 65,
|
|
1586
|
+
KEYBOARD_B: 66,
|
|
1587
|
+
KEYBOARD_C: 67,
|
|
1588
|
+
KEYBOARD_D: 68,
|
|
1589
|
+
KEYBOARD_E: 69,
|
|
1590
|
+
KEYBOARD_F: 70,
|
|
1591
|
+
KEYBOARD_G: 71,
|
|
1592
|
+
KEYBOARD_H: 72,
|
|
1593
|
+
KEYBOARD_I: 73,
|
|
1594
|
+
KEYBOARD_J: 74,
|
|
1595
|
+
KEYBOARD_K: 75,
|
|
1596
|
+
KEYBOARD_L: 76,
|
|
1597
|
+
KEYBOARD_M: 77,
|
|
1598
|
+
KEYBOARD_N: 78,
|
|
1599
|
+
KEYBOARD_O: 79,
|
|
1600
|
+
KEYBOARD_P: 80,
|
|
1601
|
+
KEYBOARD_Q: 81,
|
|
1602
|
+
KEYBOARD_R: 82,
|
|
1603
|
+
KEYBOARD_S: 83,
|
|
1604
|
+
KEYBOARD_T: 84,
|
|
1605
|
+
KEYBOARD_U: 85,
|
|
1606
|
+
KEYBOARD_V: 86,
|
|
1607
|
+
KEYBOARD_W: 87,
|
|
1608
|
+
KEYBOARD_X: 88,
|
|
1609
|
+
KEYBOARD_Y: 89,
|
|
1610
|
+
KEYBOARD_Z: 90,
|
|
1611
|
+
KEYBOARD_NUMPAD_0: 96,
|
|
1612
|
+
KEYBOARD_NUMPAD_1: 97,
|
|
1613
|
+
KEYBOARD_NUMPAD_2: 98,
|
|
1614
|
+
KEYBOARD_NUMPAD_3: 99,
|
|
1615
|
+
KEYBOARD_NUMPAD_4: 100,
|
|
1616
|
+
KEYBOARD_NUMPAD_5: 101,
|
|
1617
|
+
KEYBOARD_NUMPAD_6: 102,
|
|
1618
|
+
KEYBOARD_NUMPAD_7: 103,
|
|
1619
|
+
KEYBOARD_NUMPAD_8: 104,
|
|
1620
|
+
KEYBOARD_NUMPAD_9: 105,
|
|
1621
|
+
KEYBOARD_NUMPAD_MULTIPLY: 106,
|
|
1622
|
+
KEYBOARD_NUMPAD_ADD: 107,
|
|
1623
|
+
KEYBOARD_NUMPAD_SUBTRACT: 109,
|
|
1624
|
+
KEYBOARD_NUMPAD_DECIMAL: 110,
|
|
1625
|
+
KEYBOARD_NUMPAD_DIVIDE: 111,
|
|
1626
|
+
KEYBOARD_F1: 112,
|
|
1627
|
+
KEYBOARD_F2: 113,
|
|
1628
|
+
KEYBOARD_F3: 114,
|
|
1629
|
+
KEYBOARD_F4: 115,
|
|
1630
|
+
KEYBOARD_F5: 116,
|
|
1631
|
+
KEYBOARD_F6: 117,
|
|
1632
|
+
KEYBOARD_F7: 118,
|
|
1633
|
+
KEYBOARD_F8: 119,
|
|
1634
|
+
KEYBOARD_F9: 120,
|
|
1635
|
+
KEYBOARD_F10: 121,
|
|
1636
|
+
KEYBOARD_F11: 122,
|
|
1637
|
+
KEYBOARD_F12: 123,
|
|
1638
|
+
MOUSE_DOWN_LEFT: 0,
|
|
1639
|
+
MOUSE_DOWN_RIGHT: 1,
|
|
1640
|
+
MOUSE_DOWN_MIDDLE: 2,
|
|
1641
|
+
MOUSE_NO_BUTTON: 3,
|
|
1642
|
+
MOUSE_PRESS_LEFT: 4,
|
|
1643
|
+
MOUSE_PRESS_RIGHT: 5,
|
|
1644
|
+
MOUSE_PRESS_MIDDLE: 6,
|
|
1645
|
+
MOUSE_RELEASE_LEFT: 7,
|
|
1646
|
+
MOUSE_RELEASE_RIGHT: 8,
|
|
1647
|
+
MOUSE_RELEASE_MIDDLE: 9,
|
|
1648
|
+
MOUSE_ENTER: 10,
|
|
1649
|
+
MOUSE_LEAVE: 11,
|
|
1650
|
+
GLOBAL_MOUSE_DOWN_LEFT: 50,
|
|
1651
|
+
GLOBAL_MOUSE_DOWN_RIGHT: 51,
|
|
1652
|
+
GLOBAL_MOUSE_DOWN_MIDDLE: 52,
|
|
1653
|
+
GLOBAL_MOUSE_PRESS_LEFT: 53,
|
|
1654
|
+
GLOBAL_MOUSE_PRESS_RIGHT: 54,
|
|
1655
|
+
GLOBAL_MOUSE_PRESS_MIDDLE: 55,
|
|
1656
|
+
GLOBAL_MOUSE_RELEASE_LEFT: 56,
|
|
1657
|
+
GLOBAL_MOUSE_RELEASE_RIGHT: 57,
|
|
1658
|
+
GLOBAL_MOUSE_RELEASE_MIDDLE: 58,
|
|
1659
|
+
MOUSE_WHEEL_UP: 60,
|
|
1660
|
+
MOUSE_WHEEL_DOWN: 61,
|
|
1661
|
+
MOUSE_SCROLL_UP: 60,
|
|
1662
|
+
MOUSE_SCROLL_DOWN: 61,
|
|
1663
|
+
OUTSIDE_ROOM: 0,
|
|
1664
|
+
INTERSECT_BOUNDARY: 1,
|
|
1665
|
+
GAME_START: 2,
|
|
1666
|
+
GAME_END: 3,
|
|
1667
|
+
ROOM_START: 4,
|
|
1668
|
+
ROOM_END: 5,
|
|
1669
|
+
ANIMATION_END: 7,
|
|
1670
|
+
PATH_END: 8,
|
|
1671
|
+
USER_EVENT_0: 10,
|
|
1672
|
+
USER_EVENT_1: 11,
|
|
1673
|
+
USER_EVENT_2: 12,
|
|
1674
|
+
USER_EVENT_3: 13,
|
|
1675
|
+
USER_EVENT_4: 14,
|
|
1676
|
+
USER_EVENT_5: 15,
|
|
1677
|
+
USER_EVENT_6: 16,
|
|
1678
|
+
USER_EVENT_7: 17,
|
|
1679
|
+
USER_EVENT_8: 18,
|
|
1680
|
+
USER_EVENT_9: 19,
|
|
1681
|
+
USER_EVENT_10: 20,
|
|
1682
|
+
USER_EVENT_11: 21,
|
|
1683
|
+
USER_EVENT_12: 22,
|
|
1684
|
+
USER_EVENT_13: 23,
|
|
1685
|
+
USER_EVENT_14: 24,
|
|
1686
|
+
USER_EVENT_15: 25,
|
|
1687
|
+
OUTSIDE_VIEW_0: 40,
|
|
1688
|
+
OUTSIDE_VIEW_1: 41,
|
|
1689
|
+
OUTSIDE_VIEW_2: 42,
|
|
1690
|
+
OUTSIDE_VIEW_3: 43,
|
|
1691
|
+
OUTSIDE_VIEW_4: 44,
|
|
1692
|
+
OUTSIDE_VIEW_5: 45,
|
|
1693
|
+
OUTSIDE_VIEW_6: 46,
|
|
1694
|
+
OUTSIDE_VIEW_7: 47,
|
|
1695
|
+
INTERSECT_VIEW_0_BOUNDARY: 50,
|
|
1696
|
+
INTERSECT_VIEW_1_BOUNDARY: 51,
|
|
1697
|
+
INTERSECT_VIEW_2_BOUNDARY: 52,
|
|
1698
|
+
INTERSECT_VIEW_3_BOUNDARY: 53,
|
|
1699
|
+
INTERSECT_VIEW_4_BOUNDARY: 54,
|
|
1700
|
+
INTERSECT_VIEW_5_BOUNDARY: 55,
|
|
1701
|
+
INTERSECT_VIEW_6_BOUNDARY: 56,
|
|
1702
|
+
INTERSECT_VIEW_7_BOUNDARY: 57,
|
|
1703
|
+
ANIMATION_UPDATE: 58,
|
|
1704
|
+
ANIMATION_EVENT: 59,
|
|
1705
|
+
ASYNC_IMAGE_LOADED: 60,
|
|
1706
|
+
ASYNC_HTTP: 62,
|
|
1707
|
+
ASYNC_DIALOG: 63,
|
|
1708
|
+
ASYNC_IN_APP_PURCHASE: 66,
|
|
1709
|
+
ASYNC_CLOUD: 67,
|
|
1710
|
+
ASYNC_NETWORKING: 68,
|
|
1711
|
+
ASYNC_STEAM: 69,
|
|
1712
|
+
ASYNC_SOCIAL: 70,
|
|
1713
|
+
ASYNC_PUSH_NOTIFICATION: 71,
|
|
1714
|
+
ASYNC_SAVE_LOAD: 72,
|
|
1715
|
+
ASYNC_AUDIO_RECORDING: 73,
|
|
1716
|
+
ASYNC_AUDIO_PLAYBACK: 74,
|
|
1717
|
+
ASYNC_SYSTEM: 75,
|
|
1718
|
+
BROADCAST_MESSAGE: 76,
|
|
1719
|
+
WALLPAPER_SUBS_DATA: 81,
|
|
1720
|
+
STEP: 0,
|
|
1721
|
+
STEP_BEGIN: 1,
|
|
1722
|
+
STEP_END: 2
|
|
1723
|
+
};
|
|
1724
|
+
var EventName = {
|
|
1725
|
+
[0 /* CREATE */]: "Create",
|
|
1726
|
+
[1 /* DESTROY */]: "Destroy",
|
|
1727
|
+
[2 /* ALARM */]: "Alarm",
|
|
1728
|
+
[3 /* STEP */]: "Step",
|
|
1729
|
+
[4 /* COLLISION */]: "Collision",
|
|
1730
|
+
[5 /* KEY_DOWN */]: "Keyboard",
|
|
1731
|
+
[6 /* MOUSE */]: "Mouse",
|
|
1732
|
+
[7 /* OTHER */]: "Other",
|
|
1733
|
+
[8 /* DRAW */]: "Draw",
|
|
1734
|
+
[9 /* KEY_PRESS */]: "KeyPress",
|
|
1735
|
+
[10 /* KEY_RELEASE */]: "KeyRelease",
|
|
1736
|
+
[12 /* CLEAN_UP */]: "CleanUp",
|
|
1737
|
+
[13 /* GESTURE */]: "Gesture"
|
|
1738
|
+
};
|
|
1739
|
+
// src/generator/extract.ts
|
|
1740
|
+
function getEventFile(eventInput, numInput) {
|
|
1741
|
+
let event = 0;
|
|
1742
|
+
let needNum = true;
|
|
1743
|
+
let dynNum = false;
|
|
1744
|
+
switch (eventInput.toLowerCase()) {
|
|
1745
|
+
case "create":
|
|
1746
|
+
event = 0 /* CREATE */;
|
|
1747
|
+
needNum = false;
|
|
1748
|
+
break;
|
|
1749
|
+
case "destroy":
|
|
1750
|
+
event = 1 /* DESTROY */;
|
|
1751
|
+
needNum = false;
|
|
1752
|
+
break;
|
|
1753
|
+
case "alarm":
|
|
1754
|
+
event = 2 /* ALARM */;
|
|
1755
|
+
break;
|
|
1756
|
+
case "step":
|
|
1757
|
+
event = 3 /* STEP */;
|
|
1758
|
+
break;
|
|
1759
|
+
case "collision":
|
|
1760
|
+
event = 4 /* COLLISION */;
|
|
1761
|
+
dynNum = true;
|
|
1762
|
+
break;
|
|
1763
|
+
case "keydown":
|
|
1764
|
+
case "key_down":
|
|
1765
|
+
case "keyboard":
|
|
1766
|
+
event = 5 /* KEY_DOWN */;
|
|
1767
|
+
break;
|
|
1768
|
+
case "mouse":
|
|
1769
|
+
event = 6 /* MOUSE */;
|
|
1770
|
+
break;
|
|
1771
|
+
case "other":
|
|
1772
|
+
case "async":
|
|
1773
|
+
event = 7 /* OTHER */;
|
|
1774
|
+
break;
|
|
1775
|
+
case "draw":
|
|
1776
|
+
event = 8 /* DRAW */;
|
|
1777
|
+
break;
|
|
1778
|
+
case "keypress":
|
|
1779
|
+
case "key_press":
|
|
1780
|
+
event = 9 /* KEY_PRESS */;
|
|
1781
|
+
break;
|
|
1782
|
+
case "keyrelease":
|
|
1783
|
+
case "key_release":
|
|
1784
|
+
event = 10 /* KEY_RELEASE */;
|
|
1785
|
+
break;
|
|
1786
|
+
case "cleanup":
|
|
1787
|
+
case "clean_up":
|
|
1788
|
+
event = 12 /* CLEAN_UP */;
|
|
1789
|
+
break;
|
|
1790
|
+
case "gesture":
|
|
1791
|
+
event = 13 /* GESTURE */;
|
|
1792
|
+
break;
|
|
1793
|
+
default:
|
|
1794
|
+
event = null;
|
|
1795
|
+
}
|
|
1796
|
+
if (event === null)
|
|
1797
|
+
return { type: null, needNum: false, dynamicNum: false, num: null, numStr: null, name: null, fileName: null, collObj: null };
|
|
1798
|
+
const name = EventName[event];
|
|
1799
|
+
let numStr = event === 4 /* COLLISION */ ? numInput : numInput.toUpperCase();
|
|
1800
|
+
if (needNum && !dynNum && !(numStr in EVENT))
|
|
1801
|
+
return { type: event, needNum, dynamicNum: dynNum, num: null, numStr, name, fileName: null, collObj: null };
|
|
1802
|
+
return {
|
|
1803
|
+
type: event,
|
|
1804
|
+
num: EVENT[numStr] ?? 0,
|
|
1805
|
+
needNum,
|
|
1806
|
+
dynamicNum: dynNum,
|
|
1807
|
+
numStr,
|
|
1808
|
+
name,
|
|
1809
|
+
fileName: event === 4 /* COLLISION */ ? `${name}_${numInput}` : `${name}_${EVENT[numStr] ?? 0}`,
|
|
1810
|
+
collObj: event === 4 /* COLLISION */ ? `{"name":"${numInput}","path":"objects/${numInput}/${numInput}.yy",}` : null
|
|
1811
|
+
};
|
|
1812
|
+
}
|
|
1813
|
+
function extractIntegrationData(file, config) {
|
|
1814
|
+
const res = [];
|
|
1815
|
+
let invalid = false;
|
|
1816
|
+
const blocks = [];
|
|
1817
|
+
for (const match of file.content.matchAll(intgBlockRegex)) {
|
|
1818
|
+
const { name: header, body } = match.groups;
|
|
1819
|
+
const flags = header.split("--")[1]?.split(" ").map((f) => f.trim()) ?? [];
|
|
1820
|
+
const headerSplit = header.split("--")[0].split("as").map((h) => !h.includes("collision") ? h.trim().toLowerCase() : h.trim());
|
|
1821
|
+
const name = headerSplit[0].replace("event", "").replace("ev", "").trim();
|
|
1822
|
+
const eventType = headerSplit[0].endsWith("event") || headerSplit[0].endsWith("ev") ? name : headerSplit[1] ?? null;
|
|
1823
|
+
if (!name || !body) {
|
|
1824
|
+
if (config.onNotFound === "error") {
|
|
1825
|
+
log.error(`Invalid integration block found: \x1B[34m${match[0]}\x1B[0m. Aborting...`);
|
|
1826
|
+
return null;
|
|
1827
|
+
} else {
|
|
1828
|
+
log.warn(`Invalid integration block found: \x1B[34m${match[0]}\x1B[0m. Skipping this block...`);
|
|
1829
|
+
continue;
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
let event = null;
|
|
1833
|
+
if (eventType) {
|
|
1834
|
+
const eventSplit = eventType.split(":").filter(Boolean).map((e) => e.trim());
|
|
1835
|
+
event = getEventFile(eventSplit[0], eventSplit[1] ?? "");
|
|
1836
|
+
if (event.type === null) {
|
|
1837
|
+
if (config.onNotFound === "error") {
|
|
1838
|
+
log.error(`Invalid event type \x1B[33m${eventType}\x1B[0m found: \x1B[34m#[${header}]\x1B[0m. Aborting...`);
|
|
1839
|
+
return null;
|
|
1840
|
+
} else {
|
|
1841
|
+
log.warn(`Invalid event type \x1B[33m${eventType}\x1B[0m found: \x1B[34m#[${header}]\x1B[0m. Skipping this block...`);
|
|
1842
|
+
continue;
|
|
1843
|
+
}
|
|
1844
|
+
} else if (event.numStr === null && event.needNum) {
|
|
1845
|
+
if (config.onNotFound === "error") {
|
|
1846
|
+
log.error(`Invalid event number \x1B[33m${event.numStr}\x1B[0m found: \x1B[34m#[${header}]\x1B[0m. Aborting...`);
|
|
1847
|
+
return null;
|
|
1848
|
+
} else {
|
|
1849
|
+
log.warn(`Invalid event number \x1B[33m${event.numStr}\x1B[0m found: \x1B[34m#[${header}]\x1B[0m. Skipping this block...`);
|
|
1850
|
+
continue;
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
if (config.debugLevel >= 1)
|
|
1855
|
+
log.debug(`Integration block found: \x1B[34m#[${name}]\x1B[0m in \x1B[33m${file.name}\x1B[0m from \x1B[32m${file.path}\x1B[0m.`);
|
|
1856
|
+
let processedBody = body.trim();
|
|
1857
|
+
if (flags.includes("debug") && config.debugLevel >= 1)
|
|
1858
|
+
processedBody = "";
|
|
1859
|
+
if ((flags.includes("dev") || flags.includes("development")) && config.production)
|
|
1860
|
+
processedBody = "";
|
|
1861
|
+
if ((flags.includes("prod") || flags.includes("production")) && !config.production)
|
|
1862
|
+
processedBody = "";
|
|
1863
|
+
if (flags.includes("skip") || flags.includes("disabled"))
|
|
1864
|
+
processedBody = "";
|
|
1865
|
+
if (config.targetPlatform !== "all") {
|
|
1866
|
+
const platformExclusion = flags.find((f) => f.startsWith("!"));
|
|
1867
|
+
if (platformExclusion) {
|
|
1868
|
+
if (platformExclusion.slice(1) === config.targetPlatform)
|
|
1869
|
+
processedBody = "";
|
|
1870
|
+
} else if (!flags.includes(config.targetPlatform) && !flags.includes("all"))
|
|
1871
|
+
processedBody = "";
|
|
1872
|
+
}
|
|
1873
|
+
const existsBlock = blocks.find((b) => b.name === name);
|
|
1874
|
+
if (existsBlock) {
|
|
1875
|
+
existsBlock.body += (existsBlock.body !== "" ? `
|
|
1876
|
+
|
|
1877
|
+
` : "") + processedBody;
|
|
1878
|
+
if (flags.find((f) => f === "exclude") && !existsBlock.removeBodies.includes(processedBody))
|
|
1879
|
+
existsBlock.removeBodies.push(processedBody);
|
|
1880
|
+
} else {
|
|
1881
|
+
blocks.push({
|
|
1882
|
+
name,
|
|
1883
|
+
body: processedBody,
|
|
1884
|
+
path: "",
|
|
1885
|
+
event,
|
|
1886
|
+
backup: null,
|
|
1887
|
+
flags,
|
|
1888
|
+
removeBodies: flags.find((f) => f === "exclude") ? [processedBody] : []
|
|
1889
|
+
});
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
[...file.content.matchAll(intgRegex)].forEach((match) => {
|
|
1893
|
+
if (invalid)
|
|
1894
|
+
return;
|
|
1895
|
+
const { targets, path: path3 } = match.groups;
|
|
1896
|
+
if (!targets || !path3) {
|
|
1897
|
+
if (config.onNotFound === "error") {
|
|
1898
|
+
log.error(`Invalid integration statement found: \x1B[34m${match[0]}\x1B[0m in \x1B[33m${file.name}\x1B[0m from \x1B[32m${file.path}\x1B[0m. Aborting...`);
|
|
1899
|
+
invalid = true;
|
|
1900
|
+
} else
|
|
1901
|
+
log.warn(`Invalid integration statement found: \x1B[34m${match[0]}\x1B[0m in \x1B[33m${file.name}\x1B[0m from \x1B[32m${file.path}\x1B[0m. Skipping this statement...`);
|
|
1902
|
+
return;
|
|
1903
|
+
}
|
|
1904
|
+
const targetPath = normalizePath(resolvePath(path3.slice(1, -1))).replace(".gml", "");
|
|
1905
|
+
if (targets === "*") {
|
|
1906
|
+
if (config.debugLevel >= 1)
|
|
1907
|
+
log.debug(`Integration statement found: \x1B[34mintg * to ${path3}\x1B[0m in \x1B[33m${file.name}\x1B[0m from \x1B[32m${file.path}\x1B[0m.`);
|
|
1908
|
+
res.push({
|
|
1909
|
+
path: targetPath,
|
|
1910
|
+
targets: blocks,
|
|
1911
|
+
backup: null,
|
|
1912
|
+
content: {}
|
|
1913
|
+
});
|
|
1914
|
+
} else {
|
|
1915
|
+
const targetsArr = !targets.startsWith("{") ? [targets] : targets.slice(1, -1).split(",").map((t) => t.trim());
|
|
1916
|
+
res.push({
|
|
1917
|
+
path: targetPath,
|
|
1918
|
+
targets: [],
|
|
1919
|
+
backup: null,
|
|
1920
|
+
content: {}
|
|
1921
|
+
});
|
|
1922
|
+
for (const target of targetsArr) {
|
|
1923
|
+
const targetBlock = blocks.find((b) => b.name === target.toLowerCase());
|
|
1924
|
+
if (!targetBlock) {
|
|
1925
|
+
if (config.onNotFound === "error") {
|
|
1926
|
+
log.error(`Target \x1B[33m${target}\x1B[0m not found for integration targets: \x1B[34m${targets}\x1B[0m. Aborting...`);
|
|
1927
|
+
invalid = true;
|
|
1928
|
+
} else
|
|
1929
|
+
log.warn(`Target \x1B[33m${target}\x1B[0m not found for integration targets: \x1B[34m${targets}\x1B[0m. Skipping this statement...`);
|
|
1930
|
+
return;
|
|
1931
|
+
}
|
|
1932
|
+
if (config.debugLevel >= 1)
|
|
1933
|
+
log.debug(`Integration statement found: \x1B[34mintg ${target} to ${path3}\x1B[0m in \x1B[33m${file.name === "" ? "index" : file.name}\x1B[0m from \x1B[32m${file.path}\x1B[0m.`);
|
|
1934
|
+
res.at(-1).targets.push(targetBlock);
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
});
|
|
1938
|
+
if (invalid)
|
|
1939
|
+
return null;
|
|
1940
|
+
return res;
|
|
1941
|
+
}
|
|
1942
|
+
// src/generator/write.ts
|
|
1943
|
+
async function generateSourceCode(intgData, config, projectPath) {
|
|
1944
|
+
const res = {};
|
|
1945
|
+
const projectPathSplit = projectPath.split("/");
|
|
1946
|
+
let generatedCnt = 0;
|
|
1947
|
+
if (config.clearOutputDir) {
|
|
1948
|
+
await clearOutDir();
|
|
1949
|
+
log.debug(`Output directory cleared.`);
|
|
1950
|
+
}
|
|
1951
|
+
projectPathSplit.pop();
|
|
1952
|
+
log.debug(`Generating source code...`);
|
|
1953
|
+
for (const data of intgData) {
|
|
1954
|
+
if (!data)
|
|
1955
|
+
continue;
|
|
1956
|
+
let genPath = data.path.replace(commonPath([resolvePath("./out"), data.path]) ?? "", "");
|
|
1957
|
+
if (config.useGmAssetPath && genPath.includes("scripts"))
|
|
1958
|
+
genPath += "/" + (data.path.split("scripts")[1]?.split("/").pop() ?? "");
|
|
1959
|
+
const outFile = normalizePath(resolvePath(`./.out${genPath}.gml`));
|
|
1960
|
+
const outFileSplit = outFile.split("/");
|
|
1961
|
+
if (genPath === "") {
|
|
1962
|
+
if (config.onNotFound === "error") {
|
|
1963
|
+
log.error(`Failed to generate source code for \x1B[33m${data.path}\x1B[0m. Aborting...`);
|
|
1964
|
+
return res;
|
|
1965
|
+
} else {
|
|
1966
|
+
log.warn(`Failed to generate source code for \x1B[33m${data.path}\x1B[0m. Skipping this file...`);
|
|
1967
|
+
continue;
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
let body = "";
|
|
1971
|
+
const dataLen = data.targets.length;
|
|
1972
|
+
if (config.useGmAssetPath && genPath.includes("objects")) {
|
|
1973
|
+
for (const [idx, target] of data.targets.entries()) {
|
|
1974
|
+
if (!target.event) {
|
|
1975
|
+
body += target.body + `
|
|
1976
|
+
`;
|
|
1977
|
+
if (idx < dataLen - 1)
|
|
1978
|
+
body += `
|
|
1979
|
+
`;
|
|
1980
|
+
} else {
|
|
1981
|
+
const eventFile = normalizePath(resolvePath(`./.out${genPath}/${target.event.fileName}.gml`));
|
|
1982
|
+
const resolvedGenPath = eventFile.replace(".out/", "");
|
|
1983
|
+
const resvGenPathSplit = resolvedGenPath.split("/");
|
|
1984
|
+
const outFileName = resvGenPathSplit.pop();
|
|
1985
|
+
const objectIdx = resvGenPathSplit.findIndex((slug) => slug === "objects");
|
|
1986
|
+
const writePath = `${projectPathSplit.join("/")}/objects/${resvGenPathSplit.at(-1)}/${outFileName}`;
|
|
1987
|
+
const writePathRel = writePath.split("/").slice(-3).join("/");
|
|
1988
|
+
await fsRuntime.writeText(eventFile, target.body + `
|
|
1989
|
+
`);
|
|
1990
|
+
data.content[resolvedGenPath] = target.body;
|
|
1991
|
+
target.path = resolvedGenPath;
|
|
1992
|
+
res[writePath] = {
|
|
1993
|
+
fullPath: resolvedGenPath,
|
|
1994
|
+
dirPath: `${resvGenPathSplit.slice(objectIdx + 1, -1).join("/")}`,
|
|
1995
|
+
content: target.body,
|
|
1996
|
+
backup: null,
|
|
1997
|
+
isNew: false,
|
|
1998
|
+
event: target.event,
|
|
1999
|
+
toRemoves: target.removeBodies
|
|
2000
|
+
};
|
|
2001
|
+
generatedCnt++;
|
|
2002
|
+
if (!config.noBackup) {
|
|
2003
|
+
if (await fileExists(writePath)) {
|
|
2004
|
+
try {
|
|
2005
|
+
target.backup = await fsRuntime.readText(writePath);
|
|
2006
|
+
res[writePath].backup = target.backup;
|
|
2007
|
+
log.debug(`Backup created for \x1B[32m${writePathRel}\x1B[0m.`);
|
|
2008
|
+
} catch (error) {
|
|
2009
|
+
log.error(`Failed to create backup for \x1B[32m${writePathRel}\x1B[0m: ${error}`);
|
|
2010
|
+
}
|
|
2011
|
+
} else
|
|
2012
|
+
log.debug(`File: \x1B[33m${writePathRel}\x1B[0m not found. No backup created.`);
|
|
2013
|
+
}
|
|
2014
|
+
log.info(`Source code generated for \x1B[34m${target.event.name + (target.event.numStr ? `\x1B[0m:\x1B[36m${target.event.numStr}\x1B[34m` : "")} Event\x1B[0m to \x1B[32m${writePathRel}\x1B[0m.`);
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
if (body !== "") {
|
|
2018
|
+
await fsRuntime.writeText(outFile, body);
|
|
2019
|
+
generatedCnt++;
|
|
2020
|
+
log.debug(`Source code generated for \x1B[34m${outFileSplit.pop()}\x1B[0m to \x1B[32m${outFileSplit.slice(-2).join("/")}\x1B[0m. This file won't be integrated to the GM project due to non-regular GM asset path.`);
|
|
2021
|
+
} else if (!generatedCnt)
|
|
2022
|
+
log.warn(`No source code generated for \x1B[34m${outFileSplit.pop()}\x1B[0m to \x1B[32m${outFileSplit.slice(-3).join("/")}\x1B[0m.`);
|
|
2023
|
+
} else {
|
|
2024
|
+
for (const [idx, target] of data.targets.entries()) {
|
|
2025
|
+
body += target.body + `
|
|
2026
|
+
`;
|
|
2027
|
+
if (idx < dataLen - 1)
|
|
2028
|
+
body += `
|
|
2029
|
+
`;
|
|
2030
|
+
}
|
|
2031
|
+
if (body !== "") {
|
|
2032
|
+
const resolvedGenPath = normalizePath(resolvePath(`./${genPath}`)) + ".gml";
|
|
2033
|
+
const resvGenPathSplit = resolvedGenPath.split("/");
|
|
2034
|
+
const outFileName = resvGenPathSplit.pop();
|
|
2035
|
+
const scriptIdx = resvGenPathSplit.findIndex((slug) => slug === "scripts");
|
|
2036
|
+
const writePath = `${projectPathSplit.join("/")}/scripts/${outFileName.replace(".gml", "")}/${outFileName}`;
|
|
2037
|
+
const writePathRel = writePath.split("/").slice(-3).join("/");
|
|
2038
|
+
await fsRuntime.writeText(outFile, body);
|
|
2039
|
+
data.content[resolvedGenPath] = body;
|
|
2040
|
+
res[writePath] = {
|
|
2041
|
+
fullPath: resolvedGenPath,
|
|
2042
|
+
dirPath: `${resvGenPathSplit.slice(scriptIdx + 1, -1).join("/")}`,
|
|
2043
|
+
content: body,
|
|
2044
|
+
backup: null,
|
|
2045
|
+
isNew: false,
|
|
2046
|
+
event: null,
|
|
2047
|
+
toRemoves: data.targets.reduce((acc, curr) => {
|
|
2048
|
+
acc.push(...curr.removeBodies);
|
|
2049
|
+
return acc;
|
|
2050
|
+
}, [])
|
|
2051
|
+
};
|
|
2052
|
+
generatedCnt++;
|
|
2053
|
+
if (!config.noBackup) {
|
|
2054
|
+
if (await fileExists(writePath)) {
|
|
2055
|
+
try {
|
|
2056
|
+
data.backup = await fsRuntime.readText(writePath);
|
|
2057
|
+
res[writePath].backup = data.backup;
|
|
2058
|
+
log.debug(`Backup created for \x1B[32m${writePathRel}\x1B[0m.`);
|
|
2059
|
+
} catch (error) {
|
|
2060
|
+
log.error(`Failed to create backup for \x1B[32m${writePathRel}\x1B[0m: ${error}`);
|
|
2061
|
+
}
|
|
2062
|
+
} else
|
|
2063
|
+
log.debug(`File: \x1B[33m${writePathRel}\x1B[0m not found. No backup created.`);
|
|
2064
|
+
}
|
|
2065
|
+
log.info(`Source code generated for \x1B[34m${outFileSplit.pop()}\x1B[0m to \x1B[32m${outFileSplit.slice(-3).join("/")}\x1B[0m.`);
|
|
2066
|
+
} else
|
|
2067
|
+
log.warn(`No source code generated for \x1B[34m${outFileSplit.pop()}\x1B[0m to \x1B[32m${outFileSplit.slice(-3).join("/")}\x1B[0m.`);
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
log.info(`All source code generated. Generated \x1B[32m${generatedCnt}\x1B[0m file(s).`);
|
|
2071
|
+
return res;
|
|
2072
|
+
}
|
|
2073
|
+
// src/generator/gm-asset.ts
|
|
2074
|
+
function stripTrailingCommas(input) {
|
|
2075
|
+
const result = [];
|
|
2076
|
+
let i = 0;
|
|
2077
|
+
while (i < input.length) {
|
|
2078
|
+
const ch = input[i];
|
|
2079
|
+
if (ch === '"') {
|
|
2080
|
+
result.push(ch);
|
|
2081
|
+
i++;
|
|
2082
|
+
while (i < input.length) {
|
|
2083
|
+
const sc = input[i];
|
|
2084
|
+
result.push(sc);
|
|
2085
|
+
if (sc === "\\") {
|
|
2086
|
+
i++;
|
|
2087
|
+
if (i < input.length) {
|
|
2088
|
+
result.push(input[i]);
|
|
2089
|
+
i++;
|
|
2090
|
+
}
|
|
2091
|
+
} else if (sc === '"') {
|
|
2092
|
+
i++;
|
|
2093
|
+
break;
|
|
2094
|
+
} else {
|
|
2095
|
+
i++;
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
continue;
|
|
2099
|
+
}
|
|
2100
|
+
if (ch === ",") {
|
|
2101
|
+
let j = i + 1;
|
|
2102
|
+
while (j < input.length && /\s/.test(input[j]))
|
|
2103
|
+
j++;
|
|
2104
|
+
if (j < input.length && (input[j] === "}" || input[j] === "]")) {
|
|
2105
|
+
i++;
|
|
2106
|
+
continue;
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
result.push(ch);
|
|
2110
|
+
i++;
|
|
2111
|
+
}
|
|
2112
|
+
return result.join("");
|
|
2113
|
+
}
|
|
2114
|
+
function createYYScript(projectYyp, rescName, dir, options) {
|
|
2115
|
+
const dirSplit = dir.split("/");
|
|
2116
|
+
return `{
|
|
2117
|
+
"$GMScript":"v1",
|
|
2118
|
+
"%Name":"${rescName}",
|
|
2119
|
+
"isCompatibility":false,
|
|
2120
|
+
"isDnD":${options?.isDnd ?? false},
|
|
2121
|
+
"name":"${rescName}",
|
|
2122
|
+
"parent":{
|
|
2123
|
+
"name":"${dir !== "" ? dirSplit.pop() : projectYyp.replace(".yyp", "")}",
|
|
2124
|
+
"path":"${dir !== "" ? `folders/${dir}.yy` : `${projectYyp}.yyp`}",
|
|
2125
|
+
},
|
|
2126
|
+
"resourceType":"GMScript",
|
|
2127
|
+
"resourceVersion":"2.0",
|
|
2128
|
+
}`;
|
|
2129
|
+
}
|
|
2130
|
+
function createYYObject(projectYyp, rescName, dir, eventList, options) {
|
|
2131
|
+
const dirSplit = dir.split("/");
|
|
2132
|
+
const eventStr = eventList?.join(`
|
|
2133
|
+
`) ?? null;
|
|
2134
|
+
return `{
|
|
2135
|
+
"$GMObject":"",
|
|
2136
|
+
"%Name":"${rescName}",
|
|
2137
|
+
"eventList":[
|
|
2138
|
+
${eventStr ?? ""}
|
|
2139
|
+
],
|
|
2140
|
+
"managed":true,
|
|
2141
|
+
"name":"${rescName}",
|
|
2142
|
+
"overriddenProperties":[],
|
|
2143
|
+
"parent":{
|
|
2144
|
+
"name":"${dir !== "" ? dirSplit.pop() : projectYyp.replace(".yyp", "")}",
|
|
2145
|
+
"path":"${dir !== "" ? `folders/${dir}.yy` : `${projectYyp}.yyp`}",
|
|
2146
|
+
},
|
|
2147
|
+
"parentObjectId":null,
|
|
2148
|
+
"persistent":false,
|
|
2149
|
+
"physicsAngularDamping":0.1,
|
|
2150
|
+
"physicsDensity":0.5,
|
|
2151
|
+
"physicsFriction":0.2,
|
|
2152
|
+
"physicsGroup":1,
|
|
2153
|
+
"physicsKinematic":false,
|
|
2154
|
+
"physicsLinearDamping":0.1,
|
|
2155
|
+
"physicsObject":false,
|
|
2156
|
+
"physicsRestitution":0.1,
|
|
2157
|
+
"physicsSensor":false,
|
|
2158
|
+
"physicsShape":1,
|
|
2159
|
+
"physicsShapePoints":[],
|
|
2160
|
+
"physicsStartAwake":true,
|
|
2161
|
+
"properties":[],
|
|
2162
|
+
"resourceType":"GMObject",
|
|
2163
|
+
"resourceVersion":"2.0",
|
|
2164
|
+
"solid":false,
|
|
2165
|
+
"spriteId":null,
|
|
2166
|
+
"spriteMaskId":null,
|
|
2167
|
+
"visible":true,
|
|
2168
|
+
}`;
|
|
2169
|
+
}
|
|
2170
|
+
function parseGMJson(raw) {
|
|
2171
|
+
const validJson = stripTrailingCommas(raw);
|
|
2172
|
+
return JSON.parse(validJson);
|
|
2173
|
+
}
|
|
2174
|
+
function createGMFolderStr(path3) {
|
|
2175
|
+
const name = path3.split("/").pop();
|
|
2176
|
+
return ` {"$GMFolder":"","%Name":"${name}","folderPath":"folders/${path3}.yy","name":"${name}","resourceType":"GMFolder","resourceVersion":"2.0",},`;
|
|
2177
|
+
}
|
|
2178
|
+
function createGMResourceStr(type, name) {
|
|
2179
|
+
return ` {"id":{"name":"${name}","path":"${type}/${name}/${name}.yy",},},`;
|
|
2180
|
+
}
|
|
2181
|
+
function createGMEventStr(eventType, eventNum, options) {
|
|
2182
|
+
return ` {"$GMEvent":"v1","%Name":"",${eventNum !== null ? `"collisionObjectId":${typeof eventNum === "string" ? eventNum : null},` : ""}"eventNum":${typeof eventNum === "number" ? eventNum : 0},"eventType":${eventType},"isDnD":${options?.isDnd ?? false},"name":"","resourceType":"GMEvent","resourceVersion":"2.0",},`;
|
|
2183
|
+
}
|
|
2184
|
+
async function createGMResource(project, filePath, intgContent, options) {
|
|
2185
|
+
const pathSplit = filePath.split("/");
|
|
2186
|
+
if (filePath.includes("scripts")) {
|
|
2187
|
+
const yyFilePath = filePath.replace(".gml", ".yy");
|
|
2188
|
+
if (!await fileExists(yyFilePath)) {
|
|
2189
|
+
const yyContent = createYYScript(project["%Name"], pathSplit.at(-2), intgContent.dirPath, options);
|
|
2190
|
+
try {
|
|
2191
|
+
await fsRuntime.writeText(yyFilePath, yyContent);
|
|
2192
|
+
return { type: "scripts", name: pathSplit.at(-2), dir: null };
|
|
2193
|
+
} catch (error) {
|
|
2194
|
+
console.error(`Failed to create .yy file for ${filePath}: ${error}`);
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
} else if (filePath.includes("objects")) {
|
|
2198
|
+
const yyFilePath = `${pathSplit.slice(0, -1).join("/")}/${pathSplit.at(-2)}.yy`;
|
|
2199
|
+
if (!await fileExists(yyFilePath)) {
|
|
2200
|
+
const yyContent = createYYObject(project["%Name"], pathSplit.at(-2), intgContent.dirPath, intgContent.event ? [createGMEventStr(intgContent.event.type, intgContent.event.type === 4 /* COLLISION */ ? intgContent.event.collObj : intgContent.event.num, options)] : [], options);
|
|
2201
|
+
try {
|
|
2202
|
+
await fsRuntime.writeText(yyFilePath, yyContent);
|
|
2203
|
+
return { type: "objects", name: pathSplit.at(-2), dir: null };
|
|
2204
|
+
} catch (error) {
|
|
2205
|
+
console.error(`Failed to create .yy file for ${filePath}: ${error}`);
|
|
2206
|
+
}
|
|
2207
|
+
} else {
|
|
2208
|
+
const yyFileSplit = (await fsRuntime.readText(yyFilePath)).split(`
|
|
2209
|
+
`);
|
|
2210
|
+
const eventStartIdx = yyFileSplit.findIndex((line) => line.includes("eventList"));
|
|
2211
|
+
const eventEndIdx = yyFileSplit.findIndex((line, idx) => idx > eventStartIdx && line.includes("]"));
|
|
2212
|
+
const existsEvents = yyFileSplit.slice(eventStartIdx + 1, eventEndIdx);
|
|
2213
|
+
const typeNum = `${intgContent.event.type}|${intgContent.event.num}`;
|
|
2214
|
+
const dupe = existsEvents.find((line) => {
|
|
2215
|
+
const evNum = line.split('eventNum":')[1]?.split(",")[0];
|
|
2216
|
+
const evType = line.split('eventType":')[1]?.split(",")[0];
|
|
2217
|
+
return `${evType}|${evNum}` === typeNum;
|
|
2218
|
+
});
|
|
2219
|
+
if (!dupe) {
|
|
2220
|
+
const eventStr = createGMEventStr(intgContent.event.type, intgContent.event.type === 4 /* COLLISION */ ? intgContent.event.collObj : intgContent.event.num, options);
|
|
2221
|
+
yyFileSplit.splice(eventEndIdx, 0, eventStr);
|
|
2222
|
+
try {
|
|
2223
|
+
await fsRuntime.writeText(yyFilePath, yyFileSplit.join(`
|
|
2224
|
+
`));
|
|
2225
|
+
} catch (error) {
|
|
2226
|
+
console.error(`Failed to modify .yy file for ${filePath}: ${error}`);
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
return { type: null, name: null, dir: null };
|
|
2232
|
+
}
|
|
2233
|
+
async function removeGMResource(filePath, scanPath, intgContent) {
|
|
2234
|
+
const pathSplit = filePath.split("/");
|
|
2235
|
+
if (filePath.includes("scripts")) {
|
|
2236
|
+
try {
|
|
2237
|
+
await fsRuntime.delete(filePath);
|
|
2238
|
+
await fsRuntime.delete(filePath.replace(".gml", ".yy"));
|
|
2239
|
+
pathSplit.pop();
|
|
2240
|
+
await deleteDir(pathSplit.join("/"), scanPath);
|
|
2241
|
+
return { type: "scripts", name: pathSplit.at(-1), dir: intgContent.dirPath };
|
|
2242
|
+
} catch (error) {
|
|
2243
|
+
console.error(`Failed to remove resource for ${filePath}: ${error}`);
|
|
2244
|
+
}
|
|
2245
|
+
} else if (filePath.includes("objects")) {
|
|
2246
|
+
try {
|
|
2247
|
+
await fsRuntime.delete(filePath);
|
|
2248
|
+
pathSplit.pop();
|
|
2249
|
+
if (intgContent.isNew) {
|
|
2250
|
+
await deleteDir(pathSplit.join("/"), scanPath);
|
|
2251
|
+
return { type: "objects", name: pathSplit.at(-1), dir: intgContent.dirPath };
|
|
2252
|
+
} else {
|
|
2253
|
+
const yyFilePath = `${pathSplit.join("/")}/${pathSplit.at(-1)}.yy`;
|
|
2254
|
+
const yyFileSplit = (await fsRuntime.readText(yyFilePath)).split(`
|
|
2255
|
+
`);
|
|
2256
|
+
const eventStartIdx = yyFileSplit.findIndex((line) => line.includes("eventList"));
|
|
2257
|
+
const eventEndIdx = yyFileSplit.findIndex((line, idx) => idx > eventStartIdx && line.includes("]"));
|
|
2258
|
+
const existsEvents = yyFileSplit.slice(eventStartIdx + 1, eventEndIdx);
|
|
2259
|
+
const typeNum = `${intgContent.event.type}|${intgContent.event.num}`;
|
|
2260
|
+
const evIdx = existsEvents.findIndex((line) => {
|
|
2261
|
+
const evNum = line.split('eventNum":')[1]?.split(",")[0];
|
|
2262
|
+
const evType = line.split('eventType":')[1]?.split(",")[0];
|
|
2263
|
+
return `${evType}|${evNum}` === typeNum;
|
|
2264
|
+
});
|
|
2265
|
+
if (evIdx > -1) {
|
|
2266
|
+
yyFileSplit.splice(eventStartIdx + evIdx + 1, 1);
|
|
2267
|
+
await fsRuntime.writeText(yyFilePath, yyFileSplit.join(`
|
|
2268
|
+
`));
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
} catch (error) {
|
|
2272
|
+
console.error(`Failed to remove resource for ${filePath}: ${error}`);
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
return { type: null, name: null, dir: null };
|
|
2276
|
+
}
|
|
2277
|
+
async function modifyYyProject(type, projectPath, resource = null, folder = null) {
|
|
2278
|
+
const raw = await fsRuntime.readText(projectPath);
|
|
2279
|
+
const rawLines = raw.split(`
|
|
2280
|
+
`);
|
|
2281
|
+
const toAddRescs = resource !== null && !Array.isArray(resource) ? [resource] : resource;
|
|
2282
|
+
const toAddFolders = folder !== null && !Array.isArray(folder) ? [folder] : folder;
|
|
2283
|
+
if (type === "add") {
|
|
2284
|
+
let addedFolderCnt = 0;
|
|
2285
|
+
if (toAddFolders) {
|
|
2286
|
+
let folderStartIdx = rawLines.findIndex((line) => line.includes("Folders"));
|
|
2287
|
+
let folderEndIdx = rawLines.findIndex((line, idx) => idx >= folderStartIdx && line.includes("]"));
|
|
2288
|
+
const existsFolders = folderEndIdx > folderStartIdx ? rawLines.slice(folderStartIdx + 1, folderEndIdx) : [];
|
|
2289
|
+
for (const folderDir of toAddFolders) {
|
|
2290
|
+
const folderSplit = folderDir.split("/");
|
|
2291
|
+
for (let i = 0;i < folderSplit.length; i++) {
|
|
2292
|
+
let name = "";
|
|
2293
|
+
for (let j = 0;j < i + 1; j++) {
|
|
2294
|
+
name += folderSplit[j] + "/";
|
|
2295
|
+
}
|
|
2296
|
+
name = name.slice(0, -1);
|
|
2297
|
+
const dupe = existsFolders.find((line) => line.includes(`"%Name":"${name}"`));
|
|
2298
|
+
if (!dupe) {
|
|
2299
|
+
if (!existsFolders.length) {
|
|
2300
|
+
rawLines[folderStartIdx] = ' "Folders":[';
|
|
2301
|
+
rawLines.splice(folderStartIdx + 1, 0, " ],");
|
|
2302
|
+
folderEndIdx = folderStartIdx + 1;
|
|
2303
|
+
}
|
|
2304
|
+
rawLines.splice(folderEndIdx + addedFolderCnt, 0, createGMFolderStr(name));
|
|
2305
|
+
addedFolderCnt++;
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
let addedRescCnt = 0;
|
|
2311
|
+
if (toAddRescs) {
|
|
2312
|
+
const rescStartIdx = rawLines.findIndex((line) => line.includes("resources"));
|
|
2313
|
+
const rescEndIdx = rawLines.findIndex((line, idx) => idx > rescStartIdx && line.includes("]"));
|
|
2314
|
+
const existsRescs = rawLines.slice(rescStartIdx + 1, rescEndIdx);
|
|
2315
|
+
for (const resc of toAddRescs) {
|
|
2316
|
+
const name = resc.split('name":"')[1].split('",')[0];
|
|
2317
|
+
const dupe = existsRescs.find((line) => line.includes(`"name":"${name}"`));
|
|
2318
|
+
if (!dupe) {
|
|
2319
|
+
rawLines.splice(rescEndIdx + addedRescCnt, 0, resc);
|
|
2320
|
+
addedRescCnt++;
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
} else {
|
|
2325
|
+
if (toAddRescs) {
|
|
2326
|
+
const rescStartIdx = rawLines.findIndex((line) => line.includes("resources"));
|
|
2327
|
+
let rescEndIdx = rawLines.findIndex((line, idx) => idx > rescStartIdx && line.includes("]"));
|
|
2328
|
+
for (const resc of toAddRescs) {
|
|
2329
|
+
const [type2, name] = resc.split(",");
|
|
2330
|
+
if (!type2 || !name)
|
|
2331
|
+
continue;
|
|
2332
|
+
const rescIdx = rawLines.findIndex((line, idx) => idx > rescStartIdx && idx < rescEndIdx && line.includes(`name":"${name}`));
|
|
2333
|
+
if (rescIdx > -1) {
|
|
2334
|
+
rawLines.splice(rescIdx, 1);
|
|
2335
|
+
rescEndIdx--;
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
const res = rawLines.join(`
|
|
2341
|
+
`);
|
|
2342
|
+
try {
|
|
2343
|
+
await fsRuntime.writeText(projectPath, res);
|
|
2344
|
+
} catch (error) {
|
|
2345
|
+
console.error(`Failed to modify project: ${error}`);
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
// src/integration/integrate.ts
|
|
2349
|
+
async function integrateSourceCodes(genFile, config, projectPath) {
|
|
2350
|
+
const integrations = Object.entries(genFile);
|
|
2351
|
+
if (integrations.length <= 0)
|
|
2352
|
+
return 0;
|
|
2353
|
+
projectPath = normalizePath(resolvePath(projectPath));
|
|
2354
|
+
if (!projectPath.endsWith(".yyp")) {
|
|
2355
|
+
log.error(`Invalid project path: \x1B[32m${projectPath}\x1B[0m. Aborting...`);
|
|
2356
|
+
return null;
|
|
2357
|
+
}
|
|
2358
|
+
const limit = 10;
|
|
2359
|
+
const gmProject = parseGMJson(await fsRuntime.readText(projectPath));
|
|
2360
|
+
const projectPathSplit = projectPath.split("/");
|
|
2361
|
+
const newFolders = [];
|
|
2362
|
+
const newResources = [];
|
|
2363
|
+
let intgCnt = 0;
|
|
2364
|
+
log.debug(`Integrating generated source codes to \x1B[34m${projectPathSplit.pop()?.replace(".yyp", "")}\x1B[0m project...`);
|
|
2365
|
+
for (let i = 0;i < integrations.length; i += limit) {
|
|
2366
|
+
const batch = integrations.slice(i, i + limit);
|
|
2367
|
+
await Promise.all(batch.map(async (intg) => {
|
|
2368
|
+
const [path3, data] = intg;
|
|
2369
|
+
const relPath = path3.split("/").slice(-3).join("/");
|
|
2370
|
+
const isExists = await fileExists(path3);
|
|
2371
|
+
if (isExists) {
|
|
2372
|
+
log.debug(`Updating \x1B[32m${relPath}\x1B[0m...`);
|
|
2373
|
+
} else {
|
|
2374
|
+
log.debug(`File \x1B[32m${relPath}\x1B[0m not found. Creating new resource...`);
|
|
2375
|
+
const { type, name } = await createGMResource(gmProject, path3, data, config.integrationOption);
|
|
2376
|
+
if (type && name) {
|
|
2377
|
+
if (data.dirPath !== "")
|
|
2378
|
+
newFolders.push(data.dirPath);
|
|
2379
|
+
newResources.push(createGMResourceStr(type, name));
|
|
2380
|
+
data.isNew = true;
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
for (const rmBody of data.toRemoves) {
|
|
2384
|
+
data.content = data.content.replace(rmBody, "");
|
|
2385
|
+
}
|
|
2386
|
+
data.content = data.content.trim() + `
|
|
2387
|
+
`;
|
|
2388
|
+
await fsRuntime.writeText(path3, data.content);
|
|
2389
|
+
intgCnt++;
|
|
2390
|
+
}));
|
|
2391
|
+
}
|
|
2392
|
+
log.info(`All source codes written. Please click \x1B[1mReload\x1B[0m button in the GameMaker IDE to apply changes.`);
|
|
2393
|
+
if (newFolders.length > 0 || newResources.length > 0)
|
|
2394
|
+
await modifyYyProject("add", projectPath, newResources, newFolders);
|
|
2395
|
+
if (!config.acceptAllIntegrations) {
|
|
2396
|
+
console.log("---");
|
|
2397
|
+
log.info(`\x1B[34macceptAllIntegrations\x1B[0m flag is set to \x1B[33mfalse\x1B[0m in the \x1B[32mscaff.config\x1B[0m. Please review the generated source codes before integrating.`);
|
|
2398
|
+
const deleteResources = [];
|
|
2399
|
+
for (const [path3, data] of integrations) {
|
|
2400
|
+
const pathSlice = path3.split("/");
|
|
2401
|
+
const fileName = pathSlice.pop();
|
|
2402
|
+
const relPath2 = pathSlice.slice(-2).join("/");
|
|
2403
|
+
const relPath3 = pathSlice.slice(-3).join("/");
|
|
2404
|
+
if (data.backup) {
|
|
2405
|
+
const restore = !data.event ? await fsRuntime.prompt(`---
|
|
2406
|
+
\x1B[35m[INPUT]\x1B[0m Revert file \x1B[34m${fileName}\x1B[0m from \x1B[32m${relPath2}\x1B[0m? (y/N) -> `) ?? "n" : await fsRuntime.prompt(`---
|
|
2407
|
+
\x1B[35m[INPUT]\x1B[0m Revert \x1B[34m${data.event.name + (data.event.numStr ? `\x1B[0m:\x1B[36m${data.event.numStr}\x1B[34m` : "")} Event\x1B[0m from \x1B[32m${relPath2}\x1B[0m? (y/N) -> `) ?? "n";
|
|
2408
|
+
if (restore.toLowerCase() === "y") {
|
|
2409
|
+
intgCnt--;
|
|
2410
|
+
await fsRuntime.writeText(path3, data.backup);
|
|
2411
|
+
log.debug(`File \x1B[34m${relPath3}\x1B[0m restored to the original source code.`);
|
|
2412
|
+
}
|
|
2413
|
+
} else {
|
|
2414
|
+
const remove = !data.event ? await fsRuntime.prompt(`---
|
|
2415
|
+
\x1B[35m[INPUT]\x1B[0m Remove file \x1B[34m${fileName}\x1B[0m from \x1B[32m${relPath2}\x1B[0m? (y/N) -> `) ?? "n" : await fsRuntime.prompt(`---
|
|
2416
|
+
\x1B[35m[INPUT]\x1B[0m Remove \x1B[34m${data.event.name + (data.event.numStr ? `\x1B[0m:\x1B[36m${data.event.numStr}\x1B[34m` : "")} Event\x1B[0m from \x1B[32m${relPath2}\x1B[0m? (y/N) -> `) ?? "n";
|
|
2417
|
+
if (remove.toLowerCase() === "y") {
|
|
2418
|
+
intgCnt--;
|
|
2419
|
+
const { type, name } = await removeGMResource(path3, projectPathSplit.join("/"), data);
|
|
2420
|
+
if (type && name) {
|
|
2421
|
+
deleteResources.push(`${type},${name},${data.dirPath}`);
|
|
2422
|
+
}
|
|
2423
|
+
log.debug(`File \x1B[34m${relPath3}\x1B[0m removed from the project.`);
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
if (deleteResources.length > 0)
|
|
2428
|
+
await modifyYyProject("remove", projectPath, deleteResources);
|
|
2429
|
+
}
|
|
2430
|
+
console.log("---");
|
|
2431
|
+
if (intgCnt > 0)
|
|
2432
|
+
log.info(`All source codes integrated successfully. Integrated \x1B[32m${intgCnt}\x1B[0m file(s).`);
|
|
2433
|
+
return intgCnt;
|
|
2434
|
+
}
|
|
2435
|
+
// src/cli/main.ts
|
|
2436
|
+
async function main() {
|
|
2437
|
+
const args = process.argv.slice(2);
|
|
2438
|
+
const input = await parseArgs(...args);
|
|
2439
|
+
if (!input)
|
|
2440
|
+
return;
|
|
2441
|
+
switch (input.cmd) {
|
|
2442
|
+
case "generate":
|
|
2443
|
+
log.debug("Getting Scaff config...");
|
|
2444
|
+
const config = await getScaffConfig();
|
|
2445
|
+
const files = await getScaffFiles(resolvePath(config.source));
|
|
2446
|
+
log.debug("Processing files...");
|
|
2447
|
+
const fileGroup = await readAndSplitFiles(files, config);
|
|
2448
|
+
if (!fileGroup) {
|
|
2449
|
+
log.error("Failed to process files. Aborting...");
|
|
2450
|
+
return;
|
|
2451
|
+
}
|
|
2452
|
+
log.debug("Files processed successfully.");
|
|
2453
|
+
log.debug("Getting exported modules...");
|
|
2454
|
+
const module2 = getExportedModules(fileGroup, config);
|
|
2455
|
+
log.debug("Exported modules retrieved successfully.");
|
|
2456
|
+
log.debug("Implementing classes...");
|
|
2457
|
+
const implValid = implementClass(module2, fileGroup, config);
|
|
2458
|
+
if (!implValid)
|
|
2459
|
+
return;
|
|
2460
|
+
log.debug("Classes implemented successfully.");
|
|
2461
|
+
log.debug("Implementing modules...");
|
|
2462
|
+
for (const file of files) {
|
|
2463
|
+
reexportModule(module2, file, config);
|
|
2464
|
+
}
|
|
2465
|
+
const implMods = [];
|
|
2466
|
+
for (const file of files) {
|
|
2467
|
+
const mod = await implementModules(module2, fileGroup, file, config);
|
|
2468
|
+
implMods.push(mod);
|
|
2469
|
+
}
|
|
2470
|
+
if (implMods && implMods.length && implMods.every((modUsage) => modUsage && (modUsage.length === 0 || modUsage.length && modUsage.every((mm) => mm && mm.cmd)))) {
|
|
2471
|
+
log.debug("Modules implemented successfully.");
|
|
2472
|
+
} else {
|
|
2473
|
+
log.error("Failed to implement modules. Aborting...");
|
|
2474
|
+
return;
|
|
2475
|
+
}
|
|
2476
|
+
log.debug("Extracting integration data...");
|
|
2477
|
+
const intgData = fileGroup.generate.reduce((acc, file) => {
|
|
2478
|
+
const data = extractIntegrationData(file, config);
|
|
2479
|
+
if (data)
|
|
2480
|
+
acc.push(...data);
|
|
2481
|
+
return acc;
|
|
2482
|
+
}, []);
|
|
2483
|
+
log.debug("Integration data extracted successfully.");
|
|
2484
|
+
const genFiles = await generateSourceCode(intgData, config, input.projectPath);
|
|
2485
|
+
if (!config.noIntegration && !input.options.noIntegration || input.options.integrate) {
|
|
2486
|
+
log.debug("Integrating source code...");
|
|
2487
|
+
const modified = await integrateSourceCodes(genFiles, config, input.projectPath);
|
|
2488
|
+
if (modified === null) {
|
|
2489
|
+
log.error("Failed to integrate source code. Aborting...");
|
|
2490
|
+
return;
|
|
2491
|
+
} else if (modified === 0) {
|
|
2492
|
+
log.debug("No source code integrated.");
|
|
2493
|
+
console.log("---");
|
|
2494
|
+
log.info("Program executed successfully. Thanks for using ScaffScript!");
|
|
2495
|
+
return;
|
|
2496
|
+
} else
|
|
2497
|
+
log.info("Program executed successfully. Thanks for using ScaffScript!");
|
|
2498
|
+
} else {
|
|
2499
|
+
console.log("---");
|
|
2500
|
+
if (input.options.noIntegration)
|
|
2501
|
+
log.info("\x1B[34m--no-integration\x1B[0m option is set. No source code will be integrated. Thanks for using ScaffScript!");
|
|
2502
|
+
else
|
|
2503
|
+
log.info("\x1B[34mnoIntegration\x1B[0m flag is set to \x1B[33mtrue\x1B[0m in the \x1B[32mscaff.config\x1B[0m. No source code will be integrated. Thanks for using ScaffScript!");
|
|
2504
|
+
}
|
|
2505
|
+
console.log("");
|
|
2506
|
+
break;
|
|
2507
|
+
}
|
|
2508
|
+
console.log("");
|
|
2509
|
+
}
|
|
2510
|
+
// src/index-node.ts
|
|
2511
|
+
main();
|