@dennisrepa/elevate 1.4.0 → 1.6.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 +1413 -333
- package/dist/cli.js.map +1 -1
- package/docs/de.md +12 -0
- package/docs/en.md +12 -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,799 @@ 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.6.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 eligibleVersions(versions, options) {
|
|
1467
|
+
const project = options.rustVersion ? rust(options.rustVersion) : void 0;
|
|
1468
|
+
return versions.filter((v) => !v.yanked && semver5.valid(v.version)).filter((v) => options.channel === "all" || semver5.prerelease(v.version) === null).filter((v) => {
|
|
1469
|
+
const needs = v.rustVersion ? rust(v.rustVersion) : void 0;
|
|
1470
|
+
return !(project && needs && semver5.gt(needs, project));
|
|
1471
|
+
}).map((v) => v.version).sort(semver5.rcompare);
|
|
1472
|
+
}
|
|
1473
|
+
function selectVersion(versions, options) {
|
|
1474
|
+
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));
|
|
1475
|
+
const project = options.rustVersion ? rust(options.rustVersion) : void 0;
|
|
1476
|
+
let blocked;
|
|
1477
|
+
for (const candidate of candidates) {
|
|
1478
|
+
const needs = candidate.rustVersion ? rust(candidate.rustVersion) : void 0;
|
|
1479
|
+
if (project && needs && semver5.gt(needs, project)) {
|
|
1480
|
+
blocked ??= { version: candidate.version, requires: candidate.rustVersion };
|
|
1481
|
+
continue;
|
|
1482
|
+
}
|
|
1483
|
+
return { version: candidate.version, ...blocked ? { blockedByMsrv: blocked } : {} };
|
|
1484
|
+
}
|
|
1485
|
+
return { version: void 0, ...blocked ? { blockedByMsrv: blocked } : {} };
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
// src/adapters/cargo/cargo-registry.ts
|
|
1489
|
+
var CargoRegistryAdapter = class {
|
|
1490
|
+
constructor(deps) {
|
|
1491
|
+
this.deps = deps;
|
|
1492
|
+
}
|
|
1493
|
+
deps;
|
|
1494
|
+
async resolveEndpoint(coordinate, cwd) {
|
|
1495
|
+
const { index, isPublic } = await this.deps.endpoint(coordinate.registry, cwd);
|
|
1496
|
+
return { url: index, isPublic };
|
|
1497
|
+
}
|
|
1498
|
+
async getLatestVersion(coordinate, channel = "stable", cwd = process.cwd()) {
|
|
1499
|
+
return selectVersion(await this.load(coordinate, cwd), { channel }).version ?? null;
|
|
1500
|
+
}
|
|
1501
|
+
/** Published, not yanked, newest first. Lookup errors propagate so they are not mistaken for "no versions". */
|
|
1502
|
+
async getAllVersions(coordinate, cwd = process.cwd()) {
|
|
1503
|
+
return (await this.load(coordinate, cwd)).filter((v) => !v.yanked && semver6.valid(v.version)).map((v) => v.version).sort(semver6.rcompare);
|
|
1504
|
+
}
|
|
1505
|
+
async load(coordinate, cwd) {
|
|
1506
|
+
const { index } = await this.deps.endpoint(coordinate.registry, cwd);
|
|
1507
|
+
return this.deps.index.versions(index, coordinate.artifact);
|
|
1508
|
+
}
|
|
1509
|
+
};
|
|
1510
|
+
|
|
1511
|
+
// src/adapters/cargo/cargo-requirement.ts
|
|
1512
|
+
import semver7 from "semver";
|
|
1513
|
+
var SIMPLE = /^(\^|~|=)?\s*(\d+(?:\.\d+){0,2})$/;
|
|
1514
|
+
function toSemverRange(requirement) {
|
|
1515
|
+
const parts = requirement.split(",").map((p) => p.trim());
|
|
1516
|
+
if (parts.some((p) => p === "")) return null;
|
|
1517
|
+
const converted = parts.map((part) => {
|
|
1518
|
+
const bare = part.match(/^\d/);
|
|
1519
|
+
return bare ? `^${part}` : part;
|
|
1520
|
+
});
|
|
1521
|
+
return semver7.validRange(converted.join(" ")) ? converted.join(" ") : null;
|
|
1522
|
+
}
|
|
1523
|
+
function satisfiesRequirement(requirement, version) {
|
|
1524
|
+
const range = toSemverRange(requirement);
|
|
1525
|
+
return range !== null && semver7.satisfies(version, range, { includePrerelease: false });
|
|
1526
|
+
}
|
|
1527
|
+
function isUpgradableRequirement(requirement) {
|
|
1528
|
+
return SIMPLE.test(requirement.trim());
|
|
1529
|
+
}
|
|
1530
|
+
function requirementFloor(requirement) {
|
|
1531
|
+
const range = toSemverRange(requirement);
|
|
1532
|
+
return range ? semver7.minVersion(range)?.version ?? null : null;
|
|
1533
|
+
}
|
|
1534
|
+
function rewriteRequirement(current, target) {
|
|
1535
|
+
const match = current.trim().match(SIMPLE);
|
|
1536
|
+
if (!match) return target;
|
|
1537
|
+
if (semver7.prerelease(target)) return `${match[1] ?? ""}${target}`;
|
|
1538
|
+
const precision = match[2].split(".").length;
|
|
1539
|
+
const [major, minor, patch] = target.split(/[-+]/)[0].split(".");
|
|
1540
|
+
const written = [major, minor, patch].slice(0, precision).join(".");
|
|
1541
|
+
return `${match[1] ?? ""}${written}`;
|
|
1542
|
+
}
|
|
1543
|
+
function cargoDiff(from, to) {
|
|
1544
|
+
const a = semver7.parse(from);
|
|
1545
|
+
const b = semver7.parse(to);
|
|
1546
|
+
if (!a || !b) return "minor";
|
|
1547
|
+
if (a.major > 0 || b.major > a.major) {
|
|
1548
|
+
if (b.major !== a.major) return "major";
|
|
1549
|
+
return b.minor !== a.minor ? "minor" : "patch";
|
|
1550
|
+
}
|
|
1551
|
+
if (a.minor > 0) {
|
|
1552
|
+
if (b.minor !== a.minor) return "major";
|
|
1553
|
+
return "minor";
|
|
1554
|
+
}
|
|
1555
|
+
return "major";
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
// src/adapters/cargo/cargo-scanner.ts
|
|
1559
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
1560
|
+
import { join as join11, relative as relative4 } from "path";
|
|
1561
|
+
import semver8 from "semver";
|
|
1562
|
+
|
|
1563
|
+
// src/adapters/cargo/cargo-toml.ts
|
|
1564
|
+
import { parseTOML as parseTOML2 } from "toml-eslint-parser";
|
|
1565
|
+
var SECTIONS2 = {
|
|
1566
|
+
dependencies: "dependencies",
|
|
1567
|
+
"dev-dependencies": "dev-dependencies",
|
|
1568
|
+
dev_dependencies: "dev-dependencies",
|
|
1569
|
+
"build-dependencies": "build-dependencies",
|
|
1570
|
+
build_dependencies: "build-dependencies"
|
|
1571
|
+
};
|
|
1572
|
+
function parseCargoManifest(source) {
|
|
1573
|
+
const root = parseTOML2(source).body[0];
|
|
1574
|
+
const leaves = [];
|
|
1575
|
+
const keyPath = (key) => key.keys.map((k) => k.type === "TOMLBare" ? k.name : k.value);
|
|
1576
|
+
const visitPair = (prefix, pair) => {
|
|
1577
|
+
const path = [...prefix, ...keyPath(pair.key)];
|
|
1578
|
+
leaves.push({ path, node: pair.value });
|
|
1579
|
+
if (pair.value.type === "TOMLInlineTable") for (const inner of pair.value.body) visitPair(path, inner);
|
|
1580
|
+
};
|
|
1581
|
+
for (const element of root.body) {
|
|
1582
|
+
if (element.type === "TOMLKeyValue") visitPair([], element);
|
|
1583
|
+
else if (element.type === "TOMLTable") {
|
|
1584
|
+
const header = keyPath(element.key);
|
|
1585
|
+
for (const pair of element.body) visitPair(header, pair);
|
|
1586
|
+
leaves.push({ path: header, node: element });
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
1590
|
+
for (const { path, node } of leaves) {
|
|
1591
|
+
const located = locateSection(path);
|
|
1592
|
+
if (!located || located.rest.length === 0) continue;
|
|
1593
|
+
const [key, field] = located.rest;
|
|
1594
|
+
const id = `${located.inWorkspace}|${located.target ?? ""}|${located.section}|${key}`;
|
|
1595
|
+
let decl = byKey.get(id);
|
|
1596
|
+
if (!decl) {
|
|
1597
|
+
decl = { key, section: located.section, target: located.target, inWorkspace: located.inWorkspace, inherited: false };
|
|
1598
|
+
byKey.set(id, decl);
|
|
1599
|
+
}
|
|
1600
|
+
if (located.rest.length === 1 && node.type === "TOMLValue" && node.kind === "string") {
|
|
1601
|
+
decl.version = literalOf(node);
|
|
1602
|
+
} else if (field === "version" && located.rest.length === 2 && node.type === "TOMLValue" && node.kind === "string") {
|
|
1603
|
+
decl.version = literalOf(node);
|
|
1604
|
+
} else if (field === "workspace" && located.rest.length === 2 && node.value === true) {
|
|
1605
|
+
decl.inherited = true;
|
|
1606
|
+
} else if (field === "package" && located.rest.length === 2 && node.type === "TOMLValue") {
|
|
1607
|
+
decl.package = String(node.value);
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
return [...byKey.values()];
|
|
1611
|
+
}
|
|
1612
|
+
function literalOf(node) {
|
|
1613
|
+
return { value: node.value, start: node.range[0] + 1, end: node.range[1] - 1 };
|
|
1614
|
+
}
|
|
1615
|
+
function locateSection(path) {
|
|
1616
|
+
let i = 0;
|
|
1617
|
+
let target;
|
|
1618
|
+
let inWorkspace = false;
|
|
1619
|
+
if (path[0] === "workspace" && path[1] === "dependencies") {
|
|
1620
|
+
return { section: "dependencies", inWorkspace: true, target, rest: path.slice(2) };
|
|
1621
|
+
}
|
|
1622
|
+
if (path[0] === "target" && path.length >= 3) {
|
|
1623
|
+
target = path[1];
|
|
1624
|
+
i = 2;
|
|
1625
|
+
}
|
|
1626
|
+
const section = SECTIONS2[path[i] ?? ""];
|
|
1627
|
+
if (!section) return void 0;
|
|
1628
|
+
return { section, inWorkspace, target, rest: path.slice(i + 1) };
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
// src/adapters/cargo/cargo-lockfile.ts
|
|
1632
|
+
import { existsSync as existsSync7, readFileSync as readFileSync10 } from "fs";
|
|
1633
|
+
import { getStaticTOMLValue as getStaticTOMLValue2, parseTOML as parseTOML3 } from "toml-eslint-parser";
|
|
1634
|
+
function readLockedVersions(lockFile) {
|
|
1635
|
+
const locked = /* @__PURE__ */ new Map();
|
|
1636
|
+
if (!existsSync7(lockFile)) return locked;
|
|
1637
|
+
try {
|
|
1638
|
+
const data = getStaticTOMLValue2(parseTOML3(readFileSync10(lockFile, "utf8")));
|
|
1639
|
+
for (const pkg of data.package ?? []) locked.set(pkg.name, [...locked.get(pkg.name) ?? [], pkg.version]);
|
|
1640
|
+
} catch {
|
|
1641
|
+
}
|
|
1642
|
+
return locked;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
// src/adapters/cargo/cargo-scanner.ts
|
|
1646
|
+
var CONCURRENCY2 = 6;
|
|
1647
|
+
var DIFF_ORDER2 = { major: 0, minor: 1, patch: 2 };
|
|
1648
|
+
var CargoDependencyAdapter = class {
|
|
1649
|
+
constructor(deps) {
|
|
1650
|
+
this.deps = deps;
|
|
1651
|
+
}
|
|
1652
|
+
deps;
|
|
1653
|
+
async scan(module, context) {
|
|
1654
|
+
const metadata = await this.deps.loadMetadata(module.aggregatorDir ?? module.path);
|
|
1655
|
+
const pkg = metadata.packages.find((p) => samePath(join11(p.manifest_path, ".."), module.path));
|
|
1656
|
+
if (!pkg) return { candidates: [], skipped: [] };
|
|
1657
|
+
const own = readDeclarations(pkg.manifest_path);
|
|
1658
|
+
const locks = readLockedVersions(join11(metadata.workspace_root, "Cargo.lock"));
|
|
1659
|
+
const rootManifest = join11(metadata.workspace_root, "Cargo.toml");
|
|
1660
|
+
const root = readDeclarations(rootManifest);
|
|
1661
|
+
const groups = groupDependencies(pkg.dependencies);
|
|
1662
|
+
const outcomes = await mapPooled(groups, CONCURRENCY2, async (group) => {
|
|
1663
|
+
const primary = group.find((d) => d.kind === null) ?? group[0];
|
|
1664
|
+
const key = primary.rename ?? primary.name;
|
|
1665
|
+
const decl = findDeclaration(own, key, primary);
|
|
1666
|
+
const located = locate(decl, root, key, context.rootDir, pkg.manifest_path, rootManifest);
|
|
1667
|
+
const scope = group.every((d) => d.kind === "dev") ? "dev" : "prod";
|
|
1668
|
+
const qualifier = primary.target ?? (primary.kind === "build" ? "build" : void 0);
|
|
1669
|
+
const common = { scope, qualifier };
|
|
1670
|
+
if (primary.source === null && primary.path) {
|
|
1671
|
+
const target = [...context.workspace.values()].find((m) => samePath(m.path, primary.path));
|
|
1672
|
+
return target ? alignment(primary, target, located, common) : null;
|
|
1673
|
+
}
|
|
1674
|
+
if (!primary.source || primary.source.startsWith("git+")) return null;
|
|
1675
|
+
const outcome = await this.registryOutcome(primary, located, pkg, metadata, locks, common, context);
|
|
1676
|
+
const other = group.find((d) => d !== primary && !sameRequirement(d.req, primary.req));
|
|
1677
|
+
if (outcome && "candidate" in outcome && other) {
|
|
1678
|
+
outcome.note = {
|
|
1679
|
+
identifier: primary.name,
|
|
1680
|
+
origin: outcome.candidate.origin.kind,
|
|
1681
|
+
reason: "declaration-mismatch",
|
|
1682
|
+
detail: `${sectionOf(other)} requires ${other.req}; it is not changed`
|
|
1683
|
+
};
|
|
1684
|
+
}
|
|
1685
|
+
return outcome;
|
|
1686
|
+
});
|
|
1687
|
+
const candidates = [];
|
|
1688
|
+
const skipped = [];
|
|
1689
|
+
for (const outcome of outcomes) {
|
|
1690
|
+
if (!outcome) continue;
|
|
1691
|
+
if ("candidate" in outcome) {
|
|
1692
|
+
candidates.push(outcome.candidate);
|
|
1693
|
+
if (outcome.note) skipped.push(outcome.note);
|
|
1694
|
+
} else skipped.push(outcome.skipped);
|
|
1695
|
+
}
|
|
1696
|
+
candidates.sort((a, b) => (DIFF_ORDER2[a.diff] ?? 3) - (DIFF_ORDER2[b.diff] ?? 3));
|
|
1697
|
+
return { candidates, skipped };
|
|
1698
|
+
}
|
|
1699
|
+
async registryOutcome(dep, located, pkg, metadata, locks, common, context) {
|
|
1700
|
+
const literal = located?.version.value ?? dep.req;
|
|
1701
|
+
if (!isUpgradableRequirement(literal)) return null;
|
|
1702
|
+
if (!located || !sameRequirement(located.version.value, dep.req)) {
|
|
1703
|
+
return { skipped: { identifier: dep.name, origin: "public", reason: "declaration-mismatch", detail: `${literal} in the manifest, ${dep.req} for Cargo` } };
|
|
1704
|
+
}
|
|
1705
|
+
let origin = { kind: "public" };
|
|
1706
|
+
if (dep.registry) origin = { kind: "private", matchedBy: "scoped-registry", registry: dep.registry };
|
|
1707
|
+
else if (matchesInternalScope(dep.name, "cargo", context.internalScopes)) origin = { kind: "private", matchedBy: "internal-scope" };
|
|
1708
|
+
const endpoint = await this.deps.endpoint(dep.registry ?? void 0, context.rootDir);
|
|
1709
|
+
if (origin.kind === "private" && endpoint.isPublic) {
|
|
1710
|
+
return { skipped: { identifier: dep.name, origin: "private", reason: "private-on-public-registry", detail: endpoint.index } };
|
|
1711
|
+
}
|
|
1712
|
+
let versions;
|
|
1713
|
+
try {
|
|
1714
|
+
versions = await this.deps.index.versions(endpoint.index, dep.name);
|
|
1715
|
+
} catch (err) {
|
|
1716
|
+
return { skipped: { identifier: dep.name, origin: origin.kind, reason: "lookup-failed", detail: err instanceof Error ? err.message : String(err) } };
|
|
1717
|
+
}
|
|
1718
|
+
const chosen = selectVersion(versions, { channel: context.channel, rustVersion: pkg.rust_version ?? void 0 });
|
|
1719
|
+
const floor = requirementFloor(literal);
|
|
1720
|
+
if (!chosen.version) {
|
|
1721
|
+
if (chosen.blockedByMsrv && floor) {
|
|
1722
|
+
const { version, requires } = chosen.blockedByMsrv;
|
|
1723
|
+
return { skipped: { identifier: dep.name, origin: origin.kind, reason: "incompatible-rust-version", detail: ` (${version} needs Rust ${requires})` } };
|
|
1724
|
+
}
|
|
1725
|
+
return { skipped: { identifier: dep.name, origin: origin.kind, reason: "lookup-failed", detail: "no usable version in the registry" } };
|
|
1726
|
+
}
|
|
1727
|
+
const latest = chosen.version;
|
|
1728
|
+
if (!floor || !semver8.gt(latest, floor)) {
|
|
1729
|
+
const held = chosen.blockedByMsrv;
|
|
1730
|
+
if (floor && held && semver8.gt(held.version, floor)) {
|
|
1731
|
+
return { skipped: { identifier: dep.name, origin: origin.kind, reason: "incompatible-rust-version", detail: ` (${held.version} needs Rust ${held.requires})` } };
|
|
1732
|
+
}
|
|
1733
|
+
return null;
|
|
1734
|
+
}
|
|
1735
|
+
const newRange = rewriteRequirement(literal, latest);
|
|
1736
|
+
if (newRange === literal) return lockOutcome(dep, literal, origin, versions, pkg, metadata, locks, common, context);
|
|
1737
|
+
const tag = semver8.prerelease(latest) ? extractPreReleaseTag(latest) ?? "PRE" : void 0;
|
|
1738
|
+
const diff = cargoDiff(floor, latest);
|
|
1739
|
+
const candidate = {
|
|
1740
|
+
coordinate: { identifier: dep.name, artifact: dep.name, ecosystem: "cargo", ...dep.registry ? { registry: dep.registry } : {} },
|
|
1741
|
+
currentRange: literal,
|
|
1742
|
+
currentClean: floor,
|
|
1743
|
+
latest,
|
|
1744
|
+
newRange,
|
|
1745
|
+
diff,
|
|
1746
|
+
scope: common.scope,
|
|
1747
|
+
selected: diff !== "major",
|
|
1748
|
+
origin,
|
|
1749
|
+
action: "update",
|
|
1750
|
+
declaration: located.declaration,
|
|
1751
|
+
isPreRelease: tag !== void 0,
|
|
1752
|
+
preReleaseTag: tag,
|
|
1753
|
+
...common.qualifier ? { qualifier: common.qualifier } : {},
|
|
1754
|
+
...located.inherited ? { sharedWith: sharersOf(metadata, pkg, dep.rename ?? dep.name) } : {}
|
|
1755
|
+
};
|
|
1756
|
+
return { candidate };
|
|
1757
|
+
}
|
|
1758
|
+
};
|
|
1759
|
+
function lockOutcome(dep, requirement, origin, versions, pkg, metadata, locks, common, context) {
|
|
1760
|
+
const locked = (locks.get(dep.name) ?? []).filter((v) => satisfiesRequirement(requirement, v)).sort(semver8.rcompare)[0];
|
|
1761
|
+
if (!locked) return null;
|
|
1762
|
+
const options = { channel: context.channel, rustVersion: pkg.rust_version ?? void 0 };
|
|
1763
|
+
const allowed = versions.filter((v) => satisfiesRequirement(requirement, v.version));
|
|
1764
|
+
const newer = eligibleVersions(allowed, options).filter((v) => semver8.gt(v, locked));
|
|
1765
|
+
const latest = newer[0];
|
|
1766
|
+
if (!latest) return null;
|
|
1767
|
+
const lockFile = join11(metadata.workspace_root, "Cargo.lock");
|
|
1768
|
+
return {
|
|
1769
|
+
candidate: {
|
|
1770
|
+
coordinate: { identifier: dep.name, artifact: dep.name, ecosystem: "cargo", ...dep.registry ? { registry: dep.registry } : {} },
|
|
1771
|
+
currentRange: locked,
|
|
1772
|
+
currentClean: locked,
|
|
1773
|
+
latest,
|
|
1774
|
+
newRange: latest,
|
|
1775
|
+
diff: cargoDiff(locked, latest),
|
|
1776
|
+
scope: common.scope,
|
|
1777
|
+
// A release the requirement allows is compatible by definition, so it is safe to preselect.
|
|
1778
|
+
selected: true,
|
|
1779
|
+
origin,
|
|
1780
|
+
action: "lock",
|
|
1781
|
+
declaration: { file: lockFile, kind: "lockfile", displayPath: display(context.rootDir, lockFile) },
|
|
1782
|
+
availableVersions: newer,
|
|
1783
|
+
...common.qualifier ? { qualifier: common.qualifier } : {}
|
|
1784
|
+
}
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
function alignment(dep, target, located, common) {
|
|
1788
|
+
const local = target.version;
|
|
1789
|
+
if (!located || !local || !semver8.valid(local)) return null;
|
|
1790
|
+
const literal = located.version.value;
|
|
1791
|
+
if (!isUpgradableRequirement(literal) || satisfiesRequirement(literal, local)) return null;
|
|
1792
|
+
const floor = requirementFloor(literal) ?? "0.0.0";
|
|
1793
|
+
const diff = cargoDiff(floor, local);
|
|
1794
|
+
const tag = semver8.prerelease(local) ? extractPreReleaseTag(local) ?? "PRE" : void 0;
|
|
1795
|
+
return {
|
|
1796
|
+
candidate: {
|
|
1797
|
+
coordinate: { identifier: dep.name, artifact: dep.name, ecosystem: "cargo" },
|
|
1798
|
+
currentRange: literal,
|
|
1799
|
+
currentClean: floor,
|
|
1800
|
+
latest: local,
|
|
1801
|
+
newRange: rewriteRequirement(literal, local),
|
|
1802
|
+
diff,
|
|
1803
|
+
scope: common.scope,
|
|
1804
|
+
selected: diff !== "major",
|
|
1805
|
+
origin: { kind: "workspace", moduleId: target.id, moduleRelPath: target.relPath },
|
|
1806
|
+
action: "align",
|
|
1807
|
+
declaration: located.declaration,
|
|
1808
|
+
availableVersions: [local],
|
|
1809
|
+
isPreRelease: tag !== void 0,
|
|
1810
|
+
preReleaseTag: tag,
|
|
1811
|
+
...common.qualifier ? { qualifier: common.qualifier } : {}
|
|
1812
|
+
}
|
|
1813
|
+
};
|
|
1814
|
+
}
|
|
1815
|
+
function groupDependencies(deps) {
|
|
1816
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1817
|
+
for (const dep of deps) {
|
|
1818
|
+
const id = `${dep.rename ?? dep.name}`;
|
|
1819
|
+
groups.set(id, [...groups.get(id) ?? [], dep]);
|
|
1820
|
+
}
|
|
1821
|
+
return [...groups.values()];
|
|
1822
|
+
}
|
|
1823
|
+
function readDeclarations(manifestPath) {
|
|
1824
|
+
try {
|
|
1825
|
+
return parseCargoManifest(readFileSync11(manifestPath, "utf8"));
|
|
1826
|
+
} catch {
|
|
1827
|
+
return [];
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
var sectionOf = (dep) => dep.kind === "dev" ? "dev-dependencies" : dep.kind === "build" ? "build-dependencies" : "dependencies";
|
|
1831
|
+
function findDeclaration(decls, key, dep) {
|
|
1832
|
+
const candidates = decls.filter((d) => !d.inWorkspace && d.key === key);
|
|
1833
|
+
return candidates.find((d) => d.section === sectionOf(dep) && (d.target ?? null) === dep.target) ?? candidates[0];
|
|
1834
|
+
}
|
|
1835
|
+
function locate(decl, root, key, repoRoot, manifestPath, rootManifest) {
|
|
1836
|
+
if (!decl) return void 0;
|
|
1837
|
+
if (decl.inherited) {
|
|
1838
|
+
const shared = root.find((d) => d.inWorkspace && d.key === key);
|
|
1839
|
+
if (!shared?.version) return void 0;
|
|
1840
|
+
return {
|
|
1841
|
+
version: shared.version,
|
|
1842
|
+
inherited: true,
|
|
1843
|
+
declaration: { file: rootManifest, kind: "dependency-management", displayPath: display(repoRoot, rootManifest) }
|
|
1844
|
+
};
|
|
1845
|
+
}
|
|
1846
|
+
if (!decl.version) return void 0;
|
|
1847
|
+
return {
|
|
1848
|
+
version: decl.version,
|
|
1849
|
+
inherited: false,
|
|
1850
|
+
declaration: { file: manifestPath, kind: "dependency", displayPath: display(repoRoot, manifestPath) }
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
function sharersOf(metadata, self, key) {
|
|
1854
|
+
return metadata.packages.filter((p) => p !== self).filter((p) => readDeclarations(p.manifest_path).some((d) => d.key === key && d.inherited)).map((p) => p.name);
|
|
1855
|
+
}
|
|
1856
|
+
var display = (repoRoot, file) => relative4(repoRoot, file).replace(/\\/g, "/");
|
|
1857
|
+
var samePath = (a, b) => relative4(a, b) === "";
|
|
1858
|
+
function sameRequirement(literal, req) {
|
|
1859
|
+
const a = toSemverRange(literal);
|
|
1860
|
+
const b = toSemverRange(req);
|
|
1861
|
+
return a !== null && b !== null && semver8.validRange(a) === semver8.validRange(b);
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
// src/adapters/cargo/cargo-updater.ts
|
|
1865
|
+
import { existsSync as existsSync8, readFileSync as readFileSync12, writeFileSync as writeFileSync2 } from "fs";
|
|
1866
|
+
import { join as join12, resolve as resolve6 } from "path";
|
|
1867
|
+
import semver9 from "semver";
|
|
1063
1868
|
|
|
1064
1869
|
// src/adapters/shared/xml.ts
|
|
1065
1870
|
import { SaxesParser } from "saxes";
|
|
@@ -1192,9 +1997,241 @@ function localName(qualified) {
|
|
|
1192
1997
|
return colon >= 0 ? qualified.slice(colon + 1) : qualified;
|
|
1193
1998
|
}
|
|
1194
1999
|
|
|
2000
|
+
// src/adapters/shared/text-edit.ts
|
|
2001
|
+
function applyRangeEdits(source, edits) {
|
|
2002
|
+
for (const edit of edits) {
|
|
2003
|
+
const found = source.slice(edit.start, edit.end);
|
|
2004
|
+
if (found !== edit.expected) {
|
|
2005
|
+
throw new StaleEditError(`Found '${found}', expected '${edit.expected}'. The file changed since it was scanned.`);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
const ordered = [...edits].sort((a, b) => b.start - a.start);
|
|
2009
|
+
for (let i = 1; i < ordered.length; i++) {
|
|
2010
|
+
if (ordered[i].end > ordered[i - 1].start) throw new StaleEditError("Overlapping edits on the same text.");
|
|
2011
|
+
}
|
|
2012
|
+
let result = source;
|
|
2013
|
+
for (const edit of ordered) result = result.slice(0, edit.start) + edit.replacement + result.slice(edit.end);
|
|
2014
|
+
return result;
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
// src/adapters/cargo/cargo-updater.ts
|
|
2018
|
+
var UPDATE_TIMEOUT_MS = 10 * 6e4;
|
|
2019
|
+
var workspaceDir = (module, rootDir) => module.aggregatorDir ?? module.path ?? rootDir;
|
|
2020
|
+
var CargoUpdaterAdapter = class {
|
|
2021
|
+
affectedFiles(module, rootDir, updates) {
|
|
2022
|
+
const files = new Set(updates.map((u) => resolve6(u.declaration?.file ?? join12(module.path, "Cargo.toml"))));
|
|
2023
|
+
files.add(join12(workspaceDir(module, rootDir), "Cargo.lock"));
|
|
2024
|
+
return [...files];
|
|
2025
|
+
}
|
|
2026
|
+
async applyUpdates(module, rootDir, updates, onProgress) {
|
|
2027
|
+
const dir = workspaceDir(module, rootDir);
|
|
2028
|
+
const lockFile = join12(dir, "Cargo.lock");
|
|
2029
|
+
const lockedBefore = readLockedVersions(lockFile);
|
|
2030
|
+
const lockUpdates = updates.filter((u) => u.action === "lock");
|
|
2031
|
+
updates = updates.filter((u) => u.action !== "lock");
|
|
2032
|
+
this.checkLockUpdates(lockUpdates, lockFile, lockedBefore);
|
|
2033
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
2034
|
+
for (const update of updates) {
|
|
2035
|
+
const file = resolve6(update.declaration?.file ?? join12(module.path, "Cargo.toml"));
|
|
2036
|
+
byFile.set(file, [...byFile.get(file) ?? [], update]);
|
|
2037
|
+
}
|
|
2038
|
+
const writes = [];
|
|
2039
|
+
for (const [file, fileUpdates] of byFile) {
|
|
2040
|
+
onProgress(`Preparing ${fileUpdates.length} change(s) in ${file}\u2026`);
|
|
2041
|
+
const source = readFileSync12(file, "utf8");
|
|
2042
|
+
const decls = parseCargoManifest(source);
|
|
2043
|
+
const edits = [];
|
|
2044
|
+
for (const update of fileUpdates) {
|
|
2045
|
+
const inWorkspace = update.declaration?.kind === "dependency-management";
|
|
2046
|
+
const name = update.coordinate.identifier;
|
|
2047
|
+
const found = decls.filter(
|
|
2048
|
+
(d) => d.inWorkspace === inWorkspace && (d.package ?? d.key) === name && d.version?.value === update.currentRange
|
|
2049
|
+
);
|
|
2050
|
+
if (found.length === 0) {
|
|
2051
|
+
throw new InstallError(`${file}: cannot find the version of ${name} (${update.currentRange}) any more; rescan the module.`);
|
|
2052
|
+
}
|
|
2053
|
+
for (const d of found) {
|
|
2054
|
+
edits.push({ start: d.version.start, end: d.version.end, expected: d.version.value, replacement: update.newRange });
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
try {
|
|
2058
|
+
writes.push({ file, content: applyRangeEdits(source, edits) });
|
|
2059
|
+
} catch (err) {
|
|
2060
|
+
if (err instanceof StaleEditError) throw new InstallError(`${file}: ${err.message}`);
|
|
2061
|
+
throw err;
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
onProgress("Writing Cargo.toml files\u2026");
|
|
2065
|
+
for (const write of writes) writeFileSync2(write.file, write.content, "utf8");
|
|
2066
|
+
const registryUpdates = updates.filter((u) => u.action === "update");
|
|
2067
|
+
if (existsSync8(lockFile) && registryUpdates.length > 0) {
|
|
2068
|
+
await this.refreshLock(dir, lockFile, lockedBefore, registryUpdates, onProgress);
|
|
2069
|
+
}
|
|
2070
|
+
for (const update of lockUpdates) {
|
|
2071
|
+
const name = update.coordinate.identifier;
|
|
2072
|
+
onProgress(`Locking ${name} at ${update.latest}\u2026`);
|
|
2073
|
+
await this.cargoUpdate(dir, ["-p", `${name}@${update.currentClean}`, "--precise", update.latest]);
|
|
2074
|
+
}
|
|
2075
|
+
const parts = [];
|
|
2076
|
+
if (updates.length > 0) {
|
|
2077
|
+
parts.push(`Updated ${updates.length} requirement(s) in ${writes.length} Cargo.toml file(s)${existsSync8(lockFile) ? "; Cargo.lock refreshed" : ""}`);
|
|
2078
|
+
}
|
|
2079
|
+
if (lockUpdates.length > 0) parts.push(`locked ${lockUpdates.length} crate(s) at a newer version in Cargo.lock`);
|
|
2080
|
+
const message2 = parts.join("; ");
|
|
2081
|
+
return {
|
|
2082
|
+
updatedCount: updates.length + lockUpdates.length,
|
|
2083
|
+
auditMessage: `${message2.charAt(0).toUpperCase()}${message2.slice(1)}.`,
|
|
2084
|
+
auditSeverity: "clean",
|
|
2085
|
+
integrityViolations: []
|
|
2086
|
+
};
|
|
2087
|
+
}
|
|
2088
|
+
/** A lock-only update needs a lock file that still holds the version the scan saw. */
|
|
2089
|
+
checkLockUpdates(lockUpdates, lockFile, locked) {
|
|
2090
|
+
if (lockUpdates.length === 0) return;
|
|
2091
|
+
if (!existsSync8(lockFile)) {
|
|
2092
|
+
throw new InstallError(`${lockFile} does not exist any more; rescan the module.`);
|
|
2093
|
+
}
|
|
2094
|
+
for (const update of lockUpdates) {
|
|
2095
|
+
const name = update.coordinate.identifier;
|
|
2096
|
+
if (!(locked.get(name) ?? []).includes(update.currentClean)) {
|
|
2097
|
+
throw new InstallError(`Cargo.lock no longer locks ${name} at ${update.currentClean}; rescan the module.`);
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
async resync() {
|
|
2102
|
+
}
|
|
2103
|
+
async refreshLock(dir, lockFile, before, updates, onProgress) {
|
|
2104
|
+
const specs = [];
|
|
2105
|
+
for (const update of updates) {
|
|
2106
|
+
const name = update.coordinate.identifier;
|
|
2107
|
+
const versions = before.get(name) ?? [];
|
|
2108
|
+
const old = versions.find((v) => satisfiesRequirement(update.currentRange, v)) ?? versions[0];
|
|
2109
|
+
if (old) specs.push(`${name}@${old}`);
|
|
2110
|
+
}
|
|
2111
|
+
if (specs.length === 0) return;
|
|
2112
|
+
onProgress("Updating Cargo.lock\u2026");
|
|
2113
|
+
await this.cargoUpdate(dir, specs.flatMap((s) => ["-p", s]));
|
|
2114
|
+
const after = readLockedVersions(lockFile);
|
|
2115
|
+
for (const update of updates) {
|
|
2116
|
+
const name = update.coordinate.identifier;
|
|
2117
|
+
const locked = (after.get(name) ?? []).find((v) => satisfiesRequirement(update.newRange, v));
|
|
2118
|
+
if (locked && semver9.valid(update.latest) && locked !== update.latest) {
|
|
2119
|
+
onProgress(`Pinning ${name} to ${update.latest}\u2026`);
|
|
2120
|
+
await this.cargoUpdate(dir, ["-p", `${name}@${locked}`, "--precise", update.latest]);
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
async cargoUpdate(dir, args) {
|
|
2125
|
+
const result = await runCommand("cargo", ["update", ...args], { cwd: dir, timeoutMs: UPDATE_TIMEOUT_MS });
|
|
2126
|
+
if (result.exitCode !== 0) {
|
|
2127
|
+
const reason = result.timedOut ? "cargo update timed out" : "cargo update failed";
|
|
2128
|
+
throw new InstallError(reason, tail(result.stderr || result.stdout));
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
};
|
|
2132
|
+
|
|
2133
|
+
// src/adapters/cargo/cargo-verifier.ts
|
|
2134
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2135
|
+
import { join as join13, relative as relative5, resolve as resolve7 } from "path";
|
|
2136
|
+
var VERIFY_TIMEOUT_MS2 = 30 * 6e4;
|
|
2137
|
+
var same = (a, b) => relative5(resolve7(a), resolve7(b)) === "";
|
|
2138
|
+
function planCheck(metadata, module, changedFiles, locked) {
|
|
2139
|
+
const args = ["check", "--all-targets", ...locked ? ["--locked"] : []];
|
|
2140
|
+
const rootManifest = join13(metadata.workspace_root, "Cargo.toml");
|
|
2141
|
+
const lockFile = join13(metadata.workspace_root, "Cargo.lock");
|
|
2142
|
+
const memberManifests = new Set(metadata.packages.map((p) => resolve7(p.manifest_path)));
|
|
2143
|
+
if (changedFiles.some((f) => same(f, rootManifest))) {
|
|
2144
|
+
const isVirtualRoot = !memberManifests.has(resolve7(rootManifest));
|
|
2145
|
+
if (isVirtualRoot || metadata.packages.length > 1) return [...args, "--workspace"];
|
|
2146
|
+
}
|
|
2147
|
+
if (changedFiles.length > 0 && changedFiles.every((f) => same(f, lockFile))) return [...args, "--workspace"];
|
|
2148
|
+
const changed = new Set(
|
|
2149
|
+
metadata.packages.filter((p) => changedFiles.some((f) => same(f, p.manifest_path))).map((p) => p.name)
|
|
2150
|
+
);
|
|
2151
|
+
if (changed.size === 0) changed.add(module.id);
|
|
2152
|
+
for (let grew = true; grew; ) {
|
|
2153
|
+
grew = false;
|
|
2154
|
+
for (const p of metadata.packages) {
|
|
2155
|
+
if (changed.has(p.name)) continue;
|
|
2156
|
+
if (p.dependencies.some((d) => d.source === null && d.path && changed.has(d.name))) {
|
|
2157
|
+
changed.add(p.name);
|
|
2158
|
+
grew = true;
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
const ordered = metadata.packages.filter((p) => changed.has(p.name)).map((p) => p.name);
|
|
2163
|
+
return [...args, ...ordered.flatMap((name) => ["-p", name])];
|
|
2164
|
+
}
|
|
2165
|
+
var CargoVerificationAdapter = class {
|
|
2166
|
+
constructor(deps = { loadMetadata, hasLock: (dir) => existsSync9(join13(dir, "Cargo.lock")) }) {
|
|
2167
|
+
this.deps = deps;
|
|
2168
|
+
}
|
|
2169
|
+
deps;
|
|
2170
|
+
async verify(module, rootDir, options = {}) {
|
|
2171
|
+
const dir = module.aggregatorDir ?? module.path ?? rootDir;
|
|
2172
|
+
if (options.customScript) {
|
|
2173
|
+
const label2 = options.customLabel || options.customScript;
|
|
2174
|
+
const result2 = await runShellScript(options.customScript, { cwd: dir, timeoutMs: VERIFY_TIMEOUT_MS2 });
|
|
2175
|
+
return result2.exitCode === 0 ? { status: "clean", details: "Verification script succeeded.", label: label2 } : { status: "warn", details: tail(result2.stderr || result2.stdout), label: label2 };
|
|
2176
|
+
}
|
|
2177
|
+
const metadata = await this.deps.loadMetadata(dir);
|
|
2178
|
+
const args = planCheck(metadata, module, options.changedFiles ?? [], this.deps.hasLock(dir));
|
|
2179
|
+
const label = `Cargo build (cargo ${args.join(" ")})`;
|
|
2180
|
+
const result = await runCargo(args, { cwd: dir, timeoutMs: VERIFY_TIMEOUT_MS2 });
|
|
2181
|
+
if (result.exitCode === 0) return { status: "clean", details: "All affected crates type-check, including tests.", label };
|
|
2182
|
+
return {
|
|
2183
|
+
status: "warn",
|
|
2184
|
+
details: result.timedOut ? "cargo check timed out." : cargoErrors(`${result.stderr}
|
|
2185
|
+
${result.stdout}`),
|
|
2186
|
+
label
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
};
|
|
2190
|
+
|
|
2191
|
+
// src/adapters/cargo/cargo-strategy.ts
|
|
2192
|
+
var cargoVersionPolicy = {
|
|
2193
|
+
normalize: (v) => semver10.valid(v),
|
|
2194
|
+
rewrite: rewriteRequirement,
|
|
2195
|
+
diff: (current, target) => semver10.valid(current) && semver10.valid(target) ? semver10.eq(current, target) ? "patch" : cargoDiff(current, target) : "minor",
|
|
2196
|
+
// Updates are written where the scan found the declaration.
|
|
2197
|
+
allowsManualTargets: false
|
|
2198
|
+
};
|
|
2199
|
+
var CargoEcosystemStrategy = class {
|
|
2200
|
+
ecosystem = "cargo";
|
|
2201
|
+
displayName = "Rust / Cargo";
|
|
2202
|
+
icon = "\u{1F980}";
|
|
2203
|
+
manifestFile = "Cargo.toml";
|
|
2204
|
+
versioning = cargoVersionPolicy;
|
|
2205
|
+
config = new CargoConfigReader();
|
|
2206
|
+
index = new CargoIndexClient();
|
|
2207
|
+
endpoint = (registry, cwd) => this.config.resolve(registry, cwd);
|
|
2208
|
+
discovery = new CargoModuleDiscoveryAdapter();
|
|
2209
|
+
registry = new CargoRegistryAdapter({ index: this.index, endpoint: this.endpoint });
|
|
2210
|
+
reader = new CargoDependencyAdapter({ loadMetadata, index: this.index, endpoint: this.endpoint });
|
|
2211
|
+
updater = new CargoUpdaterAdapter();
|
|
2212
|
+
verifier = new CargoVerificationAdapter();
|
|
2213
|
+
getTabLabels(counts, t) {
|
|
2214
|
+
return {
|
|
2215
|
+
all: t.tabs.all(counts.total),
|
|
2216
|
+
prod: t.tabs.dependencies(counts.prodCount),
|
|
2217
|
+
dev: t.tabs.devDependencies(counts.devCount)
|
|
2218
|
+
};
|
|
2219
|
+
}
|
|
2220
|
+
};
|
|
2221
|
+
|
|
2222
|
+
// src/adapters/maven/maven-discovery.ts
|
|
2223
|
+
import { existsSync as existsSync12 } from "fs";
|
|
2224
|
+
import { dirname as dirname7, join as join16, relative as relative6, resolve as resolve10 } from "path";
|
|
2225
|
+
|
|
2226
|
+
// src/adapters/maven/maven-project.ts
|
|
2227
|
+
import { existsSync as existsSync11, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
2228
|
+
import { join as join15, resolve as resolve9 } from "path";
|
|
2229
|
+
|
|
1195
2230
|
// src/adapters/maven/maven-pom.ts
|
|
2231
|
+
import { existsSync as existsSync10, readFileSync as readFileSync13, statSync } from "fs";
|
|
2232
|
+
import { dirname as dirname6, join as join14, resolve as resolve8 } from "path";
|
|
1196
2233
|
function readPom(file) {
|
|
1197
|
-
const source =
|
|
2234
|
+
const source = readFileSync13(file, "utf8");
|
|
1198
2235
|
return parsePom(source, file);
|
|
1199
2236
|
}
|
|
1200
2237
|
function parsePom(source, file) {
|
|
@@ -1213,7 +2250,7 @@ function parsePom(source, file) {
|
|
|
1213
2250
|
}
|
|
1214
2251
|
return {
|
|
1215
2252
|
file,
|
|
1216
|
-
dir:
|
|
2253
|
+
dir: dirname6(file),
|
|
1217
2254
|
groupId: childText(project, "groupId"),
|
|
1218
2255
|
artifactId: childText(project, "artifactId") ?? "unknown",
|
|
1219
2256
|
version: childText(project, "version"),
|
|
@@ -1242,9 +2279,9 @@ function parentPomFile(pom) {
|
|
|
1242
2279
|
if (!pom.parent) return void 0;
|
|
1243
2280
|
const relativePath = pom.parent.relativePath ?? "../pom.xml";
|
|
1244
2281
|
if (relativePath === "") return void 0;
|
|
1245
|
-
let candidate =
|
|
1246
|
-
if (
|
|
1247
|
-
return
|
|
2282
|
+
let candidate = resolve8(pom.dir, relativePath);
|
|
2283
|
+
if (existsSync10(candidate) && statSync(candidate).isDirectory()) candidate = join14(candidate, "pom.xml");
|
|
2284
|
+
return existsSync10(candidate) ? candidate : void 0;
|
|
1248
2285
|
}
|
|
1249
2286
|
function builtinProperty(name, module) {
|
|
1250
2287
|
switch (name) {
|
|
@@ -1319,9 +2356,9 @@ var MavenProject = class _MavenProject {
|
|
|
1319
2356
|
}
|
|
1320
2357
|
poms.push(pom);
|
|
1321
2358
|
for (const entry of pom.modules) {
|
|
1322
|
-
const target =
|
|
1323
|
-
const moduleFile = target.endsWith(".xml") ? target :
|
|
1324
|
-
if (!seen.has(moduleFile) &&
|
|
2359
|
+
const target = resolve9(pom.dir, entry);
|
|
2360
|
+
const moduleFile = target.endsWith(".xml") ? target : join15(target, "pom.xml");
|
|
2361
|
+
if (!seen.has(moduleFile) && existsSync11(moduleFile)) {
|
|
1325
2362
|
seen.add(moduleFile);
|
|
1326
2363
|
queue.push(moduleFile);
|
|
1327
2364
|
}
|
|
@@ -1330,7 +2367,7 @@ var MavenProject = class _MavenProject {
|
|
|
1330
2367
|
return new _MavenProject(rootDir, poms);
|
|
1331
2368
|
}
|
|
1332
2369
|
pomAt(file) {
|
|
1333
|
-
return this.byFile.get(
|
|
2370
|
+
return this.byFile.get(resolve9(file));
|
|
1334
2371
|
}
|
|
1335
2372
|
/** The project followed by its local parents, stopping at the first external parent. */
|
|
1336
2373
|
chainOf(pom) {
|
|
@@ -1373,7 +2410,7 @@ var MavenProject = class _MavenProject {
|
|
|
1373
2410
|
return groupId === parent.groupId && candidate.artifactId === parent.artifactId && (version.includes("${") || expected.includes("${") || version === expected);
|
|
1374
2411
|
};
|
|
1375
2412
|
const file = parentPomFile(pom);
|
|
1376
|
-
const byPath = file ? this.byFile.get(
|
|
2413
|
+
const byPath = file ? this.byFile.get(resolve9(file)) : void 0;
|
|
1377
2414
|
return matches(byPath) ? byPath : void 0;
|
|
1378
2415
|
}
|
|
1379
2416
|
/**
|
|
@@ -1390,8 +2427,8 @@ var MavenProject = class _MavenProject {
|
|
|
1390
2427
|
if (this.aggregators.has(pom.file)) continue;
|
|
1391
2428
|
this.aggregators.set(pom.file, aggregator.dir);
|
|
1392
2429
|
for (const entry of pom.modules) {
|
|
1393
|
-
const target =
|
|
1394
|
-
const file = target.endsWith(".xml") ? target :
|
|
2430
|
+
const target = resolve9(pom.dir, entry);
|
|
2431
|
+
const file = target.endsWith(".xml") ? target : join15(target, "pom.xml");
|
|
1395
2432
|
const modulePom = this.byFile.get(file);
|
|
1396
2433
|
if (modulePom) queue.push(modulePom);
|
|
1397
2434
|
}
|
|
@@ -1401,23 +2438,23 @@ var MavenProject = class _MavenProject {
|
|
|
1401
2438
|
};
|
|
1402
2439
|
function findPomFiles(rootDir) {
|
|
1403
2440
|
const results = [];
|
|
1404
|
-
const rootPom =
|
|
1405
|
-
if (
|
|
2441
|
+
const rootPom = join15(rootDir, "pom.xml");
|
|
2442
|
+
if (existsSync11(rootPom)) results.push(resolve9(rootPom));
|
|
1406
2443
|
const walk = (dir, depth) => {
|
|
1407
2444
|
if (depth > MAX_DEPTH) return;
|
|
1408
2445
|
let entries2;
|
|
1409
2446
|
try {
|
|
1410
|
-
entries2 =
|
|
2447
|
+
entries2 = readdirSync2(dir, { withFileTypes: true });
|
|
1411
2448
|
} catch {
|
|
1412
2449
|
return;
|
|
1413
2450
|
}
|
|
1414
|
-
const isProject =
|
|
2451
|
+
const isProject = existsSync11(join15(dir, "pom.xml"));
|
|
1415
2452
|
for (const entry of entries2) {
|
|
1416
2453
|
if (!entry.isDirectory() || entry.name.startsWith(".") || SKIPPED_DIRS.has(entry.name)) continue;
|
|
1417
2454
|
if (isProject && entry.name === "src") continue;
|
|
1418
|
-
const subDir =
|
|
1419
|
-
const pom =
|
|
1420
|
-
if (
|
|
2455
|
+
const subDir = join15(dir, entry.name);
|
|
2456
|
+
const pom = join15(subDir, "pom.xml");
|
|
2457
|
+
if (existsSync11(pom) && statSync2(pom).isFile()) results.push(resolve9(pom));
|
|
1421
2458
|
walk(subDir, depth + 1);
|
|
1422
2459
|
}
|
|
1423
2460
|
};
|
|
@@ -1432,9 +2469,9 @@ var MavenModuleDiscoveryAdapter = class {
|
|
|
1432
2469
|
* the POM above until there is none: the top of the connected POM chain.
|
|
1433
2470
|
*/
|
|
1434
2471
|
findRoot(startDir, boundaryDir) {
|
|
1435
|
-
let dir =
|
|
1436
|
-
if (!
|
|
1437
|
-
const boundary = boundaryDir ?
|
|
2472
|
+
let dir = resolve10(startDir);
|
|
2473
|
+
if (!existsSync12(join16(dir, "pom.xml"))) return void 0;
|
|
2474
|
+
const boundary = boundaryDir ? resolve10(boundaryDir) : void 0;
|
|
1438
2475
|
for (; ; ) {
|
|
1439
2476
|
const above = pomAbove(dir, boundary);
|
|
1440
2477
|
if (!above) return dir;
|
|
@@ -1445,7 +2482,7 @@ var MavenModuleDiscoveryAdapter = class {
|
|
|
1445
2482
|
const project = MavenProject.load(rootDir);
|
|
1446
2483
|
const modules = project.poms.map((pom) => {
|
|
1447
2484
|
const coordinates = project.coordinatesOf(pom);
|
|
1448
|
-
const relPath =
|
|
2485
|
+
const relPath = relative6(rootDir, pom.dir).replace(/\\/g, "/");
|
|
1449
2486
|
return {
|
|
1450
2487
|
id: coordinates.groupId ? `${coordinates.groupId}:${coordinates.artifactId}` : coordinates.artifactId,
|
|
1451
2488
|
name: pom.name ? interpolate(pom.name, project.chainOf(pom).poms) : pom.artifactId,
|
|
@@ -1472,7 +2509,7 @@ function ancestorsWithin(dir, boundary) {
|
|
|
1472
2509
|
const result = [];
|
|
1473
2510
|
let current = dir;
|
|
1474
2511
|
while (current !== boundary) {
|
|
1475
|
-
const parent =
|
|
2512
|
+
const parent = dirname7(current);
|
|
1476
2513
|
if (parent === current) break;
|
|
1477
2514
|
result.push(parent);
|
|
1478
2515
|
current = parent;
|
|
@@ -1480,22 +2517,22 @@ function ancestorsWithin(dir, boundary) {
|
|
|
1480
2517
|
return result;
|
|
1481
2518
|
}
|
|
1482
2519
|
function pomAbove(dir, boundary) {
|
|
1483
|
-
const pom = tryReadPom(
|
|
2520
|
+
const pom = tryReadPom(join16(dir, "pom.xml"));
|
|
1484
2521
|
if (!pom) return void 0;
|
|
1485
2522
|
const ancestors = ancestorsWithin(dir, boundary);
|
|
1486
2523
|
const found = [];
|
|
1487
2524
|
const parentFile = parentPomFile(pom);
|
|
1488
|
-
if (parentFile && pom.parent && ancestors.includes(
|
|
2525
|
+
if (parentFile && pom.parent && ancestors.includes(dirname7(parentFile))) {
|
|
1489
2526
|
const parent = tryReadPom(parentFile);
|
|
1490
2527
|
const groupId = parent?.groupId ?? parent?.parent?.groupId;
|
|
1491
2528
|
if (parent && parent.artifactId === pom.parent.artifactId && groupId === pom.parent.groupId) {
|
|
1492
|
-
found.push(
|
|
2529
|
+
found.push(dirname7(parentFile));
|
|
1493
2530
|
}
|
|
1494
2531
|
}
|
|
1495
|
-
const ownFile =
|
|
2532
|
+
const ownFile = join16(dir, "pom.xml");
|
|
1496
2533
|
const aggregator = ancestors.find(
|
|
1497
|
-
(ancestor) => tryReadPom(
|
|
1498
|
-
const target =
|
|
2534
|
+
(ancestor) => tryReadPom(join16(ancestor, "pom.xml"))?.modules.some((entry) => {
|
|
2535
|
+
const target = resolve10(ancestor, entry);
|
|
1499
2536
|
return target === dir || target === ownFile;
|
|
1500
2537
|
})
|
|
1501
2538
|
);
|
|
@@ -1504,12 +2541,12 @@ function pomAbove(dir, boundary) {
|
|
|
1504
2541
|
}
|
|
1505
2542
|
|
|
1506
2543
|
// src/adapters/maven/maven-registry.ts
|
|
1507
|
-
import { existsSync as
|
|
1508
|
-
import { join as
|
|
2544
|
+
import { existsSync as existsSync15 } from "fs";
|
|
2545
|
+
import { join as join19 } from "path";
|
|
1509
2546
|
|
|
1510
2547
|
// src/adapters/maven/maven-command.ts
|
|
1511
|
-
import { existsSync as
|
|
1512
|
-
import { dirname as
|
|
2548
|
+
import { existsSync as existsSync13 } from "fs";
|
|
2549
|
+
import { dirname as dirname8, isAbsolute as isAbsolute2, join as join17, relative as relative7, resolve as resolve11, sep } from "path";
|
|
1513
2550
|
var MavenUnavailableError = class extends Error {
|
|
1514
2551
|
constructor() {
|
|
1515
2552
|
super("Maven is not available: no Maven Wrapper (mvnw) in the project and no `mvn` on the PATH.");
|
|
@@ -1529,27 +2566,27 @@ var WRAPPER = IS_WINDOWS2 ? "mvnw.cmd" : "mvnw";
|
|
|
1529
2566
|
var BATCH_ARGS = ["-B", "--no-transfer-progress"];
|
|
1530
2567
|
var mvnOnPath;
|
|
1531
2568
|
function findMavenWrapper(fromDir, rootDir) {
|
|
1532
|
-
let dir =
|
|
1533
|
-
const root =
|
|
2569
|
+
let dir = resolve11(fromDir);
|
|
2570
|
+
const root = resolve11(rootDir);
|
|
1534
2571
|
for (; ; ) {
|
|
1535
|
-
const candidate =
|
|
1536
|
-
if (
|
|
1537
|
-
if (dir === root ||
|
|
1538
|
-
const parent =
|
|
2572
|
+
const candidate = join17(dir, WRAPPER);
|
|
2573
|
+
if (existsSync13(candidate)) return candidate;
|
|
2574
|
+
if (dir === root || relative7(root, dir).startsWith("..")) return void 0;
|
|
2575
|
+
const parent = dirname8(dir);
|
|
1539
2576
|
if (parent === dir) return void 0;
|
|
1540
2577
|
dir = parent;
|
|
1541
2578
|
}
|
|
1542
2579
|
}
|
|
1543
2580
|
function relativeToCwd(cwd, file) {
|
|
1544
|
-
const path =
|
|
1545
|
-
return
|
|
2581
|
+
const path = relative7(cwd, file);
|
|
2582
|
+
return isAbsolute2(path) ? file : path || ".";
|
|
1546
2583
|
}
|
|
1547
2584
|
async function runMaven(args, options) {
|
|
1548
2585
|
const wrapper = findMavenWrapper(options.cwd, options.rootDir);
|
|
1549
2586
|
const run = { cwd: options.cwd, timeoutMs: options.timeoutMs };
|
|
1550
2587
|
if (wrapper) {
|
|
1551
2588
|
const relativeWrapper = relativeToCwd(options.cwd, wrapper);
|
|
1552
|
-
const path =
|
|
2589
|
+
const path = isAbsolute2(relativeWrapper) || relativeWrapper.includes(sep) ? relativeWrapper : `.${sep}${relativeWrapper}`;
|
|
1553
2590
|
return IS_WINDOWS2 ? runCommand(path, [...BATCH_ARGS, ...args], run) : runCommand("sh", [path, ...BATCH_ARGS, ...args], run);
|
|
1554
2591
|
}
|
|
1555
2592
|
mvnOnPath ??= runCommand("mvn", ["-v"], { cwd: options.cwd, timeoutMs: 3e4 }).then(
|
|
@@ -1566,8 +2603,8 @@ function mavenErrors(output) {
|
|
|
1566
2603
|
|
|
1567
2604
|
// src/adapters/maven/maven-resolution.ts
|
|
1568
2605
|
import { randomUUID } from "crypto";
|
|
1569
|
-
import { existsSync as
|
|
1570
|
-
import { join as
|
|
2606
|
+
import { existsSync as existsSync14, mkdirSync, readFileSync as readFileSync14, readdirSync as readdirSync3, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
2607
|
+
import { join as join18 } from "path";
|
|
1571
2608
|
var DEFAULT_MAVEN_PLUGINS = { help: "3.5.2", versions: "2.22.0" };
|
|
1572
2609
|
var HELP_PLUGIN_ID = "org.apache.maven.plugins:maven-help-plugin";
|
|
1573
2610
|
var VERSIONS_PLUGIN_ID = "org.codehaus.mojo:versions-maven-plugin";
|
|
@@ -1599,21 +2636,21 @@ var ScratchDirectory = class {
|
|
|
1599
2636
|
target;
|
|
1600
2637
|
createdTarget;
|
|
1601
2638
|
constructor(moduleDir) {
|
|
1602
|
-
this.target =
|
|
1603
|
-
this.createdTarget = !
|
|
1604
|
-
this.path =
|
|
2639
|
+
this.target = join18(moduleDir, "target");
|
|
2640
|
+
this.createdTarget = !existsSync14(this.target);
|
|
2641
|
+
this.path = join18(this.target, `.elevate-${randomUUID().slice(0, 8)}`);
|
|
1605
2642
|
mkdirSync(this.path, { recursive: true });
|
|
1606
2643
|
}
|
|
1607
2644
|
dispose() {
|
|
1608
2645
|
rmSync(this.path, { recursive: true, force: true });
|
|
1609
2646
|
try {
|
|
1610
|
-
if (this.createdTarget &&
|
|
2647
|
+
if (this.createdTarget && readdirSync3(this.target).length === 0) rmSync(this.target, { recursive: true });
|
|
1611
2648
|
} catch {
|
|
1612
2649
|
}
|
|
1613
2650
|
}
|
|
1614
2651
|
};
|
|
1615
2652
|
async function readEffectivePoms(pomFile, moduleDir, rootDir, scratch, plugins, recursive = false) {
|
|
1616
|
-
const output =
|
|
2653
|
+
const output = join18(scratch.path, "effective-pom.xml");
|
|
1617
2654
|
const args = [
|
|
1618
2655
|
"-f",
|
|
1619
2656
|
relativeToCwd(moduleDir, pomFile),
|
|
@@ -1625,7 +2662,7 @@ async function readEffectivePoms(pomFile, moduleDir, rootDir, scratch, plugins,
|
|
|
1625
2662
|
rootDir,
|
|
1626
2663
|
timeoutMs: MAVEN_TIMEOUT_MS
|
|
1627
2664
|
});
|
|
1628
|
-
if (result.exitCode !== 0 || !
|
|
2665
|
+
if (result.exitCode !== 0 || !existsSync14(output)) {
|
|
1629
2666
|
const text = `${result.stdout}
|
|
1630
2667
|
${result.stderr}`;
|
|
1631
2668
|
if (isPluginUnavailable(text, HELP_PLUGIN_ID)) {
|
|
@@ -1637,7 +2674,7 @@ ${result.stderr}`;
|
|
|
1637
2674
|
throw new MavenCommandError(`Maven could not build the effective POM:
|
|
1638
2675
|
${mavenErrors(text)}`, text);
|
|
1639
2676
|
}
|
|
1640
|
-
return parseEffectivePoms(
|
|
2677
|
+
return parseEffectivePoms(readFileSync14(output, "utf8"));
|
|
1641
2678
|
}
|
|
1642
2679
|
function parseEffectivePoms(source) {
|
|
1643
2680
|
const root = parseXml(source);
|
|
@@ -1649,7 +2686,7 @@ function parseEffectivePoms(source) {
|
|
|
1649
2686
|
return models;
|
|
1650
2687
|
}
|
|
1651
2688
|
function mergeRepositories(models) {
|
|
1652
|
-
const
|
|
2689
|
+
const merge2 = (name, pick) => {
|
|
1653
2690
|
const byId = /* @__PURE__ */ new Map();
|
|
1654
2691
|
for (const element of models.map(pick)) {
|
|
1655
2692
|
for (const repository of element?.children ?? []) {
|
|
@@ -1662,8 +2699,8 @@ function mergeRepositories(models) {
|
|
|
1662
2699
|
return {
|
|
1663
2700
|
dependencies: [],
|
|
1664
2701
|
managedDependencies: [],
|
|
1665
|
-
repositories:
|
|
1666
|
-
pluginRepositories:
|
|
2702
|
+
repositories: merge2("repositories", (m) => m.repositories),
|
|
2703
|
+
pluginRepositories: merge2("pluginRepositories", (m) => m.pluginRepositories)
|
|
1667
2704
|
};
|
|
1668
2705
|
}
|
|
1669
2706
|
function toEffectiveModel(project) {
|
|
@@ -1721,10 +2758,10 @@ function buildProbePom(artifacts, model) {
|
|
|
1721
2758
|
}
|
|
1722
2759
|
async function queryNewerVersions(artifacts, model, moduleDir, rootDir, scratch, plugins) {
|
|
1723
2760
|
if (artifacts.length === 0) return { newerVersions: /* @__PURE__ */ new Map(), failedLookups: /* @__PURE__ */ new Set() };
|
|
1724
|
-
const probeDir =
|
|
2761
|
+
const probeDir = join18(scratch.path, "probe");
|
|
1725
2762
|
mkdirSync(probeDir, { recursive: true });
|
|
1726
|
-
const probePom =
|
|
1727
|
-
|
|
2763
|
+
const probePom = join18(probeDir, "pom.xml");
|
|
2764
|
+
writeFileSync3(probePom, buildProbePom(artifacts, model), "utf8");
|
|
1728
2765
|
const result = await runMaven(
|
|
1729
2766
|
[
|
|
1730
2767
|
"-N",
|
|
@@ -1738,8 +2775,8 @@ async function queryNewerVersions(artifacts, model, moduleDir, rootDir, scratch,
|
|
|
1738
2775
|
);
|
|
1739
2776
|
const log = `${result.stdout}
|
|
1740
2777
|
${result.stderr}`;
|
|
1741
|
-
const reportFile =
|
|
1742
|
-
if (result.exitCode !== 0 || !
|
|
2778
|
+
const reportFile = join18(probeDir, "target", "dependency-updates-report.xml");
|
|
2779
|
+
if (result.exitCode !== 0 || !existsSync14(reportFile)) {
|
|
1743
2780
|
if (isPluginUnavailable(log, VERSIONS_PLUGIN_ID)) {
|
|
1744
2781
|
throw new MavenPluginUnavailableError(
|
|
1745
2782
|
{ id: VERSIONS_PLUGIN_ID, version: plugins.versions, key: "versions", purpose: "look up newer versions" },
|
|
@@ -1750,7 +2787,7 @@ ${result.stderr}`;
|
|
|
1750
2787
|
${mavenErrors(log)}`, log);
|
|
1751
2788
|
}
|
|
1752
2789
|
return {
|
|
1753
|
-
newerVersions: parseUpdatesReport(
|
|
2790
|
+
newerVersions: parseUpdatesReport(readFileSync14(reportFile, "utf8")),
|
|
1754
2791
|
failedLookups: parseFailedLookups(log)
|
|
1755
2792
|
};
|
|
1756
2793
|
}
|
|
@@ -1835,18 +2872,18 @@ var MavenRegistryAdapter = class {
|
|
|
1835
2872
|
}
|
|
1836
2873
|
};
|
|
1837
2874
|
async function projectRepositories(cwd, scratch, plugins) {
|
|
1838
|
-
const rootPom =
|
|
1839
|
-
if (!
|
|
2875
|
+
const rootPom = join19(cwd, "pom.xml");
|
|
2876
|
+
if (!existsSync15(rootPom)) return NO_REPOSITORIES;
|
|
1840
2877
|
const models = await readEffectivePoms(rootPom, cwd, cwd, scratch, plugins);
|
|
1841
2878
|
return [...models.values()][0] ?? NO_REPOSITORIES;
|
|
1842
2879
|
}
|
|
1843
2880
|
|
|
1844
2881
|
// src/adapters/maven/maven-scanner.ts
|
|
1845
|
-
import { join as
|
|
1846
|
-
import
|
|
2882
|
+
import { join as join20 } from "path";
|
|
2883
|
+
import semver11 from "semver";
|
|
1847
2884
|
|
|
1848
2885
|
// src/adapters/maven/maven-locator.ts
|
|
1849
|
-
import { relative as
|
|
2886
|
+
import { relative as relative8 } from "path";
|
|
1850
2887
|
function locateDependencyVersion(chain, target, rootDir) {
|
|
1851
2888
|
for (const pom of chain) {
|
|
1852
2889
|
const dependency = findDependency(pom.dependencies, target, chain);
|
|
@@ -1927,13 +2964,13 @@ function resolveLiteral(pom, kind, versionElement, chain, target, rootDir) {
|
|
|
1927
2964
|
file,
|
|
1928
2965
|
kind: propertyName ? "property" : kind,
|
|
1929
2966
|
propertyName,
|
|
1930
|
-
displayPath:
|
|
2967
|
+
displayPath: relative8(rootDir, file).replace(/\\/g, "/")
|
|
1931
2968
|
};
|
|
1932
2969
|
return { found: true, declaration, element, value };
|
|
1933
2970
|
}
|
|
1934
2971
|
|
|
1935
2972
|
// src/adapters/maven/maven-scanner.ts
|
|
1936
|
-
var
|
|
2973
|
+
var DIFF_ORDER3 = { major: 0, minor: 1, patch: 2 };
|
|
1937
2974
|
var MavenDependencyAdapter = class {
|
|
1938
2975
|
constructor(plugins = DEFAULT_MAVEN_PLUGINS) {
|
|
1939
2976
|
this.plugins = plugins;
|
|
@@ -1987,14 +3024,14 @@ function groupModules(project, modules) {
|
|
|
1987
3024
|
if (module.aggregatorDir) reactorSizes.set(module.aggregatorDir, (reactorSizes.get(module.aggregatorDir) ?? 0) + 1);
|
|
1988
3025
|
}
|
|
1989
3026
|
for (const module of modules) {
|
|
1990
|
-
const pom = project.pomAt(
|
|
3027
|
+
const pom = project.pomAt(join20(module.path, "pom.xml"));
|
|
1991
3028
|
if (!pom) continue;
|
|
1992
3029
|
const reactor = module.aggregatorDir;
|
|
1993
3030
|
const batched = reactor !== void 0 && (reactorSizes.get(reactor) ?? 0) > 1;
|
|
1994
3031
|
const key = batched ? `reactor:${reactor}` : `module:${module.path}`;
|
|
1995
3032
|
const group = groups.get(key) ?? {
|
|
1996
3033
|
dir: batched ? reactor : module.path,
|
|
1997
|
-
aggregatorPom: batched ?
|
|
3034
|
+
aggregatorPom: batched ? join20(reactor, "pom.xml") : void 0,
|
|
1998
3035
|
members: []
|
|
1999
3036
|
};
|
|
2000
3037
|
group.members.push({ module, pom });
|
|
@@ -2083,7 +3120,7 @@ function finishModule(plan, report, channel) {
|
|
|
2083
3120
|
collect2(updateCandidate(subject, latest, newer, origin), candidates, skipped);
|
|
2084
3121
|
}
|
|
2085
3122
|
markSharedDeclarations(candidates);
|
|
2086
|
-
candidates.sort((a, b) => (
|
|
3123
|
+
candidates.sort((a, b) => (DIFF_ORDER3[a.diff] ?? 3) - (DIFF_ORDER3[b.diff] ?? 3));
|
|
2087
3124
|
return { candidates, skipped };
|
|
2088
3125
|
}
|
|
2089
3126
|
function newerThan(current, ascending) {
|
|
@@ -2114,7 +3151,7 @@ function buildCandidate(subject, target, origin, action) {
|
|
|
2114
3151
|
}
|
|
2115
3152
|
const currentClean = cleanJavaVersion(subject.version) ?? "0.0.0";
|
|
2116
3153
|
const targetClean = cleanJavaVersion(target) ?? currentClean;
|
|
2117
|
-
const diff =
|
|
3154
|
+
const diff = semver11.eq(currentClean, targetClean) ? "patch" : diffVersions(currentClean, targetClean);
|
|
2118
3155
|
const isPreRelease = isPreReleaseVersion(target);
|
|
2119
3156
|
return {
|
|
2120
3157
|
candidate: {
|
|
@@ -2162,11 +3199,11 @@ function markSharedDeclarations(candidates) {
|
|
|
2162
3199
|
}
|
|
2163
3200
|
|
|
2164
3201
|
// src/adapters/maven/maven-updater.ts
|
|
2165
|
-
import { readFileSync as
|
|
2166
|
-
import { join as
|
|
3202
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync4 } from "fs";
|
|
3203
|
+
import { join as join21, resolve as resolve12 } from "path";
|
|
2167
3204
|
var MavenUpdaterAdapter = class {
|
|
2168
3205
|
affectedFiles(module, _rootDir, updates) {
|
|
2169
|
-
const files = new Set(updates.map((u) =>
|
|
3206
|
+
const files = new Set(updates.map((u) => resolve12(u.declaration?.file ?? join21(module.path, "pom.xml"))));
|
|
2170
3207
|
return [...files];
|
|
2171
3208
|
}
|
|
2172
3209
|
async applyUpdates(module, rootDir, updates, onProgress) {
|
|
@@ -2176,13 +3213,13 @@ var MavenUpdaterAdapter = class {
|
|
|
2176
3213
|
if (!update.declaration) {
|
|
2177
3214
|
throw new InstallError(`No version declaration known for ${update.coordinate.identifier}; rescan the module.`);
|
|
2178
3215
|
}
|
|
2179
|
-
const file =
|
|
3216
|
+
const file = resolve12(update.declaration.file);
|
|
2180
3217
|
byFile.set(file, [...byFile.get(file) ?? [], update]);
|
|
2181
3218
|
}
|
|
2182
3219
|
const writes = [];
|
|
2183
3220
|
for (const [file, fileUpdates] of byFile) {
|
|
2184
3221
|
onProgress(`Preparing ${fileUpdates.length} change(s) in ${fileUpdates[0].declaration.displayPath}\u2026`);
|
|
2185
|
-
const source =
|
|
3222
|
+
const source = readFileSync15(file, "utf8");
|
|
2186
3223
|
const pom = parsePom(source, file);
|
|
2187
3224
|
const chain = project.pomAt(file) ? project.chainOf(project.pomAt(file)).poms : [pom];
|
|
2188
3225
|
const edits = collectEdits(pom, [pom, ...chain.slice(1)], fileUpdates);
|
|
@@ -2194,7 +3231,7 @@ var MavenUpdaterAdapter = class {
|
|
|
2194
3231
|
}
|
|
2195
3232
|
}
|
|
2196
3233
|
onProgress("Writing POM files\u2026");
|
|
2197
|
-
for (const write of writes)
|
|
3234
|
+
for (const write of writes) writeFileSync4(write.file, write.content, "utf8");
|
|
2198
3235
|
return {
|
|
2199
3236
|
updatedCount: updates.length,
|
|
2200
3237
|
auditMessage: `Updated ${updates.length} version declaration(s) in ${writes.length} POM file(s).`,
|
|
@@ -2252,13 +3289,13 @@ function describe(declaration) {
|
|
|
2252
3289
|
}
|
|
2253
3290
|
|
|
2254
3291
|
// src/adapters/maven/maven-verifier.ts
|
|
2255
|
-
import { dirname as
|
|
2256
|
-
var
|
|
3292
|
+
import { dirname as dirname9, join as join22, relative as relative9, resolve as resolve13 } from "path";
|
|
3293
|
+
var VERIFY_TIMEOUT_MS3 = 30 * 6e4;
|
|
2257
3294
|
var MavenVerificationAdapter = class {
|
|
2258
3295
|
async verify(module, rootDir, options = {}) {
|
|
2259
3296
|
if (options.customScript) {
|
|
2260
3297
|
const label2 = options.customLabel || options.customScript;
|
|
2261
|
-
const result = await runShellScript(options.customScript, { cwd: module.path, timeoutMs:
|
|
3298
|
+
const result = await runShellScript(options.customScript, { cwd: module.path, timeoutMs: VERIFY_TIMEOUT_MS3 });
|
|
2262
3299
|
return result.exitCode === 0 ? { status: "clean", details: "Verification script succeeded.", label: label2 } : { status: "warn", details: failureDetails2(result), label: label2 };
|
|
2263
3300
|
}
|
|
2264
3301
|
const plan = planBuild(module, rootDir, options.changedFiles ?? []);
|
|
@@ -2267,7 +3304,7 @@ var MavenVerificationAdapter = class {
|
|
|
2267
3304
|
const result = await runMaven(["-q", "test-compile", ...plan.args], {
|
|
2268
3305
|
cwd: plan.cwd,
|
|
2269
3306
|
rootDir,
|
|
2270
|
-
timeoutMs:
|
|
3307
|
+
timeoutMs: VERIFY_TIMEOUT_MS3
|
|
2271
3308
|
});
|
|
2272
3309
|
if (result.exitCode === 0) {
|
|
2273
3310
|
return { status: "clean", details: "All affected modules compile, including tests.", label };
|
|
@@ -2289,15 +3326,15 @@ function planBuild(module, rootDir, changedFiles) {
|
|
|
2289
3326
|
const aggregatorDir = module.aggregatorDir;
|
|
2290
3327
|
if (!aggregatorDir) return { cwd: module.path, args: [] };
|
|
2291
3328
|
const project = MavenProject.load(rootDir);
|
|
2292
|
-
const files = changedFiles.length > 0 ? changedFiles : [
|
|
3329
|
+
const files = changedFiles.length > 0 ? changedFiles : [join22(module.path, "pom.xml")];
|
|
2293
3330
|
const selected = /* @__PURE__ */ new Set();
|
|
2294
3331
|
for (const file of files) {
|
|
2295
3332
|
const pom = project.pomAt(file);
|
|
2296
|
-
const dir =
|
|
2297
|
-
if (!pom || project.aggregatorDirOf(pom) !== aggregatorDir || dir ===
|
|
3333
|
+
const dir = resolve13(dirname9(file));
|
|
3334
|
+
if (!pom || project.aggregatorDirOf(pom) !== aggregatorDir || dir === resolve13(aggregatorDir)) {
|
|
2298
3335
|
return { cwd: aggregatorDir, args: [] };
|
|
2299
3336
|
}
|
|
2300
|
-
selected.add(
|
|
3337
|
+
selected.add(relative9(aggregatorDir, dir).replace(/\\/g, "/"));
|
|
2301
3338
|
}
|
|
2302
3339
|
return { cwd: aggregatorDir, args: ["-pl", [...selected].join(","), "-amd"] };
|
|
2303
3340
|
}
|
|
@@ -2308,6 +3345,7 @@ var MavenEcosystemStrategy = class {
|
|
|
2308
3345
|
displayName = "Java / Maven";
|
|
2309
3346
|
icon = "\u2615";
|
|
2310
3347
|
manifestFile = "pom.xml";
|
|
3348
|
+
versioning = mavenVersionPolicy;
|
|
2311
3349
|
discovery = new MavenModuleDiscoveryAdapter();
|
|
2312
3350
|
registry;
|
|
2313
3351
|
reader;
|
|
@@ -2345,6 +3383,8 @@ var EcosystemFactory = class {
|
|
|
2345
3383
|
if (!strategy) {
|
|
2346
3384
|
if (ecosystem === "maven") {
|
|
2347
3385
|
strategy = new MavenEcosystemStrategy(this.settings.mavenPlugins);
|
|
3386
|
+
} else if (ecosystem === "cargo") {
|
|
3387
|
+
strategy = new CargoEcosystemStrategy();
|
|
2348
3388
|
} else {
|
|
2349
3389
|
strategy = new NpmEcosystemStrategy();
|
|
2350
3390
|
}
|
|
@@ -2354,18 +3394,23 @@ var EcosystemFactory = class {
|
|
|
2354
3394
|
}
|
|
2355
3395
|
/** All supported ecosystems. */
|
|
2356
3396
|
static getAvailableEcosystems() {
|
|
2357
|
-
return ["npm", "maven"];
|
|
3397
|
+
return ["npm", "maven", "cargo"];
|
|
3398
|
+
}
|
|
3399
|
+
/** The ecosystem after `current`, for the dashboard's ecosystem switch. */
|
|
3400
|
+
static next(current) {
|
|
3401
|
+
const all = this.getAvailableEcosystems();
|
|
3402
|
+
return all[(all.indexOf(current) + 1) % all.length];
|
|
2358
3403
|
}
|
|
2359
3404
|
};
|
|
2360
3405
|
|
|
2361
3406
|
// src/config.ts
|
|
2362
3407
|
function findRepositoryRoot(startDir) {
|
|
2363
|
-
const start =
|
|
3408
|
+
const start = resolve14(startDir);
|
|
2364
3409
|
const boundary = findCheckoutBoundary(start);
|
|
2365
3410
|
let dir = start;
|
|
2366
3411
|
for (; ; ) {
|
|
2367
|
-
if (
|
|
2368
|
-
const parent =
|
|
3412
|
+
if (existsSync16(join23(dir, "elevate.config.json"))) return dir;
|
|
3413
|
+
const parent = dirname10(dir);
|
|
2369
3414
|
if (dir === boundary || parent === dir) break;
|
|
2370
3415
|
dir = parent;
|
|
2371
3416
|
}
|
|
@@ -2430,15 +3475,15 @@ function resolveConfig(rootDir, file, source = "elevate.config.json") {
|
|
|
2430
3475
|
}
|
|
2431
3476
|
function loadConfig2(startDir = process.cwd()) {
|
|
2432
3477
|
const rootDir = findRepositoryRoot(startDir);
|
|
2433
|
-
const configPath =
|
|
3478
|
+
const configPath = join23(rootDir, "elevate.config.json");
|
|
2434
3479
|
let file = {};
|
|
2435
|
-
if (
|
|
3480
|
+
if (existsSync16(configPath)) {
|
|
2436
3481
|
const unreadable = (detail) => new ConfigError(
|
|
2437
3482
|
`Cannot read elevate.config.json: ${detail} (${configPath}). Fix the file or remove it; ${NO_CONTINUE}`
|
|
2438
3483
|
);
|
|
2439
3484
|
let parsed;
|
|
2440
3485
|
try {
|
|
2441
|
-
parsed = JSON.parse(
|
|
3486
|
+
parsed = JSON.parse(readFileSync16(configPath, "utf8").replace(/^/, ""));
|
|
2442
3487
|
} catch (err) {
|
|
2443
3488
|
throw unreadable(err instanceof Error ? err.message : String(err));
|
|
2444
3489
|
}
|
|
@@ -2499,7 +3544,9 @@ var de = {
|
|
|
2499
3544
|
dev: "(dev/test)",
|
|
2500
3545
|
prod: "(prod/compile)",
|
|
2501
3546
|
align: "[Angleichen]",
|
|
2502
|
-
internal: "[Intern]"
|
|
3547
|
+
internal: "[Intern]",
|
|
3548
|
+
lock: "[Lock]",
|
|
3549
|
+
shared: (count) => `[+${count} Crates]`
|
|
2503
3550
|
},
|
|
2504
3551
|
status: {
|
|
2505
3552
|
selectedOf: (selected, total) => `${selected} von ${total}`,
|
|
@@ -2528,6 +3575,7 @@ var de = {
|
|
|
2528
3575
|
hint: "[\u2191/\u2193/j/k] Navigieren \u2502 [Enter] Ausw\xE4hlen \u2502 [Esc] Abbrechen \u2502 [Backspace] Filter l\xF6schen",
|
|
2529
3576
|
latestBadge: "LATEST",
|
|
2530
3577
|
currentBadge: "AKTUELL",
|
|
3578
|
+
alsoChanges: (names) => `\xC4ndert auch: ${names.join(", ")}`,
|
|
2531
3579
|
customBadge: "MANUELL"
|
|
2532
3580
|
},
|
|
2533
3581
|
updating: {
|
|
@@ -2556,6 +3604,7 @@ var de = {
|
|
|
2556
3604
|
scanningChirps: ["*such*", "pr\xFCfe...", "oh, Updates?", "einen Moment!"],
|
|
2557
3605
|
updatingChirps: ["*schuft*", "baue Code...", "gleich fertig!", "arbeite emsig..."],
|
|
2558
3606
|
successChirps: ["hurra! \u{1F389}", "alles aktuell!", "top gepflegt! \u{1FAB6}", "super gemacht!"],
|
|
3607
|
+
cargoChirps: ["*krabbel* \u{1F980}", "cargo check l\xE4uft durch!", "Borrow Checker nickt!", "furchtlose Updates!"],
|
|
2559
3608
|
mavenChirps: ["*kaffee schl\xFCrf* \u2615", "Spring Boot l\xE4uft!", "Java 25 rockt!", "sauber gebaut!"]
|
|
2560
3609
|
},
|
|
2561
3610
|
splash: {
|
|
@@ -2607,7 +3656,9 @@ var en = {
|
|
|
2607
3656
|
dev: "(dev/test)",
|
|
2608
3657
|
prod: "(prod/compile)",
|
|
2609
3658
|
align: "[Align]",
|
|
2610
|
-
internal: "[Internal]"
|
|
3659
|
+
internal: "[Internal]",
|
|
3660
|
+
lock: "[Lock]",
|
|
3661
|
+
shared: (count) => `[+${count} crates]`
|
|
2611
3662
|
},
|
|
2612
3663
|
status: {
|
|
2613
3664
|
selectedOf: (selected, total) => `${selected} of ${total}`,
|
|
@@ -2636,6 +3687,7 @@ var en = {
|
|
|
2636
3687
|
hint: "[\u2191/\u2193/j/k] Navigate \u2502 [Enter] Select \u2502 [Esc] Cancel \u2502 [Backspace] Clear filter",
|
|
2637
3688
|
latestBadge: "LATEST",
|
|
2638
3689
|
currentBadge: "CURRENT",
|
|
3690
|
+
alsoChanges: (names) => `Also changes: ${names.join(", ")}`,
|
|
2639
3691
|
customBadge: "MANUAL"
|
|
2640
3692
|
},
|
|
2641
3693
|
updating: {
|
|
@@ -2664,6 +3716,7 @@ var en = {
|
|
|
2664
3716
|
scanningChirps: ["*peeking*", "checking...", "ooh, updates?", "just a sec!"],
|
|
2665
3717
|
updatingChirps: ["*working hard*", "building...", "almost there!", "packing modules..."],
|
|
2666
3718
|
successChirps: ["hooray! \u{1F389}", "all updated!", "looking sharp! \u{1FAB6}", "great job!"],
|
|
3719
|
+
cargoChirps: ["*crab walk* \u{1F980}", "cargo check passes!", "borrow checker approves!", "fearless updates!"],
|
|
2667
3720
|
mavenChirps: ["*sipping coffee* \u2615", "Spring Boot rocks!", "Java 25 is fly!", "clean build!"]
|
|
2668
3721
|
},
|
|
2669
3722
|
splash: {
|
|
@@ -2700,48 +3753,12 @@ function useI18n(initialLocale = "de") {
|
|
|
2700
3753
|
|
|
2701
3754
|
// src/hooks/use-workspaces.ts
|
|
2702
3755
|
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
3756
|
|
|
2744
3757
|
// src/application/scan.ts
|
|
3758
|
+
async function discoverModules(strategy, rootDir) {
|
|
3759
|
+
const modules = await strategy.discovery.discover(rootDir);
|
|
3760
|
+
return { modules, diagnostics: strategy.discovery.diagnostics?.() ?? [] };
|
|
3761
|
+
}
|
|
2745
3762
|
function createScanContext(modules, config, channel) {
|
|
2746
3763
|
return {
|
|
2747
3764
|
rootDir: config.rootDir,
|
|
@@ -2801,7 +3818,52 @@ function candidateToJson(u) {
|
|
|
2801
3818
|
};
|
|
2802
3819
|
}
|
|
2803
3820
|
|
|
3821
|
+
// src/hooks/use-workspaces.ts
|
|
3822
|
+
function useWorkspaces(strategy, rootDir) {
|
|
3823
|
+
const [modules, setModules] = useState2([]);
|
|
3824
|
+
const [loading, setLoading] = useState2(true);
|
|
3825
|
+
const [activeIndex, setActiveIndex] = useState2(0);
|
|
3826
|
+
const [diagnostics, setDiagnostics] = useState2([]);
|
|
3827
|
+
const reload = useCallback2(async () => {
|
|
3828
|
+
setLoading(true);
|
|
3829
|
+
setModules([]);
|
|
3830
|
+
setActiveIndex(0);
|
|
3831
|
+
setDiagnostics([]);
|
|
3832
|
+
try {
|
|
3833
|
+
const found = await discoverModules(strategy, rootDir);
|
|
3834
|
+
setModules(found.modules);
|
|
3835
|
+
setDiagnostics(found.diagnostics);
|
|
3836
|
+
} catch (err) {
|
|
3837
|
+
setModules([]);
|
|
3838
|
+
setDiagnostics([err instanceof Error ? err.message : String(err)]);
|
|
3839
|
+
} finally {
|
|
3840
|
+
setLoading(false);
|
|
3841
|
+
}
|
|
3842
|
+
}, [strategy, rootDir]);
|
|
3843
|
+
useEffect(() => {
|
|
3844
|
+
reload();
|
|
3845
|
+
}, [reload]);
|
|
3846
|
+
const active = modules[activeIndex] || {
|
|
3847
|
+
id: "empty",
|
|
3848
|
+
name: "No module found",
|
|
3849
|
+
path: rootDir,
|
|
3850
|
+
relPath: "Root",
|
|
3851
|
+
ecosystem: strategy.ecosystem,
|
|
3852
|
+
isRoot: true
|
|
3853
|
+
};
|
|
3854
|
+
return {
|
|
3855
|
+
modules,
|
|
3856
|
+
active,
|
|
3857
|
+
activeIndex,
|
|
3858
|
+
setActiveIndex,
|
|
3859
|
+
loading,
|
|
3860
|
+
diagnostics,
|
|
3861
|
+
reload
|
|
3862
|
+
};
|
|
3863
|
+
}
|
|
3864
|
+
|
|
2804
3865
|
// src/hooks/use-packages.ts
|
|
3866
|
+
import { useState as useState3, useEffect as useEffect2, useMemo as useMemo2, useCallback as useCallback3, useRef } from "react";
|
|
2805
3867
|
function usePackages(strategy, module, modules, config, channel = "stable") {
|
|
2806
3868
|
const [candidates, setCandidates] = useState3([]);
|
|
2807
3869
|
const [skipped, setSkipped] = useState3([]);
|
|
@@ -2868,15 +3930,12 @@ function usePackages(strategy, module, modules, config, channel = "stable") {
|
|
|
2868
3930
|
setCandidates(
|
|
2869
3931
|
(prev) => prev.map((c) => {
|
|
2870
3932
|
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
|
-
}
|
|
3933
|
+
const policy = strategy.versioning;
|
|
3934
|
+
const diff = c.currentClean ? policy.diff(c.currentClean, chosenVersion) : "minor";
|
|
2876
3935
|
const isPre = isPreReleaseVersion(chosenVersion);
|
|
2877
3936
|
return {
|
|
2878
3937
|
...c,
|
|
2879
|
-
newRange:
|
|
3938
|
+
newRange: policy.rewrite(c.currentRange, chosenVersion),
|
|
2880
3939
|
latest: chosenVersion,
|
|
2881
3940
|
diff,
|
|
2882
3941
|
selected: true,
|
|
@@ -2886,7 +3945,7 @@ function usePackages(strategy, module, modules, config, channel = "stable") {
|
|
|
2886
3945
|
};
|
|
2887
3946
|
})
|
|
2888
3947
|
);
|
|
2889
|
-
}, []);
|
|
3948
|
+
}, [strategy]);
|
|
2890
3949
|
const switchTab = useCallback3((tab) => {
|
|
2891
3950
|
setActiveTab(tab);
|
|
2892
3951
|
setCursor(0);
|
|
@@ -2918,11 +3977,11 @@ function usePackages(strategy, module, modules, config, channel = "stable") {
|
|
|
2918
3977
|
import { useState as useState4, useCallback as useCallback4 } from "react";
|
|
2919
3978
|
|
|
2920
3979
|
// src/application/update-workflow.ts
|
|
2921
|
-
import { existsSync as
|
|
2922
|
-
import { relative as
|
|
3980
|
+
import { existsSync as existsSync18 } from "fs";
|
|
3981
|
+
import { relative as relative10 } from "path";
|
|
2923
3982
|
|
|
2924
3983
|
// src/adapters/shared/file-snapshot.ts
|
|
2925
|
-
import { existsSync as
|
|
3984
|
+
import { existsSync as existsSync17, readFileSync as readFileSync17, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
2926
3985
|
var FileSnapshot = class _FileSnapshot {
|
|
2927
3986
|
constructor(entries2) {
|
|
2928
3987
|
this.entries = entries2;
|
|
@@ -2933,7 +3992,7 @@ var FileSnapshot = class _FileSnapshot {
|
|
|
2933
3992
|
const entries2 = /* @__PURE__ */ new Map();
|
|
2934
3993
|
for (const file of files) {
|
|
2935
3994
|
if (entries2.has(file)) continue;
|
|
2936
|
-
entries2.set(file,
|
|
3995
|
+
entries2.set(file, existsSync17(file) ? readFileSync17(file) : null);
|
|
2937
3996
|
}
|
|
2938
3997
|
return new _FileSnapshot(entries2);
|
|
2939
3998
|
}
|
|
@@ -2945,9 +4004,9 @@ var FileSnapshot = class _FileSnapshot {
|
|
|
2945
4004
|
changedFiles() {
|
|
2946
4005
|
return this.files.filter((file) => {
|
|
2947
4006
|
const before = this.entries.get(file) ?? null;
|
|
2948
|
-
const exists =
|
|
4007
|
+
const exists = existsSync17(file);
|
|
2949
4008
|
if (before === null) return exists;
|
|
2950
|
-
return !exists || !
|
|
4009
|
+
return !exists || !readFileSync17(file).equals(before);
|
|
2951
4010
|
});
|
|
2952
4011
|
}
|
|
2953
4012
|
/** Writes every file back to its captured state; removes files that did not exist. */
|
|
@@ -2956,7 +4015,7 @@ var FileSnapshot = class _FileSnapshot {
|
|
|
2956
4015
|
if (content === null) {
|
|
2957
4016
|
rmSync2(file, { force: true });
|
|
2958
4017
|
} else {
|
|
2959
|
-
|
|
4018
|
+
writeFileSync5(file, content);
|
|
2960
4019
|
}
|
|
2961
4020
|
}
|
|
2962
4021
|
}
|
|
@@ -2995,7 +4054,7 @@ Restoring the installed state failed as well: ${message(err)}`;
|
|
|
2995
4054
|
let baseline;
|
|
2996
4055
|
if (!options.skipVerification) {
|
|
2997
4056
|
progress("Running verification on the current state\u2026");
|
|
2998
|
-
baseline = await verify(snapshot.files.filter((file) =>
|
|
4057
|
+
baseline = await verify(snapshot.files.filter((file) => existsSync18(file)));
|
|
2999
4058
|
}
|
|
3000
4059
|
let applied;
|
|
3001
4060
|
try {
|
|
@@ -3041,7 +4100,7 @@ function message(err) {
|
|
|
3041
4100
|
return err instanceof Error ? err.message : String(err);
|
|
3042
4101
|
}
|
|
3043
4102
|
function displayPaths(files, rootDir) {
|
|
3044
|
-
return files.map((file) =>
|
|
4103
|
+
return files.map((file) => relative10(rootDir, file).replace(/\\/g, "/"));
|
|
3045
4104
|
}
|
|
3046
4105
|
|
|
3047
4106
|
// src/hooks/use-updater.ts
|
|
@@ -3255,33 +4314,39 @@ var Header = ({
|
|
|
3255
4314
|
// src/components/workspace-bar.tsx
|
|
3256
4315
|
import { Box as Box3, Text as Text3 } from "ink";
|
|
3257
4316
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
3258
|
-
var WorkspaceBar = ({ module, internalScopes, strategy, t }) => {
|
|
4317
|
+
var WorkspaceBar = ({ module, internalScopes, strategy, t, diagnostics = [] }) => {
|
|
3259
4318
|
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
|
-
|
|
4319
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
4320
|
+
/* @__PURE__ */ jsxs3(Box3, { marginY: 1, justifyContent: "space-between", alignItems: "center", children: [
|
|
4321
|
+
/* @__PURE__ */ jsxs3(Box3, { alignItems: "center", flexShrink: 1, children: [
|
|
4322
|
+
/* @__PURE__ */ jsxs3(Text3, { bold: true, children: [
|
|
4323
|
+
"\u{1F4CD} ",
|
|
4324
|
+
strategy.icon,
|
|
4325
|
+
" ",
|
|
4326
|
+
t.workspace.label,
|
|
4327
|
+
": "
|
|
4328
|
+
] }),
|
|
4329
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, color: theme.colors.brandLight, children: module.name }),
|
|
4330
|
+
/* @__PURE__ */ jsxs3(Text3, { color: theme.colors.muted, children: [
|
|
4331
|
+
" (",
|
|
4332
|
+
module.relPath,
|
|
4333
|
+
")"
|
|
4334
|
+
] })
|
|
3268
4335
|
] }),
|
|
3269
|
-
/* @__PURE__ */
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
4336
|
+
/* @__PURE__ */ jsxs3(Box3, { alignItems: "center", marginLeft: 2, children: [
|
|
4337
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.colors.muted, children: scopeHint }),
|
|
4338
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.colors.muted, children: " \u2502 " }),
|
|
4339
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.colors.brandLight, bold: true, children: "[W]" }),
|
|
4340
|
+
/* @__PURE__ */ jsxs3(Text3, { color: theme.colors.muted, children: [
|
|
4341
|
+
" ",
|
|
4342
|
+
t.workspace.switchHint.replace("[W]", "").trim()
|
|
4343
|
+
] })
|
|
3274
4344
|
] })
|
|
3275
4345
|
] }),
|
|
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
|
-
] })
|
|
4346
|
+
diagnostics.slice(0, 2).map((d) => /* @__PURE__ */ jsxs3(Text3, { color: theme.colors.danger, wrap: "truncate-end", children: [
|
|
4347
|
+
"\u26A0 ",
|
|
4348
|
+
d.split("\n")[0]
|
|
4349
|
+
] }, d))
|
|
3285
4350
|
] });
|
|
3286
4351
|
};
|
|
3287
4352
|
|
|
@@ -3331,7 +4396,9 @@ import { Box as Box5, Text as Text5 } from "ink";
|
|
|
3331
4396
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
3332
4397
|
var PackageRow = ({ item, isFocused, t }) => {
|
|
3333
4398
|
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
|
|
4399
|
+
const shared = item.sharedWith && item.sharedWith.length > 0 ? ` ${t.badges.shared(item.sharedWith.length)}` : "";
|
|
4400
|
+
const label = item.qualifier ? `${item.coordinate.identifier} (${item.qualifier})` : item.coordinate.identifier;
|
|
4401
|
+
const displayName = label.slice(0, 42 - shared.length) + shared;
|
|
3335
4402
|
const isDevOrTest = item.scope === "dev" || item.scope === "test";
|
|
3336
4403
|
const property = item.declaration?.kind === "property" ? `\${${item.declaration.propertyName}}` : void 0;
|
|
3337
4404
|
return /* @__PURE__ */ jsxs5(Box5, { justifyContent: "space-between", children: [
|
|
@@ -3361,6 +4428,10 @@ var PackageRow = ({ item, isFocused, t }) => {
|
|
|
3361
4428
|
t.badges.align,
|
|
3362
4429
|
" "
|
|
3363
4430
|
] }),
|
|
4431
|
+
item.action === "lock" && /* @__PURE__ */ jsxs5(Text5, { bold: true, color: theme.colors.brandLight, children: [
|
|
4432
|
+
t.badges.lock,
|
|
4433
|
+
" "
|
|
4434
|
+
] }),
|
|
3364
4435
|
item.origin.kind === "private" && /* @__PURE__ */ jsxs5(Text5, { color: theme.colors.brandLight, children: [
|
|
3365
4436
|
t.badges.internal,
|
|
3366
4437
|
" "
|
|
@@ -3510,7 +4581,7 @@ var ChickenMascot = ({ state = "idle", t, ecosystem = "npm" }) => {
|
|
|
3510
4581
|
chickFace = cur.face;
|
|
3511
4582
|
wingLeft = cur.l;
|
|
3512
4583
|
wingRight = cur.r;
|
|
3513
|
-
const chirpsPool = ecosystem === "maven" ? [...t.mascot.idleChirps, ...t.mascot.mavenChirps] : t.mascot.idleChirps;
|
|
4584
|
+
const chirpsPool = ecosystem === "maven" ? [...t.mascot.idleChirps, ...t.mascot.mavenChirps] : ecosystem === "cargo" ? [...t.mascot.idleChirps, ...t.mascot.cargoChirps] : t.mascot.idleChirps;
|
|
3514
4585
|
currentChirp = chirpsPool[chirpIndex % chirpsPool.length];
|
|
3515
4586
|
}
|
|
3516
4587
|
return /* @__PURE__ */ jsxs7(Box7, { alignItems: "center", children: [
|
|
@@ -3788,6 +4859,7 @@ var VersionModal = ({
|
|
|
3788
4859
|
] }),
|
|
3789
4860
|
/* @__PURE__ */ jsx11(Text11, { color: theme.colors.muted, children: t.versionModal.current(candidate.currentRange) })
|
|
3790
4861
|
] }),
|
|
4862
|
+
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
4863
|
/* @__PURE__ */ jsxs11(
|
|
3792
4864
|
Box11,
|
|
3793
4865
|
{
|
|
@@ -3962,10 +5034,10 @@ var App = ({ config }) => {
|
|
|
3962
5034
|
const [versionPickerCandidate, setVersionPickerCandidate] = useState8(null);
|
|
3963
5035
|
const [ecosystem, setEcosystem] = useState8("npm");
|
|
3964
5036
|
const strategy = useMemo4(() => EcosystemFactory.getStrategy(ecosystem), [ecosystem]);
|
|
3965
|
-
const nextEcosystem = ecosystem
|
|
5037
|
+
const nextEcosystem = EcosystemFactory.next(ecosystem);
|
|
3966
5038
|
const nextStrategy = useMemo4(() => EcosystemFactory.getStrategy(nextEcosystem), [nextEcosystem]);
|
|
3967
5039
|
const toggleEcosystem = useCallback5(() => {
|
|
3968
|
-
setEcosystem((prev) => prev
|
|
5040
|
+
setEcosystem((prev) => EcosystemFactory.next(prev));
|
|
3969
5041
|
}, []);
|
|
3970
5042
|
const { locale, toggleLocale, t } = useI18n(resolveInitialLocale(config.locale));
|
|
3971
5043
|
const ws = useWorkspaces(strategy, config.rootDir);
|
|
@@ -4117,7 +5189,8 @@ var App = ({ config }) => {
|
|
|
4117
5189
|
module: ws.active,
|
|
4118
5190
|
internalScopes: config.internalScopes,
|
|
4119
5191
|
strategy,
|
|
4120
|
-
t
|
|
5192
|
+
t,
|
|
5193
|
+
diagnostics: ws.diagnostics
|
|
4121
5194
|
}
|
|
4122
5195
|
),
|
|
4123
5196
|
/* @__PURE__ */ jsx14(
|
|
@@ -4157,6 +5230,7 @@ var App = ({ config }) => {
|
|
|
4157
5230
|
};
|
|
4158
5231
|
|
|
4159
5232
|
// src/cli/parser.ts
|
|
5233
|
+
var isEcosystem = (v) => EcosystemFactory.getAvailableEcosystems().includes(v);
|
|
4160
5234
|
function parseCliArgs(args) {
|
|
4161
5235
|
const [first, ...rest] = args;
|
|
4162
5236
|
if (!first || first === "--help" || first === "-h" || first === "help") {
|
|
@@ -4194,10 +5268,10 @@ function parseCliArgs(args) {
|
|
|
4194
5268
|
result.help = true;
|
|
4195
5269
|
} else if (arg.startsWith("--ecosystem=")) {
|
|
4196
5270
|
const val = arg.split("=")[1];
|
|
4197
|
-
if (val
|
|
5271
|
+
if (isEcosystem(val)) result.ecosystem = val;
|
|
4198
5272
|
} else if (arg === "-e" || arg === "--ecosystem") {
|
|
4199
5273
|
const val = remaining[++i];
|
|
4200
|
-
if (val
|
|
5274
|
+
if (isEcosystem(val)) result.ecosystem = val;
|
|
4201
5275
|
} else if (arg.startsWith("--module=")) {
|
|
4202
5276
|
result.module = arg.split("=")[1];
|
|
4203
5277
|
} else if (arg === "-m" || arg === "--module") {
|
|
@@ -4239,7 +5313,7 @@ SUBCOMMANDS:
|
|
|
4239
5313
|
mcp Start stdio-based MCP server
|
|
4240
5314
|
|
|
4241
5315
|
FLAGS:
|
|
4242
|
-
-e, --ecosystem <npm|maven> Target ecosystem (default: npm)
|
|
5316
|
+
-e, --ecosystem <npm|maven|cargo> Target ecosystem (default: npm)
|
|
4243
5317
|
-m, --module <name|path> Target module ID or relative path (default: root)
|
|
4244
5318
|
-c, --channel <stable|all> Release channel (default: stable)
|
|
4245
5319
|
-p, --packages <pkg1,pkg2@1.2.3> Comma-separated list of packages to update
|
|
@@ -4267,11 +5341,11 @@ EXAMPLES:
|
|
|
4267
5341
|
async function handleModulesCommand(options, config) {
|
|
4268
5342
|
const ecosystem = options.ecosystem ?? "npm";
|
|
4269
5343
|
const strategy = EcosystemFactory.getStrategy(ecosystem);
|
|
4270
|
-
const modules = await strategy
|
|
5344
|
+
const { modules, diagnostics } = await discoverModules(strategy, config.rootDir);
|
|
4271
5345
|
const displayName = strategy.displayNameFor?.(config.rootDir) ?? strategy.displayName;
|
|
4272
5346
|
const packageManager = strategy.packageManagerFor?.(config.rootDir);
|
|
4273
5347
|
if (options.json) {
|
|
4274
|
-
console.log(JSON.stringify({ ecosystem, ...packageManager ? { packageManager } : {}, count: modules.length, modules }, null, 2));
|
|
5348
|
+
console.log(JSON.stringify({ ecosystem, ...packageManager ? { packageManager } : {}, count: modules.length, modules, diagnostics }, null, 2));
|
|
4275
5349
|
return 0;
|
|
4276
5350
|
}
|
|
4277
5351
|
console.log(`
|
|
@@ -4282,6 +5356,7 @@ async function handleModulesCommand(options, config) {
|
|
|
4282
5356
|
console.log(` \u2022 ${m.name.padEnd(45)} (${m.relPath})${isRootBadge}`);
|
|
4283
5357
|
}
|
|
4284
5358
|
console.log("");
|
|
5359
|
+
for (const problem of diagnostics) console.error(`\u26A0\uFE0F ${problem}`);
|
|
4285
5360
|
return 0;
|
|
4286
5361
|
}
|
|
4287
5362
|
|
|
@@ -4303,6 +5378,8 @@ function describeSkip(skipped) {
|
|
|
4303
5378
|
return `workspace package that pnpm does not link unless it is declared with the workspace: protocol${detail}`;
|
|
4304
5379
|
case "catalog-unresolved":
|
|
4305
5380
|
return `catalog entry could not be resolved${detail}`;
|
|
5381
|
+
case "incompatible-rust-version":
|
|
5382
|
+
return `newer versions need a newer Rust than the project's rust-version${detail}`;
|
|
4306
5383
|
}
|
|
4307
5384
|
}
|
|
4308
5385
|
|
|
@@ -4311,7 +5388,8 @@ async function handleScanCommand(options, config) {
|
|
|
4311
5388
|
const ecosystem = options.ecosystem ?? "npm";
|
|
4312
5389
|
const channel = options.channel ?? config.channel ?? "stable";
|
|
4313
5390
|
const strategy = EcosystemFactory.getStrategy(ecosystem);
|
|
4314
|
-
const modules = await strategy
|
|
5391
|
+
const { modules, diagnostics } = await discoverModules(strategy, config.rootDir);
|
|
5392
|
+
for (const problem of diagnostics) console.error(`\u26A0\uFE0F ${problem}`);
|
|
4315
5393
|
if (modules.length === 0) {
|
|
4316
5394
|
if (options.json) {
|
|
4317
5395
|
console.log(JSON.stringify({ error: `No ${strategy.displayName} modules found.`, ecosystem }, null, 2));
|
|
@@ -4390,7 +5468,7 @@ async function handleScanCommand(options, config) {
|
|
|
4390
5468
|
if (result.candidates.length === 0) console.log(" \u2728 All dependencies are up to date.");
|
|
4391
5469
|
for (const u of result.candidates) {
|
|
4392
5470
|
const diffTag = `[${u.diff.toUpperCase()}]`.padEnd(9);
|
|
4393
|
-
const actionTag = u.action === "align" ? "[ALIGN] " : u.origin.kind === "private" ? "[INTERNAL] " : "";
|
|
5471
|
+
const actionTag = u.action === "align" ? "[ALIGN] " : u.action === "lock" ? "[LOCK] " : u.origin.kind === "private" ? "[INTERNAL] " : "";
|
|
4394
5472
|
const preBadge = u.isPreRelease ? `[${u.preReleaseTag || "PRE"}] ` : "";
|
|
4395
5473
|
const where = u.declaration && u.declaration.displayPath !== modulePom ? ` (in ${u.declaration.displayPath}${u.declaration.propertyName ? ` \${${u.declaration.propertyName}}` : ""})` : "";
|
|
4396
5474
|
console.log(
|
|
@@ -4461,7 +5539,7 @@ async function handleVersionsCommand(options, config) {
|
|
|
4461
5539
|
}
|
|
4462
5540
|
|
|
4463
5541
|
// src/application/update-selection.ts
|
|
4464
|
-
import
|
|
5542
|
+
import semver12 from "semver";
|
|
4465
5543
|
var UpdateSelectionError = class extends Error {
|
|
4466
5544
|
constructor(message2) {
|
|
4467
5545
|
super(message2);
|
|
@@ -4486,9 +5564,9 @@ function selectRequested(scan, requests, options) {
|
|
|
4486
5564
|
`'${request.identifier}' has no pending update. Specify a target version (e.g. ${request.identifier}@1.2.3).`
|
|
4487
5565
|
);
|
|
4488
5566
|
}
|
|
4489
|
-
if (options.
|
|
5567
|
+
if (!policyOf(options).allowsManualTargets) {
|
|
4490
5568
|
throw new UpdateSelectionError(
|
|
4491
|
-
`'${request.identifier}' has no pending update.
|
|
5569
|
+
`'${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
5570
|
);
|
|
4493
5571
|
}
|
|
4494
5572
|
if (options.originOf(request.identifier) !== "public") {
|
|
@@ -4513,12 +5591,12 @@ function retarget(found, target, options) {
|
|
|
4513
5591
|
`'${found.coordinate.identifier}' is a workspace module; it can only be aligned to its local version ${found.latest}.`
|
|
4514
5592
|
);
|
|
4515
5593
|
}
|
|
4516
|
-
if (options.
|
|
5594
|
+
if (!policyOf(options).allowsManualTargets && found.availableVersions && !found.availableVersions.includes(target)) {
|
|
4517
5595
|
throw new UpdateSelectionError(
|
|
4518
5596
|
`'${found.coordinate.identifier}' has no version ${target} in the configured repositories. Available: ${found.availableVersions.slice(0, 5).join(", ")}${found.availableVersions.length > 5 ? ", \u2026" : ""}.`
|
|
4519
5597
|
);
|
|
4520
5598
|
}
|
|
4521
|
-
const diff =
|
|
5599
|
+
const diff = policyOf(options).diff(found.currentClean, target);
|
|
4522
5600
|
if (diff === "major" && !options.allowMajor) {
|
|
4523
5601
|
throw new UpdateSelectionError(
|
|
4524
5602
|
`'${found.coordinate.identifier}' requires a major upgrade to ${target}. Pass --allow-major / allowMajor.`
|
|
@@ -4527,7 +5605,9 @@ function retarget(found, target, options) {
|
|
|
4527
5605
|
const isPreRelease = isPreReleaseVersion(target);
|
|
4528
5606
|
return {
|
|
4529
5607
|
...found,
|
|
4530
|
-
|
|
5608
|
+
// A lock update pins exactly the version that was picked.
|
|
5609
|
+
...found.action === "lock" ? { latest: target } : {},
|
|
5610
|
+
newRange: policyOf(options).rewrite(found.currentRange, target),
|
|
4531
5611
|
diff,
|
|
4532
5612
|
selected: true,
|
|
4533
5613
|
isCustomVersion: true,
|
|
@@ -4536,7 +5616,7 @@ function retarget(found, target, options) {
|
|
|
4536
5616
|
};
|
|
4537
5617
|
}
|
|
4538
5618
|
function manualNpmCandidate(identifier, target, options) {
|
|
4539
|
-
if (!
|
|
5619
|
+
if (!semver12.valid(target)) {
|
|
4540
5620
|
throw new UpdateSelectionError(`'${target}' is not a valid version for '${identifier}'.`);
|
|
4541
5621
|
}
|
|
4542
5622
|
return {
|
|
@@ -4553,10 +5633,8 @@ function manualNpmCandidate(identifier, target, options) {
|
|
|
4553
5633
|
isCustomVersion: true
|
|
4554
5634
|
};
|
|
4555
5635
|
}
|
|
4556
|
-
function
|
|
4557
|
-
|
|
4558
|
-
if (!to || !semver6.valid(current)) return "minor";
|
|
4559
|
-
return semver6.eq(current, to) ? "patch" : diffVersions(current, to);
|
|
5636
|
+
function policyOf(options) {
|
|
5637
|
+
return EcosystemFactory.getStrategy(options.ecosystem).versioning;
|
|
4560
5638
|
}
|
|
4561
5639
|
|
|
4562
5640
|
// src/cli/command-update.ts
|
|
@@ -4568,7 +5646,8 @@ async function handleUpdateCommand(options, config) {
|
|
|
4568
5646
|
else console.error(`\u274C ${message2}`);
|
|
4569
5647
|
return code;
|
|
4570
5648
|
};
|
|
4571
|
-
const modules = await strategy
|
|
5649
|
+
const { modules, diagnostics } = await discoverModules(strategy, config.rootDir);
|
|
5650
|
+
for (const problem of diagnostics) console.error(`\u26A0\uFE0F ${problem}`);
|
|
4572
5651
|
if (modules.length === 0) return fail(`No ${strategy.displayName} modules found.`);
|
|
4573
5652
|
const targetModule = options.module ? findModule(modules, options.module) : modules[0];
|
|
4574
5653
|
if (!targetModule) return fail(`Module '${options.module}' not found.`);
|
|
@@ -4709,9 +5788,9 @@ function parsePackageArgument(item) {
|
|
|
4709
5788
|
}
|
|
4710
5789
|
|
|
4711
5790
|
// src/cli/command-init.ts
|
|
4712
|
-
import { existsSync as
|
|
4713
|
-
import { homedir, platform } from "os";
|
|
4714
|
-
import { join as
|
|
5791
|
+
import { existsSync as existsSync19, readFileSync as readFileSync18, writeFileSync as writeFileSync6, mkdirSync as mkdirSync2 } from "fs";
|
|
5792
|
+
import { homedir as homedir2, platform } from "os";
|
|
5793
|
+
import { join as join24, resolve as resolve15 } from "path";
|
|
4715
5794
|
function mcpServerEntry() {
|
|
4716
5795
|
return {
|
|
4717
5796
|
command: "npx",
|
|
@@ -4754,10 +5833,10 @@ automatic rollback.
|
|
|
4754
5833
|
requested). A failed install or verification is rolled back automatically.
|
|
4755
5834
|
4. \`elevate_verify\` \u2014 Verify build integrity when needed
|
|
4756
5835
|
`;
|
|
4757
|
-
var home =
|
|
5836
|
+
var home = homedir2();
|
|
4758
5837
|
var isWin = platform() === "win32";
|
|
4759
5838
|
function appData() {
|
|
4760
|
-
return process.env.APPDATA ||
|
|
5839
|
+
return process.env.APPDATA || join24(home, "AppData", "Roaming");
|
|
4761
5840
|
}
|
|
4762
5841
|
function detectCursor(cwd) {
|
|
4763
5842
|
const env = {
|
|
@@ -4769,25 +5848,25 @@ function detectCursor(cwd) {
|
|
|
4769
5848
|
mcpConfigured: false,
|
|
4770
5849
|
skillConfigured: false
|
|
4771
5850
|
};
|
|
4772
|
-
const workspaceCursorDir =
|
|
4773
|
-
const globalCursorDir =
|
|
4774
|
-
if (
|
|
5851
|
+
const workspaceCursorDir = join24(cwd, ".cursor");
|
|
5852
|
+
const globalCursorDir = join24(home, ".cursor");
|
|
5853
|
+
if (existsSync19(workspaceCursorDir) || existsSync19(globalCursorDir)) {
|
|
4775
5854
|
env.detected = true;
|
|
4776
|
-
env.reason =
|
|
5855
|
+
env.reason = existsSync19(workspaceCursorDir) ? `Found .cursor/ in workspace` : `Found ~/.cursor/ global config`;
|
|
4777
5856
|
}
|
|
4778
|
-
const mcpPath =
|
|
4779
|
-
if (
|
|
5857
|
+
const mcpPath = join24(cwd, ".cursor", "mcp.json");
|
|
5858
|
+
if (existsSync19(mcpPath)) {
|
|
4780
5859
|
try {
|
|
4781
|
-
const content = JSON.parse(
|
|
5860
|
+
const content = JSON.parse(readFileSync18(mcpPath, "utf8"));
|
|
4782
5861
|
if (content?.mcpServers?.elevate) {
|
|
4783
5862
|
env.mcpConfigured = true;
|
|
4784
5863
|
}
|
|
4785
5864
|
} catch {
|
|
4786
5865
|
}
|
|
4787
5866
|
}
|
|
4788
|
-
const rulesPath =
|
|
4789
|
-
if (
|
|
4790
|
-
const content =
|
|
5867
|
+
const rulesPath = join24(cwd, ".cursorrules");
|
|
5868
|
+
if (existsSync19(rulesPath)) {
|
|
5869
|
+
const content = readFileSync18(rulesPath, "utf8");
|
|
4791
5870
|
if (content.includes("elevate") || content.includes("Elevate")) {
|
|
4792
5871
|
env.skillConfigured = true;
|
|
4793
5872
|
}
|
|
@@ -4804,12 +5883,12 @@ function detectClaudeDesktop() {
|
|
|
4804
5883
|
mcpConfigured: false,
|
|
4805
5884
|
skillConfigured: false
|
|
4806
5885
|
};
|
|
4807
|
-
const configPath = isWin ?
|
|
4808
|
-
if (
|
|
5886
|
+
const configPath = isWin ? join24(appData(), "Claude", "claude_desktop_config.json") : join24(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
5887
|
+
if (existsSync19(configPath)) {
|
|
4809
5888
|
env.detected = true;
|
|
4810
5889
|
env.reason = `Found ${configPath}`;
|
|
4811
5890
|
try {
|
|
4812
|
-
const content = JSON.parse(
|
|
5891
|
+
const content = JSON.parse(readFileSync18(configPath, "utf8"));
|
|
4813
5892
|
if (content?.mcpServers?.elevate) {
|
|
4814
5893
|
env.mcpConfigured = true;
|
|
4815
5894
|
}
|
|
@@ -4828,23 +5907,23 @@ function detectClaudeCode(cwd) {
|
|
|
4828
5907
|
mcpConfigured: false,
|
|
4829
5908
|
skillConfigured: false
|
|
4830
5909
|
};
|
|
4831
|
-
const mcpJsonPath =
|
|
4832
|
-
const claudeMdPath =
|
|
4833
|
-
if (
|
|
5910
|
+
const mcpJsonPath = join24(cwd, ".mcp.json");
|
|
5911
|
+
const claudeMdPath = join24(cwd, "CLAUDE.md");
|
|
5912
|
+
if (existsSync19(mcpJsonPath) || existsSync19(claudeMdPath)) {
|
|
4834
5913
|
env.detected = true;
|
|
4835
|
-
env.reason =
|
|
5914
|
+
env.reason = existsSync19(mcpJsonPath) ? `Found .mcp.json in workspace` : `Found CLAUDE.md in workspace`;
|
|
4836
5915
|
}
|
|
4837
|
-
if (
|
|
5916
|
+
if (existsSync19(mcpJsonPath)) {
|
|
4838
5917
|
try {
|
|
4839
|
-
const content = JSON.parse(
|
|
5918
|
+
const content = JSON.parse(readFileSync18(mcpJsonPath, "utf8"));
|
|
4840
5919
|
if (content?.mcpServers?.elevate) {
|
|
4841
5920
|
env.mcpConfigured = true;
|
|
4842
5921
|
}
|
|
4843
5922
|
} catch {
|
|
4844
5923
|
}
|
|
4845
5924
|
}
|
|
4846
|
-
if (
|
|
4847
|
-
const content =
|
|
5925
|
+
if (existsSync19(claudeMdPath)) {
|
|
5926
|
+
const content = readFileSync18(claudeMdPath, "utf8");
|
|
4848
5927
|
if (content.includes("elevate") || content.includes("Elevate")) {
|
|
4849
5928
|
env.skillConfigured = true;
|
|
4850
5929
|
}
|
|
@@ -4861,20 +5940,20 @@ function detectAntigravity(cwd) {
|
|
|
4861
5940
|
mcpConfigured: false,
|
|
4862
5941
|
skillConfigured: false
|
|
4863
5942
|
};
|
|
4864
|
-
const globalGeminiDir =
|
|
4865
|
-
const workspaceAgentsDir =
|
|
4866
|
-
if (
|
|
5943
|
+
const globalGeminiDir = join24(home, ".gemini");
|
|
5944
|
+
const workspaceAgentsDir = join24(cwd, ".agents");
|
|
5945
|
+
if (existsSync19(globalGeminiDir) || existsSync19(workspaceAgentsDir)) {
|
|
4867
5946
|
env.detected = true;
|
|
4868
|
-
env.reason =
|
|
5947
|
+
env.reason = existsSync19(workspaceAgentsDir) ? `Found .agents/ in workspace` : `Found ~/.gemini/ global config`;
|
|
4869
5948
|
}
|
|
4870
5949
|
const mcpPaths = [
|
|
4871
|
-
|
|
4872
|
-
|
|
5950
|
+
join24(cwd, ".agents", "mcp_config.json"),
|
|
5951
|
+
join24(home, ".gemini", "config", "mcp_config.json")
|
|
4873
5952
|
];
|
|
4874
5953
|
for (const p of mcpPaths) {
|
|
4875
|
-
if (
|
|
5954
|
+
if (existsSync19(p)) {
|
|
4876
5955
|
try {
|
|
4877
|
-
const content = JSON.parse(
|
|
5956
|
+
const content = JSON.parse(readFileSync18(p, "utf8"));
|
|
4878
5957
|
if (content?.mcpServers?.elevate) {
|
|
4879
5958
|
env.mcpConfigured = true;
|
|
4880
5959
|
break;
|
|
@@ -4895,24 +5974,24 @@ function detectVSCode(cwd) {
|
|
|
4895
5974
|
mcpConfigured: false,
|
|
4896
5975
|
skillConfigured: false
|
|
4897
5976
|
};
|
|
4898
|
-
const vscodeDir =
|
|
4899
|
-
if (
|
|
5977
|
+
const vscodeDir = join24(cwd, ".vscode");
|
|
5978
|
+
if (existsSync19(vscodeDir)) {
|
|
4900
5979
|
env.detected = true;
|
|
4901
5980
|
env.reason = `Found .vscode/ in workspace`;
|
|
4902
5981
|
}
|
|
4903
|
-
const mcpPath =
|
|
4904
|
-
if (
|
|
5982
|
+
const mcpPath = join24(cwd, ".vscode", "mcp.json");
|
|
5983
|
+
if (existsSync19(mcpPath)) {
|
|
4905
5984
|
try {
|
|
4906
|
-
const content = JSON.parse(
|
|
5985
|
+
const content = JSON.parse(readFileSync18(mcpPath, "utf8"));
|
|
4907
5986
|
if (content?.servers?.elevate) {
|
|
4908
5987
|
env.mcpConfigured = true;
|
|
4909
5988
|
}
|
|
4910
5989
|
} catch {
|
|
4911
5990
|
}
|
|
4912
5991
|
}
|
|
4913
|
-
const copilotInstructions =
|
|
4914
|
-
if (
|
|
4915
|
-
const content =
|
|
5992
|
+
const copilotInstructions = join24(cwd, ".github", "copilot-instructions.md");
|
|
5993
|
+
if (existsSync19(copilotInstructions)) {
|
|
5994
|
+
const content = readFileSync18(copilotInstructions, "utf8");
|
|
4916
5995
|
if (content.includes("elevate") || content.includes("Elevate")) {
|
|
4917
5996
|
env.skillConfigured = true;
|
|
4918
5997
|
}
|
|
@@ -4929,14 +6008,14 @@ function detectWindsurf() {
|
|
|
4929
6008
|
mcpConfigured: false,
|
|
4930
6009
|
skillConfigured: false
|
|
4931
6010
|
};
|
|
4932
|
-
const configPath = isWin ?
|
|
4933
|
-
const altPath =
|
|
4934
|
-
if (
|
|
6011
|
+
const configPath = isWin ? join24(appData(), "Codeium", "Windsurf", "mcp_config.json") : join24(home, ".codeium", "windsurf", "mcp_config.json");
|
|
6012
|
+
const altPath = join24(home, ".codeium", "windsurf", "mcp_config.json");
|
|
6013
|
+
if (existsSync19(configPath) || existsSync19(altPath)) {
|
|
4935
6014
|
env.detected = true;
|
|
4936
6015
|
env.reason = `Found Windsurf config`;
|
|
4937
|
-
const path =
|
|
6016
|
+
const path = existsSync19(configPath) ? configPath : altPath;
|
|
4938
6017
|
try {
|
|
4939
|
-
const content = JSON.parse(
|
|
6018
|
+
const content = JSON.parse(readFileSync18(path, "utf8"));
|
|
4940
6019
|
if (content?.mcpServers?.elevate) {
|
|
4941
6020
|
env.mcpConfigured = true;
|
|
4942
6021
|
}
|
|
@@ -4947,9 +6026,9 @@ function detectWindsurf() {
|
|
|
4947
6026
|
}
|
|
4948
6027
|
function mergeJsonMcpServer(filePath, serverKey, topLevelKey) {
|
|
4949
6028
|
let existing = {};
|
|
4950
|
-
if (
|
|
6029
|
+
if (existsSync19(filePath)) {
|
|
4951
6030
|
try {
|
|
4952
|
-
existing = JSON.parse(
|
|
6031
|
+
existing = JSON.parse(readFileSync18(filePath, "utf8"));
|
|
4953
6032
|
} catch {
|
|
4954
6033
|
existing = {};
|
|
4955
6034
|
}
|
|
@@ -4961,69 +6040,69 @@ function mergeJsonMcpServer(filePath, serverKey, topLevelKey) {
|
|
|
4961
6040
|
servers[serverKey] = mcpServerEntry();
|
|
4962
6041
|
existing[topLevelKey] = servers;
|
|
4963
6042
|
const dir = filePath.substring(0, filePath.lastIndexOf(isWin ? "\\" : "/"));
|
|
4964
|
-
if (dir && !
|
|
6043
|
+
if (dir && !existsSync19(dir)) {
|
|
4965
6044
|
mkdirSync2(dir, { recursive: true });
|
|
4966
6045
|
}
|
|
4967
|
-
|
|
6046
|
+
writeFileSync6(filePath, JSON.stringify(existing, null, 2) + "\n", "utf8");
|
|
4968
6047
|
return { written: true, message: `${filePath}: elevate MCP server registered` };
|
|
4969
6048
|
}
|
|
4970
6049
|
function appendSkillToFile(filePath, content, marker) {
|
|
4971
|
-
if (
|
|
4972
|
-
const existing =
|
|
6050
|
+
if (existsSync19(filePath)) {
|
|
6051
|
+
const existing = readFileSync18(filePath, "utf8");
|
|
4973
6052
|
if (existing.includes(marker)) {
|
|
4974
6053
|
return { written: false, message: `${filePath}: skill already present` };
|
|
4975
6054
|
}
|
|
4976
|
-
|
|
6055
|
+
writeFileSync6(filePath, existing + "\n\n" + content, "utf8");
|
|
4977
6056
|
return { written: true, message: `${filePath}: skill appended` };
|
|
4978
6057
|
}
|
|
4979
6058
|
const dir = filePath.substring(0, filePath.lastIndexOf(isWin ? "\\" : "/"));
|
|
4980
|
-
if (dir && !
|
|
6059
|
+
if (dir && !existsSync19(dir)) {
|
|
4981
6060
|
mkdirSync2(dir, { recursive: true });
|
|
4982
6061
|
}
|
|
4983
|
-
|
|
6062
|
+
writeFileSync6(filePath, content, "utf8");
|
|
4984
6063
|
return { written: true, message: `${filePath}: skill created` };
|
|
4985
6064
|
}
|
|
4986
6065
|
function configureCursor(cwd, result) {
|
|
4987
|
-
const mcpPath =
|
|
6066
|
+
const mcpPath = join24(cwd, ".cursor", "mcp.json");
|
|
4988
6067
|
const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
|
|
4989
6068
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Cursor MCP: ${mcp.message}`);
|
|
4990
|
-
const rulesPath =
|
|
6069
|
+
const rulesPath = join24(cwd, ".cursorrules");
|
|
4991
6070
|
const skill = appendSkillToFile(rulesPath, ELEVATE_SKILL_CONTENT, "elevate_scan");
|
|
4992
6071
|
result.actions.push(`${skill.written ? "\u2705" : "\u23ED\uFE0F"} Cursor Rules: ${skill.message}`);
|
|
4993
6072
|
}
|
|
4994
6073
|
function configureClaudeDesktop(result) {
|
|
4995
|
-
const configPath = isWin ?
|
|
6074
|
+
const configPath = isWin ? join24(appData(), "Claude", "claude_desktop_config.json") : join24(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
4996
6075
|
const mcp = mergeJsonMcpServer(configPath, "elevate", "mcpServers");
|
|
4997
6076
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Claude Desktop MCP: ${mcp.message}`);
|
|
4998
6077
|
}
|
|
4999
6078
|
function configureClaudeCode(cwd, result) {
|
|
5000
|
-
const mcpPath =
|
|
6079
|
+
const mcpPath = join24(cwd, ".mcp.json");
|
|
5001
6080
|
const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
|
|
5002
6081
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Claude Code MCP: ${mcp.message}`);
|
|
5003
|
-
const claudeMdPath =
|
|
6082
|
+
const claudeMdPath = join24(cwd, "CLAUDE.md");
|
|
5004
6083
|
const skill = appendSkillToFile(claudeMdPath, ELEVATE_SKILL_CONTENT, "elevate_scan");
|
|
5005
6084
|
result.actions.push(`${skill.written ? "\u2705" : "\u23ED\uFE0F"} Claude Code Skill: ${skill.message}`);
|
|
5006
6085
|
}
|
|
5007
6086
|
function configureAntigravity(cwd, result) {
|
|
5008
|
-
const mcpPath =
|
|
6087
|
+
const mcpPath = join24(cwd, ".agents", "mcp_config.json");
|
|
5009
6088
|
const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
|
|
5010
6089
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Antigravity MCP: ${mcp.message}`);
|
|
5011
6090
|
}
|
|
5012
6091
|
function configureVSCode(cwd, result) {
|
|
5013
|
-
const mcpPath =
|
|
6092
|
+
const mcpPath = join24(cwd, ".vscode", "mcp.json");
|
|
5014
6093
|
const mcp = mergeJsonMcpServer(mcpPath, "elevate", "servers");
|
|
5015
6094
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} VS Code MCP: ${mcp.message}`);
|
|
5016
|
-
const instructionsPath =
|
|
6095
|
+
const instructionsPath = join24(cwd, ".github", "copilot-instructions.md");
|
|
5017
6096
|
const skill = appendSkillToFile(instructionsPath, ELEVATE_SKILL_CONTENT, "elevate_scan");
|
|
5018
6097
|
result.actions.push(`${skill.written ? "\u2705" : "\u23ED\uFE0F"} Copilot Instructions: ${skill.message}`);
|
|
5019
6098
|
}
|
|
5020
6099
|
function configureWindsurf(result) {
|
|
5021
|
-
const configPath = isWin ?
|
|
6100
|
+
const configPath = isWin ? join24(appData(), "Codeium", "Windsurf", "mcp_config.json") : join24(home, ".codeium", "windsurf", "mcp_config.json");
|
|
5022
6101
|
const mcp = mergeJsonMcpServer(configPath, "elevate", "mcpServers");
|
|
5023
6102
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Windsurf MCP: ${mcp.message}`);
|
|
5024
6103
|
}
|
|
5025
6104
|
async function handleInitCommand(options) {
|
|
5026
|
-
const cwd =
|
|
6105
|
+
const cwd = resolve15(process.cwd());
|
|
5027
6106
|
const result = {
|
|
5028
6107
|
cwd,
|
|
5029
6108
|
environments: [],
|
|
@@ -5051,7 +6130,7 @@ async function handleInitCommand(options) {
|
|
|
5051
6130
|
console.log(` Creating portable .mcp.json for broad compatibility.
|
|
5052
6131
|
`);
|
|
5053
6132
|
}
|
|
5054
|
-
const mcpPath =
|
|
6133
|
+
const mcpPath = join24(cwd, ".mcp.json");
|
|
5055
6134
|
const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
|
|
5056
6135
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Generic MCP: ${mcp.message}`);
|
|
5057
6136
|
} else {
|
|
@@ -5118,14 +6197,14 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
5118
6197
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
5119
6198
|
var ECOSYSTEM_PROPERTY = {
|
|
5120
6199
|
type: "string",
|
|
5121
|
-
enum:
|
|
6200
|
+
enum: EcosystemFactory.getAvailableEcosystems(),
|
|
5122
6201
|
description: "Target ecosystem (default: npm)"
|
|
5123
6202
|
};
|
|
5124
6203
|
async function startMcpServer(config) {
|
|
5125
6204
|
const server = new Server(
|
|
5126
6205
|
{
|
|
5127
6206
|
name: "elevate-mcp",
|
|
5128
|
-
version:
|
|
6207
|
+
version: VERSION
|
|
5129
6208
|
},
|
|
5130
6209
|
{
|
|
5131
6210
|
capabilities: {
|
|
@@ -5138,7 +6217,7 @@ async function startMcpServer(config) {
|
|
|
5138
6217
|
tools: [
|
|
5139
6218
|
{
|
|
5140
6219
|
name: "elevate_discover_modules",
|
|
5141
|
-
description: "Discovers all repository modules and workspaces for Node/npm (package.json)
|
|
6220
|
+
description: "Discovers all repository modules and workspaces for Node/npm (package.json), Java/Maven (pom.xml) or Rust/Cargo (Cargo.toml).",
|
|
5142
6221
|
inputSchema: {
|
|
5143
6222
|
type: "object",
|
|
5144
6223
|
properties: {
|
|
@@ -5178,7 +6257,7 @@ async function startMcpServer(config) {
|
|
|
5178
6257
|
ecosystem: ECOSYSTEM_PROPERTY,
|
|
5179
6258
|
identifier: {
|
|
5180
6259
|
type: "string",
|
|
5181
|
-
description: "Package identifier (npm: chalk, maven: org.slf4j:slf4j-api)"
|
|
6260
|
+
description: "Package identifier (npm: chalk, maven: org.slf4j:slf4j-api, cargo: serde)"
|
|
5182
6261
|
}
|
|
5183
6262
|
},
|
|
5184
6263
|
required: ["identifier"]
|
|
@@ -5259,12 +6338,13 @@ async function startMcpServer(config) {
|
|
|
5259
6338
|
};
|
|
5260
6339
|
try {
|
|
5261
6340
|
if (name === "elevate_discover_modules") {
|
|
5262
|
-
const modules = await strategy
|
|
6341
|
+
const { modules, diagnostics } = await discoverModules(strategy, config.rootDir);
|
|
5263
6342
|
const packageManager = strategy.packageManagerFor?.(config.rootDir);
|
|
5264
6343
|
return reply({
|
|
5265
6344
|
ecosystem,
|
|
5266
6345
|
...packageManager ? { packageManager } : {},
|
|
5267
6346
|
count: modules.length,
|
|
6347
|
+
diagnostics,
|
|
5268
6348
|
modules: modules.map((m) => ({
|
|
5269
6349
|
id: m.id,
|
|
5270
6350
|
name: m.name,
|