@mnci/cli 4.49.0 → 4.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -3187,7 +3187,7 @@ function runPhase(phase, workspaceRoot, dependencies) {
3187
3187
  }
3188
3188
 
3189
3189
  // src/project-scaffolding/add-project.use-case.ts
3190
- var import_node_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: [] },
@@ -7997,12 +8241,12 @@ function runKinds(options) {
7997
8241
 
7998
8242
  // src/workspace-diagnostics/check-invariants.use-case.ts
7999
8243
  var import_node_child_process = require("child_process");
8000
- var import_node_fs31 = require("fs");
8001
- var import_node_path36 = require("path");
8244
+ var import_node_fs32 = require("fs");
8245
+ var import_node_path37 = require("path");
8002
8246
 
8003
8247
  // src/workspace-diagnostics/locate-stranded-release-tags.use-case.ts
8004
- var import_node_fs30 = require("fs");
8005
- var import_node_path35 = require("path");
8248
+ var import_node_fs31 = require("fs");
8249
+ var import_node_path36 = require("path");
8006
8250
 
8007
8251
  // src/workspace-diagnostics/stranded-release-tags.algorithm.ts
8008
8252
  function compareVersions2(left, right) {
@@ -8039,7 +8283,7 @@ function locateStrandedReleaseTags(workspaceRoot, tagPattern) {
8039
8283
  if (listed.status !== 0) {
8040
8284
  return [];
8041
8285
  }
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);
8286
+ const projects = (0, import_node_fs31.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot }).map((path) => readJson((0, import_node_path36.join)(workspaceRoot, path))).filter((manifest) => manifest.private !== true && manifest.name !== void 0).map((manifest) => manifest.name);
8043
8287
  return findStrandedReleaseTags(projects, listed.stdout.split(/\r?\n/).filter(Boolean));
8044
8288
  }
8045
8289
 
@@ -8049,8 +8293,8 @@ function toPosix2(path) {
8049
8293
  return path.replaceAll("\\", "/");
8050
8294
  }
8051
8295
  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}", {
8296
+ const rootConfigs = (0, import_node_fs32.globSync)("eslint.config.{js,mjs,cjs,ts,mts,cts}", { cwd: workspaceRoot });
8297
+ const projectConfigs = (0, import_node_fs32.globSync)("{apps,libs,packages}/*/eslint.config.{js,mjs,cjs,ts,mts,cts}", {
8054
8298
  cwd: workspaceRoot
8055
8299
  }).map((config) => toPosix2(config));
8056
8300
  return [
@@ -8070,10 +8314,10 @@ function checkEslintConfigs(workspaceRoot) {
8070
8314
  ];
8071
8315
  }
8072
8316
  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") : "";
