@defold-typescript/cli 0.5.5 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/bin.js +1414 -395
- package/dist/bob-command.d.ts +31 -0
- package/dist/bob.d.ts +19 -0
- package/dist/boot-path.d.ts +5 -0
- package/dist/build-output.d.ts +7 -1
- package/dist/debug-launcher.d.ts +7 -1
- package/dist/directory-walls.d.ts +23 -0
- package/dist/dispatch.d.ts +6 -0
- package/dist/index.js +1268 -248
- package/dist/init.d.ts +1 -2
- package/dist/install-reminder.d.ts +1 -0
- package/dist/json-output.d.ts +26 -2
- package/dist/materialize.d.ts +0 -3
- package/dist/mise-scaffold.d.ts +1 -1
- package/dist/scan.d.ts +1 -0
- package/dist/script-kind.d.ts +2 -1
- package/dist/setup-debug.d.ts +40 -0
- package/dist/wall-interactive.d.ts +16 -0
- package/dist/wall.d.ts +4 -0
- package/dist/watch.d.ts +1 -0
- package/package.json +11 -3
- package/src/bob-command.ts +137 -0
- package/src/bob.ts +59 -0
- package/src/boot-path.ts +113 -0
- package/src/build-output.ts +67 -3
- package/src/build-session.ts +31 -11
- package/src/build.ts +6 -5
- package/src/debug-launcher.ts +36 -12
- package/src/directory-walls.ts +214 -0
- package/src/dispatch.ts +264 -18
- package/src/init.ts +83 -38
- package/src/install-reminder.ts +18 -0
- package/src/json-output.ts +52 -7
- package/src/materialize.ts +14 -12
- package/src/mise-scaffold.ts +16 -10
- package/src/scan.ts +7 -1
- package/src/script-kind.ts +31 -19
- package/src/setup-debug.ts +422 -0
- package/src/wall-interactive.ts +60 -0
- package/src/wall.ts +71 -0
- package/src/watch.ts +15 -3
package/dist/bin.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
2
4
|
|
|
3
5
|
// src/dispatch.ts
|
|
4
|
-
import { existsSync as
|
|
5
|
-
import * as
|
|
6
|
+
import { existsSync as existsSync10, readFileSync as readFileSync12 } from "node:fs";
|
|
7
|
+
import * as path15 from "node:path";
|
|
6
8
|
|
|
7
9
|
// src/api-registry.ts
|
|
8
10
|
import { existsSync, readFileSync } from "node:fs";
|
|
9
|
-
import { createRequire } from "node:module";
|
|
11
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
10
12
|
import * as path from "node:path";
|
|
11
13
|
function findPackageRoot(start) {
|
|
12
14
|
let dir = start;
|
|
@@ -23,7 +25,7 @@ function findPackageRoot(start) {
|
|
|
23
25
|
}
|
|
24
26
|
function resolveTypesPackageRoot() {
|
|
25
27
|
try {
|
|
26
|
-
const require2 =
|
|
28
|
+
const require2 = createRequire2(import.meta.url);
|
|
27
29
|
const entry = require2.resolve("@defold-typescript/types");
|
|
28
30
|
return findPackageRoot(path.dirname(entry));
|
|
29
31
|
} catch {
|
|
@@ -81,17 +83,266 @@ function selectApiSurface(resolvedVersion) {
|
|
|
81
83
|
return { surfaceId: null, available: false };
|
|
82
84
|
}
|
|
83
85
|
|
|
86
|
+
// src/bob-command.ts
|
|
87
|
+
import { existsSync as existsSync2, mkdirSync } from "node:fs";
|
|
88
|
+
import { delimiter, dirname as dirname2, join as join3 } from "node:path";
|
|
89
|
+
|
|
90
|
+
// src/bob.ts
|
|
91
|
+
import { homedir } from "node:os";
|
|
92
|
+
import * as path2 from "node:path";
|
|
93
|
+
|
|
94
|
+
// src/debug-launcher.ts
|
|
95
|
+
var PLATFORM_TARGETS = {
|
|
96
|
+
"darwin-arm64": {
|
|
97
|
+
enginePlatform: "arm64-macos",
|
|
98
|
+
buildFolder: "arm64-macos",
|
|
99
|
+
executable: "dmengine"
|
|
100
|
+
},
|
|
101
|
+
"darwin-x64": {
|
|
102
|
+
enginePlatform: "x86_64-macos",
|
|
103
|
+
buildFolder: "x86_64-macos",
|
|
104
|
+
executable: "dmengine"
|
|
105
|
+
},
|
|
106
|
+
"linux-x64": {
|
|
107
|
+
enginePlatform: "x86_64-linux",
|
|
108
|
+
buildFolder: "x86_64-linux",
|
|
109
|
+
executable: "dmengine"
|
|
110
|
+
},
|
|
111
|
+
"win32-x64": {
|
|
112
|
+
enginePlatform: "x86_64-win32",
|
|
113
|
+
buildFolder: "x86_64-win32",
|
|
114
|
+
executable: "dmengine.exe"
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
var ENGINE_INFO_URL = "https://d.defold.com/stable/info.json";
|
|
118
|
+
var ENGINE_ARCHIVE_BASE = "https://d.defold.com/archive/stable";
|
|
119
|
+
var DEBUG_LAUNCHER_REL = ".vscode/defold-debug.ts";
|
|
120
|
+
function debugLaunchConfig() {
|
|
121
|
+
return {
|
|
122
|
+
name: "Defold: Debug (TypeScript)",
|
|
123
|
+
type: "lua-local",
|
|
124
|
+
request: "launch",
|
|
125
|
+
stopOnEntry: false,
|
|
126
|
+
verbose: false,
|
|
127
|
+
internalConsoleOptions: "openOnSessionStart",
|
|
128
|
+
program: { command: "bun" },
|
|
129
|
+
args: [DEBUG_LAUNCHER_REL],
|
|
130
|
+
scriptFiles: ["src/**/*.ts.script"],
|
|
131
|
+
scriptRoots: [".", "src"]
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
var VSCODE_LAUNCH_CONTENT = {
|
|
135
|
+
version: "0.2.0",
|
|
136
|
+
configurations: [debugLaunchConfig()]
|
|
137
|
+
};
|
|
138
|
+
function renderDebugLauncher() {
|
|
139
|
+
const targets = JSON.stringify(PLATFORM_TARGETS, null, 2);
|
|
140
|
+
return `import { chmodSync, copyFileSync, existsSync, mkdirSync } from "node:fs";
|
|
141
|
+
import * as path from "node:path";
|
|
142
|
+
|
|
143
|
+
// Windows only: paths to OpenAL32.dll and wrap_oal.dll from your Defold SDK
|
|
144
|
+
// (defoldsdk/ext/lib/x86_64-win32/). Leave empty on macOS/Linux.
|
|
145
|
+
const WINDOWS_OPENAL32_PATH = "";
|
|
146
|
+
const WINDOWS_WRAPOAL_PATH = "";
|
|
147
|
+
|
|
148
|
+
interface EngineTarget {
|
|
149
|
+
enginePlatform: string;
|
|
150
|
+
buildFolder: string;
|
|
151
|
+
executable: string;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const PLATFORM_TARGETS: Record<string, EngineTarget> = ${targets};
|
|
155
|
+
|
|
156
|
+
const ENGINE_INFO_URL = "${ENGINE_INFO_URL}";
|
|
157
|
+
const ENGINE_ARCHIVE_BASE = "${ENGINE_ARCHIVE_BASE}";
|
|
158
|
+
|
|
159
|
+
const target = PLATFORM_TARGETS[\`\${process.platform}-\${process.arch}\`];
|
|
160
|
+
if (!target) {
|
|
161
|
+
console.error(\`Unsupported platform: \${process.platform}-\${process.arch}\`);
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const here = path.dirname(new URL(import.meta.url).pathname);
|
|
166
|
+
const stockEnginePath = path.join(here, target.executable);
|
|
167
|
+
|
|
168
|
+
if (!existsSync(stockEnginePath)) {
|
|
169
|
+
const info = (await (await fetch(ENGINE_INFO_URL)).json()) as { sha1: string };
|
|
170
|
+
const url = \`\${ENGINE_ARCHIVE_BASE}/\${info.sha1}/engine/\${target.enginePlatform}/\${target.executable}\`;
|
|
171
|
+
console.log(\`Fetching \${url}\`);
|
|
172
|
+
const res = await fetch(url);
|
|
173
|
+
if (!res.ok) {
|
|
174
|
+
console.error(\`Engine download failed: \${res.status} \${res.statusText}\`);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
await Bun.write(stockEnginePath, res);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const buildFolder = path.join("build", target.buildFolder);
|
|
181
|
+
const buildEnginePath = path.join(buildFolder, target.executable);
|
|
182
|
+
let enginePath = existsSync(buildEnginePath) ? buildEnginePath : stockEnginePath;
|
|
183
|
+
|
|
184
|
+
if (process.platform === "win32" && enginePath === buildEnginePath) {
|
|
185
|
+
for (const [src, name] of [
|
|
186
|
+
[WINDOWS_OPENAL32_PATH, "OpenAL32.dll"],
|
|
187
|
+
[WINDOWS_WRAPOAL_PATH, "wrap_oal.dll"],
|
|
188
|
+
] as const) {
|
|
189
|
+
const dest = path.join(buildFolder, name);
|
|
190
|
+
if (src && !existsSync(dest)) {
|
|
191
|
+
copyFileSync(src, dest);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// macOS: a build engine launched in place attaches to the editor process; copy
|
|
197
|
+
// it aside first so it runs standalone.
|
|
198
|
+
if (process.platform === "darwin" && enginePath === buildEnginePath) {
|
|
199
|
+
const tempEngine = path.join(buildFolder, "temp", target.executable);
|
|
200
|
+
mkdirSync(path.dirname(tempEngine), { recursive: true });
|
|
201
|
+
copyFileSync(buildEnginePath, tempEngine);
|
|
202
|
+
enginePath = tempEngine;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (process.platform !== "win32") {
|
|
206
|
+
chmodSync(enginePath, 0o755);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const projectc = path.join("build", "default", "game.projectc");
|
|
210
|
+
console.log(\`Launching \${enginePath} \${projectc}\`);
|
|
211
|
+
const proc = Bun.spawn([enginePath, projectc], {
|
|
212
|
+
stdio: ["inherit", "inherit", "inherit"],
|
|
213
|
+
});
|
|
214
|
+
process.exit(await proc.exited);
|
|
215
|
+
`;
|
|
216
|
+
}
|
|
217
|
+
var DEBUG_LAUNCHER_SOURCE = renderDebugLauncher();
|
|
218
|
+
|
|
219
|
+
// src/bob.ts
|
|
220
|
+
function bobDownloadUrl(sha1) {
|
|
221
|
+
return `${ENGINE_ARCHIVE_BASE}/${sha1}/bob/bob.jar`;
|
|
222
|
+
}
|
|
223
|
+
function bobCacheDir(env = process.env, home = homedir) {
|
|
224
|
+
if (env.DEFOLD_TYPESCRIPT_CACHE) {
|
|
225
|
+
return path2.join(env.DEFOLD_TYPESCRIPT_CACHE, "bob");
|
|
226
|
+
}
|
|
227
|
+
return path2.join(env.XDG_CACHE_HOME ?? path2.join(home(), ".cache"), "defold-typescript", "bob");
|
|
228
|
+
}
|
|
229
|
+
function bobCachePath(opts) {
|
|
230
|
+
return path2.join(opts.cacheDir, opts.sha1, "bob.jar");
|
|
231
|
+
}
|
|
232
|
+
function resolveBobJar(opts) {
|
|
233
|
+
const jarPath = bobCachePath({ sha1: opts.sha1, cacheDir: opts.cacheDir });
|
|
234
|
+
return { jarPath, cached: opts.probe(jarPath) };
|
|
235
|
+
}
|
|
236
|
+
function resolveJava(opts) {
|
|
237
|
+
if (opts.override) {
|
|
238
|
+
return opts.override;
|
|
239
|
+
}
|
|
240
|
+
if (opts.probe("java")) {
|
|
241
|
+
return "java";
|
|
242
|
+
}
|
|
243
|
+
throw new Error('defold-typescript: no Java runtime found. Install a JDK and ensure "java" is on PATH, ' + "or pass --java <path> (or set DEFOLD_JAVA). bob.jar requires a JVM to run.");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// src/bob-command.ts
|
|
247
|
+
var DEFOLD_SUBCOMMANDS = ["resolve", "build", "bundle"];
|
|
248
|
+
function isDefoldSubcommand(value) {
|
|
249
|
+
return value !== undefined && DEFOLD_SUBCOMMANDS.includes(value);
|
|
250
|
+
}
|
|
251
|
+
function composeBobArgv(opts) {
|
|
252
|
+
const base = [opts.java, "-jar", opts.jar];
|
|
253
|
+
const server = opts.buildServer ? ["--build-server", opts.buildServer] : [];
|
|
254
|
+
switch (opts.subcommand) {
|
|
255
|
+
case "resolve":
|
|
256
|
+
return [...base, ...server, "resolve"];
|
|
257
|
+
case "build":
|
|
258
|
+
return [...base, "--variant", "debug", ...server, "build"];
|
|
259
|
+
case "bundle":
|
|
260
|
+
return [...base, ...server, "bundle"];
|
|
261
|
+
default:
|
|
262
|
+
throw new Error(`defold-typescript: unknown defold subcommand "${opts.subcommand}"; expected resolve|build|bundle.`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
async function runDefoldCommand(opts) {
|
|
266
|
+
const { io } = opts;
|
|
267
|
+
const sha = await io.fetchSha();
|
|
268
|
+
const { jarPath, cached } = resolveBobJar({
|
|
269
|
+
sha1: sha,
|
|
270
|
+
cacheDir: io.cacheDir,
|
|
271
|
+
probe: io.probe
|
|
272
|
+
});
|
|
273
|
+
if (!cached) {
|
|
274
|
+
await io.download(bobDownloadUrl(sha), jarPath);
|
|
275
|
+
}
|
|
276
|
+
const java = resolveJava({
|
|
277
|
+
...opts.java !== undefined ? { override: opts.java } : {},
|
|
278
|
+
probe: io.javaProbe
|
|
279
|
+
});
|
|
280
|
+
const argv = composeBobArgv({
|
|
281
|
+
java,
|
|
282
|
+
jar: jarPath,
|
|
283
|
+
subcommand: opts.subcommand,
|
|
284
|
+
...opts.buildServer !== undefined ? { buildServer: opts.buildServer } : {}
|
|
285
|
+
});
|
|
286
|
+
const exitCode = await io.spawn(argv, opts.cwd);
|
|
287
|
+
return { ok: exitCode === 0, subcommand: opts.subcommand, exitCode, argv };
|
|
288
|
+
}
|
|
289
|
+
async function fetchStableSha() {
|
|
290
|
+
const res = await fetch(ENGINE_INFO_URL);
|
|
291
|
+
if (!res.ok) {
|
|
292
|
+
throw new Error(`defold-typescript: could not resolve the stable Defold sha (${ENGINE_INFO_URL} -> ${res.status} ${res.statusText}).`);
|
|
293
|
+
}
|
|
294
|
+
const info = await res.json();
|
|
295
|
+
if (!info.sha1) {
|
|
296
|
+
throw new Error(`defold-typescript: ${ENGINE_INFO_URL} returned no sha1.`);
|
|
297
|
+
}
|
|
298
|
+
return info.sha1;
|
|
299
|
+
}
|
|
300
|
+
function javaOnPath(cmd, env = process.env) {
|
|
301
|
+
const pathVar = env.PATH ?? env.Path ?? "";
|
|
302
|
+
const exts = process.platform === "win32" ? [".exe", ".bat", ".cmd", ""] : [""];
|
|
303
|
+
for (const dir of pathVar.split(delimiter).filter(Boolean)) {
|
|
304
|
+
for (const ext of exts) {
|
|
305
|
+
if (existsSync2(join3(dir, cmd + ext))) {
|
|
306
|
+
return true;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
async function spawnInherit(argv, cwd) {
|
|
313
|
+
const proc = Bun.spawn(argv, { cwd, stdio: ["inherit", "inherit", "inherit"] });
|
|
314
|
+
return proc.exited;
|
|
315
|
+
}
|
|
316
|
+
async function downloadTo(url, dest) {
|
|
317
|
+
const res = await fetch(url);
|
|
318
|
+
if (!res.ok) {
|
|
319
|
+
throw new Error(`defold-typescript: bob.jar download failed (${url} -> ${res.status} ${res.statusText}).`);
|
|
320
|
+
}
|
|
321
|
+
mkdirSync(dirname2(dest), { recursive: true });
|
|
322
|
+
await Bun.write(dest, res);
|
|
323
|
+
}
|
|
324
|
+
function defaultDefoldIo() {
|
|
325
|
+
return {
|
|
326
|
+
cacheDir: bobCacheDir(),
|
|
327
|
+
fetchSha: fetchStableSha,
|
|
328
|
+
probe: existsSync2,
|
|
329
|
+
javaProbe: (cmd) => javaOnPath(cmd),
|
|
330
|
+
spawn: spawnInherit,
|
|
331
|
+
download: downloadTo
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
84
335
|
// src/build.ts
|
|
85
336
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
86
|
-
import * as
|
|
337
|
+
import * as path5 from "node:path";
|
|
87
338
|
import { transpileProject } from "@defold-typescript/transpiler";
|
|
88
339
|
|
|
89
340
|
// src/build-output.ts
|
|
90
|
-
import { mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
91
|
-
import * as
|
|
341
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
342
|
+
import * as path3 from "node:path";
|
|
92
343
|
var DEFAULT_INCLUDE = ["src/**/*.ts"];
|
|
93
344
|
var PROJECT_BUCKET = "<project>";
|
|
94
|
-
function toPosix(p, sep2 =
|
|
345
|
+
function toPosix(p, sep2 = path3.sep) {
|
|
95
346
|
return p.split(sep2).join("/");
|
|
96
347
|
}
|
|
97
348
|
var TRANSPILER_SOURCE_RE = /\.(ts|tsx|cts|mts)$/;
|
|
@@ -99,7 +350,7 @@ function isTranspilerSource(rel) {
|
|
|
99
350
|
return TRANSPILER_SOURCE_RE.test(toPosix(rel));
|
|
100
351
|
}
|
|
101
352
|
function readBuildConfig(cwd) {
|
|
102
|
-
const tsconfigPath =
|
|
353
|
+
const tsconfigPath = path3.join(cwd, "tsconfig.json");
|
|
103
354
|
let raw;
|
|
104
355
|
try {
|
|
105
356
|
raw = readFileSync2(tsconfigPath, "utf8");
|
|
@@ -114,24 +365,61 @@ function readBuildConfig(cwd) {
|
|
|
114
365
|
function stripIncludeBase(pattern) {
|
|
115
366
|
const firstWildcard = pattern.search(/[*?[]/);
|
|
116
367
|
if (firstWildcard === -1) {
|
|
117
|
-
return pattern.endsWith("/") ? pattern : `${
|
|
368
|
+
return pattern.endsWith("/") ? pattern : `${path3.posix.dirname(pattern)}/`;
|
|
118
369
|
}
|
|
119
370
|
const upToWildcard = pattern.slice(0, firstWildcard);
|
|
120
371
|
const lastSlash = upToWildcard.lastIndexOf("/");
|
|
121
372
|
return lastSlash === -1 ? "" : upToWildcard.slice(0, lastSlash + 1);
|
|
122
373
|
}
|
|
123
|
-
|
|
374
|
+
var SCRIPT_SUFFIX_BY_KIND = {
|
|
375
|
+
script: ".ts.script",
|
|
376
|
+
"gui-script": ".ts.gui_script",
|
|
377
|
+
"render-script": ".ts.render_script"
|
|
378
|
+
};
|
|
379
|
+
var FACTORY_KINDS = [
|
|
380
|
+
["render-script", /\bdefineRenderScript\s*\(/],
|
|
381
|
+
["gui-script", /\bdefineGuiScript\s*\(/],
|
|
382
|
+
["script", /\bdefineScript\s*\(/]
|
|
383
|
+
];
|
|
384
|
+
function detectSourceOutputKind(source) {
|
|
385
|
+
for (const [kind, re] of FACTORY_KINDS) {
|
|
386
|
+
if (re.test(source)) {
|
|
387
|
+
return kind;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return "module";
|
|
391
|
+
}
|
|
392
|
+
function relUnderOutDir(rel, config) {
|
|
124
393
|
const { outDir, include } = config;
|
|
125
394
|
if (outDir === undefined || outDir === "" || outDir === ".") {
|
|
126
|
-
return rel
|
|
395
|
+
return rel;
|
|
127
396
|
}
|
|
128
397
|
const includeBase = include.map(stripIncludeBase).filter((base) => rel.startsWith(base)).sort((a, b) => b.length - a.length)[0] ?? "";
|
|
129
398
|
const relUnderBase = rel.slice(includeBase.length);
|
|
130
|
-
return
|
|
399
|
+
return path3.posix.join(outDir, relUnderBase);
|
|
400
|
+
}
|
|
401
|
+
function computeOutputRel(rel, config, kind) {
|
|
402
|
+
const baseRel = relUnderOutDir(rel, config);
|
|
403
|
+
if (kind === "module") {
|
|
404
|
+
return baseRel.replace(/\.ts$/, ".lua");
|
|
405
|
+
}
|
|
406
|
+
return baseRel.replace(/\.ts$/, SCRIPT_SUFFIX_BY_KIND[kind]);
|
|
407
|
+
}
|
|
408
|
+
function outputRelsForSource(rel, config) {
|
|
409
|
+
const outputs = [
|
|
410
|
+
computeOutputRel(rel, config, "module"),
|
|
411
|
+
computeOutputRel(rel, config, "script"),
|
|
412
|
+
computeOutputRel(rel, config, "gui-script"),
|
|
413
|
+
computeOutputRel(rel, config, "render-script")
|
|
414
|
+
];
|
|
415
|
+
return outputs.flatMap((output) => [output, `${output}.map`]);
|
|
131
416
|
}
|
|
132
417
|
function collectFailures(diagnostics) {
|
|
133
418
|
const failures = new Map;
|
|
134
419
|
for (const diag of diagnostics) {
|
|
420
|
+
if (diag.category === "warning") {
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
135
423
|
const bucket = diag.file ?? PROJECT_BUCKET;
|
|
136
424
|
const list = failures.get(bucket);
|
|
137
425
|
if (list) {
|
|
@@ -152,10 +440,10 @@ function throwIfFailures(failures) {
|
|
|
152
440
|
${formatted}`);
|
|
153
441
|
}
|
|
154
442
|
function writeScriptFile(cwd, scriptRel, lua, map) {
|
|
155
|
-
const scriptAbs =
|
|
156
|
-
|
|
443
|
+
const scriptAbs = path3.join(cwd, scriptRel);
|
|
444
|
+
mkdirSync2(path3.dirname(scriptAbs), { recursive: true });
|
|
157
445
|
if (map) {
|
|
158
|
-
const mapBasename = `${
|
|
446
|
+
const mapBasename = `${path3.posix.basename(scriptRel)}.map`;
|
|
159
447
|
writeFileSync(`${scriptAbs}.map`, map);
|
|
160
448
|
writeFileSync(scriptAbs, `${lua}
|
|
161
449
|
--# sourceMappingURL=${mapBasename}
|
|
@@ -167,9 +455,12 @@ function writeScriptFile(cwd, scriptRel, lua, map) {
|
|
|
167
455
|
|
|
168
456
|
// src/scan.ts
|
|
169
457
|
import { globSync, statSync } from "node:fs";
|
|
170
|
-
import * as
|
|
458
|
+
import * as path4 from "node:path";
|
|
459
|
+
function normalizeScannedPath(rel) {
|
|
460
|
+
return rel.split(/[/\\]/).join("/");
|
|
461
|
+
}
|
|
171
462
|
function scanFilesSync(cwd, pattern) {
|
|
172
|
-
return globSync(pattern, { cwd }).filter((rel) => statSync(
|
|
463
|
+
return globSync(pattern, { cwd }).map(normalizeScannedPath).filter((rel) => statSync(path4.join(cwd, rel)).isFile());
|
|
173
464
|
}
|
|
174
465
|
|
|
175
466
|
// src/build.ts
|
|
@@ -188,7 +479,7 @@ function runBuild(opts) {
|
|
|
188
479
|
}
|
|
189
480
|
const files = {};
|
|
190
481
|
for (const rel of sources) {
|
|
191
|
-
files[rel] = readFileSync3(
|
|
482
|
+
files[rel] = readFileSync3(path5.join(cwd, rel), "utf8");
|
|
192
483
|
}
|
|
193
484
|
const result = transpileProject({ files });
|
|
194
485
|
const failures = collectFailures(result.diagnostics);
|
|
@@ -201,25 +492,25 @@ function runBuild(opts) {
|
|
|
201
492
|
if (!lua) {
|
|
202
493
|
continue;
|
|
203
494
|
}
|
|
204
|
-
const
|
|
205
|
-
writeScriptFile(cwd,
|
|
206
|
-
written.push(
|
|
495
|
+
const outputRel = computeOutputRel(rel, config, detectSourceOutputKind(files[rel] ?? ""));
|
|
496
|
+
writeScriptFile(cwd, outputRel, lua, result.sourceMaps[rel]);
|
|
497
|
+
written.push(outputRel);
|
|
207
498
|
}
|
|
208
499
|
throwIfFailures(failures);
|
|
209
|
-
return { written };
|
|
500
|
+
return { written: written.sort() };
|
|
210
501
|
}
|
|
211
502
|
|
|
212
503
|
// src/cli-version.ts
|
|
213
504
|
import { readFileSync as readFileSync4 } from "node:fs";
|
|
214
|
-
import * as
|
|
505
|
+
import * as path6 from "node:path";
|
|
215
506
|
import { fileURLToPath } from "node:url";
|
|
216
507
|
var VERSION_FALLBACK = "0.0.0";
|
|
217
508
|
function defaultPackageRoot() {
|
|
218
|
-
return
|
|
509
|
+
return path6.join(path6.dirname(fileURLToPath(import.meta.url)), "..");
|
|
219
510
|
}
|
|
220
511
|
function readCliVersion(packageRoot = defaultPackageRoot()) {
|
|
221
512
|
try {
|
|
222
|
-
const pkg = JSON.parse(readFileSync4(
|
|
513
|
+
const pkg = JSON.parse(readFileSync4(path6.join(packageRoot, "package.json"), "utf8"));
|
|
223
514
|
return typeof pkg.version === "string" ? pkg.version : VERSION_FALLBACK;
|
|
224
515
|
} catch {
|
|
225
516
|
return VERSION_FALLBACK;
|
|
@@ -227,136 +518,31 @@ function readCliVersion(packageRoot = defaultPackageRoot()) {
|
|
|
227
518
|
}
|
|
228
519
|
|
|
229
520
|
// src/init.ts
|
|
230
|
-
import { existsSync as
|
|
231
|
-
import * as
|
|
521
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "node:fs";
|
|
522
|
+
import * as path7 from "node:path";
|
|
232
523
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
233
524
|
|
|
234
|
-
// src/debug-launcher.ts
|
|
235
|
-
var PLATFORM_TARGETS = {
|
|
236
|
-
darwin: { enginePlatform: "x86_64-darwin", buildFolder: "x86_64-osx", executable: "dmengine" },
|
|
237
|
-
linux: { enginePlatform: "x86_64-linux", buildFolder: "x86_64-linux", executable: "dmengine" },
|
|
238
|
-
win32: {
|
|
239
|
-
enginePlatform: "x86_64-win32",
|
|
240
|
-
buildFolder: "x86_64-win32",
|
|
241
|
-
executable: "dmengine.exe"
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
var ENGINE_INFO_URL = "https://d.defold.com/stable/info.json";
|
|
245
|
-
var ENGINE_ARCHIVE_BASE = "https://d.defold.com/archive/stable";
|
|
246
|
-
var DEBUG_LAUNCHER_REL = ".vscode/defold-debug.ts";
|
|
247
|
-
function debugLaunchConfig() {
|
|
248
|
-
return {
|
|
249
|
-
name: "Defold: Debug (TypeScript)",
|
|
250
|
-
type: "lua-local",
|
|
251
|
-
request: "launch",
|
|
252
|
-
stopOnEntry: false,
|
|
253
|
-
verbose: false,
|
|
254
|
-
internalConsoleOptions: "openOnSessionStart",
|
|
255
|
-
program: { command: "bun" },
|
|
256
|
-
args: [DEBUG_LAUNCHER_REL]
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
var VSCODE_LAUNCH_CONTENT = {
|
|
260
|
-
version: "0.2.0",
|
|
261
|
-
configurations: [debugLaunchConfig()]
|
|
262
|
-
};
|
|
263
|
-
function renderDebugLauncher() {
|
|
264
|
-
const targets = JSON.stringify(PLATFORM_TARGETS, null, 2);
|
|
265
|
-
return `import { chmodSync, copyFileSync, existsSync, mkdirSync } from "node:fs";
|
|
266
|
-
import * as path from "node:path";
|
|
267
|
-
|
|
268
|
-
// Windows only: paths to OpenAL32.dll and wrap_oal.dll from your Defold SDK
|
|
269
|
-
// (defoldsdk/ext/lib/x86_64-win32/). Leave empty on macOS/Linux.
|
|
270
|
-
const WINDOWS_OPENAL32_PATH = "";
|
|
271
|
-
const WINDOWS_WRAPOAL_PATH = "";
|
|
272
|
-
|
|
273
|
-
interface EngineTarget {
|
|
274
|
-
enginePlatform: string;
|
|
275
|
-
buildFolder: string;
|
|
276
|
-
executable: string;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const PLATFORM_TARGETS: Record<string, EngineTarget> = ${targets};
|
|
280
|
-
|
|
281
|
-
const ENGINE_INFO_URL = "${ENGINE_INFO_URL}";
|
|
282
|
-
const ENGINE_ARCHIVE_BASE = "${ENGINE_ARCHIVE_BASE}";
|
|
283
|
-
|
|
284
|
-
const target = PLATFORM_TARGETS[process.platform];
|
|
285
|
-
if (!target) {
|
|
286
|
-
console.error(\`Unsupported platform: \${process.platform}\`);
|
|
287
|
-
process.exit(1);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const here = path.dirname(new URL(import.meta.url).pathname);
|
|
291
|
-
const stockEnginePath = path.join(here, target.executable);
|
|
292
|
-
|
|
293
|
-
if (!existsSync(stockEnginePath)) {
|
|
294
|
-
const info = (await (await fetch(ENGINE_INFO_URL)).json()) as { sha1: string };
|
|
295
|
-
const url = \`\${ENGINE_ARCHIVE_BASE}/\${info.sha1}/engine/\${target.enginePlatform}/\${target.executable}\`;
|
|
296
|
-
console.log(\`Fetching \${url}\`);
|
|
297
|
-
const res = await fetch(url);
|
|
298
|
-
if (!res.ok) {
|
|
299
|
-
console.error(\`Engine download failed: \${res.status} \${res.statusText}\`);
|
|
300
|
-
process.exit(1);
|
|
301
|
-
}
|
|
302
|
-
await Bun.write(stockEnginePath, res);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const buildFolder = path.join("build", target.buildFolder);
|
|
306
|
-
const buildEnginePath = path.join(buildFolder, target.executable);
|
|
307
|
-
let enginePath = existsSync(buildEnginePath) ? buildEnginePath : stockEnginePath;
|
|
308
|
-
|
|
309
|
-
if (process.platform === "win32" && enginePath === buildEnginePath) {
|
|
310
|
-
for (const [src, name] of [
|
|
311
|
-
[WINDOWS_OPENAL32_PATH, "OpenAL32.dll"],
|
|
312
|
-
[WINDOWS_WRAPOAL_PATH, "wrap_oal.dll"],
|
|
313
|
-
] as const) {
|
|
314
|
-
const dest = path.join(buildFolder, name);
|
|
315
|
-
if (src && !existsSync(dest)) {
|
|
316
|
-
copyFileSync(src, dest);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// macOS: a build engine launched in place attaches to the editor process; copy
|
|
322
|
-
// it aside first so it runs standalone.
|
|
323
|
-
if (process.platform === "darwin" && enginePath === buildEnginePath) {
|
|
324
|
-
const tempEngine = path.join(buildFolder, "temp", target.executable);
|
|
325
|
-
mkdirSync(path.dirname(tempEngine), { recursive: true });
|
|
326
|
-
copyFileSync(buildEnginePath, tempEngine);
|
|
327
|
-
enginePath = tempEngine;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
if (process.platform !== "win32") {
|
|
331
|
-
chmodSync(enginePath, 0o755);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
const projectc = path.join("build", "default", "game.projectc");
|
|
335
|
-
console.log(\`Launching \${enginePath} \${projectc}\`);
|
|
336
|
-
const proc = Bun.spawn([enginePath, projectc], {
|
|
337
|
-
stdio: ["inherit", "inherit", "inherit"],
|
|
338
|
-
});
|
|
339
|
-
process.exit(await proc.exited);
|
|
340
|
-
`;
|
|
341
|
-
}
|
|
342
|
-
var DEBUG_LAUNCHER_SOURCE = renderDebugLauncher();
|
|
343
|
-
|
|
344
525
|
// src/mise-scaffold.ts
|
|
345
526
|
var MANAGED_MARKER = "# managed by @defold-typescript";
|
|
346
527
|
var MISE_TASKS_TOML = `${MANAGED_MARKER}
|
|
347
528
|
[tasks."defold-typescript:build"]
|
|
348
|
-
description = "Build the TypeScript sources with the
|
|
349
|
-
run = "bunx
|
|
529
|
+
description = "Build the TypeScript sources with the defold-typescript CLI"
|
|
530
|
+
run = "bunx @defold-typescript/cli build"
|
|
350
531
|
|
|
351
532
|
${MANAGED_MARKER}
|
|
352
533
|
[tasks."defold-typescript:watch"]
|
|
353
|
-
description = "Watch and rebuild the TypeScript sources with the
|
|
354
|
-
run = "bunx
|
|
534
|
+
description = "Watch and rebuild the TypeScript sources with the defold-typescript CLI"
|
|
535
|
+
run = "bunx @defold-typescript/cli watch"
|
|
536
|
+
|
|
537
|
+
${MANAGED_MARKER}
|
|
538
|
+
[tasks."defold-typescript:setup-debug"]
|
|
539
|
+
description = "Wire the lldebugger game.project dependency and entry-script bootstrap with the defold-typescript CLI"
|
|
540
|
+
run = "bunx @defold-typescript/cli setup-debug"
|
|
355
541
|
|
|
356
542
|
${MANAGED_MARKER}
|
|
357
543
|
[tasks."defold-typescript:upgrade"]
|
|
358
544
|
description = "Upgrade the defold-typescript CLI to its latest release and re-pin the types dependency"
|
|
359
|
-
run = ["bunx @defold-typescript/cli@latest init --force", "bun install"]
|
|
545
|
+
run = ["bunx @defold-typescript/cli@latest init --force --suppress-install-reminder", "bun install"]
|
|
360
546
|
`;
|
|
361
547
|
function stripManagedBlocks(text) {
|
|
362
548
|
const lines = text.split(`
|
|
@@ -415,18 +601,6 @@ function isComponentPath(relPath) {
|
|
|
415
601
|
}
|
|
416
602
|
return Object.keys(KIND_BY_EXT).some((ext) => relPath.endsWith(ext));
|
|
417
603
|
}
|
|
418
|
-
function detectScriptKinds(cwd) {
|
|
419
|
-
const kinds = new Set;
|
|
420
|
-
for (const [ext, kind] of Object.entries(KIND_BY_EXT)) {
|
|
421
|
-
for (const match of scanFilesSync(cwd, `**/*${ext}`)) {
|
|
422
|
-
if (!isSkipped(match) && !isGeneratedScript(match)) {
|
|
423
|
-
kinds.add(kind);
|
|
424
|
-
break;
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
return kinds;
|
|
429
|
-
}
|
|
430
604
|
function selectScriptKind(kinds) {
|
|
431
605
|
if (kinds.size !== 1) {
|
|
432
606
|
return null;
|
|
@@ -440,19 +614,6 @@ function selectScriptKindEntrypoint(kinds) {
|
|
|
440
614
|
const kind = selectScriptKind(kinds);
|
|
441
615
|
return kind === null ? DEFAULT_TYPES_ENTRYPOINT : `${DEFAULT_TYPES_ENTRYPOINT}/${kind}`;
|
|
442
616
|
}
|
|
443
|
-
var RESTRICTED_MODULES = {
|
|
444
|
-
script: "",
|
|
445
|
-
"gui-script": "gui",
|
|
446
|
-
"render-script": "render"
|
|
447
|
-
};
|
|
448
|
-
var ALL_RESTRICTED_MODULES = ["gui", "render"];
|
|
449
|
-
function excludedModulesForKind(kind) {
|
|
450
|
-
if (kind === null) {
|
|
451
|
-
return new Set;
|
|
452
|
-
}
|
|
453
|
-
const allowed = RESTRICTED_MODULES[kind];
|
|
454
|
-
return new Set(ALL_RESTRICTED_MODULES.filter((mod) => mod !== allowed));
|
|
455
|
-
}
|
|
456
617
|
|
|
457
618
|
// src/init.ts
|
|
458
619
|
var CONFLICTING_TS_CONFIGS = [
|
|
@@ -469,11 +630,29 @@ var TSCONFIG_COMPILER_OPTIONS = {
|
|
|
469
630
|
strict: true,
|
|
470
631
|
skipLibCheck: true
|
|
471
632
|
};
|
|
472
|
-
var GITIGNORE_LINES = [
|
|
633
|
+
var GITIGNORE_LINES = [
|
|
634
|
+
"src/**/*.ts.script",
|
|
635
|
+
"src/**/*.ts.script.map",
|
|
636
|
+
"src/**/*.ts.gui_script",
|
|
637
|
+
"src/**/*.ts.gui_script.map",
|
|
638
|
+
"src/**/*.ts.render_script",
|
|
639
|
+
"src/**/*.ts.render_script.map",
|
|
640
|
+
"src/**/*.lua",
|
|
641
|
+
"src/**/*.lua.map"
|
|
642
|
+
];
|
|
473
643
|
var BIOME_JSON_CONTENT = {
|
|
474
644
|
$schema: "https://biomejs.dev/schemas/2.4.15/schema.json",
|
|
475
645
|
files: {
|
|
476
|
-
includes: [
|
|
646
|
+
includes: [
|
|
647
|
+
"src/**/*.ts",
|
|
648
|
+
"!**/dist",
|
|
649
|
+
"!**/node_modules",
|
|
650
|
+
"!**/*.ts.script",
|
|
651
|
+
"!**/*.ts.gui_script",
|
|
652
|
+
"!**/*.ts.render_script",
|
|
653
|
+
"!src/**/*.lua",
|
|
654
|
+
"!src/**/*.lua.map"
|
|
655
|
+
]
|
|
477
656
|
},
|
|
478
657
|
formatter: {
|
|
479
658
|
enabled: true,
|
|
@@ -505,9 +684,15 @@ var BIOME_JSON_CONTENT = {
|
|
|
505
684
|
}
|
|
506
685
|
};
|
|
507
686
|
var VSCODE_EXTENSIONS_CONTENT = {
|
|
508
|
-
recommendations: ["
|
|
687
|
+
recommendations: ["tomblind.local-lua-debugger-vscode"],
|
|
509
688
|
unwantedRecommendations: ["johnnymorganz.luau-lsp"]
|
|
510
689
|
};
|
|
690
|
+
var MANAGED_RECOMMENDATIONS = [
|
|
691
|
+
"tomblind.local-lua-debugger-vscode",
|
|
692
|
+
"sumneko.lua",
|
|
693
|
+
"astronachos.defold"
|
|
694
|
+
];
|
|
695
|
+
var MANAGED_UNWANTED = ["johnnymorganz.luau-lsp"];
|
|
511
696
|
var VSCODE_SETTINGS_CONTENT = {
|
|
512
697
|
"Lua.workspace.ignoreDir": ["src"]
|
|
513
698
|
};
|
|
@@ -552,7 +737,7 @@ function inlineSnippetBody(factory, includeOnInput) {
|
|
|
552
737
|
return [
|
|
553
738
|
`import { ${factory} } from "@defold-typescript/types";`,
|
|
554
739
|
"",
|
|
555
|
-
`export
|
|
740
|
+
`export default ${factory}({`,
|
|
556
741
|
` // ${HOOK_COMMENTS.init}`,
|
|
557
742
|
" init() {",
|
|
558
743
|
" return { $0 };",
|
|
@@ -570,7 +755,7 @@ function typedSnippetBody(factory, includeOnInput) {
|
|
|
570
755
|
" $1",
|
|
571
756
|
"};",
|
|
572
757
|
"",
|
|
573
|
-
`export
|
|
758
|
+
`export default ${factory}<Self>({`,
|
|
574
759
|
` // ${HOOK_COMMENTS.init}`,
|
|
575
760
|
" init(): Self {",
|
|
576
761
|
" return { $0 };",
|
|
@@ -617,21 +802,20 @@ var VSCODE_SNIPPETS_CONTENT = {
|
|
|
617
802
|
description: "Empty Defold render script with an explicit Self type."
|
|
618
803
|
}
|
|
619
804
|
};
|
|
620
|
-
var MAIN_TS_CONTENT = `
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
function final(self) end
|
|
805
|
+
var MAIN_TS_CONTENT = `import { defineScript } from "@defold-typescript/types";
|
|
806
|
+
|
|
807
|
+
export default defineScript({
|
|
808
|
+
init() {
|
|
809
|
+
const start = vmath.vector3(0, 0, 0);
|
|
810
|
+
return { start };
|
|
811
|
+
},
|
|
812
|
+
});
|
|
629
813
|
`;
|
|
630
814
|
var MAIN_COLLECTION_CONTENT = `name: "main"
|
|
631
815
|
scale_along_z: 0
|
|
632
816
|
embedded_instances {
|
|
633
817
|
id: "main"
|
|
634
|
-
data: "components {\\n id: \\"main\\"\\n component: \\"/
|
|
818
|
+
data: "components {\\n id: \\"main\\"\\n component: \\"/src/main.ts.script\\"\\n}\\n"
|
|
635
819
|
position { x: 0.0 y: 0.0 z: 0.0 }
|
|
636
820
|
rotation { x: 0.0 y: 0.0 z: 0.0 w: 1.0 }
|
|
637
821
|
scale3 { x: 1.0 y: 1.0 z: 1.0 }
|
|
@@ -639,8 +823,8 @@ embedded_instances {
|
|
|
639
823
|
`;
|
|
640
824
|
function typesVersionSpec() {
|
|
641
825
|
try {
|
|
642
|
-
const here =
|
|
643
|
-
const pkg = JSON.parse(readFileSync5(
|
|
826
|
+
const here = path7.dirname(fileURLToPath2(import.meta.url));
|
|
827
|
+
const pkg = JSON.parse(readFileSync5(path7.join(here, "..", "package.json"), "utf8"));
|
|
644
828
|
return pkg.version ? `^${pkg.version}` : "latest";
|
|
645
829
|
} catch {
|
|
646
830
|
return "latest";
|
|
@@ -664,8 +848,8 @@ function writeJson(filePath, value) {
|
|
|
664
848
|
`);
|
|
665
849
|
}
|
|
666
850
|
function writeGitignore(cwd) {
|
|
667
|
-
const gitignorePath =
|
|
668
|
-
if (
|
|
851
|
+
const gitignorePath = path7.join(cwd, ".gitignore");
|
|
852
|
+
if (existsSync3(gitignorePath)) {
|
|
669
853
|
const existing = readFileSync5(gitignorePath, "utf8");
|
|
670
854
|
const present = new Set(existing.split(`
|
|
671
855
|
`).map((line) => line.trim()));
|
|
@@ -686,16 +870,16 @@ function writeGitignore(cwd) {
|
|
|
686
870
|
}
|
|
687
871
|
}
|
|
688
872
|
function writeBiome(cwd, written) {
|
|
689
|
-
const biomePath =
|
|
690
|
-
if (
|
|
873
|
+
const biomePath = path7.join(cwd, "biome.json");
|
|
874
|
+
if (existsSync3(biomePath)) {
|
|
691
875
|
return;
|
|
692
876
|
}
|
|
693
877
|
writeJson(biomePath, BIOME_JSON_CONTENT);
|
|
694
878
|
written.push("biome.json");
|
|
695
879
|
}
|
|
696
880
|
function writeMiseTasks(cwd, written) {
|
|
697
|
-
const misePath =
|
|
698
|
-
const existing =
|
|
881
|
+
const misePath = path7.join(cwd, "mise.toml");
|
|
882
|
+
const existing = existsSync3(misePath) ? readFileSync5(misePath, "utf8") : undefined;
|
|
699
883
|
writeFileSync2(misePath, mergeMiseToml(existing));
|
|
700
884
|
written.push("mise.toml");
|
|
701
885
|
}
|
|
@@ -759,6 +943,27 @@ function unionStrings(existing, additions) {
|
|
|
759
943
|
}
|
|
760
944
|
return out;
|
|
761
945
|
}
|
|
946
|
+
function reconcileManagedList(existing, managed, canonical) {
|
|
947
|
+
const managedSet = new Set(managed);
|
|
948
|
+
const canonicalSet = new Set(canonical);
|
|
949
|
+
const out = [];
|
|
950
|
+
const values = Array.isArray(existing) ? existing.filter((value) => typeof value === "string") : [];
|
|
951
|
+
for (const value of values) {
|
|
952
|
+
if (out.includes(value)) {
|
|
953
|
+
continue;
|
|
954
|
+
}
|
|
955
|
+
if (managedSet.has(value) && !canonicalSet.has(value)) {
|
|
956
|
+
continue;
|
|
957
|
+
}
|
|
958
|
+
out.push(value);
|
|
959
|
+
}
|
|
960
|
+
for (const value of canonical) {
|
|
961
|
+
if (!out.includes(value)) {
|
|
962
|
+
out.push(value);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
return out;
|
|
966
|
+
}
|
|
762
967
|
function readVscodeJson(filePath) {
|
|
763
968
|
try {
|
|
764
969
|
const parsed = parseJsonc(readFileSync5(filePath, "utf8"));
|
|
@@ -768,26 +973,29 @@ function readVscodeJson(filePath) {
|
|
|
768
973
|
}
|
|
769
974
|
}
|
|
770
975
|
function writeVscodeExtensions(cwd, written) {
|
|
771
|
-
const dir =
|
|
772
|
-
const filePath =
|
|
773
|
-
if (
|
|
976
|
+
const dir = path7.join(cwd, ".vscode");
|
|
977
|
+
const filePath = path7.join(dir, "extensions.json");
|
|
978
|
+
if (existsSync3(filePath)) {
|
|
774
979
|
const existing = readVscodeJson(filePath);
|
|
775
980
|
if (existing === null) {
|
|
776
981
|
return;
|
|
777
982
|
}
|
|
778
|
-
|
|
779
|
-
existing.
|
|
780
|
-
|
|
983
|
+
const before = JSON.stringify(existing);
|
|
984
|
+
existing.recommendations = reconcileManagedList(existing.recommendations, MANAGED_RECOMMENDATIONS, VSCODE_EXTENSIONS_CONTENT.recommendations);
|
|
985
|
+
existing.unwantedRecommendations = reconcileManagedList(existing.unwantedRecommendations, MANAGED_UNWANTED, VSCODE_EXTENSIONS_CONTENT.unwantedRecommendations);
|
|
986
|
+
if (JSON.stringify(existing) !== before) {
|
|
987
|
+
writeJson(filePath, existing);
|
|
988
|
+
}
|
|
781
989
|
return;
|
|
782
990
|
}
|
|
783
|
-
|
|
991
|
+
mkdirSync3(dir, { recursive: true });
|
|
784
992
|
writeJson(filePath, VSCODE_EXTENSIONS_CONTENT);
|
|
785
993
|
written.push(".vscode/extensions.json");
|
|
786
994
|
}
|
|
787
995
|
function writeVscodeSettings(cwd, written) {
|
|
788
|
-
const dir =
|
|
789
|
-
const filePath =
|
|
790
|
-
if (
|
|
996
|
+
const dir = path7.join(cwd, ".vscode");
|
|
997
|
+
const filePath = path7.join(dir, "settings.json");
|
|
998
|
+
if (existsSync3(filePath)) {
|
|
791
999
|
const existing = readVscodeJson(filePath);
|
|
792
1000
|
if (existing === null) {
|
|
793
1001
|
return;
|
|
@@ -796,14 +1004,14 @@ function writeVscodeSettings(cwd, written) {
|
|
|
796
1004
|
writeJson(filePath, existing);
|
|
797
1005
|
return;
|
|
798
1006
|
}
|
|
799
|
-
|
|
1007
|
+
mkdirSync3(dir, { recursive: true });
|
|
800
1008
|
writeJson(filePath, VSCODE_SETTINGS_CONTENT);
|
|
801
1009
|
written.push(".vscode/settings.json");
|
|
802
1010
|
}
|
|
803
1011
|
function writeVscodeSnippets(cwd, written) {
|
|
804
|
-
const dir =
|
|
805
|
-
const filePath =
|
|
806
|
-
if (
|
|
1012
|
+
const dir = path7.join(cwd, ".vscode");
|
|
1013
|
+
const filePath = path7.join(dir, "defold-typescript.code-snippets");
|
|
1014
|
+
if (existsSync3(filePath)) {
|
|
807
1015
|
const existing = readVscodeJson(filePath);
|
|
808
1016
|
if (existing === null) {
|
|
809
1017
|
return;
|
|
@@ -816,15 +1024,15 @@ function writeVscodeSnippets(cwd, written) {
|
|
|
816
1024
|
writeJson(filePath, existing);
|
|
817
1025
|
return;
|
|
818
1026
|
}
|
|
819
|
-
|
|
1027
|
+
mkdirSync3(dir, { recursive: true });
|
|
820
1028
|
writeJson(filePath, VSCODE_SNIPPETS_CONTENT);
|
|
821
1029
|
written.push(".vscode/defold-typescript.code-snippets");
|
|
822
1030
|
}
|
|
823
1031
|
function writeVscodeLaunch(cwd, written) {
|
|
824
|
-
const dir =
|
|
825
|
-
const filePath =
|
|
1032
|
+
const dir = path7.join(cwd, ".vscode");
|
|
1033
|
+
const filePath = path7.join(dir, "launch.json");
|
|
826
1034
|
const ours = debugLaunchConfig();
|
|
827
|
-
if (
|
|
1035
|
+
if (existsSync3(filePath)) {
|
|
828
1036
|
const existing = readVscodeJson(filePath);
|
|
829
1037
|
if (existing === null) {
|
|
830
1038
|
return;
|
|
@@ -839,36 +1047,35 @@ function writeVscodeLaunch(cwd, written) {
|
|
|
839
1047
|
writeJson(filePath, existing);
|
|
840
1048
|
return;
|
|
841
1049
|
}
|
|
842
|
-
|
|
1050
|
+
mkdirSync3(dir, { recursive: true });
|
|
843
1051
|
writeJson(filePath, VSCODE_LAUNCH_CONTENT);
|
|
844
1052
|
written.push(".vscode/launch.json");
|
|
845
1053
|
}
|
|
846
1054
|
function writeVscodeDebugLauncher(cwd, written) {
|
|
847
|
-
const dir =
|
|
848
|
-
const filePath =
|
|
849
|
-
if (
|
|
1055
|
+
const dir = path7.join(cwd, ".vscode");
|
|
1056
|
+
const filePath = path7.join(dir, "defold-debug.ts");
|
|
1057
|
+
if (existsSync3(filePath)) {
|
|
850
1058
|
return;
|
|
851
1059
|
}
|
|
852
|
-
|
|
1060
|
+
mkdirSync3(dir, { recursive: true });
|
|
853
1061
|
writeFileSync2(filePath, DEBUG_LAUNCHER_SOURCE);
|
|
854
1062
|
written.push(".vscode/defold-debug.ts");
|
|
855
1063
|
}
|
|
856
1064
|
function writeTsSurface(cwd, written, force = false) {
|
|
857
|
-
|
|
858
|
-
writeFileSync2(
|
|
1065
|
+
mkdirSync3(path7.join(cwd, "src"), { recursive: true });
|
|
1066
|
+
writeFileSync2(path7.join(cwd, "src", "main.ts"), MAIN_TS_CONTENT);
|
|
859
1067
|
written.push("src/main.ts");
|
|
860
|
-
const kinds = detectScriptKinds(cwd);
|
|
861
1068
|
const tsconfig = {
|
|
862
1069
|
compilerOptions: {
|
|
863
1070
|
...TSCONFIG_COMPILER_OPTIONS,
|
|
864
|
-
types: [
|
|
1071
|
+
types: [DEFAULT_TYPES_ENTRYPOINT]
|
|
865
1072
|
},
|
|
866
1073
|
include: ["src/**/*.ts"]
|
|
867
1074
|
};
|
|
868
|
-
writeJson(
|
|
1075
|
+
writeJson(path7.join(cwd, "tsconfig.json"), tsconfig);
|
|
869
1076
|
written.push("tsconfig.json");
|
|
870
|
-
const pkgPath =
|
|
871
|
-
if (
|
|
1077
|
+
const pkgPath = path7.join(cwd, "package.json");
|
|
1078
|
+
if (existsSync3(pkgPath)) {
|
|
872
1079
|
const existing = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
873
1080
|
const devDeps = { ...existing.devDependencies ?? {} };
|
|
874
1081
|
for (const [name, version] of Object.entries(SCAFFOLD_DEV_DEPS)) {
|
|
@@ -882,7 +1089,7 @@ function writeTsSurface(cwd, written, force = false) {
|
|
|
882
1089
|
writeJson(pkgPath, existing);
|
|
883
1090
|
} else {
|
|
884
1091
|
const fresh = {
|
|
885
|
-
name:
|
|
1092
|
+
name: path7.basename(cwd),
|
|
886
1093
|
version: "0.0.0",
|
|
887
1094
|
type: "module",
|
|
888
1095
|
devDependencies: { ...SCAFFOLD_DEV_DEPS },
|
|
@@ -900,43 +1107,56 @@ function writeTsSurface(cwd, written, force = false) {
|
|
|
900
1107
|
writeVscodeSnippets(cwd, written);
|
|
901
1108
|
writeVscodeLaunch(cwd, written);
|
|
902
1109
|
writeVscodeDebugLauncher(cwd, written);
|
|
903
|
-
return selectScriptKind(kinds);
|
|
904
1110
|
}
|
|
905
1111
|
function runNewProjectInit(cwd, force = false) {
|
|
906
|
-
if (!
|
|
907
|
-
|
|
1112
|
+
if (!existsSync3(cwd)) {
|
|
1113
|
+
mkdirSync3(cwd, { recursive: true });
|
|
908
1114
|
} else if (readdirSync(cwd).length > 0 && !force) {
|
|
909
1115
|
throw new Error(`defold-typescript init: refusing to synthesize a new Defold project into non-empty directory ${cwd}. Pass --force to proceed.`);
|
|
910
1116
|
}
|
|
911
1117
|
const written = [];
|
|
912
|
-
writeFileSync2(
|
|
913
|
-
title = ${
|
|
1118
|
+
writeFileSync2(path7.join(cwd, "game.project"), `[project]
|
|
1119
|
+
title = ${path7.basename(cwd)}
|
|
914
1120
|
main_collection = /main/main.collectionc
|
|
915
1121
|
`);
|
|
916
1122
|
written.push("game.project");
|
|
917
|
-
|
|
918
|
-
writeFileSync2(
|
|
1123
|
+
mkdirSync3(path7.join(cwd, "main"), { recursive: true });
|
|
1124
|
+
writeFileSync2(path7.join(cwd, "main", "main.collection"), MAIN_COLLECTION_CONTENT);
|
|
919
1125
|
written.push("main/main.collection");
|
|
920
|
-
|
|
921
|
-
written
|
|
922
|
-
const scriptKind = writeTsSurface(cwd, written, force);
|
|
923
|
-
return { written, scriptKind };
|
|
1126
|
+
writeTsSurface(cwd, written, force);
|
|
1127
|
+
return { written };
|
|
924
1128
|
}
|
|
925
1129
|
function runInit(opts) {
|
|
926
1130
|
const { cwd, force = false } = opts;
|
|
927
|
-
if (!
|
|
1131
|
+
if (!existsSync3(path7.join(cwd, "game.project"))) {
|
|
928
1132
|
return runNewProjectInit(cwd, force);
|
|
929
1133
|
}
|
|
930
1134
|
if (!force) {
|
|
931
1135
|
for (const rel of CONFLICTING_TS_CONFIGS) {
|
|
932
|
-
if (
|
|
1136
|
+
if (existsSync3(path7.join(cwd, rel))) {
|
|
933
1137
|
throw new Error(`defold-typescript init: refusing to overwrite existing TS config: ${rel}. Pass --force to overwrite.`);
|
|
934
1138
|
}
|
|
935
1139
|
}
|
|
936
1140
|
}
|
|
937
1141
|
const written = [];
|
|
938
|
-
|
|
939
|
-
return { written
|
|
1142
|
+
writeTsSurface(cwd, written, force);
|
|
1143
|
+
return { written };
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
// src/install-reminder.ts
|
|
1147
|
+
function installHint(env = process.env) {
|
|
1148
|
+
const agent = env.npm_config_user_agent ?? "";
|
|
1149
|
+
const manager = agent.split("/")[0];
|
|
1150
|
+
if (manager === "pnpm") {
|
|
1151
|
+
return "pnpm install";
|
|
1152
|
+
}
|
|
1153
|
+
if (manager === "yarn") {
|
|
1154
|
+
return "yarn install";
|
|
1155
|
+
}
|
|
1156
|
+
if (manager === "npm") {
|
|
1157
|
+
return "npm install";
|
|
1158
|
+
}
|
|
1159
|
+
return "bun install";
|
|
940
1160
|
}
|
|
941
1161
|
|
|
942
1162
|
// src/json-output.ts
|
|
@@ -945,176 +1165,765 @@ function renderResult(input) {
|
|
|
945
1165
|
const base = ok ? { command: input.command, ok, written: input.written ?? [] } : { command: input.command, ok, error: input.error };
|
|
946
1166
|
const withVersion = input.defoldVersion === undefined ? base : { ...base, defoldVersion: input.defoldVersion };
|
|
947
1167
|
const withSurface = "apiSurface" in input ? { ...withVersion, apiSurface: input.apiSurface } : withVersion;
|
|
948
|
-
const
|
|
949
|
-
const
|
|
1168
|
+
const withMaterialized = "materializedSurface" in input ? { ...withSurface, materializedSurface: input.materializedSurface } : withSurface;
|
|
1169
|
+
const withWalls = "directoryWalls" in input ? { ...withMaterialized, directoryWalls: input.directoryWalls } : withMaterialized;
|
|
1170
|
+
const withEligible = "eligible" in input ? { ...withWalls, eligible: input.eligible } : withWalls;
|
|
1171
|
+
const withInstall = "installCommand" in input ? { ...withEligible, installCommand: input.installCommand } : withEligible;
|
|
1172
|
+
const withManual = "manualSteps" in input ? { ...withInstall, manualSteps: input.manualSteps } : withInstall;
|
|
1173
|
+
const withActions = "actions" in input ? { ...withManual, actions: input.actions } : withManual;
|
|
1174
|
+
const withAdded = "addedTo" in input ? { ...withActions, addedTo: input.addedTo } : withActions;
|
|
1175
|
+
const withRemoved = "removedFrom" in input ? { ...withAdded, removedFrom: input.removedFrom } : withAdded;
|
|
1176
|
+
const withBoot = "bootPath" in input ? { ...withRemoved, bootPath: input.bootPath } : withRemoved;
|
|
1177
|
+
const withSub = "subcommand" in input ? { ...withBoot, subcommand: input.subcommand } : withBoot;
|
|
1178
|
+
const payload = "exitCode" in input ? { ...withSub, exitCode: input.exitCode } : withSub;
|
|
950
1179
|
return `${JSON.stringify(payload)}
|
|
951
1180
|
`;
|
|
952
1181
|
}
|
|
1182
|
+
function renderWatchEvent(input) {
|
|
1183
|
+
const ok = input.error === undefined;
|
|
1184
|
+
const base = ok ? { command: "watch", event: input.event, ok, written: input.written ?? [] } : { command: "watch", event: input.event, ok, error: input.error };
|
|
1185
|
+
const withChanged = "changed" in input ? { ...base, changed: input.changed } : base;
|
|
1186
|
+
const withRemoved = "removed" in input ? { ...withChanged, removed: input.removed } : withChanged;
|
|
1187
|
+
return `${JSON.stringify(withRemoved)}
|
|
1188
|
+
`;
|
|
1189
|
+
}
|
|
953
1190
|
|
|
954
1191
|
// src/materialize.ts
|
|
955
1192
|
import {
|
|
956
1193
|
copyFileSync,
|
|
957
|
-
existsSync as
|
|
958
|
-
mkdirSync as
|
|
1194
|
+
existsSync as existsSync4,
|
|
1195
|
+
mkdirSync as mkdirSync4,
|
|
959
1196
|
readdirSync as readdirSync2,
|
|
960
1197
|
readFileSync as readFileSync6,
|
|
961
1198
|
rmSync,
|
|
962
1199
|
writeFileSync as writeFileSync3
|
|
963
1200
|
} from "node:fs";
|
|
964
|
-
import * as
|
|
1201
|
+
import * as path8 from "node:path";
|
|
965
1202
|
var MATERIALIZED_ROOT = ".defold-types";
|
|
1203
|
+
var CORE_TYPES_REEXPORT = `export * from "@defold-typescript/types/core-types";
|
|
1204
|
+
`;
|
|
966
1205
|
function writeJson2(filePath, value) {
|
|
967
1206
|
writeFileSync3(filePath, `${JSON.stringify(value, null, 2)}
|
|
968
1207
|
`);
|
|
969
1208
|
}
|
|
970
|
-
function listDts(dir) {
|
|
971
|
-
return readdirSync2(dir).filter((file) => file.endsWith(".d.ts")).sort();
|
|
1209
|
+
function listDts(dir) {
|
|
1210
|
+
return readdirSync2(dir).filter((file) => file.endsWith(".d.ts")).sort();
|
|
1211
|
+
}
|
|
1212
|
+
function materializeApiSurface(opts) {
|
|
1213
|
+
const { cwd, surface, sourceGeneratedDir } = opts;
|
|
1214
|
+
if (!surface.available || surface.surfaceId === null || sourceGeneratedDir === null) {
|
|
1215
|
+
return { materializedDir: null, active: null };
|
|
1216
|
+
}
|
|
1217
|
+
const { surfaceId } = surface;
|
|
1218
|
+
const relDir = path8.posix.join(MATERIALIZED_ROOT, surfaceId);
|
|
1219
|
+
const absDir = path8.join(cwd, MATERIALIZED_ROOT, surfaceId);
|
|
1220
|
+
mkdirSync4(absDir, { recursive: true });
|
|
1221
|
+
const sources = listDts(sourceGeneratedDir).filter((file) => file !== "index.d.ts");
|
|
1222
|
+
const srcDir = path8.resolve(sourceGeneratedDir, "..", "src");
|
|
1223
|
+
const overloads = ["msg-overloads.d.ts", "message-guard.d.ts", "go-overloads.d.ts"].filter((file) => existsSync4(path8.join(srcDir, file)));
|
|
1224
|
+
const coreTypesSrc = path8.join(srcDir, "core-types.ts");
|
|
1225
|
+
const includeCoreTypes = overloads.length > 0 && existsSync4(coreTypesSrc);
|
|
1226
|
+
const engineGlobalsSrc = path8.join(srcDir, "engine-globals.d.ts");
|
|
1227
|
+
const includeEngineGlobals = includeCoreTypes && existsSync4(engineGlobalsSrc);
|
|
1228
|
+
const wanted = new Set(sources);
|
|
1229
|
+
for (const file of overloads) {
|
|
1230
|
+
wanted.add(file);
|
|
1231
|
+
}
|
|
1232
|
+
if (includeCoreTypes) {
|
|
1233
|
+
wanted.add("core-types.d.ts");
|
|
1234
|
+
}
|
|
1235
|
+
if (includeEngineGlobals) {
|
|
1236
|
+
wanted.add("engine-globals.d.ts");
|
|
1237
|
+
}
|
|
1238
|
+
for (const existing of readdirSync2(absDir)) {
|
|
1239
|
+
if (existing.endsWith(".d.ts") && existing !== "index.d.ts" && !wanted.has(existing)) {
|
|
1240
|
+
rmSync(path8.join(absDir, existing));
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
for (const file of sources) {
|
|
1244
|
+
writeFileSync3(path8.join(absDir, file), readFileSync6(path8.join(sourceGeneratedDir, file), "utf8"));
|
|
1245
|
+
}
|
|
1246
|
+
if (includeCoreTypes) {
|
|
1247
|
+
writeFileSync3(path8.join(absDir, "core-types.d.ts"), CORE_TYPES_REEXPORT);
|
|
1248
|
+
}
|
|
1249
|
+
if (includeEngineGlobals) {
|
|
1250
|
+
writeFileSync3(path8.join(absDir, "engine-globals.d.ts"), readFileSync6(engineGlobalsSrc, "utf8"));
|
|
1251
|
+
}
|
|
1252
|
+
for (const file of overloads) {
|
|
1253
|
+
writeFileSync3(path8.join(absDir, file), readFileSync6(path8.join(srcDir, file), "utf8"));
|
|
1254
|
+
}
|
|
1255
|
+
const modules = [...sources, ...overloads].map((file) => file.replace(/\.d\.ts$/, ""));
|
|
1256
|
+
if (includeEngineGlobals) {
|
|
1257
|
+
modules.push("engine-globals");
|
|
1258
|
+
}
|
|
1259
|
+
const imports = modules.map((mod) => `import "./${mod}";`).join(`
|
|
1260
|
+
`);
|
|
1261
|
+
writeFileSync3(path8.join(absDir, "index.d.ts"), `${imports}
|
|
1262
|
+
|
|
1263
|
+
export {};
|
|
1264
|
+
`);
|
|
1265
|
+
writeJson2(path8.join(absDir, "package.json"), {
|
|
1266
|
+
name: `@defold-typescript/materialized-${surfaceId}`,
|
|
1267
|
+
types: "index.d.ts"
|
|
1268
|
+
});
|
|
1269
|
+
return { materializedDir: relDir, active: surfaceId };
|
|
1270
|
+
}
|
|
1271
|
+
function ensureGitignoreLine(cwd, line) {
|
|
1272
|
+
const gitignorePath = path8.join(cwd, ".gitignore");
|
|
1273
|
+
if (!existsSync4(gitignorePath)) {
|
|
1274
|
+
writeFileSync3(gitignorePath, `${line}
|
|
1275
|
+
`);
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
const existing = readFileSync6(gitignorePath, "utf8");
|
|
1279
|
+
const present = new Set(existing.split(`
|
|
1280
|
+
`).map((entry) => entry.trim()));
|
|
1281
|
+
if (present.has(line)) {
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
const prefix = existing.endsWith(`
|
|
1285
|
+
`) || existing === "" ? "" : `
|
|
1286
|
+
`;
|
|
1287
|
+
writeFileSync3(gitignorePath, `${existing}${prefix}${line}
|
|
1288
|
+
`);
|
|
1289
|
+
}
|
|
1290
|
+
function ensureMaterializedReference(cwd, materializedDir) {
|
|
1291
|
+
if (materializedDir === null) {
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1294
|
+
const surfaceId = path8.posix.basename(materializedDir);
|
|
1295
|
+
const tsconfigPath = path8.join(cwd, "tsconfig.json");
|
|
1296
|
+
if (existsSync4(tsconfigPath)) {
|
|
1297
|
+
const tsconfig = JSON.parse(readFileSync6(tsconfigPath, "utf8"));
|
|
1298
|
+
const current = tsconfig.compilerOptions ?? {};
|
|
1299
|
+
const alreadyRepointed = JSON.stringify(current.typeRoots) === JSON.stringify([MATERIALIZED_ROOT]) && JSON.stringify(current.types) === JSON.stringify([surfaceId]);
|
|
1300
|
+
if (!alreadyRepointed) {
|
|
1301
|
+
tsconfig.compilerOptions = {
|
|
1302
|
+
...current,
|
|
1303
|
+
typeRoots: [MATERIALIZED_ROOT],
|
|
1304
|
+
types: [surfaceId]
|
|
1305
|
+
};
|
|
1306
|
+
writeJson2(tsconfigPath, tsconfig);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
ensureGitignoreLine(cwd, `${MATERIALIZED_ROOT}/`);
|
|
1310
|
+
}
|
|
1311
|
+
function resolveCurrentSurfaceGeneratedDir() {
|
|
1312
|
+
const root = resolveTypesPackageRoot();
|
|
1313
|
+
return root === null ? null : path8.join(root, "generated");
|
|
1314
|
+
}
|
|
1315
|
+
async function materializeRefDocSurface(opts) {
|
|
1316
|
+
const { cwd, surfaceId, resolveOpts } = opts;
|
|
1317
|
+
const root = resolveTypesPackageRoot();
|
|
1318
|
+
if (root === null) {
|
|
1319
|
+
return { materializedDir: null, active: null };
|
|
1320
|
+
}
|
|
1321
|
+
const registry = opts.registry ?? loadApiTargetsRegistry();
|
|
1322
|
+
const target = registry.find((t) => t.id === surfaceId);
|
|
1323
|
+
if (target?.source?.kind !== "ref-doc") {
|
|
1324
|
+
return { materializedDir: null, active: null };
|
|
1325
|
+
}
|
|
1326
|
+
const relDir = path8.posix.join(MATERIALIZED_ROOT, surfaceId);
|
|
1327
|
+
const absDir = path8.join(cwd, MATERIALIZED_ROOT, surfaceId);
|
|
1328
|
+
try {
|
|
1329
|
+
const mod = await import(path8.join(root, "scripts", "materialize-version.ts"));
|
|
1330
|
+
const selfContained = { ...target, coreTypesImport: "./core-types" };
|
|
1331
|
+
await mod.materializeVersionedSurface(selfContained, {
|
|
1332
|
+
destDir: absDir,
|
|
1333
|
+
...resolveOpts ? { resolveOpts } : {}
|
|
1334
|
+
});
|
|
1335
|
+
writeFileSync3(path8.join(absDir, "core-types.d.ts"), CORE_TYPES_REEXPORT);
|
|
1336
|
+
copyFileSync(path8.join(root, "src", "engine-globals.d.ts"), path8.join(absDir, "engine-globals.d.ts"));
|
|
1337
|
+
const indexPath = path8.join(absDir, "index.d.ts");
|
|
1338
|
+
writeFileSync3(indexPath, `import "./engine-globals";
|
|
1339
|
+
${readFileSync6(indexPath, "utf8")}`);
|
|
1340
|
+
} catch {
|
|
1341
|
+
rmSync(absDir, { recursive: true, force: true });
|
|
1342
|
+
return { materializedDir: null, active: null };
|
|
1343
|
+
}
|
|
1344
|
+
return { materializedDir: relDir, active: surfaceId };
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
// src/setup-debug.ts
|
|
1348
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1349
|
+
import * as path10 from "node:path";
|
|
1350
|
+
|
|
1351
|
+
// src/boot-path.ts
|
|
1352
|
+
import { existsSync as existsSync5, readFileSync as readFileSync7 } from "node:fs";
|
|
1353
|
+
import * as path9 from "node:path";
|
|
1354
|
+
var MAIN_COLLECTION_RE = /^main_collection\s*=\s*(.+)$/m;
|
|
1355
|
+
var COMPONENT_RE = /component:\s*\\?"([^"\\]+)\\?"/;
|
|
1356
|
+
var COLLECTION_RE = /collection:\s*\\?"([^"\\]+\.collectionc?)\\?"/;
|
|
1357
|
+
var TOP_LEVEL_ID_RE = /^\s*id:\s*"([^"\\]+)"\s*$/;
|
|
1358
|
+
var TS_SCRIPT_SUFFIX = ".ts.script";
|
|
1359
|
+
function projectPathToAbs(cwd, projectPath) {
|
|
1360
|
+
return path9.join(cwd, projectPath.replace(/^\//, ""));
|
|
1361
|
+
}
|
|
1362
|
+
function relPosix(cwd, abs) {
|
|
1363
|
+
return path9.relative(cwd, abs).split(path9.sep).join("/");
|
|
1364
|
+
}
|
|
1365
|
+
function resolveBootPathScripts(cwd) {
|
|
1366
|
+
const gameProjectPath = path9.join(cwd, "game.project");
|
|
1367
|
+
if (!existsSync5(gameProjectPath)) {
|
|
1368
|
+
return [];
|
|
1369
|
+
}
|
|
1370
|
+
let gameProject;
|
|
1371
|
+
try {
|
|
1372
|
+
gameProject = readFileSync7(gameProjectPath, "utf8");
|
|
1373
|
+
} catch {
|
|
1374
|
+
return [];
|
|
1375
|
+
}
|
|
1376
|
+
const mainMatch = gameProject.match(MAIN_COLLECTION_RE);
|
|
1377
|
+
if (mainMatch?.[1] === undefined) {
|
|
1378
|
+
return [];
|
|
1379
|
+
}
|
|
1380
|
+
const mainCollection = mainMatch[1].trim().replace(/\.collectionc$/, ".collection");
|
|
1381
|
+
const candidates = [];
|
|
1382
|
+
const visited = new Set;
|
|
1383
|
+
const walk = (collectionAbs, tracePrefix) => {
|
|
1384
|
+
if (visited.has(collectionAbs)) {
|
|
1385
|
+
return;
|
|
1386
|
+
}
|
|
1387
|
+
visited.add(collectionAbs);
|
|
1388
|
+
if (!existsSync5(collectionAbs)) {
|
|
1389
|
+
return;
|
|
1390
|
+
}
|
|
1391
|
+
let content;
|
|
1392
|
+
try {
|
|
1393
|
+
content = readFileSync7(collectionAbs, "utf8");
|
|
1394
|
+
} catch {
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
const trace = [...tracePrefix, relPosix(cwd, collectionAbs)];
|
|
1398
|
+
const children = [];
|
|
1399
|
+
let currentId = null;
|
|
1400
|
+
for (const line of content.split(`
|
|
1401
|
+
`)) {
|
|
1402
|
+
const idMatch = line.match(TOP_LEVEL_ID_RE);
|
|
1403
|
+
if (idMatch?.[1] !== undefined) {
|
|
1404
|
+
currentId = idMatch[1];
|
|
1405
|
+
continue;
|
|
1406
|
+
}
|
|
1407
|
+
const componentMatch = line.match(COMPONENT_RE);
|
|
1408
|
+
const component = componentMatch?.[1];
|
|
1409
|
+
if (component?.endsWith(TS_SCRIPT_SUFFIX)) {
|
|
1410
|
+
const candidate = component.replace(/^\//, "").replace(/\.script$/, "");
|
|
1411
|
+
candidates.push({
|
|
1412
|
+
candidate,
|
|
1413
|
+
trace: [...trace, currentId ?? "?", component]
|
|
1414
|
+
});
|
|
1415
|
+
continue;
|
|
1416
|
+
}
|
|
1417
|
+
const collectionMatch = line.match(COLLECTION_RE);
|
|
1418
|
+
if (collectionMatch?.[1] !== undefined) {
|
|
1419
|
+
const ref = collectionMatch[1].replace(/\.collectionc$/, ".collection");
|
|
1420
|
+
children.push(projectPathToAbs(cwd, ref));
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
for (const child of children) {
|
|
1424
|
+
walk(child, trace);
|
|
1425
|
+
}
|
|
1426
|
+
};
|
|
1427
|
+
walk(projectPathToAbs(cwd, mainCollection), ["game.project"]);
|
|
1428
|
+
return candidates;
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
// src/setup-debug.ts
|
|
1432
|
+
var LLDEBUGGER_URL = "https://github.com/defold-typescript/toolchain/releases/download/lldebugger-v1/lldebugger.zip";
|
|
1433
|
+
var LEGACY_BOOTSTRAP_MARKER = "// lldebugger-bootstrap: debug entry, inert in release builds";
|
|
1434
|
+
var AMBIENT_DTS_REL = "src/lldebugger.debug.d.ts";
|
|
1435
|
+
var AMBIENT_DECLARATION = `/** @noResolution */
|
|
1436
|
+
declare module "lldebugger.debug" {
|
|
1437
|
+
export function start(): void;
|
|
1438
|
+
}
|
|
1439
|
+
`;
|
|
1440
|
+
var BLOCK_BEGIN = "// defold-typescript:setup-debug BEGIN — managed block, do not edit";
|
|
1441
|
+
var BLOCK_END = "// defold-typescript:setup-debug END";
|
|
1442
|
+
var MANAGED_BLOCK = `${BLOCK_BEGIN}
|
|
1443
|
+
import * as lldebugger from "lldebugger.debug";
|
|
1444
|
+
|
|
1445
|
+
if (sys.get_engine_info().is_debug) {
|
|
1446
|
+
lldebugger.start();
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
${BLOCK_END}`;
|
|
1450
|
+
var FACTORY_NAMES = ["defineScript", "defineGuiScript", "defineRenderScript"];
|
|
1451
|
+
var MANUAL_STEPS = [
|
|
1452
|
+
"Install the Local Lua Debugger extension (tomblind.local-lua-debugger-vscode) in VS Code.",
|
|
1453
|
+
"Run Project -> Fetch Libraries in the Defold editor to download the lldebugger module."
|
|
1454
|
+
];
|
|
1455
|
+
function isProjectHeader(line) {
|
|
1456
|
+
return line.trim() === "[project]";
|
|
1457
|
+
}
|
|
1458
|
+
function isSectionHeader(line) {
|
|
1459
|
+
return /^\[.+\]\s*$/.test(line.trim());
|
|
1460
|
+
}
|
|
1461
|
+
function addLldebuggerDependency(gameProjectText) {
|
|
1462
|
+
const lines = gameProjectText.split(`
|
|
1463
|
+
`);
|
|
1464
|
+
if (!lines.some(isProjectHeader)) {
|
|
1465
|
+
throw new Error("defold-typescript setup-debug: game.project has no [project] section; this is not a Defold project.");
|
|
1466
|
+
}
|
|
1467
|
+
if (gameProjectText.includes(LLDEBUGGER_URL)) {
|
|
1468
|
+
return gameProjectText;
|
|
1469
|
+
}
|
|
1470
|
+
let inProject = false;
|
|
1471
|
+
let maxIndex = -1;
|
|
1472
|
+
let lastDepLine = -1;
|
|
1473
|
+
let lastProjectLine = -1;
|
|
1474
|
+
for (let i = 0;i < lines.length; i++) {
|
|
1475
|
+
const line = lines[i] ?? "";
|
|
1476
|
+
if (isSectionHeader(line)) {
|
|
1477
|
+
inProject = isProjectHeader(line);
|
|
1478
|
+
if (inProject) {
|
|
1479
|
+
lastProjectLine = i;
|
|
1480
|
+
}
|
|
1481
|
+
continue;
|
|
1482
|
+
}
|
|
1483
|
+
if (inProject && line.trim() !== "") {
|
|
1484
|
+
lastProjectLine = i;
|
|
1485
|
+
const dep = line.match(/^dependencies#(\d+)\s*=/);
|
|
1486
|
+
if (dep?.[1] !== undefined) {
|
|
1487
|
+
const n = Number(dep[1]);
|
|
1488
|
+
if (n > maxIndex) {
|
|
1489
|
+
maxIndex = n;
|
|
1490
|
+
}
|
|
1491
|
+
lastDepLine = i;
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
const newLine = `dependencies#${maxIndex + 1} = ${LLDEBUGGER_URL}`;
|
|
1496
|
+
const insertAt = (lastDepLine >= 0 ? lastDepLine : lastProjectLine) + 1;
|
|
1497
|
+
lines.splice(insertAt, 0, newLine);
|
|
1498
|
+
return lines.join(`
|
|
1499
|
+
`);
|
|
1500
|
+
}
|
|
1501
|
+
var LEGACY_BLOCK = `${LEGACY_BOOTSTRAP_MARKER}
|
|
1502
|
+
/** @noResolution */
|
|
1503
|
+
declare module "lldebugger.debug" {
|
|
1504
|
+
export function start(): void;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
import * as lldebugger from "lldebugger.debug";
|
|
1508
|
+
|
|
1509
|
+
if (sys.get_engine_info().is_debug) {
|
|
1510
|
+
lldebugger.start();
|
|
1511
|
+
}
|
|
1512
|
+
`;
|
|
1513
|
+
function occurrences(haystack, needle) {
|
|
1514
|
+
return haystack.split(needle).length - 1;
|
|
1515
|
+
}
|
|
1516
|
+
function upsertManagedBlock(source) {
|
|
1517
|
+
const begins = occurrences(source, BLOCK_BEGIN);
|
|
1518
|
+
const ends = occurrences(source, BLOCK_END);
|
|
1519
|
+
if (begins !== ends || begins > 1) {
|
|
1520
|
+
throw new Error("defold-typescript setup-debug: malformed managed block (mismatched, duplicate, or out-of-order sentinels); refusing to edit.");
|
|
1521
|
+
}
|
|
1522
|
+
if (begins === 1) {
|
|
1523
|
+
const beginAt = source.indexOf(BLOCK_BEGIN);
|
|
1524
|
+
const endAt = source.indexOf(BLOCK_END);
|
|
1525
|
+
if (endAt < beginAt) {
|
|
1526
|
+
throw new Error("defold-typescript setup-debug: malformed managed block (END before BEGIN); refusing to edit.");
|
|
1527
|
+
}
|
|
1528
|
+
const regionEnd = endAt + BLOCK_END.length;
|
|
1529
|
+
const region = source.slice(beginAt, regionEnd);
|
|
1530
|
+
if (region === MANAGED_BLOCK) {
|
|
1531
|
+
return { text: source, action: "unchanged" };
|
|
1532
|
+
}
|
|
1533
|
+
const text = source.slice(0, beginAt) + MANAGED_BLOCK + source.slice(regionEnd);
|
|
1534
|
+
return { text, action: "refreshed" };
|
|
1535
|
+
}
|
|
1536
|
+
if (source.includes(LEGACY_BLOCK)) {
|
|
1537
|
+
return { text: source.replace(LEGACY_BLOCK, `${MANAGED_BLOCK}
|
|
1538
|
+
`), action: "refreshed" };
|
|
1539
|
+
}
|
|
1540
|
+
return { text: `${MANAGED_BLOCK}
|
|
1541
|
+
|
|
1542
|
+
${source}`, action: "injected" };
|
|
1543
|
+
}
|
|
1544
|
+
function stripManagedBlock(source) {
|
|
1545
|
+
const begins = occurrences(source, BLOCK_BEGIN);
|
|
1546
|
+
const ends = occurrences(source, BLOCK_END);
|
|
1547
|
+
if (begins === 0 && ends === 0) {
|
|
1548
|
+
return { text: source, removed: false };
|
|
1549
|
+
}
|
|
1550
|
+
if (begins !== ends || begins > 1) {
|
|
1551
|
+
throw new Error("defold-typescript setup-debug: malformed managed block (mismatched, duplicate, or out-of-order sentinels); refusing to edit.");
|
|
1552
|
+
}
|
|
1553
|
+
const beginAt = source.indexOf(BLOCK_BEGIN);
|
|
1554
|
+
const endAt = source.indexOf(BLOCK_END);
|
|
1555
|
+
if (endAt < beginAt) {
|
|
1556
|
+
throw new Error("defold-typescript setup-debug: malformed managed block (END before BEGIN); refusing to edit.");
|
|
1557
|
+
}
|
|
1558
|
+
const regionEnd = endAt + BLOCK_END.length;
|
|
1559
|
+
const after = source.slice(regionEnd).replace(/^\n{1,2}/, "");
|
|
1560
|
+
return { text: source.slice(0, beginAt) + after, removed: true };
|
|
972
1561
|
}
|
|
973
|
-
function
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1562
|
+
function hasFactoryCall(text) {
|
|
1563
|
+
return FACTORY_NAMES.some((name) => text.includes(`${name}(`));
|
|
1564
|
+
}
|
|
1565
|
+
function findEntryScriptCandidates(cwd, scanner = scanFilesSync) {
|
|
1566
|
+
const srcDir = path10.join(cwd, "src");
|
|
1567
|
+
if (!existsSync6(srcDir)) {
|
|
1568
|
+
return [];
|
|
977
1569
|
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
const
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1570
|
+
return scanner(cwd, "src/**/*.ts").map(normalizeScannedPath).filter((rel) => !isSkipped(rel)).filter((rel) => hasFactoryCall(readFileSync8(path10.join(cwd, rel), "utf8"))).sort();
|
|
1571
|
+
}
|
|
1572
|
+
function failure(error) {
|
|
1573
|
+
return { ok: false, written: [], actions: {}, manualSteps: MANUAL_STEPS, error };
|
|
1574
|
+
}
|
|
1575
|
+
async function defaultChooseScript(candidates) {
|
|
1576
|
+
const { select } = await import("@inquirer/prompts");
|
|
1577
|
+
return select({
|
|
1578
|
+
message: "Select the entry script to receive the debugger bootstrap:",
|
|
1579
|
+
choices: candidates.map((candidate) => ({ name: candidate, value: candidate }))
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
async function pickCandidate(candidates, opts) {
|
|
1583
|
+
if (candidates.length === 1) {
|
|
1584
|
+
return candidates[0];
|
|
993
1585
|
}
|
|
994
|
-
|
|
995
|
-
|
|
1586
|
+
const chooser = opts.chooseScript ?? (opts.json ? undefined : defaultChooseScript);
|
|
1587
|
+
if (chooser === undefined) {
|
|
1588
|
+
return failure(`defold-typescript setup-debug: multiple entry scripts found (${candidates.join(", ")}); pass --script to choose one.`);
|
|
996
1589
|
}
|
|
997
|
-
|
|
998
|
-
|
|
1590
|
+
return chooser(candidates);
|
|
1591
|
+
}
|
|
1592
|
+
async function resolveTargetScript(opts) {
|
|
1593
|
+
const { cwd } = opts;
|
|
1594
|
+
const script = opts.script === undefined ? undefined : normalizeScannedPath(opts.script);
|
|
1595
|
+
const bootCandidates = resolveBootPathScripts(cwd).filter((entry) => existsSync6(path10.join(cwd, entry.candidate)));
|
|
1596
|
+
if (script !== undefined) {
|
|
1597
|
+
if (!existsSync6(path10.join(cwd, script))) {
|
|
1598
|
+
return failure(`defold-typescript setup-debug: script not found: ${script}`);
|
|
1599
|
+
}
|
|
1600
|
+
const onPath = bootCandidates.find((entry) => entry.candidate === script);
|
|
1601
|
+
return { target: script, bootPath: onPath?.trace ?? [] };
|
|
999
1602
|
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1603
|
+
if (bootCandidates.length > 0) {
|
|
1604
|
+
const picked2 = await pickCandidate(bootCandidates.map((entry) => entry.candidate), opts);
|
|
1605
|
+
if (typeof picked2 !== "string") {
|
|
1606
|
+
return picked2;
|
|
1003
1607
|
}
|
|
1608
|
+
const onPath = bootCandidates.find((entry) => entry.candidate === picked2);
|
|
1609
|
+
return { target: picked2, bootPath: onPath?.trace ?? [] };
|
|
1004
1610
|
}
|
|
1005
|
-
|
|
1006
|
-
|
|
1611
|
+
const scanned = findEntryScriptCandidates(cwd, opts.scanFiles);
|
|
1612
|
+
if (scanned.length === 0) {
|
|
1613
|
+
return failure("defold-typescript setup-debug: no entry script with a lifecycle factory call found under src/.");
|
|
1007
1614
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1615
|
+
const picked = await pickCandidate(scanned, opts);
|
|
1616
|
+
if (typeof picked !== "string") {
|
|
1617
|
+
return picked;
|
|
1010
1618
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1619
|
+
return { target: picked, bootPath: [] };
|
|
1620
|
+
}
|
|
1621
|
+
async function runSetupDebug(opts) {
|
|
1622
|
+
const { cwd } = opts;
|
|
1623
|
+
const gameProjectPath = path10.join(cwd, "game.project");
|
|
1624
|
+
if (!existsSync6(gameProjectPath)) {
|
|
1625
|
+
return failure("defold-typescript setup-debug: no game.project here; this is not a Defold project.");
|
|
1013
1626
|
}
|
|
1014
|
-
|
|
1015
|
-
|
|
1627
|
+
const resolved = await resolveTargetScript(opts);
|
|
1628
|
+
if (!("target" in resolved)) {
|
|
1629
|
+
return resolved;
|
|
1016
1630
|
}
|
|
1017
|
-
const
|
|
1018
|
-
|
|
1019
|
-
|
|
1631
|
+
const { target, bootPath } = resolved;
|
|
1632
|
+
const written = [];
|
|
1633
|
+
const actions = {};
|
|
1634
|
+
const dtsPath = path10.join(cwd, AMBIENT_DTS_REL);
|
|
1635
|
+
mkdirSync5(path10.dirname(dtsPath), { recursive: true });
|
|
1636
|
+
const existingDts = existsSync6(dtsPath) ? readFileSync8(dtsPath, "utf8") : null;
|
|
1637
|
+
if (existingDts === null) {
|
|
1638
|
+
writeFileSync4(dtsPath, AMBIENT_DECLARATION);
|
|
1639
|
+
actions[AMBIENT_DTS_REL] = "injected";
|
|
1640
|
+
} else if (existingDts !== AMBIENT_DECLARATION) {
|
|
1641
|
+
writeFileSync4(dtsPath, AMBIENT_DECLARATION);
|
|
1642
|
+
actions[AMBIENT_DTS_REL] = "refreshed";
|
|
1643
|
+
} else {
|
|
1644
|
+
actions[AMBIENT_DTS_REL] = "unchanged";
|
|
1645
|
+
}
|
|
1646
|
+
written.push(AMBIENT_DTS_REL);
|
|
1647
|
+
const scriptPath = path10.join(cwd, target);
|
|
1648
|
+
const source = readFileSync8(scriptPath, "utf8");
|
|
1649
|
+
const { text: injected, action: scriptAction } = upsertManagedBlock(source);
|
|
1650
|
+
if (injected !== source) {
|
|
1651
|
+
writeFileSync4(scriptPath, injected);
|
|
1652
|
+
}
|
|
1653
|
+
actions[target] = scriptAction;
|
|
1654
|
+
written.push(target);
|
|
1655
|
+
const gameProjectText = readFileSync8(gameProjectPath, "utf8");
|
|
1656
|
+
const updated = addLldebuggerDependency(gameProjectText);
|
|
1657
|
+
if (updated !== gameProjectText) {
|
|
1658
|
+
writeFileSync4(gameProjectPath, updated);
|
|
1659
|
+
actions["game.project"] = "injected";
|
|
1660
|
+
} else {
|
|
1661
|
+
actions["game.project"] = "unchanged";
|
|
1020
1662
|
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1663
|
+
written.push("game.project");
|
|
1664
|
+
const removedFrom = [];
|
|
1665
|
+
for (const scannedRel of (opts.scanFiles ?? scanFilesSync)(cwd, "src/**/*.ts")) {
|
|
1666
|
+
const rel = normalizeScannedPath(scannedRel);
|
|
1667
|
+
if (isSkipped(rel) || rel === target) {
|
|
1668
|
+
continue;
|
|
1669
|
+
}
|
|
1670
|
+
const otherPath = path10.join(cwd, rel);
|
|
1671
|
+
const otherSource = readFileSync8(otherPath, "utf8");
|
|
1672
|
+
let stripped;
|
|
1673
|
+
try {
|
|
1674
|
+
stripped = stripManagedBlock(otherSource);
|
|
1675
|
+
} catch {
|
|
1676
|
+
continue;
|
|
1677
|
+
}
|
|
1678
|
+
if (stripped.removed) {
|
|
1679
|
+
writeFileSync4(otherPath, stripped.text);
|
|
1680
|
+
actions[rel] = "removed";
|
|
1681
|
+
removedFrom.push(rel);
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
removedFrom.sort();
|
|
1685
|
+
return {
|
|
1686
|
+
ok: true,
|
|
1687
|
+
written,
|
|
1688
|
+
actions,
|
|
1689
|
+
manualSteps: MANUAL_STEPS,
|
|
1690
|
+
addedTo: target,
|
|
1691
|
+
removedFrom,
|
|
1692
|
+
bootPath
|
|
1693
|
+
};
|
|
1694
|
+
}
|
|
1024
1695
|
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1696
|
+
// src/wall.ts
|
|
1697
|
+
import { existsSync as existsSync8, readFileSync as readFileSync10, rmSync as rmSync2 } from "node:fs";
|
|
1698
|
+
import * as path12 from "node:path";
|
|
1699
|
+
|
|
1700
|
+
// src/directory-walls.ts
|
|
1701
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "node:fs";
|
|
1702
|
+
import * as path11 from "node:path";
|
|
1703
|
+
function describeWall(dir, kind) {
|
|
1704
|
+
return {
|
|
1705
|
+
dir,
|
|
1706
|
+
kind,
|
|
1707
|
+
typesEntrypoint: selectScriptKindEntrypoint(new Set([kind]))
|
|
1708
|
+
};
|
|
1032
1709
|
}
|
|
1033
|
-
function
|
|
1034
|
-
const
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1710
|
+
function groupSourceScriptKindsByDirectory(cwd) {
|
|
1711
|
+
const byDir = new Map;
|
|
1712
|
+
const seen = new Set;
|
|
1713
|
+
for (const pattern of readBuildConfig(cwd).include) {
|
|
1714
|
+
for (const match of scanFilesSync(cwd, pattern)) {
|
|
1715
|
+
const rel = match.split(path11.sep).join("/");
|
|
1716
|
+
if (seen.has(rel) || !isTranspilerSource(rel) || isSkipped(rel)) {
|
|
1717
|
+
continue;
|
|
1718
|
+
}
|
|
1719
|
+
seen.add(rel);
|
|
1720
|
+
const kind = detectSourceOutputKind(readFileSync9(path11.join(cwd, match), "utf8"));
|
|
1721
|
+
if (kind === "module") {
|
|
1722
|
+
continue;
|
|
1723
|
+
}
|
|
1724
|
+
const dir = path11.posix.dirname(rel);
|
|
1725
|
+
let set = byDir.get(dir);
|
|
1726
|
+
if (set === undefined) {
|
|
1727
|
+
set = new Set;
|
|
1728
|
+
byDir.set(dir, set);
|
|
1729
|
+
}
|
|
1730
|
+
set.add(kind);
|
|
1731
|
+
}
|
|
1039
1732
|
}
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1733
|
+
return byDir;
|
|
1734
|
+
}
|
|
1735
|
+
function planSourceDirectoryWalls(cwd) {
|
|
1736
|
+
const walls = [];
|
|
1737
|
+
for (const [dir, kinds] of groupSourceScriptKindsByDirectory(cwd)) {
|
|
1738
|
+
const kind = selectScriptKind(kinds);
|
|
1739
|
+
if (kind !== null) {
|
|
1740
|
+
walls.push(describeWall(dir, kind));
|
|
1741
|
+
}
|
|
1045
1742
|
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1743
|
+
return walls.sort((a, b) => a.dir < b.dir ? -1 : a.dir > b.dir ? 1 : 0);
|
|
1744
|
+
}
|
|
1745
|
+
function directoryWallTsconfig(wall) {
|
|
1746
|
+
const depth = wall.dir.split("/").length;
|
|
1747
|
+
return {
|
|
1748
|
+
extends: `${"../".repeat(depth)}tsconfig.json`,
|
|
1749
|
+
compilerOptions: { composite: true, typeRoots: null, types: [wall.typesEntrypoint] },
|
|
1750
|
+
include: ["**/*.ts"],
|
|
1751
|
+
exclude: []
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
function writeJson3(filePath, value) {
|
|
1755
|
+
mkdirSync6(path11.dirname(filePath), { recursive: true });
|
|
1756
|
+
writeFileSync5(filePath, `${JSON.stringify(value, null, 2)}
|
|
1050
1757
|
`);
|
|
1051
1758
|
}
|
|
1052
|
-
function
|
|
1053
|
-
|
|
1054
|
-
|
|
1759
|
+
function sortedWallDirs(walls) {
|
|
1760
|
+
return walls.map((w) => w.dir).filter((dir) => dir !== ".").sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
1761
|
+
}
|
|
1762
|
+
function isInsideAnyDir(rel, dirs) {
|
|
1763
|
+
return dirs.some((dir) => rel === dir || rel.startsWith(`${dir}/`));
|
|
1764
|
+
}
|
|
1765
|
+
function hasRootOwnedTranspilerSources(cwd, wallDirs) {
|
|
1766
|
+
const seen = new Set;
|
|
1767
|
+
for (const pattern of readBuildConfig(cwd).include) {
|
|
1768
|
+
for (const match of scanFilesSync(cwd, pattern)) {
|
|
1769
|
+
const rel = match.split(path11.sep).join("/");
|
|
1770
|
+
if (seen.has(rel) || !isTranspilerSource(rel) || isSkipped(rel)) {
|
|
1771
|
+
continue;
|
|
1772
|
+
}
|
|
1773
|
+
seen.add(rel);
|
|
1774
|
+
if (!isInsideAnyDir(rel, wallDirs)) {
|
|
1775
|
+
return true;
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1055
1778
|
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1779
|
+
return false;
|
|
1780
|
+
}
|
|
1781
|
+
function wireWallReferences(cwd, walls) {
|
|
1782
|
+
const rootPath = path11.join(cwd, "tsconfig.json");
|
|
1783
|
+
const current = JSON.parse(readFileSync9(rootPath, "utf8"));
|
|
1784
|
+
const wallDirs = sortedWallDirs(walls);
|
|
1785
|
+
const previousReferences = current.references ?? [];
|
|
1786
|
+
const previousManaged = new Set(previousReferences.map((ref) => ref.path));
|
|
1787
|
+
const nextExclude = [
|
|
1788
|
+
...new Set([
|
|
1789
|
+
...(current.exclude ?? []).filter((entry) => !previousManaged.has(entry)),
|
|
1790
|
+
...wallDirs
|
|
1791
|
+
])
|
|
1792
|
+
];
|
|
1793
|
+
const next = { ...current };
|
|
1794
|
+
if (wallDirs.length > 0) {
|
|
1795
|
+
next.references = wallDirs.map((dir) => ({ path: dir }));
|
|
1796
|
+
} else {
|
|
1797
|
+
delete next.references;
|
|
1798
|
+
}
|
|
1799
|
+
if (nextExclude.length > 0) {
|
|
1800
|
+
next.exclude = nextExclude;
|
|
1801
|
+
} else {
|
|
1802
|
+
delete next.exclude;
|
|
1803
|
+
}
|
|
1804
|
+
if (wallDirs.length > 0 && !hasRootOwnedTranspilerSources(cwd, wallDirs)) {
|
|
1805
|
+
next.files = [];
|
|
1806
|
+
} else if (previousReferences.length > 0 && JSON.stringify(next.files) === JSON.stringify([])) {
|
|
1807
|
+
delete next.files;
|
|
1808
|
+
}
|
|
1809
|
+
if (JSON.stringify(next) !== JSON.stringify(current)) {
|
|
1810
|
+
writeJson3(rootPath, next);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
function writeDirectoryWallTsconfigs(cwd, walls) {
|
|
1814
|
+
const written = [];
|
|
1815
|
+
for (const w of walls) {
|
|
1816
|
+
if (w.dir === ".") {
|
|
1817
|
+
continue;
|
|
1818
|
+
}
|
|
1819
|
+
const rel = `${w.dir}/tsconfig.json`;
|
|
1820
|
+
const target = path11.join(cwd, w.dir, "tsconfig.json");
|
|
1821
|
+
const desired = directoryWallTsconfig(w);
|
|
1822
|
+
if (existsSync7(target)) {
|
|
1823
|
+
const current = JSON.parse(readFileSync9(target, "utf8"));
|
|
1824
|
+
const options = current.compilerOptions ?? {};
|
|
1825
|
+
const alreadyNarrowed = current.extends === desired.extends && options.composite === desired.compilerOptions.composite && options.typeRoots === desired.compilerOptions.typeRoots && JSON.stringify(options.types) === JSON.stringify(desired.compilerOptions.types) && JSON.stringify(current.include) === JSON.stringify(desired.include) && JSON.stringify(current.exclude) === JSON.stringify(desired.exclude);
|
|
1826
|
+
if (!alreadyNarrowed) {
|
|
1827
|
+
writeJson3(target, {
|
|
1828
|
+
...current,
|
|
1829
|
+
extends: desired.extends,
|
|
1830
|
+
compilerOptions: {
|
|
1831
|
+
...options,
|
|
1832
|
+
composite: desired.compilerOptions.composite,
|
|
1833
|
+
typeRoots: desired.compilerOptions.typeRoots,
|
|
1834
|
+
types: desired.compilerOptions.types
|
|
1835
|
+
},
|
|
1836
|
+
include: desired.include,
|
|
1837
|
+
exclude: desired.exclude
|
|
1838
|
+
});
|
|
1839
|
+
written.push(rel);
|
|
1840
|
+
}
|
|
1841
|
+
} else {
|
|
1842
|
+
writeJson3(target, desired);
|
|
1843
|
+
written.push(rel);
|
|
1069
1844
|
}
|
|
1070
1845
|
}
|
|
1071
|
-
|
|
1846
|
+
return written.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
1072
1847
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1848
|
+
|
|
1849
|
+
// src/wall.ts
|
|
1850
|
+
function sortDirs(items) {
|
|
1851
|
+
return items.sort((a, b) => a.dir < b.dir ? -1 : a.dir > b.dir ? 1 : 0);
|
|
1076
1852
|
}
|
|
1077
|
-
|
|
1078
|
-
const
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
return { materializedDir: null, active: null };
|
|
1082
|
-
}
|
|
1083
|
-
const registry = opts.registry ?? loadApiTargetsRegistry();
|
|
1084
|
-
const target = registry.find((t) => t.id === surfaceId);
|
|
1085
|
-
if (target?.source?.kind !== "ref-doc") {
|
|
1086
|
-
return { materializedDir: null, active: null };
|
|
1853
|
+
function currentWalledDirs(cwd) {
|
|
1854
|
+
const rootPath = path12.join(cwd, "tsconfig.json");
|
|
1855
|
+
if (!existsSync8(rootPath)) {
|
|
1856
|
+
return [];
|
|
1087
1857
|
}
|
|
1088
|
-
const excludeModules = [...excludedModulesForKind(opts.scriptKind ?? null)];
|
|
1089
|
-
const relDir = path7.posix.join(MATERIALIZED_ROOT, surfaceId);
|
|
1090
|
-
const absDir = path7.join(cwd, MATERIALIZED_ROOT, surfaceId);
|
|
1091
1858
|
try {
|
|
1092
|
-
const
|
|
1093
|
-
|
|
1094
|
-
await mod.materializeVersionedSurface(selfContained, {
|
|
1095
|
-
destDir: absDir,
|
|
1096
|
-
...resolveOpts ? { resolveOpts } : {},
|
|
1097
|
-
...excludeModules.length > 0 ? { excludeModules } : {}
|
|
1098
|
-
});
|
|
1099
|
-
copyFileSync(path7.join(root, "src", "core-types.ts"), path7.join(absDir, "core-types.d.ts"));
|
|
1100
|
-
copyFileSync(path7.join(root, "src", "engine-globals.d.ts"), path7.join(absDir, "engine-globals.d.ts"));
|
|
1101
|
-
const indexPath = path7.join(absDir, "index.d.ts");
|
|
1102
|
-
writeFileSync3(indexPath, `import "./engine-globals";
|
|
1103
|
-
${readFileSync6(indexPath, "utf8")}`);
|
|
1859
|
+
const parsed = JSON.parse(readFileSync10(rootPath, "utf8"));
|
|
1860
|
+
return (parsed.references ?? []).map((ref) => ref.path).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
1104
1861
|
} catch {
|
|
1105
|
-
|
|
1106
|
-
return { materializedDir: null, active: null };
|
|
1862
|
+
return [];
|
|
1107
1863
|
}
|
|
1108
|
-
|
|
1864
|
+
}
|
|
1865
|
+
function eligibleWalls(cwd) {
|
|
1866
|
+
return planSourceDirectoryWalls(cwd);
|
|
1867
|
+
}
|
|
1868
|
+
function applyWallSelection(cwd, desiredDirs) {
|
|
1869
|
+
const byDir = new Map(eligibleWalls(cwd).map((wall) => [wall.dir, wall]));
|
|
1870
|
+
const desired = [];
|
|
1871
|
+
const desiredSet = new Set;
|
|
1872
|
+
for (const dir of desiredDirs) {
|
|
1873
|
+
if (desiredSet.has(dir)) {
|
|
1874
|
+
continue;
|
|
1875
|
+
}
|
|
1876
|
+
const wall = byDir.get(dir);
|
|
1877
|
+
if (wall === undefined) {
|
|
1878
|
+
throw new Error(`defold-typescript wall: ${dir} is not a single-kind source directory that can be walled`);
|
|
1879
|
+
}
|
|
1880
|
+
desired.push(wall);
|
|
1881
|
+
desiredSet.add(dir);
|
|
1882
|
+
}
|
|
1883
|
+
for (const dir of currentWalledDirs(cwd)) {
|
|
1884
|
+
if (!desiredSet.has(dir)) {
|
|
1885
|
+
rmSync2(path12.join(cwd, dir, "tsconfig.json"), { force: true });
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
writeDirectoryWallTsconfigs(cwd, desired);
|
|
1889
|
+
wireWallReferences(cwd, desired);
|
|
1890
|
+
return sortDirs(desired);
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
// src/wall-interactive.ts
|
|
1894
|
+
function buildWallChoices(cwd) {
|
|
1895
|
+
const current = new Set(currentWalledDirs(cwd));
|
|
1896
|
+
const choices = [];
|
|
1897
|
+
for (const [dir, kinds] of groupSourceScriptKindsByDirectory(cwd)) {
|
|
1898
|
+
const kind = selectScriptKind(kinds);
|
|
1899
|
+
if (kind === null) {
|
|
1900
|
+
choices.push({ value: dir, name: dir, disabled: `mixed: ${[...kinds].sort().join(", ")}` });
|
|
1901
|
+
} else {
|
|
1902
|
+
choices.push({ value: dir, name: `${dir} (${kind})`, checked: current.has(dir) });
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
return choices.sort((a, b) => a.value < b.value ? -1 : a.value > b.value ? 1 : 0);
|
|
1906
|
+
}
|
|
1907
|
+
async function defaultCheckbox() {
|
|
1908
|
+
const { checkbox } = await import("@inquirer/prompts");
|
|
1909
|
+
return (opts) => checkbox({ message: opts.message, choices: opts.choices });
|
|
1910
|
+
}
|
|
1911
|
+
async function runWallInteractive(cwd, deps = {}) {
|
|
1912
|
+
const checkbox = deps.checkbox ?? await defaultCheckbox();
|
|
1913
|
+
const selection = await checkbox({
|
|
1914
|
+
message: "Select the source directories to wall (space toggles, enter confirms):",
|
|
1915
|
+
choices: buildWallChoices(cwd)
|
|
1916
|
+
});
|
|
1917
|
+
return applyWallSelection(cwd, selection);
|
|
1109
1918
|
}
|
|
1110
1919
|
|
|
1111
1920
|
// src/watch.ts
|
|
1112
|
-
import { existsSync as
|
|
1113
|
-
import * as
|
|
1921
|
+
import { existsSync as existsSync9, watch as fsWatch } from "node:fs";
|
|
1922
|
+
import * as path14 from "node:path";
|
|
1114
1923
|
|
|
1115
1924
|
// src/build-session.ts
|
|
1116
|
-
import { readFileSync as
|
|
1117
|
-
import * as
|
|
1925
|
+
import { readFileSync as readFileSync11, rmSync as rmSync3 } from "node:fs";
|
|
1926
|
+
import * as path13 from "node:path";
|
|
1118
1927
|
import {
|
|
1119
1928
|
createTranspileSession
|
|
1120
1929
|
} from "@defold-typescript/transpiler";
|
|
@@ -1122,7 +1931,14 @@ function createBuildSession(opts) {
|
|
|
1122
1931
|
const { cwd } = opts;
|
|
1123
1932
|
const config = readBuildConfig(cwd);
|
|
1124
1933
|
const session = createTranspileSession();
|
|
1125
|
-
function
|
|
1934
|
+
function pruneOutputs(rel, keepRel) {
|
|
1935
|
+
for (const outputRel of outputRelsForSource(rel, config)) {
|
|
1936
|
+
if (outputRel !== keepRel && outputRel !== `${keepRel}.map`) {
|
|
1937
|
+
rmSync3(path13.join(cwd, outputRel), { force: true });
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
function writeOutputs(result, keys, sources, pruneAlternatives = false) {
|
|
1126
1942
|
const failures = collectFailures(result.diagnostics);
|
|
1127
1943
|
const written = [];
|
|
1128
1944
|
for (const rel of keys) {
|
|
@@ -1133,12 +1949,15 @@ function createBuildSession(opts) {
|
|
|
1133
1949
|
if (lua === undefined) {
|
|
1134
1950
|
continue;
|
|
1135
1951
|
}
|
|
1136
|
-
const
|
|
1137
|
-
|
|
1138
|
-
|
|
1952
|
+
const outputRel = computeOutputRel(rel, config, detectSourceOutputKind(sources[rel] ?? ""));
|
|
1953
|
+
if (pruneAlternatives) {
|
|
1954
|
+
pruneOutputs(rel, outputRel);
|
|
1955
|
+
}
|
|
1956
|
+
writeScriptFile(cwd, outputRel, lua, result.sourceMaps[rel]);
|
|
1957
|
+
written.push(outputRel);
|
|
1139
1958
|
}
|
|
1140
1959
|
throwIfFailures(failures);
|
|
1141
|
-
return { written };
|
|
1960
|
+
return { written: written.sort() };
|
|
1142
1961
|
}
|
|
1143
1962
|
function buildAll() {
|
|
1144
1963
|
const seen = new Set;
|
|
@@ -1153,28 +1972,30 @@ function createBuildSession(opts) {
|
|
|
1153
1972
|
}
|
|
1154
1973
|
const files = {};
|
|
1155
1974
|
for (const rel of sources) {
|
|
1156
|
-
files[rel] =
|
|
1975
|
+
files[rel] = readFileSync11(path13.join(cwd, rel), "utf8");
|
|
1157
1976
|
}
|
|
1158
1977
|
const result = session.update(files);
|
|
1159
|
-
return writeOutputs(result, sources);
|
|
1978
|
+
return writeOutputs(result, sources, files);
|
|
1160
1979
|
}
|
|
1161
1980
|
function applyEvents(changed, removed) {
|
|
1162
1981
|
const sourceChanged = changed.filter(isTranspilerSource);
|
|
1163
1982
|
const sourceRemoved = removed.filter(isTranspilerSource);
|
|
1164
1983
|
const changes = {};
|
|
1165
1984
|
for (const rel of sourceChanged) {
|
|
1166
|
-
changes[rel] =
|
|
1985
|
+
changes[rel] = readFileSync11(path13.join(cwd, rel), "utf8");
|
|
1167
1986
|
}
|
|
1168
1987
|
for (const rel of sourceRemoved) {
|
|
1169
1988
|
changes[rel] = null;
|
|
1170
1989
|
}
|
|
1171
1990
|
const result = session.update(changes);
|
|
1172
1991
|
for (const rel of sourceRemoved) {
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1992
|
+
pruneOutputs(rel);
|
|
1993
|
+
}
|
|
1994
|
+
const changedSources = {};
|
|
1995
|
+
for (const rel of sourceChanged) {
|
|
1996
|
+
changedSources[rel] = changes[rel] ?? "";
|
|
1176
1997
|
}
|
|
1177
|
-
return writeOutputs(result, sourceChanged);
|
|
1998
|
+
return writeOutputs(result, sourceChanged, changedSources, true);
|
|
1178
1999
|
}
|
|
1179
2000
|
return { buildAll, applyEvents };
|
|
1180
2001
|
}
|
|
@@ -1213,7 +2034,7 @@ function runWatch(opts) {
|
|
|
1213
2034
|
opts.syncSurface?.();
|
|
1214
2035
|
session = createBuildSession({ cwd });
|
|
1215
2036
|
const { written } = session.buildAll();
|
|
1216
|
-
stdout.write(formatBuildLine(written));
|
|
2037
|
+
stdout.write(opts.json ? renderWatchEvent({ event: "build", written }) : formatBuildLine(written));
|
|
1217
2038
|
} catch (err) {
|
|
1218
2039
|
rejectDone(rewrapInitError(err));
|
|
1219
2040
|
return {
|
|
@@ -1222,7 +2043,7 @@ function runWatch(opts) {
|
|
|
1222
2043
|
waitForIdle: () => Promise.resolve()
|
|
1223
2044
|
};
|
|
1224
2045
|
}
|
|
1225
|
-
const srcDir =
|
|
2046
|
+
const srcDir = path14.join(cwd, "src");
|
|
1226
2047
|
let scheduled = null;
|
|
1227
2048
|
let syncScheduled = null;
|
|
1228
2049
|
let rebuildBusy = false;
|
|
@@ -1246,7 +2067,7 @@ function runWatch(opts) {
|
|
|
1246
2067
|
const removed = [];
|
|
1247
2068
|
for (const rel of drained) {
|
|
1248
2069
|
const key = `src/${toPosix(rel)}`;
|
|
1249
|
-
if (
|
|
2070
|
+
if (existsSync9(path14.join(srcDir, rel))) {
|
|
1250
2071
|
changed.push(key);
|
|
1251
2072
|
} else {
|
|
1252
2073
|
removed.push(key);
|
|
@@ -1254,11 +2075,15 @@ function runWatch(opts) {
|
|
|
1254
2075
|
}
|
|
1255
2076
|
try {
|
|
1256
2077
|
const { written } = session.applyEvents(changed, removed);
|
|
1257
|
-
stdout.write(formatBuildLine(written));
|
|
2078
|
+
stdout.write(opts.json ? renderWatchEvent({ event: "rebuild", written, changed, removed }) : formatBuildLine(written));
|
|
1258
2079
|
} catch (err) {
|
|
1259
2080
|
const message = err instanceof Error ? err.message : String(err);
|
|
1260
|
-
|
|
2081
|
+
if (opts.json) {
|
|
2082
|
+
stdout.write(renderWatchEvent({ event: "rebuild", error: message }));
|
|
2083
|
+
} else {
|
|
2084
|
+
stderr.write(`${message}
|
|
1261
2085
|
`);
|
|
2086
|
+
}
|
|
1262
2087
|
}
|
|
1263
2088
|
rebuildBusy = false;
|
|
1264
2089
|
notifyIdle();
|
|
@@ -1328,7 +2153,9 @@ function runWatch(opts) {
|
|
|
1328
2153
|
}
|
|
1329
2154
|
|
|
1330
2155
|
// src/dispatch.ts
|
|
1331
|
-
var USAGE = `Usage: defold-typescript <init|build|watch> [path]
|
|
2156
|
+
var USAGE = `Usage: defold-typescript <init|build|watch|wall|setup-debug|defold> [path]
|
|
2157
|
+
`;
|
|
2158
|
+
var DEFOLD_USAGE = `Usage: defold-typescript defold <resolve|build|bundle> [path]
|
|
1332
2159
|
`;
|
|
1333
2160
|
function parseDefoldVersionFlag(argv) {
|
|
1334
2161
|
let flag;
|
|
@@ -1346,13 +2173,47 @@ function parseDefoldVersionFlag(argv) {
|
|
|
1346
2173
|
}
|
|
1347
2174
|
return { flag, rest };
|
|
1348
2175
|
}
|
|
2176
|
+
function parseScriptFlag(argv) {
|
|
2177
|
+
let script;
|
|
2178
|
+
const rest = [];
|
|
2179
|
+
for (let i = 0;i < argv.length; i++) {
|
|
2180
|
+
const arg = argv[i];
|
|
2181
|
+
if (arg === "--script") {
|
|
2182
|
+
script = argv[i + 1];
|
|
2183
|
+
i++;
|
|
2184
|
+
} else if (arg?.startsWith("--script=")) {
|
|
2185
|
+
script = arg.slice("--script=".length);
|
|
2186
|
+
} else if (arg !== undefined) {
|
|
2187
|
+
rest.push(arg);
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
return { script, rest };
|
|
2191
|
+
}
|
|
2192
|
+
function parseValueFlag(argv, name) {
|
|
2193
|
+
const long = `--${name}`;
|
|
2194
|
+
const eq = `${long}=`;
|
|
2195
|
+
let value;
|
|
2196
|
+
const rest = [];
|
|
2197
|
+
for (let i = 0;i < argv.length; i++) {
|
|
2198
|
+
const arg = argv[i];
|
|
2199
|
+
if (arg === long) {
|
|
2200
|
+
value = argv[i + 1];
|
|
2201
|
+
i++;
|
|
2202
|
+
} else if (arg?.startsWith(eq)) {
|
|
2203
|
+
value = arg.slice(eq.length);
|
|
2204
|
+
} else if (arg !== undefined) {
|
|
2205
|
+
rest.push(arg);
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
return { value, rest };
|
|
2209
|
+
}
|
|
1349
2210
|
function readProjectPin(cwd) {
|
|
1350
|
-
const pkgPath =
|
|
1351
|
-
if (!
|
|
2211
|
+
const pkgPath = path15.join(cwd, "package.json");
|
|
2212
|
+
if (!existsSync10(pkgPath)) {
|
|
1352
2213
|
return;
|
|
1353
2214
|
}
|
|
1354
2215
|
try {
|
|
1355
|
-
return readDefoldVersionPin(JSON.parse(
|
|
2216
|
+
return readDefoldVersionPin(JSON.parse(readFileSync12(pkgPath, "utf8")));
|
|
1356
2217
|
} catch {
|
|
1357
2218
|
return;
|
|
1358
2219
|
}
|
|
@@ -1367,10 +2228,16 @@ function dispatch(argv, io, internals) {
|
|
|
1367
2228
|
return 0;
|
|
1368
2229
|
}
|
|
1369
2230
|
const force = argv.includes("--force");
|
|
1370
|
-
const
|
|
1371
|
-
const
|
|
2231
|
+
const suppressInstallReminder = argv.includes("--suppress-install-reminder");
|
|
2232
|
+
const wallRemove = argv.includes("--remove");
|
|
2233
|
+
const wallList = argv.includes("--list");
|
|
2234
|
+
const { flag: defoldVersionFlag, rest: afterVersionArgs } = parseDefoldVersionFlag(argv);
|
|
2235
|
+
const { script: scriptFlag, rest: afterScriptArgs } = parseScriptFlag(afterVersionArgs);
|
|
2236
|
+
const { value: javaFlag, rest: afterJavaArgs } = parseValueFlag(afterScriptArgs, "java");
|
|
2237
|
+
const { value: buildServerFlag, rest: nonFlagArgs } = parseValueFlag(afterJavaArgs, "build-server");
|
|
2238
|
+
const positional = nonFlagArgs.filter((a) => a !== "--json" && a !== "--force" && a !== "--suppress-install-reminder" && a !== "--remove" && a !== "--list");
|
|
1372
2239
|
const [command, ...rest] = positional;
|
|
1373
|
-
const cwd = rest[0] ?
|
|
2240
|
+
const cwd = rest[0] ? path15.resolve(rest[0]) : process.cwd();
|
|
1374
2241
|
const pin = readProjectPin(cwd);
|
|
1375
2242
|
const resolvedVersion = resolveDefoldVersion({
|
|
1376
2243
|
...defoldVersionFlag !== undefined ? { flag: defoldVersionFlag } : {},
|
|
@@ -1380,18 +2247,22 @@ function dispatch(argv, io, internals) {
|
|
|
1380
2247
|
const apiSurface = surface.surfaceId;
|
|
1381
2248
|
if (command === "init") {
|
|
1382
2249
|
try {
|
|
1383
|
-
const { written
|
|
2250
|
+
const { written } = runInit({ cwd, force });
|
|
1384
2251
|
if (json) {
|
|
1385
2252
|
io.stdout.write(renderResult({
|
|
1386
2253
|
command: "init",
|
|
1387
2254
|
written,
|
|
1388
2255
|
defoldVersion: resolvedVersion,
|
|
1389
2256
|
apiSurface,
|
|
1390
|
-
|
|
2257
|
+
installCommand: installHint()
|
|
1391
2258
|
}));
|
|
1392
2259
|
} else {
|
|
1393
2260
|
io.stdout.write(`defold-typescript init: wrote ${written.length} files: ${written.join(", ")}
|
|
1394
2261
|
`);
|
|
2262
|
+
if (!suppressInstallReminder) {
|
|
2263
|
+
io.stdout.write(`Next: run \`${installHint()}\` to install dependencies.
|
|
2264
|
+
`);
|
|
2265
|
+
}
|
|
1395
2266
|
}
|
|
1396
2267
|
return 0;
|
|
1397
2268
|
} catch (err) {
|
|
@@ -1405,8 +2276,52 @@ function dispatch(argv, io, internals) {
|
|
|
1405
2276
|
return 1;
|
|
1406
2277
|
}
|
|
1407
2278
|
}
|
|
2279
|
+
if (command === "setup-debug") {
|
|
2280
|
+
return (async () => {
|
|
2281
|
+
const result = await runSetupDebug({
|
|
2282
|
+
cwd,
|
|
2283
|
+
json,
|
|
2284
|
+
...scriptFlag !== undefined ? { script: scriptFlag } : {}
|
|
2285
|
+
});
|
|
2286
|
+
if (json) {
|
|
2287
|
+
io.stdout.write(renderResult(result.ok ? {
|
|
2288
|
+
command: "setup-debug",
|
|
2289
|
+
written: result.written,
|
|
2290
|
+
actions: result.actions,
|
|
2291
|
+
manualSteps: result.manualSteps,
|
|
2292
|
+
...result.addedTo !== undefined ? { addedTo: result.addedTo } : {},
|
|
2293
|
+
removedFrom: result.removedFrom ?? [],
|
|
2294
|
+
bootPath: result.bootPath ?? []
|
|
2295
|
+
} : { command: "setup-debug", error: result.error ?? "setup-debug failed" }));
|
|
2296
|
+
} else if (result.ok) {
|
|
2297
|
+
io.stdout.write(`defold-typescript setup-debug: wrote ${result.written.length} files: ${result.written.join(", ")}
|
|
2298
|
+
`);
|
|
2299
|
+
if (result.addedTo !== undefined) {
|
|
2300
|
+
io.stdout.write(`Debugger bootstrap added to: ${result.addedTo}
|
|
2301
|
+
`);
|
|
2302
|
+
}
|
|
2303
|
+
if (result.removedFrom !== undefined && result.removedFrom.length > 0) {
|
|
2304
|
+
io.stdout.write(`Removed stale bootstrap from: ${result.removedFrom.join(", ")}
|
|
2305
|
+
`);
|
|
2306
|
+
}
|
|
2307
|
+
if (result.bootPath !== undefined && result.bootPath.length > 0) {
|
|
2308
|
+
io.stdout.write(`Boot path: ${result.bootPath.join(" -> ")}
|
|
2309
|
+
`);
|
|
2310
|
+
}
|
|
2311
|
+
io.stdout.write(`Remaining manual steps:
|
|
2312
|
+
`);
|
|
2313
|
+
for (const step of result.manualSteps) {
|
|
2314
|
+
io.stdout.write(` - ${step}
|
|
2315
|
+
`);
|
|
2316
|
+
}
|
|
2317
|
+
} else {
|
|
2318
|
+
io.stderr.write(`${result.error}
|
|
2319
|
+
`);
|
|
2320
|
+
}
|
|
2321
|
+
return result.ok ? 0 : 1;
|
|
2322
|
+
})();
|
|
2323
|
+
}
|
|
1408
2324
|
if (command === "build") {
|
|
1409
|
-
const scriptKind = selectScriptKind(detectScriptKinds(cwd));
|
|
1410
2325
|
const reportBuild = (written, materializedDir) => {
|
|
1411
2326
|
ensureMaterializedReference(cwd, materializedDir);
|
|
1412
2327
|
if (json) {
|
|
@@ -1415,7 +2330,6 @@ function dispatch(argv, io, internals) {
|
|
|
1415
2330
|
written,
|
|
1416
2331
|
defoldVersion: resolvedVersion,
|
|
1417
2332
|
apiSurface,
|
|
1418
|
-
scriptKind,
|
|
1419
2333
|
materializedSurface: materializedDir
|
|
1420
2334
|
}));
|
|
1421
2335
|
} else {
|
|
@@ -1443,7 +2357,6 @@ function dispatch(argv, io, internals) {
|
|
|
1443
2357
|
const { materializedDir } = await materializeRefDocSurface({
|
|
1444
2358
|
cwd,
|
|
1445
2359
|
surfaceId,
|
|
1446
|
-
scriptKind,
|
|
1447
2360
|
...internals?.resolveOpts ? { resolveOpts: internals.resolveOpts } : {},
|
|
1448
2361
|
...internals?.refDocRegistry ? { registry: internals.refDocRegistry } : {}
|
|
1449
2362
|
});
|
|
@@ -1463,8 +2376,7 @@ function dispatch(argv, io, internals) {
|
|
|
1463
2376
|
const { materializedDir } = materializeApiSurface({
|
|
1464
2377
|
cwd,
|
|
1465
2378
|
surface,
|
|
1466
|
-
sourceGeneratedDir
|
|
1467
|
-
scriptKind
|
|
2379
|
+
sourceGeneratedDir
|
|
1468
2380
|
});
|
|
1469
2381
|
return reportBuild(written, materializedDir);
|
|
1470
2382
|
} catch (err) {
|
|
@@ -1478,12 +2390,10 @@ function dispatch(argv, io, internals) {
|
|
|
1478
2390
|
if (!isRefDocSurface) {
|
|
1479
2391
|
const sourceGeneratedDir = internals?.sourceGeneratedDir ?? resolveCurrentSurfaceGeneratedDir();
|
|
1480
2392
|
syncSurface = () => {
|
|
1481
|
-
const scriptKind = selectScriptKind(detectScriptKinds(cwd));
|
|
1482
2393
|
const { materializedDir } = materializeApiSurface({
|
|
1483
2394
|
cwd,
|
|
1484
2395
|
surface,
|
|
1485
|
-
sourceGeneratedDir
|
|
1486
|
-
scriptKind
|
|
2396
|
+
sourceGeneratedDir
|
|
1487
2397
|
});
|
|
1488
2398
|
ensureMaterializedReference(cwd, materializedDir);
|
|
1489
2399
|
};
|
|
@@ -1497,7 +2407,8 @@ function dispatch(argv, io, internals) {
|
|
|
1497
2407
|
...internals?.watcherFactory ? { watcherFactory: internals.watcherFactory } : {},
|
|
1498
2408
|
...internals?.debounceMs !== undefined ? { debounceMs: internals.debounceMs } : {},
|
|
1499
2409
|
...syncSurface ? { syncSurface } : {},
|
|
1500
|
-
...componentWatcherFactory ? { componentWatcherFactory } : {}
|
|
2410
|
+
...componentWatcherFactory ? { componentWatcherFactory } : {},
|
|
2411
|
+
...json ? { json: true } : {}
|
|
1501
2412
|
};
|
|
1502
2413
|
const handle = runWatch(watchOpts);
|
|
1503
2414
|
if (internals) {
|
|
@@ -1514,12 +2425,10 @@ function dispatch(argv, io, internals) {
|
|
|
1514
2425
|
};
|
|
1515
2426
|
if (isRefDocSurface) {
|
|
1516
2427
|
const surfaceId = surface.surfaceId;
|
|
1517
|
-
const scriptKind = selectScriptKind(detectScriptKinds(cwd));
|
|
1518
2428
|
return (async () => {
|
|
1519
2429
|
const { materializedDir } = await materializeRefDocSurface({
|
|
1520
2430
|
cwd,
|
|
1521
2431
|
surfaceId,
|
|
1522
|
-
scriptKind,
|
|
1523
2432
|
...internals?.resolveOpts ? { resolveOpts: internals.resolveOpts } : {},
|
|
1524
2433
|
...internals?.refDocRegistry ? { registry: internals.refDocRegistry } : {}
|
|
1525
2434
|
});
|
|
@@ -1529,6 +2438,116 @@ function dispatch(argv, io, internals) {
|
|
|
1529
2438
|
}
|
|
1530
2439
|
return launchWatch();
|
|
1531
2440
|
}
|
|
2441
|
+
if (command === "wall") {
|
|
2442
|
+
const wallCwd = internals?.cwd ?? process.cwd();
|
|
2443
|
+
const dirs = rest;
|
|
2444
|
+
const toJsonWall = (w) => ({
|
|
2445
|
+
dir: w.dir,
|
|
2446
|
+
kind: w.kind
|
|
2447
|
+
});
|
|
2448
|
+
const reportWalls = (walls) => {
|
|
2449
|
+
if (json) {
|
|
2450
|
+
io.stdout.write(renderResult({ command: "wall", directoryWalls: walls.map(toJsonWall) }));
|
|
2451
|
+
} else if (walls.length === 0) {
|
|
2452
|
+
io.stdout.write(`defold-typescript wall: no directories walled
|
|
2453
|
+
`);
|
|
2454
|
+
} else {
|
|
2455
|
+
io.stdout.write(`defold-typescript wall: walled ${walls.map((w) => w.dir).join(", ")}
|
|
2456
|
+
`);
|
|
2457
|
+
}
|
|
2458
|
+
};
|
|
2459
|
+
if (wallList) {
|
|
2460
|
+
const current = currentWalledDirs(wallCwd);
|
|
2461
|
+
const eligible = eligibleWalls(wallCwd);
|
|
2462
|
+
const currentWalls = eligible.filter((w) => current.includes(w.dir));
|
|
2463
|
+
if (json) {
|
|
2464
|
+
io.stdout.write(renderResult({
|
|
2465
|
+
command: "wall",
|
|
2466
|
+
directoryWalls: currentWalls.map(toJsonWall),
|
|
2467
|
+
eligible: eligible.map(toJsonWall)
|
|
2468
|
+
}));
|
|
2469
|
+
} else {
|
|
2470
|
+
io.stdout.write(`defold-typescript wall: walled [${current.join(", ")}]; eligible [${eligible.map((w) => w.dir).join(", ")}]
|
|
2471
|
+
`);
|
|
2472
|
+
}
|
|
2473
|
+
return 0;
|
|
2474
|
+
}
|
|
2475
|
+
if (dirs.length > 0) {
|
|
2476
|
+
try {
|
|
2477
|
+
const current = currentWalledDirs(wallCwd);
|
|
2478
|
+
const desired = wallRemove ? current.filter((d) => !dirs.includes(d)) : [...current, ...dirs];
|
|
2479
|
+
reportWalls(applyWallSelection(wallCwd, desired));
|
|
2480
|
+
return 0;
|
|
2481
|
+
} catch (err) {
|
|
2482
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2483
|
+
if (json) {
|
|
2484
|
+
io.stdout.write(renderResult({ command: "wall", error: message }));
|
|
2485
|
+
} else {
|
|
2486
|
+
io.stderr.write(`${message}
|
|
2487
|
+
`);
|
|
2488
|
+
}
|
|
2489
|
+
return 1;
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
const interactive = !json && (internals?.isTty ?? Boolean(process.stdout.isTTY));
|
|
2493
|
+
if (!interactive) {
|
|
2494
|
+
io.stderr.write(`defold-typescript wall: no directory given; pass <dir> or run in a terminal for the interactive menu
|
|
2495
|
+
`);
|
|
2496
|
+
return 1;
|
|
2497
|
+
}
|
|
2498
|
+
return (async () => {
|
|
2499
|
+
try {
|
|
2500
|
+
reportWalls(await runWallInteractive(wallCwd, internals?.wallCheckbox ? { checkbox: internals.wallCheckbox } : {}));
|
|
2501
|
+
return 0;
|
|
2502
|
+
} catch (err) {
|
|
2503
|
+
io.stderr.write(`${err instanceof Error ? err.message : String(err)}
|
|
2504
|
+
`);
|
|
2505
|
+
return 1;
|
|
2506
|
+
}
|
|
2507
|
+
})();
|
|
2508
|
+
}
|
|
2509
|
+
if (command === "defold") {
|
|
2510
|
+
const subcommand = rest[0];
|
|
2511
|
+
const defoldCwd = rest[1] ? path15.resolve(rest[1]) : process.cwd();
|
|
2512
|
+
if (!isDefoldSubcommand(subcommand)) {
|
|
2513
|
+
io.stderr.write(DEFOLD_USAGE);
|
|
2514
|
+
return 1;
|
|
2515
|
+
}
|
|
2516
|
+
const javaOverride = javaFlag ?? process.env.DEFOLD_JAVA;
|
|
2517
|
+
const defoldIo = { ...defaultDefoldIo(), ...internals?.defoldIo };
|
|
2518
|
+
return (async () => {
|
|
2519
|
+
try {
|
|
2520
|
+
const result = await runDefoldCommand({
|
|
2521
|
+
cwd: defoldCwd,
|
|
2522
|
+
subcommand,
|
|
2523
|
+
...javaOverride !== undefined ? { java: javaOverride } : {},
|
|
2524
|
+
...buildServerFlag !== undefined ? { buildServer: buildServerFlag } : {},
|
|
2525
|
+
io: defoldIo
|
|
2526
|
+
});
|
|
2527
|
+
if (json) {
|
|
2528
|
+
io.stdout.write(renderResult(result.ok ? { command: "defold", subcommand: result.subcommand, exitCode: result.exitCode } : {
|
|
2529
|
+
command: "defold",
|
|
2530
|
+
subcommand: result.subcommand,
|
|
2531
|
+
exitCode: result.exitCode,
|
|
2532
|
+
error: `bob ${result.subcommand} exited with code ${result.exitCode}`
|
|
2533
|
+
}));
|
|
2534
|
+
} else if (!result.ok) {
|
|
2535
|
+
io.stderr.write(`defold-typescript defold ${result.subcommand}: bob exited with code ${result.exitCode}
|
|
2536
|
+
`);
|
|
2537
|
+
}
|
|
2538
|
+
return result.exitCode;
|
|
2539
|
+
} catch (err) {
|
|
2540
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2541
|
+
if (json) {
|
|
2542
|
+
io.stdout.write(renderResult({ command: "defold", subcommand, error: message }));
|
|
2543
|
+
} else {
|
|
2544
|
+
io.stderr.write(`${message}
|
|
2545
|
+
`);
|
|
2546
|
+
}
|
|
2547
|
+
return 1;
|
|
2548
|
+
}
|
|
2549
|
+
})();
|
|
2550
|
+
}
|
|
1532
2551
|
io.stderr.write(USAGE);
|
|
1533
2552
|
return 1;
|
|
1534
2553
|
}
|