@defold-typescript/cli 0.5.1 → 0.5.3

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 CHANGED
@@ -4,11 +4,16 @@ The end-user CLI for scaffolding and building [Defold](https://defold.com/)
4
4
  projects written in TypeScript, transpiled to Lua.
5
5
 
6
6
  ```sh
7
- bunx @defold-typescript/cli init # add TypeScript to a Defold project, or scaffold a new one
8
- bunx @defold-typescript/cli build # transpile src/ to Lua alongside your Defold sources
9
- bunx @defold-typescript/cli watch # rebuild on change
7
+ bunx @defold-typescript/cli@latest init # add TypeScript to a Defold project, or scaffold a new one
8
+ bun install # install the scaffolded devDependencies (types, biome)
9
+ bunx @defold-typescript/cli build # transpile src/ to Lua alongside your Defold sources
10
+ bunx @defold-typescript/cli watch # rebuild on change
10
11
  ```
11
12
 
13
+ Scaffold with the `@latest` tag — `init` writes your `@defold-typescript/types`
14
+ version pin, so a stale `bunx` cache would pin an older release. Run `bun install`
15
+ once after `init` to put the scaffolded dev dependencies on disk.
16
+
12
17
  The package name is scoped (`@defold-typescript/cli`); `bunx defold-typescript`
13
18
  without the scope resolves a different, nonexistent package. Install it to get
14
19
  the shorter `defold-typescript` binary on your `PATH` and pin the version:
package/dist/bin.js CHANGED
@@ -231,6 +231,169 @@ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFi
231
231
  import * as path6 from "node:path";
232
232
  import { fileURLToPath as fileURLToPath2 } from "node:url";
233
233
 
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
+ // src/mise-scaffold.ts
345
+ var MANAGED_MARKER = "# managed by @defold-typescript";
346
+ var MISE_TASKS_TOML = `${MANAGED_MARKER}
347
+ [tasks."defold-typescript:build"]
348
+ description = "Build the TypeScript sources with the installed defold-typescript CLI"
349
+ run = "bunx --no-install defold-typescript build"
350
+
351
+ ${MANAGED_MARKER}
352
+ [tasks."defold-typescript:watch"]
353
+ description = "Watch and rebuild the TypeScript sources with the installed defold-typescript CLI"
354
+ run = "bunx --no-install defold-typescript watch"
355
+
356
+ ${MANAGED_MARKER}
357
+ [tasks."defold-typescript:upgrade"]
358
+ 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"]
360
+ `;
361
+ function stripManagedBlocks(text) {
362
+ const lines = text.split(`
363
+ `);
364
+ const out = [];
365
+ let i = 0;
366
+ while (i < lines.length) {
367
+ const line = lines[i] ?? "";
368
+ if (line.trim() === MANAGED_MARKER) {
369
+ i += 1;
370
+ while (i < lines.length && (lines[i] ?? "").trim() !== "") {
371
+ i += 1;
372
+ }
373
+ if (i < lines.length) {
374
+ i += 1;
375
+ }
376
+ continue;
377
+ }
378
+ out.push(line);
379
+ i += 1;
380
+ }
381
+ return out.join(`
382
+ `);
383
+ }
384
+ function mergeMiseToml(existing) {
385
+ if (existing === undefined) {
386
+ return MISE_TASKS_TOML;
387
+ }
388
+ const userContent = stripManagedBlocks(existing).replace(/\s*$/, "");
389
+ if (userContent === "") {
390
+ return MISE_TASKS_TOML;
391
+ }
392
+ return `${userContent}
393
+
394
+ ${MISE_TASKS_TOML}`;
395
+ }
396
+
234
397
  // src/script-kind.ts
235
398
  var DEFAULT_TYPES_ENTRYPOINT = "@defold-typescript/types";
236
399
  var KIND_BY_EXT = {
@@ -342,12 +505,118 @@ var BIOME_JSON_CONTENT = {
342
505
  }
343
506
  };
344
507
  var VSCODE_EXTENSIONS_CONTENT = {
345
- recommendations: ["sumneko.lua", "astronachos.defold"],
508
+ recommendations: ["sumneko.lua", "astronachos.defold", "tomblind.local-lua-debugger-vscode"],
346
509
  unwantedRecommendations: ["johnnymorganz.luau-lsp"]
347
510
  };
348
511
  var VSCODE_SETTINGS_CONTENT = {
349
512
  "Lua.workspace.ignoreDir": ["src"]
350
513
  };
514
+ var HOOK_COMMENTS = {
515
+ init: "Initialize the component and return its state.",
516
+ update: "Update the component every frame; `dt` is the time step.",
517
+ fixed_update: "Update at the fixed physics time step.",
518
+ late_update: "Update every frame after `update`.",
519
+ on_message: "Handle an incoming message.",
520
+ on_input: "Handle input once input focus is acquired.",
521
+ final: "Clean up when the component is deleted.",
522
+ on_reload: "React to a hot reload of this script."
523
+ };
524
+ var HOOK_SIGNATURES = {
525
+ init: "",
526
+ update: "self, dt",
527
+ fixed_update: "self, dt",
528
+ late_update: "self, dt",
529
+ on_message: "self, message_id, message, sender",
530
+ on_input: "self, action_id, action",
531
+ final: "self",
532
+ on_reload: "self"
533
+ };
534
+ var SNIPPET_HOOK_ORDER = Object.keys(HOOK_SIGNATURES);
535
+ function hookLines(includeOnInput, startTabStop) {
536
+ const lines = [];
537
+ let tabStop = startTabStop;
538
+ for (const hook of SNIPPET_HOOK_ORDER) {
539
+ if (hook === "init") {
540
+ continue;
541
+ }
542
+ if (hook === "on_input" && !includeOnInput) {
543
+ continue;
544
+ }
545
+ lines.push(` // ${HOOK_COMMENTS[hook]}`);
546
+ lines.push(` ${hook}(${HOOK_SIGNATURES[hook]}) {$${tabStop}},`);
547
+ tabStop += 1;
548
+ }
549
+ return lines;
550
+ }
551
+ function inlineSnippetBody(factory, includeOnInput) {
552
+ return [
553
+ `import { ${factory} } from "@defold-typescript/types";`,
554
+ "",
555
+ `export const script = ${factory}({`,
556
+ ` // ${HOOK_COMMENTS.init}`,
557
+ " init() {",
558
+ " return { $0 };",
559
+ " },",
560
+ ...hookLines(includeOnInput, 1),
561
+ "});"
562
+ ];
563
+ }
564
+ function typedSnippetBody(factory, includeOnInput) {
565
+ return [
566
+ `import { ${factory} } from "@defold-typescript/types";`,
567
+ "",
568
+ "type Self = {",
569
+ " // Your script's state goes here.",
570
+ " $1",
571
+ "};",
572
+ "",
573
+ `export const script = ${factory}<Self>({`,
574
+ ` // ${HOOK_COMMENTS.init}`,
575
+ " init(): Self {",
576
+ " return { $0 };",
577
+ " },",
578
+ ...hookLines(includeOnInput, 2),
579
+ "});"
580
+ ];
581
+ }
582
+ var VSCODE_SNIPPETS_CONTENT = {
583
+ "Defold script (inferred self)": {
584
+ scope: "typescript",
585
+ prefix: "defold-script",
586
+ body: inlineSnippetBody("defineScript", true),
587
+ description: "Empty Defold script; state inferred from init's return."
588
+ },
589
+ "Defold script (typed self)": {
590
+ scope: "typescript",
591
+ prefix: "defold-script-typed",
592
+ body: typedSnippetBody("defineScript", true),
593
+ description: "Empty Defold script with an explicit Self type."
594
+ },
595
+ "Defold GUI script (inferred self)": {
596
+ scope: "typescript",
597
+ prefix: "defold-gui",
598
+ body: inlineSnippetBody("defineGuiScript", true),
599
+ description: "Empty Defold GUI script; state inferred from init's return."
600
+ },
601
+ "Defold GUI script (typed self)": {
602
+ scope: "typescript",
603
+ prefix: "defold-gui-typed",
604
+ body: typedSnippetBody("defineGuiScript", true),
605
+ description: "Empty Defold GUI script with an explicit Self type."
606
+ },
607
+ "Defold render script (inferred self)": {
608
+ scope: "typescript",
609
+ prefix: "defold-render",
610
+ body: inlineSnippetBody("defineRenderScript", false),
611
+ description: "Empty Defold render script; state inferred from init's return."
612
+ },
613
+ "Defold render script (typed self)": {
614
+ scope: "typescript",
615
+ prefix: "defold-render-typed",
616
+ body: typedSnippetBody("defineRenderScript", false),
617
+ description: "Empty Defold render script with an explicit Self type."
618
+ }
619
+ };
351
620
  var MAIN_TS_CONTENT = `export function init(): void {
352
621
  const start = vmath.vector3(0, 0, 0);
353
622
  msg.post("main:/hero", "spawn", { start });
@@ -421,6 +690,12 @@ function writeBiome(cwd, written) {
421
690
  writeJson(biomePath, BIOME_JSON_CONTENT);
422
691
  written.push("biome.json");
423
692
  }
693
+ function writeMiseTasks(cwd, written) {
694
+ const misePath = path6.join(cwd, "mise.toml");
695
+ const existing = existsSync2(misePath) ? readFileSync5(misePath, "utf8") : undefined;
696
+ writeFileSync2(misePath, mergeMiseToml(existing));
697
+ written.push("mise.toml");
698
+ }
424
699
  function parseJsonc(text) {
425
700
  let out = "";
426
701
  let inString = false;
@@ -522,6 +797,59 @@ function writeVscodeSettings(cwd, written) {
522
797
  writeJson(filePath, VSCODE_SETTINGS_CONTENT);
523
798
  written.push(".vscode/settings.json");
524
799
  }
800
+ function writeVscodeSnippets(cwd, written) {
801
+ const dir = path6.join(cwd, ".vscode");
802
+ const filePath = path6.join(dir, "defold-typescript.code-snippets");
803
+ if (existsSync2(filePath)) {
804
+ const existing = readVscodeJson(filePath);
805
+ if (existing === null) {
806
+ return;
807
+ }
808
+ for (const [key, snippet] of Object.entries(VSCODE_SNIPPETS_CONTENT)) {
809
+ if (!(key in existing)) {
810
+ existing[key] = snippet;
811
+ }
812
+ }
813
+ writeJson(filePath, existing);
814
+ return;
815
+ }
816
+ mkdirSync2(dir, { recursive: true });
817
+ writeJson(filePath, VSCODE_SNIPPETS_CONTENT);
818
+ written.push(".vscode/defold-typescript.code-snippets");
819
+ }
820
+ function writeVscodeLaunch(cwd, written) {
821
+ const dir = path6.join(cwd, ".vscode");
822
+ const filePath = path6.join(dir, "launch.json");
823
+ const ours = debugLaunchConfig();
824
+ if (existsSync2(filePath)) {
825
+ const existing = readVscodeJson(filePath);
826
+ if (existing === null) {
827
+ return;
828
+ }
829
+ const configs = Array.isArray(existing.configurations) ? [...existing.configurations] : [];
830
+ const names = new Set(configs.map((c) => isJsonObject(c) ? c.name : undefined));
831
+ if (!names.has(ours.name)) {
832
+ configs.push(ours);
833
+ }
834
+ existing.configurations = configs;
835
+ existing.version ??= VSCODE_LAUNCH_CONTENT.version;
836
+ writeJson(filePath, existing);
837
+ return;
838
+ }
839
+ mkdirSync2(dir, { recursive: true });
840
+ writeJson(filePath, VSCODE_LAUNCH_CONTENT);
841
+ written.push(".vscode/launch.json");
842
+ }
843
+ function writeVscodeDebugLauncher(cwd, written) {
844
+ const dir = path6.join(cwd, ".vscode");
845
+ const filePath = path6.join(dir, "defold-debug.ts");
846
+ if (existsSync2(filePath)) {
847
+ return;
848
+ }
849
+ mkdirSync2(dir, { recursive: true });
850
+ writeFileSync2(filePath, DEBUG_LAUNCHER_SOURCE);
851
+ written.push(".vscode/defold-debug.ts");
852
+ }
525
853
  function writeTsSurface(cwd, written, force = false) {
526
854
  mkdirSync2(path6.join(cwd, "src"), { recursive: true });
527
855
  writeFileSync2(path6.join(cwd, "src", "main.ts"), MAIN_TS_CONTENT);
@@ -563,8 +891,12 @@ function writeTsSurface(cwd, written, force = false) {
563
891
  writeGitignore(cwd);
564
892
  written.push(".gitignore");
565
893
  writeBiome(cwd, written);
894
+ writeMiseTasks(cwd, written);
566
895
  writeVscodeExtensions(cwd, written);
567
896
  writeVscodeSettings(cwd, written);
897
+ writeVscodeSnippets(cwd, written);
898
+ writeVscodeLaunch(cwd, written);
899
+ writeVscodeDebugLauncher(cwd, written);
568
900
  return selectScriptKind(kinds);
569
901
  }
570
902
  function runNewProjectInit(cwd, force = false) {
@@ -647,7 +979,7 @@ function materializeApiSurface(opts) {
647
979
  const excluded = excludedModulesForKind(opts.scriptKind ?? null);
648
980
  const sources = listDts(sourceGeneratedDir).filter((file) => file !== "index.d.ts").filter((file) => !excluded.has(file.replace(/\.d\.ts$/, "")));
649
981
  const srcDir = path7.resolve(sourceGeneratedDir, "..", "src");
650
- const overloads = ["msg-overloads.d.ts", "go-overloads.d.ts"].filter((file) => existsSync3(path7.join(srcDir, file)));
982
+ const overloads = ["msg-overloads.d.ts", "message-guard.d.ts", "go-overloads.d.ts"].filter((file) => existsSync3(path7.join(srcDir, file)));
651
983
  const coreTypesSrc = path7.join(srcDir, "core-types.ts");
652
984
  const includeCoreTypes = overloads.length > 0 && existsSync3(coreTypesSrc);
653
985
  const engineGlobalsSrc = path7.join(srcDir, "engine-globals.d.ts");
@@ -722,12 +1054,16 @@ function ensureMaterializedReference(cwd, materializedDir) {
722
1054
  const tsconfigPath = path7.join(cwd, "tsconfig.json");
723
1055
  if (existsSync3(tsconfigPath)) {
724
1056
  const tsconfig = JSON.parse(readFileSync6(tsconfigPath, "utf8"));
725
- tsconfig.compilerOptions = {
726
- ...tsconfig.compilerOptions ?? {},
727
- typeRoots: [MATERIALIZED_ROOT],
728
- types: [surfaceId]
729
- };
730
- writeJson2(tsconfigPath, tsconfig);
1057
+ const current = tsconfig.compilerOptions ?? {};
1058
+ const alreadyRepointed = JSON.stringify(current.typeRoots) === JSON.stringify([MATERIALIZED_ROOT]) && JSON.stringify(current.types) === JSON.stringify([surfaceId]);
1059
+ if (!alreadyRepointed) {
1060
+ tsconfig.compilerOptions = {
1061
+ ...current,
1062
+ typeRoots: [MATERIALIZED_ROOT],
1063
+ types: [surfaceId]
1064
+ };
1065
+ writeJson2(tsconfigPath, tsconfig);
1066
+ }
731
1067
  }
732
1068
  ensureGitignoreLine(cwd, `${MATERIALIZED_ROOT}/`);
733
1069
  }
@@ -0,0 +1,43 @@
1
+ export interface EngineTarget {
2
+ readonly enginePlatform: string;
3
+ readonly buildFolder: string;
4
+ readonly executable: string;
5
+ }
6
+ export declare const DEBUG_LAUNCHER_REL = ".vscode/defold-debug.ts";
7
+ export declare function targetPlatform(platform: NodeJS.Platform): EngineTarget;
8
+ export declare function engineDownloadUrl(sha1: string, enginePlatform: string, executable: string): string;
9
+ export interface ResolveEngineOptions {
10
+ readonly cwd: string;
11
+ readonly target: EngineTarget;
12
+ readonly stockPath: string;
13
+ readonly probe: (candidate: string) => boolean;
14
+ }
15
+ export declare function resolveEnginePath(opts: ResolveEngineOptions): string;
16
+ export declare function debugLaunchConfig(): {
17
+ name: string;
18
+ type: string;
19
+ request: string;
20
+ stopOnEntry: boolean;
21
+ verbose: boolean;
22
+ internalConsoleOptions: string;
23
+ program: {
24
+ command: string;
25
+ };
26
+ args: string[];
27
+ };
28
+ export declare const VSCODE_LAUNCH_CONTENT: {
29
+ version: string;
30
+ configurations: {
31
+ name: string;
32
+ type: string;
33
+ request: string;
34
+ stopOnEntry: boolean;
35
+ verbose: boolean;
36
+ internalConsoleOptions: string;
37
+ program: {
38
+ command: string;
39
+ };
40
+ args: string[];
41
+ }[];
42
+ };
43
+ export declare const DEBUG_LAUNCHER_SOURCE: string;