@mnci/cli 4.51.0 → 4.53.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 +54 -3
- package/dist/cli.js +417 -97
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4805,11 +4805,11 @@ function addGoInternalLib(workspaceRoot, name) {
|
|
|
4805
4805
|
}
|
|
4806
4806
|
}
|
|
4807
4807
|
|
|
4808
|
-
// src/
|
|
4808
|
+
// src/esm-conversion/esm-conversion.use-case.ts
|
|
4809
4809
|
var import_node_fs19 = require("fs");
|
|
4810
4810
|
var import_node_path18 = require("path");
|
|
4811
4811
|
|
|
4812
|
-
// src/
|
|
4812
|
+
// src/esm-conversion/esm-specifiers.algorithm.ts
|
|
4813
4813
|
var RELATIVE_SPECIFIER = /((?:\bfrom|\bimport)\s*)(['"])(\.{1,2}\/[^'"\n]*|\.{1,2})\2/g;
|
|
4814
4814
|
var HAS_EXTENSION = /\.(?:[cm]?js|json|node)$/;
|
|
4815
4815
|
function withJsSpecifiers(source, directories = []) {
|
|
@@ -4822,7 +4822,7 @@ function withJsSpecifiers(source, directories = []) {
|
|
|
4822
4822
|
});
|
|
4823
4823
|
}
|
|
4824
4824
|
|
|
4825
|
-
// src/
|
|
4825
|
+
// src/esm-conversion/esm-conversion.use-case.ts
|
|
4826
4826
|
var SAMPLE_DIRECTORIES = ["./hello"];
|
|
4827
4827
|
var JEST_ANCHOR = " moduleFileExtensions:";
|
|
4828
4828
|
var JEST_MAPPER = String.raw` moduleNameMapper: { '^(\\.{1,2}/.*)\\.js$': '$1' },`;
|
|
@@ -8360,11 +8360,26 @@ var import_node_child_process2 = require("child_process");
|
|
|
8360
8360
|
var import_node_fs32 = require("fs");
|
|
8361
8361
|
var import_node_path38 = require("path");
|
|
8362
8362
|
|
|
8363
|
-
// src/
|
|
8363
|
+
// src/release-tag-lineage/create-baseline-tags.use-case.ts
|
|
8364
|
+
function createBaselineTags(repositoryRoot, stranded) {
|
|
8365
|
+
const result = { created: [], existing: [], failed: [] };
|
|
8366
|
+
for (const entry of stranded) {
|
|
8367
|
+
const present = runCapture("git", ["rev-parse", "--verify", "--quiet", `refs/tags/${entry.newTag}`], repositoryRoot);
|
|
8368
|
+
if (present.status === 0) {
|
|
8369
|
+
result.existing.push(entry.newTag);
|
|
8370
|
+
continue;
|
|
8371
|
+
}
|
|
8372
|
+
const made = runCapture("git", ["tag", entry.newTag, `${entry.oldTag}^{commit}`], repositoryRoot);
|
|
8373
|
+
(made.status === 0 ? result.created : result.failed).push(entry.newTag);
|
|
8374
|
+
}
|
|
8375
|
+
return result;
|
|
8376
|
+
}
|
|
8377
|
+
|
|
8378
|
+
// src/release-tag-lineage/locate-stranded-release-tags.use-case.ts
|
|
8364
8379
|
var import_node_fs31 = require("fs");
|
|
8365
8380
|
var import_node_path37 = require("path");
|
|
8366
8381
|
|
|
8367
|
-
// src/
|
|
8382
|
+
// src/release-tag-lineage/stranded-release-tags.algorithm.ts
|
|
8368
8383
|
function compareVersions2(left, right) {
|
|
8369
8384
|
const a = left.split(".").map(Number);
|
|
8370
8385
|
const b = right.split(".").map(Number);
|
|
@@ -8389,7 +8404,7 @@ function findStrandedReleaseTags(projects, tags) {
|
|
|
8389
8404
|
return stranded;
|
|
8390
8405
|
}
|
|
8391
8406
|
|
|
8392
|
-
// src/
|
|
8407
|
+
// src/release-tag-lineage/locate-stranded-release-tags.use-case.ts
|
|
8393
8408
|
var MNCI_TAG_PATTERN = "{projectName}@{version}";
|
|
8394
8409
|
function locateStrandedReleaseTags(workspaceRoot, tagPattern) {
|
|
8395
8410
|
if (tagPattern !== void 0 && tagPattern !== MNCI_TAG_PATTERN) {
|
|
@@ -8403,6 +8418,17 @@ function locateStrandedReleaseTags(workspaceRoot, tagPattern) {
|
|
|
8403
8418
|
return findStrandedReleaseTags(projects, listed.stdout.split(/\r?\n/).filter(Boolean));
|
|
8404
8419
|
}
|
|
8405
8420
|
|
|
8421
|
+
// src/workspace-diagnostics/check-release-tags-resolve.use-case.ts
|
|
8422
|
+
function checkReleaseTagsResolve(workspaceRoot, nxJson) {
|
|
8423
|
+
const stranded = locateStrandedReleaseTags(workspaceRoot, nxJson.release?.releaseTag?.pattern);
|
|
8424
|
+
return {
|
|
8425
|
+
check: "release tags resolve under each project's current name",
|
|
8426
|
+
ok: stranded.length === 0,
|
|
8427
|
+
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`,
|
|
8428
|
+
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`
|
|
8429
|
+
};
|
|
8430
|
+
}
|
|
8431
|
+
|
|
8406
8432
|
// src/workspace-diagnostics/check-invariants.use-case.ts
|
|
8407
8433
|
var SUPPORTED_ESLINT_MAJOR = ESLINT_VERSION.replace(/^\D*/, "").split(".", 1)[0];
|
|
8408
8434
|
function toPosix2(path) {
|
|
@@ -8872,15 +8898,6 @@ function checkLockfileHasNoLocalRegistry(workspaceRoot) {
|
|
|
8872
8898
|
remedy: "run `mnci upgrade`, which removes it, then commit package-lock.json"
|
|
8873
8899
|
};
|
|
8874
8900
|
}
|
|
8875
|
-
function checkReleaseTagsResolve(workspaceRoot, nxJson) {
|
|
8876
|
-
const stranded = locateStrandedReleaseTags(workspaceRoot, nxJson.release?.releaseTag?.pattern);
|
|
8877
|
-
return {
|
|
8878
|
-
check: "release tags resolve under each project's current name",
|
|
8879
|
-
ok: stranded.length === 0,
|
|
8880
|
-
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`,
|
|
8881
|
-
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`
|
|
8882
|
-
};
|
|
8883
|
-
}
|
|
8884
8901
|
function collectFindings(workspaceRoot) {
|
|
8885
8902
|
const nxJsonPath = (0, import_node_path38.join)(workspaceRoot, "nx.json");
|
|
8886
8903
|
if (!fileExists(nxJsonPath)) {
|
|
@@ -8946,11 +8963,11 @@ function runDoctor(workspaceRoot, options = {}) {
|
|
|
8946
8963
|
process.exitCode = 1;
|
|
8947
8964
|
}
|
|
8948
8965
|
|
|
8949
|
-
// src/
|
|
8966
|
+
// src/dependency-adoption/adopt-dependencies.use-case.ts
|
|
8950
8967
|
var import_node_fs33 = require("fs");
|
|
8951
8968
|
var import_node_path39 = require("path");
|
|
8952
8969
|
|
|
8953
|
-
// src/
|
|
8970
|
+
// src/clean-working-tree/clean-working-tree.validator.ts
|
|
8954
8971
|
function requireCleanWorkingTree(repositoryRoot, capture = runCapture) {
|
|
8955
8972
|
if (capture("git", ["rev-parse", "--is-inside-work-tree"], repositoryRoot).status !== 0) {
|
|
8956
8973
|
throw new Error("This directory is not a git repository. Every adoption step is one diff to review, so it needs git.");
|
|
@@ -8960,7 +8977,7 @@ function requireCleanWorkingTree(repositoryRoot, capture = runCapture) {
|
|
|
8960
8977
|
}
|
|
8961
8978
|
}
|
|
8962
8979
|
|
|
8963
|
-
// src/
|
|
8980
|
+
// src/dependency-adoption/extract-imports.algorithm.ts
|
|
8964
8981
|
var import_node_module = require("module");
|
|
8965
8982
|
var SPECIFIER2 = /(?:\bfrom\s*|\bimport\s*(?:\(\s*)?|\brequire\s*\(\s*)(['"`])([^'"`\n]+)\1/g;
|
|
8966
8983
|
var BUILTINS = new Set(import_node_module.builtinModules);
|
|
@@ -8986,7 +9003,7 @@ function extractImportedPackages(source) {
|
|
|
8986
9003
|
return found;
|
|
8987
9004
|
}
|
|
8988
9005
|
|
|
8989
|
-
// src/
|
|
9006
|
+
// src/dependency-adoption/plan-dependency-moves.algorithm.ts
|
|
8990
9007
|
function planDependencyMoves(rootDependencies, usage, declared) {
|
|
8991
9008
|
const plan = { moves: [], removed: [], keptAtRoot: [], conflicts: [] };
|
|
8992
9009
|
for (const [name, range] of Object.entries(rootDependencies)) {
|
|
@@ -9014,7 +9031,7 @@ function planDependencyMoves(rootDependencies, usage, declared) {
|
|
|
9014
9031
|
return plan;
|
|
9015
9032
|
}
|
|
9016
9033
|
|
|
9017
|
-
// src/
|
|
9034
|
+
// src/dependency-adoption/adopt-dependencies.use-case.ts
|
|
9018
9035
|
var SOURCE_FILE = /\.(?:[cm]?[jt]s|[jt]sx)$/;
|
|
9019
9036
|
var TEST_FILE = /\.(?:spec|test)\.[^.]+$|[\\/](?:__tests__|tests?|e2e)[\\/]/;
|
|
9020
9037
|
var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", "out", ".git", ".nx", "tmp"]);
|
|
@@ -9107,11 +9124,11 @@ function adoptDependencies(repositoryRoot, dependencies = {}) {
|
|
|
9107
9124
|
return { plan, installed };
|
|
9108
9125
|
}
|
|
9109
9126
|
|
|
9110
|
-
// src/
|
|
9127
|
+
// src/kind-adoption/adopt-kinds.use-case.ts
|
|
9111
9128
|
var import_node_fs34 = require("fs");
|
|
9112
9129
|
var import_node_path40 = require("path");
|
|
9113
9130
|
|
|
9114
|
-
// src/
|
|
9131
|
+
// src/kind-adoption/infer-project-kind.algorithm.ts
|
|
9115
9132
|
var SERVER_FRAMEWORKS = ["express", "koa", "fastify", "@nestjs/core"];
|
|
9116
9133
|
function declares(manifest, name) {
|
|
9117
9134
|
return ["dependencies", "devDependencies", "peerDependencies"].some((key) => {
|
|
@@ -9207,7 +9224,7 @@ function inferProjectKind(evidence) {
|
|
|
9207
9224
|
}
|
|
9208
9225
|
}
|
|
9209
9226
|
|
|
9210
|
-
// src/
|
|
9227
|
+
// src/kind-adoption/adopt-kinds.use-case.ts
|
|
9211
9228
|
var TYPE_TAG = "type:";
|
|
9212
9229
|
function readEvidence(repositoryRoot, location) {
|
|
9213
9230
|
const directory = (0, import_node_path40.join)(repositoryRoot, location.dir);
|
|
@@ -9483,7 +9500,7 @@ function detectTestRunner(repositoryRoot) {
|
|
|
9483
9500
|
function adoptOverlay(repositoryRoot, options, dependencies = {}) {
|
|
9484
9501
|
requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
|
|
9485
9502
|
if (!fileExists((0, import_node_path42.join)(repositoryRoot, "nx.json"))) {
|
|
9486
|
-
throw new Error("There is no nx.json, so Nx is not set up here.
|
|
9503
|
+
throw new Error("There is no nx.json, so Nx is not set up here. Run `mnci adopt --toolchain` first: it sets Nx up (nx init, then nx add), and the overlay needs that.");
|
|
9487
9504
|
}
|
|
9488
9505
|
const persisted = readMnciConfig(repositoryRoot);
|
|
9489
9506
|
const filled = {
|
|
@@ -9494,10 +9511,10 @@ function adoptOverlay(repositoryRoot, options, dependencies = {}) {
|
|
|
9494
9511
|
(dependencies.upgrade ?? runUpgrade)(repositoryRoot, filled);
|
|
9495
9512
|
}
|
|
9496
9513
|
|
|
9497
|
-
// src/
|
|
9498
|
-
var
|
|
9514
|
+
// src/toolchain-adoption/adopt-toolchain.use-case.ts
|
|
9515
|
+
var import_node_path45 = require("path");
|
|
9499
9516
|
|
|
9500
|
-
// src/
|
|
9517
|
+
// src/toolchain-adoption/align-nx-family.algorithm.ts
|
|
9501
9518
|
var VERSION = /^[\^~]?(\d+)\.(\d+)\.(\d+)$/;
|
|
9502
9519
|
function isNxFamily(name) {
|
|
9503
9520
|
return name === "nx" || name.startsWith("@nx/");
|
|
@@ -9532,25 +9549,56 @@ function alignNxFamily(dependencies, version) {
|
|
|
9532
9549
|
return { dependencies: aligned, changed };
|
|
9533
9550
|
}
|
|
9534
9551
|
|
|
9535
|
-
// src/
|
|
9536
|
-
var import_node_fs37 = require("fs");
|
|
9552
|
+
// src/toolchain-adoption/bootstrap-nx.use-case.ts
|
|
9537
9553
|
var import_node_path43 = require("path");
|
|
9554
|
+
var BASE_PLUGIN = "@nx/js";
|
|
9555
|
+
var TOOL_PLUGINS = [
|
|
9556
|
+
["eslint", "@nx/eslint"],
|
|
9557
|
+
["jest", "@nx/jest"],
|
|
9558
|
+
["vitest", "@nx/vite"]
|
|
9559
|
+
];
|
|
9560
|
+
function pluginsFor(repositoryRoot) {
|
|
9561
|
+
const manifestPath = (0, import_node_path43.join)(repositoryRoot, "package.json");
|
|
9562
|
+
const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
|
|
9563
|
+
const declared = Object.keys({ ...manifest.dependencies, ...manifest.devDependencies });
|
|
9564
|
+
return [BASE_PLUGIN, ...TOOL_PLUGINS.filter(([tool]) => declared.includes(tool)).map(([, plugin]) => plugin)];
|
|
9565
|
+
}
|
|
9566
|
+
function bootstrapNx(repositoryRoot, nxVersion, dependencies) {
|
|
9567
|
+
if (fileExists((0, import_node_path43.join)(repositoryRoot, "nx.json"))) {
|
|
9568
|
+
return { bootstrapped: false, plugins: [] };
|
|
9569
|
+
}
|
|
9570
|
+
const initialised = dependencies.run("npx", ["--yes", `nx@${nxVersion ?? "latest"}`, "init", "--interactive=false", "--nxCloud=skip", "--plugins=skip"], repositoryRoot);
|
|
9571
|
+
if (initialised !== 0) {
|
|
9572
|
+
throw new Error("`nx init` failed, so Nx could not be set up here. Read its output above; nothing else was changed after it.");
|
|
9573
|
+
}
|
|
9574
|
+
const plugins = pluginsFor(repositoryRoot);
|
|
9575
|
+
for (const plugin of plugins) {
|
|
9576
|
+
if (dependencies.run("npx", ["nx", "add", plugin], repositoryRoot) !== 0) {
|
|
9577
|
+
throw new Error(`\`nx add ${plugin}\` failed. Nx itself is set up (nx.json exists); add the plugin by hand once its error is dealt with.`);
|
|
9578
|
+
}
|
|
9579
|
+
}
|
|
9580
|
+
return { bootstrapped: true, plugins };
|
|
9581
|
+
}
|
|
9582
|
+
|
|
9583
|
+
// src/toolchain-adoption/retire-tooling.use-case.ts
|
|
9584
|
+
var import_node_fs37 = require("fs");
|
|
9585
|
+
var import_node_path44 = require("path");
|
|
9538
9586
|
var RETIRED_COMMAND = /\b(?:prettier|oxfmt|oxlint)\b/;
|
|
9539
9587
|
function retireTooling(repositoryRoot) {
|
|
9540
9588
|
const removed = [];
|
|
9541
9589
|
for (const file of RETIRED_FORMATTER_FILES) {
|
|
9542
|
-
if (!fileExists((0,
|
|
9590
|
+
if (!fileExists((0, import_node_path44.join)(repositoryRoot, file))) {
|
|
9543
9591
|
continue;
|
|
9544
9592
|
}
|
|
9545
|
-
(0, import_node_fs37.rmSync)((0,
|
|
9593
|
+
(0, import_node_fs37.rmSync)((0, import_node_path44.join)(repositoryRoot, file), { force: true });
|
|
9546
9594
|
removed.push(file);
|
|
9547
9595
|
}
|
|
9548
|
-
const hadRegistry = fileExists((0,
|
|
9596
|
+
const hadRegistry = fileExists((0, import_node_path44.join)(repositoryRoot, ".verdaccio"));
|
|
9549
9597
|
removeLocalRegistryScaffolding(repositoryRoot);
|
|
9550
9598
|
if (hadRegistry) {
|
|
9551
9599
|
removed.push(".verdaccio, its dependency and the local-registry target");
|
|
9552
9600
|
}
|
|
9553
|
-
const manifestPath = (0,
|
|
9601
|
+
const manifestPath = (0, import_node_path44.join)(repositoryRoot, "package.json");
|
|
9554
9602
|
if (!fileExists(manifestPath)) {
|
|
9555
9603
|
return removed;
|
|
9556
9604
|
}
|
|
@@ -9578,7 +9626,7 @@ function retireTooling(repositoryRoot) {
|
|
|
9578
9626
|
return removed;
|
|
9579
9627
|
}
|
|
9580
9628
|
|
|
9581
|
-
// src/
|
|
9629
|
+
// src/toolchain-adoption/adopt-toolchain.use-case.ts
|
|
9582
9630
|
var MAX_AUDIT_FIX_PASSES = 4;
|
|
9583
9631
|
function publishedNxVersions(repositoryRoot, capture) {
|
|
9584
9632
|
const answer = capture("npm", ["view", "nx", "versions", "--json"], repositoryRoot);
|
|
@@ -9597,10 +9645,14 @@ function adoptToolchain(repositoryRoot, options = {}, dependencies = {}) {
|
|
|
9597
9645
|
console.log(message);
|
|
9598
9646
|
});
|
|
9599
9647
|
requireCleanWorkingTree(repositoryRoot, capture);
|
|
9600
|
-
const manifestPath = (0,
|
|
9648
|
+
const manifestPath = (0, import_node_path45.join)(repositoryRoot, "package.json");
|
|
9601
9649
|
if (!fileExists(manifestPath)) {
|
|
9602
9650
|
throw new Error("No package.json at the repository root.");
|
|
9603
9651
|
}
|
|
9652
|
+
const bootstrap = bootstrapNx(repositoryRoot, options.nxVersion, { run });
|
|
9653
|
+
if (bootstrap.bootstrapped) {
|
|
9654
|
+
log(`Set Nx up (nx init, then nx add ${bootstrap.plugins.join(" ")})`);
|
|
9655
|
+
}
|
|
9604
9656
|
log("Removing retired tooling");
|
|
9605
9657
|
const removed = retireTooling(repositoryRoot);
|
|
9606
9658
|
const manifest = readJson(manifestPath);
|
|
@@ -9628,27 +9680,12 @@ function adoptToolchain(repositoryRoot, options = {}, dependencies = {}) {
|
|
|
9628
9680
|
run("npm", ["audit", "fix", "--no-fund"], repositoryRoot);
|
|
9629
9681
|
passes = audit(repositoryRoot) === 0;
|
|
9630
9682
|
}
|
|
9631
|
-
return { removed, aligned: { names, version }, auditFixes, auditPasses: passes };
|
|
9632
|
-
}
|
|
9633
|
-
|
|
9634
|
-
// src/repository-adoption/create-baseline-tags.use-case.ts
|
|
9635
|
-
function createBaselineTags(repositoryRoot, stranded) {
|
|
9636
|
-
const result = { created: [], existing: [], failed: [] };
|
|
9637
|
-
for (const entry of stranded) {
|
|
9638
|
-
const present = runCapture("git", ["rev-parse", "--verify", "--quiet", `refs/tags/${entry.newTag}`], repositoryRoot);
|
|
9639
|
-
if (present.status === 0) {
|
|
9640
|
-
result.existing.push(entry.newTag);
|
|
9641
|
-
continue;
|
|
9642
|
-
}
|
|
9643
|
-
const made = runCapture("git", ["tag", entry.newTag, `${entry.oldTag}^{commit}`], repositoryRoot);
|
|
9644
|
-
(made.status === 0 ? result.created : result.failed).push(entry.newTag);
|
|
9645
|
-
}
|
|
9646
|
-
return result;
|
|
9683
|
+
return { bootstrapped: bootstrap.plugins, removed, aligned: { names, version }, auditFixes, auditPasses: passes };
|
|
9647
9684
|
}
|
|
9648
9685
|
|
|
9649
|
-
// src/
|
|
9686
|
+
// src/adoption-report/inspect-repository.use-case.ts
|
|
9650
9687
|
var import_node_fs38 = require("fs");
|
|
9651
|
-
var
|
|
9688
|
+
var import_node_path46 = require("path");
|
|
9652
9689
|
var LOCKFILES = [
|
|
9653
9690
|
["package-lock.json", "npm"],
|
|
9654
9691
|
["pnpm-lock.yaml", "pnpm"],
|
|
@@ -9669,22 +9706,22 @@ function inspectRepository(repositoryRoot) {
|
|
|
9669
9706
|
const isGitRepo = inside.status === 0 && inside.stdout.trim() === "true";
|
|
9670
9707
|
const dirty = isGitRepo && runCapture("git", ["status", "--porcelain"], repositoryRoot).stdout.trim() !== "";
|
|
9671
9708
|
const tags = isGitRepo ? runCapture("git", ["tag", "--list"], repositoryRoot).stdout.split(/\r?\n/).filter(Boolean) : [];
|
|
9672
|
-
const manifestPath = (0,
|
|
9709
|
+
const manifestPath = (0, import_node_path46.join)(repositoryRoot, "package.json");
|
|
9673
9710
|
const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
|
|
9674
|
-
const nxJsonPath = (0,
|
|
9711
|
+
const nxJsonPath = (0, import_node_path46.join)(repositoryRoot, "nx.json");
|
|
9675
9712
|
const nxJson = fileExists(nxJsonPath) ? readJson(nxJsonPath) : {};
|
|
9676
9713
|
const ci = [];
|
|
9677
9714
|
const pipelines = [];
|
|
9678
|
-
if ((0, import_node_fs38.existsSync)((0,
|
|
9715
|
+
if ((0, import_node_fs38.existsSync)((0, import_node_path46.join)(repositoryRoot, "azure-pipelines.yml"))) {
|
|
9679
9716
|
ci.push("azure");
|
|
9680
|
-
pipelines.push((0,
|
|
9717
|
+
pipelines.push((0, import_node_path46.join)(repositoryRoot, "azure-pipelines.yml"));
|
|
9681
9718
|
}
|
|
9682
|
-
const workflows = (0,
|
|
9719
|
+
const workflows = (0, import_node_path46.join)(repositoryRoot, ".github", "workflows");
|
|
9683
9720
|
if ((0, import_node_fs38.existsSync)(workflows)) {
|
|
9684
9721
|
const files = (0, import_node_fs38.readdirSync)(workflows).filter((file) => /\.ya?ml$/.test(file));
|
|
9685
9722
|
if (files.length > 0) {
|
|
9686
9723
|
ci.push("github");
|
|
9687
|
-
pipelines.push(...files.map((file) => (0,
|
|
9724
|
+
pipelines.push(...files.map((file) => (0, import_node_path46.join)(workflows, file)));
|
|
9688
9725
|
}
|
|
9689
9726
|
}
|
|
9690
9727
|
const rootFiles = (0, import_node_fs38.readdirSync)(repositoryRoot);
|
|
@@ -9704,7 +9741,7 @@ function inspectRepository(repositoryRoot) {
|
|
|
9704
9741
|
};
|
|
9705
9742
|
}
|
|
9706
9743
|
|
|
9707
|
-
// src/
|
|
9744
|
+
// src/adoption-report/judge-adoption.policy.ts
|
|
9708
9745
|
function judgeAdoption(facts) {
|
|
9709
9746
|
const findings = [];
|
|
9710
9747
|
const blocker = (detail, step) => {
|
|
@@ -9728,7 +9765,7 @@ function judgeAdoption(facts) {
|
|
|
9728
9765
|
warning("nx.json already carries an mnci block, so this is an mnci workspace", "use `mnci upgrade`; adopt is for repositories mnci has not touched");
|
|
9729
9766
|
}
|
|
9730
9767
|
if (facts.nxVersion === void 0) {
|
|
9731
|
-
warning("Nx is not installed", "
|
|
9768
|
+
warning("Nx is not installed", "run `mnci adopt --toolchain`, which sets Nx up through nx init and nx add before it does anything else; the overlay step needs the nx.json that makes");
|
|
9732
9769
|
}
|
|
9733
9770
|
if (facts.strandedTags.length > 0) {
|
|
9734
9771
|
warning(
|
|
@@ -9749,10 +9786,12 @@ function judgeAdoption(facts) {
|
|
|
9749
9786
|
return { ready: ordered.every((finding) => finding.severity !== "blocker"), facts, findings: ordered };
|
|
9750
9787
|
}
|
|
9751
9788
|
|
|
9752
|
-
// src/
|
|
9789
|
+
// src/adoption-report/report-adoption.use-case.ts
|
|
9753
9790
|
function reportAdoption(repositoryRoot) {
|
|
9754
9791
|
return judgeAdoption(inspectRepository(repositoryRoot));
|
|
9755
9792
|
}
|
|
9793
|
+
|
|
9794
|
+
// src/repository-adoption/run-adopt.use-case.ts
|
|
9756
9795
|
function runAdopt(repositoryRoot, options = {}) {
|
|
9757
9796
|
const report = reportAdoption(repositoryRoot);
|
|
9758
9797
|
if (options.dependencies === true) {
|
|
@@ -9833,6 +9872,9 @@ function runToolchain(repositoryRoot, options) {
|
|
|
9833
9872
|
const result = adoptToolchain(repositoryRoot, { nxVersion: options.nx }, { log: (message) => {
|
|
9834
9873
|
logger.step(message);
|
|
9835
9874
|
} });
|
|
9875
|
+
if (result.bootstrapped.length > 0) {
|
|
9876
|
+
logger.info(`Nx was not set up here, so it is now: ${result.bootstrapped.join(", ")} (nx.json and the plugins; review it with \`git diff\`).`);
|
|
9877
|
+
}
|
|
9836
9878
|
for (const line of result.removed) {
|
|
9837
9879
|
logger.info(`removed ${line}`);
|
|
9838
9880
|
}
|
|
@@ -9996,13 +10038,13 @@ function runCommands(program, options) {
|
|
|
9996
10038
|
}
|
|
9997
10039
|
|
|
9998
10040
|
// src/workspace-info/list-projects.use-case.ts
|
|
9999
|
-
var
|
|
10041
|
+
var import_node_path47 = require("path");
|
|
10000
10042
|
var TYPE_TAG_PREFIX = "type:";
|
|
10001
10043
|
function listProjects(workspaceRoot) {
|
|
10002
10044
|
return locateProjects(workspaceRoot).map((location) => {
|
|
10003
|
-
const projectJsonPath = (0,
|
|
10045
|
+
const projectJsonPath = (0, import_node_path47.join)(workspaceRoot, location.dir, "project.json");
|
|
10004
10046
|
const project = fileExists(projectJsonPath) ? readJson(projectJsonPath) : {};
|
|
10005
|
-
const manifestPath = (0,
|
|
10047
|
+
const manifestPath = (0, import_node_path47.join)(workspaceRoot, location.dir, "package.json");
|
|
10006
10048
|
const manifestTags = fileExists(manifestPath) ? readJson(manifestPath).nx?.tags ?? [] : [];
|
|
10007
10049
|
const typeTag = [...project.tags ?? [], ...manifestTags].find((tag) => tag.startsWith(TYPE_TAG_PREFIX));
|
|
10008
10050
|
return {
|
|
@@ -10015,7 +10057,7 @@ function listProjects(workspaceRoot) {
|
|
|
10015
10057
|
}).toSorted((a, b) => a.dir.localeCompare(b.dir));
|
|
10016
10058
|
}
|
|
10017
10059
|
function runProjects(workspaceRoot, options) {
|
|
10018
|
-
if (!fileExists((0,
|
|
10060
|
+
if (!fileExists((0, import_node_path47.join)(workspaceRoot, "nx.json"))) {
|
|
10019
10061
|
throw new Error("No nx.json found here. Run `projects` from the workspace root.");
|
|
10020
10062
|
}
|
|
10021
10063
|
const projects = listProjects(workspaceRoot);
|
|
@@ -10029,11 +10071,11 @@ function runProjects(workspaceRoot, options) {
|
|
|
10029
10071
|
}
|
|
10030
10072
|
|
|
10031
10073
|
// src/workspace-info/read-workspace-info.use-case.ts
|
|
10032
|
-
var
|
|
10074
|
+
var import_node_path49 = require("path");
|
|
10033
10075
|
|
|
10034
10076
|
// src/cli-version/check-for-update.use-case.ts
|
|
10035
10077
|
var import_node_fs39 = require("fs");
|
|
10036
|
-
var
|
|
10078
|
+
var import_node_path48 = require("path");
|
|
10037
10079
|
var import_cross_spawn4 = __toESM(require("cross-spawn"));
|
|
10038
10080
|
var PACKAGE_NAME2 = "@mnci/cli";
|
|
10039
10081
|
var REGISTRY_TIMEOUT_MS = 2e3;
|
|
@@ -10092,7 +10134,7 @@ function releaseTriple(version) {
|
|
|
10092
10134
|
}
|
|
10093
10135
|
function readCliVersion(packageDirectory) {
|
|
10094
10136
|
try {
|
|
10095
|
-
const manifestPath = (0,
|
|
10137
|
+
const manifestPath = (0, import_node_path48.join)(packageDirectory, "..", "package.json");
|
|
10096
10138
|
const manifest = JSON.parse((0, import_node_fs39.readFileSync)(manifestPath, "utf8"));
|
|
10097
10139
|
return manifest.version ?? "0.0.0";
|
|
10098
10140
|
} catch {
|
|
@@ -10103,7 +10145,7 @@ function readCliVersion(packageDirectory) {
|
|
|
10103
10145
|
// src/workspace-info/read-workspace-info.use-case.ts
|
|
10104
10146
|
function readWorkspaceInfo(workspaceRoot, cliVersion) {
|
|
10105
10147
|
const latest = fetchLatestVersion() ?? null;
|
|
10106
|
-
const isWorkspace = fileExists((0,
|
|
10148
|
+
const isWorkspace = fileExists((0, import_node_path49.join)(workspaceRoot, "nx.json"));
|
|
10107
10149
|
return {
|
|
10108
10150
|
cli: {
|
|
10109
10151
|
version: cliVersion,
|
|
@@ -10124,7 +10166,7 @@ function runInfo(workspaceRoot, cliVersion, options) {
|
|
|
10124
10166
|
}
|
|
10125
10167
|
|
|
10126
10168
|
// src/interactive-wizard/interactive-wizard.use-case.ts
|
|
10127
|
-
var
|
|
10169
|
+
var import_node_path50 = require("path");
|
|
10128
10170
|
|
|
10129
10171
|
// src/interactive-wizard/adopt-steps.config.ts
|
|
10130
10172
|
var ADOPT_GLOBAL_OPTIONS = ["json"];
|
|
@@ -10305,7 +10347,7 @@ function wizardMenu(commands, inWorkspace) {
|
|
|
10305
10347
|
}
|
|
10306
10348
|
async function runInteractive(program, prompter = createInquirerPrompter(), workingDirectory = process.cwd()) {
|
|
10307
10349
|
const commands = describeCommands(program);
|
|
10308
|
-
const inWorkspace = fileExists((0,
|
|
10350
|
+
const inWorkspace = fileExists((0, import_node_path50.join)(workingDirectory, "nx.json"));
|
|
10309
10351
|
const name = await prompter.select("What would you like to do?", wizardMenu(commands, inWorkspace));
|
|
10310
10352
|
const command = commands.find((candidate) => candidate.name === name);
|
|
10311
10353
|
const argv = buildArgv(command, await askCommand(command, prompter));
|
|
@@ -10317,9 +10359,281 @@ async function runInteractive(program, prompter = createInquirerPrompter(), work
|
|
|
10317
10359
|
}
|
|
10318
10360
|
}
|
|
10319
10361
|
|
|
10320
|
-
// src/workspace-
|
|
10362
|
+
// src/workspace-presets/preset-catalog.config.ts
|
|
10363
|
+
var PRESETS = [
|
|
10364
|
+
{
|
|
10365
|
+
id: "web-api",
|
|
10366
|
+
summary: "a React frontend and an Express API that share one internal library: the web asks the API, the API answers with the shared greeting, and `mnci dev web api` starts both",
|
|
10367
|
+
projects: [
|
|
10368
|
+
{ kind: "internal-lib", name: "shared", options: {} },
|
|
10369
|
+
{ kind: "node-app", name: "api", options: { framework: "express" } },
|
|
10370
|
+
{ kind: "react-app", name: "web", options: {} }
|
|
10371
|
+
]
|
|
10372
|
+
}
|
|
10373
|
+
];
|
|
10374
|
+
var PRESET_IDS = PRESETS.map((preset) => preset.id);
|
|
10375
|
+
|
|
10376
|
+
// src/workspace-presets/wire-web-api.use-case.ts
|
|
10321
10377
|
var import_node_fs40 = require("fs");
|
|
10322
|
-
var
|
|
10378
|
+
var import_node_path51 = require("path");
|
|
10379
|
+
|
|
10380
|
+
// src/workspace-presets/web-api-wiring.algorithm.ts
|
|
10381
|
+
function apiHandler(shared) {
|
|
10382
|
+
return `import { greet } from '${shared}'
|
|
10383
|
+
import type { Request, Response } from 'express'
|
|
10384
|
+
|
|
10385
|
+
export function helloHandler (request: Request, response: Response): void {
|
|
10386
|
+
const name = typeof request.query.name === 'string' ? request.query.name : 'world'
|
|
10387
|
+
response.send(greet(name))
|
|
10388
|
+
}
|
|
10389
|
+
`;
|
|
10390
|
+
}
|
|
10391
|
+
function apiHandlerSpec(shared) {
|
|
10392
|
+
return String.raw`import { greet } from '${shared}'
|
|
10393
|
+
import type { Request, Response } from 'express'
|
|
10394
|
+
import { helloHandler } from './hello.handler'
|
|
10395
|
+
|
|
10396
|
+
/** A response that keeps what it was sent, so the spec needs no mock library and runs under Jest or Vitest. */
|
|
10397
|
+
function recordingResponse (): { response: Response, sent: unknown[] } {
|
|
10398
|
+
const sent: unknown[] = []
|
|
10399
|
+
|
|
10400
|
+
return { sent, response: { send: (value: unknown) => { sent.push(value) } } as unknown as Response }
|
|
10401
|
+
}
|
|
10402
|
+
|
|
10403
|
+
describe('helloHandler', () => {
|
|
10404
|
+
it('answers with the shared library\'s greeting for the name asked', () => {
|
|
10405
|
+
const { response, sent } = recordingResponse()
|
|
10406
|
+
|
|
10407
|
+
helloHandler({ query: { name: 'web' } } as unknown as Request, response)
|
|
10408
|
+
|
|
10409
|
+
expect(sent).toEqual([greet('web')])
|
|
10410
|
+
})
|
|
10411
|
+
|
|
10412
|
+
it('greets the world when no name is asked', () => {
|
|
10413
|
+
const { response, sent } = recordingResponse()
|
|
10414
|
+
|
|
10415
|
+
helloHandler({ query: {} } as unknown as Request, response)
|
|
10416
|
+
|
|
10417
|
+
expect(sent).toEqual([greet('world')])
|
|
10418
|
+
})
|
|
10419
|
+
})
|
|
10420
|
+
`;
|
|
10421
|
+
}
|
|
10422
|
+
function apiBarrel() {
|
|
10423
|
+
return "export * from './hello.handler'\n";
|
|
10424
|
+
}
|
|
10425
|
+
function webGreetingClient(shared) {
|
|
10426
|
+
return `import type { Greeting } from '${shared}'
|
|
10427
|
+
|
|
10428
|
+
export async function fetchGreeting (name: string, request: typeof fetch = fetch): Promise<Greeting> {
|
|
10429
|
+
const response = await request('/api/greeting?name=' + encodeURIComponent(name))
|
|
10430
|
+
|
|
10431
|
+
return await response.json() as Greeting
|
|
10432
|
+
}
|
|
10433
|
+
`;
|
|
10434
|
+
}
|
|
10435
|
+
function webGreetingClientSpec() {
|
|
10436
|
+
return String.raw`import { fetchGreeting } from './greeting.client'
|
|
10437
|
+
|
|
10438
|
+
describe('fetchGreeting', () => {
|
|
10439
|
+
it('asks the API\'s greeting route for the name and returns the answer', async () => {
|
|
10440
|
+
const requested: string[] = []
|
|
10441
|
+
const answering = (async (input: string) => {
|
|
10442
|
+
requested.push(input)
|
|
10443
|
+
|
|
10444
|
+
return { json: async () => ({ message: 'Hello, web!' }) } as Response
|
|
10445
|
+
}) as unknown as typeof fetch
|
|
10446
|
+
|
|
10447
|
+
expect(await fetchGreeting('web', answering)).toEqual({ message: 'Hello, web!' })
|
|
10448
|
+
expect(requested).toEqual(['/api/greeting?name=web'])
|
|
10449
|
+
})
|
|
10450
|
+
})
|
|
10451
|
+
`;
|
|
10452
|
+
}
|
|
10453
|
+
function webGreetingComponent(shared) {
|
|
10454
|
+
return `import { useEffect, useState } from 'react'
|
|
10455
|
+
import type { Greeting } from '${shared}'
|
|
10456
|
+
import { fetchGreeting } from './greeting.client'
|
|
10457
|
+
|
|
10458
|
+
export interface GreetingViewProps {
|
|
10459
|
+
readonly name: string
|
|
10460
|
+
readonly load?: (name: string) => Promise<Greeting>
|
|
10461
|
+
}
|
|
10462
|
+
|
|
10463
|
+
export function GreetingView ({ name, load = fetchGreeting }: GreetingViewProps) {
|
|
10464
|
+
const [greeting, setGreeting] = useState<Greeting | undefined>()
|
|
10465
|
+
const [failed, setFailed] = useState(false)
|
|
10466
|
+
|
|
10467
|
+
useEffect(() => {
|
|
10468
|
+
void (async () => {
|
|
10469
|
+
try {
|
|
10470
|
+
setGreeting(await load(name))
|
|
10471
|
+
} catch {
|
|
10472
|
+
setFailed(true)
|
|
10473
|
+
}
|
|
10474
|
+
})()
|
|
10475
|
+
}, [name, load])
|
|
10476
|
+
|
|
10477
|
+
if (failed) {
|
|
10478
|
+
return <p>Could not reach the API.</p>
|
|
10479
|
+
}
|
|
10480
|
+
|
|
10481
|
+
return <p>{greeting?.message ?? 'Loading...'}</p>
|
|
10482
|
+
}
|
|
10483
|
+
`;
|
|
10484
|
+
}
|
|
10485
|
+
function webGreetingSpec() {
|
|
10486
|
+
return `import { render } from '@testing-library/react'
|
|
10487
|
+
|
|
10488
|
+
import { GreetingView } from './greeting.component'
|
|
10489
|
+
|
|
10490
|
+
async function answering (name: string): Promise<{ message: string }> {
|
|
10491
|
+
return { message: 'Hello, ' + name + '!' }
|
|
10492
|
+
}
|
|
10493
|
+
|
|
10494
|
+
async function offline (): Promise<never> {
|
|
10495
|
+
throw new Error('offline')
|
|
10496
|
+
}
|
|
10497
|
+
|
|
10498
|
+
describe('GreetingView', () => {
|
|
10499
|
+
it('shows the greeting its loader returns for its name', async () => {
|
|
10500
|
+
const { findByText } = render(<GreetingView name='world' load={answering} />)
|
|
10501
|
+
|
|
10502
|
+
expect(await findByText('Hello, world!')).toBeTruthy()
|
|
10503
|
+
})
|
|
10504
|
+
|
|
10505
|
+
it('says so when the API cannot be reached', async () => {
|
|
10506
|
+
const { findByText } = render(<GreetingView name='world' load={offline} />)
|
|
10507
|
+
|
|
10508
|
+
expect(await findByText('Could not reach the API.')).toBeTruthy()
|
|
10509
|
+
})
|
|
10510
|
+
})
|
|
10511
|
+
`;
|
|
10512
|
+
}
|
|
10513
|
+
function webGreetingBarrel() {
|
|
10514
|
+
return "export * from './greeting.client'\nexport * from './greeting.component'\n";
|
|
10515
|
+
}
|
|
10516
|
+
function webAppSpec() {
|
|
10517
|
+
return `import { render } from '@testing-library/react'
|
|
10518
|
+
|
|
10519
|
+
import App from './app.component'
|
|
10520
|
+
|
|
10521
|
+
describe('App', () => {
|
|
10522
|
+
it('renders, and shows that it is loading the greeting', () => {
|
|
10523
|
+
const { getByText } = render(<App />)
|
|
10524
|
+
|
|
10525
|
+
expect(getByText('Loading...')).toBeTruthy()
|
|
10526
|
+
})
|
|
10527
|
+
})
|
|
10528
|
+
`;
|
|
10529
|
+
}
|
|
10530
|
+
function withApiProxy(config, target) {
|
|
10531
|
+
const start = config.indexOf("server:");
|
|
10532
|
+
const end = start === -1 ? -1 : config.indexOf("},", start);
|
|
10533
|
+
if (end === -1 || config.includes("proxy:")) {
|
|
10534
|
+
return config;
|
|
10535
|
+
}
|
|
10536
|
+
const block = config.slice(start, end).trimEnd();
|
|
10537
|
+
return `${config.slice(0, start)}${block}
|
|
10538
|
+
proxy: { '/api': '${target}' },
|
|
10539
|
+
${config.slice(end)}`;
|
|
10540
|
+
}
|
|
10541
|
+
|
|
10542
|
+
// src/workspace-presets/wire-web-api.use-case.ts
|
|
10543
|
+
var API_ORIGIN = "http://localhost:3000";
|
|
10544
|
+
function declareDependency(manifestPath, name) {
|
|
10545
|
+
const manifest = readJson(manifestPath);
|
|
10546
|
+
writeFileEnsured(manifestPath, toJson({ ...manifest, dependencies: { ...manifest.dependencies, [name]: "*" } }));
|
|
10547
|
+
}
|
|
10548
|
+
function removeFiles(slice, files) {
|
|
10549
|
+
for (const file of files) {
|
|
10550
|
+
(0, import_node_fs40.rmSync)((0, import_node_path51.join)(slice, file), { force: true });
|
|
10551
|
+
}
|
|
10552
|
+
}
|
|
10553
|
+
function replaceOnce(path, anchor, replacement) {
|
|
10554
|
+
const text = (0, import_node_fs40.readFileSync)(path, "utf8");
|
|
10555
|
+
if (!text.includes(anchor)) {
|
|
10556
|
+
throw new Error(`${path} has no ${anchor}; the generator's output changed, so the preset cannot wire it.`);
|
|
10557
|
+
}
|
|
10558
|
+
writeFileEnsured(path, text.replace(anchor, () => replacement));
|
|
10559
|
+
}
|
|
10560
|
+
function proxyApi(viteConfigPath) {
|
|
10561
|
+
const text = (0, import_node_fs40.readFileSync)(viteConfigPath, "utf8");
|
|
10562
|
+
const proxied = withApiProxy(text, API_ORIGIN);
|
|
10563
|
+
if (proxied === text && !text.includes("proxy:")) {
|
|
10564
|
+
throw new Error(`${viteConfigPath} has no server block to add the /api proxy to; the generator's output changed, so the preset cannot wire it.`);
|
|
10565
|
+
}
|
|
10566
|
+
writeFileEnsured(viteConfigPath, proxied);
|
|
10567
|
+
}
|
|
10568
|
+
function wireWebApi(workspaceRoot, projects) {
|
|
10569
|
+
const sharedManifest = (0, import_node_path51.join)(workspaceRoot, "libs", projects.shared, "package.json");
|
|
10570
|
+
if (!fileExists(sharedManifest)) {
|
|
10571
|
+
throw new Error(`The shared library was not added at libs/${projects.shared}, so there is nothing to wire the API and the frontend to.`);
|
|
10572
|
+
}
|
|
10573
|
+
const shared = readJson(sharedManifest).name;
|
|
10574
|
+
const api = (0, import_node_path51.join)(workspaceRoot, "apps", projects.api);
|
|
10575
|
+
declareDependency((0, import_node_path51.join)(api, "package.json"), shared);
|
|
10576
|
+
const hello = (0, import_node_path51.join)(api, "src", "hello");
|
|
10577
|
+
writeFileEnsured((0, import_node_path51.join)(hello, "hello.handler.ts"), apiHandler(shared));
|
|
10578
|
+
writeFileEnsured((0, import_node_path51.join)(hello, "hello.handler.spec.ts"), apiHandlerSpec(shared));
|
|
10579
|
+
writeFileEnsured((0, import_node_path51.join)(hello, "index.ts"), apiBarrel());
|
|
10580
|
+
removeFiles(hello, ["greet.use-case.ts", "greet.use-case.spec.ts", "greeting.contract.ts"]);
|
|
10581
|
+
replaceOnce((0, import_node_path51.join)(api, "src", "main.ts"), "app.get('/', helloHandler)", "app.get('/api/greeting', helloHandler)");
|
|
10582
|
+
const web = (0, import_node_path51.join)(workspaceRoot, "apps", projects.web);
|
|
10583
|
+
declareDependency((0, import_node_path51.join)(web, "package.json"), shared);
|
|
10584
|
+
const greeting = (0, import_node_path51.join)(web, "src", "greeting");
|
|
10585
|
+
writeFileEnsured((0, import_node_path51.join)(greeting, "greeting.client.ts"), webGreetingClient(shared));
|
|
10586
|
+
writeFileEnsured((0, import_node_path51.join)(greeting, "greeting.client.spec.ts"), webGreetingClientSpec());
|
|
10587
|
+
writeFileEnsured((0, import_node_path51.join)(greeting, "greeting.component.tsx"), webGreetingComponent(shared));
|
|
10588
|
+
writeFileEnsured((0, import_node_path51.join)(greeting, "greeting.component.spec.tsx"), webGreetingSpec());
|
|
10589
|
+
writeFileEnsured((0, import_node_path51.join)(greeting, "index.ts"), webGreetingBarrel());
|
|
10590
|
+
removeFiles(greeting, ["greet.use-case.ts", "greet.use-case.spec.ts", "greeting.contract.ts"]);
|
|
10591
|
+
writeFileEnsured((0, import_node_path51.join)(web, "src", "app", "app.component.spec.tsx"), webAppSpec());
|
|
10592
|
+
proxyApi((0, import_node_path51.join)(web, "vite.config.mts"));
|
|
10593
|
+
}
|
|
10594
|
+
|
|
10595
|
+
// src/workspace-presets/apply-preset.use-case.ts
|
|
10596
|
+
function findPreset(id) {
|
|
10597
|
+
const preset = PRESETS.find((candidate) => candidate.id === id);
|
|
10598
|
+
if (preset === void 0) {
|
|
10599
|
+
throw new Error(`Unknown preset '${id}'. Presets: ${PRESET_IDS.join(", ")}.`);
|
|
10600
|
+
}
|
|
10601
|
+
return preset;
|
|
10602
|
+
}
|
|
10603
|
+
function nameOf(preset, kind) {
|
|
10604
|
+
const project = preset.projects.find((candidate) => candidate.kind === kind);
|
|
10605
|
+
if (project === void 0) {
|
|
10606
|
+
throw new Error(`The ${preset.id} preset has no ${kind}.`);
|
|
10607
|
+
}
|
|
10608
|
+
return project.name;
|
|
10609
|
+
}
|
|
10610
|
+
async function applyPreset(workspaceRoot, presetId, dependencies = {}) {
|
|
10611
|
+
const preset = findPreset(presetId);
|
|
10612
|
+
const add = dependencies.add ?? runAdd;
|
|
10613
|
+
const install = dependencies.install ?? ((root) => runShell("npm", ["install", "--ignore-scripts", "--no-audit", "--no-fund"], root));
|
|
10614
|
+
const sync = dependencies.sync ?? syncProjectReferences;
|
|
10615
|
+
const format = dependencies.format ?? runFormatter;
|
|
10616
|
+
logger.step(`Adding the ${preset.id} preset: ${preset.projects.map((project) => project.name).join(", ")}`);
|
|
10617
|
+
const previous = process.cwd();
|
|
10618
|
+
process.chdir(workspaceRoot);
|
|
10619
|
+
try {
|
|
10620
|
+
for (const project of preset.projects) {
|
|
10621
|
+
await add(project.kind, project.name, project.options);
|
|
10622
|
+
}
|
|
10623
|
+
} finally {
|
|
10624
|
+
process.chdir(previous);
|
|
10625
|
+
}
|
|
10626
|
+
wireWebApi(workspaceRoot, { shared: nameOf(preset, "internal-lib"), api: nameOf(preset, "node-app"), web: nameOf(preset, "react-app") });
|
|
10627
|
+
if (install(workspaceRoot) !== 0) {
|
|
10628
|
+
throw new Error("npm install failed after wiring the preset. Run `npm install` and read its error; the projects themselves were added.");
|
|
10629
|
+
}
|
|
10630
|
+
sync(workspaceRoot);
|
|
10631
|
+
format(workspaceRoot);
|
|
10632
|
+
}
|
|
10633
|
+
|
|
10634
|
+
// src/workspace-creation/adopt-directory.use-case.ts
|
|
10635
|
+
var import_node_fs41 = require("fs");
|
|
10636
|
+
var import_node_path52 = require("path");
|
|
10323
10637
|
var NEVER_COPIED = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
10324
10638
|
var AGENT_AND_CI_ENTRIES = [
|
|
10325
10639
|
"AGENTS.md",
|
|
@@ -10350,13 +10664,13 @@ var PREFLIGHT_BLOCKERS = [
|
|
|
10350
10664
|
"packages"
|
|
10351
10665
|
];
|
|
10352
10666
|
function assertAdoptableDirectory(target) {
|
|
10353
|
-
if (!(0,
|
|
10667
|
+
if (!(0, import_node_fs41.existsSync)(target)) {
|
|
10354
10668
|
throw new Error(`--into '${target}' does not exist. Create or clone it first.`);
|
|
10355
10669
|
}
|
|
10356
|
-
if (!(0,
|
|
10670
|
+
if (!(0, import_node_fs41.statSync)(target).isDirectory()) {
|
|
10357
10671
|
throw new Error(`--into '${target}' is not a directory.`);
|
|
10358
10672
|
}
|
|
10359
|
-
const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0,
|
|
10673
|
+
const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs41.existsSync)((0, import_node_path52.join)(target, entry)));
|
|
10360
10674
|
if (blockers.length > 0) {
|
|
10361
10675
|
throw new Error(
|
|
10362
10676
|
`--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.`
|
|
@@ -10364,15 +10678,15 @@ function assertAdoptableDirectory(target) {
|
|
|
10364
10678
|
}
|
|
10365
10679
|
}
|
|
10366
10680
|
function mergeGitignore(generatedFile, targetFile) {
|
|
10367
|
-
const existing = (0,
|
|
10681
|
+
const existing = (0, import_node_fs41.readFileSync)(targetFile, "utf8");
|
|
10368
10682
|
const present = new Set(existing.split("\n").map((line) => line.trim()));
|
|
10369
|
-
const additions = (0,
|
|
10683
|
+
const additions = (0, import_node_fs41.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
|
|
10370
10684
|
const trimmed = line.trim();
|
|
10371
10685
|
return trimmed !== "" && !trimmed.startsWith("#") && !present.has(trimmed);
|
|
10372
10686
|
});
|
|
10373
10687
|
if (additions.length === 0) return false;
|
|
10374
10688
|
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
10375
|
-
(0,
|
|
10689
|
+
(0, import_node_fs41.writeFileSync)(
|
|
10376
10690
|
targetFile,
|
|
10377
10691
|
`${existing}${separator}
|
|
10378
10692
|
# Added by mnci (Nx workspace)
|
|
@@ -10382,9 +10696,9 @@ ${additions.join("\n")}
|
|
|
10382
10696
|
return true;
|
|
10383
10697
|
}
|
|
10384
10698
|
function adoptGeneratedWorkspace(generatedRoot, target) {
|
|
10385
|
-
const entries = (0,
|
|
10699
|
+
const entries = (0, import_node_fs41.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
|
|
10386
10700
|
const collisions = entries.filter(
|
|
10387
|
-
(entry) => (0,
|
|
10701
|
+
(entry) => (0, import_node_fs41.existsSync)((0, import_node_path52.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
|
|
10388
10702
|
);
|
|
10389
10703
|
if (collisions.length > 0) {
|
|
10390
10704
|
throw new Error(
|
|
@@ -10393,28 +10707,28 @@ function adoptGeneratedWorkspace(generatedRoot, target) {
|
|
|
10393
10707
|
}
|
|
10394
10708
|
const result = { copied: [], kept: [], mergedGitignore: false };
|
|
10395
10709
|
for (const entry of entries) {
|
|
10396
|
-
const from = (0,
|
|
10397
|
-
const to = (0,
|
|
10398
|
-
if (entry === ".gitignore" && (0,
|
|
10710
|
+
const from = (0, import_node_path52.join)(generatedRoot, entry);
|
|
10711
|
+
const to = (0, import_node_path52.join)(target, entry);
|
|
10712
|
+
if (entry === ".gitignore" && (0, import_node_fs41.existsSync)(to)) {
|
|
10399
10713
|
result.mergedGitignore = mergeGitignore(from, to);
|
|
10400
10714
|
result.kept.push(entry);
|
|
10401
10715
|
continue;
|
|
10402
10716
|
}
|
|
10403
|
-
if ((0,
|
|
10717
|
+
if ((0, import_node_fs41.existsSync)(to)) {
|
|
10404
10718
|
result.kept.push(entry);
|
|
10405
10719
|
continue;
|
|
10406
10720
|
}
|
|
10407
|
-
(0,
|
|
10721
|
+
(0, import_node_fs41.cpSync)(from, to, { recursive: true });
|
|
10408
10722
|
result.copied.push(entry);
|
|
10409
10723
|
}
|
|
10410
|
-
(0,
|
|
10724
|
+
(0, import_node_fs41.rmSync)(generatedRoot, { recursive: true, force: true });
|
|
10411
10725
|
return result;
|
|
10412
10726
|
}
|
|
10413
10727
|
|
|
10414
10728
|
// src/workspace-creation/create-workspace.use-case.ts
|
|
10415
|
-
var
|
|
10729
|
+
var import_node_fs42 = require("fs");
|
|
10416
10730
|
var import_node_os2 = require("os");
|
|
10417
|
-
var
|
|
10731
|
+
var import_node_path53 = require("path");
|
|
10418
10732
|
function nxCloudProviderValue(ci) {
|
|
10419
10733
|
return ci === "azure" ? "azure" : "github";
|
|
10420
10734
|
}
|
|
@@ -10457,9 +10771,10 @@ async function resolveRegistry2(options) {
|
|
|
10457
10771
|
return await promptRegistry();
|
|
10458
10772
|
}
|
|
10459
10773
|
async function runNew(name, options) {
|
|
10460
|
-
const adoptTarget = options.into === void 0 ? void 0 : (0,
|
|
10774
|
+
const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path53.resolve)(process.cwd(), options.into);
|
|
10461
10775
|
if (adoptTarget !== void 0) assertAdoptableDirectory(adoptTarget);
|
|
10462
|
-
|
|
10776
|
+
if (options.preset !== void 0) findPreset(options.preset);
|
|
10777
|
+
const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path53.basename)(adoptTarget));
|
|
10463
10778
|
assertValidProjectName(workspaceName, "Workspace name");
|
|
10464
10779
|
const scope = options.scope ?? (options.yes ? `@${workspaceName}` : await promptText("npm scope for publishable packages", `@${workspaceName}`));
|
|
10465
10780
|
const registry = await resolveRegistry2(options);
|
|
@@ -10472,8 +10787,8 @@ async function runNew(name, options) {
|
|
|
10472
10787
|
const npmAuth = resolveNpmAuth(options.npmAuth, registry, ci);
|
|
10473
10788
|
const stack = await resolveStack(options);
|
|
10474
10789
|
const nxCloud = options.nxCloud ?? (options.yes ? false : await promptNxCloud());
|
|
10475
|
-
const stagingParent = adoptTarget === void 0 ? process.cwd() : (0,
|
|
10476
|
-
let workspaceRoot = (0,
|
|
10790
|
+
const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs42.mkdtempSync)((0, import_node_path53.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
|
|
10791
|
+
let workspaceRoot = (0, import_node_path53.join)(stagingParent, workspaceName);
|
|
10477
10792
|
try {
|
|
10478
10793
|
logger.step(`Creating Nx workspace '${workspaceName}' (preset: ts)`);
|
|
10479
10794
|
runNpx(
|
|
@@ -10496,7 +10811,7 @@ async function runNew(name, options) {
|
|
|
10496
10811
|
workspaceRoot = adoptTarget;
|
|
10497
10812
|
}
|
|
10498
10813
|
} finally {
|
|
10499
|
-
if (adoptTarget !== void 0) (0,
|
|
10814
|
+
if (adoptTarget !== void 0) (0, import_node_fs42.rmSync)(stagingParent, { recursive: true, force: true });
|
|
10500
10815
|
}
|
|
10501
10816
|
logger.step(
|
|
10502
10817
|
"Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
|
|
@@ -10506,8 +10821,8 @@ async function runNew(name, options) {
|
|
|
10506
10821
|
{ workspaceName, scope, registry, agent, variableGroup, ci, stack, ...npmAuth && { npmAuth } },
|
|
10507
10822
|
logger.detail
|
|
10508
10823
|
);
|
|
10509
|
-
(0,
|
|
10510
|
-
(0,
|
|
10824
|
+
(0, import_node_fs42.rmSync)((0, import_node_path53.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
|
|
10825
|
+
(0, import_node_fs42.rmSync)((0, import_node_path53.join)(workspaceRoot, "package-lock.json"), { force: true });
|
|
10511
10826
|
logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
|
|
10512
10827
|
const installStatus = runShell(
|
|
10513
10828
|
"npm",
|
|
@@ -10520,6 +10835,9 @@ async function runNew(name, options) {
|
|
|
10520
10835
|
runShell("npx", ["husky"], workspaceRoot);
|
|
10521
10836
|
logger.step("Formatting the workspace (eslint --fix, JavaScript Standard Style)");
|
|
10522
10837
|
runFormatter(workspaceRoot);
|
|
10838
|
+
if (options.preset !== void 0) {
|
|
10839
|
+
await applyPreset(workspaceRoot, options.preset);
|
|
10840
|
+
}
|
|
10523
10841
|
logger.success("Done. Next steps:");
|
|
10524
10842
|
if (adoptTarget === void 0) logger.info(` cd ${workspaceName}`);
|
|
10525
10843
|
for (const line of projectKindHintLines()) {
|
|
@@ -10557,6 +10875,8 @@ function buildProgram(cliVersion) {
|
|
|
10557
10875
|
).addOption(new import_commander.Option("--ci <provider>", "CI provider").choices(["azure", "github", "both"])).option("--test-runner <runner>", "unit-test runner: jest | vitest").option("--nx-cloud", "connect the workspace to Nx Cloud (remote caching + CI insights)").option(
|
|
10558
10876
|
"--into <dir>",
|
|
10559
10877
|
"bootstrap into an existing directory (a fresh clone holding only .git); the directory name is the workspace name unless one is given"
|
|
10878
|
+
).addOption(
|
|
10879
|
+
new import_commander.Option("--preset <preset>", "also scaffold a whole shape of workspace, its projects wired together (web-api: a React frontend and an Express API sharing one library)").choices(PRESET_IDS)
|
|
10560
10880
|
).action(async (name, options) => {
|
|
10561
10881
|
await runNew(name, options);
|
|
10562
10882
|
});
|