@mnci/cli 4.50.0 → 4.52.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 +38 -0
- package/dist/cli.js +509 -111
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8239,14 +8239,130 @@ function runKinds(options) {
|
|
|
8239
8239
|
}
|
|
8240
8240
|
}
|
|
8241
8241
|
|
|
8242
|
-
// src/
|
|
8242
|
+
// src/dev-servers/run-dev.use-case.ts
|
|
8243
|
+
var import_prompts4 = require("@inquirer/prompts");
|
|
8244
|
+
|
|
8245
|
+
// src/dev-servers/spawn-project.client.ts
|
|
8243
8246
|
var import_node_child_process = require("child_process");
|
|
8247
|
+
var import_node_readline = require("readline");
|
|
8248
|
+
var import_cross_spawn3 = __toESM(require("cross-spawn"));
|
|
8249
|
+
function spawnProject(workspaceRoot, project, target, onLine) {
|
|
8250
|
+
const windows = process.platform === "win32";
|
|
8251
|
+
const child = (0, import_cross_spawn3.default)("npx", ["nx", "run", `${project}:${target}`], { cwd: workspaceRoot, stdio: ["ignore", "pipe", "pipe"], detached: !windows });
|
|
8252
|
+
for (const stream of [child.stdout, child.stderr]) {
|
|
8253
|
+
if (stream !== null) {
|
|
8254
|
+
(0, import_node_readline.createInterface)({ input: stream }).on("line", onLine);
|
|
8255
|
+
}
|
|
8256
|
+
}
|
|
8257
|
+
const exited = new Promise((resolve2) => {
|
|
8258
|
+
child.on("error", () => {
|
|
8259
|
+
resolve2(1);
|
|
8260
|
+
});
|
|
8261
|
+
child.on("close", (code) => {
|
|
8262
|
+
resolve2(code ?? 1);
|
|
8263
|
+
});
|
|
8264
|
+
});
|
|
8265
|
+
const stop = () => {
|
|
8266
|
+
if (child.pid === void 0 || child.exitCode !== null) {
|
|
8267
|
+
return;
|
|
8268
|
+
}
|
|
8269
|
+
if (windows) {
|
|
8270
|
+
(0, import_node_child_process.spawnSync)("taskkill", ["/pid", String(child.pid), "/T", "/F"], { stdio: "ignore" });
|
|
8271
|
+
} else {
|
|
8272
|
+
try {
|
|
8273
|
+
process.kill(-child.pid, "SIGTERM");
|
|
8274
|
+
} catch {
|
|
8275
|
+
child.kill("SIGTERM");
|
|
8276
|
+
}
|
|
8277
|
+
}
|
|
8278
|
+
};
|
|
8279
|
+
return { stop, exited };
|
|
8280
|
+
}
|
|
8281
|
+
|
|
8282
|
+
// src/dev-servers/startable-projects.use-case.ts
|
|
8283
|
+
var import_node_path36 = require("path");
|
|
8284
|
+
var START_SUFFIX = ":start";
|
|
8285
|
+
var NX_RUN = /\bnx run [^\s:]+:(\S+)/;
|
|
8286
|
+
function listStartableProjects(workspaceRoot) {
|
|
8287
|
+
const manifestPath = (0, import_node_path36.join)(workspaceRoot, "package.json");
|
|
8288
|
+
if (!fileExists(manifestPath)) {
|
|
8289
|
+
return [];
|
|
8290
|
+
}
|
|
8291
|
+
const scripts = readJson(manifestPath).scripts ?? {};
|
|
8292
|
+
return Object.entries(scripts).filter(([script]) => script.endsWith(START_SUFFIX)).map(([script, command]) => ({ project: script.slice(0, -START_SUFFIX.length), target: NX_RUN.exec(command)?.[1] ?? "start" })).toSorted((a, b) => a.project.localeCompare(b.project));
|
|
8293
|
+
}
|
|
8294
|
+
|
|
8295
|
+
// src/dev-servers/run-dev.use-case.ts
|
|
8296
|
+
async function chooseProjects(available) {
|
|
8297
|
+
return await (0, import_prompts4.checkbox)({
|
|
8298
|
+
message: "Which projects should start together?",
|
|
8299
|
+
choices: available.map((name) => ({ name, value: name })),
|
|
8300
|
+
required: true
|
|
8301
|
+
});
|
|
8302
|
+
}
|
|
8303
|
+
async function selectProjects(available, named, options, dependencies) {
|
|
8304
|
+
const names = available.map((entry) => entry.project);
|
|
8305
|
+
const unknown = named.filter((name) => !names.includes(name));
|
|
8306
|
+
if (unknown.length > 0) {
|
|
8307
|
+
throw new Error(`Cannot start ${unknown.join(", ")}: not a project with a start command. Startable: ${names.join(", ")}.`);
|
|
8308
|
+
}
|
|
8309
|
+
let chosen = named;
|
|
8310
|
+
if (chosen.length === 0) {
|
|
8311
|
+
if (options.all === true) {
|
|
8312
|
+
chosen = names;
|
|
8313
|
+
} else if (dependencies.interactive ?? process.stdin.isTTY) {
|
|
8314
|
+
chosen = await (dependencies.choose ?? chooseProjects)(names);
|
|
8315
|
+
} else {
|
|
8316
|
+
throw new Error(`Name the projects to start, or pass --all. Startable: ${names.join(", ")}.`);
|
|
8317
|
+
}
|
|
8318
|
+
}
|
|
8319
|
+
return available.filter((entry) => chosen.includes(entry.project));
|
|
8320
|
+
}
|
|
8321
|
+
async function runDev(workspaceRoot, named, options = {}, dependencies = {}) {
|
|
8322
|
+
const available = listStartableProjects(workspaceRoot);
|
|
8323
|
+
if (available.length === 0) {
|
|
8324
|
+
throw new Error("No project here has a start command. Add an app (`mnci add`) first; libraries are not started.");
|
|
8325
|
+
}
|
|
8326
|
+
const selected = await selectProjects(available, named, options, dependencies);
|
|
8327
|
+
for (const { project, target } of selected) {
|
|
8328
|
+
logger.info(`${project}: nx run ${project}:${target}`);
|
|
8329
|
+
}
|
|
8330
|
+
if (options.dryRun === true) {
|
|
8331
|
+
return;
|
|
8332
|
+
}
|
|
8333
|
+
const start = dependencies.start ?? ((project, target, onLine) => spawnProject(workspaceRoot, project, target, onLine));
|
|
8334
|
+
const print = dependencies.print ?? ((line) => {
|
|
8335
|
+
console.log(line);
|
|
8336
|
+
});
|
|
8337
|
+
const running = selected.map(({ project, target }) => start(project, target, (line) => {
|
|
8338
|
+
print(`[${project}] ${line}`);
|
|
8339
|
+
}));
|
|
8340
|
+
const stopAll = () => {
|
|
8341
|
+
for (const each of running) {
|
|
8342
|
+
each.stop();
|
|
8343
|
+
}
|
|
8344
|
+
};
|
|
8345
|
+
process.once("SIGINT", stopAll);
|
|
8346
|
+
process.once("SIGTERM", stopAll);
|
|
8347
|
+
const first = await Promise.race(running.map(async (each, index) => ({ index, status: await each.exited })));
|
|
8348
|
+
stopAll();
|
|
8349
|
+
await Promise.all(running.map(async (each) => await each.exited));
|
|
8350
|
+
process.removeListener("SIGINT", stopAll);
|
|
8351
|
+
process.removeListener("SIGTERM", stopAll);
|
|
8352
|
+
logger.info(`${selected[first.index].project} stopped (${first.status}), so the rest were stopped too.`);
|
|
8353
|
+
if (first.status !== 0) {
|
|
8354
|
+
process.exitCode = first.status;
|
|
8355
|
+
}
|
|
8356
|
+
}
|
|
8357
|
+
|
|
8358
|
+
// src/workspace-diagnostics/check-invariants.use-case.ts
|
|
8359
|
+
var import_node_child_process2 = require("child_process");
|
|
8244
8360
|
var import_node_fs32 = require("fs");
|
|
8245
|
-
var
|
|
8361
|
+
var import_node_path38 = require("path");
|
|
8246
8362
|
|
|
8247
8363
|
// src/workspace-diagnostics/locate-stranded-release-tags.use-case.ts
|
|
8248
8364
|
var import_node_fs31 = require("fs");
|
|
8249
|
-
var
|
|
8365
|
+
var import_node_path37 = require("path");
|
|
8250
8366
|
|
|
8251
8367
|
// src/workspace-diagnostics/stranded-release-tags.algorithm.ts
|
|
8252
8368
|
function compareVersions2(left, right) {
|
|
@@ -8283,7 +8399,7 @@ function locateStrandedReleaseTags(workspaceRoot, tagPattern) {
|
|
|
8283
8399
|
if (listed.status !== 0) {
|
|
8284
8400
|
return [];
|
|
8285
8401
|
}
|
|
8286
|
-
const projects = (0, import_node_fs31.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot }).map((path) => readJson((0,
|
|
8402
|
+
const projects = (0, import_node_fs31.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot }).map((path) => readJson((0, import_node_path37.join)(workspaceRoot, path))).filter((manifest) => manifest.private !== true && manifest.name !== void 0).map((manifest) => manifest.name);
|
|
8287
8403
|
return findStrandedReleaseTags(projects, listed.stdout.split(/\r?\n/).filter(Boolean));
|
|
8288
8404
|
}
|
|
8289
8405
|
|
|
@@ -8314,9 +8430,9 @@ function checkEslintConfigs(workspaceRoot) {
|
|
|
8314
8430
|
];
|
|
8315
8431
|
}
|
|
8316
8432
|
function checkEslintEntryPointReachesTheRules(workspaceRoot) {
|
|
8317
|
-
const rules = (0,
|
|
8433
|
+
const rules = (0, import_node_path38.join)(workspaceRoot, ESLINT_MNCI_FILENAME);
|
|
8318
8434
|
if (!(0, import_node_fs32.existsSync)(rules)) return [];
|
|
8319
|
-
const entryPoint = (0,
|
|
8435
|
+
const entryPoint = (0, import_node_path38.join)(workspaceRoot, ESLINT_USER_FILENAME);
|
|
8320
8436
|
const source = (0, import_node_fs32.existsSync)(entryPoint) ? (0, import_node_fs32.readFileSync)(entryPoint, "utf8") : "";
|
|
8321
8437
|
return [
|
|
8322
8438
|
{
|
|
@@ -8330,7 +8446,7 @@ function checkEslintEntryPointReachesTheRules(workspaceRoot) {
|
|
|
8330
8446
|
function declaredDevDependencies(workspaceRoot) {
|
|
8331
8447
|
try {
|
|
8332
8448
|
const manifest = readJson(
|
|
8333
|
-
(0,
|
|
8449
|
+
(0, import_node_path38.join)(workspaceRoot, "package.json")
|
|
8334
8450
|
);
|
|
8335
8451
|
return manifest.devDependencies ?? {};
|
|
8336
8452
|
} catch {
|
|
@@ -8338,7 +8454,7 @@ function declaredDevDependencies(workspaceRoot) {
|
|
|
8338
8454
|
}
|
|
8339
8455
|
}
|
|
8340
8456
|
function checkNoRetiredFormatter(workspaceRoot) {
|
|
8341
|
-
const files = RETIRED_FORMATTER_FILES.filter((file) => fileExists((0,
|
|
8457
|
+
const files = RETIRED_FORMATTER_FILES.filter((file) => fileExists((0, import_node_path38.join)(workspaceRoot, file)));
|
|
8342
8458
|
const declared = ["prettier", "eslint-config-prettier", "oxlint", "oxfmt", "@mnci/oxlint-config"].filter((name) => declaredDevDependencies(workspaceRoot)[name] !== void 0);
|
|
8343
8459
|
const stale = [...files, ...declared];
|
|
8344
8460
|
if (stale.length === 0) {
|
|
@@ -8364,7 +8480,7 @@ function checkEslintPlugin(nxJson) {
|
|
|
8364
8480
|
};
|
|
8365
8481
|
}
|
|
8366
8482
|
function checkResolvedEslint(workspaceRoot) {
|
|
8367
|
-
const manifestPath = (0,
|
|
8483
|
+
const manifestPath = (0, import_node_path38.join)(workspaceRoot, "node_modules/eslint/package.json");
|
|
8368
8484
|
if (!fileExists(manifestPath)) {
|
|
8369
8485
|
return void 0;
|
|
8370
8486
|
}
|
|
@@ -8382,7 +8498,7 @@ function checkResolvedEslint(workspaceRoot) {
|
|
|
8382
8498
|
}
|
|
8383
8499
|
}
|
|
8384
8500
|
function checkNpmrc(workspaceRoot, registry, scope) {
|
|
8385
|
-
const npmrcPath = (0,
|
|
8501
|
+
const npmrcPath = (0, import_node_path38.join)(workspaceRoot, ".npmrc");
|
|
8386
8502
|
if (!registry || !fileExists(npmrcPath)) {
|
|
8387
8503
|
return void 0;
|
|
8388
8504
|
}
|
|
@@ -8403,7 +8519,7 @@ function checkNpmrc(workspaceRoot, registry, scope) {
|
|
|
8403
8519
|
};
|
|
8404
8520
|
}
|
|
8405
8521
|
function checkNpmrcCredentialResolves(workspaceRoot, environment = process.env) {
|
|
8406
|
-
const npmrcPath = (0,
|
|
8522
|
+
const npmrcPath = (0, import_node_path38.join)(workspaceRoot, ".npmrc");
|
|
8407
8523
|
if (!fileExists(npmrcPath)) {
|
|
8408
8524
|
return void 0;
|
|
8409
8525
|
}
|
|
@@ -8431,8 +8547,8 @@ function checkNpmrcCredentialResolves(workspaceRoot, environment = process.env)
|
|
|
8431
8547
|
};
|
|
8432
8548
|
}
|
|
8433
8549
|
function checkNpmAuthMatchesPipeline(workspaceRoot, registry) {
|
|
8434
|
-
const npmrcPath = (0,
|
|
8435
|
-
const pipelinePath = (0,
|
|
8550
|
+
const npmrcPath = (0, import_node_path38.join)(workspaceRoot, ".npmrc");
|
|
8551
|
+
const pipelinePath = (0, import_node_path38.join)(workspaceRoot, "azure-pipelines.yml");
|
|
8436
8552
|
if (registry?.kind !== "azure-artifacts" || !fileExists(npmrcPath) || !fileExists(pipelinePath)) {
|
|
8437
8553
|
return void 0;
|
|
8438
8554
|
}
|
|
@@ -8488,7 +8604,7 @@ function checkVersionActions(workspaceRoot) {
|
|
|
8488
8604
|
].map((manifest) => toPosix2(manifest));
|
|
8489
8605
|
return candidates.flatMap((manifest) => {
|
|
8490
8606
|
const projectRoot = manifest.slice(0, manifest.lastIndexOf("/"));
|
|
8491
|
-
const projectJsonPath = (0,
|
|
8607
|
+
const projectJsonPath = (0, import_node_path38.join)(workspaceRoot, projectRoot, "project.json");
|
|
8492
8608
|
let hasOverride = false;
|
|
8493
8609
|
try {
|
|
8494
8610
|
const projectJson = readJson(projectJsonPath);
|
|
@@ -8517,7 +8633,7 @@ function declaredTargets(workspaceRoot, projectRoot) {
|
|
|
8517
8633
|
]) {
|
|
8518
8634
|
try {
|
|
8519
8635
|
const targets = extract(
|
|
8520
|
-
readJson((0,
|
|
8636
|
+
readJson((0, import_node_path38.join)(workspaceRoot, projectRoot, file))
|
|
8521
8637
|
);
|
|
8522
8638
|
if (targets && typeof targets === "object") {
|
|
8523
8639
|
sources.push({
|
|
@@ -8549,7 +8665,7 @@ function checkOneTargetsFiles(workspaceRoot, source, targetName, target) {
|
|
|
8549
8665
|
return TARGET_FILE_OPTIONS.flatMap((option) => {
|
|
8550
8666
|
const value = target?.options?.[option];
|
|
8551
8667
|
const checkable = typeof value === "string" && !value.includes("{");
|
|
8552
|
-
if (!checkable || fileExists((0,
|
|
8668
|
+
if (!checkable || fileExists((0, import_node_path38.join)(workspaceRoot, value))) {
|
|
8553
8669
|
return [];
|
|
8554
8670
|
}
|
|
8555
8671
|
return [
|
|
@@ -8574,7 +8690,7 @@ ${result.stdout.trim()}` : ""}`,
|
|
|
8574
8690
|
}
|
|
8575
8691
|
function hasCCompiler() {
|
|
8576
8692
|
const candidates = [process.env.CC?.split(" ", 1)[0], "gcc", "clang", "cc"];
|
|
8577
|
-
return candidates.some((command) => command !== void 0 && command !== "" && (0,
|
|
8693
|
+
return candidates.some((command) => command !== void 0 && command !== "" && (0, import_node_child_process2.spawnSync)(command, ["--version"], { stdio: "ignore" }).status === 0);
|
|
8578
8694
|
}
|
|
8579
8695
|
function checkGoPluginRegistered(workspaceRoot) {
|
|
8580
8696
|
if (!hasGoProject(workspaceRoot)) {
|
|
@@ -8588,7 +8704,7 @@ function checkGoPluginRegistered(workspaceRoot) {
|
|
|
8588
8704
|
};
|
|
8589
8705
|
}
|
|
8590
8706
|
function checkGoWorkInSync(workspaceRoot) {
|
|
8591
|
-
const goWorkPath = (0,
|
|
8707
|
+
const goWorkPath = (0, import_node_path38.join)(workspaceRoot, "go.work");
|
|
8592
8708
|
if (!fileExists(goWorkPath)) {
|
|
8593
8709
|
return void 0;
|
|
8594
8710
|
}
|
|
@@ -8596,7 +8712,7 @@ function checkGoWorkInSync(workspaceRoot) {
|
|
|
8596
8712
|
const blockEntries = Array.from(contents.matchAll(/(?:^|\n)\s*use\s+\(([^)]*)\)/g), (match) => match[1].split("\n")).flat();
|
|
8597
8713
|
const singleEntries = Array.from(contents.matchAll(/(?:^|\n)\s*use\s+(\.\S+)/g), (match) => match[1]);
|
|
8598
8714
|
const uses = [...blockEntries, ...singleEntries].map((entry) => entry.trim()).filter(Boolean);
|
|
8599
|
-
const stale = uses.filter((entry) => !(0, import_node_fs32.existsSync)((0,
|
|
8715
|
+
const stale = uses.filter((entry) => !(0, import_node_fs32.existsSync)((0, import_node_path38.join)(workspaceRoot, entry, "go.mod")));
|
|
8600
8716
|
return {
|
|
8601
8717
|
check: "every go.work `use` entry points to a module that still exists",
|
|
8602
8718
|
ok: stale.length === 0,
|
|
@@ -8612,9 +8728,9 @@ function checkNativeApps(workspaceRoot) {
|
|
|
8612
8728
|
{ file: ".github/workflows/ci.yml", marker: "native (${{ matrix.os }})" },
|
|
8613
8729
|
{ file: "azure-pipelines.yml", marker: "- job: native" }
|
|
8614
8730
|
];
|
|
8615
|
-
const findings = pipelines.filter(({ file }) => fileExists((0,
|
|
8731
|
+
const findings = pipelines.filter(({ file }) => fileExists((0, import_node_path38.join)(workspaceRoot, file))).map(({ file, marker }) => ({
|
|
8616
8732
|
check: `${file} builds the native apps on every OS`,
|
|
8617
|
-
ok: (0, import_node_fs32.readFileSync)((0,
|
|
8733
|
+
ok: (0, import_node_fs32.readFileSync)((0, import_node_path38.join)(workspaceRoot, file), "utf8").includes(marker),
|
|
8618
8734
|
detail: `${file} has no native job: CI would verify a cgo app on the one agent that cannot build it`,
|
|
8619
8735
|
remedy: "run `mnci upgrade`"
|
|
8620
8736
|
}));
|
|
@@ -8627,7 +8743,7 @@ function checkNativeApps(workspaceRoot) {
|
|
|
8627
8743
|
return findings;
|
|
8628
8744
|
}
|
|
8629
8745
|
function checkNoRootRuntimeDependencies(workspaceRoot) {
|
|
8630
|
-
const manifestPath = (0,
|
|
8746
|
+
const manifestPath = (0, import_node_path38.join)(workspaceRoot, "package.json");
|
|
8631
8747
|
if (!fileExists(manifestPath)) {
|
|
8632
8748
|
return void 0;
|
|
8633
8749
|
}
|
|
@@ -8651,7 +8767,7 @@ function checkRollupSourceMaps(workspaceRoot) {
|
|
|
8651
8767
|
cwd: workspaceRoot
|
|
8652
8768
|
}).map((config) => toPosix2(config));
|
|
8653
8769
|
return configs.flatMap((relativePath) => {
|
|
8654
|
-
const configPath = (0,
|
|
8770
|
+
const configPath = (0, import_node_path38.join)(workspaceRoot, relativePath);
|
|
8655
8771
|
let config;
|
|
8656
8772
|
try {
|
|
8657
8773
|
config = (0, import_node_fs32.readFileSync)(configPath, "utf8");
|
|
@@ -8673,7 +8789,7 @@ function checkDeclarationSpecifiers(workspaceRoot) {
|
|
|
8673
8789
|
cwd: workspaceRoot
|
|
8674
8790
|
}).map((config) => toPosix2(config));
|
|
8675
8791
|
return configs.flatMap((relativePath) => {
|
|
8676
|
-
const configPath = (0,
|
|
8792
|
+
const configPath = (0, import_node_path38.join)(workspaceRoot, relativePath);
|
|
8677
8793
|
if (!fileExists(configPath)) {
|
|
8678
8794
|
return [];
|
|
8679
8795
|
}
|
|
@@ -8704,8 +8820,8 @@ function checkDeclarationSpecifiers(workspaceRoot) {
|
|
|
8704
8820
|
}
|
|
8705
8821
|
function checkPipelinePhases(workspaceRoot) {
|
|
8706
8822
|
const files = ["azure-pipelines.yml", ".github/workflows/ci.yml"];
|
|
8707
|
-
return files.filter((file) => fileExists((0,
|
|
8708
|
-
const { verifyActive, switchedOff } = inspectPipeline((0, import_node_fs32.readFileSync)((0,
|
|
8823
|
+
return files.filter((file) => fileExists((0, import_node_path38.join)(workspaceRoot, file))).flatMap((file) => {
|
|
8824
|
+
const { verifyActive, switchedOff } = inspectPipeline((0, import_node_fs32.readFileSync)((0, import_node_path38.join)(workspaceRoot, file), "utf8"));
|
|
8709
8825
|
const verify = {
|
|
8710
8826
|
check: `${file} runs the enforced verify phase`,
|
|
8711
8827
|
ok: verifyActive,
|
|
@@ -8746,7 +8862,7 @@ function checkDependencyRangesNotPreserved(nxJson) {
|
|
|
8746
8862
|
};
|
|
8747
8863
|
}
|
|
8748
8864
|
function checkLockfileHasNoLocalRegistry(workspaceRoot) {
|
|
8749
|
-
if (!fileExists((0,
|
|
8865
|
+
if (!fileExists((0, import_node_path38.join)(workspaceRoot, "package-lock.json"))) {
|
|
8750
8866
|
return void 0;
|
|
8751
8867
|
}
|
|
8752
8868
|
return {
|
|
@@ -8766,7 +8882,7 @@ function checkReleaseTagsResolve(workspaceRoot, nxJson) {
|
|
|
8766
8882
|
};
|
|
8767
8883
|
}
|
|
8768
8884
|
function collectFindings(workspaceRoot) {
|
|
8769
|
-
const nxJsonPath = (0,
|
|
8885
|
+
const nxJsonPath = (0, import_node_path38.join)(workspaceRoot, "nx.json");
|
|
8770
8886
|
if (!fileExists(nxJsonPath)) {
|
|
8771
8887
|
throw new Error(
|
|
8772
8888
|
`No nx.json found in ${workspaceRoot} \u2014 run 'mnci doctor' from the workspace root.`
|
|
@@ -8832,7 +8948,7 @@ function runDoctor(workspaceRoot, options = {}) {
|
|
|
8832
8948
|
|
|
8833
8949
|
// src/repository-adoption/adopt-dependencies.use-case.ts
|
|
8834
8950
|
var import_node_fs33 = require("fs");
|
|
8835
|
-
var
|
|
8951
|
+
var import_node_path39 = require("path");
|
|
8836
8952
|
|
|
8837
8953
|
// src/repository-adoption/clean-working-tree.validator.ts
|
|
8838
8954
|
function requireCleanWorkingTree(repositoryRoot, capture = runCapture) {
|
|
@@ -8903,7 +9019,7 @@ var SOURCE_FILE = /\.(?:[cm]?[jt]s|[jt]sx)$/;
|
|
|
8903
9019
|
var TEST_FILE = /\.(?:spec|test)\.[^.]+$|[\\/](?:__tests__|tests?|e2e)[\\/]/;
|
|
8904
9020
|
var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", "out", ".git", ".nx", "tmp"]);
|
|
8905
9021
|
function sourceFiles(directory, relative = "") {
|
|
8906
|
-
const entries = (0, import_node_fs33.readdirSync)((0,
|
|
9022
|
+
const entries = (0, import_node_fs33.readdirSync)((0, import_node_path39.join)(directory, relative), { withFileTypes: true });
|
|
8907
9023
|
const found = [];
|
|
8908
9024
|
for (const entry of entries) {
|
|
8909
9025
|
const path = relative === "" ? entry.name : `${relative}/${entry.name}`;
|
|
@@ -8930,7 +9046,7 @@ function importsOfProject(projectDirectory, wanted) {
|
|
|
8930
9046
|
const tested = /* @__PURE__ */ new Set();
|
|
8931
9047
|
const files = sourceFiles(projectDirectory);
|
|
8932
9048
|
for (const file of files) {
|
|
8933
|
-
const names = wantedImportsOf((0,
|
|
9049
|
+
const names = wantedImportsOf((0, import_node_path39.join)(projectDirectory, file), wanted);
|
|
8934
9050
|
const target = TEST_FILE.test(file) ? tested : shipped;
|
|
8935
9051
|
for (const name of names) {
|
|
8936
9052
|
target.add(name);
|
|
@@ -8942,7 +9058,7 @@ function importsOfProject(projectDirectory, wanted) {
|
|
|
8942
9058
|
function findUsage(repositoryRoot, projectDirectories, wanted) {
|
|
8943
9059
|
const usage = {};
|
|
8944
9060
|
for (const dir of projectDirectories) {
|
|
8945
|
-
const { shipped, testsOnly } = importsOfProject((0,
|
|
9061
|
+
const { shipped, testsOnly } = importsOfProject((0, import_node_path39.join)(repositoryRoot, dir), wanted);
|
|
8946
9062
|
for (const name of shipped) {
|
|
8947
9063
|
usage[name] ??= { runtime: [], testsOnly: [] };
|
|
8948
9064
|
usage[name].runtime.push(dir);
|
|
@@ -8957,7 +9073,7 @@ function findUsage(repositoryRoot, projectDirectories, wanted) {
|
|
|
8957
9073
|
function adoptDependencies(repositoryRoot, dependencies = {}) {
|
|
8958
9074
|
const run = dependencies.run ?? runShell;
|
|
8959
9075
|
requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
|
|
8960
|
-
const rootPath = (0,
|
|
9076
|
+
const rootPath = (0, import_node_path39.join)(repositoryRoot, "package.json");
|
|
8961
9077
|
if (!fileExists(rootPath)) {
|
|
8962
9078
|
throw new Error("No package.json at the repository root.");
|
|
8963
9079
|
}
|
|
@@ -8993,7 +9109,7 @@ function adoptDependencies(repositoryRoot, dependencies = {}) {
|
|
|
8993
9109
|
|
|
8994
9110
|
// src/repository-adoption/adopt-kinds.use-case.ts
|
|
8995
9111
|
var import_node_fs34 = require("fs");
|
|
8996
|
-
var
|
|
9112
|
+
var import_node_path40 = require("path");
|
|
8997
9113
|
|
|
8998
9114
|
// src/repository-adoption/infer-project-kind.algorithm.ts
|
|
8999
9115
|
var SERVER_FRAMEWORKS = ["express", "koa", "fastify", "@nestjs/core"];
|
|
@@ -9094,32 +9210,32 @@ function inferProjectKind(evidence) {
|
|
|
9094
9210
|
// src/repository-adoption/adopt-kinds.use-case.ts
|
|
9095
9211
|
var TYPE_TAG = "type:";
|
|
9096
9212
|
function readEvidence(repositoryRoot, location) {
|
|
9097
|
-
const directory = (0,
|
|
9213
|
+
const directory = (0, import_node_path40.join)(repositoryRoot, location.dir);
|
|
9098
9214
|
const files = (0, import_node_fs34.readdirSync)(directory);
|
|
9099
9215
|
const manifest = location.ecosystem === "npm" ? readJson(location.manifestPath) : {};
|
|
9100
9216
|
const text = location.ecosystem === "npm" ? "" : (0, import_node_fs34.readFileSync)(location.manifestPath, "utf8");
|
|
9101
9217
|
let hasProgram = false;
|
|
9102
9218
|
if (location.ecosystem === "go") {
|
|
9103
|
-
hasProgram = files.filter((file) => file.endsWith(".go")).some((file) => /^package main\b/m.test((0, import_node_fs34.readFileSync)((0,
|
|
9219
|
+
hasProgram = files.filter((file) => file.endsWith(".go")).some((file) => /^package main\b/m.test((0, import_node_fs34.readFileSync)((0, import_node_path40.join)(directory, file), "utf8")));
|
|
9104
9220
|
} else if (location.ecosystem === "pub") {
|
|
9105
|
-
hasProgram = (0, import_node_fs34.existsSync)((0,
|
|
9221
|
+
hasProgram = (0, import_node_fs34.existsSync)((0, import_node_path40.join)(directory, "lib", "main.dart"));
|
|
9106
9222
|
}
|
|
9107
9223
|
return { ecosystem: location.ecosystem, manifest, text, files, hasProgram };
|
|
9108
9224
|
}
|
|
9109
9225
|
function existingKind(directory) {
|
|
9110
9226
|
const sources = [];
|
|
9111
|
-
if (fileExists((0,
|
|
9112
|
-
sources.push(readJson((0,
|
|
9227
|
+
if (fileExists((0, import_node_path40.join)(directory, "project.json"))) {
|
|
9228
|
+
sources.push(readJson((0, import_node_path40.join)(directory, "project.json")).tags ?? []);
|
|
9113
9229
|
}
|
|
9114
|
-
if (fileExists((0,
|
|
9115
|
-
sources.push(readJson((0,
|
|
9230
|
+
if (fileExists((0, import_node_path40.join)(directory, "package.json"))) {
|
|
9231
|
+
sources.push(readJson((0, import_node_path40.join)(directory, "package.json")).nx?.tags ?? []);
|
|
9116
9232
|
}
|
|
9117
9233
|
return sources.flat().find((tag) => tag.startsWith(TYPE_TAG))?.slice(TYPE_TAG.length);
|
|
9118
9234
|
}
|
|
9119
9235
|
function writeKindTag(directory, name, kind) {
|
|
9120
9236
|
const tag = `${TYPE_TAG}${kind}`;
|
|
9121
|
-
const projectJson = (0,
|
|
9122
|
-
const packageJson = (0,
|
|
9237
|
+
const projectJson = (0, import_node_path40.join)(directory, "project.json");
|
|
9238
|
+
const packageJson = (0, import_node_path40.join)(directory, "package.json");
|
|
9123
9239
|
if (fileExists(projectJson)) {
|
|
9124
9240
|
const project = readJson(projectJson);
|
|
9125
9241
|
writeFileEnsured(projectJson, toJson({ ...project, tags: [...project.tags ?? [], tag] }));
|
|
@@ -9143,7 +9259,7 @@ function adoptKinds(repositoryRoot, overrides = {}, dependencies = {}) {
|
|
|
9143
9259
|
}
|
|
9144
9260
|
}
|
|
9145
9261
|
return locations.map((location) => {
|
|
9146
|
-
const directory = (0,
|
|
9262
|
+
const directory = (0, import_node_path40.join)(repositoryRoot, location.dir);
|
|
9147
9263
|
const present = existingKind(directory);
|
|
9148
9264
|
if (present !== void 0) {
|
|
9149
9265
|
return { dir: location.dir, outcome: "already", kind: present, reason: PROJECT_KINDS.includes(present) ? "it already has a type tag" : "it already has a type tag of its own, which is not an mnci kind name; left as it is" };
|
|
@@ -9160,11 +9276,11 @@ function adoptKinds(repositoryRoot, overrides = {}, dependencies = {}) {
|
|
|
9160
9276
|
|
|
9161
9277
|
// src/repository-adoption/adopt-overlay.use-case.ts
|
|
9162
9278
|
var import_node_fs36 = require("fs");
|
|
9163
|
-
var
|
|
9279
|
+
var import_node_path42 = require("path");
|
|
9164
9280
|
|
|
9165
9281
|
// src/workspace-upgrade/upgrade-workspace.use-case.ts
|
|
9166
9282
|
var import_node_fs35 = require("fs");
|
|
9167
|
-
var
|
|
9283
|
+
var import_node_path41 = require("path");
|
|
9168
9284
|
function resolveRegistry(options, persisted) {
|
|
9169
9285
|
if (options.registry === "azure-artifacts" || options.organization && options.artifactsFeed) {
|
|
9170
9286
|
const persistedAzure = persisted?.kind === "azure-artifacts" ? persisted : void 0;
|
|
@@ -9201,11 +9317,11 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
|
|
|
9201
9317
|
}
|
|
9202
9318
|
} catch {
|
|
9203
9319
|
}
|
|
9204
|
-
return (0,
|
|
9320
|
+
return (0, import_node_path41.basename)(workspaceRoot);
|
|
9205
9321
|
}
|
|
9206
9322
|
function detectNpmAuth(workspaceRoot) {
|
|
9207
9323
|
try {
|
|
9208
|
-
return (0, import_node_fs35.readFileSync)((0,
|
|
9324
|
+
return (0, import_node_fs35.readFileSync)((0, import_node_path41.join)(workspaceRoot, "azure-pipelines.yml"), "utf8").includes("npmAuthenticate@0") ? "build-identity" : void 0;
|
|
9209
9325
|
} catch {
|
|
9210
9326
|
return void 0;
|
|
9211
9327
|
}
|
|
@@ -9252,7 +9368,7 @@ function resolveOverlayOptions(workspaceRoot, options, persisted) {
|
|
|
9252
9368
|
};
|
|
9253
9369
|
}
|
|
9254
9370
|
function runUpgrade(workspaceRoot, options) {
|
|
9255
|
-
if (!fileExists((0,
|
|
9371
|
+
if (!fileExists((0, import_node_path41.join)(workspaceRoot, "nx.json"))) {
|
|
9256
9372
|
throw new Error(
|
|
9257
9373
|
`No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
|
|
9258
9374
|
);
|
|
@@ -9330,7 +9446,7 @@ function runUpgrade(workspaceRoot, options) {
|
|
|
9330
9446
|
"Formatting the workspace (eslint --fix) \u2014 the slowest step, minutes on a large workspace"
|
|
9331
9447
|
);
|
|
9332
9448
|
runFormatter(workspaceRoot);
|
|
9333
|
-
const nxJson = readJson((0,
|
|
9449
|
+
const nxJson = readJson((0, import_node_path41.join)(workspaceRoot, "nx.json"));
|
|
9334
9450
|
const stranded = locateStrandedReleaseTags(workspaceRoot, nxJson.release?.releaseTag?.pattern);
|
|
9335
9451
|
for (const entry of stranded) {
|
|
9336
9452
|
logger.warn(`${entry.project} has release tags only under ${entry.oldTag}: nx release would restart it from its disk version. Run \`mnci adopt --tags\` before the next release to create ${entry.newTag} locally (\`mnci doctor\` lists them all).`);
|
|
@@ -9341,8 +9457,8 @@ function runUpgrade(workspaceRoot, options) {
|
|
|
9341
9457
|
|
|
9342
9458
|
// src/repository-adoption/adopt-overlay.use-case.ts
|
|
9343
9459
|
function detectCiProvider(repositoryRoot) {
|
|
9344
|
-
const azure = (0, import_node_fs36.existsSync)((0,
|
|
9345
|
-
const workflows = (0,
|
|
9460
|
+
const azure = (0, import_node_fs36.existsSync)((0, import_node_path42.join)(repositoryRoot, "azure-pipelines.yml"));
|
|
9461
|
+
const workflows = (0, import_node_path42.join)(repositoryRoot, ".github", "workflows");
|
|
9346
9462
|
const github = (0, import_node_fs36.existsSync)(workflows) && (0, import_node_fs36.readdirSync)(workflows).some((file) => /\.ya?ml$/.test(file));
|
|
9347
9463
|
if (azure && github) {
|
|
9348
9464
|
return "both";
|
|
@@ -9353,7 +9469,7 @@ function detectCiProvider(repositoryRoot) {
|
|
|
9353
9469
|
return github ? "github" : void 0;
|
|
9354
9470
|
}
|
|
9355
9471
|
function detectTestRunner(repositoryRoot) {
|
|
9356
|
-
const manifestPath = (0,
|
|
9472
|
+
const manifestPath = (0, import_node_path42.join)(repositoryRoot, "package.json");
|
|
9357
9473
|
if (!fileExists(manifestPath)) {
|
|
9358
9474
|
return void 0;
|
|
9359
9475
|
}
|
|
@@ -9366,7 +9482,7 @@ function detectTestRunner(repositoryRoot) {
|
|
|
9366
9482
|
}
|
|
9367
9483
|
function adoptOverlay(repositoryRoot, options, dependencies = {}) {
|
|
9368
9484
|
requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
|
|
9369
|
-
if (!fileExists((0,
|
|
9485
|
+
if (!fileExists((0, import_node_path42.join)(repositoryRoot, "nx.json"))) {
|
|
9370
9486
|
throw new Error("There is no nx.json, so Nx is not set up here. Set it up first (`npx nx@latest init`); adopt does not install Nx yet.");
|
|
9371
9487
|
}
|
|
9372
9488
|
const persisted = readMnciConfig(repositoryRoot);
|
|
@@ -9379,7 +9495,7 @@ function adoptOverlay(repositoryRoot, options, dependencies = {}) {
|
|
|
9379
9495
|
}
|
|
9380
9496
|
|
|
9381
9497
|
// src/repository-adoption/adopt-toolchain.use-case.ts
|
|
9382
|
-
var
|
|
9498
|
+
var import_node_path44 = require("path");
|
|
9383
9499
|
|
|
9384
9500
|
// src/repository-adoption/align-nx-family.algorithm.ts
|
|
9385
9501
|
var VERSION = /^[\^~]?(\d+)\.(\d+)\.(\d+)$/;
|
|
@@ -9418,23 +9534,23 @@ function alignNxFamily(dependencies, version) {
|
|
|
9418
9534
|
|
|
9419
9535
|
// src/repository-adoption/retire-tooling.use-case.ts
|
|
9420
9536
|
var import_node_fs37 = require("fs");
|
|
9421
|
-
var
|
|
9537
|
+
var import_node_path43 = require("path");
|
|
9422
9538
|
var RETIRED_COMMAND = /\b(?:prettier|oxfmt|oxlint)\b/;
|
|
9423
9539
|
function retireTooling(repositoryRoot) {
|
|
9424
9540
|
const removed = [];
|
|
9425
9541
|
for (const file of RETIRED_FORMATTER_FILES) {
|
|
9426
|
-
if (!fileExists((0,
|
|
9542
|
+
if (!fileExists((0, import_node_path43.join)(repositoryRoot, file))) {
|
|
9427
9543
|
continue;
|
|
9428
9544
|
}
|
|
9429
|
-
(0, import_node_fs37.rmSync)((0,
|
|
9545
|
+
(0, import_node_fs37.rmSync)((0, import_node_path43.join)(repositoryRoot, file), { force: true });
|
|
9430
9546
|
removed.push(file);
|
|
9431
9547
|
}
|
|
9432
|
-
const hadRegistry = fileExists((0,
|
|
9548
|
+
const hadRegistry = fileExists((0, import_node_path43.join)(repositoryRoot, ".verdaccio"));
|
|
9433
9549
|
removeLocalRegistryScaffolding(repositoryRoot);
|
|
9434
9550
|
if (hadRegistry) {
|
|
9435
9551
|
removed.push(".verdaccio, its dependency and the local-registry target");
|
|
9436
9552
|
}
|
|
9437
|
-
const manifestPath = (0,
|
|
9553
|
+
const manifestPath = (0, import_node_path43.join)(repositoryRoot, "package.json");
|
|
9438
9554
|
if (!fileExists(manifestPath)) {
|
|
9439
9555
|
return removed;
|
|
9440
9556
|
}
|
|
@@ -9481,7 +9597,7 @@ function adoptToolchain(repositoryRoot, options = {}, dependencies = {}) {
|
|
|
9481
9597
|
console.log(message);
|
|
9482
9598
|
});
|
|
9483
9599
|
requireCleanWorkingTree(repositoryRoot, capture);
|
|
9484
|
-
const manifestPath = (0,
|
|
9600
|
+
const manifestPath = (0, import_node_path44.join)(repositoryRoot, "package.json");
|
|
9485
9601
|
if (!fileExists(manifestPath)) {
|
|
9486
9602
|
throw new Error("No package.json at the repository root.");
|
|
9487
9603
|
}
|
|
@@ -9532,7 +9648,7 @@ function createBaselineTags(repositoryRoot, stranded) {
|
|
|
9532
9648
|
|
|
9533
9649
|
// src/repository-adoption/inspect-repository.use-case.ts
|
|
9534
9650
|
var import_node_fs38 = require("fs");
|
|
9535
|
-
var
|
|
9651
|
+
var import_node_path45 = require("path");
|
|
9536
9652
|
var LOCKFILES = [
|
|
9537
9653
|
["package-lock.json", "npm"],
|
|
9538
9654
|
["pnpm-lock.yaml", "pnpm"],
|
|
@@ -9553,22 +9669,22 @@ function inspectRepository(repositoryRoot) {
|
|
|
9553
9669
|
const isGitRepo = inside.status === 0 && inside.stdout.trim() === "true";
|
|
9554
9670
|
const dirty = isGitRepo && runCapture("git", ["status", "--porcelain"], repositoryRoot).stdout.trim() !== "";
|
|
9555
9671
|
const tags = isGitRepo ? runCapture("git", ["tag", "--list"], repositoryRoot).stdout.split(/\r?\n/).filter(Boolean) : [];
|
|
9556
|
-
const manifestPath = (0,
|
|
9672
|
+
const manifestPath = (0, import_node_path45.join)(repositoryRoot, "package.json");
|
|
9557
9673
|
const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
|
|
9558
|
-
const nxJsonPath = (0,
|
|
9674
|
+
const nxJsonPath = (0, import_node_path45.join)(repositoryRoot, "nx.json");
|
|
9559
9675
|
const nxJson = fileExists(nxJsonPath) ? readJson(nxJsonPath) : {};
|
|
9560
9676
|
const ci = [];
|
|
9561
9677
|
const pipelines = [];
|
|
9562
|
-
if ((0, import_node_fs38.existsSync)((0,
|
|
9678
|
+
if ((0, import_node_fs38.existsSync)((0, import_node_path45.join)(repositoryRoot, "azure-pipelines.yml"))) {
|
|
9563
9679
|
ci.push("azure");
|
|
9564
|
-
pipelines.push((0,
|
|
9680
|
+
pipelines.push((0, import_node_path45.join)(repositoryRoot, "azure-pipelines.yml"));
|
|
9565
9681
|
}
|
|
9566
|
-
const workflows = (0,
|
|
9682
|
+
const workflows = (0, import_node_path45.join)(repositoryRoot, ".github", "workflows");
|
|
9567
9683
|
if ((0, import_node_fs38.existsSync)(workflows)) {
|
|
9568
9684
|
const files = (0, import_node_fs38.readdirSync)(workflows).filter((file) => /\.ya?ml$/.test(file));
|
|
9569
9685
|
if (files.length > 0) {
|
|
9570
9686
|
ci.push("github");
|
|
9571
|
-
pipelines.push(...files.map((file) => (0,
|
|
9687
|
+
pipelines.push(...files.map((file) => (0, import_node_path45.join)(workflows, file)));
|
|
9572
9688
|
}
|
|
9573
9689
|
}
|
|
9574
9690
|
const rootFiles = (0, import_node_fs38.readdirSync)(repositoryRoot);
|
|
@@ -9815,6 +9931,7 @@ var COMMAND_GROUPS = {
|
|
|
9815
9931
|
doctor: "workspace",
|
|
9816
9932
|
adopt: "inspect",
|
|
9817
9933
|
add: "projects",
|
|
9934
|
+
dev: "projects",
|
|
9818
9935
|
install: "dependencies",
|
|
9819
9936
|
sync: "dependencies",
|
|
9820
9937
|
up: "dependencies",
|
|
@@ -9879,13 +9996,13 @@ function runCommands(program, options) {
|
|
|
9879
9996
|
}
|
|
9880
9997
|
|
|
9881
9998
|
// src/workspace-info/list-projects.use-case.ts
|
|
9882
|
-
var
|
|
9999
|
+
var import_node_path46 = require("path");
|
|
9883
10000
|
var TYPE_TAG_PREFIX = "type:";
|
|
9884
10001
|
function listProjects(workspaceRoot) {
|
|
9885
10002
|
return locateProjects(workspaceRoot).map((location) => {
|
|
9886
|
-
const projectJsonPath = (0,
|
|
10003
|
+
const projectJsonPath = (0, import_node_path46.join)(workspaceRoot, location.dir, "project.json");
|
|
9887
10004
|
const project = fileExists(projectJsonPath) ? readJson(projectJsonPath) : {};
|
|
9888
|
-
const manifestPath = (0,
|
|
10005
|
+
const manifestPath = (0, import_node_path46.join)(workspaceRoot, location.dir, "package.json");
|
|
9889
10006
|
const manifestTags = fileExists(manifestPath) ? readJson(manifestPath).nx?.tags ?? [] : [];
|
|
9890
10007
|
const typeTag = [...project.tags ?? [], ...manifestTags].find((tag) => tag.startsWith(TYPE_TAG_PREFIX));
|
|
9891
10008
|
return {
|
|
@@ -9898,7 +10015,7 @@ function listProjects(workspaceRoot) {
|
|
|
9898
10015
|
}).toSorted((a, b) => a.dir.localeCompare(b.dir));
|
|
9899
10016
|
}
|
|
9900
10017
|
function runProjects(workspaceRoot, options) {
|
|
9901
|
-
if (!fileExists((0,
|
|
10018
|
+
if (!fileExists((0, import_node_path46.join)(workspaceRoot, "nx.json"))) {
|
|
9902
10019
|
throw new Error("No nx.json found here. Run `projects` from the workspace root.");
|
|
9903
10020
|
}
|
|
9904
10021
|
const projects = listProjects(workspaceRoot);
|
|
@@ -9912,12 +10029,12 @@ function runProjects(workspaceRoot, options) {
|
|
|
9912
10029
|
}
|
|
9913
10030
|
|
|
9914
10031
|
// src/workspace-info/read-workspace-info.use-case.ts
|
|
9915
|
-
var
|
|
10032
|
+
var import_node_path48 = require("path");
|
|
9916
10033
|
|
|
9917
10034
|
// src/cli-version/check-for-update.use-case.ts
|
|
9918
10035
|
var import_node_fs39 = require("fs");
|
|
9919
|
-
var
|
|
9920
|
-
var
|
|
10036
|
+
var import_node_path47 = require("path");
|
|
10037
|
+
var import_cross_spawn4 = __toESM(require("cross-spawn"));
|
|
9921
10038
|
var PACKAGE_NAME2 = "@mnci/cli";
|
|
9922
10039
|
var REGISTRY_TIMEOUT_MS = 2e3;
|
|
9923
10040
|
function checkForUpdate(currentVersion2) {
|
|
@@ -9935,7 +10052,7 @@ function checkForUpdate(currentVersion2) {
|
|
|
9935
10052
|
}
|
|
9936
10053
|
}
|
|
9937
10054
|
function fetchLatestVersion() {
|
|
9938
|
-
const result =
|
|
10055
|
+
const result = import_cross_spawn4.default.sync("npm", ["view", PACKAGE_NAME2, "version"], {
|
|
9939
10056
|
encoding: "utf8",
|
|
9940
10057
|
timeout: REGISTRY_TIMEOUT_MS,
|
|
9941
10058
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -9975,7 +10092,7 @@ function releaseTriple(version) {
|
|
|
9975
10092
|
}
|
|
9976
10093
|
function readCliVersion(packageDirectory) {
|
|
9977
10094
|
try {
|
|
9978
|
-
const manifestPath = (0,
|
|
10095
|
+
const manifestPath = (0, import_node_path47.join)(packageDirectory, "..", "package.json");
|
|
9979
10096
|
const manifest = JSON.parse((0, import_node_fs39.readFileSync)(manifestPath, "utf8"));
|
|
9980
10097
|
return manifest.version ?? "0.0.0";
|
|
9981
10098
|
} catch {
|
|
@@ -9986,7 +10103,7 @@ function readCliVersion(packageDirectory) {
|
|
|
9986
10103
|
// src/workspace-info/read-workspace-info.use-case.ts
|
|
9987
10104
|
function readWorkspaceInfo(workspaceRoot, cliVersion) {
|
|
9988
10105
|
const latest = fetchLatestVersion() ?? null;
|
|
9989
|
-
const isWorkspace = fileExists((0,
|
|
10106
|
+
const isWorkspace = fileExists((0, import_node_path48.join)(workspaceRoot, "nx.json"));
|
|
9990
10107
|
return {
|
|
9991
10108
|
cli: {
|
|
9992
10109
|
version: cliVersion,
|
|
@@ -10007,7 +10124,7 @@ function runInfo(workspaceRoot, cliVersion, options) {
|
|
|
10007
10124
|
}
|
|
10008
10125
|
|
|
10009
10126
|
// src/interactive-wizard/interactive-wizard.use-case.ts
|
|
10010
|
-
var
|
|
10127
|
+
var import_node_path49 = require("path");
|
|
10011
10128
|
|
|
10012
10129
|
// src/interactive-wizard/adopt-steps.config.ts
|
|
10013
10130
|
var ADOPT_GLOBAL_OPTIONS = ["json"];
|
|
@@ -10143,14 +10260,14 @@ function formatCommandLine(argv) {
|
|
|
10143
10260
|
}
|
|
10144
10261
|
|
|
10145
10262
|
// src/interactive-wizard/inquirer-prompter.client.ts
|
|
10146
|
-
var
|
|
10263
|
+
var import_prompts5 = require("@inquirer/prompts");
|
|
10147
10264
|
function toRows(choices) {
|
|
10148
10265
|
const rows = [];
|
|
10149
10266
|
let current;
|
|
10150
10267
|
for (const choice of choices) {
|
|
10151
10268
|
if (choice.group !== void 0 && choice.group !== current) {
|
|
10152
10269
|
current = choice.group;
|
|
10153
|
-
rows.push(new
|
|
10270
|
+
rows.push(new import_prompts5.Separator(`-- ${choice.group} --`));
|
|
10154
10271
|
}
|
|
10155
10272
|
rows.push({ name: choice.name, value: choice.value, description: choice.description });
|
|
10156
10273
|
}
|
|
@@ -10158,10 +10275,10 @@ function toRows(choices) {
|
|
|
10158
10275
|
}
|
|
10159
10276
|
function createInquirerPrompter() {
|
|
10160
10277
|
return {
|
|
10161
|
-
select: async (message, choices) => await (0,
|
|
10162
|
-
checkbox: async (message, choices) => await (0,
|
|
10163
|
-
input: async (message, required) => await (0,
|
|
10164
|
-
confirm: async (message, fallback) => await (0,
|
|
10278
|
+
select: async (message, choices) => await (0, import_prompts5.select)({ message, choices: toRows(choices), pageSize: 18 }),
|
|
10279
|
+
checkbox: async (message, choices) => await (0, import_prompts5.checkbox)({ message, choices: toRows(choices), pageSize: 18 }),
|
|
10280
|
+
input: async (message, required) => await (0, import_prompts5.input)({ message, validate: (value) => !required || value.trim().length > 0 || "A value is required" }),
|
|
10281
|
+
confirm: async (message, fallback) => await (0, import_prompts5.confirm)({ message, default: fallback })
|
|
10165
10282
|
};
|
|
10166
10283
|
}
|
|
10167
10284
|
|
|
@@ -10188,7 +10305,7 @@ function wizardMenu(commands, inWorkspace) {
|
|
|
10188
10305
|
}
|
|
10189
10306
|
async function runInteractive(program, prompter = createInquirerPrompter(), workingDirectory = process.cwd()) {
|
|
10190
10307
|
const commands = describeCommands(program);
|
|
10191
|
-
const inWorkspace = fileExists((0,
|
|
10308
|
+
const inWorkspace = fileExists((0, import_node_path49.join)(workingDirectory, "nx.json"));
|
|
10192
10309
|
const name = await prompter.select("What would you like to do?", wizardMenu(commands, inWorkspace));
|
|
10193
10310
|
const command = commands.find((candidate) => candidate.name === name);
|
|
10194
10311
|
const argv = buildArgv(command, await askCommand(command, prompter));
|
|
@@ -10200,9 +10317,281 @@ async function runInteractive(program, prompter = createInquirerPrompter(), work
|
|
|
10200
10317
|
}
|
|
10201
10318
|
}
|
|
10202
10319
|
|
|
10203
|
-
// src/workspace-
|
|
10320
|
+
// src/workspace-presets/preset-catalog.config.ts
|
|
10321
|
+
var PRESETS = [
|
|
10322
|
+
{
|
|
10323
|
+
id: "web-api",
|
|
10324
|
+
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",
|
|
10325
|
+
projects: [
|
|
10326
|
+
{ kind: "internal-lib", name: "shared", options: {} },
|
|
10327
|
+
{ kind: "node-app", name: "api", options: { framework: "express" } },
|
|
10328
|
+
{ kind: "react-app", name: "web", options: {} }
|
|
10329
|
+
]
|
|
10330
|
+
}
|
|
10331
|
+
];
|
|
10332
|
+
var PRESET_IDS = PRESETS.map((preset) => preset.id);
|
|
10333
|
+
|
|
10334
|
+
// src/workspace-presets/wire-web-api.use-case.ts
|
|
10204
10335
|
var import_node_fs40 = require("fs");
|
|
10205
|
-
var
|
|
10336
|
+
var import_node_path50 = require("path");
|
|
10337
|
+
|
|
10338
|
+
// src/workspace-presets/web-api-wiring.algorithm.ts
|
|
10339
|
+
function apiHandler(shared) {
|
|
10340
|
+
return `import { greet } from '${shared}'
|
|
10341
|
+
import type { Request, Response } from 'express'
|
|
10342
|
+
|
|
10343
|
+
export function helloHandler (request: Request, response: Response): void {
|
|
10344
|
+
const name = typeof request.query.name === 'string' ? request.query.name : 'world'
|
|
10345
|
+
response.send(greet(name))
|
|
10346
|
+
}
|
|
10347
|
+
`;
|
|
10348
|
+
}
|
|
10349
|
+
function apiHandlerSpec(shared) {
|
|
10350
|
+
return String.raw`import { greet } from '${shared}'
|
|
10351
|
+
import type { Request, Response } from 'express'
|
|
10352
|
+
import { helloHandler } from './hello.handler'
|
|
10353
|
+
|
|
10354
|
+
/** A response that keeps what it was sent, so the spec needs no mock library and runs under Jest or Vitest. */
|
|
10355
|
+
function recordingResponse (): { response: Response, sent: unknown[] } {
|
|
10356
|
+
const sent: unknown[] = []
|
|
10357
|
+
|
|
10358
|
+
return { sent, response: { send: (value: unknown) => { sent.push(value) } } as unknown as Response }
|
|
10359
|
+
}
|
|
10360
|
+
|
|
10361
|
+
describe('helloHandler', () => {
|
|
10362
|
+
it('answers with the shared library\'s greeting for the name asked', () => {
|
|
10363
|
+
const { response, sent } = recordingResponse()
|
|
10364
|
+
|
|
10365
|
+
helloHandler({ query: { name: 'web' } } as unknown as Request, response)
|
|
10366
|
+
|
|
10367
|
+
expect(sent).toEqual([greet('web')])
|
|
10368
|
+
})
|
|
10369
|
+
|
|
10370
|
+
it('greets the world when no name is asked', () => {
|
|
10371
|
+
const { response, sent } = recordingResponse()
|
|
10372
|
+
|
|
10373
|
+
helloHandler({ query: {} } as unknown as Request, response)
|
|
10374
|
+
|
|
10375
|
+
expect(sent).toEqual([greet('world')])
|
|
10376
|
+
})
|
|
10377
|
+
})
|
|
10378
|
+
`;
|
|
10379
|
+
}
|
|
10380
|
+
function apiBarrel() {
|
|
10381
|
+
return "export * from './hello.handler'\n";
|
|
10382
|
+
}
|
|
10383
|
+
function webGreetingClient(shared) {
|
|
10384
|
+
return `import type { Greeting } from '${shared}'
|
|
10385
|
+
|
|
10386
|
+
export async function fetchGreeting (name: string, request: typeof fetch = fetch): Promise<Greeting> {
|
|
10387
|
+
const response = await request('/api/greeting?name=' + encodeURIComponent(name))
|
|
10388
|
+
|
|
10389
|
+
return await response.json() as Greeting
|
|
10390
|
+
}
|
|
10391
|
+
`;
|
|
10392
|
+
}
|
|
10393
|
+
function webGreetingClientSpec() {
|
|
10394
|
+
return String.raw`import { fetchGreeting } from './greeting.client'
|
|
10395
|
+
|
|
10396
|
+
describe('fetchGreeting', () => {
|
|
10397
|
+
it('asks the API\'s greeting route for the name and returns the answer', async () => {
|
|
10398
|
+
const requested: string[] = []
|
|
10399
|
+
const answering = (async (input: string) => {
|
|
10400
|
+
requested.push(input)
|
|
10401
|
+
|
|
10402
|
+
return { json: async () => ({ message: 'Hello, web!' }) } as Response
|
|
10403
|
+
}) as unknown as typeof fetch
|
|
10404
|
+
|
|
10405
|
+
expect(await fetchGreeting('web', answering)).toEqual({ message: 'Hello, web!' })
|
|
10406
|
+
expect(requested).toEqual(['/api/greeting?name=web'])
|
|
10407
|
+
})
|
|
10408
|
+
})
|
|
10409
|
+
`;
|
|
10410
|
+
}
|
|
10411
|
+
function webGreetingComponent(shared) {
|
|
10412
|
+
return `import { useEffect, useState } from 'react'
|
|
10413
|
+
import type { Greeting } from '${shared}'
|
|
10414
|
+
import { fetchGreeting } from './greeting.client'
|
|
10415
|
+
|
|
10416
|
+
export interface GreetingViewProps {
|
|
10417
|
+
readonly name: string
|
|
10418
|
+
readonly load?: (name: string) => Promise<Greeting>
|
|
10419
|
+
}
|
|
10420
|
+
|
|
10421
|
+
export function GreetingView ({ name, load = fetchGreeting }: GreetingViewProps) {
|
|
10422
|
+
const [greeting, setGreeting] = useState<Greeting | undefined>()
|
|
10423
|
+
const [failed, setFailed] = useState(false)
|
|
10424
|
+
|
|
10425
|
+
useEffect(() => {
|
|
10426
|
+
void (async () => {
|
|
10427
|
+
try {
|
|
10428
|
+
setGreeting(await load(name))
|
|
10429
|
+
} catch {
|
|
10430
|
+
setFailed(true)
|
|
10431
|
+
}
|
|
10432
|
+
})()
|
|
10433
|
+
}, [name, load])
|
|
10434
|
+
|
|
10435
|
+
if (failed) {
|
|
10436
|
+
return <p>Could not reach the API.</p>
|
|
10437
|
+
}
|
|
10438
|
+
|
|
10439
|
+
return <p>{greeting?.message ?? 'Loading...'}</p>
|
|
10440
|
+
}
|
|
10441
|
+
`;
|
|
10442
|
+
}
|
|
10443
|
+
function webGreetingSpec() {
|
|
10444
|
+
return `import { render } from '@testing-library/react'
|
|
10445
|
+
|
|
10446
|
+
import { GreetingView } from './greeting.component'
|
|
10447
|
+
|
|
10448
|
+
async function answering (name: string): Promise<{ message: string }> {
|
|
10449
|
+
return { message: 'Hello, ' + name + '!' }
|
|
10450
|
+
}
|
|
10451
|
+
|
|
10452
|
+
async function offline (): Promise<never> {
|
|
10453
|
+
throw new Error('offline')
|
|
10454
|
+
}
|
|
10455
|
+
|
|
10456
|
+
describe('GreetingView', () => {
|
|
10457
|
+
it('shows the greeting its loader returns for its name', async () => {
|
|
10458
|
+
const { findByText } = render(<GreetingView name='world' load={answering} />)
|
|
10459
|
+
|
|
10460
|
+
expect(await findByText('Hello, world!')).toBeTruthy()
|
|
10461
|
+
})
|
|
10462
|
+
|
|
10463
|
+
it('says so when the API cannot be reached', async () => {
|
|
10464
|
+
const { findByText } = render(<GreetingView name='world' load={offline} />)
|
|
10465
|
+
|
|
10466
|
+
expect(await findByText('Could not reach the API.')).toBeTruthy()
|
|
10467
|
+
})
|
|
10468
|
+
})
|
|
10469
|
+
`;
|
|
10470
|
+
}
|
|
10471
|
+
function webGreetingBarrel() {
|
|
10472
|
+
return "export * from './greeting.client'\nexport * from './greeting.component'\n";
|
|
10473
|
+
}
|
|
10474
|
+
function webAppSpec() {
|
|
10475
|
+
return `import { render } from '@testing-library/react'
|
|
10476
|
+
|
|
10477
|
+
import App from './app.component'
|
|
10478
|
+
|
|
10479
|
+
describe('App', () => {
|
|
10480
|
+
it('renders, and shows that it is loading the greeting', () => {
|
|
10481
|
+
const { getByText } = render(<App />)
|
|
10482
|
+
|
|
10483
|
+
expect(getByText('Loading...')).toBeTruthy()
|
|
10484
|
+
})
|
|
10485
|
+
})
|
|
10486
|
+
`;
|
|
10487
|
+
}
|
|
10488
|
+
function withApiProxy(config, target) {
|
|
10489
|
+
const start = config.indexOf("server:");
|
|
10490
|
+
const end = start === -1 ? -1 : config.indexOf("},", start);
|
|
10491
|
+
if (end === -1 || config.includes("proxy:")) {
|
|
10492
|
+
return config;
|
|
10493
|
+
}
|
|
10494
|
+
const block = config.slice(start, end).trimEnd();
|
|
10495
|
+
return `${config.slice(0, start)}${block}
|
|
10496
|
+
proxy: { '/api': '${target}' },
|
|
10497
|
+
${config.slice(end)}`;
|
|
10498
|
+
}
|
|
10499
|
+
|
|
10500
|
+
// src/workspace-presets/wire-web-api.use-case.ts
|
|
10501
|
+
var API_ORIGIN = "http://localhost:3000";
|
|
10502
|
+
function declareDependency(manifestPath, name) {
|
|
10503
|
+
const manifest = readJson(manifestPath);
|
|
10504
|
+
writeFileEnsured(manifestPath, toJson({ ...manifest, dependencies: { ...manifest.dependencies, [name]: "*" } }));
|
|
10505
|
+
}
|
|
10506
|
+
function removeFiles(slice, files) {
|
|
10507
|
+
for (const file of files) {
|
|
10508
|
+
(0, import_node_fs40.rmSync)((0, import_node_path50.join)(slice, file), { force: true });
|
|
10509
|
+
}
|
|
10510
|
+
}
|
|
10511
|
+
function replaceOnce(path, anchor, replacement) {
|
|
10512
|
+
const text = (0, import_node_fs40.readFileSync)(path, "utf8");
|
|
10513
|
+
if (!text.includes(anchor)) {
|
|
10514
|
+
throw new Error(`${path} has no ${anchor}; the generator's output changed, so the preset cannot wire it.`);
|
|
10515
|
+
}
|
|
10516
|
+
writeFileEnsured(path, text.replace(anchor, () => replacement));
|
|
10517
|
+
}
|
|
10518
|
+
function proxyApi(viteConfigPath) {
|
|
10519
|
+
const text = (0, import_node_fs40.readFileSync)(viteConfigPath, "utf8");
|
|
10520
|
+
const proxied = withApiProxy(text, API_ORIGIN);
|
|
10521
|
+
if (proxied === text && !text.includes("proxy:")) {
|
|
10522
|
+
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.`);
|
|
10523
|
+
}
|
|
10524
|
+
writeFileEnsured(viteConfigPath, proxied);
|
|
10525
|
+
}
|
|
10526
|
+
function wireWebApi(workspaceRoot, projects) {
|
|
10527
|
+
const sharedManifest = (0, import_node_path50.join)(workspaceRoot, "libs", projects.shared, "package.json");
|
|
10528
|
+
if (!fileExists(sharedManifest)) {
|
|
10529
|
+
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.`);
|
|
10530
|
+
}
|
|
10531
|
+
const shared = readJson(sharedManifest).name;
|
|
10532
|
+
const api = (0, import_node_path50.join)(workspaceRoot, "apps", projects.api);
|
|
10533
|
+
declareDependency((0, import_node_path50.join)(api, "package.json"), shared);
|
|
10534
|
+
const hello = (0, import_node_path50.join)(api, "src", "hello");
|
|
10535
|
+
writeFileEnsured((0, import_node_path50.join)(hello, "hello.handler.ts"), apiHandler(shared));
|
|
10536
|
+
writeFileEnsured((0, import_node_path50.join)(hello, "hello.handler.spec.ts"), apiHandlerSpec(shared));
|
|
10537
|
+
writeFileEnsured((0, import_node_path50.join)(hello, "index.ts"), apiBarrel());
|
|
10538
|
+
removeFiles(hello, ["greet.use-case.ts", "greet.use-case.spec.ts", "greeting.contract.ts"]);
|
|
10539
|
+
replaceOnce((0, import_node_path50.join)(api, "src", "main.ts"), "app.get('/', helloHandler)", "app.get('/api/greeting', helloHandler)");
|
|
10540
|
+
const web = (0, import_node_path50.join)(workspaceRoot, "apps", projects.web);
|
|
10541
|
+
declareDependency((0, import_node_path50.join)(web, "package.json"), shared);
|
|
10542
|
+
const greeting = (0, import_node_path50.join)(web, "src", "greeting");
|
|
10543
|
+
writeFileEnsured((0, import_node_path50.join)(greeting, "greeting.client.ts"), webGreetingClient(shared));
|
|
10544
|
+
writeFileEnsured((0, import_node_path50.join)(greeting, "greeting.client.spec.ts"), webGreetingClientSpec());
|
|
10545
|
+
writeFileEnsured((0, import_node_path50.join)(greeting, "greeting.component.tsx"), webGreetingComponent(shared));
|
|
10546
|
+
writeFileEnsured((0, import_node_path50.join)(greeting, "greeting.component.spec.tsx"), webGreetingSpec());
|
|
10547
|
+
writeFileEnsured((0, import_node_path50.join)(greeting, "index.ts"), webGreetingBarrel());
|
|
10548
|
+
removeFiles(greeting, ["greet.use-case.ts", "greet.use-case.spec.ts", "greeting.contract.ts"]);
|
|
10549
|
+
writeFileEnsured((0, import_node_path50.join)(web, "src", "app", "app.component.spec.tsx"), webAppSpec());
|
|
10550
|
+
proxyApi((0, import_node_path50.join)(web, "vite.config.mts"));
|
|
10551
|
+
}
|
|
10552
|
+
|
|
10553
|
+
// src/workspace-presets/apply-preset.use-case.ts
|
|
10554
|
+
function findPreset(id) {
|
|
10555
|
+
const preset = PRESETS.find((candidate) => candidate.id === id);
|
|
10556
|
+
if (preset === void 0) {
|
|
10557
|
+
throw new Error(`Unknown preset '${id}'. Presets: ${PRESET_IDS.join(", ")}.`);
|
|
10558
|
+
}
|
|
10559
|
+
return preset;
|
|
10560
|
+
}
|
|
10561
|
+
function nameOf(preset, kind) {
|
|
10562
|
+
const project = preset.projects.find((candidate) => candidate.kind === kind);
|
|
10563
|
+
if (project === void 0) {
|
|
10564
|
+
throw new Error(`The ${preset.id} preset has no ${kind}.`);
|
|
10565
|
+
}
|
|
10566
|
+
return project.name;
|
|
10567
|
+
}
|
|
10568
|
+
async function applyPreset(workspaceRoot, presetId, dependencies = {}) {
|
|
10569
|
+
const preset = findPreset(presetId);
|
|
10570
|
+
const add = dependencies.add ?? runAdd;
|
|
10571
|
+
const install = dependencies.install ?? ((root) => runShell("npm", ["install", "--ignore-scripts", "--no-audit", "--no-fund"], root));
|
|
10572
|
+
const sync = dependencies.sync ?? syncProjectReferences;
|
|
10573
|
+
const format = dependencies.format ?? runFormatter;
|
|
10574
|
+
logger.step(`Adding the ${preset.id} preset: ${preset.projects.map((project) => project.name).join(", ")}`);
|
|
10575
|
+
const previous = process.cwd();
|
|
10576
|
+
process.chdir(workspaceRoot);
|
|
10577
|
+
try {
|
|
10578
|
+
for (const project of preset.projects) {
|
|
10579
|
+
await add(project.kind, project.name, project.options);
|
|
10580
|
+
}
|
|
10581
|
+
} finally {
|
|
10582
|
+
process.chdir(previous);
|
|
10583
|
+
}
|
|
10584
|
+
wireWebApi(workspaceRoot, { shared: nameOf(preset, "internal-lib"), api: nameOf(preset, "node-app"), web: nameOf(preset, "react-app") });
|
|
10585
|
+
if (install(workspaceRoot) !== 0) {
|
|
10586
|
+
throw new Error("npm install failed after wiring the preset. Run `npm install` and read its error; the projects themselves were added.");
|
|
10587
|
+
}
|
|
10588
|
+
sync(workspaceRoot);
|
|
10589
|
+
format(workspaceRoot);
|
|
10590
|
+
}
|
|
10591
|
+
|
|
10592
|
+
// src/workspace-creation/adopt-directory.use-case.ts
|
|
10593
|
+
var import_node_fs41 = require("fs");
|
|
10594
|
+
var import_node_path51 = require("path");
|
|
10206
10595
|
var NEVER_COPIED = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
10207
10596
|
var AGENT_AND_CI_ENTRIES = [
|
|
10208
10597
|
"AGENTS.md",
|
|
@@ -10233,13 +10622,13 @@ var PREFLIGHT_BLOCKERS = [
|
|
|
10233
10622
|
"packages"
|
|
10234
10623
|
];
|
|
10235
10624
|
function assertAdoptableDirectory(target) {
|
|
10236
|
-
if (!(0,
|
|
10625
|
+
if (!(0, import_node_fs41.existsSync)(target)) {
|
|
10237
10626
|
throw new Error(`--into '${target}' does not exist. Create or clone it first.`);
|
|
10238
10627
|
}
|
|
10239
|
-
if (!(0,
|
|
10628
|
+
if (!(0, import_node_fs41.statSync)(target).isDirectory()) {
|
|
10240
10629
|
throw new Error(`--into '${target}' is not a directory.`);
|
|
10241
10630
|
}
|
|
10242
|
-
const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0,
|
|
10631
|
+
const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs41.existsSync)((0, import_node_path51.join)(target, entry)));
|
|
10243
10632
|
if (blockers.length > 0) {
|
|
10244
10633
|
throw new Error(
|
|
10245
10634
|
`--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.`
|
|
@@ -10247,15 +10636,15 @@ function assertAdoptableDirectory(target) {
|
|
|
10247
10636
|
}
|
|
10248
10637
|
}
|
|
10249
10638
|
function mergeGitignore(generatedFile, targetFile) {
|
|
10250
|
-
const existing = (0,
|
|
10639
|
+
const existing = (0, import_node_fs41.readFileSync)(targetFile, "utf8");
|
|
10251
10640
|
const present = new Set(existing.split("\n").map((line) => line.trim()));
|
|
10252
|
-
const additions = (0,
|
|
10641
|
+
const additions = (0, import_node_fs41.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
|
|
10253
10642
|
const trimmed = line.trim();
|
|
10254
10643
|
return trimmed !== "" && !trimmed.startsWith("#") && !present.has(trimmed);
|
|
10255
10644
|
});
|
|
10256
10645
|
if (additions.length === 0) return false;
|
|
10257
10646
|
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
10258
|
-
(0,
|
|
10647
|
+
(0, import_node_fs41.writeFileSync)(
|
|
10259
10648
|
targetFile,
|
|
10260
10649
|
`${existing}${separator}
|
|
10261
10650
|
# Added by mnci (Nx workspace)
|
|
@@ -10265,9 +10654,9 @@ ${additions.join("\n")}
|
|
|
10265
10654
|
return true;
|
|
10266
10655
|
}
|
|
10267
10656
|
function adoptGeneratedWorkspace(generatedRoot, target) {
|
|
10268
|
-
const entries = (0,
|
|
10657
|
+
const entries = (0, import_node_fs41.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
|
|
10269
10658
|
const collisions = entries.filter(
|
|
10270
|
-
(entry) => (0,
|
|
10659
|
+
(entry) => (0, import_node_fs41.existsSync)((0, import_node_path51.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
|
|
10271
10660
|
);
|
|
10272
10661
|
if (collisions.length > 0) {
|
|
10273
10662
|
throw new Error(
|
|
@@ -10276,28 +10665,28 @@ function adoptGeneratedWorkspace(generatedRoot, target) {
|
|
|
10276
10665
|
}
|
|
10277
10666
|
const result = { copied: [], kept: [], mergedGitignore: false };
|
|
10278
10667
|
for (const entry of entries) {
|
|
10279
|
-
const from = (0,
|
|
10280
|
-
const to = (0,
|
|
10281
|
-
if (entry === ".gitignore" && (0,
|
|
10668
|
+
const from = (0, import_node_path51.join)(generatedRoot, entry);
|
|
10669
|
+
const to = (0, import_node_path51.join)(target, entry);
|
|
10670
|
+
if (entry === ".gitignore" && (0, import_node_fs41.existsSync)(to)) {
|
|
10282
10671
|
result.mergedGitignore = mergeGitignore(from, to);
|
|
10283
10672
|
result.kept.push(entry);
|
|
10284
10673
|
continue;
|
|
10285
10674
|
}
|
|
10286
|
-
if ((0,
|
|
10675
|
+
if ((0, import_node_fs41.existsSync)(to)) {
|
|
10287
10676
|
result.kept.push(entry);
|
|
10288
10677
|
continue;
|
|
10289
10678
|
}
|
|
10290
|
-
(0,
|
|
10679
|
+
(0, import_node_fs41.cpSync)(from, to, { recursive: true });
|
|
10291
10680
|
result.copied.push(entry);
|
|
10292
10681
|
}
|
|
10293
|
-
(0,
|
|
10682
|
+
(0, import_node_fs41.rmSync)(generatedRoot, { recursive: true, force: true });
|
|
10294
10683
|
return result;
|
|
10295
10684
|
}
|
|
10296
10685
|
|
|
10297
10686
|
// src/workspace-creation/create-workspace.use-case.ts
|
|
10298
|
-
var
|
|
10687
|
+
var import_node_fs42 = require("fs");
|
|
10299
10688
|
var import_node_os2 = require("os");
|
|
10300
|
-
var
|
|
10689
|
+
var import_node_path52 = require("path");
|
|
10301
10690
|
function nxCloudProviderValue(ci) {
|
|
10302
10691
|
return ci === "azure" ? "azure" : "github";
|
|
10303
10692
|
}
|
|
@@ -10340,9 +10729,10 @@ async function resolveRegistry2(options) {
|
|
|
10340
10729
|
return await promptRegistry();
|
|
10341
10730
|
}
|
|
10342
10731
|
async function runNew(name, options) {
|
|
10343
|
-
const adoptTarget = options.into === void 0 ? void 0 : (0,
|
|
10732
|
+
const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path52.resolve)(process.cwd(), options.into);
|
|
10344
10733
|
if (adoptTarget !== void 0) assertAdoptableDirectory(adoptTarget);
|
|
10345
|
-
|
|
10734
|
+
if (options.preset !== void 0) findPreset(options.preset);
|
|
10735
|
+
const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path52.basename)(adoptTarget));
|
|
10346
10736
|
assertValidProjectName(workspaceName, "Workspace name");
|
|
10347
10737
|
const scope = options.scope ?? (options.yes ? `@${workspaceName}` : await promptText("npm scope for publishable packages", `@${workspaceName}`));
|
|
10348
10738
|
const registry = await resolveRegistry2(options);
|
|
@@ -10355,8 +10745,8 @@ async function runNew(name, options) {
|
|
|
10355
10745
|
const npmAuth = resolveNpmAuth(options.npmAuth, registry, ci);
|
|
10356
10746
|
const stack = await resolveStack(options);
|
|
10357
10747
|
const nxCloud = options.nxCloud ?? (options.yes ? false : await promptNxCloud());
|
|
10358
|
-
const stagingParent = adoptTarget === void 0 ? process.cwd() : (0,
|
|
10359
|
-
let workspaceRoot = (0,
|
|
10748
|
+
const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs42.mkdtempSync)((0, import_node_path52.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
|
|
10749
|
+
let workspaceRoot = (0, import_node_path52.join)(stagingParent, workspaceName);
|
|
10360
10750
|
try {
|
|
10361
10751
|
logger.step(`Creating Nx workspace '${workspaceName}' (preset: ts)`);
|
|
10362
10752
|
runNpx(
|
|
@@ -10379,7 +10769,7 @@ async function runNew(name, options) {
|
|
|
10379
10769
|
workspaceRoot = adoptTarget;
|
|
10380
10770
|
}
|
|
10381
10771
|
} finally {
|
|
10382
|
-
if (adoptTarget !== void 0) (0,
|
|
10772
|
+
if (adoptTarget !== void 0) (0, import_node_fs42.rmSync)(stagingParent, { recursive: true, force: true });
|
|
10383
10773
|
}
|
|
10384
10774
|
logger.step(
|
|
10385
10775
|
"Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
|
|
@@ -10389,8 +10779,8 @@ async function runNew(name, options) {
|
|
|
10389
10779
|
{ workspaceName, scope, registry, agent, variableGroup, ci, stack, ...npmAuth && { npmAuth } },
|
|
10390
10780
|
logger.detail
|
|
10391
10781
|
);
|
|
10392
|
-
(0,
|
|
10393
|
-
(0,
|
|
10782
|
+
(0, import_node_fs42.rmSync)((0, import_node_path52.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
|
|
10783
|
+
(0, import_node_fs42.rmSync)((0, import_node_path52.join)(workspaceRoot, "package-lock.json"), { force: true });
|
|
10394
10784
|
logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
|
|
10395
10785
|
const installStatus = runShell(
|
|
10396
10786
|
"npm",
|
|
@@ -10403,6 +10793,9 @@ async function runNew(name, options) {
|
|
|
10403
10793
|
runShell("npx", ["husky"], workspaceRoot);
|
|
10404
10794
|
logger.step("Formatting the workspace (eslint --fix, JavaScript Standard Style)");
|
|
10405
10795
|
runFormatter(workspaceRoot);
|
|
10796
|
+
if (options.preset !== void 0) {
|
|
10797
|
+
await applyPreset(workspaceRoot, options.preset);
|
|
10798
|
+
}
|
|
10406
10799
|
logger.success("Done. Next steps:");
|
|
10407
10800
|
if (adoptTarget === void 0) logger.info(` cd ${workspaceName}`);
|
|
10408
10801
|
for (const line of projectKindHintLines()) {
|
|
@@ -10440,6 +10833,8 @@ function buildProgram(cliVersion) {
|
|
|
10440
10833
|
).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(
|
|
10441
10834
|
"--into <dir>",
|
|
10442
10835
|
"bootstrap into an existing directory (a fresh clone holding only .git); the directory name is the workspace name unless one is given"
|
|
10836
|
+
).addOption(
|
|
10837
|
+
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)
|
|
10443
10838
|
).action(async (name, options) => {
|
|
10444
10839
|
await runNew(name, options);
|
|
10445
10840
|
});
|
|
@@ -10454,6 +10849,9 @@ function buildProgram(cliVersion) {
|
|
|
10454
10849
|
).action((options) => {
|
|
10455
10850
|
runUpgrade(process.cwd(), options);
|
|
10456
10851
|
});
|
|
10852
|
+
program.command("dev").argument("[projects...]", "the projects to start (those with a start command)").description("Start several projects together, such as a frontend and the API it calls: runs their start targets as one Nx command, so Ctrl+C stops them all. Name the projects, pass --all, or pick from a list").option("--all", "start every project that has a start command").option("--dry-run", "print the Nx command and start nothing").action(async (projects, options) => {
|
|
10853
|
+
await runDev(process.cwd(), projects, options);
|
|
10854
|
+
});
|
|
10457
10855
|
program.command("doctor").description(
|
|
10458
10856
|
"Check this workspace against the invariants mnci maintains (one ESLint config, no stray .prettierrc, the eslint plugin registered, the resolved eslint major, .npmrc vs the recorded registry, versionActions overrides, nx sync) \u2014 read-only; exits non-zero if anything failed"
|
|
10459
10857
|
).option("--json", "print the findings as one JSON document, each with its remedy").action((options) => {
|