@defold-typescript/cli 0.5.5 → 0.6.0

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