@mnci/cli 4.48.0 → 4.50.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
@@ -3187,7 +3187,7 @@ function runPhase(phase, workspaceRoot, dependencies) {
3187
3187
  }
3188
3188
 
3189
3189
  // src/project-scaffolding/add-project.use-case.ts
3190
- var import_node_path33 = require("path");
3190
+ var import_node_path35 = require("path");
3191
3191
  var import_prompts3 = require("@inquirer/prompts");
3192
3192
 
3193
3193
  // src/project-name/project-name.validator.ts
@@ -3372,7 +3372,10 @@ function findCodeWorkspaceFile(workspaceRoot) {
3372
3372
  function projectTask(name, kind) {
3373
3373
  const script = `${name}:${kind}`;
3374
3374
  const base = { label: `${name}: ${kind}`, type: "npm", script, problemMatcher: [] };
3375
- return kind === "start" ? { ...base, isBackground: true } : { ...base, group: kind };
3375
+ if (kind === "start") {
3376
+ return { ...base, isBackground: true };
3377
+ }
3378
+ return kind === "build" || kind === "qa" ? { ...base, group: kind } : base;
3376
3379
  }
3377
3380
  function renameScaffoldPlaceholder(projectRoot, name, empty = false) {
3378
3381
  reshapeScaffoldSlice(projectRoot, name, "ts", "use-case", empty);
@@ -3521,7 +3524,7 @@ function registerProjectCommands(workspaceRoot, name, commands) {
3521
3524
  removeStaleGitkeeps(workspaceRoot);
3522
3525
  ensurePythonArtefactsIgnored(workspaceRoot);
3523
3526
  const scripts = {
3524
- [`${name}:qa`]: `nx run ${name}:lint && nx run ${name}:test`
3527
+ [`${name}:qa`]: commands.qa ?? `nx run ${name}:lint && nx run ${name}:test`
3525
3528
  };
3526
3529
  if (commands.build) {
3527
3530
  scripts[`${name}:build`] = `nx run ${name}:build`;
@@ -3529,6 +3532,10 @@ function registerProjectCommands(workspaceRoot, name, commands) {
3529
3532
  if (commands.start) {
3530
3533
  scripts[`${name}:start`] = commands.start;
3531
3534
  }
3535
+ const extraScripts = Object.entries(commands.extra ?? {});
3536
+ for (const [suffix, command] of extraScripts) {
3537
+ scripts[`${name}:${suffix}`] = command;
3538
+ }
3532
3539
  const dependabotPath = (0, import_node_path11.join)(workspaceRoot, ".github/dependabot.yml");
3533
3540
  if ((0, import_node_fs13.existsSync)(dependabotPath)) {
3534
3541
  writeFileEnsured(dependabotPath, dependabotConfig(workspaceRoot));
@@ -3555,7 +3562,8 @@ function registerProjectCommands(workspaceRoot, name, commands) {
3555
3562
  const newTasks = [
3556
3563
  projectTask(name, "qa"),
3557
3564
  ...commands.build ? [projectTask(name, "build")] : [],
3558
- ...commands.start ? [projectTask(name, "start")] : []
3565
+ ...commands.start ? [projectTask(name, "start")] : [],
3566
+ ...Object.keys(commands.extra ?? {}).map((suffix) => projectTask(name, suffix))
3559
3567
  ];
3560
3568
  writeFileEnsured(
3561
3569
  codeWorkspacePath,
@@ -4797,9 +4805,70 @@ function addGoInternalLib(workspaceRoot, name) {
4797
4805
  }
4798
4806
  }
4799
4807
 
4800
- // src/project-scaffolding/node.use-case.ts
4808
+ // src/project-scaffolding/esm-conversion.use-case.ts
4809
+ var import_node_fs19 = require("fs");
4801
4810
  var import_node_path18 = require("path");
4802
4811
 
4812
+ // src/project-scaffolding/esm-specifiers.algorithm.ts
4813
+ var RELATIVE_SPECIFIER = /((?:\bfrom|\bimport)\s*)(['"])(\.{1,2}\/[^'"\n]*|\.{1,2})\2/g;
4814
+ var HAS_EXTENSION = /\.(?:[cm]?js|json|node)$/;
4815
+ function withJsSpecifiers(source, directories = []) {
4816
+ return source.replaceAll(RELATIVE_SPECIFIER, (_match, lead, quote, specifier) => {
4817
+ if (HAS_EXTENSION.test(specifier)) {
4818
+ return `${lead}${quote}${specifier}${quote}`;
4819
+ }
4820
+ const extended = directories.includes(specifier) ? `${specifier}/index.js` : `${specifier}.js`;
4821
+ return `${lead}${quote}${extended}${quote}`;
4822
+ });
4823
+ }
4824
+
4825
+ // src/project-scaffolding/esm-conversion.use-case.ts
4826
+ var SAMPLE_DIRECTORIES = ["./hello"];
4827
+ var JEST_ANCHOR = " moduleFileExtensions:";
4828
+ var JEST_MAPPER = String.raw` moduleNameMapper: { '^(\\.{1,2}/.*)\\.js$': '$1' },`;
4829
+ function typescriptFiles(directory) {
4830
+ const entries = (0, import_node_fs19.readdirSync)(directory, { withFileTypes: true });
4831
+ return entries.flatMap((entry) => {
4832
+ const path = (0, import_node_path18.join)(directory, entry.name);
4833
+ if (entry.isDirectory()) {
4834
+ return typescriptFiles(path);
4835
+ }
4836
+ return entry.name.endsWith(".ts") ? [path] : [];
4837
+ });
4838
+ }
4839
+ function convertAppToEsm(appRoot) {
4840
+ const manifestPath = (0, import_node_path18.join)(appRoot, "package.json");
4841
+ const manifest = readJson(manifestPath);
4842
+ const build = manifest.nx?.targets?.build;
4843
+ if (build?.options === void 0) {
4844
+ throw new Error(`${manifestPath} has no nx build target to switch to ESM; the generator's output changed.`);
4845
+ }
4846
+ build.options.format = ["esm"];
4847
+ writeFileEnsured(manifestPath, toJson({ ...manifest, type: "module" }));
4848
+ const sources = typescriptFiles((0, import_node_path18.join)(appRoot, "src"));
4849
+ for (const file of sources) {
4850
+ const source = (0, import_node_fs19.readFileSync)(file, "utf8");
4851
+ const converted = withJsSpecifiers(source, SAMPLE_DIRECTORIES);
4852
+ if (converted !== source) {
4853
+ writeFileEnsured(file, converted);
4854
+ }
4855
+ }
4856
+ const jestPath = (0, import_node_path18.join)(appRoot, "jest.config.cts");
4857
+ if (fileExists(jestPath)) {
4858
+ const jest = (0, import_node_fs19.readFileSync)(jestPath, "utf8");
4859
+ if (!jest.includes("moduleNameMapper")) {
4860
+ if (!jest.includes(JEST_ANCHOR)) {
4861
+ throw new Error(`${jestPath} has no moduleFileExtensions line to put the .js mapper above; the generator's output changed.`);
4862
+ }
4863
+ writeFileEnsured(jestPath, jest.replace(JEST_ANCHOR, () => `${JEST_MAPPER}
4864
+ ${JEST_ANCHOR}`));
4865
+ }
4866
+ }
4867
+ }
4868
+
4869
+ // src/project-scaffolding/node.use-case.ts
4870
+ var import_node_path19 = require("path");
4871
+
4803
4872
  // src/project-scaffolding/node-app-example.algorithm.ts
4804
4873
  var GREET_USE_CASE = `import type { Greeting } from './greeting.contract'
4805
4874
 
@@ -4921,12 +4990,12 @@ function runNodeApp(workspaceRoot, name, stack, framework) {
4921
4990
  ],
4922
4991
  workspaceRoot
4923
4992
  );
4924
- const appRoot = (0, import_node_path18.join)(workspaceRoot, "apps", name);
4993
+ const appRoot = (0, import_node_path19.join)(workspaceRoot, "apps", name);
4925
4994
  separateDeclarationOutput(appRoot);
4926
4995
  repairAssetMapping(appRoot, name);
4927
4996
  }
4928
4997
  function separateDeclarationOutput(appRoot) {
4929
- const tsconfigPath = (0, import_node_path18.join)(appRoot, "tsconfig.app.json");
4998
+ const tsconfigPath = (0, import_node_path19.join)(appRoot, "tsconfig.app.json");
4930
4999
  if (!fileExists(tsconfigPath)) {
4931
5000
  return;
4932
5001
  }
@@ -4948,7 +5017,7 @@ function separateDeclarationOutput(appRoot) {
4948
5017
  );
4949
5018
  }
4950
5019
  function repairAssetMapping(appRoot, name) {
4951
- const manifestPath = (0, import_node_path18.join)(appRoot, "package.json");
5020
+ const manifestPath = (0, import_node_path19.join)(appRoot, "package.json");
4952
5021
  if (!fileExists(manifestPath)) {
4953
5022
  return;
4954
5023
  }
@@ -4972,16 +5041,19 @@ function nodeAppPackageTarget(name) {
4972
5041
  options: { command }
4973
5042
  };
4974
5043
  }
4975
- function addNodeApp(workspaceRoot, name, stack, framework = "none") {
5044
+ function addNodeApp(workspaceRoot, name, stack, framework = "none", esm = false) {
4976
5045
  runNodeApp(workspaceRoot, name, stack, framework);
4977
5046
  const example = nodeAppExampleFiles(framework);
4978
5047
  if (example !== null) {
4979
5048
  for (const [relative, contents] of Object.entries(example)) {
4980
- writeFileEnsured((0, import_node_path18.join)(workspaceRoot, "apps", name, "src", relative), contents);
5049
+ writeFileEnsured((0, import_node_path19.join)(workspaceRoot, "apps", name, "src", relative), contents);
4981
5050
  }
4982
5051
  }
5052
+ if (esm) {
5053
+ convertAppToEsm((0, import_node_path19.join)(workspaceRoot, "apps", name));
5054
+ }
4983
5055
  ensureAdmZip(workspaceRoot);
4984
- addNxTargets((0, import_node_path18.join)(workspaceRoot, "apps", name, "package.json"), {
5056
+ addNxTargets((0, import_node_path19.join)(workspaceRoot, "apps", name, "package.json"), {
4985
5057
  package: nodeAppPackageTarget(name)
4986
5058
  });
4987
5059
  removeGeneratedEslintConfig(workspaceRoot, `apps/${name}`);
@@ -5050,10 +5122,10 @@ describe('greet', () => {
5050
5122
  })
5051
5123
  `;
5052
5124
  function repairNodeFunctionAppManifest(nodeFunctionAppRoot, workspaceRoot) {
5053
- const manifestPath = (0, import_node_path18.join)(nodeFunctionAppRoot, "package.json");
5125
+ const manifestPath = (0, import_node_path19.join)(nodeFunctionAppRoot, "package.json");
5054
5126
  const manifest = readJson(manifestPath);
5055
5127
  const azureFunctionsVersion = readJson(
5056
- (0, import_node_path18.join)(workspaceRoot, "node_modules/@azure/functions/package.json")
5128
+ (0, import_node_path19.join)(workspaceRoot, "node_modules/@azure/functions/package.json")
5057
5129
  ).version;
5058
5130
  const dependencies = manifest.dependencies ?? {};
5059
5131
  writeFileEnsured(
@@ -5093,23 +5165,26 @@ function nodeFunctionAppStartTarget(name) {
5093
5165
  options: { command: "func start", cwd: `apps/${name}` }
5094
5166
  };
5095
5167
  }
5096
- function addNodeFunctionApp(workspaceRoot, name, stack, empty = false) {
5168
+ function addNodeFunctionApp(workspaceRoot, name, stack, empty = false, esm = false) {
5097
5169
  runNodeApp(workspaceRoot, name, stack, "none");
5098
5170
  ensureAzureFunctionsPackage(workspaceRoot);
5099
- const nodeFunctionAppRoot = (0, import_node_path18.join)(workspaceRoot, "apps", name);
5100
- writeFileEnsured((0, import_node_path18.join)(nodeFunctionAppRoot, "src/main.ts"), empty ? NODE_FUNCTION_APP_MAIN_EMPTY : NODE_FUNCTION_APP_MAIN);
5171
+ const nodeFunctionAppRoot = (0, import_node_path19.join)(workspaceRoot, "apps", name);
5172
+ writeFileEnsured((0, import_node_path19.join)(nodeFunctionAppRoot, "src/main.ts"), empty ? NODE_FUNCTION_APP_MAIN_EMPTY : NODE_FUNCTION_APP_MAIN);
5101
5173
  if (!empty) {
5102
- const slice = (0, import_node_path18.join)(nodeFunctionAppRoot, "src/hello");
5103
- writeFileEnsured((0, import_node_path18.join)(slice, "hello.handler.ts"), NODE_FUNCTION_APP_HELLO);
5104
- writeFileEnsured((0, import_node_path18.join)(slice, "greet.use-case.ts"), NODE_FUNCTION_APP_GREET_USE_CASE);
5105
- writeFileEnsured((0, import_node_path18.join)(slice, "greet.use-case.spec.ts"), NODE_FUNCTION_APP_GREET_SPEC);
5106
- writeFileEnsured((0, import_node_path18.join)(slice, "greeting.contract.ts"), NODE_FUNCTION_APP_GREETING_CONTRACT);
5107
- writeFileEnsured((0, import_node_path18.join)(slice, "index.ts"), NODE_FUNCTION_APP_HELLO_BARREL);
5108
- }
5109
- writeFileEnsured((0, import_node_path18.join)(nodeFunctionAppRoot, "host.json"), NODE_FUNCTION_APP_HOST_JSON);
5174
+ const slice = (0, import_node_path19.join)(nodeFunctionAppRoot, "src/hello");
5175
+ writeFileEnsured((0, import_node_path19.join)(slice, "hello.handler.ts"), NODE_FUNCTION_APP_HELLO);
5176
+ writeFileEnsured((0, import_node_path19.join)(slice, "greet.use-case.ts"), NODE_FUNCTION_APP_GREET_USE_CASE);
5177
+ writeFileEnsured((0, import_node_path19.join)(slice, "greet.use-case.spec.ts"), NODE_FUNCTION_APP_GREET_SPEC);
5178
+ writeFileEnsured((0, import_node_path19.join)(slice, "greeting.contract.ts"), NODE_FUNCTION_APP_GREETING_CONTRACT);
5179
+ writeFileEnsured((0, import_node_path19.join)(slice, "index.ts"), NODE_FUNCTION_APP_HELLO_BARREL);
5180
+ }
5181
+ writeFileEnsured((0, import_node_path19.join)(nodeFunctionAppRoot, "host.json"), NODE_FUNCTION_APP_HOST_JSON);
5110
5182
  repairNodeFunctionAppManifest(nodeFunctionAppRoot, workspaceRoot);
5183
+ if (esm) {
5184
+ convertAppToEsm(nodeFunctionAppRoot);
5185
+ }
5111
5186
  ensureAdmZip(workspaceRoot);
5112
- addNxTargets((0, import_node_path18.join)(nodeFunctionAppRoot, "package.json"), {
5187
+ addNxTargets((0, import_node_path19.join)(nodeFunctionAppRoot, "package.json"), {
5113
5188
  package: nodeFunctionAppPackageTarget(name),
5114
5189
  prune: nodeFunctionAppPruneTarget(name),
5115
5190
  start: nodeFunctionAppStartTarget(name),
@@ -5121,24 +5196,24 @@ function addNodeFunctionApp(workspaceRoot, name, stack, empty = false) {
5121
5196
  }
5122
5197
 
5123
5198
  // src/project-scaffolding/npm-lib.use-case.ts
5124
- var import_node_path24 = require("path");
5199
+ var import_node_path25 = require("path");
5125
5200
 
5126
5201
  // src/lockfile-pruning/prune-stale-local-registry.use-case.ts
5127
- var import_node_fs20 = require("fs");
5128
- var import_node_path20 = require("path");
5202
+ var import_node_fs21 = require("fs");
5203
+ var import_node_path21 = require("path");
5129
5204
 
5130
5205
  // src/lockfile-pruning/stale-local-registry.validator.ts
5131
- var import_node_fs19 = require("fs");
5132
- var import_node_path19 = require("path");
5206
+ var import_node_fs20 = require("fs");
5207
+ var import_node_path20 = require("path");
5133
5208
  var LOCAL_REGISTRY_PACKAGE = "verdaccio";
5134
5209
  var LOCAL_REGISTRY_LOCK_KEY = `node_modules/${LOCAL_REGISTRY_PACKAGE}`;
5135
5210
  var DEPENDENCY_FIELDS = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"];
5136
5211
  function readJsonFile(path) {
5137
- if (!(0, import_node_fs19.existsSync)(path)) {
5212
+ if (!(0, import_node_fs20.existsSync)(path)) {
5138
5213
  return void 0;
5139
5214
  }
5140
5215
  try {
5141
- return JSON.parse((0, import_node_fs19.readFileSync)(path, "utf8"));
5216
+ return JSON.parse((0, import_node_fs20.readFileSync)(path, "utf8"));
5142
5217
  } catch {
5143
5218
  return void 0;
5144
5219
  }
@@ -5150,13 +5225,13 @@ function declaresLocalRegistry(manifest) {
5150
5225
  });
5151
5226
  }
5152
5227
  function hasStaleLocalRegistry(workspaceRoot) {
5153
- const lock = readJsonFile((0, import_node_path19.join)(workspaceRoot, "package-lock.json"));
5228
+ const lock = readJsonFile((0, import_node_path20.join)(workspaceRoot, "package-lock.json"));
5154
5229
  const entries = lock?.packages ?? {};
5155
5230
  if (!Object.hasOwn(entries, LOCAL_REGISTRY_LOCK_KEY)) {
5156
5231
  return false;
5157
5232
  }
5158
5233
  const members = Object.keys(entries).filter((key) => key !== "" && !key.startsWith("node_modules/") && !key.includes("/node_modules/"));
5159
- const manifests = ["", ...members].map((directory) => readJsonFile((0, import_node_path19.join)(workspaceRoot, directory, "package.json")));
5234
+ const manifests = ["", ...members].map((directory) => readJsonFile((0, import_node_path20.join)(workspaceRoot, directory, "package.json")));
5160
5235
  return manifests.every((manifest) => !declaresLocalRegistry(manifest));
5161
5236
  }
5162
5237
 
@@ -5165,21 +5240,21 @@ function pruneStaleLocalRegistry(workspaceRoot, resolve2 = runShell) {
5165
5240
  if (!hasStaleLocalRegistry(workspaceRoot)) {
5166
5241
  return { stale: false, status: 0 };
5167
5242
  }
5168
- const lockPath = (0, import_node_path20.join)(workspaceRoot, "package-lock.json");
5169
- const lock = JSON.parse((0, import_node_fs20.readFileSync)(lockPath, "utf8"));
5243
+ const lockPath = (0, import_node_path21.join)(workspaceRoot, "package-lock.json");
5244
+ const lock = JSON.parse((0, import_node_fs21.readFileSync)(lockPath, "utf8"));
5170
5245
  Reflect.deleteProperty(lock.packages, LOCAL_REGISTRY_LOCK_KEY);
5171
5246
  const root = lock.packages[""];
5172
5247
  if (root?.devDependencies !== void 0) {
5173
5248
  Reflect.deleteProperty(root.devDependencies, LOCAL_REGISTRY_PACKAGE);
5174
5249
  }
5175
- (0, import_node_fs20.writeFileSync)(lockPath, `${JSON.stringify(lock, void 0, 2)}
5250
+ (0, import_node_fs21.writeFileSync)(lockPath, `${JSON.stringify(lock, void 0, 2)}
5176
5251
  `);
5177
5252
  return { stale: true, status: resolve2("npm", ["install", "--package-lock-only", "--ignore-scripts", "--no-audit", "--no-fund"], workspaceRoot) };
5178
5253
  }
5179
5254
 
5180
5255
  // src/rollup-library/repair-publishable-manifest.use-case.ts
5181
- var import_node_fs21 = require("fs");
5182
- var import_node_path21 = require("path");
5256
+ var import_node_fs22 = require("fs");
5257
+ var import_node_path22 = require("path");
5183
5258
  var WRONG_TYPES_PATH = "./dist/index.esm.d.ts";
5184
5259
  var ACTUAL_TYPES_PATH = "./dist/src/index.d.ts";
5185
5260
  var DECLARATION_MAP_EXCLUSION = "!**/*.d.ts.map";
@@ -5202,14 +5277,14 @@ function repairPublishableManifest(manifestPath) {
5202
5277
  }
5203
5278
  function repairPublishableManifests(workspaceRoot) {
5204
5279
  const changed = [];
5205
- const manifests = (0, import_node_fs21.globSync)(["packages/*/package.json", "libs/*/package.json"], {
5280
+ const manifests = (0, import_node_fs22.globSync)(["packages/*/package.json", "libs/*/package.json"], {
5206
5281
  cwd: workspaceRoot
5207
5282
  });
5208
5283
  for (const relativePath of manifests) {
5209
- const manifestPath = (0, import_node_path21.join)(workspaceRoot, relativePath);
5210
- const before = (0, import_node_fs21.readFileSync)(manifestPath, "utf8");
5284
+ const manifestPath = (0, import_node_path22.join)(workspaceRoot, relativePath);
5285
+ const before = (0, import_node_fs22.readFileSync)(manifestPath, "utf8");
5211
5286
  repairPublishableManifest(manifestPath);
5212
- const after = (0, import_node_fs21.readFileSync)(manifestPath, "utf8");
5287
+ const after = (0, import_node_fs22.readFileSync)(manifestPath, "utf8");
5213
5288
  if (after !== before) {
5214
5289
  changed.push(relativePath.replaceAll("\\", "/"));
5215
5290
  }
@@ -5218,8 +5293,8 @@ function repairPublishableManifests(workspaceRoot) {
5218
5293
  }
5219
5294
 
5220
5295
  // src/rollup-library/repair-rollup-config.use-case.ts
5221
- var import_node_fs22 = require("fs");
5222
- var import_node_path22 = require("path");
5296
+ var import_node_fs23 = require("fs");
5297
+ var import_node_path23 = require("path");
5223
5298
 
5224
5299
  // src/rollup-library/rollup-config.algorithm.ts
5225
5300
  var ROLLUP_CONFIG_PLACEHOLDER = [
@@ -5437,12 +5512,12 @@ ${DECLARATION_SPECIFIER_PLUGIN},`);
5437
5512
  // src/rollup-library/repair-rollup-config.use-case.ts
5438
5513
  function repairRollupSourceMaps(workspaceRoot) {
5439
5514
  const changed = [];
5440
- const configs = (0, import_node_fs22.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
5515
+ const configs = (0, import_node_fs23.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
5441
5516
  cwd: workspaceRoot
5442
5517
  });
5443
5518
  for (const relativePath of configs) {
5444
- const configPath = (0, import_node_path22.join)(workspaceRoot, relativePath);
5445
- const before = (0, import_node_fs22.readFileSync)(configPath, "utf8");
5519
+ const configPath = (0, import_node_path23.join)(workspaceRoot, relativePath);
5520
+ const before = (0, import_node_fs23.readFileSync)(configPath, "utf8");
5446
5521
  const after = withRollupSourceMaps(before);
5447
5522
  if (after !== before) {
5448
5523
  writeFileEnsured(configPath, after);
@@ -5453,12 +5528,12 @@ function repairRollupSourceMaps(workspaceRoot) {
5453
5528
  }
5454
5529
  function upgradeDeclarationSpecifierPlugins(workspaceRoot) {
5455
5530
  const changed = [];
5456
- const configs = (0, import_node_fs22.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
5531
+ const configs = (0, import_node_fs23.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
5457
5532
  cwd: workspaceRoot
5458
5533
  });
5459
5534
  for (const relativePath of configs) {
5460
- const configPath = (0, import_node_path22.join)(workspaceRoot, relativePath);
5461
- const before = (0, import_node_fs22.readFileSync)(configPath, "utf8");
5535
+ const configPath = (0, import_node_path23.join)(workspaceRoot, relativePath);
5536
+ const before = (0, import_node_fs23.readFileSync)(configPath, "utf8");
5462
5537
  const after = withUpgradedDeclarationSpecifierPlugin(before);
5463
5538
  if (after !== before) {
5464
5539
  writeFileEnsured(configPath, after);
@@ -5468,11 +5543,11 @@ function upgradeDeclarationSpecifierPlugins(workspaceRoot) {
5468
5543
  return changed;
5469
5544
  }
5470
5545
  function repairDeclarationSpecifiers(projectRoot) {
5471
- const configPath = (0, import_node_path22.join)(projectRoot, "rollup.config.cjs");
5546
+ const configPath = (0, import_node_path23.join)(projectRoot, "rollup.config.cjs");
5472
5547
  if (!fileExists(configPath)) {
5473
5548
  return;
5474
5549
  }
5475
- const config = (0, import_node_fs22.readFileSync)(configPath, "utf8");
5550
+ const config = (0, import_node_fs23.readFileSync)(configPath, "utf8");
5476
5551
  if (!config.includes(ROLLUP_CONFIG_PLACEHOLDER)) {
5477
5552
  const repaired = withRollupSourceMaps(withDeclarationSpecifierPlugin(config));
5478
5553
  if (repaired !== config) {
@@ -5487,12 +5562,12 @@ function repairDeclarationSpecifiers(projectRoot) {
5487
5562
  }
5488
5563
  function addMissingDeclarationSpecifierPlugins(workspaceRoot) {
5489
5564
  const changed = [];
5490
- const configs = (0, import_node_fs22.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
5565
+ const configs = (0, import_node_fs23.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
5491
5566
  cwd: workspaceRoot
5492
5567
  });
5493
5568
  for (const relativePath of configs) {
5494
- const configPath = (0, import_node_path22.join)(workspaceRoot, relativePath);
5495
- const before = (0, import_node_fs22.readFileSync)(configPath, "utf8");
5569
+ const configPath = (0, import_node_path23.join)(workspaceRoot, relativePath);
5570
+ const before = (0, import_node_fs23.readFileSync)(configPath, "utf8");
5496
5571
  const after = withDeclarationSpecifierPlugin(before);
5497
5572
  if (after !== before) {
5498
5573
  writeFileEnsured(configPath, after);
@@ -5503,10 +5578,10 @@ function addMissingDeclarationSpecifierPlugins(workspaceRoot) {
5503
5578
  }
5504
5579
 
5505
5580
  // src/rollup-library/rollup-config.repository.ts
5506
- var import_node_fs23 = require("fs");
5507
- var import_node_path23 = require("path");
5581
+ var import_node_fs24 = require("fs");
5582
+ var import_node_path24 = require("path");
5508
5583
  function resolveRequireTarget(fromDir, specifier) {
5509
- const base = (0, import_node_path23.join)(fromDir, specifier);
5584
+ const base = (0, import_node_path24.join)(fromDir, specifier);
5510
5585
  return [base, `${base}.cjs`, `${base}.js`].find((candidate) => fileExists(candidate));
5511
5586
  }
5512
5587
  function resolveRollupConfigText(configPath, visited = /* @__PURE__ */ new Set()) {
@@ -5516,7 +5591,7 @@ function resolveRollupConfigText(configPath, visited = /* @__PURE__ */ new Set()
5516
5591
  visited.add(configPath);
5517
5592
  let text;
5518
5593
  try {
5519
- text = (0, import_node_fs23.readFileSync)(configPath, "utf8");
5594
+ text = (0, import_node_fs24.readFileSync)(configPath, "utf8");
5520
5595
  } catch {
5521
5596
  return "";
5522
5597
  }
@@ -5524,15 +5599,15 @@ function resolveRollupConfigText(configPath, visited = /* @__PURE__ */ new Set()
5524
5599
  text.matchAll(/require\((['"])(\.[^'"]*)\1\)/g),
5525
5600
  (match) => match[2]
5526
5601
  );
5527
- const requiredText = specifiers.map((specifier) => resolveRequireTarget((0, import_node_path23.dirname)(configPath), specifier)).filter((target) => target !== void 0).map((target) => resolveRollupConfigText(target, visited));
5602
+ const requiredText = specifiers.map((specifier) => resolveRequireTarget((0, import_node_path24.dirname)(configPath), specifier)).filter((target) => target !== void 0).map((target) => resolveRollupConfigText(target, visited));
5528
5603
  return [text, ...requiredText].join("\n");
5529
5604
  }
5530
5605
 
5531
5606
  // src/project-scaffolding/npm-lib.use-case.ts
5532
5607
  async function addNpmLib(workspaceRoot, name, options, kindProvided, stack) {
5533
5608
  const scope = options.scope ?? (kindProvided ? defaultScope(workspaceRoot) : await promptText("npm scope for the published package", defaultScope(workspaceRoot)));
5534
- const projectRoot = (0, import_node_path24.join)(workspaceRoot, "packages", name);
5535
- const manifestPath = (0, import_node_path24.join)(projectRoot, "package.json");
5609
+ const projectRoot = (0, import_node_path25.join)(workspaceRoot, "packages", name);
5610
+ const manifestPath = (0, import_node_path25.join)(projectRoot, "package.json");
5536
5611
  runGeneratorAndRepair(
5537
5612
  workspaceRoot,
5538
5613
  [
@@ -5565,8 +5640,8 @@ async function addNpmLib(workspaceRoot, name, options, kindProvided, stack) {
5565
5640
  }
5566
5641
 
5567
5642
  // src/project-scaffolding/python.use-case.ts
5568
- var import_node_fs24 = require("fs");
5569
- var import_node_path25 = require("path");
5643
+ var import_node_fs25 = require("fs");
5644
+ var import_node_path26 = require("path");
5570
5645
  function ensurePython(workspaceRoot) {
5571
5646
  const hasPython = runShell("python3", ["--version"], workspaceRoot) === 0 || runShell("python", ["--version"], workspaceRoot) === 0;
5572
5647
  if (!hasPython) {
@@ -5589,7 +5664,7 @@ function ensurePythonPipPlugin(workspaceRoot) {
5589
5664
  registerPythonGraphPlugin(workspaceRoot);
5590
5665
  }
5591
5666
  function registerPythonGraphPlugin(workspaceRoot) {
5592
- const nxJsonPath = (0, import_node_path25.join)(workspaceRoot, "nx.json");
5667
+ const nxJsonPath = (0, import_node_path26.join)(workspaceRoot, "nx.json");
5593
5668
  if (!fileExists(nxJsonPath)) {
5594
5669
  return;
5595
5670
  }
@@ -5612,7 +5687,7 @@ pytest
5612
5687
  pip-audit
5613
5688
  `;
5614
5689
  function ensureRequirementsDev(workspaceRoot) {
5615
- const requirementsDevPath = (0, import_node_path25.join)(workspaceRoot, "requirements-dev.txt");
5690
+ const requirementsDevPath = (0, import_node_path26.join)(workspaceRoot, "requirements-dev.txt");
5616
5691
  if (!fileExists(requirementsDevPath)) {
5617
5692
  writeFileEnsured(requirementsDevPath, PYTHON_REQUIREMENTS_DEV);
5618
5693
  }
@@ -5672,8 +5747,8 @@ function addPythonApp(workspaceRoot, name) {
5672
5747
  ["g", "@mnci/nx-python-pip:application", name, `--directory=apps/${name}`, "--no-interactive"],
5673
5748
  workspaceRoot
5674
5749
  );
5675
- writeFileEnsured((0, import_node_path25.join)(workspaceRoot, "apps", name, "main.py"), pythonAppMain(name));
5676
- addProjectJsonTargets((0, import_node_path25.join)(workspaceRoot, "apps", name, "project.json"), {
5750
+ writeFileEnsured((0, import_node_path26.join)(workspaceRoot, "apps", name, "main.py"), pythonAppMain(name));
5751
+ addProjectJsonTargets((0, import_node_path26.join)(workspaceRoot, "apps", name, "project.json"), {
5677
5752
  package: pythonAppPackageTarget(name),
5678
5753
  start: pythonAppStartTarget(name)
5679
5754
  });
@@ -5695,7 +5770,7 @@ function addPythonFunctionApp(workspaceRoot, name) {
5695
5770
  workspaceRoot
5696
5771
  );
5697
5772
  const moduleDirectory = pythonModuleDirectory(name);
5698
- addProjectJsonTargets((0, import_node_path25.join)(workspaceRoot, "apps", name, "project.json"), {
5773
+ addProjectJsonTargets((0, import_node_path26.join)(workspaceRoot, "apps", name, "project.json"), {
5699
5774
  package: pythonFunctionAppPackageTarget(name, moduleDirectory),
5700
5775
  start: pythonFunctionAppStartTarget(name)
5701
5776
  });
@@ -5736,7 +5811,7 @@ function addPythonInternalLib(workspaceRoot, name) {
5736
5811
  var PYTHON_PROJECT_DIRS = ["apps", "python-packages", "libs"];
5737
5812
  function findPythonProjectDirectory(workspaceRoot, name) {
5738
5813
  return PYTHON_PROJECT_DIRS.find(
5739
- (directory) => fileExists((0, import_node_path25.join)(workspaceRoot, directory, name, "pyproject.toml"))
5814
+ (directory) => fileExists((0, import_node_path26.join)(workspaceRoot, directory, name, "pyproject.toml"))
5740
5815
  );
5741
5816
  }
5742
5817
  function parseVendorEntries(pyprojectToml) {
@@ -5775,7 +5850,7 @@ async function addPythonVendor(workspaceRoot, consumer, options, kindProvided) {
5775
5850
  `No Python project named '${consumer}' with a pyproject.toml found (checked apps/, python-packages/, libs/). A Python function app has no pyproject.toml \u2014 there is nothing to vendor into.`
5776
5851
  );
5777
5852
  }
5778
- if (!fileExists((0, import_node_path25.join)(workspaceRoot, "libs", lib, "pyproject.toml"))) {
5853
+ if (!fileExists((0, import_node_path26.join)(workspaceRoot, "libs", lib, "pyproject.toml"))) {
5779
5854
  throw new Error(
5780
5855
  `No internal Python library named '${lib}' found at libs/${lib}/pyproject.toml. Only an internal library (added via 'mnci add python-internal-lib') can be vendored.`
5781
5856
  );
@@ -5783,8 +5858,8 @@ async function addPythonVendor(workspaceRoot, consumer, options, kindProvided) {
5783
5858
  if (consumerDirectory === "libs" && consumer === lib) {
5784
5859
  throw new Error(`'${consumer}' cannot vendor itself.`);
5785
5860
  }
5786
- const pyprojectPath = (0, import_node_path25.join)(workspaceRoot, consumerDirectory, consumer, "pyproject.toml");
5787
- const pyprojectToml = (0, import_node_fs24.readFileSync)(pyprojectPath, "utf8");
5861
+ const pyprojectPath = (0, import_node_path26.join)(workspaceRoot, consumerDirectory, consumer, "pyproject.toml");
5862
+ const pyprojectToml = (0, import_node_fs25.readFileSync)(pyprojectPath, "utf8");
5788
5863
  if (parseVendorEntries(pyprojectToml).includes(lib)) {
5789
5864
  logger.info(`'${consumer}' already vendors '${lib}' \u2014 nothing to do.`);
5790
5865
  return;
@@ -5795,9 +5870,228 @@ async function addPythonVendor(workspaceRoot, consumer, options, kindProvided) {
5795
5870
  );
5796
5871
  }
5797
5872
 
5873
+ // src/project-scaffolding/container.use-case.ts
5874
+ var import_node_fs26 = require("fs");
5875
+ var import_node_path27 = require("path");
5876
+
5877
+ // src/project-scaffolding/container-image.algorithm.ts
5878
+ var CONTAINER_TARGETS = ["node-app", "react-app", "go-app"];
5879
+ var NGINX_SPA = `server {
5880
+ listen 80;
5881
+ root /usr/share/nginx/html;
5882
+ index index.html;
5883
+
5884
+ location / {
5885
+ try_files $uri $uri/ /index.html;
5886
+ }
5887
+ }
5888
+ `;
5889
+ function planNode(app, port) {
5890
+ const portLines = port === void 0 ? "" : `ENV HOST=0.0.0.0
5891
+ ENV PORT=${port}
5892
+ EXPOSE ${port}
5893
+ `;
5894
+ return {
5895
+ dockerfile: `# Written by MoNecromanCI. The build context is the app's pruned output (its manifest, lockfile and compiled
5896
+ # code), so the image holds only what the app needs to run.
5897
+ FROM node:24-alpine
5898
+ WORKDIR /app
5899
+ COPY . .
5900
+ RUN npm install --omit=dev --ignore-scripts --no-audit --no-fund
5901
+ ENV NODE_ENV=production
5902
+ ${portLines}USER node
5903
+ CMD ["node", "main.js"]
5904
+ `,
5905
+ dockerignore: "node_modules\n",
5906
+ files: {},
5907
+ contextDirectory: `apps/${app}/dist`,
5908
+ dependsOn: "prune",
5909
+ ...port !== void 0 && { publish: `${port}:${port}` }
5910
+ };
5911
+ }
5912
+ function planReact(app) {
5913
+ return {
5914
+ dockerfile: `# Written by MoNecromanCI. The build context is the app's production build.
5915
+ FROM nginx:1.27-alpine
5916
+ COPY --from=files default.conf /etc/nginx/conf.d/default.conf
5917
+ COPY . /usr/share/nginx/html
5918
+ EXPOSE 80
5919
+ `,
5920
+ dockerignore: "",
5921
+ files: { "default.conf": NGINX_SPA },
5922
+ contextDirectory: `apps/${app}/dist`,
5923
+ dependsOn: "build",
5924
+ publish: "8080:80"
5925
+ };
5926
+ }
5927
+ function planGo(app, port) {
5928
+ return {
5929
+ dockerfile: `# Written by MoNecromanCI. The build context holds the app's static linux-amd64 binary from build-all.
5930
+ FROM gcr.io/distroless/static-debian12:nonroot
5931
+ COPY ${app} /app
5932
+ ${port === void 0 ? "" : `EXPOSE ${port}
5933
+ `}ENTRYPOINT ["/app"]
5934
+ `,
5935
+ dockerignore: "",
5936
+ files: {},
5937
+ contextDirectory: `dist/platforms/${app}/linux-amd64`,
5938
+ dependsOn: "build-all",
5939
+ ...port !== void 0 && { publish: `${port}:${port}` }
5940
+ };
5941
+ }
5942
+ function planContainer(target, app, port) {
5943
+ switch (target) {
5944
+ case "node-app": {
5945
+ return planNode(app, port);
5946
+ }
5947
+ case "react-app": {
5948
+ return planReact(app);
5949
+ }
5950
+ case "go-app": {
5951
+ return planGo(app, port);
5952
+ }
5953
+ }
5954
+ }
5955
+
5956
+ // src/project-scaffolding/container.use-case.ts
5957
+ var CONTAINER_TAG = "type:container";
5958
+ var CONTAINER_SCRIPT_PATH = "tools/container-image.cjs";
5959
+ var CONTAINER_SCRIPT = `// Written by MoNecromanCI; \`mnci upgrade\` rewrites it. Builds and runs a container image.
5960
+ // node tools/container-image.cjs build --image <name> --file <Dockerfile> --context <dir> [--files <dir>]
5961
+ // node tools/container-image.cjs start --image <name> [--publish <host:container>]
5962
+ const { spawnSync } = require('node:child_process')
5963
+ const { existsSync } = require('node:fs')
5964
+
5965
+ const [command, ...rest] = process.argv.slice(2)
5966
+ const options = {}
5967
+ for (let index = 0; index < rest.length; index += 2) {
5968
+ options[rest[index].replace(/^--/, '')] = rest[index + 1]
5969
+ }
5970
+ const version = process.env.VERSION || 'dev'
5971
+
5972
+ /** Runs docker with the output shown, and exits with its status. */
5973
+ function docker (args) {
5974
+ const result = spawnSync('docker', args, { stdio: 'inherit' })
5975
+ if (result.error) {
5976
+ console.error('Docker is not available: ' + result.error.message)
5977
+ process.exit(1)
5978
+ }
5979
+ if (result.status !== 0) {
5980
+ process.exit(result.status ?? 1)
5981
+ }
5982
+ }
5983
+
5984
+ if (command === 'build') {
5985
+ const args = ['build', '-f', options.file, '-t', options.image + ':' + version, '-t', options.image + ':latest']
5986
+ if (options.files && existsSync(options.files)) {
5987
+ args.push('--build-context', 'files=' + options.files)
5988
+ }
5989
+ args.push(options.context)
5990
+ docker(args)
5991
+ } else if (command === 'start') {
5992
+ const args = ['run', '--rm', '--init']
5993
+ if (options.publish) {
5994
+ args.push('-p', options.publish)
5995
+ }
5996
+ args.push(options.image + ':latest')
5997
+ docker(args)
5998
+ } else {
5999
+ console.error('usage: container-image.cjs build|start --image <name> ...')
6000
+ process.exit(2)
6001
+ }
6002
+ `;
6003
+ function detectTarget(appRoot) {
6004
+ if (fileExists((0, import_node_path27.join)(appRoot, "go.mod"))) {
6005
+ return fileExists((0, import_node_path27.join)(appRoot, "host.json")) ? void 0 : "go-app";
6006
+ }
6007
+ const manifestPath = (0, import_node_path27.join)(appRoot, "package.json");
6008
+ if (!fileExists(manifestPath) || fileExists((0, import_node_path27.join)(appRoot, "host.json"))) {
6009
+ return void 0;
6010
+ }
6011
+ const manifest = readJson(manifestPath);
6012
+ const declared = Object.keys({ ...manifest.dependencies, ...manifest.devDependencies });
6013
+ if (declared.includes("react") && fileExists((0, import_node_path27.join)(appRoot, "index.html"))) {
6014
+ return "react-app";
6015
+ }
6016
+ return manifest.nx?.targets?.build?.executor === "@nx/esbuild:esbuild" ? "node-app" : void 0;
6017
+ }
6018
+ function containerTargets(name, appProject, plan) {
6019
+ const build = [
6020
+ "node",
6021
+ CONTAINER_SCRIPT_PATH,
6022
+ "build",
6023
+ "--image",
6024
+ name,
6025
+ "--file",
6026
+ `apps/${name}/Dockerfile`,
6027
+ "--context",
6028
+ plan.contextDirectory,
6029
+ "--files",
6030
+ `apps/${name}/files`
6031
+ ].join(" ");
6032
+ const start = ["node", CONTAINER_SCRIPT_PATH, "start", "--image", name, ...plan.publish === void 0 ? [] : ["--publish", plan.publish]].join(" ");
6033
+ return {
6034
+ // Not called `build`: CI runs every project's `build`, and building an image needs a Docker engine that
6035
+ // an agent may not have (a Windows agent cannot run Linux images at all).
6036
+ image: { executor: "nx:run-commands", cache: false, dependsOn: [`${appProject}:${plan.dependsOn}`], options: { command: build } },
6037
+ start: { executor: "nx:run-commands", continuous: true, dependsOn: ["image"], options: { command: start } }
6038
+ };
6039
+ }
6040
+ function addContainer(workspaceRoot, name, options) {
6041
+ const appRoot = (0, import_node_path27.join)(workspaceRoot, "apps", options.app);
6042
+ if (!fileExists(appRoot)) {
6043
+ throw new Error(`--app ${options.app}: there is no apps/${options.app} to put in an image. Add the app first.`);
6044
+ }
6045
+ const target = detectTarget(appRoot);
6046
+ if (target === void 0) {
6047
+ throw new Error(`--app ${options.app}: apps/${options.app} is not an app an image is built for (${CONTAINER_TARGETS.join(", ")}). An Azure Functions app has its own host image and is not supported yet.`);
6048
+ }
6049
+ const plan = planContainer(target, options.app, options.port);
6050
+ const appManifestPath = (0, import_node_path27.join)(appRoot, "package.json");
6051
+ const appProject = fileExists(appManifestPath) ? readJson(appManifestPath).name : options.app;
6052
+ const scope = appProject.startsWith("@") ? `${appProject.split("/", 1)[0]}/` : "";
6053
+ const projectRoot = (0, import_node_path27.join)(workspaceRoot, "apps", name);
6054
+ writeFileEnsured((0, import_node_path27.join)(projectRoot, "Dockerfile"), plan.dockerfile);
6055
+ writeFileEnsured((0, import_node_path27.join)(projectRoot, "Dockerfile.dockerignore"), plan.dockerignore);
6056
+ for (const [file, text] of Object.entries(plan.files)) {
6057
+ writeFileEnsured((0, import_node_path27.join)(projectRoot, "files", file), text);
6058
+ }
6059
+ writeFileEnsured((0, import_node_path27.join)(workspaceRoot, CONTAINER_SCRIPT_PATH), CONTAINER_SCRIPT);
6060
+ writeFileEnsured((0, import_node_path27.join)(projectRoot, "package.json"), toJson({
6061
+ name: `${scope}${name}`,
6062
+ version: "0.0.1",
6063
+ private: true,
6064
+ nx: { tags: [CONTAINER_TAG], implicitDependencies: [appProject], targets: containerTargets(name, appProject, plan) }
6065
+ }));
6066
+ registerProjectCommands(workspaceRoot, name, {
6067
+ build: false,
6068
+ start: `nx run ${name}:start`,
6069
+ qa: `nx run ${name}:image`,
6070
+ extra: { image: `nx run ${name}:image` }
6071
+ });
6072
+ }
6073
+ function refreshContainerScript(workspaceRoot) {
6074
+ let entries;
6075
+ try {
6076
+ entries = (0, import_node_fs26.readdirSync)((0, import_node_path27.join)(workspaceRoot, "apps"));
6077
+ } catch {
6078
+ return false;
6079
+ }
6080
+ const hasContainer = entries.some((entry) => {
6081
+ const manifestPath = (0, import_node_path27.join)(workspaceRoot, "apps", entry, "package.json");
6082
+ return fileExists(manifestPath) && (readJson(manifestPath).nx?.tags ?? []).includes(CONTAINER_TAG);
6083
+ });
6084
+ const path = (0, import_node_path27.join)(workspaceRoot, CONTAINER_SCRIPT_PATH);
6085
+ if (!hasContainer || fileExists(path) && (0, import_node_fs26.readFileSync)(path, "utf8") === CONTAINER_SCRIPT) {
6086
+ return false;
6087
+ }
6088
+ writeFileEnsured(path, CONTAINER_SCRIPT);
6089
+ return true;
6090
+ }
6091
+
5798
6092
  // src/project-scaffolding/react-app.use-case.ts
5799
- var import_node_fs25 = require("fs");
5800
- var import_node_path26 = require("path");
6093
+ var import_node_fs27 = require("fs");
6094
+ var import_node_path28 = require("path");
5801
6095
 
5802
6096
  // src/project-scaffolding/react-app-example.algorithm.ts
5803
6097
  function reactAppExampleFiles(name) {
@@ -5876,6 +6170,15 @@ describe('App', () => {
5876
6170
  `
5877
6171
  };
5878
6172
  }
6173
+ function reactAppE2eSpec(name) {
6174
+ return `import { expect, test } from '@playwright/test'
6175
+
6176
+ test('shows the greeting', async ({ page }) => {
6177
+ await page.goto('/')
6178
+ await expect(page.getByText('Hello, ${name}!')).toBeVisible()
6179
+ })
6180
+ `;
6181
+ }
5879
6182
 
5880
6183
  // src/project-scaffolding/react-app.use-case.ts
5881
6184
  var REACT_ENVIRONMENTS = ["dev", "uat", "prod"];
@@ -5914,12 +6217,12 @@ function reactAppTargets(name) {
5914
6217
  return targets;
5915
6218
  }
5916
6219
  function allowEnvFiles(workspaceRoot) {
5917
- const gitignorePath = (0, import_node_path26.join)(workspaceRoot, ".gitignore");
6220
+ const gitignorePath = (0, import_node_path28.join)(workspaceRoot, ".gitignore");
5918
6221
  if (!fileExists(gitignorePath)) {
5919
6222
  return;
5920
6223
  }
5921
6224
  const marker = "# MoNecromanCI: keep the committed per-environment Vite config (public VITE_ values)";
5922
- const current = (0, import_node_fs25.readFileSync)(gitignorePath, "utf8");
6225
+ const current = (0, import_node_fs27.readFileSync)(gitignorePath, "utf8");
5923
6226
  if (current.includes(marker)) {
5924
6227
  return;
5925
6228
  }
@@ -5929,7 +6232,7 @@ ${marker}
5929
6232
  ${allow}
5930
6233
  `);
5931
6234
  }
5932
- function addReactApp(workspaceRoot, name, stack) {
6235
+ function addReactApp(workspaceRoot, name, stack, e2e = false) {
5933
6236
  ensurePlugin(workspaceRoot, "@nx/react");
5934
6237
  runNx(
5935
6238
  [
@@ -5940,36 +6243,50 @@ function addReactApp(workspaceRoot, name, stack) {
5940
6243
  `--unitTestRunner=${stack.testRunner}`,
5941
6244
  "--linter=none",
5942
6245
  "--style=css",
5943
- "--e2eTestRunner=none",
6246
+ `--e2eTestRunner=${e2e ? "playwright" : "none"}`,
5944
6247
  "--no-interactive"
5945
6248
  ],
5946
6249
  workspaceRoot
5947
6250
  );
5948
6251
  ensureAdmZip(workspaceRoot);
5949
- const reactAppRoot = (0, import_node_path26.join)(workspaceRoot, "apps", name);
6252
+ const reactAppRoot = (0, import_node_path28.join)(workspaceRoot, "apps", name);
5950
6253
  for (const environment of REACT_ENVIRONMENTS) {
5951
- writeFileEnsured((0, import_node_path26.join)(reactAppRoot, `.env.${environment}`), reactEnvironmentFile(environment));
6254
+ writeFileEnsured((0, import_node_path28.join)(reactAppRoot, `.env.${environment}`), reactEnvironmentFile(environment));
5952
6255
  }
5953
6256
  allowEnvFiles(workspaceRoot);
5954
- addNxTargets((0, import_node_path26.join)(reactAppRoot, "package.json"), reactAppTargets(name));
6257
+ addNxTargets((0, import_node_path28.join)(reactAppRoot, "package.json"), reactAppTargets(name));
5955
6258
  reshapeReactAppScaffold(reactAppRoot);
5956
- (0, import_node_fs25.rmSync)((0, import_node_path26.join)(reactAppRoot, "src/app/nx-welcome.component.tsx"), { force: true });
6259
+ (0, import_node_fs27.rmSync)((0, import_node_path28.join)(reactAppRoot, "src/app/nx-welcome.component.tsx"), { force: true });
5957
6260
  const example = reactAppExampleFiles(name);
5958
6261
  for (const [relative, contents] of Object.entries(example)) {
5959
- writeFileEnsured((0, import_node_path26.join)(reactAppRoot, "src", relative), contents);
6262
+ writeFileEnsured((0, import_node_path28.join)(reactAppRoot, "src", relative), contents);
5960
6263
  }
5961
6264
  removeGeneratedEslintConfig(workspaceRoot, `apps/${name}`);
5962
6265
  registerProjectCommands(workspaceRoot, name, { build: true, start: `nx run ${name}:serve` });
6266
+ if (e2e) {
6267
+ pairE2eProject(workspaceRoot, name);
6268
+ }
6269
+ }
6270
+ function pairE2eProject(workspaceRoot, name) {
6271
+ const e2eName = `${name}-e2e`;
6272
+ const e2eRoot = (0, import_node_path28.join)(workspaceRoot, "apps", e2eName);
6273
+ (0, import_node_fs27.rmSync)((0, import_node_path28.join)(e2eRoot, "src/example.spec.ts"), { force: true });
6274
+ writeFileEnsured((0, import_node_path28.join)(e2eRoot, "src/greeting.e2e.spec.ts"), reactAppE2eSpec(name));
6275
+ removeGeneratedEslintConfig(workspaceRoot, `apps/${e2eName}`);
6276
+ registerProjectCommands(workspaceRoot, e2eName, {
6277
+ build: false,
6278
+ qa: `nx run ${e2eName}:lint && nx run ${e2eName}:typecheck`
6279
+ });
5963
6280
  }
5964
6281
 
5965
6282
  // src/project-scaffolding/react-lib.use-case.ts
5966
- var import_node_path27 = require("path");
6283
+ var import_node_path29 = require("path");
5967
6284
  var REACT_LIB_BUNDLER = "rollup";
5968
6285
  async function addReactLib(workspaceRoot, name, options, kindProvided, stack) {
5969
6286
  ensurePlugin(workspaceRoot, "@nx/react");
5970
6287
  const scope = options.scope ?? (kindProvided ? defaultScope(workspaceRoot) : await promptText("npm scope for the published package", defaultScope(workspaceRoot)));
5971
- const projectRoot = (0, import_node_path27.join)(workspaceRoot, "packages", name);
5972
- const publishableManifest = (0, import_node_path27.join)(projectRoot, "package.json");
6288
+ const projectRoot = (0, import_node_path29.join)(workspaceRoot, "packages", name);
6289
+ const publishableManifest = (0, import_node_path29.join)(projectRoot, "package.json");
5973
6290
  runGeneratorAndRepair(
5974
6291
  workspaceRoot,
5975
6292
  [
@@ -6013,8 +6330,8 @@ function dropNxProjectName(manifestPath) {
6013
6330
  }
6014
6331
  function addReactInternalLib(workspaceRoot, name, stack, empty = false) {
6015
6332
  ensurePlugin(workspaceRoot, "@nx/react");
6016
- const projectRoot = (0, import_node_path27.join)(workspaceRoot, "libs", name);
6017
- const privateManifest = (0, import_node_path27.join)(projectRoot, "package.json");
6333
+ const projectRoot = (0, import_node_path29.join)(workspaceRoot, "libs", name);
6334
+ const privateManifest = (0, import_node_path29.join)(projectRoot, "package.json");
6018
6335
  runGeneratorAndRepair(
6019
6336
  workspaceRoot,
6020
6337
  [
@@ -6041,8 +6358,8 @@ function addReactInternalLib(workspaceRoot, name, stack, empty = false) {
6041
6358
  }
6042
6359
 
6043
6360
  // src/project-scaffolding/vscode-extension.use-case.ts
6044
- var import_node_fs26 = require("fs");
6045
- var import_node_path28 = require("path");
6361
+ var import_node_fs28 = require("fs");
6362
+ var import_node_path30 = require("path");
6046
6363
  var VSCODE_EXTENSION_SCRIPT_PATH = "tools/vscode-extension.cjs";
6047
6364
  var VSCODE_ENGINE_FLOOR = "1.96.0";
6048
6365
  var VSCODE_SIDECAR_TARGETS = {
@@ -6227,8 +6544,8 @@ export const window = {
6227
6544
  var VSCODE_STUB_PATH = "test/vscode.stub.ts";
6228
6545
  function mapVscodeToStub(projectRoot, testRunner) {
6229
6546
  const [file, anchor, mapping] = testRunner === "jest" ? ["jest.config.cts", "testEnvironment: 'node',", `moduleNameMapper: { '^vscode$': '<rootDir>/${VSCODE_STUB_PATH}' },`] : ["vitest.config.mts", "environment: 'node',", `alias: { vscode: \`\${import.meta.dirname}/${VSCODE_STUB_PATH}\` },`];
6230
- const path = (0, import_node_path28.join)(projectRoot, file);
6231
- const content = fileExists(path) ? (0, import_node_fs26.readFileSync)(path, "utf8") : "";
6547
+ const path = (0, import_node_path30.join)(projectRoot, file);
6548
+ const content = fileExists(path) ? (0, import_node_fs28.readFileSync)(path, "utf8") : "";
6232
6549
  const line = content.split("\n").find((candidate) => candidate.trim() === anchor);
6233
6550
  if (line === void 0) {
6234
6551
  throw new Error(`${file} has no "${anchor}" line to map the vscode module after; map 'vscode' to ${VSCODE_STUB_PATH} by hand.`);
@@ -6248,11 +6565,11 @@ function ensureVscodeToolchain(workspaceRoot) {
6248
6565
  }
6249
6566
  }
6250
6567
  function defaultPublisher(workspaceRoot) {
6251
- const { name } = readJson((0, import_node_path28.join)(workspaceRoot, "package.json"));
6568
+ const { name } = readJson((0, import_node_path30.join)(workspaceRoot, "package.json"));
6252
6569
  return (name ?? "publisher").replace(/^@/, "").split("/", 1)[0];
6253
6570
  }
6254
6571
  function reshapeManifest(projectRoot, name, publisher) {
6255
- const manifestPath = (0, import_node_path28.join)(projectRoot, "package.json");
6572
+ const manifestPath = (0, import_node_path30.join)(projectRoot, "package.json");
6256
6573
  const manifest = readJson(manifestPath);
6257
6574
  const { nx = {}, ...rest } = manifest;
6258
6575
  const targets = { ...nx.targets };
@@ -6316,11 +6633,11 @@ function vscodeExtensionPublishTarget(name, sidecar) {
6316
6633
  };
6317
6634
  }
6318
6635
  function addDebugLaunch(workspaceRoot, name) {
6319
- const file = (0, import_node_fs26.readdirSync)(workspaceRoot).find((entry) => entry.endsWith(".code-workspace"));
6636
+ const file = (0, import_node_fs28.readdirSync)(workspaceRoot).find((entry) => entry.endsWith(".code-workspace"));
6320
6637
  if (file === void 0) {
6321
6638
  return;
6322
6639
  }
6323
- const path = (0, import_node_path28.join)(workspaceRoot, file);
6640
+ const path = (0, import_node_path30.join)(workspaceRoot, file);
6324
6641
  const workspace = readCodeWorkspace(path) ?? {};
6325
6642
  const folder = `\${workspaceFolder:${workspace.folders?.[0]?.name ?? file.replace(/\.code-workspace$/, "")}}`;
6326
6643
  const taskLabel = `${name}: build (development)`;
@@ -6353,39 +6670,39 @@ function addDebugLaunch(workspaceRoot, name) {
6353
6670
  );
6354
6671
  }
6355
6672
  function refreshVscodeExtensionScript(workspaceRoot) {
6356
- const apps = (0, import_node_path28.join)(workspaceRoot, "apps");
6673
+ const apps = (0, import_node_path30.join)(workspaceRoot, "apps");
6357
6674
  let entries;
6358
6675
  try {
6359
- entries = (0, import_node_fs26.readdirSync)(apps);
6676
+ entries = (0, import_node_fs28.readdirSync)(apps);
6360
6677
  } catch {
6361
6678
  return false;
6362
6679
  }
6363
6680
  const hasExtension = entries.some((entry) => {
6364
- const manifestPath = (0, import_node_path28.join)(apps, entry, "package.json");
6681
+ const manifestPath = (0, import_node_path30.join)(apps, entry, "package.json");
6365
6682
  if (!fileExists(manifestPath)) {
6366
6683
  return false;
6367
6684
  }
6368
6685
  const tags = readJson(manifestPath).nx?.tags ?? [];
6369
6686
  return tags.includes(VSCODE_EXTENSION_TAG);
6370
6687
  });
6371
- const path = (0, import_node_path28.join)(workspaceRoot, VSCODE_EXTENSION_SCRIPT_PATH);
6372
- if (!hasExtension || fileExists(path) && (0, import_node_fs26.readFileSync)(path, "utf8") === VSCODE_EXTENSION_SCRIPT) {
6688
+ const path = (0, import_node_path30.join)(workspaceRoot, VSCODE_EXTENSION_SCRIPT_PATH);
6689
+ if (!hasExtension || fileExists(path) && (0, import_node_fs28.readFileSync)(path, "utf8") === VSCODE_EXTENSION_SCRIPT) {
6373
6690
  return false;
6374
6691
  }
6375
6692
  writeFileEnsured(path, VSCODE_EXTENSION_SCRIPT);
6376
6693
  return true;
6377
6694
  }
6378
6695
  function pinVscodeExtensionProjectNames(workspaceRoot) {
6379
- const apps = (0, import_node_path28.join)(workspaceRoot, "apps");
6696
+ const apps = (0, import_node_path30.join)(workspaceRoot, "apps");
6380
6697
  let entries;
6381
6698
  try {
6382
- entries = (0, import_node_fs26.readdirSync)(apps);
6699
+ entries = (0, import_node_fs28.readdirSync)(apps);
6383
6700
  } catch {
6384
6701
  return [];
6385
6702
  }
6386
6703
  const updated = [];
6387
6704
  for (const entry of entries) {
6388
- const manifestPath = (0, import_node_path28.join)(apps, entry, "package.json");
6705
+ const manifestPath = (0, import_node_path30.join)(apps, entry, "package.json");
6389
6706
  if (!fileExists(manifestPath)) {
6390
6707
  continue;
6391
6708
  }
@@ -6405,22 +6722,22 @@ function refreshLockFile(workspaceRoot, name) {
6405
6722
  }
6406
6723
  function addVscodeExtension(workspaceRoot, name, stack, options = {}) {
6407
6724
  const { sidecar } = options;
6408
- if (sidecar !== void 0 && !fileExists((0, import_node_path28.join)(workspaceRoot, "apps", sidecar, "project.json"))) {
6725
+ if (sidecar !== void 0 && !fileExists((0, import_node_path30.join)(workspaceRoot, "apps", sidecar, "project.json"))) {
6409
6726
  throw new Error(`--sidecar ${sidecar}: no Go app at apps/${sidecar}. Add it first with \`mnci add go-app ${sidecar}\`.`);
6410
6727
  }
6411
6728
  runNodeApp(workspaceRoot, name, stack, "none");
6412
6729
  ensureVscodeToolchain(workspaceRoot);
6413
- const projectRoot = (0, import_node_path28.join)(workspaceRoot, "apps", name);
6730
+ const projectRoot = (0, import_node_path30.join)(workspaceRoot, "apps", name);
6414
6731
  reshapeManifest(projectRoot, name, options.publisher ?? defaultPublisher(workspaceRoot));
6415
- addVscodeTypes((0, import_node_path28.join)(projectRoot, "tsconfig.app.json"));
6416
- addVscodeTypes((0, import_node_path28.join)(projectRoot, "tsconfig.spec.json"));
6417
- writeFileEnsured((0, import_node_path28.join)(projectRoot, "src/main.ts"), vscodeExtensionMain(name));
6418
- writeFileEnsured((0, import_node_path28.join)(projectRoot, "src/main.spec.ts"), vscodeExtensionMainSpec(name));
6419
- writeFileEnsured((0, import_node_path28.join)(projectRoot, VSCODE_STUB_PATH), VSCODE_STUB);
6732
+ addVscodeTypes((0, import_node_path30.join)(projectRoot, "tsconfig.app.json"));
6733
+ addVscodeTypes((0, import_node_path30.join)(projectRoot, "tsconfig.spec.json"));
6734
+ writeFileEnsured((0, import_node_path30.join)(projectRoot, "src/main.ts"), vscodeExtensionMain(name));
6735
+ writeFileEnsured((0, import_node_path30.join)(projectRoot, "src/main.spec.ts"), vscodeExtensionMainSpec(name));
6736
+ writeFileEnsured((0, import_node_path30.join)(projectRoot, VSCODE_STUB_PATH), VSCODE_STUB);
6420
6737
  mapVscodeToStub(projectRoot, stack.testRunner);
6421
- writeFileEnsured((0, import_node_path28.join)(projectRoot, ".vscodeignore"), VSCODE_IGNORE);
6422
- writeFileEnsured((0, import_node_path28.join)(workspaceRoot, VSCODE_EXTENSION_SCRIPT_PATH), VSCODE_EXTENSION_SCRIPT);
6423
- addNxTargets((0, import_node_path28.join)(projectRoot, "package.json"), {
6738
+ writeFileEnsured((0, import_node_path30.join)(projectRoot, ".vscodeignore"), VSCODE_IGNORE);
6739
+ writeFileEnsured((0, import_node_path30.join)(workspaceRoot, VSCODE_EXTENSION_SCRIPT_PATH), VSCODE_EXTENSION_SCRIPT);
6740
+ addNxTargets((0, import_node_path30.join)(projectRoot, "package.json"), {
6424
6741
  "package": vscodeExtensionPackageTarget(name, sidecar),
6425
6742
  "nx-release-publish": vscodeExtensionPublishTarget(name, sidecar)
6426
6743
  });
@@ -6431,12 +6748,12 @@ function addVscodeExtension(workspaceRoot, name, stack, options = {}) {
6431
6748
  }
6432
6749
 
6433
6750
  // src/dependency-management/install-dependencies.use-case.ts
6434
- var import_node_fs28 = require("fs");
6435
- var import_node_path30 = require("path");
6751
+ var import_node_fs30 = require("fs");
6752
+ var import_node_path32 = require("path");
6436
6753
 
6437
6754
  // src/dependency-management/manifest.repository.ts
6438
- var import_node_fs27 = require("fs");
6439
- var import_node_path29 = require("path");
6755
+ var import_node_fs29 = require("fs");
6756
+ var import_node_path31 = require("path");
6440
6757
  var ECOSYSTEMS = ["npm", "pip", "pub", "nuget", "go"];
6441
6758
  var ROOT_LABEL = "(root)";
6442
6759
  var MANIFEST_GLOBS = {
@@ -6469,23 +6786,23 @@ function projectOf(manifestRelativePath) {
6469
6786
  }
6470
6787
  function hasEcosystem(workspaceRoot, ecosystem) {
6471
6788
  if (ecosystem === "go") {
6472
- return fileExists((0, import_node_path29.join)(workspaceRoot, "go.mod"));
6789
+ return fileExists((0, import_node_path31.join)(workspaceRoot, "go.mod"));
6473
6790
  }
6474
6791
  if (ecosystem === "nuget") {
6475
- return (0, import_node_fs27.globSync)(MANIFEST_GLOBS.nuget, { cwd: workspaceRoot }).length > 0;
6792
+ return (0, import_node_fs29.globSync)(MANIFEST_GLOBS.nuget, { cwd: workspaceRoot }).length > 0;
6476
6793
  }
6477
6794
  const rootMarker = {
6478
6795
  npm: "package.json",
6479
6796
  pip: "requirements-dev.txt",
6480
6797
  pub: "pubspec.yaml"
6481
6798
  };
6482
- return fileExists((0, import_node_path29.join)(workspaceRoot, rootMarker[ecosystem])) || (0, import_node_fs27.globSync)(MANIFEST_GLOBS[ecosystem], { cwd: workspaceRoot }).length > 0;
6799
+ return fileExists((0, import_node_path31.join)(workspaceRoot, rootMarker[ecosystem])) || (0, import_node_fs29.globSync)(MANIFEST_GLOBS[ecosystem], { cwd: workspaceRoot }).length > 0;
6483
6800
  }
6484
6801
  function collectNpm(workspaceRoot) {
6485
6802
  const sites = [];
6486
- const relativePaths = ["package.json", ...(0, import_node_fs27.globSync)(MANIFEST_GLOBS.npm, { cwd: workspaceRoot })];
6803
+ const relativePaths = ["package.json", ...(0, import_node_fs29.globSync)(MANIFEST_GLOBS.npm, { cwd: workspaceRoot })];
6487
6804
  for (const relativePath of relativePaths) {
6488
- const manifestPath = (0, import_node_path29.join)(workspaceRoot, relativePath);
6805
+ const manifestPath = (0, import_node_path31.join)(workspaceRoot, relativePath);
6489
6806
  try {
6490
6807
  const manifest = readJson(manifestPath);
6491
6808
  sites.push(...npmManifestSites(manifest, relativePath, manifestPath));
@@ -6546,17 +6863,17 @@ function collectPip(workspaceRoot) {
6546
6863
  name: parsed.name,
6547
6864
  ecosystem: "pip",
6548
6865
  project: projectOf(relativePath),
6549
- manifestPath: (0, import_node_path29.join)(workspaceRoot, relativePath),
6866
+ manifestPath: (0, import_node_path31.join)(workspaceRoot, relativePath),
6550
6867
  section,
6551
6868
  spec: parsed.spec,
6552
6869
  rewritable: parsed.rewritable && parsed.spec !== ""
6553
6870
  });
6554
6871
  };
6555
- const pyprojectPaths = (0, import_node_fs27.globSync)(MANIFEST_GLOBS.pip, { cwd: workspaceRoot });
6872
+ const pyprojectPaths = (0, import_node_fs29.globSync)(MANIFEST_GLOBS.pip, { cwd: workspaceRoot });
6556
6873
  for (const relativePath of pyprojectPaths) {
6557
6874
  let content;
6558
6875
  try {
6559
- content = (0, import_node_fs27.readFileSync)((0, import_node_path29.join)(workspaceRoot, relativePath), "utf8");
6876
+ content = (0, import_node_fs29.readFileSync)((0, import_node_path31.join)(workspaceRoot, relativePath), "utf8");
6560
6877
  } catch {
6561
6878
  continue;
6562
6879
  }
@@ -6567,14 +6884,14 @@ function collectPip(workspaceRoot) {
6567
6884
  }
6568
6885
  const requirementsPaths = [
6569
6886
  "requirements-dev.txt",
6570
- ...(0, import_node_fs27.globSync)("apps/*/requirements.txt", { cwd: workspaceRoot })
6887
+ ...(0, import_node_fs29.globSync)("apps/*/requirements.txt", { cwd: workspaceRoot })
6571
6888
  ];
6572
6889
  for (const relativePath of requirementsPaths) {
6573
- if (!fileExists((0, import_node_path29.join)(workspaceRoot, relativePath))) {
6890
+ if (!fileExists((0, import_node_path31.join)(workspaceRoot, relativePath))) {
6574
6891
  continue;
6575
6892
  }
6576
6893
  const section = relativePath === "requirements-dev.txt" ? "devDep" : "dep";
6577
- const lines = (0, import_node_fs27.readFileSync)((0, import_node_path29.join)(workspaceRoot, relativePath), "utf8").split(/\r?\n/);
6894
+ const lines = (0, import_node_fs29.readFileSync)((0, import_node_path31.join)(workspaceRoot, relativePath), "utf8").split(/\r?\n/);
6578
6895
  for (const line of lines) {
6579
6896
  push(relativePath, section, line);
6580
6897
  }
@@ -6612,11 +6929,11 @@ function pyprojectDependencies(content) {
6612
6929
  }
6613
6930
  function collectPub(workspaceRoot) {
6614
6931
  const sites = [];
6615
- const pubspecPaths = (0, import_node_fs27.globSync)(MANIFEST_GLOBS.pub, { cwd: workspaceRoot });
6932
+ const pubspecPaths = (0, import_node_fs29.globSync)(MANIFEST_GLOBS.pub, { cwd: workspaceRoot });
6616
6933
  for (const relativePath of pubspecPaths) {
6617
6934
  let content;
6618
6935
  try {
6619
- content = (0, import_node_fs27.readFileSync)((0, import_node_path29.join)(workspaceRoot, relativePath), "utf8");
6936
+ content = (0, import_node_fs29.readFileSync)((0, import_node_path31.join)(workspaceRoot, relativePath), "utf8");
6620
6937
  } catch {
6621
6938
  continue;
6622
6939
  }
@@ -6626,7 +6943,7 @@ function collectPub(workspaceRoot) {
6626
6943
  name: entry.name,
6627
6944
  ecosystem: "pub",
6628
6945
  project: projectOf(relativePath),
6629
- manifestPath: (0, import_node_path29.join)(workspaceRoot, relativePath),
6946
+ manifestPath: (0, import_node_path31.join)(workspaceRoot, relativePath),
6630
6947
  section: block === "dependencies" ? "dep" : "devDep",
6631
6948
  spec: entry.spec,
6632
6949
  rewritable: entry.spec !== ""
@@ -6676,11 +6993,11 @@ function stripQuotes(value) {
6676
6993
  }
6677
6994
  function collectNuget(workspaceRoot) {
6678
6995
  const sites = [];
6679
- const csprojPaths = (0, import_node_fs27.globSync)(MANIFEST_GLOBS.nuget, { cwd: workspaceRoot });
6996
+ const csprojPaths = (0, import_node_fs29.globSync)(MANIFEST_GLOBS.nuget, { cwd: workspaceRoot });
6680
6997
  for (const relativePath of csprojPaths) {
6681
6998
  let content;
6682
6999
  try {
6683
- content = (0, import_node_fs27.readFileSync)((0, import_node_path29.join)(workspaceRoot, relativePath), "utf8");
7000
+ content = (0, import_node_fs29.readFileSync)((0, import_node_path31.join)(workspaceRoot, relativePath), "utf8");
6684
7001
  } catch {
6685
7002
  continue;
6686
7003
  }
@@ -6689,7 +7006,7 @@ function collectNuget(workspaceRoot) {
6689
7006
  name: entry.name,
6690
7007
  ecosystem: "nuget",
6691
7008
  project: projectOf(relativePath),
6692
- manifestPath: (0, import_node_path29.join)(workspaceRoot, relativePath),
7009
+ manifestPath: (0, import_node_path31.join)(workspaceRoot, relativePath),
6693
7010
  section: "dep",
6694
7011
  spec: entry.spec,
6695
7012
  // A plain SemVer string is rewritable; an MSBuild property reference
@@ -6714,13 +7031,13 @@ function csprojPackageReferences(content) {
6714
7031
  return entries;
6715
7032
  }
6716
7033
  function collectGo(workspaceRoot) {
6717
- const manifestPath = (0, import_node_path29.join)(workspaceRoot, "go.mod");
7034
+ const manifestPath = (0, import_node_path31.join)(workspaceRoot, "go.mod");
6718
7035
  if (!fileExists(manifestPath)) {
6719
7036
  return [];
6720
7037
  }
6721
7038
  let content;
6722
7039
  try {
6723
- content = (0, import_node_fs27.readFileSync)(manifestPath, "utf8");
7040
+ content = (0, import_node_fs29.readFileSync)(manifestPath, "utf8");
6724
7041
  } catch {
6725
7042
  return [];
6726
7043
  }
@@ -6769,7 +7086,7 @@ var PROJECT_GLOBS = {
6769
7086
  function locateProjects(workspaceRoot) {
6770
7087
  const locations = [];
6771
7088
  for (const ecosystem of ECOSYSTEMS) {
6772
- const relativePaths = (0, import_node_fs27.globSync)(PROJECT_GLOBS[ecosystem], { cwd: workspaceRoot });
7089
+ const relativePaths = (0, import_node_fs29.globSync)(PROJECT_GLOBS[ecosystem], { cwd: workspaceRoot });
6773
7090
  for (const relativePath of relativePaths) {
6774
7091
  const posix = toPosix(relativePath);
6775
7092
  const dir = posix.slice(0, posix.lastIndexOf("/"));
@@ -6777,7 +7094,7 @@ function locateProjects(workspaceRoot) {
6777
7094
  ecosystem,
6778
7095
  dir,
6779
7096
  name: dir.slice(dir.lastIndexOf("/") + 1),
6780
- manifestPath: (0, import_node_path29.join)(workspaceRoot, relativePath)
7097
+ manifestPath: (0, import_node_path31.join)(workspaceRoot, relativePath)
6781
7098
  });
6782
7099
  }
6783
7100
  }
@@ -6798,7 +7115,7 @@ function rewriteSpec(site, spec) {
6798
7115
  writeFileEnsured(site.manifestPath, toJson(manifest));
6799
7116
  return true;
6800
7117
  }
6801
- const before = (0, import_node_fs27.readFileSync)(site.manifestPath, "utf8");
7118
+ const before = (0, import_node_fs29.readFileSync)(site.manifestPath, "utf8");
6802
7119
  const after = (() => {
6803
7120
  switch (site.ecosystem) {
6804
7121
  case "pip": {
@@ -6889,13 +7206,13 @@ function escapeForRegex(value) {
6889
7206
  function isWorkspaceProject(workspaceRoot, name) {
6890
7207
  const short = name.includes("/") ? name.split("/").at(-1) : name;
6891
7208
  return ["apps", "libs", "packages"].some(
6892
- (directory) => fileExists((0, import_node_path29.join)(workspaceRoot, directory, short ?? name, "package.json"))
7209
+ (directory) => fileExists((0, import_node_path31.join)(workspaceRoot, directory, short ?? name, "package.json"))
6893
7210
  );
6894
7211
  }
6895
7212
  function isAliasedInstall(workspaceRoot, name) {
6896
7213
  try {
6897
7214
  const installed = readJson(
6898
- (0, import_node_path29.join)(workspaceRoot, "node_modules", name, "package.json")
7215
+ (0, import_node_path31.join)(workspaceRoot, "node_modules", name, "package.json")
6899
7216
  );
6900
7217
  return installed.name !== void 0 && installed.name !== name;
6901
7218
  } catch {
@@ -6906,14 +7223,14 @@ function resolvedVersion(workspaceRoot, ecosystem, name) {
6906
7223
  if (ecosystem === "npm") {
6907
7224
  try {
6908
7225
  return readJson(
6909
- (0, import_node_path29.join)(workspaceRoot, "node_modules", name, "package.json")
7226
+ (0, import_node_path31.join)(workspaceRoot, "node_modules", name, "package.json")
6910
7227
  ).version;
6911
7228
  } catch {
6912
7229
  return void 0;
6913
7230
  }
6914
7231
  }
6915
7232
  if (ecosystem === "pub") {
6916
- const lockPath = (0, import_node_path29.join)(workspaceRoot, "pubspec.lock");
7233
+ const lockPath = (0, import_node_path31.join)(workspaceRoot, "pubspec.lock");
6917
7234
  if (!fileExists(lockPath)) {
6918
7235
  return void 0;
6919
7236
  }
@@ -6921,7 +7238,7 @@ function resolvedVersion(workspaceRoot, ecosystem, name) {
6921
7238
  const match = new RegExp(
6922
7239
  String.raw`^\s{2}${escapeForRegex(name)}:[^]*?^\s+version:\s*["']?([^"'\s]+)`,
6923
7240
  "m"
6924
- ).exec((0, import_node_fs27.readFileSync)(lockPath, "utf8"));
7241
+ ).exec((0, import_node_fs29.readFileSync)(lockPath, "utf8"));
6925
7242
  return match?.[1];
6926
7243
  } catch {
6927
7244
  return void 0;
@@ -6987,7 +7304,7 @@ function resolveTarget(locations, target) {
6987
7304
  return byName[0];
6988
7305
  }
6989
7306
  function installInto(workspaceRoot, location, packages, saveDev) {
6990
- const projectDir = (0, import_node_path30.join)(workspaceRoot, location.dir);
7307
+ const projectDir = (0, import_node_path32.join)(workspaceRoot, location.dir);
6991
7308
  switch (location.ecosystem) {
6992
7309
  case "npm": {
6993
7310
  const flags = saveDev ? ["--save-dev"] : [];
@@ -7040,7 +7357,7 @@ function installInto(workspaceRoot, location, packages, saveDev) {
7040
7357
  function addPythonDependency(manifestPath, specifier) {
7041
7358
  let content;
7042
7359
  try {
7043
- content = (0, import_node_fs28.readFileSync)(manifestPath, "utf8");
7360
+ content = (0, import_node_fs30.readFileSync)(manifestPath, "utf8");
7044
7361
  } catch {
7045
7362
  logger.warn(`Could not read ${manifestPath} \u2014 skipped ${specifier}`);
7046
7363
  return;
@@ -7069,7 +7386,7 @@ function installEverything(workspaceRoot, only) {
7069
7386
  }
7070
7387
  function ecosystemPresent(workspaceRoot, ecosystem) {
7071
7388
  if (ecosystem === "go") {
7072
- return fileExists((0, import_node_path30.join)(workspaceRoot, "go.work")) || fileExists((0, import_node_path30.join)(workspaceRoot, "go.mod"));
7389
+ return fileExists((0, import_node_path32.join)(workspaceRoot, "go.work")) || fileExists((0, import_node_path32.join)(workspaceRoot, "go.mod"));
7073
7390
  }
7074
7391
  return hasEcosystem(workspaceRoot, ecosystem);
7075
7392
  }
@@ -7088,7 +7405,7 @@ function splitAtLast(value, separator) {
7088
7405
  }
7089
7406
 
7090
7407
  // src/dependency-management/sync-dependencies.use-case.ts
7091
- var import_node_path31 = require("path");
7408
+ var import_node_path33 = require("path");
7092
7409
 
7093
7410
  // src/dependency-management/semver.algorithm.ts
7094
7411
  var UPDATE_KINDS = ["patch", "minor", "major", "non-semver"];
@@ -7265,7 +7582,7 @@ function describe(finding) {
7265
7582
  return `${finding.name} \u2192 ${finding.target} (${finding.reason}); disagreeing: ${sites}`;
7266
7583
  }
7267
7584
  function runSync(workspaceRoot, options) {
7268
- if (!fileExists((0, import_node_path31.join)(workspaceRoot, "nx.json"))) {
7585
+ if (!fileExists((0, import_node_path33.join)(workspaceRoot, "nx.json"))) {
7269
7586
  throw new Error(
7270
7587
  `No nx.json found in ${workspaceRoot} \u2014 run 'mnci sync' from the workspace root.`
7271
7588
  );
@@ -7315,7 +7632,7 @@ function runSync(workspaceRoot, options) {
7315
7632
  }
7316
7633
 
7317
7634
  // src/dependency-management/update-dependencies.use-case.ts
7318
- var import_node_path32 = require("path");
7635
+ var import_node_path34 = require("path");
7319
7636
  var import_prompts2 = require("@inquirer/prompts");
7320
7637
 
7321
7638
  // src/dependency-management/registry.client.ts
@@ -7598,7 +7915,7 @@ function reinstall(workspaceRoot, ecosystems) {
7598
7915
  function hasPythonInstallScript(workspaceRoot) {
7599
7916
  try {
7600
7917
  const manifest = readJson(
7601
- (0, import_node_path32.join)(workspaceRoot, "package.json")
7918
+ (0, import_node_path34.join)(workspaceRoot, "package.json")
7602
7919
  );
7603
7920
  return manifest.scripts?.["python:install"] !== void 0;
7604
7921
  } catch {
@@ -7614,7 +7931,7 @@ function applyGoUpdates(workspaceRoot, entries) {
7614
7931
  }
7615
7932
  }
7616
7933
  async function runUp(workspaceRoot, options) {
7617
- if (!fileExists((0, import_node_path32.join)(workspaceRoot, "nx.json"))) {
7934
+ if (!fileExists((0, import_node_path34.join)(workspaceRoot, "nx.json"))) {
7618
7935
  throw new Error(`No nx.json found in ${workspaceRoot} \u2014 run 'mnci up' from the workspace root.`);
7619
7936
  }
7620
7937
  const ecosystems = resolveEcosystems(options.ecosystem);
@@ -7686,12 +8003,13 @@ var PROJECT_KINDS = [
7686
8003
  "csharp-function-app",
7687
8004
  "csharp-lib",
7688
8005
  "csharp-internal-lib",
7689
- "vscode-extension"
8006
+ "vscode-extension",
8007
+ "container"
7690
8008
  ];
7691
8009
  var EMPTY_KINDS = /* @__PURE__ */ new Set(["npm-lib", "internal-lib", "react-lib", "react-internal-lib", "node-function-app"]);
7692
8010
  async function runAdd(kind, name, options) {
7693
8011
  const workspaceRoot = process.cwd();
7694
- if (!fileExists((0, import_node_path33.join)(workspaceRoot, "nx.json"))) {
8012
+ if (!fileExists((0, import_node_path35.join)(workspaceRoot, "nx.json"))) {
7695
8013
  throw new Error("No nx.json found here. Run `add` from the workspace root.");
7696
8014
  }
7697
8015
  const stack = readWorkspaceStack(workspaceRoot);
@@ -7712,13 +8030,35 @@ async function runAdd(kind, name, options) {
7712
8030
  if (options.empty === true && !EMPTY_KINDS.has(resolvedKind)) {
7713
8031
  throw new Error(`--empty applies to ${[...EMPTY_KINDS].join(", ")}, not ${resolvedKind}.`);
7714
8032
  }
8033
+ if (options.esm === true) {
8034
+ if (resolvedKind !== "node-app" && resolvedKind !== "node-function-app") {
8035
+ throw new Error(`--esm applies to node-app and node-function-app, not ${resolvedKind}.`);
8036
+ }
8037
+ if (options.framework === "fastify" || options.framework === "nest") {
8038
+ throw new Error(`--esm is not supported with --framework ${options.framework}: its layout (autoloaded routes, decorators) is mandated by the framework. Use express, koa or none.`);
8039
+ }
8040
+ }
8041
+ if (resolvedKind === "container" && options.app === void 0) {
8042
+ throw new Error("container needs --app <project>: the app to put in an image, for example `mnci add container api-image --app api`.");
8043
+ }
8044
+ for (const [flag, requested] of [["--app", options.app !== void 0], ["--port", options.port !== void 0]]) {
8045
+ if (resolvedKind !== "container" && requested) {
8046
+ throw new Error(`${flag} applies to container, not ${resolvedKind}.`);
8047
+ }
8048
+ }
8049
+ if (options.port !== void 0 && !/^\d+$/.test(options.port)) {
8050
+ throw new Error(`--port ${options.port}: a port is a whole number.`);
8051
+ }
8052
+ if (resolvedKind !== "react-app" && options.e2e === true) {
8053
+ throw new Error(`--e2e applies to react-app, not ${resolvedKind}.`);
8054
+ }
7715
8055
  const resolvedName = name ?? await promptText("Project name");
7716
8056
  assertValidProjectName(resolvedName, "Project name");
7717
8057
  const rootDependenciesBefore = rootRuntimeDependencies(workspaceRoot);
7718
8058
  ensureNxPeerOverrides(workspaceRoot);
7719
8059
  switch (resolvedKind) {
7720
8060
  case "react-app": {
7721
- addReactApp(workspaceRoot, resolvedName, stack);
8061
+ addReactApp(workspaceRoot, resolvedName, stack, options.e2e === true);
7722
8062
  break;
7723
8063
  }
7724
8064
  case "react-lib": {
@@ -7730,11 +8070,11 @@ async function runAdd(kind, name, options) {
7730
8070
  break;
7731
8071
  }
7732
8072
  case "node-app": {
7733
- addNodeApp(workspaceRoot, resolvedName, stack, options.framework);
8073
+ addNodeApp(workspaceRoot, resolvedName, stack, options.framework, options.esm === true);
7734
8074
  break;
7735
8075
  }
7736
8076
  case "node-function-app": {
7737
- addNodeFunctionApp(workspaceRoot, resolvedName, stack, options.empty === true);
8077
+ addNodeFunctionApp(workspaceRoot, resolvedName, stack, options.empty === true, options.esm === true);
7738
8078
  break;
7739
8079
  }
7740
8080
  case "npm-lib": {
@@ -7754,8 +8094,8 @@ async function runAdd(kind, name, options) {
7754
8094
  ],
7755
8095
  workspaceRoot
7756
8096
  );
7757
- markPrivate((0, import_node_path33.join)(workspaceRoot, "libs", resolvedName, "package.json"));
7758
- renameScaffoldPlaceholder((0, import_node_path33.join)(workspaceRoot, "libs", resolvedName), resolvedName, options.empty === true);
8097
+ markPrivate((0, import_node_path35.join)(workspaceRoot, "libs", resolvedName, "package.json"));
8098
+ renameScaffoldPlaceholder((0, import_node_path35.join)(workspaceRoot, "libs", resolvedName), resolvedName, options.empty === true);
7759
8099
  removeGeneratedEslintConfig(workspaceRoot, `libs/${resolvedName}`);
7760
8100
  registerProjectCommands(workspaceRoot, resolvedName, { build: true });
7761
8101
  break;
@@ -7776,6 +8116,10 @@ async function runAdd(kind, name, options) {
7776
8116
  addPythonInternalLib(workspaceRoot, resolvedName);
7777
8117
  break;
7778
8118
  }
8119
+ case "container": {
8120
+ addContainer(workspaceRoot, resolvedName, { app: options.app, port: options.port === void 0 ? void 0 : Number(options.port) });
8121
+ break;
8122
+ }
7779
8123
  case "go-app": {
7780
8124
  addGoApp(workspaceRoot, resolvedName, { release: options.release, cgo: options.cgo, web: options.web });
7781
8125
  break;
@@ -7847,7 +8191,7 @@ async function runAdd(kind, name, options) {
7847
8191
  }
7848
8192
  function readWorkspaceStack(workspaceRoot) {
7849
8193
  const nxJson = readJson(
7850
- (0, import_node_path33.join)(workspaceRoot, "nx.json")
8194
+ (0, import_node_path35.join)(workspaceRoot, "nx.json")
7851
8195
  );
7852
8196
  const stack = nxJson.mnci?.stack;
7853
8197
  return {
@@ -7857,13 +8201,14 @@ function readWorkspaceStack(workspaceRoot) {
7857
8201
 
7858
8202
  // src/project-scaffolding/project-kind-catalog.config.ts
7859
8203
  var PROJECT_KIND_CATALOG = [
7860
- { kind: "react-app", language: "typescript", label: "React app", description: "A Vite React app, built per environment", flags: [], requiredFlags: [] },
8204
+ { kind: "react-app", language: "typescript", label: "React app", description: "A Vite React app, built per environment", flags: ["e2e"], requiredFlags: [] },
7861
8205
  { kind: "react-lib", language: "typescript", label: "React library", description: "A publishable React component library", flags: ["scope", "empty"], requiredFlags: [] },
7862
8206
  { kind: "react-internal-lib", language: "typescript", label: "React internal library", description: "A private React library for apps in this workspace", flags: ["empty"], requiredFlags: [] },
7863
- { kind: "node-app", language: "typescript", label: "Node app", description: "A Node service, with Express, Fastify, Koa or Nest if you choose one", flags: ["framework"], requiredFlags: [] },
7864
- { kind: "node-function-app", language: "typescript", label: "Node function app", description: "An Azure Functions (v4) app in TypeScript", flags: ["empty"], requiredFlags: [] },
8207
+ { kind: "node-app", language: "typescript", label: "Node app", description: "A Node service, with Express, Fastify, Koa or Nest if you choose one", flags: ["framework", "esm"], requiredFlags: [] },
8208
+ { kind: "node-function-app", language: "typescript", label: "Node function app", description: "An Azure Functions (v4) app in TypeScript", flags: ["empty", "esm"], requiredFlags: [] },
7865
8209
  { kind: "npm-lib", language: "typescript", label: "npm library", description: "A library published to npm, bundled with Rollup", flags: ["scope", "empty"], requiredFlags: [] },
7866
8210
  { kind: "internal-lib", language: "typescript", label: "Internal library", description: "A private TypeScript library for apps in this workspace", flags: ["empty"], requiredFlags: [] },
8211
+ { kind: "container", language: "container", label: "Container image", description: "A Dockerfile and image targets for an existing Node, React or Go app", flags: ["app", "port"], requiredFlags: ["app"] },
7867
8212
  { kind: "vscode-extension", language: "typescript", label: "VS Code extension", description: "A bundled Marketplace extension, optionally shipping a Go sidecar", flags: ["publisher", "sidecar"], requiredFlags: [] },
7868
8213
  { kind: "python-app", language: "python", label: "Python app", description: "A pip-native Python application (Ruff, pytest)", flags: [], requiredFlags: [] },
7869
8214
  { kind: "python-function-app", language: "python", label: "Python function app", description: "An Azure Functions (v2 model) app in Python", flags: [], requiredFlags: [] },
@@ -7896,12 +8241,12 @@ function runKinds(options) {
7896
8241
 
7897
8242
  // src/workspace-diagnostics/check-invariants.use-case.ts
7898
8243
  var import_node_child_process = require("child_process");
7899
- var import_node_fs30 = require("fs");
7900
- var import_node_path35 = require("path");
8244
+ var import_node_fs32 = require("fs");
8245
+ var import_node_path37 = require("path");
7901
8246
 
7902
8247
  // src/workspace-diagnostics/locate-stranded-release-tags.use-case.ts
7903
- var import_node_fs29 = require("fs");
7904
- var import_node_path34 = require("path");
8248
+ var import_node_fs31 = require("fs");
8249
+ var import_node_path36 = require("path");
7905
8250
 
7906
8251
  // src/workspace-diagnostics/stranded-release-tags.algorithm.ts
7907
8252
  function compareVersions2(left, right) {
@@ -7938,7 +8283,7 @@ function locateStrandedReleaseTags(workspaceRoot, tagPattern) {
7938
8283
  if (listed.status !== 0) {
7939
8284
  return [];
7940
8285
  }
7941
- const projects = (0, import_node_fs29.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot }).map((path) => readJson((0, import_node_path34.join)(workspaceRoot, path))).filter((manifest) => manifest.private !== true && manifest.name !== void 0).map((manifest) => manifest.name);
8286
+ const projects = (0, import_node_fs31.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot }).map((path) => readJson((0, import_node_path36.join)(workspaceRoot, path))).filter((manifest) => manifest.private !== true && manifest.name !== void 0).map((manifest) => manifest.name);
7942
8287
  return findStrandedReleaseTags(projects, listed.stdout.split(/\r?\n/).filter(Boolean));
7943
8288
  }
7944
8289
 
@@ -7948,8 +8293,8 @@ function toPosix2(path) {
7948
8293
  return path.replaceAll("\\", "/");
7949
8294
  }
7950
8295
  function checkEslintConfigs(workspaceRoot) {
7951
- const rootConfigs = (0, import_node_fs30.globSync)("eslint.config.{js,mjs,cjs,ts,mts,cts}", { cwd: workspaceRoot });
7952
- const projectConfigs = (0, import_node_fs30.globSync)("{apps,libs,packages}/*/eslint.config.{js,mjs,cjs,ts,mts,cts}", {
8296
+ const rootConfigs = (0, import_node_fs32.globSync)("eslint.config.{js,mjs,cjs,ts,mts,cts}", { cwd: workspaceRoot });
8297
+ const projectConfigs = (0, import_node_fs32.globSync)("{apps,libs,packages}/*/eslint.config.{js,mjs,cjs,ts,mts,cts}", {
7953
8298
  cwd: workspaceRoot
7954
8299
  }).map((config) => toPosix2(config));
7955
8300
  return [
@@ -7969,10 +8314,10 @@ function checkEslintConfigs(workspaceRoot) {
7969
8314
  ];
7970
8315
  }
7971
8316
  function checkEslintEntryPointReachesTheRules(workspaceRoot) {
7972
- const rules = (0, import_node_path35.join)(workspaceRoot, ESLINT_MNCI_FILENAME);
7973
- if (!(0, import_node_fs30.existsSync)(rules)) return [];
7974
- const entryPoint = (0, import_node_path35.join)(workspaceRoot, ESLINT_USER_FILENAME);
7975
- const source = (0, import_node_fs30.existsSync)(entryPoint) ? (0, import_node_fs30.readFileSync)(entryPoint, "utf8") : "";
8317
+ const rules = (0, import_node_path37.join)(workspaceRoot, ESLINT_MNCI_FILENAME);
8318
+ if (!(0, import_node_fs32.existsSync)(rules)) return [];
8319
+ const entryPoint = (0, import_node_path37.join)(workspaceRoot, ESLINT_USER_FILENAME);
8320
+ const source = (0, import_node_fs32.existsSync)(entryPoint) ? (0, import_node_fs32.readFileSync)(entryPoint, "utf8") : "";
7976
8321
  return [
7977
8322
  {
7978
8323
  check: `${ESLINT_USER_FILENAME} imports the mnci rules`,
@@ -7985,7 +8330,7 @@ function checkEslintEntryPointReachesTheRules(workspaceRoot) {
7985
8330
  function declaredDevDependencies(workspaceRoot) {
7986
8331
  try {
7987
8332
  const manifest = readJson(
7988
- (0, import_node_path35.join)(workspaceRoot, "package.json")
8333
+ (0, import_node_path37.join)(workspaceRoot, "package.json")
7989
8334
  );
7990
8335
  return manifest.devDependencies ?? {};
7991
8336
  } catch {
@@ -7993,7 +8338,7 @@ function declaredDevDependencies(workspaceRoot) {
7993
8338
  }
7994
8339
  }
7995
8340
  function checkNoRetiredFormatter(workspaceRoot) {
7996
- const files = RETIRED_FORMATTER_FILES.filter((file) => fileExists((0, import_node_path35.join)(workspaceRoot, file)));
8341
+ const files = RETIRED_FORMATTER_FILES.filter((file) => fileExists((0, import_node_path37.join)(workspaceRoot, file)));
7997
8342
  const declared = ["prettier", "eslint-config-prettier", "oxlint", "oxfmt", "@mnci/oxlint-config"].filter((name) => declaredDevDependencies(workspaceRoot)[name] !== void 0);
7998
8343
  const stale = [...files, ...declared];
7999
8344
  if (stale.length === 0) {
@@ -8019,7 +8364,7 @@ function checkEslintPlugin(nxJson) {
8019
8364
  };
8020
8365
  }
8021
8366
  function checkResolvedEslint(workspaceRoot) {
8022
- const manifestPath = (0, import_node_path35.join)(workspaceRoot, "node_modules/eslint/package.json");
8367
+ const manifestPath = (0, import_node_path37.join)(workspaceRoot, "node_modules/eslint/package.json");
8023
8368
  if (!fileExists(manifestPath)) {
8024
8369
  return void 0;
8025
8370
  }
@@ -8037,11 +8382,11 @@ function checkResolvedEslint(workspaceRoot) {
8037
8382
  }
8038
8383
  }
8039
8384
  function checkNpmrc(workspaceRoot, registry, scope) {
8040
- const npmrcPath = (0, import_node_path35.join)(workspaceRoot, ".npmrc");
8385
+ const npmrcPath = (0, import_node_path37.join)(workspaceRoot, ".npmrc");
8041
8386
  if (!registry || !fileExists(npmrcPath)) {
8042
8387
  return void 0;
8043
8388
  }
8044
- const npmrc = (0, import_node_fs30.readFileSync)(npmrcPath, "utf8");
8389
+ const npmrc = (0, import_node_fs32.readFileSync)(npmrcPath, "utf8");
8045
8390
  if (registry.kind === "npm") {
8046
8391
  return {
8047
8392
  check: ".npmrc authenticates the public registry",
@@ -8058,11 +8403,11 @@ function checkNpmrc(workspaceRoot, registry, scope) {
8058
8403
  };
8059
8404
  }
8060
8405
  function checkNpmrcCredentialResolves(workspaceRoot, environment = process.env) {
8061
- const npmrcPath = (0, import_node_path35.join)(workspaceRoot, ".npmrc");
8406
+ const npmrcPath = (0, import_node_path37.join)(workspaceRoot, ".npmrc");
8062
8407
  if (!fileExists(npmrcPath)) {
8063
8408
  return void 0;
8064
8409
  }
8065
- const npmrc = (0, import_node_fs30.readFileSync)(npmrcPath, "utf8");
8410
+ const npmrc = (0, import_node_fs32.readFileSync)(npmrcPath, "utf8");
8066
8411
  const credentials = npmrc.split("\n").filter((line) => !line.trimStart().startsWith(";") && !line.trimStart().startsWith("#")).filter((line) => /(?:_authToken|_password|_auth)\s*=/u.test(line));
8067
8412
  const referenced = [
8068
8413
  ...new Set(
@@ -8086,13 +8431,13 @@ function checkNpmrcCredentialResolves(workspaceRoot, environment = process.env)
8086
8431
  };
8087
8432
  }
8088
8433
  function checkNpmAuthMatchesPipeline(workspaceRoot, registry) {
8089
- const npmrcPath = (0, import_node_path35.join)(workspaceRoot, ".npmrc");
8090
- const pipelinePath = (0, import_node_path35.join)(workspaceRoot, "azure-pipelines.yml");
8434
+ const npmrcPath = (0, import_node_path37.join)(workspaceRoot, ".npmrc");
8435
+ const pipelinePath = (0, import_node_path37.join)(workspaceRoot, "azure-pipelines.yml");
8091
8436
  if (registry?.kind !== "azure-artifacts" || !fileExists(npmrcPath) || !fileExists(pipelinePath)) {
8092
8437
  return void 0;
8093
8438
  }
8094
- const holdsCredentials = (0, import_node_fs30.readFileSync)(npmrcPath, "utf8").split("\n").filter((line) => !line.trimStart().startsWith(";") && !line.trimStart().startsWith("#")).some((line) => /(?:_authToken|_password|_auth)\s*=/u.test(line));
8095
- const runsTask = (0, import_node_fs30.readFileSync)(pipelinePath, "utf8").includes("npmAuthenticate@0");
8439
+ const holdsCredentials = (0, import_node_fs32.readFileSync)(npmrcPath, "utf8").split("\n").filter((line) => !line.trimStart().startsWith(";") && !line.trimStart().startsWith("#")).some((line) => /(?:_authToken|_password|_auth)\s*=/u.test(line));
8440
+ const runsTask = (0, import_node_fs32.readFileSync)(pipelinePath, "utf8").includes("npmAuthenticate@0");
8096
8441
  if (!holdsCredentials && !runsTask) {
8097
8442
  return {
8098
8443
  check: "npm auth: .npmrc and azure-pipelines.yml agree",
@@ -8137,13 +8482,13 @@ function npmjsCredentialShape(credentials, environment) {
8137
8482
  }
8138
8483
  function checkVersionActions(workspaceRoot) {
8139
8484
  const candidates = [
8140
- ...(0, import_node_fs30.globSync)("packages/*/pubspec.yaml", { cwd: workspaceRoot }),
8141
- ...(0, import_node_fs30.globSync)("packages/*/*.csproj", { cwd: workspaceRoot }),
8142
- ...(0, import_node_fs30.globSync)("python-packages/*/pyproject.toml", { cwd: workspaceRoot })
8485
+ ...(0, import_node_fs32.globSync)("packages/*/pubspec.yaml", { cwd: workspaceRoot }),
8486
+ ...(0, import_node_fs32.globSync)("packages/*/*.csproj", { cwd: workspaceRoot }),
8487
+ ...(0, import_node_fs32.globSync)("python-packages/*/pyproject.toml", { cwd: workspaceRoot })
8143
8488
  ].map((manifest) => toPosix2(manifest));
8144
8489
  return candidates.flatMap((manifest) => {
8145
8490
  const projectRoot = manifest.slice(0, manifest.lastIndexOf("/"));
8146
- const projectJsonPath = (0, import_node_path35.join)(workspaceRoot, projectRoot, "project.json");
8491
+ const projectJsonPath = (0, import_node_path37.join)(workspaceRoot, projectRoot, "project.json");
8147
8492
  let hasOverride = false;
8148
8493
  try {
8149
8494
  const projectJson = readJson(projectJsonPath);
@@ -8172,7 +8517,7 @@ function declaredTargets(workspaceRoot, projectRoot) {
8172
8517
  ]) {
8173
8518
  try {
8174
8519
  const targets = extract(
8175
- readJson((0, import_node_path35.join)(workspaceRoot, projectRoot, file))
8520
+ readJson((0, import_node_path37.join)(workspaceRoot, projectRoot, file))
8176
8521
  );
8177
8522
  if (targets && typeof targets === "object") {
8178
8523
  sources.push({
@@ -8188,8 +8533,8 @@ function declaredTargets(workspaceRoot, projectRoot) {
8188
8533
  function checkTargetFilesExist(workspaceRoot) {
8189
8534
  const projectRoots = new Set(
8190
8535
  [
8191
- ...(0, import_node_fs30.globSync)("{apps,libs,packages}/*/project.json", { cwd: workspaceRoot }),
8192
- ...(0, import_node_fs30.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot })
8536
+ ...(0, import_node_fs32.globSync)("{apps,libs,packages}/*/project.json", { cwd: workspaceRoot }),
8537
+ ...(0, import_node_fs32.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot })
8193
8538
  ].map((manifest) => toPosix2(manifest).split("/").slice(0, 2).join("/"))
8194
8539
  );
8195
8540
  return [...projectRoots].flatMap(
@@ -8204,7 +8549,7 @@ function checkOneTargetsFiles(workspaceRoot, source, targetName, target) {
8204
8549
  return TARGET_FILE_OPTIONS.flatMap((option) => {
8205
8550
  const value = target?.options?.[option];
8206
8551
  const checkable = typeof value === "string" && !value.includes("{");
8207
- if (!checkable || fileExists((0, import_node_path35.join)(workspaceRoot, value))) {
8552
+ if (!checkable || fileExists((0, import_node_path37.join)(workspaceRoot, value))) {
8208
8553
  return [];
8209
8554
  }
8210
8555
  return [
@@ -8243,15 +8588,15 @@ function checkGoPluginRegistered(workspaceRoot) {
8243
8588
  };
8244
8589
  }
8245
8590
  function checkGoWorkInSync(workspaceRoot) {
8246
- const goWorkPath = (0, import_node_path35.join)(workspaceRoot, "go.work");
8591
+ const goWorkPath = (0, import_node_path37.join)(workspaceRoot, "go.work");
8247
8592
  if (!fileExists(goWorkPath)) {
8248
8593
  return void 0;
8249
8594
  }
8250
- const contents = (0, import_node_fs30.existsSync)(goWorkPath) ? (0, import_node_fs30.readFileSync)(goWorkPath, "utf8") : "";
8595
+ const contents = (0, import_node_fs32.existsSync)(goWorkPath) ? (0, import_node_fs32.readFileSync)(goWorkPath, "utf8") : "";
8251
8596
  const blockEntries = Array.from(contents.matchAll(/(?:^|\n)\s*use\s+\(([^)]*)\)/g), (match) => match[1].split("\n")).flat();
8252
8597
  const singleEntries = Array.from(contents.matchAll(/(?:^|\n)\s*use\s+(\.\S+)/g), (match) => match[1]);
8253
8598
  const uses = [...blockEntries, ...singleEntries].map((entry) => entry.trim()).filter(Boolean);
8254
- const stale = uses.filter((entry) => !(0, import_node_fs30.existsSync)((0, import_node_path35.join)(workspaceRoot, entry, "go.mod")));
8599
+ const stale = uses.filter((entry) => !(0, import_node_fs32.existsSync)((0, import_node_path37.join)(workspaceRoot, entry, "go.mod")));
8255
8600
  return {
8256
8601
  check: "every go.work `use` entry points to a module that still exists",
8257
8602
  ok: stale.length === 0,
@@ -8267,9 +8612,9 @@ function checkNativeApps(workspaceRoot) {
8267
8612
  { file: ".github/workflows/ci.yml", marker: "native (${{ matrix.os }})" },
8268
8613
  { file: "azure-pipelines.yml", marker: "- job: native" }
8269
8614
  ];
8270
- const findings = pipelines.filter(({ file }) => fileExists((0, import_node_path35.join)(workspaceRoot, file))).map(({ file, marker }) => ({
8615
+ const findings = pipelines.filter(({ file }) => fileExists((0, import_node_path37.join)(workspaceRoot, file))).map(({ file, marker }) => ({
8271
8616
  check: `${file} builds the native apps on every OS`,
8272
- ok: (0, import_node_fs30.readFileSync)((0, import_node_path35.join)(workspaceRoot, file), "utf8").includes(marker),
8617
+ ok: (0, import_node_fs32.readFileSync)((0, import_node_path37.join)(workspaceRoot, file), "utf8").includes(marker),
8273
8618
  detail: `${file} has no native job: CI would verify a cgo app on the one agent that cannot build it`,
8274
8619
  remedy: "run `mnci upgrade`"
8275
8620
  }));
@@ -8282,7 +8627,7 @@ function checkNativeApps(workspaceRoot) {
8282
8627
  return findings;
8283
8628
  }
8284
8629
  function checkNoRootRuntimeDependencies(workspaceRoot) {
8285
- const manifestPath = (0, import_node_path35.join)(workspaceRoot, "package.json");
8630
+ const manifestPath = (0, import_node_path37.join)(workspaceRoot, "package.json");
8286
8631
  if (!fileExists(manifestPath)) {
8287
8632
  return void 0;
8288
8633
  }
@@ -8302,14 +8647,14 @@ function checkNoRootRuntimeDependencies(workspaceRoot) {
8302
8647
  };
8303
8648
  }
8304
8649
  function checkRollupSourceMaps(workspaceRoot) {
8305
- const configs = (0, import_node_fs30.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
8650
+ const configs = (0, import_node_fs32.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
8306
8651
  cwd: workspaceRoot
8307
8652
  }).map((config) => toPosix2(config));
8308
8653
  return configs.flatMap((relativePath) => {
8309
- const configPath = (0, import_node_path35.join)(workspaceRoot, relativePath);
8654
+ const configPath = (0, import_node_path37.join)(workspaceRoot, relativePath);
8310
8655
  let config;
8311
8656
  try {
8312
- config = (0, import_node_fs30.readFileSync)(configPath, "utf8");
8657
+ config = (0, import_node_fs32.readFileSync)(configPath, "utf8");
8313
8658
  } catch {
8314
8659
  return [];
8315
8660
  }
@@ -8324,11 +8669,11 @@ function checkRollupSourceMaps(workspaceRoot) {
8324
8669
  });
8325
8670
  }
8326
8671
  function checkDeclarationSpecifiers(workspaceRoot) {
8327
- const configs = (0, import_node_fs30.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
8672
+ const configs = (0, import_node_fs32.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
8328
8673
  cwd: workspaceRoot
8329
8674
  }).map((config) => toPosix2(config));
8330
8675
  return configs.flatMap((relativePath) => {
8331
- const configPath = (0, import_node_path35.join)(workspaceRoot, relativePath);
8676
+ const configPath = (0, import_node_path37.join)(workspaceRoot, relativePath);
8332
8677
  if (!fileExists(configPath)) {
8333
8678
  return [];
8334
8679
  }
@@ -8343,7 +8688,7 @@ function checkDeclarationSpecifiers(workspaceRoot) {
8343
8688
  // `plugins` array (what @nx/react:library writes). For any other shape it would no-op, and
8344
8689
  // naming a command that silently does nothing is the failure mode the source-map check
8345
8690
  // above goes out of its way to avoid, so the remedy depends on the shape.
8346
- remedy: canAddDeclarationSpecifierPlugin((0, import_node_fs30.readFileSync)(configPath, "utf8")) ? "run `mnci upgrade`, which adds the plugin to the plugins array of withNx's second argument" : "add the plugin to withNx's second argument by hand (copy it from a freshly `mnci add npm-lib`'d project) \u2014 mnci does not recognise the shape of this config, so `mnci upgrade` cannot add it"
8691
+ remedy: canAddDeclarationSpecifierPlugin((0, import_node_fs32.readFileSync)(configPath, "utf8")) ? "run `mnci upgrade`, which adds the plugin to the plugins array of withNx's second argument" : "add the plugin to withNx's second argument by hand (copy it from a freshly `mnci add npm-lib`'d project) \u2014 mnci does not recognise the shape of this config, so `mnci upgrade` cannot add it"
8347
8692
  }
8348
8693
  ];
8349
8694
  }
@@ -8359,8 +8704,8 @@ function checkDeclarationSpecifiers(workspaceRoot) {
8359
8704
  }
8360
8705
  function checkPipelinePhases(workspaceRoot) {
8361
8706
  const files = ["azure-pipelines.yml", ".github/workflows/ci.yml"];
8362
- return files.filter((file) => fileExists((0, import_node_path35.join)(workspaceRoot, file))).flatMap((file) => {
8363
- const { verifyActive, switchedOff } = inspectPipeline((0, import_node_fs30.readFileSync)((0, import_node_path35.join)(workspaceRoot, file), "utf8"));
8707
+ return files.filter((file) => fileExists((0, import_node_path37.join)(workspaceRoot, file))).flatMap((file) => {
8708
+ const { verifyActive, switchedOff } = inspectPipeline((0, import_node_fs32.readFileSync)((0, import_node_path37.join)(workspaceRoot, file), "utf8"));
8364
8709
  const verify = {
8365
8710
  check: `${file} runs the enforced verify phase`,
8366
8711
  ok: verifyActive,
@@ -8401,7 +8746,7 @@ function checkDependencyRangesNotPreserved(nxJson) {
8401
8746
  };
8402
8747
  }
8403
8748
  function checkLockfileHasNoLocalRegistry(workspaceRoot) {
8404
- if (!fileExists((0, import_node_path35.join)(workspaceRoot, "package-lock.json"))) {
8749
+ if (!fileExists((0, import_node_path37.join)(workspaceRoot, "package-lock.json"))) {
8405
8750
  return void 0;
8406
8751
  }
8407
8752
  return {
@@ -8421,7 +8766,7 @@ function checkReleaseTagsResolve(workspaceRoot, nxJson) {
8421
8766
  };
8422
8767
  }
8423
8768
  function collectFindings(workspaceRoot) {
8424
- const nxJsonPath = (0, import_node_path35.join)(workspaceRoot, "nx.json");
8769
+ const nxJsonPath = (0, import_node_path37.join)(workspaceRoot, "nx.json");
8425
8770
  if (!fileExists(nxJsonPath)) {
8426
8771
  throw new Error(
8427
8772
  `No nx.json found in ${workspaceRoot} \u2014 run 'mnci doctor' from the workspace root.`
@@ -8486,8 +8831,8 @@ function runDoctor(workspaceRoot, options = {}) {
8486
8831
  }
8487
8832
 
8488
8833
  // src/repository-adoption/adopt-dependencies.use-case.ts
8489
- var import_node_fs31 = require("fs");
8490
- var import_node_path36 = require("path");
8834
+ var import_node_fs33 = require("fs");
8835
+ var import_node_path38 = require("path");
8491
8836
 
8492
8837
  // src/repository-adoption/clean-working-tree.validator.ts
8493
8838
  function requireCleanWorkingTree(repositoryRoot, capture = runCapture) {
@@ -8558,7 +8903,7 @@ var SOURCE_FILE = /\.(?:[cm]?[jt]s|[jt]sx)$/;
8558
8903
  var TEST_FILE = /\.(?:spec|test)\.[^.]+$|[\\/](?:__tests__|tests?|e2e)[\\/]/;
8559
8904
  var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", "out", ".git", ".nx", "tmp"]);
8560
8905
  function sourceFiles(directory, relative = "") {
8561
- const entries = (0, import_node_fs31.readdirSync)((0, import_node_path36.join)(directory, relative), { withFileTypes: true });
8906
+ const entries = (0, import_node_fs33.readdirSync)((0, import_node_path38.join)(directory, relative), { withFileTypes: true });
8562
8907
  const found = [];
8563
8908
  for (const entry of entries) {
8564
8909
  const path = relative === "" ? entry.name : `${relative}/${entry.name}`;
@@ -8574,7 +8919,7 @@ function sourceFiles(directory, relative = "") {
8574
8919
  }
8575
8920
  function wantedImportsOf(path, wanted) {
8576
8921
  try {
8577
- const imported = [...extractImportedPackages((0, import_node_fs31.readFileSync)(path, "utf8"))];
8922
+ const imported = [...extractImportedPackages((0, import_node_fs33.readFileSync)(path, "utf8"))];
8578
8923
  return imported.filter((name) => wanted.has(name));
8579
8924
  } catch {
8580
8925
  return [];
@@ -8585,7 +8930,7 @@ function importsOfProject(projectDirectory, wanted) {
8585
8930
  const tested = /* @__PURE__ */ new Set();
8586
8931
  const files = sourceFiles(projectDirectory);
8587
8932
  for (const file of files) {
8588
- const names = wantedImportsOf((0, import_node_path36.join)(projectDirectory, file), wanted);
8933
+ const names = wantedImportsOf((0, import_node_path38.join)(projectDirectory, file), wanted);
8589
8934
  const target = TEST_FILE.test(file) ? tested : shipped;
8590
8935
  for (const name of names) {
8591
8936
  target.add(name);
@@ -8597,7 +8942,7 @@ function importsOfProject(projectDirectory, wanted) {
8597
8942
  function findUsage(repositoryRoot, projectDirectories, wanted) {
8598
8943
  const usage = {};
8599
8944
  for (const dir of projectDirectories) {
8600
- const { shipped, testsOnly } = importsOfProject((0, import_node_path36.join)(repositoryRoot, dir), wanted);
8945
+ const { shipped, testsOnly } = importsOfProject((0, import_node_path38.join)(repositoryRoot, dir), wanted);
8601
8946
  for (const name of shipped) {
8602
8947
  usage[name] ??= { runtime: [], testsOnly: [] };
8603
8948
  usage[name].runtime.push(dir);
@@ -8612,7 +8957,7 @@ function findUsage(repositoryRoot, projectDirectories, wanted) {
8612
8957
  function adoptDependencies(repositoryRoot, dependencies = {}) {
8613
8958
  const run = dependencies.run ?? runShell;
8614
8959
  requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
8615
- const rootPath = (0, import_node_path36.join)(repositoryRoot, "package.json");
8960
+ const rootPath = (0, import_node_path38.join)(repositoryRoot, "package.json");
8616
8961
  if (!fileExists(rootPath)) {
8617
8962
  throw new Error("No package.json at the repository root.");
8618
8963
  }
@@ -8647,8 +8992,8 @@ function adoptDependencies(repositoryRoot, dependencies = {}) {
8647
8992
  }
8648
8993
 
8649
8994
  // src/repository-adoption/adopt-kinds.use-case.ts
8650
- var import_node_fs32 = require("fs");
8651
- var import_node_path37 = require("path");
8995
+ var import_node_fs34 = require("fs");
8996
+ var import_node_path39 = require("path");
8652
8997
 
8653
8998
  // src/repository-adoption/infer-project-kind.algorithm.ts
8654
8999
  var SERVER_FRAMEWORKS = ["express", "koa", "fastify", "@nestjs/core"];
@@ -8749,32 +9094,32 @@ function inferProjectKind(evidence) {
8749
9094
  // src/repository-adoption/adopt-kinds.use-case.ts
8750
9095
  var TYPE_TAG = "type:";
8751
9096
  function readEvidence(repositoryRoot, location) {
8752
- const directory = (0, import_node_path37.join)(repositoryRoot, location.dir);
8753
- const files = (0, import_node_fs32.readdirSync)(directory);
9097
+ const directory = (0, import_node_path39.join)(repositoryRoot, location.dir);
9098
+ const files = (0, import_node_fs34.readdirSync)(directory);
8754
9099
  const manifest = location.ecosystem === "npm" ? readJson(location.manifestPath) : {};
8755
- const text = location.ecosystem === "npm" ? "" : (0, import_node_fs32.readFileSync)(location.manifestPath, "utf8");
9100
+ const text = location.ecosystem === "npm" ? "" : (0, import_node_fs34.readFileSync)(location.manifestPath, "utf8");
8756
9101
  let hasProgram = false;
8757
9102
  if (location.ecosystem === "go") {
8758
- hasProgram = files.filter((file) => file.endsWith(".go")).some((file) => /^package main\b/m.test((0, import_node_fs32.readFileSync)((0, import_node_path37.join)(directory, file), "utf8")));
9103
+ hasProgram = files.filter((file) => file.endsWith(".go")).some((file) => /^package main\b/m.test((0, import_node_fs34.readFileSync)((0, import_node_path39.join)(directory, file), "utf8")));
8759
9104
  } else if (location.ecosystem === "pub") {
8760
- hasProgram = (0, import_node_fs32.existsSync)((0, import_node_path37.join)(directory, "lib", "main.dart"));
9105
+ hasProgram = (0, import_node_fs34.existsSync)((0, import_node_path39.join)(directory, "lib", "main.dart"));
8761
9106
  }
8762
9107
  return { ecosystem: location.ecosystem, manifest, text, files, hasProgram };
8763
9108
  }
8764
9109
  function existingKind(directory) {
8765
9110
  const sources = [];
8766
- if (fileExists((0, import_node_path37.join)(directory, "project.json"))) {
8767
- sources.push(readJson((0, import_node_path37.join)(directory, "project.json")).tags ?? []);
9111
+ if (fileExists((0, import_node_path39.join)(directory, "project.json"))) {
9112
+ sources.push(readJson((0, import_node_path39.join)(directory, "project.json")).tags ?? []);
8768
9113
  }
8769
- if (fileExists((0, import_node_path37.join)(directory, "package.json"))) {
8770
- sources.push(readJson((0, import_node_path37.join)(directory, "package.json")).nx?.tags ?? []);
9114
+ if (fileExists((0, import_node_path39.join)(directory, "package.json"))) {
9115
+ sources.push(readJson((0, import_node_path39.join)(directory, "package.json")).nx?.tags ?? []);
8771
9116
  }
8772
9117
  return sources.flat().find((tag) => tag.startsWith(TYPE_TAG))?.slice(TYPE_TAG.length);
8773
9118
  }
8774
9119
  function writeKindTag(directory, name, kind) {
8775
9120
  const tag = `${TYPE_TAG}${kind}`;
8776
- const projectJson = (0, import_node_path37.join)(directory, "project.json");
8777
- const packageJson = (0, import_node_path37.join)(directory, "package.json");
9121
+ const projectJson = (0, import_node_path39.join)(directory, "project.json");
9122
+ const packageJson = (0, import_node_path39.join)(directory, "package.json");
8778
9123
  if (fileExists(projectJson)) {
8779
9124
  const project = readJson(projectJson);
8780
9125
  writeFileEnsured(projectJson, toJson({ ...project, tags: [...project.tags ?? [], tag] }));
@@ -8798,7 +9143,7 @@ function adoptKinds(repositoryRoot, overrides = {}, dependencies = {}) {
8798
9143
  }
8799
9144
  }
8800
9145
  return locations.map((location) => {
8801
- const directory = (0, import_node_path37.join)(repositoryRoot, location.dir);
9146
+ const directory = (0, import_node_path39.join)(repositoryRoot, location.dir);
8802
9147
  const present = existingKind(directory);
8803
9148
  if (present !== void 0) {
8804
9149
  return { dir: location.dir, outcome: "already", kind: present, reason: PROJECT_KINDS.includes(present) ? "it already has a type tag" : "it already has a type tag of its own, which is not an mnci kind name; left as it is" };
@@ -8814,12 +9159,12 @@ function adoptKinds(repositoryRoot, overrides = {}, dependencies = {}) {
8814
9159
  }
8815
9160
 
8816
9161
  // src/repository-adoption/adopt-overlay.use-case.ts
8817
- var import_node_fs34 = require("fs");
8818
- var import_node_path39 = require("path");
9162
+ var import_node_fs36 = require("fs");
9163
+ var import_node_path41 = require("path");
8819
9164
 
8820
9165
  // src/workspace-upgrade/upgrade-workspace.use-case.ts
8821
- var import_node_fs33 = require("fs");
8822
- var import_node_path38 = require("path");
9166
+ var import_node_fs35 = require("fs");
9167
+ var import_node_path40 = require("path");
8823
9168
  function resolveRegistry(options, persisted) {
8824
9169
  if (options.registry === "azure-artifacts" || options.organization && options.artifactsFeed) {
8825
9170
  const persistedAzure = persisted?.kind === "azure-artifacts" ? persisted : void 0;
@@ -8848,7 +9193,7 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
8848
9193
  return persisted.workspaceName;
8849
9194
  }
8850
9195
  try {
8851
- const existing = (0, import_node_fs33.readdirSync)(workspaceRoot).find(
9196
+ const existing = (0, import_node_fs35.readdirSync)(workspaceRoot).find(
8852
9197
  (file) => file.endsWith(".code-workspace") && file !== "undefined.code-workspace"
8853
9198
  );
8854
9199
  if (existing) {
@@ -8856,11 +9201,11 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
8856
9201
  }
8857
9202
  } catch {
8858
9203
  }
8859
- return (0, import_node_path38.basename)(workspaceRoot);
9204
+ return (0, import_node_path40.basename)(workspaceRoot);
8860
9205
  }
8861
9206
  function detectNpmAuth(workspaceRoot) {
8862
9207
  try {
8863
- return (0, import_node_fs33.readFileSync)((0, import_node_path38.join)(workspaceRoot, "azure-pipelines.yml"), "utf8").includes("npmAuthenticate@0") ? "build-identity" : void 0;
9208
+ return (0, import_node_fs35.readFileSync)((0, import_node_path40.join)(workspaceRoot, "azure-pipelines.yml"), "utf8").includes("npmAuthenticate@0") ? "build-identity" : void 0;
8864
9209
  } catch {
8865
9210
  return void 0;
8866
9211
  }
@@ -8907,7 +9252,7 @@ function resolveOverlayOptions(workspaceRoot, options, persisted) {
8907
9252
  };
8908
9253
  }
8909
9254
  function runUpgrade(workspaceRoot, options) {
8910
- if (!fileExists((0, import_node_path38.join)(workspaceRoot, "nx.json"))) {
9255
+ if (!fileExists((0, import_node_path40.join)(workspaceRoot, "nx.json"))) {
8911
9256
  throw new Error(
8912
9257
  `No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
8913
9258
  );
@@ -8955,6 +9300,10 @@ function runUpgrade(workspaceRoot, options) {
8955
9300
  logger.step("Updating the VS Code extension packaging script");
8956
9301
  logger.detail(`updated ${VSCODE_EXTENSION_SCRIPT_PATH}`);
8957
9302
  }
9303
+ if (refreshContainerScript(workspaceRoot)) {
9304
+ logger.step("Updating the container image script");
9305
+ logger.detail("updated tools/container-image.cjs");
9306
+ }
8958
9307
  if (refreshGoReleaseScript(workspaceRoot)) {
8959
9308
  logger.step("Updating the releasable Go app script");
8960
9309
  logger.detail(`updated ${GO_RELEASE_SCRIPT_PATH}`);
@@ -8981,7 +9330,7 @@ function runUpgrade(workspaceRoot, options) {
8981
9330
  "Formatting the workspace (eslint --fix) \u2014 the slowest step, minutes on a large workspace"
8982
9331
  );
8983
9332
  runFormatter(workspaceRoot);
8984
- const nxJson = readJson((0, import_node_path38.join)(workspaceRoot, "nx.json"));
9333
+ const nxJson = readJson((0, import_node_path40.join)(workspaceRoot, "nx.json"));
8985
9334
  const stranded = locateStrandedReleaseTags(workspaceRoot, nxJson.release?.releaseTag?.pattern);
8986
9335
  for (const entry of stranded) {
8987
9336
  logger.warn(`${entry.project} has release tags only under ${entry.oldTag}: nx release would restart it from its disk version. Run \`mnci adopt --tags\` before the next release to create ${entry.newTag} locally (\`mnci doctor\` lists them all).`);
@@ -8992,9 +9341,9 @@ function runUpgrade(workspaceRoot, options) {
8992
9341
 
8993
9342
  // src/repository-adoption/adopt-overlay.use-case.ts
8994
9343
  function detectCiProvider(repositoryRoot) {
8995
- const azure = (0, import_node_fs34.existsSync)((0, import_node_path39.join)(repositoryRoot, "azure-pipelines.yml"));
8996
- const workflows = (0, import_node_path39.join)(repositoryRoot, ".github", "workflows");
8997
- const github = (0, import_node_fs34.existsSync)(workflows) && (0, import_node_fs34.readdirSync)(workflows).some((file) => /\.ya?ml$/.test(file));
9344
+ const azure = (0, import_node_fs36.existsSync)((0, import_node_path41.join)(repositoryRoot, "azure-pipelines.yml"));
9345
+ const workflows = (0, import_node_path41.join)(repositoryRoot, ".github", "workflows");
9346
+ const github = (0, import_node_fs36.existsSync)(workflows) && (0, import_node_fs36.readdirSync)(workflows).some((file) => /\.ya?ml$/.test(file));
8998
9347
  if (azure && github) {
8999
9348
  return "both";
9000
9349
  }
@@ -9004,7 +9353,7 @@ function detectCiProvider(repositoryRoot) {
9004
9353
  return github ? "github" : void 0;
9005
9354
  }
9006
9355
  function detectTestRunner(repositoryRoot) {
9007
- const manifestPath = (0, import_node_path39.join)(repositoryRoot, "package.json");
9356
+ const manifestPath = (0, import_node_path41.join)(repositoryRoot, "package.json");
9008
9357
  if (!fileExists(manifestPath)) {
9009
9358
  return void 0;
9010
9359
  }
@@ -9017,7 +9366,7 @@ function detectTestRunner(repositoryRoot) {
9017
9366
  }
9018
9367
  function adoptOverlay(repositoryRoot, options, dependencies = {}) {
9019
9368
  requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
9020
- if (!fileExists((0, import_node_path39.join)(repositoryRoot, "nx.json"))) {
9369
+ if (!fileExists((0, import_node_path41.join)(repositoryRoot, "nx.json"))) {
9021
9370
  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.");
9022
9371
  }
9023
9372
  const persisted = readMnciConfig(repositoryRoot);
@@ -9030,7 +9379,7 @@ function adoptOverlay(repositoryRoot, options, dependencies = {}) {
9030
9379
  }
9031
9380
 
9032
9381
  // src/repository-adoption/adopt-toolchain.use-case.ts
9033
- var import_node_path41 = require("path");
9382
+ var import_node_path43 = require("path");
9034
9383
 
9035
9384
  // src/repository-adoption/align-nx-family.algorithm.ts
9036
9385
  var VERSION = /^[\^~]?(\d+)\.(\d+)\.(\d+)$/;
@@ -9068,24 +9417,24 @@ function alignNxFamily(dependencies, version) {
9068
9417
  }
9069
9418
 
9070
9419
  // src/repository-adoption/retire-tooling.use-case.ts
9071
- var import_node_fs35 = require("fs");
9072
- var import_node_path40 = require("path");
9420
+ var import_node_fs37 = require("fs");
9421
+ var import_node_path42 = require("path");
9073
9422
  var RETIRED_COMMAND = /\b(?:prettier|oxfmt|oxlint)\b/;
9074
9423
  function retireTooling(repositoryRoot) {
9075
9424
  const removed = [];
9076
9425
  for (const file of RETIRED_FORMATTER_FILES) {
9077
- if (!fileExists((0, import_node_path40.join)(repositoryRoot, file))) {
9426
+ if (!fileExists((0, import_node_path42.join)(repositoryRoot, file))) {
9078
9427
  continue;
9079
9428
  }
9080
- (0, import_node_fs35.rmSync)((0, import_node_path40.join)(repositoryRoot, file), { force: true });
9429
+ (0, import_node_fs37.rmSync)((0, import_node_path42.join)(repositoryRoot, file), { force: true });
9081
9430
  removed.push(file);
9082
9431
  }
9083
- const hadRegistry = fileExists((0, import_node_path40.join)(repositoryRoot, ".verdaccio"));
9432
+ const hadRegistry = fileExists((0, import_node_path42.join)(repositoryRoot, ".verdaccio"));
9084
9433
  removeLocalRegistryScaffolding(repositoryRoot);
9085
9434
  if (hadRegistry) {
9086
9435
  removed.push(".verdaccio, its dependency and the local-registry target");
9087
9436
  }
9088
- const manifestPath = (0, import_node_path40.join)(repositoryRoot, "package.json");
9437
+ const manifestPath = (0, import_node_path42.join)(repositoryRoot, "package.json");
9089
9438
  if (!fileExists(manifestPath)) {
9090
9439
  return removed;
9091
9440
  }
@@ -9132,7 +9481,7 @@ function adoptToolchain(repositoryRoot, options = {}, dependencies = {}) {
9132
9481
  console.log(message);
9133
9482
  });
9134
9483
  requireCleanWorkingTree(repositoryRoot, capture);
9135
- const manifestPath = (0, import_node_path41.join)(repositoryRoot, "package.json");
9484
+ const manifestPath = (0, import_node_path43.join)(repositoryRoot, "package.json");
9136
9485
  if (!fileExists(manifestPath)) {
9137
9486
  throw new Error("No package.json at the repository root.");
9138
9487
  }
@@ -9182,8 +9531,8 @@ function createBaselineTags(repositoryRoot, stranded) {
9182
9531
  }
9183
9532
 
9184
9533
  // src/repository-adoption/inspect-repository.use-case.ts
9185
- var import_node_fs36 = require("fs");
9186
- var import_node_path42 = require("path");
9534
+ var import_node_fs38 = require("fs");
9535
+ var import_node_path44 = require("path");
9187
9536
  var LOCKFILES = [
9188
9537
  ["package-lock.json", "npm"],
9189
9538
  ["pnpm-lock.yaml", "pnpm"],
@@ -9204,25 +9553,25 @@ function inspectRepository(repositoryRoot) {
9204
9553
  const isGitRepo = inside.status === 0 && inside.stdout.trim() === "true";
9205
9554
  const dirty = isGitRepo && runCapture("git", ["status", "--porcelain"], repositoryRoot).stdout.trim() !== "";
9206
9555
  const tags = isGitRepo ? runCapture("git", ["tag", "--list"], repositoryRoot).stdout.split(/\r?\n/).filter(Boolean) : [];
9207
- const manifestPath = (0, import_node_path42.join)(repositoryRoot, "package.json");
9556
+ const manifestPath = (0, import_node_path44.join)(repositoryRoot, "package.json");
9208
9557
  const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
9209
- const nxJsonPath = (0, import_node_path42.join)(repositoryRoot, "nx.json");
9558
+ const nxJsonPath = (0, import_node_path44.join)(repositoryRoot, "nx.json");
9210
9559
  const nxJson = fileExists(nxJsonPath) ? readJson(nxJsonPath) : {};
9211
9560
  const ci = [];
9212
9561
  const pipelines = [];
9213
- if ((0, import_node_fs36.existsSync)((0, import_node_path42.join)(repositoryRoot, "azure-pipelines.yml"))) {
9562
+ if ((0, import_node_fs38.existsSync)((0, import_node_path44.join)(repositoryRoot, "azure-pipelines.yml"))) {
9214
9563
  ci.push("azure");
9215
- pipelines.push((0, import_node_path42.join)(repositoryRoot, "azure-pipelines.yml"));
9564
+ pipelines.push((0, import_node_path44.join)(repositoryRoot, "azure-pipelines.yml"));
9216
9565
  }
9217
- const workflows = (0, import_node_path42.join)(repositoryRoot, ".github", "workflows");
9218
- if ((0, import_node_fs36.existsSync)(workflows)) {
9219
- const files = (0, import_node_fs36.readdirSync)(workflows).filter((file) => /\.ya?ml$/.test(file));
9566
+ const workflows = (0, import_node_path44.join)(repositoryRoot, ".github", "workflows");
9567
+ if ((0, import_node_fs38.existsSync)(workflows)) {
9568
+ const files = (0, import_node_fs38.readdirSync)(workflows).filter((file) => /\.ya?ml$/.test(file));
9220
9569
  if (files.length > 0) {
9221
9570
  ci.push("github");
9222
- pipelines.push(...files.map((file) => (0, import_node_path42.join)(workflows, file)));
9571
+ pipelines.push(...files.map((file) => (0, import_node_path44.join)(workflows, file)));
9223
9572
  }
9224
9573
  }
9225
- const rootFiles = (0, import_node_fs36.readdirSync)(repositoryRoot);
9574
+ const rootFiles = (0, import_node_fs38.readdirSync)(repositoryRoot);
9226
9575
  return {
9227
9576
  isGitRepo,
9228
9577
  dirty,
@@ -9233,7 +9582,7 @@ function inspectRepository(repositoryRoot) {
9233
9582
  tagCount: tags.length,
9234
9583
  strandedTags: isGitRepo ? locateStrandedReleaseTags(repositoryRoot, nxJson.release?.releaseTag?.pattern) : [],
9235
9584
  ci,
9236
- pipelineUsesMnci: pipelines.some((file) => /\bmnci ci\b/.test((0, import_node_fs36.readFileSync)(file, "utf8"))),
9585
+ pipelineUsesMnci: pipelines.some((file) => /\bmnci ci\b/.test((0, import_node_fs38.readFileSync)(file, "utf8"))),
9237
9586
  retiredTooling: [.../* @__PURE__ */ new Set([...rootFiles.filter((file) => RETIRED_FILE_PATTERN.test(file) || file === ".verdaccio"), ...retiredNamesIn(manifest)])],
9238
9587
  personalFiles: PERSONAL_FILES.filter((file) => rootFiles.includes(file))
9239
9588
  };
@@ -9530,13 +9879,13 @@ function runCommands(program, options) {
9530
9879
  }
9531
9880
 
9532
9881
  // src/workspace-info/list-projects.use-case.ts
9533
- var import_node_path43 = require("path");
9882
+ var import_node_path45 = require("path");
9534
9883
  var TYPE_TAG_PREFIX = "type:";
9535
9884
  function listProjects(workspaceRoot) {
9536
9885
  return locateProjects(workspaceRoot).map((location) => {
9537
- const projectJsonPath = (0, import_node_path43.join)(workspaceRoot, location.dir, "project.json");
9886
+ const projectJsonPath = (0, import_node_path45.join)(workspaceRoot, location.dir, "project.json");
9538
9887
  const project = fileExists(projectJsonPath) ? readJson(projectJsonPath) : {};
9539
- const manifestPath = (0, import_node_path43.join)(workspaceRoot, location.dir, "package.json");
9888
+ const manifestPath = (0, import_node_path45.join)(workspaceRoot, location.dir, "package.json");
9540
9889
  const manifestTags = fileExists(manifestPath) ? readJson(manifestPath).nx?.tags ?? [] : [];
9541
9890
  const typeTag = [...project.tags ?? [], ...manifestTags].find((tag) => tag.startsWith(TYPE_TAG_PREFIX));
9542
9891
  return {
@@ -9549,7 +9898,7 @@ function listProjects(workspaceRoot) {
9549
9898
  }).toSorted((a, b) => a.dir.localeCompare(b.dir));
9550
9899
  }
9551
9900
  function runProjects(workspaceRoot, options) {
9552
- if (!fileExists((0, import_node_path43.join)(workspaceRoot, "nx.json"))) {
9901
+ if (!fileExists((0, import_node_path45.join)(workspaceRoot, "nx.json"))) {
9553
9902
  throw new Error("No nx.json found here. Run `projects` from the workspace root.");
9554
9903
  }
9555
9904
  const projects = listProjects(workspaceRoot);
@@ -9563,11 +9912,11 @@ function runProjects(workspaceRoot, options) {
9563
9912
  }
9564
9913
 
9565
9914
  // src/workspace-info/read-workspace-info.use-case.ts
9566
- var import_node_path45 = require("path");
9915
+ var import_node_path47 = require("path");
9567
9916
 
9568
9917
  // src/cli-version/check-for-update.use-case.ts
9569
- var import_node_fs37 = require("fs");
9570
- var import_node_path44 = require("path");
9918
+ var import_node_fs39 = require("fs");
9919
+ var import_node_path46 = require("path");
9571
9920
  var import_cross_spawn3 = __toESM(require("cross-spawn"));
9572
9921
  var PACKAGE_NAME2 = "@mnci/cli";
9573
9922
  var REGISTRY_TIMEOUT_MS = 2e3;
@@ -9626,8 +9975,8 @@ function releaseTriple(version) {
9626
9975
  }
9627
9976
  function readCliVersion(packageDirectory) {
9628
9977
  try {
9629
- const manifestPath = (0, import_node_path44.join)(packageDirectory, "..", "package.json");
9630
- const manifest = JSON.parse((0, import_node_fs37.readFileSync)(manifestPath, "utf8"));
9978
+ const manifestPath = (0, import_node_path46.join)(packageDirectory, "..", "package.json");
9979
+ const manifest = JSON.parse((0, import_node_fs39.readFileSync)(manifestPath, "utf8"));
9631
9980
  return manifest.version ?? "0.0.0";
9632
9981
  } catch {
9633
9982
  return "0.0.0";
@@ -9637,7 +9986,7 @@ function readCliVersion(packageDirectory) {
9637
9986
  // src/workspace-info/read-workspace-info.use-case.ts
9638
9987
  function readWorkspaceInfo(workspaceRoot, cliVersion) {
9639
9988
  const latest = fetchLatestVersion() ?? null;
9640
- const isWorkspace = fileExists((0, import_node_path45.join)(workspaceRoot, "nx.json"));
9989
+ const isWorkspace = fileExists((0, import_node_path47.join)(workspaceRoot, "nx.json"));
9641
9990
  return {
9642
9991
  cli: {
9643
9992
  version: cliVersion,
@@ -9658,7 +10007,7 @@ function runInfo(workspaceRoot, cliVersion, options) {
9658
10007
  }
9659
10008
 
9660
10009
  // src/interactive-wizard/interactive-wizard.use-case.ts
9661
- var import_node_path46 = require("path");
10010
+ var import_node_path48 = require("path");
9662
10011
 
9663
10012
  // src/interactive-wizard/adopt-steps.config.ts
9664
10013
  var ADOPT_GLOBAL_OPTIONS = ["json"];
@@ -9839,7 +10188,7 @@ function wizardMenu(commands, inWorkspace) {
9839
10188
  }
9840
10189
  async function runInteractive(program, prompter = createInquirerPrompter(), workingDirectory = process.cwd()) {
9841
10190
  const commands = describeCommands(program);
9842
- const inWorkspace = fileExists((0, import_node_path46.join)(workingDirectory, "nx.json"));
10191
+ const inWorkspace = fileExists((0, import_node_path48.join)(workingDirectory, "nx.json"));
9843
10192
  const name = await prompter.select("What would you like to do?", wizardMenu(commands, inWorkspace));
9844
10193
  const command = commands.find((candidate) => candidate.name === name);
9845
10194
  const argv = buildArgv(command, await askCommand(command, prompter));
@@ -9852,8 +10201,8 @@ async function runInteractive(program, prompter = createInquirerPrompter(), work
9852
10201
  }
9853
10202
 
9854
10203
  // src/workspace-creation/adopt-directory.use-case.ts
9855
- var import_node_fs38 = require("fs");
9856
- var import_node_path47 = require("path");
10204
+ var import_node_fs40 = require("fs");
10205
+ var import_node_path49 = require("path");
9857
10206
  var NEVER_COPIED = /* @__PURE__ */ new Set([".git", "node_modules"]);
9858
10207
  var AGENT_AND_CI_ENTRIES = [
9859
10208
  "AGENTS.md",
@@ -9884,13 +10233,13 @@ var PREFLIGHT_BLOCKERS = [
9884
10233
  "packages"
9885
10234
  ];
9886
10235
  function assertAdoptableDirectory(target) {
9887
- if (!(0, import_node_fs38.existsSync)(target)) {
10236
+ if (!(0, import_node_fs40.existsSync)(target)) {
9888
10237
  throw new Error(`--into '${target}' does not exist. Create or clone it first.`);
9889
10238
  }
9890
- if (!(0, import_node_fs38.statSync)(target).isDirectory()) {
10239
+ if (!(0, import_node_fs40.statSync)(target).isDirectory()) {
9891
10240
  throw new Error(`--into '${target}' is not a directory.`);
9892
10241
  }
9893
- const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs38.existsSync)((0, import_node_path47.join)(target, entry)));
10242
+ const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs40.existsSync)((0, import_node_path49.join)(target, entry)));
9894
10243
  if (blockers.length > 0) {
9895
10244
  throw new Error(
9896
10245
  `--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.`
@@ -9898,15 +10247,15 @@ function assertAdoptableDirectory(target) {
9898
10247
  }
9899
10248
  }
9900
10249
  function mergeGitignore(generatedFile, targetFile) {
9901
- const existing = (0, import_node_fs38.readFileSync)(targetFile, "utf8");
10250
+ const existing = (0, import_node_fs40.readFileSync)(targetFile, "utf8");
9902
10251
  const present = new Set(existing.split("\n").map((line) => line.trim()));
9903
- const additions = (0, import_node_fs38.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
10252
+ const additions = (0, import_node_fs40.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
9904
10253
  const trimmed = line.trim();
9905
10254
  return trimmed !== "" && !trimmed.startsWith("#") && !present.has(trimmed);
9906
10255
  });
9907
10256
  if (additions.length === 0) return false;
9908
10257
  const separator = existing.endsWith("\n") ? "" : "\n";
9909
- (0, import_node_fs38.writeFileSync)(
10258
+ (0, import_node_fs40.writeFileSync)(
9910
10259
  targetFile,
9911
10260
  `${existing}${separator}
9912
10261
  # Added by mnci (Nx workspace)
@@ -9916,9 +10265,9 @@ ${additions.join("\n")}
9916
10265
  return true;
9917
10266
  }
9918
10267
  function adoptGeneratedWorkspace(generatedRoot, target) {
9919
- const entries = (0, import_node_fs38.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
10268
+ const entries = (0, import_node_fs40.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
9920
10269
  const collisions = entries.filter(
9921
- (entry) => (0, import_node_fs38.existsSync)((0, import_node_path47.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
10270
+ (entry) => (0, import_node_fs40.existsSync)((0, import_node_path49.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
9922
10271
  );
9923
10272
  if (collisions.length > 0) {
9924
10273
  throw new Error(
@@ -9927,28 +10276,28 @@ function adoptGeneratedWorkspace(generatedRoot, target) {
9927
10276
  }
9928
10277
  const result = { copied: [], kept: [], mergedGitignore: false };
9929
10278
  for (const entry of entries) {
9930
- const from = (0, import_node_path47.join)(generatedRoot, entry);
9931
- const to = (0, import_node_path47.join)(target, entry);
9932
- if (entry === ".gitignore" && (0, import_node_fs38.existsSync)(to)) {
10279
+ const from = (0, import_node_path49.join)(generatedRoot, entry);
10280
+ const to = (0, import_node_path49.join)(target, entry);
10281
+ if (entry === ".gitignore" && (0, import_node_fs40.existsSync)(to)) {
9933
10282
  result.mergedGitignore = mergeGitignore(from, to);
9934
10283
  result.kept.push(entry);
9935
10284
  continue;
9936
10285
  }
9937
- if ((0, import_node_fs38.existsSync)(to)) {
10286
+ if ((0, import_node_fs40.existsSync)(to)) {
9938
10287
  result.kept.push(entry);
9939
10288
  continue;
9940
10289
  }
9941
- (0, import_node_fs38.cpSync)(from, to, { recursive: true });
10290
+ (0, import_node_fs40.cpSync)(from, to, { recursive: true });
9942
10291
  result.copied.push(entry);
9943
10292
  }
9944
- (0, import_node_fs38.rmSync)(generatedRoot, { recursive: true, force: true });
10293
+ (0, import_node_fs40.rmSync)(generatedRoot, { recursive: true, force: true });
9945
10294
  return result;
9946
10295
  }
9947
10296
 
9948
10297
  // src/workspace-creation/create-workspace.use-case.ts
9949
- var import_node_fs39 = require("fs");
10298
+ var import_node_fs41 = require("fs");
9950
10299
  var import_node_os2 = require("os");
9951
- var import_node_path48 = require("path");
10300
+ var import_node_path50 = require("path");
9952
10301
  function nxCloudProviderValue(ci) {
9953
10302
  return ci === "azure" ? "azure" : "github";
9954
10303
  }
@@ -9991,9 +10340,9 @@ async function resolveRegistry2(options) {
9991
10340
  return await promptRegistry();
9992
10341
  }
9993
10342
  async function runNew(name, options) {
9994
- const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path48.resolve)(process.cwd(), options.into);
10343
+ const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path50.resolve)(process.cwd(), options.into);
9995
10344
  if (adoptTarget !== void 0) assertAdoptableDirectory(adoptTarget);
9996
- const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path48.basename)(adoptTarget));
10345
+ const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path50.basename)(adoptTarget));
9997
10346
  assertValidProjectName(workspaceName, "Workspace name");
9998
10347
  const scope = options.scope ?? (options.yes ? `@${workspaceName}` : await promptText("npm scope for publishable packages", `@${workspaceName}`));
9999
10348
  const registry = await resolveRegistry2(options);
@@ -10006,8 +10355,8 @@ async function runNew(name, options) {
10006
10355
  const npmAuth = resolveNpmAuth(options.npmAuth, registry, ci);
10007
10356
  const stack = await resolveStack(options);
10008
10357
  const nxCloud = options.nxCloud ?? (options.yes ? false : await promptNxCloud());
10009
- const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs39.mkdtempSync)((0, import_node_path48.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
10010
- let workspaceRoot = (0, import_node_path48.join)(stagingParent, workspaceName);
10358
+ const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs41.mkdtempSync)((0, import_node_path50.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
10359
+ let workspaceRoot = (0, import_node_path50.join)(stagingParent, workspaceName);
10011
10360
  try {
10012
10361
  logger.step(`Creating Nx workspace '${workspaceName}' (preset: ts)`);
10013
10362
  runNpx(
@@ -10030,7 +10379,7 @@ async function runNew(name, options) {
10030
10379
  workspaceRoot = adoptTarget;
10031
10380
  }
10032
10381
  } finally {
10033
- if (adoptTarget !== void 0) (0, import_node_fs39.rmSync)(stagingParent, { recursive: true, force: true });
10382
+ if (adoptTarget !== void 0) (0, import_node_fs41.rmSync)(stagingParent, { recursive: true, force: true });
10034
10383
  }
10035
10384
  logger.step(
10036
10385
  "Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
@@ -10040,8 +10389,8 @@ async function runNew(name, options) {
10040
10389
  { workspaceName, scope, registry, agent, variableGroup, ci, stack, ...npmAuth && { npmAuth } },
10041
10390
  logger.detail
10042
10391
  );
10043
- (0, import_node_fs39.rmSync)((0, import_node_path48.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
10044
- (0, import_node_fs39.rmSync)((0, import_node_path48.join)(workspaceRoot, "package-lock.json"), { force: true });
10392
+ (0, import_node_fs41.rmSync)((0, import_node_path50.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
10393
+ (0, import_node_fs41.rmSync)((0, import_node_path50.join)(workspaceRoot, "package-lock.json"), { force: true });
10045
10394
  logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
10046
10395
  const installStatus = runShell(
10047
10396
  "npm",
@@ -10143,6 +10492,12 @@ function buildProgram(cliVersion) {
10143
10492
  ).option(
10144
10493
  "--empty",
10145
10494
  "npm-lib, internal-lib, react-lib, react-internal-lib, node-function-app: scaffold the slice skeleton only, with no sample code or spec"
10495
+ ).option("--app <project>", "container only: the app to put in an image (a node-app, react-app or go-app)").option("--port <port>", "container only: the port the app listens on, exposed by the image and published by start").option(
10496
+ "--e2e",
10497
+ "react-app only: also scaffold a Playwright end-to-end project, <name>-e2e, that runs the app and checks its greeting; its e2e target needs a browser (npx playwright install) and is not part of CI verify"
10498
+ ).option(
10499
+ "--esm",
10500
+ 'node-app, node-function-app: ES module output ("type": "module", explicit .js import specifiers, Jest mapped to match); not with fastify or nest'
10146
10501
  ).option(
10147
10502
  "--lib <name>",
10148
10503
  "python-vendor only: the internal Python library (libs/<name>) to vendor into <name>"