@nanoforge-dev/cli 1.3.0 → 1.4.1

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
@@ -40,7 +40,7 @@ var require_package = __commonJS({
40
40
  module.exports = {
41
41
  $schema: "https://json.schemastore.org/package.json",
42
42
  name: "@nanoforge-dev/cli",
43
- version: "1.3.0",
43
+ version: "1.4.1",
44
44
  description: "NanoForge CLI",
45
45
  keywords: [
46
46
  "nanoforge",
@@ -103,6 +103,7 @@ var require_package = __commonJS({
103
103
  commander: "catalog:cli",
104
104
  dotenv: "catalog:libs",
105
105
  "node-emoji": "catalog:cli",
106
+ open: "catalog:libs",
106
107
  ora: "catalog:cli",
107
108
  rc9: "catalog:libs",
108
109
  "reflect-metadata": "catalog:libs"
@@ -126,7 +127,10 @@ var require_package = __commonJS({
126
127
  typescript: "catalog:build",
127
128
  vitest: "catalog:tests"
128
129
  },
129
- packageManager: "pnpm@10.28.1",
130
+ optionalDependencies: {
131
+ "@nanoforge-dev/editor": "file:../editor"
132
+ },
133
+ packageManager: "pnpm@10.33.0",
130
134
  engines: {
131
135
  node: "25"
132
136
  },
@@ -149,6 +153,110 @@ var require_package = __commonJS({
149
153
  import { program } from "commander";
150
154
  import "reflect-metadata";
151
155
 
156
+ // src/lib/tree-kill/tree-kill.ts
157
+ import { execSync, spawn } from "child_process";
158
+ var treeKill = /* @__PURE__ */ __name((pid, signal) => {
159
+ function extracted(resolve4) {
160
+ const tree = {};
161
+ const pidsToProcess = {};
162
+ tree[pid] = [];
163
+ pidsToProcess[pid] = 1;
164
+ switch (process.platform) {
165
+ case "win32":
166
+ execSync("taskkill /pid " + pid + " /T /F");
167
+ resolve4();
168
+ break;
169
+ case "darwin":
170
+ buildProcessTree(
171
+ pid,
172
+ tree,
173
+ pidsToProcess,
174
+ function(parentPid) {
175
+ return spawn("pgrep", ["-P", parentPid]);
176
+ },
177
+ function() {
178
+ killAll(tree, signal);
179
+ resolve4();
180
+ }
181
+ );
182
+ break;
183
+ default:
184
+ buildProcessTree(
185
+ pid,
186
+ tree,
187
+ pidsToProcess,
188
+ function(parentPid) {
189
+ return spawn("ps", ["-o", "pid", "--no-headers", "--ppid", parentPid]);
190
+ },
191
+ function() {
192
+ killAll(tree, signal);
193
+ resolve4();
194
+ }
195
+ );
196
+ break;
197
+ }
198
+ }
199
+ __name(extracted, "extracted");
200
+ return new Promise((resolve4) => {
201
+ extracted(resolve4);
202
+ });
203
+ }, "treeKill");
204
+ function killAll(tree, signal) {
205
+ const killed = {};
206
+ Object.keys(tree).reverse().forEach(function(pidStr) {
207
+ const pid = Number(pidStr);
208
+ tree[pid]?.forEach(function(pidpid) {
209
+ if (!killed[pidpid]) {
210
+ killPid(pidpid, signal);
211
+ killed[pidpid] = true;
212
+ }
213
+ });
214
+ if (!killed[pid]) {
215
+ killPid(pid, signal);
216
+ killed[pid] = true;
217
+ }
218
+ });
219
+ }
220
+ __name(killAll, "killAll");
221
+ function killPid(pid, signal) {
222
+ try {
223
+ process.kill(pid, signal);
224
+ } catch (err) {
225
+ const e = err;
226
+ if (e.code !== "ESRCH") throw err;
227
+ }
228
+ }
229
+ __name(killPid, "killPid");
230
+ function buildProcessTree(parentPid, tree, pidsToProcess, spawnChildProcessesList, cb) {
231
+ const ps = spawnChildProcessesList(parentPid.toString());
232
+ let allData = "";
233
+ ps.stdout.on("data", function(data) {
234
+ allData += data.toString("ascii");
235
+ });
236
+ const onClose = /* @__PURE__ */ __name(function(code) {
237
+ delete pidsToProcess[parentPid];
238
+ if (code != 0) {
239
+ if (Object.keys(pidsToProcess).length == 0) {
240
+ cb();
241
+ }
242
+ return;
243
+ }
244
+ if (allData)
245
+ allData.match(/\d+/g)?.forEach(function(pid) {
246
+ const intPid = Number(pid);
247
+ if (tree[parentPid] === void 0) {
248
+ tree[parentPid] = [];
249
+ }
250
+ tree[parentPid].push(intPid);
251
+ tree[intPid] = [];
252
+ pidsToProcess[intPid] = 1;
253
+ buildProcessTree(intPid, tree, pidsToProcess, spawnChildProcessesList, cb);
254
+ });
255
+ }, "onClose");
256
+ ps.on("close", onClose);
257
+ }
258
+ __name(buildProcessTree, "buildProcessTree");
259
+
152
260
  // src/lib/utils/local-binaries.ts
153
261
  import { existsSync } from "fs";
154
262
  import { join, posix } from "path";
@@ -162,10 +270,10 @@ var loadLocalBinCommandLoader = /* @__PURE__ */ __name(async () => {
162
270
  }, "loadLocalBinCommandLoader");
163
271
 
164
272
  // src/command/command.loader.ts
165
- import { red as red6 } from "ansis";
273
+ import { red as red7 } from "ansis";
166
274
 
167
275
  // src/lib/ui/messages.ts
168
- import { green } from "ansis";
276
+ import { bold, green } from "ansis";
169
277
 
170
278
  // src/lib/ui/emojis.ts
171
279
  import { get } from "node-emoji";
@@ -200,8 +308,9 @@ var Messages = {
200
308
  BUILD_WATCH_START: "Watching for changes...",
201
309
  BUILD_PART_IN_PROGRESS: /* @__PURE__ */ __name((part) => `Building ${part}`, "BUILD_PART_IN_PROGRESS"),
202
310
  BUILD_PART_WATCH_IN_PROGRESS: /* @__PURE__ */ __name((part) => `${part} updated, rebuilding`, "BUILD_PART_WATCH_IN_PROGRESS"),
311
+ BUILD_PART_SUCCESS: /* @__PURE__ */ __name((name) => success(`Build of ${name} succeeded!`), "BUILD_PART_SUCCESS"),
203
312
  BUILD_PART_FAILED: /* @__PURE__ */ __name((part, command) => failure(`Build of ${part} failed!
204
- Try running manually: ${command}`), "BUILD_PART_FAILED"),
313
+ Try running manually: ${bold(command)}`), "BUILD_PART_FAILED"),
205
314
  // --- Install ---
206
315
  INSTALL_START: "NanoForge Installation",
207
316
  INSTALL_SUCCESS: success("Installation completed!"),
@@ -221,6 +330,7 @@ Try running manually: ${command}`), "BUILD_PART_FAILED"),
221
330
  NEW_START: "NanoForge Project Creation",
222
331
  NEW_SUCCESS: success("Project successfully created!"),
223
332
  NEW_FAILED: failure("Project creation failed!"),
333
+ NEW_GENERATION_START: "Creating project...",
224
334
  NEW_NAME_QUESTION: "What is the name of your project?",
225
335
  NEW_PACKAGE_MANAGER_QUESTION: "Which package manager do you want to use?",
226
336
  NEW_LANGUAGE_QUESTION: "Which language do you want to use?",
@@ -228,6 +338,13 @@ Try running manually: ${command}`), "BUILD_PART_FAILED"),
228
338
  NEW_SERVER_QUESTION: "Do you want to generate a server for multiplayer?",
229
339
  NEW_SKIP_INSTALL_QUESTION: "Do you want to skip dependency installation?",
230
340
  NEW_DOCKER_QUESTION: "Do you want to add a Dockerfile for containerization?",
341
+ NEW_GIT_QUESTION: "Do you want to create a git repository?",
342
+ NEW_GIT_REMOTE_QUESTION: "Do you want to setup a git remote? (leave empty if you don't want to)",
343
+ // --- Create ---
344
+ CREATE_START: "NanoForge Component/System Creation",
345
+ CREATE_SUCCESS: success("Element successfully created!"),
346
+ CREATE_FAILED: failure("Creation failed!"),
347
+ CREATE_NAME_QUESTION: "What is the name of your component/system?",
231
348
  // --- Generate ---
232
349
  GENERATE_START: "NanoForge Generate",
233
350
  GENERATE_SUCCESS: success("Generation succeeded!"),
@@ -244,6 +361,9 @@ Try running manually: ${command}`), "BUILD_PART_FAILED"),
244
361
  START_PART_IN_PROGRESS: /* @__PURE__ */ __name((part) => `Starting ${part}...`, "START_PART_IN_PROGRESS"),
245
362
  START_PART_SUCCESS: /* @__PURE__ */ __name((part) => success(`${part} terminated.`), "START_PART_SUCCESS"),
246
363
  START_PART_FAILED: /* @__PURE__ */ __name((part) => failure(`${part} failed!`), "START_PART_FAILED"),
364
+ EDITOR_START: "NanoForge Editor",
365
+ EDITOR_SUCCESS: "Editor ended",
366
+ EDITOR_FAILED: failure("Editor failed!"),
247
367
  // --- Publish ---
248
368
  PUBLISH_START: "NanoForge Publish",
249
369
  PUBLISH_SUCCESS: success("Publish completed!"),
@@ -255,7 +375,6 @@ Try running manually: ${command}`), "BUILD_PART_FAILED"),
255
375
  UNPUBLISH_FAILED: failure("Unpublish failed!"),
256
376
  UNPUBLISH_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Unpublishing ${name}...`, "UNPUBLISH_IN_PROGRESS"),
257
377
  // --- Schematics ---
258
- SCHEMATICS_START: "Running schematics",
259
378
  SCHEMATIC_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Generating ${name}...`, "SCHEMATIC_IN_PROGRESS"),
260
379
  SCHEMATIC_WATCH_IN_PROGRESS: /* @__PURE__ */ __name((name) => `Change detected, regenerating ${name}...`, "SCHEMATIC_WATCH_IN_PROGRESS"),
261
380
  SCHEMATIC_SUCCESS: /* @__PURE__ */ __name((name) => success(`${name} generated successfully!`), "SCHEMATIC_SUCCESS"),
@@ -265,10 +384,20 @@ Try running manually: ${command}`), "BUILD_PART_FAILED"),
265
384
  PACKAGE_MANAGER_INSTALLATION_NOTHING: "Nothing to install.",
266
385
  PACKAGE_MANAGER_INSTALLATION_SUCCEED: /* @__PURE__ */ __name((names) => names ? success(`Packages installed: ${names.map((n) => green(n)).join(", ")}`) : success("Packages installed!"), "PACKAGE_MANAGER_INSTALLATION_SUCCEED"),
267
386
  PACKAGE_MANAGER_INSTALLATION_FAILED: /* @__PURE__ */ __name((command) => failure(`Package installation failed!
268
- Try running manually: ${command}`), "PACKAGE_MANAGER_INSTALLATION_FAILED"),
387
+ Try running manually: ${bold(command)}`), "PACKAGE_MANAGER_INSTALLATION_FAILED"),
388
+ // --- Git init ---
389
+ GIT_INIT_IN_PROGRESS: `Initializing git repository... ${Emojis.COFFEE}`,
390
+ GIT_INIT_SUCCEED: success("Git repository initialized!"),
391
+ GIT_INIT_FAILED: /* @__PURE__ */ __name((command) => failure(`Git repository initialization failed!
392
+ Try running manually: ${bold(command)}`), "GIT_INIT_FAILED"),
393
+ // --- Git remote ---
394
+ GIT_REMOTE_IN_PROGRESS: `Adding git remote... ${Emojis.COFFEE}`,
395
+ GIT_REMOTE_SUCCEED: success("Git remote added!"),
396
+ GIT_REMOTE_FAILED: /* @__PURE__ */ __name((command) => failure(`Git remote addition failed!
397
+ Try running manually: ${bold(command)}`), "GIT_REMOTE_FAILED"),
269
398
  // --- Runner ---
270
399
  RUNNER_EXECUTION_ERROR: /* @__PURE__ */ __name((command) => `
271
- Failed to execute command: ${command}`, "RUNNER_EXECUTION_ERROR")
400
+ Failed to execute command: ${bold(command)}`, "RUNNER_EXECUTION_ERROR")
272
401
  };
273
402
 
274
403
  // src/lib/ui/prefixes.ts
@@ -284,7 +413,7 @@ var getSpinner = /* @__PURE__ */ __name((message) => ora({ text: message }), "ge
284
413
 
285
414
  // src/action/actions/build.action.ts
286
415
  import { watch } from "chokidar";
287
- import { dirname, join as join4 } from "path";
416
+ import { dirname, join as join5 } from "path";
288
417
 
289
418
  // src/lib/input/base-inputs.ts
290
419
  var getStringInput = /* @__PURE__ */ __name((input2, field) => {
@@ -333,21 +462,10 @@ var getConfigInput = /* @__PURE__ */ __name((inputs) => {
333
462
  return getStringInputWithDefault(inputs, "config", ".");
334
463
  }, "getConfigInput");
335
464
 
336
- // src/lib/input/inputs/watch.input.ts
337
- var getWatchInput = /* @__PURE__ */ __name((inputs) => {
338
- return getBooleanInputWithDefault(inputs, "watch", false);
339
- }, "getWatchInput");
340
-
341
- // src/lib/input/inputs/dev/generate.input.ts
342
- var getDevGenerateInput = /* @__PURE__ */ __name((inputs) => {
343
- return getBooleanInputWithDefault(inputs, "generate", false);
344
- }, "getDevGenerateInput");
345
-
346
- // src/lib/input/inputs/install/lib.input.ts
347
- function getInstallLibInput(inputs) {
348
- return getBooleanInputWithDefault(inputs, "lib", false);
349
- }
350
- __name(getInstallLibInput, "getInstallLibInput");
465
+ // src/lib/input/inputs/editor.input.ts
466
+ var getEditorInput = /* @__PURE__ */ __name((inputs) => {
467
+ return getBooleanInputWithDefault(inputs, "editor", false);
468
+ }, "getEditorInput");
351
469
 
352
470
  // src/lib/question/questions/confirm.question.ts
353
471
  import { confirm } from "@inquirer/prompts";
@@ -437,6 +555,71 @@ var askSelect = /* @__PURE__ */ __name(async (question, choices, baseOptions) =>
437
555
  }).catch(promptError);
438
556
  }, "askSelect");
439
557
 
558
+ // src/lib/input/inputs/name.input.ts
559
+ var getNameInput = /* @__PURE__ */ __name((inputs) => {
560
+ return getStringInput(inputs, "name");
561
+ }, "getNameInput");
562
+ var getNewNameInputOrAsk = /* @__PURE__ */ __name((inputs) => {
563
+ return getInputOrAsk(
564
+ getNameInput(inputs),
565
+ () => askInput(Messages.NEW_NAME_QUESTION, {
566
+ required: true,
567
+ default: "nanoforge-app"
568
+ })
569
+ );
570
+ }, "getNewNameInputOrAsk");
571
+ var getCreateNameInputOrAsk = /* @__PURE__ */ __name((inputs) => {
572
+ return getInputOrAsk(
573
+ getNameInput(inputs),
574
+ () => askInput(Messages.CREATE_NAME_QUESTION, {
575
+ required: true,
576
+ default: "example"
577
+ })
578
+ );
579
+ }, "getCreateNameInputOrAsk");
580
+
581
+ // src/lib/input/inputs/path.input.ts
582
+ var getPathInput = /* @__PURE__ */ __name((inputs) => {
583
+ return getStringInput(inputs, "path");
584
+ }, "getPathInput");
585
+ var getPathInputWithDefault = /* @__PURE__ */ __name((inputs, defaultValue) => {
586
+ return getStringInputWithDefault(inputs, "path", defaultValue);
587
+ }, "getPathInputWithDefault");
588
+
589
+ // src/lib/input/inputs/server.input.ts
590
+ function getServerInput(inputs) {
591
+ return getBooleanInputWithDefault(inputs, "server", false);
592
+ }
593
+ __name(getServerInput, "getServerInput");
594
+
595
+ // src/lib/input/inputs/watch.input.ts
596
+ var getWatchInput = /* @__PURE__ */ __name((inputs) => {
597
+ return getBooleanInputWithDefault(inputs, "watch", false);
598
+ }, "getWatchInput");
599
+
600
+ // src/lib/input/inputs/create/type.input.ts
601
+ var getCreateTypeInput = /* @__PURE__ */ __name((inputs) => {
602
+ const res = getStringInput(inputs, "type");
603
+ if (res && ["component", "system"].includes(res)) return res;
604
+ throw new Error("Invalid type. Please enter 'component' or 'system'.");
605
+ }, "getCreateTypeInput");
606
+
607
+ // src/lib/input/inputs/dev/generate.input.ts
608
+ var getDevGenerateInput = /* @__PURE__ */ __name((inputs) => {
609
+ return getBooleanInputWithDefault(inputs, "generate", false);
610
+ }, "getDevGenerateInput");
611
+
612
+ // src/lib/input/inputs/editor/open.input.ts
613
+ var getEditorOpenInput = /* @__PURE__ */ __name((inputs, defaultValue) => {
614
+ return getBooleanInputWithDefault(inputs, "open", defaultValue);
615
+ }, "getEditorOpenInput");
616
+
617
+ // src/lib/input/inputs/install/lib.input.ts
618
+ function getInstallLibInput(inputs) {
619
+ return getBooleanInputWithDefault(inputs, "lib", false);
620
+ }
621
+ __name(getInstallLibInput, "getInstallLibInput");
622
+
440
623
  // src/lib/input/inputs/install/names.input.ts
441
624
  var getNamesInput = /* @__PURE__ */ __name((inputs) => {
442
625
  return getArrayInput(inputs, "names");
@@ -448,12 +631,6 @@ var getInstallNamesInputOrAsk = /* @__PURE__ */ __name((inputs) => {
448
631
  );
449
632
  }, "getInstallNamesInputOrAsk");
450
633
 
451
- // src/lib/input/inputs/install/server.input.ts
452
- function getInstallServerInput(inputs) {
453
- return getBooleanInputWithDefault(inputs, "server", false);
454
- }
455
- __name(getInstallServerInput, "getInstallServerInput");
456
-
457
634
  // src/lib/input/inputs/login-out/api-key.input.ts
458
635
  var getApiKeyInput = /* @__PURE__ */ __name((inputs) => {
459
636
  return getStringInput(inputs, "apiKey");
@@ -507,20 +684,6 @@ var getNewLintInput = /* @__PURE__ */ __name((inputs) => {
507
684
  return getBooleanInputWithDefault(inputs, "lint", true);
508
685
  }, "getNewLintInput");
509
686
 
510
- // src/lib/input/inputs/new/name.input.ts
511
- var getNameInput = /* @__PURE__ */ __name((inputs) => {
512
- return getStringInput(inputs, "name");
513
- }, "getNameInput");
514
- var getNewNameInputOrAsk = /* @__PURE__ */ __name((inputs) => {
515
- return getInputOrAsk(
516
- getNameInput(inputs),
517
- () => askInput(Messages.NEW_NAME_QUESTION, {
518
- required: true,
519
- default: "nanoforge-app"
520
- })
521
- );
522
- }, "getNewNameInputOrAsk");
523
-
524
687
  // src/lib/input/inputs/new/package-manager.input.ts
525
688
  var getPackageManagerInput = /* @__PURE__ */ __name((inputs) => {
526
689
  return getStringInput(inputs, "packageManager");
@@ -538,11 +701,6 @@ var getNewPackageManagerInputOrAsk = /* @__PURE__ */ __name((inputs) => {
538
701
  );
539
702
  }, "getNewPackageManagerInputOrAsk");
540
703
 
541
- // src/lib/input/inputs/new/path.input.ts
542
- var getNewPathInput = /* @__PURE__ */ __name((inputs) => {
543
- return getStringInput(inputs, "path");
544
- }, "getNewPathInput");
545
-
546
704
  // src/lib/input/inputs/new/server.input.ts
547
705
  var getNewServerInput = /* @__PURE__ */ __name((inputs) => {
548
706
  return getBooleanInput(inputs, "server");
@@ -577,7 +735,7 @@ var getNewStrictOrAsk = /* @__PURE__ */ __name((inputs) => {
577
735
  }, "getNewStrictOrAsk");
578
736
 
579
737
  // src/lib/package-manager/package-manager.ts
580
- import { bold, red as red3 } from "ansis";
738
+ import { red as red4 } from "ansis";
581
739
 
582
740
  // src/lib/runner/process-logger.ts
583
741
  import { green as green2, red as red2, yellow } from "ansis";
@@ -624,15 +782,16 @@ var resolveCLINodeBinaryPath = /* @__PURE__ */ __name((name) => {
624
782
  }, "resolveCLINodeBinaryPath");
625
783
 
626
784
  // src/lib/utils/spinner.ts
627
- var withSpinner = /* @__PURE__ */ __name(async (message, task, onError) => {
628
- const spinner = getSpinner(message);
785
+ import { red as red3 } from "ansis";
786
+ var withSpinner = /* @__PURE__ */ __name(async (task, loadingMessage, successMessage, failureMessage, onError) => {
787
+ const spinner = getSpinner(loadingMessage);
629
788
  spinner.start();
630
789
  try {
631
790
  const value = await task(spinner);
632
- spinner.succeed();
791
+ spinner.succeed(successMessage);
633
792
  return { success: true, value };
634
793
  } catch (error) {
635
- spinner.fail();
794
+ spinner.fail(red3(failureMessage));
636
795
  if (onError) onError();
637
796
  return { success: false, error };
638
797
  }
@@ -645,18 +804,19 @@ var PackageManager = class {
645
804
  this.commands = commands;
646
805
  this.runner = runner;
647
806
  }
807
+ name;
808
+ commands;
809
+ runner;
648
810
  static {
649
811
  __name(this, "PackageManager");
650
812
  }
651
813
  async install(directory) {
652
814
  const args = [this.commands.install, this.commands.silentFlag];
653
815
  const result = await withSpinner(
816
+ () => this.exec(args, directory),
654
817
  Messages.PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS,
655
- async (spinner) => {
656
- await this.exec(args, directory, { onFail: /* @__PURE__ */ __name(() => spinner.fail(), "onFail") });
657
- this.logSuccess(Messages.PACKAGE_MANAGER_INSTALLATION_SUCCEED());
658
- },
659
- () => this.logFailure(this.formatFailCommand([this.commands.install]))
818
+ Messages.PACKAGE_MANAGER_INSTALLATION_SUCCEED(),
819
+ Messages.PACKAGE_MANAGER_INSTALLATION_FAILED(this.formatFailCommand([this.commands.install]))
660
820
  );
661
821
  return result.success;
662
822
  }
@@ -679,11 +839,10 @@ var PackageManager = class {
679
839
  ...flags
680
840
  ];
681
841
  const result = await withSpinner(
842
+ () => this.exec(args, directory),
682
843
  message,
683
- async (spinner) => {
684
- await this.exec(args, directory, { onFail: /* @__PURE__ */ __name(() => spinner.fail(), "onFail") });
685
- },
686
- () => this.logBuildFailure(name)
844
+ Messages.BUILD_PART_SUCCESS(name),
845
+ Messages.BUILD_PART_FAILED(name, this.formatFailCommand(args))
687
846
  );
688
847
  return result.success;
689
848
  }
@@ -701,7 +860,25 @@ var PackageManager = class {
701
860
  console.info(Messages.START_PART_SUCCESS(name));
702
861
  return true;
703
862
  } catch {
704
- console.error(red3(Messages.START_PART_FAILED(name)));
863
+ console.error(red4(Messages.START_PART_FAILED(name)));
864
+ return false;
865
+ }
866
+ }
867
+ async runFile(name, directory, script, params, env2 = {}, flags = [], silent = false) {
868
+ console.info(Messages.START_PART_IN_PROGRESS(name));
869
+ try {
870
+ const args = this.buildRunFileArgs(script, params, flags, silent);
871
+ await this.exec(args, directory, {
872
+ env: env2,
873
+ listeners: {
874
+ onStdout: createStdoutLogger(name),
875
+ onStderr: createStderrLogger(name)
876
+ }
877
+ });
878
+ console.info(Messages.START_PART_SUCCESS(name));
879
+ return true;
880
+ } catch {
881
+ console.error(red4(Messages.START_PART_FAILED(name)));
705
882
  return false;
706
883
  }
707
884
  }
@@ -722,12 +899,10 @@ var PackageManager = class {
722
899
  }
723
900
  const args = [this.commands.add, saveFlag, ...dependencies];
724
901
  const result = await withSpinner(
902
+ () => this.exec(args, directory),
725
903
  Messages.PACKAGE_MANAGER_INSTALLATION_IN_PROGRESS,
726
- async (spinner) => {
727
- await this.exec(args, directory, { onFail: /* @__PURE__ */ __name(() => spinner.fail(), "onFail") });
728
- this.logSuccess(Messages.PACKAGE_MANAGER_INSTALLATION_SUCCEED(dependencies));
729
- },
730
- () => this.logFailure(this.formatFailCommand(args))
904
+ Messages.PACKAGE_MANAGER_INSTALLATION_SUCCEED(),
905
+ Messages.PACKAGE_MANAGER_INSTALLATION_FAILED(this.formatFailCommand(args))
731
906
  );
732
907
  return result.success;
733
908
  }
@@ -744,6 +919,15 @@ var PackageManager = class {
744
919
  if (this.commands.runArgsFlag) args.push(this.commands.runArgsFlag);
745
920
  return args.concat(params);
746
921
  }
922
+ buildRunFileArgs(script, params, flags, silent) {
923
+ if (!this.commands.runFile) throw new Error("Package manager does not support runFile");
924
+ const args = [...flags, this.commands.runFile];
925
+ if (silent) args.push(this.commands.silentFlag);
926
+ args.push(script);
927
+ if (params.length === 0) return args;
928
+ if (this.commands.runArgsFlag) args.push(this.commands.runArgsFlag);
929
+ return args.concat(params);
930
+ }
747
931
  exec(args, directory, options = {}) {
748
932
  return this.runner.run(args, {
749
933
  collect: options.collect ?? true,
@@ -756,18 +940,6 @@ var PackageManager = class {
756
940
  formatFailCommand(args) {
757
941
  return this.runner.fullCommand(args);
758
942
  }
759
- logSuccess(message) {
760
- console.info();
761
- console.info(message);
762
- console.info();
763
- }
764
- logFailure(command) {
765
- console.error(red3(Messages.PACKAGE_MANAGER_INSTALLATION_FAILED(bold(command))));
766
- }
767
- logBuildFailure(name) {
768
- const command = this.formatFailCommand([this.commands.install]);
769
- console.error(red3(Messages.BUILD_PART_FAILED(name, bold(command))));
770
- }
771
943
  logEmpty(message) {
772
944
  console.info();
773
945
  console.info(message);
@@ -780,14 +952,16 @@ import fs2 from "fs";
780
952
  import { resolve as resolve2 } from "path";
781
953
 
782
954
  // src/lib/runner/runner.ts
783
- import { red as red4 } from "ansis";
784
- import { spawn } from "child_process";
955
+ import { red as red5 } from "ansis";
956
+ import { spawn as spawn2 } from "child_process";
785
957
  import * as process2 from "process";
786
958
  var Runner = class {
787
959
  constructor(binary, baseArgs = []) {
788
960
  this.binary = binary;
789
961
  this.baseArgs = baseArgs;
790
962
  }
963
+ binary;
964
+ baseArgs;
791
965
  static {
792
966
  __name(this, "Runner");
793
967
  }
@@ -796,7 +970,7 @@ var Runner = class {
796
970
  const spawnOpts = this.buildSpawnOptions(collect, cwd2, env2);
797
971
  const fullArgs = [...this.baseArgs, ...args];
798
972
  return new Promise((resolve4, reject) => {
799
- const child = spawn(`${this.binary} ${fullArgs.join(" ")}`, spawnOpts);
973
+ const child = spawn2(`${this.binary} ${fullArgs.join(" ")}`, spawnOpts);
800
974
  const output = this.attachOutputHandlers(child, listeners);
801
975
  child.on("close", (code) => {
802
976
  if (code === 0) {
@@ -835,7 +1009,7 @@ var Runner = class {
835
1009
  this.logCapturedOutput(output);
836
1010
  }
837
1011
  logFailedCommand(args) {
838
- console.error(red4(`
1012
+ console.error(red5(`
839
1013
  Failed to execute command: ${this.binary} ${args.join(" ")}`));
840
1014
  }
841
1015
  logCapturedOutput(output) {
@@ -992,14 +1166,29 @@ var PackageManagerFactory = class {
992
1166
  }
993
1167
  };
994
1168
 
1169
+ // src/lib/utils/files.ts
1170
+ import fs3 from "fs";
1171
+ import { join as join3 } from "path";
1172
+ var copyFiles = /* @__PURE__ */ __name((from, to) => {
1173
+ if (!fs3.existsSync(from)) return;
1174
+ if (!fs3.existsSync(to)) throw new Error(`Directory ${to} does not exist`);
1175
+ fs3.readdirSync(from, { recursive: true }).forEach((file) => {
1176
+ fs3.copyFileSync(join3(from, file.toString()), join3(to, file.toString()));
1177
+ });
1178
+ }, "copyFiles");
1179
+ var resetFolder = /* @__PURE__ */ __name((folder) => {
1180
+ if (fs3.existsSync(folder)) fs3.rmSync(folder, { recursive: true, force: true });
1181
+ fs3.mkdirSync(folder, { recursive: true });
1182
+ }, "resetFolder");
1183
+
995
1184
  // src/lib/utils/run-safe.ts
996
- import { red as red5 } from "ansis";
1185
+ import { red as red6 } from "ansis";
997
1186
  var runSafe = /* @__PURE__ */ __name(async (fn, fallback) => {
998
1187
  try {
999
1188
  return await fn();
1000
1189
  } catch (error) {
1001
1190
  const msg = getErrorMessage(error);
1002
- if (msg) console.error(red5(msg));
1191
+ if (msg) console.error(red6(msg));
1003
1192
  return fallback;
1004
1193
  }
1005
1194
  }, "runSafe");
@@ -1011,42 +1200,77 @@ var BuildConfig = class {
1011
1200
  static {
1012
1201
  __name(this, "BuildConfig");
1013
1202
  }
1014
- entryFile;
1015
- outDir;
1203
+ entry;
1204
+ staticDir;
1016
1205
  };
1017
1206
  __decorateClass([
1018
1207
  Expose(),
1019
1208
  IsString(),
1020
1209
  IsNotEmpty()
1021
- ], BuildConfig.prototype, "entryFile", 2);
1210
+ ], BuildConfig.prototype, "entry", 2);
1022
1211
  __decorateClass([
1023
1212
  Expose(),
1024
1213
  IsString(),
1025
1214
  IsNotEmpty()
1026
- ], BuildConfig.prototype, "outDir", 2);
1027
- var RunConfig = class {
1215
+ ], BuildConfig.prototype, "staticDir", 2);
1216
+ var EditorConfig = class {
1028
1217
  static {
1029
- __name(this, "RunConfig");
1218
+ __name(this, "EditorConfig");
1030
1219
  }
1031
- dir;
1220
+ entry;
1221
+ save;
1032
1222
  };
1033
1223
  __decorateClass([
1034
1224
  Expose(),
1035
1225
  IsString(),
1036
1226
  IsNotEmpty()
1037
- ], RunConfig.prototype, "dir", 2);
1227
+ ], EditorConfig.prototype, "entry", 2);
1228
+ __decorateClass([
1229
+ Expose(),
1230
+ IsString(),
1231
+ IsNotEmpty()
1232
+ ], EditorConfig.prototype, "save", 2);
1233
+ var DirsConfig = class {
1234
+ static {
1235
+ __name(this, "DirsConfig");
1236
+ }
1237
+ components;
1238
+ systems;
1239
+ };
1240
+ __decorateClass([
1241
+ Expose(),
1242
+ IsString(),
1243
+ IsNotEmpty()
1244
+ ], DirsConfig.prototype, "components", 2);
1245
+ __decorateClass([
1246
+ Expose(),
1247
+ IsString(),
1248
+ IsNotEmpty()
1249
+ ], DirsConfig.prototype, "systems", 2);
1038
1250
  var ClientConfig = class {
1039
1251
  static {
1040
1252
  __name(this, "ClientConfig");
1041
1253
  }
1254
+ enable;
1042
1255
  port;
1256
+ outDir;
1043
1257
  build;
1044
- runtime;
1258
+ editor;
1259
+ dirs;
1045
1260
  };
1261
+ __decorateClass([
1262
+ Expose(),
1263
+ IsBoolean()
1264
+ ], ClientConfig.prototype, "enable", 2);
1046
1265
  __decorateClass([
1047
1266
  Expose(),
1048
1267
  IsPort()
1049
1268
  ], ClientConfig.prototype, "port", 2);
1269
+ __decorateClass([
1270
+ Expose(),
1271
+ IsString(),
1272
+ IsNotEmpty()
1273
+ ], ClientConfig.prototype, "outDir", 2);
1050
1274
  __decorateClass([
1051
1275
  Expose(),
1052
1276
  Type(() => BuildConfig),
@@ -1054,21 +1278,33 @@ __decorateClass([
1054
1278
  ], ClientConfig.prototype, "build", 2);
1055
1279
  __decorateClass([
1056
1280
  Expose(),
1057
- Type(() => RunConfig),
1281
+ Type(() => EditorConfig),
1282
+ ValidateNested()
1283
+ ], ClientConfig.prototype, "editor", 2);
1284
+ __decorateClass([
1285
+ Expose(),
1286
+ Type(() => DirsConfig),
1058
1287
  ValidateNested()
1059
- ], ClientConfig.prototype, "runtime", 2);
1288
+ ], ClientConfig.prototype, "dirs", 2);
1060
1289
  var ServerConfig = class {
1061
1290
  static {
1062
1291
  __name(this, "ServerConfig");
1063
1292
  }
1064
1293
  enable;
1294
+ outDir;
1065
1295
  build;
1066
- runtime;
1296
+ editor;
1297
+ dirs;
1067
1298
  };
1068
1299
  __decorateClass([
1069
1300
  Expose(),
1070
1301
  IsBoolean()
1071
1302
  ], ServerConfig.prototype, "enable", 2);
1303
+ __decorateClass([
1304
+ Expose(),
1305
+ IsString(),
1306
+ IsNotEmpty()
1307
+ ], ServerConfig.prototype, "outDir", 2);
1072
1308
  __decorateClass([
1073
1309
  Expose(),
1074
1310
  Type(() => BuildConfig),
@@ -1076,9 +1312,14 @@ __decorateClass([
1076
1312
  ], ServerConfig.prototype, "build", 2);
1077
1313
  __decorateClass([
1078
1314
  Expose(),
1079
- Type(() => RunConfig),
1315
+ Type(() => EditorConfig),
1316
+ ValidateNested()
1317
+ ], ServerConfig.prototype, "editor", 2);
1318
+ __decorateClass([
1319
+ Expose(),
1320
+ Type(() => DirsConfig),
1080
1321
  ValidateNested()
1081
- ], ServerConfig.prototype, "runtime", 2);
1322
+ ], ServerConfig.prototype, "dirs", 2);
1082
1323
  var Config = class {
1083
1324
  static {
1084
1325
  __name(this, "Config");
@@ -1117,13 +1358,12 @@ __decorateClass([
1117
1358
  import { plainToInstance } from "class-transformer";
1118
1359
  import { validate } from "class-validator";
1119
1360
  import { existsSync as existsSync2, readFileSync } from "fs";
1120
- import { join as join3 } from "path";
1361
+ import { join as join4 } from "path";
1121
1362
 
1122
1363
  // src/lib/constants.ts
1123
1364
  var CONFIG_FILE_NAME = "nanoforge.config.json";
1124
1365
  var MANIFEST_FILE_NAME = "nanoforge.manifest.json";
1125
1366
  var GLOBAL_CONFIG_FILE_NAME = ".nanoforgerc";
1126
- var NANOFORGE_DIR = ".nanoforge";
1127
1367
  var REGISTRY_URL = "https://api.nanoforge.dev";
1128
1368
 
1129
1369
  // src/lib/utils/object.ts
@@ -1153,23 +1393,36 @@ var CONFIG_DEFAULTS = {
1153
1393
  language: "ts",
1154
1394
  initFunctions: true,
1155
1395
  client: {
1396
+ enable: true,
1156
1397
  port: "3000",
1398
+ outDir: ".nanoforge/client",
1157
1399
  build: {
1158
- entryFile: "client/main.ts",
1159
- outDir: ".nanoforge/client"
1400
+ entry: "client/main.ts",
1401
+ staticDir: "client/static"
1402
+ },
1403
+ editor: {
1404
+ entry: ".nanoforge/editor/client/main.ts",
1405
+ save: ".nanoforge/client.save.json"
1160
1406
  },
1161
- runtime: {
1162
- dir: ".nanoforge/client"
1407
+ dirs: {
1408
+ components: "client/components",
1409
+ systems: "client/systems"
1163
1410
  }
1164
1411
  },
1165
1412
  server: {
1166
1413
  enable: false,
1414
+ outDir: ".nanoforge/server",
1167
1415
  build: {
1168
- entryFile: "server/main.ts",
1169
- outDir: ".nanoforge/server"
1416
+ entry: "server/main.ts",
1417
+ staticDir: "server/static"
1418
+ },
1419
+ editor: {
1420
+ entry: ".nanoforge/editor/server/main.ts",
1421
+ save: ".nanoforge/server.save.json"
1170
1422
  },
1171
- runtime: {
1172
- dir: ".nanoforge/server"
1423
+ dirs: {
1424
+ components: "server/components",
1425
+ systems: "server/systems"
1173
1426
  }
1174
1427
  }
1175
1428
  };
@@ -1178,23 +1431,23 @@ var CONFIG_DEFAULTS = {
1178
1431
  var config;
1179
1432
  var getConfigPath = /* @__PURE__ */ __name((directory, name) => {
1180
1433
  if (name) {
1181
- return join3(directory, name);
1434
+ return join4(directory, name);
1182
1435
  } else {
1183
1436
  for (const n of [CONFIG_FILE_NAME]) {
1184
- const path = join3(directory, n);
1437
+ const path = join4(directory, n);
1185
1438
  if (existsSync2(path)) return path;
1186
1439
  }
1187
1440
  throw new Error(`No config file found in directory: ${directory}`);
1188
1441
  }
1189
1442
  }, "getConfigPath");
1190
- var loadConfig = /* @__PURE__ */ __name(async (directory, name) => {
1443
+ var loadConfig = /* @__PURE__ */ __name(async (directory, name, noThrow = false) => {
1191
1444
  if (config) return config;
1192
1445
  let rawData;
1193
1446
  const path = getConfigPath(directory, name);
1194
1447
  try {
1195
1448
  rawData = deepMerge(CONFIG_DEFAULTS, JSON.parse(readFileSync(path, "utf-8")));
1196
1449
  } catch {
1197
- rawData = null;
1450
+ rawData = noThrow ? CONFIG_DEFAULTS : null;
1198
1451
  }
1199
1452
  if (!rawData) throw new Error(`Not able to read config file : ${path}`);
1200
1453
  const data = plainToInstance(Config, rawData, {
@@ -1209,8 +1462,8 @@ ${errors.toString().replace(/,/g, "\n")}`);
1209
1462
  }, "loadConfig");
1210
1463
 
1211
1464
  // src/action/common/config.ts
1212
- var getConfig = /* @__PURE__ */ __name((inputs, dir) => {
1213
- return loadConfig(dir, getConfigInput(inputs));
1465
+ var getConfig = /* @__PURE__ */ __name((inputs, dir, noThrow) => {
1466
+ return loadConfig(dir, getConfigInput(inputs), noThrow);
1214
1467
  }, "getConfig");
1215
1468
 
1216
1469
  // src/action/abstract.action.ts
@@ -1257,40 +1510,53 @@ var BuildAction = class extends AbstractAction {
1257
1510
  async handle(_args, options) {
1258
1511
  const directory = getDirectoryInput(options);
1259
1512
  const config2 = await getConfig(options, directory);
1513
+ const isEditor = getEditorInput(options);
1260
1514
  const isWatch = getWatchInput(options);
1261
- const targets = this.resolveTargets(config2, options);
1515
+ const targets = this.resolveTargets(config2, options, isEditor);
1262
1516
  const results = await this.buildAll(targets, directory, isWatch);
1263
1517
  if (isWatch) {
1264
1518
  return this.enterWatchMode();
1265
1519
  }
1266
1520
  return { success: results.every(Boolean) };
1267
1521
  }
1268
- resolveTargets(config2, options) {
1269
- const targets = [
1270
- this.createTarget(
1271
- "Client",
1272
- config2.client.build,
1273
- "browser",
1274
- getStringInput(options, "clientDirectory")
1275
- )
1276
- ];
1277
- if (config2.server.enable) {
1522
+ resolveTargets(config2, options, isEditor) {
1523
+ const targets = [];
1524
+ if (config2.client.enable)
1525
+ targets.push(
1526
+ this.createTarget(
1527
+ "Client",
1528
+ "browser",
1529
+ getStringInputWithDefault(
1530
+ options,
1531
+ "clientEntry",
1532
+ !isEditor ? config2.client.build.entry : config2.client.editor.entry
1533
+ ),
1534
+ getStringInputWithDefault(options, "clientStaticDir", config2.client.build.staticDir),
1535
+ getStringInputWithDefault(options, "clientOutDir", config2.client.outDir)
1536
+ )
1537
+ );
1538
+ if (config2.server.enable)
1278
1539
  targets.push(
1279
1540
  this.createTarget(
1280
1541
  "Server",
1281
- config2.server.build,
1282
1542
  "node",
1283
- getStringInput(options, "serverDirectory")
1543
+ getStringInputWithDefault(
1544
+ options,
1545
+ "serverEntry",
1546
+ !isEditor ? config2.server.build.entry : config2.server.editor.entry
1547
+ ),
1548
+ getStringInputWithDefault(options, "serverStaticDir", config2.server.build.staticDir),
1549
+ getStringInputWithDefault(options, "serverOutDir", config2.server.outDir)
1284
1550
  )
1285
1551
  );
1286
- }
1287
1552
  return targets;
1288
1553
  }
1289
- createTarget(name, config2, platform, outDirOverride) {
1554
+ createTarget(name, platform, entryFile, staticDir, outDir) {
1290
1555
  return {
1291
1556
  name,
1292
- entry: config2.entryFile,
1293
- output: outDirOverride || config2.outDir,
1557
+ entry: entryFile,
1558
+ static: staticDir,
1559
+ output: outDir,
1294
1560
  platform
1295
1561
  };
1296
1562
  }
@@ -1304,17 +1570,18 @@ var BuildAction = class extends AbstractAction {
1304
1570
  }
1305
1571
  async buildTarget(target, directory, isWatch) {
1306
1572
  const packageManager = PackageManagerFactory.create("local_bun" /* LOCAL_BUN */);
1307
- const executeBuild = /* @__PURE__ */ __name((rebuild = false) => runSafe(
1308
- () => packageManager.build(
1573
+ const executeBuild = /* @__PURE__ */ __name((rebuild = false) => runSafe(() => {
1574
+ this.resetOut(target.output, directory);
1575
+ this.copyFiles(target, directory);
1576
+ return packageManager.build(
1309
1577
  target.name,
1310
1578
  directory,
1311
1579
  target.entry,
1312
1580
  target.output,
1313
1581
  ["--asset-naming", "[name].[ext]", "--target", target.platform],
1314
1582
  rebuild
1315
- ),
1316
- false
1317
- ), "executeBuild");
1583
+ );
1584
+ }, false), "executeBuild");
1318
1585
  if (isWatch) {
1319
1586
  this.watchDirectory(directory, target.entry, () => executeBuild(true));
1320
1587
  }
@@ -1322,7 +1589,7 @@ var BuildAction = class extends AbstractAction {
1322
1589
  return result !== false;
1323
1590
  }
1324
1591
  watchDirectory(directory, entry, onChange) {
1325
- const watchPath = dirname(join4(getCwd(directory), entry));
1592
+ const watchPath = dirname(join5(getCwd(directory), entry));
1326
1593
  watch(watchPath).on("change", onChange);
1327
1594
  }
1328
1595
  enterWatchMode() {
@@ -1331,43 +1598,16 @@ var BuildAction = class extends AbstractAction {
1331
1598
  console.info();
1332
1599
  return { keepAlive: true };
1333
1600
  }
1334
- };
1335
-
1336
- // src/action/actions/dev.action.ts
1337
- var DevAction = class extends AbstractAction {
1338
- static {
1339
- __name(this, "DevAction");
1601
+ resetOut(outDir, directory) {
1602
+ resetFolder(getCwd(join5(directory, outDir)));
1340
1603
  }
1341
- startMessage = Messages.DEV_START;
1342
- successMessage = Messages.DEV_SUCCESS;
1343
- failureMessage = Messages.DEV_FAILED;
1344
- async handle(_args, options) {
1345
- const directory = getDirectoryInput(options);
1346
- const generate = getDevGenerateInput(options);
1347
- const tasks = this.buildTaskList(directory, generate);
1348
- await Promise.all(tasks);
1349
- return { keepAlive: true };
1350
- }
1351
- buildTaskList(directory, generate) {
1352
- const tasks = [];
1353
- if (generate) {
1354
- tasks.push(this.runSubCommand("generate", directory, { silent: true }));
1355
- }
1356
- tasks.push(this.runSubCommand("build", directory, { silent: true }));
1357
- tasks.push(this.runSubCommand("start", directory, { silent: false }));
1358
- return tasks;
1359
- }
1360
- async runSubCommand(command, directory, options) {
1361
- await runSafe(async () => {
1362
- const packageManager = await PackageManagerFactory.find(directory);
1363
- await packageManager.runDev(directory, "nf", {}, [command, "--watch"], options.silent);
1364
- });
1604
+ copyFiles(target, directory) {
1605
+ const from = getCwd(join5(directory, target.static));
1606
+ const to = getCwd(join5(directory, target.output));
1607
+ copyFiles(from, to);
1365
1608
  }
1366
1609
  };
1367
1610
 
1368
- // src/action/actions/generate.action.ts
1369
- import { join as join5 } from "path";
1370
-
1371
1611
  // src/lib/schematics/abstract.collection.ts
1372
1612
  var AbstractCollection = class {
1373
1613
  constructor(collection, runner, cwd2) {
@@ -1375,6 +1615,9 @@ var AbstractCollection = class {
1375
1615
  this.runner = runner;
1376
1616
  this.cwd = cwd2;
1377
1617
  }
1618
+ collection;
1619
+ runner;
1620
+ cwd;
1378
1621
  static {
1379
1622
  __name(this, "AbstractCollection");
1380
1623
  }
@@ -1424,6 +1667,16 @@ var NanoforgeCollection = class _NanoforgeCollection extends AbstractCollection
1424
1667
  name: "docker",
1425
1668
  alias: "docker",
1426
1669
  description: "Generate a Dockerfile for the application"
1670
+ },
1671
+ {
1672
+ name: "component",
1673
+ alias: "component",
1674
+ description: "Generate a Component for an application"
1675
+ },
1676
+ {
1677
+ name: "system",
1678
+ alias: "system",
1679
+ description: "Generate a System for an application"
1427
1680
  }
1428
1681
  ];
1429
1682
  constructor(runner, cwd2) {
@@ -1467,6 +1720,7 @@ var CollectionFactory = class {
1467
1720
  var toKebabCase = /* @__PURE__ */ __name((str) => {
1468
1721
  return str.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
1469
1722
  }, "toKebabCase");
1723
+ var capitalize = /* @__PURE__ */ __name((str) => str.charAt(0).toUpperCase() + str.slice(1), "capitalize");
1470
1724
 
1471
1725
  // src/lib/schematics/schematic.option.ts
1472
1726
  var SchematicOption = class {
@@ -1474,6 +1728,8 @@ var SchematicOption = class {
1474
1728
  this.name = name;
1475
1729
  this.value = value;
1476
1730
  }
1731
+ name;
1732
+ value;
1477
1733
  static {
1478
1734
  __name(this, "SchematicOption");
1479
1735
  }
@@ -1544,7 +1800,108 @@ var mapSchematicOptions = /* @__PURE__ */ __name((inputs) => {
1544
1800
  }, []);
1545
1801
  }, "mapSchematicOptions");
1546
1802
 
1803
+ // src/action/actions/create.action.ts
1804
+ var CreateAction = class extends AbstractAction {
1805
+ static {
1806
+ __name(this, "CreateAction");
1807
+ }
1808
+ startMessage = Messages.CREATE_START;
1809
+ successMessage = Messages.CREATE_SUCCESS;
1810
+ failureMessage = Messages.CREATE_FAILED;
1811
+ async handle(args, options) {
1812
+ const directory = getDirectoryInput(options);
1813
+ const config2 = await getConfig(options, directory, true);
1814
+ const type = getCreateTypeInput(args);
1815
+ const name = await getCreateNameInputOrAsk(options);
1816
+ const isServer = getServerInput(options);
1817
+ const path = getPathInputWithDefault(
1818
+ options,
1819
+ config2[isServer ? "server" : "client"].dirs[type === "component" ? "components" : "systems"]
1820
+ );
1821
+ await this.generateElement(directory, type, {
1822
+ name,
1823
+ directory: path,
1824
+ part: isServer ? "server" : "client",
1825
+ language: config2.language
1826
+ });
1827
+ return {};
1828
+ }
1829
+ async generateElement(directory, type, values) {
1830
+ const collection = CollectionFactory.create("@nanoforge-dev/schematics" /* NANOFORGE */, directory);
1831
+ await executeSchematic(capitalize(type), collection, type, values);
1832
+ }
1833
+ };
1834
+
1835
+ // src/action/actions/dev.action.ts
1836
+ var DevAction = class extends AbstractAction {
1837
+ static {
1838
+ __name(this, "DevAction");
1839
+ }
1840
+ startMessage = Messages.DEV_START;
1841
+ successMessage = Messages.DEV_SUCCESS;
1842
+ failureMessage = Messages.DEV_FAILED;
1843
+ async handle(_args, options) {
1844
+ const directory = getDirectoryInput(options);
1845
+ const generate = getDevGenerateInput(options);
1846
+ const tasks = this.buildTaskList(directory, generate);
1847
+ await Promise.all(tasks);
1848
+ return { keepAlive: true };
1849
+ }
1850
+ buildTaskList(directory, generate) {
1851
+ const tasks = [];
1852
+ if (generate) {
1853
+ tasks.push(this.runSubCommand("generate", directory, { silent: true }));
1854
+ }
1855
+ tasks.push(this.runSubCommand("build", directory, { silent: true }));
1856
+ tasks.push(this.runSubCommand("start", directory, { silent: false }));
1857
+ return tasks;
1858
+ }
1859
+ async runSubCommand(command, directory, options) {
1860
+ await runSafe(async () => {
1861
+ const packageManager = await PackageManagerFactory.find(directory);
1862
+ await packageManager.runDev(directory, "nf", {}, [command, "--watch"], options.silent);
1863
+ });
1864
+ }
1865
+ };
1866
+
1867
+ // src/action/actions/editor.action.ts
1868
+ import open from "open";
1869
+ import { join as join6 } from "path";
1870
+ var EditorAction = class extends AbstractAction {
1871
+ static {
1872
+ __name(this, "EditorAction");
1873
+ }
1874
+ startMessage = Messages.EDITOR_START;
1875
+ successMessage = Messages.EDITOR_SUCCESS;
1876
+ failureMessage = Messages.EDITOR_FAILED;
1877
+ async handle(args, options) {
1878
+ const directory = getDirectoryInput(options);
1879
+ const path = getPathInput(args);
1880
+ const open2 = getEditorOpenInput(options, !!path);
1881
+ void this.startEditor(directory);
1882
+ if (open2) await this.openEditor(path);
1883
+ return { keepAlive: true };
1884
+ }
1885
+ async startEditor(directory) {
1886
+ const editorPath = join6(
1887
+ getModulePath("@nanoforge-dev/editor/package.json", true),
1888
+ "dist",
1889
+ "index.js"
1890
+ );
1891
+ await runSafe(async () => {
1892
+ const packageManager = PackageManagerFactory.create("local_bun" /* LOCAL_BUN */);
1893
+ await packageManager.run("Editor", directory, editorPath, [], {}, [], true);
1894
+ });
1895
+ }
1896
+ async openEditor(path) {
1897
+ const query = path ? `?projectPath=${encodeURIComponent(path)}` : "";
1898
+ const url = `http://localhost:3000/load-project${query}`;
1899
+ await open(url);
1900
+ }
1901
+ };
1902
+
1547
1903
  // src/action/actions/generate.action.ts
1904
+ import { join as join7 } from "path";
1548
1905
  var GenerateAction = class extends AbstractAction {
1549
1906
  static {
1550
1907
  __name(this, "GenerateAction");
@@ -1555,9 +1912,9 @@ var GenerateAction = class extends AbstractAction {
1555
1912
  async handle(_args, options) {
1556
1913
  const directory = getDirectoryInput(options);
1557
1914
  const config2 = await getConfig(options, directory);
1915
+ const isEditor = getEditorInput(options);
1558
1916
  const isWatch = getWatchInput(options);
1559
- const values = this.extractValues(config2);
1560
- await this.generateParts(values, directory, isWatch);
1917
+ await this.generateParts(config2, directory, isEditor, isWatch);
1561
1918
  if (isWatch) {
1562
1919
  return this.enterWatchMode();
1563
1920
  }
@@ -1565,43 +1922,45 @@ var GenerateAction = class extends AbstractAction {
1565
1922
  }
1566
1923
  extractValues(config2) {
1567
1924
  return {
1568
- name: config2.name,
1569
1925
  directory: ".",
1570
1926
  language: config2.language,
1571
- server: config2.server.enable,
1572
1927
  initFunctions: config2.initFunctions
1573
1928
  };
1574
1929
  }
1575
- async generateParts(values, directory, watch3) {
1930
+ async generateParts(config2, directory, isEditor, watch3) {
1576
1931
  const collection = CollectionFactory.create("@nanoforge-dev/schematics" /* NANOFORGE */, directory);
1577
- const baseOptions = this.baseSchematicOptions(values);
1578
- await executeSchematic(
1579
- "Client main file",
1580
- collection,
1581
- "part-main",
1582
- { ...baseOptions, part: "client" },
1583
- watch3 ? this.watchPath(directory, values.directory, "client") : void 0
1584
- );
1585
- if (values.server) {
1932
+ const values = this.extractValues(config2);
1933
+ if (config2.client.enable)
1934
+ await executeSchematic(
1935
+ "Client main file",
1936
+ collection,
1937
+ "part-main",
1938
+ {
1939
+ ...values,
1940
+ part: "client",
1941
+ outFile: !isEditor ? config2.client.build.entry : config2.client.editor.entry,
1942
+ saveFile: config2.client.editor.save,
1943
+ editor: isEditor
1944
+ },
1945
+ watch3 ? this.watchPath(directory, values.directory, config2.client.editor.save) : void 0
1946
+ );
1947
+ if (config2.server.enable)
1586
1948
  await executeSchematic(
1587
1949
  "Server main file",
1588
1950
  collection,
1589
1951
  "part-main",
1590
- { ...baseOptions, part: "server" },
1591
- this.watchPath(directory, values.directory, "server")
1952
+ {
1953
+ ...values,
1954
+ part: "server",
1955
+ outFile: !isEditor ? config2.server.build.entry : config2.server.editor.entry,
1956
+ saveFile: config2.server.editor.save,
1957
+ editor: isEditor
1958
+ },
1959
+ this.watchPath(directory, values.directory, config2.server.editor.save)
1592
1960
  );
1593
- }
1594
- }
1595
- baseSchematicOptions(values) {
1596
- return {
1597
- name: values.name,
1598
- directory: values.directory,
1599
- language: values.language,
1600
- initFunctions: values.initFunctions
1601
- };
1602
1961
  }
1603
- watchPath(directory, subDir, part) {
1604
- return join5(getCwd(directory), subDir, NANOFORGE_DIR, `${part}.save.json`);
1962
+ watchPath(directory, subDir, saveFile) {
1963
+ return join7(getCwd(directory), subDir, saveFile);
1605
1964
  }
1606
1965
  enterWatchMode() {
1607
1966
  console.info();
@@ -1612,7 +1971,7 @@ var GenerateAction = class extends AbstractAction {
1612
1971
  };
1613
1972
 
1614
1973
  // src/action/actions/install.action.ts
1615
- import { join as join7 } from "path";
1974
+ import { join as join9 } from "path";
1616
1975
 
1617
1976
  // src/lib/global-config/global-config-handler.ts
1618
1977
  import { read, readUser, write, writeUser } from "rc9";
@@ -1855,8 +2214,8 @@ var concatDeps = /* @__PURE__ */ __name((deps) => {
1855
2214
  }, "concatDeps");
1856
2215
 
1857
2216
  // src/lib/registry/registry.ts
1858
- import fs3 from "fs";
1859
- import { join as join6 } from "path";
2217
+ import fs4 from "fs";
2218
+ import { join as join8 } from "path";
1860
2219
  var Registry = class {
1861
2220
  static {
1862
2221
  __name(this, "Registry");
@@ -1887,9 +2246,9 @@ var Registry = class {
1887
2246
  }
1888
2247
  static async installPackage(client2, manifest, dir) {
1889
2248
  const file = await client2.getFile(`/registry/${manifest.name}/-/${manifest._file}`);
1890
- const path = join6(dir, this.getTypeSubFolder(manifest.type));
1891
- fs3.mkdirSync(path, { recursive: true });
1892
- fs3.writeFileSync(join6(path, manifest._file), await file.bytes());
2249
+ const path = join8(dir, this.getTypeSubFolder(manifest.type));
2250
+ fs4.mkdirSync(path, { recursive: true });
2251
+ fs4.writeFileSync(join8(path, manifest._file), await file.bytes());
1893
2252
  }
1894
2253
  static getTypeSubFolder(type) {
1895
2254
  if (type === "component") return "components";
@@ -1901,14 +2260,14 @@ var Registry = class {
1901
2260
  return withAuth(config2.apiKey, force, !headers ? {} : void 0);
1902
2261
  }
1903
2262
  static _getPackageFile(filename, dir) {
1904
- const path = join6(getCwd(dir ?? "."), filename);
1905
- if (!fs3.existsSync(path))
2263
+ const path = join8(getCwd(dir ?? "."), filename);
2264
+ if (!fs4.existsSync(path))
1906
2265
  throw new Error(
1907
- "Package not found, please specify path in the nanoforge.manifest.json : `publish.paths.components`!"
2266
+ "Package not found, please specify path in the nanoforge.manifest.json : `publish.paths.package`!"
1908
2267
  );
1909
2268
  try {
1910
- fs3.accessSync(path, fs3.constants.R_OK);
1911
- return fs3.openAsBlob(path);
2269
+ fs4.accessSync(path, fs4.constants.R_OK);
2270
+ return fs4.openAsBlob(path);
1912
2271
  } catch {
1913
2272
  throw new Error("Cannot read package file, please verify your file permissions!");
1914
2273
  }
@@ -1927,7 +2286,7 @@ var InstallAction = class extends AbstractAction {
1927
2286
  const names = await getInstallNamesInputOrAsk(args);
1928
2287
  const directory = getDirectoryInput(options);
1929
2288
  const isLib = getInstallLibInput(options);
1930
- const isServer = getInstallServerInput(options);
2289
+ const isServer = getServerInput(options);
1931
2290
  return isLib ? this._installLibs(directory, names) : this._installNfPackages(directory, names, isServer);
1932
2291
  }
1933
2292
  async _installLibs(directory, names) {
@@ -1936,17 +2295,16 @@ var InstallAction = class extends AbstractAction {
1936
2295
  }
1937
2296
  async _installNfPackages(directory, names, isServer) {
1938
2297
  const deps = await resolveManifestDependencies(names, directory);
2298
+ const part = isServer ? "server" : "client";
1939
2299
  const libSuccess = await this._installLibs(
1940
2300
  directory,
1941
2301
  deps.npm.map(([name, version]) => `${name}@${version}`)
1942
2302
  );
1943
2303
  if (!libSuccess) return { success: false };
1944
- return withSpinner(Messages.INSTALL_PACKAGES_IN_PROGRESS, async () => {
1945
- await Registry.install(
1946
- Object.values(deps.nf),
1947
- join7(directory, isServer ? "server" : "client")
1948
- );
1949
- });
2304
+ return withSpinner(
2305
+ () => Registry.install(Object.values(deps.nf), join9(directory, part)),
2306
+ Messages.INSTALL_PACKAGES_IN_PROGRESS
2307
+ );
1950
2308
  }
1951
2309
  };
1952
2310
 
@@ -1997,7 +2355,82 @@ var LogoutAction = class extends AbstractAction {
1997
2355
  };
1998
2356
 
1999
2357
  // src/action/actions/new.action.ts
2000
- import { join as join8 } from "path";
2358
+ import { join as join10 } from "path";
2359
+
2360
+ // src/lib/git/git-runner.ts
2361
+ var GIT = "git";
2362
+ var GIT_COMMANDS = {
2363
+ init: "init",
2364
+ remote: "remote"
2365
+ };
2366
+ var GitRunner = class {
2367
+ static {
2368
+ __name(this, "GitRunner");
2369
+ }
2370
+ runner;
2371
+ constructor() {
2372
+ this.runner = new Runner(GIT);
2373
+ }
2374
+ async init(directory) {
2375
+ const args = [GIT_COMMANDS.init];
2376
+ const result = await withSpinner(
2377
+ () => this.exec(args, directory),
2378
+ Messages.GIT_INIT_IN_PROGRESS,
2379
+ Messages.GIT_INIT_SUCCEED,
2380
+ Messages.GIT_INIT_FAILED(this.formatFailCommand(args))
2381
+ );
2382
+ return result.success;
2383
+ }
2384
+ async addRemote(directory, remote) {
2385
+ const args = [GIT_COMMANDS.remote, "add", "origin", remote];
2386
+ const result = await withSpinner(
2387
+ () => this.exec(args, directory),
2388
+ Messages.GIT_REMOTE_IN_PROGRESS,
2389
+ Messages.GIT_REMOTE_SUCCEED,
2390
+ Messages.GIT_REMOTE_FAILED(this.formatFailCommand(args))
2391
+ );
2392
+ return result.success;
2393
+ }
2394
+ exec(args, directory, options = {}) {
2395
+ return this.runner.run(args, {
2396
+ collect: options.collect ?? true,
2397
+ cwd: getCwd(directory),
2398
+ env: options.env,
2399
+ listeners: options.listeners,
2400
+ onFail: options.onFail
2401
+ });
2402
+ }
2403
+ formatFailCommand(args) {
2404
+ return this.runner.fullCommand(args);
2405
+ }
2406
+ };
2407
+
2408
+ // src/lib/input/inputs/new/git-remote.input.ts
2409
+ var getGitRemoteInput = /* @__PURE__ */ __name((inputs) => {
2410
+ return getStringInput(inputs, "gitRemote");
2411
+ }, "getGitRemoteInput");
2412
+ var getNewGitRemoteInputOrAsk = /* @__PURE__ */ __name((inputs) => {
2413
+ return getInputOrAsk(
2414
+ getGitRemoteInput(inputs),
2415
+ () => askInput(Messages.NEW_GIT_REMOTE_QUESTION, {
2416
+ required: false,
2417
+ default: ""
2418
+ })
2419
+ );
2420
+ }, "getNewGitRemoteInputOrAsk");
2421
+
2422
+ // src/lib/input/inputs/new/git.input.ts
2423
+ var getNewGitInput = /* @__PURE__ */ __name((inputs) => {
2424
+ return getBooleanInput(inputs, "git");
2425
+ }, "getNewGitInput");
2426
+ var getNewGitOrAsk = /* @__PURE__ */ __name((inputs) => {
2427
+ return getInputOrAsk(
2428
+ getNewGitInput(inputs),
2429
+ () => askConfirm(Messages.NEW_GIT_QUESTION, { default: true })
2430
+ );
2431
+ }, "getNewGitOrAsk");
2432
+
2433
+ // src/action/actions/new.action.ts
2001
2434
  var NewAction = class extends AbstractAction {
2002
2435
  static {
2003
2436
  __name(this, "NewAction");
@@ -2010,18 +2443,16 @@ var NewAction = class extends AbstractAction {
2010
2443
  const values = await this.collectValues(options);
2011
2444
  await this.scaffold(values, cwdDirectory);
2012
2445
  let res = true;
2446
+ const distDir = join10(cwdDirectory, values.directory);
2013
2447
  if (!values.skipInstall) {
2014
- res = await this.installDependencies(
2015
- values.packageManager,
2016
- join8(cwdDirectory, values.directory ?? values.name)
2017
- );
2448
+ res = await this.installDependencies(values.packageManager, distDir);
2018
2449
  }
2450
+ if (values.git) await this.setupGitRepository(values.gitRemote, distDir);
2019
2451
  return { success: res };
2020
2452
  }
2021
2453
  async collectValues(inputs) {
2022
- return {
2454
+ const values = {
2023
2455
  name: await getNewNameInputOrAsk(inputs),
2024
- directory: getNewPathInput(inputs),
2025
2456
  packageManager: await getNewPackageManagerInputOrAsk(inputs),
2026
2457
  language: await getNewLanguageInputOrAsk(inputs),
2027
2458
  strict: await getNewStrictOrAsk(inputs),
@@ -2029,12 +2460,20 @@ var NewAction = class extends AbstractAction {
2029
2460
  initFunctions: getNewInitFunctionsWithDefault(inputs),
2030
2461
  skipInstall: await getNewSkipInstallOrAsk(inputs),
2031
2462
  docker: await getNewDockerOrAsk(inputs),
2032
- lint: getNewLintInput(inputs)
2463
+ lint: getNewLintInput(inputs),
2464
+ editor: getEditorInput(inputs),
2465
+ git: await getNewGitOrAsk(inputs)
2466
+ };
2467
+ return {
2468
+ ...values,
2469
+ directory: getPathInput(inputs) ?? values.name,
2470
+ gitRemote: values.git ? await getNewGitRemoteInputOrAsk(inputs) || null : null
2033
2471
  };
2034
2472
  }
2035
2473
  async scaffold(values, directory) {
2036
2474
  const collection = CollectionFactory.create("@nanoforge-dev/schematics" /* NANOFORGE */, directory);
2037
- console.info(Messages.SCHEMATICS_START);
2475
+ console.info();
2476
+ console.info(Messages.NEW_GENERATION_START);
2038
2477
  console.info();
2039
2478
  await this.generateApplication(collection, values);
2040
2479
  await this.generateConfiguration(collection, values);
@@ -2052,14 +2491,17 @@ var NewAction = class extends AbstractAction {
2052
2491
  language: values.language,
2053
2492
  strict: values.strict,
2054
2493
  server: values.server,
2055
- lint: values.lint
2494
+ lint: values.lint,
2495
+ editor: values.editor
2056
2496
  });
2057
2497
  }
2058
2498
  generateConfiguration(collection, values) {
2059
2499
  return executeSchematic("Configuration", collection, "configuration", {
2060
2500
  name: values.name,
2061
2501
  directory: values.directory,
2062
- server: values.server
2502
+ server: values.server,
2503
+ language: values.language,
2504
+ initFunctions: values.initFunctions
2063
2505
  });
2064
2506
  }
2065
2507
  async generateClientParts(collection, values) {
@@ -2068,7 +2510,9 @@ var NewAction = class extends AbstractAction {
2068
2510
  ...partOptions,
2069
2511
  server: values.server
2070
2512
  });
2071
- await executeSchematic("Client main file", collection, "part-main", partOptions);
2513
+ await executeSchematic("Client main file", collection, "part-main", {
2514
+ ...partOptions
2515
+ });
2072
2516
  }
2073
2517
  async generateServerParts(collection, values) {
2074
2518
  const partOptions = this.partOptions(values, "server");
@@ -2076,18 +2520,18 @@ var NewAction = class extends AbstractAction {
2076
2520
  ...partOptions,
2077
2521
  server: values.server
2078
2522
  });
2079
- await executeSchematic("Server main file", collection, "part-main", partOptions);
2523
+ await executeSchematic("Server main file", collection, "part-main", {
2524
+ ...partOptions
2525
+ });
2080
2526
  }
2081
2527
  async generateDocker(collection, values) {
2082
2528
  await executeSchematic("Docker", collection, "docker", {
2083
- name: values.name,
2084
2529
  directory: values.directory,
2085
2530
  packageManager: values.packageManager
2086
2531
  });
2087
2532
  }
2088
2533
  partOptions(values, part) {
2089
2534
  return {
2090
- name: values.name,
2091
2535
  part,
2092
2536
  directory: values.directory,
2093
2537
  language: values.language,
@@ -2098,6 +2542,13 @@ var NewAction = class extends AbstractAction {
2098
2542
  const packageManager = PackageManagerFactory.create(packageManagerName);
2099
2543
  return await packageManager.install(directory);
2100
2544
  }
2545
+ async setupGitRepository(gitRemote, dir) {
2546
+ const runner = new GitRunner();
2547
+ let res;
2548
+ res = await runner.init(dir);
2549
+ if (res && gitRemote) res = await runner.addRemote(dir, gitRemote);
2550
+ return res;
2551
+ }
2101
2552
  };
2102
2553
 
2103
2554
  // src/lib/manifest/manifest.type.ts
@@ -2147,6 +2598,7 @@ var Manifest = class {
2147
2598
  name;
2148
2599
  type;
2149
2600
  description;
2601
+ tags;
2150
2602
  dependencies;
2151
2603
  publish;
2152
2604
  npmDependencies;
@@ -2168,6 +2620,11 @@ __decorateClass([
2168
2620
  IsString2(),
2169
2621
  IsOptional()
2170
2622
  ], Manifest.prototype, "description", 2);
2623
+ __decorateClass([
2624
+ Expose2(),
2625
+ IsString2({ each: true }),
2626
+ IsOptional()
2627
+ ], Manifest.prototype, "tags", 2);
2171
2628
  __decorateClass([
2172
2629
  Expose2(),
2173
2630
  IsString2({ each: true }),
@@ -2190,10 +2647,10 @@ __decorateClass([
2190
2647
  import { plainToInstance as plainToInstance2 } from "class-transformer";
2191
2648
  import { validate as validate2 } from "class-validator";
2192
2649
  import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
2193
- import { join as join9 } from "path";
2650
+ import { join as join11 } from "path";
2194
2651
  var getManifestPath = /* @__PURE__ */ __name((directory) => {
2195
2652
  for (const n of [MANIFEST_FILE_NAME]) {
2196
- const path = join9(directory, n);
2653
+ const path = join11(directory, n);
2197
2654
  if (existsSync3(path)) return path;
2198
2655
  }
2199
2656
  throw new Error(`No manifest file found in directory: ${directory}`);
@@ -2228,15 +2685,16 @@ var PublishAction = class extends AbstractAction {
2228
2685
  async handle(_args, options) {
2229
2686
  const directory = getDirectoryInput(options);
2230
2687
  const manifest = await loadManifest(directory);
2231
- return withSpinner(Messages.PUBLISH_IN_PROGRESS(manifest.name), async () => {
2232
- await Registry.publish(manifest, directory);
2233
- });
2688
+ return withSpinner(
2689
+ () => Registry.publish(manifest, directory),
2690
+ Messages.PUBLISH_IN_PROGRESS(manifest.name)
2691
+ );
2234
2692
  }
2235
2693
  };
2236
2694
 
2237
2695
  // src/action/actions/start.action.ts
2238
2696
  import dotenv from "dotenv";
2239
- import { join as join10, resolve as resolve3 } from "path";
2697
+ import { join as join12, resolve as resolve3 } from "path";
2240
2698
  var StartAction = class extends AbstractAction {
2241
2699
  static {
2242
2700
  __name(this, "StartAction");
@@ -2247,10 +2705,18 @@ var StartAction = class extends AbstractAction {
2247
2705
  async handle(_args, options) {
2248
2706
  const directory = getDirectoryInput(options);
2249
2707
  const config2 = await getConfig(options, directory);
2708
+ const clientDir = getStringInputWithDefault(options, "clientDir", config2.client.outDir);
2709
+ const serverDir = getStringInputWithDefault(options, "serverDir", config2.server.outDir);
2250
2710
  const watch3 = getWatchInput(options);
2251
2711
  const port = getStringInputWithDefault(options, "port", config2.client.port);
2252
2712
  const ssl = this.resolveSSL(options);
2253
- const tasks = this.buildStartTasks(config2, directory, watch3, port, ssl);
2713
+ const tasks = this.buildStartTasks(config2, directory, {
2714
+ clientDir,
2715
+ serverDir,
2716
+ watch: watch3,
2717
+ port,
2718
+ ssl
2719
+ });
2254
2720
  await Promise.all(tasks);
2255
2721
  return { keepAlive: true };
2256
2722
  }
@@ -2265,13 +2731,16 @@ var StartAction = class extends AbstractAction {
2265
2731
  key
2266
2732
  };
2267
2733
  }
2268
- buildStartTasks(config2, directory, watch3, port, ssl) {
2734
+ buildStartTasks(config2, directory, options) {
2269
2735
  const env2 = this.parseEnv(directory);
2270
2736
  const tasks = [];
2271
- if (config2.server.enable) {
2272
- tasks.push(this.startServer(directory, config2, { watch: watch3 }, env2));
2273
- }
2274
- tasks.push(this.startClient(directory, config2, { watch: watch3, port, ssl }, env2));
2737
+ const { clientDir, serverDir, watch: watch3, port, ssl } = options;
2738
+ if (config2.server.enable)
2739
+ tasks.push(this.startServer(directory, config2, { serverDir, watch: watch3 }, env2));
2740
+ if (config2.client.enable)
2741
+ tasks.push(
2742
+ this.startClient(directory, config2, { clientDir, serverDir, watch: watch3, port, ssl }, env2)
2743
+ );
2275
2744
  return tasks;
2276
2745
  }
2277
2746
  async startClient(directory, config2, options, env2) {
@@ -2286,14 +2755,14 @@ var StartAction = class extends AbstractAction {
2286
2755
  }
2287
2756
  buildClientParams(directory, config2, options) {
2288
2757
  const params = {
2289
- "-d": getCwd(join10(directory, config2.client.runtime.dir)),
2758
+ "-d": getCwd(join12(directory, options.clientDir)),
2290
2759
  "-p": options.port
2291
2760
  };
2292
2761
  if (options.watch) params["--watch"] = true;
2293
2762
  if (options.watch) {
2294
2763
  params["--watch"] = true;
2295
2764
  if (config2.server.enable) {
2296
- params["--watch-server-dir"] = getCwd(join10(directory, config2.server.runtime.dir));
2765
+ params["--watch-server-dir"] = getCwd(join12(directory, options.serverDir));
2297
2766
  }
2298
2767
  }
2299
2768
  if (options.ssl) {
@@ -2302,9 +2771,9 @@ var StartAction = class extends AbstractAction {
2302
2771
  }
2303
2772
  return this.buildParams(params);
2304
2773
  }
2305
- buildServerParams(directory, config2, options) {
2774
+ buildServerParams(directory, _config, options) {
2306
2775
  const params = {
2307
- "-d": getCwd(join10(directory, config2.server.runtime.dir))
2776
+ "-d": getCwd(join12(directory, options.serverDir))
2308
2777
  };
2309
2778
  if (options.watch) params["--watch"] = true;
2310
2779
  return this.buildParams(params);
@@ -2320,7 +2789,7 @@ var StartAction = class extends AbstractAction {
2320
2789
  ...process.env
2321
2790
  };
2322
2791
  dotenv.config({
2323
- path: resolve3(getCwd(join10(dir, ".env"))),
2792
+ path: resolve3(getCwd(join12(dir, ".env"))),
2324
2793
  processEnv: rawEnv
2325
2794
  });
2326
2795
  const baseEnv = Object.entries(rawEnv).filter(
@@ -2354,9 +2823,10 @@ var UnpublishAction = class extends AbstractAction {
2354
2823
  async handle(_args, options) {
2355
2824
  const directory = getDirectoryInput(options);
2356
2825
  const manifest = await loadManifest(directory);
2357
- return withSpinner(Messages.UNPUBLISH_IN_PROGRESS(manifest.name), async () => {
2358
- await Registry.unpublish(manifest, directory);
2359
- });
2826
+ return withSpinner(
2827
+ () => Registry.unpublish(manifest, directory),
2828
+ Messages.UNPUBLISH_IN_PROGRESS(manifest.name)
2829
+ );
2360
2830
  }
2361
2831
  };
2362
2832
 
@@ -2365,6 +2835,7 @@ var AbstractCommand = class {
2365
2835
  constructor(action) {
2366
2836
  this.action = action;
2367
2837
  }
2838
+ action;
2368
2839
  static {
2369
2840
  __name(this, "AbstractCommand");
2370
2841
  }
@@ -2383,12 +2854,17 @@ var BuildCommand = class extends AbstractCommand {
2383
2854
  __name(this, "BuildCommand");
2384
2855
  }
2385
2856
  load(program2) {
2386
- program2.command("build").description("build your game").option("-d, --directory [directory]", "specify the working directory of the command").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) => {
2857
+ program2.command("build").description("build your game").option("-d, --directory <directory>", "specify the working directory of the command").option("-c, --config <config>", "path to the config file", CONFIG_FILE_NAME).option("--client-entry <clientEntry>", "specify the entry file of the client").option("--server-entry <serverEntry>", "specify the entry file of the server").option("--client-static-dir <clientStaticDir>", "specify the static directory of the client").option("--server-static-dir <serverStaticDir>", "specify the static directory of the server").option("--client-out-dir <clientOutDir>", "specify the output directory of the client").option("--server-out-dir <serverOutDir>", "specify the output directory of the server").option("--editor", "specify if the project must build with editor config").option("--watch", "build app in watching mode", false).action(async (rawOptions) => {
2387
2858
  const options = AbstractCommand.mapToInput({
2388
2859
  directory: rawOptions.directory,
2389
2860
  config: rawOptions.config,
2390
- clientDirectory: rawOptions.clientOutDir,
2391
- serverDirectory: rawOptions.serverOutDir,
2861
+ clientEntry: rawOptions.clientEntry,
2862
+ serverEntry: rawOptions.serverEntry,
2863
+ clientStaticDir: rawOptions.clientStaticDir,
2864
+ serverStaticDir: rawOptions.serverStaticDir,
2865
+ clientOutDir: rawOptions.clientOutDir,
2866
+ serverOutDir: rawOptions.serverOutDir,
2867
+ editor: rawOptions.editor,
2392
2868
  watch: rawOptions.watch
2393
2869
  });
2394
2870
  await this.action.run(/* @__PURE__ */ new Map(), options);
@@ -2396,13 +2872,42 @@ var BuildCommand = class extends AbstractCommand {
2396
2872
  }
2397
2873
  };
2398
2874
 
2875
+ // src/command/commands/create.command.ts
2876
+ var CreateCommand = class extends AbstractCommand {
2877
+ static {
2878
+ __name(this, "CreateCommand");
2879
+ }
2880
+ load(program2) {
2881
+ program2.command("create <type>").description("create nanoforge components or systems").option("-d, --directory <directory>", "specify the working directory of the command").option("-c, --config <config>", "path to the config file", CONFIG_FILE_NAME).option("-n, --name <name>", "name of the component/system").option(
2882
+ "-s, --server",
2883
+ "install components/systems on server (default install on client)",
2884
+ false
2885
+ ).option(
2886
+ "-p, --path <path>",
2887
+ "path to the component/system folder (default: <part>/<components|systems>)"
2888
+ ).action(async (type, rawOptions) => {
2889
+ const args = AbstractCommand.mapToInput({
2890
+ type
2891
+ });
2892
+ const options = AbstractCommand.mapToInput({
2893
+ directory: rawOptions.directory,
2894
+ config: rawOptions.config,
2895
+ name: rawOptions.name,
2896
+ server: rawOptions.server,
2897
+ path: rawOptions.path
2898
+ });
2899
+ await this.action.run(args, options);
2900
+ });
2901
+ }
2902
+ };
2903
+
2399
2904
  // src/command/commands/dev.command.ts
2400
2905
  var DevCommand = class extends AbstractCommand {
2401
2906
  static {
2402
2907
  __name(this, "DevCommand");
2403
2908
  }
2404
2909
  load(program2) {
2405
- program2.command("dev").description("run your game in dev mode").option("-d, --directory [directory]", "specify the working directory of the command").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option("--generate", "generate app from config", false).action(async (rawOptions) => {
2910
+ program2.command("dev").description("run your game in dev mode").option("-d, --directory <directory>", "specify the working directory of the command").option("-c, --config <config>", "path to the config file", CONFIG_FILE_NAME).option("--generate", "generate app from config", false).action(async (rawOptions) => {
2406
2911
  const options = AbstractCommand.mapToInput({
2407
2912
  directory: rawOptions.directory,
2408
2913
  config: rawOptions.config,
@@ -2413,16 +2918,39 @@ var DevCommand = class extends AbstractCommand {
2413
2918
  }
2414
2919
  };
2415
2920
 
2921
+ // src/command/commands/editor.command.ts
2922
+ var EditorCommand = class extends AbstractCommand {
2923
+ static {
2924
+ __name(this, "EditorCommand");
2925
+ }
2926
+ load(program2) {
2927
+ program2.command("editor [path]").description("start the editor").option("-d, --directory <directory>", "specify the working directory of the command").option(
2928
+ "--open",
2929
+ "open the editor on the default web browser (default: true if path is specified, false otherwise)"
2930
+ ).option("--no-open", "do not open the editor on the default web browser").action(async (path, rawOptions) => {
2931
+ const args = AbstractCommand.mapToInput({
2932
+ path
2933
+ });
2934
+ const options = AbstractCommand.mapToInput({
2935
+ directory: rawOptions.directory,
2936
+ open: rawOptions.open
2937
+ });
2938
+ await this.action.run(args, options);
2939
+ });
2940
+ }
2941
+ };
2942
+
2416
2943
  // src/command/commands/generate.command.ts
2417
2944
  var GenerateCommand = class extends AbstractCommand {
2418
2945
  static {
2419
2946
  __name(this, "GenerateCommand");
2420
2947
  }
2421
2948
  load(program2) {
2422
- program2.command("generate").description("generate nanoforge files from config").option("-d, --directory [directory]", "specify the working directory of the command").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option("--watch", "generate app in watching mode", false).action(async (rawOptions) => {
2949
+ program2.command("generate").description("generate nanoforge files from config").option("-d, --directory <directory>", "specify the working directory of the command").option("-c, --config <config>", "path to the config file", CONFIG_FILE_NAME).option("--editor", "specify if the project must generate editor main file").option("--watch", "generate app in watching mode", false).action(async (rawOptions) => {
2423
2950
  const options = AbstractCommand.mapToInput({
2424
2951
  directory: rawOptions.directory,
2425
2952
  config: rawOptions.config,
2953
+ editor: rawOptions.editor,
2426
2954
  watch: rawOptions.watch
2427
2955
  });
2428
2956
  await this.action.run(/* @__PURE__ */ new Map(), options);
@@ -2436,7 +2964,7 @@ var InstallCommand = class extends AbstractCommand {
2436
2964
  __name(this, "InstallCommand");
2437
2965
  }
2438
2966
  load(program2) {
2439
- program2.command("install [names...]").alias("add").description("add Nanoforge components and systems to your project").option("-d, --directory [directory]", "specify the working directory of the command").option("-l, --lib", "install library instead of component/system", false).option(
2967
+ program2.command("install [names...]").alias("add").description("add Nanoforge components and systems to your project").option("-d, --directory <directory>", "specify the working directory of the command").option("-l, --lib", "install library instead of component/system", false).option(
2440
2968
  "-s, --server",
2441
2969
  "install components/systems on server (default install on client)",
2442
2970
  false
@@ -2460,7 +2988,7 @@ var LoginCommand = class extends AbstractCommand {
2460
2988
  __name(this, "LoginCommand");
2461
2989
  }
2462
2990
  load(program2) {
2463
- program2.command("login").description("login to Nanoforge registry").option("-d, --directory [directory]", "specify the working directory of the command").option("-l, --local", "login only for the project", false).option("-k, --api-key <key>", "api key for Nanoforge registry").action(async (rawOptions) => {
2991
+ program2.command("login").description("login to Nanoforge registry").option("-d, --directory <directory>", "specify the working directory of the command").option("-l, --local", "login only for the project", false).option("-k, --api-key <key>", "api key for Nanoforge registry").action(async (rawOptions) => {
2464
2992
  const options = AbstractCommand.mapToInput({
2465
2993
  directory: rawOptions.directory,
2466
2994
  local: rawOptions.local,
@@ -2477,7 +3005,7 @@ var LogoutCommand = class extends AbstractCommand {
2477
3005
  __name(this, "LogoutCommand");
2478
3006
  }
2479
3007
  load(program2) {
2480
- program2.command("logout").description("logout from Nanoforge registry").option("-d, --directory [directory]", "specify the working directory of the command").option("-l, --local", "logout only for the project").action(async (rawOptions) => {
3008
+ program2.command("logout").description("logout from Nanoforge registry").option("-d, --directory <directory>", "specify the working directory of the command").option("-l, --local", "logout only for the project").action(async (rawOptions) => {
2481
3009
  const options = AbstractCommand.mapToInput({
2482
3010
  directory: rawOptions.directory,
2483
3011
  local: rawOptions.local
@@ -2493,10 +3021,13 @@ var NewCommand = class extends AbstractCommand {
2493
3021
  __name(this, "NewCommand");
2494
3022
  }
2495
3023
  load(program2) {
2496
- program2.command("new").description("create a new nanoforge project").option("-d, --directory [directory]", "specify the working directory of the command").option("--name [name]", "specify the name of your project").option(
2497
- "--path [path]",
3024
+ program2.command("new").description("create a new nanoforge project").option("-d, --directory <directory>", "specify the working directory of the command").option("--name <name>", "specify the name of your project").option(
3025
+ "--path <path>",
2498
3026
  "specify the relative path where your project will be created (default: name of the project)"
2499
- ).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").option("--no-lint", "do not generate lint files").action(async (rawOptions) => {
3027
+ ).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").option("--no-lint", "do not generate lint files").option("--editor", "do add editor dependencies").option("--git", "generate git repository").option("--no-git", "do not generate git repository").option(
3028
+ "--git-remote <gitRemote>",
3029
+ "setup git remote to git repository (required if --git is used)"
3030
+ ).option("--no-git-remote", "do not setup git remote to git repository").action(async (rawOptions) => {
2500
3031
  const options = AbstractCommand.mapToInput({
2501
3032
  directory: rawOptions.directory,
2502
3033
  name: rawOptions.name,
@@ -2508,7 +3039,10 @@ var NewCommand = class extends AbstractCommand {
2508
3039
  initFunctions: rawOptions.initFunctions,
2509
3040
  skipInstall: rawOptions.skipInstall,
2510
3041
  docker: rawOptions.docker,
2511
- lint: rawOptions.lint
3042
+ lint: rawOptions.lint,
3043
+ editor: rawOptions.editor,
3044
+ git: rawOptions.git,
3045
+ gitRemote: rawOptions.gitRemote || void 0
2512
3046
  });
2513
3047
  await this.action.run(/* @__PURE__ */ new Map(), options);
2514
3048
  });
@@ -2521,7 +3055,7 @@ var PublishCommand = class extends AbstractCommand {
2521
3055
  __name(this, "PublishCommand");
2522
3056
  }
2523
3057
  load(program2) {
2524
- program2.command("publish").description("publish package to Nanoforge registry").option("-d, --directory [directory]", "specify the working directory of the command").action(async (rawOptions) => {
3058
+ program2.command("publish").description("publish package to Nanoforge registry").option("-d, --directory <directory>", "specify the working directory of the command").action(async (rawOptions) => {
2525
3059
  const options = AbstractCommand.mapToInput({
2526
3060
  directory: rawOptions.directory
2527
3061
  });
@@ -2536,11 +3070,13 @@ var StartCommand = class extends AbstractCommand {
2536
3070
  __name(this, "StartCommand");
2537
3071
  }
2538
3072
  load(program2) {
2539
- program2.command("start").description("start your game").option("-d, --directory [directory]", "specify the working directory of the command").option("-c, --config [config]", "path to the config file", CONFIG_FILE_NAME).option("-p, --port [port]", "specify the port of the loader (the website to load the game)").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) => {
3073
+ program2.command("start").description("start your game").option("-d, --directory <directory>", "specify the working directory of the command").option("-c, --config <config>", "path to the config file", CONFIG_FILE_NAME).option("-p, --port <port>", "specify the port of the loader (the website to load the game)").option("--client-dir <clientDirectory>", "specify the directory of the client").option("--server-dir <serverDirectory>", "specify the directory 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) => {
2540
3074
  const options = AbstractCommand.mapToInput({
2541
3075
  directory: rawOptions.directory,
2542
3076
  config: rawOptions.config,
2543
3077
  port: rawOptions.port,
3078
+ clientDir: rawOptions.clientDir,
3079
+ serverDir: rawOptions.serverDir,
2544
3080
  watch: rawOptions.watch,
2545
3081
  cert: rawOptions.cert,
2546
3082
  key: rawOptions.key
@@ -2556,7 +3092,7 @@ var UnpublishCommand = class extends AbstractCommand {
2556
3092
  __name(this, "UnpublishCommand");
2557
3093
  }
2558
3094
  load(program2) {
2559
- program2.command("unpublish").description("unpublish package to Nanoforge registry").option("-d, --directory [directory]", "specify the working directory of the command").action(async (rawOptions) => {
3095
+ program2.command("unpublish").description("unpublish package to Nanoforge registry").option("-d, --directory <directory>", "specify the working directory of the command").action(async (rawOptions) => {
2560
3096
  const options = AbstractCommand.mapToInput({
2561
3097
  directory: rawOptions.directory
2562
3098
  });
@@ -2576,7 +3112,9 @@ var CommandLoader = class {
2576
3112
  new BuildCommand(new BuildAction()).load(program2);
2577
3113
  new StartCommand(new StartAction()).load(program2);
2578
3114
  new DevCommand(new DevAction()).load(program2);
3115
+ new EditorCommand(new EditorAction()).load(program2);
2579
3116
  new GenerateCommand(new GenerateAction()).load(program2);
3117
+ new CreateCommand(new CreateAction()).load(program2);
2580
3118
  new LoginCommand(new LoginAction()).load(program2);
2581
3119
  new LogoutCommand(new LogoutAction()).load(program2);
2582
3120
  new PublishCommand(new PublishAction()).load(program2);
@@ -2586,8 +3124,8 @@ var CommandLoader = class {
2586
3124
  static handleInvalidCommand(program2) {
2587
3125
  program2.on("command:*", () => {
2588
3126
  console.error(`
2589
- ${Prefixes.ERROR} Invalid command: ${red6`%s`}`, program2.args.join(" "));
2590
- console.log(`See ${red6`--help`} for a list of available commands.
3127
+ ${Prefixes.ERROR} Invalid command: ${red7`%s`}`, program2.args.join(" "));
3128
+ console.log(`See ${red7`--help`} for a list of available commands.
2591
3129
  `);
2592
3130
  process.exit(1);
2593
3131
  });
@@ -2596,6 +3134,14 @@ ${Prefixes.ERROR} Invalid command: ${red6`%s`}`, program2.args.join(" "));
2596
3134
 
2597
3135
  // src/bin/nf.ts
2598
3136
  var bootstrap = /* @__PURE__ */ __name(async () => {
3137
+ const signals = ["SIGINT", "SIGTERM", "SIGHUP", "SIGQUIT", "SIGBREAK"];
3138
+ signals.forEach((signal) => {
3139
+ const listener = /* @__PURE__ */ __name(async () => {
3140
+ process.off(signal, listener);
3141
+ await treeKill(process.pid, signal);
3142
+ }, "listener");
3143
+ process.on(signal, listener);
3144
+ });
2599
3145
  program.version(
2600
3146
  (await Promise.resolve().then(() => __toESM(require_package(), 1))).version ?? "unknown",
2601
3147
  "-v, --version",