@mnci/cli 4.46.0 → 4.48.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/README.md +26 -1
- package/dist/cli.js +458 -108
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8485,7 +8485,7 @@ function runDoctor(workspaceRoot, options = {}) {
|
|
|
8485
8485
|
process.exitCode = 1;
|
|
8486
8486
|
}
|
|
8487
8487
|
|
|
8488
|
-
// src/repository-adoption/adopt-
|
|
8488
|
+
// src/repository-adoption/adopt-dependencies.use-case.ts
|
|
8489
8489
|
var import_node_fs31 = require("fs");
|
|
8490
8490
|
var import_node_path36 = require("path");
|
|
8491
8491
|
|
|
@@ -8499,6 +8499,157 @@ function requireCleanWorkingTree(repositoryRoot, capture = runCapture) {
|
|
|
8499
8499
|
}
|
|
8500
8500
|
}
|
|
8501
8501
|
|
|
8502
|
+
// src/repository-adoption/extract-imports.algorithm.ts
|
|
8503
|
+
var import_node_module = require("module");
|
|
8504
|
+
var SPECIFIER2 = /(?:\bfrom\s*|\bimport\s*(?:\(\s*)?|\brequire\s*\(\s*)(['"`])([^'"`\n]+)\1/g;
|
|
8505
|
+
var BUILTINS = new Set(import_node_module.builtinModules);
|
|
8506
|
+
function packageOfSpecifier(specifier) {
|
|
8507
|
+
if (/^(?:[./#~]|[a-z]+:)/i.test(specifier)) {
|
|
8508
|
+
return void 0;
|
|
8509
|
+
}
|
|
8510
|
+
const parts = specifier.split("/");
|
|
8511
|
+
const name = specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
8512
|
+
if (name === "" || BUILTINS.has(name) || specifier.startsWith("@") && parts.length < 2) {
|
|
8513
|
+
return void 0;
|
|
8514
|
+
}
|
|
8515
|
+
return name;
|
|
8516
|
+
}
|
|
8517
|
+
function extractImportedPackages(source) {
|
|
8518
|
+
const found = /* @__PURE__ */ new Set();
|
|
8519
|
+
for (const match of source.matchAll(SPECIFIER2)) {
|
|
8520
|
+
const name = packageOfSpecifier(match[2]);
|
|
8521
|
+
if (name !== void 0) {
|
|
8522
|
+
found.add(name);
|
|
8523
|
+
}
|
|
8524
|
+
}
|
|
8525
|
+
return found;
|
|
8526
|
+
}
|
|
8527
|
+
|
|
8528
|
+
// src/repository-adoption/plan-dependency-moves.algorithm.ts
|
|
8529
|
+
function planDependencyMoves(rootDependencies, usage, declared) {
|
|
8530
|
+
const plan = { moves: [], removed: [], keptAtRoot: [], conflicts: [] };
|
|
8531
|
+
for (const [name, range] of Object.entries(rootDependencies)) {
|
|
8532
|
+
const where = usage[name];
|
|
8533
|
+
const runtime = where?.runtime ?? [];
|
|
8534
|
+
const testsOnly = where?.testsOnly ?? [];
|
|
8535
|
+
if (runtime.length === 0 && testsOnly.length === 0) {
|
|
8536
|
+
plan.keptAtRoot.push({ name, reason: "no project imports it: it may be tooling the root uses, or unused" });
|
|
8537
|
+
continue;
|
|
8538
|
+
}
|
|
8539
|
+
const targets = [
|
|
8540
|
+
...runtime.map((dir) => [dir, "dependencies"]),
|
|
8541
|
+
...testsOnly.map((dir) => [dir, "devDependencies"])
|
|
8542
|
+
];
|
|
8543
|
+
for (const [dir, field] of targets) {
|
|
8544
|
+
const existing = declared[dir]?.[name];
|
|
8545
|
+
if (existing === void 0) {
|
|
8546
|
+
plan.moves.push({ name, range, dir, field });
|
|
8547
|
+
} else if (existing !== range) {
|
|
8548
|
+
plan.conflicts.push({ name, dir, projectRange: existing, rootRange: range });
|
|
8549
|
+
}
|
|
8550
|
+
}
|
|
8551
|
+
plan.removed.push(name);
|
|
8552
|
+
}
|
|
8553
|
+
return plan;
|
|
8554
|
+
}
|
|
8555
|
+
|
|
8556
|
+
// src/repository-adoption/adopt-dependencies.use-case.ts
|
|
8557
|
+
var SOURCE_FILE = /\.(?:[cm]?[jt]s|[jt]sx)$/;
|
|
8558
|
+
var TEST_FILE = /\.(?:spec|test)\.[^.]+$|[\\/](?:__tests__|tests?|e2e)[\\/]/;
|
|
8559
|
+
var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", "out", ".git", ".nx", "tmp"]);
|
|
8560
|
+
function sourceFiles(directory, relative = "") {
|
|
8561
|
+
const entries = (0, import_node_fs31.readdirSync)((0, import_node_path36.join)(directory, relative), { withFileTypes: true });
|
|
8562
|
+
const found = [];
|
|
8563
|
+
for (const entry of entries) {
|
|
8564
|
+
const path = relative === "" ? entry.name : `${relative}/${entry.name}`;
|
|
8565
|
+
if (entry.isDirectory()) {
|
|
8566
|
+
if (!SKIPPED_DIRECTORIES.has(entry.name)) {
|
|
8567
|
+
found.push(...sourceFiles(directory, path));
|
|
8568
|
+
}
|
|
8569
|
+
} else if (SOURCE_FILE.test(entry.name)) {
|
|
8570
|
+
found.push(path);
|
|
8571
|
+
}
|
|
8572
|
+
}
|
|
8573
|
+
return found;
|
|
8574
|
+
}
|
|
8575
|
+
function wantedImportsOf(path, wanted) {
|
|
8576
|
+
try {
|
|
8577
|
+
const imported = [...extractImportedPackages((0, import_node_fs31.readFileSync)(path, "utf8"))];
|
|
8578
|
+
return imported.filter((name) => wanted.has(name));
|
|
8579
|
+
} catch {
|
|
8580
|
+
return [];
|
|
8581
|
+
}
|
|
8582
|
+
}
|
|
8583
|
+
function importsOfProject(projectDirectory, wanted) {
|
|
8584
|
+
const shipped = /* @__PURE__ */ new Set();
|
|
8585
|
+
const tested = /* @__PURE__ */ new Set();
|
|
8586
|
+
const files = sourceFiles(projectDirectory);
|
|
8587
|
+
for (const file of files) {
|
|
8588
|
+
const names = wantedImportsOf((0, import_node_path36.join)(projectDirectory, file), wanted);
|
|
8589
|
+
const target = TEST_FILE.test(file) ? tested : shipped;
|
|
8590
|
+
for (const name of names) {
|
|
8591
|
+
target.add(name);
|
|
8592
|
+
}
|
|
8593
|
+
}
|
|
8594
|
+
const shippedNames = [...shipped];
|
|
8595
|
+
return { shipped: shippedNames, testsOnly: [...tested].filter((name) => !shippedNames.includes(name)) };
|
|
8596
|
+
}
|
|
8597
|
+
function findUsage(repositoryRoot, projectDirectories, wanted) {
|
|
8598
|
+
const usage = {};
|
|
8599
|
+
for (const dir of projectDirectories) {
|
|
8600
|
+
const { shipped, testsOnly } = importsOfProject((0, import_node_path36.join)(repositoryRoot, dir), wanted);
|
|
8601
|
+
for (const name of shipped) {
|
|
8602
|
+
usage[name] ??= { runtime: [], testsOnly: [] };
|
|
8603
|
+
usage[name].runtime.push(dir);
|
|
8604
|
+
}
|
|
8605
|
+
for (const name of testsOnly) {
|
|
8606
|
+
usage[name] ??= { runtime: [], testsOnly: [] };
|
|
8607
|
+
usage[name].testsOnly.push(dir);
|
|
8608
|
+
}
|
|
8609
|
+
}
|
|
8610
|
+
return usage;
|
|
8611
|
+
}
|
|
8612
|
+
function adoptDependencies(repositoryRoot, dependencies = {}) {
|
|
8613
|
+
const run = dependencies.run ?? runShell;
|
|
8614
|
+
requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
|
|
8615
|
+
const rootPath = (0, import_node_path36.join)(repositoryRoot, "package.json");
|
|
8616
|
+
if (!fileExists(rootPath)) {
|
|
8617
|
+
throw new Error("No package.json at the repository root.");
|
|
8618
|
+
}
|
|
8619
|
+
const root = readJson(rootPath);
|
|
8620
|
+
const rootDependencies = root.dependencies ?? {};
|
|
8621
|
+
const projects = locateProjects(repositoryRoot).filter((location) => location.ecosystem === "npm");
|
|
8622
|
+
const manifests = new Map(projects.map((location) => [location.dir, readJson(location.manifestPath)]));
|
|
8623
|
+
const usage = findUsage(repositoryRoot, projects.map((location) => location.dir), new Set(Object.keys(rootDependencies)));
|
|
8624
|
+
const declared = Object.fromEntries([...manifests].map(([dir, manifest]) => [dir, { ...manifest.devDependencies, ...manifest.dependencies }]));
|
|
8625
|
+
const plan = planDependencyMoves(rootDependencies, usage, declared);
|
|
8626
|
+
if (plan.moves.length === 0 && plan.removed.length === 0) {
|
|
8627
|
+
return { plan, installed: true };
|
|
8628
|
+
}
|
|
8629
|
+
for (const move of plan.moves) {
|
|
8630
|
+
const manifest = manifests.get(move.dir);
|
|
8631
|
+
manifest[move.field] = Object.fromEntries(Object.entries({ ...manifest[move.field], [move.name]: move.range }).toSorted(([a], [b]) => a.localeCompare(b)));
|
|
8632
|
+
}
|
|
8633
|
+
for (const location of projects) {
|
|
8634
|
+
if (plan.moves.some((move) => move.dir === location.dir)) {
|
|
8635
|
+
writeFileEnsured(location.manifestPath, toJson(manifests.get(location.dir)));
|
|
8636
|
+
}
|
|
8637
|
+
}
|
|
8638
|
+
const remaining = Object.fromEntries(Object.entries(rootDependencies).filter(([name]) => !plan.removed.includes(name)));
|
|
8639
|
+
if (Object.keys(remaining).length === 0) {
|
|
8640
|
+
Reflect.deleteProperty(root, "dependencies");
|
|
8641
|
+
} else {
|
|
8642
|
+
root.dependencies = remaining;
|
|
8643
|
+
}
|
|
8644
|
+
writeFileEnsured(rootPath, toJson(root));
|
|
8645
|
+
const installed = run("npm", ["install", "--ignore-scripts", "--no-audit", "--no-fund"], repositoryRoot) === 0;
|
|
8646
|
+
return { plan, installed };
|
|
8647
|
+
}
|
|
8648
|
+
|
|
8649
|
+
// src/repository-adoption/adopt-kinds.use-case.ts
|
|
8650
|
+
var import_node_fs32 = require("fs");
|
|
8651
|
+
var import_node_path37 = require("path");
|
|
8652
|
+
|
|
8502
8653
|
// src/repository-adoption/infer-project-kind.algorithm.ts
|
|
8503
8654
|
var SERVER_FRAMEWORKS = ["express", "koa", "fastify", "@nestjs/core"];
|
|
8504
8655
|
function declares(manifest, name) {
|
|
@@ -8598,32 +8749,32 @@ function inferProjectKind(evidence) {
|
|
|
8598
8749
|
// src/repository-adoption/adopt-kinds.use-case.ts
|
|
8599
8750
|
var TYPE_TAG = "type:";
|
|
8600
8751
|
function readEvidence(repositoryRoot, location) {
|
|
8601
|
-
const directory = (0,
|
|
8602
|
-
const files = (0,
|
|
8752
|
+
const directory = (0, import_node_path37.join)(repositoryRoot, location.dir);
|
|
8753
|
+
const files = (0, import_node_fs32.readdirSync)(directory);
|
|
8603
8754
|
const manifest = location.ecosystem === "npm" ? readJson(location.manifestPath) : {};
|
|
8604
|
-
const text = location.ecosystem === "npm" ? "" : (0,
|
|
8755
|
+
const text = location.ecosystem === "npm" ? "" : (0, import_node_fs32.readFileSync)(location.manifestPath, "utf8");
|
|
8605
8756
|
let hasProgram = false;
|
|
8606
8757
|
if (location.ecosystem === "go") {
|
|
8607
|
-
hasProgram = files.filter((file) => file.endsWith(".go")).some((file) => /^package main\b/m.test((0,
|
|
8758
|
+
hasProgram = files.filter((file) => file.endsWith(".go")).some((file) => /^package main\b/m.test((0, import_node_fs32.readFileSync)((0, import_node_path37.join)(directory, file), "utf8")));
|
|
8608
8759
|
} else if (location.ecosystem === "pub") {
|
|
8609
|
-
hasProgram = (0,
|
|
8760
|
+
hasProgram = (0, import_node_fs32.existsSync)((0, import_node_path37.join)(directory, "lib", "main.dart"));
|
|
8610
8761
|
}
|
|
8611
8762
|
return { ecosystem: location.ecosystem, manifest, text, files, hasProgram };
|
|
8612
8763
|
}
|
|
8613
8764
|
function existingKind(directory) {
|
|
8614
8765
|
const sources = [];
|
|
8615
|
-
if (fileExists((0,
|
|
8616
|
-
sources.push(readJson((0,
|
|
8766
|
+
if (fileExists((0, import_node_path37.join)(directory, "project.json"))) {
|
|
8767
|
+
sources.push(readJson((0, import_node_path37.join)(directory, "project.json")).tags ?? []);
|
|
8617
8768
|
}
|
|
8618
|
-
if (fileExists((0,
|
|
8619
|
-
sources.push(readJson((0,
|
|
8769
|
+
if (fileExists((0, import_node_path37.join)(directory, "package.json"))) {
|
|
8770
|
+
sources.push(readJson((0, import_node_path37.join)(directory, "package.json")).nx?.tags ?? []);
|
|
8620
8771
|
}
|
|
8621
8772
|
return sources.flat().find((tag) => tag.startsWith(TYPE_TAG))?.slice(TYPE_TAG.length);
|
|
8622
8773
|
}
|
|
8623
8774
|
function writeKindTag(directory, name, kind) {
|
|
8624
8775
|
const tag = `${TYPE_TAG}${kind}`;
|
|
8625
|
-
const projectJson = (0,
|
|
8626
|
-
const packageJson = (0,
|
|
8776
|
+
const projectJson = (0, import_node_path37.join)(directory, "project.json");
|
|
8777
|
+
const packageJson = (0, import_node_path37.join)(directory, "package.json");
|
|
8627
8778
|
if (fileExists(projectJson)) {
|
|
8628
8779
|
const project = readJson(projectJson);
|
|
8629
8780
|
writeFileEnsured(projectJson, toJson({ ...project, tags: [...project.tags ?? [], tag] }));
|
|
@@ -8647,7 +8798,7 @@ function adoptKinds(repositoryRoot, overrides = {}, dependencies = {}) {
|
|
|
8647
8798
|
}
|
|
8648
8799
|
}
|
|
8649
8800
|
return locations.map((location) => {
|
|
8650
|
-
const directory = (0,
|
|
8801
|
+
const directory = (0, import_node_path37.join)(repositoryRoot, location.dir);
|
|
8651
8802
|
const present = existingKind(directory);
|
|
8652
8803
|
if (present !== void 0) {
|
|
8653
8804
|
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" };
|
|
@@ -8663,12 +8814,12 @@ function adoptKinds(repositoryRoot, overrides = {}, dependencies = {}) {
|
|
|
8663
8814
|
}
|
|
8664
8815
|
|
|
8665
8816
|
// src/repository-adoption/adopt-overlay.use-case.ts
|
|
8666
|
-
var
|
|
8667
|
-
var
|
|
8817
|
+
var import_node_fs34 = require("fs");
|
|
8818
|
+
var import_node_path39 = require("path");
|
|
8668
8819
|
|
|
8669
8820
|
// src/workspace-upgrade/upgrade-workspace.use-case.ts
|
|
8670
|
-
var
|
|
8671
|
-
var
|
|
8821
|
+
var import_node_fs33 = require("fs");
|
|
8822
|
+
var import_node_path38 = require("path");
|
|
8672
8823
|
function resolveRegistry(options, persisted) {
|
|
8673
8824
|
if (options.registry === "azure-artifacts" || options.organization && options.artifactsFeed) {
|
|
8674
8825
|
const persistedAzure = persisted?.kind === "azure-artifacts" ? persisted : void 0;
|
|
@@ -8697,7 +8848,7 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
|
|
|
8697
8848
|
return persisted.workspaceName;
|
|
8698
8849
|
}
|
|
8699
8850
|
try {
|
|
8700
|
-
const existing = (0,
|
|
8851
|
+
const existing = (0, import_node_fs33.readdirSync)(workspaceRoot).find(
|
|
8701
8852
|
(file) => file.endsWith(".code-workspace") && file !== "undefined.code-workspace"
|
|
8702
8853
|
);
|
|
8703
8854
|
if (existing) {
|
|
@@ -8705,11 +8856,11 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
|
|
|
8705
8856
|
}
|
|
8706
8857
|
} catch {
|
|
8707
8858
|
}
|
|
8708
|
-
return (0,
|
|
8859
|
+
return (0, import_node_path38.basename)(workspaceRoot);
|
|
8709
8860
|
}
|
|
8710
8861
|
function detectNpmAuth(workspaceRoot) {
|
|
8711
8862
|
try {
|
|
8712
|
-
return (0,
|
|
8863
|
+
return (0, import_node_fs33.readFileSync)((0, import_node_path38.join)(workspaceRoot, "azure-pipelines.yml"), "utf8").includes("npmAuthenticate@0") ? "build-identity" : void 0;
|
|
8713
8864
|
} catch {
|
|
8714
8865
|
return void 0;
|
|
8715
8866
|
}
|
|
@@ -8756,7 +8907,7 @@ function resolveOverlayOptions(workspaceRoot, options, persisted) {
|
|
|
8756
8907
|
};
|
|
8757
8908
|
}
|
|
8758
8909
|
function runUpgrade(workspaceRoot, options) {
|
|
8759
|
-
if (!fileExists((0,
|
|
8910
|
+
if (!fileExists((0, import_node_path38.join)(workspaceRoot, "nx.json"))) {
|
|
8760
8911
|
throw new Error(
|
|
8761
8912
|
`No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
|
|
8762
8913
|
);
|
|
@@ -8830,7 +8981,7 @@ function runUpgrade(workspaceRoot, options) {
|
|
|
8830
8981
|
"Formatting the workspace (eslint --fix) \u2014 the slowest step, minutes on a large workspace"
|
|
8831
8982
|
);
|
|
8832
8983
|
runFormatter(workspaceRoot);
|
|
8833
|
-
const nxJson = readJson((0,
|
|
8984
|
+
const nxJson = readJson((0, import_node_path38.join)(workspaceRoot, "nx.json"));
|
|
8834
8985
|
const stranded = locateStrandedReleaseTags(workspaceRoot, nxJson.release?.releaseTag?.pattern);
|
|
8835
8986
|
for (const entry of stranded) {
|
|
8836
8987
|
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).`);
|
|
@@ -8841,9 +8992,9 @@ function runUpgrade(workspaceRoot, options) {
|
|
|
8841
8992
|
|
|
8842
8993
|
// src/repository-adoption/adopt-overlay.use-case.ts
|
|
8843
8994
|
function detectCiProvider(repositoryRoot) {
|
|
8844
|
-
const azure = (0,
|
|
8845
|
-
const workflows = (0,
|
|
8846
|
-
const github = (0,
|
|
8995
|
+
const azure = (0, import_node_fs34.existsSync)((0, import_node_path39.join)(repositoryRoot, "azure-pipelines.yml"));
|
|
8996
|
+
const workflows = (0, import_node_path39.join)(repositoryRoot, ".github", "workflows");
|
|
8997
|
+
const github = (0, import_node_fs34.existsSync)(workflows) && (0, import_node_fs34.readdirSync)(workflows).some((file) => /\.ya?ml$/.test(file));
|
|
8847
8998
|
if (azure && github) {
|
|
8848
8999
|
return "both";
|
|
8849
9000
|
}
|
|
@@ -8853,7 +9004,7 @@ function detectCiProvider(repositoryRoot) {
|
|
|
8853
9004
|
return github ? "github" : void 0;
|
|
8854
9005
|
}
|
|
8855
9006
|
function detectTestRunner(repositoryRoot) {
|
|
8856
|
-
const manifestPath = (0,
|
|
9007
|
+
const manifestPath = (0, import_node_path39.join)(repositoryRoot, "package.json");
|
|
8857
9008
|
if (!fileExists(manifestPath)) {
|
|
8858
9009
|
return void 0;
|
|
8859
9010
|
}
|
|
@@ -8866,7 +9017,7 @@ function detectTestRunner(repositoryRoot) {
|
|
|
8866
9017
|
}
|
|
8867
9018
|
function adoptOverlay(repositoryRoot, options, dependencies = {}) {
|
|
8868
9019
|
requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
|
|
8869
|
-
if (!fileExists((0,
|
|
9020
|
+
if (!fileExists((0, import_node_path39.join)(repositoryRoot, "nx.json"))) {
|
|
8870
9021
|
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.");
|
|
8871
9022
|
}
|
|
8872
9023
|
const persisted = readMnciConfig(repositoryRoot);
|
|
@@ -8879,7 +9030,7 @@ function adoptOverlay(repositoryRoot, options, dependencies = {}) {
|
|
|
8879
9030
|
}
|
|
8880
9031
|
|
|
8881
9032
|
// src/repository-adoption/adopt-toolchain.use-case.ts
|
|
8882
|
-
var
|
|
9033
|
+
var import_node_path41 = require("path");
|
|
8883
9034
|
|
|
8884
9035
|
// src/repository-adoption/align-nx-family.algorithm.ts
|
|
8885
9036
|
var VERSION = /^[\^~]?(\d+)\.(\d+)\.(\d+)$/;
|
|
@@ -8917,24 +9068,24 @@ function alignNxFamily(dependencies, version) {
|
|
|
8917
9068
|
}
|
|
8918
9069
|
|
|
8919
9070
|
// src/repository-adoption/retire-tooling.use-case.ts
|
|
8920
|
-
var
|
|
8921
|
-
var
|
|
9071
|
+
var import_node_fs35 = require("fs");
|
|
9072
|
+
var import_node_path40 = require("path");
|
|
8922
9073
|
var RETIRED_COMMAND = /\b(?:prettier|oxfmt|oxlint)\b/;
|
|
8923
9074
|
function retireTooling(repositoryRoot) {
|
|
8924
9075
|
const removed = [];
|
|
8925
9076
|
for (const file of RETIRED_FORMATTER_FILES) {
|
|
8926
|
-
if (!fileExists((0,
|
|
9077
|
+
if (!fileExists((0, import_node_path40.join)(repositoryRoot, file))) {
|
|
8927
9078
|
continue;
|
|
8928
9079
|
}
|
|
8929
|
-
(0,
|
|
9080
|
+
(0, import_node_fs35.rmSync)((0, import_node_path40.join)(repositoryRoot, file), { force: true });
|
|
8930
9081
|
removed.push(file);
|
|
8931
9082
|
}
|
|
8932
|
-
const hadRegistry = fileExists((0,
|
|
9083
|
+
const hadRegistry = fileExists((0, import_node_path40.join)(repositoryRoot, ".verdaccio"));
|
|
8933
9084
|
removeLocalRegistryScaffolding(repositoryRoot);
|
|
8934
9085
|
if (hadRegistry) {
|
|
8935
9086
|
removed.push(".verdaccio, its dependency and the local-registry target");
|
|
8936
9087
|
}
|
|
8937
|
-
const manifestPath = (0,
|
|
9088
|
+
const manifestPath = (0, import_node_path40.join)(repositoryRoot, "package.json");
|
|
8938
9089
|
if (!fileExists(manifestPath)) {
|
|
8939
9090
|
return removed;
|
|
8940
9091
|
}
|
|
@@ -8981,7 +9132,7 @@ function adoptToolchain(repositoryRoot, options = {}, dependencies = {}) {
|
|
|
8981
9132
|
console.log(message);
|
|
8982
9133
|
});
|
|
8983
9134
|
requireCleanWorkingTree(repositoryRoot, capture);
|
|
8984
|
-
const manifestPath = (0,
|
|
9135
|
+
const manifestPath = (0, import_node_path41.join)(repositoryRoot, "package.json");
|
|
8985
9136
|
if (!fileExists(manifestPath)) {
|
|
8986
9137
|
throw new Error("No package.json at the repository root.");
|
|
8987
9138
|
}
|
|
@@ -9031,8 +9182,8 @@ function createBaselineTags(repositoryRoot, stranded) {
|
|
|
9031
9182
|
}
|
|
9032
9183
|
|
|
9033
9184
|
// src/repository-adoption/inspect-repository.use-case.ts
|
|
9034
|
-
var
|
|
9035
|
-
var
|
|
9185
|
+
var import_node_fs36 = require("fs");
|
|
9186
|
+
var import_node_path42 = require("path");
|
|
9036
9187
|
var LOCKFILES = [
|
|
9037
9188
|
["package-lock.json", "npm"],
|
|
9038
9189
|
["pnpm-lock.yaml", "pnpm"],
|
|
@@ -9053,25 +9204,25 @@ function inspectRepository(repositoryRoot) {
|
|
|
9053
9204
|
const isGitRepo = inside.status === 0 && inside.stdout.trim() === "true";
|
|
9054
9205
|
const dirty = isGitRepo && runCapture("git", ["status", "--porcelain"], repositoryRoot).stdout.trim() !== "";
|
|
9055
9206
|
const tags = isGitRepo ? runCapture("git", ["tag", "--list"], repositoryRoot).stdout.split(/\r?\n/).filter(Boolean) : [];
|
|
9056
|
-
const manifestPath = (0,
|
|
9207
|
+
const manifestPath = (0, import_node_path42.join)(repositoryRoot, "package.json");
|
|
9057
9208
|
const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
|
|
9058
|
-
const nxJsonPath = (0,
|
|
9209
|
+
const nxJsonPath = (0, import_node_path42.join)(repositoryRoot, "nx.json");
|
|
9059
9210
|
const nxJson = fileExists(nxJsonPath) ? readJson(nxJsonPath) : {};
|
|
9060
9211
|
const ci = [];
|
|
9061
9212
|
const pipelines = [];
|
|
9062
|
-
if ((0,
|
|
9213
|
+
if ((0, import_node_fs36.existsSync)((0, import_node_path42.join)(repositoryRoot, "azure-pipelines.yml"))) {
|
|
9063
9214
|
ci.push("azure");
|
|
9064
|
-
pipelines.push((0,
|
|
9215
|
+
pipelines.push((0, import_node_path42.join)(repositoryRoot, "azure-pipelines.yml"));
|
|
9065
9216
|
}
|
|
9066
|
-
const workflows = (0,
|
|
9067
|
-
if ((0,
|
|
9068
|
-
const files = (0,
|
|
9217
|
+
const workflows = (0, import_node_path42.join)(repositoryRoot, ".github", "workflows");
|
|
9218
|
+
if ((0, import_node_fs36.existsSync)(workflows)) {
|
|
9219
|
+
const files = (0, import_node_fs36.readdirSync)(workflows).filter((file) => /\.ya?ml$/.test(file));
|
|
9069
9220
|
if (files.length > 0) {
|
|
9070
9221
|
ci.push("github");
|
|
9071
|
-
pipelines.push(...files.map((file) => (0,
|
|
9222
|
+
pipelines.push(...files.map((file) => (0, import_node_path42.join)(workflows, file)));
|
|
9072
9223
|
}
|
|
9073
9224
|
}
|
|
9074
|
-
const rootFiles = (0,
|
|
9225
|
+
const rootFiles = (0, import_node_fs36.readdirSync)(repositoryRoot);
|
|
9075
9226
|
return {
|
|
9076
9227
|
isGitRepo,
|
|
9077
9228
|
dirty,
|
|
@@ -9082,7 +9233,7 @@ function inspectRepository(repositoryRoot) {
|
|
|
9082
9233
|
tagCount: tags.length,
|
|
9083
9234
|
strandedTags: isGitRepo ? locateStrandedReleaseTags(repositoryRoot, nxJson.release?.releaseTag?.pattern) : [],
|
|
9084
9235
|
ci,
|
|
9085
|
-
pipelineUsesMnci: pipelines.some((file) => /\bmnci ci\b/.test((0,
|
|
9236
|
+
pipelineUsesMnci: pipelines.some((file) => /\bmnci ci\b/.test((0, import_node_fs36.readFileSync)(file, "utf8"))),
|
|
9086
9237
|
retiredTooling: [.../* @__PURE__ */ new Set([...rootFiles.filter((file) => RETIRED_FILE_PATTERN.test(file) || file === ".verdaccio"), ...retiredNamesIn(manifest)])],
|
|
9087
9238
|
personalFiles: PERSONAL_FILES.filter((file) => rootFiles.includes(file))
|
|
9088
9239
|
};
|
|
@@ -9139,6 +9290,10 @@ function reportAdoption(repositoryRoot) {
|
|
|
9139
9290
|
}
|
|
9140
9291
|
function runAdopt(repositoryRoot, options = {}) {
|
|
9141
9292
|
const report = reportAdoption(repositoryRoot);
|
|
9293
|
+
if (options.dependencies === true) {
|
|
9294
|
+
runDependencies(repositoryRoot);
|
|
9295
|
+
return;
|
|
9296
|
+
}
|
|
9142
9297
|
if (options.kinds === true) {
|
|
9143
9298
|
runKinds2(repositoryRoot, options);
|
|
9144
9299
|
return;
|
|
@@ -9278,6 +9433,31 @@ function runKinds2(repositoryRoot, options) {
|
|
|
9278
9433
|
process.exitCode = 1;
|
|
9279
9434
|
}
|
|
9280
9435
|
}
|
|
9436
|
+
function runDependencies(repositoryRoot) {
|
|
9437
|
+
try {
|
|
9438
|
+
const { plan, installed } = adoptDependencies(repositoryRoot);
|
|
9439
|
+
for (const move of plan.moves) {
|
|
9440
|
+
logger.success(`${move.name} ${move.range} -> ${move.dir} (${move.field})`);
|
|
9441
|
+
}
|
|
9442
|
+
for (const conflict of plan.conflicts) {
|
|
9443
|
+
logger.warn(`${conflict.name}: ${conflict.dir} already declares ${conflict.projectRange}; the root had ${conflict.rootRange}. The project's own range is kept.`);
|
|
9444
|
+
}
|
|
9445
|
+
for (const kept of plan.keptAtRoot) {
|
|
9446
|
+
logger.warn(`${kept.name} stays at the root: ${kept.reason}`);
|
|
9447
|
+
}
|
|
9448
|
+
if (plan.removed.length === 0) {
|
|
9449
|
+
logger.success("The root declares no runtime dependency that a project imports. Nothing to move.");
|
|
9450
|
+
} else if (installed) {
|
|
9451
|
+
logger.success(`Moved ${plan.removed.length} package(s) out of the root and reinstalled. Review with \`git diff\` and commit package.json files and package-lock.json.`);
|
|
9452
|
+
} else {
|
|
9453
|
+
logger.error("The moves were written, but npm install failed. Run `npm install` and read its error before committing.");
|
|
9454
|
+
process.exitCode = 1;
|
|
9455
|
+
}
|
|
9456
|
+
} catch (error) {
|
|
9457
|
+
logger.error(error instanceof Error ? error.message : String(error));
|
|
9458
|
+
process.exitCode = 1;
|
|
9459
|
+
}
|
|
9460
|
+
}
|
|
9281
9461
|
|
|
9282
9462
|
// src/command-catalog/command-groups.config.ts
|
|
9283
9463
|
var COMMAND_GROUPS = {
|
|
@@ -9350,13 +9530,13 @@ function runCommands(program, options) {
|
|
|
9350
9530
|
}
|
|
9351
9531
|
|
|
9352
9532
|
// src/workspace-info/list-projects.use-case.ts
|
|
9353
|
-
var
|
|
9533
|
+
var import_node_path43 = require("path");
|
|
9354
9534
|
var TYPE_TAG_PREFIX = "type:";
|
|
9355
9535
|
function listProjects(workspaceRoot) {
|
|
9356
9536
|
return locateProjects(workspaceRoot).map((location) => {
|
|
9357
|
-
const projectJsonPath = (0,
|
|
9537
|
+
const projectJsonPath = (0, import_node_path43.join)(workspaceRoot, location.dir, "project.json");
|
|
9358
9538
|
const project = fileExists(projectJsonPath) ? readJson(projectJsonPath) : {};
|
|
9359
|
-
const manifestPath = (0,
|
|
9539
|
+
const manifestPath = (0, import_node_path43.join)(workspaceRoot, location.dir, "package.json");
|
|
9360
9540
|
const manifestTags = fileExists(manifestPath) ? readJson(manifestPath).nx?.tags ?? [] : [];
|
|
9361
9541
|
const typeTag = [...project.tags ?? [], ...manifestTags].find((tag) => tag.startsWith(TYPE_TAG_PREFIX));
|
|
9362
9542
|
return {
|
|
@@ -9369,7 +9549,7 @@ function listProjects(workspaceRoot) {
|
|
|
9369
9549
|
}).toSorted((a, b) => a.dir.localeCompare(b.dir));
|
|
9370
9550
|
}
|
|
9371
9551
|
function runProjects(workspaceRoot, options) {
|
|
9372
|
-
if (!fileExists((0,
|
|
9552
|
+
if (!fileExists((0, import_node_path43.join)(workspaceRoot, "nx.json"))) {
|
|
9373
9553
|
throw new Error("No nx.json found here. Run `projects` from the workspace root.");
|
|
9374
9554
|
}
|
|
9375
9555
|
const projects = listProjects(workspaceRoot);
|
|
@@ -9383,11 +9563,11 @@ function runProjects(workspaceRoot, options) {
|
|
|
9383
9563
|
}
|
|
9384
9564
|
|
|
9385
9565
|
// src/workspace-info/read-workspace-info.use-case.ts
|
|
9386
|
-
var
|
|
9566
|
+
var import_node_path45 = require("path");
|
|
9387
9567
|
|
|
9388
9568
|
// src/cli-version/check-for-update.use-case.ts
|
|
9389
|
-
var
|
|
9390
|
-
var
|
|
9569
|
+
var import_node_fs37 = require("fs");
|
|
9570
|
+
var import_node_path44 = require("path");
|
|
9391
9571
|
var import_cross_spawn3 = __toESM(require("cross-spawn"));
|
|
9392
9572
|
var PACKAGE_NAME2 = "@mnci/cli";
|
|
9393
9573
|
var REGISTRY_TIMEOUT_MS = 2e3;
|
|
@@ -9446,8 +9626,8 @@ function releaseTriple(version) {
|
|
|
9446
9626
|
}
|
|
9447
9627
|
function readCliVersion(packageDirectory) {
|
|
9448
9628
|
try {
|
|
9449
|
-
const manifestPath = (0,
|
|
9450
|
-
const manifest = JSON.parse((0,
|
|
9629
|
+
const manifestPath = (0, import_node_path44.join)(packageDirectory, "..", "package.json");
|
|
9630
|
+
const manifest = JSON.parse((0, import_node_fs37.readFileSync)(manifestPath, "utf8"));
|
|
9451
9631
|
return manifest.version ?? "0.0.0";
|
|
9452
9632
|
} catch {
|
|
9453
9633
|
return "0.0.0";
|
|
@@ -9457,7 +9637,7 @@ function readCliVersion(packageDirectory) {
|
|
|
9457
9637
|
// src/workspace-info/read-workspace-info.use-case.ts
|
|
9458
9638
|
function readWorkspaceInfo(workspaceRoot, cliVersion) {
|
|
9459
9639
|
const latest = fetchLatestVersion() ?? null;
|
|
9460
|
-
const isWorkspace = fileExists((0,
|
|
9640
|
+
const isWorkspace = fileExists((0, import_node_path45.join)(workspaceRoot, "nx.json"));
|
|
9461
9641
|
return {
|
|
9462
9642
|
cli: {
|
|
9463
9643
|
version: cliVersion,
|
|
@@ -9477,9 +9657,203 @@ function runInfo(workspaceRoot, cliVersion, options) {
|
|
|
9477
9657
|
logger.info(info.workspace ? `workspace: ${info.workspace.root}` : "not inside a workspace");
|
|
9478
9658
|
}
|
|
9479
9659
|
|
|
9660
|
+
// src/interactive-wizard/interactive-wizard.use-case.ts
|
|
9661
|
+
var import_node_path46 = require("path");
|
|
9662
|
+
|
|
9663
|
+
// src/interactive-wizard/adopt-steps.config.ts
|
|
9664
|
+
var ADOPT_GLOBAL_OPTIONS = ["json"];
|
|
9665
|
+
var ADOPT_STEPS = [
|
|
9666
|
+
{ id: "report", label: "Report", summary: "read the repository and say what adopting it involves (changes nothing)", options: [] },
|
|
9667
|
+
{ id: "tags", label: "Baseline release tags", summary: "create the tag each renamed project needs so a release continues from its last version", switch: "tags", options: [] },
|
|
9668
|
+
{ id: "toolchain", label: "Toolchain", summary: "retire old tooling, align Nx to one version, make the audit pass", switch: "toolchain", options: ["nx"] },
|
|
9669
|
+
{ id: "kinds", label: "Project kinds", summary: "record each project's kind as a type tag", switch: "kinds", options: ["kind"] },
|
|
9670
|
+
{ id: "dependencies", label: "Dependencies", summary: "move root runtime dependencies into the projects that import them", switch: "dependencies", options: [] },
|
|
9671
|
+
{
|
|
9672
|
+
id: "overlay",
|
|
9673
|
+
label: "Overlay and pipeline",
|
|
9674
|
+
summary: "apply the release config, .npmrc, commitlint and the CI pipeline",
|
|
9675
|
+
switch: "overlay",
|
|
9676
|
+
options: ["scope", "registry", "organization", "project", "artifacts-feed", "agent", "variable-group", "npm-auth", "ci", "test-runner"]
|
|
9677
|
+
}
|
|
9678
|
+
];
|
|
9679
|
+
|
|
9680
|
+
// src/interactive-wizard/applicable-options.policy.ts
|
|
9681
|
+
function optionsOfAdd(command, kind) {
|
|
9682
|
+
const claimed = new Set(PROJECT_KIND_CATALOG.flatMap((entry) => entry.flags));
|
|
9683
|
+
const own = new Set(PROJECT_KIND_CATALOG.find((entry) => entry.kind === kind)?.flags);
|
|
9684
|
+
return command.options.filter((option) => !claimed.has(option.name) || own.has(option.name));
|
|
9685
|
+
}
|
|
9686
|
+
function applicableOptions(command, context = {}) {
|
|
9687
|
+
if (command.name === "add") {
|
|
9688
|
+
return optionsOfAdd(command, context.kind);
|
|
9689
|
+
}
|
|
9690
|
+
if (command.name === "adopt") {
|
|
9691
|
+
const step = context.adoptStep;
|
|
9692
|
+
const wanted = /* @__PURE__ */ new Set([...step?.options ?? [], ...ADOPT_GLOBAL_OPTIONS]);
|
|
9693
|
+
return command.options.filter((option) => wanted.has(option.name));
|
|
9694
|
+
}
|
|
9695
|
+
return [...command.options];
|
|
9696
|
+
}
|
|
9697
|
+
|
|
9698
|
+
// src/interactive-wizard/ask-command.use-case.ts
|
|
9699
|
+
var NOT_ASKED = /* @__PURE__ */ new Set(["help", "version"]);
|
|
9700
|
+
var SKIP = "";
|
|
9701
|
+
async function askArgument(argument, prompter) {
|
|
9702
|
+
const message = `${argument.name}${argument.required ? "" : " (optional)"}: ${argument.description}`;
|
|
9703
|
+
if (argument.choices !== void 0) {
|
|
9704
|
+
const choices = argument.choices.map((choice) => ({ name: choice, value: choice }));
|
|
9705
|
+
const value = await prompter.select(message, argument.required ? choices : [{ name: "(skip)", value: SKIP }, ...choices]);
|
|
9706
|
+
return value === SKIP ? [] : [value];
|
|
9707
|
+
}
|
|
9708
|
+
const typed = await prompter.input(message, argument.required);
|
|
9709
|
+
const answer = typed.trim();
|
|
9710
|
+
if (answer === "") {
|
|
9711
|
+
return [];
|
|
9712
|
+
}
|
|
9713
|
+
return argument.variadic ? answer.split(/\s+/) : [answer];
|
|
9714
|
+
}
|
|
9715
|
+
async function askOptionValue(option, prompter) {
|
|
9716
|
+
if (option.takesValue === "none") {
|
|
9717
|
+
return true;
|
|
9718
|
+
}
|
|
9719
|
+
const message = `${option.flags}: ${option.description}`;
|
|
9720
|
+
if (option.choices !== void 0) {
|
|
9721
|
+
return await prompter.select(message, option.choices.map((choice) => ({ name: choice, value: choice })));
|
|
9722
|
+
}
|
|
9723
|
+
if (option.repeatable) {
|
|
9724
|
+
const values = [];
|
|
9725
|
+
let typed2 = await prompter.input(`${message} (an empty answer ends the list)`, false);
|
|
9726
|
+
while (typed2.trim() !== "") {
|
|
9727
|
+
values.push(typed2.trim());
|
|
9728
|
+
typed2 = await prompter.input(`${option.flags} (another, or empty to finish)`, false);
|
|
9729
|
+
}
|
|
9730
|
+
return values;
|
|
9731
|
+
}
|
|
9732
|
+
const typed = await prompter.input(message, option.takesValue === "required");
|
|
9733
|
+
const answer = typed.trim();
|
|
9734
|
+
return answer === "" ? true : answer;
|
|
9735
|
+
}
|
|
9736
|
+
async function askAdoptStep(prompter) {
|
|
9737
|
+
const id = await prompter.select("Which adoption step?", ADOPT_STEPS.map((step) => ({ name: step.label, value: step.id, description: step.summary })));
|
|
9738
|
+
return ADOPT_STEPS.find((step) => step.id === id);
|
|
9739
|
+
}
|
|
9740
|
+
async function askCommand(command, prompter) {
|
|
9741
|
+
const answers = { arguments: [], options: {} };
|
|
9742
|
+
for (const argument of command.arguments) {
|
|
9743
|
+
answers.arguments.push(...await askArgument(argument, prompter));
|
|
9744
|
+
}
|
|
9745
|
+
let adoptStep;
|
|
9746
|
+
if (command.name === "adopt") {
|
|
9747
|
+
adoptStep = await askAdoptStep(prompter);
|
|
9748
|
+
if (adoptStep.switch !== void 0) {
|
|
9749
|
+
answers.options[adoptStep.switch] = true;
|
|
9750
|
+
}
|
|
9751
|
+
}
|
|
9752
|
+
const offered = applicableOptions(command, { kind: command.name === "add" ? answers.arguments[0] : void 0, adoptStep }).filter((option) => !NOT_ASKED.has(option.name) && answers.options[option.name] === void 0);
|
|
9753
|
+
if (offered.length === 0) {
|
|
9754
|
+
return answers;
|
|
9755
|
+
}
|
|
9756
|
+
const chosen = await prompter.checkbox("Options (space to select, enter to continue)", offered.map((option) => ({ name: option.flags, value: option.name, description: option.description })));
|
|
9757
|
+
for (const name of chosen) {
|
|
9758
|
+
const option = offered.find((candidate) => candidate.name === name);
|
|
9759
|
+
const value = await askOptionValue(option, prompter);
|
|
9760
|
+
if (!(Array.isArray(value) && value.length === 0)) {
|
|
9761
|
+
answers.options[name] = value;
|
|
9762
|
+
}
|
|
9763
|
+
}
|
|
9764
|
+
return answers;
|
|
9765
|
+
}
|
|
9766
|
+
|
|
9767
|
+
// src/interactive-wizard/build-argv.algorithm.ts
|
|
9768
|
+
function longFlag(option) {
|
|
9769
|
+
return `--${option.name}`;
|
|
9770
|
+
}
|
|
9771
|
+
function buildArgv(command, answers) {
|
|
9772
|
+
const argv = [command.name, ...answers.arguments];
|
|
9773
|
+
for (const [name, answer] of Object.entries(answers.options)) {
|
|
9774
|
+
const option = command.options.find((candidate) => candidate.name === name);
|
|
9775
|
+
if (option === void 0) {
|
|
9776
|
+
throw new Error(`mnci ${command.name} has no option --${name}`);
|
|
9777
|
+
}
|
|
9778
|
+
if (answer === false) {
|
|
9779
|
+
continue;
|
|
9780
|
+
}
|
|
9781
|
+
if (typeof answer === "boolean") {
|
|
9782
|
+
argv.push(longFlag(option));
|
|
9783
|
+
} else {
|
|
9784
|
+
const values = Array.isArray(answer) ? answer : [answer];
|
|
9785
|
+
for (const value of values) {
|
|
9786
|
+
argv.push(longFlag(option), value);
|
|
9787
|
+
}
|
|
9788
|
+
}
|
|
9789
|
+
}
|
|
9790
|
+
return argv;
|
|
9791
|
+
}
|
|
9792
|
+
function formatCommandLine(argv) {
|
|
9793
|
+
return ["mnci", ...argv.map((argument) => /\s/.test(argument) ? JSON.stringify(argument) : argument)].join(" ");
|
|
9794
|
+
}
|
|
9795
|
+
|
|
9796
|
+
// src/interactive-wizard/inquirer-prompter.client.ts
|
|
9797
|
+
var import_prompts4 = require("@inquirer/prompts");
|
|
9798
|
+
function toRows(choices) {
|
|
9799
|
+
const rows = [];
|
|
9800
|
+
let current;
|
|
9801
|
+
for (const choice of choices) {
|
|
9802
|
+
if (choice.group !== void 0 && choice.group !== current) {
|
|
9803
|
+
current = choice.group;
|
|
9804
|
+
rows.push(new import_prompts4.Separator(`-- ${choice.group} --`));
|
|
9805
|
+
}
|
|
9806
|
+
rows.push({ name: choice.name, value: choice.value, description: choice.description });
|
|
9807
|
+
}
|
|
9808
|
+
return rows;
|
|
9809
|
+
}
|
|
9810
|
+
function createInquirerPrompter() {
|
|
9811
|
+
return {
|
|
9812
|
+
select: async (message, choices) => await (0, import_prompts4.select)({ message, choices: toRows(choices), pageSize: 18 }),
|
|
9813
|
+
checkbox: async (message, choices) => await (0, import_prompts4.checkbox)({ message, choices: toRows(choices), pageSize: 18 }),
|
|
9814
|
+
input: async (message, required) => await (0, import_prompts4.input)({ message, validate: (value) => !required || value.trim().length > 0 || "A value is required" }),
|
|
9815
|
+
confirm: async (message, fallback) => await (0, import_prompts4.confirm)({ message, default: fallback })
|
|
9816
|
+
};
|
|
9817
|
+
}
|
|
9818
|
+
|
|
9819
|
+
// src/interactive-wizard/interactive-wizard.use-case.ts
|
|
9820
|
+
var GROUP_LABELS = {
|
|
9821
|
+
workspace: "Workspace",
|
|
9822
|
+
projects: "Projects",
|
|
9823
|
+
dependencies: "Dependencies",
|
|
9824
|
+
pipeline: "Pipeline",
|
|
9825
|
+
inspect: "Inspect"
|
|
9826
|
+
};
|
|
9827
|
+
var WIZARD_GROUP_OVERRIDES = { adopt: "workspace" };
|
|
9828
|
+
var ORDER_IN_WORKSPACE = ["projects", "dependencies", "workspace", "pipeline", "inspect"];
|
|
9829
|
+
var ORDER_OUTSIDE = ["workspace", "projects", "dependencies", "pipeline", "inspect"];
|
|
9830
|
+
var SUMMARY_LENGTH = 110;
|
|
9831
|
+
function summarise(description) {
|
|
9832
|
+
const first = description.split(/\.\s| — /, 1)[0];
|
|
9833
|
+
return first.length > SUMMARY_LENGTH ? `${first.slice(0, SUMMARY_LENGTH - 1)}\u2026` : first;
|
|
9834
|
+
}
|
|
9835
|
+
function wizardMenu(commands, inWorkspace) {
|
|
9836
|
+
const groupOf = (command) => WIZARD_GROUP_OVERRIDES[command.name] ?? command.group;
|
|
9837
|
+
const order = inWorkspace ? ORDER_IN_WORKSPACE : ORDER_OUTSIDE;
|
|
9838
|
+
return order.flatMap((group) => commands.filter((command) => groupOf(command) === group).map((command) => ({ name: `${command.name.padEnd(9)} ${summarise(command.description)}`, value: command.name, group: GROUP_LABELS[group] })));
|
|
9839
|
+
}
|
|
9840
|
+
async function runInteractive(program, prompter = createInquirerPrompter(), workingDirectory = process.cwd()) {
|
|
9841
|
+
const commands = describeCommands(program);
|
|
9842
|
+
const inWorkspace = fileExists((0, import_node_path46.join)(workingDirectory, "nx.json"));
|
|
9843
|
+
const name = await prompter.select("What would you like to do?", wizardMenu(commands, inWorkspace));
|
|
9844
|
+
const command = commands.find((candidate) => candidate.name === name);
|
|
9845
|
+
const argv = buildArgv(command, await askCommand(command, prompter));
|
|
9846
|
+
logger.info(`Running: ${formatCommandLine(argv)}`);
|
|
9847
|
+
if (await prompter.confirm("Run it?", true)) {
|
|
9848
|
+
await program.parseAsync(argv, { from: "user" });
|
|
9849
|
+
} else {
|
|
9850
|
+
logger.info("Nothing was run.");
|
|
9851
|
+
}
|
|
9852
|
+
}
|
|
9853
|
+
|
|
9480
9854
|
// src/workspace-creation/adopt-directory.use-case.ts
|
|
9481
|
-
var
|
|
9482
|
-
var
|
|
9855
|
+
var import_node_fs38 = require("fs");
|
|
9856
|
+
var import_node_path47 = require("path");
|
|
9483
9857
|
var NEVER_COPIED = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
9484
9858
|
var AGENT_AND_CI_ENTRIES = [
|
|
9485
9859
|
"AGENTS.md",
|
|
@@ -9510,13 +9884,13 @@ var PREFLIGHT_BLOCKERS = [
|
|
|
9510
9884
|
"packages"
|
|
9511
9885
|
];
|
|
9512
9886
|
function assertAdoptableDirectory(target) {
|
|
9513
|
-
if (!(0,
|
|
9887
|
+
if (!(0, import_node_fs38.existsSync)(target)) {
|
|
9514
9888
|
throw new Error(`--into '${target}' does not exist. Create or clone it first.`);
|
|
9515
9889
|
}
|
|
9516
|
-
if (!(0,
|
|
9890
|
+
if (!(0, import_node_fs38.statSync)(target).isDirectory()) {
|
|
9517
9891
|
throw new Error(`--into '${target}' is not a directory.`);
|
|
9518
9892
|
}
|
|
9519
|
-
const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0,
|
|
9893
|
+
const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs38.existsSync)((0, import_node_path47.join)(target, entry)));
|
|
9520
9894
|
if (blockers.length > 0) {
|
|
9521
9895
|
throw new Error(
|
|
9522
9896
|
`--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.`
|
|
@@ -9524,15 +9898,15 @@ function assertAdoptableDirectory(target) {
|
|
|
9524
9898
|
}
|
|
9525
9899
|
}
|
|
9526
9900
|
function mergeGitignore(generatedFile, targetFile) {
|
|
9527
|
-
const existing = (0,
|
|
9901
|
+
const existing = (0, import_node_fs38.readFileSync)(targetFile, "utf8");
|
|
9528
9902
|
const present = new Set(existing.split("\n").map((line) => line.trim()));
|
|
9529
|
-
const additions = (0,
|
|
9903
|
+
const additions = (0, import_node_fs38.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
|
|
9530
9904
|
const trimmed = line.trim();
|
|
9531
9905
|
return trimmed !== "" && !trimmed.startsWith("#") && !present.has(trimmed);
|
|
9532
9906
|
});
|
|
9533
9907
|
if (additions.length === 0) return false;
|
|
9534
9908
|
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
9535
|
-
(0,
|
|
9909
|
+
(0, import_node_fs38.writeFileSync)(
|
|
9536
9910
|
targetFile,
|
|
9537
9911
|
`${existing}${separator}
|
|
9538
9912
|
# Added by mnci (Nx workspace)
|
|
@@ -9542,9 +9916,9 @@ ${additions.join("\n")}
|
|
|
9542
9916
|
return true;
|
|
9543
9917
|
}
|
|
9544
9918
|
function adoptGeneratedWorkspace(generatedRoot, target) {
|
|
9545
|
-
const entries = (0,
|
|
9919
|
+
const entries = (0, import_node_fs38.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
|
|
9546
9920
|
const collisions = entries.filter(
|
|
9547
|
-
(entry) => (0,
|
|
9921
|
+
(entry) => (0, import_node_fs38.existsSync)((0, import_node_path47.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
|
|
9548
9922
|
);
|
|
9549
9923
|
if (collisions.length > 0) {
|
|
9550
9924
|
throw new Error(
|
|
@@ -9553,28 +9927,28 @@ function adoptGeneratedWorkspace(generatedRoot, target) {
|
|
|
9553
9927
|
}
|
|
9554
9928
|
const result = { copied: [], kept: [], mergedGitignore: false };
|
|
9555
9929
|
for (const entry of entries) {
|
|
9556
|
-
const from = (0,
|
|
9557
|
-
const to = (0,
|
|
9558
|
-
if (entry === ".gitignore" && (0,
|
|
9930
|
+
const from = (0, import_node_path47.join)(generatedRoot, entry);
|
|
9931
|
+
const to = (0, import_node_path47.join)(target, entry);
|
|
9932
|
+
if (entry === ".gitignore" && (0, import_node_fs38.existsSync)(to)) {
|
|
9559
9933
|
result.mergedGitignore = mergeGitignore(from, to);
|
|
9560
9934
|
result.kept.push(entry);
|
|
9561
9935
|
continue;
|
|
9562
9936
|
}
|
|
9563
|
-
if ((0,
|
|
9937
|
+
if ((0, import_node_fs38.existsSync)(to)) {
|
|
9564
9938
|
result.kept.push(entry);
|
|
9565
9939
|
continue;
|
|
9566
9940
|
}
|
|
9567
|
-
(0,
|
|
9941
|
+
(0, import_node_fs38.cpSync)(from, to, { recursive: true });
|
|
9568
9942
|
result.copied.push(entry);
|
|
9569
9943
|
}
|
|
9570
|
-
(0,
|
|
9944
|
+
(0, import_node_fs38.rmSync)(generatedRoot, { recursive: true, force: true });
|
|
9571
9945
|
return result;
|
|
9572
9946
|
}
|
|
9573
9947
|
|
|
9574
9948
|
// src/workspace-creation/create-workspace.use-case.ts
|
|
9575
|
-
var
|
|
9949
|
+
var import_node_fs39 = require("fs");
|
|
9576
9950
|
var import_node_os2 = require("os");
|
|
9577
|
-
var
|
|
9951
|
+
var import_node_path48 = require("path");
|
|
9578
9952
|
function nxCloudProviderValue(ci) {
|
|
9579
9953
|
return ci === "azure" ? "azure" : "github";
|
|
9580
9954
|
}
|
|
@@ -9617,9 +9991,9 @@ async function resolveRegistry2(options) {
|
|
|
9617
9991
|
return await promptRegistry();
|
|
9618
9992
|
}
|
|
9619
9993
|
async function runNew(name, options) {
|
|
9620
|
-
const adoptTarget = options.into === void 0 ? void 0 : (0,
|
|
9994
|
+
const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path48.resolve)(process.cwd(), options.into);
|
|
9621
9995
|
if (adoptTarget !== void 0) assertAdoptableDirectory(adoptTarget);
|
|
9622
|
-
const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0,
|
|
9996
|
+
const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path48.basename)(adoptTarget));
|
|
9623
9997
|
assertValidProjectName(workspaceName, "Workspace name");
|
|
9624
9998
|
const scope = options.scope ?? (options.yes ? `@${workspaceName}` : await promptText("npm scope for publishable packages", `@${workspaceName}`));
|
|
9625
9999
|
const registry = await resolveRegistry2(options);
|
|
@@ -9632,8 +10006,8 @@ async function runNew(name, options) {
|
|
|
9632
10006
|
const npmAuth = resolveNpmAuth(options.npmAuth, registry, ci);
|
|
9633
10007
|
const stack = await resolveStack(options);
|
|
9634
10008
|
const nxCloud = options.nxCloud ?? (options.yes ? false : await promptNxCloud());
|
|
9635
|
-
const stagingParent = adoptTarget === void 0 ? process.cwd() : (0,
|
|
9636
|
-
let workspaceRoot = (0,
|
|
10009
|
+
const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs39.mkdtempSync)((0, import_node_path48.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
|
|
10010
|
+
let workspaceRoot = (0, import_node_path48.join)(stagingParent, workspaceName);
|
|
9637
10011
|
try {
|
|
9638
10012
|
logger.step(`Creating Nx workspace '${workspaceName}' (preset: ts)`);
|
|
9639
10013
|
runNpx(
|
|
@@ -9656,7 +10030,7 @@ async function runNew(name, options) {
|
|
|
9656
10030
|
workspaceRoot = adoptTarget;
|
|
9657
10031
|
}
|
|
9658
10032
|
} finally {
|
|
9659
|
-
if (adoptTarget !== void 0) (0,
|
|
10033
|
+
if (adoptTarget !== void 0) (0, import_node_fs39.rmSync)(stagingParent, { recursive: true, force: true });
|
|
9660
10034
|
}
|
|
9661
10035
|
logger.step(
|
|
9662
10036
|
"Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
|
|
@@ -9666,8 +10040,8 @@ async function runNew(name, options) {
|
|
|
9666
10040
|
{ workspaceName, scope, registry, agent, variableGroup, ci, stack, ...npmAuth && { npmAuth } },
|
|
9667
10041
|
logger.detail
|
|
9668
10042
|
);
|
|
9669
|
-
(0,
|
|
9670
|
-
(0,
|
|
10043
|
+
(0, import_node_fs39.rmSync)((0, import_node_path48.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
|
|
10044
|
+
(0, import_node_fs39.rmSync)((0, import_node_path48.join)(workspaceRoot, "package-lock.json"), { force: true });
|
|
9671
10045
|
logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
|
|
9672
10046
|
const installStatus = runShell(
|
|
9673
10047
|
"npm",
|
|
@@ -9704,30 +10078,6 @@ function projectKindHintLines(kinds = PROJECT_KINDS) {
|
|
|
9704
10078
|
return lines;
|
|
9705
10079
|
}
|
|
9706
10080
|
|
|
9707
|
-
// src/workspace-creation/interactive-wizard.use-case.ts
|
|
9708
|
-
var import_node_path47 = require("path");
|
|
9709
|
-
var import_prompts4 = require("@inquirer/prompts");
|
|
9710
|
-
async function runInteractive() {
|
|
9711
|
-
const inWorkspace = fileExists((0, import_node_path47.join)(process.cwd(), "nx.json"));
|
|
9712
|
-
const choiceNew = { name: "Create a new monorepo", value: "new" };
|
|
9713
|
-
const choiceAdd = { name: "Add a project to this workspace", value: "add" };
|
|
9714
|
-
const choiceUpgrade = {
|
|
9715
|
-
name: "Upgrade this workspace (re-apply the latest overlay)",
|
|
9716
|
-
value: "upgrade"
|
|
9717
|
-
};
|
|
9718
|
-
const action = await (0, import_prompts4.select)({
|
|
9719
|
-
message: "What would you like to do?",
|
|
9720
|
-
choices: inWorkspace ? [choiceAdd, choiceUpgrade, choiceNew] : [choiceNew, choiceAdd, choiceUpgrade]
|
|
9721
|
-
});
|
|
9722
|
-
if (action === "new") {
|
|
9723
|
-
await runNew(void 0, {});
|
|
9724
|
-
} else if (action === "upgrade") {
|
|
9725
|
-
runUpgrade(process.cwd(), {});
|
|
9726
|
-
} else {
|
|
9727
|
-
await runAdd(void 0, void 0, {});
|
|
9728
|
-
}
|
|
9729
|
-
}
|
|
9730
|
-
|
|
9731
10081
|
// src/main.ts
|
|
9732
10082
|
function buildProgram(cliVersion) {
|
|
9733
10083
|
const program = new import_commander.Command();
|
|
@@ -9760,7 +10110,7 @@ function buildProgram(cliVersion) {
|
|
|
9760
10110
|
).option("--json", "print the findings as one JSON document, each with its remedy").action((options) => {
|
|
9761
10111
|
runDoctor(process.cwd(), options);
|
|
9762
10112
|
});
|
|
9763
|
-
program.command("adopt").description("Read an existing repository and report what bringing it under mnci would involve: blockers, warnings and the step that clears each. Read-only unless a step flag is given; exits non-zero when something blocks").option("--json", "print the report as one JSON document").option("--tags", "create, locally, the baseline tag each project whose release tags are stranded under an old name needs; nothing is pushed").option("--toolchain", "retire old formatter and registry tooling, align the Nx family to one version and run npm audit fix until the audit gate passes; needs a clean git tree and leaves the changes uncommitted").option("--nx <version>", "the Nx version --toolchain aligns to (default: the newest published in the major already in use)").option("--kinds", "record each project's mnci kind as a type:<kind> tag (npm, Python, Go, Flutter and C# projects); a project where two kinds fit is listed with a guess and not tagged; needs a clean git tree").option("--kind <dir=kind>", "the kind you choose for one project, for --kinds (repeatable)", (value, previous = []) => [...previous, value]).option("--overlay", "apply the mnci overlay (release config, pipeline, npmrc, commitlint, scripts) with the flags mnci upgrade takes; the pipeline keeps the steps mnci does not recognise in its slots; needs a clean git tree and an nx.json").option("--scope <scope>", "npm scope for publishable packages (--overlay)").option("--registry <kind>", "azure-artifacts | npm (--overlay)").option("--organization <name>", "Azure DevOps organization (--overlay)").option("--project <name>", "Azure DevOps project (--overlay)").option("--artifacts-feed <name>", "Azure Artifacts feed (--overlay)").option("--agent <pool>", "CI build agent (--overlay)").option("--variable-group <name>", "Azure DevOps variable group holding the npm PAT (--overlay)").option("--npm-auth <mode>", "pat (default) | build-identity (--overlay, --ci azure only)").option("--ci <provider>", "azure | github | both (--overlay; default: what the repository already has)").option("--test-runner <runner>", "jest | vitest (--overlay; default: what the repository already declares)").action((options) => {
|
|
10113
|
+
program.command("adopt").description("Read an existing repository and report what bringing it under mnci would involve: blockers, warnings and the step that clears each. Read-only unless a step flag is given; exits non-zero when something blocks").option("--json", "print the report as one JSON document").option("--tags", "create, locally, the baseline tag each project whose release tags are stranded under an old name needs; nothing is pushed").option("--toolchain", "retire old formatter and registry tooling, align the Nx family to one version and run npm audit fix until the audit gate passes; needs a clean git tree and leaves the changes uncommitted").option("--nx <version>", "the Nx version --toolchain aligns to (default: the newest published in the major already in use)").option("--dependencies", "move the root package.json runtime dependencies into the projects that import them (same range), then reinstall; a package no project imports stays and is listed; needs a clean git tree").option("--kinds", "record each project's mnci kind as a type:<kind> tag (npm, Python, Go, Flutter and C# projects); a project where two kinds fit is listed with a guess and not tagged; needs a clean git tree").option("--kind <dir=kind>", "the kind you choose for one project, for --kinds (repeatable)", (value, previous = []) => [...previous, value]).option("--overlay", "apply the mnci overlay (release config, pipeline, npmrc, commitlint, scripts) with the flags mnci upgrade takes; the pipeline keeps the steps mnci does not recognise in its slots; needs a clean git tree and an nx.json").option("--scope <scope>", "npm scope for publishable packages (--overlay)").option("--registry <kind>", "azure-artifacts | npm (--overlay)").option("--organization <name>", "Azure DevOps organization (--overlay)").option("--project <name>", "Azure DevOps project (--overlay)").option("--artifacts-feed <name>", "Azure Artifacts feed (--overlay)").option("--agent <pool>", "CI build agent (--overlay)").option("--variable-group <name>", "Azure DevOps variable group holding the npm PAT (--overlay)").option("--npm-auth <mode>", "pat (default) | build-identity (--overlay, --ci azure only)").option("--ci <provider>", "azure | github | both (--overlay; default: what the repository already has)").option("--test-runner <runner>", "jest | vitest (--overlay; default: what the repository already declares)").action((options) => {
|
|
9764
10114
|
runAdopt(process.cwd(), options);
|
|
9765
10115
|
});
|
|
9766
10116
|
program.command("ci").addArgument(new import_commander.Argument("<phase>", "which phase of the pipeline to run").choices(CI_PHASES)).description(
|
|
@@ -9829,7 +10179,7 @@ function buildProgram(cliVersion) {
|
|
|
9829
10179
|
runInfo(process.cwd(), cliVersion, options);
|
|
9830
10180
|
});
|
|
9831
10181
|
program.action(async () => {
|
|
9832
|
-
await runInteractive();
|
|
10182
|
+
await runInteractive(program);
|
|
9833
10183
|
});
|
|
9834
10184
|
return program;
|
|
9835
10185
|
}
|