@defold-typescript/cli 0.1.0 → 0.3.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 +110 -63
- package/dist/build-output.d.ts +1 -0
- package/dist/cli-version.d.ts +1 -0
- package/dist/dispatch.d.ts +1 -0
- package/dist/index.js +105 -58
- package/package.json +4 -3
- package/src/build-output.ts +6 -0
- package/src/build-session.ts +7 -4
- package/src/cli-version.ts +26 -0
- package/src/dispatch.ts +13 -0
- package/src/init.ts +36 -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
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/dispatch.ts
|
|
4
|
-
import { existsSync as existsSync5, readFileSync as
|
|
5
|
-
import * as
|
|
4
|
+
import { existsSync as existsSync5, readFileSync as readFileSync8 } from "node:fs";
|
|
5
|
+
import * as path10 from "node:path";
|
|
6
6
|
|
|
7
7
|
// src/api-registry.ts
|
|
8
8
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -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;
|
|
@@ -205,9 +209,27 @@ function runBuild(opts) {
|
|
|
205
209
|
return { written };
|
|
206
210
|
}
|
|
207
211
|
|
|
208
|
-
// src/
|
|
209
|
-
import {
|
|
212
|
+
// src/cli-version.ts
|
|
213
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
210
214
|
import * as path5 from "node:path";
|
|
215
|
+
import { fileURLToPath } from "node:url";
|
|
216
|
+
var VERSION_FALLBACK = "0.0.0";
|
|
217
|
+
function defaultPackageRoot() {
|
|
218
|
+
return path5.join(path5.dirname(fileURLToPath(import.meta.url)), "..");
|
|
219
|
+
}
|
|
220
|
+
function readCliVersion(packageRoot = defaultPackageRoot()) {
|
|
221
|
+
try {
|
|
222
|
+
const pkg = JSON.parse(readFileSync4(path5.join(packageRoot, "package.json"), "utf8"));
|
|
223
|
+
return typeof pkg.version === "string" ? pkg.version : VERSION_FALLBACK;
|
|
224
|
+
} catch {
|
|
225
|
+
return VERSION_FALLBACK;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/init.ts
|
|
230
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "node:fs";
|
|
231
|
+
import * as path6 from "node:path";
|
|
232
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
211
233
|
|
|
212
234
|
// src/script-kind.ts
|
|
213
235
|
var DEFAULT_TYPES_ENTRYPOINT = "@defold-typescript/types";
|
|
@@ -332,19 +354,33 @@ embedded_instances {
|
|
|
332
354
|
scale3 { x: 1.0 y: 1.0 z: 1.0 }
|
|
333
355
|
}
|
|
334
356
|
`;
|
|
357
|
+
function typesVersionSpec() {
|
|
358
|
+
try {
|
|
359
|
+
const here = path6.dirname(fileURLToPath2(import.meta.url));
|
|
360
|
+
const pkg = JSON.parse(readFileSync5(path6.join(here, "..", "package.json"), "utf8"));
|
|
361
|
+
return pkg.version ? `^${pkg.version}` : "latest";
|
|
362
|
+
} catch {
|
|
363
|
+
return "latest";
|
|
364
|
+
}
|
|
365
|
+
}
|
|
335
366
|
var SCAFFOLD_DEV_DEPS = {
|
|
336
|
-
"@defold-typescript/
|
|
337
|
-
"@defold-typescript/types": "workspace:*",
|
|
367
|
+
"@defold-typescript/types": typesVersionSpec(),
|
|
338
368
|
"@biomejs/biome": "^2.2.0"
|
|
339
369
|
};
|
|
370
|
+
function repairManagedDevDeps(devDeps) {
|
|
371
|
+
delete devDeps["@defold-typescript/transpiler"];
|
|
372
|
+
if (devDeps["@defold-typescript/types"]?.startsWith("workspace:")) {
|
|
373
|
+
devDeps["@defold-typescript/types"] = typesVersionSpec();
|
|
374
|
+
}
|
|
375
|
+
}
|
|
340
376
|
function writeJson(filePath, value) {
|
|
341
377
|
writeFileSync2(filePath, `${JSON.stringify(value, null, 2)}
|
|
342
378
|
`);
|
|
343
379
|
}
|
|
344
380
|
function writeGitignore(cwd) {
|
|
345
|
-
const gitignorePath =
|
|
381
|
+
const gitignorePath = path6.join(cwd, ".gitignore");
|
|
346
382
|
if (existsSync2(gitignorePath)) {
|
|
347
|
-
const existing =
|
|
383
|
+
const existing = readFileSync5(gitignorePath, "utf8");
|
|
348
384
|
const present = new Set(existing.split(`
|
|
349
385
|
`).map((line) => line.trim()));
|
|
350
386
|
const missing = GITIGNORE_LINES.filter((line) => !present.has(line));
|
|
@@ -364,7 +400,7 @@ function writeGitignore(cwd) {
|
|
|
364
400
|
}
|
|
365
401
|
}
|
|
366
402
|
function writeBiome(cwd, written) {
|
|
367
|
-
const biomePath =
|
|
403
|
+
const biomePath = path6.join(cwd, "biome.json");
|
|
368
404
|
if (existsSync2(biomePath)) {
|
|
369
405
|
return;
|
|
370
406
|
}
|
|
@@ -372,8 +408,8 @@ function writeBiome(cwd, written) {
|
|
|
372
408
|
written.push("biome.json");
|
|
373
409
|
}
|
|
374
410
|
function writeTsSurface(cwd, written) {
|
|
375
|
-
mkdirSync2(
|
|
376
|
-
writeFileSync2(
|
|
411
|
+
mkdirSync2(path6.join(cwd, "src"), { recursive: true });
|
|
412
|
+
writeFileSync2(path6.join(cwd, "src", "main.ts"), MAIN_TS_CONTENT);
|
|
377
413
|
written.push("src/main.ts");
|
|
378
414
|
const kinds = detectScriptKinds(cwd);
|
|
379
415
|
const tsconfig = {
|
|
@@ -383,23 +419,24 @@ function writeTsSurface(cwd, written) {
|
|
|
383
419
|
},
|
|
384
420
|
include: ["src/**/*.ts"]
|
|
385
421
|
};
|
|
386
|
-
writeJson(
|
|
422
|
+
writeJson(path6.join(cwd, "tsconfig.json"), tsconfig);
|
|
387
423
|
written.push("tsconfig.json");
|
|
388
|
-
const pkgPath =
|
|
424
|
+
const pkgPath = path6.join(cwd, "package.json");
|
|
389
425
|
if (existsSync2(pkgPath)) {
|
|
390
|
-
const existing = JSON.parse(
|
|
426
|
+
const existing = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
391
427
|
const devDeps = { ...existing.devDependencies ?? {} };
|
|
392
428
|
for (const [name, version] of Object.entries(SCAFFOLD_DEV_DEPS)) {
|
|
393
429
|
if (!(name in devDeps)) {
|
|
394
430
|
devDeps[name] = version;
|
|
395
431
|
}
|
|
396
432
|
}
|
|
433
|
+
repairManagedDevDeps(devDeps);
|
|
397
434
|
existing.devDependencies = devDeps;
|
|
398
435
|
existing["defold-typescript"] ??= { "defold-version": CURRENT_STABLE_DEFOLD_VERSION };
|
|
399
436
|
writeJson(pkgPath, existing);
|
|
400
437
|
} else {
|
|
401
438
|
const fresh = {
|
|
402
|
-
name:
|
|
439
|
+
name: path6.basename(cwd),
|
|
403
440
|
version: "0.0.0",
|
|
404
441
|
type: "module",
|
|
405
442
|
devDependencies: { ...SCAFFOLD_DEV_DEPS },
|
|
@@ -420,27 +457,27 @@ function runNewProjectInit(cwd, force = false) {
|
|
|
420
457
|
throw new Error(`defold-typescript init: refusing to synthesize a new Defold project into non-empty directory ${cwd}. Pass --force to proceed.`);
|
|
421
458
|
}
|
|
422
459
|
const written = [];
|
|
423
|
-
writeFileSync2(
|
|
424
|
-
title = ${
|
|
460
|
+
writeFileSync2(path6.join(cwd, "game.project"), `[project]
|
|
461
|
+
title = ${path6.basename(cwd)}
|
|
425
462
|
main_collection = /main/main.collectionc
|
|
426
463
|
`);
|
|
427
464
|
written.push("game.project");
|
|
428
|
-
mkdirSync2(
|
|
429
|
-
writeFileSync2(
|
|
465
|
+
mkdirSync2(path6.join(cwd, "main"), { recursive: true });
|
|
466
|
+
writeFileSync2(path6.join(cwd, "main", "main.collection"), MAIN_COLLECTION_CONTENT);
|
|
430
467
|
written.push("main/main.collection");
|
|
431
|
-
writeFileSync2(
|
|
468
|
+
writeFileSync2(path6.join(cwd, "main", "main.script"), MAIN_SCRIPT_CONTENT);
|
|
432
469
|
written.push("main/main.script");
|
|
433
470
|
const scriptKind = writeTsSurface(cwd, written);
|
|
434
471
|
return { written, scriptKind };
|
|
435
472
|
}
|
|
436
473
|
function runInit(opts) {
|
|
437
474
|
const { cwd, force = false } = opts;
|
|
438
|
-
if (!existsSync2(
|
|
475
|
+
if (!existsSync2(path6.join(cwd, "game.project"))) {
|
|
439
476
|
return runNewProjectInit(cwd, force);
|
|
440
477
|
}
|
|
441
478
|
if (!force) {
|
|
442
479
|
for (const rel of CONFLICTING_TS_CONFIGS) {
|
|
443
|
-
if (existsSync2(
|
|
480
|
+
if (existsSync2(path6.join(cwd, rel))) {
|
|
444
481
|
throw new Error(`defold-typescript init: refusing to overwrite existing TS config: ${rel}. Pass --force to overwrite.`);
|
|
445
482
|
}
|
|
446
483
|
}
|
|
@@ -468,11 +505,11 @@ import {
|
|
|
468
505
|
existsSync as existsSync3,
|
|
469
506
|
mkdirSync as mkdirSync3,
|
|
470
507
|
readdirSync as readdirSync2,
|
|
471
|
-
readFileSync as
|
|
508
|
+
readFileSync as readFileSync6,
|
|
472
509
|
rmSync,
|
|
473
510
|
writeFileSync as writeFileSync3
|
|
474
511
|
} from "node:fs";
|
|
475
|
-
import * as
|
|
512
|
+
import * as path7 from "node:path";
|
|
476
513
|
var MATERIALIZED_ROOT = ".defold-types";
|
|
477
514
|
function writeJson2(filePath, value) {
|
|
478
515
|
writeFileSync3(filePath, `${JSON.stringify(value, null, 2)}
|
|
@@ -487,14 +524,14 @@ function materializeApiSurface(opts) {
|
|
|
487
524
|
return { materializedDir: null, active: null };
|
|
488
525
|
}
|
|
489
526
|
const { surfaceId } = surface;
|
|
490
|
-
const relDir =
|
|
491
|
-
const absDir =
|
|
527
|
+
const relDir = path7.posix.join(MATERIALIZED_ROOT, surfaceId);
|
|
528
|
+
const absDir = path7.join(cwd, MATERIALIZED_ROOT, surfaceId);
|
|
492
529
|
mkdirSync3(absDir, { recursive: true });
|
|
493
530
|
const excluded = excludedModulesForKind(opts.scriptKind ?? null);
|
|
494
531
|
const sources = listDts(sourceGeneratedDir).filter((file) => file !== "index.d.ts").filter((file) => !excluded.has(file.replace(/\.d\.ts$/, "")));
|
|
495
|
-
const srcDir =
|
|
496
|
-
const overloads = ["msg-overloads.d.ts", "go-overloads.d.ts"].filter((file) => existsSync3(
|
|
497
|
-
const coreTypesSrc =
|
|
532
|
+
const srcDir = path7.resolve(sourceGeneratedDir, "..", "src");
|
|
533
|
+
const overloads = ["msg-overloads.d.ts", "go-overloads.d.ts"].filter((file) => existsSync3(path7.join(srcDir, file)));
|
|
534
|
+
const coreTypesSrc = path7.join(srcDir, "core-types.ts");
|
|
498
535
|
const includeCoreTypes = overloads.length > 0 && existsSync3(coreTypesSrc);
|
|
499
536
|
const wanted = new Set(sources);
|
|
500
537
|
for (const file of overloads) {
|
|
@@ -505,39 +542,39 @@ function materializeApiSurface(opts) {
|
|
|
505
542
|
}
|
|
506
543
|
for (const existing of readdirSync2(absDir)) {
|
|
507
544
|
if (existing.endsWith(".d.ts") && existing !== "index.d.ts" && !wanted.has(existing)) {
|
|
508
|
-
rmSync(
|
|
545
|
+
rmSync(path7.join(absDir, existing));
|
|
509
546
|
}
|
|
510
547
|
}
|
|
511
548
|
for (const file of sources) {
|
|
512
|
-
writeFileSync3(
|
|
549
|
+
writeFileSync3(path7.join(absDir, file), readFileSync6(path7.join(sourceGeneratedDir, file), "utf8"));
|
|
513
550
|
}
|
|
514
551
|
if (includeCoreTypes) {
|
|
515
|
-
writeFileSync3(
|
|
552
|
+
writeFileSync3(path7.join(absDir, "core-types.d.ts"), readFileSync6(coreTypesSrc, "utf8"));
|
|
516
553
|
}
|
|
517
554
|
for (const file of overloads) {
|
|
518
|
-
writeFileSync3(
|
|
555
|
+
writeFileSync3(path7.join(absDir, file), readFileSync6(path7.join(srcDir, file), "utf8"));
|
|
519
556
|
}
|
|
520
557
|
const modules = [...sources, ...overloads].map((file) => file.replace(/\.d\.ts$/, ""));
|
|
521
558
|
const imports = modules.map((mod) => `import "./${mod}";`).join(`
|
|
522
559
|
`);
|
|
523
|
-
writeFileSync3(
|
|
560
|
+
writeFileSync3(path7.join(absDir, "index.d.ts"), `${imports}
|
|
524
561
|
|
|
525
562
|
export {};
|
|
526
563
|
`);
|
|
527
|
-
writeJson2(
|
|
564
|
+
writeJson2(path7.join(absDir, "package.json"), {
|
|
528
565
|
name: `@defold-typescript/materialized-${surfaceId}`,
|
|
529
566
|
types: "index.d.ts"
|
|
530
567
|
});
|
|
531
568
|
return { materializedDir: relDir, active: surfaceId };
|
|
532
569
|
}
|
|
533
570
|
function ensureGitignoreLine(cwd, line) {
|
|
534
|
-
const gitignorePath =
|
|
571
|
+
const gitignorePath = path7.join(cwd, ".gitignore");
|
|
535
572
|
if (!existsSync3(gitignorePath)) {
|
|
536
573
|
writeFileSync3(gitignorePath, `${line}
|
|
537
574
|
`);
|
|
538
575
|
return;
|
|
539
576
|
}
|
|
540
|
-
const existing =
|
|
577
|
+
const existing = readFileSync6(gitignorePath, "utf8");
|
|
541
578
|
const present = new Set(existing.split(`
|
|
542
579
|
`).map((entry) => entry.trim()));
|
|
543
580
|
if (present.has(line)) {
|
|
@@ -553,10 +590,10 @@ function ensureMaterializedReference(cwd, materializedDir) {
|
|
|
553
590
|
if (materializedDir === null) {
|
|
554
591
|
return;
|
|
555
592
|
}
|
|
556
|
-
const surfaceId =
|
|
557
|
-
const tsconfigPath =
|
|
593
|
+
const surfaceId = path7.posix.basename(materializedDir);
|
|
594
|
+
const tsconfigPath = path7.join(cwd, "tsconfig.json");
|
|
558
595
|
if (existsSync3(tsconfigPath)) {
|
|
559
|
-
const tsconfig = JSON.parse(
|
|
596
|
+
const tsconfig = JSON.parse(readFileSync6(tsconfigPath, "utf8"));
|
|
560
597
|
tsconfig.compilerOptions = {
|
|
561
598
|
...tsconfig.compilerOptions ?? {},
|
|
562
599
|
typeRoots: [MATERIALIZED_ROOT],
|
|
@@ -568,7 +605,7 @@ function ensureMaterializedReference(cwd, materializedDir) {
|
|
|
568
605
|
}
|
|
569
606
|
function resolveCurrentSurfaceGeneratedDir() {
|
|
570
607
|
const root = resolveTypesPackageRoot();
|
|
571
|
-
return root === null ? null :
|
|
608
|
+
return root === null ? null : path7.join(root, "generated");
|
|
572
609
|
}
|
|
573
610
|
async function materializeRefDocSurface(opts) {
|
|
574
611
|
const { cwd, surfaceId, resolveOpts } = opts;
|
|
@@ -578,21 +615,21 @@ async function materializeRefDocSurface(opts) {
|
|
|
578
615
|
}
|
|
579
616
|
const registry = opts.registry ?? loadApiTargetsRegistry();
|
|
580
617
|
const target = registry.find((t) => t.id === surfaceId);
|
|
581
|
-
if (
|
|
618
|
+
if (target?.source?.kind !== "ref-doc") {
|
|
582
619
|
return { materializedDir: null, active: null };
|
|
583
620
|
}
|
|
584
621
|
const excludeModules = [...excludedModulesForKind(opts.scriptKind ?? null)];
|
|
585
|
-
const relDir =
|
|
586
|
-
const absDir =
|
|
622
|
+
const relDir = path7.posix.join(MATERIALIZED_ROOT, surfaceId);
|
|
623
|
+
const absDir = path7.join(cwd, MATERIALIZED_ROOT, surfaceId);
|
|
587
624
|
try {
|
|
588
|
-
const mod = await import(
|
|
625
|
+
const mod = await import(path7.join(root, "scripts", "materialize-version.ts"));
|
|
589
626
|
const selfContained = { ...target, coreTypesImport: "./core-types" };
|
|
590
627
|
await mod.materializeVersionedSurface(selfContained, {
|
|
591
628
|
destDir: absDir,
|
|
592
629
|
...resolveOpts ? { resolveOpts } : {},
|
|
593
630
|
...excludeModules.length > 0 ? { excludeModules } : {}
|
|
594
631
|
});
|
|
595
|
-
copyFileSync(
|
|
632
|
+
copyFileSync(path7.join(root, "src", "core-types.ts"), path7.join(absDir, "core-types.d.ts"));
|
|
596
633
|
} catch {
|
|
597
634
|
rmSync(absDir, { recursive: true, force: true });
|
|
598
635
|
return { materializedDir: null, active: null };
|
|
@@ -602,11 +639,11 @@ async function materializeRefDocSurface(opts) {
|
|
|
602
639
|
|
|
603
640
|
// src/watch.ts
|
|
604
641
|
import { existsSync as existsSync4, watch as fsWatch } from "node:fs";
|
|
605
|
-
import * as
|
|
642
|
+
import * as path9 from "node:path";
|
|
606
643
|
|
|
607
644
|
// src/build-session.ts
|
|
608
|
-
import { readFileSync as
|
|
609
|
-
import * as
|
|
645
|
+
import { readFileSync as readFileSync7, rmSync as rmSync2 } from "node:fs";
|
|
646
|
+
import * as path8 from "node:path";
|
|
610
647
|
import {
|
|
611
648
|
createTranspileSession
|
|
612
649
|
} from "@defold-typescript/transpiler";
|
|
@@ -645,26 +682,28 @@ function createBuildSession(opts) {
|
|
|
645
682
|
}
|
|
646
683
|
const files = {};
|
|
647
684
|
for (const rel of sources) {
|
|
648
|
-
files[rel] =
|
|
685
|
+
files[rel] = readFileSync7(path8.join(cwd, rel), "utf8");
|
|
649
686
|
}
|
|
650
687
|
const result = session.update(files);
|
|
651
688
|
return writeOutputs(result, sources);
|
|
652
689
|
}
|
|
653
690
|
function applyEvents(changed, removed) {
|
|
691
|
+
const sourceChanged = changed.filter(isTranspilerSource);
|
|
692
|
+
const sourceRemoved = removed.filter(isTranspilerSource);
|
|
654
693
|
const changes = {};
|
|
655
|
-
for (const rel of
|
|
656
|
-
changes[rel] =
|
|
694
|
+
for (const rel of sourceChanged) {
|
|
695
|
+
changes[rel] = readFileSync7(path8.join(cwd, rel), "utf8");
|
|
657
696
|
}
|
|
658
|
-
for (const rel of
|
|
697
|
+
for (const rel of sourceRemoved) {
|
|
659
698
|
changes[rel] = null;
|
|
660
699
|
}
|
|
661
700
|
const result = session.update(changes);
|
|
662
|
-
for (const rel of
|
|
663
|
-
const luaAbs =
|
|
701
|
+
for (const rel of sourceRemoved) {
|
|
702
|
+
const luaAbs = path8.join(cwd, computeLuaRel(rel, config));
|
|
664
703
|
rmSync2(luaAbs, { force: true });
|
|
665
704
|
rmSync2(`${luaAbs}.map`, { force: true });
|
|
666
705
|
}
|
|
667
|
-
return writeOutputs(result,
|
|
706
|
+
return writeOutputs(result, sourceChanged);
|
|
668
707
|
}
|
|
669
708
|
return { buildAll, applyEvents };
|
|
670
709
|
}
|
|
@@ -712,7 +751,7 @@ function runWatch(opts) {
|
|
|
712
751
|
waitForIdle: () => Promise.resolve()
|
|
713
752
|
};
|
|
714
753
|
}
|
|
715
|
-
const srcDir =
|
|
754
|
+
const srcDir = path9.join(cwd, "src");
|
|
716
755
|
let scheduled = null;
|
|
717
756
|
let syncScheduled = null;
|
|
718
757
|
let rebuildBusy = false;
|
|
@@ -736,7 +775,7 @@ function runWatch(opts) {
|
|
|
736
775
|
const removed = [];
|
|
737
776
|
for (const rel of drained) {
|
|
738
777
|
const key = `src/${toPosix(rel)}`;
|
|
739
|
-
if (existsSync4(
|
|
778
|
+
if (existsSync4(path9.join(srcDir, rel))) {
|
|
740
779
|
changed.push(key);
|
|
741
780
|
} else {
|
|
742
781
|
removed.push(key);
|
|
@@ -756,9 +795,10 @@ function runWatch(opts) {
|
|
|
756
795
|
function onEvent(e) {
|
|
757
796
|
if (stopped)
|
|
758
797
|
return;
|
|
798
|
+
if (!e.path || !isTranspilerSource(e.path))
|
|
799
|
+
return;
|
|
759
800
|
rebuildBusy = true;
|
|
760
|
-
|
|
761
|
-
pending.add(e.path);
|
|
801
|
+
pending.add(e.path);
|
|
762
802
|
if (scheduled)
|
|
763
803
|
clearTimeout(scheduled);
|
|
764
804
|
scheduled = setTimeout(rebuild, debounceMs);
|
|
@@ -836,23 +876,30 @@ function parseDefoldVersionFlag(argv) {
|
|
|
836
876
|
return { flag, rest };
|
|
837
877
|
}
|
|
838
878
|
function readProjectPin(cwd) {
|
|
839
|
-
const pkgPath =
|
|
879
|
+
const pkgPath = path10.join(cwd, "package.json");
|
|
840
880
|
if (!existsSync5(pkgPath)) {
|
|
841
881
|
return;
|
|
842
882
|
}
|
|
843
883
|
try {
|
|
844
|
-
return readDefoldVersionPin(JSON.parse(
|
|
884
|
+
return readDefoldVersionPin(JSON.parse(readFileSync8(pkgPath, "utf8")));
|
|
845
885
|
} catch {
|
|
846
886
|
return;
|
|
847
887
|
}
|
|
848
888
|
}
|
|
849
889
|
function dispatch(argv, io, internals) {
|
|
850
890
|
const json = argv.includes("--json");
|
|
891
|
+
if (argv.includes("--version") || argv.includes("-v")) {
|
|
892
|
+
const version = internals?.cliVersion ?? readCliVersion();
|
|
893
|
+
io.stdout.write(json ? `{"command":"version","ok":true,"version":${JSON.stringify(version)}}
|
|
894
|
+
` : `defold-typescript ${version}
|
|
895
|
+
`);
|
|
896
|
+
return 0;
|
|
897
|
+
}
|
|
851
898
|
const force = argv.includes("--force");
|
|
852
899
|
const { flag: defoldVersionFlag, rest: nonFlagArgs } = parseDefoldVersionFlag(argv);
|
|
853
900
|
const positional = nonFlagArgs.filter((a) => a !== "--json" && a !== "--force");
|
|
854
901
|
const [command, ...rest] = positional;
|
|
855
|
-
const cwd = rest[0] ?
|
|
902
|
+
const cwd = rest[0] ? path10.resolve(rest[0]) : process.cwd();
|
|
856
903
|
const pin = readProjectPin(cwd);
|
|
857
904
|
const resolvedVersion = resolveDefoldVersion({
|
|
858
905
|
...defoldVersionFlag !== undefined ? { flag: defoldVersionFlag } : {},
|
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[]>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function readCliVersion(packageRoot?: string): string;
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -13,5 +13,6 @@ export interface DispatchInternals {
|
|
|
13
13
|
readonly sourceGeneratedDir?: string;
|
|
14
14
|
readonly resolveOpts?: RefDocResolveOptions;
|
|
15
15
|
readonly refDocRegistry?: readonly RegistryTarget[];
|
|
16
|
+
readonly cliVersion?: string;
|
|
16
17
|
}
|
|
17
18
|
export declare function dispatch(argv: string[], io: DispatchIo, internals?: DispatchInternals): number | Promise<number>;
|
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,26 +133,28 @@ 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
|
}
|
|
149
155
|
// src/dispatch.ts
|
|
150
|
-
import { existsSync as existsSync5, readFileSync as
|
|
151
|
-
import * as
|
|
156
|
+
import { existsSync as existsSync5, readFileSync as readFileSync8 } from "node:fs";
|
|
157
|
+
import * as path10 from "node:path";
|
|
152
158
|
|
|
153
159
|
// src/api-registry.ts
|
|
154
160
|
import { existsSync, readFileSync as readFileSync3 } from "node:fs";
|
|
@@ -267,9 +273,27 @@ function runBuild(opts) {
|
|
|
267
273
|
return { written };
|
|
268
274
|
}
|
|
269
275
|
|
|
270
|
-
// src/
|
|
271
|
-
import {
|
|
276
|
+
// src/cli-version.ts
|
|
277
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
272
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";
|
|
273
297
|
|
|
274
298
|
// src/script-kind.ts
|
|
275
299
|
var DEFAULT_TYPES_ENTRYPOINT = "@defold-typescript/types";
|
|
@@ -394,19 +418,33 @@ embedded_instances {
|
|
|
394
418
|
scale3 { x: 1.0 y: 1.0 z: 1.0 }
|
|
395
419
|
}
|
|
396
420
|
`;
|
|
421
|
+
function typesVersionSpec() {
|
|
422
|
+
try {
|
|
423
|
+
const here = path7.dirname(fileURLToPath2(import.meta.url));
|
|
424
|
+
const pkg = JSON.parse(readFileSync6(path7.join(here, "..", "package.json"), "utf8"));
|
|
425
|
+
return pkg.version ? `^${pkg.version}` : "latest";
|
|
426
|
+
} catch {
|
|
427
|
+
return "latest";
|
|
428
|
+
}
|
|
429
|
+
}
|
|
397
430
|
var SCAFFOLD_DEV_DEPS = {
|
|
398
|
-
"@defold-typescript/
|
|
399
|
-
"@defold-typescript/types": "workspace:*",
|
|
431
|
+
"@defold-typescript/types": typesVersionSpec(),
|
|
400
432
|
"@biomejs/biome": "^2.2.0"
|
|
401
433
|
};
|
|
434
|
+
function repairManagedDevDeps(devDeps) {
|
|
435
|
+
delete devDeps["@defold-typescript/transpiler"];
|
|
436
|
+
if (devDeps["@defold-typescript/types"]?.startsWith("workspace:")) {
|
|
437
|
+
devDeps["@defold-typescript/types"] = typesVersionSpec();
|
|
438
|
+
}
|
|
439
|
+
}
|
|
402
440
|
function writeJson(filePath, value) {
|
|
403
441
|
writeFileSync2(filePath, `${JSON.stringify(value, null, 2)}
|
|
404
442
|
`);
|
|
405
443
|
}
|
|
406
444
|
function writeGitignore(cwd) {
|
|
407
|
-
const gitignorePath =
|
|
445
|
+
const gitignorePath = path7.join(cwd, ".gitignore");
|
|
408
446
|
if (existsSync2(gitignorePath)) {
|
|
409
|
-
const existing =
|
|
447
|
+
const existing = readFileSync6(gitignorePath, "utf8");
|
|
410
448
|
const present = new Set(existing.split(`
|
|
411
449
|
`).map((line) => line.trim()));
|
|
412
450
|
const missing = GITIGNORE_LINES.filter((line) => !present.has(line));
|
|
@@ -426,7 +464,7 @@ function writeGitignore(cwd) {
|
|
|
426
464
|
}
|
|
427
465
|
}
|
|
428
466
|
function writeBiome(cwd, written) {
|
|
429
|
-
const biomePath =
|
|
467
|
+
const biomePath = path7.join(cwd, "biome.json");
|
|
430
468
|
if (existsSync2(biomePath)) {
|
|
431
469
|
return;
|
|
432
470
|
}
|
|
@@ -434,8 +472,8 @@ function writeBiome(cwd, written) {
|
|
|
434
472
|
written.push("biome.json");
|
|
435
473
|
}
|
|
436
474
|
function writeTsSurface(cwd, written) {
|
|
437
|
-
mkdirSync2(
|
|
438
|
-
writeFileSync2(
|
|
475
|
+
mkdirSync2(path7.join(cwd, "src"), { recursive: true });
|
|
476
|
+
writeFileSync2(path7.join(cwd, "src", "main.ts"), MAIN_TS_CONTENT);
|
|
439
477
|
written.push("src/main.ts");
|
|
440
478
|
const kinds = detectScriptKinds(cwd);
|
|
441
479
|
const tsconfig = {
|
|
@@ -445,23 +483,24 @@ function writeTsSurface(cwd, written) {
|
|
|
445
483
|
},
|
|
446
484
|
include: ["src/**/*.ts"]
|
|
447
485
|
};
|
|
448
|
-
writeJson(
|
|
486
|
+
writeJson(path7.join(cwd, "tsconfig.json"), tsconfig);
|
|
449
487
|
written.push("tsconfig.json");
|
|
450
|
-
const pkgPath =
|
|
488
|
+
const pkgPath = path7.join(cwd, "package.json");
|
|
451
489
|
if (existsSync2(pkgPath)) {
|
|
452
|
-
const existing = JSON.parse(
|
|
490
|
+
const existing = JSON.parse(readFileSync6(pkgPath, "utf8"));
|
|
453
491
|
const devDeps = { ...existing.devDependencies ?? {} };
|
|
454
492
|
for (const [name, version] of Object.entries(SCAFFOLD_DEV_DEPS)) {
|
|
455
493
|
if (!(name in devDeps)) {
|
|
456
494
|
devDeps[name] = version;
|
|
457
495
|
}
|
|
458
496
|
}
|
|
497
|
+
repairManagedDevDeps(devDeps);
|
|
459
498
|
existing.devDependencies = devDeps;
|
|
460
499
|
existing["defold-typescript"] ??= { "defold-version": CURRENT_STABLE_DEFOLD_VERSION };
|
|
461
500
|
writeJson(pkgPath, existing);
|
|
462
501
|
} else {
|
|
463
502
|
const fresh = {
|
|
464
|
-
name:
|
|
503
|
+
name: path7.basename(cwd),
|
|
465
504
|
version: "0.0.0",
|
|
466
505
|
type: "module",
|
|
467
506
|
devDependencies: { ...SCAFFOLD_DEV_DEPS },
|
|
@@ -482,27 +521,27 @@ function runNewProjectInit(cwd, force = false) {
|
|
|
482
521
|
throw new Error(`defold-typescript init: refusing to synthesize a new Defold project into non-empty directory ${cwd}. Pass --force to proceed.`);
|
|
483
522
|
}
|
|
484
523
|
const written = [];
|
|
485
|
-
writeFileSync2(
|
|
486
|
-
title = ${
|
|
524
|
+
writeFileSync2(path7.join(cwd, "game.project"), `[project]
|
|
525
|
+
title = ${path7.basename(cwd)}
|
|
487
526
|
main_collection = /main/main.collectionc
|
|
488
527
|
`);
|
|
489
528
|
written.push("game.project");
|
|
490
|
-
mkdirSync2(
|
|
491
|
-
writeFileSync2(
|
|
529
|
+
mkdirSync2(path7.join(cwd, "main"), { recursive: true });
|
|
530
|
+
writeFileSync2(path7.join(cwd, "main", "main.collection"), MAIN_COLLECTION_CONTENT);
|
|
492
531
|
written.push("main/main.collection");
|
|
493
|
-
writeFileSync2(
|
|
532
|
+
writeFileSync2(path7.join(cwd, "main", "main.script"), MAIN_SCRIPT_CONTENT);
|
|
494
533
|
written.push("main/main.script");
|
|
495
534
|
const scriptKind = writeTsSurface(cwd, written);
|
|
496
535
|
return { written, scriptKind };
|
|
497
536
|
}
|
|
498
537
|
function runInit(opts) {
|
|
499
538
|
const { cwd, force = false } = opts;
|
|
500
|
-
if (!existsSync2(
|
|
539
|
+
if (!existsSync2(path7.join(cwd, "game.project"))) {
|
|
501
540
|
return runNewProjectInit(cwd, force);
|
|
502
541
|
}
|
|
503
542
|
if (!force) {
|
|
504
543
|
for (const rel of CONFLICTING_TS_CONFIGS) {
|
|
505
|
-
if (existsSync2(
|
|
544
|
+
if (existsSync2(path7.join(cwd, rel))) {
|
|
506
545
|
throw new Error(`defold-typescript init: refusing to overwrite existing TS config: ${rel}. Pass --force to overwrite.`);
|
|
507
546
|
}
|
|
508
547
|
}
|
|
@@ -530,11 +569,11 @@ import {
|
|
|
530
569
|
existsSync as existsSync3,
|
|
531
570
|
mkdirSync as mkdirSync3,
|
|
532
571
|
readdirSync as readdirSync2,
|
|
533
|
-
readFileSync as
|
|
572
|
+
readFileSync as readFileSync7,
|
|
534
573
|
rmSync as rmSync2,
|
|
535
574
|
writeFileSync as writeFileSync3
|
|
536
575
|
} from "node:fs";
|
|
537
|
-
import * as
|
|
576
|
+
import * as path8 from "node:path";
|
|
538
577
|
var MATERIALIZED_ROOT = ".defold-types";
|
|
539
578
|
function writeJson2(filePath, value) {
|
|
540
579
|
writeFileSync3(filePath, `${JSON.stringify(value, null, 2)}
|
|
@@ -549,14 +588,14 @@ function materializeApiSurface(opts) {
|
|
|
549
588
|
return { materializedDir: null, active: null };
|
|
550
589
|
}
|
|
551
590
|
const { surfaceId } = surface;
|
|
552
|
-
const relDir =
|
|
553
|
-
const absDir =
|
|
591
|
+
const relDir = path8.posix.join(MATERIALIZED_ROOT, surfaceId);
|
|
592
|
+
const absDir = path8.join(cwd, MATERIALIZED_ROOT, surfaceId);
|
|
554
593
|
mkdirSync3(absDir, { recursive: true });
|
|
555
594
|
const excluded = excludedModulesForKind(opts.scriptKind ?? null);
|
|
556
595
|
const sources = listDts(sourceGeneratedDir).filter((file) => file !== "index.d.ts").filter((file) => !excluded.has(file.replace(/\.d\.ts$/, "")));
|
|
557
|
-
const srcDir =
|
|
558
|
-
const overloads = ["msg-overloads.d.ts", "go-overloads.d.ts"].filter((file) => existsSync3(
|
|
559
|
-
const coreTypesSrc =
|
|
596
|
+
const srcDir = path8.resolve(sourceGeneratedDir, "..", "src");
|
|
597
|
+
const overloads = ["msg-overloads.d.ts", "go-overloads.d.ts"].filter((file) => existsSync3(path8.join(srcDir, file)));
|
|
598
|
+
const coreTypesSrc = path8.join(srcDir, "core-types.ts");
|
|
560
599
|
const includeCoreTypes = overloads.length > 0 && existsSync3(coreTypesSrc);
|
|
561
600
|
const wanted = new Set(sources);
|
|
562
601
|
for (const file of overloads) {
|
|
@@ -567,39 +606,39 @@ function materializeApiSurface(opts) {
|
|
|
567
606
|
}
|
|
568
607
|
for (const existing of readdirSync2(absDir)) {
|
|
569
608
|
if (existing.endsWith(".d.ts") && existing !== "index.d.ts" && !wanted.has(existing)) {
|
|
570
|
-
rmSync2(
|
|
609
|
+
rmSync2(path8.join(absDir, existing));
|
|
571
610
|
}
|
|
572
611
|
}
|
|
573
612
|
for (const file of sources) {
|
|
574
|
-
writeFileSync3(
|
|
613
|
+
writeFileSync3(path8.join(absDir, file), readFileSync7(path8.join(sourceGeneratedDir, file), "utf8"));
|
|
575
614
|
}
|
|
576
615
|
if (includeCoreTypes) {
|
|
577
|
-
writeFileSync3(
|
|
616
|
+
writeFileSync3(path8.join(absDir, "core-types.d.ts"), readFileSync7(coreTypesSrc, "utf8"));
|
|
578
617
|
}
|
|
579
618
|
for (const file of overloads) {
|
|
580
|
-
writeFileSync3(
|
|
619
|
+
writeFileSync3(path8.join(absDir, file), readFileSync7(path8.join(srcDir, file), "utf8"));
|
|
581
620
|
}
|
|
582
621
|
const modules = [...sources, ...overloads].map((file) => file.replace(/\.d\.ts$/, ""));
|
|
583
622
|
const imports = modules.map((mod) => `import "./${mod}";`).join(`
|
|
584
623
|
`);
|
|
585
|
-
writeFileSync3(
|
|
624
|
+
writeFileSync3(path8.join(absDir, "index.d.ts"), `${imports}
|
|
586
625
|
|
|
587
626
|
export {};
|
|
588
627
|
`);
|
|
589
|
-
writeJson2(
|
|
628
|
+
writeJson2(path8.join(absDir, "package.json"), {
|
|
590
629
|
name: `@defold-typescript/materialized-${surfaceId}`,
|
|
591
630
|
types: "index.d.ts"
|
|
592
631
|
});
|
|
593
632
|
return { materializedDir: relDir, active: surfaceId };
|
|
594
633
|
}
|
|
595
634
|
function ensureGitignoreLine(cwd, line) {
|
|
596
|
-
const gitignorePath =
|
|
635
|
+
const gitignorePath = path8.join(cwd, ".gitignore");
|
|
597
636
|
if (!existsSync3(gitignorePath)) {
|
|
598
637
|
writeFileSync3(gitignorePath, `${line}
|
|
599
638
|
`);
|
|
600
639
|
return;
|
|
601
640
|
}
|
|
602
|
-
const existing =
|
|
641
|
+
const existing = readFileSync7(gitignorePath, "utf8");
|
|
603
642
|
const present = new Set(existing.split(`
|
|
604
643
|
`).map((entry) => entry.trim()));
|
|
605
644
|
if (present.has(line)) {
|
|
@@ -615,10 +654,10 @@ function ensureMaterializedReference(cwd, materializedDir) {
|
|
|
615
654
|
if (materializedDir === null) {
|
|
616
655
|
return;
|
|
617
656
|
}
|
|
618
|
-
const surfaceId =
|
|
619
|
-
const tsconfigPath =
|
|
657
|
+
const surfaceId = path8.posix.basename(materializedDir);
|
|
658
|
+
const tsconfigPath = path8.join(cwd, "tsconfig.json");
|
|
620
659
|
if (existsSync3(tsconfigPath)) {
|
|
621
|
-
const tsconfig = JSON.parse(
|
|
660
|
+
const tsconfig = JSON.parse(readFileSync7(tsconfigPath, "utf8"));
|
|
622
661
|
tsconfig.compilerOptions = {
|
|
623
662
|
...tsconfig.compilerOptions ?? {},
|
|
624
663
|
typeRoots: [MATERIALIZED_ROOT],
|
|
@@ -630,7 +669,7 @@ function ensureMaterializedReference(cwd, materializedDir) {
|
|
|
630
669
|
}
|
|
631
670
|
function resolveCurrentSurfaceGeneratedDir() {
|
|
632
671
|
const root = resolveTypesPackageRoot();
|
|
633
|
-
return root === null ? null :
|
|
672
|
+
return root === null ? null : path8.join(root, "generated");
|
|
634
673
|
}
|
|
635
674
|
async function materializeRefDocSurface(opts) {
|
|
636
675
|
const { cwd, surfaceId, resolveOpts } = opts;
|
|
@@ -640,21 +679,21 @@ async function materializeRefDocSurface(opts) {
|
|
|
640
679
|
}
|
|
641
680
|
const registry = opts.registry ?? loadApiTargetsRegistry();
|
|
642
681
|
const target = registry.find((t) => t.id === surfaceId);
|
|
643
|
-
if (
|
|
682
|
+
if (target?.source?.kind !== "ref-doc") {
|
|
644
683
|
return { materializedDir: null, active: null };
|
|
645
684
|
}
|
|
646
685
|
const excludeModules = [...excludedModulesForKind(opts.scriptKind ?? null)];
|
|
647
|
-
const relDir =
|
|
648
|
-
const absDir =
|
|
686
|
+
const relDir = path8.posix.join(MATERIALIZED_ROOT, surfaceId);
|
|
687
|
+
const absDir = path8.join(cwd, MATERIALIZED_ROOT, surfaceId);
|
|
649
688
|
try {
|
|
650
|
-
const mod = await import(
|
|
689
|
+
const mod = await import(path8.join(root, "scripts", "materialize-version.ts"));
|
|
651
690
|
const selfContained = { ...target, coreTypesImport: "./core-types" };
|
|
652
691
|
await mod.materializeVersionedSurface(selfContained, {
|
|
653
692
|
destDir: absDir,
|
|
654
693
|
...resolveOpts ? { resolveOpts } : {},
|
|
655
694
|
...excludeModules.length > 0 ? { excludeModules } : {}
|
|
656
695
|
});
|
|
657
|
-
copyFileSync(
|
|
696
|
+
copyFileSync(path8.join(root, "src", "core-types.ts"), path8.join(absDir, "core-types.d.ts"));
|
|
658
697
|
} catch {
|
|
659
698
|
rmSync2(absDir, { recursive: true, force: true });
|
|
660
699
|
return { materializedDir: null, active: null };
|
|
@@ -664,7 +703,7 @@ async function materializeRefDocSurface(opts) {
|
|
|
664
703
|
|
|
665
704
|
// src/watch.ts
|
|
666
705
|
import { existsSync as existsSync4, watch as fsWatch } from "node:fs";
|
|
667
|
-
import * as
|
|
706
|
+
import * as path9 from "node:path";
|
|
668
707
|
var DEFAULT_DEBOUNCE_MS = 50;
|
|
669
708
|
var recursiveWatcherFactory = (srcDir, onEvent) => {
|
|
670
709
|
const w = fsWatch(srcDir, { recursive: true }, (eventType, filename) => {
|
|
@@ -707,7 +746,7 @@ function runWatch(opts) {
|
|
|
707
746
|
waitForIdle: () => Promise.resolve()
|
|
708
747
|
};
|
|
709
748
|
}
|
|
710
|
-
const srcDir =
|
|
749
|
+
const srcDir = path9.join(cwd, "src");
|
|
711
750
|
let scheduled = null;
|
|
712
751
|
let syncScheduled = null;
|
|
713
752
|
let rebuildBusy = false;
|
|
@@ -731,7 +770,7 @@ function runWatch(opts) {
|
|
|
731
770
|
const removed = [];
|
|
732
771
|
for (const rel of drained) {
|
|
733
772
|
const key = `src/${toPosix(rel)}`;
|
|
734
|
-
if (existsSync4(
|
|
773
|
+
if (existsSync4(path9.join(srcDir, rel))) {
|
|
735
774
|
changed.push(key);
|
|
736
775
|
} else {
|
|
737
776
|
removed.push(key);
|
|
@@ -751,9 +790,10 @@ function runWatch(opts) {
|
|
|
751
790
|
function onEvent(e) {
|
|
752
791
|
if (stopped)
|
|
753
792
|
return;
|
|
793
|
+
if (!e.path || !isTranspilerSource(e.path))
|
|
794
|
+
return;
|
|
754
795
|
rebuildBusy = true;
|
|
755
|
-
|
|
756
|
-
pending.add(e.path);
|
|
796
|
+
pending.add(e.path);
|
|
757
797
|
if (scheduled)
|
|
758
798
|
clearTimeout(scheduled);
|
|
759
799
|
scheduled = setTimeout(rebuild, debounceMs);
|
|
@@ -831,23 +871,30 @@ function parseDefoldVersionFlag(argv) {
|
|
|
831
871
|
return { flag, rest };
|
|
832
872
|
}
|
|
833
873
|
function readProjectPin(cwd) {
|
|
834
|
-
const pkgPath =
|
|
874
|
+
const pkgPath = path10.join(cwd, "package.json");
|
|
835
875
|
if (!existsSync5(pkgPath)) {
|
|
836
876
|
return;
|
|
837
877
|
}
|
|
838
878
|
try {
|
|
839
|
-
return readDefoldVersionPin(JSON.parse(
|
|
879
|
+
return readDefoldVersionPin(JSON.parse(readFileSync8(pkgPath, "utf8")));
|
|
840
880
|
} catch {
|
|
841
881
|
return;
|
|
842
882
|
}
|
|
843
883
|
}
|
|
844
884
|
function dispatch(argv, io, internals) {
|
|
845
885
|
const json = argv.includes("--json");
|
|
886
|
+
if (argv.includes("--version") || argv.includes("-v")) {
|
|
887
|
+
const version = internals?.cliVersion ?? readCliVersion();
|
|
888
|
+
io.stdout.write(json ? `{"command":"version","ok":true,"version":${JSON.stringify(version)}}
|
|
889
|
+
` : `defold-typescript ${version}
|
|
890
|
+
`);
|
|
891
|
+
return 0;
|
|
892
|
+
}
|
|
846
893
|
const force = argv.includes("--force");
|
|
847
894
|
const { flag: defoldVersionFlag, rest: nonFlagArgs } = parseDefoldVersionFlag(argv);
|
|
848
895
|
const positional = nonFlagArgs.filter((a) => a !== "--json" && a !== "--force");
|
|
849
896
|
const [command, ...rest] = positional;
|
|
850
|
-
const cwd = rest[0] ?
|
|
897
|
+
const cwd = rest[0] ? path10.resolve(rest[0]) : process.cwd();
|
|
851
898
|
const pin = readProjectPin(cwd);
|
|
852
899
|
const resolvedVersion = resolveDefoldVersion({
|
|
853
900
|
...defoldVersionFlag !== undefined ? { flag: defoldVersionFlag } : {},
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defold-typescript/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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.3.0",
|
|
35
|
+
"@defold-typescript/types": "0.3.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 };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const VERSION_FALLBACK = "0.0.0";
|
|
6
|
+
|
|
7
|
+
// Anchor on the module URL, not `import.meta.dir` — the latter is a Bun-only
|
|
8
|
+
// property and is undefined when the bundled CLI runs under node (the `npx`
|
|
9
|
+
// path), which would silently anchor at the cwd. The bundled bin (`dist/bin.js`)
|
|
10
|
+
// and the in-repo `src/` loop both sit one level below the package root, so
|
|
11
|
+
// `package.json` is always `../package.json` from this module's directory. A
|
|
12
|
+
// missing or malformed manifest reports the fallback rather than throwing.
|
|
13
|
+
function defaultPackageRoot(): string {
|
|
14
|
+
return path.join(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function readCliVersion(packageRoot: string = defaultPackageRoot()): string {
|
|
18
|
+
try {
|
|
19
|
+
const pkg = JSON.parse(readFileSync(path.join(packageRoot, "package.json"), "utf8")) as {
|
|
20
|
+
version?: unknown;
|
|
21
|
+
};
|
|
22
|
+
return typeof pkg.version === "string" ? pkg.version : VERSION_FALLBACK;
|
|
23
|
+
} catch {
|
|
24
|
+
return VERSION_FALLBACK;
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/dispatch.ts
CHANGED
|
@@ -3,6 +3,7 @@ import * as path from "node:path";
|
|
|
3
3
|
import type { RegistryTarget } from "./api-registry";
|
|
4
4
|
import { CURRENT_STABLE_SURFACE_ID, selectApiSurface } from "./api-surface";
|
|
5
5
|
import { runBuild } from "./build";
|
|
6
|
+
import { readCliVersion } from "./cli-version";
|
|
6
7
|
import { readDefoldVersionPin, resolveDefoldVersion } from "./defold-version";
|
|
7
8
|
import { runInit } from "./init";
|
|
8
9
|
import { renderResult } from "./json-output";
|
|
@@ -35,6 +36,7 @@ export interface DispatchInternals {
|
|
|
35
36
|
readonly sourceGeneratedDir?: string;
|
|
36
37
|
readonly resolveOpts?: RefDocResolveOptions;
|
|
37
38
|
readonly refDocRegistry?: readonly RegistryTarget[];
|
|
39
|
+
readonly cliVersion?: string;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
const USAGE = "Usage: defold-typescript <init|build|watch> [path]\n";
|
|
@@ -74,6 +76,17 @@ export function dispatch(
|
|
|
74
76
|
internals?: DispatchInternals,
|
|
75
77
|
): number | Promise<number> {
|
|
76
78
|
const json = argv.includes("--json");
|
|
79
|
+
|
|
80
|
+
if (argv.includes("--version") || argv.includes("-v")) {
|
|
81
|
+
const version = internals?.cliVersion ?? readCliVersion();
|
|
82
|
+
io.stdout.write(
|
|
83
|
+
json
|
|
84
|
+
? `{"command":"version","ok":true,"version":${JSON.stringify(version)}}\n`
|
|
85
|
+
: `defold-typescript ${version}\n`,
|
|
86
|
+
);
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
77
90
|
const force = argv.includes("--force");
|
|
78
91
|
const { flag: defoldVersionFlag, rest: nonFlagArgs } = parseDefoldVersionFlag(argv);
|
|
79
92
|
const positional = nonFlagArgs.filter((a) => a !== "--json" && a !== "--force");
|
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,12 +104,44 @@ 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
|
|
|
131
|
+
// Older scaffolds wrote both managed `@defold-typescript/*` devDeps as
|
|
132
|
+
// `workspace:*`, which only resolves inside this monorepo and breaks
|
|
133
|
+
// `bun install` in consumers. The additive merge in `writeTsSurface` never
|
|
134
|
+
// repairs an entry it didn't itself create, so repair them explicitly: the
|
|
135
|
+
// transpiler is CLI-internal and must not be a consumer dep at all, and a
|
|
136
|
+
// `workspace:` types pin must become a concrete published version. A concrete
|
|
137
|
+
// user-chosen types pin is left alone.
|
|
138
|
+
function repairManagedDevDeps(devDeps: Record<string, string>): void {
|
|
139
|
+
delete devDeps["@defold-typescript/transpiler"];
|
|
140
|
+
if (devDeps["@defold-typescript/types"]?.startsWith("workspace:")) {
|
|
141
|
+
devDeps["@defold-typescript/types"] = typesVersionSpec();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
112
145
|
function writeJson(filePath: string, value: unknown): void {
|
|
113
146
|
writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
114
147
|
}
|
|
@@ -163,6 +196,7 @@ function writeTsSurface(cwd: string, written: string[]): ScriptKind | null {
|
|
|
163
196
|
devDeps[name] = version;
|
|
164
197
|
}
|
|
165
198
|
}
|
|
199
|
+
repairManagedDevDeps(devDeps);
|
|
166
200
|
existing.devDependencies = devDeps;
|
|
167
201
|
existing["defold-typescript"] ??= { "defold-version": CURRENT_STABLE_DEFOLD_VERSION };
|
|
168
202
|
writeJson(pkgPath, existing);
|
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
|
}
|