8317
+ const rules = (0, import_node_path37.join)(workspaceRoot, ESLINT_MNCI_FILENAME);
8318
+ if (!(0, import_node_fs32.existsSync)(rules)) return [];
8319
+ const entryPoint = (0, import_node_path37.join)(workspaceRoot, ESLINT_USER_FILENAME);
8320
+ const source = (0, import_node_fs32.existsSync)(entryPoint) ? (0, import_node_fs32.readFileSync)(entryPoint, "utf8") : "";
8077
8321
  return [
8078
8322
  {
8079
8323
  check: `${ESLINT_USER_FILENAME} imports the mnci rules`,
@@ -8086,7 +8330,7 @@ function checkEslintEntryPointReachesTheRules(workspaceRoot) {
8086
8330
  function declaredDevDependencies(workspaceRoot) {
8087
8331
  try {
8088
8332
  const manifest = readJson(
8089
- (0, import_node_path36.join)(workspaceRoot, "package.json")
8333
+ (0, import_node_path37.join)(workspaceRoot, "package.json")
8090
8334
  );
8091
8335
  return manifest.devDependencies ?? {};
8092
8336
  } catch {
@@ -8094,7 +8338,7 @@ function declaredDevDependencies(workspaceRoot) {
8094
8338
  }
8095
8339
  }
8096
8340
  function checkNoRetiredFormatter(workspaceRoot) {
8097
- const files = RETIRED_FORMATTER_FILES.filter((file) => fileExists((0, import_node_path36.join)(workspaceRoot, file)));
8341
+ const files = RETIRED_FORMATTER_FILES.filter((file) => fileExists((0, import_node_path37.join)(workspaceRoot, file)));
8098
8342
  const declared = ["prettier", "eslint-config-prettier", "oxlint", "oxfmt", "@mnci/oxlint-config"].filter((name) => declaredDevDependencies(workspaceRoot)[name] !== void 0);
8099
8343
  const stale = [...files, ...declared];
8100
8344
  if (stale.length === 0) {
@@ -8120,7 +8364,7 @@ function checkEslintPlugin(nxJson) {
8120
8364
  };
8121
8365
  }
8122
8366
  function checkResolvedEslint(workspaceRoot) {
8123
- const manifestPath = (0, import_node_path36.join)(workspaceRoot, "node_modules/eslint/package.json");
8367
+ const manifestPath = (0, import_node_path37.join)(workspaceRoot, "node_modules/eslint/package.json");
8124
8368
  if (!fileExists(manifestPath)) {
8125
8369
  return void 0;
8126
8370
  }
@@ -8138,11 +8382,11 @@ function checkResolvedEslint(workspaceRoot) {
8138
8382
  }
8139
8383
  }
8140
8384
  function checkNpmrc(workspaceRoot, registry, scope) {
8141
- const npmrcPath = (0, import_node_path36.join)(workspaceRoot, ".npmrc");
8385
+ const npmrcPath = (0, import_node_path37.join)(workspaceRoot, ".npmrc");
8142
8386
  if (!registry || !fileExists(npmrcPath)) {
8143
8387
  return void 0;
8144
8388
  }
8145
- const npmrc = (0, import_node_fs31.readFileSync)(npmrcPath, "utf8");
8389
+ const npmrc = (0, import_node_fs32.readFileSync)(npmrcPath, "utf8");
8146
8390
  if (registry.kind === "npm") {
8147
8391
  return {
8148
8392
  check: ".npmrc authenticates the public registry",
@@ -8159,11 +8403,11 @@ function checkNpmrc(workspaceRoot, registry, scope) {
8159
8403
  };
8160
8404
  }
8161
8405
  function checkNpmrcCredentialResolves(workspaceRoot, environment = process.env) {
8162
- const npmrcPath = (0, import_node_path36.join)(workspaceRoot, ".npmrc");
8406
+ const npmrcPath = (0, import_node_path37.join)(workspaceRoot, ".npmrc");
8163
8407
  if (!fileExists(npmrcPath)) {
8164
8408
  return void 0;
8165
8409
  }
8166
- const npmrc = (0, import_node_fs31.readFileSync)(npmrcPath, "utf8");
8410
+ const npmrc = (0, import_node_fs32.readFileSync)(npmrcPath, "utf8");
8167
8411
  const credentials = npmrc.split("\n").filter((line) => !line.trimStart().startsWith(";") && !line.trimStart().startsWith("#")).filter((line) => /(?:_authToken|_password|_auth)\s*=/u.test(line));
8168
8412
  const referenced = [
8169
8413
  ...new Set(
@@ -8187,13 +8431,13 @@ function checkNpmrcCredentialResolves(workspaceRoot, environment = process.env)
8187
8431
  };
8188
8432
  }
8189
8433
  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");
8434
+ const npmrcPath = (0, import_node_path37.join)(workspaceRoot, ".npmrc");
8435
+ const pipelinePath = (0, import_node_path37.join)(workspaceRoot, "azure-pipelines.yml");
8192
8436
  if (registry?.kind !== "azure-artifacts" || !fileExists(npmrcPath) || !fileExists(pipelinePath)) {
8193
8437
  return void 0;
8194
8438
  }
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");
8439
+ const holdsCredentials = (0, import_node_fs32.readFileSync)(npmrcPath, "utf8").split("\n").filter((line) => !line.trimStart().startsWith(";") && !line.trimStart().startsWith("#")).some((line) => /(?:_authToken|_password|_auth)\s*=/u.test(line));
8440
+ const runsTask = (0, import_node_fs32.readFileSync)(pipelinePath, "utf8").includes("npmAuthenticate@0");
8197
8441
  if (!holdsCredentials && !runsTask) {
8198
8442
  return {
8199
8443
  check: "npm auth: .npmrc and azure-pipelines.yml agree",
@@ -8238,13 +8482,13 @@ function npmjsCredentialShape(credentials, environment) {
8238
8482
  }
8239
8483
  function checkVersionActions(workspaceRoot) {
8240
8484
  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 })
8485
+ ...(0, import_node_fs32.globSync)("packages/*/pubspec.yaml", { cwd: workspaceRoot }),
8486
+ ...(0, import_node_fs32.globSync)("packages/*/*.csproj", { cwd: workspaceRoot }),
8487
+ ...(0, import_node_fs32.globSync)("python-packages/*/pyproject.toml", { cwd: workspaceRoot })
8244
8488
  ].map((manifest) => toPosix2(manifest));
8245
8489
  return candidates.flatMap((manifest) => {
8246
8490
  const projectRoot = manifest.slice(0, manifest.lastIndexOf("/"));
8247
- const projectJsonPath = (0, import_node_path36.join)(workspaceRoot, projectRoot, "project.json");
8491
+ const projectJsonPath = (0, import_node_path37.join)(workspaceRoot, projectRoot, "project.json");
8248
8492
  let hasOverride = false;
8249
8493
  try {
8250
8494
  const projectJson = readJson(projectJsonPath);
@@ -8273,7 +8517,7 @@ function declaredTargets(workspaceRoot, projectRoot) {
8273
8517
  ]) {
8274
8518
  try {
8275
8519
  const targets = extract(
8276
- readJson((0, import_node_path36.join)(workspaceRoot, projectRoot, file))
8520
+ readJson((0, import_node_path37.join)(workspaceRoot, projectRoot, file))
8277
8521
  );
8278
8522
  if (targets && typeof targets === "object") {
8279
8523
  sources.push({
@@ -8289,8 +8533,8 @@ function declaredTargets(workspaceRoot, projectRoot) {
8289
8533
  function checkTargetFilesExist(workspaceRoot) {
8290
8534
  const projectRoots = new Set(
8291
8535
  [
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 })
8536
+ ...(0, import_node_fs32.globSync)("{apps,libs,packages}/*/project.json", { cwd: workspaceRoot }),
8537
+ ...(0, import_node_fs32.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot })
8294
8538
  ].map((manifest) => toPosix2(manifest).split("/").slice(0, 2).join("/"))
8295
8539
  );
8296
8540
  return [...projectRoots].flatMap(
@@ -8305,7 +8549,7 @@ function checkOneTargetsFiles(workspaceRoot, source, targetName, target) {
8305
8549
  return TARGET_FILE_OPTIONS.flatMap((option) => {
8306
8550
  const value = target?.options?.[option];
8307
8551
  const checkable = typeof value === "string" && !value.includes("{");
8308
- if (!checkable || fileExists((0, import_node_path36.join)(workspaceRoot, value))) {
8552
+ if (!checkable || fileExists((0, import_node_path37.join)(workspaceRoot, value))) {
8309
8553
  return [];
8310
8554
  }
8311
8555
  return [
@@ -8344,15 +8588,15 @@ function checkGoPluginRegistered(workspaceRoot) {
8344
8588
  };
8345
8589
  }
8346
8590
  function checkGoWorkInSync(workspaceRoot) {
8347
- const goWorkPath = (0, import_node_path36.join)(workspaceRoot, "go.work");
8591
+ const goWorkPath = (0, import_node_path37.join)(workspaceRoot, "go.work");
8348
8592
  if (!fileExists(goWorkPath)) {
8349
8593
  return void 0;
8350
8594
  }
8351
- const contents = (0, import_node_fs31.existsSync)(goWorkPath) ? (0, import_node_fs31.readFileSync)(goWorkPath, "utf8") : "";
8595
+ const contents = (0, import_node_fs32.existsSync)(goWorkPath) ? (0, import_node_fs32.readFileSync)(goWorkPath, "utf8") : "";
8352
8596
  const blockEntries = Array.from(contents.matchAll(/(?:^|\n)\s*use\s+\(([^)]*)\)/g), (match) => match[1].split("\n")).flat();
8353
8597
  const singleEntries = Array.from(contents.matchAll(/(?:^|\n)\s*use\s+(\.\S+)/g), (match) => match[1]);
8354
8598
  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")));
8599
+ const stale = uses.filter((entry) => !(0, import_node_fs32.existsSync)((0, import_node_path37.join)(workspaceRoot, entry, "go.mod")));
8356
8600
  return {
8357
8601
  check: "every go.work `use` entry points to a module that still exists",
8358
8602
  ok: stale.length === 0,
@@ -8368,9 +8612,9 @@ function checkNativeApps(workspaceRoot) {
8368
8612
  { file: ".github/workflows/ci.yml", marker: "native (${{ matrix.os }})" },
8369
8613
  { file: "azure-pipelines.yml", marker: "- job: native" }
8370
8614
  ];
8371
- const findings = pipelines.filter(({ file }) => fileExists((0, import_node_path36.join)(workspaceRoot, file))).map(({ file, marker }) => ({
8615
+ const findings = pipelines.filter(({ file }) => fileExists((0, import_node_path37.join)(workspaceRoot, file))).map(({ file, marker }) => ({
8372
8616
  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),
8617
+ ok: (0, import_node_fs32.readFileSync)((0, import_node_path37.join)(workspaceRoot, file), "utf8").includes(marker),
8374
8618
  detail: `${file} has no native job: CI would verify a cgo app on the one agent that cannot build it`,
8375
8619
  remedy: "run `mnci upgrade`"
8376
8620
  }));
@@ -8383,7 +8627,7 @@ function checkNativeApps(workspaceRoot) {
8383
8627
  return findings;
8384
8628
  }
8385
8629
  function checkNoRootRuntimeDependencies(workspaceRoot) {
8386
- const manifestPath = (0, import_node_path36.join)(workspaceRoot, "package.json");
8630
+ const manifestPath = (0, import_node_path37.join)(workspaceRoot, "package.json");
8387
8631
  if (!fileExists(manifestPath)) {
8388
8632
  return void 0;
8389
8633
  }
@@ -8403,14 +8647,14 @@ function checkNoRootRuntimeDependencies(workspaceRoot) {
8403
8647
  };
8404
8648
  }
8405
8649
  function checkRollupSourceMaps(workspaceRoot) {
8406
- const configs = (0, import_node_fs31.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
8650
+ const configs = (0, import_node_fs32.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
8407
8651
  cwd: workspaceRoot
8408
8652
  }).map((config) => toPosix2(config));
8409
8653
  return configs.flatMap((relativePath) => {
8410
- const configPath = (0, import_node_path36.join)(workspaceRoot, relativePath);
8654
+ const configPath = (0, import_node_path37.join)(workspaceRoot, relativePath);
8411
8655
  let config;
8412
8656
  try {
8413
- config = (0, import_node_fs31.readFileSync)(configPath, "utf8");
8657
+ config = (0, import_node_fs32.readFileSync)(configPath, "utf8");
8414
8658
  } catch {
8415
8659
  return [];
8416
8660
  }
@@ -8425,11 +8669,11 @@ function checkRollupSourceMaps(workspaceRoot) {
8425
8669
  });
8426
8670
  }
8427
8671
  function checkDeclarationSpecifiers(workspaceRoot) {
8428
- const configs = (0, import_node_fs31.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
8672
+ const configs = (0, import_node_fs32.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
8429
8673
  cwd: workspaceRoot
8430
8674
  }).map((config) => toPosix2(config));
8431
8675
  return configs.flatMap((relativePath) => {
8432
- const configPath = (0, import_node_path36.join)(workspaceRoot, relativePath);
8676
+ const configPath = (0, import_node_path37.join)(workspaceRoot, relativePath);
8433
8677
  if (!fileExists(configPath)) {
8434
8678
  return [];
8435
8679
  }
@@ -8444,7 +8688,7 @@ function checkDeclarationSpecifiers(workspaceRoot) {
8444
8688
  // `plugins` array (what @nx/react:library writes). For any other shape it would no-op, and
8445
8689
  // naming a command that silently does nothing is the failure mode the source-map check
8446
8690
  // 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"
8691
+ remedy: canAddDeclarationSpecifierPlugin((0, import_node_fs32.readFileSync)(configPath, "utf8")) ? "run `mnci upgrade`, which adds the plugin to the plugins array of withNx's second argument" : "add the plugin to withNx's second argument by hand (copy it from a freshly `mnci add npm-lib`'d project) \u2014 mnci does not recognise the shape of this config, so `mnci upgrade` cannot add it"
8448
8692
  }
8449
8693
  ];
8450
8694
  }
@@ -8460,8 +8704,8 @@ function checkDeclarationSpecifiers(workspaceRoot) {
8460
8704
  }
8461
8705
  function checkPipelinePhases(workspaceRoot) {
8462
8706
  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"));
8707
+ return files.filter((file) => fileExists((0, import_node_path37.join)(workspaceRoot, file))).flatMap((file) => {
8708
+ const { verifyActive, switchedOff } = inspectPipeline((0, import_node_fs32.readFileSync)((0, import_node_path37.join)(workspaceRoot, file), "utf8"));
8465
8709
  const verify = {
8466
8710
  check: `${file} runs the enforced verify phase`,
8467
8711
  ok: verifyActive,
@@ -8502,7 +8746,7 @@ function checkDependencyRangesNotPreserved(nxJson) {
8502
8746
  };
8503
8747
  }
8504
8748
  function checkLockfileHasNoLocalRegistry(workspaceRoot) {
8505
- if (!fileExists((0, import_node_path36.join)(workspaceRoot, "package-lock.json"))) {
8749
+ if (!fileExists((0, import_node_path37.join)(workspaceRoot, "package-lock.json"))) {
8506
8750
  return void 0;
8507
8751
  }
8508
8752
  return {
@@ -8522,7 +8766,7 @@ function checkReleaseTagsResolve(workspaceRoot, nxJson) {
8522
8766
  };
8523
8767
  }
8524
8768
  function collectFindings(workspaceRoot) {
8525
- const nxJsonPath = (0, import_node_path36.join)(workspaceRoot, "nx.json");
8769
+ const nxJsonPath = (0, import_node_path37.join)(workspaceRoot, "nx.json");
8526
8770
  if (!fileExists(nxJsonPath)) {
8527
8771
  throw new Error(
8528
8772
  `No nx.json found in ${workspaceRoot} \u2014 run 'mnci doctor' from the workspace root.`
@@ -8587,8 +8831,8 @@ function runDoctor(workspaceRoot, options = {}) {
8587
8831
  }
8588
8832
 
8589
8833
  // src/repository-adoption/adopt-dependencies.use-case.ts
8590
- var import_node_fs32 = require("fs");
8591
- var import_node_path37 = require("path");
8834
+ var import_node_fs33 = require("fs");
8835
+ var import_node_path38 = require("path");
8592
8836
 
8593
8837
  // src/repository-adoption/clean-working-tree.validator.ts
8594
8838
  function requireCleanWorkingTree(repositoryRoot, capture = runCapture) {
@@ -8659,7 +8903,7 @@ var SOURCE_FILE = /\.(?:[cm]?[jt]s|[jt]sx)$/;
8659
8903
  var TEST_FILE = /\.(?:spec|test)\.[^.]+$|[\\/](?:__tests__|tests?|e2e)[\\/]/;
8660
8904
  var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", "out", ".git", ".nx", "tmp"]);
8661
8905
  function sourceFiles(directory, relative = "") {
8662
- const entries = (0, import_node_fs32.readdirSync)((0, import_node_path37.join)(directory, relative), { withFileTypes: true });
8906
+ const entries = (0, import_node_fs33.readdirSync)((0, import_node_path38.join)(directory, relative), { withFileTypes: true });
8663
8907
  const found = [];
8664
8908
  for (const entry of entries) {
8665
8909
  const path = relative === "" ? entry.name : `${relative}/${entry.name}`;
@@ -8675,7 +8919,7 @@ function sourceFiles(directory, relative = "") {
8675
8919
  }
8676
8920
  function wantedImportsOf(path, wanted) {
8677
8921
  try {
8678
- const imported = [...extractImportedPackages((0, import_node_fs32.readFileSync)(path, "utf8"))];
8922
+ const imported = [...extractImportedPackages((0, import_node_fs33.readFileSync)(path, "utf8"))];
8679
8923
  return imported.filter((name) => wanted.has(name));
8680
8924
  } catch {
8681
8925
  return [];
@@ -8686,7 +8930,7 @@ function importsOfProject(projectDirectory, wanted) {
8686
8930
  const tested = /* @__PURE__ */ new Set();
8687
8931
  const files = sourceFiles(projectDirectory);
8688
8932
  for (const file of files) {
8689
- const names = wantedImportsOf((0, import_node_path37.join)(projectDirectory, file), wanted);
8933
+ const names = wantedImportsOf((0, import_node_path38.join)(projectDirectory, file), wanted);
8690
8934
  const target = TEST_FILE.test(file) ? tested : shipped;
8691
8935
  for (const name of names) {
8692
8936
  target.add(name);
@@ -8698,7 +8942,7 @@ function importsOfProject(projectDirectory, wanted) {
8698
8942
  function findUsage(repositoryRoot, projectDirectories, wanted) {
8699
8943
  const usage = {};
8700
8944
  for (const dir of projectDirectories) {
8701
- const { shipped, testsOnly } = importsOfProject((0, import_node_path37.join)(repositoryRoot, dir), wanted);
8945
+ const { shipped, testsOnly } = importsOfProject((0, import_node_path38.join)(repositoryRoot, dir), wanted);
8702
8946
  for (const name of shipped) {
8703
8947
  usage[name] ??= { runtime: [], testsOnly: [] };
8704
8948
  usage[name].runtime.push(dir);
@@ -8713,7 +8957,7 @@ function findUsage(repositoryRoot, projectDirectories, wanted) {
8713
8957
  function adoptDependencies(repositoryRoot, dependencies = {}) {
8714
8958
  const run = dependencies.run ?? runShell;
8715
8959
  requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
8716
- const rootPath = (0, import_node_path37.join)(repositoryRoot, "package.json");
8960
+ const rootPath = (0, import_node_path38.join)(repositoryRoot, "package.json");
8717
8961
  if (!fileExists(rootPath)) {
8718
8962
  throw new Error("No package.json at the repository root.");
8719
8963
  }
@@ -8748,8 +8992,8 @@ function adoptDependencies(repositoryRoot, dependencies = {}) {
8748
8992
  }
8749
8993
 
8750
8994
  // src/repository-adoption/adopt-kinds.use-case.ts
8751
- var import_node_fs33 = require("fs");
8752
- var import_node_path38 = require("path");
8995
+ var import_node_fs34 = require("fs");
8996
+ var import_node_path39 = require("path");
8753
8997
 
8754
8998
  // src/repository-adoption/infer-project-kind.algorithm.ts
8755
8999
  var SERVER_FRAMEWORKS = ["express", "koa", "fastify", "@nestjs/core"];
@@ -8850,32 +9094,32 @@ function inferProjectKind(evidence) {
8850
9094
  // src/repository-adoption/adopt-kinds.use-case.ts
8851
9095
  var TYPE_TAG = "type:";
8852
9096
  function readEvidence(repositoryRoot, location) {
8853
- const directory = (0, import_node_path38.join)(repositoryRoot, location.dir);
8854
- const files = (0, import_node_fs33.readdirSync)(directory);
9097
+ const directory = (0, import_node_path39.join)(repositoryRoot, location.dir);
9098
+ const files = (0, import_node_fs34.readdirSync)(directory);
8855
9099
  const manifest = location.ecosystem === "npm" ? readJson(location.manifestPath) : {};
8856
- const text = location.ecosystem === "npm" ? "" : (0, import_node_fs33.readFileSync)(location.manifestPath, "utf8");
9100
+ const text = location.ecosystem === "npm" ? "" : (0, import_node_fs34.readFileSync)(location.manifestPath, "utf8");
8857
9101
  let hasProgram = false;
8858
9102
  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")));
9103
+ hasProgram = files.filter((file) => file.endsWith(".go")).some((file) => /^package main\b/m.test((0, import_node_fs34.readFileSync)((0, import_node_path39.join)(directory, file), "utf8")));
8860
9104
  } else if (location.ecosystem === "pub") {
8861
- hasProgram = (0, import_node_fs33.existsSync)((0, import_node_path38.join)(directory, "lib", "main.dart"));
9105
+ hasProgram = (0, import_node_fs34.existsSync)((0, import_node_path39.join)(directory, "lib", "main.dart"));
8862
9106
  }
8863
9107
  return { ecosystem: location.ecosystem, manifest, text, files, hasProgram };
8864
9108
  }
8865
9109
  function existingKind(directory) {
8866
9110
  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 ?? []);
9111
+ if (fileExists((0, import_node_path39.join)(directory, "project.json"))) {
9112
+ sources.push(readJson((0, import_node_path39.join)(directory, "project.json")).tags ?? []);
8869
9113
  }
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 ?? []);
9114
+ if (fileExists((0, import_node_path39.join)(directory, "package.json"))) {
9115
+ sources.push(readJson((0, import_node_path39.join)(directory, "package.json")).nx?.tags ?? []);
8872
9116
  }
8873
9117
  return sources.flat().find((tag) => tag.startsWith(TYPE_TAG))?.slice(TYPE_TAG.length);
8874
9118
  }
8875
9119
  function writeKindTag(directory, name, kind) {
8876
9120
  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");
9121
+ const projectJson = (0, import_node_path39.join)(directory, "project.json");
9122
+ const packageJson = (0, import_node_path39.join)(directory, "package.json");
8879
9123
  if (fileExists(projectJson)) {
8880
9124
  const project = readJson(projectJson);
8881
9125
  writeFileEnsured(projectJson, toJson({ ...project, tags: [...project.tags ?? [], tag] }));
@@ -8899,7 +9143,7 @@ function adoptKinds(repositoryRoot, overrides = {}, dependencies = {}) {
8899
9143
  }
8900
9144
  }
8901
9145
  return locations.map((location) => {
8902
- const directory = (0, import_node_path38.join)(repositoryRoot, location.dir);
9146
+ const directory = (0, import_node_path39.join)(repositoryRoot, location.dir);
8903
9147
  const present = existingKind(directory);
8904
9148
  if (present !== void 0) {
8905
9149
  return { dir: location.dir, outcome: "already", kind: present, reason: PROJECT_KINDS.includes(present) ? "it already has a type tag" : "it already has a type tag of its own, which is not an mnci kind name; left as it is" };
@@ -8915,12 +9159,12 @@ function adoptKinds(repositoryRoot, overrides = {}, dependencies = {}) {
8915
9159
  }
8916
9160
 
8917
9161
  // src/repository-adoption/adopt-overlay.use-case.ts
8918
- var import_node_fs35 = require("fs");
8919
- var import_node_path40 = require("path");
9162
+ var import_node_fs36 = require("fs");
9163
+ var import_node_path41 = require("path");
8920
9164
 
8921
9165
  // src/workspace-upgrade/upgrade-workspace.use-case.ts
8922
- var import_node_fs34 = require("fs");
8923
- var import_node_path39 = require("path");
9166
+ var import_node_fs35 = require("fs");
9167
+ var import_node_path40 = require("path");
8924
9168
  function resolveRegistry(options, persisted) {
8925
9169
  if (options.registry === "azure-artifacts" || options.organization && options.artifactsFeed) {
8926
9170
  const persistedAzure = persisted?.kind === "azure-artifacts" ? persisted : void 0;
@@ -8949,7 +9193,7 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
8949
9193
  return persisted.workspaceName;
8950
9194
  }
8951
9195
  try {
8952
- const existing = (0, import_node_fs34.readdirSync)(workspaceRoot).find(
9196
+ const existing = (0, import_node_fs35.readdirSync)(workspaceRoot).find(
8953
9197
  (file) => file.endsWith(".code-workspace") && file !== "undefined.code-workspace"
8954
9198
  );
8955
9199
  if (existing) {
@@ -8957,11 +9201,11 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
8957
9201
  }
8958
9202
  } catch {
8959
9203
  }
8960
- return (0, import_node_path39.basename)(workspaceRoot);
9204
+ return (0, import_node_path40.basename)(workspaceRoot);
8961
9205
  }
8962
9206
  function detectNpmAuth(workspaceRoot) {
8963
9207
  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;
9208
+ return (0, import_node_fs35.readFileSync)((0, import_node_path40.join)(workspaceRoot, "azure-pipelines.yml"), "utf8").includes("npmAuthenticate@0") ? "build-identity" : void 0;
8965
9209
  } catch {
8966
9210
  return void 0;
8967
9211
  }
@@ -9008,7 +9252,7 @@ function resolveOverlayOptions(workspaceRoot, options, persisted) {
9008
9252
  };
9009
9253
  }
9010
9254
  function runUpgrade(workspaceRoot, options) {
9011
- if (!fileExists((0, import_node_path39.join)(workspaceRoot, "nx.json"))) {
9255
+ if (!fileExists((0, import_node_path40.join)(workspaceRoot, "nx.json"))) {
9012
9256
  throw new Error(
9013
9257
  `No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
9014
9258
  );
@@ -9056,6 +9300,10 @@ function runUpgrade(workspaceRoot, options) {
9056
9300
  logger.step("Updating the VS Code extension packaging script");
9057
9301
  logger.detail(`updated ${VSCODE_EXTENSION_SCRIPT_PATH}`);
9058
9302
  }
9303
+ if (refreshContainerScript(workspaceRoot)) {
9304
+ logger.step("Updating the container image script");
9305
+ logger.detail("updated tools/container-image.cjs");
9306
+ }
9059
9307
  if (refreshGoReleaseScript(workspaceRoot)) {
9060
9308
  logger.step("Updating the releasable Go app script");
9061
9309
  logger.detail(`updated ${GO_RELEASE_SCRIPT_PATH}`);
@@ -9082,7 +9330,7 @@ function runUpgrade(workspaceRoot, options) {
9082
9330
  "Formatting the workspace (eslint --fix) \u2014 the slowest step, minutes on a large workspace"
9083
9331
  );
9084
9332
  runFormatter(workspaceRoot);
9085
- const nxJson = readJson((0, import_node_path39.join)(workspaceRoot, "nx.json"));
9333
+ const nxJson = readJson((0, import_node_path40.join)(workspaceRoot, "nx.json"));
9086
9334
  const stranded = locateStrandedReleaseTags(workspaceRoot, nxJson.release?.releaseTag?.pattern);
9087
9335
  for (const entry of stranded) {
9088
9336
  logger.warn(`${entry.project} has release tags only under ${entry.oldTag}: nx release would restart it from its disk version. Run \`mnci adopt --tags\` before the next release to create ${entry.newTag} locally (\`mnci doctor\` lists them all).`);
@@ -9093,9 +9341,9 @@ function runUpgrade(workspaceRoot, options) {
9093
9341
 
9094
9342
  // src/repository-adoption/adopt-overlay.use-case.ts
9095
9343
  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));
9344
+ const azure = (0, import_node_fs36.existsSync)((0, import_node_path41.join)(repositoryRoot, "azure-pipelines.yml"));
9345
+ const workflows = (0, import_node_path41.join)(repositoryRoot, ".github", "workflows");
9346
+ const github = (0, import_node_fs36.existsSync)(workflows) && (0, import_node_fs36.readdirSync)(workflows).some((file) => /\.ya?ml$/.test(file));
9099
9347
  if (azure && github) {
9100
9348
  return "both";
9101
9349
  }
@@ -9105,7 +9353,7 @@ function detectCiProvider(repositoryRoot) {
9105
9353
  return github ? "github" : void 0;
9106
9354
  }
9107
9355
  function detectTestRunner(repositoryRoot) {
9108
- const manifestPath = (0, import_node_path40.join)(repositoryRoot, "package.json");
9356
+ const manifestPath = (0, import_node_path41.join)(repositoryRoot, "package.json");
9109
9357
  if (!fileExists(manifestPath)) {
9110
9358
  return void 0;
9111
9359
  }
@@ -9118,7 +9366,7 @@ function detectTestRunner(repositoryRoot) {
9118
9366
  }
9119
9367
  function adoptOverlay(repositoryRoot, options, dependencies = {}) {
9120
9368
  requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
9121
- if (!fileExists((0, import_node_path40.join)(repositoryRoot, "nx.json"))) {
9369
+ if (!fileExists((0, import_node_path41.join)(repositoryRoot, "nx.json"))) {
9122
9370
  throw new Error("There is no nx.json, so Nx is not set up here. Set it up first (`npx nx@latest init`); adopt does not install Nx yet.");
9123
9371
  }
9124
9372
  const persisted = readMnciConfig(repositoryRoot);
@@ -9131,7 +9379,7 @@ function adoptOverlay(repositoryRoot, options, dependencies = {}) {
9131
9379
  }
9132
9380
 
9133
9381
  // src/repository-adoption/adopt-toolchain.use-case.ts
9134
- var import_node_path42 = require("path");
9382
+ var import_node_path43 = require("path");
9135
9383
 
9136
9384
  // src/repository-adoption/align-nx-family.algorithm.ts
9137
9385
  var VERSION = /^[\^~]?(\d+)\.(\d+)\.(\d+)$/;
@@ -9169,24 +9417,24 @@ function alignNxFamily(dependencies, version) {
9169
9417
  }
9170
9418
 
9171
9419
  // src/repository-adoption/retire-tooling.use-case.ts
9172
- var import_node_fs36 = require("fs");
9173
- var import_node_path41 = require("path");
9420
+ var import_node_fs37 = require("fs");
9421
+ var import_node_path42 = require("path");
9174
9422
  var RETIRED_COMMAND = /\b(?:prettier|oxfmt|oxlint)\b/;
9175
9423
  function retireTooling(repositoryRoot) {
9176
9424
  const removed = [];
9177
9425
  for (const file of RETIRED_FORMATTER_FILES) {
9178
- if (!fileExists((0, import_node_path41.join)(repositoryRoot, file))) {
9426
+ if (!fileExists((0, import_node_path42.join)(repositoryRoot, file))) {
9179
9427
  continue;
9180
9428
  }
9181
- (0, import_node_fs36.rmSync)((0, import_node_path41.join)(repositoryRoot, file), { force: true });
9429
+ (0, import_node_fs37.rmSync)((0, import_node_path42.join)(repositoryRoot, file), { force: true });
9182
9430
  removed.push(file);
9183
9431
  }
9184
- const hadRegistry = fileExists((0, import_node_path41.join)(repositoryRoot, ".verdaccio"));
9432
+ const hadRegistry = fileExists((0, import_node_path42.join)(repositoryRoot, ".verdaccio"));
9185
9433
  removeLocalRegistryScaffolding(repositoryRoot);
9186
9434
  if (hadRegistry) {
9187
9435
  removed.push(".verdaccio, its dependency and the local-registry target");
9188
9436
  }
9189
- const manifestPath = (0, import_node_path41.join)(repositoryRoot, "package.json");
9437
+ const manifestPath = (0, import_node_path42.join)(repositoryRoot, "package.json");
9190
9438
  if (!fileExists(manifestPath)) {
9191
9439
  return removed;
9192
9440
  }
@@ -9233,7 +9481,7 @@ function adoptToolchain(repositoryRoot, options = {}, dependencies = {}) {
9233
9481
  console.log(message);
9234
9482
  });
9235
9483
  requireCleanWorkingTree(repositoryRoot, capture);
9236
- const manifestPath = (0, import_node_path42.join)(repositoryRoot, "package.json");
9484
+ const manifestPath = (0, import_node_path43.join)(repositoryRoot, "package.json");
9237
9485
  if (!fileExists(manifestPath)) {
9238
9486
  throw new Error("No package.json at the repository root.");
9239
9487
  }
@@ -9283,8 +9531,8 @@ function createBaselineTags(repositoryRoot, stranded) {
9283
9531
  }
9284
9532
 
9285
9533
  // src/repository-adoption/inspect-repository.use-case.ts
9286
- var import_node_fs37 = require("fs");
9287
- var import_node_path43 = require("path");
9534
+ var import_node_fs38 = require("fs");
9535
+ var import_node_path44 = require("path");
9288
9536
  var LOCKFILES = [
9289
9537
  ["package-lock.json", "npm"],
9290
9538
  ["pnpm-lock.yaml", "pnpm"],
@@ -9305,25 +9553,25 @@ function inspectRepository(repositoryRoot) {
9305
9553
  const isGitRepo = inside.status === 0 && inside.stdout.trim() === "true";
9306
9554
  const dirty = isGitRepo && runCapture("git", ["status", "--porcelain"], repositoryRoot).stdout.trim() !== "";
9307
9555
  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");
9556
+ const manifestPath = (0, import_node_path44.join)(repositoryRoot, "package.json");
9309
9557
  const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
9310
- const nxJsonPath = (0, import_node_path43.join)(repositoryRoot, "nx.json");
9558
+ const nxJsonPath = (0, import_node_path44.join)(repositoryRoot, "nx.json");
9311
9559
  const nxJson = fileExists(nxJsonPath) ? readJson(nxJsonPath) : {};
9312
9560
  const ci = [];
9313
9561
  const pipelines = [];
9314
- if ((0, import_node_fs37.existsSync)((0, import_node_path43.join)(repositoryRoot, "azure-pipelines.yml"))) {
9562
+ if ((0, import_node_fs38.existsSync)((0, import_node_path44.join)(repositoryRoot, "azure-pipelines.yml"))) {
9315
9563
  ci.push("azure");
9316
- pipelines.push((0, import_node_path43.join)(repositoryRoot, "azure-pipelines.yml"));
9564
+ pipelines.push((0, import_node_path44.join)(repositoryRoot, "azure-pipelines.yml"));
9317
9565
  }
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));
9566
+ const workflows = (0, import_node_path44.join)(repositoryRoot, ".github", "workflows");
9567
+ if ((0, import_node_fs38.existsSync)(workflows)) {
9568
+ const files = (0, import_node_fs38.readdirSync)(workflows).filter((file) => /\.ya?ml$/.test(file));
9321
9569
  if (files.length > 0) {
9322
9570
  ci.push("github");
9323
- pipelines.push(...files.map((file) => (0, import_node_path43.join)(workflows, file)));
9571
+ pipelines.push(...files.map((file) => (0, import_node_path44.join)(workflows, file)));
9324
9572
  }
9325
9573
  }
9326
- const rootFiles = (0, import_node_fs37.readdirSync)(repositoryRoot);
9574
+ const rootFiles = (0, import_node_fs38.readdirSync)(repositoryRoot);
9327
9575
  return {
9328
9576
  isGitRepo,
9329
9577
  dirty,
@@ -9334,7 +9582,7 @@ function inspectRepository(repositoryRoot) {
9334
9582
  tagCount: tags.length,
9335
9583
  strandedTags: isGitRepo ? locateStrandedReleaseTags(repositoryRoot, nxJson.release?.releaseTag?.pattern) : [],
9336
9584
  ci,
9337
- pipelineUsesMnci: pipelines.some((file) => /\bmnci ci\b/.test((0, import_node_fs37.readFileSync)(file, "utf8"))),
9585
+ pipelineUsesMnci: pipelines.some((file) => /\bmnci ci\b/.test((0, import_node_fs38.readFileSync)(file, "utf8"))),
9338
9586
  retiredTooling: [.../* @__PURE__ */ new Set([...rootFiles.filter((file) => RETIRED_FILE_PATTERN.test(file) || file === ".verdaccio"), ...retiredNamesIn(manifest)])],
9339
9587
  personalFiles: PERSONAL_FILES.filter((file) => rootFiles.includes(file))
9340
9588
  };
@@ -9631,13 +9879,13 @@ function runCommands(program, options) {
9631
9879
  }
9632
9880
 
9633
9881
  // src/workspace-info/list-projects.use-case.ts
9634
- var import_node_path44 = require("path");
9882
+ var import_node_path45 = require("path");
9635
9883
  var TYPE_TAG_PREFIX = "type:";
9636
9884
  function listProjects(workspaceRoot) {
9637
9885
  return locateProjects(workspaceRoot).map((location) => {
9638
- const projectJsonPath = (0, import_node_path44.join)(workspaceRoot, location.dir, "project.json");
9886
+ const projectJsonPath = (0, import_node_path45.join)(workspaceRoot, location.dir, "project.json");
9639
9887
  const project = fileExists(projectJsonPath) ? readJson(projectJsonPath) : {};
9640
- const manifestPath = (0, import_node_path44.join)(workspaceRoot, location.dir, "package.json");
9888
+ const manifestPath = (0, import_node_path45.join)(workspaceRoot, location.dir, "package.json");
9641
9889
  const manifestTags = fileExists(manifestPath) ? readJson(manifestPath).nx?.tags ?? [] : [];
9642
9890
  const typeTag = [...project.tags ?? [], ...manifestTags].find((tag) => tag.startsWith(TYPE_TAG_PREFIX));
9643
9891
  return {
@@ -9650,7 +9898,7 @@ function listProjects(workspaceRoot) {
9650
9898
  }).toSorted((a, b) => a.dir.localeCompare(b.dir));
9651
9899
  }
9652
9900
  function runProjects(workspaceRoot, options) {
9653
- if (!fileExists((0, import_node_path44.join)(workspaceRoot, "nx.json"))) {
9901
+ if (!fileExists((0, import_node_path45.join)(workspaceRoot, "nx.json"))) {
9654
9902
  throw new Error("No nx.json found here. Run `projects` from the workspace root.");
9655
9903
  }
9656
9904
  const projects = listProjects(workspaceRoot);
@@ -9664,11 +9912,11 @@ function runProjects(workspaceRoot, options) {
9664
9912
  }
9665
9913
 
9666
9914
  // src/workspace-info/read-workspace-info.use-case.ts
9667
- var import_node_path46 = require("path");
9915
+ var import_node_path47 = require("path");
9668
9916
 
9669
9917
  // src/cli-version/check-for-update.use-case.ts
9670
- var import_node_fs38 = require("fs");
9671
- var import_node_path45 = require("path");
9918
+ var import_node_fs39 = require("fs");
9919
+ var import_node_path46 = require("path");
9672
9920
  var import_cross_spawn3 = __toESM(require("cross-spawn"));
9673
9921
  var PACKAGE_NAME2 = "@mnci/cli";
9674
9922
  var REGISTRY_TIMEOUT_MS = 2e3;
@@ -9727,8 +9975,8 @@ function releaseTriple(version) {
9727
9975
  }
9728
9976
  function readCliVersion(packageDirectory) {
9729
9977
  try {
9730
- const manifestPath = (0, import_node_path45.join)(packageDirectory, "..", "package.json");
9731
- const manifest = JSON.parse((0, import_node_fs38.readFileSync)(manifestPath, "utf8"));
9978
+ const manifestPath = (0, import_node_path46.join)(packageDirectory, "..", "package.json");
9979
+ const manifest = JSON.parse((0, import_node_fs39.readFileSync)(manifestPath, "utf8"));
9732
9980
  return manifest.version ?? "0.0.0";
9733
9981
  } catch {
9734
9982
  return "0.0.0";
@@ -9738,7 +9986,7 @@ function readCliVersion(packageDirectory) {
9738
9986
  // src/workspace-info/read-workspace-info.use-case.ts
9739
9987
  function readWorkspaceInfo(workspaceRoot, cliVersion) {
9740
9988
  const latest = fetchLatestVersion() ?? null;
9741
- const isWorkspace = fileExists((0, import_node_path46.join)(workspaceRoot, "nx.json"));
9989
+ const isWorkspace = fileExists((0, import_node_path47.join)(workspaceRoot, "nx.json"));
9742
9990
  return {
9743
9991
  cli: {
9744
9992
  version: cliVersion,
@@ -9759,7 +10007,7 @@ function runInfo(workspaceRoot, cliVersion, options) {
9759
10007
  }
9760
10008
 
9761
10009
  // src/interactive-wizard/interactive-wizard.use-case.ts
9762
- var import_node_path47 = require("path");
10010
+ var import_node_path48 = require("path");
9763
10011
 
9764
10012
  // src/interactive-wizard/adopt-steps.config.ts
9765
10013
  var ADOPT_GLOBAL_OPTIONS = ["json"];
@@ -9940,7 +10188,7 @@ function wizardMenu(commands, inWorkspace) {
9940
10188
  }
9941
10189
  async function runInteractive(program, prompter = createInquirerPrompter(), workingDirectory = process.cwd()) {
9942
10190
  const commands = describeCommands(program);
9943
- const inWorkspace = fileExists((0, import_node_path47.join)(workingDirectory, "nx.json"));
10191
+ const inWorkspace = fileExists((0, import_node_path48.join)(workingDirectory, "nx.json"));
9944
10192
  const name = await prompter.select("What would you like to do?", wizardMenu(commands, inWorkspace));
9945
10193
  const command = commands.find((candidate) => candidate.name === name);
9946
10194
  const argv = buildArgv(command, await askCommand(command, prompter));
@@ -9953,8 +10201,8 @@ async function runInteractive(program, prompter = createInquirerPrompter(), work
9953
10201
  }
9954
10202
 
9955
10203
  // src/workspace-creation/adopt-directory.use-case.ts
9956
- var import_node_fs39 = require("fs");
9957
- var import_node_path48 = require("path");
10204
+ var import_node_fs40 = require("fs");
10205
+ var import_node_path49 = require("path");
9958
10206
  var NEVER_COPIED = /* @__PURE__ */ new Set([".git", "node_modules"]);
9959
10207
  var AGENT_AND_CI_ENTRIES = [
9960
10208
  "AGENTS.md",
@@ -9985,13 +10233,13 @@ var PREFLIGHT_BLOCKERS = [
9985
10233
  "packages"
9986
10234
  ];
9987
10235
  function assertAdoptableDirectory(target) {
9988
- if (!(0, import_node_fs39.existsSync)(target)) {
10236
+ if (!(0, import_node_fs40.existsSync)(target)) {
9989
10237
  throw new Error(`--into '${target}' does not exist. Create or clone it first.`);
9990
10238
  }
9991
- if (!(0, import_node_fs39.statSync)(target).isDirectory()) {
10239
+ if (!(0, import_node_fs40.statSync)(target).isDirectory()) {
9992
10240
  throw new Error(`--into '${target}' is not a directory.`);
9993
10241
  }
9994
- const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs39.existsSync)((0, import_node_path48.join)(target, entry)));
10242
+ const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs40.existsSync)((0, import_node_path49.join)(target, entry)));
9995
10243
  if (blockers.length > 0) {
9996
10244
  throw new Error(
9997
10245
  `--into '${target}' already holds ${blockers.join(", ")}, so it is a project already. Use \`mnci upgrade\` to re-apply the overlay to an existing workspace, or point --into at an empty checkout.`
@@ -9999,15 +10247,15 @@ function assertAdoptableDirectory(target) {
9999
10247
  }
10000
10248
  }
10001
10249
  function mergeGitignore(generatedFile, targetFile) {
10002
- const existing = (0, import_node_fs39.readFileSync)(targetFile, "utf8");
10250
+ const existing = (0, import_node_fs40.readFileSync)(targetFile, "utf8");
10003
10251
  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) => {
10252
+ const additions = (0, import_node_fs40.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
10005
10253
  const trimmed = line.trim();
10006
10254
  return trimmed !== "" && !trimmed.startsWith("#") && !present.has(trimmed);
10007
10255
  });
10008
10256
  if (additions.length === 0) return false;
10009
10257
  const separator = existing.endsWith("\n") ? "" : "\n";
10010
- (0, import_node_fs39.writeFileSync)(
10258
+ (0, import_node_fs40.writeFileSync)(
10011
10259
  targetFile,
10012
10260
  `${existing}${separator}
10013
10261
  # Added by mnci (Nx workspace)
@@ -10017,9 +10265,9 @@ ${additions.join("\n")}
10017
10265
  return true;
10018
10266
  }
10019
10267
  function adoptGeneratedWorkspace(generatedRoot, target) {
10020
- const entries = (0, import_node_fs39.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
10268
+ const entries = (0, import_node_fs40.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
10021
10269
  const collisions = entries.filter(
10022
- (entry) => (0, import_node_fs39.existsSync)((0, import_node_path48.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
10270
+ (entry) => (0, import_node_fs40.existsSync)((0, import_node_path49.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
10023
10271
  );
10024
10272
  if (collisions.length > 0) {
10025
10273
  throw new Error(
@@ -10028,28 +10276,28 @@ function adoptGeneratedWorkspace(generatedRoot, target) {
10028
10276
  }
10029
10277
  const result = { copied: [], kept: [], mergedGitignore: false };
10030
10278
  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)) {
10279
+ const from = (0, import_node_path49.join)(generatedRoot, entry);
10280
+ const to = (0, import_node_path49.join)(target, entry);
10281
+ if (entry === ".gitignore" && (0, import_node_fs40.existsSync)(to)) {
10034
10282
  result.mergedGitignore = mergeGitignore(from, to);
10035
10283
  result.kept.push(entry);
10036
10284
  continue;
10037
10285
  }
10038
- if ((0, import_node_fs39.existsSync)(to)) {
10286
+ if ((0, import_node_fs40.existsSync)(to)) {
10039
10287
  result.kept.push(entry);
10040
10288
  continue;
10041
10289
  }
10042
- (0, import_node_fs39.cpSync)(from, to, { recursive: true });
10290
+ (0, import_node_fs40.cpSync)(from, to, { recursive: true });
10043
10291
  result.copied.push(entry);
10044
10292
  }
10045
- (0, import_node_fs39.rmSync)(generatedRoot, { recursive: true, force: true });
10293
+ (0, import_node_fs40.rmSync)(generatedRoot, { recursive: true, force: true });
10046
10294
  return result;
10047
10295
  }
10048
10296
 
10049
10297
  // src/workspace-creation/create-workspace.use-case.ts
10050
- var import_node_fs40 = require("fs");
10298
+ var import_node_fs41 = require("fs");
10051
10299
  var import_node_os2 = require("os");
10052
- var import_node_path49 = require("path");
10300
+ var import_node_path50 = require("path");
10053
10301
  function nxCloudProviderValue(ci) {
10054
10302
  return ci === "azure" ? "azure" : "github";
10055
10303
  }
@@ -10092,9 +10340,9 @@ async function resolveRegistry2(options) {
10092
10340
  return await promptRegistry();
10093
10341
  }
10094
10342
  async function runNew(name, options) {
10095
- const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path49.resolve)(process.cwd(), options.into);
10343
+ const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path50.resolve)(process.cwd(), options.into);
10096
10344
  if (adoptTarget !== void 0) assertAdoptableDirectory(adoptTarget);
10097
- const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path49.basename)(adoptTarget));
10345
+ const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path50.basename)(adoptTarget));
10098
10346
  assertValidProjectName(workspaceName, "Workspace name");
10099
10347
  const scope = options.scope ?? (options.yes ? `@${workspaceName}` : await promptText("npm scope for publishable packages", `@${workspaceName}`));
10100
10348
  const registry = await resolveRegistry2(options);
@@ -10107,8 +10355,8 @@ async function runNew(name, options) {
10107
10355
  const npmAuth = resolveNpmAuth(options.npmAuth, registry, ci);
10108
10356
  const stack = await resolveStack(options);
10109
10357
  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);
10358
+ const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs41.mkdtempSync)((0, import_node_path50.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
10359
+ let workspaceRoot = (0, import_node_path50.join)(stagingParent, workspaceName);
10112
10360
  try {
10113
10361
  logger.step(`Creating Nx workspace '${workspaceName}' (preset: ts)`);
10114
10362
  runNpx(
@@ -10131,7 +10379,7 @@ async function runNew(name, options) {
10131
10379
  workspaceRoot = adoptTarget;
10132
10380
  }
10133
10381
  } finally {
10134
- if (adoptTarget !== void 0) (0, import_node_fs40.rmSync)(stagingParent, { recursive: true, force: true });
10382
+ if (adoptTarget !== void 0) (0, import_node_fs41.rmSync)(stagingParent, { recursive: true, force: true });
10135
10383
  }
10136
10384
  logger.step(
10137
10385
  "Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
@@ -10141,8 +10389,8 @@ async function runNew(name, options) {
10141
10389
  { workspaceName, scope, registry, agent, variableGroup, ci, stack, ...npmAuth && { npmAuth } },
10142
10390
  logger.detail
10143
10391
  );
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 });
10392
+ (0, import_node_fs41.rmSync)((0, import_node_path50.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
10393
+ (0, import_node_fs41.rmSync)((0, import_node_path50.join)(workspaceRoot, "package-lock.json"), { force: true });
10146
10394
  logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
10147
10395
  const installStatus = runShell(
10148
10396
  "npm",
@@ -10244,7 +10492,7 @@ function buildProgram(cliVersion) {
10244
10492
  ).option(
10245
10493
  "--empty",
10246
10494
  "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(
10495
+ ).option("--app <project>", "container only: the app to put in an image (a node-app, react-app or go-app)").option("--port <port>", "container only: the port the app listens on, exposed by the image and published by start").option(
10248
10496
  "--e2e",
10249
10497
  "react-app only: also scaffold a Playwright end-to-end project, <name>-e2e, that runs the app and checks its greeting; its e2e target needs a browser (npx playwright install) and is not part of CI verify"
10250
10498
  ).option(