@nanoforge-dev/cli 1.1.0 → 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,11 +107,13 @@ 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
- import { dirname, join as join2 } from "path";
116
+ import { dirname, join as join3 } from "path";
100
117
 
101
118
  // src/lib/input/base-inputs.ts
102
119
  var getStringInput = /* @__PURE__ */ __name((input2, field) => {
@@ -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,82 +265,31 @@ var getInstallNamesInputOrAsk = /* @__PURE__ */ __name((inputs) => {
236
265
  );
237
266
  }, "getInstallNamesInputOrAsk");
238
267
 
239
- // src/lib/package-manager/package-manager.factory.ts
240
- import fs 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
- import { resolve } from "path";
291
+ import fs from "fs";
292
+ import { join, resolve } from "path";
315
293
  var getCwd = /* @__PURE__ */ __name((directory) => {
316
294
  return resolve(directory);
317
295
  }, "getCwd");
@@ -320,430 +298,394 @@ var getModulePath = /* @__PURE__ */ __name((name, removeLast = false) => {
320
298
  if (removeLast) return path.split("/").slice(0, -1).join("/");
321
299
  return path;
322
300
  }, "getModulePath");
323
- var getNodeBinaryPath = /* @__PURE__ */ __name((name) => {
324
- return resolve("node_modules", ".bin", name);
325
- }, "getNodeBinaryPath");
326
-
327
- // src/lib/runner/runners/local-bun.runner.ts
328
- var LocalBunRunner = class extends AbstractRunner {
329
- static {
330
- __name(this, "LocalBunRunner");
331
- }
332
- constructor() {
333
- super(getNodeBinaryPath("bun"));
334
- }
335
- };
336
-
337
- // src/lib/runner/runners/npm.runner.ts
338
- var NpmRunner = class extends AbstractRunner {
339
- static {
340
- __name(this, "NpmRunner");
341
- }
342
- constructor() {
343
- super("npm");
344
- }
345
- };
346
-
347
- // src/lib/runner/runners/pnpm.runner.ts
348
- var PnpmRunner = class extends AbstractRunner {
349
- static {
350
- __name(this, "PnpmRunner");
351
- }
352
- constructor() {
353
- super("pnpm");
354
- }
355
- };
356
-
357
- // src/lib/runner/runners/schematic.runner.ts
358
- var SchematicRunner = class _SchematicRunner extends AbstractRunner {
359
- static {
360
- __name(this, "SchematicRunner");
361
- }
362
- static getModulePaths() {
363
- return module.paths;
364
- }
365
- static findClosestSchematicsBinary() {
301
+ var resolveCLINodeBinaryPath = /* @__PURE__ */ __name((name) => {
302
+ let base = join(getModulePath(".", true), "..");
303
+ while (base.length >= 1) {
304
+ const path = join(base, "node_modules", ".bin", name);
366
305
  try {
367
- return getModulePath("@angular-devkit/schematics-cli/bin/schematics.js");
368
- } catch (e) {
369
- console.error(e);
370
- throw new Error("'schematics' binary path could not be found!");
371
- }
372
- }
373
- constructor() {
374
- super(`node`, [`"${_SchematicRunner.findClosestSchematicsBinary()}"`]);
375
- }
376
- };
377
-
378
- // src/lib/runner/runners/yarn.runner.ts
379
- var YarnRunner = class extends AbstractRunner {
380
- static {
381
- __name(this, "YarnRunner");
382
- }
383
- constructor() {
384
- super("yarn");
385
- }
386
- };
387
-
388
- // src/lib/runner/runner.factory.ts
389
- var RunnerFactory = class {
390
- static {
391
- __name(this, "RunnerFactory");
392
- }
393
- static create(runner) {
394
- switch (runner) {
395
- case 0 /* BUN */:
396
- return new BunRunner();
397
- case 1 /* LOCAL_BUN */:
398
- return new LocalBunRunner();
399
- case 2 /* NPM */:
400
- return new NpmRunner();
401
- case 3 /* PNPM */:
402
- return new PnpmRunner();
403
- case 4 /* SCHEMATIC */:
404
- return new SchematicRunner();
405
- case 5 /* YARN */:
406
- return new YarnRunner();
407
- default:
408
- console.info(yellow`[WARN] Unsupported runner: ${runner}`);
409
- throw Error(`Unsupported runner: ${runner}`);
306
+ fs.accessSync(path);
307
+ return path;
308
+ } catch {
309
+ base = join(base, "..");
410
310
  }
411
311
  }
412
- };
312
+ throw new Error("Could not find module path");
313
+ }, "resolveCLINodeBinaryPath");
413
314
 
414
- // src/lib/package-manager/abstract.package-manager.ts
415
- import { bold, green as green2, red as red2, yellow as yellow2 } from "ansis";
416
- import ora from "ora";
417
- var SPINNER = /* @__PURE__ */ __name((message) => ora({
418
- text: message
419
- }), "SPINNER");
420
- var FAIL_SPINNER = /* @__PURE__ */ __name((spinner) => () => spinner.fail(), "FAIL_SPINNER");
421
- var AbstractPackageManager = class {
422
- 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;
423
320
  this.runner = runner;
424
321
  }
425
322
  static {
426
- __name(this, "AbstractPackageManager");
323
+ __name(this, "PackageManager");
427
324
  }
428
325
  async install(directory) {
429
- const spinner = SPINNER(Messages.PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS);
430
- spinner.start();
431
- try {
432
- const commandArgs = [this.cli.install, this.cli.silentFlag];
433
- const collect = true;
434
- await this.runner.run(
435
- commandArgs,
436
- collect,
437
- getCwd(directory),
438
- void 0,
439
- void 0,
440
- () => spinner.fail()
441
- );
442
- spinner.succeed();
443
- this.printInstallSuccess();
444
- } catch {
445
- const commandArgs = [this.cli.install];
446
- const commandToRun = this.runner.rawFullCommand(commandArgs);
447
- this.printInstallFailure(commandToRun);
448
- }
449
- }
450
- version() {
451
- const commandArguments = ["--version"];
452
- const collect = true;
453
- return this.runner.run(commandArguments, collect);
454
- }
455
- addProduction(directory, dependencies) {
456
- const command = [this.cli.add, this.cli.saveFlag];
457
- return this.add(command, directory, dependencies);
458
- }
459
- addDevelopment(directory, dependencies) {
460
- const command = [this.cli.add, this.cli.saveDevFlag];
461
- return this.add(command, directory, dependencies);
462
- }
463
- async build(name, directory, entry, output, flags, watch3) {
464
- if (!this.cli.build) throw new Error(`Package manager ${this.name} does not support building`);
465
- const spinner = SPINNER(
466
- (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]))
467
334
  );
468
- spinner.start();
469
- try {
470
- const commandArgs = [
471
- this.cli.build,
472
- this.cli.silentFlag,
473
- entry,
474
- "--outdir",
475
- output,
476
- ...flags ?? []
477
- ];
478
- const collect = true;
479
- await this.runner.run(
480
- commandArgs,
481
- collect,
482
- getCwd(directory),
483
- void 0,
484
- void 0,
485
- FAIL_SPINNER(spinner)
486
- );
487
- spinner.succeed();
488
- return true;
489
- } catch {
490
- const commandArgs = [this.cli.install];
491
- const commandToRun = this.runner.rawFullCommand(commandArgs);
492
- console.error(red2(Messages.BUILD_PART_FAILED(name, bold(commandToRun))));
493
- return false;
494
- }
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;
495
363
  }
