@mnci/cli 4.45.0 → 4.47.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 +21 -0
- package/dist/cli.js +472 -83
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8417,7 +8417,7 @@ function checkReleaseTagsResolve(workspaceRoot, nxJson) {
|
|
|
8417
8417
|
check: "release tags resolve under each project's current name",
|
|
8418
8418
|
ok: stranded.length === 0,
|
|
8419
8419
|
detail: `${stranded.map((entry) => `${entry.project} has only ${entry.oldTag}`).join("; ")} - nx release finds no tag under the new name and would release from the disk version, a downgrade`,
|
|
8420
|
-
remedy: `
|
|
8420
|
+
remedy: `run \`mnci adopt --tags\` to create the baseline tag for each (${stranded.map((entry) => entry.newTag).join(", ")}) on the commit of its old tag, then push the tags`
|
|
8421
8421
|
};
|
|
8422
8422
|
}
|
|
8423
8423
|
function collectFindings(workspaceRoot) {
|
|
@@ -8485,13 +8485,341 @@ 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
|
+
var import_node_fs31 = require("fs");
|
|
8490
|
+
var import_node_path36 = require("path");
|
|
8491
|
+
|
|
8492
|
+
// src/repository-adoption/clean-working-tree.validator.ts
|
|
8493
|
+
function requireCleanWorkingTree(repositoryRoot, capture = runCapture) {
|
|
8494
|
+
if (capture("git", ["rev-parse", "--is-inside-work-tree"], repositoryRoot).status !== 0) {
|
|
8495
|
+
throw new Error("This directory is not a git repository. Every adoption step is one diff to review, so it needs git.");
|
|
8496
|
+
}
|
|
8497
|
+
if (capture("git", ["status", "--porcelain"], repositoryRoot).stdout.trim() !== "") {
|
|
8498
|
+
throw new Error("The working tree has uncommitted changes. Commit or stash them, so this step is one diff you can review or discard.");
|
|
8499
|
+
}
|
|
8500
|
+
}
|
|
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
|
|
8489
8650
|
var import_node_fs32 = require("fs");
|
|
8490
8651
|
var import_node_path37 = require("path");
|
|
8491
8652
|
|
|
8653
|
+
// src/repository-adoption/infer-project-kind.algorithm.ts
|
|
8654
|
+
var SERVER_FRAMEWORKS = ["express", "koa", "fastify", "@nestjs/core"];
|
|
8655
|
+
function declares(manifest, name) {
|
|
8656
|
+
return ["dependencies", "devDependencies", "peerDependencies"].some((key) => {
|
|
8657
|
+
const block = manifest[key];
|
|
8658
|
+
return typeof block === "object" && block !== null && Object.keys(block).includes(name);
|
|
8659
|
+
});
|
|
8660
|
+
}
|
|
8661
|
+
function inferNpm(evidence) {
|
|
8662
|
+
const { manifest, files } = evidence;
|
|
8663
|
+
const engines = manifest.engines;
|
|
8664
|
+
if (engines?.vscode !== void 0) {
|
|
8665
|
+
return { kind: "vscode-extension", certainty: "certain", reason: "engines.vscode is set" };
|
|
8666
|
+
}
|
|
8667
|
+
if (files.includes("host.json") || declares(manifest, "@azure/functions")) {
|
|
8668
|
+
return { kind: "node-function-app", certainty: "certain", reason: "an Azure Functions host.json or @azure/functions" };
|
|
8669
|
+
}
|
|
8670
|
+
const privatePackage = manifest.private === true;
|
|
8671
|
+
if (declares(manifest, "react") || declares(manifest, "react-dom")) {
|
|
8672
|
+
if (files.includes("index.html")) {
|
|
8673
|
+
return { kind: "react-app", certainty: "certain", reason: "React with an index.html entry" };
|
|
8674
|
+
}
|
|
8675
|
+
return { kind: privatePackage ? "react-internal-lib" : "react-lib", certainty: "certain", reason: `React without an index.html, ${privatePackage ? "private" : "publishable"}` };
|
|
8676
|
+
}
|
|
8677
|
+
const framework = SERVER_FRAMEWORKS.find((name) => declares(manifest, name));
|
|
8678
|
+
if (framework !== void 0) {
|
|
8679
|
+
return { kind: "node-app", certainty: "certain", reason: `a ${framework} server` };
|
|
8680
|
+
}
|
|
8681
|
+
const scripts = manifest.scripts;
|
|
8682
|
+
if (manifest.bin !== void 0 || scripts?.start !== void 0) {
|
|
8683
|
+
return { kind: "node-app", certainty: "guess", reason: "it has a bin or a start script, but no known server framework" };
|
|
8684
|
+
}
|
|
8685
|
+
if (privatePackage) {
|
|
8686
|
+
return { kind: "internal-lib", certainty: "certain", reason: "private, with no entry point of its own" };
|
|
8687
|
+
}
|
|
8688
|
+
const hasEntry = ["main", "module", "exports"].some((key) => manifest[key] !== void 0);
|
|
8689
|
+
return { kind: "npm-lib", certainty: hasEntry ? "certain" : "guess", reason: hasEntry ? "publishable, with a library entry" : "publishable, but no main, module or exports" };
|
|
8690
|
+
}
|
|
8691
|
+
function inferPython(evidence) {
|
|
8692
|
+
const { files, text } = evidence;
|
|
8693
|
+
if (files.includes("function_app.py") || files.includes("host.json")) {
|
|
8694
|
+
return { kind: "python-function-app", certainty: "certain", reason: "an Azure Functions function_app.py or host.json" };
|
|
8695
|
+
}
|
|
8696
|
+
if (text.includes("[project.scripts]") || files.includes("__main__.py")) {
|
|
8697
|
+
return { kind: "python-app", certainty: "guess", reason: "it declares a script or a __main__.py, which a library can too" };
|
|
8698
|
+
}
|
|
8699
|
+
if (text.includes("Private :: Do Not Upload")) {
|
|
8700
|
+
return { kind: "python-internal-lib", certainty: "certain", reason: "classified Private :: Do Not Upload" };
|
|
8701
|
+
}
|
|
8702
|
+
return { kind: "python-lib", certainty: "guess", reason: "a library, but internal or published is a choice only you can make" };
|
|
8703
|
+
}
|
|
8704
|
+
function inferFlutter(evidence) {
|
|
8705
|
+
if (evidence.hasProgram) {
|
|
8706
|
+
return { kind: "flutter-app", certainty: "certain", reason: "lib/main.dart is the entry point" };
|
|
8707
|
+
}
|
|
8708
|
+
const internal = /^publish_to:\s*['"]?none['"]?\s*$/m.test(evidence.text);
|
|
8709
|
+
return { kind: internal ? "flutter-internal-lib" : "flutter-lib", certainty: "certain", reason: internal ? "publish_to: none" : "publishable, with no entry point" };
|
|
8710
|
+
}
|
|
8711
|
+
function inferCsharp(evidence) {
|
|
8712
|
+
const { text } = evidence;
|
|
8713
|
+
if (/<OutputType>\s*Exe\s*<\/OutputType>/i.test(text)) {
|
|
8714
|
+
const functions = /Microsoft\.Azure\.Functions|Azure\.Functions\.Sdk/.test(text);
|
|
8715
|
+
return { kind: functions ? "csharp-function-app" : "csharp-app", certainty: "certain", reason: functions ? "an executable using the Azure Functions worker" : "an executable" };
|
|
8716
|
+
}
|
|
8717
|
+
if (/<IsPackable>\s*false\s*<\/IsPackable>/i.test(text)) {
|
|
8718
|
+
return { kind: "csharp-internal-lib", certainty: "certain", reason: "IsPackable is false" };
|
|
8719
|
+
}
|
|
8720
|
+
return { kind: "csharp-lib", certainty: "guess", reason: "a library, but internal or published is a choice only you can make" };
|
|
8721
|
+
}
|
|
8722
|
+
function inferGo(evidence) {
|
|
8723
|
+
if (evidence.hasProgram) {
|
|
8724
|
+
const functions = evidence.files.includes("host.json");
|
|
8725
|
+
return { kind: functions ? "go-function-app" : "go-app", certainty: "certain", reason: functions ? "package main with an Azure Functions host.json" : "a package main" };
|
|
8726
|
+
}
|
|
8727
|
+
return { kind: "go-lib", certainty: "guess", reason: "a library, but internal or published is a choice only you can make" };
|
|
8728
|
+
}
|
|
8729
|
+
function inferProjectKind(evidence) {
|
|
8730
|
+
switch (evidence.ecosystem) {
|
|
8731
|
+
case "npm": {
|
|
8732
|
+
return inferNpm(evidence);
|
|
8733
|
+
}
|
|
8734
|
+
case "pip": {
|
|
8735
|
+
return inferPython(evidence);
|
|
8736
|
+
}
|
|
8737
|
+
case "pub": {
|
|
8738
|
+
return inferFlutter(evidence);
|
|
8739
|
+
}
|
|
8740
|
+
case "nuget": {
|
|
8741
|
+
return inferCsharp(evidence);
|
|
8742
|
+
}
|
|
8743
|
+
case "go": {
|
|
8744
|
+
return inferGo(evidence);
|
|
8745
|
+
}
|
|
8746
|
+
}
|
|
8747
|
+
}
|
|
8748
|
+
|
|
8749
|
+
// src/repository-adoption/adopt-kinds.use-case.ts
|
|
8750
|
+
var TYPE_TAG = "type:";
|
|
8751
|
+
function readEvidence(repositoryRoot, location) {
|
|
8752
|
+
const directory = (0, import_node_path37.join)(repositoryRoot, location.dir);
|
|
8753
|
+
const files = (0, import_node_fs32.readdirSync)(directory);
|
|
8754
|
+
const manifest = location.ecosystem === "npm" ? readJson(location.manifestPath) : {};
|
|
8755
|
+
const text = location.ecosystem === "npm" ? "" : (0, import_node_fs32.readFileSync)(location.manifestPath, "utf8");
|
|
8756
|
+
let hasProgram = false;
|
|
8757
|
+
if (location.ecosystem === "go") {
|
|
8758
|
+
hasProgram = files.filter((file) => file.endsWith(".go")).some((file) => /^package main\b/m.test((0, import_node_fs32.readFileSync)((0, import_node_path37.join)(directory, file), "utf8")));
|
|
8759
|
+
} else if (location.ecosystem === "pub") {
|
|
8760
|
+
hasProgram = (0, import_node_fs32.existsSync)((0, import_node_path37.join)(directory, "lib", "main.dart"));
|
|
8761
|
+
}
|
|
8762
|
+
return { ecosystem: location.ecosystem, manifest, text, files, hasProgram };
|
|
8763
|
+
}
|
|
8764
|
+
function existingKind(directory) {
|
|
8765
|
+
const sources = [];
|
|
8766
|
+
if (fileExists((0, import_node_path37.join)(directory, "project.json"))) {
|
|
8767
|
+
sources.push(readJson((0, import_node_path37.join)(directory, "project.json")).tags ?? []);
|
|
8768
|
+
}
|
|
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 ?? []);
|
|
8771
|
+
}
|
|
8772
|
+
return sources.flat().find((tag) => tag.startsWith(TYPE_TAG))?.slice(TYPE_TAG.length);
|
|
8773
|
+
}
|
|
8774
|
+
function writeKindTag(directory, name, kind) {
|
|
8775
|
+
const tag = `${TYPE_TAG}${kind}`;
|
|
8776
|
+
const projectJson = (0, import_node_path37.join)(directory, "project.json");
|
|
8777
|
+
const packageJson = (0, import_node_path37.join)(directory, "package.json");
|
|
8778
|
+
if (fileExists(projectJson)) {
|
|
8779
|
+
const project = readJson(projectJson);
|
|
8780
|
+
writeFileEnsured(projectJson, toJson({ ...project, tags: [...project.tags ?? [], tag] }));
|
|
8781
|
+
} else if (fileExists(packageJson)) {
|
|
8782
|
+
const manifest = readJson(packageJson);
|
|
8783
|
+
writeFileEnsured(packageJson, toJson({ ...manifest, nx: { ...manifest.nx, tags: [...manifest.nx?.tags ?? [], tag] } }));
|
|
8784
|
+
} else {
|
|
8785
|
+
writeFileEnsured(projectJson, toJson({ name, tags: [tag] }));
|
|
8786
|
+
}
|
|
8787
|
+
}
|
|
8788
|
+
function adoptKinds(repositoryRoot, overrides = {}, dependencies = {}) {
|
|
8789
|
+
requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
|
|
8790
|
+
const locations = locateProjects(repositoryRoot);
|
|
8791
|
+
const known = new Set(locations.map((location) => location.dir));
|
|
8792
|
+
for (const [dir, kind] of Object.entries(overrides)) {
|
|
8793
|
+
if (!known.has(dir)) {
|
|
8794
|
+
throw new Error(`--kind ${dir}=${kind}: no project at ${dir}. Projects found: ${[...known].join(", ")}`);
|
|
8795
|
+
}
|
|
8796
|
+
if (!PROJECT_KINDS.includes(kind)) {
|
|
8797
|
+
throw new Error(`--kind ${dir}=${kind}: ${kind} is not a kind. Kinds: ${PROJECT_KINDS.join(", ")}`);
|
|
8798
|
+
}
|
|
8799
|
+
}
|
|
8800
|
+
return locations.map((location) => {
|
|
8801
|
+
const directory = (0, import_node_path37.join)(repositoryRoot, location.dir);
|
|
8802
|
+
const present = existingKind(directory);
|
|
8803
|
+
if (present !== void 0) {
|
|
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" };
|
|
8805
|
+
}
|
|
8806
|
+
const chosen = overrides[location.dir];
|
|
8807
|
+
const proposal = chosen === void 0 ? inferProjectKind(readEvidence(repositoryRoot, location)) : { kind: chosen, certainty: "certain", reason: "named with --kind" };
|
|
8808
|
+
if (proposal.certainty === "guess") {
|
|
8809
|
+
return { dir: location.dir, outcome: "undecided", kind: proposal.kind, reason: proposal.reason };
|
|
8810
|
+
}
|
|
8811
|
+
writeKindTag(directory, location.name, proposal.kind);
|
|
8812
|
+
return { dir: location.dir, outcome: "tagged", kind: proposal.kind, reason: proposal.reason };
|
|
8813
|
+
}).toSorted((a, b) => a.dir.localeCompare(b.dir));
|
|
8814
|
+
}
|
|
8815
|
+
|
|
8816
|
+
// src/repository-adoption/adopt-overlay.use-case.ts
|
|
8817
|
+
var import_node_fs34 = require("fs");
|
|
8818
|
+
var import_node_path39 = require("path");
|
|
8819
|
+
|
|
8492
8820
|
// src/workspace-upgrade/upgrade-workspace.use-case.ts
|
|
8493
|
-
var
|
|
8494
|
-
var
|
|
8821
|
+
var import_node_fs33 = require("fs");
|
|
8822
|
+
var import_node_path38 = require("path");
|
|
8495
8823
|
function resolveRegistry(options, persisted) {
|
|
8496
8824
|
if (options.registry === "azure-artifacts" || options.organization && options.artifactsFeed) {
|
|
8497
8825
|
const persistedAzure = persisted?.kind === "azure-artifacts" ? persisted : void 0;
|
|
@@ -8520,7 +8848,7 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
|
|
|
8520
8848
|
return persisted.workspaceName;
|
|
8521
8849
|
}
|
|
8522
8850
|
try {
|
|
8523
|
-
const existing = (0,
|
|
8851
|
+
const existing = (0, import_node_fs33.readdirSync)(workspaceRoot).find(
|
|
8524
8852
|
(file) => file.endsWith(".code-workspace") && file !== "undefined.code-workspace"
|
|
8525
8853
|
);
|
|
8526
8854
|
if (existing) {
|
|
@@ -8528,11 +8856,11 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
|
|
|
8528
8856
|
}
|
|
8529
8857
|
} catch {
|
|
8530
8858
|
}
|
|
8531
|
-
return (0,
|
|
8859
|
+
return (0, import_node_path38.basename)(workspaceRoot);
|
|
8532
8860
|
}
|
|
8533
8861
|
function detectNpmAuth(workspaceRoot) {
|
|
8534
8862
|
try {
|
|
8535
|
-
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;
|
|
8536
8864
|
} catch {
|
|
8537
8865
|
return void 0;
|
|
8538
8866
|
}
|
|
@@ -8579,7 +8907,7 @@ function resolveOverlayOptions(workspaceRoot, options, persisted) {
|
|
|
8579
8907
|
};
|
|
8580
8908
|
}
|
|
8581
8909
|
function runUpgrade(workspaceRoot, options) {
|
|
8582
|
-
if (!fileExists((0,
|
|
8910
|
+
if (!fileExists((0, import_node_path38.join)(workspaceRoot, "nx.json"))) {
|
|
8583
8911
|
throw new Error(
|
|
8584
8912
|
`No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
|
|
8585
8913
|
);
|
|
@@ -8653,30 +8981,20 @@ function runUpgrade(workspaceRoot, options) {
|
|
|
8653
8981
|
"Formatting the workspace (eslint --fix) \u2014 the slowest step, minutes on a large workspace"
|
|
8654
8982
|
);
|
|
8655
8983
|
runFormatter(workspaceRoot);
|
|
8656
|
-
const nxJson = readJson((0,
|
|
8984
|
+
const nxJson = readJson((0, import_node_path38.join)(workspaceRoot, "nx.json"));
|
|
8657
8985
|
const stranded = locateStrandedReleaseTags(workspaceRoot, nxJson.release?.releaseTag?.pattern);
|
|
8658
8986
|
for (const entry of stranded) {
|
|
8659
|
-
logger.warn(`${entry.project} has release tags only under ${entry.oldTag}: nx release would restart it from its disk version. Run \`
|
|
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).`);
|
|
8660
8988
|
}
|
|
8661
8989
|
logger.success("Done. Review the changes with `git diff` before committing.");
|
|
8662
8990
|
logger.info("Run `npm install` and commit package-lock.json too: the overlay may have added or moved a dependency, and CI installs with `npm ci`, which refuses a stale lockfile.");
|
|
8663
8991
|
}
|
|
8664
8992
|
|
|
8665
|
-
// src/repository-adoption/clean-working-tree.validator.ts
|
|
8666
|
-
function requireCleanWorkingTree(repositoryRoot, capture = runCapture) {
|
|
8667
|
-
if (capture("git", ["rev-parse", "--is-inside-work-tree"], repositoryRoot).status !== 0) {
|
|
8668
|
-
throw new Error("This directory is not a git repository. Every adoption step is one diff to review, so it needs git.");
|
|
8669
|
-
}
|
|
8670
|
-
if (capture("git", ["status", "--porcelain"], repositoryRoot).stdout.trim() !== "") {
|
|
8671
|
-
throw new Error("The working tree has uncommitted changes. Commit or stash them, so this step is one diff you can review or discard.");
|
|
8672
|
-
}
|
|
8673
|
-
}
|
|
8674
|
-
|
|
8675
8993
|
// src/repository-adoption/adopt-overlay.use-case.ts
|
|
8676
8994
|
function detectCiProvider(repositoryRoot) {
|
|
8677
|
-
const azure = (0,
|
|
8678
|
-
const workflows = (0,
|
|
8679
|
-
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));
|
|
8680
8998
|
if (azure && github) {
|
|
8681
8999
|
return "both";
|
|
8682
9000
|
}
|
|
@@ -8686,7 +9004,7 @@ function detectCiProvider(repositoryRoot) {
|
|
|
8686
9004
|
return github ? "github" : void 0;
|
|
8687
9005
|
}
|
|
8688
9006
|
function detectTestRunner(repositoryRoot) {
|
|
8689
|
-
const manifestPath = (0,
|
|
9007
|
+
const manifestPath = (0, import_node_path39.join)(repositoryRoot, "package.json");
|
|
8690
9008
|
if (!fileExists(manifestPath)) {
|
|
8691
9009
|
return void 0;
|
|
8692
9010
|
}
|
|
@@ -8699,7 +9017,7 @@ function detectTestRunner(repositoryRoot) {
|
|
|
8699
9017
|
}
|
|
8700
9018
|
function adoptOverlay(repositoryRoot, options, dependencies = {}) {
|
|
8701
9019
|
requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
|
|
8702
|
-
if (!fileExists((0,
|
|
9020
|
+
if (!fileExists((0, import_node_path39.join)(repositoryRoot, "nx.json"))) {
|
|
8703
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.");
|
|
8704
9022
|
}
|
|
8705
9023
|
const persisted = readMnciConfig(repositoryRoot);
|
|
@@ -8712,7 +9030,7 @@ function adoptOverlay(repositoryRoot, options, dependencies = {}) {
|
|
|
8712
9030
|
}
|
|
8713
9031
|
|
|
8714
9032
|
// src/repository-adoption/adopt-toolchain.use-case.ts
|
|
8715
|
-
var
|
|
9033
|
+
var import_node_path41 = require("path");
|
|
8716
9034
|
|
|
8717
9035
|
// src/repository-adoption/align-nx-family.algorithm.ts
|
|
8718
9036
|
var VERSION = /^[\^~]?(\d+)\.(\d+)\.(\d+)$/;
|
|
@@ -8750,24 +9068,24 @@ function alignNxFamily(dependencies, version) {
|
|
|
8750
9068
|
}
|
|
8751
9069
|
|
|
8752
9070
|
// src/repository-adoption/retire-tooling.use-case.ts
|
|
8753
|
-
var
|
|
8754
|
-
var
|
|
9071
|
+
var import_node_fs35 = require("fs");
|
|
9072
|
+
var import_node_path40 = require("path");
|
|
8755
9073
|
var RETIRED_COMMAND = /\b(?:prettier|oxfmt|oxlint)\b/;
|
|
8756
9074
|
function retireTooling(repositoryRoot) {
|
|
8757
9075
|
const removed = [];
|
|
8758
9076
|
for (const file of RETIRED_FORMATTER_FILES) {
|
|
8759
|
-
if (!fileExists((0,
|
|
9077
|
+
if (!fileExists((0, import_node_path40.join)(repositoryRoot, file))) {
|
|
8760
9078
|
continue;
|
|
8761
9079
|
}
|
|
8762
|
-
(0,
|
|
9080
|
+
(0, import_node_fs35.rmSync)((0, import_node_path40.join)(repositoryRoot, file), { force: true });
|
|
8763
9081
|
removed.push(file);
|
|
8764
9082
|
}
|
|
8765
|
-
const hadRegistry = fileExists((0,
|
|
9083
|
+
const hadRegistry = fileExists((0, import_node_path40.join)(repositoryRoot, ".verdaccio"));
|
|
8766
9084
|
removeLocalRegistryScaffolding(repositoryRoot);
|
|
8767
9085
|
if (hadRegistry) {
|
|
8768
9086
|
removed.push(".verdaccio, its dependency and the local-registry target");
|
|
8769
9087
|
}
|
|
8770
|
-
const manifestPath = (0,
|
|
9088
|
+
const manifestPath = (0, import_node_path40.join)(repositoryRoot, "package.json");
|
|
8771
9089
|
if (!fileExists(manifestPath)) {
|
|
8772
9090
|
return removed;
|
|
8773
9091
|
}
|
|
@@ -8814,7 +9132,7 @@ function adoptToolchain(repositoryRoot, options = {}, dependencies = {}) {
|
|
|
8814
9132
|
console.log(message);
|
|
8815
9133
|
});
|
|
8816
9134
|
requireCleanWorkingTree(repositoryRoot, capture);
|
|
8817
|
-
const manifestPath = (0,
|
|
9135
|
+
const manifestPath = (0, import_node_path41.join)(repositoryRoot, "package.json");
|
|
8818
9136
|
if (!fileExists(manifestPath)) {
|
|
8819
9137
|
throw new Error("No package.json at the repository root.");
|
|
8820
9138
|
}
|
|
@@ -8864,8 +9182,8 @@ function createBaselineTags(repositoryRoot, stranded) {
|
|
|
8864
9182
|
}
|
|
8865
9183
|
|
|
8866
9184
|
// src/repository-adoption/inspect-repository.use-case.ts
|
|
8867
|
-
var
|
|
8868
|
-
var
|
|
9185
|
+
var import_node_fs36 = require("fs");
|
|
9186
|
+
var import_node_path42 = require("path");
|
|
8869
9187
|
var LOCKFILES = [
|
|
8870
9188
|
["package-lock.json", "npm"],
|
|
8871
9189
|
["pnpm-lock.yaml", "pnpm"],
|
|
@@ -8886,25 +9204,25 @@ function inspectRepository(repositoryRoot) {
|
|
|
8886
9204
|
const isGitRepo = inside.status === 0 && inside.stdout.trim() === "true";
|
|
8887
9205
|
const dirty = isGitRepo && runCapture("git", ["status", "--porcelain"], repositoryRoot).stdout.trim() !== "";
|
|
8888
9206
|
const tags = isGitRepo ? runCapture("git", ["tag", "--list"], repositoryRoot).stdout.split(/\r?\n/).filter(Boolean) : [];
|
|
8889
|
-
const manifestPath = (0,
|
|
9207
|
+
const manifestPath = (0, import_node_path42.join)(repositoryRoot, "package.json");
|
|
8890
9208
|
const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
|
|
8891
|
-
const nxJsonPath = (0,
|
|
9209
|
+
const nxJsonPath = (0, import_node_path42.join)(repositoryRoot, "nx.json");
|
|
8892
9210
|
const nxJson = fileExists(nxJsonPath) ? readJson(nxJsonPath) : {};
|
|
8893
9211
|
const ci = [];
|
|
8894
9212
|
const pipelines = [];
|
|
8895
|
-
if ((0,
|
|
9213
|
+
if ((0, import_node_fs36.existsSync)((0, import_node_path42.join)(repositoryRoot, "azure-pipelines.yml"))) {
|
|
8896
9214
|
ci.push("azure");
|
|
8897
|
-
pipelines.push((0,
|
|
9215
|
+
pipelines.push((0, import_node_path42.join)(repositoryRoot, "azure-pipelines.yml"));
|
|
8898
9216
|
}
|
|
8899
|
-
const workflows = (0,
|
|
8900
|
-
if ((0,
|
|
8901
|
-
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));
|
|
8902
9220
|
if (files.length > 0) {
|
|
8903
9221
|
ci.push("github");
|
|
8904
|
-
pipelines.push(...files.map((file) => (0,
|
|
9222
|
+
pipelines.push(...files.map((file) => (0, import_node_path42.join)(workflows, file)));
|
|
8905
9223
|
}
|
|
8906
9224
|
}
|
|
8907
|
-
const rootFiles = (0,
|
|
9225
|
+
const rootFiles = (0, import_node_fs36.readdirSync)(repositoryRoot);
|
|
8908
9226
|
return {
|
|
8909
9227
|
isGitRepo,
|
|
8910
9228
|
dirty,
|
|
@@ -8915,7 +9233,7 @@ function inspectRepository(repositoryRoot) {
|
|
|
8915
9233
|
tagCount: tags.length,
|
|
8916
9234
|
strandedTags: isGitRepo ? locateStrandedReleaseTags(repositoryRoot, nxJson.release?.releaseTag?.pattern) : [],
|
|
8917
9235
|
ci,
|
|
8918
|
-
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"))),
|
|
8919
9237
|
retiredTooling: [.../* @__PURE__ */ new Set([...rootFiles.filter((file) => RETIRED_FILE_PATTERN.test(file) || file === ".verdaccio"), ...retiredNamesIn(manifest)])],
|
|
8920
9238
|
personalFiles: PERSONAL_FILES.filter((file) => rootFiles.includes(file))
|
|
8921
9239
|
};
|
|
@@ -8972,6 +9290,14 @@ function reportAdoption(repositoryRoot) {
|
|
|
8972
9290
|
}
|
|
8973
9291
|
function runAdopt(repositoryRoot, options = {}) {
|
|
8974
9292
|
const report = reportAdoption(repositoryRoot);
|
|
9293
|
+
if (options.dependencies === true) {
|
|
9294
|
+
runDependencies(repositoryRoot);
|
|
9295
|
+
return;
|
|
9296
|
+
}
|
|
9297
|
+
if (options.kinds === true) {
|
|
9298
|
+
runKinds2(repositoryRoot, options);
|
|
9299
|
+
return;
|
|
9300
|
+
}
|
|
8975
9301
|
if (options.overlay === true) {
|
|
8976
9302
|
runOverlay(repositoryRoot, options);
|
|
8977
9303
|
return;
|
|
@@ -9071,6 +9397,67 @@ function runOverlay(repositoryRoot, options) {
|
|
|
9071
9397
|
process.exitCode = 1;
|
|
9072
9398
|
}
|
|
9073
9399
|
}
|
|
9400
|
+
function parseKindChoices(choices) {
|
|
9401
|
+
const parsed = {};
|
|
9402
|
+
for (const choice of choices) {
|
|
9403
|
+
const [dir, kind] = choice.split("=", 2);
|
|
9404
|
+
if (dir === "" || kind === void 0 || kind === "") {
|
|
9405
|
+
throw new Error(`--kind ${choice}: write it as <directory>=<kind>, for example --kind apps/api=node-app`);
|
|
9406
|
+
}
|
|
9407
|
+
parsed[dir] = kind;
|
|
9408
|
+
}
|
|
9409
|
+
return parsed;
|
|
9410
|
+
}
|
|
9411
|
+
function runKinds2(repositoryRoot, options) {
|
|
9412
|
+
try {
|
|
9413
|
+
const outcomes = adoptKinds(repositoryRoot, parseKindChoices(options.kind ?? []));
|
|
9414
|
+
for (const outcome of outcomes) {
|
|
9415
|
+
const line = `${outcome.dir.padEnd(30)} ${outcome.kind} (${outcome.reason})`;
|
|
9416
|
+
if (outcome.outcome === "tagged") {
|
|
9417
|
+
logger.success(`tagged ${line}`);
|
|
9418
|
+
} else if (outcome.outcome === "already") {
|
|
9419
|
+
logger.info(`kept ${line}`);
|
|
9420
|
+
} else {
|
|
9421
|
+
logger.warn(`guessed ${line}`);
|
|
9422
|
+
}
|
|
9423
|
+
}
|
|
9424
|
+
const undecided = outcomes.filter((outcome) => outcome.outcome === "undecided");
|
|
9425
|
+
if (undecided.length > 0) {
|
|
9426
|
+
logger.error(`${undecided.length} project(s) need you to choose. Run again with ${undecided.map((outcome) => `--kind ${outcome.dir}=${outcome.kind}`).join(" ")} to accept the guesses, or name another kind.`);
|
|
9427
|
+
process.exitCode = 1;
|
|
9428
|
+
} else {
|
|
9429
|
+
logger.success("Every project has a kind. Review with `git diff` and commit.");
|
|
9430
|
+
}
|
|
9431
|
+
} catch (error) {
|
|
9432
|
+
logger.error(error instanceof Error ? error.message : String(error));
|
|
9433
|
+
process.exitCode = 1;
|
|
9434
|
+
}
|
|
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
|
+
}
|
|
9074
9461
|
|
|
9075
9462
|
// src/command-catalog/command-groups.config.ts
|
|
9076
9463
|
var COMMAND_GROUPS = {
|
|
@@ -9143,13 +9530,15 @@ function runCommands(program, options) {
|
|
|
9143
9530
|
}
|
|
9144
9531
|
|
|
9145
9532
|
// src/workspace-info/list-projects.use-case.ts
|
|
9146
|
-
var
|
|
9533
|
+
var import_node_path43 = require("path");
|
|
9147
9534
|
var TYPE_TAG_PREFIX = "type:";
|
|
9148
9535
|
function listProjects(workspaceRoot) {
|
|
9149
9536
|
return locateProjects(workspaceRoot).map((location) => {
|
|
9150
|
-
const projectJsonPath = (0,
|
|
9537
|
+
const projectJsonPath = (0, import_node_path43.join)(workspaceRoot, location.dir, "project.json");
|
|
9151
9538
|
const project = fileExists(projectJsonPath) ? readJson(projectJsonPath) : {};
|
|
9152
|
-
const
|
|
9539
|
+
const manifestPath = (0, import_node_path43.join)(workspaceRoot, location.dir, "package.json");
|
|
9540
|
+
const manifestTags = fileExists(manifestPath) ? readJson(manifestPath).nx?.tags ?? [] : [];
|
|
9541
|
+
const typeTag = [...project.tags ?? [], ...manifestTags].find((tag) => tag.startsWith(TYPE_TAG_PREFIX));
|
|
9153
9542
|
return {
|
|
9154
9543
|
name: location.name,
|
|
9155
9544
|
dir: location.dir,
|
|
@@ -9160,7 +9549,7 @@ function listProjects(workspaceRoot) {
|
|
|
9160
9549
|
}).toSorted((a, b) => a.dir.localeCompare(b.dir));
|
|
9161
9550
|
}
|
|
9162
9551
|
function runProjects(workspaceRoot, options) {
|
|
9163
|
-
if (!fileExists((0,
|
|
9552
|
+
if (!fileExists((0, import_node_path43.join)(workspaceRoot, "nx.json"))) {
|
|
9164
9553
|
throw new Error("No nx.json found here. Run `projects` from the workspace root.");
|
|
9165
9554
|
}
|
|
9166
9555
|
const projects = listProjects(workspaceRoot);
|
|
@@ -9174,11 +9563,11 @@ function runProjects(workspaceRoot, options) {
|
|
|
9174
9563
|
}
|
|
9175
9564
|
|
|
9176
9565
|
// src/workspace-info/read-workspace-info.use-case.ts
|
|
9177
|
-
var
|
|
9566
|
+
var import_node_path45 = require("path");
|
|
9178
9567
|
|
|
9179
9568
|
// src/cli-version/check-for-update.use-case.ts
|
|
9180
|
-
var
|
|
9181
|
-
var
|
|
9569
|
+
var import_node_fs37 = require("fs");
|
|
9570
|
+
var import_node_path44 = require("path");
|
|
9182
9571
|
var import_cross_spawn3 = __toESM(require("cross-spawn"));
|
|
9183
9572
|
var PACKAGE_NAME2 = "@mnci/cli";
|
|
9184
9573
|
var REGISTRY_TIMEOUT_MS = 2e3;
|
|
@@ -9237,8 +9626,8 @@ function releaseTriple(version) {
|
|
|
9237
9626
|
}
|
|
9238
9627
|
function readCliVersion(packageDirectory) {
|
|
9239
9628
|
try {
|
|
9240
|
-
const manifestPath = (0,
|
|
9241
|
-
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"));
|
|
9242
9631
|
return manifest.version ?? "0.0.0";
|
|
9243
9632
|
} catch {
|
|
9244
9633
|
return "0.0.0";
|
|
@@ -9248,7 +9637,7 @@ function readCliVersion(packageDirectory) {
|
|
|
9248
9637
|
// src/workspace-info/read-workspace-info.use-case.ts
|
|
9249
9638
|
function readWorkspaceInfo(workspaceRoot, cliVersion) {
|
|
9250
9639
|
const latest = fetchLatestVersion() ?? null;
|
|
9251
|
-
const isWorkspace = fileExists((0,
|
|
9640
|
+
const isWorkspace = fileExists((0, import_node_path45.join)(workspaceRoot, "nx.json"));
|
|
9252
9641
|
return {
|
|
9253
9642
|
cli: {
|
|
9254
9643
|
version: cliVersion,
|
|
@@ -9269,8 +9658,8 @@ function runInfo(workspaceRoot, cliVersion, options) {
|
|
|
9269
9658
|
}
|
|
9270
9659
|
|
|
9271
9660
|
// src/workspace-creation/adopt-directory.use-case.ts
|
|
9272
|
-
var
|
|
9273
|
-
var
|
|
9661
|
+
var import_node_fs38 = require("fs");
|
|
9662
|
+
var import_node_path46 = require("path");
|
|
9274
9663
|
var NEVER_COPIED = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
9275
9664
|
var AGENT_AND_CI_ENTRIES = [
|
|
9276
9665
|
"AGENTS.md",
|
|
@@ -9301,13 +9690,13 @@ var PREFLIGHT_BLOCKERS = [
|
|
|
9301
9690
|
"packages"
|
|
9302
9691
|
];
|
|
9303
9692
|
function assertAdoptableDirectory(target) {
|
|
9304
|
-
if (!(0,
|
|
9693
|
+
if (!(0, import_node_fs38.existsSync)(target)) {
|
|
9305
9694
|
throw new Error(`--into '${target}' does not exist. Create or clone it first.`);
|
|
9306
9695
|
}
|
|
9307
|
-
if (!(0,
|
|
9696
|
+
if (!(0, import_node_fs38.statSync)(target).isDirectory()) {
|
|
9308
9697
|
throw new Error(`--into '${target}' is not a directory.`);
|
|
9309
9698
|
}
|
|
9310
|
-
const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0,
|
|
9699
|
+
const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs38.existsSync)((0, import_node_path46.join)(target, entry)));
|
|
9311
9700
|
if (blockers.length > 0) {
|
|
9312
9701
|
throw new Error(
|
|
9313
9702
|
`--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.`
|
|
@@ -9315,15 +9704,15 @@ function assertAdoptableDirectory(target) {
|
|
|
9315
9704
|
}
|
|
9316
9705
|
}
|
|
9317
9706
|
function mergeGitignore(generatedFile, targetFile) {
|
|
9318
|
-
const existing = (0,
|
|
9707
|
+
const existing = (0, import_node_fs38.readFileSync)(targetFile, "utf8");
|
|
9319
9708
|
const present = new Set(existing.split("\n").map((line) => line.trim()));
|
|
9320
|
-
const additions = (0,
|
|
9709
|
+
const additions = (0, import_node_fs38.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
|
|
9321
9710
|
const trimmed = line.trim();
|
|
9322
9711
|
return trimmed !== "" && !trimmed.startsWith("#") && !present.has(trimmed);
|
|
9323
9712
|
});
|
|
9324
9713
|
if (additions.length === 0) return false;
|
|
9325
9714
|
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
9326
|
-
(0,
|
|
9715
|
+
(0, import_node_fs38.writeFileSync)(
|
|
9327
9716
|
targetFile,
|
|
9328
9717
|
`${existing}${separator}
|
|
9329
9718
|
# Added by mnci (Nx workspace)
|
|
@@ -9333,9 +9722,9 @@ ${additions.join("\n")}
|
|
|
9333
9722
|
return true;
|
|
9334
9723
|
}
|
|
9335
9724
|
function adoptGeneratedWorkspace(generatedRoot, target) {
|
|
9336
|
-
const entries = (0,
|
|
9725
|
+
const entries = (0, import_node_fs38.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
|
|
9337
9726
|
const collisions = entries.filter(
|
|
9338
|
-
(entry) => (0,
|
|
9727
|
+
(entry) => (0, import_node_fs38.existsSync)((0, import_node_path46.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
|
|
9339
9728
|
);
|
|
9340
9729
|
if (collisions.length > 0) {
|
|
9341
9730
|
throw new Error(
|
|
@@ -9344,28 +9733,28 @@ function adoptGeneratedWorkspace(generatedRoot, target) {
|
|
|
9344
9733
|
}
|
|
9345
9734
|
const result = { copied: [], kept: [], mergedGitignore: false };
|
|
9346
9735
|
for (const entry of entries) {
|
|
9347
|
-
const from = (0,
|
|
9348
|
-
const to = (0,
|
|
9349
|
-
if (entry === ".gitignore" && (0,
|
|
9736
|
+
const from = (0, import_node_path46.join)(generatedRoot, entry);
|
|
9737
|
+
const to = (0, import_node_path46.join)(target, entry);
|
|
9738
|
+
if (entry === ".gitignore" && (0, import_node_fs38.existsSync)(to)) {
|
|
9350
9739
|
result.mergedGitignore = mergeGitignore(from, to);
|
|
9351
9740
|
result.kept.push(entry);
|
|
9352
9741
|
continue;
|
|
9353
9742
|
}
|
|
9354
|
-
if ((0,
|
|
9743
|
+
if ((0, import_node_fs38.existsSync)(to)) {
|
|
9355
9744
|
result.kept.push(entry);
|
|
9356
9745
|
continue;
|
|
9357
9746
|
}
|
|
9358
|
-
(0,
|
|
9747
|
+
(0, import_node_fs38.cpSync)(from, to, { recursive: true });
|
|
9359
9748
|
result.copied.push(entry);
|
|
9360
9749
|
}
|
|
9361
|
-
(0,
|
|
9750
|
+
(0, import_node_fs38.rmSync)(generatedRoot, { recursive: true, force: true });
|
|
9362
9751
|
return result;
|
|
9363
9752
|
}
|
|
9364
9753
|
|
|
9365
9754
|
// src/workspace-creation/create-workspace.use-case.ts
|
|
9366
|
-
var
|
|
9755
|
+
var import_node_fs39 = require("fs");
|
|
9367
9756
|
var import_node_os2 = require("os");
|
|
9368
|
-
var
|
|
9757
|
+
var import_node_path47 = require("path");
|
|
9369
9758
|
function nxCloudProviderValue(ci) {
|
|
9370
9759
|
return ci === "azure" ? "azure" : "github";
|
|
9371
9760
|
}
|
|
@@ -9408,9 +9797,9 @@ async function resolveRegistry2(options) {
|
|
|
9408
9797
|
return await promptRegistry();
|
|
9409
9798
|
}
|
|
9410
9799
|
async function runNew(name, options) {
|
|
9411
|
-
const adoptTarget = options.into === void 0 ? void 0 : (0,
|
|
9800
|
+
const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path47.resolve)(process.cwd(), options.into);
|
|
9412
9801
|
if (adoptTarget !== void 0) assertAdoptableDirectory(adoptTarget);
|
|
9413
|
-
const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0,
|
|
9802
|
+
const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path47.basename)(adoptTarget));
|
|
9414
9803
|
assertValidProjectName(workspaceName, "Workspace name");
|
|
9415
9804
|
const scope = options.scope ?? (options.yes ? `@${workspaceName}` : await promptText("npm scope for publishable packages", `@${workspaceName}`));
|
|
9416
9805
|
const registry = await resolveRegistry2(options);
|
|
@@ -9423,8 +9812,8 @@ async function runNew(name, options) {
|
|
|
9423
9812
|
const npmAuth = resolveNpmAuth(options.npmAuth, registry, ci);
|
|
9424
9813
|
const stack = await resolveStack(options);
|
|
9425
9814
|
const nxCloud = options.nxCloud ?? (options.yes ? false : await promptNxCloud());
|
|
9426
|
-
const stagingParent = adoptTarget === void 0 ? process.cwd() : (0,
|
|
9427
|
-
let workspaceRoot = (0,
|
|
9815
|
+
const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs39.mkdtempSync)((0, import_node_path47.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
|
|
9816
|
+
let workspaceRoot = (0, import_node_path47.join)(stagingParent, workspaceName);
|
|
9428
9817
|
try {
|
|
9429
9818
|
logger.step(`Creating Nx workspace '${workspaceName}' (preset: ts)`);
|
|
9430
9819
|
runNpx(
|
|
@@ -9447,7 +9836,7 @@ async function runNew(name, options) {
|
|
|
9447
9836
|
workspaceRoot = adoptTarget;
|
|
9448
9837
|
}
|
|
9449
9838
|
} finally {
|
|
9450
|
-
if (adoptTarget !== void 0) (0,
|
|
9839
|
+
if (adoptTarget !== void 0) (0, import_node_fs39.rmSync)(stagingParent, { recursive: true, force: true });
|
|
9451
9840
|
}
|
|
9452
9841
|
logger.step(
|
|
9453
9842
|
"Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
|
|
@@ -9457,8 +9846,8 @@ async function runNew(name, options) {
|
|
|
9457
9846
|
{ workspaceName, scope, registry, agent, variableGroup, ci, stack, ...npmAuth && { npmAuth } },
|
|
9458
9847
|
logger.detail
|
|
9459
9848
|
);
|
|
9460
|
-
(0,
|
|
9461
|
-
(0,
|
|
9849
|
+
(0, import_node_fs39.rmSync)((0, import_node_path47.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
|
|
9850
|
+
(0, import_node_fs39.rmSync)((0, import_node_path47.join)(workspaceRoot, "package-lock.json"), { force: true });
|
|
9462
9851
|
logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
|
|
9463
9852
|
const installStatus = runShell(
|
|
9464
9853
|
"npm",
|
|
@@ -9496,10 +9885,10 @@ function projectKindHintLines(kinds = PROJECT_KINDS) {
|
|
|
9496
9885
|
}
|
|
9497
9886
|
|
|
9498
9887
|
// src/workspace-creation/interactive-wizard.use-case.ts
|
|
9499
|
-
var
|
|
9888
|
+
var import_node_path48 = require("path");
|
|
9500
9889
|
var import_prompts4 = require("@inquirer/prompts");
|
|
9501
9890
|
async function runInteractive() {
|
|
9502
|
-
const inWorkspace = fileExists((0,
|
|
9891
|
+
const inWorkspace = fileExists((0, import_node_path48.join)(process.cwd(), "nx.json"));
|
|
9503
9892
|
const choiceNew = { name: "Create a new monorepo", value: "new" };
|
|
9504
9893
|
const choiceAdd = { name: "Add a project to this workspace", value: "add" };
|
|
9505
9894
|
const choiceUpgrade = {
|
|
@@ -9551,7 +9940,7 @@ function buildProgram(cliVersion) {
|
|
|
9551
9940
|
).option("--json", "print the findings as one JSON document, each with its remedy").action((options) => {
|
|
9552
9941
|
runDoctor(process.cwd(), options);
|
|
9553
9942
|
});
|
|
9554
|
-
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("--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) => {
|
|
9943
|
+
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) => {
|
|
9555
9944
|
runAdopt(process.cwd(), options);
|
|
9556
9945
|
});
|
|
9557
9946
|
program.command("ci").addArgument(new import_commander.Argument("<phase>", "which phase of the pipeline to run").choices(CI_PHASES)).description(
|