@nanoforge-dev/cli 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,7 +11,7 @@ var __decorateClass = (decorators, target, key, kind) => {
11
11
  };
12
12
 
13
13
  // src/command/command.loader.ts
14
- import { red as red8 } from "ansis";
14
+ import { red as red6 } from "ansis";
15
15
 
16
16
  // src/lib/ui/messages.ts
17
17
  import { green } from "ansis";
@@ -39,48 +39,63 @@ var Emojis = {
39
39
  };
40
40
 
41
41
  // src/lib/ui/messages.ts
42
+ var success = /* @__PURE__ */ __name((text) => `${Emojis.ROCKET} ${text}`, "success");
43
+ var failure = /* @__PURE__ */ __name((text) => `${Emojis.SCREAM} ${text}`, "failure");
42
44
  var Messages = {
45
+ // --- Build ---
43
46
  BUILD_START: "NanoForge Build",
44
- BUILD_WATCH_START: "Start watching mode",
47
+ BUILD_SUCCESS: success("Build succeeded!"),
48
+ BUILD_FAILED: failure("Build failed!"),
49
+ BUILD_WATCH_START: "Watching for changes...",
45
50
  BUILD_PART_IN_PROGRESS: /* @__PURE__ */ __name((part) => `Building ${part}`, "BUILD_PART_IN_PROGRESS"),
46
- BUILD_PART_WATCH_IN_PROGRESS: /* @__PURE__ */ __name((part) => `${part} updated. Rebuilding`, "BUILD_PART_WATCH_IN_PROGRESS"),
47
- BUILD_NOTHING: "Nothing to build, terminated.",
48
- BUILD_SUCCESS: `${Emojis.ROCKET} Build succeeded !`,
49
- BUILD_PART_FAILED: /* @__PURE__ */ __name((part, commandToRunManually) => `${Emojis.SCREAM} Build of ${part} failed !
50
- In case you don't see any errors above, consider manually running the failed command ${commandToRunManually} to see more details on why it errored out.`, "BUILD_PART_FAILED"),
51
- BUILD_FAILED: `${Emojis.SCREAM} Build failed !`,
51
+ BUILD_PART_WATCH_IN_PROGRESS: /* @__PURE__ */ __name((part) => `${part} updated, rebuilding`, "BUILD_PART_WATCH_IN_PROGRESS"),
52
+ BUILD_PART_FAILED: /* @__PURE__ */ __name((part, command) => failure(`Build of ${part} failed!
53
+ Try running manually: ${command}`), "BUILD_PART_FAILED"),
54
+ // --- Install ---
52
55
  INSTALL_START: "NanoForge Installation",
53
- INSTALL_NAMES_QUESTION: "Witch libraries do you want to install ?",
56
+ INSTALL_SUCCESS: success("Installation completed!"),
57
+ INSTALL_FAILED: failure("Installation failed!"),
58
+ INSTALL_NAMES_QUESTION: "Which libraries do you want to install?",
59
+ // --- New Project ---
54
60
  NEW_START: "NanoForge Project Creation",
55
- NEW_SUCCESS: `${Emojis.ROCKET} Project successfully created !`,
56
- NEW_FAILED: `${Emojis.SCREAM} Project creation failed !`,
57
- NEW_NAME_QUESTION: "What is the name of your project ?",
58
- NEW_PACKAGE_MANAGER_QUESTION: "Which package manager do you want to use ?",
59
- NEW_LANGUAGE_QUESTION: "Which language do you want to use ?",
60
- NEW_STRICT_QUESTION: "Do you want to use types strict mode ?",
61
- NEW_SERVER_QUESTION: "Do you want generate a server to create a multiplayer game ?",
62
- NEW_SKIP_INSTALL_QUESTION: "Do you want to skip installation ?",
61
+ NEW_SUCCESS: success("Project successfully created!"),
62
+ NEW_FAILED: failure("Project creation failed!"),
63
+ NEW_NAME_QUESTION: "What is the name of your project?",
64
+ NEW_PACKAGE_MANAGER_QUESTION: "Which package manager do you want to use?",
65
+ NEW_LANGUAGE_QUESTION: "Which language do you want to use?",
66
+ NEW_STRICT_QUESTION: "Do you want to use strict type checking?",
67
+ NEW_SERVER_QUESTION: "Do you want to generate a server for multiplayer?",
68
+ NEW_SKIP_INSTALL_QUESTION: "Do you want to skip dependency installation?",
69
+ NEW_DOCKER_QUESTION: "Do you want to add a Dockerfile for containerization?",
70
+ // --- Generate ---
63
71
  GENERATE_START: "NanoForge Generate",
64
- GENERATE_WATCH_START: "Start watching mode",
65
- GENERATE_SUCCESS: `${Emojis.ROCKET} Generate succeeded !`,
66
- GENERATE_FAILED: `${Emojis.SCREAM} Generate failed !`,
67
- DEV_START: "NanoForge Dev mode",
72
+ GENERATE_SUCCESS: success("Generation succeeded!"),
73
+ GENERATE_FAILED: failure("Generation failed!"),
74
+ GENERATE_WATCH_START: "Watching for changes...",
75
+ // --- Dev ---
76
+ DEV_START: "NanoForge Dev Mode",
68
77
  DEV_SUCCESS: "Dev mode ended",
69
- DEV_FAILED: `${Emojis.SCREAM} Dev failed !`,
70
- SCHEMATICS_START: "Schematics execution",
71
- SCHEMATIC_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Executing schematic ${name}...`, "SCHEMATIC_IN_PROGRESS"),
72
- SCHEMATIC_WATCH_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Update watched. Executing schematic ${name}...`, "SCHEMATIC_WATCH_IN_PROGRESS"),
73
- SCHEMATIC_SUCCESS: /* @__PURE__ */ __name((name) => `${Emojis.ROCKET} Schematic ${name} executed successfully !`, "SCHEMATIC_SUCCESS"),
74
- SCHEMATIC_FAILED: /* @__PURE__ */ __name((name) => `${Emojis.SCREAM} Schematic ${name} execution failed. See error below for more details.`, "SCHEMATIC_FAILED"),
75
- PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS: `Installation in progress... ${Emojis.COFFEE}`,
76
- PACKAGE_MANAGER_INSTALLATION_NOTHING: "Nothing to install, terminated.",
77
- PACKAGE_MANAGER_INSTALLATION_SUCCEED: /* @__PURE__ */ __name((names) => names ? `${Emojis.ROCKET} Packages successfully installed : ${names.map((name) => green(name)).join(", ")} !` : `${Emojis.ROCKET} Packages successfully installed !`, "PACKAGE_MANAGER_INSTALLATION_SUCCEED"),
78
- PACKAGE_MANAGER_INSTALLATION_FAILED: /* @__PURE__ */ __name((commandToRunManually) => `${Emojis.SCREAM} Packages installation failed !
79
- In case you don't see any errors above, consider manually running the failed command ${commandToRunManually} to see more details on why it errored out.`, "PACKAGE_MANAGER_INSTALLATION_FAILED"),
80
- RUN_START: "NanoForge Run",
81
- RUN_PART_IN_PROGRESS: /* @__PURE__ */ __name((part) => `Running ${part}...`, "RUN_PART_IN_PROGRESS"),
82
- RUN_PART_SUCCESS: /* @__PURE__ */ __name((part) => `${Emojis.ROCKET} Run of ${part} terminated.`, "RUN_PART_SUCCESS"),
83
- RUN_PART_FAILED: /* @__PURE__ */ __name((part) => `${Emojis.SCREAM} Run of ${part} failed !`, "RUN_PART_FAILED"),
78
+ DEV_FAILED: failure("Dev mode failed!"),
79
+ // --- Start ---
80
+ START_START: "NanoForge Start",
81
+ START_SUCCESS: success("Start completed!"),
82
+ START_FAILED: failure("Start failed!"),
83
+ START_PART_IN_PROGRESS: /* @__PURE__ */ __name((part) => `Starting ${part}...`, "START_PART_IN_PROGRESS"),
84
+ START_PART_SUCCESS: /* @__PURE__ */ __name((part) => success(`${part} terminated.`), "START_PART_SUCCESS"),
85
+ START_PART_FAILED: /* @__PURE__ */ __name((part) => failure(`${part} failed!`), "START_PART_FAILED"),
86
+ // --- Schematics ---
87
+ SCHEMATICS_START: "Running schematics",
88
+ SCHEMATIC_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Generating ${name}...`, "SCHEMATIC_IN_PROGRESS"),
89
+ SCHEMATIC_WATCH_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Change detected, regenerating ${name}...`, "SCHEMATIC_WATCH_IN_PROGRESS"),
90
+ SCHEMATIC_SUCCESS: /* @__PURE__ */ __name((name) => success(`${name} generated successfully!`), "SCHEMATIC_SUCCESS"),
91
+ SCHEMATIC_FAILED: /* @__PURE__ */ __name((name) => failure(`${name} generation failed.`), "SCHEMATIC_FAILED"),
92
+ // --- Package Manager ---
93
+ PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS: `Installing dependencies... ${Emojis.COFFEE}`,
94
+ PACKAGE_MANAGER_INSTALLATION_NOTHING: "Nothing to install.",
95
+ PACKAGE_MANAGER_INSTALLATION_SUCCEED: /* @__PURE__ */ __name((names) => names ? success(`Packages installed: ${names.map((n) => green(n)).join(", ")}`) : success("Packages installed!"), "PACKAGE_MANAGER_INSTALLATION_SUCCEED"),
96
+ PACKAGE_MANAGER_INSTALLATION_FAILED: /* @__PURE__ */ __name((command) => failure(`Package installation failed!
97
+ Try running manually: ${command}`), "PACKAGE_MANAGER_INSTALLATION_FAILED"),
98
+ // --- Runner ---
84
99
  RUNNER_EXECUTION_ERROR: /* @__PURE__ */ __name((command) => `
85
100
  Failed to execute command: ${command}`, "RUNNER_EXECUTION_ERROR")
86
101
  };
@@ -92,10 +107,12 @@ var Prefixes = {
92
107
  ERROR: bgRgb(210, 0, 75).bold.rgb(0, 0, 0)(" Error ")
93
108
  };
94
109
 
110
+ // src/lib/ui/spinner.ts
111
+ import ora from "ora";
112
+ var getSpinner = /* @__PURE__ */ __name((message) => ora({ text: message }), "getSpinner");
113
+
95
114
  // src/action/actions/build.action.ts
96
- import * as ansis from "ansis";
97
115
  import { watch } from "chokidar";
98
- import * as console2 from "console";
99
116
  import { dirname, join as join3 } from "path";
100
117
 
101
118
  // src/lib/input/base-inputs.ts