496
- 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));
497
366
  try {
498
- console.info(Messages.RUN_PART_IN_PROGRESS(name));
499
- const commandArgs = [...flags, this.cli.run];
500
- if (silent) commandArgs.push(this.cli.silentFlag);
501
- commandArgs.push(file);
502
- await this.runner.run(commandArgs, true, getCwd(directory), env2, {
503
- onStdout: this.onRunStdout(name),
504
- 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
+ }
505
374
  });
506
- console.info(Messages.RUN_PART_SUCCESS(name));
375
+ console.info(Messages.START_PART_SUCCESS(name));
507
376
  return true;
508
377
  } catch {
509
- console.error(red2(Messages.RUN_PART_FAILED(name)));
378
+ console.error(red3(Messages.START_PART_FAILED(name)));
510
379
  return false;
511
380
  }
512
381
  }
513
382
  async runDev(directory, command, env2 = {}, flags = [], collect = true) {
514
383
  try {
515
- const commandArgs = [this.cli.run, command, ...flags];
516
- await this.runner.run(commandArgs, collect, getCwd(directory), env2);
384
+ await this.exec([this.commands.run, command, ...flags], directory, { collect, env: env2 });
517
385
  return true;
518
386
  } catch {
519
387
  return false;
520
388
  }
521
389
  }
522
- async add(args, directory, dependencies) {
523
- if (!dependencies.length) {
524
- console.info();
525
- console.info(Messages.PACKAGE_MANAGER_INSTALLATION_NOTHING);
526
- console.info();
527
- return true;
528
- }
529
- const commandArguments = [...args, ...dependencies];
530
- const spinner = SPINNER(Messages.PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS);
390
+ async withSpinner(message, task, onError) {
391
+ const spinner = getSpinner(message);
531
392
  spinner.start();
532
393
  try {
533
- const collect = true;
534
- await this.runner.run(
535
- commandArguments,
536
- collect,
537
- getCwd(directory),
538
- void 0,
539
- void 0,
540
- FAIL_SPINNER(spinner)
541
- );
394
+ const value = await task(spinner);
542
395
  spinner.succeed();
543
- this.printInstallSuccess(dependencies);
544
- return true;
396
+ return { success: true, value };
545
397
  } catch {
546
398
  spinner.fail();
547
- const commandToRun = this.runner.rawFullCommand(commandArguments);
548
- this.printInstallFailure(commandToRun);
549
- return false;
399
+ if (onError) onError();
400
+ return { success: false };
550
401
  }
551
402
  }
552
- 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) {
553
443
  console.info();
554
- console.info(Messages.PACKAGE_MANAGER_INSTALLATION_SUCCEED(dependencies));
444
+ console.info(message);
555
445
  console.info();
556
446
  }
557
- printInstallFailure(command) {
558
- 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();
559
458
  }
560
- onRunStdout = /* @__PURE__ */ __name((name) => (chunk) => {
561
- chunk.toString().replace(/\r\n|\n/g, "\n").replace(/^\n+|\n+$/g, "").split("\n").forEach((line) => {
562
- const date = yellow2(`[${(/* @__PURE__ */ new Date()).toISOString()}]`);
563
- const prompt = green2(`(${name}) INFO -`);
564
- console.info(`${date} ${prompt} ${line}`);
565
- });
566
- }, "onRunStdout");
567
- onRunStderr = /* @__PURE__ */ __name((name) => (chunk) => {
568
- chunk.toString().replace(/\r\n|\n/g, "\n").replace(/^\n+|\n+$/g, "").split("\n").forEach((line) => {
569
- const date = yellow2(`[${(/* @__PURE__ */ new Date()).toISOString()}]`);
570
- const prompt = red2(`(${name}) ERROR -`);
571
- console.error(`${date} ${prompt} ${line}`);
572
- });
573
- }, "onRunStderr");
574
459
  };
575
460
 
576
- // src/lib/package-manager/package-managers/bun.package-manager.ts
577
- 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
+ }
578
474
  static {
579
- __name(this, "BunPackageManager");
475
+ __name(this, "Runner");
580
476
  }
581
- constructor() {
582
- 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
+ });
583
493
  }
