@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/dispatch.ts
CHANGED
|
@@ -2,10 +2,17 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
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
|
+
import {
|
|
6
|
+
type DefoldIo,
|
|
7
|
+
defaultDefoldIo,
|
|
8
|
+
isDefoldSubcommand,
|
|
9
|
+
runDefoldCommand,
|
|
10
|
+
} from "./bob-command";
|
|
5
11
|
import { runBuild } from "./build";
|
|
6
12
|
import { readCliVersion } from "./cli-version";
|
|
7
13
|
import { readDefoldVersionPin, resolveDefoldVersion } from "./defold-version";
|
|
8
14
|
import { runInit } from "./init";
|
|
15
|
+
import { installHint } from "./install-reminder";
|
|
9
16
|
import { renderResult } from "./json-output";
|
|
10
17
|
import {
|
|
11
18
|
ensureMaterializedReference,
|
|
@@ -14,7 +21,9 @@ import {
|
|
|
14
21
|
type RefDocResolveOptions,
|
|
15
22
|
resolveCurrentSurfaceGeneratedDir,
|
|
16
23
|
} from "./materialize";
|
|
17
|
-
import {
|
|
24
|
+
import { runSetupDebug } from "./setup-debug";
|
|
25
|
+
import { applyWallSelection, currentWalledDirs, eligibleWalls } from "./wall";
|
|
26
|
+
import { type CheckboxPrompt, runWallInteractive } from "./wall-interactive";
|
|
18
27
|
import {
|
|
19
28
|
type RunWatchHandle,
|
|
20
29
|
type RunWatchOptions,
|
|
@@ -37,9 +46,16 @@ export interface DispatchInternals {
|
|
|
37
46
|
readonly resolveOpts?: RefDocResolveOptions;
|
|
38
47
|
readonly refDocRegistry?: readonly RegistryTarget[];
|
|
39
48
|
readonly cliVersion?: string;
|
|
49
|
+
readonly defoldIo?: Partial<DefoldIo>;
|
|
50
|
+
// `wall` takes its target directories as positionals (not a cwd path arg like
|
|
51
|
+
// the other commands), so tests inject the project root and TTY state here.
|
|
52
|
+
readonly cwd?: string;
|
|
53
|
+
readonly isTty?: boolean;
|
|
54
|
+
readonly wallCheckbox?: CheckboxPrompt;
|
|
40
55
|
}
|
|
41
56
|
|
|
42
|
-
const USAGE = "Usage: defold-typescript <init|build|watch> [path]\n";
|
|
57
|
+
const USAGE = "Usage: defold-typescript <init|build|watch|wall|setup-debug|defold> [path]\n";
|
|
58
|
+
const DEFOLD_USAGE = "Usage: defold-typescript defold <resolve|build|bundle> [path]\n";
|
|
43
59
|
|
|
44
60
|
function parseDefoldVersionFlag(argv: string[]): { flag: string | undefined; rest: string[] } {
|
|
45
61
|
let flag: string | undefined;
|
|
@@ -58,6 +74,45 @@ function parseDefoldVersionFlag(argv: string[]): { flag: string | undefined; res
|
|
|
58
74
|
return { flag, rest };
|
|
59
75
|
}
|
|
60
76
|
|
|
77
|
+
function parseScriptFlag(argv: string[]): { script: string | undefined; rest: string[] } {
|
|
78
|
+
let script: string | undefined;
|
|
79
|
+
const rest: string[] = [];
|
|
80
|
+
for (let i = 0; i < argv.length; i++) {
|
|
81
|
+
const arg = argv[i];
|
|
82
|
+
if (arg === "--script") {
|
|
83
|
+
script = argv[i + 1];
|
|
84
|
+
i++;
|
|
85
|
+
} else if (arg?.startsWith("--script=")) {
|
|
86
|
+
script = arg.slice("--script=".length);
|
|
87
|
+
} else if (arg !== undefined) {
|
|
88
|
+
rest.push(arg);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return { script, rest };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function parseValueFlag(
|
|
95
|
+
argv: string[],
|
|
96
|
+
name: string,
|
|
97
|
+
): { value: string | undefined; rest: string[] } {
|
|
98
|
+
const long = `--${name}`;
|
|
99
|
+
const eq = `${long}=`;
|
|
100
|
+
let value: string | undefined;
|
|
101
|
+
const rest: string[] = [];
|
|
102
|
+
for (let i = 0; i < argv.length; i++) {
|
|
103
|
+
const arg = argv[i];
|
|
104
|
+
if (arg === long) {
|
|
105
|
+
value = argv[i + 1];
|
|
106
|
+
i++;
|
|
107
|
+
} else if (arg?.startsWith(eq)) {
|
|
108
|
+
value = arg.slice(eq.length);
|
|
109
|
+
} else if (arg !== undefined) {
|
|
110
|
+
rest.push(arg);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return { value, rest };
|
|
114
|
+
}
|
|
115
|
+
|
|
61
116
|
function readProjectPin(cwd: string): string | undefined {
|
|
62
117
|
const pkgPath = path.join(cwd, "package.json");
|
|
63
118
|
if (!existsSync(pkgPath)) {
|
|
@@ -88,8 +143,24 @@ export function dispatch(
|
|
|
88
143
|
}
|
|
89
144
|
|
|
90
145
|
const force = argv.includes("--force");
|
|
91
|
-
const
|
|
92
|
-
const
|
|
146
|
+
const suppressInstallReminder = argv.includes("--suppress-install-reminder");
|
|
147
|
+
const wallRemove = argv.includes("--remove");
|
|
148
|
+
const wallList = argv.includes("--list");
|
|
149
|
+
const { flag: defoldVersionFlag, rest: afterVersionArgs } = parseDefoldVersionFlag(argv);
|
|
150
|
+
const { script: scriptFlag, rest: afterScriptArgs } = parseScriptFlag(afterVersionArgs);
|
|
151
|
+
const { value: javaFlag, rest: afterJavaArgs } = parseValueFlag(afterScriptArgs, "java");
|
|
152
|
+
const { value: buildServerFlag, rest: nonFlagArgs } = parseValueFlag(
|
|
153
|
+
afterJavaArgs,
|
|
154
|
+
"build-server",
|
|
155
|
+
);
|
|
156
|
+
const positional = nonFlagArgs.filter(
|
|
157
|
+
(a) =>
|
|
158
|
+
a !== "--json" &&
|
|
159
|
+
a !== "--force" &&
|
|
160
|
+
a !== "--suppress-install-reminder" &&
|
|
161
|
+
a !== "--remove" &&
|
|
162
|
+
a !== "--list",
|
|
163
|
+
);
|
|
93
164
|
const [command, ...rest] = positional;
|
|
94
165
|
const cwd = rest[0] ? path.resolve(rest[0]) : process.cwd();
|
|
95
166
|
|
|
@@ -103,7 +174,7 @@ export function dispatch(
|
|
|
103
174
|
|
|
104
175
|
if (command === "init") {
|
|
105
176
|
try {
|
|
106
|
-
const { written
|
|
177
|
+
const { written } = runInit({ cwd, force });
|
|
107
178
|
if (json) {
|
|
108
179
|
io.stdout.write(
|
|
109
180
|
renderResult({
|
|
@@ -111,13 +182,16 @@ export function dispatch(
|
|
|
111
182
|
written,
|
|
112
183
|
defoldVersion: resolvedVersion,
|
|
113
184
|
apiSurface,
|
|
114
|
-
|
|
185
|
+
installCommand: installHint(),
|
|
115
186
|
}),
|
|
116
187
|
);
|
|
117
188
|
} else {
|
|
118
189
|
io.stdout.write(
|
|
119
190
|
`defold-typescript init: wrote ${written.length} files: ${written.join(", ")}\n`,
|
|
120
191
|
);
|
|
192
|
+
if (!suppressInstallReminder) {
|
|
193
|
+
io.stdout.write(`Next: run \`${installHint()}\` to install dependencies.\n`);
|
|
194
|
+
}
|
|
121
195
|
}
|
|
122
196
|
return 0;
|
|
123
197
|
} catch (err) {
|
|
@@ -131,10 +205,57 @@ export function dispatch(
|
|
|
131
205
|
}
|
|
132
206
|
}
|
|
133
207
|
|
|
208
|
+
if (command === "setup-debug") {
|
|
209
|
+
return (async (): Promise<number> => {
|
|
210
|
+
const result = await runSetupDebug({
|
|
211
|
+
cwd,
|
|
212
|
+
json,
|
|
213
|
+
...(scriptFlag !== undefined ? { script: scriptFlag } : {}),
|
|
214
|
+
});
|
|
215
|
+
if (json) {
|
|
216
|
+
io.stdout.write(
|
|
217
|
+
renderResult(
|
|
218
|
+
result.ok
|
|
219
|
+
? {
|
|
220
|
+
command: "setup-debug",
|
|
221
|
+
written: result.written,
|
|
222
|
+
actions: result.actions,
|
|
223
|
+
manualSteps: result.manualSteps,
|
|
224
|
+
...(result.addedTo !== undefined ? { addedTo: result.addedTo } : {}),
|
|
225
|
+
removedFrom: result.removedFrom ?? [],
|
|
226
|
+
bootPath: result.bootPath ?? [],
|
|
227
|
+
}
|
|
228
|
+
: { command: "setup-debug", error: result.error ?? "setup-debug failed" },
|
|
229
|
+
),
|
|
230
|
+
);
|
|
231
|
+
} else if (result.ok) {
|
|
232
|
+
io.stdout.write(
|
|
233
|
+
`defold-typescript setup-debug: wrote ${result.written.length} files: ${result.written.join(", ")}\n`,
|
|
234
|
+
);
|
|
235
|
+
if (result.addedTo !== undefined) {
|
|
236
|
+
io.stdout.write(`Debugger bootstrap added to: ${result.addedTo}\n`);
|
|
237
|
+
}
|
|
238
|
+
if (result.removedFrom !== undefined && result.removedFrom.length > 0) {
|
|
239
|
+
io.stdout.write(`Removed stale bootstrap from: ${result.removedFrom.join(", ")}\n`);
|
|
240
|
+
}
|
|
241
|
+
if (result.bootPath !== undefined && result.bootPath.length > 0) {
|
|
242
|
+
io.stdout.write(`Boot path: ${result.bootPath.join(" -> ")}\n`);
|
|
243
|
+
}
|
|
244
|
+
io.stdout.write("Remaining manual steps:\n");
|
|
245
|
+
for (const step of result.manualSteps) {
|
|
246
|
+
io.stdout.write(` - ${step}\n`);
|
|
247
|
+
}
|
|
248
|
+
} else {
|
|
249
|
+
io.stderr.write(`${result.error}\n`);
|
|
250
|
+
}
|
|
251
|
+
return result.ok ? 0 : 1;
|
|
252
|
+
})();
|
|
253
|
+
}
|
|
254
|
+
|
|
134
255
|
if (command === "build") {
|
|
135
|
-
const scriptKind = selectScriptKind(detectScriptKinds(cwd));
|
|
136
256
|
const reportBuild = (written: readonly string[], materializedDir: string | null): number => {
|
|
137
257
|
ensureMaterializedReference(cwd, materializedDir);
|
|
258
|
+
// walls are opt-in via the wall command
|
|
138
259
|
if (json) {
|
|
139
260
|
io.stdout.write(
|
|
140
261
|
renderResult({
|
|
@@ -142,7 +263,6 @@ export function dispatch(
|
|
|
142
263
|
written,
|
|
143
264
|
defoldVersion: resolvedVersion,
|
|
144
265
|
apiSurface,
|
|
145
|
-
scriptKind,
|
|
146
266
|
materializedSurface: materializedDir,
|
|
147
267
|
}),
|
|
148
268
|
);
|
|
@@ -176,7 +296,6 @@ export function dispatch(
|
|
|
176
296
|
const { materializedDir } = await materializeRefDocSurface({
|
|
177
297
|
cwd,
|
|
178
298
|
surfaceId,
|
|
179
|
-
scriptKind,
|
|
180
299
|
...(internals?.resolveOpts ? { resolveOpts: internals.resolveOpts } : {}),
|
|
181
300
|
...(internals?.refDocRegistry ? { registry: internals.refDocRegistry } : {}),
|
|
182
301
|
});
|
|
@@ -200,7 +319,6 @@ export function dispatch(
|
|
|
200
319
|
cwd,
|
|
201
320
|
surface,
|
|
202
321
|
sourceGeneratedDir,
|
|
203
|
-
scriptKind,
|
|
204
322
|
});
|
|
205
323
|
return reportBuild(written, materializedDir);
|
|
206
324
|
} catch (err) {
|
|
@@ -220,14 +338,13 @@ export function dispatch(
|
|
|
220
338
|
const sourceGeneratedDir =
|
|
221
339
|
internals?.sourceGeneratedDir ?? resolveCurrentSurfaceGeneratedDir();
|
|
222
340
|
syncSurface = (): void => {
|
|
223
|
-
const scriptKind = selectScriptKind(detectScriptKinds(cwd));
|
|
224
341
|
const { materializedDir } = materializeApiSurface({
|
|
225
342
|
cwd,
|
|
226
343
|
surface,
|
|
227
344
|
sourceGeneratedDir,
|
|
228
|
-
scriptKind,
|
|
229
345
|
});
|
|
230
346
|
ensureMaterializedReference(cwd, materializedDir);
|
|
347
|
+
// walls are opt-in via the wall command
|
|
231
348
|
};
|
|
232
349
|
componentWatcherFactory = internals
|
|
233
350
|
? internals.componentWatcherFactory
|
|
@@ -243,6 +360,7 @@ export function dispatch(
|
|
|
243
360
|
...(internals?.debounceMs !== undefined ? { debounceMs: internals.debounceMs } : {}),
|
|
244
361
|
...(syncSurface ? { syncSurface } : {}),
|
|
245
362
|
...(componentWatcherFactory ? { componentWatcherFactory } : {}),
|
|
363
|
+
...(json ? { json: true } : {}),
|
|
246
364
|
};
|
|
247
365
|
const handle = runWatch(watchOpts);
|
|
248
366
|
if (internals) {
|
|
@@ -258,18 +376,15 @@ export function dispatch(
|
|
|
258
376
|
};
|
|
259
377
|
|
|
260
378
|
// A pinned ref-doc surface is generated on the fly, so it has no
|
|
261
|
-
// `syncSurface`;
|
|
262
|
-
// start the watcher.
|
|
263
|
-
//
|
|
264
|
-
// ref-doc surfaces stays deferred.
|
|
379
|
+
// `syncSurface`; generate it once at startup the same way `build` does, then
|
|
380
|
+
// start the watcher. The full surface materializes; walls are opt-in via the
|
|
381
|
+
// wall command.
|
|
265
382
|
if (isRefDocSurface) {
|
|
266
383
|
const surfaceId = surface.surfaceId as string;
|
|
267
|
-
const scriptKind = selectScriptKind(detectScriptKinds(cwd));
|
|
268
384
|
return (async (): Promise<number> => {
|
|
269
385
|
const { materializedDir } = await materializeRefDocSurface({
|
|
270
386
|
cwd,
|
|
271
387
|
surfaceId,
|
|
272
|
-
scriptKind,
|
|
273
388
|
...(internals?.resolveOpts ? { resolveOpts: internals.resolveOpts } : {}),
|
|
274
389
|
...(internals?.refDocRegistry ? { registry: internals.refDocRegistry } : {}),
|
|
275
390
|
});
|
|
@@ -281,6 +396,137 @@ export function dispatch(
|
|
|
281
396
|
return launchWatch();
|
|
282
397
|
}
|
|
283
398
|
|
|
399
|
+
if (command === "wall") {
|
|
400
|
+
const wallCwd = internals?.cwd ?? process.cwd();
|
|
401
|
+
const dirs = rest;
|
|
402
|
+
const toJsonWall = (w: { dir: string; kind: string }): { dir: string; kind: string } => ({
|
|
403
|
+
dir: w.dir,
|
|
404
|
+
kind: w.kind,
|
|
405
|
+
});
|
|
406
|
+
const reportWalls = (walls: { dir: string; kind: string }[]): void => {
|
|
407
|
+
if (json) {
|
|
408
|
+
io.stdout.write(renderResult({ command: "wall", directoryWalls: walls.map(toJsonWall) }));
|
|
409
|
+
} else if (walls.length === 0) {
|
|
410
|
+
io.stdout.write("defold-typescript wall: no directories walled\n");
|
|
411
|
+
} else {
|
|
412
|
+
io.stdout.write(`defold-typescript wall: walled ${walls.map((w) => w.dir).join(", ")}\n`);
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
if (wallList) {
|
|
417
|
+
const current = currentWalledDirs(wallCwd);
|
|
418
|
+
const eligible = eligibleWalls(wallCwd);
|
|
419
|
+
const currentWalls = eligible.filter((w) => current.includes(w.dir));
|
|
420
|
+
if (json) {
|
|
421
|
+
io.stdout.write(
|
|
422
|
+
renderResult({
|
|
423
|
+
command: "wall",
|
|
424
|
+
directoryWalls: currentWalls.map(toJsonWall),
|
|
425
|
+
eligible: eligible.map(toJsonWall),
|
|
426
|
+
}),
|
|
427
|
+
);
|
|
428
|
+
} else {
|
|
429
|
+
io.stdout.write(
|
|
430
|
+
`defold-typescript wall: walled [${current.join(", ")}]; eligible [${eligible
|
|
431
|
+
.map((w) => w.dir)
|
|
432
|
+
.join(", ")}]\n`,
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
return 0;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (dirs.length > 0) {
|
|
439
|
+
try {
|
|
440
|
+
const current = currentWalledDirs(wallCwd);
|
|
441
|
+
const desired = wallRemove
|
|
442
|
+
? current.filter((d) => !dirs.includes(d))
|
|
443
|
+
: [...current, ...dirs];
|
|
444
|
+
reportWalls(applyWallSelection(wallCwd, desired));
|
|
445
|
+
return 0;
|
|
446
|
+
} catch (err) {
|
|
447
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
448
|
+
if (json) {
|
|
449
|
+
io.stdout.write(renderResult({ command: "wall", error: message }));
|
|
450
|
+
} else {
|
|
451
|
+
io.stderr.write(`${message}\n`);
|
|
452
|
+
}
|
|
453
|
+
return 1;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// `--json` is machine-driven intent, so it never prompts even on a TTY.
|
|
458
|
+
const interactive = !json && (internals?.isTty ?? Boolean(process.stdout.isTTY));
|
|
459
|
+
if (!interactive) {
|
|
460
|
+
io.stderr.write(
|
|
461
|
+
"defold-typescript wall: no directory given; pass <dir> or run in a terminal for the interactive menu\n",
|
|
462
|
+
);
|
|
463
|
+
return 1;
|
|
464
|
+
}
|
|
465
|
+
return (async (): Promise<number> => {
|
|
466
|
+
try {
|
|
467
|
+
reportWalls(
|
|
468
|
+
await runWallInteractive(
|
|
469
|
+
wallCwd,
|
|
470
|
+
internals?.wallCheckbox ? { checkbox: internals.wallCheckbox } : {},
|
|
471
|
+
),
|
|
472
|
+
);
|
|
473
|
+
return 0;
|
|
474
|
+
} catch (err) {
|
|
475
|
+
io.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
476
|
+
return 1;
|
|
477
|
+
}
|
|
478
|
+
})();
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (command === "defold") {
|
|
482
|
+
const subcommand = rest[0];
|
|
483
|
+
const defoldCwd = rest[1] ? path.resolve(rest[1]) : process.cwd();
|
|
484
|
+
if (!isDefoldSubcommand(subcommand)) {
|
|
485
|
+
io.stderr.write(DEFOLD_USAGE);
|
|
486
|
+
return 1;
|
|
487
|
+
}
|
|
488
|
+
const javaOverride = javaFlag ?? process.env.DEFOLD_JAVA;
|
|
489
|
+
const defoldIo: DefoldIo = { ...defaultDefoldIo(), ...internals?.defoldIo };
|
|
490
|
+
return (async (): Promise<number> => {
|
|
491
|
+
try {
|
|
492
|
+
const result = await runDefoldCommand({
|
|
493
|
+
cwd: defoldCwd,
|
|
494
|
+
subcommand,
|
|
495
|
+
...(javaOverride !== undefined ? { java: javaOverride } : {}),
|
|
496
|
+
...(buildServerFlag !== undefined ? { buildServer: buildServerFlag } : {}),
|
|
497
|
+
io: defoldIo,
|
|
498
|
+
});
|
|
499
|
+
if (json) {
|
|
500
|
+
io.stdout.write(
|
|
501
|
+
renderResult(
|
|
502
|
+
result.ok
|
|
503
|
+
? { command: "defold", subcommand: result.subcommand, exitCode: result.exitCode }
|
|
504
|
+
: {
|
|
505
|
+
command: "defold",
|
|
506
|
+
subcommand: result.subcommand,
|
|
507
|
+
exitCode: result.exitCode,
|
|
508
|
+
error: `bob ${result.subcommand} exited with code ${result.exitCode}`,
|
|
509
|
+
},
|
|
510
|
+
),
|
|
511
|
+
);
|
|
512
|
+
} else if (!result.ok) {
|
|
513
|
+
io.stderr.write(
|
|
514
|
+
`defold-typescript defold ${result.subcommand}: bob exited with code ${result.exitCode}\n`,
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
return result.exitCode;
|
|
518
|
+
} catch (err) {
|
|
519
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
520
|
+
if (json) {
|
|
521
|
+
io.stdout.write(renderResult({ command: "defold", subcommand, error: message }));
|
|
522
|
+
} else {
|
|
523
|
+
io.stderr.write(`${message}\n`);
|
|
524
|
+
}
|
|
525
|
+
return 1;
|
|
526
|
+
}
|
|
527
|
+
})();
|
|
528
|
+
}
|
|
529
|
+
|
|
284
530
|
io.stderr.write(USAGE);
|
|
285
531
|
return 1;
|
|
286
532
|
}
|
package/src/init.ts
CHANGED
|
@@ -5,12 +5,7 @@ import type { ScriptHookName } from "@defold-typescript/types";
|
|
|
5
5
|
import { DEBUG_LAUNCHER_SOURCE, debugLaunchConfig, VSCODE_LAUNCH_CONTENT } from "./debug-launcher";
|
|
6
6
|
import { CURRENT_STABLE_DEFOLD_VERSION } from "./defold-version";
|
|
7
7
|
import { mergeMiseToml } from "./mise-scaffold";
|
|
8
|
-
import {
|
|
9
|
-
detectScriptKinds,
|
|
10
|
-
type ScriptKind,
|
|
11
|
-
selectScriptKind,
|
|
12
|
-
selectScriptKindEntrypoint,
|
|
13
|
-
} from "./script-kind";
|
|
8
|
+
import { DEFAULT_TYPES_ENTRYPOINT } from "./script-kind";
|
|
14
9
|
|
|
15
10
|
export interface RunInitOptions {
|
|
16
11
|
readonly cwd: string;
|
|
@@ -19,7 +14,6 @@ export interface RunInitOptions {
|
|
|
19
14
|
|
|
20
15
|
export interface RunInitResult {
|
|
21
16
|
readonly written: string[];
|
|
22
|
-
readonly scriptKind: ScriptKind | null;
|
|
23
17
|
}
|
|
24
18
|
|
|
25
19
|
const CONFLICTING_TS_CONFIGS = [
|
|
@@ -38,12 +32,30 @@ const TSCONFIG_COMPILER_OPTIONS = {
|
|
|
38
32
|
skipLibCheck: true,
|
|
39
33
|
};
|
|
40
34
|
|
|
41
|
-
const GITIGNORE_LINES = [
|
|
35
|
+
const GITIGNORE_LINES = [
|
|
36
|
+
"src/**/*.ts.script",
|
|
37
|
+
"src/**/*.ts.script.map",
|
|
38
|
+
"src/**/*.ts.gui_script",
|
|
39
|
+
"src/**/*.ts.gui_script.map",
|
|
40
|
+
"src/**/*.ts.render_script",
|
|
41
|
+
"src/**/*.ts.render_script.map",
|
|
42
|
+
"src/**/*.lua",
|
|
43
|
+
"src/**/*.lua.map",
|
|
44
|
+
];
|
|
42
45
|
|
|
43
46
|
const BIOME_JSON_CONTENT = {
|
|
44
47
|
$schema: "https://biomejs.dev/schemas/2.4.15/schema.json",
|
|
45
48
|
files: {
|
|
46
|
-
includes: [
|
|
49
|
+
includes: [
|
|
50
|
+
"src/**/*.ts",
|
|
51
|
+
"!**/dist",
|
|
52
|
+
"!**/node_modules",
|
|
53
|
+
"!**/*.ts.script",
|
|
54
|
+
"!**/*.ts.gui_script",
|
|
55
|
+
"!**/*.ts.render_script",
|
|
56
|
+
"!src/**/*.lua",
|
|
57
|
+
"!src/**/*.lua.map",
|
|
58
|
+
],
|
|
47
59
|
},
|
|
48
60
|
formatter: {
|
|
49
61
|
enabled: true,
|
|
@@ -76,10 +88,17 @@ const BIOME_JSON_CONTENT = {
|
|
|
76
88
|
};
|
|
77
89
|
|
|
78
90
|
const VSCODE_EXTENSIONS_CONTENT = {
|
|
79
|
-
recommendations: ["
|
|
91
|
+
recommendations: ["tomblind.local-lua-debugger-vscode"],
|
|
80
92
|
unwantedRecommendations: ["johnnymorganz.luau-lsp"],
|
|
81
93
|
};
|
|
82
94
|
|
|
95
|
+
const MANAGED_RECOMMENDATIONS = [
|
|
96
|
+
"tomblind.local-lua-debugger-vscode",
|
|
97
|
+
"sumneko.lua",
|
|
98
|
+
"astronachos.defold",
|
|
99
|
+
];
|
|
100
|
+
const MANAGED_UNWANTED = ["johnnymorganz.luau-lsp"];
|
|
101
|
+
|
|
83
102
|
const VSCODE_SETTINGS_CONTENT = {
|
|
84
103
|
"Lua.workspace.ignoreDir": ["src"],
|
|
85
104
|
};
|
|
@@ -154,7 +173,7 @@ function inlineSnippetBody(factory: string, includeOnInput: boolean): string[] {
|
|
|
154
173
|
return [
|
|
155
174
|
`import { ${factory} } from "@defold-typescript/types";`,
|
|
156
175
|
"",
|
|
157
|
-
`export
|
|
176
|
+
`export default ${factory}({`,
|
|
158
177
|
` // ${HOOK_COMMENTS.init}`,
|
|
159
178
|
" init() {",
|
|
160
179
|
" return { $0 };",
|
|
@@ -173,7 +192,7 @@ function typedSnippetBody(factory: string, includeOnInput: boolean): string[] {
|
|
|
173
192
|
" $1",
|
|
174
193
|
"};",
|
|
175
194
|
"",
|
|
176
|
-
`export
|
|
195
|
+
`export default ${factory}<Self>({`,
|
|
177
196
|
` // ${HOOK_COMMENTS.init}`,
|
|
178
197
|
" init(): Self {",
|
|
179
198
|
" return { $0 };",
|
|
@@ -222,23 +241,21 @@ const VSCODE_SNIPPETS_CONTENT: Record<string, VscodeSnippet> = {
|
|
|
222
241
|
},
|
|
223
242
|
};
|
|
224
243
|
|
|
225
|
-
const MAIN_TS_CONTENT = `
|
|
226
|
-
const start = vmath.vector3(0, 0, 0);
|
|
227
|
-
msg.post("main:/hero", "spawn", { start });
|
|
228
|
-
}
|
|
229
|
-
`;
|
|
244
|
+
const MAIN_TS_CONTENT = `import { defineScript } from "@defold-typescript/types";
|
|
230
245
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
246
|
+
export default defineScript({
|
|
247
|
+
init() {
|
|
248
|
+
const start = vmath.vector3(0, 0, 0);
|
|
249
|
+
return { start };
|
|
250
|
+
},
|
|
251
|
+
});
|
|
235
252
|
`;
|
|
236
253
|
|
|
237
254
|
const MAIN_COLLECTION_CONTENT = `name: "main"
|
|
238
255
|
scale_along_z: 0
|
|
239
256
|
embedded_instances {
|
|
240
257
|
id: "main"
|
|
241
|
-
data: "components {\\n id: \\"main\\"\\n component: \\"/
|
|
258
|
+
data: "components {\\n id: \\"main\\"\\n component: \\"/src/main.ts.script\\"\\n}\\n"
|
|
242
259
|
position { x: 0.0 y: 0.0 z: 0.0 }
|
|
243
260
|
rotation { x: 0.0 y: 0.0 z: 0.0 w: 1.0 }
|
|
244
261
|
scale3 { x: 1.0 y: 1.0 z: 1.0 }
|
|
@@ -270,8 +287,8 @@ function typesVersionSpec(): string {
|
|
|
270
287
|
}
|
|
271
288
|
|
|
272
289
|
// @defold-typescript/types (type-only, for the editor) and @defold-typescript/cli
|
|
273
|
-
// (the local bin the managed `bunx
|
|
274
|
-
//
|
|
290
|
+
// (the local bin the managed `bunx @defold-typescript/cli` mise tasks resolve
|
|
291
|
+
// inside an installed project) both ship into the consumer. The transpiler must NOT be a direct
|
|
275
292
|
// consumer dep — it arrives transitively through the CLI. Pin both managed deps
|
|
276
293
|
// to this CLI's own version so the coordinated-release set stays in lockstep.
|
|
277
294
|
export const SCAFFOLD_DEV_DEPS: Record<string, string> = {
|
|
@@ -400,6 +417,34 @@ function unionStrings(existing: unknown, additions: readonly string[]): string[]
|
|
|
400
417
|
return out;
|
|
401
418
|
}
|
|
402
419
|
|
|
420
|
+
export function reconcileManagedList(
|
|
421
|
+
existing: unknown,
|
|
422
|
+
managed: readonly string[],
|
|
423
|
+
canonical: readonly string[],
|
|
424
|
+
): string[] {
|
|
425
|
+
const managedSet = new Set(managed);
|
|
426
|
+
const canonicalSet = new Set(canonical);
|
|
427
|
+
const out: string[] = [];
|
|
428
|
+
const values = Array.isArray(existing)
|
|
429
|
+
? existing.filter((value): value is string => typeof value === "string")
|
|
430
|
+
: [];
|
|
431
|
+
for (const value of values) {
|
|
432
|
+
if (out.includes(value)) {
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
if (managedSet.has(value) && !canonicalSet.has(value)) {
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
out.push(value);
|
|
439
|
+
}
|
|
440
|
+
for (const value of canonical) {
|
|
441
|
+
if (!out.includes(value)) {
|
|
442
|
+
out.push(value);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return out;
|
|
446
|
+
}
|
|
447
|
+
|
|
403
448
|
function readVscodeJson(filePath: string): Record<string, unknown> | null {
|
|
404
449
|
try {
|
|
405
450
|
const parsed = parseJsonc(readFileSync(filePath, "utf8"));
|
|
@@ -417,15 +462,20 @@ function writeVscodeExtensions(cwd: string, written: string[]): void {
|
|
|
417
462
|
if (existing === null) {
|
|
418
463
|
return;
|
|
419
464
|
}
|
|
420
|
-
|
|
465
|
+
const before = JSON.stringify(existing);
|
|
466
|
+
existing.recommendations = reconcileManagedList(
|
|
421
467
|
existing.recommendations,
|
|
468
|
+
MANAGED_RECOMMENDATIONS,
|
|
422
469
|
VSCODE_EXTENSIONS_CONTENT.recommendations,
|
|
423
470
|
);
|
|
424
|
-
existing.unwantedRecommendations =
|
|
471
|
+
existing.unwantedRecommendations = reconcileManagedList(
|
|
425
472
|
existing.unwantedRecommendations,
|
|
473
|
+
MANAGED_UNWANTED,
|
|
426
474
|
VSCODE_EXTENSIONS_CONTENT.unwantedRecommendations,
|
|
427
475
|
);
|
|
428
|
-
|
|
476
|
+
if (JSON.stringify(existing) !== before) {
|
|
477
|
+
writeJson(filePath, existing);
|
|
478
|
+
}
|
|
429
479
|
return;
|
|
430
480
|
}
|
|
431
481
|
mkdirSync(dir, { recursive: true });
|
|
@@ -509,16 +559,15 @@ function writeVscodeDebugLauncher(cwd: string, written: string[]): void {
|
|
|
509
559
|
written.push(".vscode/defold-debug.ts");
|
|
510
560
|
}
|
|
511
561
|
|
|
512
|
-
function writeTsSurface(cwd: string, written: string[], force = false):
|
|
562
|
+
function writeTsSurface(cwd: string, written: string[], force = false): void {
|
|
513
563
|
mkdirSync(path.join(cwd, "src"), { recursive: true });
|
|
514
564
|
writeFileSync(path.join(cwd, "src", "main.ts"), MAIN_TS_CONTENT);
|
|
515
565
|
written.push("src/main.ts");
|
|
516
566
|
|
|
517
|
-
const kinds = detectScriptKinds(cwd);
|
|
518
567
|
const tsconfig = {
|
|
519
568
|
compilerOptions: {
|
|
520
569
|
...TSCONFIG_COMPILER_OPTIONS,
|
|
521
|
-
types: [
|
|
570
|
+
types: [DEFAULT_TYPES_ENTRYPOINT],
|
|
522
571
|
},
|
|
523
572
|
include: ["src/**/*.ts"],
|
|
524
573
|
};
|
|
@@ -561,8 +610,6 @@ function writeTsSurface(cwd: string, written: string[], force = false): ScriptKi
|
|
|
561
610
|
writeVscodeSnippets(cwd, written);
|
|
562
611
|
writeVscodeLaunch(cwd, written);
|
|
563
612
|
writeVscodeDebugLauncher(cwd, written);
|
|
564
|
-
|
|
565
|
-
return selectScriptKind(kinds);
|
|
566
613
|
}
|
|
567
614
|
|
|
568
615
|
export function runNewProjectInit(cwd: string, force = false): RunInitResult {
|
|
@@ -585,12 +632,10 @@ export function runNewProjectInit(cwd: string, force = false): RunInitResult {
|
|
|
585
632
|
mkdirSync(path.join(cwd, "main"), { recursive: true });
|
|
586
633
|
writeFileSync(path.join(cwd, "main", "main.collection"), MAIN_COLLECTION_CONTENT);
|
|
587
634
|
written.push("main/main.collection");
|
|
588
|
-
writeFileSync(path.join(cwd, "main", "main.script"), MAIN_SCRIPT_CONTENT);
|
|
589
|
-
written.push("main/main.script");
|
|
590
635
|
|
|
591
|
-
|
|
636
|
+
writeTsSurface(cwd, written, force);
|
|
592
637
|
|
|
593
|
-
return { written
|
|
638
|
+
return { written };
|
|
594
639
|
}
|
|
595
640
|
|
|
596
641
|
export function runInit(opts: RunInitOptions): RunInitResult {
|
|
@@ -611,6 +656,6 @@ export function runInit(opts: RunInitOptions): RunInitResult {
|
|
|
611
656
|
}
|
|
612
657
|
|
|
613
658
|
const written: string[] = [];
|
|
614
|
-
|
|
615
|
-
return { written
|
|
659
|
+
writeTsSurface(cwd, written, force);
|
|
660
|
+
return { written };
|
|
616
661
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// The runner (`bunx`/`npx`/`pnpm dlx`/`yarn dlx`) sets `npm_config_user_agent`
|
|
2
|
+
// with the manager as its first token; when the bin is run directly the var is
|
|
3
|
+
// unset. A wrong guess only mis-advises — it never writes a lockfile — so the
|
|
4
|
+
// fallback to bun (the repo's primary manager) is safe.
|
|
5
|
+
export function installHint(env: NodeJS.ProcessEnv = process.env): string {
|
|
6
|
+
const agent = env.npm_config_user_agent ?? "";
|
|
7
|
+
const manager = agent.split("/")[0];
|
|
8
|
+
if (manager === "pnpm") {
|
|
9
|
+
return "pnpm install";
|
|
10
|
+
}
|
|
11
|
+
if (manager === "yarn") {
|
|
12
|
+
return "yarn install";
|
|
13
|
+
}
|
|
14
|
+
if (manager === "npm") {
|
|
15
|
+
return "npm install";
|
|
16
|
+
}
|
|
17
|
+
return "bun install";
|
|
18
|
+
}
|