@mnci/cli 4.3.2 → 4.4.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 +56 -5
- package/dist/cli.js +1829 -1805
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -28,23 +28,23 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
));
|
|
29
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
30
|
|
|
31
|
-
// src/
|
|
32
|
-
var
|
|
33
|
-
__export(
|
|
31
|
+
// src/main.ts
|
|
32
|
+
var main_exports = {};
|
|
33
|
+
__export(main_exports, {
|
|
34
34
|
buildProgram: () => buildProgram,
|
|
35
35
|
main: () => main
|
|
36
36
|
});
|
|
37
|
-
module.exports = __toCommonJS(
|
|
37
|
+
module.exports = __toCommonJS(main_exports);
|
|
38
38
|
var import_commander = require("commander");
|
|
39
39
|
|
|
40
|
-
// src/
|
|
41
|
-
var
|
|
42
|
-
var
|
|
40
|
+
// src/project-scaffolding/add-project.use-case.ts
|
|
41
|
+
var import_node_path18 = require("path");
|
|
42
|
+
var import_prompts3 = require("@inquirer/prompts");
|
|
43
43
|
|
|
44
|
-
// src/nx.ts
|
|
44
|
+
// src/nx-workspace/nx.client.ts
|
|
45
45
|
var import_cross_spawn = __toESM(require("cross-spawn"));
|
|
46
46
|
|
|
47
|
-
// src/
|
|
47
|
+
// src/terminal/logger.client.ts
|
|
48
48
|
var logger = {
|
|
49
49
|
info(message) {
|
|
50
50
|
console.log(message);
|
|
@@ -73,7 +73,62 @@ var logger = {
|
|
|
73
73
|
}
|
|
74
74
|
};
|
|
75
75
|
|
|
76
|
-
// src/
|
|
76
|
+
// src/terminal/prompts.client.ts
|
|
77
|
+
var import_prompts = require("@inquirer/prompts");
|
|
78
|
+
async function promptText(message, fallback) {
|
|
79
|
+
const value = await (0, import_prompts.input)({
|
|
80
|
+
message,
|
|
81
|
+
default: fallback,
|
|
82
|
+
validate: (value2) => value2.trim().length > 0 || "A value is required"
|
|
83
|
+
});
|
|
84
|
+
return value.trim();
|
|
85
|
+
}
|
|
86
|
+
async function promptRegistry(fallbackOrganization) {
|
|
87
|
+
const kind = await (0, import_prompts.select)({
|
|
88
|
+
message: "Package registry for publishable libraries",
|
|
89
|
+
choices: [
|
|
90
|
+
{ name: "Azure Artifacts", value: "azure-artifacts" },
|
|
91
|
+
{ name: "Public npm", value: "npm" }
|
|
92
|
+
]
|
|
93
|
+
});
|
|
94
|
+
if (kind === "npm") {
|
|
95
|
+
return { kind };
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
kind,
|
|
99
|
+
organization: await promptText("Azure DevOps organization", fallbackOrganization),
|
|
100
|
+
project: await promptText("Azure DevOps project"),
|
|
101
|
+
artifactsFeed: await promptText("Artifacts feed name")
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
async function promptCi() {
|
|
105
|
+
return await (0, import_prompts.select)({
|
|
106
|
+
message: "CI provider",
|
|
107
|
+
choices: [
|
|
108
|
+
{ name: "Azure Pipelines", value: "azure" },
|
|
109
|
+
{ name: "GitHub Actions", value: "github" },
|
|
110
|
+
{ name: "Both", value: "both" }
|
|
111
|
+
]
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
async function promptNxCloud() {
|
|
115
|
+
return await (0, import_prompts.confirm)({
|
|
116
|
+
message: "Connect this workspace to Nx Cloud (remote caching + CI insights)?",
|
|
117
|
+
default: false
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
async function promptStack() {
|
|
121
|
+
const testRunner = await (0, import_prompts.select)({
|
|
122
|
+
message: "Unit-test runner",
|
|
123
|
+
choices: [
|
|
124
|
+
{ name: "Jest", value: "jest" },
|
|
125
|
+
{ name: "Vitest", value: "vitest" }
|
|
126
|
+
]
|
|
127
|
+
});
|
|
128
|
+
return { testRunner };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/nx-workspace/nx.client.ts
|
|
77
132
|
function runShell(command, arguments_, cwd) {
|
|
78
133
|
const result = import_cross_spawn.default.sync(command, arguments_, { stdio: "inherit", cwd });
|
|
79
134
|
return result.status ?? 1;
|
|
@@ -145,62 +200,7 @@ function runFormatter(cwd, target = ".") {
|
|
|
145
200
|
}
|
|
146
201
|
}
|
|
147
202
|
|
|
148
|
-
// src/
|
|
149
|
-
var import_prompts = require("@inquirer/prompts");
|
|
150
|
-
async function promptText(message, fallback) {
|
|
151
|
-
const value = await (0, import_prompts.input)({
|
|
152
|
-
message,
|
|
153
|
-
default: fallback,
|
|
154
|
-
validate: (value2) => value2.trim().length > 0 || "A value is required"
|
|
155
|
-
});
|
|
156
|
-
return value.trim();
|
|
157
|
-
}
|
|
158
|
-
async function promptRegistry(fallbackOrganization) {
|
|
159
|
-
const kind = await (0, import_prompts.select)({
|
|
160
|
-
message: "Package registry for publishable libraries",
|
|
161
|
-
choices: [
|
|
162
|
-
{ name: "Azure Artifacts", value: "azure-artifacts" },
|
|
163
|
-
{ name: "Public npm", value: "npm" }
|
|
164
|
-
]
|
|
165
|
-
});
|
|
166
|
-
if (kind === "npm") {
|
|
167
|
-
return { kind };
|
|
168
|
-
}
|
|
169
|
-
return {
|
|
170
|
-
kind,
|
|
171
|
-
organization: await promptText("Azure DevOps organization", fallbackOrganization),
|
|
172
|
-
project: await promptText("Azure DevOps project"),
|
|
173
|
-
artifactsFeed: await promptText("Artifacts feed name")
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
async function promptCi() {
|
|
177
|
-
return await (0, import_prompts.select)({
|
|
178
|
-
message: "CI provider",
|
|
179
|
-
choices: [
|
|
180
|
-
{ name: "Azure Pipelines", value: "azure" },
|
|
181
|
-
{ name: "GitHub Actions", value: "github" },
|
|
182
|
-
{ name: "Both", value: "both" }
|
|
183
|
-
]
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
async function promptNxCloud() {
|
|
187
|
-
return await (0, import_prompts.confirm)({
|
|
188
|
-
message: "Connect this workspace to Nx Cloud (remote caching + CI insights)?",
|
|
189
|
-
default: false
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
async function promptStack() {
|
|
193
|
-
const testRunner = await (0, import_prompts.select)({
|
|
194
|
-
message: "Unit-test runner",
|
|
195
|
-
choices: [
|
|
196
|
-
{ name: "Jest", value: "jest" },
|
|
197
|
-
{ name: "Vitest", value: "vitest" }
|
|
198
|
-
]
|
|
199
|
-
});
|
|
200
|
-
return { testRunner };
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// src/util/fsx.ts
|
|
203
|
+
// src/file-system/file-system.client.ts
|
|
204
204
|
var import_node_fs = require("fs");
|
|
205
205
|
var import_node_path = require("path");
|
|
206
206
|
function readJson(path) {
|
|
@@ -282,7 +282,7 @@ function fileExists(path) {
|
|
|
282
282
|
return (0, import_node_fs.existsSync)(path);
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
-
// src/
|
|
285
|
+
// src/project-name/project-name.validator.ts
|
|
286
286
|
function assertValidProjectName(name, label) {
|
|
287
287
|
if (!/^[a-z][a-z0-9-]*(?:\.[a-z0-9-]+)*$/.test(name)) {
|
|
288
288
|
throw new Error(
|
|
@@ -291,11 +291,11 @@ function assertValidProjectName(name, label) {
|
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
-
// src/
|
|
294
|
+
// src/project-scaffolding/csharp.use-case.ts
|
|
295
295
|
var import_node_fs4 = require("fs");
|
|
296
296
|
var import_node_path4 = require("path");
|
|
297
297
|
|
|
298
|
-
// src/overlay.ts
|
|
298
|
+
// src/workspace-overlay/overlay.use-case.ts
|
|
299
299
|
var import_node_fs2 = require("fs");
|
|
300
300
|
var import_node_path2 = require("path");
|
|
301
301
|
var DEFAULT_STACK = { testRunner: "jest" };
|
|
@@ -846,6 +846,12 @@ var GO_TOOL_PATH_AZURE = `node -e "${GO_TOOL_PATH_PRELUDE}console.log('##vso[tas
|
|
|
846
846
|
var GO_TOOL_PATH_GITHUB = `node -e "${GO_TOOL_PATH_PRELUDE}if(!process.env.GITHUB_PATH){console.log('Not running in GitHub Actions - skipping.');process.exit(0)}fs.appendFileSync(process.env.GITHUB_PATH,bin+${String.raw`'\n'`})"`;
|
|
847
847
|
var FLUTTER_SDK_VERSION = "3.44.8";
|
|
848
848
|
var NODE_VERSION = "24";
|
|
849
|
+
var ACTION_VERSIONS = {
|
|
850
|
+
"actions/checkout": "v7",
|
|
851
|
+
"actions/setup-node": "v7",
|
|
852
|
+
"actions/setup-dotnet": "v6",
|
|
853
|
+
"actions/upload-artifact": "v7"
|
|
854
|
+
};
|
|
849
855
|
var NPM_VERSION = "11";
|
|
850
856
|
var DOTNET_SDK_VERSION = "10.0.x";
|
|
851
857
|
var DOTNET_DETECT_AZURE = `node -e "const fs=require('node:fs');const has=[...fs.globSync('apps/*/*.csproj'),...fs.globSync('packages/*/*.csproj'),...fs.globSync('libs/*/*.csproj')].length>0;console.log('##vso[task.setvariable variable=hasDotnetProjects]'+has)"`;
|
|
@@ -1202,7 +1208,7 @@ jobs:
|
|
|
1202
1208
|
ci:
|
|
1203
1209
|
runs-on: ${agent}
|
|
1204
1210
|
steps:
|
|
1205
|
-
- uses: actions/checkout
|
|
1211
|
+
- uses: actions/checkout@${ACTION_VERSIONS["actions/checkout"]}
|
|
1206
1212
|
with:
|
|
1207
1213
|
fetch-depth: 0
|
|
1208
1214
|
|
|
@@ -1213,7 +1219,7 @@ jobs:
|
|
|
1213
1219
|
- run: git config user.name "github-actions[bot]" && git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
1214
1220
|
name: Set the git identity used for release tags
|
|
1215
1221
|
|
|
1216
|
-
- uses: actions/setup-node
|
|
1222
|
+
- uses: actions/setup-node@${ACTION_VERSIONS["actions/setup-node"]}
|
|
1217
1223
|
with:
|
|
1218
1224
|
node-version: ${NODE_VERSION}
|
|
1219
1225
|
# Caches ~/.npm keyed on package-lock.json, so \`npm ci\` restores from
|
|
@@ -1299,7 +1305,7 @@ jobs:
|
|
|
1299
1305
|
# directly rather than a hand-rolled 'node -e' guard \u2014 see
|
|
1300
1306
|
# DOTNET_DETECT_AZURE's remarks for the full reasoning. hashFiles()
|
|
1301
1307
|
# gates it inline, so a JS-only workspace pays nothing.
|
|
1302
|
-
- uses: actions/setup-dotnet
|
|
1308
|
+
- uses: actions/setup-dotnet@${ACTION_VERSIONS["actions/setup-dotnet"]}
|
|
1303
1309
|
if: \${{ hashFiles('apps/*/*.csproj', 'packages/*/*.csproj', 'libs/*/*.csproj') != '' }}
|
|
1304
1310
|
with:
|
|
1305
1311
|
dotnet-version: ${DOTNET_SDK_VERSION}
|
|
@@ -1331,7 +1337,7 @@ jobs:
|
|
|
1331
1337
|
name: Pack all apps (one zip per app -> dist/drop)
|
|
1332
1338
|
if: \${{ ${onMain} }}
|
|
1333
1339
|
|
|
1334
|
-
- uses: actions/upload-artifact
|
|
1340
|
+
- uses: actions/upload-artifact@${ACTION_VERSIONS["actions/upload-artifact"]}
|
|
1335
1341
|
if: \${{ ${onMain} }}
|
|
1336
1342
|
with:
|
|
1337
1343
|
name: drop
|
|
@@ -1596,7 +1602,7 @@ function applyOverlay(workspaceRoot, options, onProgress = () => {
|
|
|
1596
1602
|
}
|
|
1597
1603
|
}
|
|
1598
1604
|
|
|
1599
|
-
// src/
|
|
1605
|
+
// src/project-scaffolding/post-generation.use-case.ts
|
|
1600
1606
|
var import_node_fs3 = require("fs");
|
|
1601
1607
|
var import_node_path3 = require("path");
|
|
1602
1608
|
function hasPlugin(workspaceRoot, packageName) {
|
|
@@ -1636,477 +1642,180 @@ function markPublic(manifestPath) {
|
|
|
1636
1642
|
const manifest = readJson(manifestPath);
|
|
1637
1643
|
writeFileEnsured(manifestPath, toJson({ ...manifest, publishConfig: { access: "public" } }));
|
|
1638
1644
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
].join("\n");
|
|
1658
|
-
}
|
|
1659
|
-
var ROLLUP_ARG_ONE_BOUNDARY = [" },", " {", ""].join("\n");
|
|
1660
|
-
var ROLLUP_ARG_ONE_WITH_SOURCE_MAPS = [
|
|
1661
|
-
" // Added by MoNecromanCI: without this rollup emits no .js.map at all, so",
|
|
1662
|
-
" // a breakpoint in a .ts file can never bind. Not published - see `files`.",
|
|
1663
|
-
" sourceMap: true",
|
|
1664
|
-
" },",
|
|
1665
|
-
" {",
|
|
1666
|
-
""
|
|
1667
|
-
].join("\n");
|
|
1668
|
-
function hasRollupSourceMaps(config) {
|
|
1669
|
-
return /sourceMap\s*:\s*true\b/.test(config);
|
|
1670
|
-
}
|
|
1671
|
-
function canRepairRollupConfig(config) {
|
|
1672
|
-
return config.includes(ROLLUP_ARG_ONE_BOUNDARY);
|
|
1673
|
-
}
|
|
1674
|
-
function withRollupSourceMaps(config) {
|
|
1675
|
-
if (hasRollupSourceMaps(config) || !canRepairRollupConfig(config)) {
|
|
1676
|
-
return config;
|
|
1677
|
-
}
|
|
1678
|
-
const withCompiler = config.replace(
|
|
1679
|
-
GENERATED_COMPILER_PATTERN,
|
|
1680
|
-
(_match, indent) => sourceMapCapableCompiler(indent)
|
|
1681
|
-
);
|
|
1682
|
-
const withFlag = withCompiler.replace(
|
|
1683
|
-
ROLLUP_ARG_ONE_BOUNDARY,
|
|
1684
|
-
() => ROLLUP_ARG_ONE_WITH_SOURCE_MAPS
|
|
1685
|
-
);
|
|
1686
|
-
return withFlag.includes("sourcemapPathTransform") ? withFlag : withFlag.replace(
|
|
1687
|
-
ROLLUP_ARG_ONE_WITH_SOURCE_MAPS,
|
|
1688
|
-
() => `${ROLLUP_ARG_ONE_WITH_SOURCE_MAPS}${SOURCEMAP_PATH_TRANSFORM}`
|
|
1645
|
+
function writeProjectReadme(projectRoot, projectName, testRunner) {
|
|
1646
|
+
const runner = testRunner === "vitest" ? "[Vitest](https://vitest.dev)" : "[Jest](https://jestjs.io)";
|
|
1647
|
+
writeFileEnsured(
|
|
1648
|
+
(0, import_node_path3.join)(projectRoot, "README.md"),
|
|
1649
|
+
[
|
|
1650
|
+
`# ${projectName}`,
|
|
1651
|
+
"",
|
|
1652
|
+
"Generated by [MoNecromanCI](https://github.com/russoedu/MoNecromanCi).",
|
|
1653
|
+
"",
|
|
1654
|
+
"## Building",
|
|
1655
|
+
"",
|
|
1656
|
+
`Run \`nx build ${projectName}\` to build this project.`,
|
|
1657
|
+
"",
|
|
1658
|
+
"## Running unit tests",
|
|
1659
|
+
"",
|
|
1660
|
+
`Run \`nx test ${projectName}\` to execute the unit tests via ${runner}.`,
|
|
1661
|
+
""
|
|
1662
|
+
].join("\n")
|
|
1689
1663
|
);
|
|
1690
1664
|
}
|
|
1691
|
-
function
|
|
1692
|
-
const
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
changed.push(relativePath.replaceAll("\\", "/"));
|
|
1665
|
+
function removeStaleGitkeeps(workspaceRoot) {
|
|
1666
|
+
for (const scaffold of ["apps", "libs", "packages"]) {
|
|
1667
|
+
const directory = (0, import_node_path3.join)(workspaceRoot, scaffold);
|
|
1668
|
+
try {
|
|
1669
|
+
const holdsAProject = (0, import_node_fs3.readdirSync)(directory, { withFileTypes: true }).some(
|
|
1670
|
+
(entry) => entry.isDirectory()
|
|
1671
|
+
);
|
|
1672
|
+
if (holdsAProject) {
|
|
1673
|
+
(0, import_node_fs3.rmSync)((0, import_node_path3.join)(directory, ".gitkeep"), { force: true });
|
|
1674
|
+
}
|
|
1675
|
+
} catch {
|
|
1703
1676
|
}
|
|
1704
1677
|
}
|
|
1705
|
-
return changed;
|
|
1706
1678
|
}
|
|
1707
|
-
function
|
|
1708
|
-
|
|
1709
|
-
|
|
1679
|
+
function ensureAdmZip(workspaceRoot) {
|
|
1680
|
+
if (hasPlugin(workspaceRoot, "adm-zip")) {
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
logger.step("Installing the app packager (adm-zip)");
|
|
1684
|
+
if (runShell(
|
|
1685
|
+
"npm",
|
|
1686
|
+
["install", "--save-dev", "adm-zip", "--no-audit", "--no-fund"],
|
|
1687
|
+
workspaceRoot
|
|
1688
|
+
) !== 0) {
|
|
1689
|
+
throw new Error("npm install of adm-zip failed");
|
|
1690
|
+
}
|
|
1710
1691
|
}
|
|
1711
|
-
function
|
|
1712
|
-
|
|
1713
|
-
|
|
1692
|
+
function defaultScope(workspaceRoot) {
|
|
1693
|
+
const { name } = readJson((0, import_node_path3.join)(workspaceRoot, "package.json"));
|
|
1694
|
+
const base = (name.startsWith("@") ? name.slice(1) : name).split("/", 1)[0];
|
|
1695
|
+
return `@${base}`;
|
|
1696
|
+
}
|
|
1697
|
+
function addNxTargets(manifestPath, newTargets) {
|
|
1698
|
+
const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
|
|
1699
|
+
const nx = manifest.nx ?? {};
|
|
1700
|
+
const targets = nx.targets ?? {};
|
|
1701
|
+
writeFileEnsured(
|
|
1702
|
+
manifestPath,
|
|
1703
|
+
toJson({ ...manifest, nx: { ...nx, targets: { ...targets, ...newTargets } } })
|
|
1704
|
+
);
|
|
1705
|
+
}
|
|
1706
|
+
function addProjectJsonTargets(projectJsonPath, newTargets) {
|
|
1707
|
+
const project = fileExists(projectJsonPath) ? readJson(projectJsonPath) : {};
|
|
1708
|
+
const targets = project.targets ?? {};
|
|
1709
|
+
writeFileEnsured(projectJsonPath, toJson({ ...project, targets: { ...targets, ...newTargets } }));
|
|
1710
|
+
}
|
|
1711
|
+
var ESLINT_CONFIG_FILENAMES = [
|
|
1712
|
+
"eslint.config.js",
|
|
1713
|
+
"eslint.config.mjs",
|
|
1714
|
+
"eslint.config.cjs",
|
|
1715
|
+
"eslint.config.ts",
|
|
1716
|
+
"eslint.config.mts",
|
|
1717
|
+
"eslint.config.cts"
|
|
1718
|
+
];
|
|
1719
|
+
function removeGeneratedEslintConfig(workspaceRoot, projectRoot) {
|
|
1720
|
+
for (const filename of ESLINT_CONFIG_FILENAMES) {
|
|
1721
|
+
(0, import_node_fs3.rmSync)((0, import_node_path3.join)(workspaceRoot, projectRoot, filename), { force: true });
|
|
1714
1722
|
}
|
|
1715
|
-
|
|
1716
|
-
|
|
1723
|
+
(0, import_node_fs3.rmSync)((0, import_node_path3.join)(workspaceRoot, ".vscode"), { recursive: true, force: true });
|
|
1724
|
+
}
|
|
1725
|
+
function findCodeWorkspaceFile(workspaceRoot) {
|
|
1717
1726
|
try {
|
|
1718
|
-
|
|
1727
|
+
const entry = (0, import_node_fs3.readdirSync)(workspaceRoot).find((file) => file.endsWith(".code-workspace"));
|
|
1728
|
+
return entry ? (0, import_node_path3.join)(workspaceRoot, entry) : void 0;
|
|
1719
1729
|
} catch {
|
|
1720
|
-
return
|
|
1730
|
+
return void 0;
|
|
1721
1731
|
}
|
|
1722
|
-
const specifiers = Array.from(
|
|
1723
|
-
text.matchAll(/require\((['"])(\.[^'"]*)\1\)/g),
|
|
1724
|
-
(match) => match[2]
|
|
1725
|
-
);
|
|
1726
|
-
const requiredText = specifiers.map((specifier) => resolveRequireTarget((0, import_node_path3.dirname)(configPath), specifier)).filter((target) => target !== void 0).map((target) => resolveRollupConfigText(target, visited));
|
|
1727
|
-
return [text, ...requiredText].join("\n");
|
|
1728
|
-
}
|
|
1729
|
-
var SOURCEMAP_PATH_TRANSFORM = [
|
|
1730
|
-
" // Added by MoNecromanCI. rollup hands sourcemapPathTransform an OS-NATIVE",
|
|
1731
|
-
" // path with one parent segment too many, so `sources` resolve to nothing",
|
|
1732
|
-
" // and no breakpoint can bind. Separators are normalised too: a sources",
|
|
1733
|
-
" // entry is URL-style, so a backslash is wrong on every platform.",
|
|
1734
|
-
" output: {",
|
|
1735
|
-
" sourcemapPathTransform: relativeSourcePath =>",
|
|
1736
|
-
" relativeSourcePath",
|
|
1737
|
-
" .replaceAll(String.fromCodePoint(92), '/')",
|
|
1738
|
-
" .replace(/^([.][.][/])+/, '../')",
|
|
1739
|
-
" },",
|
|
1740
|
-
""
|
|
1741
|
-
].join("\n");
|
|
1742
|
-
var DECLARATION_SPECIFIER_PLUGIN_MARKER = "name: 'mnci-normalise-declaration-specifiers'";
|
|
1743
|
-
var DECLARATION_SPECIFIER_EXTENSION_MARKER = "bareRelativeSpecifier";
|
|
1744
|
-
var DECLARATION_SPECIFIER_DIRECTORY_MARKER = "resolveSpecifierSuffix";
|
|
1745
|
-
function hasDeclarationSpecifierPlugin(config) {
|
|
1746
|
-
return config.includes(DECLARATION_SPECIFIER_PLUGIN_MARKER);
|
|
1747
1732
|
}
|
|
1748
|
-
function
|
|
1749
|
-
|
|
1733
|
+
function projectTask(name, kind) {
|
|
1734
|
+
const script = `${name}:${kind}`;
|
|
1735
|
+
const base = { label: `${name}: ${kind}`, type: "npm", script, problemMatcher: [] };
|
|
1736
|
+
return kind === "start" ? { ...base, isBackground: true } : { ...base, group: kind };
|
|
1750
1737
|
}
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
" const stub = join(dir, 'index.d.ts')",
|
|
1759
|
-
" let source",
|
|
1760
|
-
" try {",
|
|
1761
|
-
" source = readFileSync(stub, 'utf8')",
|
|
1762
|
-
" } catch {",
|
|
1763
|
-
" return",
|
|
1764
|
-
" }",
|
|
1765
|
-
" // The stub carries a TWO-character escape (JSON.stringify escaped one",
|
|
1766
|
-
" // backslash), so this must not match a single one - that would turn",
|
|
1767
|
-
String.raw` // "./src\index" into "./src//index". Built from char codes so there is`,
|
|
1768
|
-
" // no escaping in this file to get wrong.",
|
|
1769
|
-
" const separator = String.fromCodePoint(92, 92)",
|
|
1770
|
-
" const normalised = source.replaceAll(separator, '/')",
|
|
1771
|
-
" if (normalised !== source) writeFileSync(stub, normalised)",
|
|
1772
|
-
"",
|
|
1773
|
-
String.raw` const ${DECLARATION_SPECIFIER_EXTENSION_MARKER} = /from(\s+)(['"])(\.[^'"]+?)\2/g`,
|
|
1774
|
-
String.raw` const hasExtension = /\.(?:mjs|cjs|jsx?|json)$/`,
|
|
1775
|
-
" let entries",
|
|
1776
|
-
" try {",
|
|
1777
|
-
" entries = readdirSync(dir, { recursive: true, withFileTypes: true })",
|
|
1778
|
-
" } catch {",
|
|
1779
|
-
" return",
|
|
1780
|
-
" }",
|
|
1781
|
-
" for (const entry of entries) {",
|
|
1782
|
-
" if (!entry.name.endsWith('.d.ts')) continue",
|
|
1783
|
-
" const from = entry.parentPath ?? entry.path",
|
|
1784
|
-
" const filePath = join(from, entry.name)",
|
|
1785
|
-
" let declaration",
|
|
1786
|
-
" try {",
|
|
1787
|
-
" declaration = readFileSync(filePath, 'utf8')",
|
|
1788
|
-
" } catch {",
|
|
1789
|
-
" continue",
|
|
1790
|
-
" }",
|
|
1791
|
-
" // A bare specifier may name a FILE or a DIRECTORY BARREL - resolved",
|
|
1792
|
-
" // against what rollup actually emitted next to this file, never",
|
|
1793
|
-
" // guessed. A directory needs /index.js, not .js (a file that was",
|
|
1794
|
-
" // never written); anything neither form matches is left alone rather",
|
|
1795
|
-
" // than rewritten to a specifier that cannot resolve.",
|
|
1796
|
-
` const ${DECLARATION_SPECIFIER_DIRECTORY_MARKER} = (specifier) => {`,
|
|
1797
|
-
" if (hasExtension.test(specifier)) return null",
|
|
1798
|
-
" if (existsSync(join(from, specifier + '.d.ts'))) return specifier + '.js'",
|
|
1799
|
-
" if (existsSync(join(from, specifier, 'index.d.ts'))) return specifier + '/index.js'",
|
|
1800
|
-
" return null",
|
|
1801
|
-
" }",
|
|
1802
|
-
" const withExtensions = declaration.replace(",
|
|
1803
|
-
` ${DECLARATION_SPECIFIER_EXTENSION_MARKER},`,
|
|
1804
|
-
" (match, space, quote, specifier) => {",
|
|
1805
|
-
` const resolved = ${DECLARATION_SPECIFIER_DIRECTORY_MARKER}(specifier)`,
|
|
1806
|
-
" return resolved === null ? match : `from${space}${quote}${resolved}${quote}`",
|
|
1807
|
-
" },",
|
|
1808
|
-
" )",
|
|
1809
|
-
" if (withExtensions !== declaration) writeFileSync(filePath, withExtensions)",
|
|
1810
|
-
" }",
|
|
1811
|
-
" }",
|
|
1812
|
-
" }"
|
|
1813
|
-
].join("\n");
|
|
1814
|
-
var ROLLUP_CONFIG_WITH_DTS_FIX = [
|
|
1815
|
-
" {",
|
|
1816
|
-
" // Added by MoNecromanCI. @nx/rollup's dts-bundle plugin writes dist/index.d.ts",
|
|
1817
|
-
" // as a stub re-exporting the real declarations, and builds that specifier with",
|
|
1818
|
-
" // path.relative() - an OS-NATIVE path. On Windows it emits",
|
|
1819
|
-
String.raw` // export * from "./src\\index";`,
|
|
1820
|
-
" // which is not a valid module specifier on ANY platform: a specifier is",
|
|
1821
|
-
" // URL-style, so / is correct everywhere and a backslash nowhere. It resolves on",
|
|
1822
|
-
" // Windows only because the resolver normalises separators there, leaving the",
|
|
1823
|
-
" // package untyped on Linux and macOS.",
|
|
1824
|
-
" //",
|
|
1825
|
-
" // mnci also points `types` past this stub, so nothing depends on it being",
|
|
1826
|
-
" // correct; this makes the emitted file correct too. Remove once Nx fixes the",
|
|
1827
|
-
" // plugin - its own devkit already exports normalizePath for exactly this.",
|
|
1828
|
-
" //",
|
|
1829
|
-
" // The second half fixes a separate defect in the REAL declarations `types`",
|
|
1830
|
-
' // points at: tsconfig.lib.json declares under moduleResolution "bundler",',
|
|
1831
|
-
' // where a bare relative specifier ("./lib/align") is valid, so every emitted',
|
|
1832
|
-
" // .d.ts keeps the source's own extensionless imports verbatim. A consumer on",
|
|
1833
|
-
' // "moduleResolution": "nodenext" - the workspace root default - requires an',
|
|
1834
|
-
" // explicit extension on every relative specifier and gets TS2834 (or,",
|
|
1835
|
-
" // combined with skipLibCheck, a silent zero-export module) instead. Declared",
|
|
1836
|
-
" // extensions are left untouched; only a bare relative specifier gets .js",
|
|
1837
|
-
" // appended, matching what tsc itself emits under node16/nodenext.",
|
|
1838
|
-
" plugins: [",
|
|
1839
|
-
DECLARATION_SPECIFIER_PLUGIN,
|
|
1840
|
-
" ]",
|
|
1841
|
-
" }"
|
|
1842
|
-
].join("\n");
|
|
1843
|
-
function findEnclosingBraceSpan(text, needle) {
|
|
1844
|
-
const needleIndex = text.indexOf(needle);
|
|
1845
|
-
if (needleIndex === -1) {
|
|
1846
|
-
return void 0;
|
|
1847
|
-
}
|
|
1848
|
-
const start = text.lastIndexOf("{", needleIndex);
|
|
1849
|
-
if (start === -1) {
|
|
1850
|
-
return void 0;
|
|
1738
|
+
function registerProjectCommands(workspaceRoot, name, commands) {
|
|
1739
|
+
removeStaleGitkeeps(workspaceRoot);
|
|
1740
|
+
const scripts = {
|
|
1741
|
+
[`${name}:qa`]: `nx run ${name}:lint && nx run ${name}:test`
|
|
1742
|
+
};
|
|
1743
|
+
if (commands.build) {
|
|
1744
|
+
scripts[`${name}:build`] = `nx run ${name}:build`;
|
|
1851
1745
|
}
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
if (text[index] === "{") {
|
|
1855
|
-
depth += 1;
|
|
1856
|
-
} else if (text[index] === "}") {
|
|
1857
|
-
depth -= 1;
|
|
1858
|
-
if (depth === 0) {
|
|
1859
|
-
return [start, index + 1];
|
|
1860
|
-
}
|
|
1861
|
-
}
|
|
1746
|
+
if (commands.start) {
|
|
1747
|
+
scripts[`${name}:start`] = commands.start;
|
|
1862
1748
|
}
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
if (!config.includes(DECLARATION_SPECIFIER_PLUGIN_MARKER) || config.includes(DECLARATION_SPECIFIER_DIRECTORY_MARKER)) {
|
|
1867
|
-
return config;
|
|
1749
|
+
const dependabotPath = (0, import_node_path3.join)(workspaceRoot, ".github/dependabot.yml");
|
|
1750
|
+
if ((0, import_node_fs3.existsSync)(dependabotPath)) {
|
|
1751
|
+
writeFileEnsured(dependabotPath, dependabotConfig(workspaceRoot));
|
|
1868
1752
|
}
|
|
1869
|
-
const
|
|
1870
|
-
|
|
1871
|
-
|
|
1753
|
+
const manifestPath = (0, import_node_path3.join)(workspaceRoot, "package.json");
|
|
1754
|
+
const manifest = readJson(manifestPath);
|
|
1755
|
+
const existingScripts = manifest.scripts ?? {};
|
|
1756
|
+
const overrides = {
|
|
1757
|
+
...manifest.overrides,
|
|
1758
|
+
...reactExpressPeerOverride(manifest)
|
|
1759
|
+
};
|
|
1760
|
+
writeFileEnsured(
|
|
1761
|
+
manifestPath,
|
|
1762
|
+
toJson({
|
|
1763
|
+
...manifest,
|
|
1764
|
+
scripts: { ...existingScripts, ...scripts },
|
|
1765
|
+
...Object.keys(overrides).length > 0 && { overrides }
|
|
1766
|
+
})
|
|
1767
|
+
);
|
|
1768
|
+
const codeWorkspacePath = findCodeWorkspaceFile(workspaceRoot);
|
|
1769
|
+
if (!codeWorkspacePath) {
|
|
1770
|
+
return;
|
|
1872
1771
|
}
|
|
1873
|
-
const
|
|
1874
|
-
|
|
1772
|
+
const workspaceFile = readCodeWorkspace(codeWorkspacePath) ?? {};
|
|
1773
|
+
const label = (task) => task.label ?? "";
|
|
1774
|
+
const existingTasks = (workspaceFile.tasks?.tasks ?? []).filter(
|
|
1775
|
+
(task) => !label(task).startsWith(`${name}: `)
|
|
1776
|
+
);
|
|
1777
|
+
const newTasks = [
|
|
1778
|
+
projectTask(name, "qa"),
|
|
1779
|
+
...commands.build ? [projectTask(name, "build")] : [],
|
|
1780
|
+
...commands.start ? [projectTask(name, "start")] : []
|
|
1781
|
+
];
|
|
1782
|
+
writeFileEnsured(
|
|
1783
|
+
codeWorkspacePath,
|
|
1784
|
+
toJson({
|
|
1785
|
+
...workspaceFile,
|
|
1786
|
+
tasks: {
|
|
1787
|
+
version: workspaceFile.tasks?.version ?? "2.0.0",
|
|
1788
|
+
tasks: [...existingTasks, ...newTasks]
|
|
1789
|
+
}
|
|
1790
|
+
})
|
|
1791
|
+
);
|
|
1875
1792
|
}
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
}
|
|
1881
|
-
|
|
1882
|
-
const configPath = (0, import_node_path3.join)(workspaceRoot, relativePath);
|
|
1883
|
-
const before = (0, import_node_fs3.readFileSync)(configPath, "utf8");
|
|
1884
|
-
const after = withUpgradedDeclarationSpecifierPlugin(before);
|
|
1885
|
-
if (after !== before) {
|
|
1886
|
-
writeFileEnsured(configPath, after);
|
|
1887
|
-
changed.push(relativePath.replaceAll("\\", "/"));
|
|
1888
|
-
}
|
|
1793
|
+
var PROJECT_MANIFEST_ROOTS = ["apps", "packages", "libs"];
|
|
1794
|
+
function rootRuntimeDependencies(workspaceRoot) {
|
|
1795
|
+
try {
|
|
1796
|
+
return readJson((0, import_node_path3.join)(workspaceRoot, "package.json")).dependencies ?? {};
|
|
1797
|
+
} catch {
|
|
1798
|
+
return {};
|
|
1889
1799
|
}
|
|
1890
|
-
return changed;
|
|
1891
1800
|
}
|
|
1892
|
-
function
|
|
1893
|
-
const
|
|
1894
|
-
|
|
1801
|
+
function relocateRootRuntimeDependencies(workspaceRoot, projectName, before) {
|
|
1802
|
+
const after = rootRuntimeDependencies(workspaceRoot);
|
|
1803
|
+
const added = Object.keys(after).filter((name) => !Object.hasOwn(before, name));
|
|
1804
|
+
if (added.length === 0) {
|
|
1895
1805
|
return;
|
|
1896
1806
|
}
|
|
1897
|
-
const
|
|
1898
|
-
|
|
1807
|
+
const manifestPath = PROJECT_MANIFEST_ROOTS.map(
|
|
1808
|
+
(root) => (0, import_node_path3.join)(workspaceRoot, root, projectName, "package.json")
|
|
1809
|
+
).find((candidate) => fileExists(candidate));
|
|
1810
|
+
if (manifestPath === void 0) {
|
|
1899
1811
|
return;
|
|
1900
1812
|
}
|
|
1901
|
-
const
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
const
|
|
1906
|
-
|
|
1907
|
-
(0, import_node_path3.join)(projectRoot, "README.md"),
|
|
1908
|
-
[
|
|
1909
|
-
`# ${projectName}`,
|
|
1910
|
-
"",
|
|
1911
|
-
"Generated by [MoNecromanCI](https://github.com/russoedu/MoNecromanCi).",
|
|
1912
|
-
"",
|
|
1913
|
-
"## Building",
|
|
1914
|
-
"",
|
|
1915
|
-
`Run \`nx build ${projectName}\` to build this project.`,
|
|
1916
|
-
"",
|
|
1917
|
-
"## Running unit tests",
|
|
1918
|
-
"",
|
|
1919
|
-
`Run \`nx test ${projectName}\` to execute the unit tests via ${runner}.`,
|
|
1920
|
-
""
|
|
1921
|
-
].join("\n")
|
|
1922
|
-
);
|
|
1923
|
-
}
|
|
1924
|
-
function removeStaleGitkeeps(workspaceRoot) {
|
|
1925
|
-
for (const scaffold of ["apps", "libs", "packages"]) {
|
|
1926
|
-
const directory = (0, import_node_path3.join)(workspaceRoot, scaffold);
|
|
1927
|
-
try {
|
|
1928
|
-
const holdsAProject = (0, import_node_fs3.readdirSync)(directory, { withFileTypes: true }).some(
|
|
1929
|
-
(entry) => entry.isDirectory()
|
|
1930
|
-
);
|
|
1931
|
-
if (holdsAProject) {
|
|
1932
|
-
(0, import_node_fs3.rmSync)((0, import_node_path3.join)(directory, ".gitkeep"), { force: true });
|
|
1933
|
-
}
|
|
1934
|
-
} catch {
|
|
1935
|
-
}
|
|
1936
|
-
}
|
|
1937
|
-
}
|
|
1938
|
-
function repairPublishableManifest(manifestPath) {
|
|
1939
|
-
const manifest = readJson(manifestPath);
|
|
1940
|
-
if (manifest.types === WRONG_TYPES_PATH) {
|
|
1941
|
-
manifest.types = ACTUAL_TYPES_PATH;
|
|
1942
|
-
}
|
|
1943
|
-
const dot = manifest.exports?.["."];
|
|
1944
|
-
if (typeof dot === "object" && dot.types === WRONG_TYPES_PATH) {
|
|
1945
|
-
dot.types = ACTUAL_TYPES_PATH;
|
|
1946
|
-
}
|
|
1947
|
-
for (const exclusion of [DECLARATION_MAP_EXCLUSION, SOURCE_MAP_EXCLUSION]) {
|
|
1948
|
-
if (manifest.files && !manifest.files.includes(exclusion)) {
|
|
1949
|
-
manifest.files.push(exclusion);
|
|
1950
|
-
}
|
|
1951
|
-
}
|
|
1952
|
-
writeFileEnsured(manifestPath, toJson(manifest));
|
|
1953
|
-
}
|
|
1954
|
-
function repairPublishableManifests(workspaceRoot) {
|
|
1955
|
-
const changed = [];
|
|
1956
|
-
const manifests = (0, import_node_fs3.globSync)(["packages/*/package.json", "libs/*/package.json"], {
|
|
1957
|
-
cwd: workspaceRoot
|
|
1958
|
-
});
|
|
1959
|
-
for (const relativePath of manifests) {
|
|
1960
|
-
const manifestPath = (0, import_node_path3.join)(workspaceRoot, relativePath);
|
|
1961
|
-
const before = (0, import_node_fs3.readFileSync)(manifestPath, "utf8");
|
|
1962
|
-
repairPublishableManifest(manifestPath);
|
|
1963
|
-
const after = (0, import_node_fs3.readFileSync)(manifestPath, "utf8");
|
|
1964
|
-
if (after !== before) {
|
|
1965
|
-
changed.push(relativePath.replaceAll("\\", "/"));
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1968
|
-
return changed;
|
|
1969
|
-
}
|
|
1970
|
-
function ensureAdmZip(workspaceRoot) {
|
|
1971
|
-
if (hasPlugin(workspaceRoot, "adm-zip")) {
|
|
1972
|
-
return;
|
|
1973
|
-
}
|
|
1974
|
-
logger.step("Installing the app packager (adm-zip)");
|
|
1975
|
-
if (runShell(
|
|
1976
|
-
"npm",
|
|
1977
|
-
["install", "--save-dev", "adm-zip", "--no-audit", "--no-fund"],
|
|
1978
|
-
workspaceRoot
|
|
1979
|
-
) !== 0) {
|
|
1980
|
-
throw new Error("npm install of adm-zip failed");
|
|
1981
|
-
}
|
|
1982
|
-
}
|
|
1983
|
-
function defaultScope(workspaceRoot) {
|
|
1984
|
-
const { name } = readJson((0, import_node_path3.join)(workspaceRoot, "package.json"));
|
|
1985
|
-
const base = (name.startsWith("@") ? name.slice(1) : name).split("/", 1)[0];
|
|
1986
|
-
return `@${base}`;
|
|
1987
|
-
}
|
|
1988
|
-
function addNxTargets(manifestPath, newTargets) {
|
|
1989
|
-
const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
|
|
1990
|
-
const nx = manifest.nx ?? {};
|
|
1991
|
-
const targets = nx.targets ?? {};
|
|
1992
|
-
writeFileEnsured(
|
|
1993
|
-
manifestPath,
|
|
1994
|
-
toJson({ ...manifest, nx: { ...nx, targets: { ...targets, ...newTargets } } })
|
|
1995
|
-
);
|
|
1996
|
-
}
|
|
1997
|
-
function addProjectJsonTargets(projectJsonPath, newTargets) {
|
|
1998
|
-
const project = fileExists(projectJsonPath) ? readJson(projectJsonPath) : {};
|
|
1999
|
-
const targets = project.targets ?? {};
|
|
2000
|
-
writeFileEnsured(projectJsonPath, toJson({ ...project, targets: { ...targets, ...newTargets } }));
|
|
2001
|
-
}
|
|
2002
|
-
var ESLINT_CONFIG_FILENAMES = [
|
|
2003
|
-
"eslint.config.js",
|
|
2004
|
-
"eslint.config.mjs",
|
|
2005
|
-
"eslint.config.cjs",
|
|
2006
|
-
"eslint.config.ts",
|
|
2007
|
-
"eslint.config.mts",
|
|
2008
|
-
"eslint.config.cts"
|
|
2009
|
-
];
|
|
2010
|
-
function removeGeneratedEslintConfig(workspaceRoot, projectRoot) {
|
|
2011
|
-
for (const filename of ESLINT_CONFIG_FILENAMES) {
|
|
2012
|
-
(0, import_node_fs3.rmSync)((0, import_node_path3.join)(workspaceRoot, projectRoot, filename), { force: true });
|
|
2013
|
-
}
|
|
2014
|
-
(0, import_node_fs3.rmSync)((0, import_node_path3.join)(workspaceRoot, ".vscode"), { recursive: true, force: true });
|
|
2015
|
-
}
|
|
2016
|
-
function findCodeWorkspaceFile(workspaceRoot) {
|
|
2017
|
-
try {
|
|
2018
|
-
const entry = (0, import_node_fs3.readdirSync)(workspaceRoot).find((file) => file.endsWith(".code-workspace"));
|
|
2019
|
-
return entry ? (0, import_node_path3.join)(workspaceRoot, entry) : void 0;
|
|
2020
|
-
} catch {
|
|
2021
|
-
return void 0;
|
|
2022
|
-
}
|
|
2023
|
-
}
|
|
2024
|
-
function projectTask(name, kind) {
|
|
2025
|
-
const script = `${name}:${kind}`;
|
|
2026
|
-
const base = { label: `${name}: ${kind}`, type: "npm", script, problemMatcher: [] };
|
|
2027
|
-
return kind === "start" ? { ...base, isBackground: true } : { ...base, group: kind };
|
|
2028
|
-
}
|
|
2029
|
-
function registerProjectCommands(workspaceRoot, name, commands) {
|
|
2030
|
-
removeStaleGitkeeps(workspaceRoot);
|
|
2031
|
-
const scripts = {
|
|
2032
|
-
[`${name}:qa`]: `nx run ${name}:lint && nx run ${name}:test`
|
|
2033
|
-
};
|
|
2034
|
-
if (commands.build) {
|
|
2035
|
-
scripts[`${name}:build`] = `nx run ${name}:build`;
|
|
2036
|
-
}
|
|
2037
|
-
if (commands.start) {
|
|
2038
|
-
scripts[`${name}:start`] = commands.start;
|
|
2039
|
-
}
|
|
2040
|
-
const dependabotPath = (0, import_node_path3.join)(workspaceRoot, ".github/dependabot.yml");
|
|
2041
|
-
if ((0, import_node_fs3.existsSync)(dependabotPath)) {
|
|
2042
|
-
writeFileEnsured(dependabotPath, dependabotConfig(workspaceRoot));
|
|
2043
|
-
}
|
|
2044
|
-
const manifestPath = (0, import_node_path3.join)(workspaceRoot, "package.json");
|
|
2045
|
-
const manifest = readJson(manifestPath);
|
|
2046
|
-
const existingScripts = manifest.scripts ?? {};
|
|
2047
|
-
const overrides = {
|
|
2048
|
-
...manifest.overrides,
|
|
2049
|
-
...reactExpressPeerOverride(manifest)
|
|
2050
|
-
};
|
|
2051
|
-
writeFileEnsured(
|
|
2052
|
-
manifestPath,
|
|
2053
|
-
toJson({
|
|
2054
|
-
...manifest,
|
|
2055
|
-
scripts: { ...existingScripts, ...scripts },
|
|
2056
|
-
...Object.keys(overrides).length > 0 && { overrides }
|
|
2057
|
-
})
|
|
2058
|
-
);
|
|
2059
|
-
const codeWorkspacePath = findCodeWorkspaceFile(workspaceRoot);
|
|
2060
|
-
if (!codeWorkspacePath) {
|
|
2061
|
-
return;
|
|
2062
|
-
}
|
|
2063
|
-
const workspaceFile = readCodeWorkspace(codeWorkspacePath) ?? {};
|
|
2064
|
-
const label = (task) => task.label ?? "";
|
|
2065
|
-
const existingTasks = (workspaceFile.tasks?.tasks ?? []).filter(
|
|
2066
|
-
(task) => !label(task).startsWith(`${name}: `)
|
|
2067
|
-
);
|
|
2068
|
-
const newTasks = [
|
|
2069
|
-
projectTask(name, "qa"),
|
|
2070
|
-
...commands.build ? [projectTask(name, "build")] : [],
|
|
2071
|
-
...commands.start ? [projectTask(name, "start")] : []
|
|
2072
|
-
];
|
|
2073
|
-
writeFileEnsured(
|
|
2074
|
-
codeWorkspacePath,
|
|
2075
|
-
toJson({
|
|
2076
|
-
...workspaceFile,
|
|
2077
|
-
tasks: {
|
|
2078
|
-
version: workspaceFile.tasks?.version ?? "2.0.0",
|
|
2079
|
-
tasks: [...existingTasks, ...newTasks]
|
|
2080
|
-
}
|
|
2081
|
-
})
|
|
2082
|
-
);
|
|
2083
|
-
}
|
|
2084
|
-
var PROJECT_MANIFEST_ROOTS = ["apps", "packages", "libs"];
|
|
2085
|
-
function rootRuntimeDependencies(workspaceRoot) {
|
|
2086
|
-
try {
|
|
2087
|
-
return readJson((0, import_node_path3.join)(workspaceRoot, "package.json")).dependencies ?? {};
|
|
2088
|
-
} catch {
|
|
2089
|
-
return {};
|
|
2090
|
-
}
|
|
2091
|
-
}
|
|
2092
|
-
function relocateRootRuntimeDependencies(workspaceRoot, projectName, before) {
|
|
2093
|
-
const after = rootRuntimeDependencies(workspaceRoot);
|
|
2094
|
-
const added = Object.keys(after).filter((name) => !Object.hasOwn(before, name));
|
|
2095
|
-
if (added.length === 0) {
|
|
2096
|
-
return;
|
|
2097
|
-
}
|
|
2098
|
-
const manifestPath = PROJECT_MANIFEST_ROOTS.map(
|
|
2099
|
-
(root) => (0, import_node_path3.join)(workspaceRoot, root, projectName, "package.json")
|
|
2100
|
-
).find((candidate) => fileExists(candidate));
|
|
2101
|
-
if (manifestPath === void 0) {
|
|
2102
|
-
return;
|
|
2103
|
-
}
|
|
2104
|
-
const manifest = readJson(manifestPath);
|
|
2105
|
-
const owned = manifest.dependencies ?? {};
|
|
2106
|
-
const moved = Object.fromEntries(added.map((name) => [name, after[name]]));
|
|
2107
|
-
writeFileEnsured(manifestPath, toJson({ ...manifest, dependencies: { ...moved, ...owned } }));
|
|
2108
|
-
const remaining = Object.fromEntries(
|
|
2109
|
-
Object.entries(after).filter(([name]) => !added.includes(name))
|
|
1813
|
+
const manifest = readJson(manifestPath);
|
|
1814
|
+
const owned = manifest.dependencies ?? {};
|
|
1815
|
+
const moved = Object.fromEntries(added.map((name) => [name, after[name]]));
|
|
1816
|
+
writeFileEnsured(manifestPath, toJson({ ...manifest, dependencies: { ...moved, ...owned } }));
|
|
1817
|
+
const remaining = Object.fromEntries(
|
|
1818
|
+
Object.entries(after).filter(([name]) => !added.includes(name))
|
|
2110
1819
|
);
|
|
2111
1820
|
const rootManifest = readJson((0, import_node_path3.join)(workspaceRoot, "package.json"));
|
|
2112
1821
|
const { dependencies: _dropped, ...rest } = rootManifest;
|
|
@@ -2126,7 +1835,7 @@ function relocateRootRuntimeDependencies(workspaceRoot, projectName, before) {
|
|
|
2126
1835
|
}
|
|
2127
1836
|
}
|
|
2128
1837
|
|
|
2129
|
-
// src/
|
|
1838
|
+
// src/project-scaffolding/csharp.use-case.ts
|
|
2130
1839
|
function ensureDotnet(workspaceRoot) {
|
|
2131
1840
|
if (runShell("dotnet", ["--version"], workspaceRoot) !== 0) {
|
|
2132
1841
|
throw new Error(".NET SDK not found. Install .NET 8+ first: https://dotnet.microsoft.com/download");
|
|
@@ -2383,7 +2092,7 @@ function addCsharpFunctionApp(workspaceRoot, name) {
|
|
|
2383
2092
|
registerProjectCommands(workspaceRoot, name, { build: true, start: `nx run ${name}:start` });
|
|
2384
2093
|
}
|
|
2385
2094
|
|
|
2386
|
-
// src/
|
|
2095
|
+
// src/project-scaffolding/flutter.use-case.ts
|
|
2387
2096
|
var import_node_path5 = require("path");
|
|
2388
2097
|
function flutterAppStartTarget(name) {
|
|
2389
2098
|
return {
|
|
@@ -2436,7 +2145,7 @@ function addFlutterInternalLib(workspaceRoot, name) {
|
|
|
2436
2145
|
registerProjectCommands(workspaceRoot, name, { build: false });
|
|
2437
2146
|
}
|
|
2438
2147
|
|
|
2439
|
-
// src/
|
|
2148
|
+
// src/project-scaffolding/go.use-case.ts
|
|
2440
2149
|
var import_node_fs5 = require("fs");
|
|
2441
2150
|
var import_node_path6 = require("path");
|
|
2442
2151
|
function ensureGo(workspaceRoot) {
|
|
@@ -2612,7 +2321,7 @@ function addGoInternalLib(workspaceRoot, name) {
|
|
|
2612
2321
|
}
|
|
2613
2322
|
}
|
|
2614
2323
|
|
|
2615
|
-
// src/
|
|
2324
|
+
// src/project-scaffolding/node.use-case.ts
|
|
2616
2325
|
var import_node_path7 = require("path");
|
|
2617
2326
|
function ensurePlugin2(workspaceRoot, packageName) {
|
|
2618
2327
|
if (hasPlugin(workspaceRoot, packageName)) {
|
|
@@ -2766,72 +2475,387 @@ function addNodeFunctionApp(workspaceRoot, name, stack) {
|
|
|
2766
2475
|
registerProjectCommands(workspaceRoot, name, { build: true, start: `nx run ${name}:start` });
|
|
2767
2476
|
}
|
|
2768
2477
|
|
|
2769
|
-
// src/
|
|
2770
|
-
var
|
|
2771
|
-
async function addNpmLib(workspaceRoot, name, options, kindProvided, stack) {
|
|
2772
|
-
const scope = options.scope ?? (kindProvided ? defaultScope(workspaceRoot) : await promptText("npm scope for the published package", defaultScope(workspaceRoot)));
|
|
2773
|
-
const projectRoot = (0, import_node_path8.join)(workspaceRoot, "packages", name);
|
|
2774
|
-
const manifestPath = (0, import_node_path8.join)(projectRoot, "package.json");
|
|
2775
|
-
runGeneratorAndRepair(
|
|
2776
|
-
workspaceRoot,
|
|
2777
|
-
[
|
|
2778
|
-
"g",
|
|
2779
|
-
"@nx/js:lib",
|
|
2780
|
-
`packages/${name}`,
|
|
2781
|
-
"--publishable",
|
|
2782
|
-
`--importPath=${scope}/${name}`,
|
|
2783
|
-
"--bundler=rollup",
|
|
2784
|
-
`--unitTestRunner=${stack.testRunner}`,
|
|
2785
|
-
"--linter=none",
|
|
2786
|
-
"--no-interactive"
|
|
2787
|
-
],
|
|
2788
|
-
manifestPath,
|
|
2789
|
-
() => {
|
|
2790
|
-
markPublic(manifestPath);
|
|
2791
|
-
repairPublishableManifest(manifestPath);
|
|
2792
|
-
repairDeclarationSpecifiers(projectRoot);
|
|
2793
|
-
writeProjectReadme(projectRoot, `${scope}/${name}`, stack.testRunner);
|
|
2794
|
-
removeGeneratedEslintConfig(workspaceRoot, `packages/${name}`);
|
|
2795
|
-
registerProjectCommands(workspaceRoot, name, { build: true });
|
|
2796
|
-
}
|
|
2797
|
-
);
|
|
2798
|
-
}
|
|
2478
|
+
// src/project-scaffolding/npm-lib.use-case.ts
|
|
2479
|
+
var import_node_path11 = require("path");
|
|
2799
2480
|
|
|
2800
|
-
// src/
|
|
2481
|
+
// src/rollup-library/repair-publishable-manifest.use-case.ts
|
|
2801
2482
|
var import_node_fs6 = require("fs");
|
|
2802
|
-
var
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2483
|
+
var import_node_path8 = require("path");
|
|
2484
|
+
var WRONG_TYPES_PATH = "./dist/index.esm.d.ts";
|
|
2485
|
+
var ACTUAL_TYPES_PATH = "./dist/src/index.d.ts";
|
|
2486
|
+
var DECLARATION_MAP_EXCLUSION = "!**/*.d.ts.map";
|
|
2487
|
+
var SOURCE_MAP_EXCLUSION = "!**/*.js.map";
|
|
2488
|
+
function repairPublishableManifest(manifestPath) {
|
|
2489
|
+
const manifest = readJson(manifestPath);
|
|
2490
|
+
if (manifest.types === WRONG_TYPES_PATH) {
|
|
2491
|
+
manifest.types = ACTUAL_TYPES_PATH;
|
|
2809
2492
|
}
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
}
|
|
2814
|
-
function ensurePythonPipPlugin(workspaceRoot) {
|
|
2815
|
-
if (hasPlugin(workspaceRoot, "@mnci/nx-python-pip")) {
|
|
2816
|
-
return;
|
|
2493
|
+
const dot = manifest.exports?.["."];
|
|
2494
|
+
if (typeof dot === "object" && dot.types === WRONG_TYPES_PATH) {
|
|
2495
|
+
dot.types = ACTUAL_TYPES_PATH;
|
|
2817
2496
|
}
|
|
2818
|
-
const
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2497
|
+
for (const exclusion of [DECLARATION_MAP_EXCLUSION, SOURCE_MAP_EXCLUSION]) {
|
|
2498
|
+
if (manifest.files && !manifest.files.includes(exclusion)) {
|
|
2499
|
+
manifest.files.push(exclusion);
|
|
2500
|
+
}
|
|
2822
2501
|
}
|
|
2502
|
+
writeFileEnsured(manifestPath, toJson(manifest));
|
|
2823
2503
|
}
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2504
|
+
function repairPublishableManifests(workspaceRoot) {
|
|
2505
|
+
const changed = [];
|
|
2506
|
+
const manifests = (0, import_node_fs6.globSync)(["packages/*/package.json", "libs/*/package.json"], {
|
|
2507
|
+
cwd: workspaceRoot
|
|
2508
|
+
});
|
|
2509
|
+
for (const relativePath of manifests) {
|
|
2510
|
+
const manifestPath = (0, import_node_path8.join)(workspaceRoot, relativePath);
|
|
2511
|
+
const before = (0, import_node_fs6.readFileSync)(manifestPath, "utf8");
|
|
2512
|
+
repairPublishableManifest(manifestPath);
|
|
2513
|
+
const after = (0, import_node_fs6.readFileSync)(manifestPath, "utf8");
|
|
2514
|
+
if (after !== before) {
|
|
2515
|
+
changed.push(relativePath.replaceAll("\\", "/"));
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
return changed;
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
// src/rollup-library/repair-rollup-config.use-case.ts
|
|
2522
|
+
var import_node_fs7 = require("fs");
|
|
2523
|
+
var import_node_path9 = require("path");
|
|
2524
|
+
|
|
2525
|
+
// src/rollup-library/rollup-config.algorithm.ts
|
|
2526
|
+
var ROLLUP_CONFIG_PLACEHOLDER = [
|
|
2527
|
+
" {",
|
|
2528
|
+
" // Provide additional rollup configuration here. See: https://rollupjs.org/configuration-options",
|
|
2529
|
+
" // e.g.",
|
|
2530
|
+
" // output: { sourcemap: true },",
|
|
2531
|
+
" }"
|
|
2532
|
+
].join("\n");
|
|
2533
|
+
var GENERATED_COMPILER_PATTERN = /^( {4})compiler\s*:\s*'swc',$/m;
|
|
2534
|
+
function sourceMapCapableCompiler(indent) {
|
|
2535
|
+
return [
|
|
2536
|
+
`${indent}// Swapped from swc by MoNecromanCI. @nx/rollup runs swc without`,
|
|
2537
|
+
`${indent}// sourceMaps, so it returns no map and the bundle's map comes out empty -`,
|
|
2538
|
+
`${indent}// valid-looking, and useless for debugging. See ROADMAP.`,
|
|
2539
|
+
`${indent}compiler: 'babel',`
|
|
2540
|
+
].join("\n");
|
|
2541
|
+
}
|
|
2542
|
+
var ROLLUP_ARG_ONE_BOUNDARY = [" },", " {", ""].join("\n");
|
|
2543
|
+
var ROLLUP_ARG_ONE_WITH_SOURCE_MAPS = [
|
|
2544
|
+
" // Added by MoNecromanCI: without this rollup emits no .js.map at all, so",
|
|
2545
|
+
" // a breakpoint in a .ts file can never bind. Not published - see `files`.",
|
|
2546
|
+
" sourceMap: true",
|
|
2547
|
+
" },",
|
|
2548
|
+
" {",
|
|
2549
|
+
""
|
|
2550
|
+
].join("\n");
|
|
2551
|
+
function hasRollupSourceMaps(config) {
|
|
2552
|
+
return /sourceMap\s*:\s*true\b/.test(config);
|
|
2553
|
+
}
|
|
2554
|
+
function canRepairRollupConfig(config) {
|
|
2555
|
+
return config.includes(ROLLUP_ARG_ONE_BOUNDARY);
|
|
2556
|
+
}
|
|
2557
|
+
function withRollupSourceMaps(config) {
|
|
2558
|
+
if (hasRollupSourceMaps(config) || !canRepairRollupConfig(config)) {
|
|
2559
|
+
return config;
|
|
2560
|
+
}
|
|
2561
|
+
const withCompiler = config.replace(
|
|
2562
|
+
GENERATED_COMPILER_PATTERN,
|
|
2563
|
+
(_match, indent) => sourceMapCapableCompiler(indent)
|
|
2564
|
+
);
|
|
2565
|
+
const withFlag = withCompiler.replace(
|
|
2566
|
+
ROLLUP_ARG_ONE_BOUNDARY,
|
|
2567
|
+
() => ROLLUP_ARG_ONE_WITH_SOURCE_MAPS
|
|
2568
|
+
);
|
|
2569
|
+
return withFlag.includes("sourcemapPathTransform") ? withFlag : withFlag.replace(
|
|
2570
|
+
ROLLUP_ARG_ONE_WITH_SOURCE_MAPS,
|
|
2571
|
+
() => `${ROLLUP_ARG_ONE_WITH_SOURCE_MAPS}${SOURCEMAP_PATH_TRANSFORM}`
|
|
2572
|
+
);
|
|
2573
|
+
}
|
|
2574
|
+
var SOURCEMAP_PATH_TRANSFORM = [
|
|
2575
|
+
" // Added by MoNecromanCI. rollup hands sourcemapPathTransform an OS-NATIVE",
|
|
2576
|
+
" // path with one parent segment too many, so `sources` resolve to nothing",
|
|
2577
|
+
" // and no breakpoint can bind. Separators are normalised too: a sources",
|
|
2578
|
+
" // entry is URL-style, so a backslash is wrong on every platform.",
|
|
2579
|
+
" output: {",
|
|
2580
|
+
" sourcemapPathTransform: relativeSourcePath =>",
|
|
2581
|
+
" relativeSourcePath",
|
|
2582
|
+
" .replaceAll(String.fromCodePoint(92), '/')",
|
|
2583
|
+
" .replace(/^([.][.][/])+/, '../')",
|
|
2584
|
+
" },",
|
|
2585
|
+
""
|
|
2586
|
+
].join("\n");
|
|
2587
|
+
var DECLARATION_SPECIFIER_PLUGIN_MARKER = "name: 'mnci-normalise-declaration-specifiers'";
|
|
2588
|
+
var DECLARATION_SPECIFIER_EXTENSION_MARKER = "bareRelativeSpecifier";
|
|
2589
|
+
var DECLARATION_SPECIFIER_DIRECTORY_MARKER = "resolveSpecifierSuffix";
|
|
2590
|
+
function hasDeclarationSpecifierPlugin(config) {
|
|
2591
|
+
return config.includes(DECLARATION_SPECIFIER_PLUGIN_MARKER);
|
|
2592
|
+
}
|
|
2593
|
+
function hasDirectoryAwareDeclarationSpecifiers(config) {
|
|
2594
|
+
return config.includes(DECLARATION_SPECIFIER_DIRECTORY_MARKER);
|
|
2595
|
+
}
|
|
2596
|
+
var DECLARATION_SPECIFIER_PLUGIN = [
|
|
2597
|
+
" {",
|
|
2598
|
+
" name: 'mnci-normalise-declaration-specifiers',",
|
|
2599
|
+
" writeBundle (outputOptions) {",
|
|
2600
|
+
" const { existsSync, readdirSync, readFileSync, writeFileSync } = require('node:fs')",
|
|
2601
|
+
" const { join } = require('node:path')",
|
|
2602
|
+
" const dir = outputOptions.dir ?? './dist'",
|
|
2603
|
+
" const stub = join(dir, 'index.d.ts')",
|
|
2604
|
+
" let source",
|
|
2605
|
+
" try {",
|
|
2606
|
+
" source = readFileSync(stub, 'utf8')",
|
|
2607
|
+
" } catch {",
|
|
2608
|
+
" return",
|
|
2609
|
+
" }",
|
|
2610
|
+
" // The stub carries a TWO-character escape (JSON.stringify escaped one",
|
|
2611
|
+
" // backslash), so this must not match a single one - that would turn",
|
|
2612
|
+
String.raw` // "./src\index" into "./src//index". Built from char codes so there is`,
|
|
2613
|
+
" // no escaping in this file to get wrong.",
|
|
2614
|
+
" const separator = String.fromCodePoint(92, 92)",
|
|
2615
|
+
" const normalised = source.replaceAll(separator, '/')",
|
|
2616
|
+
" if (normalised !== source) writeFileSync(stub, normalised)",
|
|
2617
|
+
"",
|
|
2618
|
+
String.raw` const ${DECLARATION_SPECIFIER_EXTENSION_MARKER} = /from(\s+)(['"])(\.[^'"]+?)\2/g`,
|
|
2619
|
+
String.raw` const hasExtension = /\.(?:mjs|cjs|jsx?|json)$/`,
|
|
2620
|
+
" let entries",
|
|
2621
|
+
" try {",
|
|
2622
|
+
" entries = readdirSync(dir, { recursive: true, withFileTypes: true })",
|
|
2623
|
+
" } catch {",
|
|
2624
|
+
" return",
|
|
2625
|
+
" }",
|
|
2626
|
+
" for (const entry of entries) {",
|
|
2627
|
+
" if (!entry.name.endsWith('.d.ts')) continue",
|
|
2628
|
+
" const from = entry.parentPath ?? entry.path",
|
|
2629
|
+
" const filePath = join(from, entry.name)",
|
|
2630
|
+
" let declaration",
|
|
2631
|
+
" try {",
|
|
2632
|
+
" declaration = readFileSync(filePath, 'utf8')",
|
|
2633
|
+
" } catch {",
|
|
2634
|
+
" continue",
|
|
2635
|
+
" }",
|
|
2636
|
+
" // A bare specifier may name a FILE or a DIRECTORY BARREL - resolved",
|
|
2637
|
+
" // against what rollup actually emitted next to this file, never",
|
|
2638
|
+
" // guessed. A directory needs /index.js, not .js (a file that was",
|
|
2639
|
+
" // never written); anything neither form matches is left alone rather",
|
|
2640
|
+
" // than rewritten to a specifier that cannot resolve.",
|
|
2641
|
+
` const ${DECLARATION_SPECIFIER_DIRECTORY_MARKER} = (specifier) => {`,
|
|
2642
|
+
" if (hasExtension.test(specifier)) return null",
|
|
2643
|
+
" if (existsSync(join(from, specifier + '.d.ts'))) return specifier + '.js'",
|
|
2644
|
+
" if (existsSync(join(from, specifier, 'index.d.ts'))) return specifier + '/index.js'",
|
|
2645
|
+
" return null",
|
|
2646
|
+
" }",
|
|
2647
|
+
" const withExtensions = declaration.replace(",
|
|
2648
|
+
` ${DECLARATION_SPECIFIER_EXTENSION_MARKER},`,
|
|
2649
|
+
" (match, space, quote, specifier) => {",
|
|
2650
|
+
` const resolved = ${DECLARATION_SPECIFIER_DIRECTORY_MARKER}(specifier)`,
|
|
2651
|
+
" return resolved === null ? match : `from${space}${quote}${resolved}${quote}`",
|
|
2652
|
+
" },",
|
|
2653
|
+
" )",
|
|
2654
|
+
" if (withExtensions !== declaration) writeFileSync(filePath, withExtensions)",
|
|
2655
|
+
" }",
|
|
2656
|
+
" }",
|
|
2657
|
+
" }"
|
|
2658
|
+
].join("\n");
|
|
2659
|
+
var ROLLUP_CONFIG_WITH_DTS_FIX = [
|
|
2660
|
+
" {",
|
|
2661
|
+
" // Added by MoNecromanCI. @nx/rollup's dts-bundle plugin writes dist/index.d.ts",
|
|
2662
|
+
" // as a stub re-exporting the real declarations, and builds that specifier with",
|
|
2663
|
+
" // path.relative() - an OS-NATIVE path. On Windows it emits",
|
|
2664
|
+
String.raw` // export * from "./src\\index";`,
|
|
2665
|
+
" // which is not a valid module specifier on ANY platform: a specifier is",
|
|
2666
|
+
" // URL-style, so / is correct everywhere and a backslash nowhere. It resolves on",
|
|
2667
|
+
" // Windows only because the resolver normalises separators there, leaving the",
|
|
2668
|
+
" // package untyped on Linux and macOS.",
|
|
2669
|
+
" //",
|
|
2670
|
+
" // mnci also points `types` past this stub, so nothing depends on it being",
|
|
2671
|
+
" // correct; this makes the emitted file correct too. Remove once Nx fixes the",
|
|
2672
|
+
" // plugin - its own devkit already exports normalizePath for exactly this.",
|
|
2673
|
+
" //",
|
|
2674
|
+
" // The second half fixes a separate defect in the REAL declarations `types`",
|
|
2675
|
+
' // points at: tsconfig.lib.json declares under moduleResolution "bundler",',
|
|
2676
|
+
' // where a bare relative specifier ("./lib/align") is valid, so every emitted',
|
|
2677
|
+
" // .d.ts keeps the source's own extensionless imports verbatim. A consumer on",
|
|
2678
|
+
' // "moduleResolution": "nodenext" - the workspace root default - requires an',
|
|
2679
|
+
" // explicit extension on every relative specifier and gets TS2834 (or,",
|
|
2680
|
+
" // combined with skipLibCheck, a silent zero-export module) instead. Declared",
|
|
2681
|
+
" // extensions are left untouched; only a bare relative specifier gets .js",
|
|
2682
|
+
" // appended, matching what tsc itself emits under node16/nodenext.",
|
|
2683
|
+
" plugins: [",
|
|
2684
|
+
DECLARATION_SPECIFIER_PLUGIN,
|
|
2685
|
+
" ]",
|
|
2686
|
+
" }"
|
|
2687
|
+
].join("\n");
|
|
2688
|
+
function findEnclosingBraceSpan(text, needle) {
|
|
2689
|
+
const needleIndex = text.indexOf(needle);
|
|
2690
|
+
if (needleIndex === -1) {
|
|
2691
|
+
return void 0;
|
|
2692
|
+
}
|
|
2693
|
+
const start = text.lastIndexOf("{", needleIndex);
|
|
2694
|
+
if (start === -1) {
|
|
2695
|
+
return void 0;
|
|
2696
|
+
}
|
|
2697
|
+
let depth = 0;
|
|
2698
|
+
for (let index = start; index < text.length; index += 1) {
|
|
2699
|
+
if (text[index] === "{") {
|
|
2700
|
+
depth += 1;
|
|
2701
|
+
} else if (text[index] === "}") {
|
|
2702
|
+
depth -= 1;
|
|
2703
|
+
if (depth === 0) {
|
|
2704
|
+
return [start, index + 1];
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2708
|
+
return void 0;
|
|
2709
|
+
}
|
|
2710
|
+
function withUpgradedDeclarationSpecifierPlugin(config) {
|
|
2711
|
+
if (!config.includes(DECLARATION_SPECIFIER_PLUGIN_MARKER) || config.includes(DECLARATION_SPECIFIER_DIRECTORY_MARKER)) {
|
|
2712
|
+
return config;
|
|
2713
|
+
}
|
|
2714
|
+
const span = findEnclosingBraceSpan(config, DECLARATION_SPECIFIER_PLUGIN_MARKER);
|
|
2715
|
+
if (!span) {
|
|
2716
|
+
return config;
|
|
2717
|
+
}
|
|
2718
|
+
const [start, end] = span;
|
|
2719
|
+
return `${config.slice(0, start)}${DECLARATION_SPECIFIER_PLUGIN}${config.slice(end)}`;
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2722
|
+
// src/rollup-library/repair-rollup-config.use-case.ts
|
|
2723
|
+
function repairRollupSourceMaps(workspaceRoot) {
|
|
2724
|
+
const changed = [];
|
|
2725
|
+
const configs = (0, import_node_fs7.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
|
|
2726
|
+
cwd: workspaceRoot
|
|
2727
|
+
});
|
|
2728
|
+
for (const relativePath of configs) {
|
|
2729
|
+
const configPath = (0, import_node_path9.join)(workspaceRoot, relativePath);
|
|
2730
|
+
const before = (0, import_node_fs7.readFileSync)(configPath, "utf8");
|
|
2731
|
+
const after = withRollupSourceMaps(before);
|
|
2732
|
+
if (after !== before) {
|
|
2733
|
+
writeFileEnsured(configPath, after);
|
|
2734
|
+
changed.push(relativePath.replaceAll("\\", "/"));
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
return changed;
|
|
2738
|
+
}
|
|
2739
|
+
function upgradeDeclarationSpecifierPlugins(workspaceRoot) {
|
|
2740
|
+
const changed = [];
|
|
2741
|
+
const configs = (0, import_node_fs7.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
|
|
2742
|
+
cwd: workspaceRoot
|
|
2743
|
+
});
|
|
2744
|
+
for (const relativePath of configs) {
|
|
2745
|
+
const configPath = (0, import_node_path9.join)(workspaceRoot, relativePath);
|
|
2746
|
+
const before = (0, import_node_fs7.readFileSync)(configPath, "utf8");
|
|
2747
|
+
const after = withUpgradedDeclarationSpecifierPlugin(before);
|
|
2748
|
+
if (after !== before) {
|
|
2749
|
+
writeFileEnsured(configPath, after);
|
|
2750
|
+
changed.push(relativePath.replaceAll("\\", "/"));
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
return changed;
|
|
2754
|
+
}
|
|
2755
|
+
function repairDeclarationSpecifiers(projectRoot) {
|
|
2756
|
+
const configPath = (0, import_node_path9.join)(projectRoot, "rollup.config.cjs");
|
|
2757
|
+
if (!fileExists(configPath)) {
|
|
2758
|
+
return;
|
|
2759
|
+
}
|
|
2760
|
+
const config = (0, import_node_fs7.readFileSync)(configPath, "utf8");
|
|
2761
|
+
if (!config.includes(ROLLUP_CONFIG_PLACEHOLDER)) {
|
|
2762
|
+
return;
|
|
2763
|
+
}
|
|
2764
|
+
const withDtsFix = config.split(ROLLUP_CONFIG_PLACEHOLDER).join(ROLLUP_CONFIG_WITH_DTS_FIX);
|
|
2765
|
+
writeFileEnsured(configPath, withRollupSourceMaps(withDtsFix));
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2768
|
+
// src/rollup-library/rollup-config.repository.ts
|
|
2769
|
+
var import_node_fs8 = require("fs");
|
|
2770
|
+
var import_node_path10 = require("path");
|
|
2771
|
+
function resolveRequireTarget(fromDir, specifier) {
|
|
2772
|
+
const base = (0, import_node_path10.join)(fromDir, specifier);
|
|
2773
|
+
return [base, `${base}.cjs`, `${base}.js`].find((candidate) => fileExists(candidate));
|
|
2774
|
+
}
|
|
2775
|
+
function resolveRollupConfigText(configPath, visited = /* @__PURE__ */ new Set()) {
|
|
2776
|
+
if (visited.has(configPath)) {
|
|
2777
|
+
return "";
|
|
2778
|
+
}
|
|
2779
|
+
visited.add(configPath);
|
|
2780
|
+
let text;
|
|
2781
|
+
try {
|
|
2782
|
+
text = (0, import_node_fs8.readFileSync)(configPath, "utf8");
|
|
2783
|
+
} catch {
|
|
2784
|
+
return "";
|
|
2785
|
+
}
|
|
2786
|
+
const specifiers = Array.from(
|
|
2787
|
+
text.matchAll(/require\((['"])(\.[^'"]*)\1\)/g),
|
|
2788
|
+
(match) => match[2]
|
|
2789
|
+
);
|
|
2790
|
+
const requiredText = specifiers.map((specifier) => resolveRequireTarget((0, import_node_path10.dirname)(configPath), specifier)).filter((target) => target !== void 0).map((target) => resolveRollupConfigText(target, visited));
|
|
2791
|
+
return [text, ...requiredText].join("\n");
|
|
2792
|
+
}
|
|
2793
|
+
|
|
2794
|
+
// src/project-scaffolding/npm-lib.use-case.ts
|
|
2795
|
+
async function addNpmLib(workspaceRoot, name, options, kindProvided, stack) {
|
|
2796
|
+
const scope = options.scope ?? (kindProvided ? defaultScope(workspaceRoot) : await promptText("npm scope for the published package", defaultScope(workspaceRoot)));
|
|
2797
|
+
const projectRoot = (0, import_node_path11.join)(workspaceRoot, "packages", name);
|
|
2798
|
+
const manifestPath = (0, import_node_path11.join)(projectRoot, "package.json");
|
|
2799
|
+
runGeneratorAndRepair(
|
|
2800
|
+
workspaceRoot,
|
|
2801
|
+
[
|
|
2802
|
+
"g",
|
|
2803
|
+
"@nx/js:lib",
|
|
2804
|
+
`packages/${name}`,
|
|
2805
|
+
"--publishable",
|
|
2806
|
+
`--importPath=${scope}/${name}`,
|
|
2807
|
+
"--bundler=rollup",
|
|
2808
|
+
`--unitTestRunner=${stack.testRunner}`,
|
|
2809
|
+
"--linter=none",
|
|
2810
|
+
"--no-interactive"
|
|
2811
|
+
],
|
|
2812
|
+
manifestPath,
|
|
2813
|
+
() => {
|
|
2814
|
+
markPublic(manifestPath);
|
|
2815
|
+
repairPublishableManifest(manifestPath);
|
|
2816
|
+
repairDeclarationSpecifiers(projectRoot);
|
|
2817
|
+
writeProjectReadme(projectRoot, `${scope}/${name}`, stack.testRunner);
|
|
2818
|
+
removeGeneratedEslintConfig(workspaceRoot, `packages/${name}`);
|
|
2819
|
+
registerProjectCommands(workspaceRoot, name, { build: true });
|
|
2820
|
+
}
|
|
2821
|
+
);
|
|
2822
|
+
}
|
|
2823
|
+
|
|
2824
|
+
// src/project-scaffolding/python.use-case.ts
|
|
2825
|
+
var import_node_fs9 = require("fs");
|
|
2826
|
+
var import_node_path12 = require("path");
|
|
2827
|
+
function ensurePython(workspaceRoot) {
|
|
2828
|
+
const hasPython = runShell("python3", ["--version"], workspaceRoot) === 0 || runShell("python", ["--version"], workspaceRoot) === 0;
|
|
2829
|
+
if (!hasPython) {
|
|
2830
|
+
throw new Error(
|
|
2831
|
+
"Python not found. Install Python 3.9+ first: https://www.python.org/downloads/"
|
|
2832
|
+
);
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
function pythonPipPluginSpec() {
|
|
2836
|
+
return process.env.MNCI2_PYTHON_PIP_SPEC ?? "@mnci/nx-python-pip";
|
|
2837
|
+
}
|
|
2838
|
+
function ensurePythonPipPlugin(workspaceRoot) {
|
|
2839
|
+
if (hasPlugin(workspaceRoot, "@mnci/nx-python-pip")) {
|
|
2840
|
+
return;
|
|
2841
|
+
}
|
|
2842
|
+
const spec = pythonPipPluginSpec();
|
|
2843
|
+
logger.step(`Installing the Python toolchain plugin (${spec})`);
|
|
2844
|
+
if (runShell("npm", ["install", "--save-dev", spec, "--no-audit", "--no-fund"], workspaceRoot) !== 0) {
|
|
2845
|
+
throw new Error("npm install of @mnci/nx-python-pip failed");
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
var PYTHON_REQUIREMENTS_DEV = `build
|
|
2849
|
+
twine
|
|
2850
|
+
ruff
|
|
2851
|
+
pytest
|
|
2852
|
+
pip-audit
|
|
2853
|
+
`;
|
|
2854
|
+
function ensureRequirementsDev(workspaceRoot) {
|
|
2855
|
+
const requirementsDevPath = (0, import_node_path12.join)(workspaceRoot, "requirements-dev.txt");
|
|
2856
|
+
if (!fileExists(requirementsDevPath)) {
|
|
2857
|
+
writeFileEnsured(requirementsDevPath, PYTHON_REQUIREMENTS_DEV);
|
|
2858
|
+
}
|
|
2835
2859
|
}
|
|
2836
2860
|
function pythonModuleDirectory(name) {
|
|
2837
2861
|
return name.replaceAll("-", "_");
|
|
@@ -2888,8 +2912,8 @@ function addPythonApp(workspaceRoot, name) {
|
|
|
2888
2912
|
["g", "@mnci/nx-python-pip:application", name, `--directory=apps/${name}`, "--no-interactive"],
|
|
2889
2913
|
workspaceRoot
|
|
2890
2914
|
);
|
|
2891
|
-
writeFileEnsured((0,
|
|
2892
|
-
addProjectJsonTargets((0,
|
|
2915
|
+
writeFileEnsured((0, import_node_path12.join)(workspaceRoot, "apps", name, "main.py"), pythonAppMain(name));
|
|
2916
|
+
addProjectJsonTargets((0, import_node_path12.join)(workspaceRoot, "apps", name, "project.json"), {
|
|
2893
2917
|
package: pythonAppPackageTarget(name),
|
|
2894
2918
|
start: pythonAppStartTarget(name)
|
|
2895
2919
|
});
|
|
@@ -2911,7 +2935,7 @@ function addPythonFunctionApp(workspaceRoot, name) {
|
|
|
2911
2935
|
workspaceRoot
|
|
2912
2936
|
);
|
|
2913
2937
|
const moduleDirectory = pythonModuleDirectory(name);
|
|
2914
|
-
addProjectJsonTargets((0,
|
|
2938
|
+
addProjectJsonTargets((0, import_node_path12.join)(workspaceRoot, "apps", name, "project.json"), {
|
|
2915
2939
|
package: pythonFunctionAppPackageTarget(name, moduleDirectory),
|
|
2916
2940
|
start: pythonFunctionAppStartTarget(name)
|
|
2917
2941
|
});
|
|
@@ -2952,7 +2976,7 @@ function addPythonInternalLib(workspaceRoot, name) {
|
|
|
2952
2976
|
var PYTHON_PROJECT_DIRS = ["apps", "python-packages", "libs"];
|
|
2953
2977
|
function findPythonProjectDirectory(workspaceRoot, name) {
|
|
2954
2978
|
return PYTHON_PROJECT_DIRS.find(
|
|
2955
|
-
(directory) => fileExists((0,
|
|
2979
|
+
(directory) => fileExists((0, import_node_path12.join)(workspaceRoot, directory, name, "pyproject.toml"))
|
|
2956
2980
|
);
|
|
2957
2981
|
}
|
|
2958
2982
|
function parseVendorEntries(pyprojectToml) {
|
|
@@ -2991,7 +3015,7 @@ async function addPythonVendor(workspaceRoot, consumer, options, kindProvided) {
|
|
|
2991
3015
|
`No Python project named '${consumer}' with a pyproject.toml found (checked apps/, python-packages/, libs/). A Python function app has no pyproject.toml \u2014 there is nothing to vendor into.`
|
|
2992
3016
|
);
|
|
2993
3017
|
}
|
|
2994
|
-
if (!fileExists((0,
|
|
3018
|
+
if (!fileExists((0, import_node_path12.join)(workspaceRoot, "libs", lib, "pyproject.toml"))) {
|
|
2995
3019
|
throw new Error(
|
|
2996
3020
|
`No internal Python library named '${lib}' found at libs/${lib}/pyproject.toml. Only an internal library (added via 'mnci add python-internal-lib') can be vendored.`
|
|
2997
3021
|
);
|
|
@@ -2999,8 +3023,8 @@ async function addPythonVendor(workspaceRoot, consumer, options, kindProvided) {
|
|
|
2999
3023
|
if (consumerDirectory === "libs" && consumer === lib) {
|
|
3000
3024
|
throw new Error(`'${consumer}' cannot vendor itself.`);
|
|
3001
3025
|
}
|
|
3002
|
-
const pyprojectPath = (0,
|
|
3003
|
-
const pyprojectToml = (0,
|
|
3026
|
+
const pyprojectPath = (0, import_node_path12.join)(workspaceRoot, consumerDirectory, consumer, "pyproject.toml");
|
|
3027
|
+
const pyprojectToml = (0, import_node_fs9.readFileSync)(pyprojectPath, "utf8");
|
|
3004
3028
|
if (parseVendorEntries(pyprojectToml).includes(lib)) {
|
|
3005
3029
|
logger.info(`'${consumer}' already vendors '${lib}' \u2014 nothing to do.`);
|
|
3006
3030
|
return;
|
|
@@ -3011,9 +3035,9 @@ async function addPythonVendor(workspaceRoot, consumer, options, kindProvided) {
|
|
|
3011
3035
|
);
|
|
3012
3036
|
}
|
|
3013
3037
|
|
|
3014
|
-
// src/
|
|
3015
|
-
var
|
|
3016
|
-
var
|
|
3038
|
+
// src/project-scaffolding/react-app.use-case.ts
|
|
3039
|
+
var import_node_fs10 = require("fs");
|
|
3040
|
+
var import_node_path13 = require("path");
|
|
3017
3041
|
var REACT_ENVIRONMENTS = ["dev", "uat", "prod"];
|
|
3018
3042
|
function reactEnvironmentFile(environment) {
|
|
3019
3043
|
return `# Vite compiles VITE_-prefixed vars into the '${environment}' bundle at build time.
|
|
@@ -3050,12 +3074,12 @@ function reactAppTargets(name) {
|
|
|
3050
3074
|
return targets;
|
|
3051
3075
|
}
|
|
3052
3076
|
function allowEnvFiles(workspaceRoot) {
|
|
3053
|
-
const gitignorePath = (0,
|
|
3077
|
+
const gitignorePath = (0, import_node_path13.join)(workspaceRoot, ".gitignore");
|
|
3054
3078
|
if (!fileExists(gitignorePath)) {
|
|
3055
3079
|
return;
|
|
3056
3080
|
}
|
|
3057
3081
|
const marker = "# MoNecromanCI: keep the committed per-environment Vite config (public VITE_ values)";
|
|
3058
|
-
const current = (0,
|
|
3082
|
+
const current = (0, import_node_fs10.readFileSync)(gitignorePath, "utf8");
|
|
3059
3083
|
if (current.includes(marker)) {
|
|
3060
3084
|
return;
|
|
3061
3085
|
}
|
|
@@ -3082,24 +3106,24 @@ function addReactApp(workspaceRoot, name, stack) {
|
|
|
3082
3106
|
workspaceRoot
|
|
3083
3107
|
);
|
|
3084
3108
|
ensureAdmZip(workspaceRoot);
|
|
3085
|
-
const reactAppRoot = (0,
|
|
3109
|
+
const reactAppRoot = (0, import_node_path13.join)(workspaceRoot, "apps", name);
|
|
3086
3110
|
for (const environment of REACT_ENVIRONMENTS) {
|
|
3087
|
-
writeFileEnsured((0,
|
|
3111
|
+
writeFileEnsured((0, import_node_path13.join)(reactAppRoot, `.env.${environment}`), reactEnvironmentFile(environment));
|
|
3088
3112
|
}
|
|
3089
3113
|
allowEnvFiles(workspaceRoot);
|
|
3090
|
-
addNxTargets((0,
|
|
3114
|
+
addNxTargets((0, import_node_path13.join)(reactAppRoot, "package.json"), reactAppTargets(name));
|
|
3091
3115
|
removeGeneratedEslintConfig(workspaceRoot, `apps/${name}`);
|
|
3092
3116
|
registerProjectCommands(workspaceRoot, name, { build: true, start: `nx run ${name}:serve` });
|
|
3093
3117
|
}
|
|
3094
3118
|
|
|
3095
|
-
// src/
|
|
3096
|
-
var
|
|
3119
|
+
// src/project-scaffolding/react-lib.use-case.ts
|
|
3120
|
+
var import_node_path14 = require("path");
|
|
3097
3121
|
var REACT_LIB_BUNDLER = "rollup";
|
|
3098
3122
|
async function addReactLib(workspaceRoot, name, options, kindProvided, stack) {
|
|
3099
3123
|
ensurePlugin(workspaceRoot, "@nx/react");
|
|
3100
3124
|
const scope = options.scope ?? (kindProvided ? defaultScope(workspaceRoot) : await promptText("npm scope for the published package", defaultScope(workspaceRoot)));
|
|
3101
|
-
const projectRoot = (0,
|
|
3102
|
-
const publishableManifest = (0,
|
|
3125
|
+
const projectRoot = (0, import_node_path14.join)(workspaceRoot, "packages", name);
|
|
3126
|
+
const publishableManifest = (0, import_node_path14.join)(projectRoot, "package.json");
|
|
3103
3127
|
runGeneratorAndRepair(
|
|
3104
3128
|
workspaceRoot,
|
|
3105
3129
|
[
|
|
@@ -3127,8 +3151,8 @@ async function addReactLib(workspaceRoot, name, options, kindProvided, stack) {
|
|
|
3127
3151
|
}
|
|
3128
3152
|
function addReactInternalLib(workspaceRoot, name, stack) {
|
|
3129
3153
|
ensurePlugin(workspaceRoot, "@nx/react");
|
|
3130
|
-
const projectRoot = (0,
|
|
3131
|
-
const privateManifest = (0,
|
|
3154
|
+
const projectRoot = (0, import_node_path14.join)(workspaceRoot, "libs", name);
|
|
3155
|
+
const privateManifest = (0, import_node_path14.join)(projectRoot, "package.json");
|
|
3132
3156
|
runGeneratorAndRepair(
|
|
3133
3157
|
workspaceRoot,
|
|
3134
3158
|
[
|
|
@@ -3153,12 +3177,12 @@ function addReactInternalLib(workspaceRoot, name, stack) {
|
|
|
3153
3177
|
);
|
|
3154
3178
|
}
|
|
3155
3179
|
|
|
3156
|
-
// src/
|
|
3157
|
-
var
|
|
3180
|
+
// src/dependency-management/sync-dependencies.use-case.ts
|
|
3181
|
+
var import_node_path16 = require("path");
|
|
3158
3182
|
|
|
3159
|
-
// src/
|
|
3160
|
-
var
|
|
3161
|
-
var
|
|
3183
|
+
// src/dependency-management/manifest.repository.ts
|
|
3184
|
+
var import_node_fs11 = require("fs");
|
|
3185
|
+
var import_node_path15 = require("path");
|
|
3162
3186
|
var ECOSYSTEMS = ["npm", "pip", "pub", "nuget", "go"];
|
|
3163
3187
|
var ROOT_LABEL = "(root)";
|
|
3164
3188
|
var MANIFEST_GLOBS = {
|
|
@@ -3191,23 +3215,23 @@ function projectOf(manifestRelativePath) {
|
|
|
3191
3215
|
}
|
|
3192
3216
|
function hasEcosystem(workspaceRoot, ecosystem) {
|
|
3193
3217
|
if (ecosystem === "go") {
|
|
3194
|
-
return fileExists((0,
|
|
3218
|
+
return fileExists((0, import_node_path15.join)(workspaceRoot, "go.mod"));
|
|
3195
3219
|
}
|
|
3196
3220
|
if (ecosystem === "nuget") {
|
|
3197
|
-
return (0,
|
|
3221
|
+
return (0, import_node_fs11.globSync)(MANIFEST_GLOBS.nuget, { cwd: workspaceRoot }).length > 0;
|
|
3198
3222
|
}
|
|
3199
3223
|
const rootMarker = {
|
|
3200
3224
|
npm: "package.json",
|
|
3201
3225
|
pip: "requirements-dev.txt",
|
|
3202
3226
|
pub: "pubspec.yaml"
|
|
3203
3227
|
};
|
|
3204
|
-
return fileExists((0,
|
|
3228
|
+
return fileExists((0, import_node_path15.join)(workspaceRoot, rootMarker[ecosystem])) || (0, import_node_fs11.globSync)(MANIFEST_GLOBS[ecosystem], { cwd: workspaceRoot }).length > 0;
|
|
3205
3229
|
}
|
|
3206
3230
|
function collectNpm(workspaceRoot) {
|
|
3207
3231
|
const sites = [];
|
|
3208
|
-
const relativePaths = ["package.json", ...(0,
|
|
3232
|
+
const relativePaths = ["package.json", ...(0, import_node_fs11.globSync)(MANIFEST_GLOBS.npm, { cwd: workspaceRoot })];
|
|
3209
3233
|
for (const relativePath of relativePaths) {
|
|
3210
|
-
const manifestPath = (0,
|
|
3234
|
+
const manifestPath = (0, import_node_path15.join)(workspaceRoot, relativePath);
|
|
3211
3235
|
try {
|
|
3212
3236
|
const manifest = readJson(manifestPath);
|
|
3213
3237
|
sites.push(...npmManifestSites(manifest, relativePath, manifestPath));
|
|
@@ -3268,17 +3292,17 @@ function collectPip(workspaceRoot) {
|
|
|
3268
3292
|
name: parsed.name,
|
|
3269
3293
|
ecosystem: "pip",
|
|
3270
3294
|
project: projectOf(relativePath),
|
|
3271
|
-
manifestPath: (0,
|
|
3295
|
+
manifestPath: (0, import_node_path15.join)(workspaceRoot, relativePath),
|
|
3272
3296
|
section,
|
|
3273
3297
|
spec: parsed.spec,
|
|
3274
3298
|
rewritable: parsed.rewritable && parsed.spec !== ""
|
|
3275
3299
|
});
|
|
3276
3300
|
};
|
|
3277
|
-
const pyprojectPaths = (0,
|
|
3301
|
+
const pyprojectPaths = (0, import_node_fs11.globSync)(MANIFEST_GLOBS.pip, { cwd: workspaceRoot });
|
|
3278
3302
|
for (const relativePath of pyprojectPaths) {
|
|
3279
3303
|
let content;
|
|
3280
3304
|
try {
|
|
3281
|
-
content = (0,
|
|
3305
|
+
content = (0, import_node_fs11.readFileSync)((0, import_node_path15.join)(workspaceRoot, relativePath), "utf8");
|
|
3282
3306
|
} catch {
|
|
3283
3307
|
continue;
|
|
3284
3308
|
}
|
|
@@ -3289,14 +3313,14 @@ function collectPip(workspaceRoot) {
|
|
|
3289
3313
|
}
|
|
3290
3314
|
const requirementsPaths = [
|
|
3291
3315
|
"requirements-dev.txt",
|
|
3292
|
-
...(0,
|
|
3316
|
+
...(0, import_node_fs11.globSync)("apps/*/requirements.txt", { cwd: workspaceRoot })
|
|
3293
3317
|
];
|
|
3294
3318
|
for (const relativePath of requirementsPaths) {
|
|
3295
|
-
if (!fileExists((0,
|
|
3319
|
+
if (!fileExists((0, import_node_path15.join)(workspaceRoot, relativePath))) {
|
|
3296
3320
|
continue;
|
|
3297
3321
|
}
|
|
3298
3322
|
const section = relativePath === "requirements-dev.txt" ? "devDep" : "dep";
|
|
3299
|
-
const lines = (0,
|
|
3323
|
+
const lines = (0, import_node_fs11.readFileSync)((0, import_node_path15.join)(workspaceRoot, relativePath), "utf8").split(/\r?\n/);
|
|
3300
3324
|
for (const line of lines) {
|
|
3301
3325
|
push(relativePath, section, line);
|
|
3302
3326
|
}
|
|
@@ -3334,11 +3358,11 @@ function pyprojectDependencies(content) {
|
|
|
3334
3358
|
}
|
|
3335
3359
|
function collectPub(workspaceRoot) {
|
|
3336
3360
|
const sites = [];
|
|
3337
|
-
const pubspecPaths = (0,
|
|
3361
|
+
const pubspecPaths = (0, import_node_fs11.globSync)(MANIFEST_GLOBS.pub, { cwd: workspaceRoot });
|
|
3338
3362
|
for (const relativePath of pubspecPaths) {
|
|
3339
3363
|
let content;
|
|
3340
3364
|
try {
|
|
3341
|
-
content = (0,
|
|
3365
|
+
content = (0, import_node_fs11.readFileSync)((0, import_node_path15.join)(workspaceRoot, relativePath), "utf8");
|
|
3342
3366
|
} catch {
|
|
3343
3367
|
continue;
|
|
3344
3368
|
}
|
|
@@ -3348,7 +3372,7 @@ function collectPub(workspaceRoot) {
|
|
|
3348
3372
|
name: entry.name,
|
|
3349
3373
|
ecosystem: "pub",
|
|
3350
3374
|
project: projectOf(relativePath),
|
|
3351
|
-
manifestPath: (0,
|
|
3375
|
+
manifestPath: (0, import_node_path15.join)(workspaceRoot, relativePath),
|
|
3352
3376
|
section: block === "dependencies" ? "dep" : "devDep",
|
|
3353
3377
|
spec: entry.spec,
|
|
3354
3378
|
rewritable: entry.spec !== ""
|
|
@@ -3398,11 +3422,11 @@ function stripQuotes(value) {
|
|
|
3398
3422
|
}
|
|
3399
3423
|
function collectNuget(workspaceRoot) {
|
|
3400
3424
|
const sites = [];
|
|
3401
|
-
const csprojPaths = (0,
|
|
3425
|
+
const csprojPaths = (0, import_node_fs11.globSync)(MANIFEST_GLOBS.nuget, { cwd: workspaceRoot });
|
|
3402
3426
|
for (const relativePath of csprojPaths) {
|
|
3403
3427
|
let content;
|
|
3404
3428
|
try {
|
|
3405
|
-
content = (0,
|
|
3429
|
+
content = (0, import_node_fs11.readFileSync)((0, import_node_path15.join)(workspaceRoot, relativePath), "utf8");
|
|
3406
3430
|
} catch {
|
|
3407
3431
|
continue;
|
|
3408
3432
|
}
|
|
@@ -3411,7 +3435,7 @@ function collectNuget(workspaceRoot) {
|
|
|
3411
3435
|
name: entry.name,
|
|
3412
3436
|
ecosystem: "nuget",
|
|
3413
3437
|
project: projectOf(relativePath),
|
|
3414
|
-
manifestPath: (0,
|
|
3438
|
+
manifestPath: (0, import_node_path15.join)(workspaceRoot, relativePath),
|
|
3415
3439
|
section: "dep",
|
|
3416
3440
|
spec: entry.spec,
|
|
3417
3441
|
// A plain SemVer string is rewritable; an MSBuild property reference
|
|
@@ -3436,13 +3460,13 @@ function csprojPackageReferences(content) {
|
|
|
3436
3460
|
return entries;
|
|
3437
3461
|
}
|
|
3438
3462
|
function collectGo(workspaceRoot) {
|
|
3439
|
-
const manifestPath = (0,
|
|
3463
|
+
const manifestPath = (0, import_node_path15.join)(workspaceRoot, "go.mod");
|
|
3440
3464
|
if (!fileExists(manifestPath)) {
|
|
3441
3465
|
return [];
|
|
3442
3466
|
}
|
|
3443
3467
|
let content;
|
|
3444
3468
|
try {
|
|
3445
|
-
content = (0,
|
|
3469
|
+
content = (0, import_node_fs11.readFileSync)(manifestPath, "utf8");
|
|
3446
3470
|
} catch {
|
|
3447
3471
|
return [];
|
|
3448
3472
|
}
|
|
@@ -3499,7 +3523,7 @@ function rewriteSpec(site, spec) {
|
|
|
3499
3523
|
writeFileEnsured(site.manifestPath, toJson(manifest));
|
|
3500
3524
|
return true;
|
|
3501
3525
|
}
|
|
3502
|
-
const before = (0,
|
|
3526
|
+
const before = (0, import_node_fs11.readFileSync)(site.manifestPath, "utf8");
|
|
3503
3527
|
const after = (() => {
|
|
3504
3528
|
switch (site.ecosystem) {
|
|
3505
3529
|
case "pip": {
|
|
@@ -3555,13 +3579,13 @@ function escapeForRegex(value) {
|
|
|
3555
3579
|
function isWorkspaceProject(workspaceRoot, name) {
|
|
3556
3580
|
const short = name.includes("/") ? name.split("/").at(-1) : name;
|
|
3557
3581
|
return ["apps", "libs", "packages"].some(
|
|
3558
|
-
(directory) => fileExists((0,
|
|
3582
|
+
(directory) => fileExists((0, import_node_path15.join)(workspaceRoot, directory, short ?? name, "package.json"))
|
|
3559
3583
|
);
|
|
3560
3584
|
}
|
|
3561
3585
|
function isAliasedInstall(workspaceRoot, name) {
|
|
3562
3586
|
try {
|
|
3563
3587
|
const installed = readJson(
|
|
3564
|
-
(0,
|
|
3588
|
+
(0, import_node_path15.join)(workspaceRoot, "node_modules", name, "package.json")
|
|
3565
3589
|
);
|
|
3566
3590
|
return installed.name !== void 0 && installed.name !== name;
|
|
3567
3591
|
} catch {
|
|
@@ -3572,1340 +3596,1340 @@ function resolvedVersion(workspaceRoot, ecosystem, name) {
|
|
|
3572
3596
|
if (ecosystem === "npm") {
|
|
3573
3597
|
try {
|
|
3574
3598
|
return readJson(
|
|
3575
|
-
(0,
|
|
3599
|
+
(0, import_node_path15.join)(workspaceRoot, "node_modules", name, "package.json")
|
|
3576
3600
|
).version;
|
|
3577
3601
|
} catch {
|
|
3578
3602
|
return void 0;
|
|
3579
3603
|
}
|
|
3580
3604
|
}
|
|
3581
3605
|
if (ecosystem === "pub") {
|
|
3582
|
-
const lockPath = (0,
|
|
3606
|
+
const lockPath = (0, import_node_path15.join)(workspaceRoot, "pubspec.lock");
|
|
3583
3607
|
if (!fileExists(lockPath)) {
|
|
3584
3608
|
return void 0;
|
|
3585
3609
|
}
|
|
3586
3610
|
try {
|
|
3587
3611
|
const match = new RegExp(
|
|
3588
|
-
String.raw`^\s{2}${escapeForRegex(name)}:[^]*?^\s+version:\s*["']?([^"'\s]+)`,
|
|
3589
|
-
"m"
|
|
3590
|
-
).exec((0,
|
|
3591
|
-
return match?.[1];
|
|
3592
|
-
} catch {
|
|
3593
|
-
return void 0;
|
|
3594
|
-
}
|
|
3595
|
-
}
|
|
3596
|
-
if (ecosystem === "pip") {
|
|
3597
|
-
const python = process.platform === "win32" ? "python" : "python3";
|
|
3598
|
-
const result = runCapture(python, ["-m", "pip", "show", name], workspaceRoot);
|
|
3599
|
-
if (result.status !== 0) {
|
|
3600
|
-
return void 0;
|
|
3601
|
-
}
|
|
3602
|
-
return /^Version:\s*(\S+)/m.exec(result.stdout)?.[1];
|
|
3603
|
-
}
|
|
3604
|
-
return void 0;
|
|
3605
|
-
}
|
|
3606
|
-
|
|
3607
|
-
// src/deps/semver.ts
|
|
3608
|
-
var UPDATE_KINDS = ["patch", "minor", "major", "non-semver"];
|
|
3609
|
-
var UPDATE_KIND_LABELS = {
|
|
3610
|
-
"patch": { title: "Patch Update", blurb: "Backwards-compatible bug fixes." },
|
|
3611
|
-
"minor": { title: "Minor Update", blurb: "New backwards-compatible features." },
|
|
3612
|
-
"major": { title: "Major Update", blurb: "Potentially breaking API changes. Use caution." },
|
|
3613
|
-
"non-semver": { title: "Non-Semver", blurb: "Versions less than 1.0.0, caution." }
|
|
3614
|
-
};
|
|
3615
|
-
var RANGE_OPERATORS = [">=", "<=", "^", "~", ">", "<", "="];
|
|
3616
|
-
function parseVersion(raw) {
|
|
3617
|
-
const match = /^(\d+)\.(\d+)\.(\d+)(?:-([\w.-]+))?/.exec(raw.trim());
|
|
3618
|
-
if (!match) {
|
|
3619
|
-
return void 0;
|
|
3620
|
-
}
|
|
3621
|
-
return {
|
|
3622
|
-
major: Number(match[1]),
|
|
3623
|
-
minor: Number(match[2]),
|
|
3624
|
-
patch: Number(match[3]),
|
|
3625
|
-
prerelease: match[4]
|
|
3626
|
-
};
|
|
3627
|
-
}
|
|
3628
|
-
function rangeOperator(spec) {
|
|
3629
|
-
const trimmed = spec.trim();
|
|
3630
|
-
return RANGE_OPERATORS.find((operator) => trimmed.startsWith(operator)) ?? "";
|
|
3631
|
-
}
|
|
3632
|
-
function specVersion(spec) {
|
|
3633
|
-
const bare2 = spec.trim().slice(rangeOperator(spec).length).trim();
|
|
3634
|
-
return /^\d+\.\d+(?:\.\d+)?(?:-[\w.-]+)?$/.test(bare2) ? bare2 : void 0;
|
|
3635
|
-
}
|
|
3636
|
-
function compareVersions(left, right) {
|
|
3637
|
-
const a = parseVersion(left);
|
|
3638
|
-
const b = parseVersion(right);
|
|
3639
|
-
if (!a || !b) {
|
|
3640
|
-
return 0;
|
|
3641
|
-
}
|
|
3642
|
-
if (a.major !== b.major) {
|
|
3643
|
-
return a.major - b.major;
|
|
3644
|
-
}
|
|
3645
|
-
if (a.minor !== b.minor) {
|
|
3646
|
-
return a.minor - b.minor;
|
|
3647
|
-
}
|
|
3648
|
-
if (a.patch !== b.patch) {
|
|
3649
|
-
return a.patch - b.patch;
|
|
3650
|
-
}
|
|
3651
|
-
if (a.prerelease === b.prerelease) {
|
|
3652
|
-
return 0;
|
|
3653
|
-
}
|
|
3654
|
-
if (!a.prerelease) {
|
|
3655
|
-
return 1;
|
|
3656
|
-
}
|
|
3657
|
-
if (!b.prerelease) {
|
|
3658
|
-
return -1;
|
|
3659
|
-
}
|
|
3660
|
-
return a.prerelease < b.prerelease ? -1 : 1;
|
|
3661
|
-
}
|
|
3662
|
-
function isNewer(candidate, current) {
|
|
3663
|
-
return compareVersions(candidate, current) > 0;
|
|
3664
|
-
}
|
|
3665
|
-
function classify(current, latest) {
|
|
3666
|
-
const from = parseVersion(current);
|
|
3667
|
-
const to = parseVersion(latest);
|
|
3668
|
-
if (!from || !to || from.major === 0) {
|
|
3669
|
-
return "non-semver";
|
|
3670
|
-
}
|
|
3671
|
-
if (from.major !== to.major) {
|
|
3672
|
-
return "major";
|
|
3673
|
-
}
|
|
3674
|
-
return from.minor === to.minor ? "patch" : "minor";
|
|
3675
|
-
}
|
|
3676
|
-
function highestSpec(specs) {
|
|
3677
|
-
let best;
|
|
3678
|
-
let bestVersion;
|
|
3679
|
-
for (const spec of specs) {
|
|
3680
|
-
const version = specVersion(spec);
|
|
3681
|
-
if (!version) {
|
|
3682
|
-
continue;
|
|
3683
|
-
}
|
|
3684
|
-
if (!bestVersion || isNewer(version, bestVersion)) {
|
|
3685
|
-
best = spec;
|
|
3686
|
-
bestVersion = version;
|
|
3687
|
-
}
|
|
3688
|
-
}
|
|
3689
|
-
return best;
|
|
3690
|
-
}
|
|
3691
|
-
|
|
3692
|
-
// src/commands/sync.ts
|
|
3693
|
-
function resolveEcosystems(requested) {
|
|
3694
|
-
if (!requested) {
|
|
3695
|
-
return ECOSYSTEMS;
|
|
3696
|
-
}
|
|
3697
|
-
const match = ECOSYSTEMS.find((ecosystem) => ecosystem === requested);
|
|
3698
|
-
if (!match) {
|
|
3699
|
-
throw new Error(
|
|
3700
|
-
`Unknown ecosystem '${requested}'. Expected one of: ${ECOSYSTEMS.join(", ")}.`
|
|
3701
|
-
);
|
|
3702
|
-
}
|
|
3703
|
-
return [match];
|
|
3704
|
-
}
|
|
3705
|
-
function collectDrift(workspaceRoot, ecosystems = ECOSYSTEMS) {
|
|
3706
|
-
const reconcilable = ecosystems.filter((ecosystem) => ecosystem !== "go");
|
|
3707
|
-
const inventory = collectInventory(workspaceRoot, reconcilable);
|
|
3708
|
-
const drift = [];
|
|
3709
|
-
for (const [name, allSites] of inventory) {
|
|
3710
|
-
const sites = allSites.filter((site) => site.section !== PEER_SECTION);
|
|
3711
|
-
if (sites.length < 2 || isWorkspaceLocal(workspaceRoot, sites, name)) {
|
|
3712
|
-
continue;
|
|
3713
|
-
}
|
|
3714
|
-
const distinct = new Set(sites.map((site) => site.spec));
|
|
3715
|
-
if (distinct.size < 2) {
|
|
3716
|
-
continue;
|
|
3717
|
-
}
|
|
3718
|
-
const installed = resolvedVersion(workspaceRoot, sites[0].ecosystem, name);
|
|
3719
|
-
const target = chooseTarget(sites, installed);
|
|
3720
|
-
if (!target) {
|
|
3721
|
-
continue;
|
|
3722
|
-
}
|
|
3723
|
-
const disagreeing = sites.filter((site) => site.spec !== target);
|
|
3724
|
-
drift.push({
|
|
3725
|
-
name,
|
|
3726
|
-
ecosystem: sites[0].ecosystem,
|
|
3727
|
-
target,
|
|
3728
|
-
reason: installed ? "resolved" : "highest",
|
|
3729
|
-
fixable: disagreeing.filter((site) => site.rewritable),
|
|
3730
|
-
blocked: disagreeing.filter((site) => !site.rewritable)
|
|
3731
|
-
});
|
|
3732
|
-
}
|
|
3733
|
-
return drift.toSorted((left, right) => left.name.localeCompare(right.name));
|
|
3734
|
-
}
|
|
3735
|
-
function isWorkspaceLocal(workspaceRoot, sites, name) {
|
|
3736
|
-
return sites[0].ecosystem === "npm" && isWorkspaceProject(workspaceRoot, name);
|
|
3737
|
-
}
|
|
3738
|
-
function chooseTarget(sites, installed) {
|
|
3739
|
-
if (!installed) {
|
|
3740
|
-
return highestSpec(sites.map((site) => site.spec));
|
|
3741
|
-
}
|
|
3742
|
-
const counts = /* @__PURE__ */ new Map();
|
|
3743
|
-
for (const site of sites) {
|
|
3744
|
-
if (!specVersion(site.spec)) {
|
|
3745
|
-
continue;
|
|
3746
|
-
}
|
|
3747
|
-
const operator2 = rangeOperator(site.spec);
|
|
3748
|
-
counts.set(operator2, (counts.get(operator2) ?? 0) + 1);
|
|
3749
|
-
}
|
|
3750
|
-
if (counts.size === 0) {
|
|
3751
|
-
return void 0;
|
|
3752
|
-
}
|
|
3753
|
-
const [operator] = [...counts].toSorted(
|
|
3754
|
-
(left, right) => right[1] - left[1] || (left[0] === "^" ? -1 : 1)
|
|
3755
|
-
)[0];
|
|
3756
|
-
return `${operator}${installed}`;
|
|
3757
|
-
}
|
|
3758
|
-
function applyDrift(drift) {
|
|
3759
|
-
const changed = /* @__PURE__ */ new Set();
|
|
3760
|
-
for (const finding of drift) {
|
|
3761
|
-
for (const site of finding.fixable) {
|
|
3762
|
-
if (rewriteSpec(site, finding.target)) {
|
|
3763
|
-
changed.add(site.manifestPath);
|
|
3764
|
-
}
|
|
3612
|
+
String.raw`^\s{2}${escapeForRegex(name)}:[^]*?^\s+version:\s*["']?([^"'\s]+)`,
|
|
3613
|
+
"m"
|
|
3614
|
+
).exec((0, import_node_fs11.readFileSync)(lockPath, "utf8"));
|
|
3615
|
+
return match?.[1];
|
|
3616
|
+
} catch {
|
|
3617
|
+
return void 0;
|
|
3765
3618
|
}
|
|
3766
3619
|
}
|
|
3767
|
-
|
|
3620
|
+
if (ecosystem === "pip") {
|
|
3621
|
+
const python = process.platform === "win32" ? "python" : "python3";
|
|
3622
|
+
const result = runCapture(python, ["-m", "pip", "show", name], workspaceRoot);
|
|
3623
|
+
if (result.status !== 0) {
|
|
3624
|
+
return void 0;
|
|
3625
|
+
}
|
|
3626
|
+
return /^Version:\s*(\S+)/m.exec(result.stdout)?.[1];
|
|
3627
|
+
}
|
|
3628
|
+
return void 0;
|
|
3768
3629
|
}
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3630
|
+
|
|
3631
|
+
// src/dependency-management/semver.algorithm.ts
|
|
3632
|
+
var UPDATE_KINDS = ["patch", "minor", "major", "non-semver"];
|
|
3633
|
+
var UPDATE_KIND_LABELS = {
|
|
3634
|
+
"patch": { title: "Patch Update", blurb: "Backwards-compatible bug fixes." },
|
|
3635
|
+
"minor": { title: "Minor Update", blurb: "New backwards-compatible features." },
|
|
3636
|
+
"major": { title: "Major Update", blurb: "Potentially breaking API changes. Use caution." },
|
|
3637
|
+
"non-semver": { title: "Non-Semver", blurb: "Versions less than 1.0.0, caution." }
|
|
3638
|
+
};
|
|
3639
|
+
var RANGE_OPERATORS = [">=", "<=", "^", "~", ">", "<", "="];
|
|
3640
|
+
function parseVersion(raw) {
|
|
3641
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:-([\w.-]+))?/.exec(raw.trim());
|
|
3642
|
+
if (!match) {
|
|
3643
|
+
return void 0;
|
|
3775
3644
|
}
|
|
3645
|
+
return {
|
|
3646
|
+
major: Number(match[1]),
|
|
3647
|
+
minor: Number(match[2]),
|
|
3648
|
+
patch: Number(match[3]),
|
|
3649
|
+
prerelease: match[4]
|
|
3650
|
+
};
|
|
3776
3651
|
}
|
|
3777
|
-
function
|
|
3778
|
-
const
|
|
3779
|
-
return
|
|
3652
|
+
function rangeOperator(spec) {
|
|
3653
|
+
const trimmed = spec.trim();
|
|
3654
|
+
return RANGE_OPERATORS.find((operator) => trimmed.startsWith(operator)) ?? "";
|
|
3780
3655
|
}
|
|
3781
|
-
function
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
const
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
logger.info(`\u2298 SKIPPED ${ecosystem} \u2014 no ${ecosystem} project in this workspace.`);
|
|
3791
|
-
}
|
|
3656
|
+
function specVersion(spec) {
|
|
3657
|
+
const bare2 = spec.trim().slice(rangeOperator(spec).length).trim();
|
|
3658
|
+
return /^\d+\.\d+(?:\.\d+)?(?:-[\w.-]+)?$/.test(bare2) ? bare2 : void 0;
|
|
3659
|
+
}
|
|
3660
|
+
function compareVersions(left, right) {
|
|
3661
|
+
const a = parseVersion(left);
|
|
3662
|
+
const b = parseVersion(right);
|
|
3663
|
+
if (!a || !b) {
|
|
3664
|
+
return 0;
|
|
3792
3665
|
}
|
|
3793
|
-
if (
|
|
3794
|
-
|
|
3666
|
+
if (a.major !== b.major) {
|
|
3667
|
+
return a.major - b.major;
|
|
3795
3668
|
}
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
logger.success("Every project agrees on every shared dependency version.");
|
|
3799
|
-
} else {
|
|
3800
|
-
for (const finding of drift) {
|
|
3801
|
-
logger.info(` ${describe(finding)}`);
|
|
3802
|
-
}
|
|
3669
|
+
if (a.minor !== b.minor) {
|
|
3670
|
+
return a.minor - b.minor;
|
|
3803
3671
|
}
|
|
3804
|
-
if (
|
|
3805
|
-
|
|
3806
|
-
logger.error(
|
|
3807
|
-
`${drift.length} package(s) declared at more than one version. Run 'mnci sync' to converge them.`
|
|
3808
|
-
);
|
|
3809
|
-
process.exitCode = 1;
|
|
3810
|
-
}
|
|
3811
|
-
return;
|
|
3672
|
+
if (a.patch !== b.patch) {
|
|
3673
|
+
return a.patch - b.patch;
|
|
3812
3674
|
}
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
logger.detail(`updated ${path}`);
|
|
3675
|
+
if (a.prerelease === b.prerelease) {
|
|
3676
|
+
return 0;
|
|
3816
3677
|
}
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
logger.warn(
|
|
3820
|
-
`${site.name} in ${site.project} is declared as '${site.spec}', which is not a plain version range \u2014 left unchanged.`
|
|
3821
|
-
);
|
|
3678
|
+
if (!a.prerelease) {
|
|
3679
|
+
return 1;
|
|
3822
3680
|
}
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
logger.step("Formatting the changed manifests (eslint --fix)");
|
|
3826
|
-
runFormatter(workspaceRoot);
|
|
3681
|
+
if (!b.prerelease) {
|
|
3682
|
+
return -1;
|
|
3827
3683
|
}
|
|
3828
|
-
|
|
3684
|
+
return a.prerelease < b.prerelease ? -1 : 1;
|
|
3829
3685
|
}
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
"npm-lib",
|
|
3839
|
-
"internal-lib",
|
|
3840
|
-
"python-app",
|
|
3841
|
-
"python-function-app",
|
|
3842
|
-
"python-lib",
|
|
3843
|
-
"python-internal-lib",
|
|
3844
|
-
"python-vendor",
|
|
3845
|
-
"go-app",
|
|
3846
|
-
"go-function-app",
|
|
3847
|
-
"go-lib",
|
|
3848
|
-
"go-internal-lib",
|
|
3849
|
-
"flutter-app",
|
|
3850
|
-
"flutter-lib",
|
|
3851
|
-
"flutter-internal-lib",
|
|
3852
|
-
"csharp-app",
|
|
3853
|
-
"csharp-function-app",
|
|
3854
|
-
"csharp-lib",
|
|
3855
|
-
"csharp-internal-lib"
|
|
3856
|
-
];
|
|
3857
|
-
async function runAdd(kind, name, options) {
|
|
3858
|
-
const workspaceRoot = process.cwd();
|
|
3859
|
-
if (!fileExists((0, import_node_path14.join)(workspaceRoot, "nx.json"))) {
|
|
3860
|
-
throw new Error("No nx.json found here. Run `add` from the workspace root.");
|
|
3686
|
+
function isNewer(candidate, current) {
|
|
3687
|
+
return compareVersions(candidate, current) > 0;
|
|
3688
|
+
}
|
|
3689
|
+
function classify(current, latest) {
|
|
3690
|
+
const from = parseVersion(current);
|
|
3691
|
+
const to = parseVersion(latest);
|
|
3692
|
+
if (!from || !to || from.major === 0) {
|
|
3693
|
+
return "non-semver";
|
|
3861
3694
|
}
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
const
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
break;
|
|
3875
|
-
}
|
|
3876
|
-
case "react-lib": {
|
|
3877
|
-
await addReactLib(workspaceRoot, resolvedName, options, kindProvided, stack);
|
|
3878
|
-
break;
|
|
3879
|
-
}
|
|
3880
|
-
case "react-internal-lib": {
|
|
3881
|
-
addReactInternalLib(workspaceRoot, resolvedName, stack);
|
|
3882
|
-
break;
|
|
3883
|
-
}
|
|
3884
|
-
case "node-app": {
|
|
3885
|
-
addNodeApp(workspaceRoot, resolvedName, stack, options.framework);
|
|
3886
|
-
break;
|
|
3887
|
-
}
|
|
3888
|
-
case "node-function-app": {
|
|
3889
|
-
addNodeFunctionApp(workspaceRoot, resolvedName, stack);
|
|
3890
|
-
break;
|
|
3891
|
-
}
|
|
3892
|
-
case "npm-lib": {
|
|
3893
|
-
await addNpmLib(workspaceRoot, resolvedName, options, kindProvided, stack);
|
|
3894
|
-
break;
|
|
3895
|
-
}
|
|
3896
|
-
case "internal-lib": {
|
|
3897
|
-
runNx(
|
|
3898
|
-
[
|
|
3899
|
-
"g",
|
|
3900
|
-
"@nx/js:lib",
|
|
3901
|
-
`libs/${resolvedName}`,
|
|
3902
|
-
"--bundler=tsc",
|
|
3903
|
-
`--unitTestRunner=${stack.testRunner}`,
|
|
3904
|
-
"--linter=none",
|
|
3905
|
-
"--no-interactive"
|
|
3906
|
-
],
|
|
3907
|
-
workspaceRoot
|
|
3908
|
-
);
|
|
3909
|
-
markPrivate((0, import_node_path14.join)(workspaceRoot, "libs", resolvedName, "package.json"));
|
|
3910
|
-
removeGeneratedEslintConfig(workspaceRoot, `libs/${resolvedName}`);
|
|
3911
|
-
registerProjectCommands(workspaceRoot, resolvedName, { build: true });
|
|
3912
|
-
break;
|
|
3913
|
-
}
|
|
3914
|
-
case "python-app": {
|
|
3915
|
-
addPythonApp(workspaceRoot, resolvedName);
|
|
3916
|
-
break;
|
|
3917
|
-
}
|
|
3918
|
-
case "python-function-app": {
|
|
3919
|
-
addPythonFunctionApp(workspaceRoot, resolvedName);
|
|
3920
|
-
break;
|
|
3921
|
-
}
|
|
3922
|
-
case "python-lib": {
|
|
3923
|
-
addPythonLib(workspaceRoot, resolvedName);
|
|
3924
|
-
break;
|
|
3925
|
-
}
|
|
3926
|
-
case "python-internal-lib": {
|
|
3927
|
-
addPythonInternalLib(workspaceRoot, resolvedName);
|
|
3928
|
-
break;
|
|
3929
|
-
}
|
|
3930
|
-
case "go-app": {
|
|
3931
|
-
addGoApp(workspaceRoot, resolvedName);
|
|
3932
|
-
break;
|
|
3933
|
-
}
|
|
3934
|
-
case "go-function-app": {
|
|
3935
|
-
addGoFunctionApp(workspaceRoot, resolvedName);
|
|
3936
|
-
break;
|
|
3937
|
-
}
|
|
3938
|
-
case "go-lib": {
|
|
3939
|
-
addGoLib(workspaceRoot, resolvedName);
|
|
3940
|
-
break;
|
|
3941
|
-
}
|
|
3942
|
-
case "go-internal-lib": {
|
|
3943
|
-
addGoInternalLib(workspaceRoot, resolvedName);
|
|
3944
|
-
break;
|
|
3945
|
-
}
|
|
3946
|
-
case "flutter-app": {
|
|
3947
|
-
addFlutterApp(workspaceRoot, resolvedName);
|
|
3948
|
-
break;
|
|
3949
|
-
}
|
|
3950
|
-
case "flutter-lib": {
|
|
3951
|
-
addFlutterLib(workspaceRoot, resolvedName);
|
|
3952
|
-
break;
|
|
3953
|
-
}
|
|
3954
|
-
case "flutter-internal-lib": {
|
|
3955
|
-
addFlutterInternalLib(workspaceRoot, resolvedName);
|
|
3956
|
-
break;
|
|
3957
|
-
}
|
|
3958
|
-
case "csharp-app": {
|
|
3959
|
-
addCsharpApp(workspaceRoot, resolvedName);
|
|
3960
|
-
break;
|
|
3961
|
-
}
|
|
3962
|
-
case "csharp-function-app": {
|
|
3963
|
-
addCsharpFunctionApp(workspaceRoot, resolvedName);
|
|
3964
|
-
break;
|
|
3695
|
+
if (from.major !== to.major) {
|
|
3696
|
+
return "major";
|
|
3697
|
+
}
|
|
3698
|
+
return from.minor === to.minor ? "patch" : "minor";
|
|
3699
|
+
}
|
|
3700
|
+
function highestSpec(specs) {
|
|
3701
|
+
let best;
|
|
3702
|
+
let bestVersion;
|
|
3703
|
+
for (const spec of specs) {
|
|
3704
|
+
const version = specVersion(spec);
|
|
3705
|
+
if (!version) {
|
|
3706
|
+
continue;
|
|
3965
3707
|
}
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3708
|
+
if (!bestVersion || isNewer(version, bestVersion)) {
|
|
3709
|
+
best = spec;
|
|
3710
|
+
bestVersion = version;
|
|
3969
3711
|
}
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3712
|
+
}
|
|
3713
|
+
return best;
|
|
3714
|
+
}
|
|
3715
|
+
|
|
3716
|
+
// src/dependency-management/sync-dependencies.use-case.ts
|
|
3717
|
+
function resolveEcosystems(requested) {
|
|
3718
|
+
if (!requested) {
|
|
3719
|
+
return ECOSYSTEMS;
|
|
3720
|
+
}
|
|
3721
|
+
const match = ECOSYSTEMS.find((ecosystem) => ecosystem === requested);
|
|
3722
|
+
if (!match) {
|
|
3723
|
+
throw new Error(
|
|
3724
|
+
`Unknown ecosystem '${requested}'. Expected one of: ${ECOSYSTEMS.join(", ")}.`
|
|
3725
|
+
);
|
|
3726
|
+
}
|
|
3727
|
+
return [match];
|
|
3728
|
+
}
|
|
3729
|
+
function collectDrift(workspaceRoot, ecosystems = ECOSYSTEMS) {
|
|
3730
|
+
const reconcilable = ecosystems.filter((ecosystem) => ecosystem !== "go");
|
|
3731
|
+
const inventory = collectInventory(workspaceRoot, reconcilable);
|
|
3732
|
+
const drift = [];
|
|
3733
|
+
for (const [name, allSites] of inventory) {
|
|
3734
|
+
const sites = allSites.filter((site) => site.section !== PEER_SECTION);
|
|
3735
|
+
if (sites.length < 2 || isWorkspaceLocal(workspaceRoot, sites, name)) {
|
|
3736
|
+
continue;
|
|
3973
3737
|
}
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
runFormatter(workspaceRoot);
|
|
3978
|
-
return;
|
|
3738
|
+
const distinct = new Set(sites.map((site) => site.spec));
|
|
3739
|
+
if (distinct.size < 2) {
|
|
3740
|
+
continue;
|
|
3979
3741
|
}
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
);
|
|
3742
|
+
const installed = resolvedVersion(workspaceRoot, sites[0].ecosystem, name);
|
|
3743
|
+
const target = chooseTarget(sites, installed);
|
|
3744
|
+
if (!target) {
|
|
3745
|
+
continue;
|
|
3985
3746
|
}
|
|
3747
|
+
const disagreeing = sites.filter((site) => site.spec !== target);
|
|
3748
|
+
drift.push({
|
|
3749
|
+
name,
|
|
3750
|
+
ecosystem: sites[0].ecosystem,
|
|
3751
|
+
target,
|
|
3752
|
+
reason: installed ? "resolved" : "highest",
|
|
3753
|
+
fixable: disagreeing.filter((site) => site.rewritable),
|
|
3754
|
+
blocked: disagreeing.filter((site) => !site.rewritable)
|
|
3755
|
+
});
|
|
3986
3756
|
}
|
|
3987
|
-
|
|
3988
|
-
syncProjectReferences(workspaceRoot);
|
|
3989
|
-
runFormatter(workspaceRoot);
|
|
3990
|
-
logger.success(`Added ${resolvedKind} '${resolvedName}'.`);
|
|
3991
|
-
}
|
|
3992
|
-
function readWorkspaceStack(workspaceRoot) {
|
|
3993
|
-
const nxJson = readJson(
|
|
3994
|
-
(0, import_node_path14.join)(workspaceRoot, "nx.json")
|
|
3995
|
-
);
|
|
3996
|
-
const stack = nxJson.mnci?.stack;
|
|
3997
|
-
return {
|
|
3998
|
-
testRunner: stack?.testRunner === "vitest" ? "vitest" : "jest"
|
|
3999
|
-
};
|
|
3757
|
+
return drift.toSorted((left, right) => left.name.localeCompare(right.name));
|
|
4000
3758
|
}
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
var import_node_fs9 = require("fs");
|
|
4004
|
-
var import_node_path15 = require("path");
|
|
4005
|
-
var SUPPORTED_ESLINT_MAJOR = ESLINT_VERSION.replace(/^\D*/, "").split(".", 1)[0];
|
|
4006
|
-
function toPosix2(path) {
|
|
4007
|
-
return path.replaceAll("\\", "/");
|
|
3759
|
+
function isWorkspaceLocal(workspaceRoot, sites, name) {
|
|
3760
|
+
return sites[0].ecosystem === "npm" && isWorkspaceProject(workspaceRoot, name);
|
|
4008
3761
|
}
|
|
4009
|
-
function
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
{
|
|
4016
|
-
|
|
4017
|
-
ok: rootConfigs.length === 1,
|
|
4018
|
-
detail: rootConfigs.length === 0 ? "no eslint.config.* at the workspace root" : `${rootConfigs.length} root configs: ${rootConfigs.join(", ")}`,
|
|
4019
|
-
remedy: "run `mnci upgrade` to rewrite the root config"
|
|
4020
|
-
},
|
|
4021
|
-
{
|
|
4022
|
-
check: "no per-project ESLint configs",
|
|
4023
|
-
ok: projectConfigs.length === 0,
|
|
4024
|
-
detail: `found ${projectConfigs.length}: ${projectConfigs.join(", ")}`,
|
|
4025
|
-
remedy: "run `mnci upgrade`, which sweeps {apps,libs,packages}/*/eslint.config.*"
|
|
3762
|
+
function chooseTarget(sites, installed) {
|
|
3763
|
+
if (!installed) {
|
|
3764
|
+
return highestSpec(sites.map((site) => site.spec));
|
|
3765
|
+
}
|
|
3766
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3767
|
+
for (const site of sites) {
|
|
3768
|
+
if (!specVersion(site.spec)) {
|
|
3769
|
+
continue;
|
|
4026
3770
|
}
|
|
4027
|
-
|
|
3771
|
+
const operator2 = rangeOperator(site.spec);
|
|
3772
|
+
counts.set(operator2, (counts.get(operator2) ?? 0) + 1);
|
|
3773
|
+
}
|
|
3774
|
+
if (counts.size === 0) {
|
|
3775
|
+
return void 0;
|
|
3776
|
+
}
|
|
3777
|
+
const [operator] = [...counts].toSorted(
|
|
3778
|
+
(left, right) => right[1] - left[1] || (left[0] === "^" ? -1 : 1)
|
|
3779
|
+
)[0];
|
|
3780
|
+
return `${operator}${installed}`;
|
|
4028
3781
|
}
|
|
4029
|
-
function
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
3782
|
+
function applyDrift(drift) {
|
|
3783
|
+
const changed = /* @__PURE__ */ new Set();
|
|
3784
|
+
for (const finding of drift) {
|
|
3785
|
+
for (const site of finding.fixable) {
|
|
3786
|
+
if (rewriteSpec(site, finding.target)) {
|
|
3787
|
+
changed.add(site.manifestPath);
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
4037
3790
|
}
|
|
3791
|
+
return [...changed];
|
|
4038
3792
|
}
|
|
4039
|
-
function
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
3793
|
+
function syncProjectReferences(workspaceRoot) {
|
|
3794
|
+
logger.step("Syncing TypeScript project references (nx sync)");
|
|
3795
|
+
if (runShell("npx", ["nx", "sync"], workspaceRoot) !== 0) {
|
|
3796
|
+
logger.warn(
|
|
3797
|
+
"nx sync did not complete \u2014 run `npx nx sync` yourself so cross-project imports resolve in your editor."
|
|
3798
|
+
);
|
|
4045
3799
|
}
|
|
4046
|
-
return {
|
|
4047
|
-
check: "no retired formatter is still configured",
|
|
4048
|
-
ok: false,
|
|
4049
|
-
detail: `Found: ${stale.join(", ")}. These no longer run, but an editor extension still resolves them and will reformat on save against an opinion no gate checks.`,
|
|
4050
|
-
remedy: "Run 'mnci upgrade' to remove them."
|
|
4051
|
-
};
|
|
4052
3800
|
}
|
|
4053
|
-
function
|
|
4054
|
-
const
|
|
4055
|
-
|
|
4056
|
-
(entry) => (typeof entry === "string" ? entry : entry.plugin) === "@nx/eslint/plugin"
|
|
4057
|
-
);
|
|
4058
|
-
return {
|
|
4059
|
-
check: "@nx/eslint/plugin registered",
|
|
4060
|
-
ok: registered,
|
|
4061
|
-
detail: "not in nx.json plugins \u2014 every project silently loses its lint target",
|
|
4062
|
-
remedy: "run `mnci upgrade`"
|
|
4063
|
-
};
|
|
3801
|
+
function describe(finding) {
|
|
3802
|
+
const sites = [...finding.fixable, ...finding.blocked].map((site) => `${site.project === ROOT_LABEL ? ROOT_LABEL : site.project} ${site.spec}`).join(", ");
|
|
3803
|
+
return `${finding.name} \u2192 ${finding.target} (${finding.reason}); disagreeing: ${sites}`;
|
|
4064
3804
|
}
|
|
4065
|
-
function
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
3805
|
+
function runSync(workspaceRoot, options) {
|
|
3806
|
+
if (!fileExists((0, import_node_path16.join)(workspaceRoot, "nx.json"))) {
|
|
3807
|
+
throw new Error(
|
|
3808
|
+
`No nx.json found in ${workspaceRoot} \u2014 run 'mnci sync' from the workspace root.`
|
|
3809
|
+
);
|
|
4069
3810
|
}
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
ok: major === SUPPORTED_ESLINT_MAJOR,
|
|
4076
|
-
detail: `node_modules/eslint is ${version}, but this stack supports ${SUPPORTED_ESLINT_MAJOR}.x (eslint-plugin-react has no release beyond it)`,
|
|
4077
|
-
remedy: `pin eslint to ${ESLINT_VERSION} in every package manifest, then reinstall`
|
|
4078
|
-
};
|
|
4079
|
-
} catch {
|
|
4080
|
-
return void 0;
|
|
3811
|
+
const ecosystems = resolveEcosystems(options.ecosystem);
|
|
3812
|
+
for (const ecosystem of ecosystems) {
|
|
3813
|
+
if (!hasEcosystem(workspaceRoot, ecosystem)) {
|
|
3814
|
+
logger.info(`\u2298 SKIPPED ${ecosystem} \u2014 no ${ecosystem} project in this workspace.`);
|
|
3815
|
+
}
|
|
4081
3816
|
}
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
const npmrcPath = (0, import_node_path15.join)(workspaceRoot, ".npmrc");
|
|
4085
|
-
if (!registry || !fileExists(npmrcPath)) {
|
|
4086
|
-
return void 0;
|
|
3817
|
+
if (ecosystems.includes("go") && hasEcosystem(workspaceRoot, "go")) {
|
|
3818
|
+
logger.success("go \u2014 nothing to sync; one root go.mod means one version of every module.");
|
|
4087
3819
|
}
|
|
4088
|
-
const
|
|
4089
|
-
if (
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
};
|
|
3820
|
+
const drift = collectDrift(workspaceRoot, ecosystems);
|
|
3821
|
+
if (drift.length === 0) {
|
|
3822
|
+
logger.success("Every project agrees on every shared dependency version.");
|
|
3823
|
+
} else {
|
|
3824
|
+
for (const finding of drift) {
|
|
3825
|
+
logger.info(` ${describe(finding)}`);
|
|
3826
|
+
}
|
|
4096
3827
|
}
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
3828
|
+
if (options.check) {
|
|
3829
|
+
if (drift.length > 0) {
|
|
3830
|
+
logger.error(
|
|
3831
|
+
`${drift.length} package(s) declared at more than one version. Run 'mnci sync' to converge them.`
|
|
3832
|
+
);
|
|
3833
|
+
process.exitCode = 1;
|
|
3834
|
+
}
|
|
3835
|
+
return;
|
|
3836
|
+
}
|
|
3837
|
+
const changed = applyDrift(drift);
|
|
3838
|
+
for (const path of changed) {
|
|
3839
|
+
logger.detail(`updated ${path}`);
|
|
3840
|
+
}
|
|
3841
|
+
const blocked = drift.flatMap((finding) => finding.blocked);
|
|
3842
|
+
for (const site of blocked) {
|
|
3843
|
+
logger.warn(
|
|
3844
|
+
`${site.name} in ${site.project} is declared as '${site.spec}', which is not a plain version range \u2014 left unchanged.`
|
|
3845
|
+
);
|
|
3846
|
+
}
|
|
3847
|
+
syncProjectReferences(workspaceRoot);
|
|
3848
|
+
if (changed.length > 0) {
|
|
3849
|
+
logger.step("Formatting the changed manifests (eslint --fix)");
|
|
3850
|
+
runFormatter(workspaceRoot);
|
|
3851
|
+
}
|
|
3852
|
+
logger.success("Done. Review the changes with `git diff` before committing.");
|
|
4103
3853
|
}
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
3854
|
+
|
|
3855
|
+
// src/dependency-management/update-dependencies.use-case.ts
|
|
3856
|
+
var import_node_path17 = require("path");
|
|
3857
|
+
var import_prompts2 = require("@inquirer/prompts");
|
|
3858
|
+
|
|
3859
|
+
// src/dependency-management/registry.client.ts
|
|
3860
|
+
var LOOKUP_CONCURRENCY = 8;
|
|
3861
|
+
async function latestNpmVersions(names, cwd) {
|
|
3862
|
+
const results = await pool(names, LOOKUP_CONCURRENCY, async (name) => {
|
|
3863
|
+
const result = await runCaptureAsync("npm", ["view", name, "version", "--json"], cwd);
|
|
3864
|
+
if (result.status !== 0) {
|
|
3865
|
+
return;
|
|
3866
|
+
}
|
|
4114
3867
|
try {
|
|
4115
|
-
const
|
|
4116
|
-
|
|
3868
|
+
const parsed = JSON.parse(result.stdout);
|
|
3869
|
+
const version = Array.isArray(parsed) ? parsed.at(-1) : parsed;
|
|
3870
|
+
return typeof version === "string" ? [name, version] : void 0;
|
|
4117
3871
|
} catch {
|
|
3872
|
+
return;
|
|
4118
3873
|
}
|
|
4119
|
-
return [
|
|
4120
|
-
{
|
|
4121
|
-
check: `${projectRoot} keeps its versionActions override`,
|
|
4122
|
-
ok: hasOverride,
|
|
4123
|
-
detail: "missing \u2014 nx release aborts for the ENTIRE workspace, not just this project",
|
|
4124
|
-
remedy: `add release.version.versionActions to ${projectRoot}/project.json`
|
|
4125
|
-
}
|
|
4126
|
-
];
|
|
4127
3874
|
});
|
|
3875
|
+
return new Map(results.filter((entry) => entry !== void 0));
|
|
4128
3876
|
}
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
const
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
]
|
|
4138
|
-
|
|
3877
|
+
async function latestPipVersions(names, cwd) {
|
|
3878
|
+
const python = process.platform === "win32" ? "python" : "python3";
|
|
3879
|
+
const results = await pool(names, LOOKUP_CONCURRENCY, async (name) => {
|
|
3880
|
+
const result = await runCaptureAsync(python, ["-m", "pip", "index", "versions", name], cwd);
|
|
3881
|
+
if (result.status !== 0) {
|
|
3882
|
+
return;
|
|
3883
|
+
}
|
|
3884
|
+
const match = /\(([^)]+)\)/.exec(result.stdout);
|
|
3885
|
+
return match ? [name, match[1]] : void 0;
|
|
3886
|
+
});
|
|
3887
|
+
return new Map(results.filter((entry) => entry !== void 0));
|
|
3888
|
+
}
|
|
3889
|
+
async function latestGoVersions(cwd) {
|
|
3890
|
+
const result = await runCaptureAsync("go", ["list", "-m", "-u", "-json", "all"], cwd);
|
|
3891
|
+
if (result.status !== 0) {
|
|
3892
|
+
return /* @__PURE__ */ new Map();
|
|
3893
|
+
}
|
|
3894
|
+
const latest = /* @__PURE__ */ new Map();
|
|
3895
|
+
for (const chunk of result.stdout.split(/^\}\s*$/m)) {
|
|
3896
|
+
const text = `${chunk.trim()}${chunk.trim() ? "}" : ""}`;
|
|
3897
|
+
if (!text.startsWith("{")) {
|
|
3898
|
+
continue;
|
|
3899
|
+
}
|
|
4139
3900
|
try {
|
|
4140
|
-
const
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
if (targets && typeof targets === "object") {
|
|
4144
|
-
sources.push({
|
|
4145
|
-
source: `${projectRoot}/${file}`,
|
|
4146
|
-
targets
|
|
4147
|
-
});
|
|
3901
|
+
const module_ = JSON.parse(text);
|
|
3902
|
+
if (module_.Path && module_.Update?.Version) {
|
|
3903
|
+
latest.set(module_.Path, module_.Update.Version);
|
|
4148
3904
|
}
|
|
4149
3905
|
} catch {
|
|
4150
3906
|
}
|
|
4151
3907
|
}
|
|
4152
|
-
return
|
|
3908
|
+
return latest;
|
|
4153
3909
|
}
|
|
4154
|
-
function
|
|
4155
|
-
const
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
3910
|
+
async function latestPubVersions(cwd) {
|
|
3911
|
+
const result = await runCaptureAsync("flutter", ["pub", "outdated", "--json"], cwd);
|
|
3912
|
+
if (result.status !== 0) {
|
|
3913
|
+
return /* @__PURE__ */ new Map();
|
|
3914
|
+
}
|
|
3915
|
+
try {
|
|
3916
|
+
const report = JSON.parse(result.stdout);
|
|
3917
|
+
const latest = /* @__PURE__ */ new Map();
|
|
3918
|
+
const packages = report.packages ?? [];
|
|
3919
|
+
for (const entry of packages) {
|
|
3920
|
+
if (entry.package && entry.latest?.version) {
|
|
3921
|
+
latest.set(entry.package, entry.latest.version);
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3924
|
+
return latest;
|
|
3925
|
+
} catch {
|
|
3926
|
+
return /* @__PURE__ */ new Map();
|
|
3927
|
+
}
|
|
4168
3928
|
}
|
|
4169
|
-
function
|
|
4170
|
-
|
|
4171
|
-
const
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
3929
|
+
async function latestNugetVersions(names, cwd) {
|
|
3930
|
+
const results = await pool(names, LOOKUP_CONCURRENCY, async (name) => {
|
|
3931
|
+
const result = await runCaptureAsync(
|
|
3932
|
+
"dotnet",
|
|
3933
|
+
["package", "search", name, "--exact-match", "--format", "json"],
|
|
3934
|
+
cwd
|
|
3935
|
+
);
|
|
3936
|
+
if (result.status !== 0) {
|
|
3937
|
+
return;
|
|
3938
|
+
}
|
|
3939
|
+
try {
|
|
3940
|
+
const parsed = JSON.parse(result.stdout);
|
|
3941
|
+
const packages = parsed.searchResult?.flatMap((source) => source.packages ?? []) ?? [];
|
|
3942
|
+
const match = packages.find((entry) => entry.id?.toLowerCase() === name.toLowerCase()) ?? packages[0];
|
|
3943
|
+
return match?.latestVersion ? [name, match.latestVersion] : void 0;
|
|
3944
|
+
} catch {
|
|
3945
|
+
return;
|
|
4175
3946
|
}
|
|
4176
|
-
return [
|
|
4177
|
-
{
|
|
4178
|
-
check: `${source} \u2192 ${targetName}.${option} points at a real file`,
|
|
4179
|
-
ok: false,
|
|
4180
|
-
detail: `${value} does not exist \u2014 the target fails as if the compiler were misconfigured`,
|
|
4181
|
-
remedy: `create ${value}, or correct ${option} in ${source}`
|
|
4182
|
-
}
|
|
4183
|
-
];
|
|
4184
3947
|
});
|
|
3948
|
+
return new Map(results.filter((entry) => entry !== void 0));
|
|
4185
3949
|
}
|
|
4186
|
-
function
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
3950
|
+
async function latestVersions(ecosystem, names, cwd) {
|
|
3951
|
+
switch (ecosystem) {
|
|
3952
|
+
case "npm": {
|
|
3953
|
+
return await latestNpmVersions(names, cwd);
|
|
3954
|
+
}
|
|
3955
|
+
case "pip": {
|
|
3956
|
+
return await latestPipVersions(names, cwd);
|
|
3957
|
+
}
|
|
3958
|
+
case "nuget": {
|
|
3959
|
+
return await latestNugetVersions(names, cwd);
|
|
3960
|
+
}
|
|
3961
|
+
case "go": {
|
|
3962
|
+
return await latestGoVersions(cwd);
|
|
3963
|
+
}
|
|
3964
|
+
// 'pub' explicitly, not a default: a future ecosystem added to the union
|
|
3965
|
+
// without a case here should fail to compile, not silently resolve as
|
|
3966
|
+
// Dart/Flutter's whole-graph query — exactly the trap this switch would
|
|
3967
|
+
// otherwise have laid for 'nuget' itself.
|
|
3968
|
+
case "pub": {
|
|
3969
|
+
return await latestPubVersions(cwd);
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
4193
3972
|
}
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
3973
|
+
|
|
3974
|
+
// src/dependency-management/update-dependencies.use-case.ts
|
|
3975
|
+
function currentVersion(workspaceRoot, sites) {
|
|
3976
|
+
const installed = resolvedVersion(workspaceRoot, sites[0].ecosystem, sites[0].name);
|
|
3977
|
+
if (installed) {
|
|
3978
|
+
return installed;
|
|
4198
3979
|
}
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
3980
|
+
if (sites[0].ecosystem === "go") {
|
|
3981
|
+
return bare(sites[0].spec);
|
|
3982
|
+
}
|
|
3983
|
+
const highest = highestSpec(sites.map((site) => site.spec));
|
|
3984
|
+
return highest ? specVersion(highest) : void 0;
|
|
3985
|
+
}
|
|
3986
|
+
async function collectOutdated(workspaceRoot, ecosystems) {
|
|
3987
|
+
const present = ecosystems.filter((ecosystem) => hasEcosystem(workspaceRoot, ecosystem));
|
|
3988
|
+
const inventory = collectInventory(workspaceRoot, present);
|
|
3989
|
+
const outdated = [];
|
|
3990
|
+
for (const ecosystem of present) {
|
|
3991
|
+
const forEcosystem = [...inventory].filter(
|
|
3992
|
+
([name, sites]) => offerable(workspaceRoot, ecosystem, name, sites)
|
|
4203
3993
|
);
|
|
4204
|
-
|
|
4205
|
-
|
|
3994
|
+
const latest = await latestVersions(
|
|
3995
|
+
ecosystem,
|
|
3996
|
+
forEcosystem.map(([name]) => name),
|
|
3997
|
+
workspaceRoot
|
|
3998
|
+
);
|
|
3999
|
+
outdated.push(...upgradable(workspaceRoot, ecosystem, forEcosystem, latest));
|
|
4000
|
+
}
|
|
4001
|
+
return outdated.toSorted(
|
|
4002
|
+
(left, right) => UPDATE_KINDS.indexOf(left.kind) - UPDATE_KINDS.indexOf(right.kind) || left.name.localeCompare(right.name)
|
|
4003
|
+
);
|
|
4004
|
+
}
|
|
4005
|
+
function offerable(workspaceRoot, ecosystem, name, sites) {
|
|
4006
|
+
if (sites[0].ecosystem !== ecosystem || sites[0].section === "indirect") {
|
|
4007
|
+
return false;
|
|
4008
|
+
}
|
|
4009
|
+
if (sites.every((site) => site.section === PEER_SECTION)) {
|
|
4010
|
+
return false;
|
|
4011
|
+
}
|
|
4012
|
+
if (ecosystem !== "npm") {
|
|
4013
|
+
return true;
|
|
4014
|
+
}
|
|
4015
|
+
return !isWorkspaceProject(workspaceRoot, name) && !isAliasedInstall(workspaceRoot, name);
|
|
4016
|
+
}
|
|
4017
|
+
function bare(version) {
|
|
4018
|
+
return version.replace(/^v/, "");
|
|
4019
|
+
}
|
|
4020
|
+
function upgradable(workspaceRoot, ecosystem, declared, latest) {
|
|
4021
|
+
const outdated = [];
|
|
4022
|
+
for (const [name, sites] of declared) {
|
|
4023
|
+
const newest = latest.get(name);
|
|
4024
|
+
const current = currentVersion(workspaceRoot, sites);
|
|
4025
|
+
if (!newest || !current || !isNewer(bare(newest), bare(current))) {
|
|
4026
|
+
continue;
|
|
4027
|
+
}
|
|
4028
|
+
outdated.push({
|
|
4029
|
+
name,
|
|
4030
|
+
ecosystem,
|
|
4031
|
+
current,
|
|
4032
|
+
latest: newest,
|
|
4033
|
+
kind: classify(bare(current), bare(newest)),
|
|
4034
|
+
sites
|
|
4035
|
+
});
|
|
4206
4036
|
}
|
|
4037
|
+
return outdated;
|
|
4038
|
+
}
|
|
4039
|
+
function row(entry, widths) {
|
|
4040
|
+
const sections = [...new Set(entry.sites.map((site) => site.section))].join("/");
|
|
4041
|
+
const projects = [...new Set(entry.sites.map((site) => site.project))].join(", ");
|
|
4042
|
+
return [
|
|
4043
|
+
entry.name.padEnd(widths.name),
|
|
4044
|
+
sections.padEnd(widths.section),
|
|
4045
|
+
`${entry.current.padStart(widths.current)} \u203A ${entry.latest}`,
|
|
4046
|
+
projects
|
|
4047
|
+
].join(" ");
|
|
4048
|
+
}
|
|
4049
|
+
function columnWidths(entries) {
|
|
4207
4050
|
return {
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4051
|
+
name: widthOf(entries.map((entry) => entry.name)),
|
|
4052
|
+
section: widthOf(
|
|
4053
|
+
entries.map((entry) => [...new Set(entry.sites.map((site) => site.section))].join("/"))
|
|
4054
|
+
),
|
|
4055
|
+
current: widthOf(entries.map((entry) => entry.current))
|
|
4212
4056
|
};
|
|
4213
4057
|
}
|
|
4214
|
-
function
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
}
|
|
4224
|
-
|
|
4058
|
+
function widthOf(values) {
|
|
4059
|
+
return Math.max(0, ...values.map((value) => value.length));
|
|
4060
|
+
}
|
|
4061
|
+
function reportOutdated(outdated) {
|
|
4062
|
+
const widths = columnWidths(outdated);
|
|
4063
|
+
for (const kind of UPDATE_KINDS) {
|
|
4064
|
+
const section = outdated.filter((entry) => entry.kind === kind);
|
|
4065
|
+
if (section.length === 0) {
|
|
4066
|
+
continue;
|
|
4067
|
+
}
|
|
4068
|
+
const { title, blurb } = UPDATE_KIND_LABELS[kind];
|
|
4069
|
+
logger.info("");
|
|
4070
|
+
logger.info(`${title} ${blurb}`);
|
|
4071
|
+
for (const entry of section) {
|
|
4072
|
+
logger.info(` ${row(entry, widths)}`);
|
|
4225
4073
|
}
|
|
4226
|
-
|
|
4227
|
-
{
|
|
4228
|
-
check: `source maps enabled in ${relativePath}`,
|
|
4229
|
-
ok: hasRollupSourceMaps(resolveRollupConfigText(configPath)),
|
|
4230
|
-
detail: "rollup emits no .js.map without it, so a breakpoint in a .ts file can never bind",
|
|
4231
|
-
remedy: canRepairRollupConfig(config) ? "run `mnci upgrade`, which adds it to every rollup config" : "this config delegates via require() to a shared base mnci does not own \u2014 add `sourceMap: true` to withNx's first argument there by hand; `mnci upgrade` cannot repair a config in this shape"
|
|
4232
|
-
}
|
|
4233
|
-
];
|
|
4234
|
-
});
|
|
4074
|
+
}
|
|
4235
4075
|
}
|
|
4236
|
-
function
|
|
4237
|
-
const
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
return [];
|
|
4076
|
+
async function promptForUpdates(outdated) {
|
|
4077
|
+
const widths = columnWidths(outdated);
|
|
4078
|
+
const choices = [];
|
|
4079
|
+
for (const kind of UPDATE_KINDS) {
|
|
4080
|
+
const section = outdated.filter((entry) => entry.kind === kind);
|
|
4081
|
+
if (section.length === 0) {
|
|
4082
|
+
continue;
|
|
4244
4083
|
}
|
|
4245
|
-
const
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
ok: false,
|
|
4251
|
-
detail: "no mnci-normalise-declaration-specifiers plugin \u2014 the emitted .d.ts files keep the extensionless imports tsconfig.lib.json allows, which a nodenext consumer cannot resolve",
|
|
4252
|
-
// Deliberately NOT `mnci upgrade`, which would send the user in a
|
|
4253
|
-
// circle: `upgradeDeclarationSpecifierPlugins` only rewrites a
|
|
4254
|
-
// plugin that is already there, and `repairDeclarationSpecifiers`
|
|
4255
|
-
// is anchored on the generator's own placeholder comment, which a
|
|
4256
|
-
// config in this state no longer has. Verified by running the
|
|
4257
|
-
// command against exactly this shape — it no-ops. Naming a command
|
|
4258
|
-
// that silently does nothing is the failure mode this file's own
|
|
4259
|
-
// source-map check already goes out of its way to avoid.
|
|
4260
|
-
remedy: "add the plugin to withNx's second argument by hand (copy it from a freshly `mnci add npm-lib`'d project) \u2014 `mnci upgrade` only updates a plugin that is already present, it cannot restore a missing one"
|
|
4261
|
-
}
|
|
4262
|
-
];
|
|
4084
|
+
const { title, blurb } = UPDATE_KIND_LABELS[kind];
|
|
4085
|
+
choices.push(new import_prompts2.Separator(`
|
|
4086
|
+
${title} ${blurb}`));
|
|
4087
|
+
for (const entry of section) {
|
|
4088
|
+
choices.push({ name: row(entry, widths), value: entry });
|
|
4263
4089
|
}
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
remedy: "run `mnci upgrade`, which replaces the plugin body with the current one"
|
|
4270
|
-
}
|
|
4271
|
-
];
|
|
4090
|
+
}
|
|
4091
|
+
return await (0, import_prompts2.checkbox)({
|
|
4092
|
+
message: "Choose which packages to update.",
|
|
4093
|
+
choices,
|
|
4094
|
+
pageSize: 20
|
|
4272
4095
|
});
|
|
4273
4096
|
}
|
|
4274
|
-
function
|
|
4275
|
-
const
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4097
|
+
function applyUpdate(entry) {
|
|
4098
|
+
const changed = [];
|
|
4099
|
+
for (const site of entry.sites) {
|
|
4100
|
+
if (!site.rewritable) {
|
|
4101
|
+
continue;
|
|
4102
|
+
}
|
|
4103
|
+
const spec = `${rangeOperator(site.spec)}${entry.latest}`;
|
|
4104
|
+
if (rewriteSpec(site, spec)) {
|
|
4105
|
+
changed.push(site.manifestPath);
|
|
4106
|
+
}
|
|
4280
4107
|
}
|
|
4281
|
-
|
|
4282
|
-
return [
|
|
4283
|
-
...checkEslintConfigs(workspaceRoot),
|
|
4284
|
-
checkNoRetiredFormatter(workspaceRoot),
|
|
4285
|
-
checkEslintPlugin(nxJson),
|
|
4286
|
-
checkResolvedEslint(workspaceRoot),
|
|
4287
|
-
checkNpmrc(workspaceRoot, nxJson.mnci?.registry, nxJson.mnci?.scope),
|
|
4288
|
-
checkNoRootRuntimeDependencies(workspaceRoot),
|
|
4289
|
-
...checkRollupSourceMaps(workspaceRoot),
|
|
4290
|
-
...checkDeclarationSpecifiers(workspaceRoot),
|
|
4291
|
-
...checkVersionActions(workspaceRoot),
|
|
4292
|
-
...checkTargetFilesExist(workspaceRoot),
|
|
4293
|
-
checkSync(workspaceRoot)
|
|
4294
|
-
].filter((finding) => finding !== void 0);
|
|
4108
|
+
return changed;
|
|
4295
4109
|
}
|
|
4296
|
-
function
|
|
4297
|
-
const
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4110
|
+
function reinstall(workspaceRoot, ecosystems) {
|
|
4111
|
+
const commands = [
|
|
4112
|
+
["npm", "npm", ["install"]],
|
|
4113
|
+
["pip", "npm", ["run", "python:install"]],
|
|
4114
|
+
["pub", "flutter", ["pub", "get"]],
|
|
4115
|
+
["nuget", "npx", ["nx", "run-many", "-t", "restore"]],
|
|
4116
|
+
["go", "go", ["mod", "tidy"]]
|
|
4117
|
+
];
|
|
4118
|
+
for (const [ecosystem, command, arguments_] of commands) {
|
|
4119
|
+
if (!ecosystems.has(ecosystem)) {
|
|
4120
|
+
continue;
|
|
4121
|
+
}
|
|
4122
|
+
if (ecosystem === "pip" && !hasPythonInstallScript(workspaceRoot)) {
|
|
4123
|
+
logger.warn(
|
|
4124
|
+
"This workspace has no 'python:install' script \u2014 run 'mnci upgrade' to add it, then reinstall by hand."
|
|
4125
|
+
);
|
|
4126
|
+
continue;
|
|
4127
|
+
}
|
|
4128
|
+
logger.step(`Reinstalling ${ecosystem} dependencies (${command} ${arguments_.join(" ")})`);
|
|
4129
|
+
if (runShell(command, arguments_, workspaceRoot) !== 0) {
|
|
4130
|
+
logger.warn(
|
|
4131
|
+
`${command} ${arguments_.join(" ")} failed \u2014 the manifests were updated, so re-run it once the cause is fixed.`
|
|
4132
|
+
);
|
|
4307
4133
|
}
|
|
4308
4134
|
}
|
|
4309
|
-
if (failed.length === 0) {
|
|
4310
|
-
logger.success(`All ${findings.length} checks passed.`);
|
|
4311
|
-
return;
|
|
4312
|
-
}
|
|
4313
|
-
logger.error(`${failed.length} of ${findings.length} checks failed.`);
|
|
4314
|
-
process.exitCode = 1;
|
|
4315
4135
|
}
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
return ci === "azure" ? "azure" : "github";
|
|
4136
|
+
function hasPythonInstallScript(workspaceRoot) {
|
|
4137
|
+
try {
|
|
4138
|
+
const manifest = readJson(
|
|
4139
|
+
(0, import_node_path17.join)(workspaceRoot, "package.json")
|
|
4140
|
+
);
|
|
4141
|
+
return manifest.scripts?.["python:install"] !== void 0;
|
|
4142
|
+
} catch {
|
|
4143
|
+
return false;
|
|
4144
|
+
}
|
|
4326
4145
|
}
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4146
|
+
function applyGoUpdates(workspaceRoot, entries) {
|
|
4147
|
+
for (const entry of entries) {
|
|
4148
|
+
logger.step(`go get ${entry.name}@${entry.latest}`);
|
|
4149
|
+
if (runShell("go", ["get", `${entry.name}@${entry.latest}`], workspaceRoot) !== 0) {
|
|
4150
|
+
logger.warn(`go get ${entry.name}@${entry.latest} failed \u2014 left at ${entry.current}.`);
|
|
4151
|
+
}
|
|
4330
4152
|
}
|
|
4331
|
-
const prompted = await promptStack();
|
|
4332
|
-
return { testRunner: prompted.testRunner };
|
|
4333
4153
|
}
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
return options.ci;
|
|
4154
|
+
async function runUp(workspaceRoot, options) {
|
|
4155
|
+
if (!fileExists((0, import_node_path17.join)(workspaceRoot, "nx.json"))) {
|
|
4156
|
+
throw new Error(`No nx.json found in ${workspaceRoot} \u2014 run 'mnci up' from the workspace root.`);
|
|
4338
4157
|
}
|
|
4339
|
-
|
|
4340
|
-
|
|
4158
|
+
const ecosystems = resolveEcosystems(options.ecosystem);
|
|
4159
|
+
for (const ecosystem of ecosystems) {
|
|
4160
|
+
if (!hasEcosystem(workspaceRoot, ecosystem)) {
|
|
4161
|
+
logger.info(`\u2298 SKIPPED ${ecosystem} \u2014 no ${ecosystem} project in this workspace.`);
|
|
4162
|
+
}
|
|
4341
4163
|
}
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
return
|
|
4347
|
-
kind: "azure-artifacts",
|
|
4348
|
-
organization: options.organization ?? await promptText("Azure DevOps organization"),
|
|
4349
|
-
project: options.project ?? await promptText("Azure DevOps project"),
|
|
4350
|
-
artifactsFeed: options.artifactsFeed ?? await promptText("Artifacts feed name")
|
|
4351
|
-
};
|
|
4164
|
+
logger.step("Querying registries for the latest published versions");
|
|
4165
|
+
const outdated = await collectOutdated(workspaceRoot, ecosystems);
|
|
4166
|
+
if (outdated.length === 0) {
|
|
4167
|
+
logger.success("Every dependency is on its latest published version.");
|
|
4168
|
+
return;
|
|
4352
4169
|
}
|
|
4353
|
-
|
|
4354
|
-
|
|
4170
|
+
const readOnly = options.check ?? !process.stdout.isTTY;
|
|
4171
|
+
if (readOnly) {
|
|
4172
|
+
reportOutdated(outdated);
|
|
4173
|
+
logger.info("");
|
|
4174
|
+
logger.info(`${outdated.length} package(s) have a newer release. Run 'mnci up' to update them.`);
|
|
4175
|
+
return;
|
|
4355
4176
|
}
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
const
|
|
4362
|
-
const
|
|
4363
|
-
const
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
)
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
"create-nx-workspace@latest",
|
|
4376
|
-
workspaceName,
|
|
4377
|
-
"--preset=ts",
|
|
4378
|
-
"--pm=npm",
|
|
4379
|
-
nxCloud ? `--nxCloud=${nxCloudProviderValue(ci)}` : "--nxCloud=skip",
|
|
4380
|
-
"--no-interactive"
|
|
4381
|
-
],
|
|
4382
|
-
process.cwd()
|
|
4383
|
-
);
|
|
4384
|
-
const workspaceRoot = (0, import_node_path16.join)(process.cwd(), workspaceName);
|
|
4385
|
-
logger.step(
|
|
4386
|
-
"Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
|
|
4387
|
-
);
|
|
4388
|
-
applyOverlay(
|
|
4389
|
-
workspaceRoot,
|
|
4390
|
-
{ workspaceName, scope, registry, agent, variableGroup, ci, stack },
|
|
4391
|
-
logger.detail
|
|
4392
|
-
);
|
|
4393
|
-
(0, import_node_fs10.rmSync)((0, import_node_path16.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
|
|
4394
|
-
(0, import_node_fs10.rmSync)((0, import_node_path16.join)(workspaceRoot, "package-lock.json"), { force: true });
|
|
4395
|
-
logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
|
|
4396
|
-
const installStatus = runShell(
|
|
4397
|
-
"npm",
|
|
4398
|
-
["install", "--save-dev", "husky", "@commitlint/cli", "@commitlint/config-conventional"],
|
|
4399
|
-
workspaceRoot
|
|
4400
|
-
);
|
|
4401
|
-
if (installStatus !== 0) {
|
|
4402
|
-
throw new Error(`npm install of the toolchain failed with exit code ${installStatus}`);
|
|
4177
|
+
const selected = options.yes ? [...outdated] : await promptForUpdates(outdated);
|
|
4178
|
+
if (selected.length === 0) {
|
|
4179
|
+
logger.info("Nothing selected; no manifest was changed.");
|
|
4180
|
+
return;
|
|
4181
|
+
}
|
|
4182
|
+
const goSelections = selected.filter((entry) => entry.ecosystem === "go");
|
|
4183
|
+
const changed = selected.filter((entry) => entry.ecosystem !== "go").flatMap((entry) => applyUpdate(entry));
|
|
4184
|
+
const changedManifests = new Set(changed);
|
|
4185
|
+
for (const path of changedManifests) {
|
|
4186
|
+
logger.detail(`updated ${path}`);
|
|
4187
|
+
}
|
|
4188
|
+
applyGoUpdates(workspaceRoot, goSelections);
|
|
4189
|
+
if (options.install !== false) {
|
|
4190
|
+
reinstall(workspaceRoot, new Set(selected.map((entry) => entry.ecosystem)));
|
|
4191
|
+
}
|
|
4192
|
+
if (changed.length > 0) {
|
|
4193
|
+
syncProjectReferences(workspaceRoot);
|
|
4194
|
+
logger.step("Formatting the changed manifests (eslint --fix)");
|
|
4195
|
+
runFormatter(workspaceRoot);
|
|
4403
4196
|
}
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
runFormatter(workspaceRoot);
|
|
4407
|
-
logger.success("Done. Next steps:");
|
|
4408
|
-
logger.info(` cd ${workspaceName}`);
|
|
4409
|
-
logger.info(" mnci add react-app web # or: react-lib, react-internal-lib, node-app,");
|
|
4410
|
-
logger.info(" # node-function-app, npm-lib, internal-lib,");
|
|
4411
|
-
logger.info(
|
|
4412
|
-
" # python-app, python-function-app, python-lib, python-internal-lib,"
|
|
4413
|
-
);
|
|
4414
|
-
logger.info(
|
|
4415
|
-
" # go-app, go-function-app, go-lib, go-internal-lib,"
|
|
4416
|
-
);
|
|
4417
|
-
logger.info(
|
|
4418
|
-
" # flutter-app, flutter-lib, flutter-internal-lib"
|
|
4197
|
+
logger.success(
|
|
4198
|
+
`Updated ${selected.length} package(s). Review the changes with \`git diff\` before committing.`
|
|
4419
4199
|
);
|
|
4420
|
-
logger.info(' git add -A && git commit -m "feat: initial workspace"');
|
|
4421
4200
|
}
|
|
4422
4201
|
|
|
4423
|
-
// src/
|
|
4424
|
-
var
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4202
|
+
// src/project-scaffolding/add-project.use-case.ts
|
|
4203
|
+
var PROJECT_KINDS = [
|
|
4204
|
+
"react-app",
|
|
4205
|
+
"react-lib",
|
|
4206
|
+
"react-internal-lib",
|
|
4207
|
+
"node-app",
|
|
4208
|
+
"node-function-app",
|
|
4209
|
+
"npm-lib",
|
|
4210
|
+
"internal-lib",
|
|
4211
|
+
"python-app",
|
|
4212
|
+
"python-function-app",
|
|
4213
|
+
"python-lib",
|
|
4214
|
+
"python-internal-lib",
|
|
4215
|
+
"python-vendor",
|
|
4216
|
+
"go-app",
|
|
4217
|
+
"go-function-app",
|
|
4218
|
+
"go-lib",
|
|
4219
|
+
"go-internal-lib",
|
|
4220
|
+
"flutter-app",
|
|
4221
|
+
"flutter-lib",
|
|
4222
|
+
"flutter-internal-lib",
|
|
4223
|
+
"csharp-app",
|
|
4224
|
+
"csharp-function-app",
|
|
4225
|
+
"csharp-lib",
|
|
4226
|
+
"csharp-internal-lib"
|
|
4227
|
+
];
|
|
4228
|
+
async function runAdd(kind, name, options) {
|
|
4229
|
+
const workspaceRoot = process.cwd();
|
|
4230
|
+
if (!fileExists((0, import_node_path18.join)(workspaceRoot, "nx.json"))) {
|
|
4231
|
+
throw new Error("No nx.json found here. Run `add` from the workspace root.");
|
|
4232
|
+
}
|
|
4233
|
+
const stack = readWorkspaceStack(workspaceRoot);
|
|
4234
|
+
const kindProvided = kind !== void 0;
|
|
4235
|
+
const resolvedKind = kind ?? await (0, import_prompts3.select)({
|
|
4236
|
+
message: "What kind of project?",
|
|
4237
|
+
choices: PROJECT_KINDS.map((value) => ({ name: value, value }))
|
|
4238
|
+
});
|
|
4239
|
+
const resolvedName = name ?? await promptText("Project name");
|
|
4240
|
+
assertValidProjectName(resolvedName, "Project name");
|
|
4241
|
+
const rootDependenciesBefore = rootRuntimeDependencies(workspaceRoot);
|
|
4242
|
+
switch (resolvedKind) {
|
|
4243
|
+
case "react-app": {
|
|
4244
|
+
addReactApp(workspaceRoot, resolvedName, stack);
|
|
4245
|
+
break;
|
|
4246
|
+
}
|
|
4247
|
+
case "react-lib": {
|
|
4248
|
+
await addReactLib(workspaceRoot, resolvedName, options, kindProvided, stack);
|
|
4249
|
+
break;
|
|
4250
|
+
}
|
|
4251
|
+
case "react-internal-lib": {
|
|
4252
|
+
addReactInternalLib(workspaceRoot, resolvedName, stack);
|
|
4253
|
+
break;
|
|
4254
|
+
}
|
|
4255
|
+
case "node-app": {
|
|
4256
|
+
addNodeApp(workspaceRoot, resolvedName, stack, options.framework);
|
|
4257
|
+
break;
|
|
4258
|
+
}
|
|
4259
|
+
case "node-function-app": {
|
|
4260
|
+
addNodeFunctionApp(workspaceRoot, resolvedName, stack);
|
|
4261
|
+
break;
|
|
4262
|
+
}
|
|
4263
|
+
case "npm-lib": {
|
|
4264
|
+
await addNpmLib(workspaceRoot, resolvedName, options, kindProvided, stack);
|
|
4265
|
+
break;
|
|
4266
|
+
}
|
|
4267
|
+
case "internal-lib": {
|
|
4268
|
+
runNx(
|
|
4269
|
+
[
|
|
4270
|
+
"g",
|
|
4271
|
+
"@nx/js:lib",
|
|
4272
|
+
`libs/${resolvedName}`,
|
|
4273
|
+
"--bundler=tsc",
|
|
4274
|
+
`--unitTestRunner=${stack.testRunner}`,
|
|
4275
|
+
"--linter=none",
|
|
4276
|
+
"--no-interactive"
|
|
4277
|
+
],
|
|
4278
|
+
workspaceRoot
|
|
4279
|
+
);
|
|
4280
|
+
markPrivate((0, import_node_path18.join)(workspaceRoot, "libs", resolvedName, "package.json"));
|
|
4281
|
+
removeGeneratedEslintConfig(workspaceRoot, `libs/${resolvedName}`);
|
|
4282
|
+
registerProjectCommands(workspaceRoot, resolvedName, { build: true });
|
|
4283
|
+
break;
|
|
4284
|
+
}
|
|
4285
|
+
case "python-app": {
|
|
4286
|
+
addPythonApp(workspaceRoot, resolvedName);
|
|
4287
|
+
break;
|
|
4288
|
+
}
|
|
4289
|
+
case "python-function-app": {
|
|
4290
|
+
addPythonFunctionApp(workspaceRoot, resolvedName);
|
|
4291
|
+
break;
|
|
4292
|
+
}
|
|
4293
|
+
case "python-lib": {
|
|
4294
|
+
addPythonLib(workspaceRoot, resolvedName);
|
|
4295
|
+
break;
|
|
4296
|
+
}
|
|
4297
|
+
case "python-internal-lib": {
|
|
4298
|
+
addPythonInternalLib(workspaceRoot, resolvedName);
|
|
4299
|
+
break;
|
|
4300
|
+
}
|
|
4301
|
+
case "go-app": {
|
|
4302
|
+
addGoApp(workspaceRoot, resolvedName);
|
|
4303
|
+
break;
|
|
4304
|
+
}
|
|
4305
|
+
case "go-function-app": {
|
|
4306
|
+
addGoFunctionApp(workspaceRoot, resolvedName);
|
|
4307
|
+
break;
|
|
4308
|
+
}
|
|
4309
|
+
case "go-lib": {
|
|
4310
|
+
addGoLib(workspaceRoot, resolvedName);
|
|
4311
|
+
break;
|
|
4312
|
+
}
|
|
4313
|
+
case "go-internal-lib": {
|
|
4314
|
+
addGoInternalLib(workspaceRoot, resolvedName);
|
|
4315
|
+
break;
|
|
4316
|
+
}
|
|
4317
|
+
case "flutter-app": {
|
|
4318
|
+
addFlutterApp(workspaceRoot, resolvedName);
|
|
4319
|
+
break;
|
|
4320
|
+
}
|
|
4321
|
+
case "flutter-lib": {
|
|
4322
|
+
addFlutterLib(workspaceRoot, resolvedName);
|
|
4323
|
+
break;
|
|
4324
|
+
}
|
|
4325
|
+
case "flutter-internal-lib": {
|
|
4326
|
+
addFlutterInternalLib(workspaceRoot, resolvedName);
|
|
4327
|
+
break;
|
|
4328
|
+
}
|
|
4329
|
+
case "csharp-app": {
|
|
4330
|
+
addCsharpApp(workspaceRoot, resolvedName);
|
|
4331
|
+
break;
|
|
4332
|
+
}
|
|
4333
|
+
case "csharp-function-app": {
|
|
4334
|
+
addCsharpFunctionApp(workspaceRoot, resolvedName);
|
|
4335
|
+
break;
|
|
4336
|
+
}
|
|
4337
|
+
case "csharp-lib": {
|
|
4338
|
+
await addCsharpLib(workspaceRoot, resolvedName, options, kindProvided);
|
|
4339
|
+
break;
|
|
4340
|
+
}
|
|
4341
|
+
case "csharp-internal-lib": {
|
|
4342
|
+
addCsharpInternalLib(workspaceRoot, resolvedName);
|
|
4343
|
+
break;
|
|
4344
|
+
}
|
|
4345
|
+
case "python-vendor": {
|
|
4346
|
+
await addPythonVendor(workspaceRoot, resolvedName, options, kindProvided);
|
|
4347
|
+
syncProjectReferences(workspaceRoot);
|
|
4348
|
+
runFormatter(workspaceRoot);
|
|
4349
|
+
return;
|
|
4350
|
+
}
|
|
4351
|
+
default: {
|
|
4352
|
+
const exhaustive = resolvedKind;
|
|
4433
4353
|
throw new Error(
|
|
4434
|
-
|
|
4354
|
+
`Unknown project kind '${exhaustive}'. Expected one of: ${PROJECT_KINDS.join(", ")}.`
|
|
4435
4355
|
);
|
|
4436
4356
|
}
|
|
4437
|
-
return { kind: "azure-artifacts", organization, project, artifactsFeed };
|
|
4438
|
-
}
|
|
4439
|
-
if (options.registry === "npm") {
|
|
4440
|
-
return { kind: "npm" };
|
|
4441
|
-
}
|
|
4442
|
-
if (persisted) {
|
|
4443
|
-
return persisted;
|
|
4444
4357
|
}
|
|
4445
|
-
|
|
4446
|
-
|
|
4358
|
+
relocateRootRuntimeDependencies(workspaceRoot, resolvedName, rootDependenciesBefore);
|
|
4359
|
+
syncProjectReferences(workspaceRoot);
|
|
4360
|
+
runFormatter(workspaceRoot);
|
|
4361
|
+
logger.success(`Added ${resolvedKind} '${resolvedName}'.`);
|
|
4362
|
+
}
|
|
4363
|
+
function readWorkspaceStack(workspaceRoot) {
|
|
4364
|
+
const nxJson = readJson(
|
|
4365
|
+
(0, import_node_path18.join)(workspaceRoot, "nx.json")
|
|
4447
4366
|
);
|
|
4367
|
+
const stack = nxJson.mnci?.stack;
|
|
4368
|
+
return {
|
|
4369
|
+
testRunner: stack?.testRunner === "vitest" ? "vitest" : "jest"
|
|
4370
|
+
};
|
|
4448
4371
|
}
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4372
|
+
|
|
4373
|
+
// src/workspace-diagnostics/check-invariants.use-case.ts
|
|
4374
|
+
var import_node_fs12 = require("fs");
|
|
4375
|
+
var import_node_path19 = require("path");
|
|
4376
|
+
var SUPPORTED_ESLINT_MAJOR = ESLINT_VERSION.replace(/^\D*/, "").split(".", 1)[0];
|
|
4377
|
+
function toPosix2(path) {
|
|
4378
|
+
return path.replaceAll("\\", "/");
|
|
4379
|
+
}
|
|
4380
|
+
function checkEslintConfigs(workspaceRoot) {
|
|
4381
|
+
const rootConfigs = (0, import_node_fs12.globSync)("eslint.config.{js,mjs,cjs,ts,mts,cts}", { cwd: workspaceRoot });
|
|
4382
|
+
const projectConfigs = (0, import_node_fs12.globSync)("{apps,libs,packages}/*/eslint.config.{js,mjs,cjs,ts,mts,cts}", {
|
|
4383
|
+
cwd: workspaceRoot
|
|
4384
|
+
}).map((config) => toPosix2(config));
|
|
4385
|
+
return [
|
|
4386
|
+
{
|
|
4387
|
+
check: "root ESLint config",
|
|
4388
|
+
ok: rootConfigs.length === 1,
|
|
4389
|
+
detail: rootConfigs.length === 0 ? "no eslint.config.* at the workspace root" : `${rootConfigs.length} root configs: ${rootConfigs.join(", ")}`,
|
|
4390
|
+
remedy: "run `mnci upgrade` to rewrite the root config"
|
|
4391
|
+
},
|
|
4392
|
+
{
|
|
4393
|
+
check: "no per-project ESLint configs",
|
|
4394
|
+
ok: projectConfigs.length === 0,
|
|
4395
|
+
detail: `found ${projectConfigs.length}: ${projectConfigs.join(", ")}`,
|
|
4396
|
+
remedy: "run `mnci upgrade`, which sweeps {apps,libs,packages}/*/eslint.config.*"
|
|
4459
4397
|
}
|
|
4460
|
-
|
|
4461
|
-
}
|
|
4462
|
-
return (0, import_node_path17.basename)(workspaceRoot);
|
|
4398
|
+
];
|
|
4463
4399
|
}
|
|
4464
|
-
function
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
}
|
|
4469
|
-
const registry = resolveRegistry2(options, persisted.registry);
|
|
4470
|
-
const ci = options.ci ?? persisted.ci;
|
|
4471
|
-
if (!ci) {
|
|
4472
|
-
throw new Error(
|
|
4473
|
-
"No CI provider found in nx.json's persisted config. Pass --ci azure|github|both explicitly."
|
|
4474
|
-
);
|
|
4475
|
-
}
|
|
4476
|
-
const agent = options.agent ?? persisted.agent;
|
|
4477
|
-
if (!agent) {
|
|
4478
|
-
throw new Error(
|
|
4479
|
-
"No CI build agent found in nx.json's persisted config. Pass --agent explicitly."
|
|
4480
|
-
);
|
|
4481
|
-
}
|
|
4482
|
-
const variableGroup = options.variableGroup ?? persisted.variableGroup ?? "Build";
|
|
4483
|
-
const testRunner = options.testRunner ?? persisted.stack?.testRunner;
|
|
4484
|
-
if (!testRunner) {
|
|
4485
|
-
throw new Error(
|
|
4486
|
-
"No testRunner found in nx.json's persisted config. Pass --test-runner explicitly."
|
|
4400
|
+
function declaredDevDependencies(workspaceRoot) {
|
|
4401
|
+
try {
|
|
4402
|
+
const manifest = readJson(
|
|
4403
|
+
(0, import_node_path19.join)(workspaceRoot, "package.json")
|
|
4487
4404
|
);
|
|
4405
|
+
return manifest.devDependencies ?? {};
|
|
4406
|
+
} catch {
|
|
4407
|
+
return {};
|
|
4408
|
+
}
|
|
4409
|
+
}
|
|
4410
|
+
function checkNoRetiredFormatter(workspaceRoot) {
|
|
4411
|
+
const files = RETIRED_FORMATTER_FILES.filter((file) => fileExists((0, import_node_path19.join)(workspaceRoot, file)));
|
|
4412
|
+
const declared = ["prettier", "eslint-config-prettier", "oxlint", "oxfmt", "@mnci/oxlint-config"].filter((name) => declaredDevDependencies(workspaceRoot)[name] !== void 0);
|
|
4413
|
+
const stale = [...files, ...declared];
|
|
4414
|
+
if (stale.length === 0) {
|
|
4415
|
+
return { check: "ESLint is the only linter and formatter", ok: true };
|
|
4488
4416
|
}
|
|
4489
4417
|
return {
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
variableGroup,
|
|
4495
|
-
ci,
|
|
4496
|
-
stack: { testRunner }
|
|
4418
|
+
check: "no retired formatter is still configured",
|
|
4419
|
+
ok: false,
|
|
4420
|
+
detail: `Found: ${stale.join(", ")}. These no longer run, but an editor extension still resolves them and will reformat on save against an opinion no gate checks.`,
|
|
4421
|
+
remedy: "Run 'mnci upgrade' to remove them."
|
|
4497
4422
|
};
|
|
4498
4423
|
}
|
|
4499
|
-
function
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4424
|
+
function checkEslintPlugin(nxJson) {
|
|
4425
|
+
const plugins = nxJson.plugins ?? [];
|
|
4426
|
+
const registered = plugins.some(
|
|
4427
|
+
(entry) => (typeof entry === "string" ? entry : entry.plugin) === "@nx/eslint/plugin"
|
|
4428
|
+
);
|
|
4429
|
+
return {
|
|
4430
|
+
check: "@nx/eslint/plugin registered",
|
|
4431
|
+
ok: registered,
|
|
4432
|
+
detail: "not in nx.json plugins \u2014 every project silently loses its lint target",
|
|
4433
|
+
remedy: "run `mnci upgrade`"
|
|
4434
|
+
};
|
|
4435
|
+
}
|
|
4436
|
+
function checkResolvedEslint(workspaceRoot) {
|
|
4437
|
+
const manifestPath = (0, import_node_path19.join)(workspaceRoot, "node_modules/eslint/package.json");
|
|
4438
|
+
if (!fileExists(manifestPath)) {
|
|
4439
|
+
return void 0;
|
|
4504
4440
|
}
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4441
|
+
try {
|
|
4442
|
+
const { version } = readJson(manifestPath);
|
|
4443
|
+
const major = version.split(".", 1)[0];
|
|
4444
|
+
return {
|
|
4445
|
+
check: `resolved eslint is ${SUPPORTED_ESLINT_MAJOR}.x`,
|
|
4446
|
+
ok: major === SUPPORTED_ESLINT_MAJOR,
|
|
4447
|
+
detail: `node_modules/eslint is ${version}, but this stack supports ${SUPPORTED_ESLINT_MAJOR}.x (eslint-plugin-react has no release beyond it)`,
|
|
4448
|
+
remedy: `pin eslint to ${ESLINT_VERSION} in every package manifest, then reinstall`
|
|
4449
|
+
};
|
|
4450
|
+
} catch {
|
|
4451
|
+
return void 0;
|
|
4515
4452
|
}
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
}
|
|
4453
|
+
}
|
|
4454
|
+
function checkNpmrc(workspaceRoot, registry, scope) {
|
|
4455
|
+
const npmrcPath = (0, import_node_path19.join)(workspaceRoot, ".npmrc");
|
|
4456
|
+
if (!registry || !fileExists(npmrcPath)) {
|
|
4457
|
+
return void 0;
|
|
4522
4458
|
}
|
|
4523
|
-
const
|
|
4524
|
-
if (
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4459
|
+
const npmrc = (0, import_node_fs12.readFileSync)(npmrcPath, "utf8");
|
|
4460
|
+
if (registry.kind === "npm") {
|
|
4461
|
+
return {
|
|
4462
|
+
check: ".npmrc authenticates the public registry",
|
|
4463
|
+
ok: npmrc.includes("//registry.npmjs.org/:_authToken="),
|
|
4464
|
+
detail: "no npmjs.org token line \u2014 `npm publish` cannot authenticate",
|
|
4465
|
+
remedy: "run `mnci upgrade`"
|
|
4466
|
+
};
|
|
4529
4467
|
}
|
|
4530
|
-
|
|
4531
|
-
"
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
}
|
|
4536
|
-
|
|
4537
|
-
// src/commands/interactive.ts
|
|
4538
|
-
async function runInteractive() {
|
|
4539
|
-
const inWorkspace = fileExists((0, import_node_path18.join)(process.cwd(), "nx.json"));
|
|
4540
|
-
const choiceNew = { name: "Create a new monorepo", value: "new" };
|
|
4541
|
-
const choiceAdd = { name: "Add a project to this workspace", value: "add" };
|
|
4542
|
-
const choiceUpgrade = {
|
|
4543
|
-
name: "Upgrade this workspace (re-apply the latest overlay)",
|
|
4544
|
-
value: "upgrade"
|
|
4468
|
+
return {
|
|
4469
|
+
check: ".npmrc routes the scope to the feed",
|
|
4470
|
+
ok: scope !== void 0 && npmrc.includes(`${scope}:registry=`),
|
|
4471
|
+
detail: `no '${scope ?? "@scope"}:registry=' line \u2014 a scoped package could publish to npmjs.org instead of the feed`,
|
|
4472
|
+
remedy: "run `mnci upgrade`"
|
|
4545
4473
|
};
|
|
4546
|
-
const action = await (0, import_prompts9.select)({
|
|
4547
|
-
message: "What would you like to do?",
|
|
4548
|
-
choices: inWorkspace ? [choiceAdd, choiceUpgrade, choiceNew] : [choiceNew, choiceAdd, choiceUpgrade]
|
|
4549
|
-
});
|
|
4550
|
-
if (action === "new") {
|
|
4551
|
-
await runNew(void 0, {});
|
|
4552
|
-
} else if (action === "upgrade") {
|
|
4553
|
-
runUpgrade(process.cwd(), {});
|
|
4554
|
-
} else {
|
|
4555
|
-
await runAdd(void 0, void 0, {});
|
|
4556
|
-
}
|
|
4557
4474
|
}
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
if (result.status !== 0) {
|
|
4569
|
-
return;
|
|
4570
|
-
}
|
|
4475
|
+
function checkVersionActions(workspaceRoot) {
|
|
4476
|
+
const candidates = [
|
|
4477
|
+
...(0, import_node_fs12.globSync)("packages/*/pubspec.yaml", { cwd: workspaceRoot }),
|
|
4478
|
+
...(0, import_node_fs12.globSync)("packages/*/*.csproj", { cwd: workspaceRoot }),
|
|
4479
|
+
...(0, import_node_fs12.globSync)("python-packages/*/pyproject.toml", { cwd: workspaceRoot })
|
|
4480
|
+
].map((manifest) => toPosix2(manifest));
|
|
4481
|
+
return candidates.flatMap((manifest) => {
|
|
4482
|
+
const projectRoot = manifest.slice(0, manifest.lastIndexOf("/"));
|
|
4483
|
+
const projectJsonPath = (0, import_node_path19.join)(workspaceRoot, projectRoot, "project.json");
|
|
4484
|
+
let hasOverride = false;
|
|
4571
4485
|
try {
|
|
4572
|
-
const
|
|
4573
|
-
|
|
4574
|
-
return typeof version === "string" ? [name, version] : void 0;
|
|
4486
|
+
const projectJson = readJson(projectJsonPath);
|
|
4487
|
+
hasOverride = Boolean(projectJson.release?.version?.versionActions);
|
|
4575
4488
|
} catch {
|
|
4576
|
-
return;
|
|
4577
|
-
}
|
|
4578
|
-
});
|
|
4579
|
-
return new Map(results.filter((entry) => entry !== void 0));
|
|
4580
|
-
}
|
|
4581
|
-
async function latestPipVersions(names, cwd) {
|
|
4582
|
-
const python = process.platform === "win32" ? "python" : "python3";
|
|
4583
|
-
const results = await pool(names, LOOKUP_CONCURRENCY, async (name) => {
|
|
4584
|
-
const result = await runCaptureAsync(python, ["-m", "pip", "index", "versions", name], cwd);
|
|
4585
|
-
if (result.status !== 0) {
|
|
4586
|
-
return;
|
|
4587
4489
|
}
|
|
4588
|
-
|
|
4589
|
-
|
|
4490
|
+
return [
|
|
4491
|
+
{
|
|
4492
|
+
check: `${projectRoot} keeps its versionActions override`,
|
|
4493
|
+
ok: hasOverride,
|
|
4494
|
+
detail: "missing \u2014 nx release aborts for the ENTIRE workspace, not just this project",
|
|
4495
|
+
remedy: `add release.version.versionActions to ${projectRoot}/project.json`
|
|
4496
|
+
}
|
|
4497
|
+
];
|
|
4590
4498
|
});
|
|
4591
|
-
return new Map(results.filter((entry) => entry !== void 0));
|
|
4592
4499
|
}
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
}
|
|
4500
|
+
var TARGET_FILE_OPTIONS = ["main", "tsConfig", "packageJson"];
|
|
4501
|
+
function declaredTargets(workspaceRoot, projectRoot) {
|
|
4502
|
+
const sources = [];
|
|
4503
|
+
for (const [file, extract] of [
|
|
4504
|
+
["project.json", (json) => json["targets"]],
|
|
4505
|
+
[
|
|
4506
|
+
"package.json",
|
|
4507
|
+
(json) => json["nx"]?.["targets"]
|
|
4508
|
+
]
|
|
4509
|
+
]) {
|
|
4604
4510
|
try {
|
|
4605
|
-
const
|
|
4606
|
-
|
|
4607
|
-
|
|
4511
|
+
const targets = extract(
|
|
4512
|
+
readJson((0, import_node_path19.join)(workspaceRoot, projectRoot, file))
|
|
4513
|
+
);
|
|
4514
|
+
if (targets && typeof targets === "object") {
|
|
4515
|
+
sources.push({
|
|
4516
|
+
source: `${projectRoot}/${file}`,
|
|
4517
|
+
targets
|
|
4518
|
+
});
|
|
4608
4519
|
}
|
|
4609
4520
|
} catch {
|
|
4610
4521
|
}
|
|
4611
4522
|
}
|
|
4612
|
-
return
|
|
4523
|
+
return sources;
|
|
4613
4524
|
}
|
|
4614
|
-
|
|
4615
|
-
const
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4525
|
+
function checkTargetFilesExist(workspaceRoot) {
|
|
4526
|
+
const projectRoots = new Set(
|
|
4527
|
+
[
|
|
4528
|
+
...(0, import_node_fs12.globSync)("{apps,libs,packages}/*/project.json", { cwd: workspaceRoot }),
|
|
4529
|
+
...(0, import_node_fs12.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot })
|
|
4530
|
+
].map((manifest) => toPosix2(manifest).split("/").slice(0, 2).join("/"))
|
|
4531
|
+
);
|
|
4532
|
+
return [...projectRoots].flatMap(
|
|
4533
|
+
(projectRoot) => declaredTargets(workspaceRoot, projectRoot).flatMap(
|
|
4534
|
+
({ source, targets }) => Object.entries(targets).flatMap(
|
|
4535
|
+
([targetName, target]) => checkOneTargetsFiles(workspaceRoot, source, targetName, target)
|
|
4536
|
+
)
|
|
4537
|
+
)
|
|
4538
|
+
);
|
|
4539
|
+
}
|
|
4540
|
+
function checkOneTargetsFiles(workspaceRoot, source, targetName, target) {
|
|
4541
|
+
return TARGET_FILE_OPTIONS.flatMap((option) => {
|
|
4542
|
+
const value = target?.options?.[option];
|
|
4543
|
+
const checkable = typeof value === "string" && !value.includes("{");
|
|
4544
|
+
if (!checkable || fileExists((0, import_node_path19.join)(workspaceRoot, value))) {
|
|
4545
|
+
return [];
|
|
4627
4546
|
}
|
|
4628
|
-
return
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4547
|
+
return [
|
|
4548
|
+
{
|
|
4549
|
+
check: `${source} \u2192 ${targetName}.${option} points at a real file`,
|
|
4550
|
+
ok: false,
|
|
4551
|
+
detail: `${value} does not exist \u2014 the target fails as if the compiler were misconfigured`,
|
|
4552
|
+
remedy: `create ${value}, or correct ${option} in ${source}`
|
|
4553
|
+
}
|
|
4554
|
+
];
|
|
4555
|
+
});
|
|
4632
4556
|
}
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4557
|
+
function checkSync(workspaceRoot) {
|
|
4558
|
+
return {
|
|
4559
|
+
check: "TypeScript project references synced",
|
|
4560
|
+
ok: runShell("npx", ["nx", "sync:check"], workspaceRoot) === 0,
|
|
4561
|
+
detail: "nx sync:check failed \u2014 a stale project reference was never committed",
|
|
4562
|
+
remedy: "run `npx nx sync` and commit the result"
|
|
4563
|
+
};
|
|
4564
|
+
}
|
|
4565
|
+
function checkNoRootRuntimeDependencies(workspaceRoot) {
|
|
4566
|
+
const manifestPath = (0, import_node_path19.join)(workspaceRoot, "package.json");
|
|
4567
|
+
if (!fileExists(manifestPath)) {
|
|
4568
|
+
return void 0;
|
|
4569
|
+
}
|
|
4570
|
+
let declared;
|
|
4571
|
+
try {
|
|
4572
|
+
declared = Object.keys(
|
|
4573
|
+
readJson(manifestPath).dependencies ?? {}
|
|
4639
4574
|
);
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4575
|
+
} catch {
|
|
4576
|
+
return void 0;
|
|
4577
|
+
}
|
|
4578
|
+
return {
|
|
4579
|
+
check: "no runtime dependencies in the root manifest",
|
|
4580
|
+
ok: declared.length === 0,
|
|
4581
|
+
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`,
|
|
4582
|
+
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)`
|
|
4583
|
+
};
|
|
4584
|
+
}
|
|
4585
|
+
function checkRollupSourceMaps(workspaceRoot) {
|
|
4586
|
+
const configs = (0, import_node_fs12.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
|
|
4587
|
+
cwd: workspaceRoot
|
|
4588
|
+
}).map((config) => toPosix2(config));
|
|
4589
|
+
return configs.flatMap((relativePath) => {
|
|
4590
|
+
const configPath = (0, import_node_path19.join)(workspaceRoot, relativePath);
|
|
4591
|
+
let config;
|
|
4643
4592
|
try {
|
|
4644
|
-
|
|
4645
|
-
const packages = parsed.searchResult?.flatMap((source) => source.packages ?? []) ?? [];
|
|
4646
|
-
const match = packages.find((entry) => entry.id?.toLowerCase() === name.toLowerCase()) ?? packages[0];
|
|
4647
|
-
return match?.latestVersion ? [name, match.latestVersion] : void 0;
|
|
4593
|
+
config = (0, import_node_fs12.readFileSync)(configPath, "utf8");
|
|
4648
4594
|
} catch {
|
|
4649
|
-
return;
|
|
4595
|
+
return [];
|
|
4650
4596
|
}
|
|
4597
|
+
return [
|
|
4598
|
+
{
|
|
4599
|
+
check: `source maps enabled in ${relativePath}`,
|
|
4600
|
+
ok: hasRollupSourceMaps(resolveRollupConfigText(configPath)),
|
|
4601
|
+
detail: "rollup emits no .js.map without it, so a breakpoint in a .ts file can never bind",
|
|
4602
|
+
remedy: canRepairRollupConfig(config) ? "run `mnci upgrade`, which adds it to every rollup config" : "this config delegates via require() to a shared base mnci does not own \u2014 add `sourceMap: true` to withNx's first argument there by hand; `mnci upgrade` cannot repair a config in this shape"
|
|
4603
|
+
}
|
|
4604
|
+
];
|
|
4651
4605
|
});
|
|
4652
|
-
return new Map(results.filter((entry) => entry !== void 0));
|
|
4653
4606
|
}
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
case "nuget": {
|
|
4663
|
-
return await latestNugetVersions(names, cwd);
|
|
4664
|
-
}
|
|
4665
|
-
case "go": {
|
|
4666
|
-
return await latestGoVersions(cwd);
|
|
4607
|
+
function checkDeclarationSpecifiers(workspaceRoot) {
|
|
4608
|
+
const configs = (0, import_node_fs12.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
|
|
4609
|
+
cwd: workspaceRoot
|
|
4610
|
+
}).map((config) => toPosix2(config));
|
|
4611
|
+
return configs.flatMap((relativePath) => {
|
|
4612
|
+
const configPath = (0, import_node_path19.join)(workspaceRoot, relativePath);
|
|
4613
|
+
if (!fileExists(configPath)) {
|
|
4614
|
+
return [];
|
|
4667
4615
|
}
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4616
|
+
const resolved = resolveRollupConfigText(configPath);
|
|
4617
|
+
if (!hasDeclarationSpecifierPlugin(resolved)) {
|
|
4618
|
+
return [
|
|
4619
|
+
{
|
|
4620
|
+
check: `${relativePath} normalises declaration specifiers`,
|
|
4621
|
+
ok: false,
|
|
4622
|
+
detail: "no mnci-normalise-declaration-specifiers plugin \u2014 the emitted .d.ts files keep the extensionless imports tsconfig.lib.json allows, which a nodenext consumer cannot resolve",
|
|
4623
|
+
// Deliberately NOT `mnci upgrade`, which would send the user in a
|
|
4624
|
+
// circle: `upgradeDeclarationSpecifierPlugins` only rewrites a
|
|
4625
|
+
// plugin that is already there, and `repairDeclarationSpecifiers`
|
|
4626
|
+
// is anchored on the generator's own placeholder comment, which a
|
|
4627
|
+
// config in this state no longer has. Verified by running the
|
|
4628
|
+
// command against exactly this shape — it no-ops. Naming a command
|
|
4629
|
+
// that silently does nothing is the failure mode this file's own
|
|
4630
|
+
// source-map check already goes out of its way to avoid.
|
|
4631
|
+
remedy: "add the plugin to withNx's second argument by hand (copy it from a freshly `mnci add npm-lib`'d project) \u2014 `mnci upgrade` only updates a plugin that is already present, it cannot restore a missing one"
|
|
4632
|
+
}
|
|
4633
|
+
];
|
|
4674
4634
|
}
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
}
|
|
4684
|
-
if (sites[0].ecosystem === "go") {
|
|
4685
|
-
return bare(sites[0].spec);
|
|
4686
|
-
}
|
|
4687
|
-
const highest = highestSpec(sites.map((site) => site.spec));
|
|
4688
|
-
return highest ? specVersion(highest) : void 0;
|
|
4635
|
+
return [
|
|
4636
|
+
{
|
|
4637
|
+
check: `${relativePath} resolves directory barrels in declarations`,
|
|
4638
|
+
ok: hasDirectoryAwareDeclarationSpecifiers(resolved),
|
|
4639
|
+
detail: "the plugin appends .js to every bare specifier, including ones naming a directory barrel, which needs /index.js \u2014 the named file is never emitted, so every export behind it publishes as `any` with nothing reporting it",
|
|
4640
|
+
remedy: "run `mnci upgrade`, which replaces the plugin body with the current one"
|
|
4641
|
+
}
|
|
4642
|
+
];
|
|
4643
|
+
});
|
|
4689
4644
|
}
|
|
4690
|
-
|
|
4691
|
-
const
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
const forEcosystem = [...inventory].filter(
|
|
4696
|
-
([name, sites]) => offerable(workspaceRoot, ecosystem, name, sites)
|
|
4697
|
-
);
|
|
4698
|
-
const latest = await latestVersions(
|
|
4699
|
-
ecosystem,
|
|
4700
|
-
forEcosystem.map(([name]) => name),
|
|
4701
|
-
workspaceRoot
|
|
4645
|
+
function collectFindings(workspaceRoot) {
|
|
4646
|
+
const nxJsonPath = (0, import_node_path19.join)(workspaceRoot, "nx.json");
|
|
4647
|
+
if (!fileExists(nxJsonPath)) {
|
|
4648
|
+
throw new Error(
|
|
4649
|
+
`No nx.json found in ${workspaceRoot} \u2014 run 'mnci doctor' from the workspace root.`
|
|
4702
4650
|
);
|
|
4703
|
-
outdated.push(...upgradable(workspaceRoot, ecosystem, forEcosystem, latest));
|
|
4704
4651
|
}
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4652
|
+
const nxJson = readJson(nxJsonPath);
|
|
4653
|
+
return [
|
|
4654
|
+
...checkEslintConfigs(workspaceRoot),
|
|
4655
|
+
checkNoRetiredFormatter(workspaceRoot),
|
|
4656
|
+
checkEslintPlugin(nxJson),
|
|
4657
|
+
checkResolvedEslint(workspaceRoot),
|
|
4658
|
+
checkNpmrc(workspaceRoot, nxJson.mnci?.registry, nxJson.mnci?.scope),
|
|
4659
|
+
checkNoRootRuntimeDependencies(workspaceRoot),
|
|
4660
|
+
...checkRollupSourceMaps(workspaceRoot),
|
|
4661
|
+
...checkDeclarationSpecifiers(workspaceRoot),
|
|
4662
|
+
...checkVersionActions(workspaceRoot),
|
|
4663
|
+
...checkTargetFilesExist(workspaceRoot),
|
|
4664
|
+
checkSync(workspaceRoot)
|
|
4665
|
+
].filter((finding) => finding !== void 0);
|
|
4708
4666
|
}
|
|
4709
|
-
function
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4667
|
+
function runDoctor(workspaceRoot) {
|
|
4668
|
+
const findings = collectFindings(workspaceRoot);
|
|
4669
|
+
const failed = findings.filter((finding) => !finding.ok);
|
|
4670
|
+
for (const finding of findings) {
|
|
4671
|
+
if (finding.ok) {
|
|
4672
|
+
logger.success(finding.check);
|
|
4673
|
+
} else {
|
|
4674
|
+
logger.error(`${finding.check} \u2014 ${finding.detail ?? "failed"}`);
|
|
4675
|
+
if (finding.remedy) {
|
|
4676
|
+
logger.info(` fix: ${finding.remedy}`);
|
|
4677
|
+
}
|
|
4678
|
+
}
|
|
4715
4679
|
}
|
|
4716
|
-
if (
|
|
4717
|
-
|
|
4680
|
+
if (failed.length === 0) {
|
|
4681
|
+
logger.success(`All ${findings.length} checks passed.`);
|
|
4682
|
+
return;
|
|
4718
4683
|
}
|
|
4719
|
-
|
|
4684
|
+
logger.error(`${failed.length} of ${findings.length} checks failed.`);
|
|
4685
|
+
process.exitCode = 1;
|
|
4720
4686
|
}
|
|
4721
|
-
|
|
4722
|
-
|
|
4687
|
+
|
|
4688
|
+
// src/workspace-creation/create-workspace.use-case.ts
|
|
4689
|
+
var import_node_fs13 = require("fs");
|
|
4690
|
+
var import_node_path20 = require("path");
|
|
4691
|
+
function nxCloudProviderValue(ci) {
|
|
4692
|
+
return ci === "azure" ? "azure" : "github";
|
|
4723
4693
|
}
|
|
4724
|
-
function
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
const newest = latest.get(name);
|
|
4728
|
-
const current = currentVersion(workspaceRoot, sites);
|
|
4729
|
-
if (!newest || !current || !isNewer(bare(newest), bare(current))) {
|
|
4730
|
-
continue;
|
|
4731
|
-
}
|
|
4732
|
-
outdated.push({
|
|
4733
|
-
name,
|
|
4734
|
-
ecosystem,
|
|
4735
|
-
current,
|
|
4736
|
-
latest: newest,
|
|
4737
|
-
kind: classify(bare(current), bare(newest)),
|
|
4738
|
-
sites
|
|
4739
|
-
});
|
|
4694
|
+
async function resolveStack(options) {
|
|
4695
|
+
if (options.testRunner || options.yes) {
|
|
4696
|
+
return { testRunner: options.testRunner ?? DEFAULT_STACK.testRunner };
|
|
4740
4697
|
}
|
|
4741
|
-
|
|
4742
|
-
}
|
|
4743
|
-
function row(entry, widths) {
|
|
4744
|
-
const sections = [...new Set(entry.sites.map((site) => site.section))].join("/");
|
|
4745
|
-
const projects = [...new Set(entry.sites.map((site) => site.project))].join(", ");
|
|
4746
|
-
return [
|
|
4747
|
-
entry.name.padEnd(widths.name),
|
|
4748
|
-
sections.padEnd(widths.section),
|
|
4749
|
-
`${entry.current.padStart(widths.current)} \u203A ${entry.latest}`,
|
|
4750
|
-
projects
|
|
4751
|
-
].join(" ");
|
|
4752
|
-
}
|
|
4753
|
-
function columnWidths(entries) {
|
|
4754
|
-
return {
|
|
4755
|
-
name: widthOf(entries.map((entry) => entry.name)),
|
|
4756
|
-
section: widthOf(
|
|
4757
|
-
entries.map((entry) => [...new Set(entry.sites.map((site) => site.section))].join("/"))
|
|
4758
|
-
),
|
|
4759
|
-
current: widthOf(entries.map((entry) => entry.current))
|
|
4760
|
-
};
|
|
4761
|
-
}
|
|
4762
|
-
function widthOf(values) {
|
|
4763
|
-
return Math.max(0, ...values.map((value) => value.length));
|
|
4698
|
+
const prompted = await promptStack();
|
|
4699
|
+
return { testRunner: prompted.testRunner };
|
|
4764
4700
|
}
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
const { title, blurb } = UPDATE_KIND_LABELS[kind];
|
|
4773
|
-
logger.info("");
|
|
4774
|
-
logger.info(`${title} ${blurb}`);
|
|
4775
|
-
for (const entry of section) {
|
|
4776
|
-
logger.info(` ${row(entry, widths)}`);
|
|
4777
|
-
}
|
|
4701
|
+
var CI_PROVIDERS = /* @__PURE__ */ new Set(["azure", "github", "both"]);
|
|
4702
|
+
async function resolveCi(options) {
|
|
4703
|
+
if (options.ci && CI_PROVIDERS.has(options.ci)) {
|
|
4704
|
+
return options.ci;
|
|
4705
|
+
}
|
|
4706
|
+
if (options.yes) {
|
|
4707
|
+
return "azure";
|
|
4778
4708
|
}
|
|
4709
|
+
return await promptCi();
|
|
4779
4710
|
}
|
|
4780
|
-
async function
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
}
|
|
4788
|
-
const { title, blurb } = UPDATE_KIND_LABELS[kind];
|
|
4789
|
-
choices.push(new import_prompts10.Separator(`
|
|
4790
|
-
${title} ${blurb}`));
|
|
4791
|
-
for (const entry of section) {
|
|
4792
|
-
choices.push({ name: row(entry, widths), value: entry });
|
|
4793
|
-
}
|
|
4711
|
+
async function resolveRegistry(options) {
|
|
4712
|
+
if (options.registry === "azure-artifacts" || options.organization && options.artifactsFeed) {
|
|
4713
|
+
return {
|
|
4714
|
+
kind: "azure-artifacts",
|
|
4715
|
+
organization: options.organization ?? await promptText("Azure DevOps organization"),
|
|
4716
|
+
project: options.project ?? await promptText("Azure DevOps project"),
|
|
4717
|
+
artifactsFeed: options.artifactsFeed ?? await promptText("Artifacts feed name")
|
|
4718
|
+
};
|
|
4794
4719
|
}
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
});
|
|
4720
|
+
if (options.registry === "npm" || options.yes) {
|
|
4721
|
+
return { kind: "npm" };
|
|
4722
|
+
}
|
|
4723
|
+
return await promptRegistry();
|
|
4800
4724
|
}
|
|
4801
|
-
function
|
|
4802
|
-
const
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4725
|
+
async function runNew(name, options) {
|
|
4726
|
+
const workspaceName = name ?? await promptText("Workspace name");
|
|
4727
|
+
assertValidProjectName(workspaceName, "Workspace name");
|
|
4728
|
+
const scope = options.scope ?? (options.yes ? `@${workspaceName}` : await promptText("npm scope for publishable packages", `@${workspaceName}`));
|
|
4729
|
+
const registry = await resolveRegistry(options);
|
|
4730
|
+
const ci = await resolveCi(options);
|
|
4731
|
+
const agent = options.agent ?? (options.yes ? "ubuntu-latest" : await promptText(
|
|
4732
|
+
"CI build agent/runner (vmImage, GitHub Actions runner label, or self-hosted pool name)",
|
|
4733
|
+
"ubuntu-latest"
|
|
4734
|
+
));
|
|
4735
|
+
const variableGroup = ci === "github" ? options.variableGroup ?? "Build" : options.variableGroup ?? (options.yes ? "Build" : await promptText("Azure DevOps variable group holding the npm PAT", "Build"));
|
|
4736
|
+
const stack = await resolveStack(options);
|
|
4737
|
+
const nxCloud = options.nxCloud ?? (options.yes ? false : await promptNxCloud());
|
|
4738
|
+
logger.step(`Creating Nx workspace '${workspaceName}' (preset: ts)`);
|
|
4739
|
+
runNpx(
|
|
4740
|
+
[
|
|
4741
|
+
"--yes",
|
|
4742
|
+
"create-nx-workspace@latest",
|
|
4743
|
+
workspaceName,
|
|
4744
|
+
"--preset=ts",
|
|
4745
|
+
"--pm=npm",
|
|
4746
|
+
nxCloud ? `--nxCloud=${nxCloudProviderValue(ci)}` : "--nxCloud=skip",
|
|
4747
|
+
"--no-interactive"
|
|
4748
|
+
],
|
|
4749
|
+
process.cwd()
|
|
4750
|
+
);
|
|
4751
|
+
const workspaceRoot = (0, import_node_path20.join)(process.cwd(), workspaceName);
|
|
4752
|
+
logger.step(
|
|
4753
|
+
"Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
|
|
4754
|
+
);
|
|
4755
|
+
applyOverlay(
|
|
4756
|
+
workspaceRoot,
|
|
4757
|
+
{ workspaceName, scope, registry, agent, variableGroup, ci, stack },
|
|
4758
|
+
logger.detail
|
|
4759
|
+
);
|
|
4760
|
+
(0, import_node_fs13.rmSync)((0, import_node_path20.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
|
|
4761
|
+
(0, import_node_fs13.rmSync)((0, import_node_path20.join)(workspaceRoot, "package-lock.json"), { force: true });
|
|
4762
|
+
logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
|
|
4763
|
+
const installStatus = runShell(
|
|
4764
|
+
"npm",
|
|
4765
|
+
["install", "--save-dev", "husky", "@commitlint/cli", "@commitlint/config-conventional"],
|
|
4766
|
+
workspaceRoot
|
|
4767
|
+
);
|
|
4768
|
+
if (installStatus !== 0) {
|
|
4769
|
+
throw new Error(`npm install of the toolchain failed with exit code ${installStatus}`);
|
|
4811
4770
|
}
|
|
4812
|
-
|
|
4771
|
+
runShell("npx", ["husky"], workspaceRoot);
|
|
4772
|
+
logger.step("Formatting the workspace (eslint --fix, JavaScript Standard Style)");
|
|
4773
|
+
runFormatter(workspaceRoot);
|
|
4774
|
+
logger.success("Done. Next steps:");
|
|
4775
|
+
logger.info(` cd ${workspaceName}`);
|
|
4776
|
+
logger.info(" mnci add react-app web # or: react-lib, react-internal-lib, node-app,");
|
|
4777
|
+
logger.info(" # node-function-app, npm-lib, internal-lib,");
|
|
4778
|
+
logger.info(
|
|
4779
|
+
" # python-app, python-function-app, python-lib, python-internal-lib,"
|
|
4780
|
+
);
|
|
4781
|
+
logger.info(
|
|
4782
|
+
" # go-app, go-function-app, go-lib, go-internal-lib,"
|
|
4783
|
+
);
|
|
4784
|
+
logger.info(
|
|
4785
|
+
" # flutter-app, flutter-lib, flutter-internal-lib"
|
|
4786
|
+
);
|
|
4787
|
+
logger.info(' git add -A && git commit -m "feat: initial workspace"');
|
|
4813
4788
|
}
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
}
|
|
4832
|
-
logger.step(`Reinstalling ${ecosystem} dependencies (${command} ${arguments_.join(" ")})`);
|
|
4833
|
-
if (runShell(command, arguments_, workspaceRoot) !== 0) {
|
|
4834
|
-
logger.warn(
|
|
4835
|
-
`${command} ${arguments_.join(" ")} failed \u2014 the manifests were updated, so re-run it once the cause is fixed.`
|
|
4789
|
+
|
|
4790
|
+
// src/workspace-creation/interactive-wizard.use-case.ts
|
|
4791
|
+
var import_node_path22 = require("path");
|
|
4792
|
+
var import_prompts4 = require("@inquirer/prompts");
|
|
4793
|
+
|
|
4794
|
+
// src/workspace-upgrade/upgrade-workspace.use-case.ts
|
|
4795
|
+
var import_node_fs14 = require("fs");
|
|
4796
|
+
var import_node_path21 = require("path");
|
|
4797
|
+
function resolveRegistry2(options, persisted) {
|
|
4798
|
+
if (options.registry === "azure-artifacts" || options.organization && options.artifactsFeed) {
|
|
4799
|
+
const persistedAzure = persisted?.kind === "azure-artifacts" ? persisted : void 0;
|
|
4800
|
+
const organization = options.organization ?? persistedAzure?.organization;
|
|
4801
|
+
const project = options.project ?? persistedAzure?.project;
|
|
4802
|
+
const artifactsFeed = options.artifactsFeed ?? persistedAzure?.artifactsFeed;
|
|
4803
|
+
if (!organization || !project || !artifactsFeed) {
|
|
4804
|
+
throw new Error(
|
|
4805
|
+
"Azure Artifacts registry needs --organization, --project and --artifacts-feed (none found in nx.json's persisted config either)."
|
|
4836
4806
|
);
|
|
4837
4807
|
}
|
|
4808
|
+
return { kind: "azure-artifacts", organization, project, artifactsFeed };
|
|
4809
|
+
}
|
|
4810
|
+
if (options.registry === "npm") {
|
|
4811
|
+
return { kind: "npm" };
|
|
4812
|
+
}
|
|
4813
|
+
if (persisted) {
|
|
4814
|
+
return persisted;
|
|
4838
4815
|
}
|
|
4816
|
+
throw new Error(
|
|
4817
|
+
"No registry found in nx.json's persisted config. Pass --registry npm or --registry azure-artifacts (with --organization/--project/--artifacts-feed)."
|
|
4818
|
+
);
|
|
4839
4819
|
}
|
|
4840
|
-
function
|
|
4820
|
+
function resolveWorkspaceName(workspaceRoot, persisted) {
|
|
4821
|
+
if (persisted.workspaceName) {
|
|
4822
|
+
return persisted.workspaceName;
|
|
4823
|
+
}
|
|
4841
4824
|
try {
|
|
4842
|
-
const
|
|
4843
|
-
(
|
|
4825
|
+
const existing = (0, import_node_fs14.readdirSync)(workspaceRoot).find(
|
|
4826
|
+
(file) => file.endsWith(".code-workspace") && file !== "undefined.code-workspace"
|
|
4844
4827
|
);
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
return false;
|
|
4848
|
-
}
|
|
4849
|
-
}
|
|
4850
|
-
function applyGoUpdates(workspaceRoot, entries) {
|
|
4851
|
-
for (const entry of entries) {
|
|
4852
|
-
logger.step(`go get ${entry.name}@${entry.latest}`);
|
|
4853
|
-
if (runShell("go", ["get", `${entry.name}@${entry.latest}`], workspaceRoot) !== 0) {
|
|
4854
|
-
logger.warn(`go get ${entry.name}@${entry.latest} failed \u2014 left at ${entry.current}.`);
|
|
4828
|
+
if (existing) {
|
|
4829
|
+
return existing.replace(/\.code-workspace$/, "");
|
|
4855
4830
|
}
|
|
4831
|
+
} catch {
|
|
4856
4832
|
}
|
|
4833
|
+
return (0, import_node_path21.basename)(workspaceRoot);
|
|
4857
4834
|
}
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4835
|
+
function resolveOverlayOptions(workspaceRoot, options, persisted) {
|
|
4836
|
+
const scope = options.scope ?? persisted.scope;
|
|
4837
|
+
if (!scope) {
|
|
4838
|
+
throw new Error("No npm scope found in nx.json's persisted config. Pass --scope explicitly.");
|
|
4861
4839
|
}
|
|
4862
|
-
const
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4840
|
+
const registry = resolveRegistry2(options, persisted.registry);
|
|
4841
|
+
const ci = options.ci ?? persisted.ci;
|
|
4842
|
+
if (!ci) {
|
|
4843
|
+
throw new Error(
|
|
4844
|
+
"No CI provider found in nx.json's persisted config. Pass --ci azure|github|both explicitly."
|
|
4845
|
+
);
|
|
4867
4846
|
}
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4847
|
+
const agent = options.agent ?? persisted.agent;
|
|
4848
|
+
if (!agent) {
|
|
4849
|
+
throw new Error(
|
|
4850
|
+
"No CI build agent found in nx.json's persisted config. Pass --agent explicitly."
|
|
4851
|
+
);
|
|
4873
4852
|
}
|
|
4874
|
-
const
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4853
|
+
const variableGroup = options.variableGroup ?? persisted.variableGroup ?? "Build";
|
|
4854
|
+
const testRunner = options.testRunner ?? persisted.stack?.testRunner;
|
|
4855
|
+
if (!testRunner) {
|
|
4856
|
+
throw new Error(
|
|
4857
|
+
"No testRunner found in nx.json's persisted config. Pass --test-runner explicitly."
|
|
4858
|
+
);
|
|
4880
4859
|
}
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4860
|
+
return {
|
|
4861
|
+
workspaceName: resolveWorkspaceName(workspaceRoot, persisted),
|
|
4862
|
+
scope,
|
|
4863
|
+
registry,
|
|
4864
|
+
agent,
|
|
4865
|
+
variableGroup,
|
|
4866
|
+
ci,
|
|
4867
|
+
stack: { testRunner }
|
|
4868
|
+
};
|
|
4869
|
+
}
|
|
4870
|
+
function runUpgrade(workspaceRoot, options) {
|
|
4871
|
+
if (!fileExists((0, import_node_path21.join)(workspaceRoot, "nx.json"))) {
|
|
4872
|
+
throw new Error(
|
|
4873
|
+
`No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
|
|
4874
|
+
);
|
|
4885
4875
|
}
|
|
4886
|
-
const
|
|
4887
|
-
const
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4876
|
+
const persisted = readMnciConfig(workspaceRoot);
|
|
4877
|
+
const resolved = resolveOverlayOptions(workspaceRoot, options, persisted);
|
|
4878
|
+
logger.step("Re-applying the MoNecromanCI overlay");
|
|
4879
|
+
applyOverlay(workspaceRoot, resolved, logger.detail);
|
|
4880
|
+
const repaired = repairRollupSourceMaps(workspaceRoot);
|
|
4881
|
+
if (repaired.length > 0) {
|
|
4882
|
+
logger.step("Enabling source maps in rollup configs (breakpoints in .ts files)");
|
|
4883
|
+
for (const path of repaired) {
|
|
4884
|
+
logger.detail(`updated ${path}`);
|
|
4885
|
+
}
|
|
4891
4886
|
}
|
|
4892
|
-
|
|
4893
|
-
if (
|
|
4894
|
-
|
|
4887
|
+
const repairedManifests = repairPublishableManifests(workspaceRoot);
|
|
4888
|
+
if (repairedManifests.length > 0) {
|
|
4889
|
+
logger.step("Repointing `types` at the real declaration file");
|
|
4890
|
+
for (const path of repairedManifests) {
|
|
4891
|
+
logger.detail(`updated ${path}`);
|
|
4892
|
+
}
|
|
4895
4893
|
}
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
logger.step("
|
|
4899
|
-
|
|
4894
|
+
const upgradedDtsPlugins = upgradeDeclarationSpecifierPlugins(workspaceRoot);
|
|
4895
|
+
if (upgradedDtsPlugins.length > 0) {
|
|
4896
|
+
logger.step("Upgrading declaration specifiers to resolve under nodenext");
|
|
4897
|
+
for (const path of upgradedDtsPlugins) {
|
|
4898
|
+
logger.detail(`updated ${path}`);
|
|
4899
|
+
}
|
|
4900
4900
|
}
|
|
4901
|
-
logger.
|
|
4902
|
-
|
|
4901
|
+
logger.step(
|
|
4902
|
+
"Formatting the workspace (eslint --fix) \u2014 the slowest step, minutes on a large workspace"
|
|
4903
4903
|
);
|
|
4904
|
+
runFormatter(workspaceRoot);
|
|
4905
|
+
logger.success("Done. Review the changes with `git diff` before committing.");
|
|
4904
4906
|
}
|
|
4905
4907
|
|
|
4906
|
-
// src/
|
|
4907
|
-
|
|
4908
|
-
|
|
4908
|
+
// src/workspace-creation/interactive-wizard.use-case.ts
|
|
4909
|
+
async function runInteractive() {
|
|
4910
|
+
const inWorkspace = fileExists((0, import_node_path22.join)(process.cwd(), "nx.json"));
|
|
4911
|
+
const choiceNew = { name: "Create a new monorepo", value: "new" };
|
|
4912
|
+
const choiceAdd = { name: "Add a project to this workspace", value: "add" };
|
|
4913
|
+
const choiceUpgrade = {
|
|
4914
|
+
name: "Upgrade this workspace (re-apply the latest overlay)",
|
|
4915
|
+
value: "upgrade"
|
|
4916
|
+
};
|
|
4917
|
+
const action = await (0, import_prompts4.select)({
|
|
4918
|
+
message: "What would you like to do?",
|
|
4919
|
+
choices: inWorkspace ? [choiceAdd, choiceUpgrade, choiceNew] : [choiceNew, choiceAdd, choiceUpgrade]
|
|
4920
|
+
});
|
|
4921
|
+
if (action === "new") {
|
|
4922
|
+
await runNew(void 0, {});
|
|
4923
|
+
} else if (action === "upgrade") {
|
|
4924
|
+
runUpgrade(process.cwd(), {});
|
|
4925
|
+
} else {
|
|
4926
|
+
await runAdd(void 0, void 0, {});
|
|
4927
|
+
}
|
|
4928
|
+
}
|
|
4929
|
+
|
|
4930
|
+
// src/cli-version/check-for-update.use-case.ts
|
|
4931
|
+
var import_node_fs15 = require("fs");
|
|
4932
|
+
var import_node_path23 = require("path");
|
|
4909
4933
|
var import_cross_spawn2 = __toESM(require("cross-spawn"));
|
|
4910
4934
|
var PACKAGE_NAME = "@mnci/cli";
|
|
4911
4935
|
var REGISTRY_TIMEOUT_MS = 2e3;
|
|
@@ -4961,15 +4985,15 @@ function releaseTriple(version) {
|
|
|
4961
4985
|
}
|
|
4962
4986
|
function readCliVersion(packageDirectory) {
|
|
4963
4987
|
try {
|
|
4964
|
-
const manifestPath = (0,
|
|
4965
|
-
const manifest = JSON.parse((0,
|
|
4988
|
+
const manifestPath = (0, import_node_path23.join)(packageDirectory, "..", "package.json");
|
|
4989
|
+
const manifest = JSON.parse((0, import_node_fs15.readFileSync)(manifestPath, "utf8"));
|
|
4966
4990
|
return manifest.version ?? "0.0.0";
|
|
4967
4991
|
} catch {
|
|
4968
4992
|
return "0.0.0";
|
|
4969
4993
|
}
|
|
4970
4994
|
}
|
|
4971
4995
|
|
|
4972
|
-
// src/
|
|
4996
|
+
// src/main.ts
|
|
4973
4997
|
function buildProgram(cliVersion) {
|
|
4974
4998
|
const program = new import_commander.Command();
|
|
4975
4999
|
program.name("mnci").description("MoNecromanCI \u2014 a thin CLI over official Nx plugins").version(cliVersion, "-v, --version", "display the version");
|