@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 +21 -0
- package/README.md +26 -0
- package/dist/bin.js +25 -9
- package/dist/build-output.d.ts +1 -0
- package/dist/index.js +25 -9
- package/package.json +4 -3
- package/src/build-output.ts +6 -0
- package/src/build-session.ts +7 -4
- package/src/init.ts +21 -2
- package/src/materialize.ts +1 -1
- package/src/watch.ts +3 -2
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/
|
|
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 (
|
|
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
|
|
670
|
+
for (const rel of sourceChanged) {
|
|
656
671
|
changes[rel] = readFileSync6(path7.join(cwd, rel), "utf8");
|
|
657
672
|
}
|
|
658
|
-
for (const rel of
|
|
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
|
|
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,
|
|
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
|
-
|
|
761
|
-
pending.add(e.path);
|
|
777
|
+
pending.add(e.path);
|
|
762
778
|
if (scheduled)
|
|
763
779
|
clearTimeout(scheduled);
|
|
764
780
|
scheduled = setTimeout(rebuild, debounceMs);
|
package/dist/build-output.d.ts
CHANGED
|
@@ -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
|
|
139
|
+
for (const rel of sourceChanged) {
|
|
134
140
|
changes[rel] = readFileSync2(path3.join(cwd, rel), "utf8");
|
|
135
141
|
}
|
|
136
|
-
for (const rel of
|
|
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
|
|
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,
|
|
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/
|
|
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 (
|
|
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
|
-
|
|
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.
|
|
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.
|
|
34
|
-
"@defold-typescript/types": "0.
|
|
34
|
+
"@defold-typescript/transpiler": "0.2.0",
|
|
35
|
+
"@defold-typescript/types": "0.2.0"
|
|
35
36
|
}
|
|
36
37
|
}
|
package/src/build-output.ts
CHANGED
|
@@ -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;
|
package/src/build-session.ts
CHANGED
|
@@ -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
|
|
82
|
+
for (const rel of sourceChanged) {
|
|
80
83
|
changes[rel] = readFileSync(path.join(cwd, rel), "utf8");
|
|
81
84
|
}
|
|
82
|
-
for (const rel of
|
|
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
|
|
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,
|
|
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/
|
|
108
|
-
"@defold-typescript/types": "workspace:*",
|
|
127
|
+
"@defold-typescript/types": typesVersionSpec(),
|
|
109
128
|
"@biomejs/biome": "^2.2.0",
|
|
110
129
|
};
|
|
111
130
|
|
package/src/materialize.ts
CHANGED
|
@@ -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 (
|
|
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
|
-
|
|
128
|
+
pending.add(e.path);
|
|
128
129
|
if (scheduled) clearTimeout(scheduled);
|
|
129
130
|
scheduled = setTimeout(rebuild, debounceMs);
|
|
130
131
|
}
|