@mnci/cli 4.0.7 → 4.1.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 +173 -1
- package/dist/cli.js +1235 -56
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -38,7 +38,7 @@ module.exports = __toCommonJS(cli_exports);
|
|
|
38
38
|
var import_commander = require("commander");
|
|
39
39
|
|
|
40
40
|
// src/commands/add.ts
|
|
41
|
-
var
|
|
41
|
+
var import_node_path13 = require("path");
|
|
42
42
|
var import_prompts5 = require("@inquirer/prompts");
|
|
43
43
|
|
|
44
44
|
// src/nx.ts
|
|
@@ -78,6 +78,52 @@ function runShell(command, arguments_, cwd) {
|
|
|
78
78
|
const result = import_cross_spawn.default.sync(command, arguments_, { stdio: "inherit", cwd });
|
|
79
79
|
return result.status ?? 1;
|
|
80
80
|
}
|
|
81
|
+
var CAPTURE_TIMEOUT_MS = 6e4;
|
|
82
|
+
function runCapture(command, arguments_, cwd) {
|
|
83
|
+
const result = import_cross_spawn.default.sync(command, arguments_, {
|
|
84
|
+
cwd,
|
|
85
|
+
encoding: "utf8",
|
|
86
|
+
timeout: CAPTURE_TIMEOUT_MS,
|
|
87
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
88
|
+
});
|
|
89
|
+
if (result.error) {
|
|
90
|
+
return { status: 1, stdout: "" };
|
|
91
|
+
}
|
|
92
|
+
return { status: result.status ?? 1, stdout: result.stdout ?? "" };
|
|
93
|
+
}
|
|
94
|
+
async function runCaptureAsync(command, arguments_, cwd) {
|
|
95
|
+
return await new Promise((resolve) => {
|
|
96
|
+
const child = (0, import_cross_spawn.default)(command, arguments_, {
|
|
97
|
+
cwd,
|
|
98
|
+
timeout: CAPTURE_TIMEOUT_MS,
|
|
99
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
100
|
+
});
|
|
101
|
+
let stdout = "";
|
|
102
|
+
child.stdout?.setEncoding("utf8");
|
|
103
|
+
child.stdout?.on("data", (chunk) => {
|
|
104
|
+
stdout += chunk;
|
|
105
|
+
});
|
|
106
|
+
child.on("error", () => {
|
|
107
|
+
resolve({ status: 1, stdout: "" });
|
|
108
|
+
});
|
|
109
|
+
child.on("close", (code) => {
|
|
110
|
+
resolve({ status: code ?? 1, stdout });
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
async function pool(items, limit, job) {
|
|
115
|
+
const results = Array.from({ length: items.length });
|
|
116
|
+
let next = 0;
|
|
117
|
+
const worker = async () => {
|
|
118
|
+
while (next < items.length) {
|
|
119
|
+
const index = next;
|
|
120
|
+
next += 1;
|
|
121
|
+
results[index] = await job(items[index]);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, async () => await worker()));
|
|
125
|
+
return results;
|
|
126
|
+
}
|
|
81
127
|
function runNx(arguments_, cwd) {
|
|
82
128
|
const status = runShell("npx", ["nx", ...arguments_], cwd);
|
|
83
129
|
if (status !== 0) {
|
|
@@ -1343,6 +1389,161 @@ function markPublic(manifestPath) {
|
|
|
1343
1389
|
var WRONG_TYPES_PATH = "./dist/index.esm.d.ts";
|
|
1344
1390
|
var ACTUAL_TYPES_PATH = "./dist/src/index.d.ts";
|
|
1345
1391
|
var DECLARATION_MAP_EXCLUSION = "!**/*.d.ts.map";
|
|
1392
|
+
var SOURCE_MAP_EXCLUSION = "!**/*.js.map";
|
|
1393
|
+
var ROLLUP_CONFIG_PLACEHOLDER = [
|
|
1394
|
+
" {",
|
|
1395
|
+
" // Provide additional rollup configuration here. See: https://rollupjs.org/configuration-options",
|
|
1396
|
+
" // e.g.",
|
|
1397
|
+
" // output: { sourcemap: true },",
|
|
1398
|
+
" }"
|
|
1399
|
+
].join("\n");
|
|
1400
|
+
var GENERATED_COMPILER = " compiler: 'swc',";
|
|
1401
|
+
var SOURCE_MAP_CAPABLE_COMPILER = [
|
|
1402
|
+
" // Swapped from swc by MoNecromanCI. @nx/rollup runs swc without",
|
|
1403
|
+
" // sourceMaps, so it returns no map and the bundle's map comes out empty -",
|
|
1404
|
+
" // valid-looking, and useless for debugging. See ROADMAP.",
|
|
1405
|
+
" compiler: 'babel',"
|
|
1406
|
+
].join("\n");
|
|
1407
|
+
var ROLLUP_ARG_ONE_BOUNDARY = [" },", " {", ""].join("\n");
|
|
1408
|
+
var ROLLUP_ARG_ONE_WITH_SOURCE_MAPS = [
|
|
1409
|
+
" // Added by MoNecromanCI: without this rollup emits no .js.map at all, so",
|
|
1410
|
+
" // a breakpoint in a .ts file can never bind. Not published - see `files`.",
|
|
1411
|
+
" sourceMap: true",
|
|
1412
|
+
" },",
|
|
1413
|
+
" {",
|
|
1414
|
+
""
|
|
1415
|
+
].join("\n");
|
|
1416
|
+
function hasRollupSourceMaps(config) {
|
|
1417
|
+
return config.includes("sourceMap: true");
|
|
1418
|
+
}
|
|
1419
|
+
function withRollupSourceMaps(config) {
|
|
1420
|
+
if (hasRollupSourceMaps(config) || !config.includes(ROLLUP_ARG_ONE_BOUNDARY)) {
|
|
1421
|
+
return config;
|
|
1422
|
+
}
|
|
1423
|
+
const withCompiler = config.replace(GENERATED_COMPILER, () => SOURCE_MAP_CAPABLE_COMPILER);
|
|
1424
|
+
const withFlag = withCompiler.replace(
|
|
1425
|
+
ROLLUP_ARG_ONE_BOUNDARY,
|
|
1426
|
+
() => ROLLUP_ARG_ONE_WITH_SOURCE_MAPS
|
|
1427
|
+
);
|
|
1428
|
+
return withFlag.includes("sourcemapPathTransform") ? withFlag : withFlag.replace(
|
|
1429
|
+
ROLLUP_ARG_ONE_WITH_SOURCE_MAPS,
|
|
1430
|
+
() => `${ROLLUP_ARG_ONE_WITH_SOURCE_MAPS}${SOURCEMAP_PATH_TRANSFORM}`
|
|
1431
|
+
);
|
|
1432
|
+
}
|
|
1433
|
+
function repairRollupSourceMaps(workspaceRoot) {
|
|
1434
|
+
const changed = [];
|
|
1435
|
+
const configs = (0, import_node_fs3.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
|
|
1436
|
+
cwd: workspaceRoot
|
|
1437
|
+
});
|
|
1438
|
+
for (const relativePath of configs) {
|
|
1439
|
+
const configPath = (0, import_node_path3.join)(workspaceRoot, relativePath);
|
|
1440
|
+
const before = (0, import_node_fs3.readFileSync)(configPath, "utf8");
|
|
1441
|
+
const after = withRollupSourceMaps(before);
|
|
1442
|
+
if (after !== before) {
|
|
1443
|
+
writeFileEnsured(configPath, after);
|
|
1444
|
+
changed.push(relativePath.replaceAll("\\", "/"));
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
return changed;
|
|
1448
|
+
}
|
|
1449
|
+
var SOURCEMAP_PATH_TRANSFORM = [
|
|
1450
|
+
" // Added by MoNecromanCI. rollup hands sourcemapPathTransform an OS-NATIVE",
|
|
1451
|
+
" // path with one parent segment too many, so `sources` resolve to nothing",
|
|
1452
|
+
" // and no breakpoint can bind. Separators are normalised too: a sources",
|
|
1453
|
+
" // entry is URL-style, so a backslash is wrong on every platform.",
|
|
1454
|
+
" output: {",
|
|
1455
|
+
" sourcemapPathTransform: relativeSourcePath =>",
|
|
1456
|
+
" relativeSourcePath",
|
|
1457
|
+
" .replaceAll(String.fromCodePoint(92), '/')",
|
|
1458
|
+
" .replace(/^([.][.][/])+/, '../')",
|
|
1459
|
+
" },",
|
|
1460
|
+
""
|
|
1461
|
+
].join("\n");
|
|
1462
|
+
var ROLLUP_CONFIG_WITH_DTS_FIX = [
|
|
1463
|
+
" {",
|
|
1464
|
+
" // Added by MoNecromanCI. @nx/rollup's dts-bundle plugin writes dist/index.d.ts",
|
|
1465
|
+
" // as a stub re-exporting the real declarations, and builds that specifier with",
|
|
1466
|
+
" // path.relative() - an OS-NATIVE path. On Windows it emits",
|
|
1467
|
+
String.raw` // export * from "./src\\index";`,
|
|
1468
|
+
" // which is not a valid module specifier on ANY platform: a specifier is",
|
|
1469
|
+
" // URL-style, so / is correct everywhere and a backslash nowhere. It resolves on",
|
|
1470
|
+
" // Windows only because the resolver normalises separators there, leaving the",
|
|
1471
|
+
" // package untyped on Linux and macOS.",
|
|
1472
|
+
" //",
|
|
1473
|
+
" // mnci also points `types` past this stub, so nothing depends on it being",
|
|
1474
|
+
" // correct; this makes the emitted file correct too. Remove once Nx fixes the",
|
|
1475
|
+
" // plugin - its own devkit already exports normalizePath for exactly this.",
|
|
1476
|
+
" plugins: [",
|
|
1477
|
+
" {",
|
|
1478
|
+
" name: 'mnci-normalise-declaration-specifiers',",
|
|
1479
|
+
" writeBundle (outputOptions) {",
|
|
1480
|
+
" const { readFileSync, writeFileSync } = require('node:fs')",
|
|
1481
|
+
" const { join } = require('node:path')",
|
|
1482
|
+
" const stub = join(outputOptions.dir ?? './dist', 'index.d.ts')",
|
|
1483
|
+
" let source",
|
|
1484
|
+
" try {",
|
|
1485
|
+
" source = readFileSync(stub, 'utf8')",
|
|
1486
|
+
" } catch {",
|
|
1487
|
+
" return",
|
|
1488
|
+
" }",
|
|
1489
|
+
" // The stub carries a TWO-character escape (JSON.stringify escaped one",
|
|
1490
|
+
" // backslash), so this must not match a single one - that would turn",
|
|
1491
|
+
String.raw` // "./src\index" into "./src//index". Built from char codes so there is`,
|
|
1492
|
+
" // no escaping in this file to get wrong.",
|
|
1493
|
+
" const separator = String.fromCodePoint(92, 92)",
|
|
1494
|
+
" const normalised = source.replaceAll(separator, '/')",
|
|
1495
|
+
" if (normalised !== source) writeFileSync(stub, normalised)",
|
|
1496
|
+
" }",
|
|
1497
|
+
" }",
|
|
1498
|
+
" ]",
|
|
1499
|
+
" }"
|
|
1500
|
+
].join("\n");
|
|
1501
|
+
function repairDeclarationSpecifiers(projectRoot) {
|
|
1502
|
+
const configPath = (0, import_node_path3.join)(projectRoot, "rollup.config.cjs");
|
|
1503
|
+
if (!fileExists(configPath)) {
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
const config = (0, import_node_fs3.readFileSync)(configPath, "utf8");
|
|
1507
|
+
if (!config.includes(ROLLUP_CONFIG_PLACEHOLDER)) {
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1510
|
+
const withDtsFix = config.split(ROLLUP_CONFIG_PLACEHOLDER).join(ROLLUP_CONFIG_WITH_DTS_FIX);
|
|
1511
|
+
writeFileEnsured(configPath, withRollupSourceMaps(withDtsFix));
|
|
1512
|
+
}
|
|
1513
|
+
function writeProjectReadme(projectRoot, projectName, testRunner) {
|
|
1514
|
+
const runner = testRunner === "vitest" ? "[Vitest](https://vitest.dev)" : "[Jest](https://jestjs.io)";
|
|
1515
|
+
writeFileEnsured(
|
|
1516
|
+
(0, import_node_path3.join)(projectRoot, "README.md"),
|
|
1517
|
+
[
|
|
1518
|
+
`# ${projectName}`,
|
|
1519
|
+
"",
|
|
1520
|
+
"Generated by [MoNecromanCI](https://github.com/russoedu/MoNecromanCi).",
|
|
1521
|
+
"",
|
|
1522
|
+
"## Building",
|
|
1523
|
+
"",
|
|
1524
|
+
`Run \`nx build ${projectName}\` to build this project.`,
|
|
1525
|
+
"",
|
|
1526
|
+
"## Running unit tests",
|
|
1527
|
+
"",
|
|
1528
|
+
`Run \`nx test ${projectName}\` to execute the unit tests via ${runner}.`,
|
|
1529
|
+
""
|
|
1530
|
+
].join("\n")
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
function removeStaleGitkeeps(workspaceRoot) {
|
|
1534
|
+
for (const scaffold of ["apps", "libs", "packages"]) {
|
|
1535
|
+
const directory = (0, import_node_path3.join)(workspaceRoot, scaffold);
|
|
1536
|
+
try {
|
|
1537
|
+
const holdsAProject = (0, import_node_fs3.readdirSync)(directory, { withFileTypes: true }).some(
|
|
1538
|
+
(entry) => entry.isDirectory()
|
|
1539
|
+
);
|
|
1540
|
+
if (holdsAProject) {
|
|
1541
|
+
(0, import_node_fs3.rmSync)((0, import_node_path3.join)(directory, ".gitkeep"), { force: true });
|
|
1542
|
+
}
|
|
1543
|
+
} catch {
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1346
1547
|
function repairPublishableManifest(manifestPath) {
|
|
1347
1548
|
const manifest = readJson(manifestPath);
|
|
1348
1549
|
if (manifest.types === WRONG_TYPES_PATH) {
|
|
@@ -1352,8 +1553,10 @@ function repairPublishableManifest(manifestPath) {
|
|
|
1352
1553
|
if (typeof dot === "object" && dot.types === WRONG_TYPES_PATH) {
|
|
1353
1554
|
dot.types = ACTUAL_TYPES_PATH;
|
|
1354
1555
|
}
|
|
1355
|
-
|
|
1356
|
-
manifest.files.
|
|
1556
|
+
for (const exclusion of [DECLARATION_MAP_EXCLUSION, SOURCE_MAP_EXCLUSION]) {
|
|
1557
|
+
if (manifest.files && !manifest.files.includes(exclusion)) {
|
|
1558
|
+
manifest.files.push(exclusion);
|
|
1559
|
+
}
|
|
1357
1560
|
}
|
|
1358
1561
|
writeFileEnsured(manifestPath, toJson(manifest));
|
|
1359
1562
|
}
|
|
@@ -1412,6 +1615,7 @@ function projectTask(name, kind) {
|
|
|
1412
1615
|
return kind === "start" ? { ...base, isBackground: true } : { ...base, group: kind };
|
|
1413
1616
|
}
|
|
1414
1617
|
function registerProjectCommands(workspaceRoot, name, commands) {
|
|
1618
|
+
removeStaleGitkeeps(workspaceRoot);
|
|
1415
1619
|
const scripts = {
|
|
1416
1620
|
[`${name}:qa`]: `nx run ${name}:lint && nx run ${name}:test`
|
|
1417
1621
|
};
|
|
@@ -1879,6 +2083,8 @@ async function addNpmLib(workspaceRoot, name, options, kindProvided, stack) {
|
|
|
1879
2083
|
const manifestPath = (0, import_node_path7.join)(workspaceRoot, "packages", name, "package.json");
|
|
1880
2084
|
markPublic(manifestPath);
|
|
1881
2085
|
repairPublishableManifest(manifestPath);
|
|
2086
|
+
repairDeclarationSpecifiers((0, import_node_path7.join)(workspaceRoot, `packages/${name}`));
|
|
2087
|
+
writeProjectReadme((0, import_node_path7.join)(workspaceRoot, `packages/${name}`), `${scope}/${name}`, stack.testRunner);
|
|
1882
2088
|
removeGeneratedEslintConfig(workspaceRoot, `packages/${name}`);
|
|
1883
2089
|
registerProjectCommands(workspaceRoot, name, { build: true });
|
|
1884
2090
|
}
|
|
@@ -2207,6 +2413,8 @@ async function addReactLib(workspaceRoot, name, options, kindProvided, stack) {
|
|
|
2207
2413
|
const publishableManifest = (0, import_node_path10.join)(workspaceRoot, "packages", name, "package.json");
|
|
2208
2414
|
markPublic(publishableManifest);
|
|
2209
2415
|
repairPublishableManifest(publishableManifest);
|
|
2416
|
+
repairDeclarationSpecifiers((0, import_node_path10.join)(workspaceRoot, "packages", name));
|
|
2417
|
+
writeProjectReadme((0, import_node_path10.join)(workspaceRoot, "packages", name), `${scope}/${name}`, stack.testRunner);
|
|
2210
2418
|
removeGeneratedEslintConfig(workspaceRoot, `packages/${name}`);
|
|
2211
2419
|
registerProjectCommands(workspaceRoot, name, { build: true });
|
|
2212
2420
|
}
|
|
@@ -2228,10 +2436,611 @@ function addReactInternalLib(workspaceRoot, name, stack) {
|
|
|
2228
2436
|
const privateManifest = (0, import_node_path10.join)(workspaceRoot, "libs", name, "package.json");
|
|
2229
2437
|
markPrivate(privateManifest);
|
|
2230
2438
|
repairPublishableManifest(privateManifest);
|
|
2439
|
+
repairDeclarationSpecifiers((0, import_node_path10.join)(workspaceRoot, "libs", name));
|
|
2440
|
+
writeProjectReadme((0, import_node_path10.join)(workspaceRoot, "libs", name), name, stack.testRunner);
|
|
2231
2441
|
removeGeneratedEslintConfig(workspaceRoot, `libs/${name}`);
|
|
2232
2442
|
registerProjectCommands(workspaceRoot, name, { build: true });
|
|
2233
2443
|
}
|
|
2234
2444
|
|
|
2445
|
+
// src/commands/sync.ts
|
|
2446
|
+
var import_node_path12 = require("path");
|
|
2447
|
+
|
|
2448
|
+
// src/deps/inventory.ts
|
|
2449
|
+
var import_node_fs7 = require("fs");
|
|
2450
|
+
var import_node_path11 = require("path");
|
|
2451
|
+
var ECOSYSTEMS = ["npm", "pip", "pub", "go"];
|
|
2452
|
+
var ROOT_LABEL = "(root)";
|
|
2453
|
+
var MANIFEST_GLOBS = {
|
|
2454
|
+
// `libs/*` carries private internal libs; `packages/*` the publishable ones.
|
|
2455
|
+
npm: ["apps/*/package.json", "libs/*/package.json", "packages/*/package.json"],
|
|
2456
|
+
// Matches PYTHON_PROJECT_DIRS in commands/add/python.ts.
|
|
2457
|
+
pip: ["apps/*/pyproject.toml", "python-packages/*/pyproject.toml", "libs/*/pyproject.toml"],
|
|
2458
|
+
pub: ["apps/*/pubspec.yaml", "packages/*/pubspec.yaml", "libs/*/pubspec.yaml"],
|
|
2459
|
+
// Single root module by design — there are no per-project Go manifests.
|
|
2460
|
+
go: []
|
|
2461
|
+
};
|
|
2462
|
+
var PEER_SECTION = "peerDep";
|
|
2463
|
+
var NPM_SECTIONS = {
|
|
2464
|
+
dependencies: "dep",
|
|
2465
|
+
devDependencies: "devDep",
|
|
2466
|
+
peerDependencies: "peerDep",
|
|
2467
|
+
optionalDependencies: "optionalDep"
|
|
2468
|
+
};
|
|
2469
|
+
function toPosix(path) {
|
|
2470
|
+
return path.replaceAll("\\", "/");
|
|
2471
|
+
}
|
|
2472
|
+
function projectOf(manifestRelativePath) {
|
|
2473
|
+
const posix = toPosix(manifestRelativePath);
|
|
2474
|
+
const lastSlash = posix.lastIndexOf("/");
|
|
2475
|
+
return lastSlash === -1 ? ROOT_LABEL : posix.slice(0, lastSlash);
|
|
2476
|
+
}
|
|
2477
|
+
function hasEcosystem(workspaceRoot, ecosystem) {
|
|
2478
|
+
if (ecosystem === "go") {
|
|
2479
|
+
return fileExists((0, import_node_path11.join)(workspaceRoot, "go.mod"));
|
|
2480
|
+
}
|
|
2481
|
+
const rootMarker = {
|
|
2482
|
+
npm: "package.json",
|
|
2483
|
+
pip: "requirements-dev.txt",
|
|
2484
|
+
pub: "pubspec.yaml"
|
|
2485
|
+
};
|
|
2486
|
+
return fileExists((0, import_node_path11.join)(workspaceRoot, rootMarker[ecosystem])) || (0, import_node_fs7.globSync)(MANIFEST_GLOBS[ecosystem], { cwd: workspaceRoot }).length > 0;
|
|
2487
|
+
}
|
|
2488
|
+
function collectNpm(workspaceRoot) {
|
|
2489
|
+
const sites = [];
|
|
2490
|
+
const relativePaths = ["package.json", ...(0, import_node_fs7.globSync)(MANIFEST_GLOBS.npm, { cwd: workspaceRoot })];
|
|
2491
|
+
for (const relativePath of relativePaths) {
|
|
2492
|
+
const manifestPath = (0, import_node_path11.join)(workspaceRoot, relativePath);
|
|
2493
|
+
try {
|
|
2494
|
+
const manifest = readJson(manifestPath);
|
|
2495
|
+
sites.push(...npmManifestSites(manifest, relativePath, manifestPath));
|
|
2496
|
+
} catch {
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
return sites;
|
|
2500
|
+
}
|
|
2501
|
+
function npmManifestSites(manifest, relativePath, manifestPath) {
|
|
2502
|
+
const sites = [];
|
|
2503
|
+
const project = projectOf(relativePath);
|
|
2504
|
+
for (const [block, section] of Object.entries(NPM_SECTIONS)) {
|
|
2505
|
+
const declared = manifest[block] ?? {};
|
|
2506
|
+
for (const [name, spec] of Object.entries(declared)) {
|
|
2507
|
+
sites.push({
|
|
2508
|
+
name,
|
|
2509
|
+
ecosystem: "npm",
|
|
2510
|
+
project,
|
|
2511
|
+
manifestPath,
|
|
2512
|
+
section,
|
|
2513
|
+
spec,
|
|
2514
|
+
// Two independent reasons to refuse a rewrite. A `file:`/`git+`/
|
|
2515
|
+
// `https:` target, a `workspace:` protocol or npm's `npm:pkg@range`
|
|
2516
|
+
// alias is a shape this cannot safely edit; a peer range is a shape it
|
|
2517
|
+
// could edit and must not (see PEER_SECTION).
|
|
2518
|
+
rewritable: section !== PEER_SECTION && /^[\^~>=<]*\d/.test(spec.trim())
|
|
2519
|
+
});
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
return sites;
|
|
2523
|
+
}
|
|
2524
|
+
function parseRequirement(requirement) {
|
|
2525
|
+
const comment = requirement.indexOf("#");
|
|
2526
|
+
const text = (comment === -1 ? requirement : requirement.slice(0, comment)).trim();
|
|
2527
|
+
if (!text || text.startsWith("-")) {
|
|
2528
|
+
return void 0;
|
|
2529
|
+
}
|
|
2530
|
+
const name = /^[a-z0-9][\w.-]*/i.exec(text)?.[0];
|
|
2531
|
+
if (!name) {
|
|
2532
|
+
return void 0;
|
|
2533
|
+
}
|
|
2534
|
+
const spec = text.slice(name.length).trim();
|
|
2535
|
+
return {
|
|
2536
|
+
name,
|
|
2537
|
+
spec,
|
|
2538
|
+
// Extras, environment markers and compound ranges are reportable only.
|
|
2539
|
+
rewritable: !spec.startsWith("[") && !spec.includes(";") && !spec.includes(",")
|
|
2540
|
+
};
|
|
2541
|
+
}
|
|
2542
|
+
function collectPip(workspaceRoot) {
|
|
2543
|
+
const sites = [];
|
|
2544
|
+
const push = (relativePath, section, requirement) => {
|
|
2545
|
+
const parsed = parseRequirement(requirement);
|
|
2546
|
+
if (!parsed) {
|
|
2547
|
+
return;
|
|
2548
|
+
}
|
|
2549
|
+
sites.push({
|
|
2550
|
+
name: parsed.name,
|
|
2551
|
+
ecosystem: "pip",
|
|
2552
|
+
project: projectOf(relativePath),
|
|
2553
|
+
manifestPath: (0, import_node_path11.join)(workspaceRoot, relativePath),
|
|
2554
|
+
section,
|
|
2555
|
+
spec: parsed.spec,
|
|
2556
|
+
rewritable: parsed.rewritable && parsed.spec !== ""
|
|
2557
|
+
});
|
|
2558
|
+
};
|
|
2559
|
+
const pyprojectPaths = (0, import_node_fs7.globSync)(MANIFEST_GLOBS.pip, { cwd: workspaceRoot });
|
|
2560
|
+
for (const relativePath of pyprojectPaths) {
|
|
2561
|
+
let content;
|
|
2562
|
+
try {
|
|
2563
|
+
content = (0, import_node_fs7.readFileSync)((0, import_node_path11.join)(workspaceRoot, relativePath), "utf8");
|
|
2564
|
+
} catch {
|
|
2565
|
+
continue;
|
|
2566
|
+
}
|
|
2567
|
+
const declared = pyprojectDependencies(content);
|
|
2568
|
+
for (const entry of declared) {
|
|
2569
|
+
push(relativePath, "dep", entry);
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
const requirementsPaths = [
|
|
2573
|
+
"requirements-dev.txt",
|
|
2574
|
+
...(0, import_node_fs7.globSync)("apps/*/requirements.txt", { cwd: workspaceRoot })
|
|
2575
|
+
];
|
|
2576
|
+
for (const relativePath of requirementsPaths) {
|
|
2577
|
+
if (!fileExists((0, import_node_path11.join)(workspaceRoot, relativePath))) {
|
|
2578
|
+
continue;
|
|
2579
|
+
}
|
|
2580
|
+
const section = relativePath === "requirements-dev.txt" ? "devDep" : "dep";
|
|
2581
|
+
const lines = (0, import_node_fs7.readFileSync)((0, import_node_path11.join)(workspaceRoot, relativePath), "utf8").split(/\r?\n/);
|
|
2582
|
+
for (const line of lines) {
|
|
2583
|
+
push(relativePath, section, line);
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
return sites;
|
|
2587
|
+
}
|
|
2588
|
+
function pyprojectDependencies(content) {
|
|
2589
|
+
const entries = [];
|
|
2590
|
+
let inProjectTable = false;
|
|
2591
|
+
let inArray = false;
|
|
2592
|
+
for (const line of content.split(/\r?\n/)) {
|
|
2593
|
+
const text = line.trim();
|
|
2594
|
+
if (text.startsWith("[")) {
|
|
2595
|
+
inProjectTable = text === "[project]";
|
|
2596
|
+
inArray = false;
|
|
2597
|
+
continue;
|
|
2598
|
+
}
|
|
2599
|
+
if (!inProjectTable) {
|
|
2600
|
+
continue;
|
|
2601
|
+
}
|
|
2602
|
+
if (!inArray && /^dependencies\s*=\s*\[/.test(text)) {
|
|
2603
|
+
inArray = true;
|
|
2604
|
+
}
|
|
2605
|
+
if (!inArray) {
|
|
2606
|
+
continue;
|
|
2607
|
+
}
|
|
2608
|
+
for (const quoted of text.matchAll(/"([^"]*)"|'([^']*)'/g)) {
|
|
2609
|
+
entries.push(quoted[1] ?? quoted[2]);
|
|
2610
|
+
}
|
|
2611
|
+
if (text.includes("]")) {
|
|
2612
|
+
inArray = false;
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
return entries;
|
|
2616
|
+
}
|
|
2617
|
+
function collectPub(workspaceRoot) {
|
|
2618
|
+
const sites = [];
|
|
2619
|
+
const pubspecPaths = (0, import_node_fs7.globSync)(MANIFEST_GLOBS.pub, { cwd: workspaceRoot });
|
|
2620
|
+
for (const relativePath of pubspecPaths) {
|
|
2621
|
+
let content;
|
|
2622
|
+
try {
|
|
2623
|
+
content = (0, import_node_fs7.readFileSync)((0, import_node_path11.join)(workspaceRoot, relativePath), "utf8");
|
|
2624
|
+
} catch {
|
|
2625
|
+
continue;
|
|
2626
|
+
}
|
|
2627
|
+
for (const block of ["dependencies", "dev_dependencies"]) {
|
|
2628
|
+
for (const entry of pubspecBlockEntries(content, block)) {
|
|
2629
|
+
sites.push({
|
|
2630
|
+
name: entry.name,
|
|
2631
|
+
ecosystem: "pub",
|
|
2632
|
+
project: projectOf(relativePath),
|
|
2633
|
+
manifestPath: (0, import_node_path11.join)(workspaceRoot, relativePath),
|
|
2634
|
+
section: block === "dependencies" ? "dep" : "devDep",
|
|
2635
|
+
spec: entry.spec,
|
|
2636
|
+
rewritable: entry.spec !== ""
|
|
2637
|
+
});
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
return sites;
|
|
2642
|
+
}
|
|
2643
|
+
function pubspecBlockEntries(content, block) {
|
|
2644
|
+
const lines = content.split(/\r?\n/);
|
|
2645
|
+
const start = lines.findIndex((line) => line.trimEnd() === `${block}:`);
|
|
2646
|
+
if (start === -1) {
|
|
2647
|
+
return [];
|
|
2648
|
+
}
|
|
2649
|
+
const entries = [];
|
|
2650
|
+
let indent;
|
|
2651
|
+
const body = lines.slice(start + 1);
|
|
2652
|
+
for (const line of body) {
|
|
2653
|
+
if (line.trim() === "" || line.trimStart().startsWith("#")) {
|
|
2654
|
+
continue;
|
|
2655
|
+
}
|
|
2656
|
+
const lineIndent = line.length - line.trimStart().length;
|
|
2657
|
+
if (lineIndent === 0) {
|
|
2658
|
+
break;
|
|
2659
|
+
}
|
|
2660
|
+
indent ??= lineIndent;
|
|
2661
|
+
if (lineIndent > indent) {
|
|
2662
|
+
continue;
|
|
2663
|
+
}
|
|
2664
|
+
if (lineIndent < indent) {
|
|
2665
|
+
break;
|
|
2666
|
+
}
|
|
2667
|
+
const colon = line.indexOf(":");
|
|
2668
|
+
if (colon === -1) {
|
|
2669
|
+
continue;
|
|
2670
|
+
}
|
|
2671
|
+
const key = line.slice(0, colon).trim();
|
|
2672
|
+
if (/^[a-z_][\w-]*$/i.test(key)) {
|
|
2673
|
+
entries.push({ name: key, spec: stripQuotes(line.slice(colon + 1).trim()) });
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
return entries;
|
|
2677
|
+
}
|
|
2678
|
+
function stripQuotes(value) {
|
|
2679
|
+
return /^(['"]).*\1$/.test(value) ? value.slice(1, -1) : value;
|
|
2680
|
+
}
|
|
2681
|
+
function collectGo(workspaceRoot) {
|
|
2682
|
+
const manifestPath = (0, import_node_path11.join)(workspaceRoot, "go.mod");
|
|
2683
|
+
if (!fileExists(manifestPath)) {
|
|
2684
|
+
return [];
|
|
2685
|
+
}
|
|
2686
|
+
let content;
|
|
2687
|
+
try {
|
|
2688
|
+
content = (0, import_node_fs7.readFileSync)(manifestPath, "utf8");
|
|
2689
|
+
} catch {
|
|
2690
|
+
return [];
|
|
2691
|
+
}
|
|
2692
|
+
const sites = Array.from(content.matchAll(
|
|
2693
|
+
/^\s*(?:require\s+)?([\w.~-]+(?:\/[\w.~-]+)+)\s+(v\S+)(\s*\/\/\s*indirect)?/gm
|
|
2694
|
+
), (match) => ({
|
|
2695
|
+
name: match[1],
|
|
2696
|
+
ecosystem: "go",
|
|
2697
|
+
project: ROOT_LABEL,
|
|
2698
|
+
manifestPath,
|
|
2699
|
+
section: match[3] ? "indirect" : "module",
|
|
2700
|
+
spec: match[2],
|
|
2701
|
+
rewritable: false
|
|
2702
|
+
}));
|
|
2703
|
+
return sites;
|
|
2704
|
+
}
|
|
2705
|
+
function collectInventory(workspaceRoot, ecosystems = ECOSYSTEMS) {
|
|
2706
|
+
const readers = {
|
|
2707
|
+
npm: collectNpm,
|
|
2708
|
+
pip: collectPip,
|
|
2709
|
+
pub: collectPub,
|
|
2710
|
+
go: collectGo
|
|
2711
|
+
};
|
|
2712
|
+
const inventory = /* @__PURE__ */ new Map();
|
|
2713
|
+
for (const ecosystem of ecosystems) {
|
|
2714
|
+
if (!hasEcosystem(workspaceRoot, ecosystem)) {
|
|
2715
|
+
continue;
|
|
2716
|
+
}
|
|
2717
|
+
const sites = readers[ecosystem](workspaceRoot);
|
|
2718
|
+
for (const site of sites) {
|
|
2719
|
+
const existing = inventory.get(site.name);
|
|
2720
|
+
if (existing) {
|
|
2721
|
+
existing.push(site);
|
|
2722
|
+
} else {
|
|
2723
|
+
inventory.set(site.name, [site]);
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
return inventory;
|
|
2728
|
+
}
|
|
2729
|
+
function rewriteSpec(site, spec) {
|
|
2730
|
+
if (!site.rewritable || spec === site.spec) {
|
|
2731
|
+
return false;
|
|
2732
|
+
}
|
|
2733
|
+
const { name } = site;
|
|
2734
|
+
if (site.ecosystem === "npm") {
|
|
2735
|
+
const manifest = readJson(site.manifestPath);
|
|
2736
|
+
const block = Object.entries(NPM_SECTIONS).find(([, label]) => label === site.section)?.[0];
|
|
2737
|
+
if (!block || manifest[block]?.[name] === void 0) {
|
|
2738
|
+
return false;
|
|
2739
|
+
}
|
|
2740
|
+
manifest[block][name] = spec;
|
|
2741
|
+
writeFileEnsured(site.manifestPath, toJson(manifest));
|
|
2742
|
+
return true;
|
|
2743
|
+
}
|
|
2744
|
+
const before = (0, import_node_fs7.readFileSync)(site.manifestPath, "utf8");
|
|
2745
|
+
const after = site.ecosystem === "pip" ? replacePipSpec(before, name, spec) : replacePubSpec(before, name, spec);
|
|
2746
|
+
if (after === before) {
|
|
2747
|
+
return false;
|
|
2748
|
+
}
|
|
2749
|
+
writeFileEnsured(site.manifestPath, after);
|
|
2750
|
+
return true;
|
|
2751
|
+
}
|
|
2752
|
+
function replacePipSpec(content, name, spec) {
|
|
2753
|
+
const escaped = escapeForRegex(name);
|
|
2754
|
+
return content.replaceAll(
|
|
2755
|
+
new RegExp(String.raw`(^|["'\s])(${escaped})\s*(?:[<>=!~^][^"'\n,]*)?(?=["'\n]|$)`, "gm"),
|
|
2756
|
+
(match, lead, matched) => `${lead}${matched}${spec}`
|
|
2757
|
+
);
|
|
2758
|
+
}
|
|
2759
|
+
function replacePubSpec(content, name, spec) {
|
|
2760
|
+
return content.replaceAll(
|
|
2761
|
+
new RegExp(String.raw`^(\s+${escapeForRegex(name)}:[^\S\n]*)\S[^\n]*$`, "gm"),
|
|
2762
|
+
(match, lead) => `${lead}${spec}`
|
|
2763
|
+
);
|
|
2764
|
+
}
|
|
2765
|
+
function escapeForRegex(value) {
|
|
2766
|
+
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
2767
|
+
}
|
|
2768
|
+
function isWorkspaceProject(workspaceRoot, name) {
|
|
2769
|
+
const short = name.includes("/") ? name.split("/").at(-1) : name;
|
|
2770
|
+
return ["apps", "libs", "packages"].some(
|
|
2771
|
+
(directory) => fileExists((0, import_node_path11.join)(workspaceRoot, directory, short ?? name, "package.json"))
|
|
2772
|
+
);
|
|
2773
|
+
}
|
|
2774
|
+
function isAliasedInstall(workspaceRoot, name) {
|
|
2775
|
+
try {
|
|
2776
|
+
const installed = readJson(
|
|
2777
|
+
(0, import_node_path11.join)(workspaceRoot, "node_modules", name, "package.json")
|
|
2778
|
+
);
|
|
2779
|
+
return installed.name !== void 0 && installed.name !== name;
|
|
2780
|
+
} catch {
|
|
2781
|
+
return false;
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
function resolvedVersion(workspaceRoot, ecosystem, name) {
|
|
2785
|
+
if (ecosystem === "npm") {
|
|
2786
|
+
try {
|
|
2787
|
+
return readJson(
|
|
2788
|
+
(0, import_node_path11.join)(workspaceRoot, "node_modules", name, "package.json")
|
|
2789
|
+
).version;
|
|
2790
|
+
} catch {
|
|
2791
|
+
return void 0;
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
if (ecosystem === "pub") {
|
|
2795
|
+
const lockPath = (0, import_node_path11.join)(workspaceRoot, "pubspec.lock");
|
|
2796
|
+
if (!fileExists(lockPath)) {
|
|
2797
|
+
return void 0;
|
|
2798
|
+
}
|
|
2799
|
+
try {
|
|
2800
|
+
const match = new RegExp(
|
|
2801
|
+
String.raw`^\s{2}${escapeForRegex(name)}:[^]*?^\s+version:\s*["']?([^"'\s]+)`,
|
|
2802
|
+
"m"
|
|
2803
|
+
).exec((0, import_node_fs7.readFileSync)(lockPath, "utf8"));
|
|
2804
|
+
return match?.[1];
|
|
2805
|
+
} catch {
|
|
2806
|
+
return void 0;
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
if (ecosystem === "pip") {
|
|
2810
|
+
const python = process.platform === "win32" ? "python" : "python3";
|
|
2811
|
+
const result = runCapture(python, ["-m", "pip", "show", name], workspaceRoot);
|
|
2812
|
+
if (result.status !== 0) {
|
|
2813
|
+
return void 0;
|
|
2814
|
+
}
|
|
2815
|
+
return /^Version:\s*(\S+)/m.exec(result.stdout)?.[1];
|
|
2816
|
+
}
|
|
2817
|
+
return void 0;
|
|
2818
|
+
}
|
|
2819
|
+
|
|
2820
|
+
// src/deps/semver.ts
|
|
2821
|
+
var UPDATE_KINDS = ["patch", "minor", "major", "non-semver"];
|
|
2822
|
+
var UPDATE_KIND_LABELS = {
|
|
2823
|
+
patch: { title: "Patch Update", blurb: "Backwards-compatible bug fixes." },
|
|
2824
|
+
minor: { title: "Minor Update", blurb: "New backwards-compatible features." },
|
|
2825
|
+
major: { title: "Major Update", blurb: "Potentially breaking API changes. Use caution." },
|
|
2826
|
+
"non-semver": { title: "Non-Semver", blurb: "Versions less than 1.0.0, caution." }
|
|
2827
|
+
};
|
|
2828
|
+
var RANGE_OPERATORS = [">=", "<=", "^", "~", ">", "<", "="];
|
|
2829
|
+
function parseVersion(raw) {
|
|
2830
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:-([\w.-]+))?/.exec(raw.trim());
|
|
2831
|
+
if (!match) {
|
|
2832
|
+
return void 0;
|
|
2833
|
+
}
|
|
2834
|
+
return {
|
|
2835
|
+
major: Number(match[1]),
|
|
2836
|
+
minor: Number(match[2]),
|
|
2837
|
+
patch: Number(match[3]),
|
|
2838
|
+
prerelease: match[4]
|
|
2839
|
+
};
|
|
2840
|
+
}
|
|
2841
|
+
function rangeOperator(spec) {
|
|
2842
|
+
const trimmed = spec.trim();
|
|
2843
|
+
return RANGE_OPERATORS.find((operator) => trimmed.startsWith(operator)) ?? "";
|
|
2844
|
+
}
|
|
2845
|
+
function specVersion(spec) {
|
|
2846
|
+
const bare2 = spec.trim().slice(rangeOperator(spec).length).trim();
|
|
2847
|
+
return /^\d+\.\d+(?:\.\d+)?(?:-[\w.-]+)?$/.test(bare2) ? bare2 : void 0;
|
|
2848
|
+
}
|
|
2849
|
+
function compareVersions(left, right) {
|
|
2850
|
+
const a = parseVersion(left);
|
|
2851
|
+
const b = parseVersion(right);
|
|
2852
|
+
if (!a || !b) {
|
|
2853
|
+
return 0;
|
|
2854
|
+
}
|
|
2855
|
+
if (a.major !== b.major) {
|
|
2856
|
+
return a.major - b.major;
|
|
2857
|
+
}
|
|
2858
|
+
if (a.minor !== b.minor) {
|
|
2859
|
+
return a.minor - b.minor;
|
|
2860
|
+
}
|
|
2861
|
+
if (a.patch !== b.patch) {
|
|
2862
|
+
return a.patch - b.patch;
|
|
2863
|
+
}
|
|
2864
|
+
if (a.prerelease === b.prerelease) {
|
|
2865
|
+
return 0;
|
|
2866
|
+
}
|
|
2867
|
+
if (!a.prerelease) {
|
|
2868
|
+
return 1;
|
|
2869
|
+
}
|
|
2870
|
+
if (!b.prerelease) {
|
|
2871
|
+
return -1;
|
|
2872
|
+
}
|
|
2873
|
+
return a.prerelease < b.prerelease ? -1 : 1;
|
|
2874
|
+
}
|
|
2875
|
+
function isNewer(candidate, current) {
|
|
2876
|
+
return compareVersions(candidate, current) > 0;
|
|
2877
|
+
}
|
|
2878
|
+
function classify(current, latest) {
|
|
2879
|
+
const from = parseVersion(current);
|
|
2880
|
+
const to = parseVersion(latest);
|
|
2881
|
+
if (!from || !to || from.major === 0) {
|
|
2882
|
+
return "non-semver";
|
|
2883
|
+
}
|
|
2884
|
+
if (from.major !== to.major) {
|
|
2885
|
+
return "major";
|
|
2886
|
+
}
|
|
2887
|
+
return from.minor === to.minor ? "patch" : "minor";
|
|
2888
|
+
}
|
|
2889
|
+
function highestSpec(specs) {
|
|
2890
|
+
let best;
|
|
2891
|
+
let bestVersion;
|
|
2892
|
+
for (const spec of specs) {
|
|
2893
|
+
const version = specVersion(spec);
|
|
2894
|
+
if (!version) {
|
|
2895
|
+
continue;
|
|
2896
|
+
}
|
|
2897
|
+
if (!bestVersion || isNewer(version, bestVersion)) {
|
|
2898
|
+
best = spec;
|
|
2899
|
+
bestVersion = version;
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2902
|
+
return best;
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2905
|
+
// src/commands/sync.ts
|
|
2906
|
+
function resolveEcosystems(requested) {
|
|
2907
|
+
if (!requested) {
|
|
2908
|
+
return ECOSYSTEMS;
|
|
2909
|
+
}
|
|
2910
|
+
const match = ECOSYSTEMS.find((ecosystem) => ecosystem === requested);
|
|
2911
|
+
if (!match) {
|
|
2912
|
+
throw new Error(
|
|
2913
|
+
`Unknown ecosystem '${requested}'. Expected one of: ${ECOSYSTEMS.join(", ")}.`
|
|
2914
|
+
);
|
|
2915
|
+
}
|
|
2916
|
+
return [match];
|
|
2917
|
+
}
|
|
2918
|
+
function collectDrift(workspaceRoot, ecosystems = ECOSYSTEMS) {
|
|
2919
|
+
const reconcilable = ecosystems.filter((ecosystem) => ecosystem !== "go");
|
|
2920
|
+
const inventory = collectInventory(workspaceRoot, reconcilable);
|
|
2921
|
+
const drift = [];
|
|
2922
|
+
for (const [name, allSites] of inventory) {
|
|
2923
|
+
const sites = allSites.filter((site) => site.section !== PEER_SECTION);
|
|
2924
|
+
if (sites.length < 2 || isWorkspaceLocal(workspaceRoot, sites, name)) {
|
|
2925
|
+
continue;
|
|
2926
|
+
}
|
|
2927
|
+
const distinct = new Set(sites.map((site) => site.spec));
|
|
2928
|
+
if (distinct.size < 2) {
|
|
2929
|
+
continue;
|
|
2930
|
+
}
|
|
2931
|
+
const installed = resolvedVersion(workspaceRoot, sites[0].ecosystem, name);
|
|
2932
|
+
const target = chooseTarget(sites, installed);
|
|
2933
|
+
if (!target) {
|
|
2934
|
+
continue;
|
|
2935
|
+
}
|
|
2936
|
+
const disagreeing = sites.filter((site) => site.spec !== target);
|
|
2937
|
+
drift.push({
|
|
2938
|
+
name,
|
|
2939
|
+
ecosystem: sites[0].ecosystem,
|
|
2940
|
+
target,
|
|
2941
|
+
reason: installed ? "resolved" : "highest",
|
|
2942
|
+
fixable: disagreeing.filter((site) => site.rewritable),
|
|
2943
|
+
blocked: disagreeing.filter((site) => !site.rewritable)
|
|
2944
|
+
});
|
|
2945
|
+
}
|
|
2946
|
+
return drift.toSorted((left, right) => left.name.localeCompare(right.name));
|
|
2947
|
+
}
|
|
2948
|
+
function isWorkspaceLocal(workspaceRoot, sites, name) {
|
|
2949
|
+
return sites[0].ecosystem === "npm" && isWorkspaceProject(workspaceRoot, name);
|
|
2950
|
+
}
|
|
2951
|
+
function chooseTarget(sites, installed) {
|
|
2952
|
+
if (!installed) {
|
|
2953
|
+
return highestSpec(sites.map((site) => site.spec));
|
|
2954
|
+
}
|
|
2955
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2956
|
+
for (const site of sites) {
|
|
2957
|
+
if (!specVersion(site.spec)) {
|
|
2958
|
+
continue;
|
|
2959
|
+
}
|
|
2960
|
+
const operator2 = rangeOperator(site.spec);
|
|
2961
|
+
counts.set(operator2, (counts.get(operator2) ?? 0) + 1);
|
|
2962
|
+
}
|
|
2963
|
+
if (counts.size === 0) {
|
|
2964
|
+
return void 0;
|
|
2965
|
+
}
|
|
2966
|
+
const [operator] = [...counts].toSorted(
|
|
2967
|
+
(left, right) => right[1] - left[1] || (left[0] === "^" ? -1 : 1)
|
|
2968
|
+
)[0];
|
|
2969
|
+
return `${operator}${installed}`;
|
|
2970
|
+
}
|
|
2971
|
+
function applyDrift(drift) {
|
|
2972
|
+
const changed = /* @__PURE__ */ new Set();
|
|
2973
|
+
for (const finding of drift) {
|
|
2974
|
+
for (const site of finding.fixable) {
|
|
2975
|
+
if (rewriteSpec(site, finding.target)) {
|
|
2976
|
+
changed.add(site.manifestPath);
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
return [...changed];
|
|
2981
|
+
}
|
|
2982
|
+
function syncProjectReferences(workspaceRoot) {
|
|
2983
|
+
logger.step("Syncing TypeScript project references (nx sync)");
|
|
2984
|
+
if (runShell("npx", ["nx", "sync"], workspaceRoot) !== 0) {
|
|
2985
|
+
logger.warn(
|
|
2986
|
+
"nx sync did not complete \u2014 run `npx nx sync` yourself so cross-project imports resolve in your editor."
|
|
2987
|
+
);
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
function describe(finding) {
|
|
2991
|
+
const sites = [...finding.fixable, ...finding.blocked].map((site) => `${site.project === ROOT_LABEL ? ROOT_LABEL : site.project} ${site.spec}`).join(", ");
|
|
2992
|
+
return `${finding.name} \u2192 ${finding.target} (${finding.reason}); disagreeing: ${sites}`;
|
|
2993
|
+
}
|
|
2994
|
+
function runSync(workspaceRoot, options) {
|
|
2995
|
+
if (!fileExists((0, import_node_path12.join)(workspaceRoot, "nx.json"))) {
|
|
2996
|
+
throw new Error(
|
|
2997
|
+
`No nx.json found in ${workspaceRoot} \u2014 run 'mnci sync' from the workspace root.`
|
|
2998
|
+
);
|
|
2999
|
+
}
|
|
3000
|
+
const ecosystems = resolveEcosystems(options.ecosystem);
|
|
3001
|
+
for (const ecosystem of ecosystems) {
|
|
3002
|
+
if (!hasEcosystem(workspaceRoot, ecosystem)) {
|
|
3003
|
+
logger.info(`\u2298 SKIPPED ${ecosystem} \u2014 no ${ecosystem} project in this workspace.`);
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
if (ecosystems.includes("go") && hasEcosystem(workspaceRoot, "go")) {
|
|
3007
|
+
logger.success("go \u2014 nothing to sync; one root go.mod means one version of every module.");
|
|
3008
|
+
}
|
|
3009
|
+
const drift = collectDrift(workspaceRoot, ecosystems);
|
|
3010
|
+
if (drift.length === 0) {
|
|
3011
|
+
logger.success("Every project agrees on every shared dependency version.");
|
|
3012
|
+
} else {
|
|
3013
|
+
for (const finding of drift) {
|
|
3014
|
+
logger.info(` ${describe(finding)}`);
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
if (options.check) {
|
|
3018
|
+
if (drift.length > 0) {
|
|
3019
|
+
logger.error(
|
|
3020
|
+
`${drift.length} package(s) declared at more than one version. Run 'mnci sync' to converge them.`
|
|
3021
|
+
);
|
|
3022
|
+
process.exitCode = 1;
|
|
3023
|
+
}
|
|
3024
|
+
return;
|
|
3025
|
+
}
|
|
3026
|
+
const changed = applyDrift(drift);
|
|
3027
|
+
for (const path of changed) {
|
|
3028
|
+
logger.detail(`updated ${path}`);
|
|
3029
|
+
}
|
|
3030
|
+
const blocked = drift.flatMap((finding) => finding.blocked);
|
|
3031
|
+
for (const site of blocked) {
|
|
3032
|
+
logger.warn(
|
|
3033
|
+
`${site.name} in ${site.project} is declared as '${site.spec}', which is not a plain version range \u2014 left unchanged.`
|
|
3034
|
+
);
|
|
3035
|
+
}
|
|
3036
|
+
syncProjectReferences(workspaceRoot);
|
|
3037
|
+
if (changed.length > 0) {
|
|
3038
|
+
logger.step("Formatting the changed manifests (eslint --fix)");
|
|
3039
|
+
runFormatter(workspaceRoot);
|
|
3040
|
+
}
|
|
3041
|
+
logger.success("Done. Review the changes with `git diff` before committing.");
|
|
3042
|
+
}
|
|
3043
|
+
|
|
2235
3044
|
// src/commands/add.ts
|
|
2236
3045
|
var PROJECT_KINDS = [
|
|
2237
3046
|
"react-app",
|
|
@@ -2256,7 +3065,7 @@ var PROJECT_KINDS = [
|
|
|
2256
3065
|
];
|
|
2257
3066
|
async function runAdd(kind, name, options) {
|
|
2258
3067
|
const workspaceRoot = process.cwd();
|
|
2259
|
-
if (!fileExists((0,
|
|
3068
|
+
if (!fileExists((0, import_node_path13.join)(workspaceRoot, "nx.json"))) {
|
|
2260
3069
|
throw new Error("No nx.json found here. Run `add` from the workspace root.");
|
|
2261
3070
|
}
|
|
2262
3071
|
const stack = readWorkspaceStack(workspaceRoot);
|
|
@@ -2305,7 +3114,7 @@ async function runAdd(kind, name, options) {
|
|
|
2305
3114
|
],
|
|
2306
3115
|
workspaceRoot
|
|
2307
3116
|
);
|
|
2308
|
-
markPrivate((0,
|
|
3117
|
+
markPrivate((0, import_node_path13.join)(workspaceRoot, "libs", resolvedName, "package.json"));
|
|
2309
3118
|
removeGeneratedEslintConfig(workspaceRoot, `libs/${resolvedName}`);
|
|
2310
3119
|
registerProjectCommands(workspaceRoot, resolvedName, { build: true });
|
|
2311
3120
|
break;
|
|
@@ -2371,17 +3180,9 @@ async function runAdd(kind, name, options) {
|
|
|
2371
3180
|
runFormatter(workspaceRoot);
|
|
2372
3181
|
logger.success(`Added ${resolvedKind} '${resolvedName}'.`);
|
|
2373
3182
|
}
|
|
2374
|
-
function syncProjectReferences(workspaceRoot) {
|
|
2375
|
-
logger.step("Syncing TypeScript project references (nx sync)");
|
|
2376
|
-
if (runShell("npx", ["nx", "sync"], workspaceRoot) !== 0) {
|
|
2377
|
-
logger.warn(
|
|
2378
|
-
"nx sync did not complete \u2014 run `npx nx sync` yourself so cross-project imports resolve in your editor."
|
|
2379
|
-
);
|
|
2380
|
-
}
|
|
2381
|
-
}
|
|
2382
3183
|
function readWorkspaceStack(workspaceRoot) {
|
|
2383
3184
|
const nxJson = readJson(
|
|
2384
|
-
(0,
|
|
3185
|
+
(0, import_node_path13.join)(workspaceRoot, "nx.json")
|
|
2385
3186
|
);
|
|
2386
3187
|
const stack = nxJson.mnci?.stack;
|
|
2387
3188
|
return {
|
|
@@ -2390,17 +3191,17 @@ function readWorkspaceStack(workspaceRoot) {
|
|
|
2390
3191
|
}
|
|
2391
3192
|
|
|
2392
3193
|
// src/commands/doctor.ts
|
|
2393
|
-
var
|
|
2394
|
-
var
|
|
3194
|
+
var import_node_fs8 = require("fs");
|
|
3195
|
+
var import_node_path14 = require("path");
|
|
2395
3196
|
var SUPPORTED_ESLINT_MAJOR = ESLINT_VERSION.replace(/^\D*/, "").split(".", 1)[0];
|
|
2396
|
-
function
|
|
3197
|
+
function toPosix2(path) {
|
|
2397
3198
|
return path.replaceAll("\\", "/");
|
|
2398
3199
|
}
|
|
2399
3200
|
function checkEslintConfigs(workspaceRoot) {
|
|
2400
|
-
const rootConfigs = (0,
|
|
2401
|
-
const projectConfigs = (0,
|
|
3201
|
+
const rootConfigs = (0, import_node_fs8.globSync)("eslint.config.{js,mjs,cjs,ts,mts,cts}", { cwd: workspaceRoot });
|
|
3202
|
+
const projectConfigs = (0, import_node_fs8.globSync)("{apps,libs,packages}/*/eslint.config.{js,mjs,cjs,ts,mts,cts}", {
|
|
2402
3203
|
cwd: workspaceRoot
|
|
2403
|
-
}).map((config) =>
|
|
3204
|
+
}).map((config) => toPosix2(config));
|
|
2404
3205
|
return [
|
|
2405
3206
|
{
|
|
2406
3207
|
check: "root ESLint config",
|
|
@@ -2419,7 +3220,7 @@ function checkEslintConfigs(workspaceRoot) {
|
|
|
2419
3220
|
function declaredDevDependencies(workspaceRoot) {
|
|
2420
3221
|
try {
|
|
2421
3222
|
const manifest = readJson(
|
|
2422
|
-
(0,
|
|
3223
|
+
(0, import_node_path14.join)(workspaceRoot, "package.json")
|
|
2423
3224
|
);
|
|
2424
3225
|
return manifest.devDependencies ?? {};
|
|
2425
3226
|
} catch {
|
|
@@ -2427,7 +3228,7 @@ function declaredDevDependencies(workspaceRoot) {
|
|
|
2427
3228
|
}
|
|
2428
3229
|
}
|
|
2429
3230
|
function checkNoRetiredFormatter(workspaceRoot) {
|
|
2430
|
-
const files = RETIRED_FORMATTER_FILES.filter((file) => fileExists((0,
|
|
3231
|
+
const files = RETIRED_FORMATTER_FILES.filter((file) => fileExists((0, import_node_path14.join)(workspaceRoot, file)));
|
|
2431
3232
|
const declared = ["prettier", "eslint-config-prettier", "oxlint", "oxfmt", "@mnci/oxlint-config"].filter((name) => declaredDevDependencies(workspaceRoot)[name] !== void 0);
|
|
2432
3233
|
const stale = [...files, ...declared];
|
|
2433
3234
|
if (stale.length === 0) {
|
|
@@ -2453,7 +3254,7 @@ function checkEslintPlugin(nxJson) {
|
|
|
2453
3254
|
};
|
|
2454
3255
|
}
|
|
2455
3256
|
function checkResolvedEslint(workspaceRoot) {
|
|
2456
|
-
const manifestPath = (0,
|
|
3257
|
+
const manifestPath = (0, import_node_path14.join)(workspaceRoot, "node_modules/eslint/package.json");
|
|
2457
3258
|
if (!fileExists(manifestPath)) {
|
|
2458
3259
|
return void 0;
|
|
2459
3260
|
}
|
|
@@ -2471,11 +3272,11 @@ function checkResolvedEslint(workspaceRoot) {
|
|
|
2471
3272
|
}
|
|
2472
3273
|
}
|
|
2473
3274
|
function checkNpmrc(workspaceRoot, registry, scope) {
|
|
2474
|
-
const npmrcPath = (0,
|
|
3275
|
+
const npmrcPath = (0, import_node_path14.join)(workspaceRoot, ".npmrc");
|
|
2475
3276
|
if (!registry || !fileExists(npmrcPath)) {
|
|
2476
3277
|
return void 0;
|
|
2477
3278
|
}
|
|
2478
|
-
const npmrc = (0,
|
|
3279
|
+
const npmrc = (0, import_node_fs8.readFileSync)(npmrcPath, "utf8");
|
|
2479
3280
|
if (registry.kind === "npm") {
|
|
2480
3281
|
return {
|
|
2481
3282
|
check: ".npmrc authenticates the public registry",
|
|
@@ -2493,12 +3294,12 @@ function checkNpmrc(workspaceRoot, registry, scope) {
|
|
|
2493
3294
|
}
|
|
2494
3295
|
function checkVersionActions(workspaceRoot) {
|
|
2495
3296
|
const candidates = [
|
|
2496
|
-
...(0,
|
|
2497
|
-
...(0,
|
|
2498
|
-
].map((manifest) =>
|
|
3297
|
+
...(0, import_node_fs8.globSync)("packages/*/pubspec.yaml", { cwd: workspaceRoot }),
|
|
3298
|
+
...(0, import_node_fs8.globSync)("python-packages/*/pyproject.toml", { cwd: workspaceRoot })
|
|
3299
|
+
].map((manifest) => toPosix2(manifest));
|
|
2499
3300
|
return candidates.flatMap((manifest) => {
|
|
2500
3301
|
const projectRoot = manifest.slice(0, manifest.lastIndexOf("/"));
|
|
2501
|
-
const projectJsonPath = (0,
|
|
3302
|
+
const projectJsonPath = (0, import_node_path14.join)(workspaceRoot, projectRoot, "project.json");
|
|
2502
3303
|
let hasOverride = false;
|
|
2503
3304
|
try {
|
|
2504
3305
|
const projectJson = readJson(projectJsonPath);
|
|
@@ -2527,7 +3328,7 @@ function declaredTargets(workspaceRoot, projectRoot) {
|
|
|
2527
3328
|
]) {
|
|
2528
3329
|
try {
|
|
2529
3330
|
const targets = extract(
|
|
2530
|
-
readJson((0,
|
|
3331
|
+
readJson((0, import_node_path14.join)(workspaceRoot, projectRoot, file))
|
|
2531
3332
|
);
|
|
2532
3333
|
if (targets && typeof targets === "object") {
|
|
2533
3334
|
sources.push({
|
|
@@ -2543,9 +3344,9 @@ function declaredTargets(workspaceRoot, projectRoot) {
|
|
|
2543
3344
|
function checkTargetFilesExist(workspaceRoot) {
|
|
2544
3345
|
const projectRoots = new Set(
|
|
2545
3346
|
[
|
|
2546
|
-
...(0,
|
|
2547
|
-
...(0,
|
|
2548
|
-
].map((manifest) =>
|
|
3347
|
+
...(0, import_node_fs8.globSync)("{apps,libs,packages}/*/project.json", { cwd: workspaceRoot }),
|
|
3348
|
+
...(0, import_node_fs8.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot })
|
|
3349
|
+
].map((manifest) => toPosix2(manifest).split("/").slice(0, 2).join("/"))
|
|
2549
3350
|
);
|
|
2550
3351
|
return [...projectRoots].flatMap(
|
|
2551
3352
|
(projectRoot) => declaredTargets(workspaceRoot, projectRoot).flatMap(
|
|
@@ -2559,7 +3360,7 @@ function checkOneTargetsFiles(workspaceRoot, source, targetName, target) {
|
|
|
2559
3360
|
return TARGET_FILE_OPTIONS.flatMap((option) => {
|
|
2560
3361
|
const value = target?.options?.[option];
|
|
2561
3362
|
const checkable = typeof value === "string" && !value.includes("{");
|
|
2562
|
-
if (!checkable || fileExists((0,
|
|
3363
|
+
if (!checkable || fileExists((0, import_node_path14.join)(workspaceRoot, value))) {
|
|
2563
3364
|
return [];
|
|
2564
3365
|
}
|
|
2565
3366
|
return [
|
|
@@ -2580,8 +3381,49 @@ function checkSync(workspaceRoot) {
|
|
|
2580
3381
|
remedy: "run `npx nx sync` and commit the result"
|
|
2581
3382
|
};
|
|
2582
3383
|
}
|
|
3384
|
+
function checkNoRootRuntimeDependencies(workspaceRoot) {
|
|
3385
|
+
const manifestPath = (0, import_node_path14.join)(workspaceRoot, "package.json");
|
|
3386
|
+
if (!fileExists(manifestPath)) {
|
|
3387
|
+
return void 0;
|
|
3388
|
+
}
|
|
3389
|
+
let declared;
|
|
3390
|
+
try {
|
|
3391
|
+
declared = Object.keys(
|
|
3392
|
+
readJson(manifestPath).dependencies ?? {}
|
|
3393
|
+
);
|
|
3394
|
+
} catch {
|
|
3395
|
+
return void 0;
|
|
3396
|
+
}
|
|
3397
|
+
return {
|
|
3398
|
+
check: "no runtime dependencies in the root manifest",
|
|
3399
|
+
ok: declared.length === 0,
|
|
3400
|
+
detail: `the root package.json declares ${declared.join(", ")} \u2014 the root is private and never published, and @nx/rollup externalises only what a project's own manifest declares, so a package importing one of these ships an inlined private copy instead`,
|
|
3401
|
+
remedy: `move ${declared.join(", ")} into the dependencies of each package that imports it (devDependencies at the root are fine \u2014 that is what the root is for)`
|
|
3402
|
+
};
|
|
3403
|
+
}
|
|
3404
|
+
function checkRollupSourceMaps(workspaceRoot) {
|
|
3405
|
+
const configs = (0, import_node_fs8.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
|
|
3406
|
+
cwd: workspaceRoot
|
|
3407
|
+
}).map((config) => toPosix2(config));
|
|
3408
|
+
return configs.flatMap((relativePath) => {
|
|
3409
|
+
let config;
|
|
3410
|
+
try {
|
|
3411
|
+
config = (0, import_node_fs8.readFileSync)((0, import_node_path14.join)(workspaceRoot, relativePath), "utf8");
|
|
3412
|
+
} catch {
|
|
3413
|
+
return [];
|
|
3414
|
+
}
|
|
3415
|
+
return [
|
|
3416
|
+
{
|
|
3417
|
+
check: `source maps enabled in ${relativePath}`,
|
|
3418
|
+
ok: hasRollupSourceMaps(config),
|
|
3419
|
+
detail: "rollup emits no .js.map without it, so a breakpoint in a .ts file can never bind",
|
|
3420
|
+
remedy: "run `mnci upgrade`, which adds it to every rollup config"
|
|
3421
|
+
}
|
|
3422
|
+
];
|
|
3423
|
+
});
|
|
3424
|
+
}
|
|
2583
3425
|
function collectFindings(workspaceRoot) {
|
|
2584
|
-
const nxJsonPath = (0,
|
|
3426
|
+
const nxJsonPath = (0, import_node_path14.join)(workspaceRoot, "nx.json");
|
|
2585
3427
|
if (!fileExists(nxJsonPath)) {
|
|
2586
3428
|
throw new Error(
|
|
2587
3429
|
`No nx.json found in ${workspaceRoot} \u2014 run 'mnci doctor' from the workspace root.`
|
|
@@ -2594,6 +3436,8 @@ function collectFindings(workspaceRoot) {
|
|
|
2594
3436
|
checkEslintPlugin(nxJson),
|
|
2595
3437
|
checkResolvedEslint(workspaceRoot),
|
|
2596
3438
|
checkNpmrc(workspaceRoot, nxJson.mnci?.registry, nxJson.mnci?.scope),
|
|
3439
|
+
checkNoRootRuntimeDependencies(workspaceRoot),
|
|
3440
|
+
...checkRollupSourceMaps(workspaceRoot),
|
|
2597
3441
|
...checkVersionActions(workspaceRoot),
|
|
2598
3442
|
...checkTargetFilesExist(workspaceRoot),
|
|
2599
3443
|
checkSync(workspaceRoot)
|
|
@@ -2621,12 +3465,12 @@ function runDoctor(workspaceRoot) {
|
|
|
2621
3465
|
}
|
|
2622
3466
|
|
|
2623
3467
|
// src/commands/interactive.ts
|
|
2624
|
-
var
|
|
3468
|
+
var import_node_path17 = require("path");
|
|
2625
3469
|
var import_prompts8 = require("@inquirer/prompts");
|
|
2626
3470
|
|
|
2627
3471
|
// src/commands/new.ts
|
|
2628
|
-
var
|
|
2629
|
-
var
|
|
3472
|
+
var import_node_fs9 = require("fs");
|
|
3473
|
+
var import_node_path15 = require("path");
|
|
2630
3474
|
function nxCloudProviderValue(ci) {
|
|
2631
3475
|
return ci === "azure" ? "azure" : "github";
|
|
2632
3476
|
}
|
|
@@ -2687,7 +3531,7 @@ async function runNew(name, options) {
|
|
|
2687
3531
|
],
|
|
2688
3532
|
process.cwd()
|
|
2689
3533
|
);
|
|
2690
|
-
const workspaceRoot = (0,
|
|
3534
|
+
const workspaceRoot = (0, import_node_path15.join)(process.cwd(), workspaceName);
|
|
2691
3535
|
logger.step(
|
|
2692
3536
|
"Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
|
|
2693
3537
|
);
|
|
@@ -2696,8 +3540,8 @@ async function runNew(name, options) {
|
|
|
2696
3540
|
{ workspaceName, scope, registry, agent, variableGroup, ci, stack },
|
|
2697
3541
|
logger.detail
|
|
2698
3542
|
);
|
|
2699
|
-
(0,
|
|
2700
|
-
(0,
|
|
3543
|
+
(0, import_node_fs9.rmSync)((0, import_node_path15.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
|
|
3544
|
+
(0, import_node_fs9.rmSync)((0, import_node_path15.join)(workspaceRoot, "package-lock.json"), { force: true });
|
|
2701
3545
|
logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
|
|
2702
3546
|
const installStatus = runShell(
|
|
2703
3547
|
"npm",
|
|
@@ -2727,8 +3571,8 @@ async function runNew(name, options) {
|
|
|
2727
3571
|
}
|
|
2728
3572
|
|
|
2729
3573
|
// src/commands/upgrade.ts
|
|
2730
|
-
var
|
|
2731
|
-
var
|
|
3574
|
+
var import_node_fs10 = require("fs");
|
|
3575
|
+
var import_node_path16 = require("path");
|
|
2732
3576
|
function resolveRegistry2(options, persisted) {
|
|
2733
3577
|
if (options.registry === "azure-artifacts" || options.organization && options.artifactsFeed) {
|
|
2734
3578
|
const persistedAzure = persisted?.kind === "azure-artifacts" ? persisted : void 0;
|
|
@@ -2757,7 +3601,7 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
|
|
|
2757
3601
|
return persisted.workspaceName;
|
|
2758
3602
|
}
|
|
2759
3603
|
try {
|
|
2760
|
-
const existing = (0,
|
|
3604
|
+
const existing = (0, import_node_fs10.readdirSync)(workspaceRoot).find(
|
|
2761
3605
|
(file) => file.endsWith(".code-workspace") && file !== "undefined.code-workspace"
|
|
2762
3606
|
);
|
|
2763
3607
|
if (existing) {
|
|
@@ -2765,7 +3609,7 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
|
|
|
2765
3609
|
}
|
|
2766
3610
|
} catch {
|
|
2767
3611
|
}
|
|
2768
|
-
return (0,
|
|
3612
|
+
return (0, import_node_path16.basename)(workspaceRoot);
|
|
2769
3613
|
}
|
|
2770
3614
|
function resolveOverlayOptions(workspaceRoot, options, persisted) {
|
|
2771
3615
|
const scope = options.scope ?? persisted.scope;
|
|
@@ -2803,7 +3647,7 @@ function resolveOverlayOptions(workspaceRoot, options, persisted) {
|
|
|
2803
3647
|
};
|
|
2804
3648
|
}
|
|
2805
3649
|
function runUpgrade(workspaceRoot, options) {
|
|
2806
|
-
if (!fileExists((0,
|
|
3650
|
+
if (!fileExists((0, import_node_path16.join)(workspaceRoot, "nx.json"))) {
|
|
2807
3651
|
throw new Error(
|
|
2808
3652
|
`No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
|
|
2809
3653
|
);
|
|
@@ -2812,6 +3656,13 @@ function runUpgrade(workspaceRoot, options) {
|
|
|
2812
3656
|
const resolved = resolveOverlayOptions(workspaceRoot, options, persisted);
|
|
2813
3657
|
logger.step("Re-applying the MoNecromanCI overlay");
|
|
2814
3658
|
applyOverlay(workspaceRoot, resolved, logger.detail);
|
|
3659
|
+
const repaired = repairRollupSourceMaps(workspaceRoot);
|
|
3660
|
+
if (repaired.length > 0) {
|
|
3661
|
+
logger.step("Enabling source maps in rollup configs (breakpoints in .ts files)");
|
|
3662
|
+
for (const path of repaired) {
|
|
3663
|
+
logger.detail(`updated ${path}`);
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
2815
3666
|
logger.step(
|
|
2816
3667
|
"Formatting the workspace (eslint --fix) \u2014 the slowest step, minutes on a large workspace"
|
|
2817
3668
|
);
|
|
@@ -2821,7 +3672,7 @@ function runUpgrade(workspaceRoot, options) {
|
|
|
2821
3672
|
|
|
2822
3673
|
// src/commands/interactive.ts
|
|
2823
3674
|
async function runInteractive() {
|
|
2824
|
-
const inWorkspace = fileExists((0,
|
|
3675
|
+
const inWorkspace = fileExists((0, import_node_path17.join)(process.cwd(), "nx.json"));
|
|
2825
3676
|
const choiceNew = { name: "Create a new monorepo", value: "new" };
|
|
2826
3677
|
const choiceAdd = { name: "Add a project to this workspace", value: "add" };
|
|
2827
3678
|
const choiceUpgrade = {
|
|
@@ -2841,27 +3692,345 @@ async function runInteractive() {
|
|
|
2841
3692
|
}
|
|
2842
3693
|
}
|
|
2843
3694
|
|
|
3695
|
+
// src/commands/up.ts
|
|
3696
|
+
var import_node_path18 = require("path");
|
|
3697
|
+
var import_prompts9 = require("@inquirer/prompts");
|
|
3698
|
+
|
|
3699
|
+
// src/deps/registry.ts
|
|
3700
|
+
var LOOKUP_CONCURRENCY = 8;
|
|
3701
|
+
async function latestNpmVersions(names, cwd) {
|
|
3702
|
+
const results = await pool(names, LOOKUP_CONCURRENCY, async (name) => {
|
|
3703
|
+
const result = await runCaptureAsync("npm", ["view", name, "version", "--json"], cwd);
|
|
3704
|
+
if (result.status !== 0) {
|
|
3705
|
+
return;
|
|
3706
|
+
}
|
|
3707
|
+
try {
|
|
3708
|
+
const parsed = JSON.parse(result.stdout);
|
|
3709
|
+
const version = Array.isArray(parsed) ? parsed.at(-1) : parsed;
|
|
3710
|
+
return typeof version === "string" ? [name, version] : void 0;
|
|
3711
|
+
} catch {
|
|
3712
|
+
return;
|
|
3713
|
+
}
|
|
3714
|
+
});
|
|
3715
|
+
return new Map(results.filter((entry) => entry !== void 0));
|
|
3716
|
+
}
|
|
3717
|
+
async function latestPipVersions(names, cwd) {
|
|
3718
|
+
const python = process.platform === "win32" ? "python" : "python3";
|
|
3719
|
+
const results = await pool(names, LOOKUP_CONCURRENCY, async (name) => {
|
|
3720
|
+
const result = await runCaptureAsync(python, ["-m", "pip", "index", "versions", name], cwd);
|
|
3721
|
+
if (result.status !== 0) {
|
|
3722
|
+
return;
|
|
3723
|
+
}
|
|
3724
|
+
const match = /\(([^)]+)\)/.exec(result.stdout);
|
|
3725
|
+
return match ? [name, match[1]] : void 0;
|
|
3726
|
+
});
|
|
3727
|
+
return new Map(results.filter((entry) => entry !== void 0));
|
|
3728
|
+
}
|
|
3729
|
+
async function latestGoVersions(cwd) {
|
|
3730
|
+
const result = await runCaptureAsync("go", ["list", "-m", "-u", "-json", "all"], cwd);
|
|
3731
|
+
if (result.status !== 0) {
|
|
3732
|
+
return /* @__PURE__ */ new Map();
|
|
3733
|
+
}
|
|
3734
|
+
const latest = /* @__PURE__ */ new Map();
|
|
3735
|
+
for (const chunk of result.stdout.split(/^\}\s*$/m)) {
|
|
3736
|
+
const text = `${chunk.trim()}${chunk.trim() ? "}" : ""}`;
|
|
3737
|
+
if (!text.startsWith("{")) {
|
|
3738
|
+
continue;
|
|
3739
|
+
}
|
|
3740
|
+
try {
|
|
3741
|
+
const module_ = JSON.parse(text);
|
|
3742
|
+
if (module_.Path && module_.Update?.Version) {
|
|
3743
|
+
latest.set(module_.Path, module_.Update.Version);
|
|
3744
|
+
}
|
|
3745
|
+
} catch {
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3748
|
+
return latest;
|
|
3749
|
+
}
|
|
3750
|
+
async function latestPubVersions(cwd) {
|
|
3751
|
+
const result = await runCaptureAsync("flutter", ["pub", "outdated", "--json"], cwd);
|
|
3752
|
+
if (result.status !== 0) {
|
|
3753
|
+
return /* @__PURE__ */ new Map();
|
|
3754
|
+
}
|
|
3755
|
+
try {
|
|
3756
|
+
const report = JSON.parse(result.stdout);
|
|
3757
|
+
const latest = /* @__PURE__ */ new Map();
|
|
3758
|
+
const packages = report.packages ?? [];
|
|
3759
|
+
for (const entry of packages) {
|
|
3760
|
+
if (entry.package && entry.latest?.version) {
|
|
3761
|
+
latest.set(entry.package, entry.latest.version);
|
|
3762
|
+
}
|
|
3763
|
+
}
|
|
3764
|
+
return latest;
|
|
3765
|
+
} catch {
|
|
3766
|
+
return /* @__PURE__ */ new Map();
|
|
3767
|
+
}
|
|
3768
|
+
}
|
|
3769
|
+
async function latestVersions(ecosystem, names, cwd) {
|
|
3770
|
+
switch (ecosystem) {
|
|
3771
|
+
case "npm": {
|
|
3772
|
+
return await latestNpmVersions(names, cwd);
|
|
3773
|
+
}
|
|
3774
|
+
case "pip": {
|
|
3775
|
+
return await latestPipVersions(names, cwd);
|
|
3776
|
+
}
|
|
3777
|
+
case "go": {
|
|
3778
|
+
return await latestGoVersions(cwd);
|
|
3779
|
+
}
|
|
3780
|
+
default: {
|
|
3781
|
+
return await latestPubVersions(cwd);
|
|
3782
|
+
}
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3785
|
+
|
|
3786
|
+
// src/commands/up.ts
|
|
3787
|
+
function currentVersion(workspaceRoot, sites) {
|
|
3788
|
+
const installed = resolvedVersion(workspaceRoot, sites[0].ecosystem, sites[0].name);
|
|
3789
|
+
if (installed) {
|
|
3790
|
+
return installed;
|
|
3791
|
+
}
|
|
3792
|
+
if (sites[0].ecosystem === "go") {
|
|
3793
|
+
return bare(sites[0].spec);
|
|
3794
|
+
}
|
|
3795
|
+
const highest = highestSpec(sites.map((site) => site.spec));
|
|
3796
|
+
return highest ? specVersion(highest) : void 0;
|
|
3797
|
+
}
|
|
3798
|
+
async function collectOutdated(workspaceRoot, ecosystems) {
|
|
3799
|
+
const present = ecosystems.filter((ecosystem) => hasEcosystem(workspaceRoot, ecosystem));
|
|
3800
|
+
const inventory = collectInventory(workspaceRoot, present);
|
|
3801
|
+
const outdated = [];
|
|
3802
|
+
for (const ecosystem of present) {
|
|
3803
|
+
const forEcosystem = [...inventory].filter(
|
|
3804
|
+
([name, sites]) => offerable(workspaceRoot, ecosystem, name, sites)
|
|
3805
|
+
);
|
|
3806
|
+
const latest = await latestVersions(
|
|
3807
|
+
ecosystem,
|
|
3808
|
+
forEcosystem.map(([name]) => name),
|
|
3809
|
+
workspaceRoot
|
|
3810
|
+
);
|
|
3811
|
+
outdated.push(...upgradable(workspaceRoot, ecosystem, forEcosystem, latest));
|
|
3812
|
+
}
|
|
3813
|
+
return outdated.toSorted(
|
|
3814
|
+
(left, right) => UPDATE_KINDS.indexOf(left.kind) - UPDATE_KINDS.indexOf(right.kind) || left.name.localeCompare(right.name)
|
|
3815
|
+
);
|
|
3816
|
+
}
|
|
3817
|
+
function offerable(workspaceRoot, ecosystem, name, sites) {
|
|
3818
|
+
if (sites[0].ecosystem !== ecosystem || sites[0].section === "indirect") {
|
|
3819
|
+
return false;
|
|
3820
|
+
}
|
|
3821
|
+
if (sites.every((site) => site.section === PEER_SECTION)) {
|
|
3822
|
+
return false;
|
|
3823
|
+
}
|
|
3824
|
+
if (ecosystem !== "npm") {
|
|
3825
|
+
return true;
|
|
3826
|
+
}
|
|
3827
|
+
return !isWorkspaceProject(workspaceRoot, name) && !isAliasedInstall(workspaceRoot, name);
|
|
3828
|
+
}
|
|
3829
|
+
function bare(version) {
|
|
3830
|
+
return version.replace(/^v/, "");
|
|
3831
|
+
}
|
|
3832
|
+
function upgradable(workspaceRoot, ecosystem, declared, latest) {
|
|
3833
|
+
const outdated = [];
|
|
3834
|
+
for (const [name, sites] of declared) {
|
|
3835
|
+
const newest = latest.get(name);
|
|
3836
|
+
const current = currentVersion(workspaceRoot, sites);
|
|
3837
|
+
if (!newest || !current || !isNewer(bare(newest), bare(current))) {
|
|
3838
|
+
continue;
|
|
3839
|
+
}
|
|
3840
|
+
outdated.push({
|
|
3841
|
+
name,
|
|
3842
|
+
ecosystem,
|
|
3843
|
+
current,
|
|
3844
|
+
latest: newest,
|
|
3845
|
+
kind: classify(bare(current), bare(newest)),
|
|
3846
|
+
sites
|
|
3847
|
+
});
|
|
3848
|
+
}
|
|
3849
|
+
return outdated;
|
|
3850
|
+
}
|
|
3851
|
+
function row(entry, widths) {
|
|
3852
|
+
const sections = [...new Set(entry.sites.map((site) => site.section))].join("/");
|
|
3853
|
+
const projects = [...new Set(entry.sites.map((site) => site.project))].join(", ");
|
|
3854
|
+
return [
|
|
3855
|
+
entry.name.padEnd(widths.name),
|
|
3856
|
+
sections.padEnd(widths.section),
|
|
3857
|
+
`${entry.current.padStart(widths.current)} \u203A ${entry.latest}`,
|
|
3858
|
+
projects
|
|
3859
|
+
].join(" ");
|
|
3860
|
+
}
|
|
3861
|
+
function columnWidths(entries) {
|
|
3862
|
+
return {
|
|
3863
|
+
name: widthOf(entries.map((entry) => entry.name)),
|
|
3864
|
+
section: widthOf(
|
|
3865
|
+
entries.map((entry) => [...new Set(entry.sites.map((site) => site.section))].join("/"))
|
|
3866
|
+
),
|
|
3867
|
+
current: widthOf(entries.map((entry) => entry.current))
|
|
3868
|
+
};
|
|
3869
|
+
}
|
|
3870
|
+
function widthOf(values) {
|
|
3871
|
+
return Math.max(0, ...values.map((value) => value.length));
|
|
3872
|
+
}
|
|
3873
|
+
function reportOutdated(outdated) {
|
|
3874
|
+
const widths = columnWidths(outdated);
|
|
3875
|
+
for (const kind of UPDATE_KINDS) {
|
|
3876
|
+
const section = outdated.filter((entry) => entry.kind === kind);
|
|
3877
|
+
if (section.length === 0) {
|
|
3878
|
+
continue;
|
|
3879
|
+
}
|
|
3880
|
+
const { title, blurb } = UPDATE_KIND_LABELS[kind];
|
|
3881
|
+
logger.info("");
|
|
3882
|
+
logger.info(`${title} ${blurb}`);
|
|
3883
|
+
for (const entry of section) {
|
|
3884
|
+
logger.info(` ${row(entry, widths)}`);
|
|
3885
|
+
}
|
|
3886
|
+
}
|
|
3887
|
+
}
|
|
3888
|
+
async function promptForUpdates(outdated) {
|
|
3889
|
+
const widths = columnWidths(outdated);
|
|
3890
|
+
const choices = [];
|
|
3891
|
+
for (const kind of UPDATE_KINDS) {
|
|
3892
|
+
const section = outdated.filter((entry) => entry.kind === kind);
|
|
3893
|
+
if (section.length === 0) {
|
|
3894
|
+
continue;
|
|
3895
|
+
}
|
|
3896
|
+
const { title, blurb } = UPDATE_KIND_LABELS[kind];
|
|
3897
|
+
choices.push(new import_prompts9.Separator(`
|
|
3898
|
+
${title} ${blurb}`));
|
|
3899
|
+
for (const entry of section) {
|
|
3900
|
+
choices.push({ name: row(entry, widths), value: entry });
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
return await (0, import_prompts9.checkbox)({
|
|
3904
|
+
message: "Choose which packages to update.",
|
|
3905
|
+
choices,
|
|
3906
|
+
pageSize: 20
|
|
3907
|
+
});
|
|
3908
|
+
}
|
|
3909
|
+
function applyUpdate(entry) {
|
|
3910
|
+
const changed = [];
|
|
3911
|
+
for (const site of entry.sites) {
|
|
3912
|
+
if (!site.rewritable) {
|
|
3913
|
+
continue;
|
|
3914
|
+
}
|
|
3915
|
+
const spec = `${rangeOperator(site.spec)}${entry.latest}`;
|
|
3916
|
+
if (rewriteSpec(site, spec)) {
|
|
3917
|
+
changed.push(site.manifestPath);
|
|
3918
|
+
}
|
|
3919
|
+
}
|
|
3920
|
+
return changed;
|
|
3921
|
+
}
|
|
3922
|
+
function reinstall(workspaceRoot, ecosystems) {
|
|
3923
|
+
const commands = [
|
|
3924
|
+
["npm", "npm", ["install"]],
|
|
3925
|
+
["pip", "npm", ["run", "python:install"]],
|
|
3926
|
+
["pub", "flutter", ["pub", "get"]],
|
|
3927
|
+
["go", "go", ["mod", "tidy"]]
|
|
3928
|
+
];
|
|
3929
|
+
for (const [ecosystem, command, arguments_] of commands) {
|
|
3930
|
+
if (!ecosystems.has(ecosystem)) {
|
|
3931
|
+
continue;
|
|
3932
|
+
}
|
|
3933
|
+
if (ecosystem === "pip" && !hasPythonInstallScript(workspaceRoot)) {
|
|
3934
|
+
logger.warn(
|
|
3935
|
+
"This workspace has no 'python:install' script \u2014 run 'mnci upgrade' to add it, then reinstall by hand."
|
|
3936
|
+
);
|
|
3937
|
+
continue;
|
|
3938
|
+
}
|
|
3939
|
+
logger.step(`Reinstalling ${ecosystem} dependencies (${command} ${arguments_.join(" ")})`);
|
|
3940
|
+
if (runShell(command, arguments_, workspaceRoot) !== 0) {
|
|
3941
|
+
logger.warn(
|
|
3942
|
+
`${command} ${arguments_.join(" ")} failed \u2014 the manifests were updated, so re-run it once the cause is fixed.`
|
|
3943
|
+
);
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
}
|
|
3947
|
+
function hasPythonInstallScript(workspaceRoot) {
|
|
3948
|
+
try {
|
|
3949
|
+
const manifest = readJson(
|
|
3950
|
+
(0, import_node_path18.join)(workspaceRoot, "package.json")
|
|
3951
|
+
);
|
|
3952
|
+
return manifest.scripts?.["python:install"] !== void 0;
|
|
3953
|
+
} catch {
|
|
3954
|
+
return false;
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3957
|
+
function applyGoUpdates(workspaceRoot, entries) {
|
|
3958
|
+
for (const entry of entries) {
|
|
3959
|
+
logger.step(`go get ${entry.name}@${entry.latest}`);
|
|
3960
|
+
if (runShell("go", ["get", `${entry.name}@${entry.latest}`], workspaceRoot) !== 0) {
|
|
3961
|
+
logger.warn(`go get ${entry.name}@${entry.latest} failed \u2014 left at ${entry.current}.`);
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
}
|
|
3965
|
+
async function runUp(workspaceRoot, options) {
|
|
3966
|
+
if (!fileExists((0, import_node_path18.join)(workspaceRoot, "nx.json"))) {
|
|
3967
|
+
throw new Error(`No nx.json found in ${workspaceRoot} \u2014 run 'mnci up' from the workspace root.`);
|
|
3968
|
+
}
|
|
3969
|
+
const ecosystems = resolveEcosystems(options.ecosystem);
|
|
3970
|
+
for (const ecosystem of ecosystems) {
|
|
3971
|
+
if (!hasEcosystem(workspaceRoot, ecosystem)) {
|
|
3972
|
+
logger.info(`\u2298 SKIPPED ${ecosystem} \u2014 no ${ecosystem} project in this workspace.`);
|
|
3973
|
+
}
|
|
3974
|
+
}
|
|
3975
|
+
logger.step("Querying registries for the latest published versions");
|
|
3976
|
+
const outdated = await collectOutdated(workspaceRoot, ecosystems);
|
|
3977
|
+
if (outdated.length === 0) {
|
|
3978
|
+
logger.success("Every dependency is on its latest published version.");
|
|
3979
|
+
return;
|
|
3980
|
+
}
|
|
3981
|
+
const readOnly = options.check ?? !process.stdout.isTTY;
|
|
3982
|
+
if (readOnly) {
|
|
3983
|
+
reportOutdated(outdated);
|
|
3984
|
+
logger.info("");
|
|
3985
|
+
logger.info(`${outdated.length} package(s) have a newer release. Run 'mnci up' to update them.`);
|
|
3986
|
+
return;
|
|
3987
|
+
}
|
|
3988
|
+
const selected = options.yes ? [...outdated] : await promptForUpdates(outdated);
|
|
3989
|
+
if (selected.length === 0) {
|
|
3990
|
+
logger.info("Nothing selected; no manifest was changed.");
|
|
3991
|
+
return;
|
|
3992
|
+
}
|
|
3993
|
+
const goSelections = selected.filter((entry) => entry.ecosystem === "go");
|
|
3994
|
+
const changed = selected.filter((entry) => entry.ecosystem !== "go").flatMap((entry) => applyUpdate(entry));
|
|
3995
|
+
const changedManifests = new Set(changed);
|
|
3996
|
+
for (const path of changedManifests) {
|
|
3997
|
+
logger.detail(`updated ${path}`);
|
|
3998
|
+
}
|
|
3999
|
+
applyGoUpdates(workspaceRoot, goSelections);
|
|
4000
|
+
if (options.install !== false) {
|
|
4001
|
+
reinstall(workspaceRoot, new Set(selected.map((entry) => entry.ecosystem)));
|
|
4002
|
+
}
|
|
4003
|
+
if (changed.length > 0) {
|
|
4004
|
+
syncProjectReferences(workspaceRoot);
|
|
4005
|
+
logger.step("Formatting the changed manifests (eslint --fix)");
|
|
4006
|
+
runFormatter(workspaceRoot);
|
|
4007
|
+
}
|
|
4008
|
+
logger.success(
|
|
4009
|
+
`Updated ${selected.length} package(s). Review the changes with \`git diff\` before committing.`
|
|
4010
|
+
);
|
|
4011
|
+
}
|
|
4012
|
+
|
|
2844
4013
|
// src/util/versionChecker.ts
|
|
2845
|
-
var
|
|
2846
|
-
var
|
|
4014
|
+
var import_node_fs11 = require("fs");
|
|
4015
|
+
var import_node_path19 = require("path");
|
|
2847
4016
|
var import_cross_spawn2 = __toESM(require("cross-spawn"));
|
|
2848
4017
|
var PACKAGE_NAME = "@mnci/cli";
|
|
2849
4018
|
var REGISTRY_TIMEOUT_MS = 2e3;
|
|
2850
|
-
function checkForUpdate(
|
|
4019
|
+
function checkForUpdate(currentVersion2) {
|
|
2851
4020
|
try {
|
|
2852
4021
|
if (!process.stdout.isTTY) {
|
|
2853
4022
|
return;
|
|
2854
4023
|
}
|
|
2855
4024
|
setImmediate(() => {
|
|
2856
4025
|
try {
|
|
2857
|
-
reportIfOutdated(
|
|
4026
|
+
reportIfOutdated(currentVersion2);
|
|
2858
4027
|
} catch {
|
|
2859
4028
|
}
|
|
2860
4029
|
});
|
|
2861
4030
|
} catch {
|
|
2862
4031
|
}
|
|
2863
4032
|
}
|
|
2864
|
-
function reportIfOutdated(
|
|
4033
|
+
function reportIfOutdated(currentVersion2) {
|
|
2865
4034
|
const result = import_cross_spawn2.default.sync("npm", ["view", PACKAGE_NAME, "version"], {
|
|
2866
4035
|
encoding: "utf8",
|
|
2867
4036
|
timeout: REGISTRY_TIMEOUT_MS,
|
|
@@ -2871,12 +4040,12 @@ function reportIfOutdated(currentVersion) {
|
|
|
2871
4040
|
return;
|
|
2872
4041
|
}
|
|
2873
4042
|
const latestVersion = (result.stdout ?? "").trim();
|
|
2874
|
-
if (!latestVersion || !isNewerVersion(latestVersion,
|
|
4043
|
+
if (!latestVersion || !isNewerVersion(latestVersion, currentVersion2)) {
|
|
2875
4044
|
return;
|
|
2876
4045
|
}
|
|
2877
4046
|
logger.info("");
|
|
2878
4047
|
logger.info(`A new version of ${PACKAGE_NAME} is available: ${latestVersion}`);
|
|
2879
|
-
logger.info(` Current version: ${
|
|
4048
|
+
logger.info(` Current version: ${currentVersion2}`);
|
|
2880
4049
|
logger.info(` Update with: npm install -g ${PACKAGE_NAME}@latest`);
|
|
2881
4050
|
logger.info("");
|
|
2882
4051
|
}
|
|
@@ -2899,8 +4068,8 @@ function releaseTriple(version) {
|
|
|
2899
4068
|
}
|
|
2900
4069
|
function readCliVersion(packageDirectory) {
|
|
2901
4070
|
try {
|
|
2902
|
-
const manifestPath = (0,
|
|
2903
|
-
const manifest = JSON.parse((0,
|
|
4071
|
+
const manifestPath = (0, import_node_path19.join)(packageDirectory, "..", "package.json");
|
|
4072
|
+
const manifest = JSON.parse((0, import_node_fs11.readFileSync)(manifestPath, "utf8"));
|
|
2904
4073
|
return manifest.version ?? "0.0.0";
|
|
2905
4074
|
} catch {
|
|
2906
4075
|
return "0.0.0";
|
|
@@ -2930,6 +4099,16 @@ function buildProgram(cliVersion) {
|
|
|
2930
4099
|
).action(() => {
|
|
2931
4100
|
runDoctor(process.cwd());
|
|
2932
4101
|
});
|
|
4102
|
+
program.command("sync").description(
|
|
4103
|
+
"Make every project agree: converge external dependency ranges declared at more than one version, then run 'nx sync' for TypeScript project references. Go is a no-op \u2014 one root go.mod means one version of every module"
|
|
4104
|
+
).option("--check", "report drift and exit non-zero without writing anything").option("--ecosystem <name>", "restrict to one ecosystem: npm | pip | pub | go").action((options) => {
|
|
4105
|
+
runSync(process.cwd(), options);
|
|
4106
|
+
});
|
|
4107
|
+
program.command("up").description(
|
|
4108
|
+
"Show every dependency with a newer published release \u2014 grouped patch/minor/major/non-semver, with the projects declaring each one \u2014 and interactively update the ones you pick"
|
|
4109
|
+
).option("--check", "report only; never prompt and never write (the default when piped)").option("-y, --yes", "select every available update without prompting").option("--ecosystem <name>", "restrict to one ecosystem: npm | pip | pub | go").option("--no-install", "update the manifests but skip the reinstall step").action(async (options) => {
|
|
4110
|
+
await runUp(process.cwd(), options);
|
|
4111
|
+
});
|
|
2933
4112
|
program.command("add").addArgument(new import_commander.Argument("[kind]", "project kind").choices(PROJECT_KINDS)).argument("[name]", "project name").description("Add a project by delegating to the matching Nx plugin generator").option("--scope <scope>", "npm scope for a publishable lib (defaults to @<workspace name>)").option(
|
|
2934
4113
|
"--framework <framework>",
|
|
2935
4114
|
"node-app only: express | fastify | koa | nest | none (default: none)"
|