@dennisrepa/elevate 1.4.0 → 1.5.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 +3 -1
- package/dist/cli.js +1339 -332
- package/dist/cli.js.map +1 -1
- package/docs/de.md +11 -0
- package/docs/en.md +11 -0
- package/package.json +4 -1
package/dist/cli.js
CHANGED
|
@@ -4,8 +4,65 @@
|
|
|
4
4
|
import { render } from "ink";
|
|
5
5
|
|
|
6
6
|
// src/config.ts
|
|
7
|
-
import { existsSync as
|
|
8
|
-
import { dirname as
|
|
7
|
+
import { existsSync as existsSync16, readFileSync as readFileSync16 } from "fs";
|
|
8
|
+
import { dirname as dirname10, join as join23, resolve as resolve14 } from "path";
|
|
9
|
+
|
|
10
|
+
// src/domain/version-policy.ts
|
|
11
|
+
import semver2 from "semver";
|
|
12
|
+
|
|
13
|
+
// src/domain/versions.ts
|
|
14
|
+
import semver from "semver";
|
|
15
|
+
var PRE_RELEASE = /(?:^|[.\-_+]|(?<=\d))(alpha|beta|rc|cr|milestone|preview|snapshot|ea|dev|pre|(?:a|b|m)(?=\d))(?=$|[.\-_+]|\d)/i;
|
|
16
|
+
function isPreReleaseVersion(version) {
|
|
17
|
+
return PRE_RELEASE.test(version);
|
|
18
|
+
}
|
|
19
|
+
function extractPreReleaseTag(version) {
|
|
20
|
+
const match = version.match(PRE_RELEASE);
|
|
21
|
+
if (!match) return void 0;
|
|
22
|
+
const token = match[1].toLowerCase();
|
|
23
|
+
if (token === "a") return "ALPHA";
|
|
24
|
+
if (token === "b") return "BETA";
|
|
25
|
+
if (token === "m") {
|
|
26
|
+
const number = version.slice((match.index ?? 0) + match[0].length).match(/^\d+/)?.[0];
|
|
27
|
+
return `M${number ?? ""}`;
|
|
28
|
+
}
|
|
29
|
+
return token.toUpperCase();
|
|
30
|
+
}
|
|
31
|
+
function cleanJavaVersion(version) {
|
|
32
|
+
const match = version.match(/^v?(\d+(?:\.\d+){0,2})/);
|
|
33
|
+
if (!match) return null;
|
|
34
|
+
return semver.coerce(match[1])?.version ?? null;
|
|
35
|
+
}
|
|
36
|
+
function diffVersions(from, to) {
|
|
37
|
+
const kind = semver.diff(from, to);
|
|
38
|
+
if (kind === "major" || kind === "premajor") return "major";
|
|
39
|
+
if (kind === "minor" || kind === "preminor") return "minor";
|
|
40
|
+
return "patch";
|
|
41
|
+
}
|
|
42
|
+
function rangePrefix(range) {
|
|
43
|
+
if (range.startsWith("^")) return "^";
|
|
44
|
+
if (range.startsWith("~")) return "~";
|
|
45
|
+
return "";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/domain/version-policy.ts
|
|
49
|
+
function semverDiff(current, target, normalize) {
|
|
50
|
+
const to = normalize(target);
|
|
51
|
+
if (!to || !semver2.valid(current)) return "minor";
|
|
52
|
+
return semver2.eq(current, to) ? "patch" : diffVersions(current, to);
|
|
53
|
+
}
|
|
54
|
+
var npmVersionPolicy = {
|
|
55
|
+
normalize: (v) => semver2.valid(v),
|
|
56
|
+
rewrite: (current, target) => `${rangePrefix(current)}${target}`,
|
|
57
|
+
diff: (current, target) => semverDiff(current, target, (v) => semver2.valid(v)),
|
|
58
|
+
allowsManualTargets: true
|
|
59
|
+
};
|
|
60
|
+
var mavenVersionPolicy = {
|
|
61
|
+
normalize: cleanJavaVersion,
|
|
62
|
+
rewrite: (_current, target) => target,
|
|
63
|
+
diff: (current, target) => semverDiff(current, target, cleanJavaVersion),
|
|
64
|
+
allowsManualTargets: false
|
|
65
|
+
};
|
|
9
66
|
|
|
10
67
|
// src/adapters/npm/npm-package-manager.ts
|
|
11
68
|
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
@@ -37,7 +94,7 @@ var UnsafeArgumentError = class extends Error {
|
|
|
37
94
|
var IS_WINDOWS = process.platform === "win32";
|
|
38
95
|
var WINDOWS_UNSAFE = /["%!^&|<>\r\n]/;
|
|
39
96
|
function runCommand(command, args, options) {
|
|
40
|
-
return new Promise((
|
|
97
|
+
return new Promise((resolve16, reject) => {
|
|
41
98
|
const spawnOptions = { cwd: options.cwd, windowsHide: true, env: process.env };
|
|
42
99
|
let child2;
|
|
43
100
|
if (IS_WINDOWS) {
|
|
@@ -67,19 +124,19 @@ function runCommand(command, args, options) {
|
|
|
67
124
|
});
|
|
68
125
|
child2.on("close", (code) => {
|
|
69
126
|
if (timer) clearTimeout(timer);
|
|
70
|
-
|
|
127
|
+
resolve16({ exitCode: code ?? 1, stdout, stderr, timedOut });
|
|
71
128
|
});
|
|
72
129
|
});
|
|
73
130
|
}
|
|
74
131
|
function runShellScript(script, options) {
|
|
75
|
-
return new Promise((
|
|
132
|
+
return new Promise((resolve16) => {
|
|
76
133
|
exec(
|
|
77
134
|
script,
|
|
78
135
|
{ cwd: options.cwd, timeout: options.timeoutMs, maxBuffer: 64 * 1024 * 1024, windowsHide: true },
|
|
79
136
|
(error, stdout, stderr) => {
|
|
80
137
|
const timedOut = Boolean(error?.killed);
|
|
81
138
|
const exitCode = error ? typeof error.code === "number" ? error.code : 1 : 0;
|
|
82
|
-
|
|
139
|
+
resolve16({ exitCode, stdout, stderr, timedOut });
|
|
83
140
|
}
|
|
84
141
|
);
|
|
85
142
|
});
|
|
@@ -304,7 +361,7 @@ var NpmModuleDiscoveryAdapter = class {
|
|
|
304
361
|
};
|
|
305
362
|
|
|
306
363
|
// src/adapters/npm/npm-registry.ts
|
|
307
|
-
import
|
|
364
|
+
import semver3 from "semver";
|
|
308
365
|
|
|
309
366
|
// src/domain/origin.ts
|
|
310
367
|
function buildWorkspaceIndex(modules) {
|
|
@@ -434,13 +491,13 @@ var NpmRegistryAdapter = class {
|
|
|
434
491
|
if (!isValidPackageName(coordinate.identifier)) return null;
|
|
435
492
|
if (channel === "stable") {
|
|
436
493
|
const latest = await this.view(coordinate.identifier, "dist-tags.latest", cwd);
|
|
437
|
-
if (typeof latest === "string" &&
|
|
494
|
+
if (typeof latest === "string" && semver3.valid(latest) && semver3.prerelease(latest) === null) {
|
|
438
495
|
return latest;
|
|
439
496
|
}
|
|
440
497
|
const releases = (await this.getAllVersions(coordinate, cwd)).filter(
|
|
441
|
-
(v) =>
|
|
498
|
+
(v) => semver3.valid(v) && semver3.prerelease(v) === null
|
|
442
499
|
);
|
|
443
|
-
return releases.length > 0 ?
|
|
500
|
+
return releases.length > 0 ? semver3.rsort(releases)[0] : null;
|
|
444
501
|
}
|
|
445
502
|
const version = await this.view(coordinate.identifier, "version", cwd);
|
|
446
503
|
return typeof version === "string" && version ? version : null;
|
|
@@ -470,42 +527,7 @@ var NpmRegistryAdapter = class {
|
|
|
470
527
|
// src/adapters/npm/npm-scanner.ts
|
|
471
528
|
import { readFileSync as readFileSync4 } from "fs";
|
|
472
529
|
import { join as join5 } from "path";
|
|
473
|
-
import
|
|
474
|
-
|
|
475
|
-
// src/domain/versions.ts
|
|
476
|
-
import semver2 from "semver";
|
|
477
|
-
var PRE_RELEASE = /(?:^|[.\-_+]|(?<=\d))(alpha|beta|rc|cr|milestone|preview|snapshot|ea|dev|pre|(?:a|b|m)(?=\d))(?=$|[.\-_+]|\d)/i;
|
|
478
|
-
function isPreReleaseVersion(version) {
|
|
479
|
-
return PRE_RELEASE.test(version);
|
|
480
|
-
}
|
|
481
|
-
function extractPreReleaseTag(version) {
|
|
482
|
-
const match = version.match(PRE_RELEASE);
|
|
483
|
-
if (!match) return void 0;
|
|
484
|
-
const token = match[1].toLowerCase();
|
|
485
|
-
if (token === "a") return "ALPHA";
|
|
486
|
-
if (token === "b") return "BETA";
|
|
487
|
-
if (token === "m") {
|
|
488
|
-
const number = version.slice((match.index ?? 0) + match[0].length).match(/^\d+/)?.[0];
|
|
489
|
-
return `M${number ?? ""}`;
|
|
490
|
-
}
|
|
491
|
-
return token.toUpperCase();
|
|
492
|
-
}
|
|
493
|
-
function cleanJavaVersion(version) {
|
|
494
|
-
const match = version.match(/^v?(\d+(?:\.\d+){0,2})/);
|
|
495
|
-
if (!match) return null;
|
|
496
|
-
return semver2.coerce(match[1])?.version ?? null;
|
|
497
|
-
}
|
|
498
|
-
function diffVersions(from, to) {
|
|
499
|
-
const kind = semver2.diff(from, to);
|
|
500
|
-
if (kind === "major" || kind === "premajor") return "major";
|
|
501
|
-
if (kind === "minor" || kind === "preminor") return "minor";
|
|
502
|
-
return "patch";
|
|
503
|
-
}
|
|
504
|
-
function rangePrefix(range) {
|
|
505
|
-
if (range.startsWith("^")) return "^";
|
|
506
|
-
if (range.startsWith("~")) return "~";
|
|
507
|
-
return "";
|
|
508
|
-
}
|
|
530
|
+
import semver4 from "semver";
|
|
509
531
|
|
|
510
532
|
// src/adapters/shared/pool.ts
|
|
511
533
|
async function mapPooled(items, concurrency, fn) {
|
|
@@ -636,9 +658,9 @@ var NpmDependencyAdapter = class {
|
|
|
636
658
|
if (!latest) {
|
|
637
659
|
return { skipped: { identifier: dep.name, origin: origin.kind, reason: "lookup-failed" } };
|
|
638
660
|
}
|
|
639
|
-
const currentClean =
|
|
640
|
-
if (!currentClean || !
|
|
641
|
-
const tag =
|
|
661
|
+
const currentClean = semver4.minVersion(dep.range)?.version;
|
|
662
|
+
if (!currentClean || !semver4.valid(latest) || !semver4.gt(latest, currentClean)) return null;
|
|
663
|
+
const tag = semver4.prerelease(latest) ? extractPreReleaseTag(latest) ?? "PRE" : void 0;
|
|
642
664
|
const diff = diffVersions(currentClean, latest);
|
|
643
665
|
return {
|
|
644
666
|
candidate: {
|
|
@@ -673,18 +695,18 @@ function declaredDependencies(pkg) {
|
|
|
673
695
|
function isUpgradableRange(range) {
|
|
674
696
|
const trimmed = range.trim();
|
|
675
697
|
if (trimmed === "" || trimmed === "*" || trimmed.toLowerCase() === "x") return false;
|
|
676
|
-
return
|
|
698
|
+
return semver4.validRange(trimmed) !== null;
|
|
677
699
|
}
|
|
678
700
|
function alignmentCandidate(dep, target, origin, pnpm = false) {
|
|
679
701
|
const split = pnpm ? splitRangeProtocol(dep.range) : { protocol: void 0, rest: dep.range };
|
|
680
702
|
const range = split.protocol === "workspace" ? split.rest : dep.range;
|
|
681
703
|
const protocol = split.protocol === "workspace" ? "workspace:" : "";
|
|
682
704
|
const local = target.version;
|
|
683
|
-
if (!local || !
|
|
684
|
-
if (
|
|
685
|
-
const currentClean =
|
|
705
|
+
if (!local || !semver4.valid(local) || !isUpgradableRange(range)) return null;
|
|
706
|
+
if (semver4.satisfies(local, range, { includePrerelease: true })) return null;
|
|
707
|
+
const currentClean = semver4.minVersion(range)?.version ?? "0.0.0";
|
|
686
708
|
const diff = diffVersions(currentClean, local);
|
|
687
|
-
const tag =
|
|
709
|
+
const tag = semver4.prerelease(local) ? extractPreReleaseTag(local) ?? "PRE" : void 0;
|
|
688
710
|
return {
|
|
689
711
|
coordinate: { identifier: dep.name, artifact: dep.name, ecosystem: "npm" },
|
|
690
712
|
currentRange: dep.range,
|
|
@@ -1035,6 +1057,7 @@ var NpmEcosystemStrategy = class {
|
|
|
1035
1057
|
}
|
|
1036
1058
|
icon = "\u{1F4E6}";
|
|
1037
1059
|
manifestFile = "package.json";
|
|
1060
|
+
versioning = npmVersionPolicy;
|
|
1038
1061
|
discovery = new NpmModuleDiscoveryAdapter();
|
|
1039
1062
|
registry = new NpmRegistryAdapter();
|
|
1040
1063
|
reader = new NpmDependencyAdapter(this.registry, this.registry.config);
|
|
@@ -1049,17 +1072,749 @@ var NpmEcosystemStrategy = class {
|
|
|
1049
1072
|
}
|
|
1050
1073
|
};
|
|
1051
1074
|
|
|
1052
|
-
// src/adapters/
|
|
1053
|
-
import
|
|
1054
|
-
import { dirname as dirname5, join as join11, relative as relative3, resolve as resolve6 } from "path";
|
|
1075
|
+
// src/adapters/cargo/cargo-strategy.ts
|
|
1076
|
+
import semver10 from "semver";
|
|
1055
1077
|
|
|
1056
|
-
// src/adapters/
|
|
1057
|
-
import { existsSync as
|
|
1058
|
-
import { join as
|
|
1078
|
+
// src/adapters/cargo/cargo-discovery.ts
|
|
1079
|
+
import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync8 } from "fs";
|
|
1080
|
+
import { dirname as dirname4, join as join9, relative as relative3, resolve as resolve4 } from "path";
|
|
1059
1081
|
|
|
1060
|
-
// src/adapters/
|
|
1061
|
-
|
|
1062
|
-
|
|
1082
|
+
// src/adapters/cargo/cargo-command.ts
|
|
1083
|
+
var CargoUnavailableError = class extends Error {
|
|
1084
|
+
constructor() {
|
|
1085
|
+
super("Cargo is not available: no `cargo` on the PATH. Install Rust (https://rustup.rs).");
|
|
1086
|
+
this.name = "CargoUnavailableError";
|
|
1087
|
+
}
|
|
1088
|
+
};
|
|
1089
|
+
async function runCargo(args, options) {
|
|
1090
|
+
let result;
|
|
1091
|
+
try {
|
|
1092
|
+
result = await runCommand("cargo", args, options);
|
|
1093
|
+
} catch (err) {
|
|
1094
|
+
if (err.code === "ENOENT") throw new CargoUnavailableError();
|
|
1095
|
+
throw err;
|
|
1096
|
+
}
|
|
1097
|
+
if (looksLikeMissingCommand(result) && !await cargoStarts(options.cwd)) throw new CargoUnavailableError();
|
|
1098
|
+
return result;
|
|
1099
|
+
}
|
|
1100
|
+
function looksLikeMissingCommand(result) {
|
|
1101
|
+
return process.platform === "win32" && result.exitCode !== 0 && !result.stdout.trim() && !/^error/m.test(result.stderr);
|
|
1102
|
+
}
|
|
1103
|
+
async function cargoStarts(cwd) {
|
|
1104
|
+
try {
|
|
1105
|
+
return (await runCommand("cargo", ["--version"], { cwd, timeoutMs: 3e4 })).exitCode === 0;
|
|
1106
|
+
} catch {
|
|
1107
|
+
return false;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
function cargoErrors(output) {
|
|
1111
|
+
const lines = output.split(/\r?\n/);
|
|
1112
|
+
const errors = lines.filter((l) => /^error(\[E\d+\])?:/.test(l));
|
|
1113
|
+
const summary = (errors.length > 0 ? errors : lines.filter((l) => l.trim()).slice(-10)).slice(0, 12);
|
|
1114
|
+
if (/toolchain '.+' is not installed/.test(output)) {
|
|
1115
|
+
summary.push("Install it with `rustup toolchain install` (the project pins it in rust-toolchain.toml).");
|
|
1116
|
+
}
|
|
1117
|
+
return summary.join("\n");
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
// src/adapters/cargo/cargo-metadata.ts
|
|
1121
|
+
var CargoMetadataError = class extends Error {
|
|
1122
|
+
constructor(message2, dir) {
|
|
1123
|
+
super(message2);
|
|
1124
|
+
this.dir = dir;
|
|
1125
|
+
this.name = "CargoMetadataError";
|
|
1126
|
+
}
|
|
1127
|
+
dir;
|
|
1128
|
+
};
|
|
1129
|
+
async function loadMetadata(dir) {
|
|
1130
|
+
const result = await runCargo(["metadata", "--no-deps", "--format-version", "1"], { cwd: dir, timeoutMs: 6e4 });
|
|
1131
|
+
if (result.exitCode !== 0) {
|
|
1132
|
+
throw new CargoMetadataError(`cargo metadata failed in ${dir}: ${cargoErrors(result.stderr || result.stdout)}`, dir);
|
|
1133
|
+
}
|
|
1134
|
+
try {
|
|
1135
|
+
return JSON.parse(result.stdout);
|
|
1136
|
+
} catch {
|
|
1137
|
+
throw new CargoMetadataError(`cargo metadata printed no JSON in ${dir}.`, dir);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// src/adapters/cargo/cargo-discovery.ts
|
|
1142
|
+
var IGNORED = /* @__PURE__ */ new Set(["target", "node_modules", "vendor", "dist", "build", "out", "src"]);
|
|
1143
|
+
var norm = (path) => resolve4(path).replace(/\\/g, "/");
|
|
1144
|
+
function findManifests(root) {
|
|
1145
|
+
const found = [];
|
|
1146
|
+
const walk = (dir, depth) => {
|
|
1147
|
+
let entries2;
|
|
1148
|
+
try {
|
|
1149
|
+
entries2 = readdirSync(dir, { withFileTypes: true });
|
|
1150
|
+
} catch {
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
if (entries2.some((e) => e.isFile() && e.name === "Cargo.toml")) found.push(join9(dir, "Cargo.toml"));
|
|
1154
|
+
for (const entry of entries2) {
|
|
1155
|
+
if (entry.isDirectory() && !entry.name.startsWith(".") && !IGNORED.has(entry.name)) walk(join9(dir, entry.name), depth + 1);
|
|
1156
|
+
}
|
|
1157
|
+
};
|
|
1158
|
+
walk(root, 0);
|
|
1159
|
+
return found.sort((a, b) => a.split(/[\\/]/).length - b.split(/[\\/]/).length || a.localeCompare(b));
|
|
1160
|
+
}
|
|
1161
|
+
var declaresWorkspace = (dir) => {
|
|
1162
|
+
try {
|
|
1163
|
+
return /^\s*\[workspace[\].]/m.test(readFileSync8(join9(dir, "Cargo.toml"), "utf8"));
|
|
1164
|
+
} catch {
|
|
1165
|
+
return false;
|
|
1166
|
+
}
|
|
1167
|
+
};
|
|
1168
|
+
var CargoModuleDiscoveryAdapter = class {
|
|
1169
|
+
problems = [];
|
|
1170
|
+
diagnostics() {
|
|
1171
|
+
return [...this.problems];
|
|
1172
|
+
}
|
|
1173
|
+
/** The nearest ancestor declaring `[workspace]`; failing that, the nearest directory with a manifest. */
|
|
1174
|
+
findRoot(startDir, boundaryDir) {
|
|
1175
|
+
const boundary = boundaryDir ? resolve4(boundaryDir) : void 0;
|
|
1176
|
+
const chain = [];
|
|
1177
|
+
for (let dir = resolve4(startDir); ; ) {
|
|
1178
|
+
chain.push(dir);
|
|
1179
|
+
const parent = dirname4(dir);
|
|
1180
|
+
if (dir === boundary || parent === dir) break;
|
|
1181
|
+
dir = parent;
|
|
1182
|
+
}
|
|
1183
|
+
return chain.find(declaresWorkspace) ?? chain.find((d) => existsSync5(join9(d, "Cargo.toml")));
|
|
1184
|
+
}
|
|
1185
|
+
async discover(rootDir) {
|
|
1186
|
+
const covered = /* @__PURE__ */ new Set();
|
|
1187
|
+
const modules = [];
|
|
1188
|
+
const errors = [];
|
|
1189
|
+
this.problems = [];
|
|
1190
|
+
for (const manifest of findManifests(rootDir)) {
|
|
1191
|
+
if (covered.has(norm(manifest))) continue;
|
|
1192
|
+
try {
|
|
1193
|
+
const metadata = await loadMetadata(dirname4(manifest));
|
|
1194
|
+
for (const pkg of metadata.packages) {
|
|
1195
|
+
covered.add(norm(pkg.manifest_path));
|
|
1196
|
+
const path = dirname4(pkg.manifest_path);
|
|
1197
|
+
const relPath = relative3(rootDir, path).replace(/\\/g, "/");
|
|
1198
|
+
modules.push({
|
|
1199
|
+
id: pkg.name,
|
|
1200
|
+
name: pkg.name,
|
|
1201
|
+
path,
|
|
1202
|
+
relPath: relPath === "" ? "Root" : relPath,
|
|
1203
|
+
ecosystem: "cargo",
|
|
1204
|
+
isRoot: relPath === "",
|
|
1205
|
+
version: pkg.version,
|
|
1206
|
+
aggregatorDir: resolve4(metadata.workspace_root)
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
} catch (err) {
|
|
1210
|
+
if (err instanceof CargoUnavailableError) throw err;
|
|
1211
|
+
if (err instanceof CargoMetadataError) {
|
|
1212
|
+
errors.push(err);
|
|
1213
|
+
this.problems.push(err.message);
|
|
1214
|
+
} else throw err;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
if (modules.length === 0 && errors.length > 0) throw errors[0];
|
|
1218
|
+
modules.sort((a, b) => a.isRoot ? -1 : b.isRoot ? 1 : a.relPath.localeCompare(b.relPath));
|
|
1219
|
+
const taken = /* @__PURE__ */ new Set();
|
|
1220
|
+
for (const module of modules) {
|
|
1221
|
+
if (taken.has(module.id)) module.id = `${module.name} (${module.relPath})`;
|
|
1222
|
+
taken.add(module.id);
|
|
1223
|
+
}
|
|
1224
|
+
return modules;
|
|
1225
|
+
}
|
|
1226
|
+
};
|
|
1227
|
+
|
|
1228
|
+
// src/adapters/cargo/cargo-config.ts
|
|
1229
|
+
import { existsSync as existsSync6, readFileSync as readFileSync9 } from "fs";
|
|
1230
|
+
import { homedir } from "os";
|
|
1231
|
+
import { dirname as dirname5, isAbsolute, join as join10, resolve as resolve5 } from "path";
|
|
1232
|
+
import { getStaticTOMLValue, parseTOML } from "toml-eslint-parser";
|
|
1233
|
+
var CRATES_IO = "registry+https://github.com/rust-lang/crates.io-index";
|
|
1234
|
+
var PUBLIC_HOSTS = /* @__PURE__ */ new Set(["index.crates.io", "static.crates.io", "crates.io", "github.com"]);
|
|
1235
|
+
var UnsupportedSourceError = class extends Error {
|
|
1236
|
+
constructor(detail) {
|
|
1237
|
+
super(`Cargo is configured to use a package source Elevate cannot read (${detail}). No lookup was made.`);
|
|
1238
|
+
this.name = "UnsupportedSourceError";
|
|
1239
|
+
}
|
|
1240
|
+
};
|
|
1241
|
+
function isPublicIndex(index) {
|
|
1242
|
+
try {
|
|
1243
|
+
const url = new URL(index.replace(/^(sparse|registry)\+/, ""));
|
|
1244
|
+
return PUBLIC_HOSTS.has(url.hostname.toLowerCase()) && (url.hostname !== "github.com" || url.pathname.includes("crates.io-index"));
|
|
1245
|
+
} catch {
|
|
1246
|
+
return false;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
function merge(base, top) {
|
|
1250
|
+
const out = { ...base };
|
|
1251
|
+
for (const [key, value] of Object.entries(top)) {
|
|
1252
|
+
const existing = out[key];
|
|
1253
|
+
out[key] = value && typeof value === "object" && !Array.isArray(value) && existing && typeof existing === "object" && !Array.isArray(existing) ? merge(existing, value) : value;
|
|
1254
|
+
}
|
|
1255
|
+
return out;
|
|
1256
|
+
}
|
|
1257
|
+
function loadFile(file, depth = 0) {
|
|
1258
|
+
let table;
|
|
1259
|
+
try {
|
|
1260
|
+
table = getStaticTOMLValue(parseTOML(readFileSync9(file, "utf8")));
|
|
1261
|
+
} catch {
|
|
1262
|
+
return {};
|
|
1263
|
+
}
|
|
1264
|
+
const includes = table.include;
|
|
1265
|
+
delete table.include;
|
|
1266
|
+
if (!Array.isArray(includes) || depth > 4) return table;
|
|
1267
|
+
let included = {};
|
|
1268
|
+
for (const entry of includes) {
|
|
1269
|
+
const path = typeof entry === "string" ? entry : entry?.path;
|
|
1270
|
+
if (typeof path === "string") included = merge(included, loadFile(isAbsolute(path) ? path : resolve5(dirname5(file), path), depth + 1));
|
|
1271
|
+
}
|
|
1272
|
+
return merge(included, table);
|
|
1273
|
+
}
|
|
1274
|
+
var CargoConfigReader = class {
|
|
1275
|
+
env;
|
|
1276
|
+
cache = /* @__PURE__ */ new Map();
|
|
1277
|
+
constructor(options = {}) {
|
|
1278
|
+
this.env = options.env ?? process.env;
|
|
1279
|
+
}
|
|
1280
|
+
/** The configuration as Cargo would merge it for `cwd`: nearest file first, CARGO_HOME last. */
|
|
1281
|
+
config(cwd) {
|
|
1282
|
+
const start = resolve5(cwd);
|
|
1283
|
+
let cached = this.cache.get(start);
|
|
1284
|
+
if (cached) return cached;
|
|
1285
|
+
const files = [];
|
|
1286
|
+
for (let dir = start; ; ) {
|
|
1287
|
+
for (const name of ["config.toml", "config"]) {
|
|
1288
|
+
const file = join10(dir, ".cargo", name);
|
|
1289
|
+
if (existsSync6(file)) {
|
|
1290
|
+
files.push(file);
|
|
1291
|
+
break;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
const parent = dirname5(dir);
|
|
1295
|
+
if (parent === dir) break;
|
|
1296
|
+
dir = parent;
|
|
1297
|
+
}
|
|
1298
|
+
const home2 = this.env.CARGO_HOME ?? join10(homedir(), ".cargo");
|
|
1299
|
+
for (const name of ["config.toml", "config"]) {
|
|
1300
|
+
const file = join10(home2, name);
|
|
1301
|
+
if (existsSync6(file) && !files.includes(file)) {
|
|
1302
|
+
files.push(file);
|
|
1303
|
+
break;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
cached = files.reduceRight((acc, file) => merge(acc, loadFile(file)), {});
|
|
1307
|
+
this.cache.set(start, cached);
|
|
1308
|
+
return cached;
|
|
1309
|
+
}
|
|
1310
|
+
/** `[source.*]` comes from files only: Cargo ignores `CARGO_SOURCE_*` variables (checked with Cargo 1.97). */
|
|
1311
|
+
sourceField(config, source, field) {
|
|
1312
|
+
const value = config.source?.[source]?.[field];
|
|
1313
|
+
return typeof value === "string" ? value : void 0;
|
|
1314
|
+
}
|
|
1315
|
+
/**
|
|
1316
|
+
* The index a lookup goes to. `registry` is the index URL of an alternative
|
|
1317
|
+
* registry as Cargo reports it, or undefined for crates.io.
|
|
1318
|
+
*/
|
|
1319
|
+
async resolve(registry, cwd) {
|
|
1320
|
+
const config = this.config(cwd);
|
|
1321
|
+
let name = registry === void 0 ? "crates-io" : this.sourceNamed(config, registry);
|
|
1322
|
+
let index = registry ?? CRATES_IO;
|
|
1323
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1324
|
+
while (name !== void 0) {
|
|
1325
|
+
if (seen.has(name)) throw new UnsupportedSourceError(`replacement cycle at '${name}'`);
|
|
1326
|
+
seen.add(name);
|
|
1327
|
+
const next = this.sourceField(config, name, "replace-with");
|
|
1328
|
+
if (next === void 0) break;
|
|
1329
|
+
for (const kind of ["directory", "local-registry", "git"]) {
|
|
1330
|
+
if (this.sourceField(config, next, kind) !== void 0) throw new UnsupportedSourceError(`'${next}' is a ${kind} source`);
|
|
1331
|
+
}
|
|
1332
|
+
const target = this.sourceField(config, next, "registry");
|
|
1333
|
+
if (target !== void 0) {
|
|
1334
|
+
index = target;
|
|
1335
|
+
name = next;
|
|
1336
|
+
if (this.sourceField(config, next, "replace-with") === void 0) break;
|
|
1337
|
+
continue;
|
|
1338
|
+
}
|
|
1339
|
+
if (this.sourceField(config, next, "replace-with") === void 0) {
|
|
1340
|
+
throw new UnsupportedSourceError(`'${next}' has no registry`);
|
|
1341
|
+
}
|
|
1342
|
+
name = next;
|
|
1343
|
+
}
|
|
1344
|
+
return { index, isPublic: isPublicIndex(index) };
|
|
1345
|
+
}
|
|
1346
|
+
/** Name of the `[source.*]` table whose registry is `url`, if any. */
|
|
1347
|
+
sourceNamed(config, url) {
|
|
1348
|
+
return Object.keys(config.source ?? {}).find((n) => this.sourceField(config, n, "registry") === url);
|
|
1349
|
+
}
|
|
1350
|
+
};
|
|
1351
|
+
|
|
1352
|
+
// src/version.ts
|
|
1353
|
+
import { createRequire } from "module";
|
|
1354
|
+
var VERSION = true ? "1.5.0" : fromPackage();
|
|
1355
|
+
|
|
1356
|
+
// src/adapters/cargo/cargo-index.ts
|
|
1357
|
+
var RegistryAuthRequiredError = class extends Error {
|
|
1358
|
+
constructor(index) {
|
|
1359
|
+
super(`The registry ${index} requires authentication. Elevate does not handle registry credentials.`);
|
|
1360
|
+
this.index = index;
|
|
1361
|
+
this.name = "RegistryAuthRequiredError";
|
|
1362
|
+
}
|
|
1363
|
+
index;
|
|
1364
|
+
};
|
|
1365
|
+
var CrateNotFoundError = class extends Error {
|
|
1366
|
+
constructor(crate, index) {
|
|
1367
|
+
super(`'${crate}' is not in the registry ${index}.`);
|
|
1368
|
+
this.crate = crate;
|
|
1369
|
+
this.index = index;
|
|
1370
|
+
this.name = "CrateNotFoundError";
|
|
1371
|
+
}
|
|
1372
|
+
crate;
|
|
1373
|
+
index;
|
|
1374
|
+
};
|
|
1375
|
+
var UnsupportedIndexError = class extends Error {
|
|
1376
|
+
constructor(index) {
|
|
1377
|
+
super(`The registry index ${index} is not a sparse index; Elevate cannot read it.`);
|
|
1378
|
+
this.index = index;
|
|
1379
|
+
this.name = "UnsupportedIndexError";
|
|
1380
|
+
}
|
|
1381
|
+
index;
|
|
1382
|
+
};
|
|
1383
|
+
var CRATES_IO_SPARSE = "https://index.crates.io/";
|
|
1384
|
+
var CRATES_IO_GIT = "registry+https://github.com/rust-lang/crates.io-index";
|
|
1385
|
+
function indexPath(name) {
|
|
1386
|
+
const n = name.toLowerCase();
|
|
1387
|
+
if (n.length <= 2) return `${n.length}/${n}`;
|
|
1388
|
+
if (n.length === 3) return `3/${n[0]}/${n}`;
|
|
1389
|
+
return `${n.slice(0, 2)}/${n.slice(2, 4)}/${n}`;
|
|
1390
|
+
}
|
|
1391
|
+
function parseIndex(text) {
|
|
1392
|
+
const versions = [];
|
|
1393
|
+
for (const raw of text.split(/\r?\n/)) {
|
|
1394
|
+
if (!raw.trim()) continue;
|
|
1395
|
+
try {
|
|
1396
|
+
const entry = JSON.parse(raw);
|
|
1397
|
+
if (typeof entry.vers !== "string") continue;
|
|
1398
|
+
versions.push({ version: entry.vers, yanked: entry.yanked === true, rustVersion: entry.rust_version });
|
|
1399
|
+
} catch {
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
return versions;
|
|
1403
|
+
}
|
|
1404
|
+
function sparseRoot(index) {
|
|
1405
|
+
if (index === CRATES_IO_GIT) return CRATES_IO_SPARSE;
|
|
1406
|
+
if (index.startsWith("sparse+")) {
|
|
1407
|
+
const url = index.slice("sparse+".length);
|
|
1408
|
+
return url.endsWith("/") ? url : `${url}/`;
|
|
1409
|
+
}
|
|
1410
|
+
throw new UnsupportedIndexError(index);
|
|
1411
|
+
}
|
|
1412
|
+
var userAgent = () => `elevate/${VERSION} (+https://github.com/DennisRepa/Elevate)`;
|
|
1413
|
+
function networkReason(err) {
|
|
1414
|
+
const cause = err?.cause?.message;
|
|
1415
|
+
return cause ?? (err instanceof Error ? err.message : String(err));
|
|
1416
|
+
}
|
|
1417
|
+
var CargoIndexClient = class {
|
|
1418
|
+
constructor(fetchFn = fetch, env = process.env) {
|
|
1419
|
+
this.fetchFn = fetchFn;
|
|
1420
|
+
this.env = env;
|
|
1421
|
+
}
|
|
1422
|
+
fetchFn;
|
|
1423
|
+
env;
|
|
1424
|
+
memo = /* @__PURE__ */ new Map();
|
|
1425
|
+
/** All versions of a crate in oldest-first file order. Memoised per index and crate. */
|
|
1426
|
+
versions(index, crate) {
|
|
1427
|
+
const key = `${index}
|
|
1428
|
+
${crate.toLowerCase()}`;
|
|
1429
|
+
let pending = this.memo.get(key);
|
|
1430
|
+
if (!pending) {
|
|
1431
|
+
pending = this.load(index, crate);
|
|
1432
|
+
this.memo.set(key, pending);
|
|
1433
|
+
}
|
|
1434
|
+
return pending;
|
|
1435
|
+
}
|
|
1436
|
+
async load(index, crate) {
|
|
1437
|
+
const url = `${sparseRoot(index)}${indexPath(crate)}`;
|
|
1438
|
+
let response;
|
|
1439
|
+
try {
|
|
1440
|
+
response = await this.fetchFn(url, { headers: { "user-agent": userAgent() } });
|
|
1441
|
+
} catch (err) {
|
|
1442
|
+
throw new Error(`Cannot reach ${url}: ${networkReason(err)}${this.proxyHint()}`);
|
|
1443
|
+
}
|
|
1444
|
+
if (response.status === 401 || response.status === 403) throw new RegistryAuthRequiredError(index);
|
|
1445
|
+
if (response.status === 404) throw new CrateNotFoundError(crate, index);
|
|
1446
|
+
if (!response.ok) throw new Error(`Registry answered ${response.status} for ${url}.`);
|
|
1447
|
+
return parseIndex(await response.text());
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Node's fetch ignores HTTP(S)_PROXY unless NODE_USE_ENV_PROXY=1 is set when
|
|
1451
|
+
* the process starts (measured with Node 22.23; it cannot be switched on later).
|
|
1452
|
+
*/
|
|
1453
|
+
proxyHint() {
|
|
1454
|
+
const proxied = this.env.HTTPS_PROXY || this.env.https_proxy || this.env.HTTP_PROXY || this.env.http_proxy;
|
|
1455
|
+
if (!proxied || this.env.NODE_USE_ENV_PROXY === "1") return "";
|
|
1456
|
+
return " A proxy is configured, but Node does not use it for registry lookups unless you start Elevate with NODE_USE_ENV_PROXY=1.";
|
|
1457
|
+
}
|
|
1458
|
+
};
|
|
1459
|
+
|
|
1460
|
+
// src/adapters/cargo/cargo-registry.ts
|
|
1461
|
+
import semver6 from "semver";
|
|
1462
|
+
|
|
1463
|
+
// src/adapters/cargo/cargo-versions.ts
|
|
1464
|
+
import semver5 from "semver";
|
|
1465
|
+
var rust = (v) => semver5.coerce(v)?.version;
|
|
1466
|
+
function selectVersion(versions, options) {
|
|
1467
|
+
const candidates = versions.filter((v) => !v.yanked && semver5.valid(v.version)).filter((v) => options.channel === "all" || semver5.prerelease(v.version) === null).sort((a, b) => semver5.rcompare(a.version, b.version));
|
|
1468
|
+
const project = options.rustVersion ? rust(options.rustVersion) : void 0;
|
|
1469
|
+
let blocked;
|
|
1470
|
+
for (const candidate of candidates) {
|
|
1471
|
+
const needs = candidate.rustVersion ? rust(candidate.rustVersion) : void 0;
|
|
1472
|
+
if (project && needs && semver5.gt(needs, project)) {
|
|
1473
|
+
blocked ??= { version: candidate.version, requires: candidate.rustVersion };
|
|
1474
|
+
continue;
|
|
1475
|
+
}
|
|
1476
|
+
return { version: candidate.version, ...blocked ? { blockedByMsrv: blocked } : {} };
|
|
1477
|
+
}
|
|
1478
|
+
return { version: void 0, ...blocked ? { blockedByMsrv: blocked } : {} };
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
// src/adapters/cargo/cargo-registry.ts
|
|
1482
|
+
var CargoRegistryAdapter = class {
|
|
1483
|
+
constructor(deps) {
|
|
1484
|
+
this.deps = deps;
|
|
1485
|
+
}
|
|
1486
|
+
deps;
|
|
1487
|
+
async resolveEndpoint(coordinate, cwd) {
|
|
1488
|
+
const { index, isPublic } = await this.deps.endpoint(coordinate.registry, cwd);
|
|
1489
|
+
return { url: index, isPublic };
|
|
1490
|
+
}
|
|
1491
|
+
async getLatestVersion(coordinate, channel = "stable", cwd = process.cwd()) {
|
|
1492
|
+
return selectVersion(await this.load(coordinate, cwd), { channel }).version ?? null;
|
|
1493
|
+
}
|
|
1494
|
+
/** Published, not yanked, newest first. Lookup errors propagate so they are not mistaken for "no versions". */
|
|
1495
|
+
async getAllVersions(coordinate, cwd = process.cwd()) {
|
|
1496
|
+
return (await this.load(coordinate, cwd)).filter((v) => !v.yanked && semver6.valid(v.version)).map((v) => v.version).sort(semver6.rcompare);
|
|
1497
|
+
}
|
|
1498
|
+
async load(coordinate, cwd) {
|
|
1499
|
+
const { index } = await this.deps.endpoint(coordinate.registry, cwd);
|
|
1500
|
+
return this.deps.index.versions(index, coordinate.artifact);
|
|
1501
|
+
}
|
|
1502
|
+
};
|
|
1503
|
+
|
|
1504
|
+
// src/adapters/cargo/cargo-requirement.ts
|
|
1505
|
+
import semver7 from "semver";
|
|
1506
|
+
var SIMPLE = /^(\^|~|=)?\s*(\d+(?:\.\d+){0,2})$/;
|
|
1507
|
+
function toSemverRange(requirement) {
|
|
1508
|
+
const parts = requirement.split(",").map((p) => p.trim());
|
|
1509
|
+
if (parts.some((p) => p === "")) return null;
|
|
1510
|
+
const converted = parts.map((part) => {
|
|
1511
|
+
const bare = part.match(/^\d/);
|
|
1512
|
+
return bare ? `^${part}` : part;
|
|
1513
|
+
});
|
|
1514
|
+
return semver7.validRange(converted.join(" ")) ? converted.join(" ") : null;
|
|
1515
|
+
}
|
|
1516
|
+
function satisfiesRequirement(requirement, version) {
|
|
1517
|
+
const range = toSemverRange(requirement);
|
|
1518
|
+
return range !== null && semver7.satisfies(version, range, { includePrerelease: false });
|
|
1519
|
+
}
|
|
1520
|
+
function isUpgradableRequirement(requirement) {
|
|
1521
|
+
return SIMPLE.test(requirement.trim());
|
|
1522
|
+
}
|
|
1523
|
+
function requirementFloor(requirement) {
|
|
1524
|
+
const range = toSemverRange(requirement);
|
|
1525
|
+
return range ? semver7.minVersion(range)?.version ?? null : null;
|
|
1526
|
+
}
|
|
1527
|
+
function rewriteRequirement(current, target) {
|
|
1528
|
+
const match = current.trim().match(SIMPLE);
|
|
1529
|
+
if (!match) return target;
|
|
1530
|
+
if (semver7.prerelease(target)) return `${match[1] ?? ""}${target}`;
|
|
1531
|
+
const precision = match[2].split(".").length;
|
|
1532
|
+
const [major, minor, patch] = target.split(/[-+]/)[0].split(".");
|
|
1533
|
+
const written = [major, minor, patch].slice(0, precision).join(".");
|
|
1534
|
+
return `${match[1] ?? ""}${written}`;
|
|
1535
|
+
}
|
|
1536
|
+
function cargoDiff(from, to) {
|
|
1537
|
+
const a = semver7.parse(from);
|
|
1538
|
+
const b = semver7.parse(to);
|
|
1539
|
+
if (!a || !b) return "minor";
|
|
1540
|
+
if (a.major > 0 || b.major > a.major) {
|
|
1541
|
+
if (b.major !== a.major) return "major";
|
|
1542
|
+
return b.minor !== a.minor ? "minor" : "patch";
|
|
1543
|
+
}
|
|
1544
|
+
if (a.minor > 0) {
|
|
1545
|
+
if (b.minor !== a.minor) return "major";
|
|
1546
|
+
return "minor";
|
|
1547
|
+
}
|
|
1548
|
+
return "major";
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
// src/adapters/cargo/cargo-scanner.ts
|
|
1552
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
1553
|
+
import { join as join11, relative as relative4 } from "path";
|
|
1554
|
+
import semver8 from "semver";
|
|
1555
|
+
|
|
1556
|
+
// src/adapters/cargo/cargo-toml.ts
|
|
1557
|
+
import { parseTOML as parseTOML2 } from "toml-eslint-parser";
|
|
1558
|
+
var SECTIONS2 = {
|
|
1559
|
+
dependencies: "dependencies",
|
|
1560
|
+
"dev-dependencies": "dev-dependencies",
|
|
1561
|
+
dev_dependencies: "dev-dependencies",
|
|
1562
|
+
"build-dependencies": "build-dependencies",
|
|
1563
|
+
build_dependencies: "build-dependencies"
|
|
1564
|
+
};
|
|
1565
|
+
function parseCargoManifest(source) {
|
|
1566
|
+
const root = parseTOML2(source).body[0];
|
|
1567
|
+
const leaves = [];
|
|
1568
|
+
const keyPath = (key) => key.keys.map((k) => k.type === "TOMLBare" ? k.name : k.value);
|
|
1569
|
+
const visitPair = (prefix, pair) => {
|
|
1570
|
+
const path = [...prefix, ...keyPath(pair.key)];
|
|
1571
|
+
leaves.push({ path, node: pair.value });
|
|
1572
|
+
if (pair.value.type === "TOMLInlineTable") for (const inner of pair.value.body) visitPair(path, inner);
|
|
1573
|
+
};
|
|
1574
|
+
for (const element of root.body) {
|
|
1575
|
+
if (element.type === "TOMLKeyValue") visitPair([], element);
|
|
1576
|
+
else if (element.type === "TOMLTable") {
|
|
1577
|
+
const header = keyPath(element.key);
|
|
1578
|
+
for (const pair of element.body) visitPair(header, pair);
|
|
1579
|
+
leaves.push({ path: header, node: element });
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
1583
|
+
for (const { path, node } of leaves) {
|
|
1584
|
+
const located = locateSection(path);
|
|
1585
|
+
if (!located || located.rest.length === 0) continue;
|
|
1586
|
+
const [key, field] = located.rest;
|
|
1587
|
+
const id = `${located.inWorkspace}|${located.target ?? ""}|${located.section}|${key}`;
|
|
1588
|
+
let decl = byKey.get(id);
|
|
1589
|
+
if (!decl) {
|
|
1590
|
+
decl = { key, section: located.section, target: located.target, inWorkspace: located.inWorkspace, inherited: false };
|
|
1591
|
+
byKey.set(id, decl);
|
|
1592
|
+
}
|
|
1593
|
+
if (located.rest.length === 1 && node.type === "TOMLValue" && node.kind === "string") {
|
|
1594
|
+
decl.version = literalOf(node);
|
|
1595
|
+
} else if (field === "version" && located.rest.length === 2 && node.type === "TOMLValue" && node.kind === "string") {
|
|
1596
|
+
decl.version = literalOf(node);
|
|
1597
|
+
} else if (field === "workspace" && located.rest.length === 2 && node.value === true) {
|
|
1598
|
+
decl.inherited = true;
|
|
1599
|
+
} else if (field === "package" && located.rest.length === 2 && node.type === "TOMLValue") {
|
|
1600
|
+
decl.package = String(node.value);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
return [...byKey.values()];
|
|
1604
|
+
}
|
|
1605
|
+
function literalOf(node) {
|
|
1606
|
+
return { value: node.value, start: node.range[0] + 1, end: node.range[1] - 1 };
|
|
1607
|
+
}
|
|
1608
|
+
function locateSection(path) {
|
|
1609
|
+
let i = 0;
|
|
1610
|
+
let target;
|
|
1611
|
+
let inWorkspace = false;
|
|
1612
|
+
if (path[0] === "workspace" && path[1] === "dependencies") {
|
|
1613
|
+
return { section: "dependencies", inWorkspace: true, target, rest: path.slice(2) };
|
|
1614
|
+
}
|
|
1615
|
+
if (path[0] === "target" && path.length >= 3) {
|
|
1616
|
+
target = path[1];
|
|
1617
|
+
i = 2;
|
|
1618
|
+
}
|
|
1619
|
+
const section = SECTIONS2[path[i] ?? ""];
|
|
1620
|
+
if (!section) return void 0;
|
|
1621
|
+
return { section, inWorkspace, target, rest: path.slice(i + 1) };
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
// src/adapters/cargo/cargo-scanner.ts
|
|
1625
|
+
var CONCURRENCY2 = 6;
|
|
1626
|
+
var DIFF_ORDER2 = { major: 0, minor: 1, patch: 2 };
|
|
1627
|
+
var CargoDependencyAdapter = class {
|
|
1628
|
+
constructor(deps) {
|
|
1629
|
+
this.deps = deps;
|
|
1630
|
+
}
|
|
1631
|
+
deps;
|
|
1632
|
+
async scan(module, context) {
|
|
1633
|
+
const metadata = await this.deps.loadMetadata(module.aggregatorDir ?? module.path);
|
|
1634
|
+
const pkg = metadata.packages.find((p) => samePath(join11(p.manifest_path, ".."), module.path));
|
|
1635
|
+
if (!pkg) return { candidates: [], skipped: [] };
|
|
1636
|
+
const own = readDeclarations(pkg.manifest_path);
|
|
1637
|
+
const rootManifest = join11(metadata.workspace_root, "Cargo.toml");
|
|
1638
|
+
const root = readDeclarations(rootManifest);
|
|
1639
|
+
const groups = groupDependencies(pkg.dependencies);
|
|
1640
|
+
const outcomes = await mapPooled(groups, CONCURRENCY2, async (group) => {
|
|
1641
|
+
const primary = group.find((d) => d.kind === null) ?? group[0];
|
|
1642
|
+
const key = primary.rename ?? primary.name;
|
|
1643
|
+
const decl = findDeclaration(own, key, primary);
|
|
1644
|
+
const located = locate(decl, root, key, context.rootDir, pkg.manifest_path, rootManifest);
|
|
1645
|
+
const scope = group.every((d) => d.kind === "dev") ? "dev" : "prod";
|
|
1646
|
+
const qualifier = primary.target ?? (primary.kind === "build" ? "build" : void 0);
|
|
1647
|
+
const common = { scope, qualifier };
|
|
1648
|
+
if (primary.source === null && primary.path) {
|
|
1649
|
+
const target = [...context.workspace.values()].find((m) => samePath(m.path, primary.path));
|
|
1650
|
+
return target ? alignment(primary, target, located, common) : null;
|
|
1651
|
+
}
|
|
1652
|
+
if (!primary.source || primary.source.startsWith("git+")) return null;
|
|
1653
|
+
const outcome = await this.registryOutcome(primary, located, pkg, metadata, common, context);
|
|
1654
|
+
const other = group.find((d) => d !== primary && !sameRequirement(d.req, primary.req));
|
|
1655
|
+
if (outcome && "candidate" in outcome && other) {
|
|
1656
|
+
outcome.note = {
|
|
1657
|
+
identifier: primary.name,
|
|
1658
|
+
origin: outcome.candidate.origin.kind,
|
|
1659
|
+
reason: "declaration-mismatch",
|
|
1660
|
+
detail: `${sectionOf(other)} requires ${other.req}; it is not changed`
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
return outcome;
|
|
1664
|
+
});
|
|
1665
|
+
const candidates = [];
|
|
1666
|
+
const skipped = [];
|
|
1667
|
+
for (const outcome of outcomes) {
|
|
1668
|
+
if (!outcome) continue;
|
|
1669
|
+
if ("candidate" in outcome) {
|
|
1670
|
+
candidates.push(outcome.candidate);
|
|
1671
|
+
if (outcome.note) skipped.push(outcome.note);
|
|
1672
|
+
} else skipped.push(outcome.skipped);
|
|
1673
|
+
}
|
|
1674
|
+
candidates.sort((a, b) => (DIFF_ORDER2[a.diff] ?? 3) - (DIFF_ORDER2[b.diff] ?? 3));
|
|
1675
|
+
return { candidates, skipped };
|
|
1676
|
+
}
|
|
1677
|
+
async registryOutcome(dep, located, pkg, metadata, common, context) {
|
|
1678
|
+
const literal = located?.version.value ?? dep.req;
|
|
1679
|
+
if (!isUpgradableRequirement(literal)) return null;
|
|
1680
|
+
if (!located || !sameRequirement(located.version.value, dep.req)) {
|
|
1681
|
+
return { skipped: { identifier: dep.name, origin: "public", reason: "declaration-mismatch", detail: `${literal} in the manifest, ${dep.req} for Cargo` } };
|
|
1682
|
+
}
|
|
1683
|
+
let origin = { kind: "public" };
|
|
1684
|
+
if (dep.registry) origin = { kind: "private", matchedBy: "scoped-registry", registry: dep.registry };
|
|
1685
|
+
else if (matchesInternalScope(dep.name, "cargo", context.internalScopes)) origin = { kind: "private", matchedBy: "internal-scope" };
|
|
1686
|
+
const endpoint = await this.deps.endpoint(dep.registry ?? void 0, context.rootDir);
|
|
1687
|
+
if (origin.kind === "private" && endpoint.isPublic) {
|
|
1688
|
+
return { skipped: { identifier: dep.name, origin: "private", reason: "private-on-public-registry", detail: endpoint.index } };
|
|
1689
|
+
}
|
|
1690
|
+
let versions;
|
|
1691
|
+
try {
|
|
1692
|
+
versions = await this.deps.index.versions(endpoint.index, dep.name);
|
|
1693
|
+
} catch (err) {
|
|
1694
|
+
return { skipped: { identifier: dep.name, origin: origin.kind, reason: "lookup-failed", detail: err instanceof Error ? err.message : String(err) } };
|
|
1695
|
+
}
|
|
1696
|
+
const chosen = selectVersion(versions, { channel: context.channel, rustVersion: pkg.rust_version ?? void 0 });
|
|
1697
|
+
const floor = requirementFloor(literal);
|
|
1698
|
+
if (!chosen.version) {
|
|
1699
|
+
if (chosen.blockedByMsrv && floor) {
|
|
1700
|
+
const { version, requires } = chosen.blockedByMsrv;
|
|
1701
|
+
return { skipped: { identifier: dep.name, origin: origin.kind, reason: "incompatible-rust-version", detail: ` (${version} needs Rust ${requires})` } };
|
|
1702
|
+
}
|
|
1703
|
+
return { skipped: { identifier: dep.name, origin: origin.kind, reason: "lookup-failed", detail: "no usable version in the registry" } };
|
|
1704
|
+
}
|
|
1705
|
+
const latest = chosen.version;
|
|
1706
|
+
if (!floor || !semver8.gt(latest, floor)) {
|
|
1707
|
+
const held = chosen.blockedByMsrv;
|
|
1708
|
+
if (floor && held && semver8.gt(held.version, floor)) {
|
|
1709
|
+
return { skipped: { identifier: dep.name, origin: origin.kind, reason: "incompatible-rust-version", detail: ` (${held.version} needs Rust ${held.requires})` } };
|
|
1710
|
+
}
|
|
1711
|
+
return null;
|
|
1712
|
+
}
|
|
1713
|
+
const newRange = rewriteRequirement(literal, latest);
|
|
1714
|
+
if (newRange === literal) return null;
|
|
1715
|
+
const tag = semver8.prerelease(latest) ? extractPreReleaseTag(latest) ?? "PRE" : void 0;
|
|
1716
|
+
const diff = cargoDiff(floor, latest);
|
|
1717
|
+
const candidate = {
|
|
1718
|
+
coordinate: { identifier: dep.name, artifact: dep.name, ecosystem: "cargo", ...dep.registry ? { registry: dep.registry } : {} },
|
|
1719
|
+
currentRange: literal,
|
|
1720
|
+
currentClean: floor,
|
|
1721
|
+
latest,
|
|
1722
|
+
newRange,
|
|
1723
|
+
diff,
|
|
1724
|
+
scope: common.scope,
|
|
1725
|
+
selected: diff !== "major",
|
|
1726
|
+
origin,
|
|
1727
|
+
action: "update",
|
|
1728
|
+
declaration: located.declaration,
|
|
1729
|
+
isPreRelease: tag !== void 0,
|
|
1730
|
+
preReleaseTag: tag,
|
|
1731
|
+
...common.qualifier ? { qualifier: common.qualifier } : {},
|
|
1732
|
+
...located.inherited ? { sharedWith: sharersOf(metadata, pkg, dep.rename ?? dep.name) } : {}
|
|
1733
|
+
};
|
|
1734
|
+
return { candidate };
|
|
1735
|
+
}
|
|
1736
|
+
};
|
|
1737
|
+
function alignment(dep, target, located, common) {
|
|
1738
|
+
const local = target.version;
|
|
1739
|
+
if (!located || !local || !semver8.valid(local)) return null;
|
|
1740
|
+
const literal = located.version.value;
|
|
1741
|
+
if (!isUpgradableRequirement(literal) || satisfiesRequirement(literal, local)) return null;
|
|
1742
|
+
const floor = requirementFloor(literal) ?? "0.0.0";
|
|
1743
|
+
const diff = cargoDiff(floor, local);
|
|
1744
|
+
const tag = semver8.prerelease(local) ? extractPreReleaseTag(local) ?? "PRE" : void 0;
|
|
1745
|
+
return {
|
|
1746
|
+
candidate: {
|
|
1747
|
+
coordinate: { identifier: dep.name, artifact: dep.name, ecosystem: "cargo" },
|
|
1748
|
+
currentRange: literal,
|
|
1749
|
+
currentClean: floor,
|
|
1750
|
+
latest: local,
|
|
1751
|
+
newRange: rewriteRequirement(literal, local),
|
|
1752
|
+
diff,
|
|
1753
|
+
scope: common.scope,
|
|
1754
|
+
selected: diff !== "major",
|
|
1755
|
+
origin: { kind: "workspace", moduleId: target.id, moduleRelPath: target.relPath },
|
|
1756
|
+
action: "align",
|
|
1757
|
+
declaration: located.declaration,
|
|
1758
|
+
availableVersions: [local],
|
|
1759
|
+
isPreRelease: tag !== void 0,
|
|
1760
|
+
preReleaseTag: tag,
|
|
1761
|
+
...common.qualifier ? { qualifier: common.qualifier } : {}
|
|
1762
|
+
}
|
|
1763
|
+
};
|
|
1764
|
+
}
|
|
1765
|
+
function groupDependencies(deps) {
|
|
1766
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1767
|
+
for (const dep of deps) {
|
|
1768
|
+
const id = `${dep.rename ?? dep.name}`;
|
|
1769
|
+
groups.set(id, [...groups.get(id) ?? [], dep]);
|
|
1770
|
+
}
|
|
1771
|
+
return [...groups.values()];
|
|
1772
|
+
}
|
|
1773
|
+
function readDeclarations(manifestPath) {
|
|
1774
|
+
try {
|
|
1775
|
+
return parseCargoManifest(readFileSync10(manifestPath, "utf8"));
|
|
1776
|
+
} catch {
|
|
1777
|
+
return [];
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
var sectionOf = (dep) => dep.kind === "dev" ? "dev-dependencies" : dep.kind === "build" ? "build-dependencies" : "dependencies";
|
|
1781
|
+
function findDeclaration(decls, key, dep) {
|
|
1782
|
+
const candidates = decls.filter((d) => !d.inWorkspace && d.key === key);
|
|
1783
|
+
return candidates.find((d) => d.section === sectionOf(dep) && (d.target ?? null) === dep.target) ?? candidates[0];
|
|
1784
|
+
}
|
|
1785
|
+
function locate(decl, root, key, repoRoot, manifestPath, rootManifest) {
|
|
1786
|
+
if (!decl) return void 0;
|
|
1787
|
+
if (decl.inherited) {
|
|
1788
|
+
const shared = root.find((d) => d.inWorkspace && d.key === key);
|
|
1789
|
+
if (!shared?.version) return void 0;
|
|
1790
|
+
return {
|
|
1791
|
+
version: shared.version,
|
|
1792
|
+
inherited: true,
|
|
1793
|
+
declaration: { file: rootManifest, kind: "dependency-management", displayPath: display(repoRoot, rootManifest) }
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
if (!decl.version) return void 0;
|
|
1797
|
+
return {
|
|
1798
|
+
version: decl.version,
|
|
1799
|
+
inherited: false,
|
|
1800
|
+
declaration: { file: manifestPath, kind: "dependency", displayPath: display(repoRoot, manifestPath) }
|
|
1801
|
+
};
|
|
1802
|
+
}
|
|
1803
|
+
function sharersOf(metadata, self, key) {
|
|
1804
|
+
return metadata.packages.filter((p) => p !== self).filter((p) => readDeclarations(p.manifest_path).some((d) => d.key === key && d.inherited)).map((p) => p.name);
|
|
1805
|
+
}
|
|
1806
|
+
var display = (repoRoot, file) => relative4(repoRoot, file).replace(/\\/g, "/");
|
|
1807
|
+
var samePath = (a, b) => relative4(a, b) === "";
|
|
1808
|
+
function sameRequirement(literal, req) {
|
|
1809
|
+
const a = toSemverRange(literal);
|
|
1810
|
+
const b = toSemverRange(req);
|
|
1811
|
+
return a !== null && b !== null && semver8.validRange(a) === semver8.validRange(b);
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
// src/adapters/cargo/cargo-updater.ts
|
|
1815
|
+
import { existsSync as existsSync8, readFileSync as readFileSync12, writeFileSync as writeFileSync2 } from "fs";
|
|
1816
|
+
import { join as join12, resolve as resolve6 } from "path";
|
|
1817
|
+
import semver9 from "semver";
|
|
1063
1818
|
|
|
1064
1819
|
// src/adapters/shared/xml.ts
|
|
1065
1820
|
import { SaxesParser } from "saxes";
|
|
@@ -1192,9 +1947,226 @@ function localName(qualified) {
|
|
|
1192
1947
|
return colon >= 0 ? qualified.slice(colon + 1) : qualified;
|
|
1193
1948
|
}
|
|
1194
1949
|
|
|
1950
|
+
// src/adapters/shared/text-edit.ts
|
|
1951
|
+
function applyRangeEdits(source, edits) {
|
|
1952
|
+
for (const edit of edits) {
|
|
1953
|
+
const found = source.slice(edit.start, edit.end);
|
|
1954
|
+
if (found !== edit.expected) {
|
|
1955
|
+
throw new StaleEditError(`Found '${found}', expected '${edit.expected}'. The file changed since it was scanned.`);
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
const ordered = [...edits].sort((a, b) => b.start - a.start);
|
|
1959
|
+
for (let i = 1; i < ordered.length; i++) {
|
|
1960
|
+
if (ordered[i].end > ordered[i - 1].start) throw new StaleEditError("Overlapping edits on the same text.");
|
|
1961
|
+
}
|
|
1962
|
+
let result = source;
|
|
1963
|
+
for (const edit of ordered) result = result.slice(0, edit.start) + edit.replacement + result.slice(edit.end);
|
|
1964
|
+
return result;
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
// src/adapters/cargo/cargo-lockfile.ts
|
|
1968
|
+
import { existsSync as existsSync7, readFileSync as readFileSync11 } from "fs";
|
|
1969
|
+
import { getStaticTOMLValue as getStaticTOMLValue2, parseTOML as parseTOML3 } from "toml-eslint-parser";
|
|
1970
|
+
function readLockedVersions(lockFile) {
|
|
1971
|
+
const locked = /* @__PURE__ */ new Map();
|
|
1972
|
+
if (!existsSync7(lockFile)) return locked;
|
|
1973
|
+
try {
|
|
1974
|
+
const data = getStaticTOMLValue2(parseTOML3(readFileSync11(lockFile, "utf8")));
|
|
1975
|
+
for (const pkg of data.package ?? []) locked.set(pkg.name, [...locked.get(pkg.name) ?? [], pkg.version]);
|
|
1976
|
+
} catch {
|
|
1977
|
+
}
|
|
1978
|
+
return locked;
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
// src/adapters/cargo/cargo-updater.ts
|
|
1982
|
+
var UPDATE_TIMEOUT_MS = 10 * 6e4;
|
|
1983
|
+
var workspaceDir = (module, rootDir) => module.aggregatorDir ?? module.path ?? rootDir;
|
|
1984
|
+
var CargoUpdaterAdapter = class {
|
|
1985
|
+
affectedFiles(module, rootDir, updates) {
|
|
1986
|
+
const files = new Set(updates.map((u) => resolve6(u.declaration?.file ?? join12(module.path, "Cargo.toml"))));
|
|
1987
|
+
files.add(join12(workspaceDir(module, rootDir), "Cargo.lock"));
|
|
1988
|
+
return [...files];
|
|
1989
|
+
}
|
|
1990
|
+
async applyUpdates(module, rootDir, updates, onProgress) {
|
|
1991
|
+
const dir = workspaceDir(module, rootDir);
|
|
1992
|
+
const lockFile = join12(dir, "Cargo.lock");
|
|
1993
|
+
const lockedBefore = readLockedVersions(lockFile);
|
|
1994
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
1995
|
+
for (const update of updates) {
|
|
1996
|
+
const file = resolve6(update.declaration?.file ?? join12(module.path, "Cargo.toml"));
|
|
1997
|
+
byFile.set(file, [...byFile.get(file) ?? [], update]);
|
|
1998
|
+
}
|
|
1999
|
+
const writes = [];
|
|
2000
|
+
for (const [file, fileUpdates] of byFile) {
|
|
2001
|
+
onProgress(`Preparing ${fileUpdates.length} change(s) in ${file}\u2026`);
|
|
2002
|
+
const source = readFileSync12(file, "utf8");
|
|
2003
|
+
const decls = parseCargoManifest(source);
|
|
2004
|
+
const edits = [];
|
|
2005
|
+
for (const update of fileUpdates) {
|
|
2006
|
+
const inWorkspace = update.declaration?.kind === "dependency-management";
|
|
2007
|
+
const name = update.coordinate.identifier;
|
|
2008
|
+
const found = decls.filter(
|
|
2009
|
+
(d) => d.inWorkspace === inWorkspace && (d.package ?? d.key) === name && d.version?.value === update.currentRange
|
|
2010
|
+
);
|
|
2011
|
+
if (found.length === 0) {
|
|
2012
|
+
throw new InstallError(`${file}: cannot find the version of ${name} (${update.currentRange}) any more; rescan the module.`);
|
|
2013
|
+
}
|
|
2014
|
+
for (const d of found) {
|
|
2015
|
+
edits.push({ start: d.version.start, end: d.version.end, expected: d.version.value, replacement: update.newRange });
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
try {
|
|
2019
|
+
writes.push({ file, content: applyRangeEdits(source, edits) });
|
|
2020
|
+
} catch (err) {
|
|
2021
|
+
if (err instanceof StaleEditError) throw new InstallError(`${file}: ${err.message}`);
|
|
2022
|
+
throw err;
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
onProgress("Writing Cargo.toml files\u2026");
|
|
2026
|
+
for (const write of writes) writeFileSync2(write.file, write.content, "utf8");
|
|
2027
|
+
const registryUpdates = updates.filter((u) => u.action === "update");
|
|
2028
|
+
if (existsSync8(lockFile) && registryUpdates.length > 0) {
|
|
2029
|
+
await this.refreshLock(dir, lockFile, lockedBefore, registryUpdates, onProgress);
|
|
2030
|
+
}
|
|
2031
|
+
return {
|
|
2032
|
+
updatedCount: updates.length,
|
|
2033
|
+
auditMessage: `Updated ${updates.length} requirement(s) in ${writes.length} Cargo.toml file(s)${existsSync8(lockFile) ? "; Cargo.lock refreshed" : ""}.`,
|
|
2034
|
+
auditSeverity: "clean",
|
|
2035
|
+
integrityViolations: []
|
|
2036
|
+
};
|
|
2037
|
+
}
|
|
2038
|
+
async resync() {
|
|
2039
|
+
}
|
|
2040
|
+
async refreshLock(dir, lockFile, before, updates, onProgress) {
|
|
2041
|
+
const specs = [];
|
|
2042
|
+
for (const update of updates) {
|
|
2043
|
+
const name = update.coordinate.identifier;
|
|
2044
|
+
const versions = before.get(name) ?? [];
|
|
2045
|
+
const old = versions.find((v) => satisfiesRequirement(update.currentRange, v)) ?? versions[0];
|
|
2046
|
+
if (old) specs.push(`${name}@${old}`);
|
|
2047
|
+
}
|
|
2048
|
+
if (specs.length === 0) return;
|
|
2049
|
+
onProgress("Updating Cargo.lock\u2026");
|
|
2050
|
+
await this.cargoUpdate(dir, specs.flatMap((s) => ["-p", s]));
|
|
2051
|
+
const after = readLockedVersions(lockFile);
|
|
2052
|
+
for (const update of updates) {
|
|
2053
|
+
const name = update.coordinate.identifier;
|
|
2054
|
+
const locked = (after.get(name) ?? []).find((v) => satisfiesRequirement(update.newRange, v));
|
|
2055
|
+
if (locked && semver9.valid(update.latest) && locked !== update.latest) {
|
|
2056
|
+
onProgress(`Pinning ${name} to ${update.latest}\u2026`);
|
|
2057
|
+
await this.cargoUpdate(dir, ["-p", `${name}@${locked}`, "--precise", update.latest]);
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
async cargoUpdate(dir, args) {
|
|
2062
|
+
const result = await runCommand("cargo", ["update", ...args], { cwd: dir, timeoutMs: UPDATE_TIMEOUT_MS });
|
|
2063
|
+
if (result.exitCode !== 0) {
|
|
2064
|
+
const reason = result.timedOut ? "cargo update timed out" : "cargo update failed";
|
|
2065
|
+
throw new InstallError(reason, tail(result.stderr || result.stdout));
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
};
|
|
2069
|
+
|
|
2070
|
+
// src/adapters/cargo/cargo-verifier.ts
|
|
2071
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2072
|
+
import { join as join13, relative as relative5, resolve as resolve7 } from "path";
|
|
2073
|
+
var VERIFY_TIMEOUT_MS2 = 30 * 6e4;
|
|
2074
|
+
var same = (a, b) => relative5(resolve7(a), resolve7(b)) === "";
|
|
2075
|
+
function planCheck(metadata, module, changedFiles, locked) {
|
|
2076
|
+
const args = ["check", "--all-targets", ...locked ? ["--locked"] : []];
|
|
2077
|
+
const rootManifest = join13(metadata.workspace_root, "Cargo.toml");
|
|
2078
|
+
const memberManifests = new Set(metadata.packages.map((p) => resolve7(p.manifest_path)));
|
|
2079
|
+
if (changedFiles.some((f) => same(f, rootManifest))) {
|
|
2080
|
+
const isVirtualRoot = !memberManifests.has(resolve7(rootManifest));
|
|
2081
|
+
if (isVirtualRoot || metadata.packages.length > 1) return [...args, "--workspace"];
|
|
2082
|
+
}
|
|
2083
|
+
const changed = new Set(
|
|
2084
|
+
metadata.packages.filter((p) => changedFiles.some((f) => same(f, p.manifest_path))).map((p) => p.name)
|
|
2085
|
+
);
|
|
2086
|
+
if (changed.size === 0) changed.add(module.id);
|
|
2087
|
+
for (let grew = true; grew; ) {
|
|
2088
|
+
grew = false;
|
|
2089
|
+
for (const p of metadata.packages) {
|
|
2090
|
+
if (changed.has(p.name)) continue;
|
|
2091
|
+
if (p.dependencies.some((d) => d.source === null && d.path && changed.has(d.name))) {
|
|
2092
|
+
changed.add(p.name);
|
|
2093
|
+
grew = true;
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
const ordered = metadata.packages.filter((p) => changed.has(p.name)).map((p) => p.name);
|
|
2098
|
+
return [...args, ...ordered.flatMap((name) => ["-p", name])];
|
|
2099
|
+
}
|
|
2100
|
+
var CargoVerificationAdapter = class {
|
|
2101
|
+
constructor(deps = { loadMetadata, hasLock: (dir) => existsSync9(join13(dir, "Cargo.lock")) }) {
|
|
2102
|
+
this.deps = deps;
|
|
2103
|
+
}
|
|
2104
|
+
deps;
|
|
2105
|
+
async verify(module, rootDir, options = {}) {
|
|
2106
|
+
const dir = module.aggregatorDir ?? module.path ?? rootDir;
|
|
2107
|
+
if (options.customScript) {
|
|
2108
|
+
const label2 = options.customLabel || options.customScript;
|
|
2109
|
+
const result2 = await runShellScript(options.customScript, { cwd: dir, timeoutMs: VERIFY_TIMEOUT_MS2 });
|
|
2110
|
+
return result2.exitCode === 0 ? { status: "clean", details: "Verification script succeeded.", label: label2 } : { status: "warn", details: tail(result2.stderr || result2.stdout), label: label2 };
|
|
2111
|
+
}
|
|
2112
|
+
const metadata = await this.deps.loadMetadata(dir);
|
|
2113
|
+
const args = planCheck(metadata, module, options.changedFiles ?? [], this.deps.hasLock(dir));
|
|
2114
|
+
const label = `Cargo build (cargo ${args.join(" ")})`;
|
|
2115
|
+
const result = await runCargo(args, { cwd: dir, timeoutMs: VERIFY_TIMEOUT_MS2 });
|
|
2116
|
+
if (result.exitCode === 0) return { status: "clean", details: "All affected crates type-check, including tests.", label };
|
|
2117
|
+
return {
|
|
2118
|
+
status: "warn",
|
|
2119
|
+
details: result.timedOut ? "cargo check timed out." : cargoErrors(`${result.stderr}
|
|
2120
|
+
${result.stdout}`),
|
|
2121
|
+
label
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
};
|
|
2125
|
+
|
|
2126
|
+
// src/adapters/cargo/cargo-strategy.ts
|
|
2127
|
+
var cargoVersionPolicy = {
|
|
2128
|
+
normalize: (v) => semver10.valid(v),
|
|
2129
|
+
rewrite: rewriteRequirement,
|
|
2130
|
+
diff: (current, target) => semver10.valid(current) && semver10.valid(target) ? semver10.eq(current, target) ? "patch" : cargoDiff(current, target) : "minor",
|
|
2131
|
+
// Updates are written where the scan found the declaration.
|
|
2132
|
+
allowsManualTargets: false
|
|
2133
|
+
};
|
|
2134
|
+
var CargoEcosystemStrategy = class {
|
|
2135
|
+
ecosystem = "cargo";
|
|
2136
|
+
displayName = "Rust / Cargo";
|
|
2137
|
+
icon = "\u{1F980}";
|
|
2138
|
+
manifestFile = "Cargo.toml";
|
|
2139
|
+
versioning = cargoVersionPolicy;
|
|
2140
|
+
config = new CargoConfigReader();
|
|
2141
|
+
index = new CargoIndexClient();
|
|
2142
|
+
endpoint = (registry, cwd) => this.config.resolve(registry, cwd);
|
|
2143
|
+
discovery = new CargoModuleDiscoveryAdapter();
|
|
2144
|
+
registry = new CargoRegistryAdapter({ index: this.index, endpoint: this.endpoint });
|
|
2145
|
+
reader = new CargoDependencyAdapter({ loadMetadata, index: this.index, endpoint: this.endpoint });
|
|
2146
|
+
updater = new CargoUpdaterAdapter();
|
|
2147
|
+
verifier = new CargoVerificationAdapter();
|
|
2148
|
+
getTabLabels(counts, t) {
|
|
2149
|
+
return {
|
|
2150
|
+
all: t.tabs.all(counts.total),
|
|
2151
|
+
prod: t.tabs.dependencies(counts.prodCount),
|
|
2152
|
+
dev: t.tabs.devDependencies(counts.devCount)
|
|
2153
|
+
};
|
|
2154
|
+
}
|
|
2155
|
+
};
|
|
2156
|
+
|
|
2157
|
+
// src/adapters/maven/maven-discovery.ts
|
|
2158
|
+
import { existsSync as existsSync12 } from "fs";
|
|
2159
|
+
import { dirname as dirname7, join as join16, relative as relative6, resolve as resolve10 } from "path";
|
|
2160
|
+
|
|
2161
|
+
// src/adapters/maven/maven-project.ts
|
|
2162
|
+
import { existsSync as existsSync11, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
2163
|
+
import { join as join15, resolve as resolve9 } from "path";
|
|
2164
|
+
|
|
1195
2165
|
// src/adapters/maven/maven-pom.ts
|
|
2166
|
+
import { existsSync as existsSync10, readFileSync as readFileSync13, statSync } from "fs";
|
|
2167
|
+
import { dirname as dirname6, join as join14, resolve as resolve8 } from "path";
|
|
1196
2168
|
function readPom(file) {
|
|
1197
|
-
const source =
|
|
2169
|
+
const source = readFileSync13(file, "utf8");
|
|
1198
2170
|
return parsePom(source, file);
|
|
1199
2171
|
}
|
|
1200
2172
|
function parsePom(source, file) {
|
|
@@ -1213,7 +2185,7 @@ function parsePom(source, file) {
|
|
|
1213
2185
|
}
|
|
1214
2186
|
return {
|
|
1215
2187
|
file,
|
|
1216
|
-
dir:
|
|
2188
|
+
dir: dirname6(file),
|
|
1217
2189
|
groupId: childText(project, "groupId"),
|
|
1218
2190
|
artifactId: childText(project, "artifactId") ?? "unknown",
|
|
1219
2191
|
version: childText(project, "version"),
|
|
@@ -1242,9 +2214,9 @@ function parentPomFile(pom) {
|
|
|
1242
2214
|
if (!pom.parent) return void 0;
|
|
1243
2215
|
const relativePath = pom.parent.relativePath ?? "../pom.xml";
|
|
1244
2216
|
if (relativePath === "") return void 0;
|
|
1245
|
-
let candidate =
|
|
1246
|
-
if (
|
|
1247
|
-
return
|
|
2217
|
+
let candidate = resolve8(pom.dir, relativePath);
|
|
2218
|
+
if (existsSync10(candidate) && statSync(candidate).isDirectory()) candidate = join14(candidate, "pom.xml");
|
|
2219
|
+
return existsSync10(candidate) ? candidate : void 0;
|
|
1248
2220
|
}
|
|
1249
2221
|
function builtinProperty(name, module) {
|
|
1250
2222
|
switch (name) {
|
|
@@ -1319,9 +2291,9 @@ var MavenProject = class _MavenProject {
|
|
|
1319
2291
|
}
|
|
1320
2292
|
poms.push(pom);
|
|
1321
2293
|
for (const entry of pom.modules) {
|
|
1322
|
-
const target =
|
|
1323
|
-
const moduleFile = target.endsWith(".xml") ? target :
|
|
1324
|
-
if (!seen.has(moduleFile) &&
|
|
2294
|
+
const target = resolve9(pom.dir, entry);
|
|
2295
|
+
const moduleFile = target.endsWith(".xml") ? target : join15(target, "pom.xml");
|
|
2296
|
+
if (!seen.has(moduleFile) && existsSync11(moduleFile)) {
|
|
1325
2297
|
seen.add(moduleFile);
|
|
1326
2298
|
queue.push(moduleFile);
|
|
1327
2299
|
}
|
|
@@ -1330,7 +2302,7 @@ var MavenProject = class _MavenProject {
|
|
|
1330
2302
|
return new _MavenProject(rootDir, poms);
|
|
1331
2303
|
}
|
|
1332
2304
|
pomAt(file) {
|
|
1333
|
-
return this.byFile.get(
|
|
2305
|
+
return this.byFile.get(resolve9(file));
|
|
1334
2306
|
}
|
|
1335
2307
|
/** The project followed by its local parents, stopping at the first external parent. */
|
|
1336
2308
|
chainOf(pom) {
|
|
@@ -1373,7 +2345,7 @@ var MavenProject = class _MavenProject {
|
|
|
1373
2345
|
return groupId === parent.groupId && candidate.artifactId === parent.artifactId && (version.includes("${") || expected.includes("${") || version === expected);
|
|
1374
2346
|
};
|
|
1375
2347
|
const file = parentPomFile(pom);
|
|
1376
|
-
const byPath = file ? this.byFile.get(
|
|
2348
|
+
const byPath = file ? this.byFile.get(resolve9(file)) : void 0;
|
|
1377
2349
|
return matches(byPath) ? byPath : void 0;
|
|
1378
2350
|
}
|
|
1379
2351
|
/**
|
|
@@ -1390,8 +2362,8 @@ var MavenProject = class _MavenProject {
|
|
|
1390
2362
|
if (this.aggregators.has(pom.file)) continue;
|
|
1391
2363
|
this.aggregators.set(pom.file, aggregator.dir);
|
|
1392
2364
|
for (const entry of pom.modules) {
|
|
1393
|
-
const target =
|
|
1394
|
-
const file = target.endsWith(".xml") ? target :
|
|
2365
|
+
const target = resolve9(pom.dir, entry);
|
|
2366
|
+
const file = target.endsWith(".xml") ? target : join15(target, "pom.xml");
|
|
1395
2367
|
const modulePom = this.byFile.get(file);
|
|
1396
2368
|
if (modulePom) queue.push(modulePom);
|
|
1397
2369
|
}
|
|
@@ -1401,23 +2373,23 @@ var MavenProject = class _MavenProject {
|
|
|
1401
2373
|
};
|
|
1402
2374
|
function findPomFiles(rootDir) {
|
|
1403
2375
|
const results = [];
|
|
1404
|
-
const rootPom =
|
|
1405
|
-
if (
|
|
2376
|
+
const rootPom = join15(rootDir, "pom.xml");
|
|
2377
|
+
if (existsSync11(rootPom)) results.push(resolve9(rootPom));
|
|
1406
2378
|
const walk = (dir, depth) => {
|
|
1407
2379
|
if (depth > MAX_DEPTH) return;
|
|
1408
2380
|
let entries2;
|
|
1409
2381
|
try {
|
|
1410
|
-
entries2 =
|
|
2382
|
+
entries2 = readdirSync2(dir, { withFileTypes: true });
|
|
1411
2383
|
} catch {
|
|
1412
2384
|
return;
|
|
1413
2385
|
}
|
|
1414
|
-
const isProject =
|
|
2386
|
+
const isProject = existsSync11(join15(dir, "pom.xml"));
|
|
1415
2387
|
for (const entry of entries2) {
|
|
1416
2388
|
if (!entry.isDirectory() || entry.name.startsWith(".") || SKIPPED_DIRS.has(entry.name)) continue;
|
|
1417
2389
|
if (isProject && entry.name === "src") continue;
|
|
1418
|
-
const subDir =
|
|
1419
|
-
const pom =
|
|
1420
|
-
if (
|
|
2390
|
+
const subDir = join15(dir, entry.name);
|
|
2391
|
+
const pom = join15(subDir, "pom.xml");
|
|
2392
|
+
if (existsSync11(pom) && statSync2(pom).isFile()) results.push(resolve9(pom));
|
|
1421
2393
|
walk(subDir, depth + 1);
|
|
1422
2394
|
}
|
|
1423
2395
|
};
|
|
@@ -1432,9 +2404,9 @@ var MavenModuleDiscoveryAdapter = class {
|
|
|
1432
2404
|
* the POM above until there is none: the top of the connected POM chain.
|
|
1433
2405
|
*/
|
|
1434
2406
|
findRoot(startDir, boundaryDir) {
|
|
1435
|
-
let dir =
|
|
1436
|
-
if (!
|
|
1437
|
-
const boundary = boundaryDir ?
|
|
2407
|
+
let dir = resolve10(startDir);
|
|
2408
|
+
if (!existsSync12(join16(dir, "pom.xml"))) return void 0;
|
|
2409
|
+
const boundary = boundaryDir ? resolve10(boundaryDir) : void 0;
|
|
1438
2410
|
for (; ; ) {
|
|
1439
2411
|
const above = pomAbove(dir, boundary);
|
|
1440
2412
|
if (!above) return dir;
|
|
@@ -1445,7 +2417,7 @@ var MavenModuleDiscoveryAdapter = class {
|
|
|
1445
2417
|
const project = MavenProject.load(rootDir);
|
|
1446
2418
|
const modules = project.poms.map((pom) => {
|
|
1447
2419
|
const coordinates = project.coordinatesOf(pom);
|
|
1448
|
-
const relPath =
|
|
2420
|
+
const relPath = relative6(rootDir, pom.dir).replace(/\\/g, "/");
|
|
1449
2421
|
return {
|
|
1450
2422
|
id: coordinates.groupId ? `${coordinates.groupId}:${coordinates.artifactId}` : coordinates.artifactId,
|
|
1451
2423
|
name: pom.name ? interpolate(pom.name, project.chainOf(pom).poms) : pom.artifactId,
|
|
@@ -1472,7 +2444,7 @@ function ancestorsWithin(dir, boundary) {
|
|
|
1472
2444
|
const result = [];
|
|
1473
2445
|
let current = dir;
|
|
1474
2446
|
while (current !== boundary) {
|
|
1475
|
-
const parent =
|
|
2447
|
+
const parent = dirname7(current);
|
|
1476
2448
|
if (parent === current) break;
|
|
1477
2449
|
result.push(parent);
|
|
1478
2450
|
current = parent;
|
|
@@ -1480,22 +2452,22 @@ function ancestorsWithin(dir, boundary) {
|
|
|
1480
2452
|
return result;
|
|
1481
2453
|
}
|
|
1482
2454
|
function pomAbove(dir, boundary) {
|
|
1483
|
-
const pom = tryReadPom(
|
|
2455
|
+
const pom = tryReadPom(join16(dir, "pom.xml"));
|
|
1484
2456
|
if (!pom) return void 0;
|
|
1485
2457
|
const ancestors = ancestorsWithin(dir, boundary);
|
|
1486
2458
|
const found = [];
|
|
1487
2459
|
const parentFile = parentPomFile(pom);
|
|
1488
|
-
if (parentFile && pom.parent && ancestors.includes(
|
|
2460
|
+
if (parentFile && pom.parent && ancestors.includes(dirname7(parentFile))) {
|
|
1489
2461
|
const parent = tryReadPom(parentFile);
|
|
1490
2462
|
const groupId = parent?.groupId ?? parent?.parent?.groupId;
|
|
1491
2463
|
if (parent && parent.artifactId === pom.parent.artifactId && groupId === pom.parent.groupId) {
|
|
1492
|
-
found.push(
|
|
2464
|
+
found.push(dirname7(parentFile));
|
|
1493
2465
|
}
|
|
1494
2466
|
}
|
|
1495
|
-
const ownFile =
|
|
2467
|
+
const ownFile = join16(dir, "pom.xml");
|
|
1496
2468
|
const aggregator = ancestors.find(
|
|
1497
|
-
(ancestor) => tryReadPom(
|
|
1498
|
-
const target =
|
|
2469
|
+
(ancestor) => tryReadPom(join16(ancestor, "pom.xml"))?.modules.some((entry) => {
|
|
2470
|
+
const target = resolve10(ancestor, entry);
|
|
1499
2471
|
return target === dir || target === ownFile;
|
|
1500
2472
|
})
|
|
1501
2473
|
);
|
|
@@ -1504,12 +2476,12 @@ function pomAbove(dir, boundary) {
|
|
|
1504
2476
|
}
|
|
1505
2477
|
|
|
1506
2478
|
// src/adapters/maven/maven-registry.ts
|
|
1507
|
-
import { existsSync as
|
|
1508
|
-
import { join as
|
|
2479
|
+
import { existsSync as existsSync15 } from "fs";
|
|
2480
|
+
import { join as join19 } from "path";
|
|
1509
2481
|
|
|
1510
2482
|
// src/adapters/maven/maven-command.ts
|
|
1511
|
-
import { existsSync as
|
|
1512
|
-
import { dirname as
|
|
2483
|
+
import { existsSync as existsSync13 } from "fs";
|
|
2484
|
+
import { dirname as dirname8, isAbsolute as isAbsolute2, join as join17, relative as relative7, resolve as resolve11, sep } from "path";
|
|
1513
2485
|
var MavenUnavailableError = class extends Error {
|
|
1514
2486
|
constructor() {
|
|
1515
2487
|
super("Maven is not available: no Maven Wrapper (mvnw) in the project and no `mvn` on the PATH.");
|
|
@@ -1529,27 +2501,27 @@ var WRAPPER = IS_WINDOWS2 ? "mvnw.cmd" : "mvnw";
|
|
|
1529
2501
|
var BATCH_ARGS = ["-B", "--no-transfer-progress"];
|
|
1530
2502
|
var mvnOnPath;
|
|
1531
2503
|
function findMavenWrapper(fromDir, rootDir) {
|
|
1532
|
-
let dir =
|
|
1533
|
-
const root =
|
|
2504
|
+
let dir = resolve11(fromDir);
|
|
2505
|
+
const root = resolve11(rootDir);
|
|
1534
2506
|
for (; ; ) {
|
|
1535
|
-
const candidate =
|
|
1536
|
-
if (
|
|
1537
|
-
if (dir === root ||
|
|
1538
|
-
const parent =
|
|
2507
|
+
const candidate = join17(dir, WRAPPER);
|
|
2508
|
+
if (existsSync13(candidate)) return candidate;
|
|
2509
|
+
if (dir === root || relative7(root, dir).startsWith("..")) return void 0;
|
|
2510
|
+
const parent = dirname8(dir);
|
|
1539
2511
|
if (parent === dir) return void 0;
|
|
1540
2512
|
dir = parent;
|
|
1541
2513
|
}
|
|
1542
2514
|
}
|
|
1543
2515
|
function relativeToCwd(cwd, file) {
|
|
1544
|
-
const path =
|
|
1545
|
-
return
|
|
2516
|
+
const path = relative7(cwd, file);
|
|
2517
|
+
return isAbsolute2(path) ? file : path || ".";
|
|
1546
2518
|
}
|
|
1547
2519
|
async function runMaven(args, options) {
|
|
1548
2520
|
const wrapper = findMavenWrapper(options.cwd, options.rootDir);
|
|
1549
2521
|
const run = { cwd: options.cwd, timeoutMs: options.timeoutMs };
|
|
1550
2522
|
if (wrapper) {
|
|
1551
2523
|
const relativeWrapper = relativeToCwd(options.cwd, wrapper);
|
|
1552
|
-
const path =
|
|
2524
|
+
const path = isAbsolute2(relativeWrapper) || relativeWrapper.includes(sep) ? relativeWrapper : `.${sep}${relativeWrapper}`;
|
|
1553
2525
|
return IS_WINDOWS2 ? runCommand(path, [...BATCH_ARGS, ...args], run) : runCommand("sh", [path, ...BATCH_ARGS, ...args], run);
|
|
1554
2526
|
}
|
|
1555
2527
|
mvnOnPath ??= runCommand("mvn", ["-v"], { cwd: options.cwd, timeoutMs: 3e4 }).then(
|
|
@@ -1566,8 +2538,8 @@ function mavenErrors(output) {
|
|
|
1566
2538
|
|
|
1567
2539
|
// src/adapters/maven/maven-resolution.ts
|
|
1568
2540
|
import { randomUUID } from "crypto";
|
|
1569
|
-
import { existsSync as
|
|
1570
|
-
import { join as
|
|
2541
|
+
import { existsSync as existsSync14, mkdirSync, readFileSync as readFileSync14, readdirSync as readdirSync3, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
2542
|
+
import { join as join18 } from "path";
|
|
1571
2543
|
var DEFAULT_MAVEN_PLUGINS = { help: "3.5.2", versions: "2.22.0" };
|
|
1572
2544
|
var HELP_PLUGIN_ID = "org.apache.maven.plugins:maven-help-plugin";
|
|
1573
2545
|
var VERSIONS_PLUGIN_ID = "org.codehaus.mojo:versions-maven-plugin";
|
|
@@ -1599,21 +2571,21 @@ var ScratchDirectory = class {
|
|
|
1599
2571
|
target;
|
|
1600
2572
|
createdTarget;
|
|
1601
2573
|
constructor(moduleDir) {
|
|
1602
|
-
this.target =
|
|
1603
|
-
this.createdTarget = !
|
|
1604
|
-
this.path =
|
|
2574
|
+
this.target = join18(moduleDir, "target");
|
|
2575
|
+
this.createdTarget = !existsSync14(this.target);
|
|
2576
|
+
this.path = join18(this.target, `.elevate-${randomUUID().slice(0, 8)}`);
|
|
1605
2577
|
mkdirSync(this.path, { recursive: true });
|
|
1606
2578
|
}
|
|
1607
2579
|
dispose() {
|
|
1608
2580
|
rmSync(this.path, { recursive: true, force: true });
|
|
1609
2581
|
try {
|
|
1610
|
-
if (this.createdTarget &&
|
|
2582
|
+
if (this.createdTarget && readdirSync3(this.target).length === 0) rmSync(this.target, { recursive: true });
|
|
1611
2583
|
} catch {
|
|
1612
2584
|
}
|
|
1613
2585
|
}
|
|
1614
2586
|
};
|
|
1615
2587
|
async function readEffectivePoms(pomFile, moduleDir, rootDir, scratch, plugins, recursive = false) {
|
|
1616
|
-
const output =
|
|
2588
|
+
const output = join18(scratch.path, "effective-pom.xml");
|
|
1617
2589
|
const args = [
|
|
1618
2590
|
"-f",
|
|
1619
2591
|
relativeToCwd(moduleDir, pomFile),
|
|
@@ -1625,7 +2597,7 @@ async function readEffectivePoms(pomFile, moduleDir, rootDir, scratch, plugins,
|
|
|
1625
2597
|
rootDir,
|
|
1626
2598
|
timeoutMs: MAVEN_TIMEOUT_MS
|
|
1627
2599
|
});
|
|
1628
|
-
if (result.exitCode !== 0 || !
|
|
2600
|
+
if (result.exitCode !== 0 || !existsSync14(output)) {
|
|
1629
2601
|
const text = `${result.stdout}
|
|
1630
2602
|
${result.stderr}`;
|
|
1631
2603
|
if (isPluginUnavailable(text, HELP_PLUGIN_ID)) {
|
|
@@ -1637,7 +2609,7 @@ ${result.stderr}`;
|
|
|
1637
2609
|
throw new MavenCommandError(`Maven could not build the effective POM:
|
|
1638
2610
|
${mavenErrors(text)}`, text);
|
|
1639
2611
|
}
|
|
1640
|
-
return parseEffectivePoms(
|
|
2612
|
+
return parseEffectivePoms(readFileSync14(output, "utf8"));
|
|
1641
2613
|
}
|
|
1642
2614
|
function parseEffectivePoms(source) {
|
|
1643
2615
|
const root = parseXml(source);
|
|
@@ -1649,7 +2621,7 @@ function parseEffectivePoms(source) {
|
|
|
1649
2621
|
return models;
|
|
1650
2622
|
}
|
|
1651
2623
|
function mergeRepositories(models) {
|
|
1652
|
-
const
|
|
2624
|
+
const merge2 = (name, pick) => {
|
|
1653
2625
|
const byId = /* @__PURE__ */ new Map();
|
|
1654
2626
|
for (const element of models.map(pick)) {
|
|
1655
2627
|
for (const repository of element?.children ?? []) {
|
|
@@ -1662,8 +2634,8 @@ function mergeRepositories(models) {
|
|
|
1662
2634
|
return {
|
|
1663
2635
|
dependencies: [],
|
|
1664
2636
|
managedDependencies: [],
|
|
1665
|
-
repositories:
|
|
1666
|
-
pluginRepositories:
|
|
2637
|
+
repositories: merge2("repositories", (m) => m.repositories),
|
|
2638
|
+
pluginRepositories: merge2("pluginRepositories", (m) => m.pluginRepositories)
|
|
1667
2639
|
};
|
|
1668
2640
|
}
|
|
1669
2641
|
function toEffectiveModel(project) {
|
|
@@ -1721,10 +2693,10 @@ function buildProbePom(artifacts, model) {
|
|
|
1721
2693
|
}
|
|
1722
2694
|
async function queryNewerVersions(artifacts, model, moduleDir, rootDir, scratch, plugins) {
|
|
1723
2695
|
if (artifacts.length === 0) return { newerVersions: /* @__PURE__ */ new Map(), failedLookups: /* @__PURE__ */ new Set() };
|
|
1724
|
-
const probeDir =
|
|
2696
|
+
const probeDir = join18(scratch.path, "probe");
|
|
1725
2697
|
mkdirSync(probeDir, { recursive: true });
|
|
1726
|
-
const probePom =
|
|
1727
|
-
|
|
2698
|
+
const probePom = join18(probeDir, "pom.xml");
|
|
2699
|
+
writeFileSync3(probePom, buildProbePom(artifacts, model), "utf8");
|
|
1728
2700
|
const result = await runMaven(
|
|
1729
2701
|
[
|
|
1730
2702
|
"-N",
|
|
@@ -1738,8 +2710,8 @@ async function queryNewerVersions(artifacts, model, moduleDir, rootDir, scratch,
|
|
|
1738
2710
|
);
|
|
1739
2711
|
const log = `${result.stdout}
|
|
1740
2712
|
${result.stderr}`;
|
|
1741
|
-
const reportFile =
|
|
1742
|
-
if (result.exitCode !== 0 || !
|
|
2713
|
+
const reportFile = join18(probeDir, "target", "dependency-updates-report.xml");
|
|
2714
|
+
if (result.exitCode !== 0 || !existsSync14(reportFile)) {
|
|
1743
2715
|
if (isPluginUnavailable(log, VERSIONS_PLUGIN_ID)) {
|
|
1744
2716
|
throw new MavenPluginUnavailableError(
|
|
1745
2717
|
{ id: VERSIONS_PLUGIN_ID, version: plugins.versions, key: "versions", purpose: "look up newer versions" },
|
|
@@ -1750,7 +2722,7 @@ ${result.stderr}`;
|
|
|
1750
2722
|
${mavenErrors(log)}`, log);
|
|
1751
2723
|
}
|
|
1752
2724
|
return {
|
|
1753
|
-
newerVersions: parseUpdatesReport(
|
|
2725
|
+
newerVersions: parseUpdatesReport(readFileSync14(reportFile, "utf8")),
|
|
1754
2726
|
failedLookups: parseFailedLookups(log)
|
|
1755
2727
|
};
|
|
1756
2728
|
}
|
|
@@ -1835,18 +2807,18 @@ var MavenRegistryAdapter = class {
|
|
|
1835
2807
|
}
|
|
1836
2808
|
};
|
|
1837
2809
|
async function projectRepositories(cwd, scratch, plugins) {
|
|
1838
|
-
const rootPom =
|
|
1839
|
-
if (!
|
|
2810
|
+
const rootPom = join19(cwd, "pom.xml");
|
|
2811
|
+
if (!existsSync15(rootPom)) return NO_REPOSITORIES;
|
|
1840
2812
|
const models = await readEffectivePoms(rootPom, cwd, cwd, scratch, plugins);
|
|
1841
2813
|
return [...models.values()][0] ?? NO_REPOSITORIES;
|
|
1842
2814
|
}
|
|
1843
2815
|
|
|
1844
2816
|
// src/adapters/maven/maven-scanner.ts
|
|
1845
|
-
import { join as
|
|
1846
|
-
import
|
|
2817
|
+
import { join as join20 } from "path";
|
|
2818
|
+
import semver11 from "semver";
|
|
1847
2819
|
|
|
1848
2820
|
// src/adapters/maven/maven-locator.ts
|
|
1849
|
-
import { relative as
|
|
2821
|
+
import { relative as relative8 } from "path";
|
|
1850
2822
|
function locateDependencyVersion(chain, target, rootDir) {
|
|
1851
2823
|
for (const pom of chain) {
|
|
1852
2824
|
const dependency = findDependency(pom.dependencies, target, chain);
|
|
@@ -1927,13 +2899,13 @@ function resolveLiteral(pom, kind, versionElement, chain, target, rootDir) {
|
|
|
1927
2899
|
file,
|
|
1928
2900
|
kind: propertyName ? "property" : kind,
|
|
1929
2901
|
propertyName,
|
|
1930
|
-
displayPath:
|
|
2902
|
+
displayPath: relative8(rootDir, file).replace(/\\/g, "/")
|
|
1931
2903
|
};
|
|
1932
2904
|
return { found: true, declaration, element, value };
|
|
1933
2905
|
}
|
|
1934
2906
|
|
|
1935
2907
|
// src/adapters/maven/maven-scanner.ts
|
|
1936
|
-
var
|
|
2908
|
+
var DIFF_ORDER3 = { major: 0, minor: 1, patch: 2 };
|
|
1937
2909
|
var MavenDependencyAdapter = class {
|
|
1938
2910
|
constructor(plugins = DEFAULT_MAVEN_PLUGINS) {
|
|
1939
2911
|
this.plugins = plugins;
|
|
@@ -1987,14 +2959,14 @@ function groupModules(project, modules) {
|
|
|
1987
2959
|
if (module.aggregatorDir) reactorSizes.set(module.aggregatorDir, (reactorSizes.get(module.aggregatorDir) ?? 0) + 1);
|
|
1988
2960
|
}
|
|
1989
2961
|
for (const module of modules) {
|
|
1990
|
-
const pom = project.pomAt(
|
|
2962
|
+
const pom = project.pomAt(join20(module.path, "pom.xml"));
|
|
1991
2963
|
if (!pom) continue;
|
|
1992
2964
|
const reactor = module.aggregatorDir;
|
|
1993
2965
|
const batched = reactor !== void 0 && (reactorSizes.get(reactor) ?? 0) > 1;
|
|
1994
2966
|
const key = batched ? `reactor:${reactor}` : `module:${module.path}`;
|
|
1995
2967
|
const group = groups.get(key) ?? {
|
|
1996
2968
|
dir: batched ? reactor : module.path,
|
|
1997
|
-
aggregatorPom: batched ?
|
|
2969
|
+
aggregatorPom: batched ? join20(reactor, "pom.xml") : void 0,
|
|
1998
2970
|
members: []
|
|
1999
2971
|
};
|
|
2000
2972
|
group.members.push({ module, pom });
|
|
@@ -2083,7 +3055,7 @@ function finishModule(plan, report, channel) {
|
|
|
2083
3055
|
collect2(updateCandidate(subject, latest, newer, origin), candidates, skipped);
|
|
2084
3056
|
}
|
|
2085
3057
|
markSharedDeclarations(candidates);
|
|
2086
|
-
candidates.sort((a, b) => (
|
|
3058
|
+
candidates.sort((a, b) => (DIFF_ORDER3[a.diff] ?? 3) - (DIFF_ORDER3[b.diff] ?? 3));
|
|
2087
3059
|
return { candidates, skipped };
|
|
2088
3060
|
}
|
|
2089
3061
|
function newerThan(current, ascending) {
|
|
@@ -2114,7 +3086,7 @@ function buildCandidate(subject, target, origin, action) {
|
|
|
2114
3086
|
}
|
|
2115
3087
|
const currentClean = cleanJavaVersion(subject.version) ?? "0.0.0";
|
|
2116
3088
|
const targetClean = cleanJavaVersion(target) ?? currentClean;
|
|
2117
|
-
const diff =
|
|
3089
|
+
const diff = semver11.eq(currentClean, targetClean) ? "patch" : diffVersions(currentClean, targetClean);
|
|
2118
3090
|
const isPreRelease = isPreReleaseVersion(target);
|
|
2119
3091
|
return {
|
|
2120
3092
|
candidate: {
|
|
@@ -2162,11 +3134,11 @@ function markSharedDeclarations(candidates) {
|
|
|
2162
3134
|
}
|
|
2163
3135
|
|
|
2164
3136
|
// src/adapters/maven/maven-updater.ts
|
|
2165
|
-
import { readFileSync as
|
|
2166
|
-
import { join as
|
|
3137
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync4 } from "fs";
|
|
3138
|
+
import { join as join21, resolve as resolve12 } from "path";
|
|
2167
3139
|
var MavenUpdaterAdapter = class {
|
|
2168
3140
|
affectedFiles(module, _rootDir, updates) {
|
|
2169
|
-
const files = new Set(updates.map((u) =>
|
|
3141
|
+
const files = new Set(updates.map((u) => resolve12(u.declaration?.file ?? join21(module.path, "pom.xml"))));
|
|
2170
3142
|
return [...files];
|
|
2171
3143
|
}
|
|
2172
3144
|
async applyUpdates(module, rootDir, updates, onProgress) {
|
|
@@ -2176,13 +3148,13 @@ var MavenUpdaterAdapter = class {
|
|
|
2176
3148
|
if (!update.declaration) {
|
|
2177
3149
|
throw new InstallError(`No version declaration known for ${update.coordinate.identifier}; rescan the module.`);
|
|
2178
3150
|
}
|
|
2179
|
-
const file =
|
|
3151
|
+
const file = resolve12(update.declaration.file);
|
|
2180
3152
|
byFile.set(file, [...byFile.get(file) ?? [], update]);
|
|
2181
3153
|
}
|
|
2182
3154
|
const writes = [];
|
|
2183
3155
|
for (const [file, fileUpdates] of byFile) {
|
|
2184
3156
|
onProgress(`Preparing ${fileUpdates.length} change(s) in ${fileUpdates[0].declaration.displayPath}\u2026`);
|
|
2185
|
-
const source =
|
|
3157
|
+
const source = readFileSync15(file, "utf8");
|
|
2186
3158
|
const pom = parsePom(source, file);
|
|
2187
3159
|
const chain = project.pomAt(file) ? project.chainOf(project.pomAt(file)).poms : [pom];
|
|
2188
3160
|
const edits = collectEdits(pom, [pom, ...chain.slice(1)], fileUpdates);
|
|
@@ -2194,7 +3166,7 @@ var MavenUpdaterAdapter = class {
|
|
|
2194
3166
|
}
|
|
2195
3167
|
}
|
|
2196
3168
|
onProgress("Writing POM files\u2026");
|
|
2197
|
-
for (const write of writes)
|
|
3169
|
+
for (const write of writes) writeFileSync4(write.file, write.content, "utf8");
|
|
2198
3170
|
return {
|
|
2199
3171
|
updatedCount: updates.length,
|
|
2200
3172
|
auditMessage: `Updated ${updates.length} version declaration(s) in ${writes.length} POM file(s).`,
|
|
@@ -2252,13 +3224,13 @@ function describe(declaration) {
|
|
|
2252
3224
|
}
|
|
2253
3225
|
|
|
2254
3226
|
// src/adapters/maven/maven-verifier.ts
|
|
2255
|
-
import { dirname as
|
|
2256
|
-
var
|
|
3227
|
+
import { dirname as dirname9, join as join22, relative as relative9, resolve as resolve13 } from "path";
|
|
3228
|
+
var VERIFY_TIMEOUT_MS3 = 30 * 6e4;
|
|
2257
3229
|
var MavenVerificationAdapter = class {
|
|
2258
3230
|
async verify(module, rootDir, options = {}) {
|
|
2259
3231
|
if (options.customScript) {
|
|
2260
3232
|
const label2 = options.customLabel || options.customScript;
|
|
2261
|
-
const result = await runShellScript(options.customScript, { cwd: module.path, timeoutMs:
|
|
3233
|
+
const result = await runShellScript(options.customScript, { cwd: module.path, timeoutMs: VERIFY_TIMEOUT_MS3 });
|
|
2262
3234
|
return result.exitCode === 0 ? { status: "clean", details: "Verification script succeeded.", label: label2 } : { status: "warn", details: failureDetails2(result), label: label2 };
|
|
2263
3235
|
}
|
|
2264
3236
|
const plan = planBuild(module, rootDir, options.changedFiles ?? []);
|
|
@@ -2267,7 +3239,7 @@ var MavenVerificationAdapter = class {
|
|
|
2267
3239
|
const result = await runMaven(["-q", "test-compile", ...plan.args], {
|
|
2268
3240
|
cwd: plan.cwd,
|
|
2269
3241
|
rootDir,
|
|
2270
|
-
timeoutMs:
|
|
3242
|
+
timeoutMs: VERIFY_TIMEOUT_MS3
|
|
2271
3243
|
});
|
|
2272
3244
|
if (result.exitCode === 0) {
|
|
2273
3245
|
return { status: "clean", details: "All affected modules compile, including tests.", label };
|
|
@@ -2289,15 +3261,15 @@ function planBuild(module, rootDir, changedFiles) {
|
|
|
2289
3261
|
const aggregatorDir = module.aggregatorDir;
|
|
2290
3262
|
if (!aggregatorDir) return { cwd: module.path, args: [] };
|
|
2291
3263
|
const project = MavenProject.load(rootDir);
|
|
2292
|
-
const files = changedFiles.length > 0 ? changedFiles : [
|
|
3264
|
+
const files = changedFiles.length > 0 ? changedFiles : [join22(module.path, "pom.xml")];
|
|
2293
3265
|
const selected = /* @__PURE__ */ new Set();
|
|
2294
3266
|
for (const file of files) {
|
|
2295
3267
|
const pom = project.pomAt(file);
|
|
2296
|
-
const dir =
|
|
2297
|
-
if (!pom || project.aggregatorDirOf(pom) !== aggregatorDir || dir ===
|
|
3268
|
+
const dir = resolve13(dirname9(file));
|
|
3269
|
+
if (!pom || project.aggregatorDirOf(pom) !== aggregatorDir || dir === resolve13(aggregatorDir)) {
|
|
2298
3270
|
return { cwd: aggregatorDir, args: [] };
|
|
2299
3271
|
}
|
|
2300
|
-
selected.add(
|
|
3272
|
+
selected.add(relative9(aggregatorDir, dir).replace(/\\/g, "/"));
|
|
2301
3273
|
}
|
|
2302
3274
|
return { cwd: aggregatorDir, args: ["-pl", [...selected].join(","), "-amd"] };
|
|
2303
3275
|
}
|
|
@@ -2308,6 +3280,7 @@ var MavenEcosystemStrategy = class {
|
|
|
2308
3280
|
displayName = "Java / Maven";
|
|
2309
3281
|
icon = "\u2615";
|
|
2310
3282
|
manifestFile = "pom.xml";
|
|
3283
|
+
versioning = mavenVersionPolicy;
|
|
2311
3284
|
discovery = new MavenModuleDiscoveryAdapter();
|
|
2312
3285
|
registry;
|
|
2313
3286
|
reader;
|
|
@@ -2345,6 +3318,8 @@ var EcosystemFactory = class {
|
|
|
2345
3318
|
if (!strategy) {
|
|
2346
3319
|
if (ecosystem === "maven") {
|
|
2347
3320
|
strategy = new MavenEcosystemStrategy(this.settings.mavenPlugins);
|
|
3321
|
+
} else if (ecosystem === "cargo") {
|
|
3322
|
+
strategy = new CargoEcosystemStrategy();
|
|
2348
3323
|
} else {
|
|
2349
3324
|
strategy = new NpmEcosystemStrategy();
|
|
2350
3325
|
}
|
|
@@ -2354,18 +3329,23 @@ var EcosystemFactory = class {
|
|
|
2354
3329
|
}
|
|
2355
3330
|
/** All supported ecosystems. */
|
|
2356
3331
|
static getAvailableEcosystems() {
|
|
2357
|
-
return ["npm", "maven"];
|
|
3332
|
+
return ["npm", "maven", "cargo"];
|
|
3333
|
+
}
|
|
3334
|
+
/** The ecosystem after `current`, for the dashboard's ecosystem switch. */
|
|
3335
|
+
static next(current) {
|
|
3336
|
+
const all = this.getAvailableEcosystems();
|
|
3337
|
+
return all[(all.indexOf(current) + 1) % all.length];
|
|
2358
3338
|
}
|
|
2359
3339
|
};
|
|
2360
3340
|
|
|
2361
3341
|
// src/config.ts
|
|
2362
3342
|
function findRepositoryRoot(startDir) {
|
|
2363
|
-
const start =
|
|
3343
|
+
const start = resolve14(startDir);
|
|
2364
3344
|
const boundary = findCheckoutBoundary(start);
|
|
2365
3345
|
let dir = start;
|
|
2366
3346
|
for (; ; ) {
|
|
2367
|
-
if (
|
|
2368
|
-
const parent =
|
|
3347
|
+
if (existsSync16(join23(dir, "elevate.config.json"))) return dir;
|
|
3348
|
+
const parent = dirname10(dir);
|
|
2369
3349
|
if (dir === boundary || parent === dir) break;
|
|
2370
3350
|
dir = parent;
|
|
2371
3351
|
}
|
|
@@ -2430,15 +3410,15 @@ function resolveConfig(rootDir, file, source = "elevate.config.json") {
|
|
|
2430
3410
|
}
|
|
2431
3411
|
function loadConfig2(startDir = process.cwd()) {
|
|
2432
3412
|
const rootDir = findRepositoryRoot(startDir);
|
|
2433
|
-
const configPath =
|
|
3413
|
+
const configPath = join23(rootDir, "elevate.config.json");
|
|
2434
3414
|
let file = {};
|
|
2435
|
-
if (
|
|
3415
|
+
if (existsSync16(configPath)) {
|
|
2436
3416
|
const unreadable = (detail) => new ConfigError(
|
|
2437
3417
|
`Cannot read elevate.config.json: ${detail} (${configPath}). Fix the file or remove it; ${NO_CONTINUE}`
|
|
2438
3418
|
);
|
|
2439
3419
|
let parsed;
|
|
2440
3420
|
try {
|
|
2441
|
-
parsed = JSON.parse(
|
|
3421
|
+
parsed = JSON.parse(readFileSync16(configPath, "utf8").replace(/^/, ""));
|
|
2442
3422
|
} catch (err) {
|
|
2443
3423
|
throw unreadable(err instanceof Error ? err.message : String(err));
|
|
2444
3424
|
}
|
|
@@ -2499,7 +3479,8 @@ var de = {
|
|
|
2499
3479
|
dev: "(dev/test)",
|
|
2500
3480
|
prod: "(prod/compile)",
|
|
2501
3481
|
align: "[Angleichen]",
|
|
2502
|
-
internal: "[Intern]"
|
|
3482
|
+
internal: "[Intern]",
|
|
3483
|
+
shared: (count) => `[+${count} Crates]`
|
|
2503
3484
|
},
|
|
2504
3485
|
status: {
|
|
2505
3486
|
selectedOf: (selected, total) => `${selected} von ${total}`,
|
|
@@ -2528,6 +3509,7 @@ var de = {
|
|
|
2528
3509
|
hint: "[\u2191/\u2193/j/k] Navigieren \u2502 [Enter] Ausw\xE4hlen \u2502 [Esc] Abbrechen \u2502 [Backspace] Filter l\xF6schen",
|
|
2529
3510
|
latestBadge: "LATEST",
|
|
2530
3511
|
currentBadge: "AKTUELL",
|
|
3512
|
+
alsoChanges: (names) => `\xC4ndert auch: ${names.join(", ")}`,
|
|
2531
3513
|
customBadge: "MANUELL"
|
|
2532
3514
|
},
|
|
2533
3515
|
updating: {
|
|
@@ -2556,6 +3538,7 @@ var de = {
|
|
|
2556
3538
|
scanningChirps: ["*such*", "pr\xFCfe...", "oh, Updates?", "einen Moment!"],
|
|
2557
3539
|
updatingChirps: ["*schuft*", "baue Code...", "gleich fertig!", "arbeite emsig..."],
|
|
2558
3540
|
successChirps: ["hurra! \u{1F389}", "alles aktuell!", "top gepflegt! \u{1FAB6}", "super gemacht!"],
|
|
3541
|
+
cargoChirps: ["*krabbel* \u{1F980}", "cargo check l\xE4uft durch!", "Borrow Checker nickt!", "furchtlose Updates!"],
|
|
2559
3542
|
mavenChirps: ["*kaffee schl\xFCrf* \u2615", "Spring Boot l\xE4uft!", "Java 25 rockt!", "sauber gebaut!"]
|
|
2560
3543
|
},
|
|
2561
3544
|
splash: {
|
|
@@ -2607,7 +3590,8 @@ var en = {
|
|
|
2607
3590
|
dev: "(dev/test)",
|
|
2608
3591
|
prod: "(prod/compile)",
|
|
2609
3592
|
align: "[Align]",
|
|
2610
|
-
internal: "[Internal]"
|
|
3593
|
+
internal: "[Internal]",
|
|
3594
|
+
shared: (count) => `[+${count} crates]`
|
|
2611
3595
|
},
|
|
2612
3596
|
status: {
|
|
2613
3597
|
selectedOf: (selected, total) => `${selected} of ${total}`,
|
|
@@ -2636,6 +3620,7 @@ var en = {
|
|
|
2636
3620
|
hint: "[\u2191/\u2193/j/k] Navigate \u2502 [Enter] Select \u2502 [Esc] Cancel \u2502 [Backspace] Clear filter",
|
|
2637
3621
|
latestBadge: "LATEST",
|
|
2638
3622
|
currentBadge: "CURRENT",
|
|
3623
|
+
alsoChanges: (names) => `Also changes: ${names.join(", ")}`,
|
|
2639
3624
|
customBadge: "MANUAL"
|
|
2640
3625
|
},
|
|
2641
3626
|
updating: {
|
|
@@ -2664,6 +3649,7 @@ var en = {
|
|
|
2664
3649
|
scanningChirps: ["*peeking*", "checking...", "ooh, updates?", "just a sec!"],
|
|
2665
3650
|
updatingChirps: ["*working hard*", "building...", "almost there!", "packing modules..."],
|
|
2666
3651
|
successChirps: ["hooray! \u{1F389}", "all updated!", "looking sharp! \u{1FAB6}", "great job!"],
|
|
3652
|
+
cargoChirps: ["*crab walk* \u{1F980}", "cargo check passes!", "borrow checker approves!", "fearless updates!"],
|
|
2667
3653
|
mavenChirps: ["*sipping coffee* \u2615", "Spring Boot rocks!", "Java 25 is fly!", "clean build!"]
|
|
2668
3654
|
},
|
|
2669
3655
|
splash: {
|
|
@@ -2700,48 +3686,12 @@ function useI18n(initialLocale = "de") {
|
|
|
2700
3686
|
|
|
2701
3687
|
// src/hooks/use-workspaces.ts
|
|
2702
3688
|
import { useState as useState2, useEffect, useCallback as useCallback2 } from "react";
|
|
2703
|
-
function useWorkspaces(strategy, rootDir) {
|
|
2704
|
-
const [modules, setModules] = useState2([]);
|
|
2705
|
-
const [loading, setLoading] = useState2(true);
|
|
2706
|
-
const [activeIndex, setActiveIndex] = useState2(0);
|
|
2707
|
-
const reload = useCallback2(async () => {
|
|
2708
|
-
setLoading(true);
|
|
2709
|
-
setModules([]);
|
|
2710
|
-
setActiveIndex(0);
|
|
2711
|
-
try {
|
|
2712
|
-
setModules(await strategy.discovery.discover(rootDir));
|
|
2713
|
-
} catch {
|
|
2714
|
-
setModules([]);
|
|
2715
|
-
} finally {
|
|
2716
|
-
setLoading(false);
|
|
2717
|
-
}
|
|
2718
|
-
}, [strategy, rootDir]);
|
|
2719
|
-
useEffect(() => {
|
|
2720
|
-
reload();
|
|
2721
|
-
}, [reload]);
|
|
2722
|
-
const active = modules[activeIndex] || {
|
|
2723
|
-
id: "empty",
|
|
2724
|
-
name: "No module found",
|
|
2725
|
-
path: rootDir,
|
|
2726
|
-
relPath: "Root",
|
|
2727
|
-
ecosystem: strategy.ecosystem,
|
|
2728
|
-
isRoot: true
|
|
2729
|
-
};
|
|
2730
|
-
return {
|
|
2731
|
-
modules,
|
|
2732
|
-
active,
|
|
2733
|
-
activeIndex,
|
|
2734
|
-
setActiveIndex,
|
|
2735
|
-
loading,
|
|
2736
|
-
reload
|
|
2737
|
-
};
|
|
2738
|
-
}
|
|
2739
|
-
|
|
2740
|
-
// src/hooks/use-packages.ts
|
|
2741
|
-
import { useState as useState3, useEffect as useEffect2, useMemo as useMemo2, useCallback as useCallback3, useRef } from "react";
|
|
2742
|
-
import semver5 from "semver";
|
|
2743
3689
|
|
|
2744
3690
|
// src/application/scan.ts
|
|
3691
|
+
async function discoverModules(strategy, rootDir) {
|
|
3692
|
+
const modules = await strategy.discovery.discover(rootDir);
|
|
3693
|
+
return { modules, diagnostics: strategy.discovery.diagnostics?.() ?? [] };
|
|
3694
|
+
}
|
|
2745
3695
|
function createScanContext(modules, config, channel) {
|
|
2746
3696
|
return {
|
|
2747
3697
|
rootDir: config.rootDir,
|
|
@@ -2801,7 +3751,52 @@ function candidateToJson(u) {
|
|
|
2801
3751
|
};
|
|
2802
3752
|
}
|
|
2803
3753
|
|
|
3754
|
+
// src/hooks/use-workspaces.ts
|
|
3755
|
+
function useWorkspaces(strategy, rootDir) {
|
|
3756
|
+
const [modules, setModules] = useState2([]);
|
|
3757
|
+
const [loading, setLoading] = useState2(true);
|
|
3758
|
+
const [activeIndex, setActiveIndex] = useState2(0);
|
|
3759
|
+
const [diagnostics, setDiagnostics] = useState2([]);
|
|
3760
|
+
const reload = useCallback2(async () => {
|
|
3761
|
+
setLoading(true);
|
|
3762
|
+
setModules([]);
|
|
3763
|
+
setActiveIndex(0);
|
|
3764
|
+
setDiagnostics([]);
|
|
3765
|
+
try {
|
|
3766
|
+
const found = await discoverModules(strategy, rootDir);
|
|
3767
|
+
setModules(found.modules);
|
|
3768
|
+
setDiagnostics(found.diagnostics);
|
|
3769
|
+
} catch (err) {
|
|
3770
|
+
setModules([]);
|
|
3771
|
+
setDiagnostics([err instanceof Error ? err.message : String(err)]);
|
|
3772
|
+
} finally {
|
|
3773
|
+
setLoading(false);
|
|
3774
|
+
}
|
|
3775
|
+
}, [strategy, rootDir]);
|
|
3776
|
+
useEffect(() => {
|
|
3777
|
+
reload();
|
|
3778
|
+
}, [reload]);
|
|
3779
|
+
const active = modules[activeIndex] || {
|
|
3780
|
+
id: "empty",
|
|
3781
|
+
name: "No module found",
|
|
3782
|
+
path: rootDir,
|
|
3783
|
+
relPath: "Root",
|
|
3784
|
+
ecosystem: strategy.ecosystem,
|
|
3785
|
+
isRoot: true
|
|
3786
|
+
};
|
|
3787
|
+
return {
|
|
3788
|
+
modules,
|
|
3789
|
+
active,
|
|
3790
|
+
activeIndex,
|
|
3791
|
+
setActiveIndex,
|
|
3792
|
+
loading,
|
|
3793
|
+
diagnostics,
|
|
3794
|
+
reload
|
|
3795
|
+
};
|
|
3796
|
+
}
|
|
3797
|
+
|
|
2804
3798
|
// src/hooks/use-packages.ts
|
|
3799
|
+
import { useState as useState3, useEffect as useEffect2, useMemo as useMemo2, useCallback as useCallback3, useRef } from "react";
|
|
2805
3800
|
function usePackages(strategy, module, modules, config, channel = "stable") {
|
|
2806
3801
|
const [candidates, setCandidates] = useState3([]);
|
|
2807
3802
|
const [skipped, setSkipped] = useState3([]);
|
|
@@ -2868,15 +3863,12 @@ function usePackages(strategy, module, modules, config, channel = "stable") {
|
|
|
2868
3863
|
setCandidates(
|
|
2869
3864
|
(prev) => prev.map((c) => {
|
|
2870
3865
|
if (c.coordinate.identifier !== identifier) return c;
|
|
2871
|
-
const
|
|
2872
|
-
|
|
2873
|
-
if (c.currentClean && target && semver5.valid(c.currentClean)) {
|
|
2874
|
-
diff = semver5.eq(c.currentClean, target) ? "patch" : diffVersions(c.currentClean, target);
|
|
2875
|
-
}
|
|
3866
|
+
const policy = strategy.versioning;
|
|
3867
|
+
const diff = c.currentClean ? policy.diff(c.currentClean, chosenVersion) : "minor";
|
|
2876
3868
|
const isPre = isPreReleaseVersion(chosenVersion);
|
|
2877
3869
|
return {
|
|
2878
3870
|
...c,
|
|
2879
|
-
newRange:
|
|
3871
|
+
newRange: policy.rewrite(c.currentRange, chosenVersion),
|
|
2880
3872
|
latest: chosenVersion,
|
|
2881
3873
|
diff,
|
|
2882
3874
|
selected: true,
|
|
@@ -2886,7 +3878,7 @@ function usePackages(strategy, module, modules, config, channel = "stable") {
|
|
|
2886
3878
|
};
|
|
2887
3879
|
})
|
|
2888
3880
|
);
|
|
2889
|
-
}, []);
|
|
3881
|
+
}, [strategy]);
|
|
2890
3882
|
const switchTab = useCallback3((tab) => {
|
|
2891
3883
|
setActiveTab(tab);
|
|
2892
3884
|
setCursor(0);
|
|
@@ -2918,11 +3910,11 @@ function usePackages(strategy, module, modules, config, channel = "stable") {
|
|
|
2918
3910
|
import { useState as useState4, useCallback as useCallback4 } from "react";
|
|
2919
3911
|
|
|
2920
3912
|
// src/application/update-workflow.ts
|
|
2921
|
-
import { existsSync as
|
|
2922
|
-
import { relative as
|
|
3913
|
+
import { existsSync as existsSync18 } from "fs";
|
|
3914
|
+
import { relative as relative10 } from "path";
|
|
2923
3915
|
|
|
2924
3916
|
// src/adapters/shared/file-snapshot.ts
|
|
2925
|
-
import { existsSync as
|
|
3917
|
+
import { existsSync as existsSync17, readFileSync as readFileSync17, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
2926
3918
|
var FileSnapshot = class _FileSnapshot {
|
|
2927
3919
|
constructor(entries2) {
|
|
2928
3920
|
this.entries = entries2;
|
|
@@ -2933,7 +3925,7 @@ var FileSnapshot = class _FileSnapshot {
|
|
|
2933
3925
|
const entries2 = /* @__PURE__ */ new Map();
|
|
2934
3926
|
for (const file of files) {
|
|
2935
3927
|
if (entries2.has(file)) continue;
|
|
2936
|
-
entries2.set(file,
|
|
3928
|
+
entries2.set(file, existsSync17(file) ? readFileSync17(file) : null);
|
|
2937
3929
|
}
|
|
2938
3930
|
return new _FileSnapshot(entries2);
|
|
2939
3931
|
}
|
|
@@ -2945,9 +3937,9 @@ var FileSnapshot = class _FileSnapshot {
|
|
|
2945
3937
|
changedFiles() {
|
|
2946
3938
|
return this.files.filter((file) => {
|
|
2947
3939
|
const before = this.entries.get(file) ?? null;
|
|
2948
|
-
const exists =
|
|
3940
|
+
const exists = existsSync17(file);
|
|
2949
3941
|
if (before === null) return exists;
|
|
2950
|
-
return !exists || !
|
|
3942
|
+
return !exists || !readFileSync17(file).equals(before);
|
|
2951
3943
|
});
|
|
2952
3944
|
}
|
|
2953
3945
|
/** Writes every file back to its captured state; removes files that did not exist. */
|
|
@@ -2956,7 +3948,7 @@ var FileSnapshot = class _FileSnapshot {
|
|
|
2956
3948
|
if (content === null) {
|
|
2957
3949
|
rmSync2(file, { force: true });
|
|
2958
3950
|
} else {
|
|
2959
|
-
|
|
3951
|
+
writeFileSync5(file, content);
|
|
2960
3952
|
}
|
|
2961
3953
|
}
|
|
2962
3954
|
}
|
|
@@ -2995,7 +3987,7 @@ Restoring the installed state failed as well: ${message(err)}`;
|
|
|
2995
3987
|
let baseline;
|
|
2996
3988
|
if (!options.skipVerification) {
|
|
2997
3989
|
progress("Running verification on the current state\u2026");
|
|
2998
|
-
baseline = await verify(snapshot.files.filter((file) =>
|
|
3990
|
+
baseline = await verify(snapshot.files.filter((file) => existsSync18(file)));
|
|
2999
3991
|
}
|
|
3000
3992
|
let applied;
|
|
3001
3993
|
try {
|
|
@@ -3041,7 +4033,7 @@ function message(err) {
|
|
|
3041
4033
|
return err instanceof Error ? err.message : String(err);
|
|
3042
4034
|
}
|
|
3043
4035
|
function displayPaths(files, rootDir) {
|
|
3044
|
-
return files.map((file) =>
|
|
4036
|
+
return files.map((file) => relative10(rootDir, file).replace(/\\/g, "/"));
|
|
3045
4037
|
}
|
|
3046
4038
|
|
|
3047
4039
|
// src/hooks/use-updater.ts
|
|
@@ -3255,33 +4247,39 @@ var Header = ({
|
|
|
3255
4247
|
// src/components/workspace-bar.tsx
|
|
3256
4248
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
3257
4249
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
3258
|
-
var WorkspaceBar = ({ module, internalScopes, strategy, t }) => {
|
|
4250
|
+
var WorkspaceBar = ({ module, internalScopes, strategy, t, diagnostics = [] }) => {
|
|
3259
4251
|
const scopeHint = internalScopes.length > 0 ? t.status.internalScopes(internalScopes) : t.status.noInternalScopes;
|
|
3260
|
-
return /* @__PURE__ */ jsxs3(Box3, {
|
|
3261
|
-
/* @__PURE__ */ jsxs3(Box3, {
|
|
3262
|
-
/* @__PURE__ */ jsxs3(
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
4252
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
4253
|
+
/* @__PURE__ */ jsxs3(Box3, { marginY: 1, justifyContent: "space-between", alignItems: "center", children: [
|
|
4254
|
+
/* @__PURE__ */ jsxs3(Box3, { alignItems: "center", flexShrink: 1, children: [
|
|
4255
|
+
/* @__PURE__ */ jsxs3(Text3, { bold: true, children: [
|
|
4256
|
+
"\u{1F4CD} ",
|
|
4257
|
+
strategy.icon,
|
|
4258
|
+
" ",
|
|
4259
|
+
t.workspace.label,
|
|
4260
|
+
": "
|
|
4261
|
+
] }),
|
|
4262
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.colors.brandLight, children: module.name }),
|
|
4263
|
+
/* @__PURE__ */ jsxs3(Text3, { color: theme.colors.muted, children: [
|
|
4264
|
+
" (",
|
|
4265
|
+
module.relPath,
|
|
4266
|
+
")"
|
|
4267
|
+
] })
|
|
3268
4268
|
] }),
|
|
3269
|
-
/* @__PURE__ */
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
4269
|
+
/* @__PURE__ */ jsxs3(Box3, { alignItems: "center", marginLeft: 2, children: [
|
|
4270
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.colors.muted, children: scopeHint }),
|
|
4271
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.colors.muted, children: " \u2502 " }),
|
|
4272
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.colors.brandLight, bold: true, children: "[W]" }),
|
|
4273
|
+
/* @__PURE__ */ jsxs3(Text3, { color: theme.colors.muted, children: [
|
|
4274
|
+
" ",
|
|
4275
|
+
t.workspace.switchHint.replace("[W]", "").trim()
|
|
4276
|
+
] })
|
|
3274
4277
|
] })
|
|
3275
4278
|
] }),
|
|
3276
|
-
/* @__PURE__ */ jsxs3(
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
/* @__PURE__ */ jsxs3(Text3, { color: theme.colors.muted, children: [
|
|
3281
|
-
" ",
|
|
3282
|
-
t.workspace.switchHint.replace("[W]", "").trim()
|
|
3283
|
-
] })
|
|
3284
|
-
] })
|
|
4279
|
+
diagnostics.slice(0, 2).map((d) => /* @__PURE__ */ jsxs3(Text3, { color: theme.colors.danger, wrap: "truncate-end", children: [
|
|
4280
|
+
"\u26A0 ",
|
|
4281
|
+
d.split("\n")[0]
|
|
4282
|
+
] }, d))
|
|
3285
4283
|
] });
|
|
3286
4284
|
};
|
|
3287
4285
|
|
|
@@ -3331,7 +4329,9 @@ import { Box as Box5, Text as Text5 } from "ink";
|
|
|
3331
4329
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
3332
4330
|
var PackageRow = ({ item, isFocused, t }) => {
|
|
3333
4331
|
const badge = item.diff === "major" ? /* @__PURE__ */ jsx5(Text5, { bold: true, color: theme.colors.danger, children: t.badges.major }) : item.diff === "minor" ? /* @__PURE__ */ jsx5(Text5, { color: theme.colors.warning, children: t.badges.minor }) : /* @__PURE__ */ jsx5(Text5, { color: theme.colors.success, children: t.badges.patch });
|
|
3334
|
-
const
|
|
4332
|
+
const shared = item.sharedWith && item.sharedWith.length > 0 ? ` ${t.badges.shared(item.sharedWith.length)}` : "";
|
|
4333
|
+
const label = item.qualifier ? `${item.coordinate.identifier} (${item.qualifier})` : item.coordinate.identifier;
|
|
4334
|
+
const displayName = label.slice(0, 42 - shared.length) + shared;
|
|
3335
4335
|
const isDevOrTest = item.scope === "dev" || item.scope === "test";
|
|
3336
4336
|
const property = item.declaration?.kind === "property" ? `\${${item.declaration.propertyName}}` : void 0;
|
|
3337
4337
|
return /* @__PURE__ */ jsxs5(Box5, { justifyContent: "space-between", children: [
|
|
@@ -3510,7 +4510,7 @@ var ChickenMascot = ({ state = "idle", t, ecosystem = "npm" }) => {
|
|
|
3510
4510
|
chickFace = cur.face;
|
|
3511
4511
|
wingLeft = cur.l;
|
|
3512
4512
|
wingRight = cur.r;
|
|
3513
|
-
const chirpsPool = ecosystem === "maven" ? [...t.mascot.idleChirps, ...t.mascot.mavenChirps] : t.mascot.idleChirps;
|
|
4513
|
+
const chirpsPool = ecosystem === "maven" ? [...t.mascot.idleChirps, ...t.mascot.mavenChirps] : ecosystem === "cargo" ? [...t.mascot.idleChirps, ...t.mascot.cargoChirps] : t.mascot.idleChirps;
|
|
3514
4514
|
currentChirp = chirpsPool[chirpIndex % chirpsPool.length];
|
|
3515
4515
|
}
|
|
3516
4516
|
return /* @__PURE__ */ jsxs7(Box7, { alignItems: "center", children: [
|
|
@@ -3788,6 +4788,7 @@ var VersionModal = ({
|
|
|
3788
4788
|
] }),
|
|
3789
4789
|
/* @__PURE__ */ jsx11(Text11, { color: theme.colors.muted, children: t.versionModal.current(candidate.currentRange) })
|
|
3790
4790
|
] }),
|
|
4791
|
+
candidate.sharedWith && candidate.sharedWith.length > 0 && /* @__PURE__ */ jsx11(Box11, { marginBottom: 1, children: /* @__PURE__ */ jsx11(Text11, { color: theme.colors.warning, children: t.versionModal.alsoChanges(candidate.sharedWith) }) }),
|
|
3791
4792
|
/* @__PURE__ */ jsxs11(
|
|
3792
4793
|
Box11,
|
|
3793
4794
|
{
|
|
@@ -3962,10 +4963,10 @@ var App = ({ config }) => {
|
|
|
3962
4963
|
const [versionPickerCandidate, setVersionPickerCandidate] = useState8(null);
|
|
3963
4964
|
const [ecosystem, setEcosystem] = useState8("npm");
|
|
3964
4965
|
const strategy = useMemo4(() => EcosystemFactory.getStrategy(ecosystem), [ecosystem]);
|
|
3965
|
-
const nextEcosystem = ecosystem
|
|
4966
|
+
const nextEcosystem = EcosystemFactory.next(ecosystem);
|
|
3966
4967
|
const nextStrategy = useMemo4(() => EcosystemFactory.getStrategy(nextEcosystem), [nextEcosystem]);
|
|
3967
4968
|
const toggleEcosystem = useCallback5(() => {
|
|
3968
|
-
setEcosystem((prev) => prev
|
|
4969
|
+
setEcosystem((prev) => EcosystemFactory.next(prev));
|
|
3969
4970
|
}, []);
|
|
3970
4971
|
const { locale, toggleLocale, t } = useI18n(resolveInitialLocale(config.locale));
|
|
3971
4972
|
const ws = useWorkspaces(strategy, config.rootDir);
|
|
@@ -4117,7 +5118,8 @@ var App = ({ config }) => {
|
|
|
4117
5118
|
module: ws.active,
|
|
4118
5119
|
internalScopes: config.internalScopes,
|
|
4119
5120
|
strategy,
|
|
4120
|
-
t
|
|
5121
|
+
t,
|
|
5122
|
+
diagnostics: ws.diagnostics
|
|
4121
5123
|
}
|
|
4122
5124
|
),
|
|
4123
5125
|
/* @__PURE__ */ jsx14(
|
|
@@ -4157,6 +5159,7 @@ var App = ({ config }) => {
|
|
|
4157
5159
|
};
|
|
4158
5160
|
|
|
4159
5161
|
// src/cli/parser.ts
|
|
5162
|
+
var isEcosystem = (v) => EcosystemFactory.getAvailableEcosystems().includes(v);
|
|
4160
5163
|
function parseCliArgs(args) {
|
|
4161
5164
|
const [first, ...rest] = args;
|
|
4162
5165
|
if (!first || first === "--help" || first === "-h" || first === "help") {
|
|
@@ -4194,10 +5197,10 @@ function parseCliArgs(args) {
|
|
|
4194
5197
|
result.help = true;
|
|
4195
5198
|
} else if (arg.startsWith("--ecosystem=")) {
|
|
4196
5199
|
const val = arg.split("=")[1];
|
|
4197
|
-
if (val
|
|
5200
|
+
if (isEcosystem(val)) result.ecosystem = val;
|
|
4198
5201
|
} else if (arg === "-e" || arg === "--ecosystem") {
|
|
4199
5202
|
const val = remaining[++i];
|
|
4200
|
-
if (val
|
|
5203
|
+
if (isEcosystem(val)) result.ecosystem = val;
|
|
4201
5204
|
} else if (arg.startsWith("--module=")) {
|
|
4202
5205
|
result.module = arg.split("=")[1];
|
|
4203
5206
|
} else if (arg === "-m" || arg === "--module") {
|
|
@@ -4239,7 +5242,7 @@ SUBCOMMANDS:
|
|
|
4239
5242
|
mcp Start stdio-based MCP server
|
|
4240
5243
|
|
|
4241
5244
|
FLAGS:
|
|
4242
|
-
-e, --ecosystem <npm|maven> Target ecosystem (default: npm)
|
|
5245
|
+
-e, --ecosystem <npm|maven|cargo> Target ecosystem (default: npm)
|
|
4243
5246
|
-m, --module <name|path> Target module ID or relative path (default: root)
|
|
4244
5247
|
-c, --channel <stable|all> Release channel (default: stable)
|
|
4245
5248
|
-p, --packages <pkg1,pkg2@1.2.3> Comma-separated list of packages to update
|
|
@@ -4267,11 +5270,11 @@ EXAMPLES:
|
|
|
4267
5270
|
async function handleModulesCommand(options, config) {
|
|
4268
5271
|
const ecosystem = options.ecosystem ?? "npm";
|
|
4269
5272
|
const strategy = EcosystemFactory.getStrategy(ecosystem);
|
|
4270
|
-
const modules = await strategy
|
|
5273
|
+
const { modules, diagnostics } = await discoverModules(strategy, config.rootDir);
|
|
4271
5274
|
const displayName = strategy.displayNameFor?.(config.rootDir) ?? strategy.displayName;
|
|
4272
5275
|
const packageManager = strategy.packageManagerFor?.(config.rootDir);
|
|
4273
5276
|
if (options.json) {
|
|
4274
|
-
console.log(JSON.stringify({ ecosystem, ...packageManager ? { packageManager } : {}, count: modules.length, modules }, null, 2));
|
|
5277
|
+
console.log(JSON.stringify({ ecosystem, ...packageManager ? { packageManager } : {}, count: modules.length, modules, diagnostics }, null, 2));
|
|
4275
5278
|
return 0;
|
|
4276
5279
|
}
|
|
4277
5280
|
console.log(`
|
|
@@ -4282,6 +5285,7 @@ async function handleModulesCommand(options, config) {
|
|
|
4282
5285
|
console.log(` \u2022 ${m.name.padEnd(45)} (${m.relPath})${isRootBadge}`);
|
|
4283
5286
|
}
|
|
4284
5287
|
console.log("");
|
|
5288
|
+
for (const problem of diagnostics) console.error(`\u26A0\uFE0F ${problem}`);
|
|
4285
5289
|
return 0;
|
|
4286
5290
|
}
|
|
4287
5291
|
|
|
@@ -4303,6 +5307,8 @@ function describeSkip(skipped) {
|
|
|
4303
5307
|
return `workspace package that pnpm does not link unless it is declared with the workspace: protocol${detail}`;
|
|
4304
5308
|
case "catalog-unresolved":
|
|
4305
5309
|
return `catalog entry could not be resolved${detail}`;
|
|
5310
|
+
case "incompatible-rust-version":
|
|
5311
|
+
return `newer versions need a newer Rust than the project's rust-version${detail}`;
|
|
4306
5312
|
}
|
|
4307
5313
|
}
|
|
4308
5314
|
|
|
@@ -4311,7 +5317,8 @@ async function handleScanCommand(options, config) {
|
|
|
4311
5317
|
const ecosystem = options.ecosystem ?? "npm";
|
|
4312
5318
|
const channel = options.channel ?? config.channel ?? "stable";
|
|
4313
5319
|
const strategy = EcosystemFactory.getStrategy(ecosystem);
|
|
4314
|
-
const modules = await strategy
|
|
5320
|
+
const { modules, diagnostics } = await discoverModules(strategy, config.rootDir);
|
|
5321
|
+
for (const problem of diagnostics) console.error(`\u26A0\uFE0F ${problem}`);
|
|
4315
5322
|
if (modules.length === 0) {
|
|
4316
5323
|
if (options.json) {
|
|
4317
5324
|
console.log(JSON.stringify({ error: `No ${strategy.displayName} modules found.`, ecosystem }, null, 2));
|
|
@@ -4461,7 +5468,7 @@ async function handleVersionsCommand(options, config) {
|
|
|
4461
5468
|
}
|
|
4462
5469
|
|
|
4463
5470
|
// src/application/update-selection.ts
|
|
4464
|
-
import
|
|
5471
|
+
import semver12 from "semver";
|
|
4465
5472
|
var UpdateSelectionError = class extends Error {
|
|
4466
5473
|
constructor(message2) {
|
|
4467
5474
|
super(message2);
|
|
@@ -4486,9 +5493,9 @@ function selectRequested(scan, requests, options) {
|
|
|
4486
5493
|
`'${request.identifier}' has no pending update. Specify a target version (e.g. ${request.identifier}@1.2.3).`
|
|
4487
5494
|
);
|
|
4488
5495
|
}
|
|
4489
|
-
if (options.
|
|
5496
|
+
if (!policyOf(options).allowsManualTargets) {
|
|
4490
5497
|
throw new UpdateSelectionError(
|
|
4491
|
-
`'${request.identifier}' has no pending update.
|
|
5498
|
+
`'${request.identifier}' has no pending update. Updates are written where the scan located the version; a dependency without an offered update cannot be retargeted.`
|
|
4492
5499
|
);
|
|
4493
5500
|
}
|
|
4494
5501
|
if (options.originOf(request.identifier) !== "public") {
|
|
@@ -4513,12 +5520,12 @@ function retarget(found, target, options) {
|
|
|
4513
5520
|
`'${found.coordinate.identifier}' is a workspace module; it can only be aligned to its local version ${found.latest}.`
|
|
4514
5521
|
);
|
|
4515
5522
|
}
|
|
4516
|
-
if (options.
|
|
5523
|
+
if (!policyOf(options).allowsManualTargets && found.availableVersions && !found.availableVersions.includes(target)) {
|
|
4517
5524
|
throw new UpdateSelectionError(
|
|
4518
5525
|
`'${found.coordinate.identifier}' has no version ${target} in the configured repositories. Available: ${found.availableVersions.slice(0, 5).join(", ")}${found.availableVersions.length > 5 ? ", \u2026" : ""}.`
|
|
4519
5526
|
);
|
|
4520
5527
|
}
|
|
4521
|
-
const diff =
|
|
5528
|
+
const diff = policyOf(options).diff(found.currentClean, target);
|
|
4522
5529
|
if (diff === "major" && !options.allowMajor) {
|
|
4523
5530
|
throw new UpdateSelectionError(
|
|
4524
5531
|
`'${found.coordinate.identifier}' requires a major upgrade to ${target}. Pass --allow-major / allowMajor.`
|
|
@@ -4527,7 +5534,7 @@ function retarget(found, target, options) {
|
|
|
4527
5534
|
const isPreRelease = isPreReleaseVersion(target);
|
|
4528
5535
|
return {
|
|
4529
5536
|
...found,
|
|
4530
|
-
newRange: options.
|
|
5537
|
+
newRange: policyOf(options).rewrite(found.currentRange, target),
|
|
4531
5538
|
diff,
|
|
4532
5539
|
selected: true,
|
|
4533
5540
|
isCustomVersion: true,
|
|
@@ -4536,7 +5543,7 @@ function retarget(found, target, options) {
|
|
|
4536
5543
|
};
|
|
4537
5544
|
}
|
|
4538
5545
|
function manualNpmCandidate(identifier, target, options) {
|
|
4539
|
-
if (!
|
|
5546
|
+
if (!semver12.valid(target)) {
|
|
4540
5547
|
throw new UpdateSelectionError(`'${target}' is not a valid version for '${identifier}'.`);
|
|
4541
5548
|
}
|
|
4542
5549
|
return {
|
|
@@ -4553,10 +5560,8 @@ function manualNpmCandidate(identifier, target, options) {
|
|
|
4553
5560
|
isCustomVersion: true
|
|
4554
5561
|
};
|
|
4555
5562
|
}
|
|
4556
|
-
function
|
|
4557
|
-
|
|
4558
|
-
if (!to || !semver6.valid(current)) return "minor";
|
|
4559
|
-
return semver6.eq(current, to) ? "patch" : diffVersions(current, to);
|
|
5563
|
+
function policyOf(options) {
|
|
5564
|
+
return EcosystemFactory.getStrategy(options.ecosystem).versioning;
|
|
4560
5565
|
}
|
|
4561
5566
|
|
|
4562
5567
|
// src/cli/command-update.ts
|
|
@@ -4568,7 +5573,8 @@ async function handleUpdateCommand(options, config) {
|
|
|
4568
5573
|
else console.error(`\u274C ${message2}`);
|
|
4569
5574
|
return code;
|
|
4570
5575
|
};
|
|
4571
|
-
const modules = await strategy
|
|
5576
|
+
const { modules, diagnostics } = await discoverModules(strategy, config.rootDir);
|
|
5577
|
+
for (const problem of diagnostics) console.error(`\u26A0\uFE0F ${problem}`);
|
|
4572
5578
|
if (modules.length === 0) return fail(`No ${strategy.displayName} modules found.`);
|
|
4573
5579
|
const targetModule = options.module ? findModule(modules, options.module) : modules[0];
|
|
4574
5580
|
if (!targetModule) return fail(`Module '${options.module}' not found.`);
|
|
@@ -4709,9 +5715,9 @@ function parsePackageArgument(item) {
|
|
|
4709
5715
|
}
|
|
4710
5716
|
|
|
4711
5717
|
// src/cli/command-init.ts
|
|
4712
|
-
import { existsSync as
|
|
4713
|
-
import { homedir, platform } from "os";
|
|
4714
|
-
import { join as
|
|
5718
|
+
import { existsSync as existsSync19, readFileSync as readFileSync18, writeFileSync as writeFileSync6, mkdirSync as mkdirSync2 } from "fs";
|
|
5719
|
+
import { homedir as homedir2, platform } from "os";
|
|
5720
|
+
import { join as join24, resolve as resolve15 } from "path";
|
|
4715
5721
|
function mcpServerEntry() {
|
|
4716
5722
|
return {
|
|
4717
5723
|
command: "npx",
|
|
@@ -4754,10 +5760,10 @@ automatic rollback.
|
|
|
4754
5760
|
requested). A failed install or verification is rolled back automatically.
|
|
4755
5761
|
4. \`elevate_verify\` \u2014 Verify build integrity when needed
|
|
4756
5762
|
`;
|
|
4757
|
-
var home =
|
|
5763
|
+
var home = homedir2();
|
|
4758
5764
|
var isWin = platform() === "win32";
|
|
4759
5765
|
function appData() {
|
|
4760
|
-
return process.env.APPDATA ||
|
|
5766
|
+
return process.env.APPDATA || join24(home, "AppData", "Roaming");
|
|
4761
5767
|
}
|
|
4762
5768
|
function detectCursor(cwd) {
|
|
4763
5769
|
const env = {
|
|
@@ -4769,25 +5775,25 @@ function detectCursor(cwd) {
|
|
|
4769
5775
|
mcpConfigured: false,
|
|
4770
5776
|
skillConfigured: false
|
|
4771
5777
|
};
|
|
4772
|
-
const workspaceCursorDir =
|
|
4773
|
-
const globalCursorDir =
|
|
4774
|
-
if (
|
|
5778
|
+
const workspaceCursorDir = join24(cwd, ".cursor");
|
|
5779
|
+
const globalCursorDir = join24(home, ".cursor");
|
|
5780
|
+
if (existsSync19(workspaceCursorDir) || existsSync19(globalCursorDir)) {
|
|
4775
5781
|
env.detected = true;
|
|
4776
|
-
env.reason =
|
|
5782
|
+
env.reason = existsSync19(workspaceCursorDir) ? `Found .cursor/ in workspace` : `Found ~/.cursor/ global config`;
|
|
4777
5783
|
}
|
|
4778
|
-
const mcpPath =
|
|
4779
|
-
if (
|
|
5784
|
+
const mcpPath = join24(cwd, ".cursor", "mcp.json");
|
|
5785
|
+
if (existsSync19(mcpPath)) {
|
|
4780
5786
|
try {
|
|
4781
|
-
const content = JSON.parse(
|
|
5787
|
+
const content = JSON.parse(readFileSync18(mcpPath, "utf8"));
|
|
4782
5788
|
if (content?.mcpServers?.elevate) {
|
|
4783
5789
|
env.mcpConfigured = true;
|
|
4784
5790
|
}
|
|
4785
5791
|
} catch {
|
|
4786
5792
|
}
|
|
4787
5793
|
}
|
|
4788
|
-
const rulesPath =
|
|
4789
|
-
if (
|
|
4790
|
-
const content =
|
|
5794
|
+
const rulesPath = join24(cwd, ".cursorrules");
|
|
5795
|
+
if (existsSync19(rulesPath)) {
|
|
5796
|
+
const content = readFileSync18(rulesPath, "utf8");
|
|
4791
5797
|
if (content.includes("elevate") || content.includes("Elevate")) {
|
|
4792
5798
|
env.skillConfigured = true;
|
|
4793
5799
|
}
|
|
@@ -4804,12 +5810,12 @@ function detectClaudeDesktop() {
|
|
|
4804
5810
|
mcpConfigured: false,
|
|
4805
5811
|
skillConfigured: false
|
|
4806
5812
|
};
|
|
4807
|
-
const configPath = isWin ?
|
|
4808
|
-
if (
|
|
5813
|
+
const configPath = isWin ? join24(appData(), "Claude", "claude_desktop_config.json") : join24(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
5814
|
+
if (existsSync19(configPath)) {
|
|
4809
5815
|
env.detected = true;
|
|
4810
5816
|
env.reason = `Found ${configPath}`;
|
|
4811
5817
|
try {
|
|
4812
|
-
const content = JSON.parse(
|
|
5818
|
+
const content = JSON.parse(readFileSync18(configPath, "utf8"));
|
|
4813
5819
|
if (content?.mcpServers?.elevate) {
|
|
4814
5820
|
env.mcpConfigured = true;
|
|
4815
5821
|
}
|
|
@@ -4828,23 +5834,23 @@ function detectClaudeCode(cwd) {
|
|
|
4828
5834
|
mcpConfigured: false,
|
|
4829
5835
|
skillConfigured: false
|
|
4830
5836
|
};
|
|
4831
|
-
const mcpJsonPath =
|
|
4832
|
-
const claudeMdPath =
|
|
4833
|
-
if (
|
|
5837
|
+
const mcpJsonPath = join24(cwd, ".mcp.json");
|
|
5838
|
+
const claudeMdPath = join24(cwd, "CLAUDE.md");
|
|
5839
|
+
if (existsSync19(mcpJsonPath) || existsSync19(claudeMdPath)) {
|
|
4834
5840
|
env.detected = true;
|
|
4835
|
-
env.reason =
|
|
5841
|
+
env.reason = existsSync19(mcpJsonPath) ? `Found .mcp.json in workspace` : `Found CLAUDE.md in workspace`;
|
|
4836
5842
|
}
|
|
4837
|
-
if (
|
|
5843
|
+
if (existsSync19(mcpJsonPath)) {
|
|
4838
5844
|
try {
|
|
4839
|
-
const content = JSON.parse(
|
|
5845
|
+
const content = JSON.parse(readFileSync18(mcpJsonPath, "utf8"));
|
|
4840
5846
|
if (content?.mcpServers?.elevate) {
|
|
4841
5847
|
env.mcpConfigured = true;
|
|
4842
5848
|
}
|
|
4843
5849
|
} catch {
|
|
4844
5850
|
}
|
|
4845
5851
|
}
|
|
4846
|
-
if (
|
|
4847
|
-
const content =
|
|
5852
|
+
if (existsSync19(claudeMdPath)) {
|
|
5853
|
+
const content = readFileSync18(claudeMdPath, "utf8");
|
|
4848
5854
|
if (content.includes("elevate") || content.includes("Elevate")) {
|
|
4849
5855
|
env.skillConfigured = true;
|
|
4850
5856
|
}
|
|
@@ -4861,20 +5867,20 @@ function detectAntigravity(cwd) {
|
|
|
4861
5867
|
mcpConfigured: false,
|
|
4862
5868
|
skillConfigured: false
|
|
4863
5869
|
};
|
|
4864
|
-
const globalGeminiDir =
|
|
4865
|
-
const workspaceAgentsDir =
|
|
4866
|
-
if (
|
|
5870
|
+
const globalGeminiDir = join24(home, ".gemini");
|
|
5871
|
+
const workspaceAgentsDir = join24(cwd, ".agents");
|
|
5872
|
+
if (existsSync19(globalGeminiDir) || existsSync19(workspaceAgentsDir)) {
|
|
4867
5873
|
env.detected = true;
|
|
4868
|
-
env.reason =
|
|
5874
|
+
env.reason = existsSync19(workspaceAgentsDir) ? `Found .agents/ in workspace` : `Found ~/.gemini/ global config`;
|
|
4869
5875
|
}
|
|
4870
5876
|
const mcpPaths = [
|
|
4871
|
-
|
|
4872
|
-
|
|
5877
|
+
join24(cwd, ".agents", "mcp_config.json"),
|
|
5878
|
+
join24(home, ".gemini", "config", "mcp_config.json")
|
|
4873
5879
|
];
|
|
4874
5880
|
for (const p of mcpPaths) {
|
|
4875
|
-
if (
|
|
5881
|
+
if (existsSync19(p)) {
|
|
4876
5882
|
try {
|
|
4877
|
-
const content = JSON.parse(
|
|
5883
|
+
const content = JSON.parse(readFileSync18(p, "utf8"));
|
|
4878
5884
|
if (content?.mcpServers?.elevate) {
|
|
4879
5885
|
env.mcpConfigured = true;
|
|
4880
5886
|
break;
|
|
@@ -4895,24 +5901,24 @@ function detectVSCode(cwd) {
|
|
|
4895
5901
|
mcpConfigured: false,
|
|
4896
5902
|
skillConfigured: false
|
|
4897
5903
|
};
|
|
4898
|
-
const vscodeDir =
|
|
4899
|
-
if (
|
|
5904
|
+
const vscodeDir = join24(cwd, ".vscode");
|
|
5905
|
+
if (existsSync19(vscodeDir)) {
|
|
4900
5906
|
env.detected = true;
|
|
4901
5907
|
env.reason = `Found .vscode/ in workspace`;
|
|
4902
5908
|
}
|
|
4903
|
-
const mcpPath =
|
|
4904
|
-
if (
|
|
5909
|
+
const mcpPath = join24(cwd, ".vscode", "mcp.json");
|
|
5910
|
+
if (existsSync19(mcpPath)) {
|
|
4905
5911
|
try {
|
|
4906
|
-
const content = JSON.parse(
|
|
5912
|
+
const content = JSON.parse(readFileSync18(mcpPath, "utf8"));
|
|
4907
5913
|
if (content?.servers?.elevate) {
|
|
4908
5914
|
env.mcpConfigured = true;
|
|
4909
5915
|
}
|
|
4910
5916
|
} catch {
|
|
4911
5917
|
}
|
|
4912
5918
|
}
|
|
4913
|
-
const copilotInstructions =
|
|
4914
|
-
if (
|
|
4915
|
-
const content =
|
|
5919
|
+
const copilotInstructions = join24(cwd, ".github", "copilot-instructions.md");
|
|
5920
|
+
if (existsSync19(copilotInstructions)) {
|
|
5921
|
+
const content = readFileSync18(copilotInstructions, "utf8");
|
|
4916
5922
|
if (content.includes("elevate") || content.includes("Elevate")) {
|
|
4917
5923
|
env.skillConfigured = true;
|
|
4918
5924
|
}
|
|
@@ -4929,14 +5935,14 @@ function detectWindsurf() {
|
|
|
4929
5935
|
mcpConfigured: false,
|
|
4930
5936
|
skillConfigured: false
|
|
4931
5937
|
};
|
|
4932
|
-
const configPath = isWin ?
|
|
4933
|
-
const altPath =
|
|
4934
|
-
if (
|
|
5938
|
+
const configPath = isWin ? join24(appData(), "Codeium", "Windsurf", "mcp_config.json") : join24(home, ".codeium", "windsurf", "mcp_config.json");
|
|
5939
|
+
const altPath = join24(home, ".codeium", "windsurf", "mcp_config.json");
|
|
5940
|
+
if (existsSync19(configPath) || existsSync19(altPath)) {
|
|
4935
5941
|
env.detected = true;
|
|
4936
5942
|
env.reason = `Found Windsurf config`;
|
|
4937
|
-
const path =
|
|
5943
|
+
const path = existsSync19(configPath) ? configPath : altPath;
|
|
4938
5944
|
try {
|
|
4939
|
-
const content = JSON.parse(
|
|
5945
|
+
const content = JSON.parse(readFileSync18(path, "utf8"));
|
|
4940
5946
|
if (content?.mcpServers?.elevate) {
|
|
4941
5947
|
env.mcpConfigured = true;
|
|
4942
5948
|
}
|
|
@@ -4947,9 +5953,9 @@ function detectWindsurf() {
|
|
|
4947
5953
|
}
|
|
4948
5954
|
function mergeJsonMcpServer(filePath, serverKey, topLevelKey) {
|
|
4949
5955
|
let existing = {};
|
|
4950
|
-
if (
|
|
5956
|
+
if (existsSync19(filePath)) {
|
|
4951
5957
|
try {
|
|
4952
|
-
existing = JSON.parse(
|
|
5958
|
+
existing = JSON.parse(readFileSync18(filePath, "utf8"));
|
|
4953
5959
|
} catch {
|
|
4954
5960
|
existing = {};
|
|
4955
5961
|
}
|
|
@@ -4961,69 +5967,69 @@ function mergeJsonMcpServer(filePath, serverKey, topLevelKey) {
|
|
|
4961
5967
|
servers[serverKey] = mcpServerEntry();
|
|
4962
5968
|
existing[topLevelKey] = servers;
|
|
4963
5969
|
const dir = filePath.substring(0, filePath.lastIndexOf(isWin ? "\\" : "/"));
|
|
4964
|
-
if (dir && !
|
|
5970
|
+
if (dir && !existsSync19(dir)) {
|
|
4965
5971
|
mkdirSync2(dir, { recursive: true });
|
|
4966
5972
|
}
|
|
4967
|
-
|
|
5973
|
+
writeFileSync6(filePath, JSON.stringify(existing, null, 2) + "\n", "utf8");
|
|
4968
5974
|
return { written: true, message: `${filePath}: elevate MCP server registered` };
|
|
4969
5975
|
}
|
|
4970
5976
|
function appendSkillToFile(filePath, content, marker) {
|
|
4971
|
-
if (
|
|
4972
|
-
const existing =
|
|
5977
|
+
if (existsSync19(filePath)) {
|
|
5978
|
+
const existing = readFileSync18(filePath, "utf8");
|
|
4973
5979
|
if (existing.includes(marker)) {
|
|
4974
5980
|
return { written: false, message: `${filePath}: skill already present` };
|
|
4975
5981
|
}
|
|
4976
|
-
|
|
5982
|
+
writeFileSync6(filePath, existing + "\n\n" + content, "utf8");
|
|
4977
5983
|
return { written: true, message: `${filePath}: skill appended` };
|
|
4978
5984
|
}
|
|
4979
5985
|
const dir = filePath.substring(0, filePath.lastIndexOf(isWin ? "\\" : "/"));
|
|
4980
|
-
if (dir && !
|
|
5986
|
+
if (dir && !existsSync19(dir)) {
|
|
4981
5987
|
mkdirSync2(dir, { recursive: true });
|
|
4982
5988
|
}
|
|
4983
|
-
|
|
5989
|
+
writeFileSync6(filePath, content, "utf8");
|
|
4984
5990
|
return { written: true, message: `${filePath}: skill created` };
|
|
4985
5991
|
}
|
|
4986
5992
|
function configureCursor(cwd, result) {
|
|
4987
|
-
const mcpPath =
|
|
5993
|
+
const mcpPath = join24(cwd, ".cursor", "mcp.json");
|
|
4988
5994
|
const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
|
|
4989
5995
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Cursor MCP: ${mcp.message}`);
|
|
4990
|
-
const rulesPath =
|
|
5996
|
+
const rulesPath = join24(cwd, ".cursorrules");
|
|
4991
5997
|
const skill = appendSkillToFile(rulesPath, ELEVATE_SKILL_CONTENT, "elevate_scan");
|
|
4992
5998
|
result.actions.push(`${skill.written ? "\u2705" : "\u23ED\uFE0F"} Cursor Rules: ${skill.message}`);
|
|
4993
5999
|
}
|
|
4994
6000
|
function configureClaudeDesktop(result) {
|
|
4995
|
-
const configPath = isWin ?
|
|
6001
|
+
const configPath = isWin ? join24(appData(), "Claude", "claude_desktop_config.json") : join24(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
4996
6002
|
const mcp = mergeJsonMcpServer(configPath, "elevate", "mcpServers");
|
|
4997
6003
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Claude Desktop MCP: ${mcp.message}`);
|
|
4998
6004
|
}
|
|
4999
6005
|
function configureClaudeCode(cwd, result) {
|
|
5000
|
-
const mcpPath =
|
|
6006
|
+
const mcpPath = join24(cwd, ".mcp.json");
|
|
5001
6007
|
const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
|
|
5002
6008
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Claude Code MCP: ${mcp.message}`);
|
|
5003
|
-
const claudeMdPath =
|
|
6009
|
+
const claudeMdPath = join24(cwd, "CLAUDE.md");
|
|
5004
6010
|
const skill = appendSkillToFile(claudeMdPath, ELEVATE_SKILL_CONTENT, "elevate_scan");
|
|
5005
6011
|
result.actions.push(`${skill.written ? "\u2705" : "\u23ED\uFE0F"} Claude Code Skill: ${skill.message}`);
|
|
5006
6012
|
}
|
|
5007
6013
|
function configureAntigravity(cwd, result) {
|
|
5008
|
-
const mcpPath =
|
|
6014
|
+
const mcpPath = join24(cwd, ".agents", "mcp_config.json");
|
|
5009
6015
|
const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
|
|
5010
6016
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Antigravity MCP: ${mcp.message}`);
|
|
5011
6017
|
}
|
|
5012
6018
|
function configureVSCode(cwd, result) {
|
|
5013
|
-
const mcpPath =
|
|
6019
|
+
const mcpPath = join24(cwd, ".vscode", "mcp.json");
|
|
5014
6020
|
const mcp = mergeJsonMcpServer(mcpPath, "elevate", "servers");
|
|
5015
6021
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} VS Code MCP: ${mcp.message}`);
|
|
5016
|
-
const instructionsPath =
|
|
6022
|
+
const instructionsPath = join24(cwd, ".github", "copilot-instructions.md");
|
|
5017
6023
|
const skill = appendSkillToFile(instructionsPath, ELEVATE_SKILL_CONTENT, "elevate_scan");
|
|
5018
6024
|
result.actions.push(`${skill.written ? "\u2705" : "\u23ED\uFE0F"} Copilot Instructions: ${skill.message}`);
|
|
5019
6025
|
}
|
|
5020
6026
|
function configureWindsurf(result) {
|
|
5021
|
-
const configPath = isWin ?
|
|
6027
|
+
const configPath = isWin ? join24(appData(), "Codeium", "Windsurf", "mcp_config.json") : join24(home, ".codeium", "windsurf", "mcp_config.json");
|
|
5022
6028
|
const mcp = mergeJsonMcpServer(configPath, "elevate", "mcpServers");
|
|
5023
6029
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Windsurf MCP: ${mcp.message}`);
|
|
5024
6030
|
}
|
|
5025
6031
|
async function handleInitCommand(options) {
|
|
5026
|
-
const cwd =
|
|
6032
|
+
const cwd = resolve15(process.cwd());
|
|
5027
6033
|
const result = {
|
|
5028
6034
|
cwd,
|
|
5029
6035
|
environments: [],
|
|
@@ -5051,7 +6057,7 @@ async function handleInitCommand(options) {
|
|
|
5051
6057
|
console.log(` Creating portable .mcp.json for broad compatibility.
|
|
5052
6058
|
`);
|
|
5053
6059
|
}
|
|
5054
|
-
const mcpPath =
|
|
6060
|
+
const mcpPath = join24(cwd, ".mcp.json");
|
|
5055
6061
|
const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
|
|
5056
6062
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Generic MCP: ${mcp.message}`);
|
|
5057
6063
|
} else {
|
|
@@ -5118,14 +6124,14 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
5118
6124
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
5119
6125
|
var ECOSYSTEM_PROPERTY = {
|
|
5120
6126
|
type: "string",
|
|
5121
|
-
enum:
|
|
6127
|
+
enum: EcosystemFactory.getAvailableEcosystems(),
|
|
5122
6128
|
description: "Target ecosystem (default: npm)"
|
|
5123
6129
|
};
|
|
5124
6130
|
async function startMcpServer(config) {
|
|
5125
6131
|
const server = new Server(
|
|
5126
6132
|
{
|
|
5127
6133
|
name: "elevate-mcp",
|
|
5128
|
-
version:
|
|
6134
|
+
version: VERSION
|
|
5129
6135
|
},
|
|
5130
6136
|
{
|
|
5131
6137
|
capabilities: {
|
|
@@ -5138,7 +6144,7 @@ async function startMcpServer(config) {
|
|
|
5138
6144
|
tools: [
|
|
5139
6145
|
{
|
|
5140
6146
|
name: "elevate_discover_modules",
|
|
5141
|
-
description: "Discovers all repository modules and workspaces for Node/npm (package.json)
|
|
6147
|
+
description: "Discovers all repository modules and workspaces for Node/npm (package.json), Java/Maven (pom.xml) or Rust/Cargo (Cargo.toml).",
|
|
5142
6148
|
inputSchema: {
|
|
5143
6149
|
type: "object",
|
|
5144
6150
|
properties: {
|
|
@@ -5178,7 +6184,7 @@ async function startMcpServer(config) {
|
|
|
5178
6184
|
ecosystem: ECOSYSTEM_PROPERTY,
|
|
5179
6185
|
identifier: {
|
|
5180
6186
|
type: "string",
|
|
5181
|
-
description: "Package identifier (npm: chalk, maven: org.slf4j:slf4j-api)"
|
|
6187
|
+
description: "Package identifier (npm: chalk, maven: org.slf4j:slf4j-api, cargo: serde)"
|
|
5182
6188
|
}
|
|
5183
6189
|
},
|
|
5184
6190
|
required: ["identifier"]
|
|
@@ -5259,12 +6265,13 @@ async function startMcpServer(config) {
|
|
|
5259
6265
|
};
|
|
5260
6266
|
try {
|
|
5261
6267
|
if (name === "elevate_discover_modules") {
|
|
5262
|
-
const modules = await strategy
|
|
6268
|
+
const { modules, diagnostics } = await discoverModules(strategy, config.rootDir);
|
|
5263
6269
|
const packageManager = strategy.packageManagerFor?.(config.rootDir);
|
|
5264
6270
|
return reply({
|
|
5265
6271
|
ecosystem,
|
|
5266
6272
|
...packageManager ? { packageManager } : {},
|
|
5267
6273
|
count: modules.length,
|
|
6274
|
+
diagnostics,
|
|
5268
6275
|
modules: modules.map((m) => ({
|
|
5269
6276
|
id: m.id,
|
|
5270
6277
|
name: m.name,
|