584
- get name() {
585
- return "bun" /* BUN */.toUpperCase();
494
+ fullCommand(args) {
495
+ return [this.binary, ...this.baseArgs, ...args].join(" ");
586
496
  }
587
- get cli() {
497
+ buildSpawnOptions(collect, cwd2, env2) {
588
498
  return {
589
- install: "install",
590
- add: "add",
591
- update: "update",
592
- remove: "remove",
593
- run: "run",
594
- saveFlag: "--save",
595
- saveDevFlag: "--dev",
596
- silentFlag: "--silent"
499
+ cwd: cwd2,
500
+ stdio: collect ? "pipe" : "inherit",
501
+ shell: true,
502
+ env: { ...process2.env, ...env2 }
597
503
  };
598
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
+ }
599
534
  };
600
535
 
601
- // src/lib/package-manager/package-managers/local-bun.package-manager.ts
602
- var LocalBunPackageManager = class extends AbstractPackageManager {
536
+ // src/lib/runner/runner.factory.ts
537
+ var RunnerFactory = class {
603
538
  static {
604
- __name(this, "LocalBunPackageManager");
539
+ __name(this, "RunnerFactory");
605
540
  }
606
- constructor() {
607
- super(RunnerFactory.create(1 /* LOCAL_BUN */));
541
+ static create(binary, args) {
542
+ return new Runner(binary, args);
608
543
  }
609
- get name() {
610
- return "local_bun" /* LOCAL_BUN */.toUpperCase();
544
+ static createLocal(binary, args) {
545
+ return new Runner(resolveCLINodeBinaryPath(binary), args);
611
546
  }
612
- get cli() {
613
- return {
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: {
565
+ install: "install",
566
+ add: "add",
567
+ update: "update",
568
+ remove: "remove",
569
+ exec: "exec",
570
+ run: "run",
571
+ saveFlag: "--save",
572
+ saveDevFlag: "--dev",
573
+ silentFlag: "--silent"
574
+ }
575
+ },
576
+ ["local_bun" /* LOCAL_BUN */]: {
577
+ binary: "bun",
578
+ commands: {
614
579
  install: "install",
615
580
  add: "add",
616
581
  update: "update",
617
582
  remove: "remove",
583
+ exec: "exec",
618
584
  run: "run",
619
585
  build: "build",
620
586
  runFile: "run",
621
587
  saveFlag: "--save",
622
588
  saveDevFlag: "--dev",
623
589
  silentFlag: "--silent"
624
- };
625
- }
626
- };
627
-
628
- // src/lib/package-manager/package-managers/npm.package-manager.ts
629
- var NpmPackageManager = class extends AbstractPackageManager {
630
- static {
631
- __name(this, "NpmPackageManager");
632
- }
633
- constructor() {
634
- super(RunnerFactory.create(2 /* NPM */));
635
- }
636
- get name() {
637
- return "npm" /* NPM */.toUpperCase();
638
- }
639
- get cli() {
640
- return {
590
+ }
591
+ },
592
+ ["npm" /* NPM */]: {
593
+ binary: "npm",
594
+ commands: {
641
595
  install: "install",
642
596
  add: "install",
643
597
  update: "update",
644
598
  remove: "uninstall",
599
+ exec: "exec",
645
600
  run: "run",
646
601
  saveFlag: "--save",
647
602
  saveDevFlag: "--save-dev",
648
603
  silentFlag: "--silent"
649
- };
650
- }
651
- };
652
-
653
- // src/lib/package-manager/package-managers/pnpm.package-manager.ts
654
- var PnpmPackageManager = class extends AbstractPackageManager {
655
- static {
656
- __name(this, "PnpmPackageManager");
657
- }
658
- constructor() {
659
- super(RunnerFactory.create(3 /* PNPM */));
660
- }
661
- get name() {
662
- return "pnpm" /* PNPM */.toUpperCase();
663
- }
664
- get cli() {
665
- return {
604
+ }
605
+ },
606
+ ["pnpm" /* PNPM */]: {
607
+ binary: "pnpm",
608
+ commands: {
666
609
  install: "install",
667
610
  add: "add",
668
611
  update: "update",
669
612
  remove: "remove",
613
+ exec: "exec",
670
614
  run: "run",
671
615
  saveFlag: "-P",
672
616
  saveDevFlag: "-D",
673
617
  silentFlag: "--silent"
674
- };
675
- }
676
- };
677
-
678
- // src/lib/package-manager/package-managers/yarn.package-manager.ts
679
- var YarnPackageManager = class extends AbstractPackageManager {
680
- static {
681
- __name(this, "YarnPackageManager");
682
- }
683
- constructor() {
684
- super(RunnerFactory.create(5 /* YARN */));
685
- }
686
- get name() {
687
- return "yarn" /* YARN */.toUpperCase();
688
- }
689
- get cli() {
690
- return {
618
+ }
619
+ },
620
+ ["yarn" /* YARN */]: {
621
+ binary: "yarn",
622
+ commands: {
691
623
  install: "install",
692
624
  add: "add",
693
625
  update: "update",
694
626
  remove: "remove",
627
+ exec: "exec",
695
628
  run: "run",
696
629
  saveFlag: "",
697
630
  saveDevFlag: "-D",
698
631
  silentFlag: "--silent"
699
- };
632
+ }
700
633
  }
701
634
  };
702
635
 
703
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
+ };
704
643
  var PackageManagerFactory = class {
705
644
  static {
706
645
  __name(this, "PackageManagerFactory");
707
646
  }
708
647
  static create(name) {
709
- switch (name) {
710
- case "bun" /* BUN */:
711
- return new BunPackageManager();
712
- case "local_bun" /* LOCAL_BUN */:
713
- return new LocalBunPackageManager();
714
- case "npm" /* NPM */:
715
- return new NpmPackageManager();
716
- case "pnpm" /* PNPM */:
717
- return new PnpmPackageManager();
718
- case "yarn" /* YARN */:
719
- return new YarnPackageManager();
720
- default:
721
- 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.`);
722
651
  }
652
+ const runner = this.createRunner(name, config2.binary);
653
+ return new PackageManager(name, config2.commands, runner);
723
654
  }
724
655
  static async find(directory = ".") {
725
- 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) {
726
666
  try {
727
- const files = await fs.promises.readdir(resolve2(directory));
728
- if (files.includes("bun.lock")) {
729
- return this.create("bun" /* BUN */);
730
- }
731
- if (files.includes("package-lock.json")) {
732
- return this.create("npm" /* NPM */);
667
+ const files = await fs2.promises.readdir(resolve2(directory));
668
+ for (const [lockFile, pmName] of Object.entries(LOCK_FILE_MAP)) {
669
+ if (files.includes(lockFile)) return pmName;
733
670
  }
734
- if (files.includes("pnpm-lock.yaml")) {
735
- return this.create("pnpm" /* PNPM */);
736
- }
737
- if (files.includes("yarn.lock")) {
738
- return this.create("yarn" /* YARN */);
739
- }
740
- return this.create(DEFAULT_PACKAGE_MANAGER);
741
671
  } catch {
742
- return this.create(DEFAULT_PACKAGE_MANAGER);
743
672
  }
673
+ return "npm" /* NPM */;
744
674
  }
745
675
  };
746
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
+
747
689
  // src/lib/config/config.type.ts
748
690
  import { Expose, Type } from "class-transformer";
749
691
  import { IsBoolean, IsEnum, IsNotEmpty, IsPort, IsString, ValidateNested } from "class-validator";
@@ -867,7 +809,11 @@ __decorateClass([
867
809
  import { plainToInstance } from "class-transformer";
868
810
  import { validate } from "class-validator";
869
811
  import { existsSync, readFileSync } from "fs";
870
- import { join } from "path";
812
+ import { join as join2 } from "path";
813
+
814
+ // src/lib/constants.ts
815
+ var CONFIG_FILE_NAME = "nanoforge.config.json";
816
+ var NANOFORGE_DIR = ".nanoforge";
871
817
 
872
818
  // src/lib/utils/object.ts
873
819
  var isObject = /* @__PURE__ */ __name((item) => {
@@ -922,20 +868,19 @@ var CONFIG_DEFAULTS = {
922
868
  var config;
923
869
  var getConfigPath = /* @__PURE__ */ __name((directory, name) => {
924
870
  if (name) {
925
- return join(directory, name);
871
+ return join2(directory, name);
926
872
  } else {
927
- for (const n of ["nanoforge.config.json"]) {
928
- const path = join(directory, n);
873
+ for (const n of [CONFIG_FILE_NAME]) {
874
+ const path = join2(directory, n);
929
875
  if (existsSync(path)) return path;
930
876
  }
931
- throw new Error(`Unsupported config: ${name}`);
877
+ throw new Error(`No config file found in directory: ${directory}`);
932
878
  }
933
879
  }, "getConfigPath");
934
880
  var loadConfig = /* @__PURE__ */ __name(async (directory, name) => {
935
881
  if (config) return config;
936
882
  let rawData;
937
883
  const path = getConfigPath(directory, name);
938
- if (!path) throw new Error("No config file found");
939
884
  try {
940
885
  rawData = deepMerge(CONFIG_DEFAULTS, JSON.parse(readFileSync(path, "utf-8")));
941
886
  } catch {
@@ -963,6 +908,32 @@ var AbstractAction = class {
963
908
  static {
964
909
  __name(this, "AbstractAction");
965
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
+ }
966
937
  };
967
938
 
968
939
  // src/action/actions/build.action.ts
@@ -970,119 +941,122 @@ var BuildAction = class extends AbstractAction {
970
941
  static {
971
942
  __name(this, "BuildAction");
972
943
  }
944
+ startMessage = Messages.BUILD_START;
945
+ successMessage = Messages.BUILD_SUCCESS;
946
+ failureMessage = Messages.BUILD_FAILED;
973
947
  async handle(_args, options) {
974
- console2.info(Messages.BUILD_START);
975
- console2.info();
976
- try {
977
- const directory = getDirectoryInput(options);
978
- const config2 = await getConfig(options, directory);
979
- const watch3 = getWatchInput(options);
980
- 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",
981
962
  config2.client.build,
982
- options.get("clientDirectory")?.value,
983
- "client"
984
- );
985
- let res = await buildPart("Client", client, directory, { watch: watch3 });
986
- if (config2.server.enable) {
987
- const server = getPart(
963
+ "browser",
964
+ getStringInput(options, "clientDirectory")
965
+ )
966
+ ];
967
+ if (config2.server.enable) {
968
+ targets.push(
969
+ this.createTarget(
970
+ "Server",
988
971
  config2.server.build,
989
- options.get("serverDirectory")?.value,
990
- "server"
991
- );
992
- res = await buildPart("Server", server, directory, { watch: watch3 }) ? res : false;
993
- }
994
- console2.info();
995
- if (watch3) {
996
- console2.info(Messages.BUILD_WATCH_START);
997
- console2.info();
998
- return;
999
- }
1000
- if (!res) console2.info(Messages.BUILD_FAILED);
1001
- else console2.info(Messages.BUILD_SUCCESS);
1002
- process.exit(0);
1003
- } catch (e) {
1004
- console2.error(e);
1005
- process.exit(1);
972
+ "node",
973
+ getStringInput(options, "serverDirectory")
974
+ )
975
+ );
1006
976
  }
977
+ return targets;
1007
978
  }
1008
- };
1009
- var getPart = /* @__PURE__ */ __name((config2, directoryOption, target) => {
1010
- return {
1011
- entry: config2.entryFile,
1012
- output: directoryOption || config2.outDir,
1013
- target
1014
- };
1015
- }, "getPart");
1016
- var buildPart = /* @__PURE__ */ __name(async (name, part, directory, options) => {
1017
- const packageManagerName = "local_bun" /* LOCAL_BUN */;
1018
- const packageManager = PackageManagerFactory.create(packageManagerName);
1019
- const build = /* @__PURE__ */ __name(async (watch3 = false) => {
1020
- try {
1021
- return await packageManager.build(
1022
- 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,
1023
1000
  directory,
1024
- part.entry,
1025
- part.output,
1026
- [
1027
- "--asset-naming",
1028
- "[name].[ext]",
1029
- "--target",
1030
- part.target === "client" ? "browser" : "node"
1031
- ],
1032
- watch3
1033
- );
1034
- } catch (error4) {
1035
- if (error4 && error4.message) {
1036
- console2.error(ansis.red(error4.message));
1037
- }
1038
- 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));
1039
1010
  }
1040
- }, "build");
1041
- if (options?.watch)
1042
- watch(dirname(join2(getCwd(directory), part.entry))).on("change", () => build(true));
1043
- return await build();
1044
- }, "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
+ };
1045
1025
 
1046
1026
  // src/action/actions/dev.action.ts
1047
- import * as ansis2 from "ansis";
1048
1027
  var DevAction = class extends AbstractAction {
1049
1028
  static {
1050
1029
  __name(this, "DevAction");
1051
1030
  }
1031
+ startMessage = Messages.DEV_START;
1032
+ successMessage = Messages.DEV_SUCCESS;
1033
+ failureMessage = Messages.DEV_FAILED;
1052
1034
  async handle(_args, options) {
1053
- console.info(Messages.DEV_START);
1054
- console.info();
1055
- try {
1056
- const directory = getDirectoryInput(options);
1057
- const generate = getDevGenerateInput(options);
1058
- await Promise.all([
1059
- generate ? runAction("generate", [], directory, false) : void 0,
1060
- runAction("build", [], directory, false),
1061
- runAction("start", [], directory, true)
1062
- ]);
1063
- console.info(Messages.DEV_SUCCESS);
1064
- process.exit(0);
1065
- } catch (e) {
1066
- console.error(Messages.DEV_FAILED);
1067
- console.error(e);
1068
- 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 }));
1069
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
+ });
1070
1055
  }
1071
1056
  };
1072
- var runAction = /* @__PURE__ */ __name(async (command, params, directory, stdout = false) => {
1073
- try {
1074
- const packageManager = await PackageManagerFactory.find(directory);
1075
- await packageManager.runDev(directory, "nf", {}, [command, ...params, "--watch"], !stdout);
1076
- } catch (error4) {
1077
- if (error4 && error4.message) {
1078
- console.error(ansis2.red(error4.message));
1079
- }
1080
- }
1081
- }, "runAction");
1082
1057
 
1083
1058
  // src/action/actions/generate.action.ts
1084
- import * as console3 from "console";
1085
- import { join as join3 } from "path";
1059
+ import { join as join4 } from "path";
1086
1060
 
1087
1061
  // src/lib/schematics/abstract.collection.ts
1088
1062
  var AbstractCollection = class {
@@ -1094,25 +1068,19 @@ var AbstractCollection = class {
1094
1068
  static {
1095
1069
  __name(this, "AbstractCollection");
1096
1070
  }
1097
- async execute(name, options, flags, failSpinner) {
1071
+ async execute(name, options, flags, onFail) {
1098
1072
  const command = this.buildCommandLine(name, options, flags);
1099
- await this.runner.run(
1100
- command,
1101
- true,
1102
- this.cwd ? getCwd(this.cwd) : void 0,
1103
- void 0,
1104
- void 0,
1105
- failSpinner
1106
- );
1073
+ await this.runner.run(command, {
1074
+ collect: true,
1075
+ cwd: this.cwd ? getCwd(this.cwd) : void 0,
1076
+ onFail
1077
+ });
1107
1078
  }
1108
1079
  buildCommandLine(name, options, flags = []) {
1109
- return [`${this.collection}:${name}`, ...flags, ...this.buildOptions(options)];
1080
+ return [`${this.collection}:${name}`, ...flags, ...this.serializeOptions(options)];
1110
1081
  }
1111
- buildOptions(options) {
1112
- return options.reduce(
1113
- (old, option) => [...old, ...option.toCommandString()],
1114
- []
1115
- );
1082
+ serializeOptions(options) {
1083
+ return options.flatMap((option) => option.toCommandString());
1116
1084
  }
1117
1085
  };
1118
1086
 
@@ -1141,6 +1109,11 @@ var NanoforgeCollection = class _NanoforgeCollection extends AbstractCollection
1141
1109
  name: "part-main",
1142
1110
  alias: "main",
1143
1111
  description: "Generate a NanoForge Part Main file"
1112
+ },
1113
+ {
1114
+ name: "docker",
1115
+ alias: "docker",
1116
+ description: "Generate a Dockerfile for the application"
1144
1117
  }
1145
1118
  ];
1146
1119
  constructor(runner, cwd2) {
@@ -1172,12 +1145,11 @@ var CollectionFactory = class {
1172
1145
  __name(this, "CollectionFactory");
1173
1146
  }
1174
1147
  static create(collection, directory) {
1175
- const schematicRunner = RunnerFactory.create(4 /* SCHEMATIC */);
1148
+ const schematicRunner = RunnerFactory.createSchematic();
1176
1149
  if (collection === "@nanoforge-dev/schematics" /* NANOFORGE */) {
1177
1150
  return new NanoforgeCollection(schematicRunner, directory);
1178
- } else {
1179
- return new NanoforgeCollection(schematicRunner, directory);
1180
1151
  }
1152
+ throw new Error(`Unknown collection: ${collection}`);
1181
1153
  }
1182
1154
  };
1183
1155
 
@@ -1210,7 +1182,7 @@ var SchematicOption = class {
1210
1182
  }
1211
1183
  } else if (typeof this.value === "boolean") {
1212
1184
  const str = normalizedName;
1213
- return this.value ? [`--${str}`] : [`--no-${str}`];
1185
+ return this.value ? [`--${str}=true`] : [`--${str}=false`];
1214
1186
  } else if (Array.isArray(this.value)) {
1215
1187
  return this.value.reduce(
1216
1188
  (old, option) => [
@@ -1235,19 +1207,10 @@ var SchematicOption = class {
1235
1207
 
1236
1208
  // src/action/common/schematics.ts
1237
1209
  import { watch as watch2 } from "chokidar";
1238
-
1239
- // src/action/common/spinner.ts
1240
- import ora2 from "ora";
1241
- var getSpinner = /* @__PURE__ */ __name((message) => ora2({
1242
- text: message
1243
- }), "getSpinner");
1244
-
1245
- // src/action/common/schematics.ts
1246
1210
  var executeSchematic = /* @__PURE__ */ __name(async (name, collection, schematicName, options, fileToWatch) => {
1247
- const execute = /* @__PURE__ */ __name(async (watch3 = false) => {
1248
- const spinner = getSpinner(
1249
- (watch3 ? Messages.SCHEMATIC_WATCH_IN_PROGRESS : Messages.SCHEMATIC_IN_PROGRESS)(name)
1250
- );
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);
1251
1214
  spinner.start();
1252
1215
  await collection.execute(
1253
1216
  schematicName,
@@ -1257,16 +1220,17 @@ var executeSchematic = /* @__PURE__ */ __name(async (name, collection, schematic
1257
1220
  );
1258
1221
  spinner.succeed(Messages.SCHEMATIC_SUCCESS(name));
1259
1222
  }, "execute");
1260
- if (fileToWatch) watch2(fileToWatch).on("change", () => execute(true));
1261
- return await execute();
1223
+ if (fileToWatch) {
1224
+ watch2(fileToWatch).on("change", () => execute(true));
1225
+ }
1226
+ await execute();
1262
1227
  }, "executeSchematic");
1263
1228
  var mapSchematicOptions = /* @__PURE__ */ __name((inputs) => {
1264
- return Object.entries(inputs).reduce((old, [key, value]) => {
1265
- if (value === void 0) return old;
1266
- return [
1267
- ...old,
1268
- new SchematicOption(key, typeof value === "object" ? mapSchematicOptions(value) : value)
1269
- ];
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;
1270
1234
  }, []);
1271
1235
  }, "mapSchematicOptions");
1272
1236
 
@@ -1275,108 +1239,98 @@ var GenerateAction = class extends AbstractAction {
1275
1239
  static {
1276
1240
  __name(this, "GenerateAction");
1277
1241
  }
1242
+ startMessage = Messages.GENERATE_START;
1243
+ successMessage = Messages.GENERATE_SUCCESS;
1244
+ failureMessage = Messages.GENERATE_FAILED;
1278
1245
  async handle(_args, options) {
1279
- console3.info(Messages.GENERATE_START);
1280
- console3.info();
1281
- try {
1282
- const directory = getDirectoryInput(options);
1283
- const config2 = await getConfig(options, directory);
1284
- const watch3 = getWatchInput(options);
1285
- const values = await getSchemaValues(config2);
1286
- await generateFiles(values, directory, watch3);
1287
- console3.info();
1288
- if (watch3) {
1289
- console3.info(Messages.GENERATE_WATCH_START);
1290
- console3.info();
1291
- return;
1292
- }
1293
- console3.info(Messages.GENERATE_SUCCESS);
1294
- process.exit(0);
1295
- } catch (e) {
1296
- console3.error(Messages.GENERATE_FAILED);
1297
- console3.error(e);
1298
- 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();
1299
1253
  }
1254
+ return {};
1300
1255
  }
1301
- };
1302
- var getSchemaValues = /* @__PURE__ */ __name(async (config2) => {
1303
- return {
1304
- name: config2.name,
1305
- directory: ".",
1306
- language: config2.language,
1307
- server: config2.server.enable,
1308
- initFunctions: config2.initFunctions
1309
- };
1310
- }, "getSchemaValues");
1311
- var generateFiles = /* @__PURE__ */ __name(async (values, directory, watch3) => {
1312
- const collection = CollectionFactory.create("@nanoforge-dev/schematics" /* NANOFORGE */, directory);
1313
- console3.info(Messages.SCHEMATICS_START);
1314
- console3.info();
1315
- await executeSchematic(
1316
- "Client main file",
1317
- collection,
1318
- "part-main",
1319
- {
1320
- name: values.name,
1321
- part: "client",
1322
- directory: values.directory,
1323
- language: values.language,
1324
- initFunctions: values.initFunctions
1325
- },
1326
- watch3 ? join3(getCwd(directory), values.directory, ".nanoforge", "client.save.json") : void 0
1327
- );
1328
- 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);
1329
1268
  await executeSchematic(
1330
- "Server main file",
1269
+ "Client main file",
1331
1270
  collection,
1332
1271
  "part-main",
1333
- {
1334
- name: values.name,
1335
- part: "server",
1336
- directory: values.directory,
1337
- language: values.language,
1338
- initFunctions: values.initFunctions
1339
- },
1340
- join3(getCwd(directory), values.directory, ".nanoforge", "server.save.json")
1272
+ { ...baseOptions, part: "client" },
1273
+ watch3 ? this.watchPath(directory, values.directory, "client") : void 0
1341
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
+ }
1342
1284
  }
1343
- }, "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
+ };
1344
1303
 
1345
1304
  // src/action/actions/install.action.ts
1346
- import * as ansis3 from "ansis";
1347
- import * as process3 from "process";
1348
1305
  var InstallAction = class extends AbstractAction {
1349
1306
  static {
1350
1307
  __name(this, "InstallAction");
1351
1308
  }
1309
+ startMessage = Messages.INSTALL_START;
1310
+ successMessage = Messages.INSTALL_SUCCESS;
1311
+ failureMessage = Messages.INSTALL_FAILED;
1352
1312
  async handle(args, options) {
1353
- console.info(Messages.INSTALL_START);
1354
- console.info();
1355
- try {
1356
- const names = await getInstallNamesInputOrAsk(args);
1357
- const directory = getDirectoryInput(options);
1358
- await installPackages(names, directory);
1359
- process3.exit(0);
1360
- } catch (e) {
1361
- console.error(e);
1362
- process3.exit(1);
1363
- }
1364
- }
1365
- };
1366
- var installPackages = /* @__PURE__ */ __name(async (names, directory) => {
1367
- try {
1313
+ const names = await getInstallNamesInputOrAsk(args);
1314
+ const directory = getDirectoryInput(options);
1368
1315
  const packageManager = await PackageManagerFactory.find(directory);
1369
- await packageManager.addProduction(directory, names);
1370
- } catch (error4) {
1371
- if (error4 && error4.message) {
1372
- console.error(ansis3.red(error4.message));
1373
- }
1316
+ const success2 = await packageManager.addProduction(directory, names);
1317
+ return { success: success2 };
1374
1318
  }
1375
- }, "installPackages");
1319
+ };
1376
1320
 
1377
1321
  // src/action/actions/new.action.ts
1378
- import * as ansis4 from "ansis";
1379
- import console4 from "console";
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");
1380
1334
 
1381
1335
  // src/lib/input/inputs/new/init-functions.input.ts
1382
1336
  var getNewInitFunctionsWithDefault = /* @__PURE__ */ __name((inputs) => {
@@ -1470,159 +1424,186 @@ var NewAction = class extends AbstractAction {
1470
1424
  static {
1471
1425
  __name(this, "NewAction");
1472
1426
  }
1427
+ startMessage = Messages.NEW_START;
1428
+ successMessage = Messages.NEW_SUCCESS;
1429
+ failureMessage = Messages.NEW_FAILED;
1473
1430
  async handle(_args, options) {
1474
- console4.info(Messages.NEW_START);
1475
- try {
1476
- const directory = getDirectoryInput(options);
1477
- const values = await getSchemaValues2(options);
1478
- await generateApplicationFiles(values, directory);
1479
- if (!values.skipInstall) await runInstall(directory, values.packageManager);
1480
- console4.info();
1481
- console4.info(Messages.NEW_SUCCESS);
1482
- process.exit(0);
1483
- } catch {
1484
- console4.error(Messages.NEW_FAILED);
1485
- process.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));
1486
1437
  }
1438
+ return { success: res };
1487
1439
  }
1488
- };
1489
- var getSchemaValues2 = /* @__PURE__ */ __name(async (inputs) => {
1490
- return {
1491
- name: await getNewNameInputOrAsk(inputs),
1492
- directory: getNewPathInput(inputs),
1493
- packageManager: await getNewPackageManagerInputOrAsk(inputs),
1494
- language: await getNewLanguageInputOrAsk(inputs),
1495
- strict: await getNewStrictOrAsk(inputs),
1496
- server: await getNewServerOrAsk(inputs),
1497
- initFunctions: getNewInitFunctionsWithDefault(inputs),
1498
- skipInstall: await getNewSkipInstallOrAsk(inputs)
1499
- };
1500
- }, "getSchemaValues");
1501
- var generateApplicationFiles = /* @__PURE__ */ __name(async (values, directory) => {
1502
- console4.info();
1503
- const collection = CollectionFactory.create("@nanoforge-dev/schematics" /* NANOFORGE */, directory);
1504
- console4.info();
1505
- console4.info(Messages.SCHEMATICS_START);
1506
- console4.info();
1507
- await executeSchematic("Application", collection, "application", {
1508
- name: values.name,
1509
- directory: values.directory,
1510
- packageManager: values.packageManager,
1511
- language: values.language,
1512
- strict: values.strict,
1513
- server: values.server
1514
- });
1515
- await executeSchematic("Configuration", collection, "configuration", {
1516
- name: values.name,
1517
- directory: values.directory,
1518
- server: values.server
1519
- });
1520
- await executeSchematic("Base Client", collection, "part-base", {
1521
- name: values.name,
1522
- part: "client",
1523
- directory: values.directory,
1524
- language: values.language,
1525
- initFunctions: values.initFunctions
1526
- });
1527
- await executeSchematic("Client main file", collection, "part-main", {
1528
- name: values.name,
1529
- part: "client",
1530
- directory: values.directory,
1531
- language: values.language,
1532
- initFunctions: values.initFunctions
1533
- });
1534
- if (values.server) {
1535
- 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", {
1536
1467
  name: values.name,
1537
- part: "server",
1538
1468
  directory: values.directory,
1469
+ packageManager: values.packageManager,
1539
1470
  language: values.language,
1540
- 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
1541
1503
  });
1542
- await executeSchematic("Server main file", collection, "part-main", {
1504
+ }
1505
+ partOptions(values, part) {
1506
+ return {
1543
1507
  name: values.name,
1544
- part: "server",
1508
+ part,
1545
1509
  directory: values.directory,
1546
1510
  language: values.language,
1547
1511
  initFunctions: values.initFunctions
1548
- });
1512
+ };
1549
1513
  }
1550
- }, "generateApplicationFiles");
1551
- var runInstall = /* @__PURE__ */ __name(async (directory, pkgManagerName) => {
1552
- try {
1553
- const packageManager = PackageManagerFactory.create(pkgManagerName);
1554
- await packageManager.install(directory);
1555
- } catch (error4) {
1556
- if (error4 && error4.message) {
1557
- console4.error(ansis4.red(error4.message));
1558
- }
1514
+ async installDependencies(packageManagerName, directory) {
1515
+ const packageManager = PackageManagerFactory.create(packageManagerName);
1516
+ return await packageManager.install(directory);
1559
1517
  }
1560
- }, "runInstall");
1518
+ };
1561
1519
 
1562
1520
  // src/action/actions/start.action.ts
1563
- import * as ansis5 from "ansis";
1564
- import * as console5 from "console";
1565
- import { join as join4 } from "path";
1521
+ import { join as join6 } from "path";
1566
1522
  var StartAction = class extends AbstractAction {
1567
1523
  static {
1568
1524
  __name(this, "StartAction");
1569
1525
  }
1526
+ startMessage = Messages.START_START;
1527
+ successMessage = Messages.START_SUCCESS;
1528
+ failureMessage = Messages.START_FAILED;
1570
1529
  async handle(_args, options) {
1571
- console5.info(Messages.RUN_START);
1572
- console5.info();
1573
- try {
1574
- const directory = getDirectoryInput(options);
1575
- const config2 = await getConfig(options, directory);
1576
- const clientDir = config2.client.runtime.dir;
1577
- const serverDir = config2.server.runtime.dir;
1578
- const clientPort = getStringInputWithDefault(options, "clientPort", config2.client.port);
1579
- const watch3 = getWatchInput(options);
1580
- await Promise.all([
1581
- config2.server.enable ? this.startServer(directory, serverDir, watch3) : void 0,
1582
- this.startClient(clientPort, directory, clientDir, {
1583
- watch: watch3,
1584
- serverGameDir: config2.server.enable ? serverDir : void 0
1585
- })
1586
- ]);
1587
- process.exit(0);
1588
- } catch (e) {
1589
- console5.error(e);
1590
- process.exit(1);
1591
- }
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
+ };
1592
1556
  }
1593
- async startClient(port, directory, gameDir, options) {
1594
- const path = getModulePath("@nanoforge-dev/loader-client/package.json", true);
1595
- const params = {
1596
- PORT: port,
1597
- GAME_DIR: getCwd(join4(directory, gameDir))
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))
1598
1575
  };
1599
- if (options?.watch) {
1600
- params["WATCH"] = "true";
1601
- if (options?.serverGameDir) {
1602
- params["WATCH_SERVER_GAME_DIR"] = getCwd(join4(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));
1603
1583
  }
1604
1584
  }
1605
- return runPart("Client", path, params);
1585
+ if (ssl) {
1586
+ env2["CERT"] = ssl.cert;
1587
+ env2["KEY"] = ssl.key;
1588
+ }
1589
+ return env2;
1606
1590
  }
1607
- startServer(directory, gameDir, watch3) {
1608
- const path = getModulePath("@nanoforge-dev/loader-server/package.json", true);
1609
- const params = {
1610
- GAME_DIR: getCwd(join4(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))
1611
1595
  };
1612
- if (watch3) params["WATCH"] = "true";
1613
- 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
+ });
1614
1605
  }
1615
1606
  };
1616
- var runPart = /* @__PURE__ */ __name(async (part, directory, env2, flags) => {
1617
- try {
1618
- const packageManager = await PackageManagerFactory.find(directory);
1619
- await packageManager.run(part, directory, "start", env2, flags, true);
1620
- } catch (error4) {
1621
- if (error4 && error4.message) {
1622
- console5.error(ansis5.red(error4.message));
1623
- }
1624
- }
1625
- }, "runPart");
1626
1607
 
1627
1608
  // src/command/abstract.command.ts
1628
1609
  var AbstractCommand = class {
@@ -1632,6 +1613,13 @@ var AbstractCommand = class {
1632
1613
  static {
1633
1614
  __name(this, "AbstractCommand");
1634
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
+ }
1635
1623
  };
1636
1624
 
1637
1625
  // src/command/commands/build.command.ts
@@ -1640,15 +1628,15 @@ var BuildCommand = class extends AbstractCommand {
1640
1628
  __name(this, "BuildCommand");
1641
1629
  }
1642
1630
  load(program) {
1643
- 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) => {
1644
- const options = /* @__PURE__ */ new Map();
1645
- options.set("directory", { value: rawOptions.directory });
1646
- options.set("config", { value: rawOptions.config });
1647
- options.set("clientDirectory", { value: rawOptions.clientOutDir });
1648
- options.set("serverDirectory", { value: rawOptions.serverOutDir });
1649
- options.set("watch", { value: rawOptions.watch });
1650
- const args = /* @__PURE__ */ new Map();
1651
- 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);
1652
1640
  });
1653
1641
  }
1654
1642
  };
@@ -1659,13 +1647,13 @@ var DevCommand = class extends AbstractCommand {
1659
1647
  __name(this, "DevCommand");
1660
1648
  }
1661
1649
  load(program) {
1662
- 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) => {
1663
- const options = /* @__PURE__ */ new Map();
1664
- options.set("directory", { value: rawOptions.directory });
1665
- options.set("config", { value: rawOptions.config });
1666
- options.set("generate", { value: rawOptions.generate });
1667
- const args = /* @__PURE__ */ new Map();
1668
- 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);
1669
1657
  });
1670
1658
  }
1671
1659
  };
@@ -1676,13 +1664,13 @@ var GenerateCommand = class extends AbstractCommand {
1676
1664
  __name(this, "GenerateCommand");
1677
1665
  }
1678
1666
  load(program) {
1679
- 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) => {
1680
- const options = /* @__PURE__ */ new Map();
1681
- options.set("directory", { value: rawOptions.directory });
1682
- options.set("config", { value: rawOptions.config });
1683
- options.set("watch", { value: rawOptions.watch });
1684
- const args = /* @__PURE__ */ new Map();
1685
- 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);
1686
1674
  });
1687
1675
  }
1688
1676
  };
@@ -1694,11 +1682,13 @@ var InstallCommand = class extends AbstractCommand {
1694
1682
  }
1695
1683
  load(program) {
1696
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) => {
1697
- const options = /* @__PURE__ */ new Map();
1698
- options.set("directory", { value: rawOptions.directory });
1699
- const args = /* @__PURE__ */ new Map();
1700
- args.set("names", { value: names.length ? names : void 0 });
1701
- 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);
1702
1692
  });
1703
1693
  }
1704
1694
  };
@@ -1709,19 +1699,20 @@ var NewCommand = class extends AbstractCommand {
1709
1699
  __name(this, "NewCommand");
1710
1700
  }
1711
1701
  load(program) {
1712
- 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) => {
1713
- const options = /* @__PURE__ */ new Map();
1714
- options.set("directory", { value: rawOptions.directory });
1715
- options.set("name", { value: rawOptions.name });
1716
- options.set("path", { value: rawOptions.path });
1717
- options.set("packageManager", { value: rawOptions.packageManager });
1718
- options.set("language", { value: rawOptions.language });
1719
- options.set("strict", { value: rawOptions.strict });
1720
- options.set("server", { value: rawOptions.server });
1721
- options.set("initFunctions", { value: rawOptions.initFunctions });
1722
- options.set("skipInstall", { value: rawOptions.skipInstall });
1723
- const args = /* @__PURE__ */ new Map();
1724
- 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);
1725
1716
  });
1726
1717
  }
1727
1718
  };
@@ -1732,21 +1723,21 @@ var StartCommand = class extends AbstractCommand {
1732
1723
  __name(this, "StartCommand");
1733
1724
  }
1734
1725
  load(program) {
1735
- 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(
1736
1727
  "-p, --client-port [clientPort]",
1737
1728
  "specify the port of the loader (the website to load the game)"
1738
- ).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).action(async (rawOptions) => {
1739
- const options = /* @__PURE__ */ new Map();
1740
- options.set("directory", { value: rawOptions.directory });
1741
- options.set("config", { value: rawOptions.config });
1742
- options.set("clientPort", { value: rawOptions.clientPort });
1743
- options.set("gameExposurePort", {
1744
- value: rawOptions.gameExposurePort
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) => {
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
1745
1739
  });
1746
- options.set("serverPort", { value: rawOptions.serverPort });
1747
- options.set("watch", { value: rawOptions.watch });
1748
- const args = /* @__PURE__ */ new Map();
1749
- await this.action.handle(args, options);
1740
+ await this.action.run(/* @__PURE__ */ new Map(), options);
1750
1741
  });
1751
1742
  }
1752
1743
  };
@@ -1768,8 +1759,8 @@ var CommandLoader = class {
1768
1759
  static handleInvalidCommand(program) {
1769
1760
  program.on("command:*", () => {
1770
1761
  console.error(`
1771
- ${Prefixes.ERROR} Invalid command: ${red8`%s`}`, program.args.join(" "));
1772
- 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.
1773
1764
  `);
1774
1765
  process.exit(1);
1775
1766
  });