@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.
package/dist/nf.js CHANGED
@@ -36,11 +36,11 @@ var __decorateClass = (decorators, target, key, kind) => {
36
36
 
37
37
  // package.json
38
38
  var require_package = __commonJS({
39
- "package.json"(exports, module2) {
40
- module2.exports = {
39
+ "package.json"(exports, module) {
40
+ module.exports = {
41
41
  $schema: "https://json.schemastore.org/package.json",
42
42
  name: "@nanoforge-dev/cli",
43
- version: "1.1.1",
43
+ version: "1.2.0",
44
44
  description: "NanoForge CLI",
45
45
  keywords: [
46
46
  "nanoforge",
@@ -159,7 +159,7 @@ var loadLocalBinCommandLoader = /* @__PURE__ */ __name(async () => {
159
159
  }, "loadLocalBinCommandLoader");
160
160
 
161
161
  // src/command/command.loader.ts
162
- import { red as red8 } from "ansis";
162
+ import { red as red6 } from "ansis";
163
163
 
164
164
  // src/lib/ui/messages.ts
165
165
  import { green } from "ansis";
@@ -187,48 +187,63 @@ var Emojis = {
187
187
  };
188
188
 
189
189
  // src/lib/ui/messages.ts
190
+ var success = /* @__PURE__ */ __name((text) => `${Emojis.ROCKET} ${text}`, "success");
191
+ var failure = /* @__PURE__ */ __name((text) => `${Emojis.SCREAM} ${text}`, "failure");
190
192
  var Messages = {
193
+ // --- Build ---
191
194
  BUILD_START: "NanoForge Build",
192
- BUILD_WATCH_START: "Start watching mode",
195
+ BUILD_SUCCESS: success("Build succeeded!"),
196
+ BUILD_FAILED: failure("Build failed!"),
197
+ BUILD_WATCH_START: "Watching for changes...",
193
198
  BUILD_PART_IN_PROGRESS: /* @__PURE__ */ __name((part) => `Building ${part}`, "BUILD_PART_IN_PROGRESS"),
194
- BUILD_PART_WATCH_IN_PROGRESS: /* @__PURE__ */ __name((part) => `${part} updated. Rebuilding`, "BUILD_PART_WATCH_IN_PROGRESS"),
195
- BUILD_NOTHING: "Nothing to build, terminated.",
196
- BUILD_SUCCESS: `${Emojis.ROCKET} Build succeeded !`,
197
- BUILD_PART_FAILED: /* @__PURE__ */ __name((part, commandToRunManually) => `${Emojis.SCREAM} Build of ${part} failed !
198
- 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"),
199
- BUILD_FAILED: `${Emojis.SCREAM} Build failed !`,
199
+ BUILD_PART_WATCH_IN_PROGRESS: /* @__PURE__ */ __name((part) => `${part} updated, rebuilding`, "BUILD_PART_WATCH_IN_PROGRESS"),
200
+ BUILD_PART_FAILED: /* @__PURE__ */ __name((part, command) => failure(`Build of ${part} failed!
201
+ Try running manually: ${command}`), "BUILD_PART_FAILED"),
202
+ // --- Install ---
200
203
  INSTALL_START: "NanoForge Installation",
201
- INSTALL_NAMES_QUESTION: "Witch libraries do you want to install ?",
204
+ INSTALL_SUCCESS: success("Installation completed!"),
205
+ INSTALL_FAILED: failure("Installation failed!"),
206
+ INSTALL_NAMES_QUESTION: "Which libraries do you want to install?",
207
+ // --- New Project ---
202
208
  NEW_START: "NanoForge Project Creation",
203
- NEW_SUCCESS: `${Emojis.ROCKET} Project successfully created !`,
204
- NEW_FAILED: `${Emojis.SCREAM} Project creation failed !`,
205
- NEW_NAME_QUESTION: "What is the name of your project ?",
206
- NEW_PACKAGE_MANAGER_QUESTION: "Which package manager do you want to use ?",
207
- NEW_LANGUAGE_QUESTION: "Which language do you want to use ?",
208
- NEW_STRICT_QUESTION: "Do you want to use types strict mode ?",
209
- NEW_SERVER_QUESTION: "Do you want generate a server to create a multiplayer game ?",
210
- NEW_SKIP_INSTALL_QUESTION: "Do you want to skip installation ?",
209
+ NEW_SUCCESS: success("Project successfully created!"),
210
+ NEW_FAILED: failure("Project creation failed!"),
211
+ NEW_NAME_QUESTION: "What is the name of your project?",
212
+ NEW_PACKAGE_MANAGER_QUESTION: "Which package manager do you want to use?",
213
+ NEW_LANGUAGE_QUESTION: "Which language do you want to use?",
214
+ NEW_STRICT_QUESTION: "Do you want to use strict type checking?",
215
+ NEW_SERVER_QUESTION: "Do you want to generate a server for multiplayer?",
216
+ NEW_SKIP_INSTALL_QUESTION: "Do you want to skip dependency installation?",
217
+ NEW_DOCKER_QUESTION: "Do you want to add a Dockerfile for containerization?",
218
+ // --- Generate ---
211
219
  GENERATE_START: "NanoForge Generate",
212
- GENERATE_WATCH_START: "Start watching mode",
213
- GENERATE_SUCCESS: `${Emojis.ROCKET} Generate succeeded !`,
214
- GENERATE_FAILED: `${Emojis.SCREAM} Generate failed !`,
215
- DEV_START: "NanoForge Dev mode",
220
+ GENERATE_SUCCESS: success("Generation succeeded!"),
221
+ GENERATE_FAILED: failure("Generation failed!"),
222
+ GENERATE_WATCH_START: "Watching for changes...",
223
+ // --- Dev ---
224
+ DEV_START: "NanoForge Dev Mode",
216
225
  DEV_SUCCESS: "Dev mode ended",
217
- DEV_FAILED: `${Emojis.SCREAM} Dev failed !`,
218
- SCHEMATICS_START: "Schematics execution",
219
- SCHEMATIC_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Executing schematic ${name}...`, "SCHEMATIC_IN_PROGRESS"),
220
- SCHEMATIC_WATCH_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Update watched. Executing schematic ${name}...`, "SCHEMATIC_WATCH_IN_PROGRESS"),
221
- SCHEMATIC_SUCCESS: /* @__PURE__ */ __name((name) => `${Emojis.ROCKET} Schematic ${name} executed successfully !`, "SCHEMATIC_SUCCESS"),
222
- SCHEMATIC_FAILED: /* @__PURE__ */ __name((name) => `${Emojis.SCREAM} Schematic ${name} execution failed. See error below for more details.`, "SCHEMATIC_FAILED"),
223
- PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS: `Installation in progress... ${Emojis.COFFEE}`,
224
- PACKAGE_MANAGER_INSTALLATION_NOTHING: "Nothing to install, terminated.",
225
- 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"),
226
- PACKAGE_MANAGER_INSTALLATION_FAILED: /* @__PURE__ */ __name((commandToRunManually) => `${Emojis.SCREAM} Packages installation failed !
227
- 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"),
228
- RUN_START: "NanoForge Run",
229
- RUN_PART_IN_PROGRESS: /* @__PURE__ */ __name((part) => `Running ${part}...`, "RUN_PART_IN_PROGRESS"),
230
- RUN_PART_SUCCESS: /* @__PURE__ */ __name((part) => `${Emojis.ROCKET} Run of ${part} terminated.`, "RUN_PART_SUCCESS"),
231
- RUN_PART_FAILED: /* @__PURE__ */ __name((part) => `${Emojis.SCREAM} Run of ${part} failed !`, "RUN_PART_FAILED"),
226
+ DEV_FAILED: failure("Dev mode failed!"),
227
+ // --- Start ---
228
+ START_START: "NanoForge Start",
229
+ START_SUCCESS: success("Start completed!"),
230
+ START_FAILED: failure("Start failed!"),
231
+ START_PART_IN_PROGRESS: /* @__PURE__ */ __name((part) => `Starting ${part}...`, "START_PART_IN_PROGRESS"),
232
+ START_PART_SUCCESS: /* @__PURE__ */ __name((part) => success(`${part} terminated.`), "START_PART_SUCCESS"),
233
+ START_PART_FAILED: /* @__PURE__ */ __name((part) => failure(`${part} failed!`), "START_PART_FAILED"),
234
+ // --- Schematics ---
235
+ SCHEMATICS_START: "Running schematics",
236
+ SCHEMATIC_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Generating ${name}...`, "SCHEMATIC_IN_PROGRESS"),
237
+ SCHEMATIC_WATCH_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Change detected, regenerating ${name}...`, "SCHEMATIC_WATCH_IN_PROGRESS"),
238
+ SCHEMATIC_SUCCESS: /* @__PURE__ */ __name((name) => success(`${name} generated successfully!`), "SCHEMATIC_SUCCESS"),
239
+ SCHEMATIC_FAILED: /* @__PURE__ */ __name((name) => failure(`${name} generation failed.`), "SCHEMATIC_FAILED"),
240
+ // --- Package Manager ---
241
+ PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS: `Installing dependencies... ${Emojis.COFFEE}`,
242
+ PACKAGE_MANAGER_INSTALLATION_NOTHING: "Nothing to install.",
243
+ PACKAGE_MANAGER_INSTALLATION_SUCCEED: /* @__PURE__ */ __name((names) => names ? success(`Packages installed: ${names.map((n) => green(n)).join(", ")}`) : success("Packages installed!"), "PACKAGE_MANAGER_INSTALLATION_SUCCEED"),
244
+ PACKAGE_MANAGER_INSTALLATION_FAILED: /* @__PURE__ */ __name((command) => failure(`Package installation failed!
245
+ Try running manually: ${command}`), "PACKAGE_MANAGER_INSTALLATION_FAILED"),
246
+ // --- Runner ---
232
247
  RUNNER_EXECUTION_ERROR: /* @__PURE__ */ __name((command) => `
233
248
  Failed to execute command: ${command}`, "RUNNER_EXECUTION_ERROR")
234
249
  };
@@ -240,10 +255,12 @@ var Prefixes = {
240
255
  ERROR: bgRgb(210, 0, 75).bold.rgb(0, 0, 0)(" Error ")
241
256
  };
242
257
 
258
+ // src/lib/ui/spinner.ts
259
+ import ora from "ora";
260
+ var getSpinner = /* @__PURE__ */ __name((message) => ora({ text: message }), "getSpinner");
261
+
243
262
  // src/action/actions/build.action.ts
244
- import * as ansis from "ansis";
245
263
  import { watch } from "chokidar";
246
- import * as console2 from "console";
247
264
  import { dirname, join as join4 } from "path";
248
265
 
249
266
  // src/lib/input/base-inputs.ts
@@ -307,12 +324,24 @@ var getDevGenerateInput = /* @__PURE__ */ __name((inputs) => {
307
324
  import { confirm } from "@inquirer/prompts";
308
325
 
309
326
  // src/lib/utils/errors.ts
327
+ import { red } from "ansis";
328
+ var getErrorMessage = /* @__PURE__ */ __name((error) => {
329
+ if (error instanceof Error) return error.message;
330
+ if (typeof error === "string") return error;
331
+ return void 0;
332
+ }, "getErrorMessage");
333
+ var handleActionError = /* @__PURE__ */ __name((context, error) => {
334
+ console.error();
335
+ console.error(red(context));
336
+ const msg = getErrorMessage(error);
337
+ if (msg) console.error(msg);
338
+ process.exit(1);
339
+ }, "handleActionError");
310
340
  var promptError = /* @__PURE__ */ __name((err) => {
311
341
  if (err.name === "ExitPromptError") {
312
342
  process.exit(1);
313
- } else {
314
- throw err;
315
343
  }
344
+ throw err;
316
345
  }, "promptError");
317
346
 
318
347
  // src/lib/question/questions/confirm.question.ts
@@ -384,79 +413,27 @@ var getInstallNamesInputOrAsk = /* @__PURE__ */ __name((inputs) => {
384
413
  );
385
414
  }, "getInstallNamesInputOrAsk");
386
415
 
387
- // src/lib/package-manager/package-manager.factory.ts
388
- import fs2 from "fs";
389
- import { resolve as resolve2 } from "path";
416
+ // src/lib/package-manager/package-manager.ts
417
+ import { bold, red as red3 } from "ansis";
390
418
 
391
- // src/lib/runner/runner.factory.ts
392
- import { yellow } from "ansis";
393
-
394
- // src/lib/runner/abstract.runner.ts
395
- import { red } from "ansis";
396
- import { spawn } from "child_process";
397
- import * as process2 from "process";
398
- var AbstractRunner = class {
399
- constructor(binary, args = []) {
400
- this.binary = binary;
401
- this.args = args;
419
+ // src/lib/runner/process-logger.ts
420
+ import { green as green2, red as red2, yellow } from "ansis";
421
+ var formatLines = /* @__PURE__ */ __name((chunk) => {
422
+ return chunk.toString().replace(/\r\n|\n/g, "\n").replace(/^\n+|\n+$/g, "").split("\n");
423
+ }, "formatLines");
424
+ var timestamp = /* @__PURE__ */ __name(() => yellow(`[${(/* @__PURE__ */ new Date()).toISOString()}]`), "timestamp");
425
+ var createStdoutLogger = /* @__PURE__ */ __name((name) => (chunk) => {
426
+ const prefix = green2(`(${name}) INFO -`);
427
+ for (const line of formatLines(chunk)) {
428
+ console.info(`${timestamp()} ${prefix} ${line}`);
402
429
  }
403
- static {
404
- __name(this, "AbstractRunner");
430
+ }, "createStdoutLogger");
431
+ var createStderrLogger = /* @__PURE__ */ __name((name) => (chunk) => {
432
+ const prefix = red2(`(${name}) ERROR -`);
433
+ for (const line of formatLines(chunk)) {
434
+ console.error(`${timestamp()} ${prefix} ${line}`);
405
435
  }
406
- async run(args, collect = false, cwd2 = process2.cwd(), env2, listeners, failSpinner) {
407
- const options = {
408
- cwd: cwd2,
409
- stdio: collect ? "pipe" : "inherit",
410
- shell: true,
411
- env: { ...process2.env, ...env2 }
412
- };
413
- return new Promise((resolve3, reject) => {
414
- const child = spawn(
415
- `${this.binary} ${[...this.args, ...args].join(" ")}`,
416
- options
417
- );
418
- const res = [];
419
- child.stdout?.on(
420
- "data",
421
- listeners?.onStdout ?? ((data) => res.push(data.toString().replace(/\r\n|\n/, "")))
422
- );
423
- child.stderr?.on(
424
- "data",
425
- listeners?.onStderr ?? ((data) => res.push(data.toString().replace(/\r\n|\n/, "")))
426
- );
427
- child.on("close", (code) => {
428
- if (code === 0) {
429
- resolve3(collect && res.length ? res.join("\n") : null);
430
- } else {
431
- if (failSpinner) failSpinner();
432
- console.error(
433
- red(Messages.RUNNER_EXECUTION_ERROR([this.binary, ...this.args, ...args].join(" ")))
434
- );
435
- if (res.length) {
436
- console.error();
437
- console.error(res.join("\n"));
438
- console.error();
439
- }
440
- reject();
441
- }
442
- });
443
- });
444
- }
445
- rawFullCommand(args) {
446
- const commandArgs = [...this.args, ...args];
447
- return `${this.binary} ${commandArgs.join(" ")}`;
448
- }
449
- };
450
-
451
- // src/lib/runner/runners/bun.runner.ts
452
- var BunRunner = class extends AbstractRunner {
453
- static {
454
- __name(this, "BunRunner");
455
- }
456
- constructor() {
457
- super("bun");
458
- }
459
- };
436
+ }, "createStderrLogger");
460
437
 
461
438
  // src/lib/utils/path.ts
462
439
  import fs from "fs";
@@ -483,268 +460,256 @@ var resolveCLINodeBinaryPath = /* @__PURE__ */ __name((name) => {
483
460
  throw new Error("Could not find module path");
484
461
  }, "resolveCLINodeBinaryPath");
485
462
 
486
- // src/lib/runner/runners/local-bun.runner.ts
487
- var LocalBunRunner = class extends AbstractRunner {
488
- static {
489
- __name(this, "LocalBunRunner");
490
- }
491
- constructor() {
492
- super(resolveCLINodeBinaryPath("bun"));
493
- }
494
- };
495
-
496
- // src/lib/runner/runners/npm.runner.ts
497
- var NpmRunner = class extends AbstractRunner {
498
- static {
499
- __name(this, "NpmRunner");
500
- }
501
- constructor() {
502
- super("npm");
503
- }
504
- };
505
-
506
- // src/lib/runner/runners/pnpm.runner.ts
507
- var PnpmRunner = class extends AbstractRunner {
508
- static {
509
- __name(this, "PnpmRunner");
510
- }
511
- constructor() {
512
- super("pnpm");
513
- }
514
- };
515
-
516
- // src/lib/runner/runners/schematic.runner.ts
517
- var SchematicRunner = class _SchematicRunner extends AbstractRunner {
518
- static {
519
- __name(this, "SchematicRunner");
520
- }
521
- static getModulePaths() {
522
- return module.paths;
523
- }
524
- static findClosestSchematicsBinary() {
525
- try {
526
- return getModulePath("@angular-devkit/schematics-cli/bin/schematics.js");
527
- } catch (e) {
528
- console.error(e);
529
- throw new Error("'schematics' binary path could not be found!");
530
- }
531
- }
532
- constructor() {
533
- super(`node`, [`"${_SchematicRunner.findClosestSchematicsBinary()}"`]);
534
- }
535
- };
536
-
537
- // src/lib/runner/runners/yarn.runner.ts
538
- var YarnRunner = class extends AbstractRunner {
539
- static {
540
- __name(this, "YarnRunner");
541
- }
542
- constructor() {
543
- super("yarn");
544
- }
545
- };
546
-
547
- // src/lib/runner/runner.factory.ts
548
- var RunnerFactory = class {
549
- static {
550
- __name(this, "RunnerFactory");
551
- }
552
- static create(runner) {
553
- switch (runner) {
554
- case 0 /* BUN */:
555
- return new BunRunner();
556
- case 1 /* LOCAL_BUN */:
557
- return new LocalBunRunner();
558
- case 2 /* NPM */:
559
- return new NpmRunner();
560
- case 3 /* PNPM */:
561
- return new PnpmRunner();
562
- case 4 /* SCHEMATIC */:
563
- return new SchematicRunner();
564
- case 5 /* YARN */:
565
- return new YarnRunner();
566
- default:
567
- console.info(yellow`[WARN] Unsupported runner: ${runner}`);
568
- throw Error(`Unsupported runner: ${runner}`);
569
- }
570
- }
571
- };
572
-
573
- // src/lib/package-manager/abstract.package-manager.ts
574
- import { bold, green as green2, red as red2, yellow as yellow2 } from "ansis";
575
- import ora from "ora";
576
- var SPINNER = /* @__PURE__ */ __name((message) => ora({
577
- text: message
578
- }), "SPINNER");
579
- var FAIL_SPINNER = /* @__PURE__ */ __name((spinner) => () => spinner.fail(), "FAIL_SPINNER");
580
- var AbstractPackageManager = class {
581
- constructor(runner) {
463
+ // src/lib/package-manager/package-manager.ts
464
+ var PackageManager = class {
465
+ constructor(name, commands, runner) {
466
+ this.name = name;
467
+ this.commands = commands;
582
468
  this.runner = runner;
583
469
  }
584
470
  static {
585
- __name(this, "AbstractPackageManager");
471
+ __name(this, "PackageManager");
586
472
  }
587
473
  async install(directory) {
588
- const spinner = SPINNER(Messages.PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS);
589
- spinner.start();
590
- try {
591
- const commandArgs = [this.cli.install, this.cli.silentFlag];
592
- const collect = true;
593
- await this.runner.run(
594
- commandArgs,
595
- collect,
596
- getCwd(directory),
597
- void 0,
598
- void 0,
599
- () => spinner.fail()
600
- );
601
- spinner.succeed();
602
- this.printInstallSuccess();
603
- } catch {
604
- const commandArgs = [this.cli.install];
605
- const commandToRun = this.runner.rawFullCommand(commandArgs);
606
- this.printInstallFailure(commandToRun);
607
- }
608
- }
609
- version() {
610
- const commandArguments = ["--version"];
611
- const collect = true;
612
- return this.runner.run(commandArguments, collect);
613
- }
614
- addProduction(directory, dependencies) {
615
- const command = [this.cli.add, this.cli.saveFlag];
616
- return this.add(command, directory, dependencies);
617
- }
618
- addDevelopment(directory, dependencies) {
619
- const command = [this.cli.add, this.cli.saveDevFlag];
620
- return this.add(command, directory, dependencies);
621
- }
622
- async build(name, directory, entry, output, flags, watch3) {
623
- if (!this.cli.build) throw new Error(`Package manager ${this.name} does not support building`);
624
- const spinner = SPINNER(
625
- (watch3 ? Messages.BUILD_PART_WATCH_IN_PROGRESS : Messages.BUILD_PART_IN_PROGRESS)(name)
474
+ const args = [this.commands.install, this.commands.silentFlag];
475
+ const result = await this.withSpinner(
476
+ Messages.PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS,
477
+ async (spinner) => {
478
+ await this.exec(args, directory, { onFail: /* @__PURE__ */ __name(() => spinner.fail(), "onFail") });
479
+ this.logSuccess(Messages.PACKAGE_MANAGER_INSTALLATION_SUCCEED());
480
+ },
481
+ () => this.logFailure(this.formatFailCommand([this.commands.install]))
626
482
  );
627
- spinner.start();
628
- try {
629
- const commandArgs = [
630
- this.cli.build,
631
- this.cli.silentFlag,
632
- entry,
633
- "--outdir",
634
- output,
635
- ...flags ?? []
636
- ];
637
- const collect = true;
638
- await this.runner.run(
639
- commandArgs,
640
- collect,
641
- getCwd(directory),
642
- void 0,
643
- void 0,
644
- FAIL_SPINNER(spinner)
645
- );
646
- spinner.succeed();
647
- return true;
648
- } catch {
649
- const commandArgs = [this.cli.install];
650
- const commandToRun = this.runner.rawFullCommand(commandArgs);
651
- console.error(red2(Messages.BUILD_PART_FAILED(name, bold(commandToRun))));
652
- return false;
653
- }
483
+ return result.success;
484
+ }
485
+ async addProduction(directory, dependencies) {
486
+ return this.addDependencies(this.commands.saveFlag, directory, dependencies);
487
+ }
488
+ async addDevelopment(directory, dependencies) {
489
+ return this.addDependencies(this.commands.saveDevFlag, directory, dependencies);
490
+ }
491
+ async build(name, directory, entry, output, flags = [], watch3 = false) {
492
+ this.assertSupports("build");
493
+ const message = watch3 ? Messages.BUILD_PART_WATCH_IN_PROGRESS(name) : Messages.BUILD_PART_IN_PROGRESS(name);
494
+ const args = [
495
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
496
+ this.commands.build,
497
+ this.commands.silentFlag,
498
+ entry,
499
+ "--outdir",
500
+ output,
501
+ ...flags
502
+ ];
503
+ const result = await this.withSpinner(
504
+ message,
505
+ async (spinner) => {
506
+ await this.exec(args, directory, { onFail: /* @__PURE__ */ __name(() => spinner.fail(), "onFail") });
507
+ },
508
+ () => this.logBuildFailure(name)
509
+ );
510
+ return result.success;
654
511
  }
655
- async run(name, directory, file, env2 = {}, flags = [], silent = false) {
512
+ async run(name, directory, script, env2 = {}, flags = [], silent = false) {
513
+ console.info(Messages.START_PART_IN_PROGRESS(name));
656
514
  try {
657
- console.info(Messages.RUN_PART_IN_PROGRESS(name));
658
- const commandArgs = [...flags, this.cli.run];
659
- if (silent) commandArgs.push(this.cli.silentFlag);
660
- commandArgs.push(file);
661
- await this.runner.run(commandArgs, true, getCwd(directory), env2, {
662
- onStdout: this.onRunStdout(name),
663
- onStderr: this.onRunStderr(name)
515
+ const args = this.buildRunArgs(script, flags, silent);
516
+ await this.exec(args, directory, {
517
+ env: env2,
518
+ listeners: {
519
+ onStdout: createStdoutLogger(name),
520
+ onStderr: createStderrLogger(name)
521
+ }
664
522
  });
665
- console.info(Messages.RUN_PART_SUCCESS(name));
523
+ console.info(Messages.START_PART_SUCCESS(name));
666
524
  return true;
667
525
  } catch {
668
- console.error(red2(Messages.RUN_PART_FAILED(name)));
526
+ console.error(red3(Messages.START_PART_FAILED(name)));
669
527
  return false;
670
528
  }
671
529
  }
672
530
  async runDev(directory, command, env2 = {}, flags = [], collect = true) {
673
531
  try {
674
- const commandArgs = [this.cli.exec, command, ...flags];
675
- await this.runner.run(commandArgs, collect, getCwd(directory), env2);
532
+ await this.exec([this.commands.run, command, ...flags], directory, { collect, env: env2 });
676
533
  return true;
677
534
  } catch {
678
535
  return false;
679
536
  }
680
537
  }
681
- async add(args, directory, dependencies) {
682
- if (!dependencies.length) {
683
- console.info();
684
- console.info(Messages.PACKAGE_MANAGER_INSTALLATION_NOTHING);
685
- console.info();
686
- return true;
687
- }
688
- const commandArguments = [...args, ...dependencies];
689
- const spinner = SPINNER(Messages.PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS);
538
+ async withSpinner(message, task, onError) {
539
+ const spinner = getSpinner(message);
690
540
  spinner.start();
691
541
  try {
692
- const collect = true;
693
- await this.runner.run(
694
- commandArguments,
695
- collect,
696
- getCwd(directory),
697
- void 0,
698
- void 0,
699
- FAIL_SPINNER(spinner)
700
- );
542
+ const value = await task(spinner);
701
543
  spinner.succeed();
702
- this.printInstallSuccess(dependencies);
703
- return true;
544
+ return { success: true, value };
704
545
  } catch {
705
546
  spinner.fail();
706
- const commandToRun = this.runner.rawFullCommand(commandArguments);
707
- this.printInstallFailure(commandToRun);
708
- return false;
547
+ if (onError) onError();
548
+ return { success: false };
709
549
  }
710
550
  }
711
- printInstallSuccess(dependencies) {
551
+ async addDependencies(saveFlag, directory, dependencies) {
552
+ if (!dependencies.length) {
553
+ this.logEmpty(Messages.PACKAGE_MANAGER_INSTALLATION_NOTHING);
554
+ return true;
555
+ }
556
+ const args = [this.commands.add, saveFlag, ...dependencies];
557
+ const result = await this.withSpinner(
558
+ Messages.PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS,
559
+ async (spinner) => {
560
+ await this.exec(args, directory, { onFail: /* @__PURE__ */ __name(() => spinner.fail(), "onFail") });
561
+ this.logSuccess(Messages.PACKAGE_MANAGER_INSTALLATION_SUCCEED(dependencies));
562
+ },
563
+ () => this.logFailure(this.formatFailCommand(args))
564
+ );
565
+ return result.success;
566
+ }
567
+ assertSupports(feature) {
568
+ if (!this.commands[feature]) {
569
+ throw new Error(`Package manager "${this.name}" does not support "${feature}"`);
570
+ }
571
+ }
572
+ buildRunArgs(script, flags, silent) {
573
+ const args = [...flags, this.commands.run];
574
+ if (silent) args.push(this.commands.silentFlag);
575
+ args.push(script);
576
+ return args;
577
+ }
578
+ exec(args, directory, options = {}) {
579
+ return this.runner.run(args, {
580
+ collect: options.collect ?? true,
581
+ cwd: getCwd(directory),
582
+ env: options.env,
583
+ listeners: options.listeners,
584
+ onFail: options.onFail
585
+ });
586
+ }
587
+ formatFailCommand(args) {
588
+ return this.runner.fullCommand(args);
589
+ }
590
+ logSuccess(message) {
712
591
  console.info();
713
- console.info(Messages.PACKAGE_MANAGER_INSTALLATION_SUCCEED(dependencies));
592
+ console.info(message);
714
593
  console.info();
715
594
  }
716
- printInstallFailure(command) {
717
- console.error(red2(Messages.PACKAGE_MANAGER_INSTALLATION_FAILED(bold(command))));
595
+ logFailure(command) {
596
+ console.error(red3(Messages.PACKAGE_MANAGER_INSTALLATION_FAILED(bold(command))));
597
+ }
598
+ logBuildFailure(name) {
599
+ const command = this.formatFailCommand([this.commands.install]);
600
+ console.error(red3(Messages.BUILD_PART_FAILED(name, bold(command))));
601
+ }
602
+ logEmpty(message) {
603
+ console.info();
604
+ console.info(message);
605
+ console.info();
718
606
  }
719
- onRunStdout = /* @__PURE__ */ __name((name) => (chunk) => {
720
- chunk.toString().replace(/\r\n|\n/g, "\n").replace(/^\n+|\n+$/g, "").split("\n").forEach((line) => {
721
- const date = yellow2(`[${(/* @__PURE__ */ new Date()).toISOString()}]`);
722
- const prompt = green2(`(${name}) INFO -`);
723
- console.info(`${date} ${prompt} ${line}`);
724
- });
725
- }, "onRunStdout");
726
- onRunStderr = /* @__PURE__ */ __name((name) => (chunk) => {
727
- chunk.toString().replace(/\r\n|\n/g, "\n").replace(/^\n+|\n+$/g, "").split("\n").forEach((line) => {
728
- const date = yellow2(`[${(/* @__PURE__ */ new Date()).toISOString()}]`);
729
- const prompt = red2(`(${name}) ERROR -`);
730
- console.error(`${date} ${prompt} ${line}`);
731
- });
732
- }, "onRunStderr");
733
607
  };
734
608
 
735
- // src/lib/package-manager/package-managers/bun.package-manager.ts
736
- var BunPackageManager = class extends AbstractPackageManager {
609
+ // src/lib/package-manager/package-manager.factory.ts
610
+ import fs2 from "fs";
611
+ import { resolve as resolve2 } from "path";
612
+
613
+ // src/lib/runner/runner.ts
614
+ import { red as red4 } from "ansis";
615
+ import { spawn } from "child_process";
616
+ import * as process2 from "process";
617
+ var Runner = class {
618
+ constructor(binary, baseArgs = []) {
619
+ this.binary = binary;
620
+ this.baseArgs = baseArgs;
621
+ }
737
622
  static {
738
- __name(this, "BunPackageManager");
623
+ __name(this, "Runner");
739
624
  }
740
- constructor() {
741
- super(RunnerFactory.create(0 /* BUN */));
625
+ async run(args, options = {}) {
626
+ const { collect = false, cwd: cwd2 = process2.cwd(), env: env2, listeners, onFail } = options;
627
+ const spawnOpts = this.buildSpawnOptions(collect, cwd2, env2);
628
+ const fullArgs = [...this.baseArgs, ...args];
629
+ return new Promise((resolve3, reject) => {
630
+ const child = spawn(`${this.binary} ${fullArgs.join(" ")}`, spawnOpts);
631
+ const output = this.attachOutputHandlers(child, listeners);
632
+ child.on("close", (code) => {
633
+ if (code === 0) {
634
+ resolve3(this.formatOutput(output, collect));
635
+ } else {
636
+ this.handleFailure(output, fullArgs, onFail);
637
+ reject(this.createError(fullArgs, code));
638
+ }
639
+ });
640
+ });
742
641
  }
743
- get name() {
744
- return "bun" /* BUN */.toUpperCase();
642
+ fullCommand(args) {
643
+ return [this.binary, ...this.baseArgs, ...args].join(" ");
745
644
  }
746
- get cli() {
645
+ buildSpawnOptions(collect, cwd2, env2) {
747
646
  return {
647
+ cwd: cwd2,
648
+ stdio: collect ? "pipe" : "inherit",
649
+ shell: true,
650
+ env: { ...process2.env, ...env2 }
651
+ };
652
+ }
653
+ attachOutputHandlers(child, listeners) {
654
+ const output = [];
655
+ const defaultHandler = /* @__PURE__ */ __name((data) => output.push(data.toString().replace(/\r\n|\n/, "")), "defaultHandler");
656
+ child.stdout?.on("data", listeners?.onStdout ?? defaultHandler);
657
+ child.stderr?.on("data", listeners?.onStderr ?? defaultHandler);
658
+ return output;
659
+ }
660
+ formatOutput(output, collect) {
661
+ return collect && output.length ? output.join("\n") : null;
662
+ }
663
+ handleFailure(output, args, onFail) {
664
+ if (onFail) onFail();
665
+ this.logFailedCommand(args);
666
+ this.logCapturedOutput(output);
667
+ }
668
+ logFailedCommand(args) {
669
+ console.error(red4(`
670
+ Failed to execute command: ${this.binary} ${args.join(" ")}`));
671
+ }
672
+ logCapturedOutput(output) {
673
+ if (output.length) {
674
+ console.error();
675
+ console.error(output.join("\n"));
676
+ console.error();
677
+ }
678
+ }
679
+ createError(args, code) {
680
+ return new Error(`Command "${this.binary} ${args.join(" ")}" exited with code ${code}`);
681
+ }
682
+ };
683
+
684
+ // src/lib/runner/runner.factory.ts
685
+ var RunnerFactory = class {
686
+ static {
687
+ __name(this, "RunnerFactory");
688
+ }
689
+ static create(binary, args) {
690
+ return new Runner(binary, args);
691
+ }
692
+ static createLocal(binary, args) {
693
+ return new Runner(resolveCLINodeBinaryPath(binary), args);
694
+ }
695
+ static createSchematic() {
696
+ const binaryPath = this.resolveSchematicBinary();
697
+ return new Runner("node", [`"${binaryPath}"`]);
698
+ }
699
+ static resolveSchematicBinary() {
700
+ try {
701
+ return getModulePath("@angular-devkit/schematics-cli/bin/schematics.js");
702
+ } catch {
703
+ throw new Error("'schematics' binary path could not be found!");
704
+ }
705
+ }
706
+ };
707
+
708
+ // src/lib/package-manager/package-manager-configs.ts
709
+ var PM_CONFIGS = {
710
+ ["bun" /* BUN */]: {
711
+ binary: "bun",
712
+ commands: {
748
713
  install: "install",
749
714
  add: "add",
750
715
  update: "update",
@@ -754,23 +719,11 @@ var BunPackageManager = class extends AbstractPackageManager {
754
719
  saveFlag: "--save",
755
720
  saveDevFlag: "--dev",
756
721
  silentFlag: "--silent"
757
- };
758
- }
759
- };
760
-
761
- // src/lib/package-manager/package-managers/local-bun.package-manager.ts
762
- var LocalBunPackageManager = class extends AbstractPackageManager {
763
- static {
764
- __name(this, "LocalBunPackageManager");
765
- }
766
- constructor() {
767
- super(RunnerFactory.create(1 /* LOCAL_BUN */));
768
- }
769
- get name() {
770
- return "local_bun" /* LOCAL_BUN */.toUpperCase();
771
- }
772
- get cli() {
773
- return {
722
+ }
723
+ },
724
+ ["local_bun" /* LOCAL_BUN */]: {
725
+ binary: "bun",
726
+ commands: {
774
727
  install: "install",
775
728
  add: "add",
776
729
  update: "update",
@@ -782,23 +735,11 @@ var LocalBunPackageManager = class extends AbstractPackageManager {
782
735
  saveFlag: "--save",
783
736
  saveDevFlag: "--dev",
784
737
  silentFlag: "--silent"
785
- };
786
- }
787
- };
788
-
789
- // src/lib/package-manager/package-managers/npm.package-manager.ts
790
- var NpmPackageManager = class extends AbstractPackageManager {
791
- static {
792
- __name(this, "NpmPackageManager");
793
- }
794
- constructor() {
795
- super(RunnerFactory.create(2 /* NPM */));
796
- }
797
- get name() {
798
- return "npm" /* NPM */.toUpperCase();
799
- }
800
- get cli() {
801
- return {
738
+ }
739
+ },
740
+ ["npm" /* NPM */]: {
741
+ binary: "npm",
742
+ commands: {
802
743
  install: "install",
803
744
  add: "install",
804
745
  update: "update",
@@ -808,23 +749,11 @@ var NpmPackageManager = class extends AbstractPackageManager {
808
749
  saveFlag: "--save",
809
750
  saveDevFlag: "--save-dev",
810
751
  silentFlag: "--silent"
811
- };
812
- }
813
- };
814
-
815
- // src/lib/package-manager/package-managers/pnpm.package-manager.ts
816
- var PnpmPackageManager = class extends AbstractPackageManager {
817
- static {
818
- __name(this, "PnpmPackageManager");
819
- }
820
- constructor() {
821
- super(RunnerFactory.create(3 /* PNPM */));
822
- }
823
- get name() {
824
- return "pnpm" /* PNPM */.toUpperCase();
825
- }
826
- get cli() {
827
- return {
752
+ }
753
+ },
754
+ ["pnpm" /* PNPM */]: {
755
+ binary: "pnpm",
756
+ commands: {
828
757
  install: "install",
829
758
  add: "add",
830
759
  update: "update",
@@ -834,23 +763,11 @@ var PnpmPackageManager = class extends AbstractPackageManager {
834
763
  saveFlag: "-P",
835
764
  saveDevFlag: "-D",
836
765
  silentFlag: "--silent"
837
- };
838
- }
839
- };
840
-
841
- // src/lib/package-manager/package-managers/yarn.package-manager.ts
842
- var YarnPackageManager = class extends AbstractPackageManager {
843
- static {
844
- __name(this, "YarnPackageManager");
845
- }
846
- constructor() {
847
- super(RunnerFactory.create(5 /* YARN */));
848
- }
849
- get name() {
850
- return "yarn" /* YARN */.toUpperCase();
851
- }
852
- get cli() {
853
- return {
766
+ }
767
+ },
768
+ ["yarn" /* YARN */]: {
769
+ binary: "yarn",
770
+ commands: {
854
771
  install: "install",
855
772
  add: "add",
856
773
  update: "update",
@@ -860,54 +777,63 @@ var YarnPackageManager = class extends AbstractPackageManager {
860
777
  saveFlag: "",
861
778
  saveDevFlag: "-D",
862
779
  silentFlag: "--silent"
863
- };
780
+ }
864
781
  }
865
782
  };
866
783
 
867
784
  // src/lib/package-manager/package-manager.factory.ts
785
+ var LOCK_FILE_MAP = {
786
+ "bun.lock": "bun" /* BUN */,
787
+ "package-lock.json": "npm" /* NPM */,
788
+ "pnpm-lock.yaml": "pnpm" /* PNPM */,
789
+ "yarn.lock": "yarn" /* YARN */
790
+ };
868
791
  var PackageManagerFactory = class {
869
792
  static {
870
793
  __name(this, "PackageManagerFactory");
871
794
  }
872
795
  static create(name) {
873
- switch (name) {
874
- case "bun" /* BUN */:
875
- return new BunPackageManager();
876
- case "local_bun" /* LOCAL_BUN */:
877
- return new LocalBunPackageManager();
878
- case "npm" /* NPM */:
879
- return new NpmPackageManager();
880
- case "pnpm" /* PNPM */:
881
- return new PnpmPackageManager();
882
- case "yarn" /* YARN */:
883
- return new YarnPackageManager();
884
- default:
885
- throw new Error(`Package manager ${name} is not managed.`);
796
+ const config2 = PM_CONFIGS[name];
797
+ if (!config2) {
798
+ throw new Error(`Package manager ${name} is not managed.`);
886
799
  }
800
+ const runner = this.createRunner(name, config2.binary);
801
+ return new PackageManager(name, config2.commands, runner);
887
802
  }
888
803
  static async find(directory = ".") {
889
- const DEFAULT_PACKAGE_MANAGER = "npm" /* NPM */;
804
+ const detected = await this.detectFromLockFile(directory);
805
+ return this.create(detected);
806
+ }
807
+ static createRunner(name, binary) {
808
+ if (name === "local_bun" /* LOCAL_BUN */) {
809
+ return RunnerFactory.createLocal("bun");
810
+ }
811
+ return RunnerFactory.create(binary);
812
+ }
813
+ static async detectFromLockFile(directory) {
890
814
  try {
891
815
  const files = await fs2.promises.readdir(resolve2(directory));
892
- if (files.includes("bun.lock")) {
893
- return this.create("bun" /* BUN */);
894
- }
895
- if (files.includes("package-lock.json")) {
896
- return this.create("npm" /* NPM */);
816
+ for (const [lockFile, pmName] of Object.entries(LOCK_FILE_MAP)) {
817
+ if (files.includes(lockFile)) return pmName;
897
818
  }
898
- if (files.includes("pnpm-lock.yaml")) {
899
- return this.create("pnpm" /* PNPM */);
900
- }
901
- if (files.includes("yarn.lock")) {
902
- return this.create("yarn" /* YARN */);
903
- }
904
- return this.create(DEFAULT_PACKAGE_MANAGER);
905
819
  } catch {
906
- return this.create(DEFAULT_PACKAGE_MANAGER);
907
820
  }
821
+ return "npm" /* NPM */;
908
822
  }
909
823
  };
910
824
 
825
+ // src/lib/utils/run-safe.ts
826
+ import { red as red5 } from "ansis";
827
+ var runSafe = /* @__PURE__ */ __name(async (fn, fallback) => {
828
+ try {
829
+ return await fn();
830
+ } catch (error) {
831
+ const msg = getErrorMessage(error);
832
+ if (msg) console.error(red5(msg));
833
+ return fallback;
834
+ }
835
+ }, "runSafe");
836
+
911
837
  // src/lib/config/config.type.ts
912
838
  import { Expose, Type } from "class-transformer";
913
839
  import { IsBoolean, IsEnum, IsNotEmpty, IsPort, IsString, ValidateNested } from "class-validator";
@@ -1033,6 +959,10 @@ import { validate } from "class-validator";
1033
959
  import { existsSync as existsSync2, readFileSync } from "fs";
1034
960
  import { join as join3 } from "path";
1035
961
 
962
+ // src/lib/constants.ts
963
+ var CONFIG_FILE_NAME = "nanoforge.config.json";
964
+ var NANOFORGE_DIR = ".nanoforge";
965
+
1036
966
  // src/lib/utils/object.ts
1037
967
  var isObject = /* @__PURE__ */ __name((item) => {
1038
968
  return item && typeof item === "object" && !Array.isArray(item);
@@ -1088,18 +1018,17 @@ var getConfigPath = /* @__PURE__ */ __name((directory, name) => {
1088
1018
  if (name) {
1089
1019
  return join3(directory, name);
1090
1020
  } else {
1091
- for (const n of ["nanoforge.config.json"]) {
1021
+ for (const n of [CONFIG_FILE_NAME]) {
1092
1022
  const path = join3(directory, n);
1093
1023
  if (existsSync2(path)) return path;
1094
1024
  }
1095
- throw new Error(`Unsupported config: ${name}`);
1025
+ throw new Error(`No config file found in directory: ${directory}`);
1096
1026
  }
1097
1027
  }, "getConfigPath");
1098
1028
  var loadConfig = /* @__PURE__ */ __name(async (directory, name) => {
1099
1029
  if (config) return config;
1100
1030
  let rawData;
1101
1031
  const path = getConfigPath(directory, name);
1102
- if (!path) throw new Error("No config file found");
1103
1032
  try {
1104
1033
  rawData = deepMerge(CONFIG_DEFAULTS, JSON.parse(readFileSync(path, "utf-8")));
1105
1034
  } catch {
@@ -1127,6 +1056,32 @@ var AbstractAction = class {
1127
1056
  static {
1128
1057
  __name(this, "AbstractAction");
1129
1058
  }
1059
+ async run(args, options, extraFlags) {
1060
+ this.logStart();
1061
+ try {
1062
+ const result = await this.handle(args, options, extraFlags);
1063
+ this.resolveResult(result);
1064
+ } catch (error) {
1065
+ handleActionError(this.failureMessage, error);
1066
+ }
1067
+ }
1068
+ logStart() {
1069
+ console.info();
1070
+ console.info(`${Prefixes.INFO} ${this.startMessage}`);
1071
+ console.info();
1072
+ }
1073
+ resolveResult(result) {
1074
+ const success2 = result?.success !== false;
1075
+ const keepAlive = result?.keepAlive === true;
1076
+ if (keepAlive) return;
1077
+ console.info();
1078
+ if (!success2) {
1079
+ if (this.failureMessage) console.error(this.failureMessage);
1080
+ process.exit(1);
1081
+ }
1082
+ if (this.successMessage) console.info(this.successMessage);
1083
+ process.exit(0);
1084
+ }
1130
1085
  };
1131
1086
 
1132
1087
  // src/action/actions/build.action.ts
@@ -1134,121 +1089,121 @@ var BuildAction = class extends AbstractAction {
1134
1089
  static {
1135
1090
  __name(this, "BuildAction");
1136
1091
  }
1092
+ startMessage = Messages.BUILD_START;
1093
+ successMessage = Messages.BUILD_SUCCESS;
1094
+ failureMessage = Messages.BUILD_FAILED;
1137
1095
  async handle(_args, options) {
1138
- console2.info(Messages.BUILD_START);
1139
- console2.info();
1140
- try {
1141
- const directory = getDirectoryInput(options);
1142
- const config2 = await getConfig(options, directory);
1143
- const watch3 = getWatchInput(options);
1144
- const client = getPart(
1096
+ const directory = getDirectoryInput(options);
1097
+ const config2 = await getConfig(options, directory);
1098
+ const isWatch = getWatchInput(options);
1099
+ const targets = this.resolveTargets(config2, options);
1100
+ const results = await this.buildAll(targets, directory, isWatch);
1101
+ if (isWatch) {
1102
+ return this.enterWatchMode();
1103
+ }
1104
+ return { success: results.every(Boolean) };
1105
+ }
1106
+ resolveTargets(config2, options) {
1107
+ const targets = [
1108
+ this.createTarget(
1109
+ "Client",
1145
1110
  config2.client.build,
1146
- options.get("clientDirectory")?.value,
1147
- "client"
1148
- );
1149
- let res = await buildPart("Client", client, directory, { watch: watch3 });
1150
- if (config2.server.enable) {
1151
- const server = getPart(
1111
+ "browser",
1112
+ getStringInput(options, "clientDirectory")
1113
+ )
1114
+ ];
1115
+ if (config2.server.enable) {
1116
+ targets.push(
1117
+ this.createTarget(
1118
+ "Server",
1152
1119
  config2.server.build,
1153
- options.get("serverDirectory")?.value,
1154
- "server"
1155
- );
1156
- res = await buildPart("Server", server, directory, { watch: watch3 }) ? res : false;
1157
- }
1158
- console2.info();
1159
- if (watch3) {
1160
- console2.info(Messages.BUILD_WATCH_START);
1161
- console2.info();
1162
- return;
1163
- }
1164
- if (!res) {
1165
- console2.info(Messages.BUILD_FAILED);
1166
- process.exit(1);
1167
- }
1168
- console2.info(Messages.BUILD_SUCCESS);
1169
- process.exit(0);
1170
- } catch (e) {
1171
- console2.error(e);
1172
- process.exit(1);
1120
+ "node",
1121
+ getStringInput(options, "serverDirectory")
1122
+ )
1123
+ );
1173
1124
  }
1125
+ return targets;
1174
1126
  }
1175
- };
1176
- var getPart = /* @__PURE__ */ __name((config2, directoryOption, target) => {
1177
- return {
1178
- entry: config2.entryFile,
1179
- output: directoryOption || config2.outDir,
1180
- target
1181
- };
1182
- }, "getPart");
1183
- var buildPart = /* @__PURE__ */ __name(async (name, part, directory, options) => {
1184
- const packageManagerName = "local_bun" /* LOCAL_BUN */;
1185
- const packageManager = PackageManagerFactory.create(packageManagerName);
1186
- const build = /* @__PURE__ */ __name(async (watch3 = false) => {
1187
- try {
1188
- return await packageManager.build(
1189
- name,
1127
+ createTarget(name, config2, platform, outDirOverride) {
1128
+ return {
1129
+ name,
1130
+ entry: config2.entryFile,
1131
+ output: outDirOverride || config2.outDir,
1132
+ platform
1133
+ };
1134
+ }
1135
+ async buildAll(targets, directory, isWatch) {
1136
+ const results = [];
1137
+ for (const target of targets) {
1138
+ const result = await this.buildTarget(target, directory, isWatch);
1139
+ results.push(result);
1140
+ }
1141
+ return results;
1142
+ }
1143
+ async buildTarget(target, directory, isWatch) {
1144
+ const packageManager = PackageManagerFactory.create("local_bun" /* LOCAL_BUN */);
1145
+ const executeBuild = /* @__PURE__ */ __name((rebuild = false) => runSafe(
1146
+ () => packageManager.build(
1147
+ target.name,
1190
1148
  directory,
1191
- part.entry,
1192
- part.output,
1193
- [
1194
- "--asset-naming",
1195
- "[name].[ext]",
1196
- "--target",
1197
- part.target === "client" ? "browser" : "node"
1198
- ],
1199
- watch3
1200
- );
1201
- } catch (error4) {
1202
- if (error4 && error4.message) {
1203
- console2.error(ansis.red(error4.message));
1204
- }
1205
- return false;
1149
+ target.entry,
1150
+ target.output,
1151
+ ["--asset-naming", "[name].[ext]", "--target", target.platform],
1152
+ rebuild
1153
+ ),
1154
+ false
1155
+ ), "executeBuild");
1156
+ if (isWatch) {
1157
+ this.watchDirectory(directory, target.entry, () => executeBuild(true));
1206
1158
  }
1207
- }, "build");
1208
- if (options?.watch)
1209
- watch(dirname(join4(getCwd(directory), part.entry))).on("change", () => build(true));
1210
- return await build();
1211
- }, "buildPart");
1159
+ const result = await executeBuild();
1160
+ return result !== false;
1161
+ }
1162
+ watchDirectory(directory, entry, onChange) {
1163
+ const watchPath = dirname(join4(getCwd(directory), entry));
1164
+ watch(watchPath).on("change", onChange);
1165
+ }
1166
+ enterWatchMode() {
1167
+ console.info();
1168
+ console.info(Messages.BUILD_WATCH_START);
1169
+ console.info();
1170
+ return { keepAlive: true };
1171
+ }
1172
+ };
1212
1173
 
1213
1174
  // src/action/actions/dev.action.ts
1214
- import * as ansis2 from "ansis";
1215
1175
  var DevAction = class extends AbstractAction {
1216
1176
  static {
1217
1177
  __name(this, "DevAction");
1218
1178
  }
1179
+ startMessage = Messages.DEV_START;
1180
+ successMessage = Messages.DEV_SUCCESS;
1181
+ failureMessage = Messages.DEV_FAILED;
1219
1182
  async handle(_args, options) {
1220
- console.info(Messages.DEV_START);
1221
- console.info();
1222
- try {
1223
- const directory = getDirectoryInput(options);
1224
- const generate = getDevGenerateInput(options);
1225
- await Promise.all([
1226
- generate ? runAction("generate", [], directory, false) : void 0,
1227
- runAction("build", [], directory, false),
1228
- runAction("start", [], directory, true)
1229
- ]);
1230
- console.info(Messages.DEV_SUCCESS);
1231
- process.exit(0);
1232
- } catch (e) {
1233
- console.error(Messages.DEV_FAILED);
1234
- console.error(e);
1235
- process.exit(1);
1183
+ const directory = getDirectoryInput(options);
1184
+ const generate = getDevGenerateInput(options);
1185
+ const tasks = this.buildTaskList(directory, generate);
1186
+ await Promise.all(tasks);
1187
+ return { keepAlive: true };
1188
+ }
1189
+ buildTaskList(directory, generate) {
1190
+ const tasks = [];
1191
+ if (generate) {
1192
+ tasks.push(this.runSubCommand("generate", directory, { silent: true }));
1236
1193
  }
1194
+ tasks.push(this.runSubCommand("build", directory, { silent: true }));
1195
+ tasks.push(this.runSubCommand("start", directory, { silent: false }));
1196
+ return tasks;
1197
+ }
1198
+ async runSubCommand(command, directory, options) {
1199
+ await runSafe(async () => {
1200
+ const packageManager = await PackageManagerFactory.find(directory);
1201
+ await packageManager.runDev(directory, "nf", {}, [command, "--watch"], options.silent);
1202
+ });
1237
1203
  }
1238
1204
  };
1239
- var runAction = /* @__PURE__ */ __name(async (command, params, directory, stdout = false) => {
1240
- try {
1241
- const packageManager = await PackageManagerFactory.find(directory);
1242
- await packageManager.runDev(directory, "nf", {}, [command, ...params, "--watch"], !stdout);
1243
- } catch (error4) {
1244
- if (error4 && error4.message) {
1245
- console.error(ansis2.red(error4.message));
1246
- }
1247
- }
1248
- }, "runAction");
1249
1205
 
1250
1206
  // src/action/actions/generate.action.ts
1251
- import * as console3 from "console";
1252
1207
  import { join as join5 } from "path";
1253
1208
 
1254
1209
  // src/lib/schematics/abstract.collection.ts
@@ -1261,25 +1216,19 @@ var AbstractCollection = class {
1261
1216
  static {
1262
1217
  __name(this, "AbstractCollection");
1263
1218
  }
1264
- async execute(name, options, flags, failSpinner) {
1219
+ async execute(name, options, flags, onFail) {
1265
1220
  const command = this.buildCommandLine(name, options, flags);
1266
- await this.runner.run(
1267
- command,
1268
- true,
1269
- this.cwd ? getCwd(this.cwd) : void 0,
1270
- void 0,
1271
- void 0,
1272
- failSpinner
1273
- );
1221
+ await this.runner.run(command, {
1222
+ collect: true,
1223
+ cwd: this.cwd ? getCwd(this.cwd) : void 0,
1224
+ onFail
1225
+ });
1274
1226
  }
1275
1227
  buildCommandLine(name, options, flags = []) {
1276
- return [`${this.collection}:${name}`, ...flags, ...this.buildOptions(options)];
1228
+ return [`${this.collection}:${name}`, ...flags, ...this.serializeOptions(options)];
1277
1229
  }
1278
- buildOptions(options) {
1279
- return options.reduce(
1280
- (old, option) => [...old, ...option.toCommandString()],
1281
- []
1282
- );
1230
+ serializeOptions(options) {
1231
+ return options.flatMap((option) => option.toCommandString());
1283
1232
  }
1284
1233
  };
1285
1234
 
@@ -1308,6 +1257,11 @@ var NanoforgeCollection = class _NanoforgeCollection extends AbstractCollection
1308
1257
  name: "part-main",
1309
1258
  alias: "main",
1310
1259
  description: "Generate a NanoForge Part Main file"
1260
+ },
1261
+ {
1262
+ name: "docker",
1263
+ alias: "docker",
1264
+ description: "Generate a Dockerfile for the application"
1311
1265
  }
1312
1266
  ];
1313
1267
  constructor(runner, cwd2) {
@@ -1339,12 +1293,11 @@ var CollectionFactory = class {
1339
1293
  __name(this, "CollectionFactory");
1340
1294
  }
1341
1295
  static create(collection, directory) {
1342
- const schematicRunner = RunnerFactory.create(4 /* SCHEMATIC */);
1296
+ const schematicRunner = RunnerFactory.createSchematic();
1343
1297
  if (collection === "@nanoforge-dev/schematics" /* NANOFORGE */) {
1344
1298
  return new NanoforgeCollection(schematicRunner, directory);
1345
- } else {
1346
- return new NanoforgeCollection(schematicRunner, directory);
1347
1299
  }
1300
+ throw new Error(`Unknown collection: ${collection}`);
1348
1301
  }
1349
1302
  };
1350
1303
 
@@ -1402,19 +1355,10 @@ var SchematicOption = class {
1402
1355
 
1403
1356
  // src/action/common/schematics.ts
1404
1357
  import { watch as watch2 } from "chokidar";
1405
-
1406
- // src/action/common/spinner.ts
1407
- import ora2 from "ora";
1408
- var getSpinner = /* @__PURE__ */ __name((message) => ora2({
1409
- text: message
1410
- }), "getSpinner");
1411
-
1412
- // src/action/common/schematics.ts
1413
1358
  var executeSchematic = /* @__PURE__ */ __name(async (name, collection, schematicName, options, fileToWatch) => {
1414
- const execute = /* @__PURE__ */ __name(async (watch3 = false) => {
1415
- const spinner = getSpinner(
1416
- (watch3 ? Messages.SCHEMATIC_WATCH_IN_PROGRESS : Messages.SCHEMATIC_IN_PROGRESS)(name)
1417
- );
1359
+ const execute = /* @__PURE__ */ __name(async (isRebuild = false) => {
1360
+ const message = isRebuild ? Messages.SCHEMATIC_WATCH_IN_PROGRESS(name) : Messages.SCHEMATIC_IN_PROGRESS(name);
1361
+ const spinner = getSpinner(message);
1418
1362
  spinner.start();
1419
1363
  await collection.execute(
1420
1364
  schematicName,
@@ -1424,16 +1368,17 @@ var executeSchematic = /* @__PURE__ */ __name(async (name, collection, schematic
1424
1368
  );
1425
1369
  spinner.succeed(Messages.SCHEMATIC_SUCCESS(name));
1426
1370
  }, "execute");
1427
- if (fileToWatch) watch2(fileToWatch).on("change", () => execute(true));
1428
- return await execute();
1371
+ if (fileToWatch) {
1372
+ watch2(fileToWatch).on("change", () => execute(true));
1373
+ }
1374
+ await execute();
1429
1375
  }, "executeSchematic");
1430
1376
  var mapSchematicOptions = /* @__PURE__ */ __name((inputs) => {
1431
- return Object.entries(inputs).reduce((old, [key, value]) => {
1432
- if (value === void 0) return old;
1433
- return [
1434
- ...old,
1435
- new SchematicOption(key, typeof value === "object" ? mapSchematicOptions(value) : value)
1436
- ];
1377
+ return Object.entries(inputs).reduce((acc, [key, value]) => {
1378
+ if (value === void 0) return acc;
1379
+ const mapped = typeof value === "object" ? new SchematicOption(key, mapSchematicOptions(value)) : new SchematicOption(key, value);
1380
+ acc.push(mapped);
1381
+ return acc;
1437
1382
  }, []);
1438
1383
  }, "mapSchematicOptions");
1439
1384
 
@@ -1442,111 +1387,98 @@ var GenerateAction = class extends AbstractAction {
1442
1387
  static {
1443
1388
  __name(this, "GenerateAction");
1444
1389
  }
1390
+ startMessage = Messages.GENERATE_START;
1391
+ successMessage = Messages.GENERATE_SUCCESS;
1392
+ failureMessage = Messages.GENERATE_FAILED;
1445
1393
  async handle(_args, options) {
1446
- console3.info(Messages.GENERATE_START);
1447
- console3.info();
1448
- try {
1449
- const directory = getDirectoryInput(options);
1450
- const config2 = await getConfig(options, directory);
1451
- const watch3 = getWatchInput(options);
1452
- const values = await getSchemaValues(config2);
1453
- await generateFiles(values, directory, watch3);
1454
- console3.info();
1455
- if (watch3) {
1456
- console3.info(Messages.GENERATE_WATCH_START);
1457
- console3.info();
1458
- return;
1459
- }
1460
- console3.info(Messages.GENERATE_SUCCESS);
1461
- process.exit(0);
1462
- } catch (e) {
1463
- console3.error(Messages.GENERATE_FAILED);
1464
- console3.error(e);
1465
- process.exit(1);
1394
+ const directory = getDirectoryInput(options);
1395
+ const config2 = await getConfig(options, directory);
1396
+ const isWatch = getWatchInput(options);
1397
+ const values = this.extractValues(config2);
1398
+ await this.generateParts(values, directory, isWatch);
1399
+ if (isWatch) {
1400
+ return this.enterWatchMode();
1466
1401
  }
1402
+ return {};
1467
1403
  }
1468
- };
1469
- var getSchemaValues = /* @__PURE__ */ __name(async (config2) => {
1470
- return {
1471
- name: config2.name,
1472
- directory: ".",
1473
- language: config2.language,
1474
- server: config2.server.enable,
1475
- initFunctions: config2.initFunctions
1476
- };
1477
- }, "getSchemaValues");
1478
- var generateFiles = /* @__PURE__ */ __name(async (values, directory, watch3) => {
1479
- const collection = CollectionFactory.create("@nanoforge-dev/schematics" /* NANOFORGE */, directory);
1480
- console3.info(Messages.SCHEMATICS_START);
1481
- console3.info();
1482
- await executeSchematic(
1483
- "Client main file",
1484
- collection,
1485
- "part-main",
1486
- {
1487
- name: values.name,
1488
- part: "client",
1489
- directory: values.directory,
1490
- language: values.language,
1491
- initFunctions: values.initFunctions
1492
- },
1493
- watch3 ? join5(getCwd(directory), values.directory, ".nanoforge", "client.save.json") : void 0
1494
- );
1495
- if (values.server) {
1404
+ extractValues(config2) {
1405
+ return {
1406
+ name: config2.name,
1407
+ directory: ".",
1408
+ language: config2.language,
1409
+ server: config2.server.enable,
1410
+ initFunctions: config2.initFunctions
1411
+ };
1412
+ }
1413
+ async generateParts(values, directory, watch3) {
1414
+ const collection = CollectionFactory.create("@nanoforge-dev/schematics" /* NANOFORGE */, directory);
1415
+ const baseOptions = this.baseSchematicOptions(values);
1496
1416
  await executeSchematic(
1497
- "Server main file",
1417
+ "Client main file",
1498
1418
  collection,
1499
1419
  "part-main",
1500
- {
1501
- name: values.name,
1502
- part: "server",
1503
- directory: values.directory,
1504
- language: values.language,
1505
- initFunctions: values.initFunctions
1506
- },
1507
- join5(getCwd(directory), values.directory, ".nanoforge", "server.save.json")
1420
+ { ...baseOptions, part: "client" },
1421
+ watch3 ? this.watchPath(directory, values.directory, "client") : void 0
1508
1422
  );
1423
+ if (values.server) {
1424
+ await executeSchematic(
1425
+ "Server main file",
1426
+ collection,
1427
+ "part-main",
1428
+ { ...baseOptions, part: "server" },
1429
+ this.watchPath(directory, values.directory, "server")
1430
+ );
1431
+ }
1509
1432
  }
1510
- }, "generateFiles");
1433
+ baseSchematicOptions(values) {
1434
+ return {
1435
+ name: values.name,
1436
+ directory: values.directory,
1437
+ language: values.language,
1438
+ initFunctions: values.initFunctions
1439
+ };
1440
+ }
1441
+ watchPath(directory, subDir, part) {
1442
+ return join5(getCwd(directory), subDir, NANOFORGE_DIR, `${part}.save.json`);
1443
+ }
1444
+ enterWatchMode() {
1445
+ console.info();
1446
+ console.info(Messages.GENERATE_WATCH_START);
1447
+ console.info();
1448
+ return { keepAlive: true };
1449
+ }
1450
+ };
1511
1451
 
1512
1452
  // src/action/actions/install.action.ts
1513
- import * as ansis3 from "ansis";
1514
- import * as process3 from "process";
1515
1453
  var InstallAction = class extends AbstractAction {
1516
1454
  static {
1517
1455
  __name(this, "InstallAction");
1518
1456
  }
1457
+ startMessage = Messages.INSTALL_START;
1458
+ successMessage = Messages.INSTALL_SUCCESS;
1459
+ failureMessage = Messages.INSTALL_FAILED;
1519
1460
  async handle(args, options) {
1520
- console.info(Messages.INSTALL_START);
1521
- console.info();
1522
- try {
1523
- const names = await getInstallNamesInputOrAsk(args);
1524
- const directory = getDirectoryInput(options);
1525
- await installPackages(names, directory);
1526
- process3.exit(0);
1527
- } catch (e) {
1528
- console.error(e);
1529
- process3.exit(1);
1530
- }
1531
- }
1532
- };
1533
- var installPackages = /* @__PURE__ */ __name(async (names, directory) => {
1534
- try {
1461
+ const names = await getInstallNamesInputOrAsk(args);
1462
+ const directory = getDirectoryInput(options);
1535
1463
  const packageManager = await PackageManagerFactory.find(directory);
1536
- const res = await packageManager.addProduction(directory, names);
1537
- if (!res) process3.exit(1);
1538
- } catch (error4) {
1539
- if (error4 && error4.message) {
1540
- console.error(ansis3.red(error4.message));
1541
- }
1542
- process3.exit(1);
1464
+ const success2 = await packageManager.addProduction(directory, names);
1465
+ return { success: success2 };
1543
1466
  }
1544
- }, "installPackages");
1467
+ };
1545
1468
 
1546
1469
  // src/action/actions/new.action.ts
1547
- import * as ansis4 from "ansis";
1548
- import console4 from "console";
1549
- import * as process4 from "process";
1470
+ import { join as join6 } from "path";
1471
+
1472
+ // src/lib/input/inputs/new/docker.input.ts
1473
+ var getDockerInput = /* @__PURE__ */ __name((inputs) => {
1474
+ return getBooleanInput(inputs, "docker");
1475
+ }, "getDockerInput");
1476
+ var getDockerOrAsk = /* @__PURE__ */ __name((inputs) => {
1477
+ return getInputOrAsk(
1478
+ getDockerInput(inputs),
1479
+ () => askConfirm(Messages.NEW_DOCKER_QUESTION, { default: true })
1480
+ );
1481
+ }, "getDockerOrAsk");
1550
1482
 
1551
1483
  // src/lib/input/inputs/new/init-functions.input.ts
1552
1484
  var getNewInitFunctionsWithDefault = /* @__PURE__ */ __name((inputs) => {
@@ -1640,172 +1572,186 @@ var NewAction = class extends AbstractAction {
1640
1572
  static {
1641
1573
  __name(this, "NewAction");
1642
1574
  }
1575
+ startMessage = Messages.NEW_START;
1576
+ successMessage = Messages.NEW_SUCCESS;
1577
+ failureMessage = Messages.NEW_FAILED;
1643
1578
  async handle(_args, options) {
1644
- console4.info(Messages.NEW_START);
1645
- try {
1646
- const directory = getDirectoryInput(options);
1647
- const values = await getSchemaValues2(options);
1648
- await generateApplicationFiles(values, directory);
1649
- if (!values.skipInstall) await runInstall(directory, values.packageManager);
1650
- console4.info();
1651
- console4.info(Messages.NEW_SUCCESS);
1652
- process4.exit(0);
1653
- } catch {
1654
- console4.error(Messages.NEW_FAILED);
1655
- process4.exit(1);
1579
+ const directory = getDirectoryInput(options);
1580
+ const values = await this.collectValues(options);
1581
+ await this.scaffold(values, directory);
1582
+ let res = true;
1583
+ if (!values.skipInstall) {
1584
+ res = await this.installDependencies(values.packageManager, join6(directory, values.name));
1656
1585
  }
1586
+ return { success: res };
1657
1587
  }
1658
- };
1659
- var getSchemaValues2 = /* @__PURE__ */ __name(async (inputs) => {
1660
- return {
1661
- name: await getNewNameInputOrAsk(inputs),
1662
- directory: getNewPathInput(inputs),
1663
- packageManager: await getNewPackageManagerInputOrAsk(inputs),
1664
- language: await getNewLanguageInputOrAsk(inputs),
1665
- strict: await getNewStrictOrAsk(inputs),
1666
- server: await getNewServerOrAsk(inputs),
1667
- initFunctions: getNewInitFunctionsWithDefault(inputs),
1668
- skipInstall: await getNewSkipInstallOrAsk(inputs)
1669
- };
1670
- }, "getSchemaValues");
1671
- var generateApplicationFiles = /* @__PURE__ */ __name(async (values, directory) => {
1672
- console4.info();
1673
- const collection = CollectionFactory.create("@nanoforge-dev/schematics" /* NANOFORGE */, directory);
1674
- console4.info();
1675
- console4.info(Messages.SCHEMATICS_START);
1676
- console4.info();
1677
- await executeSchematic("Application", collection, "application", {
1678
- name: values.name,
1679
- directory: values.directory,
1680
- packageManager: values.packageManager,
1681
- language: values.language,
1682
- strict: values.strict,
1683
- server: values.server
1684
- });
1685
- await executeSchematic("Configuration", collection, "configuration", {
1686
- name: values.name,
1687
- directory: values.directory,
1688
- server: values.server
1689
- });
1690
- await executeSchematic("Base Client", collection, "part-base", {
1691
- name: values.name,
1692
- part: "client",
1693
- directory: values.directory,
1694
- language: values.language,
1695
- initFunctions: values.initFunctions
1696
- });
1697
- await executeSchematic("Client main file", collection, "part-main", {
1698
- name: values.name,
1699
- part: "client",
1700
- directory: values.directory,
1701
- language: values.language,
1702
- initFunctions: values.initFunctions
1703
- });
1704
- if (values.server) {
1705
- await executeSchematic("Base server", collection, "part-base", {
1588
+ async collectValues(inputs) {
1589
+ return {
1590
+ name: await getNewNameInputOrAsk(inputs),
1591
+ directory: getNewPathInput(inputs),
1592
+ packageManager: await getNewPackageManagerInputOrAsk(inputs),
1593
+ language: await getNewLanguageInputOrAsk(inputs),
1594
+ strict: await getNewStrictOrAsk(inputs),
1595
+ server: await getNewServerOrAsk(inputs),
1596
+ initFunctions: getNewInitFunctionsWithDefault(inputs),
1597
+ skipInstall: await getNewSkipInstallOrAsk(inputs),
1598
+ docker: await getDockerOrAsk(inputs)
1599
+ };
1600
+ }
1601
+ async scaffold(values, directory) {
1602
+ const collection = CollectionFactory.create("@nanoforge-dev/schematics" /* NANOFORGE */, directory);
1603
+ console.info(Messages.SCHEMATICS_START);
1604
+ console.info();
1605
+ await this.generateApplication(collection, values);
1606
+ await this.generateConfiguration(collection, values);
1607
+ await this.generateClientParts(collection, values);
1608
+ await this.generateDocker(collection, values);
1609
+ if (values.server) {
1610
+ await this.generateServerParts(collection, values);
1611
+ }
1612
+ }
1613
+ generateApplication(collection, values) {
1614
+ return executeSchematic("Application", collection, "application", {
1706
1615
  name: values.name,
1707
- part: "server",
1708
1616
  directory: values.directory,
1617
+ packageManager: values.packageManager,
1709
1618
  language: values.language,
1710
- initFunctions: values.initFunctions
1619
+ strict: values.strict,
1620
+ server: values.server
1621
+ });
1622
+ }
1623
+ generateConfiguration(collection, values) {
1624
+ return executeSchematic("Configuration", collection, "configuration", {
1625
+ name: values.name,
1626
+ directory: values.directory,
1627
+ server: values.server
1628
+ });
1629
+ }
1630
+ async generateClientParts(collection, values) {
1631
+ const partOptions = this.partOptions(values, "client");
1632
+ await executeSchematic("Client base", collection, "part-base", {
1633
+ ...partOptions,
1634
+ server: values.server
1635
+ });
1636
+ await executeSchematic("Client main file", collection, "part-main", partOptions);
1637
+ }
1638
+ async generateServerParts(collection, values) {
1639
+ const partOptions = this.partOptions(values, "server");
1640
+ await executeSchematic("Server base", collection, "part-base", {
1641
+ ...partOptions,
1642
+ server: values.server
1643
+ });
1644
+ await executeSchematic("Server main file", collection, "part-main", partOptions);
1645
+ }
1646
+ async generateDocker(collection, values) {
1647
+ await executeSchematic("Docker", collection, "docker", {
1648
+ name: values.name,
1649
+ directory: values.directory,
1650
+ packageManager: values.packageManager
1711
1651
  });
1712
- await executeSchematic("Server main file", collection, "part-main", {
1652
+ }
1653
+ partOptions(values, part) {
1654
+ return {
1713
1655
  name: values.name,
1714
- part: "server",
1656
+ part,
1715
1657
  directory: values.directory,
1716
1658
  language: values.language,
1717
1659
  initFunctions: values.initFunctions
1718
- });
1660
+ };
1719
1661
  }
1720
- }, "generateApplicationFiles");
1721
- var runInstall = /* @__PURE__ */ __name(async (directory, pkgManagerName) => {
1722
- try {
1723
- const packageManager = PackageManagerFactory.create(pkgManagerName);
1724
- await packageManager.install(directory);
1725
- } catch (error4) {
1726
- if (error4 && error4.message) {
1727
- console4.error(ansis4.red(error4.message));
1728
- }
1729
- process4.exit(1);
1662
+ async installDependencies(packageManagerName, directory) {
1663
+ const packageManager = PackageManagerFactory.create(packageManagerName);
1664
+ return await packageManager.install(directory);
1730
1665
  }
1731
- }, "runInstall");
1666
+ };
1732
1667
 
1733
1668
  // src/action/actions/start.action.ts
1734
- import * as ansis5 from "ansis";
1735
- import * as console5 from "console";
1736
- import * as process5 from "process";
1737
- import { join as join6 } from "path";
1669
+ import { join as join7 } from "path";
1738
1670
  var StartAction = class extends AbstractAction {
1739
1671
  static {
1740
1672
  __name(this, "StartAction");
1741
1673
  }
1674
+ startMessage = Messages.START_START;
1675
+ successMessage = Messages.START_SUCCESS;
1676
+ failureMessage = Messages.START_FAILED;
1742
1677
  async handle(_args, options) {
1743
- console5.info(Messages.RUN_START);
1744
- console5.info();
1745
- try {
1746
- const directory = getDirectoryInput(options);
1747
- const config2 = await getConfig(options, directory);
1748
- const clientDir = config2.client.runtime.dir;
1749
- const serverDir = config2.server.runtime.dir;
1750
- const clientPort = getStringInputWithDefault(options, "clientPort", config2.client.port);
1751
- const cert = getStringInput(options, "cert");
1752
- const key = getStringInput(options, "key");
1753
- const watch3 = getWatchInput(options);
1754
- await Promise.all([
1755
- config2.server.enable ? this.startServer(directory, serverDir, watch3) : void 0,
1756
- this.startClient(
1757
- clientPort,
1758
- directory,
1759
- clientDir,
1760
- {
1761
- watch: watch3,
1762
- serverGameDir: config2.server.enable ? serverDir : void 0
1763
- },
1764
- cert,
1765
- key
1766
- )
1767
- ]);
1768
- process5.exit(0);
1769
- } catch (e) {
1770
- console5.error(e);
1771
- process5.exit(1);
1772
- }
1678
+ const directory = getDirectoryInput(options);
1679
+ const config2 = await getConfig(options, directory);
1680
+ const watch3 = getWatchInput(options);
1681
+ const ports = this.resolvePorts(options, config2);
1682
+ const ssl = this.resolveSSL(options);
1683
+ const tasks = this.buildStartTasks(config2, directory, watch3, ports, ssl);
1684
+ await Promise.all(tasks);
1685
+ return { keepAlive: true };
1686
+ }
1687
+ resolvePorts(options, config2) {
1688
+ return {
1689
+ clientPort: getStringInputWithDefault(options, "clientPort", config2.client.port),
1690
+ gameExposurePort: getStringInput(options, "gameExposurePort"),
1691
+ serverPort: getStringInput(options, "serverPort")
1692
+ };
1693
+ }
1694
+ resolveSSL(options) {
1695
+ const cert = getStringInput(options, "cert");
1696
+ const key = getStringInput(options, "key");
1697
+ if (!cert && !key) return void 0;
1698
+ if (!cert) throw new Error("No cert entered for SSL. Please enter a key with --cert.");
1699
+ if (!key) throw new Error("No key entered for SSL. Please enter a key with --key.");
1700
+ return {
1701
+ cert,
1702
+ key
1703
+ };
1773
1704
  }
1774
- async startClient(port, directory, gameDir, options, cert, key) {
1775
- const path = getModulePath("@nanoforge-dev/loader-client/package.json", true);
1776
- const params = {
1777
- PORT: port,
1778
- GAME_DIR: getCwd(join6(directory, gameDir)),
1779
- CERT: cert ? join6(getCwd(directory), cert) : void 0,
1780
- KEY: key ? join6(getCwd(directory), key) : void 0
1705
+ buildStartTasks(config2, directory, watch3, ports, ssl) {
1706
+ const tasks = [];
1707
+ if (config2.server.enable) {
1708
+ tasks.push(this.startServer(directory, config2.server.runtime.dir, watch3, ports.serverPort));
1709
+ }
1710
+ tasks.push(this.startClient(directory, config2, watch3, ports, ssl));
1711
+ return tasks;
1712
+ }
1713
+ async startClient(directory, config2, watch3, ports, ssl) {
1714
+ const loaderPath = getModulePath("@nanoforge-dev/loader-client/package.json", true);
1715
+ const gameDir = config2.client.runtime.dir;
1716
+ const env2 = this.buildClientEnv(directory, gameDir, watch3, config2, ports, ssl);
1717
+ await this.runLoader("Client", loaderPath, env2);
1718
+ }
1719
+ buildClientEnv(directory, gameDir, watch3, config2, ports, ssl) {
1720
+ const env2 = {
1721
+ PORT: ports.clientPort,
1722
+ GAME_DIR: getCwd(join7(directory, gameDir))
1781
1723
  };
1782
- if (options?.watch) {
1783
- params["WATCH"] = "true";
1784
- if (options?.serverGameDir) {
1785
- params["WATCH_SERVER_GAME_DIR"] = getCwd(join6(directory, options.serverGameDir));
1724
+ if (ports.gameExposurePort) {
1725
+ env2["GAME_EXPOSURE_PORT"] = ports.gameExposurePort;
1726
+ }
1727
+ if (watch3) {
1728
+ env2["WATCH"] = "true";
1729
+ if (config2.server.enable) {
1730
+ env2["WATCH_SERVER_GAME_DIR"] = getCwd(join7(directory, config2.server.runtime.dir));
1786
1731
  }
1787
1732
  }
1788
- return runPart("Client", path, params);
1733
+ if (ssl) {
1734
+ env2["CERT"] = ssl.cert;
1735
+ env2["KEY"] = ssl.key;
1736
+ }
1737
+ return env2;
1789
1738
  }
1790
- startServer(directory, gameDir, watch3) {
1791
- const path = getModulePath("@nanoforge-dev/loader-server/package.json", true);
1792
- const params = {
1793
- GAME_DIR: getCwd(join6(directory, gameDir))
1739
+ async startServer(directory, gameDir, watch3, port) {
1740
+ const loaderPath = getModulePath("@nanoforge-dev/loader-server/package.json", true);
1741
+ const env2 = {
1742
+ GAME_DIR: getCwd(join7(directory, gameDir))
1794
1743
  };
1795
- if (watch3) params["WATCH"] = "true";
1796
- return runPart("Server", path, params);
1744
+ if (port) env2["PORT"] = port;
1745
+ if (watch3) env2["WATCH"] = "true";
1746
+ await this.runLoader("Server", loaderPath, env2);
1747
+ }
1748
+ async runLoader(name, directory, env2) {
1749
+ await runSafe(async () => {
1750
+ const packageManager = await PackageManagerFactory.find(directory);
1751
+ await packageManager.run(name, directory, "start", env2, [], true);
1752
+ });
1797
1753
  }
1798
1754
  };
1799
- var runPart = /* @__PURE__ */ __name(async (part, directory, env2, flags) => {
1800
- try {
1801
- const packageManager = await PackageManagerFactory.find(directory);
1802
- await packageManager.run(part, directory, "start", env2, flags, true);
1803
- } catch (error4) {
1804
- if (error4 && error4.message) {
1805
- console5.error(ansis5.red(error4.message));
1806
- }
1807
- }
1808
- }, "runPart");
1809
1755
 
1810
1756
  // src/command/abstract.command.ts
1811
1757
  var AbstractCommand = class {
@@ -1815,6 +1761,13 @@ var AbstractCommand = class {
1815
1761
  static {
1816
1762
  __name(this, "AbstractCommand");
1817
1763
  }
1764
+ static mapToInput(mapping) {
1765
+ const input2 = /* @__PURE__ */ new Map();
1766
+ for (const [key, value] of Object.entries(mapping)) {
1767
+ input2.set(key, { value });
1768
+ }
1769
+ return input2;
1770
+ }
1818
1771
  };
1819
1772
 
1820
1773
  // src/command/commands/build.command.ts
@@ -1823,15 +1776,15 @@ var BuildCommand = class extends AbstractCommand {
1823
1776
  __name(this, "BuildCommand");
1824
1777
  }
1825
1778
  load(program2) {
1826
- program2.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) => {
1827
- const options = /* @__PURE__ */ new Map();
1828
- options.set("directory", { value: rawOptions.directory });
1829
- options.set("config", { value: rawOptions.config });
1830
- options.set("clientDirectory", { value: rawOptions.clientOutDir });
1831
- options.set("serverDirectory", { value: rawOptions.serverOutDir });
1832
- options.set("watch", { value: rawOptions.watch });
1833
- const args = /* @__PURE__ */ new Map();
1834
- await this.action.handle(args, options);
1779
+ program2.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) => {
1780
+ const options = AbstractCommand.mapToInput({
1781
+ directory: rawOptions.directory,
1782
+ config: rawOptions.config,
1783
+ clientDirectory: rawOptions.clientOutDir,
1784
+ serverDirectory: rawOptions.serverOutDir,
1785
+ watch: rawOptions.watch
1786
+ });
1787
+ await this.action.run(/* @__PURE__ */ new Map(), options);
1835
1788
  });
1836
1789
  }
1837
1790
  };
@@ -1842,13 +1795,13 @@ var DevCommand = class extends AbstractCommand {
1842
1795
  __name(this, "DevCommand");
1843
1796
  }
1844
1797
  load(program2) {
1845
- program2.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) => {
1846
- const options = /* @__PURE__ */ new Map();
1847
- options.set("directory", { value: rawOptions.directory });
1848
- options.set("config", { value: rawOptions.config });
1849
- options.set("generate", { value: rawOptions.generate });
1850
- const args = /* @__PURE__ */ new Map();
1851
- await this.action.handle(args, options);
1798
+ program2.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) => {
1799
+ const options = AbstractCommand.mapToInput({
1800
+ directory: rawOptions.directory,
1801
+ config: rawOptions.config,
1802
+ generate: rawOptions.generate
1803
+ });
1804
+ await this.action.run(/* @__PURE__ */ new Map(), options);
1852
1805
  });
1853
1806
  }
1854
1807
  };
@@ -1859,13 +1812,13 @@ var GenerateCommand = class extends AbstractCommand {
1859
1812
  __name(this, "GenerateCommand");
1860
1813
  }
1861
1814
  load(program2) {
1862
- program2.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) => {
1863
- const options = /* @__PURE__ */ new Map();
1864
- options.set("directory", { value: rawOptions.directory });
1865
- options.set("config", { value: rawOptions.config });
1866
- options.set("watch", { value: rawOptions.watch });
1867
- const args = /* @__PURE__ */ new Map();
1868
- await this.action.handle(args, options);
1815
+ program2.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) => {
1816
+ const options = AbstractCommand.mapToInput({
1817
+ directory: rawOptions.directory,
1818
+ config: rawOptions.config,
1819
+ watch: rawOptions.watch
1820
+ });
1821
+ await this.action.run(/* @__PURE__ */ new Map(), options);
1869
1822
  });
1870
1823
  }
1871
1824
  };
@@ -1877,11 +1830,13 @@ var InstallCommand = class extends AbstractCommand {
1877
1830
  }
1878
1831
  load(program2) {
1879
1832
  program2.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) => {
1880
- const options = /* @__PURE__ */ new Map();
1881
- options.set("directory", { value: rawOptions.directory });
1882
- const args = /* @__PURE__ */ new Map();
1883
- args.set("names", { value: names.length ? names : void 0 });
1884
- await this.action.handle(args, options);
1833
+ const options = AbstractCommand.mapToInput({
1834
+ directory: rawOptions.directory
1835
+ });
1836
+ const args = AbstractCommand.mapToInput({
1837
+ names: names.length ? names : void 0
1838
+ });
1839
+ await this.action.run(args, options);
1885
1840
  });
1886
1841
  }
1887
1842
  };
@@ -1892,19 +1847,20 @@ var NewCommand = class extends AbstractCommand {
1892
1847
  __name(this, "NewCommand");
1893
1848
  }
1894
1849
  load(program2) {
1895
- program2.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) => {
1896
- const options = /* @__PURE__ */ new Map();
1897
- options.set("directory", { value: rawOptions.directory });
1898
- options.set("name", { value: rawOptions.name });
1899
- options.set("path", { value: rawOptions.path });
1900
- options.set("packageManager", { value: rawOptions.packageManager });
1901
- options.set("language", { value: rawOptions.language });
1902
- options.set("strict", { value: rawOptions.strict });
1903
- options.set("server", { value: rawOptions.server });
1904
- options.set("initFunctions", { value: rawOptions.initFunctions });
1905
- options.set("skipInstall", { value: rawOptions.skipInstall });
1906
- const args = /* @__PURE__ */ new Map();
1907
- await this.action.handle(args, options);
1850
+ program2.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) => {
1851
+ const options = AbstractCommand.mapToInput({
1852
+ directory: rawOptions.directory,
1853
+ name: rawOptions.name,
1854
+ path: rawOptions.path,
1855
+ packageManager: rawOptions.packageManager,
1856
+ language: rawOptions.language,
1857
+ strict: rawOptions.strict,
1858
+ server: rawOptions.server,
1859
+ initFunctions: rawOptions.initFunctions,
1860
+ skipInstall: rawOptions.skipInstall,
1861
+ docker: rawOptions.docker
1862
+ });
1863
+ await this.action.run(/* @__PURE__ */ new Map(), options);
1908
1864
  });
1909
1865
  }
1910
1866
  };
@@ -1915,23 +1871,21 @@ var StartCommand = class extends AbstractCommand {
1915
1871
  __name(this, "StartCommand");
1916
1872
  }
1917
1873
  load(program2) {
1918
- program2.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(
1874
+ program2.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(
1919
1875
  "-p, --client-port [clientPort]",
1920
1876
  "specify the port of the loader (the website to load the game)"
1921
1877
  ).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) => {
1922
- const options = /* @__PURE__ */ new Map();
1923
- options.set("directory", { value: rawOptions.directory });
1924
- options.set("config", { value: rawOptions.config });
1925
- options.set("clientPort", { value: rawOptions.clientPort });
1926
- options.set("gameExposurePort", {
1927
- value: rawOptions.gameExposurePort
1878
+ const options = AbstractCommand.mapToInput({
1879
+ directory: rawOptions.directory,
1880
+ config: rawOptions.config,
1881
+ clientPort: rawOptions.clientPort,
1882
+ gameExposurePort: rawOptions.gameExposurePort,
1883
+ serverPort: rawOptions.serverPort,
1884
+ watch: rawOptions.watch,
1885
+ cert: rawOptions.cert,
1886
+ key: rawOptions.key
1928
1887
  });
1929
- options.set("serverPort", { value: rawOptions.serverPort });
1930
- options.set("watch", { value: rawOptions.watch });
1931
- options.set("cert", { value: rawOptions.cert });
1932
- options.set("key", { value: rawOptions.key });
1933
- const args = /* @__PURE__ */ new Map();
1934
- await this.action.handle(args, options);
1888
+ await this.action.run(/* @__PURE__ */ new Map(), options);
1935
1889
  });
1936
1890
  }
1937
1891
  };
@@ -1953,8 +1907,8 @@ var CommandLoader = class {
1953
1907
  static handleInvalidCommand(program2) {
1954
1908
  program2.on("command:*", () => {
1955
1909
  console.error(`
1956
- ${Prefixes.ERROR} Invalid command: ${red8`%s`}`, program2.args.join(" "));
1957
- console.log(`See ${red8`--help`} for a list of available commands.
1910
+ ${Prefixes.ERROR} Invalid command: ${red6`%s`}`, program2.args.join(" "));
1911
+ console.log(`See ${red6`--help`} for a list of available commands.
1958
1912
  `);
1959
1913
  process.exit(1);
1960
1914
  });