@defold-typescript/cli 0.5.4 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +1414 -395
- package/dist/bob-command.d.ts +31 -0
- package/dist/bob.d.ts +19 -0
- package/dist/boot-path.d.ts +5 -0
- package/dist/build-output.d.ts +7 -1
- package/dist/debug-launcher.d.ts +7 -1
- package/dist/directory-walls.d.ts +23 -0
- package/dist/dispatch.d.ts +6 -0
- package/dist/index.js +1268 -248
- package/dist/init.d.ts +1 -2
- package/dist/install-reminder.d.ts +1 -0
- package/dist/json-output.d.ts +26 -2
- package/dist/materialize.d.ts +0 -3
- package/dist/mise-scaffold.d.ts +1 -1
- package/dist/scan.d.ts +1 -0
- package/dist/script-kind.d.ts +2 -1
- package/dist/setup-debug.d.ts +40 -0
- package/dist/wall-interactive.d.ts +16 -0
- package/dist/wall.d.ts +4 -0
- package/dist/watch.d.ts +1 -0
- package/package.json +4 -3
- package/src/bob-command.ts +137 -0
- package/src/bob.ts +59 -0
- package/src/boot-path.ts +113 -0
- package/src/build-output.ts +67 -3
- package/src/build-session.ts +31 -11
- package/src/build.ts +6 -5
- package/src/debug-launcher.ts +36 -12
- package/src/directory-walls.ts +214 -0
- package/src/dispatch.ts +264 -18
- package/src/init.ts +83 -38
- package/src/install-reminder.ts +18 -0
- package/src/json-output.ts +52 -7
- package/src/materialize.ts +14 -12
- package/src/mise-scaffold.ts +16 -10
- package/src/scan.ts +7 -1
- package/src/script-kind.ts +31 -19
- package/src/setup-debug.ts +422 -0
- package/src/wall-interactive.ts +60 -0
- package/src/wall.ts +71 -0
- package/src/watch.ts +15 -3
package/src/json-output.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type CliCommand = "init" | "build";
|
|
1
|
+
export type CliCommand = "init" | "build" | "setup-debug" | "defold" | "wall";
|
|
2
2
|
|
|
3
3
|
export interface RenderResultInput {
|
|
4
4
|
readonly command: CliCommand;
|
|
@@ -6,8 +6,17 @@ export interface RenderResultInput {
|
|
|
6
6
|
readonly error?: string;
|
|
7
7
|
readonly defoldVersion?: string;
|
|
8
8
|
readonly apiSurface?: string | null;
|
|
9
|
-
readonly scriptKind?: string | null;
|
|
10
9
|
readonly materializedSurface?: string | null;
|
|
10
|
+
readonly directoryWalls?: readonly { readonly dir: string; readonly kind: string }[];
|
|
11
|
+
readonly eligible?: readonly { readonly dir: string; readonly kind: string }[];
|
|
12
|
+
readonly installCommand?: string;
|
|
13
|
+
readonly manualSteps?: readonly string[];
|
|
14
|
+
readonly actions?: Record<string, string>;
|
|
15
|
+
readonly addedTo?: string;
|
|
16
|
+
readonly removedFrom?: readonly string[];
|
|
17
|
+
readonly bootPath?: readonly string[];
|
|
18
|
+
readonly subcommand?: string;
|
|
19
|
+
readonly exitCode?: number;
|
|
11
20
|
}
|
|
12
21
|
|
|
13
22
|
export function renderResult(input: RenderResultInput): string {
|
|
@@ -19,11 +28,47 @@ export function renderResult(input: RenderResultInput): string {
|
|
|
19
28
|
input.defoldVersion === undefined ? base : { ...base, defoldVersion: input.defoldVersion };
|
|
20
29
|
const withSurface =
|
|
21
30
|
"apiSurface" in input ? { ...withVersion, apiSurface: input.apiSurface } : withVersion;
|
|
22
|
-
const
|
|
23
|
-
"scriptKind" in input ? { ...withSurface, scriptKind: input.scriptKind } : withSurface;
|
|
24
|
-
const payload =
|
|
31
|
+
const withMaterialized =
|
|
25
32
|
"materializedSurface" in input
|
|
26
|
-
? { ...
|
|
27
|
-
:
|
|
33
|
+
? { ...withSurface, materializedSurface: input.materializedSurface }
|
|
34
|
+
: withSurface;
|
|
35
|
+
const withWalls =
|
|
36
|
+
"directoryWalls" in input
|
|
37
|
+
? { ...withMaterialized, directoryWalls: input.directoryWalls }
|
|
38
|
+
: withMaterialized;
|
|
39
|
+
const withEligible = "eligible" in input ? { ...withWalls, eligible: input.eligible } : withWalls;
|
|
40
|
+
const withInstall =
|
|
41
|
+
"installCommand" in input
|
|
42
|
+
? { ...withEligible, installCommand: input.installCommand }
|
|
43
|
+
: withEligible;
|
|
44
|
+
const withManual =
|
|
45
|
+
"manualSteps" in input ? { ...withInstall, manualSteps: input.manualSteps } : withInstall;
|
|
46
|
+
const withActions = "actions" in input ? { ...withManual, actions: input.actions } : withManual;
|
|
47
|
+
const withAdded = "addedTo" in input ? { ...withActions, addedTo: input.addedTo } : withActions;
|
|
48
|
+
const withRemoved =
|
|
49
|
+
"removedFrom" in input ? { ...withAdded, removedFrom: input.removedFrom } : withAdded;
|
|
50
|
+
const withBoot = "bootPath" in input ? { ...withRemoved, bootPath: input.bootPath } : withRemoved;
|
|
51
|
+
const withSub = "subcommand" in input ? { ...withBoot, subcommand: input.subcommand } : withBoot;
|
|
52
|
+
const payload = "exitCode" in input ? { ...withSub, exitCode: input.exitCode } : withSub;
|
|
28
53
|
return `${JSON.stringify(payload)}\n`;
|
|
29
54
|
}
|
|
55
|
+
|
|
56
|
+
export type WatchEventName = "build" | "rebuild";
|
|
57
|
+
|
|
58
|
+
export interface RenderWatchEventInput {
|
|
59
|
+
readonly event: WatchEventName;
|
|
60
|
+
readonly written?: readonly string[];
|
|
61
|
+
readonly changed?: readonly string[];
|
|
62
|
+
readonly removed?: readonly string[];
|
|
63
|
+
readonly error?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function renderWatchEvent(input: RenderWatchEventInput): string {
|
|
67
|
+
const ok = input.error === undefined;
|
|
68
|
+
const base = ok
|
|
69
|
+
? { command: "watch" as const, event: input.event, ok, written: input.written ?? [] }
|
|
70
|
+
: { command: "watch" as const, event: input.event, ok, error: input.error };
|
|
71
|
+
const withChanged = "changed" in input ? { ...base, changed: input.changed } : base;
|
|
72
|
+
const withRemoved = "removed" in input ? { ...withChanged, removed: input.removed } : withChanged;
|
|
73
|
+
return `${JSON.stringify(withRemoved)}\n`;
|
|
74
|
+
}
|
package/src/materialize.ts
CHANGED
|
@@ -14,15 +14,23 @@ import {
|
|
|
14
14
|
resolveTypesPackageRoot,
|
|
15
15
|
} from "./api-registry";
|
|
16
16
|
import type { SelectedApiSurface } from "./api-surface";
|
|
17
|
-
import { excludedModulesForKind, type ScriptKind } from "./script-kind";
|
|
18
17
|
|
|
19
18
|
const MATERIALIZED_ROOT = ".defold-types";
|
|
20
19
|
|
|
20
|
+
// The materialized surface must not mint its own copy of the branded engine
|
|
21
|
+
// primitives: `Hash` & co. are `unique symbol`-branded per declaration, so a
|
|
22
|
+
// copied `core-types.d.ts` is nominally distinct from the installed
|
|
23
|
+
// `@defold-typescript/types` a consumer imports from, and the two never unify
|
|
24
|
+
// (a consumer comparing `message_id === hash(...)` or assigning an imported
|
|
25
|
+
// `Hash` would get TS2367/TS2741). Re-export the package's copy instead so the
|
|
26
|
+
// ambient surface shares one brand. `engine-globals.d.ts` stays copied; its
|
|
27
|
+
// relative `./core-types` import resolves to this re-export.
|
|
28
|
+
const CORE_TYPES_REEXPORT = 'export * from "@defold-typescript/types/core-types";\n';
|
|
29
|
+
|
|
21
30
|
export interface MaterializeApiSurfaceOptions {
|
|
22
31
|
readonly cwd: string;
|
|
23
32
|
readonly surface: SelectedApiSurface;
|
|
24
33
|
readonly sourceGeneratedDir: string | null;
|
|
25
|
-
readonly scriptKind?: ScriptKind | null;
|
|
26
34
|
}
|
|
27
35
|
|
|
28
36
|
export interface MaterializeApiSurfaceResult {
|
|
@@ -53,10 +61,7 @@ export function materializeApiSurface(
|
|
|
53
61
|
const absDir = path.join(cwd, MATERIALIZED_ROOT, surfaceId);
|
|
54
62
|
mkdirSync(absDir, { recursive: true });
|
|
55
63
|
|
|
56
|
-
const
|
|
57
|
-
const sources = listDts(sourceGeneratedDir)
|
|
58
|
-
.filter((file) => file !== "index.d.ts")
|
|
59
|
-
.filter((file) => !excluded.has(file.replace(/\.d\.ts$/, "")));
|
|
64
|
+
const sources = listDts(sourceGeneratedDir).filter((file) => file !== "index.d.ts");
|
|
60
65
|
|
|
61
66
|
// The `*-overloads` augmentations and the `core-types` they import live in the
|
|
62
67
|
// types package `src/` (sibling of `generated/`), not among the generated
|
|
@@ -97,7 +102,7 @@ export function materializeApiSurface(
|
|
|
97
102
|
);
|
|
98
103
|
}
|
|
99
104
|
if (includeCoreTypes) {
|
|
100
|
-
writeFileSync(path.join(absDir, "core-types.d.ts"),
|
|
105
|
+
writeFileSync(path.join(absDir, "core-types.d.ts"), CORE_TYPES_REEXPORT);
|
|
101
106
|
}
|
|
102
107
|
if (includeEngineGlobals) {
|
|
103
108
|
writeFileSync(path.join(absDir, "engine-globals.d.ts"), readFileSync(engineGlobalsSrc, "utf8"));
|
|
@@ -194,10 +199,9 @@ export interface MaterializeRefDocSurfaceOptions {
|
|
|
194
199
|
readonly cwd: string;
|
|
195
200
|
readonly surfaceId: string;
|
|
196
201
|
readonly resolveOpts?: RefDocResolveOptions;
|
|
197
|
-
readonly scriptKind?: ScriptKind | null;
|
|
198
202
|
// Registry override (defaults to the installed types package's
|
|
199
203
|
// api-targets.json). Injected only by tests that need a multi-module ref-doc
|
|
200
|
-
// target
|
|
204
|
+
// target.
|
|
201
205
|
readonly registry?: readonly RegistryTarget[];
|
|
202
206
|
}
|
|
203
207
|
|
|
@@ -221,7 +225,6 @@ export async function materializeRefDocSurface(
|
|
|
221
225
|
if (target?.source?.kind !== "ref-doc") {
|
|
222
226
|
return { materializedDir: null, active: null };
|
|
223
227
|
}
|
|
224
|
-
const excludeModules = [...excludedModulesForKind(opts.scriptKind ?? null)];
|
|
225
228
|
|
|
226
229
|
const relDir = path.posix.join(MATERIALIZED_ROOT, surfaceId);
|
|
227
230
|
const absDir = path.join(cwd, MATERIALIZED_ROOT, surfaceId);
|
|
@@ -233,9 +236,8 @@ export async function materializeRefDocSurface(
|
|
|
233
236
|
await mod.materializeVersionedSurface(selfContained, {
|
|
234
237
|
destDir: absDir,
|
|
235
238
|
...(resolveOpts ? { resolveOpts } : {}),
|
|
236
|
-
...(excludeModules.length > 0 ? { excludeModules } : {}),
|
|
237
239
|
});
|
|
238
|
-
|
|
240
|
+
writeFileSync(path.join(absDir, "core-types.d.ts"), CORE_TYPES_REEXPORT);
|
|
239
241
|
copyFileSync(
|
|
240
242
|
path.join(root, "src", "engine-globals.d.ts"),
|
|
241
243
|
path.join(absDir, "engine-globals.d.ts"),
|
package/src/mise-scaffold.ts
CHANGED
|
@@ -4,25 +4,31 @@
|
|
|
4
4
|
// the block without disturbing user-authored `[tools]`/`[tasks.*]` content.
|
|
5
5
|
const MANAGED_MARKER = "# managed by @defold-typescript";
|
|
6
6
|
|
|
7
|
-
// `bunx
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
7
|
+
// Build/watch/setup-debug run `bunx @defold-typescript/cli <cmd>`: inside an
|
|
8
|
+
// installed project bunx resolves the `@defold-typescript/cli` pinned in
|
|
9
|
+
// `SCAFFOLD_DEV_DEPS`, so the task runs the version locked alongside
|
|
10
|
+
// `@defold-typescript/types`. `:upgrade` is the deliberate `@latest` pull that
|
|
11
|
+
// re-pins `@defold-typescript/types` via `init --force` + reinstall; it
|
|
12
|
+
// suppresses the install reminder because its second command already reinstalls.
|
|
12
13
|
export const MISE_TASKS_TOML = `${MANAGED_MARKER}
|
|
13
14
|
[tasks."defold-typescript:build"]
|
|
14
|
-
description = "Build the TypeScript sources with the
|
|
15
|
-
run = "bunx
|
|
15
|
+
description = "Build the TypeScript sources with the defold-typescript CLI"
|
|
16
|
+
run = "bunx @defold-typescript/cli build"
|
|
16
17
|
|
|
17
18
|
${MANAGED_MARKER}
|
|
18
19
|
[tasks."defold-typescript:watch"]
|
|
19
|
-
description = "Watch and rebuild the TypeScript sources with the
|
|
20
|
-
run = "bunx
|
|
20
|
+
description = "Watch and rebuild the TypeScript sources with the defold-typescript CLI"
|
|
21
|
+
run = "bunx @defold-typescript/cli watch"
|
|
22
|
+
|
|
23
|
+
${MANAGED_MARKER}
|
|
24
|
+
[tasks."defold-typescript:setup-debug"]
|
|
25
|
+
description = "Wire the lldebugger game.project dependency and entry-script bootstrap with the defold-typescript CLI"
|
|
26
|
+
run = "bunx @defold-typescript/cli setup-debug"
|
|
21
27
|
|
|
22
28
|
${MANAGED_MARKER}
|
|
23
29
|
[tasks."defold-typescript:upgrade"]
|
|
24
30
|
description = "Upgrade the defold-typescript CLI to its latest release and re-pin the types dependency"
|
|
25
|
-
run = ["bunx @defold-typescript/cli@latest init --force", "bun install"]
|
|
31
|
+
run = ["bunx @defold-typescript/cli@latest init --force --suppress-install-reminder", "bun install"]
|
|
26
32
|
`;
|
|
27
33
|
|
|
28
34
|
// Drop every managed block (marker line through the next blank line or EOF),
|
package/src/scan.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { globSync, statSync } from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
|
|
4
|
+
export function normalizeScannedPath(rel: string): string {
|
|
5
|
+
return rel.split(/[/\\]/).join("/");
|
|
6
|
+
}
|
|
7
|
+
|
|
4
8
|
// `Bun.Glob` is undefined when the published bin runs under plain node, so the
|
|
5
9
|
// scaffold/build path must use the cross-runtime `node:fs` glob instead. Bun's
|
|
6
10
|
// `globSync` does not support `withFileTypes`, so directories are filtered out
|
|
7
11
|
// with a stat to preserve the original `onlyFiles` contract.
|
|
8
12
|
export function scanFilesSync(cwd: string, pattern: string): string[] {
|
|
9
|
-
return globSync(pattern, { cwd })
|
|
13
|
+
return globSync(pattern, { cwd })
|
|
14
|
+
.map(normalizeScannedPath)
|
|
15
|
+
.filter((rel) => statSync(path.join(cwd, rel)).isFile());
|
|
10
16
|
}
|
package/src/script-kind.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
1
2
|
import { scanFilesSync } from "./scan";
|
|
2
3
|
|
|
3
4
|
export type ScriptKind = "script" | "gui-script" | "render-script";
|
|
@@ -45,6 +46,36 @@ export function detectScriptKinds(cwd: string): Set<ScriptKind> {
|
|
|
45
46
|
return kinds;
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
export function groupScriptKindsByDirectory(cwd: string): Map<string, Set<ScriptKind>> {
|
|
50
|
+
const byDir = new Map<string, Set<ScriptKind>>();
|
|
51
|
+
for (const [ext, kind] of Object.entries(KIND_BY_EXT)) {
|
|
52
|
+
for (const match of scanFilesSync(cwd, `**/*${ext}`)) {
|
|
53
|
+
if (isSkipped(match) || isGeneratedScript(match)) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const dir = path.posix.dirname(match.split(path.sep).join("/"));
|
|
57
|
+
let set = byDir.get(dir);
|
|
58
|
+
if (set === undefined) {
|
|
59
|
+
set = new Set<ScriptKind>();
|
|
60
|
+
byDir.set(dir, set);
|
|
61
|
+
}
|
|
62
|
+
set.add(kind);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return byDir;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function selectDirectoryWalls(cwd: string): Map<string, ScriptKind> {
|
|
69
|
+
const walls = new Map<string, ScriptKind>();
|
|
70
|
+
for (const [dir, kinds] of groupScriptKindsByDirectory(cwd)) {
|
|
71
|
+
const kind = selectScriptKind(kinds);
|
|
72
|
+
if (kind !== null) {
|
|
73
|
+
walls.set(dir, kind);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return walls;
|
|
77
|
+
}
|
|
78
|
+
|
|
48
79
|
export function selectScriptKind(kinds: Set<ScriptKind>): ScriptKind | null {
|
|
49
80
|
if (kinds.size !== 1) {
|
|
50
81
|
return null;
|
|
@@ -59,22 +90,3 @@ export function selectScriptKindEntrypoint(kinds: Set<ScriptKind>): string {
|
|
|
59
90
|
const kind = selectScriptKind(kinds);
|
|
60
91
|
return kind === null ? DEFAULT_TYPES_ENTRYPOINT : `${DEFAULT_TYPES_ENTRYPOINT}/${kind}`;
|
|
61
92
|
}
|
|
62
|
-
|
|
63
|
-
// The one restricted namespace each kind allows; mirrors `regen.ts`'s
|
|
64
|
-
// RESTRICTED_NAMESPACES (gui -> gui_script, render -> render_script). `script`
|
|
65
|
-
// allows neither.
|
|
66
|
-
const RESTRICTED_MODULES: Record<ScriptKind, string> = {
|
|
67
|
-
script: "",
|
|
68
|
-
"gui-script": "gui",
|
|
69
|
-
"render-script": "render",
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const ALL_RESTRICTED_MODULES: readonly string[] = ["gui", "render"];
|
|
73
|
-
|
|
74
|
-
export function excludedModulesForKind(kind: ScriptKind | null): Set<string> {
|
|
75
|
-
if (kind === null) {
|
|
76
|
-
return new Set();
|
|
77
|
-
}
|
|
78
|
-
const allowed = RESTRICTED_MODULES[kind];
|
|
79
|
-
return new Set(ALL_RESTRICTED_MODULES.filter((mod) => mod !== allowed));
|
|
80
|
-
}
|