@defold-typescript/cli 0.1.0 → 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 king8fisher (Anton Veretennikov)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # defold-typescript
2
+
3
+ The end-user CLI for scaffolding and building [Defold](https://defold.com/)
4
+ projects written in TypeScript, transpiled to Lua.
5
+
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
10
+ ```
11
+
12
+ The package name is scoped (`@defold-typescript/cli`); `bunx defold-typescript`
13
+ without the scope resolves a different, nonexistent package. Install it to get
14
+ the shorter `defold-typescript` binary on your `PATH` and pin the version:
15
+
16
+ ```sh
17
+ npm i -D @defold-typescript/cli # then: bunx defold-typescript <command>
18
+ ```
19
+
20
+ See the repository [README](https://github.com/king8fisher/defold-typescript#readme)
21
+ and [`docs/guide/`](https://github.com/king8fisher/defold-typescript/tree/main/docs/guide)
22
+ for the full workflow.
23
+
24
+ ## License
25
+
26
+ MIT
package/dist/bin.js CHANGED
@@ -94,6 +94,10 @@ var PROJECT_BUCKET = "<project>";
94
94
  function toPosix(p, sep2 = path2.sep) {
95
95
  return p.split(sep2).join("/");
96
96
  }
97
+ var TRANSPILER_SOURCE_RE = /\.(ts|tsx|cts|mts)$/;
98
+ function isTranspilerSource(rel) {
99
+ return TRANSPILER_SOURCE_RE.test(toPosix(rel));
100
+ }
97
101
  function readBuildConfig(cwd) {
98
102
  const tsconfigPath = path2.join(cwd, "tsconfig.json");
99
103
  let raw;
@@ -208,6 +212,7 @@ function runBuild(opts) {
208
212
  // src/init.ts
209
213
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
210
214
  import * as path5 from "node:path";
215
+ import { fileURLToPath } from "node:url";
211
216
 
212
217
  // src/script-kind.ts
213
218
  var DEFAULT_TYPES_ENTRYPOINT = "@defold-typescript/types";
@@ -332,9 +337,17 @@ embedded_instances {
332
337
  scale3 { x: 1.0 y: 1.0 z: 1.0 }
333
338
  }
334
339
  `;
340
+ function typesVersionSpec() {
341
+ try {
342
+ const here = path5.dirname(fileURLToPath(import.meta.url));
343
+ const pkg = JSON.parse(readFileSync4(path5.join(here, "..", "package.json"), "utf8"));
344
+ return pkg.version ? `^${pkg.version}` : "latest";
345
+ } catch {
346
+ return "latest";
347
+ }
348
+ }
335
349
  var SCAFFOLD_DEV_DEPS = {
336
- "@defold-typescript/transpiler": "workspace:*",
337
- "@defold-typescript/types": "workspace:*",
350
+ "@defold-typescript/types": typesVersionSpec(),
338
351
  "@biomejs/biome": "^2.2.0"
339
352
  };
340
353
  function writeJson(filePath, value) {
@@ -578,7 +591,7 @@ async function materializeRefDocSurface(opts) {
578
591
  }
579
592
  const registry = opts.registry ?? loadApiTargetsRegistry();
580
593
  const target = registry.find((t) => t.id === surfaceId);
581
- if (!target || target.source?.kind !== "ref-doc") {
594
+ if (target?.source?.kind !== "ref-doc") {
582
595
  return { materializedDir: null, active: null };
583
596
  }
584
597
  const excludeModules = [...excludedModulesForKind(opts.scriptKind ?? null)];
@@ -651,20 +664,22 @@ function createBuildSession(opts) {
651
664
  return writeOutputs(result, sources);
652
665
  }
653
666
  function applyEvents(changed, removed) {
667
+ const sourceChanged = changed.filter(isTranspilerSource);
668
+ const sourceRemoved = removed.filter(isTranspilerSource);
654
669
  const changes = {};
655
- for (const rel of changed) {
670
+ for (const rel of sourceChanged) {
656
671
  changes[rel] = readFileSync6(path7.join(cwd, rel), "utf8");
657
672
  }
658
- for (const rel of removed) {
673
+ for (const rel of sourceRemoved) {
659
674
  changes[rel] = null;
660
675
  }
661
676
  const result = session.update(changes);
662
- for (const rel of removed) {
677
+ for (const rel of sourceRemoved) {
663
678
  const luaAbs = path7.join(cwd, computeLuaRel(rel, config));
664
679
  rmSync2(luaAbs, { force: true });
665
680
  rmSync2(`${luaAbs}.map`, { force: true });
666
681
  }
667
- return writeOutputs(result, changed);
682
+ return writeOutputs(result, sourceChanged);
668
683
  }
669
684
  return { buildAll, applyEvents };
670
685
  }
@@ -756,9 +771,10 @@ function runWatch(opts) {
756
771
  function onEvent(e) {
757
772
  if (stopped)
758
773
  return;
774
+ if (!e.path || !isTranspilerSource(e.path))
775
+ return;
759
776
  rebuildBusy = true;
760
- if (e.path)
761
- pending.add(e.path);
777
+ pending.add(e.path);
762
778
  if (scheduled)
763
779
  clearTimeout(scheduled);
764
780
  scheduled = setTimeout(rebuild, debounceMs);
@@ -4,6 +4,7 @@ export interface BuildConfig {
4
4
  readonly include: string[];
5
5
  }
6
6
  export declare function toPosix(p: string, sep?: string): string;
7
+ export declare function isTranspilerSource(rel: string): boolean;
7
8
  export declare function readBuildConfig(cwd: string): BuildConfig;
8
9
  export declare function computeLuaRel(rel: string, config: BuildConfig): string;
9
10
  export declare function collectFailures(diagnostics: readonly TranspileDiagnostic[]): Map<string, string[]>;
package/dist/index.js CHANGED
@@ -13,6 +13,10 @@ var PROJECT_BUCKET = "<project>";
13
13
  function toPosix(p, sep2 = path.sep) {
14
14
  return p.split(sep2).join("/");
15
15
  }
16
+ var TRANSPILER_SOURCE_RE = /\.(ts|tsx|cts|mts)$/;
17
+ function isTranspilerSource(rel) {
18
+ return TRANSPILER_SOURCE_RE.test(toPosix(rel));
19
+ }
16
20
  function readBuildConfig(cwd) {
17
21
  const tsconfigPath = path.join(cwd, "tsconfig.json");
18
22
  let raw;
@@ -129,20 +133,22 @@ function createBuildSession(opts) {
129
133
  return writeOutputs(result, sources);
130
134
  }
131
135
  function applyEvents(changed, removed) {
136
+ const sourceChanged = changed.filter(isTranspilerSource);
137
+ const sourceRemoved = removed.filter(isTranspilerSource);
132
138
  const changes = {};
133
- for (const rel of changed) {
139
+ for (const rel of sourceChanged) {
134
140
  changes[rel] = readFileSync2(path3.join(cwd, rel), "utf8");
135
141
  }
136
- for (const rel of removed) {
142
+ for (const rel of sourceRemoved) {
137
143
  changes[rel] = null;
138
144
  }
139
145
  const result = session.update(changes);
140
- for (const rel of removed) {
146
+ for (const rel of sourceRemoved) {
141
147
  const luaAbs = path3.join(cwd, computeLuaRel(rel, config));
142
148
  rmSync(luaAbs, { force: true });
143
149
  rmSync(`${luaAbs}.map`, { force: true });
144
150
  }
145
- return writeOutputs(result, changed);
151
+ return writeOutputs(result, sourceChanged);
146
152
  }
147
153
  return { buildAll, applyEvents };
148
154
  }
@@ -270,6 +276,7 @@ function runBuild(opts) {
270
276
  // src/init.ts
271
277
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "node:fs";
272
278
  import * as path6 from "node:path";
279
+ import { fileURLToPath } from "node:url";
273
280
 
274
281
  // src/script-kind.ts
275
282
  var DEFAULT_TYPES_ENTRYPOINT = "@defold-typescript/types";
@@ -394,9 +401,17 @@ embedded_instances {
394
401
  scale3 { x: 1.0 y: 1.0 z: 1.0 }
395
402
  }
396
403
  `;
404
+ function typesVersionSpec() {
405
+ try {
406
+ const here = path6.dirname(fileURLToPath(import.meta.url));
407
+ const pkg = JSON.parse(readFileSync5(path6.join(here, "..", "package.json"), "utf8"));
408
+ return pkg.version ? `^${pkg.version}` : "latest";
409
+ } catch {
410
+ return "latest";
411
+ }
412
+ }
397
413
  var SCAFFOLD_DEV_DEPS = {
398
- "@defold-typescript/transpiler": "workspace:*",
399
- "@defold-typescript/types": "workspace:*",
414
+ "@defold-typescript/types": typesVersionSpec(),
400
415
  "@biomejs/biome": "^2.2.0"
401
416
  };
402
417
  function writeJson(filePath, value) {
@@ -640,7 +655,7 @@ async function materializeRefDocSurface(opts) {
640
655
  }
641
656
  const registry = opts.registry ?? loadApiTargetsRegistry();
642
657
  const target = registry.find((t) => t.id === surfaceId);
643
- if (!target || target.source?.kind !== "ref-doc") {
658
+ if (target?.source?.kind !== "ref-doc") {
644
659
  return { materializedDir: null, active: null };
645
660
  }
646
661
  const excludeModules = [...excludedModulesForKind(opts.scriptKind ?? null)];
@@ -751,9 +766,10 @@ function runWatch(opts) {
751
766
  function onEvent(e) {
752
767
  if (stopped)
753
768
  return;
769
+ if (!e.path || !isTranspilerSource(e.path))
770
+ return;
754
771
  rebuildBusy = true;
755
- if (e.path)
756
- pending.add(e.path);
772
+ pending.add(e.path);
757
773
  if (scheduled)
758
774
  clearTimeout(scheduled);
759
775
  scheduled = setTimeout(rebuild, debounceMs);
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@defold-typescript/cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "End-user CLI for scaffolding and building Defold projects written in TypeScript.",
5
+ "license": "MIT",
5
6
  "type": "module",
6
7
  "main": "./dist/index.js",
7
8
  "types": "./dist/index.d.ts",
@@ -30,7 +31,7 @@
30
31
  "test": "bun test"
31
32
  },
32
33
  "dependencies": {
33
- "@defold-typescript/transpiler": "0.1.0",
34
- "@defold-typescript/types": "0.1.0"
34
+ "@defold-typescript/transpiler": "0.2.0",
35
+ "@defold-typescript/types": "0.2.0"
35
36
  }
36
37
  }
@@ -21,6 +21,12 @@ export function toPosix(p: string, sep: string = path.sep): string {
21
21
  return p.split(sep).join("/");
22
22
  }
23
23
 
24
+ const TRANSPILER_SOURCE_RE = /\.(ts|tsx|cts|mts)$/;
25
+
26
+ export function isTranspilerSource(rel: string): boolean {
27
+ return TRANSPILER_SOURCE_RE.test(toPosix(rel));
28
+ }
29
+
24
30
  export function readBuildConfig(cwd: string): BuildConfig {
25
31
  const tsconfigPath = path.join(cwd, "tsconfig.json");
26
32
  let raw: string;
@@ -9,6 +9,7 @@ import {
9
9
  type BuildConfig,
10
10
  collectFailures,
11
11
  computeLuaRel,
12
+ isTranspilerSource,
12
13
  readBuildConfig,
13
14
  throwIfFailures,
14
15
  toPosix,
@@ -75,23 +76,25 @@ export function createBuildSession(opts: CreateBuildSessionOptions): BuildSessio
75
76
  }
76
77
 
77
78
  function applyEvents(changed: string[], removed: string[]): BuildResult {
79
+ const sourceChanged = changed.filter(isTranspilerSource);
80
+ const sourceRemoved = removed.filter(isTranspilerSource);
78
81
  const changes: Record<string, string | null> = {};
79
- for (const rel of changed) {
82
+ for (const rel of sourceChanged) {
80
83
  changes[rel] = readFileSync(path.join(cwd, rel), "utf8");
81
84
  }
82
- for (const rel of removed) {
85
+ for (const rel of sourceRemoved) {
83
86
  changes[rel] = null;
84
87
  }
85
88
 
86
89
  const result = session.update(changes);
87
90
 
88
- for (const rel of removed) {
91
+ for (const rel of sourceRemoved) {
89
92
  const luaAbs = path.join(cwd, computeLuaRel(rel, config));
90
93
  rmSync(luaAbs, { force: true });
91
94
  rmSync(`${luaAbs}.map`, { force: true });
92
95
  }
93
96
 
94
- return writeOutputs(result, changed);
97
+ return writeOutputs(result, sourceChanged);
95
98
  }
96
99
 
97
100
  return { buildAll, applyEvents };
package/src/init.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import * as path from "node:path";
3
+ import { fileURLToPath } from "node:url";
3
4
  import { CURRENT_STABLE_DEFOLD_VERSION } from "./defold-version";
4
5
  import {
5
6
  detectScriptKinds,
@@ -103,9 +104,27 @@ interface PackageJson {
103
104
  [key: string]: unknown;
104
105
  }
105
106
 
107
+ function typesVersionSpec(): string {
108
+ try {
109
+ // Anchor on the module URL, not `import.meta.dir` — the latter is a
110
+ // Bun-only property and is undefined when the bundled CLI runs under node
111
+ // (the `npx` path), which would silently fall back to "latest".
112
+ const here = path.dirname(fileURLToPath(import.meta.url));
113
+ const pkg = JSON.parse(readFileSync(path.join(here, "..", "package.json"), "utf8")) as {
114
+ version?: string;
115
+ };
116
+ return pkg.version ? `^${pkg.version}` : "latest";
117
+ } catch {
118
+ return "latest";
119
+ }
120
+ }
121
+
122
+ // Only @defold-typescript/types ships into the consumer (type-only, for the
123
+ // editor). The transpiler is a dependency of the CLI itself, pulled in when the
124
+ // user runs `build`/`watch`; the scaffold must not duplicate it. Pin types to
125
+ // this CLI's own version so the coordinated-release set stays in lockstep.
106
126
  const SCAFFOLD_DEV_DEPS: Record<string, string> = {
107
- "@defold-typescript/transpiler": "workspace:*",
108
- "@defold-typescript/types": "workspace:*",
127
+ "@defold-typescript/types": typesVersionSpec(),
109
128
  "@biomejs/biome": "^2.2.0",
110
129
  };
111
130
 
@@ -198,7 +198,7 @@ export async function materializeRefDocSurface(
198
198
  }
199
199
  const registry = opts.registry ?? loadApiTargetsRegistry();
200
200
  const target = registry.find((t) => t.id === surfaceId);
201
- if (!target || target.source?.kind !== "ref-doc") {
201
+ if (target?.source?.kind !== "ref-doc") {
202
202
  return { materializedDir: null, active: null };
203
203
  }
204
204
  const excludeModules = [...excludedModulesForKind(opts.scriptKind ?? null)];
package/src/watch.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { existsSync, watch as fsWatch } from "node:fs";
2
2
  import * as path from "node:path";
3
- import { toPosix } from "./build-output";
3
+ import { isTranspilerSource, toPosix } from "./build-output";
4
4
  import { type BuildSession, createBuildSession } from "./build-session";
5
5
  import { isComponentPath, isSkipped } from "./script-kind";
6
6
 
@@ -123,8 +123,9 @@ export function runWatch(opts: RunWatchOptions): RunWatchHandle {
123
123
 
124
124
  function onEvent(e: WatchEvent): void {
125
125
  if (stopped) return;
126
+ if (!e.path || !isTranspilerSource(e.path)) return;
126
127
  rebuildBusy = true;
127
- if (e.path) pending.add(e.path);
128
+ pending.add(e.path);
128
129
  if (scheduled) clearTimeout(scheduled);
129
130
  scheduled = setTimeout(rebuild, debounceMs);
130
131
  }