@mnci/cli 4.49.0 → 4.51.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_path34 = 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);
@@ -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,
@@ -5862,10 +5870,229 @@ async function addPythonVendor(workspaceRoot, consumer, options, kindProvided) {
5862
5870
  );
5863
5871
  }
5864
5872
 
5865
- // src/project-scaffolding/react-app.use-case.ts
5873
+ // src/project-scaffolding/container.use-case.ts
5866
5874
  var import_node_fs26 = require("fs");
5867
5875
  var import_node_path27 = require("path");
5868
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
+
6092
+ // src/project-scaffolding/react-app.use-case.ts
6093
+ var import_node_fs27 = require("fs");
6094
+ var import_node_path28 = require("path");
6095
+
5869
6096
  // src/project-scaffolding/react-app-example.algorithm.ts
5870
6097
  function reactAppExampleFiles(name) {
5871
6098
  return {
@@ -5990,12 +6217,12 @@ function reactAppTargets(name) {
5990
6217
  return targets;
5991
6218
  }
5992
6219
  function allowEnvFiles(workspaceRoot) {
5993
- const gitignorePath = (0, import_node_path27.join)(workspaceRoot, ".gitignore");
6220
+ const gitignorePath = (0, import_node_path28.join)(workspaceRoot, ".gitignore");
5994
6221
  if (!fileExists(gitignorePath)) {
5995
6222
  return;
5996
6223
  }
5997
6224
  const marker = "# MoNecromanCI: keep the committed per-environment Vite config (public VITE_ values)";
5998
- const current = (0, import_node_fs26.readFileSync)(gitignorePath, "utf8");
6225
+ const current = (0, import_node_fs27.readFileSync)(gitignorePath, "utf8");
5999
6226
  if (current.includes(marker)) {
6000
6227
  return;
6001
6228
  }
@@ -6022,17 +6249,17 @@ function addReactApp(workspaceRoot, name, stack, e2e = false) {
6022
6249
  workspaceRoot
6023
6250
  );
6024
6251
  ensureAdmZip(workspaceRoot);
6025
- const reactAppRoot = (0, import_node_path27.join)(workspaceRoot, "apps", name);
6252
+ const reactAppRoot = (0, import_node_path28.join)(workspaceRoot, "apps", name);
6026
6253
  for (const environment of REACT_ENVIRONMENTS) {
6027
- writeFileEnsured((0, import_node_path27.join)(reactAppRoot, `.env.${environment}`), reactEnvironmentFile(environment));
6254
+ writeFileEnsured((0, import_node_path28.join)(reactAppRoot, `.env.${environment}`), reactEnvironmentFile(environment));
6028
6255
  }
6029
6256
  allowEnvFiles(workspaceRoot);
6030
- addNxTargets((0, import_node_path27.join)(reactAppRoot, "package.json"), reactAppTargets(name));
6257
+ addNxTargets((0, import_node_path28.join)(reactAppRoot, "package.json"), reactAppTargets(name));
6031
6258
  reshapeReactAppScaffold(reactAppRoot);
6032
- (0, import_node_fs26.rmSync)((0, import_node_path27.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 });
6033
6260
  const example = reactAppExampleFiles(name);
6034
6261
  for (const [relative, contents] of Object.entries(example)) {
6035
- writeFileEnsured((0, import_node_path27.join)(reactAppRoot, "src", relative), contents);
6262
+ writeFileEnsured((0, import_node_path28.join)(reactAppRoot, "src", relative), contents);
6036
6263
  }
6037
6264
  removeGeneratedEslintConfig(workspaceRoot, `apps/${name}`);
6038
6265
  registerProjectCommands(workspaceRoot, name, { build: true, start: `nx run ${name}:serve` });
@@ -6042,9 +6269,9 @@ function addReactApp(workspaceRoot, name, stack, e2e = false) {
6042
6269
  }
6043
6270
  function pairE2eProject(workspaceRoot, name) {
6044
6271
  const e2eName = `${name}-e2e`;
6045
- const e2eRoot = (0, import_node_path27.join)(workspaceRoot, "apps", e2eName);
6046
- (0, import_node_fs26.rmSync)((0, import_node_path27.join)(e2eRoot, "src/example.spec.ts"), { force: true });
6047
- writeFileEnsured((0, import_node_path27.join)(e2eRoot, "src/greeting.e2e.spec.ts"), reactAppE2eSpec(name));
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));
6048
6275
  removeGeneratedEslintConfig(workspaceRoot, `apps/${e2eName}`);
6049
6276
  registerProjectCommands(workspaceRoot, e2eName, {
6050
6277
  build: false,
@@ -6053,13 +6280,13 @@ function pairE2eProject(workspaceRoot, name) {
6053
6280
  }
6054
6281
 
6055
6282
  // src/project-scaffolding/react-lib.use-case.ts
6056
- var import_node_path28 = require("path");
6283
+ var import_node_path29 = require("path");
6057
6284
  var REACT_LIB_BUNDLER = "rollup";
6058
6285
  async function addReactLib(workspaceRoot, name, options, kindProvided, stack) {
6059
6286
  ensurePlugin(workspaceRoot, "@nx/react");
6060
6287
  const scope = options.scope ?? (kindProvided ? defaultScope(workspaceRoot) : await promptText("npm scope for the published package", defaultScope(workspaceRoot)));
6061
- const projectRoot = (0, import_node_path28.join)(workspaceRoot, "packages", name);
6062
- const publishableManifest = (0, import_node_path28.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");
6063
6290
  runGeneratorAndRepair(
6064
6291
  workspaceRoot,
6065
6292
  [
@@ -6103,8 +6330,8 @@ function dropNxProjectName(manifestPath) {
6103
6330
  }
6104
6331
  function addReactInternalLib(workspaceRoot, name, stack, empty = false) {
6105
6332
  ensurePlugin(workspaceRoot, "@nx/react");
6106
- const projectRoot = (0, import_node_path28.join)(workspaceRoot, "libs", name);
6107
- const privateManifest = (0, import_node_path28.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");
6108
6335
  runGeneratorAndRepair(
6109
6336
  workspaceRoot,
6110
6337
  [
@@ -6131,8 +6358,8 @@ function addReactInternalLib(workspaceRoot, name, stack, empty = false) {
6131
6358
  }
6132
6359
 
6133
6360
  // src/project-scaffolding/vscode-extension.use-case.ts
6134
- var import_node_fs27 = require("fs");
6135
- var import_node_path29 = require("path");
6361
+ var import_node_fs28 = require("fs");
6362
+ var import_node_path30 = require("path");
6136
6363
  var VSCODE_EXTENSION_SCRIPT_PATH = "tools/vscode-extension.cjs";
6137
6364
  var VSCODE_ENGINE_FLOOR = "1.96.0";
6138
6365
  var VSCODE_SIDECAR_TARGETS = {
@@ -6317,8 +6544,8 @@ export const window = {
6317
6544
  var VSCODE_STUB_PATH = "test/vscode.stub.ts";
6318
6545
  function mapVscodeToStub(projectRoot, testRunner) {
6319
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}\` },`];
6320
- const path = (0, import_node_path29.join)(projectRoot, file);
6321
- const content = fileExists(path) ? (0, import_node_fs27.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") : "";
6322
6549
  const line = content.split("\n").find((candidate) => candidate.trim() === anchor);
6323
6550
  if (line === void 0) {
6324
6551
  throw new Error(`${file} has no "${anchor}" line to map the vscode module after; map 'vscode' to ${VSCODE_STUB_PATH} by hand.`);
@@ -6338,11 +6565,11 @@ function ensureVscodeToolchain(workspaceRoot) {
6338
6565
  }
6339
6566
  }
6340
6567
  function defaultPublisher(workspaceRoot) {
6341
- const { name } = readJson((0, import_node_path29.join)(workspaceRoot, "package.json"));
6568
+ const { name } = readJson((0, import_node_path30.join)(workspaceRoot, "package.json"));
6342
6569
  return (name ?? "publisher").replace(/^@/, "").split("/", 1)[0];
6343
6570
  }
6344
6571
  function reshapeManifest(projectRoot, name, publisher) {
6345
- const manifestPath = (0, import_node_path29.join)(projectRoot, "package.json");
6572
+ const manifestPath = (0, import_node_path30.join)(projectRoot, "package.json");
6346
6573
  const manifest = readJson(manifestPath);
6347
6574
  const { nx = {}, ...rest } = manifest;
6348
6575
  const targets = { ...nx.targets };
@@ -6406,11 +6633,11 @@ function vscodeExtensionPublishTarget(name, sidecar) {
6406
6633
  };
6407
6634
  }
6408
6635
  function addDebugLaunch(workspaceRoot, name) {
6409
- const file = (0, import_node_fs27.readdirSync)(workspaceRoot).find((entry) => entry.endsWith(".code-workspace"));
6636
+ const file = (0, import_node_fs28.readdirSync)(workspaceRoot).find((entry) => entry.endsWith(".code-workspace"));
6410
6637
  if (file === void 0) {
6411
6638
  return;
6412
6639
  }
6413
- const path = (0, import_node_path29.join)(workspaceRoot, file);
6640
+ const path = (0, import_node_path30.join)(workspaceRoot, file);
6414
6641
  const workspace = readCodeWorkspace(path) ?? {};
6415
6642
  const folder = `\${workspaceFolder:${workspace.folders?.[0]?.name ?? file.replace(/\.code-workspace$/, "")}}`;
6416
6643
  const taskLabel = `${name}: build (development)`;
@@ -6443,39 +6670,39 @@ function addDebugLaunch(workspaceRoot, name) {
6443
6670
  );
6444
6671
  }
6445
6672
  function refreshVscodeExtensionScript(workspaceRoot) {
6446
- const apps = (0, import_node_path29.join)(workspaceRoot, "apps");
6673
+ const apps = (0, import_node_path30.join)(workspaceRoot, "apps");
6447
6674
  let entries;
6448
6675
  try {
6449
- entries = (0, import_node_fs27.readdirSync)(apps);
6676
+ entries = (0, import_node_fs28.readdirSync)(apps);
6450
6677
  } catch {
6451
6678
  return false;
6452
6679
  }
6453
6680
  const hasExtension = entries.some((entry) => {
6454
- const manifestPath = (0, import_node_path29.join)(apps, entry, "package.json");
6681
+ const manifestPath = (0, import_node_path30.join)(apps, entry, "package.json");
6455
6682
  if (!fileExists(manifestPath)) {
6456
6683
  return false;
6457
6684
  }
6458
6685
  const tags = readJson(manifestPath).nx?.tags ?? [];
6459
6686
  return tags.includes(VSCODE_EXTENSION_TAG);
6460
6687
  });
6461
- const path = (0, import_node_path29.join)(workspaceRoot, VSCODE_EXTENSION_SCRIPT_PATH);
6462
- if (!hasExtension || fileExists(path) && (0, import_node_fs27.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) {
6463
6690
  return false;
6464
6691
  }
6465
6692
  writeFileEnsured(path, VSCODE_EXTENSION_SCRIPT);
6466
6693
  return true;
6467
6694
  }
6468
6695
  function pinVscodeExtensionProjectNames(workspaceRoot) {
6469
- const apps = (0, import_node_path29.join)(workspaceRoot, "apps");
6696
+ const apps = (0, import_node_path30.join)(workspaceRoot, "apps");
6470
6697
  let entries;
6471
6698
  try {
6472
- entries = (0, import_node_fs27.readdirSync)(apps);
6699
+ entries = (0, import_node_fs28.readdirSync)(apps);
6473
6700
  } catch {
6474
6701
  return [];
6475
6702
  }
6476
6703
  const updated = [];
6477
6704
  for (const entry of entries) {
6478
- const manifestPath = (0, import_node_path29.join)(apps, entry, "package.json");
6705
+ const manifestPath = (0, import_node_path30.join)(apps, entry, "package.json");
6479
6706
  if (!fileExists(manifestPath)) {
6480
6707
  continue;
6481
6708
  }
@@ -6495,22 +6722,22 @@ function refreshLockFile(workspaceRoot, name) {
6495
6722
  }
6496
6723
  function addVscodeExtension(workspaceRoot, name, stack, options = {}) {
6497
6724
  const { sidecar } = options;
6498
- if (sidecar !== void 0 && !fileExists((0, import_node_path29.join)(workspaceRoot, "apps", sidecar, "project.json"))) {
6725
+ if (sidecar !== void 0 && !fileExists((0, import_node_path30.join)(workspaceRoot, "apps", sidecar, "project.json"))) {
6499
6726
  throw new Error(`--sidecar ${sidecar}: no Go app at apps/${sidecar}. Add it first with \`mnci add go-app ${sidecar}\`.`);
6500
6727
  }
6501
6728
  runNodeApp(workspaceRoot, name, stack, "none");
6502
6729
  ensureVscodeToolchain(workspaceRoot);
6503
- const projectRoot = (0, import_node_path29.join)(workspaceRoot, "apps", name);
6730
+ const projectRoot = (0, import_node_path30.join)(workspaceRoot, "apps", name);
6504
6731
  reshapeManifest(projectRoot, name, options.publisher ?? defaultPublisher(workspaceRoot));
6505
- addVscodeTypes((0, import_node_path29.join)(projectRoot, "tsconfig.app.json"));
6506
- addVscodeTypes((0, import_node_path29.join)(projectRoot, "tsconfig.spec.json"));
6507
- writeFileEnsured((0, import_node_path29.join)(projectRoot, "src/main.ts"), vscodeExtensionMain(name));
6508
- writeFileEnsured((0, import_node_path29.join)(projectRoot, "src/main.spec.ts"), vscodeExtensionMainSpec(name));
6509
- writeFileEnsured((0, import_node_path29.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);
6510
6737
  mapVscodeToStub(projectRoot, stack.testRunner);
6511
- writeFileEnsured((0, import_node_path29.join)(projectRoot, ".vscodeignore"), VSCODE_IGNORE);
6512
- writeFileEnsured((0, import_node_path29.join)(workspaceRoot, VSCODE_EXTENSION_SCRIPT_PATH), VSCODE_EXTENSION_SCRIPT);
6513
- addNxTargets((0, import_node_path29.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"), {
6514
6741
  "package": vscodeExtensionPackageTarget(name, sidecar),
6515
6742
  "nx-release-publish": vscodeExtensionPublishTarget(name, sidecar)
6516
6743
  });
@@ -6521,12 +6748,12 @@ function addVscodeExtension(workspaceRoot, name, stack, options = {}) {
6521
6748
  }
6522
6749
 
6523
6750
  // src/dependency-management/install-dependencies.use-case.ts
6524
- var import_node_fs29 = require("fs");
6525
- var import_node_path31 = require("path");
6751
+ var import_node_fs30 = require("fs");
6752
+ var import_node_path32 = require("path");
6526
6753
 
6527
6754
  // src/dependency-management/manifest.repository.ts
6528
- var import_node_fs28 = require("fs");
6529
- var import_node_path30 = require("path");
6755
+ var import_node_fs29 = require("fs");
6756
+ var import_node_path31 = require("path");
6530
6757
  var ECOSYSTEMS = ["npm", "pip", "pub", "nuget", "go"];
6531
6758
  var ROOT_LABEL = "(root)";
6532
6759
  var MANIFEST_GLOBS = {
@@ -6559,23 +6786,23 @@ function projectOf(manifestRelativePath) {
6559
6786
  }
6560
6787
  function hasEcosystem(workspaceRoot, ecosystem) {
6561
6788
  if (ecosystem === "go") {
6562
- return fileExists((0, import_node_path30.join)(workspaceRoot, "go.mod"));
6789
+ return fileExists((0, import_node_path31.join)(workspaceRoot, "go.mod"));
6563
6790
  }
6564
6791
  if (ecosystem === "nuget") {
6565
- return (0, import_node_fs28.globSync)(MANIFEST_GLOBS.nuget, { cwd: workspaceRoot }).length > 0;
6792
+ return (0, import_node_fs29.globSync)(MANIFEST_GLOBS.nuget, { cwd: workspaceRoot }).length > 0;
6566
6793
  }
6567
6794
  const rootMarker = {
6568
6795
  npm: "package.json",
6569
6796
  pip: "requirements-dev.txt",
6570
6797
  pub: "pubspec.yaml"
6571
6798
  };
6572
- return fileExists((0, import_node_path30.join)(workspaceRoot, rootMarker[ecosystem])) || (0, import_node_fs28.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;
6573
6800
  }
6574
6801
  function collectNpm(workspaceRoot) {
6575
6802
  const sites = [];
6576
- const relativePaths = ["package.json", ...(0, import_node_fs28.globSync)(MANIFEST_GLOBS.npm, { cwd: workspaceRoot })];
6803
+ const relativePaths = ["package.json", ...(0, import_node_fs29.globSync)(MANIFEST_GLOBS.npm, { cwd: workspaceRoot })];
6577
6804
  for (const relativePath of relativePaths) {
6578
- const manifestPath = (0, import_node_path30.join)(workspaceRoot, relativePath);
6805
+ const manifestPath = (0, import_node_path31.join)(workspaceRoot, relativePath);
6579
6806
  try {
6580
6807
  const manifest = readJson(manifestPath);
6581
6808
  sites.push(...npmManifestSites(manifest, relativePath, manifestPath));
@@ -6636,17 +6863,17 @@ function collectPip(workspaceRoot) {
6636
6863
  name: parsed.name,
6637
6864
  ecosystem: "pip",
6638
6865
  project: projectOf(relativePath),
6639
- manifestPath: (0, import_node_path30.join)(workspaceRoot, relativePath),
6866
+ manifestPath: (0, import_node_path31.join)(workspaceRoot, relativePath),
6640
6867
  section,
6641
6868
  spec: parsed.spec,
6642
6869
  rewritable: parsed.rewritable && parsed.spec !== ""
6643
6870
  });
6644
6871
  };
6645
- const pyprojectPaths = (0, import_node_fs28.globSync)(MANIFEST_GLOBS.pip, { cwd: workspaceRoot });
6872
+ const pyprojectPaths = (0, import_node_fs29.globSync)(MANIFEST_GLOBS.pip, { cwd: workspaceRoot });
6646
6873
  for (const relativePath of pyprojectPaths) {
6647
6874
  let content;
6648
6875
  try {
6649
- content = (0, import_node_fs28.readFileSync)((0, import_node_path30.join)(workspaceRoot, relativePath), "utf8");
6876
+ content = (0, import_node_fs29.readFileSync)((0, import_node_path31.join)(workspaceRoot, relativePath), "utf8");
6650
6877
  } catch {
6651
6878
  continue;
6652
6879
  }
@@ -6657,14 +6884,14 @@ function collectPip(workspaceRoot) {
6657
6884
  }
6658
6885
  const requirementsPaths = [
6659
6886
  "requirements-dev.txt",
6660
- ...(0, import_node_fs28.globSync)("apps/*/requirements.txt", { cwd: workspaceRoot })
6887
+ ...(0, import_node_fs29.globSync)("apps/*/requirements.txt", { cwd: workspaceRoot })
6661
6888
  ];
6662
6889
  for (const relativePath of requirementsPaths) {
6663
- if (!fileExists((0, import_node_path30.join)(workspaceRoot, relativePath))) {
6890
+ if (!fileExists((0, import_node_path31.join)(workspaceRoot, relativePath))) {
6664
6891
  continue;
6665
6892
  }
6666
6893
  const section = relativePath === "requirements-dev.txt" ? "devDep" : "dep";
6667
- const lines = (0, import_node_fs28.readFileSync)((0, import_node_path30.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/);
6668
6895
  for (const line of lines) {
6669
6896
  push(relativePath, section, line);
6670
6897
  }
@@ -6702,11 +6929,11 @@ function pyprojectDependencies(content) {
6702
6929
  }
6703
6930
  function collectPub(workspaceRoot) {
6704
6931
  const sites = [];
6705
- const pubspecPaths = (0, import_node_fs28.globSync)(MANIFEST_GLOBS.pub, { cwd: workspaceRoot });
6932
+ const pubspecPaths = (0, import_node_fs29.globSync)(MANIFEST_GLOBS.pub, { cwd: workspaceRoot });
6706
6933
  for (const relativePath of pubspecPaths) {
6707
6934
  let content;
6708
6935
  try {
6709
- content = (0, import_node_fs28.readFileSync)((0, import_node_path30.join)(workspaceRoot, relativePath), "utf8");
6936
+ content = (0, import_node_fs29.readFileSync)((0, import_node_path31.join)(workspaceRoot, relativePath), "utf8");
6710
6937
  } catch {
6711
6938
  continue;
6712
6939
  }
@@ -6716,7 +6943,7 @@ function collectPub(workspaceRoot) {
6716
6943
  name: entry.name,
6717
6944
  ecosystem: "pub",
6718
6945
  project: projectOf(relativePath),
6719
- manifestPath: (0, import_node_path30.join)(workspaceRoot, relativePath),
6946
+ manifestPath: (0, import_node_path31.join)(workspaceRoot, relativePath),
6720
6947
  section: block === "dependencies" ? "dep" : "devDep",
6721
6948
  spec: entry.spec,
6722
6949
  rewritable: entry.spec !== ""
@@ -6766,11 +6993,11 @@ function stripQuotes(value) {
6766
6993
  }
6767
6994
  function collectNuget(workspaceRoot) {
6768
6995
  const sites = [];
6769
- const csprojPaths = (0, import_node_fs28.globSync)(MANIFEST_GLOBS.nuget, { cwd: workspaceRoot });
6996
+ const csprojPaths = (0, import_node_fs29.globSync)(MANIFEST_GLOBS.nuget, { cwd: workspaceRoot });
6770
6997
  for (const relativePath of csprojPaths) {
6771
6998
  let content;
6772
6999
  try {
6773
- content = (0, import_node_fs28.readFileSync)((0, import_node_path30.join)(workspaceRoot, relativePath), "utf8");
7000
+ content = (0, import_node_fs29.readFileSync)((0, import_node_path31.join)(workspaceRoot, relativePath), "utf8");
6774
7001
  } catch {
6775
7002
  continue;
6776
7003
  }
@@ -6779,7 +7006,7 @@ function collectNuget(workspaceRoot) {
6779
7006
  name: entry.name,
6780
7007
  ecosystem: "nuget",
6781
7008
  project: projectOf(relativePath),
6782
- manifestPath: (0, import_node_path30.join)(workspaceRoot, relativePath),
7009
+ manifestPath: (0, import_node_path31.join)(workspaceRoot, relativePath),
6783
7010
  section: "dep",
6784
7011
  spec: entry.spec,
6785
7012
  // A plain SemVer string is rewritable; an MSBuild property reference
@@ -6804,13 +7031,13 @@ function csprojPackageReferences(content) {
6804
7031
  return entries;
6805
7032
  }
6806
7033
  function collectGo(workspaceRoot) {
6807
- const manifestPath = (0, import_node_path30.join)(workspaceRoot, "go.mod");
7034
+ const manifestPath = (0, import_node_path31.join)(workspaceRoot, "go.mod");
6808
7035
  if (!fileExists(manifestPath)) {
6809
7036
  return [];
6810
7037
  }
6811
7038
  let content;
6812
7039
  try {
6813
- content = (0, import_node_fs28.readFileSync)(manifestPath, "utf8");
7040
+ content = (0, import_node_fs29.readFileSync)(manifestPath, "utf8");
6814
7041
  } catch {
6815
7042
  return [];
6816
7043
  }
@@ -6859,7 +7086,7 @@ var PROJECT_GLOBS = {
6859
7086
  function locateProjects(workspaceRoot) {
6860
7087
  const locations = [];
6861
7088
  for (const ecosystem of ECOSYSTEMS) {
6862
- const relativePaths = (0, import_node_fs28.globSync)(PROJECT_GLOBS[ecosystem], { cwd: workspaceRoot });
7089
+ const relativePaths = (0, import_node_fs29.globSync)(PROJECT_GLOBS[ecosystem], { cwd: workspaceRoot });
6863
7090
  for (const relativePath of relativePaths) {
6864
7091
  const posix = toPosix(relativePath);
6865
7092
  const dir = posix.slice(0, posix.lastIndexOf("/"));
@@ -6867,7 +7094,7 @@ function locateProjects(workspaceRoot) {
6867
7094
  ecosystem,
6868
7095
  dir,
6869
7096
  name: dir.slice(dir.lastIndexOf("/") + 1),
6870
- manifestPath: (0, import_node_path30.join)(workspaceRoot, relativePath)
7097
+ manifestPath: (0, import_node_path31.join)(workspaceRoot, relativePath)
6871
7098
  });
6872
7099
  }
6873
7100
  }
@@ -6888,7 +7115,7 @@ function rewriteSpec(site, spec) {
6888
7115
  writeFileEnsured(site.manifestPath, toJson(manifest));
6889
7116
  return true;
6890
7117
  }
6891
- const before = (0, import_node_fs28.readFileSync)(site.manifestPath, "utf8");
7118
+ const before = (0, import_node_fs29.readFileSync)(site.manifestPath, "utf8");
6892
7119
  const after = (() => {
6893
7120
  switch (site.ecosystem) {
6894
7121
  case "pip": {
@@ -6979,13 +7206,13 @@ function escapeForRegex(value) {
6979
7206
  function isWorkspaceProject(workspaceRoot, name) {
6980
7207
  const short = name.includes("/") ? name.split("/").at(-1) : name;
6981
7208
  return ["apps", "libs", "packages"].some(
6982
- (directory) => fileExists((0, import_node_path30.join)(workspaceRoot, directory, short ?? name, "package.json"))
7209
+ (directory) => fileExists((0, import_node_path31.join)(workspaceRoot, directory, short ?? name, "package.json"))
6983
7210
  );
6984
7211
  }
6985
7212
  function isAliasedInstall(workspaceRoot, name) {
6986
7213
  try {
6987
7214
  const installed = readJson(
6988
- (0, import_node_path30.join)(workspaceRoot, "node_modules", name, "package.json")
7215
+ (0, import_node_path31.join)(workspaceRoot, "node_modules", name, "package.json")
6989
7216
  );
6990
7217
  return installed.name !== void 0 && installed.name !== name;
6991
7218
  } catch {
@@ -6996,14 +7223,14 @@ function resolvedVersion(workspaceRoot, ecosystem, name) {
6996
7223
  if (ecosystem === "npm") {
6997
7224
  try {
6998
7225
  return readJson(
6999
- (0, import_node_path30.join)(workspaceRoot, "node_modules", name, "package.json")
7226
+ (0, import_node_path31.join)(workspaceRoot, "node_modules", name, "package.json")
7000
7227
  ).version;
7001
7228
  } catch {
7002
7229
  return void 0;
7003
7230
  }
7004
7231
  }
7005
7232
  if (ecosystem === "pub") {
7006
- const lockPath = (0, import_node_path30.join)(workspaceRoot, "pubspec.lock");
7233
+ const lockPath = (0, import_node_path31.join)(workspaceRoot, "pubspec.lock");
7007
7234
  if (!fileExists(lockPath)) {
7008
7235
  return void 0;
7009
7236
  }
@@ -7011,7 +7238,7 @@ function resolvedVersion(workspaceRoot, ecosystem, name) {
7011
7238
  const match = new RegExp(
7012
7239
  String.raw`^\s{2}${escapeForRegex(name)}:[^]*?^\s+version:\s*["']?([^"'\s]+)`,
7013
7240
  "m"
7014
- ).exec((0, import_node_fs28.readFileSync)(lockPath, "utf8"));
7241
+ ).exec((0, import_node_fs29.readFileSync)(lockPath, "utf8"));
7015
7242
  return match?.[1];
7016
7243
  } catch {
7017
7244
  return void 0;
@@ -7077,7 +7304,7 @@ function resolveTarget(locations, target) {
7077
7304
  return byName[0];
7078
7305
  }
7079
7306
  function installInto(workspaceRoot, location, packages, saveDev) {
7080
- const projectDir = (0, import_node_path31.join)(workspaceRoot, location.dir);
7307
+ const projectDir = (0, import_node_path32.join)(workspaceRoot, location.dir);
7081
7308
  switch (location.ecosystem) {
7082
7309
  case "npm": {
7083
7310
  const flags = saveDev ? ["--save-dev"] : [];
@@ -7130,7 +7357,7 @@ function installInto(workspaceRoot, location, packages, saveDev) {
7130
7357
  function addPythonDependency(manifestPath, specifier) {
7131
7358
  let content;
7132
7359
  try {
7133
- content = (0, import_node_fs29.readFileSync)(manifestPath, "utf8");
7360
+ content = (0, import_node_fs30.readFileSync)(manifestPath, "utf8");
7134
7361
  } catch {
7135
7362
  logger.warn(`Could not read ${manifestPath} \u2014 skipped ${specifier}`);
7136
7363
  return;
@@ -7159,7 +7386,7 @@ function installEverything(workspaceRoot, only) {
7159
7386
  }
7160
7387
  function ecosystemPresent(workspaceRoot, ecosystem) {
7161
7388
  if (ecosystem === "go") {
7162
- return fileExists((0, import_node_path31.join)(workspaceRoot, "go.work")) || fileExists((0, import_node_path31.join)(workspaceRoot, "go.mod"));
7389
+ return fileExists((0, import_node_path32.join)(workspaceRoot, "go.work")) || fileExists((0, import_node_path32.join)(workspaceRoot, "go.mod"));
7163
7390
  }
7164
7391
  return hasEcosystem(workspaceRoot, ecosystem);
7165
7392
  }
@@ -7178,7 +7405,7 @@ function splitAtLast(value, separator) {
7178
7405
  }
7179
7406
 
7180
7407
  // src/dependency-management/sync-dependencies.use-case.ts
7181
- var import_node_path32 = require("path");
7408
+ var import_node_path33 = require("path");
7182
7409
 
7183
7410
  // src/dependency-management/semver.algorithm.ts
7184
7411
  var UPDATE_KINDS = ["patch", "minor", "major", "non-semver"];
@@ -7355,7 +7582,7 @@ function describe(finding) {
7355
7582
  return `${finding.name} \u2192 ${finding.target} (${finding.reason}); disagreeing: ${sites}`;
7356
7583
  }
7357
7584
  function runSync(workspaceRoot, options) {
7358
- if (!fileExists((0, import_node_path32.join)(workspaceRoot, "nx.json"))) {
7585
+ if (!fileExists((0, import_node_path33.join)(workspaceRoot, "nx.json"))) {
7359
7586
  throw new Error(
7360
7587
  `No nx.json found in ${workspaceRoot} \u2014 run 'mnci sync' from the workspace root.`
7361
7588
  );
@@ -7405,7 +7632,7 @@ function runSync(workspaceRoot, options) {
7405
7632
  }
7406
7633
 
7407
7634
  // src/dependency-management/update-dependencies.use-case.ts
7408
- var import_node_path33 = require("path");
7635
+ var import_node_path34 = require("path");
7409
7636
  var import_prompts2 = require("@inquirer/prompts");
7410
7637
 
7411
7638
  // src/dependency-management/registry.client.ts
@@ -7688,7 +7915,7 @@ function reinstall(workspaceRoot, ecosystems) {
7688
7915
  function hasPythonInstallScript(workspaceRoot) {
7689
7916
  try {
7690
7917
  const manifest = readJson(
7691
- (0, import_node_path33.join)(workspaceRoot, "package.json")
7918
+ (0, import_node_path34.join)(workspaceRoot, "package.json")
7692
7919
  );
7693
7920
  return manifest.scripts?.["python:install"] !== void 0;
7694
7921
  } catch {
@@ -7704,7 +7931,7 @@ function applyGoUpdates(workspaceRoot, entries) {
7704
7931
  }
7705
7932
  }
7706
7933
  async function runUp(workspaceRoot, options) {
7707
- if (!fileExists((0, import_node_path33.join)(workspaceRoot, "nx.json"))) {
7934
+ if (!fileExists((0, import_node_path34.join)(workspaceRoot, "nx.json"))) {
7708
7935
  throw new Error(`No nx.json found in ${workspaceRoot} \u2014 run 'mnci up' from the workspace root.`);
7709
7936
  }
7710
7937
  const ecosystems = resolveEcosystems(options.ecosystem);
@@ -7776,12 +8003,13 @@ var PROJECT_KINDS = [
7776
8003
  "csharp-function-app",
7777
8004
  "csharp-lib",
7778
8005
  "csharp-internal-lib",
7779
- "vscode-extension"
8006
+ "vscode-extension",
8007
+ "container"
7780
8008
  ];
7781
8009
  var EMPTY_KINDS = /* @__PURE__ */ new Set(["npm-lib", "internal-lib", "react-lib", "react-internal-lib", "node-function-app"]);
7782
8010
  async function runAdd(kind, name, options) {
7783
8011
  const workspaceRoot = process.cwd();
7784
- if (!fileExists((0, import_node_path34.join)(workspaceRoot, "nx.json"))) {
8012
+ if (!fileExists((0, import_node_path35.join)(workspaceRoot, "nx.json"))) {
7785
8013
  throw new Error("No nx.json found here. Run `add` from the workspace root.");
7786
8014
  }
7787
8015
  const stack = readWorkspaceStack(workspaceRoot);
@@ -7810,6 +8038,17 @@ async function runAdd(kind, name, options) {
7810
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.`);
7811
8039
  }
7812
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
+ }
7813
8052
  if (resolvedKind !== "react-app" && options.e2e === true) {
7814
8053
  throw new Error(`--e2e applies to react-app, not ${resolvedKind}.`);
7815
8054
  }
@@ -7855,8 +8094,8 @@ async function runAdd(kind, name, options) {
7855
8094
  ],
7856
8095
  workspaceRoot
7857
8096
  );
7858
- markPrivate((0, import_node_path34.join)(workspaceRoot, "libs", resolvedName, "package.json"));
7859
- renameScaffoldPlaceholder((0, import_node_path34.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);
7860
8099
  removeGeneratedEslintConfig(workspaceRoot, `libs/${resolvedName}`);
7861
8100
  registerProjectCommands(workspaceRoot, resolvedName, { build: true });
7862
8101
  break;
@@ -7877,6 +8116,10 @@ async function runAdd(kind, name, options) {
7877
8116
  addPythonInternalLib(workspaceRoot, resolvedName);
7878
8117
  break;
7879
8118
  }
8119
+ case "container": {
8120
+ addContainer(workspaceRoot, resolvedName, { app: options.app, port: options.port === void 0 ? void 0 : Number(options.port) });
8121
+ break;
8122
+ }
7880
8123
  case "go-app": {
7881
8124
  addGoApp(workspaceRoot, resolvedName, { release: options.release, cgo: options.cgo, web: options.web });
7882
8125
  break;
@@ -7948,7 +8191,7 @@ async function runAdd(kind, name, options) {
7948
8191
  }
7949
8192
  function readWorkspaceStack(workspaceRoot) {
7950
8193
  const nxJson = readJson(
7951
- (0, import_node_path34.join)(workspaceRoot, "nx.json")
8194
+ (0, import_node_path35.join)(workspaceRoot, "nx.json")
7952
8195
  );
7953
8196
  const stack = nxJson.mnci?.stack;
7954
8197
  return {
@@ -7965,6 +8208,7 @@ var PROJECT_KIND_CATALOG = [
7965
8208
  { kind: "node-function-app", language: "typescript", label: "Node function app", description: "An Azure Functions (v4) app in TypeScript", flags: ["empty", "esm"], requiredFlags: [] },
7966
8209
  { kind: "npm-lib", language: "typescript", label: "npm library", description: "A library published to npm, bundled with Rollup", flags: ["scope", "empty"], requiredFlags: [] },
7967
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"] },
7968
8212
  { kind: "vscode-extension", language: "typescript", label: "VS Code extension", description: "A bundled Marketplace extension, optionally shipping a Go sidecar", flags: ["publisher", "sidecar"], requiredFlags: [] },
7969
8213
  { kind: "python-app", language: "python", label: "Python app", description: "A pip-native Python application (Ruff, pytest)", flags: [], requiredFlags: [] },
7970
8214
  { kind: "python-function-app", language: "python", label: "Python function app", description: "An Azure Functions (v2 model) app in Python", flags: [], requiredFlags: [] },
@@ -7995,14 +8239,130 @@ function runKinds(options) {
7995
8239
  }
7996
8240
  }
7997
8241
 
7998
- // src/workspace-diagnostics/check-invariants.use-case.ts
8242
+ // src/dev-servers/run-dev.use-case.ts
8243
+ var import_prompts4 = require("@inquirer/prompts");
8244
+
8245
+ // src/dev-servers/spawn-project.client.ts
7999
8246
  var import_node_child_process = require("child_process");
8000
- var import_node_fs31 = require("fs");
8247
+ var import_node_readline = require("readline");
8248
+ var import_cross_spawn3 = __toESM(require("cross-spawn"));
8249
+ function spawnProject(workspaceRoot, project, target, onLine) {
8250
+ const windows = process.platform === "win32";
8251
+ const child = (0, import_cross_spawn3.default)("npx", ["nx", "run", `${project}:${target}`], { cwd: workspaceRoot, stdio: ["ignore", "pipe", "pipe"], detached: !windows });
8252
+ for (const stream of [child.stdout, child.stderr]) {
8253
+ if (stream !== null) {
8254
+ (0, import_node_readline.createInterface)({ input: stream }).on("line", onLine);
8255
+ }
8256
+ }
8257
+ const exited = new Promise((resolve2) => {
8258
+ child.on("error", () => {
8259
+ resolve2(1);
8260
+ });
8261
+ child.on("close", (code) => {
8262
+ resolve2(code ?? 1);
8263
+ });
8264
+ });
8265
+ const stop = () => {
8266
+ if (child.pid === void 0 || child.exitCode !== null) {
8267
+ return;
8268
+ }
8269
+ if (windows) {
8270
+ (0, import_node_child_process.spawnSync)("taskkill", ["/pid", String(child.pid), "/T", "/F"], { stdio: "ignore" });
8271
+ } else {
8272
+ try {
8273
+ process.kill(-child.pid, "SIGTERM");
8274
+ } catch {
8275
+ child.kill("SIGTERM");
8276
+ }
8277
+ }
8278
+ };
8279
+ return { stop, exited };
8280
+ }
8281
+
8282
+ // src/dev-servers/startable-projects.use-case.ts
8001
8283
  var import_node_path36 = require("path");
8284
+ var START_SUFFIX = ":start";
8285
+ var NX_RUN = /\bnx run [^\s:]+:(\S+)/;
8286
+ function listStartableProjects(workspaceRoot) {
8287
+ const manifestPath = (0, import_node_path36.join)(workspaceRoot, "package.json");
8288
+ if (!fileExists(manifestPath)) {
8289
+ return [];
8290
+ }
8291
+ const scripts = readJson(manifestPath).scripts ?? {};
8292
+ return Object.entries(scripts).filter(([script]) => script.endsWith(START_SUFFIX)).map(([script, command]) => ({ project: script.slice(0, -START_SUFFIX.length), target: NX_RUN.exec(command)?.[1] ?? "start" })).toSorted((a, b) => a.project.localeCompare(b.project));
8293
+ }
8294
+
8295
+ // src/dev-servers/run-dev.use-case.ts
8296
+ async function chooseProjects(available) {
8297
+ return await (0, import_prompts4.checkbox)({
8298
+ message: "Which projects should start together?",
8299
+ choices: available.map((name) => ({ name, value: name })),
8300
+ required: true
8301
+ });
8302
+ }
8303
+ async function selectProjects(available, named, options, dependencies) {
8304
+ const names = available.map((entry) => entry.project);
8305
+ const unknown = named.filter((name) => !names.includes(name));
8306
+ if (unknown.length > 0) {
8307
+ throw new Error(`Cannot start ${unknown.join(", ")}: not a project with a start command. Startable: ${names.join(", ")}.`);
8308
+ }
8309
+ let chosen = named;
8310
+ if (chosen.length === 0) {
8311
+ if (options.all === true) {
8312
+ chosen = names;
8313
+ } else if (dependencies.interactive ?? process.stdin.isTTY) {
8314
+ chosen = await (dependencies.choose ?? chooseProjects)(names);
8315
+ } else {
8316
+ throw new Error(`Name the projects to start, or pass --all. Startable: ${names.join(", ")}.`);
8317
+ }
8318
+ }
8319
+ return available.filter((entry) => chosen.includes(entry.project));
8320
+ }
8321
+ async function runDev(workspaceRoot, named, options = {}, dependencies = {}) {
8322
+ const available = listStartableProjects(workspaceRoot);
8323
+ if (available.length === 0) {
8324
+ throw new Error("No project here has a start command. Add an app (`mnci add`) first; libraries are not started.");
8325
+ }
8326
+ const selected = await selectProjects(available, named, options, dependencies);
8327
+ for (const { project, target } of selected) {
8328
+ logger.info(`${project}: nx run ${project}:${target}`);
8329
+ }
8330
+ if (options.dryRun === true) {
8331
+ return;
8332
+ }
8333
+ const start = dependencies.start ?? ((project, target, onLine) => spawnProject(workspaceRoot, project, target, onLine));
8334
+ const print = dependencies.print ?? ((line) => {
8335
+ console.log(line);
8336
+ });
8337
+ const running = selected.map(({ project, target }) => start(project, target, (line) => {
8338
+ print(`[${project}] ${line}`);
8339
+ }));
8340
+ const stopAll = () => {
8341
+ for (const each of running) {
8342
+ each.stop();
8343
+ }
8344
+ };
8345
+ process.once("SIGINT", stopAll);
8346
+ process.once("SIGTERM", stopAll);
8347
+ const first = await Promise.race(running.map(async (each, index) => ({ index, status: await each.exited })));
8348
+ stopAll();
8349
+ await Promise.all(running.map(async (each) => await each.exited));
8350
+ process.removeListener("SIGINT", stopAll);
8351
+ process.removeListener("SIGTERM", stopAll);
8352
+ logger.info(`${selected[first.index].project} stopped (${first.status}), so the rest were stopped too.`);
8353
+ if (first.status !== 0) {
8354
+ process.exitCode = first.status;
8355
+ }
8356
+ }
8357
+
8358
+ // src/workspace-diagnostics/check-invariants.use-case.ts
8359
+ var import_node_child_process2 = require("child_process");
8360
+ var import_node_fs32 = require("fs");
8361
+ var import_node_path38 = require("path");
8002
8362
 
8003
8363
  // src/workspace-diagnostics/locate-stranded-release-tags.use-case.ts
8004
- var import_node_fs30 = require("fs");
8005
- var import_node_path35 = require("path");
8364
+ var import_node_fs31 = require("fs");
8365
+ var import_node_path37 = require("path");
8006
8366
 
8007
8367
  // src/workspace-diagnostics/stranded-release-tags.algorithm.ts
8008
8368
  function compareVersions2(left, right) {
@@ -8039,7 +8399,7 @@ function locateStrandedReleaseTags(workspaceRoot, tagPattern) {
8039
8399
  if (listed.status !== 0) {
8040
8400
  return [];
8041
8401
  }
8042
- const projects = (0, import_node_fs30.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot }).map((path) => readJson((0, import_node_path35.join)(workspaceRoot, path))).filter((manifest) => manifest.private !== true && manifest.name !== void 0).map((manifest) => manifest.name);
8402
+ const projects = (0, import_node_fs31.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot }).map((path) => readJson((0, import_node_path37.join)(workspaceRoot, path))).filter((manifest) => manifest.private !== true && manifest.name !== void 0).map((manifest) => manifest.name);
8043
8403
  return findStrandedReleaseTags(projects, listed.stdout.split(/\r?\n/).filter(Boolean));
8044
8404
  }
8045
8405
 
@@ -8049,8 +8409,8 @@ function toPosix2(path) {
8049
8409
  return path.replaceAll("\\", "/");
8050
8410
  }
8051
8411
  function checkEslintConfigs(workspaceRoot) {
8052
- const rootConfigs = (0, import_node_fs31.globSync)("eslint.config.{js,mjs,cjs,ts,mts,cts}", { cwd: workspaceRoot });
8053
- const projectConfigs = (0, import_node_fs31.globSync)("{apps,libs,packages}/*/eslint.config.{js,mjs,cjs,ts,mts,cts}", {
8412
+ const rootConfigs = (0, import_node_fs32.globSync)("eslint.config.{js,mjs,cjs,ts,mts,cts}", { cwd: workspaceRoot });
8413
+ const projectConfigs = (0, import_node_fs32.globSync)("{apps,libs,packages}/*/eslint.config.{js,mjs,cjs,ts,mts,cts}", {
8054
8414
  cwd: workspaceRoot
8055
8415
  }).map((config) => toPosix2(config));
8056
8416
  return [
@@ -8070,10 +8430,10 @@ function checkEslintConfigs(workspaceRoot) {
8070
8430
  ];
8071
8431
  }
8072
8432
  function checkEslintEntryPointReachesTheRules(workspaceRoot) {
8073
- const rules = (0, import_node_path36.join)(workspaceRoot, ESLINT_MNCI_FILENAME);
8074
- if (!(0, import_node_fs31.existsSync)(rules)) return [];
8075
- const entryPoint = (0, import_node_path36.join)(workspaceRoot, ESLINT_USER_FILENAME);
8076
- const source = (0, import_node_fs31.existsSync)(entryPoint) ? (0, import_node_fs31.readFileSync)(entryPoint, "utf8") : "";
8433
+ const rules = (0, import_node_path38.join)(workspaceRoot, ESLINT_MNCI_FILENAME);
8434
+ if (!(0, import_node_fs32.existsSync)(rules)) return [];
8435
+ const entryPoint = (0, import_node_path38.join)(workspaceRoot, ESLINT_USER_FILENAME);
8436
+ const source = (0, import_node_fs32.existsSync)(entryPoint) ? (0, import_node_fs32.readFileSync)(entryPoint, "utf8") : "";
8077
8437
  return [
8078
8438
  {
8079
8439
  check: `${ESLINT_USER_FILENAME} imports the mnci rules`,
@@ -8086,7 +8446,7 @@ function checkEslintEntryPointReachesTheRules(workspaceRoot) {
8086
8446
  function declaredDevDependencies(workspaceRoot) {
8087
8447
  try {
8088
8448
  const manifest = readJson(
8089
- (0, import_node_path36.join)(workspaceRoot, "package.json")
8449
+ (0, import_node_path38.join)(workspaceRoot, "package.json")
8090
8450
  );
8091
8451
  return manifest.devDependencies ?? {};
8092
8452
  } catch {
@@ -8094,7 +8454,7 @@ function declaredDevDependencies(workspaceRoot) {
8094
8454
  }
8095
8455
  }
8096
8456
  function checkNoRetiredFormatter(workspaceRoot) {
8097
- const files = RETIRED_FORMATTER_FILES.filter((file) => fileExists((0, import_node_path36.join)(workspaceRoot, file)));
8457
+ const files = RETIRED_FORMATTER_FILES.filter((file) => fileExists((0, import_node_path38.join)(workspaceRoot, file)));
8098
8458
  const declared = ["prettier", "eslint-config-prettier", "oxlint", "oxfmt", "@mnci/oxlint-config"].filter((name) => declaredDevDependencies(workspaceRoot)[name] !== void 0);
8099
8459
  const stale = [...files, ...declared];
8100
8460
  if (stale.length === 0) {
@@ -8120,7 +8480,7 @@ function checkEslintPlugin(nxJson) {
8120
8480
  };
8121
8481
  }
8122
8482
  function checkResolvedEslint(workspaceRoot) {
8123
- const manifestPath = (0, import_node_path36.join)(workspaceRoot, "node_modules/eslint/package.json");
8483
+ const manifestPath = (0, import_node_path38.join)(workspaceRoot, "node_modules/eslint/package.json");
8124
8484
  if (!fileExists(manifestPath)) {
8125
8485
  return void 0;
8126
8486
  }
@@ -8138,11 +8498,11 @@ function checkResolvedEslint(workspaceRoot) {
8138
8498
  }
8139
8499
  }
8140
8500
  function checkNpmrc(workspaceRoot, registry, scope) {
8141
- const npmrcPath = (0, import_node_path36.join)(workspaceRoot, ".npmrc");
8501
+ const npmrcPath = (0, import_node_path38.join)(workspaceRoot, ".npmrc");
8142
8502
  if (!registry || !fileExists(npmrcPath)) {
8143
8503
  return void 0;
8144
8504
  }
8145
- const npmrc = (0, import_node_fs31.readFileSync)(npmrcPath, "utf8");
8505
+ const npmrc = (0, import_node_fs32.readFileSync)(npmrcPath, "utf8");
8146
8506
  if (registry.kind === "npm") {
8147
8507
  return {
8148
8508
  check: ".npmrc authenticates the public registry",
@@ -8159,11 +8519,11 @@ function checkNpmrc(workspaceRoot, registry, scope) {
8159
8519
  };
8160
8520
  }
8161
8521
  function checkNpmrcCredentialResolves(workspaceRoot, environment = process.env) {
8162
- const npmrcPath = (0, import_node_path36.join)(workspaceRoot, ".npmrc");
8522
+ const npmrcPath = (0, import_node_path38.join)(workspaceRoot, ".npmrc");
8163
8523
  if (!fileExists(npmrcPath)) {
8164
8524
  return void 0;
8165
8525
  }
8166
- const npmrc = (0, import_node_fs31.readFileSync)(npmrcPath, "utf8");
8526
+ const npmrc = (0, import_node_fs32.readFileSync)(npmrcPath, "utf8");
8167
8527
  const credentials = npmrc.split("\n").filter((line) => !line.trimStart().startsWith(";") && !line.trimStart().startsWith("#")).filter((line) => /(?:_authToken|_password|_auth)\s*=/u.test(line));
8168
8528
  const referenced = [
8169
8529
  ...new Set(
@@ -8187,13 +8547,13 @@ function checkNpmrcCredentialResolves(workspaceRoot, environment = process.env)
8187
8547
  };
8188
8548
  }
8189
8549
  function checkNpmAuthMatchesPipeline(workspaceRoot, registry) {
8190
- const npmrcPath = (0, import_node_path36.join)(workspaceRoot, ".npmrc");
8191
- const pipelinePath = (0, import_node_path36.join)(workspaceRoot, "azure-pipelines.yml");
8550
+ const npmrcPath = (0, import_node_path38.join)(workspaceRoot, ".npmrc");
8551
+ const pipelinePath = (0, import_node_path38.join)(workspaceRoot, "azure-pipelines.yml");
8192
8552
  if (registry?.kind !== "azure-artifacts" || !fileExists(npmrcPath) || !fileExists(pipelinePath)) {
8193
8553
  return void 0;
8194
8554
  }
8195
- const holdsCredentials = (0, import_node_fs31.readFileSync)(npmrcPath, "utf8").split("\n").filter((line) => !line.trimStart().startsWith(";") && !line.trimStart().startsWith("#")).some((line) => /(?:_authToken|_password|_auth)\s*=/u.test(line));
8196
- const runsTask = (0, import_node_fs31.readFileSync)(pipelinePath, "utf8").includes("npmAuthenticate@0");
8555
+ 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));
8556
+ const runsTask = (0, import_node_fs32.readFileSync)(pipelinePath, "utf8").includes("npmAuthenticate@0");
8197
8557
  if (!holdsCredentials && !runsTask) {
8198
8558
  return {
8199
8559
  check: "npm auth: .npmrc and azure-pipelines.yml agree",
@@ -8238,13 +8598,13 @@ function npmjsCredentialShape(credentials, environment) {
8238
8598
  }
8239
8599
  function checkVersionActions(workspaceRoot) {
8240
8600
  const candidates = [
8241
- ...(0, import_node_fs31.globSync)("packages/*/pubspec.yaml", { cwd: workspaceRoot }),
8242
- ...(0, import_node_fs31.globSync)("packages/*/*.csproj", { cwd: workspaceRoot }),
8243
- ...(0, import_node_fs31.globSync)("python-packages/*/pyproject.toml", { cwd: workspaceRoot })
8601
+ ...(0, import_node_fs32.globSync)("packages/*/pubspec.yaml", { cwd: workspaceRoot }),
8602
+ ...(0, import_node_fs32.globSync)("packages/*/*.csproj", { cwd: workspaceRoot }),
8603
+ ...(0, import_node_fs32.globSync)("python-packages/*/pyproject.toml", { cwd: workspaceRoot })
8244
8604
  ].map((manifest) => toPosix2(manifest));
8245
8605
  return candidates.flatMap((manifest) => {
8246
8606
  const projectRoot = manifest.slice(0, manifest.lastIndexOf("/"));
8247
- const projectJsonPath = (0, import_node_path36.join)(workspaceRoot, projectRoot, "project.json");
8607
+ const projectJsonPath = (0, import_node_path38.join)(workspaceRoot, projectRoot, "project.json");
8248
8608
  let hasOverride = false;
8249
8609
  try {
8250
8610
  const projectJson = readJson(projectJsonPath);
@@ -8273,7 +8633,7 @@ function declaredTargets(workspaceRoot, projectRoot) {
8273
8633
  ]) {
8274
8634
  try {
8275
8635
  const targets = extract(
8276
- readJson((0, import_node_path36.join)(workspaceRoot, projectRoot, file))
8636
+ readJson((0, import_node_path38.join)(workspaceRoot, projectRoot, file))
8277
8637
  );
8278
8638
  if (targets && typeof targets === "object") {
8279
8639
  sources.push({
@@ -8289,8 +8649,8 @@ function declaredTargets(workspaceRoot, projectRoot) {
8289
8649
  function checkTargetFilesExist(workspaceRoot) {
8290
8650
  const projectRoots = new Set(
8291
8651
  [
8292
- ...(0, import_node_fs31.globSync)("{apps,libs,packages}/*/project.json", { cwd: workspaceRoot }),
8293
- ...(0, import_node_fs31.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot })
8652
+ ...(0, import_node_fs32.globSync)("{apps,libs,packages}/*/project.json", { cwd: workspaceRoot }),
8653
+ ...(0, import_node_fs32.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot })
8294
8654
  ].map((manifest) => toPosix2(manifest).split("/").slice(0, 2).join("/"))
8295
8655
  );
8296
8656
  return [...projectRoots].flatMap(
@@ -8305,7 +8665,7 @@ function checkOneTargetsFiles(workspaceRoot, source, targetName, target) {
8305
8665
  return TARGET_FILE_OPTIONS.flatMap((option) => {
8306
8666
  const value = target?.options?.[option];
8307
8667
  const checkable = typeof value === "string" && !value.includes("{");
8308
- if (!checkable || fileExists((0, import_node_path36.join)(workspaceRoot, value))) {
8668
+ if (!checkable || fileExists((0, import_node_path38.join)(workspaceRoot, value))) {
8309
8669
  return [];
8310
8670
  }
8311
8671
  return [
@@ -8330,7 +8690,7 @@ ${result.stdout.trim()}` : ""}`,
8330
8690
  }
8331
8691
  function hasCCompiler() {
8332
8692
  const candidates = [process.env.CC?.split(" ", 1)[0], "gcc", "clang", "cc"];
8333
- return candidates.some((command) => command !== void 0 && command !== "" && (0, import_node_child_process.spawnSync)(command, ["--version"], { stdio: "ignore" }).status === 0);
8693
+ return candidates.some((command) => command !== void 0 && command !== "" && (0, import_node_child_process2.spawnSync)(command, ["--version"], { stdio: "ignore" }).status === 0);
8334
8694
  }
8335
8695
  function checkGoPluginRegistered(workspaceRoot) {
8336
8696
  if (!hasGoProject(workspaceRoot)) {
@@ -8344,15 +8704,15 @@ function checkGoPluginRegistered(workspaceRoot) {
8344
8704
  };
8345
8705
  }
8346
8706
  function checkGoWorkInSync(workspaceRoot) {
8347
- const goWorkPath = (0, import_node_path36.join)(workspaceRoot, "go.work");
8707
+ const goWorkPath = (0, import_node_path38.join)(workspaceRoot, "go.work");
8348
8708
  if (!fileExists(goWorkPath)) {
8349
8709
  return void 0;
8350
8710
  }
8351
- const contents = (0, import_node_fs31.existsSync)(goWorkPath) ? (0, import_node_fs31.readFileSync)(goWorkPath, "utf8") : "";
8711
+ const contents = (0, import_node_fs32.existsSync)(goWorkPath) ? (0, import_node_fs32.readFileSync)(goWorkPath, "utf8") : "";
8352
8712
  const blockEntries = Array.from(contents.matchAll(/(?:^|\n)\s*use\s+\(([^)]*)\)/g), (match) => match[1].split("\n")).flat();
8353
8713
  const singleEntries = Array.from(contents.matchAll(/(?:^|\n)\s*use\s+(\.\S+)/g), (match) => match[1]);
8354
8714
  const uses = [...blockEntries, ...singleEntries].map((entry) => entry.trim()).filter(Boolean);
8355
- const stale = uses.filter((entry) => !(0, import_node_fs31.existsSync)((0, import_node_path36.join)(workspaceRoot, entry, "go.mod")));
8715
+ const stale = uses.filter((entry) => !(0, import_node_fs32.existsSync)((0, import_node_path38.join)(workspaceRoot, entry, "go.mod")));
8356
8716
  return {
8357
8717
  check: "every go.work `use` entry points to a module that still exists",
8358
8718
  ok: stale.length === 0,
@@ -8368,9 +8728,9 @@ function checkNativeApps(workspaceRoot) {
8368
8728
  { file: ".github/workflows/ci.yml", marker: "native (${{ matrix.os }})" },
8369
8729
  { file: "azure-pipelines.yml", marker: "- job: native" }
8370
8730
  ];
8371
- const findings = pipelines.filter(({ file }) => fileExists((0, import_node_path36.join)(workspaceRoot, file))).map(({ file, marker }) => ({
8731
+ const findings = pipelines.filter(({ file }) => fileExists((0, import_node_path38.join)(workspaceRoot, file))).map(({ file, marker }) => ({
8372
8732
  check: `${file} builds the native apps on every OS`,
8373
- ok: (0, import_node_fs31.readFileSync)((0, import_node_path36.join)(workspaceRoot, file), "utf8").includes(marker),
8733
+ ok: (0, import_node_fs32.readFileSync)((0, import_node_path38.join)(workspaceRoot, file), "utf8").includes(marker),
8374
8734
  detail: `${file} has no native job: CI would verify a cgo app on the one agent that cannot build it`,
8375
8735
  remedy: "run `mnci upgrade`"
8376
8736
  }));
@@ -8383,7 +8743,7 @@ function checkNativeApps(workspaceRoot) {
8383
8743
  return findings;
8384
8744
  }
8385
8745
  function checkNoRootRuntimeDependencies(workspaceRoot) {
8386
- const manifestPath = (0, import_node_path36.join)(workspaceRoot, "package.json");
8746
+ const manifestPath = (0, import_node_path38.join)(workspaceRoot, "package.json");
8387
8747
  if (!fileExists(manifestPath)) {
8388
8748
  return void 0;
8389
8749
  }
@@ -8403,14 +8763,14 @@ function checkNoRootRuntimeDependencies(workspaceRoot) {
8403
8763
  };
8404
8764
  }
8405
8765
  function checkRollupSourceMaps(workspaceRoot) {
8406
- const configs = (0, import_node_fs31.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
8766
+ const configs = (0, import_node_fs32.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
8407
8767
  cwd: workspaceRoot
8408
8768
  }).map((config) => toPosix2(config));
8409
8769
  return configs.flatMap((relativePath) => {
8410
- const configPath = (0, import_node_path36.join)(workspaceRoot, relativePath);
8770
+ const configPath = (0, import_node_path38.join)(workspaceRoot, relativePath);
8411
8771
  let config;
8412
8772
  try {
8413
- config = (0, import_node_fs31.readFileSync)(configPath, "utf8");
8773
+ config = (0, import_node_fs32.readFileSync)(configPath, "utf8");
8414
8774
  } catch {
8415
8775
  return [];
8416
8776
  }
@@ -8425,11 +8785,11 @@ function checkRollupSourceMaps(workspaceRoot) {
8425
8785
  });
8426
8786
  }
8427
8787
  function checkDeclarationSpecifiers(workspaceRoot) {
8428
- const configs = (0, import_node_fs31.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
8788
+ const configs = (0, import_node_fs32.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
8429
8789
  cwd: workspaceRoot
8430
8790
  }).map((config) => toPosix2(config));
8431
8791
  return configs.flatMap((relativePath) => {
8432
- const configPath = (0, import_node_path36.join)(workspaceRoot, relativePath);
8792
+ const configPath = (0, import_node_path38.join)(workspaceRoot, relativePath);
8433
8793
  if (!fileExists(configPath)) {
8434
8794
  return [];
8435
8795
  }
@@ -8444,7 +8804,7 @@ function checkDeclarationSpecifiers(workspaceRoot) {
8444
8804
  // `plugins` array (what @nx/react:library writes). For any other shape it would no-op, and
8445
8805
  // naming a command that silently does nothing is the failure mode the source-map check
8446
8806
  // above goes out of its way to avoid, so the remedy depends on the shape.
8447
- remedy: canAddDeclarationSpecifierPlugin((0, import_node_fs31.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"
8807
+ 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"
8448
8808
  }
8449
8809
  ];
8450
8810
  }
@@ -8460,8 +8820,8 @@ function checkDeclarationSpecifiers(workspaceRoot) {
8460
8820
  }
8461
8821
  function checkPipelinePhases(workspaceRoot) {
8462
8822
  const files = ["azure-pipelines.yml", ".github/workflows/ci.yml"];
8463
- return files.filter((file) => fileExists((0, import_node_path36.join)(workspaceRoot, file))).flatMap((file) => {
8464
- const { verifyActive, switchedOff } = inspectPipeline((0, import_node_fs31.readFileSync)((0, import_node_path36.join)(workspaceRoot, file), "utf8"));
8823
+ return files.filter((file) => fileExists((0, import_node_path38.join)(workspaceRoot, file))).flatMap((file) => {
8824
+ const { verifyActive, switchedOff } = inspectPipeline((0, import_node_fs32.readFileSync)((0, import_node_path38.join)(workspaceRoot, file), "utf8"));
8465
8825
  const verify = {
8466
8826
  check: `${file} runs the enforced verify phase`,
8467
8827
  ok: verifyActive,
@@ -8502,7 +8862,7 @@ function checkDependencyRangesNotPreserved(nxJson) {
8502
8862
  };
8503
8863
  }
8504
8864
  function checkLockfileHasNoLocalRegistry(workspaceRoot) {
8505
- if (!fileExists((0, import_node_path36.join)(workspaceRoot, "package-lock.json"))) {
8865
+ if (!fileExists((0, import_node_path38.join)(workspaceRoot, "package-lock.json"))) {
8506
8866
  return void 0;
8507
8867
  }
8508
8868
  return {
@@ -8522,7 +8882,7 @@ function checkReleaseTagsResolve(workspaceRoot, nxJson) {
8522
8882
  };
8523
8883
  }
8524
8884
  function collectFindings(workspaceRoot) {
8525
- const nxJsonPath = (0, import_node_path36.join)(workspaceRoot, "nx.json");
8885
+ const nxJsonPath = (0, import_node_path38.join)(workspaceRoot, "nx.json");
8526
8886
  if (!fileExists(nxJsonPath)) {
8527
8887
  throw new Error(
8528
8888
  `No nx.json found in ${workspaceRoot} \u2014 run 'mnci doctor' from the workspace root.`
@@ -8587,8 +8947,8 @@ function runDoctor(workspaceRoot, options = {}) {
8587
8947
  }
8588
8948
 
8589
8949
  // src/repository-adoption/adopt-dependencies.use-case.ts
8590
- var import_node_fs32 = require("fs");
8591
- var import_node_path37 = require("path");
8950
+ var import_node_fs33 = require("fs");
8951
+ var import_node_path39 = require("path");
8592
8952
 
8593
8953
  // src/repository-adoption/clean-working-tree.validator.ts
8594
8954
  function requireCleanWorkingTree(repositoryRoot, capture = runCapture) {
@@ -8659,7 +9019,7 @@ var SOURCE_FILE = /\.(?:[cm]?[jt]s|[jt]sx)$/;
8659
9019
  var TEST_FILE = /\.(?:spec|test)\.[^.]+$|[\\/](?:__tests__|tests?|e2e)[\\/]/;
8660
9020
  var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", "out", ".git", ".nx", "tmp"]);
8661
9021
  function sourceFiles(directory, relative = "") {
8662
- const entries = (0, import_node_fs32.readdirSync)((0, import_node_path37.join)(directory, relative), { withFileTypes: true });
9022
+ const entries = (0, import_node_fs33.readdirSync)((0, import_node_path39.join)(directory, relative), { withFileTypes: true });
8663
9023
  const found = [];
8664
9024
  for (const entry of entries) {
8665
9025
  const path = relative === "" ? entry.name : `${relative}/${entry.name}`;
@@ -8675,7 +9035,7 @@ function sourceFiles(directory, relative = "") {
8675
9035
  }
8676
9036
  function wantedImportsOf(path, wanted) {
8677
9037
  try {
8678
- const imported = [...extractImportedPackages((0, import_node_fs32.readFileSync)(path, "utf8"))];
9038
+ const imported = [...extractImportedPackages((0, import_node_fs33.readFileSync)(path, "utf8"))];
8679
9039
  return imported.filter((name) => wanted.has(name));
8680
9040
  } catch {
8681
9041
  return [];
@@ -8686,7 +9046,7 @@ function importsOfProject(projectDirectory, wanted) {
8686
9046
  const tested = /* @__PURE__ */ new Set();
8687
9047
  const files = sourceFiles(projectDirectory);
8688
9048
  for (const file of files) {
8689
- const names = wantedImportsOf((0, import_node_path37.join)(projectDirectory, file), wanted);
9049
+ const names = wantedImportsOf((0, import_node_path39.join)(projectDirectory, file), wanted);
8690
9050
  const target = TEST_FILE.test(file) ? tested : shipped;
8691
9051
  for (const name of names) {
8692
9052
  target.add(name);
@@ -8698,7 +9058,7 @@ function importsOfProject(projectDirectory, wanted) {
8698
9058
  function findUsage(repositoryRoot, projectDirectories, wanted) {
8699
9059
  const usage = {};
8700
9060
  for (const dir of projectDirectories) {
8701
- const { shipped, testsOnly } = importsOfProject((0, import_node_path37.join)(repositoryRoot, dir), wanted);
9061
+ const { shipped, testsOnly } = importsOfProject((0, import_node_path39.join)(repositoryRoot, dir), wanted);
8702
9062
  for (const name of shipped) {
8703
9063
  usage[name] ??= { runtime: [], testsOnly: [] };
8704
9064
  usage[name].runtime.push(dir);
@@ -8713,7 +9073,7 @@ function findUsage(repositoryRoot, projectDirectories, wanted) {
8713
9073
  function adoptDependencies(repositoryRoot, dependencies = {}) {
8714
9074
  const run = dependencies.run ?? runShell;
8715
9075
  requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
8716
- const rootPath = (0, import_node_path37.join)(repositoryRoot, "package.json");
9076
+ const rootPath = (0, import_node_path39.join)(repositoryRoot, "package.json");
8717
9077
  if (!fileExists(rootPath)) {
8718
9078
  throw new Error("No package.json at the repository root.");
8719
9079
  }
@@ -8748,8 +9108,8 @@ function adoptDependencies(repositoryRoot, dependencies = {}) {
8748
9108
  }
8749
9109
 
8750
9110
  // src/repository-adoption/adopt-kinds.use-case.ts
8751
- var import_node_fs33 = require("fs");
8752
- var import_node_path38 = require("path");
9111
+ var import_node_fs34 = require("fs");
9112
+ var import_node_path40 = require("path");
8753
9113
 
8754
9114
  // src/repository-adoption/infer-project-kind.algorithm.ts
8755
9115
  var SERVER_FRAMEWORKS = ["express", "koa", "fastify", "@nestjs/core"];
@@ -8850,32 +9210,32 @@ function inferProjectKind(evidence) {
8850
9210
  // src/repository-adoption/adopt-kinds.use-case.ts
8851
9211
  var TYPE_TAG = "type:";
8852
9212
  function readEvidence(repositoryRoot, location) {
8853
- const directory = (0, import_node_path38.join)(repositoryRoot, location.dir);
8854
- const files = (0, import_node_fs33.readdirSync)(directory);
9213
+ const directory = (0, import_node_path40.join)(repositoryRoot, location.dir);
9214
+ const files = (0, import_node_fs34.readdirSync)(directory);
8855
9215
  const manifest = location.ecosystem === "npm" ? readJson(location.manifestPath) : {};
8856
- const text = location.ecosystem === "npm" ? "" : (0, import_node_fs33.readFileSync)(location.manifestPath, "utf8");
9216
+ const text = location.ecosystem === "npm" ? "" : (0, import_node_fs34.readFileSync)(location.manifestPath, "utf8");
8857
9217
  let hasProgram = false;
8858
9218
  if (location.ecosystem === "go") {
8859
- hasProgram = files.filter((file) => file.endsWith(".go")).some((file) => /^package main\b/m.test((0, import_node_fs33.readFileSync)((0, import_node_path38.join)(directory, file), "utf8")));
9219
+ hasProgram = files.filter((file) => file.endsWith(".go")).some((file) => /^package main\b/m.test((0, import_node_fs34.readFileSync)((0, import_node_path40.join)(directory, file), "utf8")));
8860
9220
  } else if (location.ecosystem === "pub") {
8861
- hasProgram = (0, import_node_fs33.existsSync)((0, import_node_path38.join)(directory, "lib", "main.dart"));
9221
+ hasProgram = (0, import_node_fs34.existsSync)((0, import_node_path40.join)(directory, "lib", "main.dart"));
8862
9222
  }
8863
9223
  return { ecosystem: location.ecosystem, manifest, text, files, hasProgram };
8864
9224
  }
8865
9225
  function existingKind(directory) {
8866
9226
  const sources = [];
8867
- if (fileExists((0, import_node_path38.join)(directory, "project.json"))) {
8868
- sources.push(readJson((0, import_node_path38.join)(directory, "project.json")).tags ?? []);
9227
+ if (fileExists((0, import_node_path40.join)(directory, "project.json"))) {
9228
+ sources.push(readJson((0, import_node_path40.join)(directory, "project.json")).tags ?? []);
8869
9229
  }
8870
- if (fileExists((0, import_node_path38.join)(directory, "package.json"))) {
8871
- sources.push(readJson((0, import_node_path38.join)(directory, "package.json")).nx?.tags ?? []);
9230
+ if (fileExists((0, import_node_path40.join)(directory, "package.json"))) {
9231
+ sources.push(readJson((0, import_node_path40.join)(directory, "package.json")).nx?.tags ?? []);
8872
9232
  }
8873
9233
  return sources.flat().find((tag) => tag.startsWith(TYPE_TAG))?.slice(TYPE_TAG.length);
8874
9234
  }
8875
9235
  function writeKindTag(directory, name, kind) {
8876
9236
  const tag = `${TYPE_TAG}${kind}`;
8877
- const projectJson = (0, import_node_path38.join)(directory, "project.json");
8878
- const packageJson = (0, import_node_path38.join)(directory, "package.json");
9237
+ const projectJson = (0, import_node_path40.join)(directory, "project.json");
9238
+ const packageJson = (0, import_node_path40.join)(directory, "package.json");
8879
9239
  if (fileExists(projectJson)) {
8880
9240
  const project = readJson(projectJson);
8881
9241
  writeFileEnsured(projectJson, toJson({ ...project, tags: [...project.tags ?? [], tag] }));
@@ -8899,7 +9259,7 @@ function adoptKinds(repositoryRoot, overrides = {}, dependencies = {}) {
8899
9259
  }
8900
9260
  }
8901
9261
  return locations.map((location) => {
8902
- const directory = (0, import_node_path38.join)(repositoryRoot, location.dir);
9262
+ const directory = (0, import_node_path40.join)(repositoryRoot, location.dir);
8903
9263
  const present = existingKind(directory);
8904
9264
  if (present !== void 0) {
8905
9265
  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" };
@@ -8915,12 +9275,12 @@ function adoptKinds(repositoryRoot, overrides = {}, dependencies = {}) {
8915
9275
  }
8916
9276
 
8917
9277
  // src/repository-adoption/adopt-overlay.use-case.ts
8918
- var import_node_fs35 = require("fs");
8919
- var import_node_path40 = require("path");
9278
+ var import_node_fs36 = require("fs");
9279
+ var import_node_path42 = require("path");
8920
9280
 
8921
9281
  // src/workspace-upgrade/upgrade-workspace.use-case.ts
8922
- var import_node_fs34 = require("fs");
8923
- var import_node_path39 = require("path");
9282
+ var import_node_fs35 = require("fs");
9283
+ var import_node_path41 = require("path");
8924
9284
  function resolveRegistry(options, persisted) {
8925
9285
  if (options.registry === "azure-artifacts" || options.organization && options.artifactsFeed) {
8926
9286
  const persistedAzure = persisted?.kind === "azure-artifacts" ? persisted : void 0;
@@ -8949,7 +9309,7 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
8949
9309
  return persisted.workspaceName;
8950
9310
  }
8951
9311
  try {
8952
- const existing = (0, import_node_fs34.readdirSync)(workspaceRoot).find(
9312
+ const existing = (0, import_node_fs35.readdirSync)(workspaceRoot).find(
8953
9313
  (file) => file.endsWith(".code-workspace") && file !== "undefined.code-workspace"
8954
9314
  );
8955
9315
  if (existing) {
@@ -8957,11 +9317,11 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
8957
9317
  }
8958
9318
  } catch {
8959
9319
  }
8960
- return (0, import_node_path39.basename)(workspaceRoot);
9320
+ return (0, import_node_path41.basename)(workspaceRoot);
8961
9321
  }
8962
9322
  function detectNpmAuth(workspaceRoot) {
8963
9323
  try {
8964
- return (0, import_node_fs34.readFileSync)((0, import_node_path39.join)(workspaceRoot, "azure-pipelines.yml"), "utf8").includes("npmAuthenticate@0") ? "build-identity" : void 0;
9324
+ return (0, import_node_fs35.readFileSync)((0, import_node_path41.join)(workspaceRoot, "azure-pipelines.yml"), "utf8").includes("npmAuthenticate@0") ? "build-identity" : void 0;
8965
9325
  } catch {
8966
9326
  return void 0;
8967
9327
  }
@@ -9008,7 +9368,7 @@ function resolveOverlayOptions(workspaceRoot, options, persisted) {
9008
9368
  };
9009
9369
  }
9010
9370
  function runUpgrade(workspaceRoot, options) {
9011
- if (!fileExists((0, import_node_path39.join)(workspaceRoot, "nx.json"))) {
9371
+ if (!fileExists((0, import_node_path41.join)(workspaceRoot, "nx.json"))) {
9012
9372
  throw new Error(
9013
9373
  `No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
9014
9374
  );
@@ -9056,6 +9416,10 @@ function runUpgrade(workspaceRoot, options) {
9056
9416
  logger.step("Updating the VS Code extension packaging script");
9057
9417
  logger.detail(`updated ${VSCODE_EXTENSION_SCRIPT_PATH}`);
9058
9418
  }
9419
+ if (refreshContainerScript(workspaceRoot)) {
9420
+ logger.step("Updating the container image script");
9421
+ logger.detail("updated tools/container-image.cjs");
9422
+ }
9059
9423
  if (refreshGoReleaseScript(workspaceRoot)) {
9060
9424
  logger.step("Updating the releasable Go app script");
9061
9425
  logger.detail(`updated ${GO_RELEASE_SCRIPT_PATH}`);
@@ -9082,7 +9446,7 @@ function runUpgrade(workspaceRoot, options) {
9082
9446
  "Formatting the workspace (eslint --fix) \u2014 the slowest step, minutes on a large workspace"
9083
9447
  );
9084
9448
  runFormatter(workspaceRoot);
9085
- const nxJson = readJson((0, import_node_path39.join)(workspaceRoot, "nx.json"));
9449
+ const nxJson = readJson((0, import_node_path41.join)(workspaceRoot, "nx.json"));
9086
9450
  const stranded = locateStrandedReleaseTags(workspaceRoot, nxJson.release?.releaseTag?.pattern);
9087
9451
  for (const entry of stranded) {
9088
9452
  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).`);
@@ -9093,9 +9457,9 @@ function runUpgrade(workspaceRoot, options) {
9093
9457
 
9094
9458
  // src/repository-adoption/adopt-overlay.use-case.ts
9095
9459
  function detectCiProvider(repositoryRoot) {
9096
- const azure = (0, import_node_fs35.existsSync)((0, import_node_path40.join)(repositoryRoot, "azure-pipelines.yml"));
9097
- const workflows = (0, import_node_path40.join)(repositoryRoot, ".github", "workflows");
9098
- const github = (0, import_node_fs35.existsSync)(workflows) && (0, import_node_fs35.readdirSync)(workflows).some((file) => /\.ya?ml$/.test(file));
9460
+ const azure = (0, import_node_fs36.existsSync)((0, import_node_path42.join)(repositoryRoot, "azure-pipelines.yml"));
9461
+ const workflows = (0, import_node_path42.join)(repositoryRoot, ".github", "workflows");
9462
+ const github = (0, import_node_fs36.existsSync)(workflows) && (0, import_node_fs36.readdirSync)(workflows).some((file) => /\.ya?ml$/.test(file));
9099
9463
  if (azure && github) {
9100
9464
  return "both";
9101
9465
  }
@@ -9105,7 +9469,7 @@ function detectCiProvider(repositoryRoot) {
9105
9469
  return github ? "github" : void 0;
9106
9470
  }
9107
9471
  function detectTestRunner(repositoryRoot) {
9108
- const manifestPath = (0, import_node_path40.join)(repositoryRoot, "package.json");
9472
+ const manifestPath = (0, import_node_path42.join)(repositoryRoot, "package.json");
9109
9473
  if (!fileExists(manifestPath)) {
9110
9474
  return void 0;
9111
9475
  }
@@ -9118,7 +9482,7 @@ function detectTestRunner(repositoryRoot) {
9118
9482
  }
9119
9483
  function adoptOverlay(repositoryRoot, options, dependencies = {}) {
9120
9484
  requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
9121
- if (!fileExists((0, import_node_path40.join)(repositoryRoot, "nx.json"))) {
9485
+ if (!fileExists((0, import_node_path42.join)(repositoryRoot, "nx.json"))) {
9122
9486
  throw new Error("There is no nx.json, so Nx is not set up here. Set it up first (`npx nx@latest init`); adopt does not install Nx yet.");
9123
9487
  }
9124
9488
  const persisted = readMnciConfig(repositoryRoot);
@@ -9131,7 +9495,7 @@ function adoptOverlay(repositoryRoot, options, dependencies = {}) {
9131
9495
  }
9132
9496
 
9133
9497
  // src/repository-adoption/adopt-toolchain.use-case.ts
9134
- var import_node_path42 = require("path");
9498
+ var import_node_path44 = require("path");
9135
9499
 
9136
9500
  // src/repository-adoption/align-nx-family.algorithm.ts
9137
9501
  var VERSION = /^[\^~]?(\d+)\.(\d+)\.(\d+)$/;
@@ -9169,24 +9533,24 @@ function alignNxFamily(dependencies, version) {
9169
9533
  }
9170
9534
 
9171
9535
  // src/repository-adoption/retire-tooling.use-case.ts
9172
- var import_node_fs36 = require("fs");
9173
- var import_node_path41 = require("path");
9536
+ var import_node_fs37 = require("fs");
9537
+ var import_node_path43 = require("path");
9174
9538
  var RETIRED_COMMAND = /\b(?:prettier|oxfmt|oxlint)\b/;
9175
9539
  function retireTooling(repositoryRoot) {
9176
9540
  const removed = [];
9177
9541
  for (const file of RETIRED_FORMATTER_FILES) {
9178
- if (!fileExists((0, import_node_path41.join)(repositoryRoot, file))) {
9542
+ if (!fileExists((0, import_node_path43.join)(repositoryRoot, file))) {
9179
9543
  continue;
9180
9544
  }
9181
- (0, import_node_fs36.rmSync)((0, import_node_path41.join)(repositoryRoot, file), { force: true });
9545
+ (0, import_node_fs37.rmSync)((0, import_node_path43.join)(repositoryRoot, file), { force: true });
9182
9546
  removed.push(file);
9183
9547
  }
9184
- const hadRegistry = fileExists((0, import_node_path41.join)(repositoryRoot, ".verdaccio"));
9548
+ const hadRegistry = fileExists((0, import_node_path43.join)(repositoryRoot, ".verdaccio"));
9185
9549
  removeLocalRegistryScaffolding(repositoryRoot);
9186
9550
  if (hadRegistry) {
9187
9551
  removed.push(".verdaccio, its dependency and the local-registry target");
9188
9552
  }
9189
- const manifestPath = (0, import_node_path41.join)(repositoryRoot, "package.json");
9553
+ const manifestPath = (0, import_node_path43.join)(repositoryRoot, "package.json");
9190
9554
  if (!fileExists(manifestPath)) {
9191
9555
  return removed;
9192
9556
  }
@@ -9233,7 +9597,7 @@ function adoptToolchain(repositoryRoot, options = {}, dependencies = {}) {
9233
9597
  console.log(message);
9234
9598
  });
9235
9599
  requireCleanWorkingTree(repositoryRoot, capture);
9236
- const manifestPath = (0, import_node_path42.join)(repositoryRoot, "package.json");
9600
+ const manifestPath = (0, import_node_path44.join)(repositoryRoot, "package.json");
9237
9601
  if (!fileExists(manifestPath)) {
9238
9602
  throw new Error("No package.json at the repository root.");
9239
9603
  }
@@ -9283,8 +9647,8 @@ function createBaselineTags(repositoryRoot, stranded) {
9283
9647
  }
9284
9648
 
9285
9649
  // src/repository-adoption/inspect-repository.use-case.ts
9286
- var import_node_fs37 = require("fs");
9287
- var import_node_path43 = require("path");
9650
+ var import_node_fs38 = require("fs");
9651
+ var import_node_path45 = require("path");
9288
9652
  var LOCKFILES = [
9289
9653
  ["package-lock.json", "npm"],
9290
9654
  ["pnpm-lock.yaml", "pnpm"],
@@ -9305,25 +9669,25 @@ function inspectRepository(repositoryRoot) {
9305
9669
  const isGitRepo = inside.status === 0 && inside.stdout.trim() === "true";
9306
9670
  const dirty = isGitRepo && runCapture("git", ["status", "--porcelain"], repositoryRoot).stdout.trim() !== "";
9307
9671
  const tags = isGitRepo ? runCapture("git", ["tag", "--list"], repositoryRoot).stdout.split(/\r?\n/).filter(Boolean) : [];
9308
- const manifestPath = (0, import_node_path43.join)(repositoryRoot, "package.json");
9672
+ const manifestPath = (0, import_node_path45.join)(repositoryRoot, "package.json");
9309
9673
  const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
9310
- const nxJsonPath = (0, import_node_path43.join)(repositoryRoot, "nx.json");
9674
+ const nxJsonPath = (0, import_node_path45.join)(repositoryRoot, "nx.json");
9311
9675
  const nxJson = fileExists(nxJsonPath) ? readJson(nxJsonPath) : {};
9312
9676
  const ci = [];
9313
9677
  const pipelines = [];
9314
- if ((0, import_node_fs37.existsSync)((0, import_node_path43.join)(repositoryRoot, "azure-pipelines.yml"))) {
9678
+ if ((0, import_node_fs38.existsSync)((0, import_node_path45.join)(repositoryRoot, "azure-pipelines.yml"))) {
9315
9679
  ci.push("azure");
9316
- pipelines.push((0, import_node_path43.join)(repositoryRoot, "azure-pipelines.yml"));
9680
+ pipelines.push((0, import_node_path45.join)(repositoryRoot, "azure-pipelines.yml"));
9317
9681
  }
9318
- const workflows = (0, import_node_path43.join)(repositoryRoot, ".github", "workflows");
9319
- if ((0, import_node_fs37.existsSync)(workflows)) {
9320
- const files = (0, import_node_fs37.readdirSync)(workflows).filter((file) => /\.ya?ml$/.test(file));
9682
+ const workflows = (0, import_node_path45.join)(repositoryRoot, ".github", "workflows");
9683
+ if ((0, import_node_fs38.existsSync)(workflows)) {
9684
+ const files = (0, import_node_fs38.readdirSync)(workflows).filter((file) => /\.ya?ml$/.test(file));
9321
9685
  if (files.length > 0) {
9322
9686
  ci.push("github");
9323
- pipelines.push(...files.map((file) => (0, import_node_path43.join)(workflows, file)));
9687
+ pipelines.push(...files.map((file) => (0, import_node_path45.join)(workflows, file)));
9324
9688
  }
9325
9689
  }
9326
- const rootFiles = (0, import_node_fs37.readdirSync)(repositoryRoot);
9690
+ const rootFiles = (0, import_node_fs38.readdirSync)(repositoryRoot);
9327
9691
  return {
9328
9692
  isGitRepo,
9329
9693
  dirty,
@@ -9334,7 +9698,7 @@ function inspectRepository(repositoryRoot) {
9334
9698
  tagCount: tags.length,
9335
9699
  strandedTags: isGitRepo ? locateStrandedReleaseTags(repositoryRoot, nxJson.release?.releaseTag?.pattern) : [],
9336
9700
  ci,
9337
- pipelineUsesMnci: pipelines.some((file) => /\bmnci ci\b/.test((0, import_node_fs37.readFileSync)(file, "utf8"))),
9701
+ pipelineUsesMnci: pipelines.some((file) => /\bmnci ci\b/.test((0, import_node_fs38.readFileSync)(file, "utf8"))),
9338
9702
  retiredTooling: [.../* @__PURE__ */ new Set([...rootFiles.filter((file) => RETIRED_FILE_PATTERN.test(file) || file === ".verdaccio"), ...retiredNamesIn(manifest)])],
9339
9703
  personalFiles: PERSONAL_FILES.filter((file) => rootFiles.includes(file))
9340
9704
  };
@@ -9567,6 +9931,7 @@ var COMMAND_GROUPS = {
9567
9931
  doctor: "workspace",
9568
9932
  adopt: "inspect",
9569
9933
  add: "projects",
9934
+ dev: "projects",
9570
9935
  install: "dependencies",
9571
9936
  sync: "dependencies",
9572
9937
  up: "dependencies",
@@ -9631,13 +9996,13 @@ function runCommands(program, options) {
9631
9996
  }
9632
9997
 
9633
9998
  // src/workspace-info/list-projects.use-case.ts
9634
- var import_node_path44 = require("path");
9999
+ var import_node_path46 = require("path");
9635
10000
  var TYPE_TAG_PREFIX = "type:";
9636
10001
  function listProjects(workspaceRoot) {
9637
10002
  return locateProjects(workspaceRoot).map((location) => {
9638
- const projectJsonPath = (0, import_node_path44.join)(workspaceRoot, location.dir, "project.json");
10003
+ const projectJsonPath = (0, import_node_path46.join)(workspaceRoot, location.dir, "project.json");
9639
10004
  const project = fileExists(projectJsonPath) ? readJson(projectJsonPath) : {};
9640
- const manifestPath = (0, import_node_path44.join)(workspaceRoot, location.dir, "package.json");
10005
+ const manifestPath = (0, import_node_path46.join)(workspaceRoot, location.dir, "package.json");
9641
10006
  const manifestTags = fileExists(manifestPath) ? readJson(manifestPath).nx?.tags ?? [] : [];
9642
10007
  const typeTag = [...project.tags ?? [], ...manifestTags].find((tag) => tag.startsWith(TYPE_TAG_PREFIX));
9643
10008
  return {
@@ -9650,7 +10015,7 @@ function listProjects(workspaceRoot) {
9650
10015
  }).toSorted((a, b) => a.dir.localeCompare(b.dir));
9651
10016
  }
9652
10017
  function runProjects(workspaceRoot, options) {
9653
- if (!fileExists((0, import_node_path44.join)(workspaceRoot, "nx.json"))) {
10018
+ if (!fileExists((0, import_node_path46.join)(workspaceRoot, "nx.json"))) {
9654
10019
  throw new Error("No nx.json found here. Run `projects` from the workspace root.");
9655
10020
  }
9656
10021
  const projects = listProjects(workspaceRoot);
@@ -9664,12 +10029,12 @@ function runProjects(workspaceRoot, options) {
9664
10029
  }
9665
10030
 
9666
10031
  // src/workspace-info/read-workspace-info.use-case.ts
9667
- var import_node_path46 = require("path");
10032
+ var import_node_path48 = require("path");
9668
10033
 
9669
10034
  // src/cli-version/check-for-update.use-case.ts
9670
- var import_node_fs38 = require("fs");
9671
- var import_node_path45 = require("path");
9672
- var import_cross_spawn3 = __toESM(require("cross-spawn"));
10035
+ var import_node_fs39 = require("fs");
10036
+ var import_node_path47 = require("path");
10037
+ var import_cross_spawn4 = __toESM(require("cross-spawn"));
9673
10038
  var PACKAGE_NAME2 = "@mnci/cli";
9674
10039
  var REGISTRY_TIMEOUT_MS = 2e3;
9675
10040
  function checkForUpdate(currentVersion2) {
@@ -9687,7 +10052,7 @@ function checkForUpdate(currentVersion2) {
9687
10052
  }
9688
10053
  }
9689
10054
  function fetchLatestVersion() {
9690
- const result = import_cross_spawn3.default.sync("npm", ["view", PACKAGE_NAME2, "version"], {
10055
+ const result = import_cross_spawn4.default.sync("npm", ["view", PACKAGE_NAME2, "version"], {
9691
10056
  encoding: "utf8",
9692
10057
  timeout: REGISTRY_TIMEOUT_MS,
9693
10058
  stdio: ["ignore", "pipe", "ignore"]
@@ -9727,8 +10092,8 @@ function releaseTriple(version) {
9727
10092
  }
9728
10093
  function readCliVersion(packageDirectory) {
9729
10094
  try {
9730
- const manifestPath = (0, import_node_path45.join)(packageDirectory, "..", "package.json");
9731
- const manifest = JSON.parse((0, import_node_fs38.readFileSync)(manifestPath, "utf8"));
10095
+ const manifestPath = (0, import_node_path47.join)(packageDirectory, "..", "package.json");
10096
+ const manifest = JSON.parse((0, import_node_fs39.readFileSync)(manifestPath, "utf8"));
9732
10097
  return manifest.version ?? "0.0.0";
9733
10098
  } catch {
9734
10099
  return "0.0.0";
@@ -9738,7 +10103,7 @@ function readCliVersion(packageDirectory) {
9738
10103
  // src/workspace-info/read-workspace-info.use-case.ts
9739
10104
  function readWorkspaceInfo(workspaceRoot, cliVersion) {
9740
10105
  const latest = fetchLatestVersion() ?? null;
9741
- const isWorkspace = fileExists((0, import_node_path46.join)(workspaceRoot, "nx.json"));
10106
+ const isWorkspace = fileExists((0, import_node_path48.join)(workspaceRoot, "nx.json"));
9742
10107
  return {
9743
10108
  cli: {
9744
10109
  version: cliVersion,
@@ -9759,7 +10124,7 @@ function runInfo(workspaceRoot, cliVersion, options) {
9759
10124
  }
9760
10125
 
9761
10126
  // src/interactive-wizard/interactive-wizard.use-case.ts
9762
- var import_node_path47 = require("path");
10127
+ var import_node_path49 = require("path");
9763
10128
 
9764
10129
  // src/interactive-wizard/adopt-steps.config.ts
9765
10130
  var ADOPT_GLOBAL_OPTIONS = ["json"];
@@ -9895,14 +10260,14 @@ function formatCommandLine(argv) {
9895
10260
  }
9896
10261
 
9897
10262
  // src/interactive-wizard/inquirer-prompter.client.ts
9898
- var import_prompts4 = require("@inquirer/prompts");
10263
+ var import_prompts5 = require("@inquirer/prompts");
9899
10264
  function toRows(choices) {
9900
10265
  const rows = [];
9901
10266
  let current;
9902
10267
  for (const choice of choices) {
9903
10268
  if (choice.group !== void 0 && choice.group !== current) {
9904
10269
  current = choice.group;
9905
- rows.push(new import_prompts4.Separator(`-- ${choice.group} --`));
10270
+ rows.push(new import_prompts5.Separator(`-- ${choice.group} --`));
9906
10271
  }
9907
10272
  rows.push({ name: choice.name, value: choice.value, description: choice.description });
9908
10273
  }
@@ -9910,10 +10275,10 @@ function toRows(choices) {
9910
10275
  }
9911
10276
  function createInquirerPrompter() {
9912
10277
  return {
9913
- select: async (message, choices) => await (0, import_prompts4.select)({ message, choices: toRows(choices), pageSize: 18 }),
9914
- checkbox: async (message, choices) => await (0, import_prompts4.checkbox)({ message, choices: toRows(choices), pageSize: 18 }),
9915
- input: async (message, required) => await (0, import_prompts4.input)({ message, validate: (value) => !required || value.trim().length > 0 || "A value is required" }),
9916
- confirm: async (message, fallback) => await (0, import_prompts4.confirm)({ message, default: fallback })
10278
+ select: async (message, choices) => await (0, import_prompts5.select)({ message, choices: toRows(choices), pageSize: 18 }),
10279
+ checkbox: async (message, choices) => await (0, import_prompts5.checkbox)({ message, choices: toRows(choices), pageSize: 18 }),
10280
+ input: async (message, required) => await (0, import_prompts5.input)({ message, validate: (value) => !required || value.trim().length > 0 || "A value is required" }),
10281
+ confirm: async (message, fallback) => await (0, import_prompts5.confirm)({ message, default: fallback })
9917
10282
  };
9918
10283
  }
9919
10284
 
@@ -9940,7 +10305,7 @@ function wizardMenu(commands, inWorkspace) {
9940
10305
  }
9941
10306
  async function runInteractive(program, prompter = createInquirerPrompter(), workingDirectory = process.cwd()) {
9942
10307
  const commands = describeCommands(program);
9943
- const inWorkspace = fileExists((0, import_node_path47.join)(workingDirectory, "nx.json"));
10308
+ const inWorkspace = fileExists((0, import_node_path49.join)(workingDirectory, "nx.json"));
9944
10309
  const name = await prompter.select("What would you like to do?", wizardMenu(commands, inWorkspace));
9945
10310
  const command = commands.find((candidate) => candidate.name === name);
9946
10311
  const argv = buildArgv(command, await askCommand(command, prompter));
@@ -9953,8 +10318,8 @@ async function runInteractive(program, prompter = createInquirerPrompter(), work
9953
10318
  }
9954
10319
 
9955
10320
  // src/workspace-creation/adopt-directory.use-case.ts
9956
- var import_node_fs39 = require("fs");
9957
- var import_node_path48 = require("path");
10321
+ var import_node_fs40 = require("fs");
10322
+ var import_node_path50 = require("path");
9958
10323
  var NEVER_COPIED = /* @__PURE__ */ new Set([".git", "node_modules"]);
9959
10324
  var AGENT_AND_CI_ENTRIES = [
9960
10325
  "AGENTS.md",
@@ -9985,13 +10350,13 @@ var PREFLIGHT_BLOCKERS = [
9985
10350
  "packages"
9986
10351
  ];
9987
10352
  function assertAdoptableDirectory(target) {
9988
- if (!(0, import_node_fs39.existsSync)(target)) {
10353
+ if (!(0, import_node_fs40.existsSync)(target)) {
9989
10354
  throw new Error(`--into '${target}' does not exist. Create or clone it first.`);
9990
10355
  }
9991
- if (!(0, import_node_fs39.statSync)(target).isDirectory()) {
10356
+ if (!(0, import_node_fs40.statSync)(target).isDirectory()) {
9992
10357
  throw new Error(`--into '${target}' is not a directory.`);
9993
10358
  }
9994
- const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs39.existsSync)((0, import_node_path48.join)(target, entry)));
10359
+ const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs40.existsSync)((0, import_node_path50.join)(target, entry)));
9995
10360
  if (blockers.length > 0) {
9996
10361
  throw new Error(
9997
10362
  `--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.`
@@ -9999,15 +10364,15 @@ function assertAdoptableDirectory(target) {
9999
10364
  }
10000
10365
  }
10001
10366
  function mergeGitignore(generatedFile, targetFile) {
10002
- const existing = (0, import_node_fs39.readFileSync)(targetFile, "utf8");
10367
+ const existing = (0, import_node_fs40.readFileSync)(targetFile, "utf8");
10003
10368
  const present = new Set(existing.split("\n").map((line) => line.trim()));
10004
- const additions = (0, import_node_fs39.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
10369
+ const additions = (0, import_node_fs40.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
10005
10370
  const trimmed = line.trim();
10006
10371
  return trimmed !== "" && !trimmed.startsWith("#") && !present.has(trimmed);
10007
10372
  });
10008
10373
  if (additions.length === 0) return false;
10009
10374
  const separator = existing.endsWith("\n") ? "" : "\n";
10010
- (0, import_node_fs39.writeFileSync)(
10375
+ (0, import_node_fs40.writeFileSync)(
10011
10376
  targetFile,
10012
10377
  `${existing}${separator}
10013
10378
  # Added by mnci (Nx workspace)
@@ -10017,9 +10382,9 @@ ${additions.join("\n")}
10017
10382
  return true;
10018
10383
  }
10019
10384
  function adoptGeneratedWorkspace(generatedRoot, target) {
10020
- const entries = (0, import_node_fs39.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
10385
+ const entries = (0, import_node_fs40.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
10021
10386
  const collisions = entries.filter(
10022
- (entry) => (0, import_node_fs39.existsSync)((0, import_node_path48.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
10387
+ (entry) => (0, import_node_fs40.existsSync)((0, import_node_path50.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
10023
10388
  );
10024
10389
  if (collisions.length > 0) {
10025
10390
  throw new Error(
@@ -10028,28 +10393,28 @@ function adoptGeneratedWorkspace(generatedRoot, target) {
10028
10393
  }
10029
10394
  const result = { copied: [], kept: [], mergedGitignore: false };
10030
10395
  for (const entry of entries) {
10031
- const from = (0, import_node_path48.join)(generatedRoot, entry);
10032
- const to = (0, import_node_path48.join)(target, entry);
10033
- if (entry === ".gitignore" && (0, import_node_fs39.existsSync)(to)) {
10396
+ const from = (0, import_node_path50.join)(generatedRoot, entry);
10397
+ const to = (0, import_node_path50.join)(target, entry);
10398
+ if (entry === ".gitignore" && (0, import_node_fs40.existsSync)(to)) {
10034
10399
  result.mergedGitignore = mergeGitignore(from, to);
10035
10400
  result.kept.push(entry);
10036
10401
  continue;
10037
10402
  }
10038
- if ((0, import_node_fs39.existsSync)(to)) {
10403
+ if ((0, import_node_fs40.existsSync)(to)) {
10039
10404
  result.kept.push(entry);
10040
10405
  continue;
10041
10406
  }
10042
- (0, import_node_fs39.cpSync)(from, to, { recursive: true });
10407
+ (0, import_node_fs40.cpSync)(from, to, { recursive: true });
10043
10408
  result.copied.push(entry);
10044
10409
  }
10045
- (0, import_node_fs39.rmSync)(generatedRoot, { recursive: true, force: true });
10410
+ (0, import_node_fs40.rmSync)(generatedRoot, { recursive: true, force: true });
10046
10411
  return result;
10047
10412
  }
10048
10413
 
10049
10414
  // src/workspace-creation/create-workspace.use-case.ts
10050
- var import_node_fs40 = require("fs");
10415
+ var import_node_fs41 = require("fs");
10051
10416
  var import_node_os2 = require("os");
10052
- var import_node_path49 = require("path");
10417
+ var import_node_path51 = require("path");
10053
10418
  function nxCloudProviderValue(ci) {
10054
10419
  return ci === "azure" ? "azure" : "github";
10055
10420
  }
@@ -10092,9 +10457,9 @@ async function resolveRegistry2(options) {
10092
10457
  return await promptRegistry();
10093
10458
  }
10094
10459
  async function runNew(name, options) {
10095
- const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path49.resolve)(process.cwd(), options.into);
10460
+ const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path51.resolve)(process.cwd(), options.into);
10096
10461
  if (adoptTarget !== void 0) assertAdoptableDirectory(adoptTarget);
10097
- const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path49.basename)(adoptTarget));
10462
+ const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path51.basename)(adoptTarget));
10098
10463
  assertValidProjectName(workspaceName, "Workspace name");
10099
10464
  const scope = options.scope ?? (options.yes ? `@${workspaceName}` : await promptText("npm scope for publishable packages", `@${workspaceName}`));
10100
10465
  const registry = await resolveRegistry2(options);
@@ -10107,8 +10472,8 @@ async function runNew(name, options) {
10107
10472
  const npmAuth = resolveNpmAuth(options.npmAuth, registry, ci);
10108
10473
  const stack = await resolveStack(options);
10109
10474
  const nxCloud = options.nxCloud ?? (options.yes ? false : await promptNxCloud());
10110
- const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs40.mkdtempSync)((0, import_node_path49.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
10111
- let workspaceRoot = (0, import_node_path49.join)(stagingParent, workspaceName);
10475
+ const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs41.mkdtempSync)((0, import_node_path51.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
10476
+ let workspaceRoot = (0, import_node_path51.join)(stagingParent, workspaceName);
10112
10477
  try {
10113
10478
  logger.step(`Creating Nx workspace '${workspaceName}' (preset: ts)`);
10114
10479
  runNpx(
@@ -10131,7 +10496,7 @@ async function runNew(name, options) {
10131
10496
  workspaceRoot = adoptTarget;
10132
10497
  }
10133
10498
  } finally {
10134
- if (adoptTarget !== void 0) (0, import_node_fs40.rmSync)(stagingParent, { recursive: true, force: true });
10499
+ if (adoptTarget !== void 0) (0, import_node_fs41.rmSync)(stagingParent, { recursive: true, force: true });
10135
10500
  }
10136
10501
  logger.step(
10137
10502
  "Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
@@ -10141,8 +10506,8 @@ async function runNew(name, options) {
10141
10506
  { workspaceName, scope, registry, agent, variableGroup, ci, stack, ...npmAuth && { npmAuth } },
10142
10507
  logger.detail
10143
10508
  );
10144
- (0, import_node_fs40.rmSync)((0, import_node_path49.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
10145
- (0, import_node_fs40.rmSync)((0, import_node_path49.join)(workspaceRoot, "package-lock.json"), { force: true });
10509
+ (0, import_node_fs41.rmSync)((0, import_node_path51.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
10510
+ (0, import_node_fs41.rmSync)((0, import_node_path51.join)(workspaceRoot, "package-lock.json"), { force: true });
10146
10511
  logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
10147
10512
  const installStatus = runShell(
10148
10513
  "npm",
@@ -10206,6 +10571,9 @@ function buildProgram(cliVersion) {
10206
10571
  ).action((options) => {
10207
10572
  runUpgrade(process.cwd(), options);
10208
10573
  });
10574
+ program.command("dev").argument("[projects...]", "the projects to start (those with a start command)").description("Start several projects together, such as a frontend and the API it calls: runs their start targets as one Nx command, so Ctrl+C stops them all. Name the projects, pass --all, or pick from a list").option("--all", "start every project that has a start command").option("--dry-run", "print the Nx command and start nothing").action(async (projects, options) => {
10575
+ await runDev(process.cwd(), projects, options);
10576
+ });
10209
10577
  program.command("doctor").description(
10210
10578
  "Check this workspace against the invariants mnci maintains (one ESLint config, no stray .prettierrc, the eslint plugin registered, the resolved eslint major, .npmrc vs the recorded registry, versionActions overrides, nx sync) \u2014 read-only; exits non-zero if anything failed"
10211
10579
  ).option("--json", "print the findings as one JSON document, each with its remedy").action((options) => {
@@ -10244,7 +10612,7 @@ function buildProgram(cliVersion) {
10244
10612
  ).option(
10245
10613
  "--empty",
10246
10614
  "npm-lib, internal-lib, react-lib, react-internal-lib, node-function-app: scaffold the slice skeleton only, with no sample code or spec"
10247
- ).option(
10615
+ ).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(
10248
10616
  "--e2e",
10249
10617
  "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"
10250
10618
  ).option(