@@ -159,12 +176,24 @@ var getDevGenerateInput = /* @__PURE__ */ __name((inputs) => {
159
176
  import { confirm } from "@inquirer/prompts";
160
177
 
161
178
  // src/lib/utils/errors.ts
179
+ import { red } from "ansis";
180
+ var getErrorMessage = /* @__PURE__ */ __name((error) => {
181
+ if (error instanceof Error) return error.message;
182
+ if (typeof error === "string") return error;
183
+ return void 0;
184
+ }, "getErrorMessage");
185
+ var handleActionError = /* @__PURE__ */ __name((context, error) => {
186
+ console.error();
187
+ console.error(red(context));
188
+ const msg = getErrorMessage(error);
189
+ if (msg) console.error(msg);
190
+ process.exit(1);
191
+ }, "handleActionError");
162
192
  var promptError = /* @__PURE__ */ __name((err) => {
163
193
  if (err.name === "ExitPromptError") {
164
194
  process.exit(1);
165
- } else {
166
- throw err;
167
195
  }
196
+ throw err;
168
197
  }, "promptError");
169
198
 
170
199
  // src/lib/question/questions/confirm.question.ts
@@ -236,79 +265,27 @@ var getInstallNamesInputOrAsk = /* @__PURE__ */ __name((inputs) => {
236
265
  );
237
266
  }, "getInstallNamesInputOrAsk");
238
267
 
239
- // src/lib/package-manager/package-manager.factory.ts
240
- import fs2 from "fs";
241
- import { resolve as resolve2 } from "path";
268
+ // src/lib/package-manager/package-manager.ts
269
+ import { bold, red as red3 } from "ansis";
242
270
 
243
- // src/lib/runner/runner.factory.ts
244
- import { yellow } from "ansis";
245
-
246
- // src/lib/runner/abstract.runner.ts
247
- import { red } from "ansis";
248
- import { spawn } from "child_process";
249
- import * as process2 from "process";
250
- var AbstractRunner = class {
251
- constructor(binary, args = []) {
252
- this.binary = binary;
253
- this.args = args;
271
+ // src/lib/runner/process-logger.ts
272
+ import { green as green2, red as red2, yellow } from "ansis";
273
+ var formatLines = /* @__PURE__ */ __name((chunk) => {
274
+ return chunk.toString().replace(/\r\n|\n/g, "\n").replace(/^\n+|\n+$/g, "").split("\n");
275
+ }, "formatLines");
276
+ var timestamp = /* @__PURE__ */ __name(() => yellow(`[${(/* @__PURE__ */ new Date()).toISOString()}]`), "timestamp");
277
+ var createStdoutLogger = /* @__PURE__ */ __name((name) => (chunk) => {
278
+ const prefix = green2(`(${name}) INFO -`);
279
+ for (const line of formatLines(chunk)) {
280
+ console.info(`${timestamp()} ${prefix} ${line}`);
254
281
  }
255
- static {
256
- __name(this, "AbstractRunner");
282
+ }, "createStdoutLogger");
283
+ var createStderrLogger = /* @__PURE__ */ __name((name) => (chunk) => {
284
+ const prefix = red2(`(${name}) ERROR -`);
285
+ for (const line of formatLines(chunk)) {
286
+ console.error(`${timestamp()} ${prefix} ${line}`);
257
287
  }
258
- async run(args, collect = false, cwd2 = process2.cwd(), env2, listeners, failSpinner) {
259
- const options = {
260
- cwd: cwd2,
261
- stdio: collect ? "pipe" : "inherit",
262
- shell: true,
263
- env: { ...process2.env, ...env2 }
264
- };
265
- return new Promise((resolve3, reject) => {
266
- const child = spawn(
267
- `${this.binary} ${[...this.args, ...args].join(" ")}`,
268
- options
269
- );
270
- const res = [];
271
- child.stdout?.on(
272
- "data",
273
- listeners?.onStdout ?? ((data) => res.push(data.toString().replace(/\r\n|\n/, "")))
274
- );
275
- child.stderr?.on(
276
- "data",
277
- listeners?.onStderr ?? ((data) => res.push(data.toString().replace(/\r\n|\n/, "")))
278
- );
279
- child.on("close", (code) => {
280
- if (code === 0) {
281
- resolve3(collect && res.length ? res.join("\n") : null);
282
- } else {
283
- if (failSpinner) failSpinner();
284
- console.error(
285
- red(Messages.RUNNER_EXECUTION_ERROR([this.binary, ...this.args, ...args].join(" ")))
286
- );
287
- if (res.length) {
288
- console.error();
289
- console.error(res.join("\n"));
290
- console.error();
291
- }
292
- reject();
293
- }
294
- });
295
- });
296
- }
297
- rawFullCommand(args) {
298
- const commandArgs = [...this.args, ...args];
299
- return `${this.binary} ${commandArgs.join(" ")}`;
300
- }
301
- };
302
-
303
- // src/lib/runner/runners/bun.runner.ts
304
- var BunRunner = class extends AbstractRunner {
305
- static {
306
- __name(this, "BunRunner");
307
- }
308
- constructor() {
309
- super("bun");
310
- }
311
- };
288
+ }, "createStderrLogger");
312
289
 
313
290
  // src/lib/utils/path.ts
314
291
  import fs from "fs";
@@ -335,268 +312,256 @@ var resolveCLINodeBinaryPath = /* @__PURE__ */ __name((name) => {
335
312
  throw new Error("Could not find module path");
336
313
  }, "resolveCLINodeBinaryPath");
337
314
 
338
- // src/lib/runner/runners/local-bun.runner.ts
339
- var LocalBunRunner = class extends AbstractRunner {
340
- static {
341
- __name(this, "LocalBunRunner");
342
- }
343
- constructor() {
344
- super(resolveCLINodeBinaryPath("bun"));
345
- }
346
- };
347
-
348
- // src/lib/runner/runners/npm.runner.ts
349
- var NpmRunner = class extends AbstractRunner {
350
- static {
351
- __name(this, "NpmRunner");
352
- }
353
- constructor() {
354
- super("npm");
355
- }
356
- };
357
-
358
- // src/lib/runner/runners/pnpm.runner.ts
359
- var PnpmRunner = class extends AbstractRunner {
360
- static {
361
- __name(this, "PnpmRunner");
362
- }
363
- constructor() {
364
- super("pnpm");
365
- }
366
- };
367
-
368
- // src/lib/runner/runners/schematic.runner.ts
369
- var SchematicRunner = class _SchematicRunner extends AbstractRunner {
370
- static {
371
- __name(this, "SchematicRunner");
372
- }
373
- static getModulePaths() {
374
- return module.paths;
375
- }
376
- static findClosestSchematicsBinary() {
377
- try {
378
- return getModulePath("@angular-devkit/schematics-cli/bin/schematics.js");
379
- } catch (e) {
380
- console.error(e);
381
- throw new Error("'schematics' binary path could not be found!");
382
- }
383
- }
384
- constructor() {
385
- super(`node`, [`"${_SchematicRunner.findClosestSchematicsBinary()}"`]);
386
- }
387
- };
388
-
389
- // src/lib/runner/runners/yarn.runner.ts
390
- var YarnRunner = class extends AbstractRunner {
391
- static {
392
- __name(this, "YarnRunner");
393
- }
394
- constructor() {
395
- super("yarn");
396
- }
397
- };
398
-
399
- // src/lib/runner/runner.factory.ts
400
- var RunnerFactory = class {
401
- static {
402
- __name(this, "RunnerFactory");
403
- }
404
- static create(runner) {
405
- switch (runner) {
406
- case 0 /* BUN */:
407
- return new BunRunner();
408
- case 1 /* LOCAL_BUN */:
409
- return new LocalBunRunner();
410
- case 2 /* NPM */:
411
- return new NpmRunner();
412
- case 3 /* PNPM */:
413
- return new PnpmRunner();
414
- case 4 /* SCHEMATIC */:
415
- return new SchematicRunner();
416
- case 5 /* YARN */:
417
- return new YarnRunner();
418
- default:
419
- console.info(yellow`[WARN] Unsupported runner: ${runner}`);
420
- throw Error(`Unsupported runner: ${runner}`);
421
- }
422
- }
423
- };
424
-
425
- // src/lib/package-manager/abstract.package-manager.ts
426
- import { bold, green as green2, red as red2, yellow as yellow2 } from "ansis";
427
- import ora from "ora";
428
- var SPINNER = /* @__PURE__ */ __name((message) => ora({
429
- text: message
430
- }), "SPINNER");
431
- var FAIL_SPINNER = /* @__PURE__ */ __name((spinner) => () => spinner.fail(), "FAIL_SPINNER");
432
- var AbstractPackageManager = class {
433
- constructor(runner) {
315
+ // src/lib/package-manager/package-manager.ts
316
+ var PackageManager = class {
317
+ constructor(name, commands, runner) {
318
+ this.name = name;
319
+ this.commands = commands;
434
320
  this.runner = runner;
435
321
  }
436
322
  static {
437
- __name(this, "AbstractPackageManager");
323
+ __name(this, "PackageManager");
438
324
  }
439
325
  async install(directory) {
440
- const spinner = SPINNER(Messages.PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS);
441
- spinner.start();
442
- try {
443
- const commandArgs = [this.cli.install, this.cli.silentFlag];
444
- const collect = true;
445
- await this.runner.run(
446
- commandArgs,
447
- collect,
448
- getCwd(directory),
449
- void 0,
450
- void 0,
451
- () => spinner.fail()
452
- );
453
- spinner.succeed();
454
- this.printInstallSuccess();
455
- } catch {
456
- const commandArgs = [this.cli.install];
457
- const commandToRun = this.runner.rawFullCommand(commandArgs);
458
- this.printInstallFailure(commandToRun);
459
- }
460
- }
461
- version() {
462
- const commandArguments = ["--version"];
463
- const collect = true;
464
- return this.runner.run(commandArguments, collect);
465
- }
466
- addProduction(directory, dependencies) {
467
- const command = [this.cli.add, this.cli.saveFlag];
468
- return this.add(command, directory, dependencies);
469
- }
470
- addDevelopment(directory, dependencies) {
471
- const command = [this.cli.add, this.cli.saveDevFlag];
472
- return this.add(command, directory, dependencies);
473
- }
474
- async build(name, directory, entry, output, flags, watch3) {
475
- if (!this.cli.build) throw new Error(`Package manager ${this.name} does not support building`);
476
- const spinner = SPINNER(
477
- (watch3 ? Messages.BUILD_PART_WATCH_IN_PROGRESS : Messages.BUILD_PART_IN_PROGRESS)(name)
326
+ const args = [this.commands.install, this.commands.silentFlag];
327
+ const result = await this.withSpinner(
328
+ Messages.PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS,
329
+ async (spinner) => {
330
+ await this.exec(args, directory, { onFail: /* @__PURE__ */ __name(() => spinner.fail(), "onFail") });
331
+ this.logSuccess(Messages.PACKAGE_MANAGER_INSTALLATION_SUCCEED());
332
+ },
333
+ () => this.logFailure(this.formatFailCommand([this.commands.install]))
478
334
  );
479
- spinner.start();
480
- try {
481
- const commandArgs = [
482
- this.cli.build,
483
- this.cli.silentFlag,
484
- entry,
485
- "--outdir",
486
- output,
487
- ...flags ?? []
488
- ];
489
- const collect = true;
490
- await this.runner.run(
491
- commandArgs,
492
- collect,
493
- getCwd(directory),
494
- void 0,
495
- void 0,
496
- FAIL_SPINNER(spinner)
497
- );
498
- spinner.succeed();
499
- return true;
500
- } catch {
501
- const commandArgs = [this.cli.install];
502
- const commandToRun = this.runner.rawFullCommand(commandArgs);
503
- console.error(red2(Messages.BUILD_PART_FAILED(name, bold(commandToRun))));
504
- return false;
505
- }
335
+ return result.success;
336
+ }
337
+ async addProduction(directory, dependencies) {
338
+ return this.addDependencies(this.commands.saveFlag, directory, dependencies);
339
+ }
340
+ async addDevelopment(directory, dependencies) {
341
+ return this.addDependencies(this.commands.saveDevFlag, directory, dependencies);
342
+ }
343
+ async build(name, directory, entry, output, flags = [], watch3 = false) {
344
+ this.assertSupports("build");
345
+ const message = watch3 ? Messages.BUILD_PART_WATCH_IN_PROGRESS(name) : Messages.BUILD_PART_IN_PROGRESS(name);
346
+ const args = [
347
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
348
+ this.commands.build,
349
+ this.commands.silentFlag,
350
+ entry,
351
+ "--outdir",
352
+ output,
353
+ ...flags
354
+ ];
355
+ const result = await this.withSpinner(
356
+ message,
357
+ async (spinner) => {
358
+ await this.exec(args, directory, { onFail: /* @__PURE__ */ __name(() => spinner.fail(), "onFail") });
359
+ },
360
+ () => this.logBuildFailure(name)
361
+ );
362
+ return result.success;
506
363
  }
507
- async run(name, directory, file, env2 = {}, flags = [], silent = false) {
364
+ async run(name, directory, script, env2 = {}, flags = [], silent = false) {
365
+ console.info(Messages.START_PART_IN_PROGRESS(name));
508
366
  try {
509
- console.info(Messages.RUN_PART_IN_PROGRESS(name));
510
- const commandArgs = [...flags, this.cli.run];
511
- if (silent) commandArgs.push(this.cli.silentFlag);
512
- commandArgs.push(file);
513
- await this.runner.run(commandArgs, true, getCwd(directory), env2, {
514
- onStdout: this.onRunStdout(name),
515
- onStderr: this.onRunStderr(name)
367
+ const args = this.buildRunArgs(script, flags, silent);
368
+ await this.exec(args, directory, {
369
+ env: env2,
370
+ listeners: {
371
+ onStdout: createStdoutLogger(name),
372
+ onStderr: createStderrLogger(name)
373
+ }
516
374
  });
517
- console.info(Messages.RUN_PART_SUCCESS(name));
375
+ console.info(Messages.START_PART_SUCCESS(name));
518
376
  return true;
519
377
  } catch {
520
- console.error(red2(Messages.RUN_PART_FAILED(name)));
378
+ console.error(red3(Messages.START_PART_FAILED(name)));
521
379
  return false;
522
380
  }
523
381
  }
524
382
  async runDev(directory, command, env2 = {}, flags = [], collect = true) {
525
383
  try {
526
- const commandArgs = [this.cli.exec, command, ...flags];
527
- await this.runner.run(commandArgs, collect, getCwd(directory), env2);
384
+ await this.exec([this.commands.run, command, ...flags], directory, { collect, env: env2 });
528
385
  return true;
529
386
  } catch {
530
387
  return false;
531
388
  }
532
389
  }
533
- async add(args, directory, dependencies) {
534
- if (!dependencies.length) {
535
- console.info();
536
- console.info(Messages.PACKAGE_MANAGER_INSTALLATION_NOTHING);
537
- console.info();
538
- return true;
539
- }
540
- const commandArguments = [...args, ...dependencies];
541
- const spinner = SPINNER(Messages.PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS);
390
+ async withSpinner(message, task, onError) {
391
+ const spinner = getSpinner(message);
542
392
  spinner.start();
543
393
  try {
544
- const collect = true;
545
- await this.runner.run(
546
- commandArguments,
547
- collect,
548
- getCwd(directory),
549
- void 0,
550
- void 0,
551
- FAIL_SPINNER(spinner)
552
- );
394
+ const value = await task(spinner);
553
395
  spinner.succeed();
554
- this.printInstallSuccess(dependencies);
555
- return true;
396
+ return { success: true, value };
556
397
  } catch {
557
398
  spinner.fail();
558
- const commandToRun = this.runner.rawFullCommand(commandArguments);
559
- this.printInstallFailure(commandToRun);
560
- return false;
399
+ if (onError) onError();
400
+ return { success: false };
561
401
  }
562
402
  }
563
- printInstallSuccess(dependencies) {
403
+ async addDependencies(saveFlag, directory, dependencies) {
404
+ if (!dependencies.length) {
405
+ this.logEmpty(Messages.PACKAGE_MANAGER_INSTALLATION_NOTHING);
406
+ return true;
407
+ }
408
+ const args = [this.commands.add, saveFlag, ...dependencies];
409
+ const result = await this.withSpinner(
410
+ Messages.PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS,
411
+ async (spinner) => {
412
+ await this.exec(args, directory, { onFail: /* @__PURE__ */ __name(() => spinner.fail(), "onFail") });
413
+ this.logSuccess(Messages.PACKAGE_MANAGER_INSTALLATION_SUCCEED(dependencies));
414
+ },
415
+ () => this.logFailure(this.formatFailCommand(args))
416
+ );
417
+ return result.success;
418
+ }
419
+ assertSupports(feature) {
420
+ if (!this.commands[feature]) {
421
+ throw new Error(`Package manager "${this.name}" does not support "${feature}"`);
422
+ }
423
+ }
424
+ buildRunArgs(script, flags, silent) {
425
+ const args = [...flags, this.commands.run];
426
+ if (silent) args.push(this.commands.silentFlag);
427
+ args.push(script);
428
+ return args;
429
+ }
430
+ exec(args, directory, options = {}) {
431
+ return this.runner.run(args, {
432
+ collect: options.collect ?? true,
433
+ cwd: getCwd(directory),
434
+ env: options.env,
435
+ listeners: options.listeners,
436
+ onFail: options.onFail
437
+ });
438
+ }
439
+ formatFailCommand(args) {
440
+ return this.runner.fullCommand(args);
441
+ }
442
+ logSuccess(message) {
564
443
  console.info();
565
- console.info(Messages.PACKAGE_MANAGER_INSTALLATION_SUCCEED(dependencies));
444
+ console.info(message);
566
445
  console.info();
567
446
  }
568
- printInstallFailure(command) {
569
- console.error(red2(Messages.PACKAGE_MANAGER_INSTALLATION_FAILED(bold(command))));
447
+ logFailure(command) {
448
+ console.error(red3(Messages.PACKAGE_MANAGER_INSTALLATION_FAILED(bold(command))));
449
+ }
450
+ logBuildFailure(name) {
451
+ const command = this.formatFailCommand([this.commands.install]);
452
+ console.error(red3(Messages.BUILD_PART_FAILED(name, bold(command))));
453
+ }
454
+ logEmpty(message) {
455
+ console.info();
456
+ console.info(message);
457
+ console.info();
570
458
  }
571
- onRunStdout = /* @__PURE__ */ __name((name) => (chunk) => {
572
- chunk.toString().replace(/\r\n|\n/g, "\n").replace(/^\n+|\n+$/g, "").split("\n").forEach((line) => {
573
- const date = yellow2(`[${(/* @__PURE__ */ new Date()).toISOString()}]`);
574
- const prompt = green2(`(${name}) INFO -`);
575
- console.info(`${date} ${prompt} ${line}`);
576
- });
577
- }, "onRunStdout");
578
- onRunStderr = /* @__PURE__ */ __name((name) => (chunk) => {
579
- chunk.toString().replace(/\r\n|\n/g, "\n").replace(/^\n+|\n+$/g, "").split("\n").forEach((line) => {
580
- const date = yellow2(`[${(/* @__PURE__ */ new Date()).toISOString()}]`);
581
- const prompt = red2(`(${name}) ERROR -`);
582
- console.error(`${date} ${prompt} ${line}`);
583
- });
584
- }, "onRunStderr");
585
459
  };
586
460
 
587
- // src/lib/package-manager/package-managers/bun.package-manager.ts
588
- var BunPackageManager = class extends AbstractPackageManager {
461
+ // src/lib/package-manager/package-manager.factory.ts
462
+ import fs2 from "fs";
463
+ import { resolve as resolve2 } from "path";
464
+
465
+ // src/lib/runner/runner.ts
466
+ import { red as red4 } from "ansis";
467
+ import { spawn } from "child_process";
468
+ import * as process2 from "process";
469
+ var Runner = class {
470
+ constructor(binary, baseArgs = []) {
471
+ this.binary = binary;
472
+ this.baseArgs = baseArgs;
473
+ }
589
474
  static {
590
- __name(this, "BunPackageManager");
475
+ __name(this, "Runner");
591
476
  }
592
- constructor() {
593
- super(RunnerFactory.create(0 /* BUN */));
477
+ async run(args, options = {}) {
478
+ const { collect = false, cwd: cwd2 = process2.cwd(), env: env2, listeners, onFail } = options;
479
+ const spawnOpts = this.buildSpawnOptions(collect, cwd2, env2);
480
+ const fullArgs = [...this.baseArgs, ...args];
481
+ return new Promise((resolve3, reject) => {
482
+ const child = spawn(`${this.binary} ${fullArgs.join(" ")}`, spawnOpts);
483
+ const output = this.attachOutputHandlers(child, listeners);
484
+ child.on("close", (code) => {
485
+ if (code === 0) {
486
+ resolve3(this.formatOutput(output, collect));
487
+ } else {
488
+ this.handleFailure(output, fullArgs, onFail);
489
+ reject(this.createError(fullArgs, code));
490
+ }
491
+ });
492
+ });
594
493
  }
595
- get name() {
596
- return "bun" /* BUN */.toUpperCase();
494
+ fullCommand(args) {
495
+ return [this.binary, ...this.baseArgs, ...args].join(" ");
597
496
  }
598
- get cli() {
497
+ buildSpawnOptions(collect, cwd2, env2) {
599
498
  return {
499
+ cwd: cwd2,
500
+ stdio: collect ? "pipe" : "inherit",
501
+ shell: true,
502
+ env: { ...process2.env, ...env2 }
503
+ };
504
+ }
505
+ attachOutputHandlers(child, listeners) {
506
+ const output = [];
507
+ const defaultHandler = /* @__PURE__ */ __name((data) => output.push(data.toString().replace(/\r\n|\n/, "")), "defaultHandler");
508
+ child.stdout?.on("data", listeners?.onStdout ?? defaultHandler);
509
+ child.stderr?.on("data", listeners?.onStderr ?? defaultHandler);
510
+ return output;
511
+ }
512
+ formatOutput(output, collect) {
513
+ return collect && output.length ? output.join("\n") : null;
514
+ }
515
+ handleFailure(output, args, onFail) {
516
+ if (onFail) onFail();
517
+ this.logFailedCommand(args);
518
+ this.logCapturedOutput(output);
519
+ }
520
+ logFailedCommand(args) {
521
+ console.error(red4(`
522
+ Failed to execute command: ${this.binary} ${args.join(" ")}`));
523
+ }
524
+ logCapturedOutput(output) {
525
+ if (output.length) {
526
+ console.error();
527
+ console.error(output.join("\n"));
528
+ console.error();
529
+ }
530
+ }
531
+ createError(args, code) {
532
+ return new Error(`Command "${this.binary} ${args.join(" ")}" exited with code ${code}`);
533
+ }
534
+ };
535
+
536
+ // src/lib/runner/runner.factory.ts
537
+ var RunnerFactory = class {
538
+ static {
539
+ __name(this, "RunnerFactory");
540
+ }
541
+ static create(binary, args) {
542
+ return new Runner(binary, args);
543
+ }
544
+ static createLocal(binary, args) {
545
+ return new Runner(resolveCLINodeBinaryPath(binary), args);
546
+ }
547
+ static createSchematic() {
548
+ const binaryPath = this.resolveSchematicBinary();
549
+ return new Runner("node", [`"${binaryPath}"`]);
550
+ }
551
+ static resolveSchematicBinary() {
552
+ try {
553
+ return getModulePath("@angular-devkit/schematics-cli/bin/schematics.js");
554
+ } catch {
555
+ throw new Error("'schematics' binary path could not be found!");
556
+ }
557
+ }
558
+ };
559
+
560
+ // src/lib/package-manager/package-manager-configs.ts
561
+ var PM_CONFIGS = {
562
+ ["bun" /* BUN */]: {
563
+ binary: "bun",
564
+ commands: {
600
565
  install: "install",
601
566
  add: "add",
602
567
  update: "update",
@@ -606,23 +571,11 @@ var BunPackageManager = class extends AbstractPackageManager {
606
571
  saveFlag: "--save",
607
572
  saveDevFlag: "--dev",
608
573
  silentFlag: "--silent"
609
- };
610
- }
611
- };
612
-
613
- // src/lib/package-manager/package-managers/local-bun.package-manager.ts
614
- var LocalBunPackageManager = class extends AbstractPackageManager {
615
- static {
616
- __name(this, "LocalBunPackageManager");
617
- }
618
- constructor() {
619
- super(RunnerFactory.create(1 /* LOCAL_BUN */));
620
- }
621
- get name() {
622
- return "local_bun" /* LOCAL_BUN */.toUpperCase();
623
- }
624
- get cli() {
625
- return {
574
+ }
575
+ },
576
+ ["local_bun" /* LOCAL_BUN */]: {
577
+ binary: "bun",
578
+ commands: {
626
579
  install: "install",
627
580
  add: "add",
628
581
  update: "update",
@@ -634,23 +587,11 @@ var LocalBunPackageManager = class extends AbstractPackageManager {
634
587
  saveFlag: "--save",
635
588
  saveDevFlag: "--dev",
636
589
  silentFlag: "--silent"
637
- };
638
- }
639
- };
640
-
641
- // src/lib/package-manager/package-managers/npm.package-manager.ts
642
- var NpmPackageManager = class extends AbstractPackageManager {
643
- static {
644
- __name(this, "NpmPackageManager");
645
- }
646
- constructor() {
647
- super(RunnerFactory.create(2 /* NPM */));
648
- }
649
- get name() {
650
- return "npm" /* NPM */.toUpperCase();
651
- }
652
- get cli() {
653
- return {
590
+ }
591
+ },
592
+ ["npm" /* NPM */]: {
593
+ binary: "npm",
594
+ commands: {
654
595
  install: "install",
655
596
  add: "install",
656
597
  update: "update",
@@ -660,23 +601,11 @@ var NpmPackageManager = class extends AbstractPackageManager {
660
601
  saveFlag: "--save",
661
602
  saveDevFlag: "--save-dev",
662
603
  silentFlag: "--silent"
663
- };
664
- }
665
- };
666
-
667
- // src/lib/package-manager/package-managers/pnpm.package-manager.ts
668
- var PnpmPackageManager = class extends AbstractPackageManager {
669
- static {
670
- __name(this, "PnpmPackageManager");
671
- }
672
- constructor() {
673
- super(RunnerFactory.create(3 /* PNPM */));
674
- }
675
- get name() {
676
- return "pnpm" /* PNPM */.toUpperCase();
677
- }
678
- get cli() {
679
- return {
604
+ }
605
+ },
606
+ ["pnpm" /* PNPM */]: {
607
+ binary: "pnpm",
608
+ commands: {
680
609
  install: "install",
681
610
  add: "add",
682
611
  update: "update",
@@ -686,23 +615,11 @@ var PnpmPackageManager = class extends AbstractPackageManager {
686
615
  saveFlag: "-P",
687
616
  saveDevFlag: "-D",
688
617
  silentFlag: "--silent"
689
- };
690
- }
691
- };
692
-
693
- // src/lib/package-manager/package-managers/yarn.package-manager.ts
694
- var YarnPackageManager = class extends AbstractPackageManager {
695
- static {
696
- __name(this, "YarnPackageManager");
697
- }
698
- constructor() {
699
- super(RunnerFactory.create(5 /* YARN */));
700
- }
701
- get name() {
702
- return "yarn" /* YARN */.toUpperCase();
703
- }
704
- get cli() {
705
- return {
618
+ }
619
+ },
620
+ ["yarn" /* YARN */]: {
621
+ binary: "yarn",
622
+ commands: {
706
623
  install: "install",
707
624
  add: "add",
708
625
  update: "update",
@@ -712,54 +629,63 @@ var YarnPackageManager = class extends AbstractPackageManager {
712
629
  saveFlag: "",
713
630
  saveDevFlag: "-D",
714
631
  silentFlag: "--silent"
715
- };
632
+ }
716
633
  }
717
634
  };
718
635
 
719
636
  // src/lib/package-manager/package-manager.factory.ts
637
+ var LOCK_FILE_MAP = {
638
+ "bun.lock": "bun" /* BUN */,
639
+ "package-lock.json": "npm" /* NPM */,
640
+ "pnpm-lock.yaml": "pnpm" /* PNPM */,
641
+ "yarn.lock": "yarn" /* YARN */
642
+ };
720
643
  var PackageManagerFactory = class {
721
644
  static {
722
645
  __name(this, "PackageManagerFactory");
723
646
  }
724
647
  static create(name) {
725
- switch (name) {
726
- case "bun" /* BUN */:
727
- return new BunPackageManager();
728
- case "local_bun" /* LOCAL_BUN */:
729
- return new LocalBunPackageManager();
730
- case "npm" /* NPM */:
731
- return new NpmPackageManager();
732
- case "pnpm" /* PNPM */:
733
- return new PnpmPackageManager();
734
- case "yarn" /* YARN */:
735
- return new YarnPackageManager();
736
- default:
737
- throw new Error(`Package manager ${name} is not managed.`);
648
+ const config2 = PM_CONFIGS[name];
649
+ if (!config2) {
650
+ throw new Error(`Package manager ${name} is not managed.`);
738
651
  }
652
+ const runner = this.createRunner(name, config2.binary);
653
+ return new PackageManager(name, config2.commands, runner);
739
654
  }
740
655
  static async find(directory = ".") {
741
- const DEFAULT_PACKAGE_MANAGER = "npm" /* NPM */;
656
+ const detected = await this.detectFromLockFile(directory);
657
+ return this.create(detected);
658
+ }
659
+ static createRunner(name, binary) {
660
+ if (name === "local_bun" /* LOCAL_BUN */) {
661
+ return RunnerFactory.createLocal("bun");
662
+ }
663
+ return RunnerFactory.create(binary);
664
+ }
665
+ static async detectFromLockFile(directory) {
742
666
  try {
743
667
  const files = await fs2.promises.readdir(resolve2(directory));
744
- if (files.includes("bun.lock")) {
745
- return this.create("bun" /* BUN */);
746
- }
747
- if (files.includes("package-lock.json")) {
748
- return this.create("npm" /* NPM */);
668
+ for (const [lockFile, pmName] of Object.entries(LOCK_FILE_MAP)) {
669
+ if (files.includes(lockFile)) return pmName;
749
670
  }
750
- if (files.includes("pnpm-lock.yaml")) {
751
- return this.create("pnpm" /* PNPM */);
752
- }
753
- if (files.includes("yarn.lock")) {
754
- return this.create("yarn" /* YARN */);
755
- }
756
- return this.create(DEFAULT_PACKAGE_MANAGER);
757
671
  } catch {
758
- return this.create(DEFAULT_PACKAGE_MANAGER);
759
672
  }
673
+ return "npm" /* NPM */;
760
674
  }
761
675
  };
762
676
 
677
+ // src/lib/utils/run-safe.ts
678
+ import { red as red5 } from "ansis";
679
+ var runSafe = /* @__PURE__ */ __name(async (fn, fallback) => {
680
+ try {
681
+ return await fn();
682
+ } catch (error) {
683
+ const msg = getErrorMessage(error);
684
+ if (msg) console.error(red5(msg));
685
+ return fallback;
686
+ }
687
+ }, "runSafe");
688
+
763
689
  // src/lib/config/config.type.ts
764
690
  import { Expose, Type } from "class-transformer";
765
691
  import { IsBoolean, IsEnum, IsNotEmpty, IsPort, IsString, ValidateNested } from "class-validator";
@@ -885,6 +811,10 @@ import { validate } from "class-validator";
885
811
  import { existsSync, readFileSync } from "fs";
886
812
  import { join as join2 } from "path";
887
813
 
814
+ // src/lib/constants.ts
815
+ var CONFIG_FILE_NAME = "nanoforge.config.json";
816
+ var NANOFORGE_DIR = ".nanoforge";
817
+
888
818
  // src/lib/utils/object.ts
889
819
  var isObject = /* @__PURE__ */ __name((item) => {
890
820
  return item && typeof item === "object" && !Array.isArray(item);
@@ -940,18 +870,17 @@ var getConfigPath = /* @__PURE__ */ __name((directory, name) => {
940
870
  if (name) {
941
871
  return join2(directory, name);
942
872
  } else {
943
- for (const n of ["nanoforge.config.json"]) {
873
+ for (const n of [CONFIG_FILE_NAME]) {
944
874
  const path = join2(directory, n);
945
875
  if (existsSync(path)) return path;
946
876
  }
947
- throw new Error(`Unsupported config: ${name}`);
877
+ throw new Error(`No config file found in directory: ${directory}`);
948
878
  }
949
879
  }, "getConfigPath");
950
880
  var loadConfig = /* @__PURE__ */ __name(async (directory, name) => {
951
881
  if (config) return config;
952
882
  let rawData;
953
883
  const path = getConfigPath(directory, name);
954
- if (!path) throw new Error("No config file found");
955
884
  try {
956
885
  rawData = deepMerge(CONFIG_DEFAULTS, JSON.parse(readFileSync(path, "utf-8")));
957
886
  } catch {
@@ -979,6 +908,32 @@ var AbstractAction = class {
979
908
  static {
980
909
  __name(this, "AbstractAction");
981
910
  }
911
+ async run(args, options, extraFlags) {
912
+ this.logStart();
913
+ try {
914
+ const result = await this.handle(args, options, extraFlags);
915
+ this.resolveResult(result);
916
+ } catch (error) {
917
+ handleActionError(this.failureMessage, error);
918
+ }
919
+ }
920
+ logStart() {
921
+ console.info();
922
+ console.info(`${Prefixes.INFO} ${this.startMessage}`);
923
+ console.info();
924
+ }
925
+ resolveResult(result) {
926
+ const success2 = result?.success !== false;
927
+ const keepAlive = result?.keepAlive === true;
928
+ if (keepAlive) return;
929
+ console.info();
930
+ if (!success2) {
931
+ if (this.failureMessage) console.error(this.failureMessage);
932
+ process.exit(1);
933
+ }
934
+ if (this.successMessage) console.info(this.successMessage);
935
+ process.exit(0);
936
+ }
982
937
  };
983
938
 
984
939
  // src/action/actions/build.action.ts
@@ -986,121 +941,121 @@ var BuildAction = class extends AbstractAction {
986
941
  static {
987
942
  __name(this, "BuildAction");
988
943
  }
944
+ startMessage = Messages.BUILD_START;
945
+ successMessage = Messages.BUILD_SUCCESS;
946
+ failureMessage = Messages.BUILD_FAILED;
989
947
  async handle(_args, options) {
990
- console2.info(Messages.BUILD_START);
991
- console2.info();
992
- try {
993
- const directory = getDirectoryInput(options);
994
- const config2 = await getConfig(options, directory);
995
- const watch3 = getWatchInput(options);
996
- const client = getPart(
948
+ const directory = getDirectoryInput(options);
949
+ const config2 = await getConfig(options, directory);
950
+ const isWatch = getWatchInput(options);
951
+ const targets = this.resolveTargets(config2, options);
952
+ const results = await this.buildAll(targets, directory, isWatch);
953
+ if (isWatch) {
954
+ return this.enterWatchMode();
955
+ }
956
+ return { success: results.every(Boolean) };
957
+ }
958
+ resolveTargets(config2, options) {
959
+ const targets = [
960
+ this.createTarget(
961
+ "Client",
997
962
  config2.client.build,
998
- options.get("clientDirectory")?.value,
999
- "client"
1000
- );
1001
- let res = await buildPart("Client", client, directory, { watch: watch3 });
1002
- if (config2.server.enable) {
1003
- const server = getPart(
963
+ "browser",
964
+ getStringInput(options, "clientDirectory")
965
+ )
966
+ ];
967
+ if (config2.server.enable) {
968
+ targets.push(
969
+ this.createTarget(
970
+ "Server",
1004
971
  config2.server.build,
1005
- options.get("serverDirectory")?.value,
1006
- "server"
1007
- );
1008
- res = await buildPart("Server", server, directory, { watch: watch3 }) ? res : false;
1009
- }
1010
- console2.info();
1011
- if (watch3) {
1012
- console2.info(Messages.BUILD_WATCH_START);
1013
- console2.info();
1014
- return;
1015
- }
1016
- if (!res) {
1017
- console2.info(Messages.BUILD_FAILED);
1018
- process.exit(1);
1019
- }
1020
- console2.info(Messages.BUILD_SUCCESS);
1021
- process.exit(0);
1022
- } catch (e) {
1023
- console2.error(e);
1024
- process.exit(1);
972
+ "node",
973
+ getStringInput(options, "serverDirectory")
974
+ )
975
+ );
1025
976
  }
977
+ return targets;
1026
978
  }
1027
- };
1028
- var getPart = /* @__PURE__ */ __name((config2, directoryOption, target) => {
1029
- return {
1030
- entry: config2.entryFile,
1031
- output: directoryOption || config2.outDir,
1032
- target
1033
- };
1034
- }, "getPart");
1035
- var buildPart = /* @__PURE__ */ __name(async (name, part, directory, options) => {
1036
- const packageManagerName = "local_bun" /* LOCAL_BUN */;
1037
- const packageManager = PackageManagerFactory.create(packageManagerName);
1038
- const build = /* @__PURE__ */ __name(async (watch3 = false) => {
1039
- try {
1040
- return await packageManager.build(
1041
- name,
979
+ createTarget(name, config2, platform, outDirOverride) {
980
+ return {
981
+ name,
982
+ entry: config2.entryFile,
983
+ output: outDirOverride || config2.outDir,
984
+ platform
985
+ };
986
+ }
987
+ async buildAll(targets, directory, isWatch) {
988
+ const results = [];
989
+ for (const target of targets) {
990
+ const result = await this.buildTarget(target, directory, isWatch);
991
+ results.push(result);
992
+ }
993
+ return results;
994
+ }
995
+ async buildTarget(target, directory, isWatch) {
996
+ const packageManager = PackageManagerFactory.create("local_bun" /* LOCAL_BUN */);
997
+ const executeBuild = /* @__PURE__ */ __name((rebuild = false) => runSafe(
998
+ () => packageManager.build(
999
+ target.name,
1042
1000
  directory,
1043
- part.entry,
1044
- part.output,
1045
- [
1046
- "--asset-naming",
1047
- "[name].[ext]",
1048
- "--target",
1049
- part.target === "client" ? "browser" : "node"
1050
- ],
1051
- watch3
1052
- );
1053
- } catch (error4) {
1054
- if (error4 && error4.message) {
1055
- console2.error(ansis.red(error4.message));
1056
- }
1057
- return false;
1001
+ target.entry,
1002
+ target.output,
1003
+ ["--asset-naming", "[name].[ext]", "--target", target.platform],
1004
+ rebuild
1005
+ ),
1006
+ false
1007
+ ), "executeBuild");
1008
+ if (isWatch) {
1009
+ this.watchDirectory(directory, target.entry, () => executeBuild(true));
1058
1010
  }
1059
- }, "build");
1060
- if (options?.watch)
1061
- watch(dirname(join3(getCwd(directory), part.entry))).on("change", () => build(true));
1062
- return await build();
1063
- }, "buildPart");
1011
+ const result = await executeBuild();
1012
+ return result !== false;
1013
+ }
1014
+ watchDirectory(directory, entry, onChange) {
1015
+ const watchPath = dirname(join3(getCwd(directory), entry));
1016
+ watch(watchPath).on("change", onChange);
1017
+ }
1018
+ enterWatchMode() {
1019
+ console.info();
1020
+ console.info(Messages.BUILD_WATCH_START);
1021
+ console.info();
1022
+ return { keepAlive: true };
1023
+ }
1024
+ };
1064
1025
 
1065
1026
  // src/action/actions/dev.action.ts
1066
- import * as ansis2 from "ansis";
1067
1027
  var DevAction = class extends AbstractAction {
1068
1028
  static {
1069
1029
  __name(this, "DevAction");
1070
1030
  }
1031
+ startMessage = Messages.DEV_START;
1032
+ successMessage = Messages.DEV_SUCCESS;
1033
+ failureMessage = Messages.DEV_FAILED;
1071
1034
  async handle(_args, options) {
1072
- console.info(Messages.DEV_START);
1073
- console.info();
1074
- try {
1075
- const directory = getDirectoryInput(options);
1076
- const generate = getDevGenerateInput(options);
1077
- await Promise.all([
1078
- generate ? runAction("generate", [], directory, false) : void 0,
1079
- runAction("build", [], directory, false),
1080
- runAction("start", [], directory, true)
1081
- ]);
1082
- console.info(Messages.DEV_SUCCESS);
1083
- process.exit(0);
1084
- } catch (e) {
1085
- console.error(Messages.DEV_FAILED);
1086
- console.error(e);
1087
- process.exit(1);
1035
+ const directory = getDirectoryInput(options);
1036
+ const generate = getDevGenerateInput(options);
1037
+ const tasks = this.buildTaskList(directory, generate);
1038
+ await Promise.all(tasks);
1039
+ return { keepAlive: true };
1040
+ }
1041
+ buildTaskList(directory, generate) {
1042
+ const tasks = [];
1043
+ if (generate) {
1044
+ tasks.push(this.runSubCommand("generate", directory, { silent: true }));
1088
1045
  }
1046
+ tasks.push(this.runSubCommand("build", directory, { silent: true }));
1047
+ tasks.push(this.runSubCommand("start", directory, { silent: false }));
1048
+ return tasks;
1049
+ }
1050
+ async runSubCommand(command, directory, options) {
1051
+ await runSafe(async () => {
1052
+ const packageManager = await PackageManagerFactory.find(directory);
1053
+ await packageManager.runDev(directory, "nf", {}, [command, "--watch"], options.silent);
1054
+ });
1089
1055
  }
1090
1056
  };
1091
- var runAction = /* @__PURE__ */ __name(async (command, params, directory, stdout = false) => {
1092
- try {
1093
- const packageManager = await PackageManagerFactory.find(directory);
1094
- await packageManager.runDev(directory, "nf", {}, [command, ...params, "--watch"], !stdout);
1095
- } catch (error4) {
1096
- if (error4 && error4.message) {
1097
- console.error(ansis2.red(error4.message));
1098
- }
1099
- }
1100
- }, "runAction");
1101
1057
 
1102
1058
  // src/action/actions/generate.action.ts
1103
- import * as console3 from "console";
1104
1059
  import { join as join4 } from "path";
1105
1060
 
1106
1061
  // src/lib/schematics/abstract.collection.ts
@@ -1113,25 +1068,19 @@ var AbstractCollection = class {
1113
1068
  static {
1114
1069
  __name(this, "AbstractCollection");
1115
1070
  }
1116
- async execute(name, options, flags, failSpinner) {
1071
+ async execute(name, options, flags, onFail) {
1117
1072
  const command = this.buildCommandLine(name, options, flags);
1118
- await this.runner.run(
1119
- command,
1120
- true,
1121
- this.cwd ? getCwd(this.cwd) : void 0,
1122
- void 0,
1123
- void 0,
1124
- failSpinner
1125
- );
1073
+ await this.runner.run(command, {
1074
+ collect: true,
1075
+ cwd: this.cwd ? getCwd(this.cwd) : void 0,
1076
+ onFail
1077
+ });
1126
1078
  }
1127
1079
  buildCommandLine(name, options, flags = []) {
1128
- return [`${this.collection}:${name}`, ...flags, ...this.buildOptions(options)];
1080
+ return [`${this.collection}:${name}`, ...flags, ...this.serializeOptions(options)];
1129
1081
  }
1130
- buildOptions(options) {
1131
- return options.reduce(
1132
- (old, option) => [...old, ...option.toCommandString()],
1133
- []
1134
- );
1082
+ serializeOptions(options) {
1083
+ return options.flatMap((option) => option.toCommandString());
1135
1084
  }
1136
1085
  };
1137
1086
 
@@ -1160,6 +1109,11 @@ var NanoforgeCollection = class _NanoforgeCollection extends AbstractCollection
1160
1109
  name: "part-main",
1161
1110
  alias: "main",
1162
1111
  description: "Generate a NanoForge Part Main file"
1112
+ },
1113
+ {
1114
+ name: "docker",
1115
+ alias: "docker",
1116
+ description: "Generate a Dockerfile for the application"
1163
1117
  }
1164
1118
  ];
1165
1119
  constructor(runner, cwd2) {
@@ -1191,12 +1145,11 @@ var CollectionFactory = class {
1191
1145
  __name(this, "CollectionFactory");
1192
1146
  }
1193
1147
  static create(collection, directory) {
1194
- const schematicRunner = RunnerFactory.create(4 /* SCHEMATIC */);
1148
+ const schematicRunner = RunnerFactory.createSchematic();
1195
1149
  if (collection === "@nanoforge-dev/schematics" /* NANOFORGE */) {
1196
1150
  return new NanoforgeCollection(schematicRunner, directory);
1197
- } else {
1198
- return new NanoforgeCollection(schematicRunner, directory);
1199
1151
  }
1152
+ throw new Error(`Unknown collection: ${collection}`);
1200
1153
  }
1201
1154
  };
1202
1155
 
@@ -1254,19 +1207,10 @@ var SchematicOption = class {
1254
1207
 
1255
1208
  // src/action/common/schematics.ts
1256
1209
  import { watch as watch2 } from "chokidar";
1257
-
1258
- // src/action/common/spinner.ts
1259
- import ora2 from "ora";
1260
- var getSpinner = /* @__PURE__ */ __name((message) => ora2({
1261
- text: message
1262
- }), "getSpinner");
1263
-
1264
- // src/action/common/schematics.ts
1265
1210
  var executeSchematic = /* @__PURE__ */ __name(async (name, collection, schematicName, options, fileToWatch) => {
1266
- const execute = /* @__PURE__ */ __name(async (watch3 = false) => {
1267
- const spinner = getSpinner(
1268
- (watch3 ? Messages.SCHEMATIC_WATCH_IN_PROGRESS : Messages.SCHEMATIC_IN_PROGRESS)(name)
1269
- );
1211
+ const execute = /* @__PURE__ */ __name(async (isRebuild = false) => {
1212
+ const message = isRebuild ? Messages.SCHEMATIC_WATCH_IN_PROGRESS(name) : Messages.SCHEMATIC_IN_PROGRESS(name);
1213
+ const spinner = getSpinner(message);
1270
1214
  spinner.start();
1271
1215
  await collection.execute(
1272
1216
  schematicName,
@@ -1276,16 +1220,17 @@ var executeSchematic = /* @__PURE__ */ __name(async (name, collection, schematic
1276
1220
  );
1277
1221
  spinner.succeed(Messages.SCHEMATIC_SUCCESS(name));
1278
1222
  }, "execute");
1279
- if (fileToWatch) watch2(fileToWatch).on("change", () => execute(true));
1280
- return await execute();
1223
+ if (fileToWatch) {
1224
+ watch2(fileToWatch).on("change", () => execute(true));
1225
+ }
1226
+ await execute();
1281
1227
  }, "executeSchematic");
1282
1228
  var mapSchematicOptions = /* @__PURE__ */ __name((inputs) => {
1283
- return Object.entries(inputs).reduce((old, [key, value]) => {
1284
- if (value === void 0) return old;
1285
- return [
1286
- ...old,
1287
- new SchematicOption(key, typeof value === "object" ? mapSchematicOptions(value) : value)
1288
- ];
1229
+ return Object.entries(inputs).reduce((acc, [key, value]) => {
1230
+ if (value === void 0) return acc;
1231
+ const mapped = typeof value === "object" ? new SchematicOption(key, mapSchematicOptions(value)) : new SchematicOption(key, value);
1232
+ acc.push(mapped);
1233
+ return acc;
1289
1234
  }, []);
1290
1235
  }, "mapSchematicOptions");
1291
1236
 
@@ -1294,111 +1239,98 @@ var GenerateAction = class extends AbstractAction {
1294
1239
  static {
1295
1240
  __name(this, "GenerateAction");
1296
1241
  }
1242
+ startMessage = Messages.GENERATE_START;
1243
+ successMessage = Messages.GENERATE_SUCCESS;
1244
+ failureMessage = Messages.GENERATE_FAILED;
1297
1245
  async handle(_args, options) {
1298
- console3.info(Messages.GENERATE_START);
1299
- console3.info();
1300
- try {
1301
- const directory = getDirectoryInput(options);
1302
- const config2 = await getConfig(options, directory);
1303
- const watch3 = getWatchInput(options);
1304
- const values = await getSchemaValues(config2);
1305
- await generateFiles(values, directory, watch3);
1306
- console3.info();
1307
- if (watch3) {
1308
- console3.info(Messages.GENERATE_WATCH_START);
1309
- console3.info();
1310
- return;
1311
- }
1312
- console3.info(Messages.GENERATE_SUCCESS);
1313
- process.exit(0);
1314
- } catch (e) {
1315
- console3.error(Messages.GENERATE_FAILED);
1316
- console3.error(e);
1317
- process.exit(1);
1246
+ const directory = getDirectoryInput(options);
1247
+ const config2 = await getConfig(options, directory);
1248
+ const isWatch = getWatchInput(options);
1249
+ const values = this.extractValues(config2);
1250
+ await this.generateParts(values, directory, isWatch);
1251
+ if (isWatch) {
1252
+ return this.enterWatchMode();
1318
1253
  }
1254
+ return {};
1319
1255
  }
1320
- };
1321
- var getSchemaValues = /* @__PURE__ */ __name(async (config2) => {
1322
- return {
1323
- name: config2.name,
1324
- directory: ".",
1325
- language: config2.language,
1326
- server: config2.server.enable,
1327
- initFunctions: config2.initFunctions
1328
- };
1329
- }, "getSchemaValues");
1330
- var generateFiles = /* @__PURE__ */ __name(async (values, directory, watch3) => {
1331
- const collection = CollectionFactory.create("@nanoforge-dev/schematics" /* NANOFORGE */, directory);
1332
- console3.info(Messages.SCHEMATICS_START);
1333
- console3.info();
1334
- await executeSchematic(
1335
- "Client main file",
1336
- collection,
1337
- "part-main",
1338
- {
1339
- name: values.name,
1340
- part: "client",
1341
- directory: values.directory,
1342
- language: values.language,
1343
- initFunctions: values.initFunctions
1344
- },
1345
- watch3 ? join4(getCwd(directory), values.directory, ".nanoforge", "client.save.json") : void 0
1346
- );
1347
- if (values.server) {
1256
+ extractValues(config2) {
1257
+ return {
1258
+ name: config2.name,
1259
+ directory: ".",
1260
+ language: config2.language,
1261
+ server: config2.server.enable,
1262
+ initFunctions: config2.initFunctions
1263
+ };
1264
+ }
1265
+ async generateParts(values, directory, watch3) {
1266
+ const collection = CollectionFactory.create("@nanoforge-dev/schematics" /* NANOFORGE */, directory);
1267
+ const baseOptions = this.baseSchematicOptions(values);
1348
1268
  await executeSchematic(
1349
- "Server main file",
1269
+ "Client main file",
1350
1270
  collection,
1351
1271
  "part-main",
1352
- {
1353
- name: values.name,
1354
- part: "server",
1355
- directory: values.directory,
1356
- language: values.language,
1357
- initFunctions: values.initFunctions
1358
- },
1359
- join4(getCwd(directory), values.directory, ".nanoforge", "server.save.json")
1272
+ { ...baseOptions, part: "client" },
1273
+ watch3 ? this.watchPath(directory, values.directory, "client") : void 0
1360
1274
  );
1275
+ if (values.server) {
1276
+ await executeSchematic(
1277
+ "Server main file",
1278
+ collection,
1279
+ "part-main",
1280
+ { ...baseOptions, part: "server" },
1281
+ this.watchPath(directory, values.directory, "server")
1282
+ );
1283
+ }
1361
1284
  }
1362
- }, "generateFiles");
1285
+ baseSchematicOptions(values) {
1286
+ return {
1287
+ name: values.name,
1288
+ directory: values.directory,
1289
+ language: values.language,
1290
+ initFunctions: values.initFunctions
1291
+ };
1292
+ }
1293
+ watchPath(directory, subDir, part) {
1294
+ return join4(getCwd(directory), subDir, NANOFORGE_DIR, `${part}.save.json`);
1295
+ }
1296
+ enterWatchMode() {
1297
+ console.info();
1298
+ console.info(Messages.GENERATE_WATCH_START);
1299
+ console.info();
1300
+ return { keepAlive: true };
1301
+ }
1302
+ };
1363
1303
 
1364
1304
  // src/action/actions/install.action.ts
1365
- import * as ansis3 from "ansis";
1366
- import * as process3 from "process";
1367
1305
  var InstallAction = class extends AbstractAction {
1368
1306
  static {
1369
1307
  __name(this, "InstallAction");
1370
1308
  }
1309
+ startMessage = Messages.INSTALL_START;
1310
+ successMessage = Messages.INSTALL_SUCCESS;
1311
+ failureMessage = Messages.INSTALL_FAILED;
1371
1312
  async handle(args, options) {
1372
- console.info(Messages.INSTALL_START);
1373
- console.info();
1374
- try {
1375
- const names = await getInstallNamesInputOrAsk(args);
1376
- const directory = getDirectoryInput(options);
1377
- await installPackages(names, directory);
1378
- process3.exit(0);
1379
- } catch (e) {
1380
- console.error(e);
1381
- process3.exit(1);
1382
- }
1383
- }
1384
- };
1385
- var installPackages = /* @__PURE__ */ __name(async (names, directory) => {
1386
- try {
1313
+ const names = await getInstallNamesInputOrAsk(args);
1314
+ const directory = getDirectoryInput(options);
1387
1315
  const packageManager = await PackageManagerFactory.find(directory);
1388
- const res = await packageManager.addProduction(directory, names);
1389
- if (!res) process3.exit(1);
1390
- } catch (error4) {
1391
- if (error4 && error4.message) {
1392
- console.error(ansis3.red(error4.message));
1393
- }
1394
- process3.exit(1);
1316
+ const success2 = await packageManager.addProduction(directory, names);
1317
+ return { success: success2 };
1395
1318
  }
1396
- }, "installPackages");
1319
+ };
1397
1320
 
1398
1321
  // src/action/actions/new.action.ts
1399
- import * as ansis4 from "ansis";
1400
- import console4 from "console";
1401
- import * as process4 from "process";
1322
+ import { join as join5 } from "path";
1323
+
1324
+ // src/lib/input/inputs/new/docker.input.ts
1325
+ var getDockerInput = /* @__PURE__ */ __name((inputs) => {
1326
+ return getBooleanInput(inputs, "docker");
1327
+ }, "getDockerInput");
1328
+ var getDockerOrAsk = /* @__PURE__ */ __name((inputs) => {
1329
+ return getInputOrAsk(
1330
+ getDockerInput(inputs),
1331
+ () => askConfirm(Messages.NEW_DOCKER_QUESTION, { default: true })
1332
+ );
1333
+ }, "getDockerOrAsk");
1402
1334
 
1403
1335
  // src/lib/input/inputs/new/init-functions.input.ts
1404
1336
  var getNewInitFunctionsWithDefault = /* @__PURE__ */ __name((inputs) => {
@@ -1492,172 +1424,186 @@ var NewAction = class extends AbstractAction {
1492
1424
  static {
1493
1425
  __name(this, "NewAction");
1494
1426
  }
1427
+ startMessage = Messages.NEW_START;
1428
+ successMessage = Messages.NEW_SUCCESS;
1429
+ failureMessage = Messages.NEW_FAILED;
1495
1430
  async handle(_args, options) {
1496
- console4.info(Messages.NEW_START);
1497
- try {
1498
- const directory = getDirectoryInput(options);
1499
- const values = await getSchemaValues2(options);
1500
- await generateApplicationFiles(values, directory);
1501
- if (!values.skipInstall) await runInstall(directory, values.packageManager);
1502
- console4.info();
1503
- console4.info(Messages.NEW_SUCCESS);
1504
- process4.exit(0);
1505
- } catch {
1506
- console4.error(Messages.NEW_FAILED);
1507
- process4.exit(1);
1431
+ const directory = getDirectoryInput(options);
1432
+ const values = await this.collectValues(options);
1433
+ await this.scaffold(values, directory);
1434
+ let res = true;
1435
+ if (!values.skipInstall) {
1436
+ res = await this.installDependencies(values.packageManager, join5(directory, values.name));
1508
1437
  }
1438
+ return { success: res };
1509
1439
  }
1510
- };
1511
- var getSchemaValues2 = /* @__PURE__ */ __name(async (inputs) => {
1512
- return {
1513
- name: await getNewNameInputOrAsk(inputs),
1514
- directory: getNewPathInput(inputs),
1515
- packageManager: await getNewPackageManagerInputOrAsk(inputs),
1516
- language: await getNewLanguageInputOrAsk(inputs),
1517
- strict: await getNewStrictOrAsk(inputs),
1518
- server: await getNewServerOrAsk(inputs),
1519
- initFunctions: getNewInitFunctionsWithDefault(inputs),
1520
- skipInstall: await getNewSkipInstallOrAsk(inputs)
1521
- };
1522
- }, "getSchemaValues");
1523
- var generateApplicationFiles = /* @__PURE__ */ __name(async (values, directory) => {
1524
- console4.info();
1525
- const collection = CollectionFactory.create("@nanoforge-dev/schematics" /* NANOFORGE */, directory);
1526
- console4.info();
1527
- console4.info(Messages.SCHEMATICS_START);
1528
- console4.info();
1529
- await executeSchematic("Application", collection, "application", {
1530
- name: values.name,
1531
- directory: values.directory,
1532
- packageManager: values.packageManager,
1533
- language: values.language,
1534
- strict: values.strict,
1535
- server: values.server
1536
- });
1537
- await executeSchematic("Configuration", collection, "configuration", {
1538
- name: values.name,
1539
- directory: values.directory,
1540
- server: values.server
1541
- });
1542
- await executeSchematic("Base Client", collection, "part-base", {
1543
- name: values.name,
1544
- part: "client",
1545
- directory: values.directory,
1546
- language: values.language,
1547
- initFunctions: values.initFunctions
1548
- });
1549
- await executeSchematic("Client main file", collection, "part-main", {
1550
- name: values.name,
1551
- part: "client",
1552
- directory: values.directory,
1553
- language: values.language,
1554
- initFunctions: values.initFunctions
1555
- });
1556
- if (values.server) {
1557
- await executeSchematic("Base server", collection, "part-base", {
1440
+ async collectValues(inputs) {
1441
+ return {
1442
+ name: await getNewNameInputOrAsk(inputs),
1443
+ directory: getNewPathInput(inputs),
1444
+ packageManager: await getNewPackageManagerInputOrAsk(inputs),
1445
+ language: await getNewLanguageInputOrAsk(inputs),
1446
+ strict: await getNewStrictOrAsk(inputs),
1447
+ server: await getNewServerOrAsk(inputs),
1448
+ initFunctions: getNewInitFunctionsWithDefault(inputs),
1449
+ skipInstall: await getNewSkipInstallOrAsk(inputs),
1450
+ docker: await getDockerOrAsk(inputs)
1451
+ };
1452
+ }
1453
+ async scaffold(values, directory) {
1454
+ const collection = CollectionFactory.create("@nanoforge-dev/schematics" /* NANOFORGE */, directory);
1455
+ console.info(Messages.SCHEMATICS_START);
1456
+ console.info();
1457
+ await this.generateApplication(collection, values);
1458
+ await this.generateConfiguration(collection, values);
1459
+ await this.generateClientParts(collection, values);
1460
+ await this.generateDocker(collection, values);
1461
+ if (values.server) {
1462
+ await this.generateServerParts(collection, values);
1463
+ }
1464
+ }
1465
+ generateApplication(collection, values) {
1466
+ return executeSchematic("Application", collection, "application", {
1558
1467
  name: values.name,
1559
- part: "server",
1560
1468
  directory: values.directory,
1469
+ packageManager: values.packageManager,
1561
1470
  language: values.language,
1562
- initFunctions: values.initFunctions
1471
+ strict: values.strict,
1472
+ server: values.server
1473
+ });
1474
+ }
1475
+ generateConfiguration(collection, values) {
1476
+ return executeSchematic("Configuration", collection, "configuration", {
1477
+ name: values.name,
1478
+ directory: values.directory,
1479
+ server: values.server
1480
+ });
1481
+ }
1482
+ async generateClientParts(collection, values) {
1483
+ const partOptions = this.partOptions(values, "client");
1484
+ await executeSchematic("Client base", collection, "part-base", {
1485
+ ...partOptions,
1486
+ server: values.server
1487
+ });
1488
+ await executeSchematic("Client main file", collection, "part-main", partOptions);
1489
+ }
1490
+ async generateServerParts(collection, values) {
1491
+ const partOptions = this.partOptions(values, "server");
1492
+ await executeSchematic("Server base", collection, "part-base", {
1493
+ ...partOptions,
1494
+ server: values.server
1495
+ });
1496
+ await executeSchematic("Server main file", collection, "part-main", partOptions);
1497
+ }
1498
+ async generateDocker(collection, values) {
1499
+ await executeSchematic("Docker", collection, "docker", {
1500
+ name: values.name,
1501
+ directory: values.directory,
1502
+ packageManager: values.packageManager
1563
1503
  });
1564
- await executeSchematic("Server main file", collection, "part-main", {
1504
+ }
1505
+ partOptions(values, part) {
1506
+ return {
1565
1507
  name: values.name,
1566
- part: "server",
1508
+ part,
1567
1509
  directory: values.directory,
1568
1510
  language: values.language,
1569
1511
  initFunctions: values.initFunctions
1570
- });
1512
+ };
1571
1513
  }
1572
- }, "generateApplicationFiles");
1573
- var runInstall = /* @__PURE__ */ __name(async (directory, pkgManagerName) => {
1574
- try {
1575
- const packageManager = PackageManagerFactory.create(pkgManagerName);
1576
- await packageManager.install(directory);
1577
- } catch (error4) {
1578
- if (error4 && error4.message) {
1579
- console4.error(ansis4.red(error4.message));
1580
- }
1581
- process4.exit(1);
1514
+ async installDependencies(packageManagerName, directory) {
1515
+ const packageManager = PackageManagerFactory.create(packageManagerName);
1516
+ return await packageManager.install(directory);
1582
1517
  }
1583
- }, "runInstall");
1518
+ };
1584
1519
 
1585
1520
  // src/action/actions/start.action.ts
1586
- import * as ansis5 from "ansis";
1587
- import * as console5 from "console";
1588
- import * as process5 from "process";
1589
- import { join as join5 } from "path";
1521
+ import { join as join6 } from "path";
1590
1522
  var StartAction = class extends AbstractAction {
1591
1523
  static {
1592
1524
  __name(this, "StartAction");
1593
1525
  }
1526
+ startMessage = Messages.START_START;
1527
+ successMessage = Messages.START_SUCCESS;
1528
+ failureMessage = Messages.START_FAILED;
1594
1529
  async handle(_args, options) {
1595
- console5.info(Messages.RUN_START);
1596
- console5.info();
1597
- try {
1598
- const directory = getDirectoryInput(options);
1599
- const config2 = await getConfig(options, directory);
1600
- const clientDir = config2.client.runtime.dir;
1601
- const serverDir = config2.server.runtime.dir;
1602
- const clientPort = getStringInputWithDefault(options, "clientPort", config2.client.port);
1603
- const cert = getStringInput(options, "cert");
1604
- const key = getStringInput(options, "key");
1605
- const watch3 = getWatchInput(options);
1606
- await Promise.all([
1607
- config2.server.enable ? this.startServer(directory, serverDir, watch3) : void 0,
1608
- this.startClient(
1609
- clientPort,
1610
- directory,
1611
- clientDir,
1612
- {
1613
- watch: watch3,
1614
- serverGameDir: config2.server.enable ? serverDir : void 0
1615
- },
1616
- cert,
1617
- key
1618
- )
1619
- ]);
1620
- process5.exit(0);
1621
- } catch (e) {
1622
- console5.error(e);
1623
- process5.exit(1);
1624
- }
1530
+ const directory = getDirectoryInput(options);
1531
+ const config2 = await getConfig(options, directory);
1532
+ const watch3 = getWatchInput(options);
1533
+ const ports = this.resolvePorts(options, config2);
1534
+ const ssl = this.resolveSSL(options);
1535
+ const tasks = this.buildStartTasks(config2, directory, watch3, ports, ssl);
1536
+ await Promise.all(tasks);
1537
+ return { keepAlive: true };
1538
+ }
1539
+ resolvePorts(options, config2) {
1540
+ return {
1541
+ clientPort: getStringInputWithDefault(options, "clientPort", config2.client.port),
1542
+ gameExposurePort: getStringInput(options, "gameExposurePort"),
1543
+ serverPort: getStringInput(options, "serverPort")
1544
+ };
1545
+ }
1546
+ resolveSSL(options) {
1547
+ const cert = getStringInput(options, "cert");
1548
+ const key = getStringInput(options, "key");
1549
+ if (!cert && !key) return void 0;
1550
+ if (!cert) throw new Error("No cert entered for SSL. Please enter a key with --cert.");
1551
+ if (!key) throw new Error("No key entered for SSL. Please enter a key with --key.");
1552
+ return {
1553
+ cert,
1554
+ key
1555
+ };
1625
1556
  }
1626
- async startClient(port, directory, gameDir, options, cert, key) {
1627
- const path = getModulePath("@nanoforge-dev/loader-client/package.json", true);
1628
- const params = {
1629
- PORT: port,
1630
- GAME_DIR: getCwd(join5(directory, gameDir)),
1631
- CERT: cert ? join5(getCwd(directory), cert) : void 0,
1632
- KEY: key ? join5(getCwd(directory), key) : void 0
1557
+ buildStartTasks(config2, directory, watch3, ports, ssl) {
1558
+ const tasks = [];
1559
+ if (config2.server.enable) {
1560
+ tasks.push(this.startServer(directory, config2.server.runtime.dir, watch3, ports.serverPort));
1561
+ }
1562
+ tasks.push(this.startClient(directory, config2, watch3, ports, ssl));
1563
+ return tasks;
1564
+ }
1565
+ async startClient(directory, config2, watch3, ports, ssl) {
1566
+ const loaderPath = getModulePath("@nanoforge-dev/loader-client/package.json", true);
1567
+ const gameDir = config2.client.runtime.dir;
1568
+ const env2 = this.buildClientEnv(directory, gameDir, watch3, config2, ports, ssl);
1569
+ await this.runLoader("Client", loaderPath, env2);
1570
+ }
1571
+ buildClientEnv(directory, gameDir, watch3, config2, ports, ssl) {
1572
+ const env2 = {
1573
+ PORT: ports.clientPort,
1574
+ GAME_DIR: getCwd(join6(directory, gameDir))
1633
1575
  };
1634
- if (options?.watch) {
1635
- params["WATCH"] = "true";
1636
- if (options?.serverGameDir) {
1637
- params["WATCH_SERVER_GAME_DIR"] = getCwd(join5(directory, options.serverGameDir));
1576
+ if (ports.gameExposurePort) {
1577
+ env2["GAME_EXPOSURE_PORT"] = ports.gameExposurePort;
1578
+ }
1579
+ if (watch3) {
1580
+ env2["WATCH"] = "true";
1581
+ if (config2.server.enable) {
1582
+ env2["WATCH_SERVER_GAME_DIR"] = getCwd(join6(directory, config2.server.runtime.dir));
1638
1583
  }
1639
1584
  }
1640
- return runPart("Client", path, params);
1585
+ if (ssl) {
1586
+ env2["CERT"] = ssl.cert;
1587
+ env2["KEY"] = ssl.key;
1588
+ }
1589
+ return env2;
1641
1590
  }
1642
- startServer(directory, gameDir, watch3) {
1643
- const path = getModulePath("@nanoforge-dev/loader-server/package.json", true);
1644
- const params = {
1645
- GAME_DIR: getCwd(join5(directory, gameDir))
1591
+ async startServer(directory, gameDir, watch3, port) {
1592
+ const loaderPath = getModulePath("@nanoforge-dev/loader-server/package.json", true);
1593
+ const env2 = {
1594
+ GAME_DIR: getCwd(join6(directory, gameDir))
1646
1595
  };
1647
- if (watch3) params["WATCH"] = "true";
1648
- return runPart("Server", path, params);
1596
+ if (port) env2["PORT"] = port;
1597
+ if (watch3) env2["WATCH"] = "true";
1598
+ await this.runLoader("Server", loaderPath, env2);
1599
+ }
1600
+ async runLoader(name, directory, env2) {
1601
+ await runSafe(async () => {
1602
+ const packageManager = await PackageManagerFactory.find(directory);
1603
+ await packageManager.run(name, directory, "start", env2, [], true);
1604
+ });
1649
1605
  }
1650
1606
  };
1651
- var runPart = /* @__PURE__ */ __name(async (part, directory, env2, flags) => {
1652
- try {
1653
- const packageManager = await PackageManagerFactory.find(directory);
1654
- await packageManager.run(part, directory, "start", env2, flags, true);
1655
- } catch (error4) {
1656
- if (error4 && error4.message) {
1657
- console5.error(ansis5.red(error4.message));
1658
- }
1659
- }
1660
- }, "runPart");
1661
1607
 
1662
1608
  // src/command/abstract.command.ts
1663
1609
  var AbstractCommand = class {
@@ -1667,6 +1613,13 @@ var AbstractCommand = class {
1667
1613
  static {
1668
1614
  __name(this, "AbstractCommand");
1669
1615
  }
1616
+ static mapToInput(mapping) {
1617
+ const input2 = /* @__PURE__ */ new Map();
1618
+ for (const [key, value] of Object.entries(mapping)) {
1619
+ input2.set(key, { value });
1620
+ }
1621
+ return input2;
1622
+ }
1670
1623
  };
1671
1624
 
1672
1625
  // src/command/commands/build.command.ts
@@ -1675,15 +1628,15 @@ var BuildCommand = class extends AbstractCommand {
1675
1628
  __name(this, "BuildCommand");
1676
1629
  }
1677
1630
  load(program) {
1678
- program.command("build").description("build your game").option("-d, --directory [directory]", "specify the directory of your project").option("-c, --config [config]", "path to the config file", "nanoforge.config.json").option("--client-outDir [clientDirectory]", "specify the output directory of the client").option("--server-outDir [serverDirectory]", "specify the output directory of the server").option("--watch", "build app in watching mode", false).action(async (rawOptions) => {
1679
- const options = /* @__PURE__ */ new Map();
1680
- options.set("directory", { value: rawOptions.directory });
1681
- options.set("config", { value: rawOptions.config });
1682
- options.set("clientDirectory", { value: rawOptions.clientOutDir });
1683
- options.set("serverDirectory", { value: rawOptions.serverOutDir });
1684
- options.set("watch", { value: rawOptions.watch });
1685
- const args = /* @__PURE__ */ new Map();
1686
- await this.action.handle(args, options);
1631
+ program.command("build").description("build your game").option("-d, --directory [directory]", "specify the directory of your project").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option("--client-outDir [clientDirectory]", "specify the output directory of the client").option("--server-outDir [serverDirectory]", "specify the output directory of the server").option("--watch", "build app in watching mode", false).action(async (rawOptions) => {
1632
+ const options = AbstractCommand.mapToInput({
1633
+ directory: rawOptions.directory,
1634
+ config: rawOptions.config,
1635
+ clientDirectory: rawOptions.clientOutDir,
1636
+ serverDirectory: rawOptions.serverOutDir,
1637
+ watch: rawOptions.watch
1638
+ });
1639
+ await this.action.run(/* @__PURE__ */ new Map(), options);
1687
1640
  });
1688
1641
  }
1689
1642
  };
@@ -1694,13 +1647,13 @@ var DevCommand = class extends AbstractCommand {
1694
1647
  __name(this, "DevCommand");
1695
1648
  }
1696
1649
  load(program) {
1697
- program.command("dev").description("run your game in dev mode").option("-d, --directory [directory]", "specify the directory of your project").option("--generate", "generate app from config", false).action(async (rawOptions) => {
1698
- const options = /* @__PURE__ */ new Map();
1699
- options.set("directory", { value: rawOptions.directory });
1700
- options.set("config", { value: rawOptions.config });
1701
- options.set("generate", { value: rawOptions.generate });
1702
- const args = /* @__PURE__ */ new Map();
1703
- await this.action.handle(args, options);
1650
+ program.command("dev").description("run your game in dev mode").option("-d, --directory [directory]", "specify the directory of your project").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option("--generate", "generate app from config", false).action(async (rawOptions) => {
1651
+ const options = AbstractCommand.mapToInput({
1652
+ directory: rawOptions.directory,
1653
+ config: rawOptions.config,
1654
+ generate: rawOptions.generate
1655
+ });
1656
+ await this.action.run(/* @__PURE__ */ new Map(), options);
1704
1657
  });
1705
1658
  }
1706
1659
  };
@@ -1711,13 +1664,13 @@ var GenerateCommand = class extends AbstractCommand {
1711
1664
  __name(this, "GenerateCommand");
1712
1665
  }
1713
1666
  load(program) {
1714
- program.command("generate").description("generate nanoforge files from config").option("-d, --directory [directory]", "specify the directory of your project").option("-c, --config [config]", "path to the config file", "nanoforge.config.json").option("--watch", "generate app in watching mode", false).action(async (rawOptions) => {
1715
- const options = /* @__PURE__ */ new Map();
1716
- options.set("directory", { value: rawOptions.directory });
1717
- options.set("config", { value: rawOptions.config });
1718
- options.set("watch", { value: rawOptions.watch });
1719
- const args = /* @__PURE__ */ new Map();
1720
- await this.action.handle(args, options);
1667
+ program.command("generate").description("generate nanoforge files from config").option("-d, --directory [directory]", "specify the directory of your project").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option("--watch", "generate app in watching mode", false).action(async (rawOptions) => {
1668
+ const options = AbstractCommand.mapToInput({
1669
+ directory: rawOptions.directory,
1670
+ config: rawOptions.config,
1671
+ watch: rawOptions.watch
1672
+ });
1673
+ await this.action.run(/* @__PURE__ */ new Map(), options);
1721
1674
  });
1722
1675
  }
1723
1676
  };
@@ -1729,11 +1682,13 @@ var InstallCommand = class extends AbstractCommand {
1729
1682
  }
1730
1683
  load(program) {
1731
1684
  program.command("install [names...]").alias("add").description("add NanoForge library to your project").option("-d, --directory [directory]", "specify the directory of your project").action(async (names, rawOptions) => {
1732
- const options = /* @__PURE__ */ new Map();
1733
- options.set("directory", { value: rawOptions.directory });
1734
- const args = /* @__PURE__ */ new Map();
1735
- args.set("names", { value: names.length ? names : void 0 });
1736
- await this.action.handle(args, options);
1685
+ const options = AbstractCommand.mapToInput({
1686
+ directory: rawOptions.directory
1687
+ });
1688
+ const args = AbstractCommand.mapToInput({
1689
+ names: names.length ? names : void 0
1690
+ });
1691
+ await this.action.run(args, options);
1737
1692
  });
1738
1693
  }
1739
1694
  };
@@ -1744,19 +1699,20 @@ var NewCommand = class extends AbstractCommand {
1744
1699
  __name(this, "NewCommand");
1745
1700
  }
1746
1701
  load(program) {
1747
- program.command("new").description("create a new nanoforge project").option("-d, --directory [directory]", "specify the directory of your project").option("--name [name]", "specify the name of your project").option("--path [path]", "specify the path of your project").option("--package-manager [packageManager]", "specify the package manager of your project").option("--language [language]", "specify the language of your project").option("--strict", "use strict mode").option("--no-strict", "do not use strict mode").option("--server", "create a server").option("--no-server", "do not create a server").option("--init-functions", "initialize functions").option("--no-init-functions", "do not initialize functions").option("--skip-install", "skip installing dependencies").option("--no-skip-install", "do not skip installing dependencies").action(async (rawOptions) => {
1748
- const options = /* @__PURE__ */ new Map();
1749
- options.set("directory", { value: rawOptions.directory });
1750
- options.set("name", { value: rawOptions.name });
1751
- options.set("path", { value: rawOptions.path });
1752
- options.set("packageManager", { value: rawOptions.packageManager });
1753
- options.set("language", { value: rawOptions.language });
1754
- options.set("strict", { value: rawOptions.strict });
1755
- options.set("server", { value: rawOptions.server });
1756
- options.set("initFunctions", { value: rawOptions.initFunctions });
1757
- options.set("skipInstall", { value: rawOptions.skipInstall });
1758
- const args = /* @__PURE__ */ new Map();
1759
- await this.action.handle(args, options);
1702
+ program.command("new").description("create a new nanoforge project").option("-d, --directory [directory]", "specify the directory of your project").option("--name [name]", "specify the name of your project").option("--path [path]", "specify the path of your project").option("--package-manager [packageManager]", "specify the package manager of your project").option("--language [language]", "specify the language of your project").option("--strict", "use strict mode").option("--no-strict", "do not use strict mode").option("--server", "create a server").option("--no-server", "do not create a server").option("--init-functions", "initialize functions").option("--no-init-functions", "do not initialize functions").option("--skip-install", "skip installing dependencies").option("--no-skip-install", "do not skip installing dependencies").option("--docker", "generate docker files").option("--no-docker", "do not generate docker files").action(async (rawOptions) => {
1703
+ const options = AbstractCommand.mapToInput({
1704
+ directory: rawOptions.directory,
1705
+ name: rawOptions.name,
1706
+ path: rawOptions.path,
1707
+ packageManager: rawOptions.packageManager,
1708
+ language: rawOptions.language,
1709
+ strict: rawOptions.strict,
1710
+ server: rawOptions.server,
1711
+ initFunctions: rawOptions.initFunctions,
1712
+ skipInstall: rawOptions.skipInstall,
1713
+ docker: rawOptions.docker
1714
+ });
1715
+ await this.action.run(/* @__PURE__ */ new Map(), options);
1760
1716
  });
1761
1717
  }
1762
1718
  };
@@ -1767,23 +1723,21 @@ var StartCommand = class extends AbstractCommand {
1767
1723
  __name(this, "StartCommand");
1768
1724
  }
1769
1725
  load(program) {
1770
- program.command("start").description("start your game").option("-d, --directory [directory]", "specify the directory of your project").option("-c, --config [config]", "path to the config file", "nanoforge.config.json").option(
1726
+ program.command("start").description("start your game").option("-d, --directory [directory]", "specify the directory of your project").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option(
1771
1727
  "-p, --client-port [clientPort]",
1772
1728
  "specify the port of the loader (the website to load the game)"
1773
1729
  ).option("--game-exposure-port [gameExposurePort]", "specify the port of the game exposure").option("--server-port [serverPort]", "specify the port of the server").option("--watch", "run app in watching mode", false).option("--cert [cert]", "path to the SSL certificate for HTTPS").option("--key [key]", "path to the SSL key for HTTPS").action(async (rawOptions) => {
1774
- const options = /* @__PURE__ */ new Map();
1775
- options.set("directory", { value: rawOptions.directory });
1776
- options.set("config", { value: rawOptions.config });
1777
- options.set("clientPort", { value: rawOptions.clientPort });
1778
- options.set("gameExposurePort", {
1779
- value: rawOptions.gameExposurePort
1730
+ const options = AbstractCommand.mapToInput({
1731
+ directory: rawOptions.directory,
1732
+ config: rawOptions.config,
1733
+ clientPort: rawOptions.clientPort,
1734
+ gameExposurePort: rawOptions.gameExposurePort,
1735
+ serverPort: rawOptions.serverPort,
1736
+ watch: rawOptions.watch,
1737
+ cert: rawOptions.cert,
1738
+ key: rawOptions.key
1780
1739
  });
1781
- options.set("serverPort", { value: rawOptions.serverPort });
1782
- options.set("watch", { value: rawOptions.watch });
1783
- options.set("cert", { value: rawOptions.cert });
1784
- options.set("key", { value: rawOptions.key });
1785
- const args = /* @__PURE__ */ new Map();
1786
- await this.action.handle(args, options);
1740
+ await this.action.run(/* @__PURE__ */ new Map(), options);
1787
1741
  });
1788
1742
  }
1789
1743
  };
@@ -1805,8 +1759,8 @@ var CommandLoader = class {
1805
1759
  static handleInvalidCommand(program) {
1806
1760
  program.on("command:*", () => {
1807
1761
  console.error(`
1808
- ${Prefixes.ERROR} Invalid command: ${red8`%s`}`, program.args.join(" "));
1809
- console.log(`See ${red8`--help`} for a list of available commands.
1762
+ ${Prefixes.ERROR} Invalid command: ${red6`%s`}`, program.args.join(" "));
1763
+ console.log(`See ${red6`--help`} for a list of available commands.
1810
1764
  `);
1811
1765
  process.exit(1);
1812
1766
  });