@mnci/cli 4.51.0 → 4.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -4805,11 +4805,11 @@ function addGoInternalLib(workspaceRoot, name) {
4805
4805
  }
4806
4806
  }
4807
4807
 
4808
- // src/project-scaffolding/esm-conversion.use-case.ts
4808
+ // src/esm-conversion/esm-conversion.use-case.ts
4809
4809
  var import_node_fs19 = require("fs");
4810
4810
  var import_node_path18 = require("path");
4811
4811
 
4812
- // src/project-scaffolding/esm-specifiers.algorithm.ts
4812
+ // src/esm-conversion/esm-specifiers.algorithm.ts
4813
4813
  var RELATIVE_SPECIFIER = /((?:\bfrom|\bimport)\s*)(['"])(\.{1,2}\/[^'"\n]*|\.{1,2})\2/g;
4814
4814
  var HAS_EXTENSION = /\.(?:[cm]?js|json|node)$/;
4815
4815
  function withJsSpecifiers(source, directories = []) {
@@ -4822,7 +4822,7 @@ function withJsSpecifiers(source, directories = []) {
4822
4822
  });
4823
4823
  }
4824
4824
 
4825
- // src/project-scaffolding/esm-conversion.use-case.ts
4825
+ // src/esm-conversion/esm-conversion.use-case.ts
4826
4826
  var SAMPLE_DIRECTORIES = ["./hello"];
4827
4827
  var JEST_ANCHOR = " moduleFileExtensions:";
4828
4828
  var JEST_MAPPER = String.raw` moduleNameMapper: { '^(\\.{1,2}/.*)\\.js$': '$1' },`;
@@ -8360,11 +8360,26 @@ var import_node_child_process2 = require("child_process");
8360
8360
  var import_node_fs32 = require("fs");
8361
8361
  var import_node_path38 = require("path");
8362
8362
 
8363
- // src/workspace-diagnostics/locate-stranded-release-tags.use-case.ts
8363
+ // src/release-tag-lineage/create-baseline-tags.use-case.ts
8364
+ function createBaselineTags(repositoryRoot, stranded) {
8365
+ const result = { created: [], existing: [], failed: [] };
8366
+ for (const entry of stranded) {
8367
+ const present = runCapture("git", ["rev-parse", "--verify", "--quiet", `refs/tags/${entry.newTag}`], repositoryRoot);
8368
+ if (present.status === 0) {
8369
+ result.existing.push(entry.newTag);
8370
+ continue;
8371
+ }
8372
+ const made = runCapture("git", ["tag", entry.newTag, `${entry.oldTag}^{commit}`], repositoryRoot);
8373
+ (made.status === 0 ? result.created : result.failed).push(entry.newTag);
8374
+ }
8375
+ return result;
8376
+ }
8377
+
8378
+ // src/release-tag-lineage/locate-stranded-release-tags.use-case.ts
8364
8379
  var import_node_fs31 = require("fs");
8365
8380
  var import_node_path37 = require("path");
8366
8381
 
8367
- // src/workspace-diagnostics/stranded-release-tags.algorithm.ts
8382
+ // src/release-tag-lineage/stranded-release-tags.algorithm.ts
8368
8383
  function compareVersions2(left, right) {
8369
8384
  const a = left.split(".").map(Number);
8370
8385
  const b = right.split(".").map(Number);
@@ -8389,7 +8404,7 @@ function findStrandedReleaseTags(projects, tags) {
8389
8404
  return stranded;
8390
8405
  }
8391
8406
 
8392
- // src/workspace-diagnostics/locate-stranded-release-tags.use-case.ts
8407
+ // src/release-tag-lineage/locate-stranded-release-tags.use-case.ts
8393
8408
  var MNCI_TAG_PATTERN = "{projectName}@{version}";
8394
8409
  function locateStrandedReleaseTags(workspaceRoot, tagPattern) {
8395
8410
  if (tagPattern !== void 0 && tagPattern !== MNCI_TAG_PATTERN) {
@@ -8403,6 +8418,17 @@ function locateStrandedReleaseTags(workspaceRoot, tagPattern) {
8403
8418
  return findStrandedReleaseTags(projects, listed.stdout.split(/\r?\n/).filter(Boolean));
8404
8419
  }
8405
8420
 
8421
+ // src/workspace-diagnostics/check-release-tags-resolve.use-case.ts
8422
+ function checkReleaseTagsResolve(workspaceRoot, nxJson) {
8423
+ const stranded = locateStrandedReleaseTags(workspaceRoot, nxJson.release?.releaseTag?.pattern);
8424
+ return {
8425
+ check: "release tags resolve under each project's current name",
8426
+ ok: stranded.length === 0,
8427
+ detail: `${stranded.map((entry) => `${entry.project} has only ${entry.oldTag}`).join("; ")} - nx release finds no tag under the new name and would release from the disk version, a downgrade`,
8428
+ remedy: `run \`mnci adopt --tags\` to create the baseline tag for each (${stranded.map((entry) => entry.newTag).join(", ")}) on the commit of its old tag, then push the tags`
8429
+ };
8430
+ }
8431
+
8406
8432
  // src/workspace-diagnostics/check-invariants.use-case.ts
8407
8433
  var SUPPORTED_ESLINT_MAJOR = ESLINT_VERSION.replace(/^\D*/, "").split(".", 1)[0];
8408
8434
  function toPosix2(path) {
@@ -8872,15 +8898,6 @@ function checkLockfileHasNoLocalRegistry(workspaceRoot) {
8872
8898
  remedy: "run `mnci upgrade`, which removes it, then commit package-lock.json"
8873
8899
  };
8874
8900
  }
8875
- function checkReleaseTagsResolve(workspaceRoot, nxJson) {
8876
- const stranded = locateStrandedReleaseTags(workspaceRoot, nxJson.release?.releaseTag?.pattern);
8877
- return {
8878
- check: "release tags resolve under each project's current name",
8879
- ok: stranded.length === 0,
8880
- detail: `${stranded.map((entry) => `${entry.project} has only ${entry.oldTag}`).join("; ")} - nx release finds no tag under the new name and would release from the disk version, a downgrade`,
8881
- remedy: `run \`mnci adopt --tags\` to create the baseline tag for each (${stranded.map((entry) => entry.newTag).join(", ")}) on the commit of its old tag, then push the tags`
8882
- };
8883
- }
8884
8901
  function collectFindings(workspaceRoot) {
8885
8902
  const nxJsonPath = (0, import_node_path38.join)(workspaceRoot, "nx.json");
8886
8903
  if (!fileExists(nxJsonPath)) {
@@ -8946,11 +8963,11 @@ function runDoctor(workspaceRoot, options = {}) {
8946
8963
  process.exitCode = 1;
8947
8964
  }
8948
8965
 
8949
- // src/repository-adoption/adopt-dependencies.use-case.ts
8966
+ // src/dependency-adoption/adopt-dependencies.use-case.ts
8950
8967
  var import_node_fs33 = require("fs");
8951
8968
  var import_node_path39 = require("path");
8952
8969
 
8953
- // src/repository-adoption/clean-working-tree.validator.ts
8970
+ // src/clean-working-tree/clean-working-tree.validator.ts
8954
8971
  function requireCleanWorkingTree(repositoryRoot, capture = runCapture) {
8955
8972
  if (capture("git", ["rev-parse", "--is-inside-work-tree"], repositoryRoot).status !== 0) {
8956
8973
  throw new Error("This directory is not a git repository. Every adoption step is one diff to review, so it needs git.");
@@ -8960,7 +8977,7 @@ function requireCleanWorkingTree(repositoryRoot, capture = runCapture) {
8960
8977
  }
8961
8978
  }
8962
8979
 
8963
- // src/repository-adoption/extract-imports.algorithm.ts
8980
+ // src/dependency-adoption/extract-imports.algorithm.ts
8964
8981
  var import_node_module = require("module");
8965
8982
  var SPECIFIER2 = /(?:\bfrom\s*|\bimport\s*(?:\(\s*)?|\brequire\s*\(\s*)(['"`])([^'"`\n]+)\1/g;
8966
8983
  var BUILTINS = new Set(import_node_module.builtinModules);
@@ -8986,7 +9003,7 @@ function extractImportedPackages(source) {
8986
9003
  return found;
8987
9004
  }
8988
9005
 
8989
- // src/repository-adoption/plan-dependency-moves.algorithm.ts
9006
+ // src/dependency-adoption/plan-dependency-moves.algorithm.ts
8990
9007
  function planDependencyMoves(rootDependencies, usage, declared) {
8991
9008
  const plan = { moves: [], removed: [], keptAtRoot: [], conflicts: [] };
8992
9009
  for (const [name, range] of Object.entries(rootDependencies)) {
@@ -9014,7 +9031,7 @@ function planDependencyMoves(rootDependencies, usage, declared) {
9014
9031
  return plan;
9015
9032
  }
9016
9033
 
9017
- // src/repository-adoption/adopt-dependencies.use-case.ts
9034
+ // src/dependency-adoption/adopt-dependencies.use-case.ts
9018
9035
  var SOURCE_FILE = /\.(?:[cm]?[jt]s|[jt]sx)$/;
9019
9036
  var TEST_FILE = /\.(?:spec|test)\.[^.]+$|[\\/](?:__tests__|tests?|e2e)[\\/]/;
9020
9037
  var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", "out", ".git", ".nx", "tmp"]);
@@ -9107,11 +9124,11 @@ function adoptDependencies(repositoryRoot, dependencies = {}) {
9107
9124
  return { plan, installed };
9108
9125
  }
9109
9126
 
9110
- // src/repository-adoption/adopt-kinds.use-case.ts
9127
+ // src/kind-adoption/adopt-kinds.use-case.ts
9111
9128
  var import_node_fs34 = require("fs");
9112
9129
  var import_node_path40 = require("path");
9113
9130
 
9114
- // src/repository-adoption/infer-project-kind.algorithm.ts
9131
+ // src/kind-adoption/infer-project-kind.algorithm.ts
9115
9132
  var SERVER_FRAMEWORKS = ["express", "koa", "fastify", "@nestjs/core"];
9116
9133
  function declares(manifest, name) {
9117
9134
  return ["dependencies", "devDependencies", "peerDependencies"].some((key) => {
@@ -9207,7 +9224,7 @@ function inferProjectKind(evidence) {
9207
9224
  }
9208
9225
  }
9209
9226
 
9210
- // src/repository-adoption/adopt-kinds.use-case.ts
9227
+ // src/kind-adoption/adopt-kinds.use-case.ts
9211
9228
  var TYPE_TAG = "type:";
9212
9229
  function readEvidence(repositoryRoot, location) {
9213
9230
  const directory = (0, import_node_path40.join)(repositoryRoot, location.dir);
@@ -9483,7 +9500,7 @@ function detectTestRunner(repositoryRoot) {
9483
9500
  function adoptOverlay(repositoryRoot, options, dependencies = {}) {
9484
9501
  requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
9485
9502
  if (!fileExists((0, import_node_path42.join)(repositoryRoot, "nx.json"))) {
9486
- throw new Error("There is no nx.json, so Nx is not set up here. Set it up first (`npx nx@latest init`); adopt does not install Nx yet.");
9503
+ throw new Error("There is no nx.json, so Nx is not set up here. Run `mnci adopt --toolchain` first: it sets Nx up (nx init, then nx add), and the overlay needs that.");
9487
9504
  }
9488
9505
  const persisted = readMnciConfig(repositoryRoot);
9489
9506
  const filled = {
@@ -9494,10 +9511,10 @@ function adoptOverlay(repositoryRoot, options, dependencies = {}) {
9494
9511
  (dependencies.upgrade ?? runUpgrade)(repositoryRoot, filled);
9495
9512
  }
9496
9513
 
9497
- // src/repository-adoption/adopt-toolchain.use-case.ts
9498
- var import_node_path44 = require("path");
9514
+ // src/toolchain-adoption/adopt-toolchain.use-case.ts
9515
+ var import_node_path45 = require("path");
9499
9516
 
9500
- // src/repository-adoption/align-nx-family.algorithm.ts
9517
+ // src/toolchain-adoption/align-nx-family.algorithm.ts
9501
9518
  var VERSION = /^[\^~]?(\d+)\.(\d+)\.(\d+)$/;
9502
9519
  function isNxFamily(name) {
9503
9520
  return name === "nx" || name.startsWith("@nx/");
@@ -9532,25 +9549,56 @@ function alignNxFamily(dependencies, version) {
9532
9549
  return { dependencies: aligned, changed };
9533
9550
  }
9534
9551
 
9535
- // src/repository-adoption/retire-tooling.use-case.ts
9536
- var import_node_fs37 = require("fs");
9552
+ // src/toolchain-adoption/bootstrap-nx.use-case.ts
9537
9553
  var import_node_path43 = require("path");
9554
+ var BASE_PLUGIN = "@nx/js";
9555
+ var TOOL_PLUGINS = [
9556
+ ["eslint", "@nx/eslint"],
9557
+ ["jest", "@nx/jest"],
9558
+ ["vitest", "@nx/vite"]
9559
+ ];
9560
+ function pluginsFor(repositoryRoot) {
9561
+ const manifestPath = (0, import_node_path43.join)(repositoryRoot, "package.json");
9562
+ const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
9563
+ const declared = Object.keys({ ...manifest.dependencies, ...manifest.devDependencies });
9564
+ return [BASE_PLUGIN, ...TOOL_PLUGINS.filter(([tool]) => declared.includes(tool)).map(([, plugin]) => plugin)];
9565
+ }
9566
+ function bootstrapNx(repositoryRoot, nxVersion, dependencies) {
9567
+ if (fileExists((0, import_node_path43.join)(repositoryRoot, "nx.json"))) {
9568
+ return { bootstrapped: false, plugins: [] };
9569
+ }
9570
+ const initialised = dependencies.run("npx", ["--yes", `nx@${nxVersion ?? "latest"}`, "init", "--interactive=false", "--nxCloud=skip", "--plugins=skip"], repositoryRoot);
9571
+ if (initialised !== 0) {
9572
+ throw new Error("`nx init` failed, so Nx could not be set up here. Read its output above; nothing else was changed after it.");
9573
+ }
9574
+ const plugins = pluginsFor(repositoryRoot);
9575
+ for (const plugin of plugins) {
9576
+ if (dependencies.run("npx", ["nx", "add", plugin], repositoryRoot) !== 0) {
9577
+ throw new Error(`\`nx add ${plugin}\` failed. Nx itself is set up (nx.json exists); add the plugin by hand once its error is dealt with.`);
9578
+ }
9579
+ }
9580
+ return { bootstrapped: true, plugins };
9581
+ }
9582
+
9583
+ // src/toolchain-adoption/retire-tooling.use-case.ts
9584
+ var import_node_fs37 = require("fs");
9585
+ var import_node_path44 = require("path");
9538
9586
  var RETIRED_COMMAND = /\b(?:prettier|oxfmt|oxlint)\b/;
9539
9587
  function retireTooling(repositoryRoot) {
9540
9588
  const removed = [];
9541
9589
  for (const file of RETIRED_FORMATTER_FILES) {
9542
- if (!fileExists((0, import_node_path43.join)(repositoryRoot, file))) {
9590
+ if (!fileExists((0, import_node_path44.join)(repositoryRoot, file))) {
9543
9591
  continue;
9544
9592
  }
9545
- (0, import_node_fs37.rmSync)((0, import_node_path43.join)(repositoryRoot, file), { force: true });
9593
+ (0, import_node_fs37.rmSync)((0, import_node_path44.join)(repositoryRoot, file), { force: true });
9546
9594
  removed.push(file);
9547
9595
  }
9548
- const hadRegistry = fileExists((0, import_node_path43.join)(repositoryRoot, ".verdaccio"));
9596
+ const hadRegistry = fileExists((0, import_node_path44.join)(repositoryRoot, ".verdaccio"));
9549
9597
  removeLocalRegistryScaffolding(repositoryRoot);
9550
9598
  if (hadRegistry) {
9551
9599
  removed.push(".verdaccio, its dependency and the local-registry target");
9552
9600
  }
9553
- const manifestPath = (0, import_node_path43.join)(repositoryRoot, "package.json");
9601
+ const manifestPath = (0, import_node_path44.join)(repositoryRoot, "package.json");
9554
9602
  if (!fileExists(manifestPath)) {
9555
9603
  return removed;
9556
9604
  }
@@ -9578,7 +9626,7 @@ function retireTooling(repositoryRoot) {
9578
9626
  return removed;
9579
9627
  }
9580
9628
 
9581
- // src/repository-adoption/adopt-toolchain.use-case.ts
9629
+ // src/toolchain-adoption/adopt-toolchain.use-case.ts
9582
9630
  var MAX_AUDIT_FIX_PASSES = 4;
9583
9631
  function publishedNxVersions(repositoryRoot, capture) {
9584
9632
  const answer = capture("npm", ["view", "nx", "versions", "--json"], repositoryRoot);
@@ -9597,10 +9645,14 @@ function adoptToolchain(repositoryRoot, options = {}, dependencies = {}) {
9597
9645
  console.log(message);
9598
9646
  });
9599
9647
  requireCleanWorkingTree(repositoryRoot, capture);
9600
- const manifestPath = (0, import_node_path44.join)(repositoryRoot, "package.json");
9648
+ const manifestPath = (0, import_node_path45.join)(repositoryRoot, "package.json");
9601
9649
  if (!fileExists(manifestPath)) {
9602
9650
  throw new Error("No package.json at the repository root.");
9603
9651
  }
9652
+ const bootstrap = bootstrapNx(repositoryRoot, options.nxVersion, { run });
9653
+ if (bootstrap.bootstrapped) {
9654
+ log(`Set Nx up (nx init, then nx add ${bootstrap.plugins.join(" ")})`);
9655
+ }
9604
9656
  log("Removing retired tooling");
9605
9657
  const removed = retireTooling(repositoryRoot);
9606
9658
  const manifest = readJson(manifestPath);
@@ -9628,27 +9680,12 @@ function adoptToolchain(repositoryRoot, options = {}, dependencies = {}) {
9628
9680
  run("npm", ["audit", "fix", "--no-fund"], repositoryRoot);
9629
9681
  passes = audit(repositoryRoot) === 0;
9630
9682
  }
9631
- return { removed, aligned: { names, version }, auditFixes, auditPasses: passes };
9632
- }
9633
-
9634
- // src/repository-adoption/create-baseline-tags.use-case.ts
9635
- function createBaselineTags(repositoryRoot, stranded) {
9636
- const result = { created: [], existing: [], failed: [] };
9637
- for (const entry of stranded) {
9638
- const present = runCapture("git", ["rev-parse", "--verify", "--quiet", `refs/tags/${entry.newTag}`], repositoryRoot);
9639
- if (present.status === 0) {
9640
- result.existing.push(entry.newTag);
9641
- continue;
9642
- }
9643
- const made = runCapture("git", ["tag", entry.newTag, `${entry.oldTag}^{commit}`], repositoryRoot);
9644
- (made.status === 0 ? result.created : result.failed).push(entry.newTag);
9645
- }
9646
- return result;
9683
+ return { bootstrapped: bootstrap.plugins, removed, aligned: { names, version }, auditFixes, auditPasses: passes };
9647
9684
  }
9648
9685
 
9649
- // src/repository-adoption/inspect-repository.use-case.ts
9686
+ // src/adoption-report/inspect-repository.use-case.ts
9650
9687
  var import_node_fs38 = require("fs");
9651
- var import_node_path45 = require("path");
9688
+ var import_node_path46 = require("path");
9652
9689
  var LOCKFILES = [
9653
9690
  ["package-lock.json", "npm"],
9654
9691
  ["pnpm-lock.yaml", "pnpm"],
@@ -9669,22 +9706,22 @@ function inspectRepository(repositoryRoot) {
9669
9706
  const isGitRepo = inside.status === 0 && inside.stdout.trim() === "true";
9670
9707
  const dirty = isGitRepo && runCapture("git", ["status", "--porcelain"], repositoryRoot).stdout.trim() !== "";
9671
9708
  const tags = isGitRepo ? runCapture("git", ["tag", "--list"], repositoryRoot).stdout.split(/\r?\n/).filter(Boolean) : [];
9672
- const manifestPath = (0, import_node_path45.join)(repositoryRoot, "package.json");
9709
+ const manifestPath = (0, import_node_path46.join)(repositoryRoot, "package.json");
9673
9710
  const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
9674
- const nxJsonPath = (0, import_node_path45.join)(repositoryRoot, "nx.json");
9711
+ const nxJsonPath = (0, import_node_path46.join)(repositoryRoot, "nx.json");
9675
9712
  const nxJson = fileExists(nxJsonPath) ? readJson(nxJsonPath) : {};
9676
9713
  const ci = [];
9677
9714
  const pipelines = [];
9678
- if ((0, import_node_fs38.existsSync)((0, import_node_path45.join)(repositoryRoot, "azure-pipelines.yml"))) {
9715
+ if ((0, import_node_fs38.existsSync)((0, import_node_path46.join)(repositoryRoot, "azure-pipelines.yml"))) {
9679
9716
  ci.push("azure");
9680
- pipelines.push((0, import_node_path45.join)(repositoryRoot, "azure-pipelines.yml"));
9717
+ pipelines.push((0, import_node_path46.join)(repositoryRoot, "azure-pipelines.yml"));
9681
9718
  }
9682
- const workflows = (0, import_node_path45.join)(repositoryRoot, ".github", "workflows");
9719
+ const workflows = (0, import_node_path46.join)(repositoryRoot, ".github", "workflows");
9683
9720
  if ((0, import_node_fs38.existsSync)(workflows)) {
9684
9721
  const files = (0, import_node_fs38.readdirSync)(workflows).filter((file) => /\.ya?ml$/.test(file));
9685
9722
  if (files.length > 0) {
9686
9723
  ci.push("github");
9687
- pipelines.push(...files.map((file) => (0, import_node_path45.join)(workflows, file)));
9724
+ pipelines.push(...files.map((file) => (0, import_node_path46.join)(workflows, file)));
9688
9725
  }
9689
9726
  }
9690
9727
  const rootFiles = (0, import_node_fs38.readdirSync)(repositoryRoot);
@@ -9704,7 +9741,7 @@ function inspectRepository(repositoryRoot) {
9704
9741
  };
9705
9742
  }
9706
9743
 
9707
- // src/repository-adoption/judge-adoption.policy.ts
9744
+ // src/adoption-report/judge-adoption.policy.ts
9708
9745
  function judgeAdoption(facts) {
9709
9746
  const findings = [];
9710
9747
  const blocker = (detail, step) => {
@@ -9728,7 +9765,7 @@ function judgeAdoption(facts) {
9728
9765
  warning("nx.json already carries an mnci block, so this is an mnci workspace", "use `mnci upgrade`; adopt is for repositories mnci has not touched");
9729
9766
  }
9730
9767
  if (facts.nxVersion === void 0) {
9731
- warning("Nx is not installed", "set Nx up first (`npx nx@latest init`); adopt does not install it yet, and the toolchain and overlay steps need an nx.json");
9768
+ warning("Nx is not installed", "run `mnci adopt --toolchain`, which sets Nx up through nx init and nx add before it does anything else; the overlay step needs the nx.json that makes");
9732
9769
  }
9733
9770
  if (facts.strandedTags.length > 0) {
9734
9771
  warning(
@@ -9749,10 +9786,12 @@ function judgeAdoption(facts) {
9749
9786
  return { ready: ordered.every((finding) => finding.severity !== "blocker"), facts, findings: ordered };
9750
9787
  }
9751
9788
 
9752
- // src/repository-adoption/run-adopt.use-case.ts
9789
+ // src/adoption-report/report-adoption.use-case.ts
9753
9790
  function reportAdoption(repositoryRoot) {
9754
9791
  return judgeAdoption(inspectRepository(repositoryRoot));
9755
9792
  }
9793
+
9794
+ // src/repository-adoption/run-adopt.use-case.ts
9756
9795
  function runAdopt(repositoryRoot, options = {}) {
9757
9796
  const report = reportAdoption(repositoryRoot);
9758
9797
  if (options.dependencies === true) {
@@ -9833,6 +9872,9 @@ function runToolchain(repositoryRoot, options) {
9833
9872
  const result = adoptToolchain(repositoryRoot, { nxVersion: options.nx }, { log: (message) => {
9834
9873
  logger.step(message);
9835
9874
  } });
9875
+ if (result.bootstrapped.length > 0) {
9876
+ logger.info(`Nx was not set up here, so it is now: ${result.bootstrapped.join(", ")} (nx.json and the plugins; review it with \`git diff\`).`);
9877
+ }
9836
9878
  for (const line of result.removed) {
9837
9879
  logger.info(`removed ${line}`);
9838
9880
  }
@@ -9996,13 +10038,13 @@ function runCommands(program, options) {
9996
10038
  }
9997
10039
 
9998
10040
  // src/workspace-info/list-projects.use-case.ts
9999
- var import_node_path46 = require("path");
10041
+ var import_node_path47 = require("path");
10000
10042
  var TYPE_TAG_PREFIX = "type:";
10001
10043
  function listProjects(workspaceRoot) {
10002
10044
  return locateProjects(workspaceRoot).map((location) => {
10003
- const projectJsonPath = (0, import_node_path46.join)(workspaceRoot, location.dir, "project.json");
10045
+ const projectJsonPath = (0, import_node_path47.join)(workspaceRoot, location.dir, "project.json");
10004
10046
  const project = fileExists(projectJsonPath) ? readJson(projectJsonPath) : {};
10005
- const manifestPath = (0, import_node_path46.join)(workspaceRoot, location.dir, "package.json");
10047
+ const manifestPath = (0, import_node_path47.join)(workspaceRoot, location.dir, "package.json");
10006
10048
  const manifestTags = fileExists(manifestPath) ? readJson(manifestPath).nx?.tags ?? [] : [];
10007
10049
  const typeTag = [...project.tags ?? [], ...manifestTags].find((tag) => tag.startsWith(TYPE_TAG_PREFIX));
10008
10050
  return {
@@ -10015,7 +10057,7 @@ function listProjects(workspaceRoot) {
10015
10057
  }).toSorted((a, b) => a.dir.localeCompare(b.dir));
10016
10058
  }
10017
10059
  function runProjects(workspaceRoot, options) {
10018
- if (!fileExists((0, import_node_path46.join)(workspaceRoot, "nx.json"))) {
10060
+ if (!fileExists((0, import_node_path47.join)(workspaceRoot, "nx.json"))) {
10019
10061
  throw new Error("No nx.json found here. Run `projects` from the workspace root.");
10020
10062
  }
10021
10063
  const projects = listProjects(workspaceRoot);
@@ -10029,11 +10071,11 @@ function runProjects(workspaceRoot, options) {
10029
10071
  }
10030
10072
 
10031
10073
  // src/workspace-info/read-workspace-info.use-case.ts
10032
- var import_node_path48 = require("path");
10074
+ var import_node_path49 = require("path");
10033
10075
 
10034
10076
  // src/cli-version/check-for-update.use-case.ts
10035
10077
  var import_node_fs39 = require("fs");
10036
- var import_node_path47 = require("path");
10078
+ var import_node_path48 = require("path");
10037
10079
  var import_cross_spawn4 = __toESM(require("cross-spawn"));
10038
10080
  var PACKAGE_NAME2 = "@mnci/cli";
10039
10081
  var REGISTRY_TIMEOUT_MS = 2e3;
@@ -10092,7 +10134,7 @@ function releaseTriple(version) {
10092
10134
  }
10093
10135
  function readCliVersion(packageDirectory) {
10094
10136
  try {
10095
- const manifestPath = (0, import_node_path47.join)(packageDirectory, "..", "package.json");
10137
+ const manifestPath = (0, import_node_path48.join)(packageDirectory, "..", "package.json");
10096
10138
  const manifest = JSON.parse((0, import_node_fs39.readFileSync)(manifestPath, "utf8"));
10097
10139
  return manifest.version ?? "0.0.0";
10098
10140
  } catch {
@@ -10103,7 +10145,7 @@ function readCliVersion(packageDirectory) {
10103
10145
  // src/workspace-info/read-workspace-info.use-case.ts
10104
10146
  function readWorkspaceInfo(workspaceRoot, cliVersion) {
10105
10147
  const latest = fetchLatestVersion() ?? null;
10106
- const isWorkspace = fileExists((0, import_node_path48.join)(workspaceRoot, "nx.json"));
10148
+ const isWorkspace = fileExists((0, import_node_path49.join)(workspaceRoot, "nx.json"));
10107
10149
  return {
10108
10150
  cli: {
10109
10151
  version: cliVersion,
@@ -10124,7 +10166,7 @@ function runInfo(workspaceRoot, cliVersion, options) {
10124
10166
  }
10125
10167
 
10126
10168
  // src/interactive-wizard/interactive-wizard.use-case.ts
10127
- var import_node_path49 = require("path");
10169
+ var import_node_path50 = require("path");
10128
10170
 
10129
10171
  // src/interactive-wizard/adopt-steps.config.ts
10130
10172
  var ADOPT_GLOBAL_OPTIONS = ["json"];
@@ -10305,7 +10347,7 @@ function wizardMenu(commands, inWorkspace) {
10305
10347
  }
10306
10348
  async function runInteractive(program, prompter = createInquirerPrompter(), workingDirectory = process.cwd()) {
10307
10349
  const commands = describeCommands(program);
10308
- const inWorkspace = fileExists((0, import_node_path49.join)(workingDirectory, "nx.json"));
10350
+ const inWorkspace = fileExists((0, import_node_path50.join)(workingDirectory, "nx.json"));
10309
10351
  const name = await prompter.select("What would you like to do?", wizardMenu(commands, inWorkspace));
10310
10352
  const command = commands.find((candidate) => candidate.name === name);
10311
10353
  const argv = buildArgv(command, await askCommand(command, prompter));
@@ -10317,9 +10359,281 @@ async function runInteractive(program, prompter = createInquirerPrompter(), work
10317
10359
  }
10318
10360
  }
10319
10361
 
10320
- // src/workspace-creation/adopt-directory.use-case.ts
10362
+ // src/workspace-presets/preset-catalog.config.ts
10363
+ var PRESETS = [
10364
+ {
10365
+ id: "web-api",
10366
+ summary: "a React frontend and an Express API that share one internal library: the web asks the API, the API answers with the shared greeting, and `mnci dev web api` starts both",
10367
+ projects: [
10368
+ { kind: "internal-lib", name: "shared", options: {} },
10369
+ { kind: "node-app", name: "api", options: { framework: "express" } },
10370
+ { kind: "react-app", name: "web", options: {} }
10371
+ ]
10372
+ }
10373
+ ];
10374
+ var PRESET_IDS = PRESETS.map((preset) => preset.id);
10375
+
10376
+ // src/workspace-presets/wire-web-api.use-case.ts
10321
10377
  var import_node_fs40 = require("fs");
10322
- var import_node_path50 = require("path");
10378
+ var import_node_path51 = require("path");
10379
+
10380
+ // src/workspace-presets/web-api-wiring.algorithm.ts
10381
+ function apiHandler(shared) {
10382
+ return `import { greet } from '${shared}'
10383
+ import type { Request, Response } from 'express'
10384
+
10385
+ export function helloHandler (request: Request, response: Response): void {
10386
+ const name = typeof request.query.name === 'string' ? request.query.name : 'world'
10387
+ response.send(greet(name))
10388
+ }
10389
+ `;
10390
+ }
10391
+ function apiHandlerSpec(shared) {
10392
+ return String.raw`import { greet } from '${shared}'
10393
+ import type { Request, Response } from 'express'
10394
+ import { helloHandler } from './hello.handler'
10395
+
10396
+ /** A response that keeps what it was sent, so the spec needs no mock library and runs under Jest or Vitest. */
10397
+ function recordingResponse (): { response: Response, sent: unknown[] } {
10398
+ const sent: unknown[] = []
10399
+
10400
+ return { sent, response: { send: (value: unknown) => { sent.push(value) } } as unknown as Response }
10401
+ }
10402
+
10403
+ describe('helloHandler', () => {
10404
+ it('answers with the shared library\'s greeting for the name asked', () => {
10405
+ const { response, sent } = recordingResponse()
10406
+
10407
+ helloHandler({ query: { name: 'web' } } as unknown as Request, response)
10408
+
10409
+ expect(sent).toEqual([greet('web')])
10410
+ })
10411
+
10412
+ it('greets the world when no name is asked', () => {
10413
+ const { response, sent } = recordingResponse()
10414
+
10415
+ helloHandler({ query: {} } as unknown as Request, response)
10416
+
10417
+ expect(sent).toEqual([greet('world')])
10418
+ })
10419
+ })
10420
+ `;
10421
+ }
10422
+ function apiBarrel() {
10423
+ return "export * from './hello.handler'\n";
10424
+ }
10425
+ function webGreetingClient(shared) {
10426
+ return `import type { Greeting } from '${shared}'
10427
+
10428
+ export async function fetchGreeting (name: string, request: typeof fetch = fetch): Promise<Greeting> {
10429
+ const response = await request('/api/greeting?name=' + encodeURIComponent(name))
10430
+
10431
+ return await response.json() as Greeting
10432
+ }
10433
+ `;
10434
+ }
10435
+ function webGreetingClientSpec() {
10436
+ return String.raw`import { fetchGreeting } from './greeting.client'
10437
+
10438
+ describe('fetchGreeting', () => {
10439
+ it('asks the API\'s greeting route for the name and returns the answer', async () => {
10440
+ const requested: string[] = []
10441
+ const answering = (async (input: string) => {
10442
+ requested.push(input)
10443
+
10444
+ return { json: async () => ({ message: 'Hello, web!' }) } as Response
10445
+ }) as unknown as typeof fetch
10446
+
10447
+ expect(await fetchGreeting('web', answering)).toEqual({ message: 'Hello, web!' })
10448
+ expect(requested).toEqual(['/api/greeting?name=web'])
10449
+ })
10450
+ })
10451
+ `;
10452
+ }
10453
+ function webGreetingComponent(shared) {
10454
+ return `import { useEffect, useState } from 'react'
10455
+ import type { Greeting } from '${shared}'
10456
+ import { fetchGreeting } from './greeting.client'
10457
+
10458
+ export interface GreetingViewProps {
10459
+ readonly name: string
10460
+ readonly load?: (name: string) => Promise<Greeting>
10461
+ }
10462
+
10463
+ export function GreetingView ({ name, load = fetchGreeting }: GreetingViewProps) {
10464
+ const [greeting, setGreeting] = useState<Greeting | undefined>()
10465
+ const [failed, setFailed] = useState(false)
10466
+
10467
+ useEffect(() => {
10468
+ void (async () => {
10469
+ try {
10470
+ setGreeting(await load(name))
10471
+ } catch {
10472
+ setFailed(true)
10473
+ }
10474
+ })()
10475
+ }, [name, load])
10476
+
10477
+ if (failed) {
10478
+ return <p>Could not reach the API.</p>
10479
+ }
10480
+
10481
+ return <p>{greeting?.message ?? 'Loading...'}</p>
10482
+ }
10483
+ `;
10484
+ }
10485
+ function webGreetingSpec() {
10486
+ return `import { render } from '@testing-library/react'
10487
+
10488
+ import { GreetingView } from './greeting.component'
10489
+
10490
+ async function answering (name: string): Promise<{ message: string }> {
10491
+ return { message: 'Hello, ' + name + '!' }
10492
+ }
10493
+
10494
+ async function offline (): Promise<never> {
10495
+ throw new Error('offline')
10496
+ }
10497
+
10498
+ describe('GreetingView', () => {
10499
+ it('shows the greeting its loader returns for its name', async () => {
10500
+ const { findByText } = render(<GreetingView name='world' load={answering} />)
10501
+
10502
+ expect(await findByText('Hello, world!')).toBeTruthy()
10503
+ })
10504
+
10505
+ it('says so when the API cannot be reached', async () => {
10506
+ const { findByText } = render(<GreetingView name='world' load={offline} />)
10507
+
10508
+ expect(await findByText('Could not reach the API.')).toBeTruthy()
10509
+ })
10510
+ })
10511
+ `;
10512
+ }
10513
+ function webGreetingBarrel() {
10514
+ return "export * from './greeting.client'\nexport * from './greeting.component'\n";
10515
+ }
10516
+ function webAppSpec() {
10517
+ return `import { render } from '@testing-library/react'
10518
+
10519
+ import App from './app.component'
10520
+
10521
+ describe('App', () => {
10522
+ it('renders, and shows that it is loading the greeting', () => {
10523
+ const { getByText } = render(<App />)
10524
+
10525
+ expect(getByText('Loading...')).toBeTruthy()
10526
+ })
10527
+ })
10528
+ `;
10529
+ }
10530
+ function withApiProxy(config, target) {
10531
+ const start = config.indexOf("server:");
10532
+ const end = start === -1 ? -1 : config.indexOf("},", start);
10533
+ if (end === -1 || config.includes("proxy:")) {
10534
+ return config;
10535
+ }
10536
+ const block = config.slice(start, end).trimEnd();
10537
+ return `${config.slice(0, start)}${block}
10538
+ proxy: { '/api': '${target}' },
10539
+ ${config.slice(end)}`;
10540
+ }
10541
+
10542
+ // src/workspace-presets/wire-web-api.use-case.ts
10543
+ var API_ORIGIN = "http://localhost:3000";
10544
+ function declareDependency(manifestPath, name) {
10545
+ const manifest = readJson(manifestPath);
10546
+ writeFileEnsured(manifestPath, toJson({ ...manifest, dependencies: { ...manifest.dependencies, [name]: "*" } }));
10547
+ }
10548
+ function removeFiles(slice, files) {
10549
+ for (const file of files) {
10550
+ (0, import_node_fs40.rmSync)((0, import_node_path51.join)(slice, file), { force: true });
10551
+ }
10552
+ }
10553
+ function replaceOnce(path, anchor, replacement) {
10554
+ const text = (0, import_node_fs40.readFileSync)(path, "utf8");
10555
+ if (!text.includes(anchor)) {
10556
+ throw new Error(`${path} has no ${anchor}; the generator's output changed, so the preset cannot wire it.`);
10557
+ }
10558
+ writeFileEnsured(path, text.replace(anchor, () => replacement));
10559
+ }
10560
+ function proxyApi(viteConfigPath) {
10561
+ const text = (0, import_node_fs40.readFileSync)(viteConfigPath, "utf8");
10562
+ const proxied = withApiProxy(text, API_ORIGIN);
10563
+ if (proxied === text && !text.includes("proxy:")) {
10564
+ throw new Error(`${viteConfigPath} has no server block to add the /api proxy to; the generator's output changed, so the preset cannot wire it.`);
10565
+ }
10566
+ writeFileEnsured(viteConfigPath, proxied);
10567
+ }
10568
+ function wireWebApi(workspaceRoot, projects) {
10569
+ const sharedManifest = (0, import_node_path51.join)(workspaceRoot, "libs", projects.shared, "package.json");
10570
+ if (!fileExists(sharedManifest)) {
10571
+ throw new Error(`The shared library was not added at libs/${projects.shared}, so there is nothing to wire the API and the frontend to.`);
10572
+ }
10573
+ const shared = readJson(sharedManifest).name;
10574
+ const api = (0, import_node_path51.join)(workspaceRoot, "apps", projects.api);
10575
+ declareDependency((0, import_node_path51.join)(api, "package.json"), shared);
10576
+ const hello = (0, import_node_path51.join)(api, "src", "hello");
10577
+ writeFileEnsured((0, import_node_path51.join)(hello, "hello.handler.ts"), apiHandler(shared));
10578
+ writeFileEnsured((0, import_node_path51.join)(hello, "hello.handler.spec.ts"), apiHandlerSpec(shared));
10579
+ writeFileEnsured((0, import_node_path51.join)(hello, "index.ts"), apiBarrel());
10580
+ removeFiles(hello, ["greet.use-case.ts", "greet.use-case.spec.ts", "greeting.contract.ts"]);
10581
+ replaceOnce((0, import_node_path51.join)(api, "src", "main.ts"), "app.get('/', helloHandler)", "app.get('/api/greeting', helloHandler)");
10582
+ const web = (0, import_node_path51.join)(workspaceRoot, "apps", projects.web);
10583
+ declareDependency((0, import_node_path51.join)(web, "package.json"), shared);
10584
+ const greeting = (0, import_node_path51.join)(web, "src", "greeting");
10585
+ writeFileEnsured((0, import_node_path51.join)(greeting, "greeting.client.ts"), webGreetingClient(shared));
10586
+ writeFileEnsured((0, import_node_path51.join)(greeting, "greeting.client.spec.ts"), webGreetingClientSpec());
10587
+ writeFileEnsured((0, import_node_path51.join)(greeting, "greeting.component.tsx"), webGreetingComponent(shared));
10588
+ writeFileEnsured((0, import_node_path51.join)(greeting, "greeting.component.spec.tsx"), webGreetingSpec());
10589
+ writeFileEnsured((0, import_node_path51.join)(greeting, "index.ts"), webGreetingBarrel());
10590
+ removeFiles(greeting, ["greet.use-case.ts", "greet.use-case.spec.ts", "greeting.contract.ts"]);
10591
+ writeFileEnsured((0, import_node_path51.join)(web, "src", "app", "app.component.spec.tsx"), webAppSpec());
10592
+ proxyApi((0, import_node_path51.join)(web, "vite.config.mts"));
10593
+ }
10594
+
10595
+ // src/workspace-presets/apply-preset.use-case.ts
10596
+ function findPreset(id) {
10597
+ const preset = PRESETS.find((candidate) => candidate.id === id);
10598
+ if (preset === void 0) {
10599
+ throw new Error(`Unknown preset '${id}'. Presets: ${PRESET_IDS.join(", ")}.`);
10600
+ }
10601
+ return preset;
10602
+ }
10603
+ function nameOf(preset, kind) {
10604
+ const project = preset.projects.find((candidate) => candidate.kind === kind);
10605
+ if (project === void 0) {
10606
+ throw new Error(`The ${preset.id} preset has no ${kind}.`);
10607
+ }
10608
+ return project.name;
10609
+ }
10610
+ async function applyPreset(workspaceRoot, presetId, dependencies = {}) {
10611
+ const preset = findPreset(presetId);
10612
+ const add = dependencies.add ?? runAdd;
10613
+ const install = dependencies.install ?? ((root) => runShell("npm", ["install", "--ignore-scripts", "--no-audit", "--no-fund"], root));
10614
+ const sync = dependencies.sync ?? syncProjectReferences;
10615
+ const format = dependencies.format ?? runFormatter;
10616
+ logger.step(`Adding the ${preset.id} preset: ${preset.projects.map((project) => project.name).join(", ")}`);
10617
+ const previous = process.cwd();
10618
+ process.chdir(workspaceRoot);
10619
+ try {
10620
+ for (const project of preset.projects) {
10621
+ await add(project.kind, project.name, project.options);
10622
+ }
10623
+ } finally {
10624
+ process.chdir(previous);
10625
+ }
10626
+ wireWebApi(workspaceRoot, { shared: nameOf(preset, "internal-lib"), api: nameOf(preset, "node-app"), web: nameOf(preset, "react-app") });
10627
+ if (install(workspaceRoot) !== 0) {
10628
+ throw new Error("npm install failed after wiring the preset. Run `npm install` and read its error; the projects themselves were added.");
10629
+ }
10630
+ sync(workspaceRoot);
10631
+ format(workspaceRoot);
10632
+ }
10633
+
10634
+ // src/workspace-creation/adopt-directory.use-case.ts
10635
+ var import_node_fs41 = require("fs");
10636
+ var import_node_path52 = require("path");
10323
10637
  var NEVER_COPIED = /* @__PURE__ */ new Set([".git", "node_modules"]);
10324
10638
  var AGENT_AND_CI_ENTRIES = [
10325
10639
  "AGENTS.md",
@@ -10350,13 +10664,13 @@ var PREFLIGHT_BLOCKERS = [
10350
10664
  "packages"
10351
10665
  ];
10352
10666
  function assertAdoptableDirectory(target) {
10353
- if (!(0, import_node_fs40.existsSync)(target)) {
10667
+ if (!(0, import_node_fs41.existsSync)(target)) {
10354
10668
  throw new Error(`--into '${target}' does not exist. Create or clone it first.`);
10355
10669
  }
10356
- if (!(0, import_node_fs40.statSync)(target).isDirectory()) {
10670
+ if (!(0, import_node_fs41.statSync)(target).isDirectory()) {
10357
10671
  throw new Error(`--into '${target}' is not a directory.`);
10358
10672
  }
10359
- const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs40.existsSync)((0, import_node_path50.join)(target, entry)));
10673
+ const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs41.existsSync)((0, import_node_path52.join)(target, entry)));
10360
10674
  if (blockers.length > 0) {
10361
10675
  throw new Error(
10362
10676
  `--into '${target}' already holds ${blockers.join(", ")}, so it is a project already. Use \`mnci upgrade\` to re-apply the overlay to an existing workspace, or point --into at an empty checkout.`
@@ -10364,15 +10678,15 @@ function assertAdoptableDirectory(target) {
10364
10678
  }
10365
10679
  }
10366
10680
  function mergeGitignore(generatedFile, targetFile) {
10367
- const existing = (0, import_node_fs40.readFileSync)(targetFile, "utf8");
10681
+ const existing = (0, import_node_fs41.readFileSync)(targetFile, "utf8");
10368
10682
  const present = new Set(existing.split("\n").map((line) => line.trim()));
10369
- const additions = (0, import_node_fs40.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
10683
+ const additions = (0, import_node_fs41.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
10370
10684
  const trimmed = line.trim();
10371
10685
  return trimmed !== "" && !trimmed.startsWith("#") && !present.has(trimmed);
10372
10686
  });
10373
10687
  if (additions.length === 0) return false;
10374
10688
  const separator = existing.endsWith("\n") ? "" : "\n";
10375
- (0, import_node_fs40.writeFileSync)(
10689
+ (0, import_node_fs41.writeFileSync)(
10376
10690
  targetFile,
10377
10691
  `${existing}${separator}
10378
10692
  # Added by mnci (Nx workspace)
@@ -10382,9 +10696,9 @@ ${additions.join("\n")}
10382
10696
  return true;
10383
10697
  }
10384
10698
  function adoptGeneratedWorkspace(generatedRoot, target) {
10385
- const entries = (0, import_node_fs40.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
10699
+ const entries = (0, import_node_fs41.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
10386
10700
  const collisions = entries.filter(
10387
- (entry) => (0, import_node_fs40.existsSync)((0, import_node_path50.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
10701
+ (entry) => (0, import_node_fs41.existsSync)((0, import_node_path52.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
10388
10702
  );
10389
10703
  if (collisions.length > 0) {
10390
10704
  throw new Error(
@@ -10393,28 +10707,28 @@ function adoptGeneratedWorkspace(generatedRoot, target) {
10393
10707
  }
10394
10708
  const result = { copied: [], kept: [], mergedGitignore: false };
10395
10709
  for (const entry of entries) {
10396
- const from = (0, import_node_path50.join)(generatedRoot, entry);
10397
- const to = (0, import_node_path50.join)(target, entry);
10398
- if (entry === ".gitignore" && (0, import_node_fs40.existsSync)(to)) {
10710
+ const from = (0, import_node_path52.join)(generatedRoot, entry);
10711
+ const to = (0, import_node_path52.join)(target, entry);
10712
+ if (entry === ".gitignore" && (0, import_node_fs41.existsSync)(to)) {
10399
10713
  result.mergedGitignore = mergeGitignore(from, to);
10400
10714
  result.kept.push(entry);
10401
10715
  continue;
10402
10716
  }
10403
- if ((0, import_node_fs40.existsSync)(to)) {
10717
+ if ((0, import_node_fs41.existsSync)(to)) {
10404
10718
  result.kept.push(entry);
10405
10719
  continue;
10406
10720
  }
10407
- (0, import_node_fs40.cpSync)(from, to, { recursive: true });
10721
+ (0, import_node_fs41.cpSync)(from, to, { recursive: true });
10408
10722
  result.copied.push(entry);
10409
10723
  }
10410
- (0, import_node_fs40.rmSync)(generatedRoot, { recursive: true, force: true });
10724
+ (0, import_node_fs41.rmSync)(generatedRoot, { recursive: true, force: true });
10411
10725
  return result;
10412
10726
  }
10413
10727
 
10414
10728
  // src/workspace-creation/create-workspace.use-case.ts
10415
- var import_node_fs41 = require("fs");
10729
+ var import_node_fs42 = require("fs");
10416
10730
  var import_node_os2 = require("os");
10417
- var import_node_path51 = require("path");
10731
+ var import_node_path53 = require("path");
10418
10732
  function nxCloudProviderValue(ci) {
10419
10733
  return ci === "azure" ? "azure" : "github";
10420
10734
  }
@@ -10457,9 +10771,10 @@ async function resolveRegistry2(options) {
10457
10771
  return await promptRegistry();
10458
10772
  }
10459
10773
  async function runNew(name, options) {
10460
- const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path51.resolve)(process.cwd(), options.into);
10774
+ const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path53.resolve)(process.cwd(), options.into);
10461
10775
  if (adoptTarget !== void 0) assertAdoptableDirectory(adoptTarget);
10462
- const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path51.basename)(adoptTarget));
10776
+ if (options.preset !== void 0) findPreset(options.preset);
10777
+ const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path53.basename)(adoptTarget));
10463
10778
  assertValidProjectName(workspaceName, "Workspace name");
10464
10779
  const scope = options.scope ?? (options.yes ? `@${workspaceName}` : await promptText("npm scope for publishable packages", `@${workspaceName}`));
10465
10780
  const registry = await resolveRegistry2(options);
@@ -10472,8 +10787,8 @@ async function runNew(name, options) {
10472
10787
  const npmAuth = resolveNpmAuth(options.npmAuth, registry, ci);
10473
10788
  const stack = await resolveStack(options);
10474
10789
  const nxCloud = options.nxCloud ?? (options.yes ? false : await promptNxCloud());
10475
- const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs41.mkdtempSync)((0, import_node_path51.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
10476
- let workspaceRoot = (0, import_node_path51.join)(stagingParent, workspaceName);
10790
+ const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs42.mkdtempSync)((0, import_node_path53.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
10791
+ let workspaceRoot = (0, import_node_path53.join)(stagingParent, workspaceName);
10477
10792
  try {
10478
10793
  logger.step(`Creating Nx workspace '${workspaceName}' (preset: ts)`);
10479
10794
  runNpx(
@@ -10496,7 +10811,7 @@ async function runNew(name, options) {
10496
10811
  workspaceRoot = adoptTarget;
10497
10812
  }
10498
10813
  } finally {
10499
- if (adoptTarget !== void 0) (0, import_node_fs41.rmSync)(stagingParent, { recursive: true, force: true });
10814
+ if (adoptTarget !== void 0) (0, import_node_fs42.rmSync)(stagingParent, { recursive: true, force: true });
10500
10815
  }
10501
10816
  logger.step(
10502
10817
  "Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
@@ -10506,8 +10821,8 @@ async function runNew(name, options) {
10506
10821
  { workspaceName, scope, registry, agent, variableGroup, ci, stack, ...npmAuth && { npmAuth } },
10507
10822
  logger.detail
10508
10823
  );
10509
- (0, import_node_fs41.rmSync)((0, import_node_path51.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
10510
- (0, import_node_fs41.rmSync)((0, import_node_path51.join)(workspaceRoot, "package-lock.json"), { force: true });
10824
+ (0, import_node_fs42.rmSync)((0, import_node_path53.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
10825
+ (0, import_node_fs42.rmSync)((0, import_node_path53.join)(workspaceRoot, "package-lock.json"), { force: true });
10511
10826
  logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
10512
10827
  const installStatus = runShell(
10513
10828
  "npm",
@@ -10520,6 +10835,9 @@ async function runNew(name, options) {
10520
10835
  runShell("npx", ["husky"], workspaceRoot);
10521
10836
  logger.step("Formatting the workspace (eslint --fix, JavaScript Standard Style)");
10522
10837
  runFormatter(workspaceRoot);
10838
+ if (options.preset !== void 0) {
10839
+ await applyPreset(workspaceRoot, options.preset);
10840
+ }
10523
10841
  logger.success("Done. Next steps:");
10524
10842
  if (adoptTarget === void 0) logger.info(` cd ${workspaceName}`);
10525
10843
  for (const line of projectKindHintLines()) {
@@ -10557,6 +10875,8 @@ function buildProgram(cliVersion) {
10557
10875
  ).addOption(new import_commander.Option("--ci <provider>", "CI provider").choices(["azure", "github", "both"])).option("--test-runner <runner>", "unit-test runner: jest | vitest").option("--nx-cloud", "connect the workspace to Nx Cloud (remote caching + CI insights)").option(
10558
10876
  "--into <dir>",
10559
10877
  "bootstrap into an existing directory (a fresh clone holding only .git); the directory name is the workspace name unless one is given"
10878
+ ).addOption(
10879
+ new import_commander.Option("--preset <preset>", "also scaffold a whole shape of workspace, its projects wired together (web-api: a React frontend and an Express API sharing one library)").choices(PRESET_IDS)
10560
10880
  ).action(async (name, options) => {
10561
10881
  await runNew(name, options);
10562
10882
  });