@dennisrepa/elevate 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +4 -2
- package/dist/cli.js +688 -343
- package/dist/cli.js.map +1 -1
- package/docs/de.md +32 -3
- package/docs/en.md +33 -5
- package/package.json +13 -9
package/dist/cli.js
CHANGED
|
@@ -4,74 +4,25 @@
|
|
|
4
4
|
import { render } from "ink";
|
|
5
5
|
|
|
6
6
|
// src/config.ts
|
|
7
|
-
import { existsSync as
|
|
8
|
-
import { dirname as dirname8, join as
|
|
7
|
+
import { existsSync as existsSync11, readFileSync as readFileSync11 } from "fs";
|
|
8
|
+
import { dirname as dirname8, join as join18, resolve as resolve10 } from "path";
|
|
9
9
|
|
|
10
|
-
// src/adapters/npm/npm-
|
|
11
|
-
import { readFileSync } from "fs";
|
|
12
|
-
import { dirname, join
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
// src/adapters/npm/npm-package-manager.ts
|
|
11
|
+
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
12
|
+
import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
|
|
13
|
+
|
|
14
|
+
// src/adapters/shared/checkout.ts
|
|
15
|
+
import { existsSync } from "fs";
|
|
16
|
+
import { dirname, join, resolve } from "path";
|
|
17
|
+
function findCheckoutBoundary(startDir) {
|
|
18
|
+
let dir = resolve(startDir);
|
|
19
|
+
for (; ; ) {
|
|
20
|
+
if (existsSync(join(dir, ".git"))) return dir;
|
|
21
|
+
const parent = dirname(dir);
|
|
22
|
+
if (parent === dir) return void 0;
|
|
23
|
+
dir = parent;
|
|
19
24
|
}
|
|
20
25
|
}
|
|
21
|
-
var NpmModuleDiscoveryAdapter = class {
|
|
22
|
-
/**
|
|
23
|
-
* The nearest directory, from `startDir` upwards to the boundary, whose
|
|
24
|
-
* package.json is valid JSON and declares `workspaces`.
|
|
25
|
-
*/
|
|
26
|
-
findRoot(startDir, boundaryDir) {
|
|
27
|
-
let dir = resolve(startDir);
|
|
28
|
-
const boundary = boundaryDir ? resolve(boundaryDir) : void 0;
|
|
29
|
-
for (; ; ) {
|
|
30
|
-
if (readJson(join(dir, "package.json"))?.workspaces) return dir;
|
|
31
|
-
const parent = dirname(dir);
|
|
32
|
-
if (dir === boundary || parent === dir) return void 0;
|
|
33
|
-
dir = parent;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
async discover(rootDir) {
|
|
37
|
-
const rootPkg = readJson(join(rootDir, "package.json"));
|
|
38
|
-
const modules = [
|
|
39
|
-
{
|
|
40
|
-
id: rootPkg?.name ?? "root",
|
|
41
|
-
name: rootPkg?.name ?? "Root Workspace (npm)",
|
|
42
|
-
path: rootDir,
|
|
43
|
-
relPath: "Root",
|
|
44
|
-
ecosystem: "npm",
|
|
45
|
-
isRoot: true,
|
|
46
|
-
version: typeof rootPkg?.version === "string" ? rootPkg.version : void 0
|
|
47
|
-
}
|
|
48
|
-
];
|
|
49
|
-
if (!rootPkg?.workspaces) return modules;
|
|
50
|
-
let workspaces;
|
|
51
|
-
try {
|
|
52
|
-
workspaces = await mapWorkspaces({ cwd: rootDir, pkg: rootPkg });
|
|
53
|
-
} catch {
|
|
54
|
-
return modules;
|
|
55
|
-
}
|
|
56
|
-
for (const [name, path] of workspaces) {
|
|
57
|
-
const pkg = readJson(join(path, "package.json"));
|
|
58
|
-
modules.push({
|
|
59
|
-
id: name,
|
|
60
|
-
name,
|
|
61
|
-
path,
|
|
62
|
-
relPath: relative(rootDir, path).replace(/\\/g, "/"),
|
|
63
|
-
ecosystem: "npm",
|
|
64
|
-
isRoot: false,
|
|
65
|
-
version: typeof pkg?.version === "string" ? pkg.version : void 0
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
modules.sort((a, b) => a.isRoot ? -1 : b.isRoot ? 1 : a.relPath.localeCompare(b.relPath));
|
|
69
|
-
return modules;
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
// src/adapters/npm/npm-registry.ts
|
|
74
|
-
import semver from "semver";
|
|
75
26
|
|
|
76
27
|
// src/adapters/shared/process.ts
|
|
77
28
|
import { exec, spawn } from "child_process";
|
|
@@ -148,6 +99,213 @@ function quoteWindows(arg) {
|
|
|
148
99
|
return /^[\w.:\\/@+-]+$/.test(arg) ? arg : `"${arg}"`;
|
|
149
100
|
}
|
|
150
101
|
|
|
102
|
+
// src/adapters/npm/npm-package-manager.ts
|
|
103
|
+
var MARKER_FILES = [
|
|
104
|
+
["npm", ["package-lock.json", "npm-shrinkwrap.json"]],
|
|
105
|
+
["pnpm", ["pnpm-lock.yaml", "pnpm-workspace.yaml"]],
|
|
106
|
+
["yarn", ["yarn.lock", ".yarnrc.yml"]],
|
|
107
|
+
["bun", ["bun.lock", "bun.lockb"]]
|
|
108
|
+
];
|
|
109
|
+
var UnsupportedPackageManagerError = class extends Error {
|
|
110
|
+
constructor(detected) {
|
|
111
|
+
super(
|
|
112
|
+
`This repository is managed by ${detected.name} (${detected.evidence}). Elevate supports npm and pnpm for Node.js projects; using either here would create a lockfile next to ${detected.name}'s. Nothing was changed.`
|
|
113
|
+
);
|
|
114
|
+
this.name = "UnsupportedPackageManagerError";
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
function detectPackageManager(dir) {
|
|
118
|
+
const boundary = findCheckoutBoundary(dir);
|
|
119
|
+
let current = resolve2(dir);
|
|
120
|
+
for (; ; ) {
|
|
121
|
+
const found = detectIn(current);
|
|
122
|
+
if (found) return found;
|
|
123
|
+
const parent = dirname2(current);
|
|
124
|
+
if (boundary === void 0 || current === boundary || parent === current) break;
|
|
125
|
+
current = parent;
|
|
126
|
+
}
|
|
127
|
+
return { name: "npm", evidence: "no other package manager detected" };
|
|
128
|
+
}
|
|
129
|
+
function detectIn(dir) {
|
|
130
|
+
const declared = readPackageManagerField(dir);
|
|
131
|
+
const name = declared?.match(/^(npm|pnpm|yarn|bun)@/)?.[1];
|
|
132
|
+
if (declared && name) return { name, evidence: `"packageManager": "${declared}" in package.json` };
|
|
133
|
+
for (const [manager, files] of MARKER_FILES) {
|
|
134
|
+
const found = files.find((file) => existsSync2(join2(dir, file)));
|
|
135
|
+
if (found) return { name: manager, evidence: found };
|
|
136
|
+
}
|
|
137
|
+
return void 0;
|
|
138
|
+
}
|
|
139
|
+
function assertSupportedPackageManager(rootDir) {
|
|
140
|
+
const detected = detectPackageManager(rootDir);
|
|
141
|
+
if (detected.name === "npm" || detected.name === "pnpm") return detected.name;
|
|
142
|
+
throw new UnsupportedPackageManagerError(detected);
|
|
143
|
+
}
|
|
144
|
+
var PackageManagerNotFoundError = class extends Error {
|
|
145
|
+
constructor(reason, detected) {
|
|
146
|
+
super(
|
|
147
|
+
`pnpm could not be run in this repository (${reason}). This repository is managed by pnpm (${detected.evidence}); install pnpm or enable Corepack (corepack enable). Nothing was changed.`
|
|
148
|
+
);
|
|
149
|
+
this.name = "PackageManagerNotFoundError";
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
var VERSION_TIMEOUT_MS = 15e3;
|
|
153
|
+
var verifiedRoots = /* @__PURE__ */ new Set();
|
|
154
|
+
async function ensurePnpmRunnable(rootDir, detected) {
|
|
155
|
+
const key = resolve2(rootDir);
|
|
156
|
+
if (verifiedRoots.has(key)) return;
|
|
157
|
+
let reason;
|
|
158
|
+
try {
|
|
159
|
+
const result = await runCommand("pnpm", ["--version"], { cwd: rootDir, timeoutMs: VERSION_TIMEOUT_MS });
|
|
160
|
+
if (result.timedOut) reason = "pnpm --version timed out";
|
|
161
|
+
else if (result.exitCode !== 0) {
|
|
162
|
+
const line = firstLine(result.stderr) || firstLine(result.stdout);
|
|
163
|
+
reason = `pnpm --version exited with code ${result.exitCode}: ${line}`;
|
|
164
|
+
}
|
|
165
|
+
} catch {
|
|
166
|
+
reason = "command not found";
|
|
167
|
+
}
|
|
168
|
+
if (reason !== void 0) throw new PackageManagerNotFoundError(reason, detected);
|
|
169
|
+
verifiedRoots.add(key);
|
|
170
|
+
}
|
|
171
|
+
async function prepareSupportedPackageManager(rootDir) {
|
|
172
|
+
const manager = assertSupportedPackageManager(rootDir);
|
|
173
|
+
if (manager === "pnpm") await ensurePnpmRunnable(rootDir, detectPackageManager(rootDir));
|
|
174
|
+
return manager;
|
|
175
|
+
}
|
|
176
|
+
function firstLine(text) {
|
|
177
|
+
return text.split(/\r?\n/).find((l) => l.trim() !== "")?.trim() ?? "";
|
|
178
|
+
}
|
|
179
|
+
function readPackageManagerField(rootDir) {
|
|
180
|
+
try {
|
|
181
|
+
const field = JSON.parse(readFileSync(join2(rootDir, "package.json"), "utf8"))?.packageManager;
|
|
182
|
+
return typeof field === "string" ? field : void 0;
|
|
183
|
+
} catch {
|
|
184
|
+
return void 0;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/adapters/npm/npm-discovery.ts
|
|
189
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
190
|
+
import { dirname as dirname3, join as join4, relative, resolve as resolve3 } from "path";
|
|
191
|
+
import mapWorkspaces from "@npmcli/map-workspaces";
|
|
192
|
+
|
|
193
|
+
// src/adapters/npm/pnpm-workspace.ts
|
|
194
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
195
|
+
import { join as join3 } from "path";
|
|
196
|
+
import { isMap, isScalar, parse, parseDocument } from "yaml";
|
|
197
|
+
var PNPM_WORKSPACE_FILE = "pnpm-workspace.yaml";
|
|
198
|
+
function isRecord(value) {
|
|
199
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
200
|
+
}
|
|
201
|
+
function entries(value) {
|
|
202
|
+
const map = /* @__PURE__ */ new Map();
|
|
203
|
+
if (isRecord(value)) {
|
|
204
|
+
for (const [name, range] of Object.entries(value)) {
|
|
205
|
+
if (typeof range === "string") map.set(name, range);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return map;
|
|
209
|
+
}
|
|
210
|
+
function readPnpmWorkspace(rootDir) {
|
|
211
|
+
const file = join3(rootDir, PNPM_WORKSPACE_FILE);
|
|
212
|
+
let data;
|
|
213
|
+
try {
|
|
214
|
+
data = parse(readFileSync2(file, "utf8"));
|
|
215
|
+
} catch {
|
|
216
|
+
return void 0;
|
|
217
|
+
}
|
|
218
|
+
if (data !== null && !isRecord(data)) return void 0;
|
|
219
|
+
const doc = data ?? {};
|
|
220
|
+
const packages = Array.isArray(doc.packages) ? doc.packages.filter((p) => typeof p === "string") : [];
|
|
221
|
+
const catalogs = /* @__PURE__ */ new Map();
|
|
222
|
+
const named = isRecord(doc.catalogs) ? doc.catalogs : {};
|
|
223
|
+
for (const [name, content] of Object.entries(named)) catalogs.set(name, entries(content));
|
|
224
|
+
const hasTopLevel = isRecord(doc.catalog);
|
|
225
|
+
const hasNamedDefault = isRecord(named.default);
|
|
226
|
+
if (hasTopLevel) catalogs.set("default", entries(doc.catalog));
|
|
227
|
+
return { file, packages, catalogs, ambiguousDefaultCatalog: hasTopLevel && hasNamedDefault };
|
|
228
|
+
}
|
|
229
|
+
function setCatalogRange(source, catalog, name, newRange) {
|
|
230
|
+
const doc = parseDocument(source);
|
|
231
|
+
const holders = catalog === "default" ? [doc.get("catalog", true), doc.getIn(["catalogs", "default"], true)] : [doc.getIn(["catalogs", catalog], true)];
|
|
232
|
+
for (const holder of holders) {
|
|
233
|
+
if (!isMap(holder)) continue;
|
|
234
|
+
const node = holder.get(name, true);
|
|
235
|
+
if (!isScalar(node) || !node.range) continue;
|
|
236
|
+
const scalar = node;
|
|
237
|
+
const [start, end] = scalar.range;
|
|
238
|
+
const quote = scalar.type === "QUOTE_DOUBLE" ? '"' : scalar.type === "QUOTE_SINGLE" ? "'" : "";
|
|
239
|
+
return source.slice(0, start) + quote + newRange + quote + source.slice(end);
|
|
240
|
+
}
|
|
241
|
+
throw new Error(`No entry for '${name}' in catalog '${catalog}' of ${PNPM_WORKSPACE_FILE}.`);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// src/adapters/npm/npm-discovery.ts
|
|
245
|
+
function readJson(filePath) {
|
|
246
|
+
try {
|
|
247
|
+
return JSON.parse(readFileSync3(filePath, "utf8"));
|
|
248
|
+
} catch {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
var NpmModuleDiscoveryAdapter = class {
|
|
253
|
+
/**
|
|
254
|
+
* The nearest directory, from `startDir` upwards to the boundary, whose
|
|
255
|
+
* package.json is valid JSON and declares `workspaces`, or that contains a
|
|
256
|
+
* valid `pnpm-workspace.yaml`.
|
|
257
|
+
*/
|
|
258
|
+
findRoot(startDir, boundaryDir) {
|
|
259
|
+
let dir = resolve3(startDir);
|
|
260
|
+
const boundary = boundaryDir ? resolve3(boundaryDir) : void 0;
|
|
261
|
+
for (; ; ) {
|
|
262
|
+
if (readJson(join4(dir, "package.json"))?.workspaces || readPnpmWorkspace(dir)) return dir;
|
|
263
|
+
const parent = dirname3(dir);
|
|
264
|
+
if (dir === boundary || parent === dir) return void 0;
|
|
265
|
+
dir = parent;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
async discover(rootDir) {
|
|
269
|
+
const rootPkg = readJson(join4(rootDir, "package.json"));
|
|
270
|
+
const modules = [
|
|
271
|
+
{
|
|
272
|
+
id: rootPkg?.name ?? "root",
|
|
273
|
+
name: rootPkg?.name ?? "Root Workspace (npm)",
|
|
274
|
+
path: rootDir,
|
|
275
|
+
relPath: "Root",
|
|
276
|
+
ecosystem: "npm",
|
|
277
|
+
isRoot: true,
|
|
278
|
+
version: typeof rootPkg?.version === "string" ? rootPkg.version : void 0
|
|
279
|
+
}
|
|
280
|
+
];
|
|
281
|
+
const patterns = detectPackageManager(rootDir).name === "pnpm" ? readPnpmWorkspace(rootDir)?.packages : rootPkg?.workspaces;
|
|
282
|
+
if (!patterns || Array.isArray(patterns) && patterns.length === 0) return modules;
|
|
283
|
+
let workspaces;
|
|
284
|
+
try {
|
|
285
|
+
workspaces = await mapWorkspaces({ cwd: rootDir, pkg: { ...rootPkg, workspaces: patterns } });
|
|
286
|
+
} catch {
|
|
287
|
+
return modules;
|
|
288
|
+
}
|
|
289
|
+
for (const [name, path] of workspaces) {
|
|
290
|
+
const pkg = readJson(join4(path, "package.json"));
|
|
291
|
+
modules.push({
|
|
292
|
+
id: name,
|
|
293
|
+
name,
|
|
294
|
+
path,
|
|
295
|
+
relPath: relative(rootDir, path).replace(/\\/g, "/"),
|
|
296
|
+
ecosystem: "npm",
|
|
297
|
+
isRoot: false,
|
|
298
|
+
version: typeof pkg?.version === "string" ? pkg.version : void 0
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
modules.sort((a, b) => a.isRoot ? -1 : b.isRoot ? 1 : a.relPath.localeCompare(b.relPath));
|
|
302
|
+
return modules;
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
// src/adapters/npm/npm-registry.ts
|
|
307
|
+
import semver from "semver";
|
|
308
|
+
|
|
151
309
|
// src/domain/origin.ts
|
|
152
310
|
function buildWorkspaceIndex(modules) {
|
|
153
311
|
const index = /* @__PURE__ */ new Map();
|
|
@@ -194,9 +352,24 @@ function npmScopeOf(name) {
|
|
|
194
352
|
return slash > 1 ? name.slice(0, slash) : void 0;
|
|
195
353
|
}
|
|
196
354
|
|
|
355
|
+
// src/adapters/npm/npm-commands.ts
|
|
356
|
+
function driverFor(dir) {
|
|
357
|
+
return detectPackageManager(dir).name === "pnpm" ? "pnpm" : "npm";
|
|
358
|
+
}
|
|
359
|
+
var commands = {
|
|
360
|
+
/** Install after Elevate wrote new ranges. pnpm is told explicitly not to freeze: `CI` would otherwise imply it. */
|
|
361
|
+
install: (m) => m === "pnpm" ? { command: "pnpm", args: ["install", "--no-frozen-lockfile"] } : { command: "npm", args: ["install"] },
|
|
362
|
+
/** Restore the installed state after a rollback. */
|
|
363
|
+
restore: (m) => m === "pnpm" ? { command: "pnpm", args: ["install", "--frozen-lockfile"] } : { command: "npm", args: ["install"] },
|
|
364
|
+
/** Default verification. */
|
|
365
|
+
verify: (m) => m === "pnpm" ? { command: "pnpm", args: ["install", "--frozen-lockfile", "--lockfile-only"] } : { command: "npm", args: ["ls"] },
|
|
366
|
+
view: (m, name, field) => ({ command: m, args: ["view", name, field, "--json"] }),
|
|
367
|
+
config: (m) => ({ command: m, args: ["config", "list", "--json"] })
|
|
368
|
+
};
|
|
369
|
+
|
|
197
370
|
// src/adapters/npm/npm-config.ts
|
|
198
371
|
var DEFAULT_REGISTRY = "https://registry.npmjs.org/";
|
|
199
|
-
var PUBLIC_NPM_HOSTS = /* @__PURE__ */ new Set(["registry.npmjs.org", "registry.npmjs.com", "registry.yarnpkg.com"]);
|
|
372
|
+
var PUBLIC_NPM_HOSTS = /* @__PURE__ */ new Set(["registry.npmjs.org", "registry.npmjs.com", "registry.yarnpkg.com", "npm.jsr.io"]);
|
|
200
373
|
var NpmConfigReader = class {
|
|
201
374
|
cache = /* @__PURE__ */ new Map();
|
|
202
375
|
/** Returns the registry configuration that applies in `cwd` (cached per directory). */
|
|
@@ -233,7 +406,8 @@ function parseNpmConfig(json) {
|
|
|
233
406
|
}
|
|
234
407
|
async function loadConfig(cwd) {
|
|
235
408
|
try {
|
|
236
|
-
const
|
|
409
|
+
const { command, args } = commands.config(driverFor(cwd));
|
|
410
|
+
const result = await runCommand(command, args, { cwd, timeoutMs: 15e3 });
|
|
237
411
|
if (result.exitCode !== 0) return parseNpmConfig({});
|
|
238
412
|
return parseNpmConfig(JSON.parse(result.stdout));
|
|
239
413
|
} catch {
|
|
@@ -277,10 +451,11 @@ var NpmRegistryAdapter = class {
|
|
|
277
451
|
const list = Array.isArray(versions) ? versions : versions ? [versions] : [];
|
|
278
452
|
return list.map(String).reverse();
|
|
279
453
|
}
|
|
280
|
-
/** Runs `npm view <name> <field> --json
|
|
454
|
+
/** Runs `npm view <name> <field> --json` (`pnpm view` in a pnpm repository); resolves null on any failure. */
|
|
281
455
|
async view(name, field, cwd) {
|
|
282
456
|
try {
|
|
283
|
-
const
|
|
457
|
+
const { command, args } = commands.view(driverFor(cwd), name, field);
|
|
458
|
+
const result = await runCommand(command, args, {
|
|
284
459
|
cwd,
|
|
285
460
|
timeoutMs: LOOKUP_TIMEOUT_MS
|
|
286
461
|
});
|
|
@@ -293,8 +468,8 @@ var NpmRegistryAdapter = class {
|
|
|
293
468
|
};
|
|
294
469
|
|
|
295
470
|
// src/adapters/npm/npm-scanner.ts
|
|
296
|
-
import { readFileSync as
|
|
297
|
-
import { join as
|
|
471
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
472
|
+
import { join as join5 } from "path";
|
|
298
473
|
import semver3 from "semver";
|
|
299
474
|
|
|
300
475
|
// src/domain/versions.ts
|
|
@@ -347,71 +522,11 @@ async function mapPooled(items, concurrency, fn) {
|
|
|
347
522
|
return results;
|
|
348
523
|
}
|
|
349
524
|
|
|
350
|
-
// src/adapters/npm/npm-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
import { existsSync } from "fs";
|
|
356
|
-
import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
|
|
357
|
-
function findCheckoutBoundary(startDir) {
|
|
358
|
-
let dir = resolve2(startDir);
|
|
359
|
-
for (; ; ) {
|
|
360
|
-
if (existsSync(join2(dir, ".git"))) return dir;
|
|
361
|
-
const parent = dirname2(dir);
|
|
362
|
-
if (parent === dir) return void 0;
|
|
363
|
-
dir = parent;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// src/adapters/npm/npm-package-manager.ts
|
|
368
|
-
var MARKER_FILES = [
|
|
369
|
-
["npm", ["package-lock.json", "npm-shrinkwrap.json"]],
|
|
370
|
-
["pnpm", ["pnpm-lock.yaml", "pnpm-workspace.yaml"]],
|
|
371
|
-
["yarn", ["yarn.lock", ".yarnrc.yml"]],
|
|
372
|
-
["bun", ["bun.lock", "bun.lockb"]]
|
|
373
|
-
];
|
|
374
|
-
var UnsupportedPackageManagerError = class extends Error {
|
|
375
|
-
constructor(detected) {
|
|
376
|
-
super(
|
|
377
|
-
`This repository is managed by ${detected.name} (${detected.evidence}). Elevate supports npm for Node.js projects; using it here would create an npm lockfile next to ${detected.name}'s. Nothing was changed.`
|
|
378
|
-
);
|
|
379
|
-
this.name = "UnsupportedPackageManagerError";
|
|
380
|
-
}
|
|
381
|
-
};
|
|
382
|
-
function detectPackageManager(dir) {
|
|
383
|
-
const boundary = findCheckoutBoundary(dir);
|
|
384
|
-
let current = resolve3(dir);
|
|
385
|
-
for (; ; ) {
|
|
386
|
-
const found = detectIn(current);
|
|
387
|
-
if (found) return found;
|
|
388
|
-
const parent = dirname3(current);
|
|
389
|
-
if (boundary === void 0 || current === boundary || parent === current) break;
|
|
390
|
-
current = parent;
|
|
391
|
-
}
|
|
392
|
-
return { name: "npm", evidence: "no other package manager detected" };
|
|
393
|
-
}
|
|
394
|
-
function detectIn(dir) {
|
|
395
|
-
const declared = readPackageManagerField(dir);
|
|
396
|
-
const name = declared?.match(/^(npm|pnpm|yarn|bun)@/)?.[1];
|
|
397
|
-
if (declared && name) return { name, evidence: `"packageManager": "${declared}" in package.json` };
|
|
398
|
-
for (const [manager, files] of MARKER_FILES) {
|
|
399
|
-
const found = files.find((file) => existsSync2(join3(dir, file)));
|
|
400
|
-
if (found) return { name: manager, evidence: found };
|
|
401
|
-
}
|
|
402
|
-
return void 0;
|
|
403
|
-
}
|
|
404
|
-
function assertNpmManaged(rootDir) {
|
|
405
|
-
const detected = detectPackageManager(rootDir);
|
|
406
|
-
if (detected.name !== "npm") throw new UnsupportedPackageManagerError(detected);
|
|
407
|
-
}
|
|
408
|
-
function readPackageManagerField(rootDir) {
|
|
409
|
-
try {
|
|
410
|
-
const field = JSON.parse(readFileSync2(join3(rootDir, "package.json"), "utf8"))?.packageManager;
|
|
411
|
-
return typeof field === "string" ? field : void 0;
|
|
412
|
-
} catch {
|
|
413
|
-
return void 0;
|
|
414
|
-
}
|
|
525
|
+
// src/adapters/npm/npm-protocols.ts
|
|
526
|
+
function splitRangeProtocol(range) {
|
|
527
|
+
if (range.startsWith("workspace:")) return { protocol: "workspace", rest: range.slice("workspace:".length) };
|
|
528
|
+
if (range.startsWith("catalog:")) return { protocol: "catalog", rest: range.slice("catalog:".length) };
|
|
529
|
+
return { protocol: void 0, rest: range };
|
|
415
530
|
}
|
|
416
531
|
|
|
417
532
|
// src/adapters/npm/npm-scanner.ts
|
|
@@ -425,10 +540,11 @@ var NpmDependencyAdapter = class {
|
|
|
425
540
|
registry;
|
|
426
541
|
config;
|
|
427
542
|
async scan(module, context) {
|
|
428
|
-
|
|
543
|
+
const manager = await prepareSupportedPackageManager(context.rootDir);
|
|
544
|
+
const pnpm = manager === "pnpm";
|
|
429
545
|
let pkg;
|
|
430
546
|
try {
|
|
431
|
-
pkg = JSON.parse(
|
|
547
|
+
pkg = JSON.parse(readFileSync4(join5(module.path, "package.json"), "utf8"));
|
|
432
548
|
} catch {
|
|
433
549
|
return { candidates: [], skipped: [] };
|
|
434
550
|
}
|
|
@@ -442,13 +558,20 @@ var NpmDependencyAdapter = class {
|
|
|
442
558
|
[...npmConfig.scopedRegistries].filter(([, url]) => !isPublicNpmRegistry(url))
|
|
443
559
|
)
|
|
444
560
|
};
|
|
561
|
+
const workspaceFile = pnpm ? readPnpmWorkspace(context.rootDir) : void 0;
|
|
445
562
|
const outcomes = await mapPooled(declaredDependencies(pkg), CONCURRENCY, async (dep) => {
|
|
446
563
|
const origin = classifyOrigin(dep.name, "npm", originContext);
|
|
447
564
|
if (origin.kind === "workspace") {
|
|
448
565
|
const target = context.workspace.get(dep.name);
|
|
449
|
-
|
|
566
|
+
if (pnpm && isUpgradableRange(dep.range) && splitRangeProtocol(dep.range).protocol === void 0) {
|
|
567
|
+
return { skipped: { identifier: dep.name, origin: "workspace", reason: "not-linked", detail: dep.range } };
|
|
568
|
+
}
|
|
569
|
+
const candidate = target ? alignmentCandidate(dep, target, origin, pnpm) : null;
|
|
450
570
|
return candidate ? { candidate } : null;
|
|
451
571
|
}
|
|
572
|
+
if (pnpm && splitRangeProtocol(dep.range).protocol === "catalog") {
|
|
573
|
+
return this.catalogOutcome(dep, origin, context, module.path, workspaceFile);
|
|
574
|
+
}
|
|
452
575
|
return this.registryOutcome(dep, origin, context);
|
|
453
576
|
});
|
|
454
577
|
const candidates = [];
|
|
@@ -461,8 +584,34 @@ var NpmDependencyAdapter = class {
|
|
|
461
584
|
candidates.sort((a, b) => (DIFF_ORDER[a.diff] ?? 3) - (DIFF_ORDER[b.diff] ?? 3));
|
|
462
585
|
return { candidates, skipped };
|
|
463
586
|
}
|
|
587
|
+
/** A `catalog:` dependency: the range comes from pnpm-workspace.yaml and is written back there. */
|
|
588
|
+
async catalogOutcome(dep, origin, context, modulePath, workspaceFile) {
|
|
589
|
+
const catalogName = splitRangeProtocol(dep.range).rest || "default";
|
|
590
|
+
const unresolved = (detail) => ({
|
|
591
|
+
skipped: { identifier: dep.name, origin: origin.kind, reason: "catalog-unresolved", detail }
|
|
592
|
+
});
|
|
593
|
+
if (!workspaceFile) return unresolved("pnpm-workspace.yaml is missing or not valid YAML");
|
|
594
|
+
if (catalogName === "default" && workspaceFile.ambiguousDefaultCatalog) {
|
|
595
|
+
return unresolved("catalog 'default' is defined twice (catalog and catalogs.default)");
|
|
596
|
+
}
|
|
597
|
+
const catalog = workspaceFile.catalogs.get(catalogName);
|
|
598
|
+
if (!catalog) return unresolved(`no catalog '${catalogName}'`);
|
|
599
|
+
const range = catalog.get(dep.name);
|
|
600
|
+
if (range === void 0) return unresolved(`no entry for '${dep.name}' in catalog '${catalogName}'`);
|
|
601
|
+
const declaration = {
|
|
602
|
+
file: workspaceFile.file,
|
|
603
|
+
kind: "catalog",
|
|
604
|
+
catalogName,
|
|
605
|
+
displayPath: "pnpm-workspace.yaml"
|
|
606
|
+
};
|
|
607
|
+
const sharedWith = usersOfCatalogEntry(context, modulePath, dep.name, catalogName);
|
|
608
|
+
return this.registryOutcome({ ...dep, range }, origin, context, {
|
|
609
|
+
declaration,
|
|
610
|
+
...sharedWith.length > 0 ? { sharedWith } : {}
|
|
611
|
+
});
|
|
612
|
+
}
|
|
464
613
|
/** Looks up a private or public dependency in its registry. */
|
|
465
|
-
async registryOutcome(dep, origin, context) {
|
|
614
|
+
async registryOutcome(dep, origin, context, extra = {}) {
|
|
466
615
|
if (!isUpgradableRange(dep.range)) return null;
|
|
467
616
|
if (!isValidPackageName(dep.name)) {
|
|
468
617
|
return { skipped: { identifier: dep.name, origin: origin.kind, reason: "invalid-name" } };
|
|
@@ -504,7 +653,8 @@ var NpmDependencyAdapter = class {
|
|
|
504
653
|
origin: resolvedOrigin,
|
|
505
654
|
action: "update",
|
|
506
655
|
isPreRelease: tag !== void 0,
|
|
507
|
-
preReleaseTag: tag
|
|
656
|
+
preReleaseTag: tag,
|
|
657
|
+
...extra
|
|
508
658
|
}
|
|
509
659
|
};
|
|
510
660
|
}
|
|
@@ -525,11 +675,14 @@ function isUpgradableRange(range) {
|
|
|
525
675
|
if (trimmed === "" || trimmed === "*" || trimmed.toLowerCase() === "x") return false;
|
|
526
676
|
return semver3.validRange(trimmed) !== null;
|
|
527
677
|
}
|
|
528
|
-
function alignmentCandidate(dep, target, origin) {
|
|
678
|
+
function alignmentCandidate(dep, target, origin, pnpm = false) {
|
|
679
|
+
const split = pnpm ? splitRangeProtocol(dep.range) : { protocol: void 0, rest: dep.range };
|
|
680
|
+
const range = split.protocol === "workspace" ? split.rest : dep.range;
|
|
681
|
+
const protocol = split.protocol === "workspace" ? "workspace:" : "";
|
|
529
682
|
const local = target.version;
|
|
530
|
-
if (!local || !semver3.valid(local) || !isUpgradableRange(
|
|
531
|
-
if (semver3.satisfies(local,
|
|
532
|
-
const currentClean = semver3.minVersion(
|
|
683
|
+
if (!local || !semver3.valid(local) || !isUpgradableRange(range)) return null;
|
|
684
|
+
if (semver3.satisfies(local, range, { includePrerelease: true })) return null;
|
|
685
|
+
const currentClean = semver3.minVersion(range)?.version ?? "0.0.0";
|
|
533
686
|
const diff = diffVersions(currentClean, local);
|
|
534
687
|
const tag = semver3.prerelease(local) ? extractPreReleaseTag(local) ?? "PRE" : void 0;
|
|
535
688
|
return {
|
|
@@ -537,7 +690,7 @@ function alignmentCandidate(dep, target, origin) {
|
|
|
537
690
|
currentRange: dep.range,
|
|
538
691
|
currentClean,
|
|
539
692
|
latest: local,
|
|
540
|
-
newRange: `${rangePrefix(
|
|
693
|
+
newRange: `${protocol}${rangePrefix(range)}${local}`,
|
|
541
694
|
diff,
|
|
542
695
|
scope: dep.scope,
|
|
543
696
|
selected: diff !== "major",
|
|
@@ -548,10 +701,27 @@ function alignmentCandidate(dep, target, origin) {
|
|
|
548
701
|
preReleaseTag: tag
|
|
549
702
|
};
|
|
550
703
|
}
|
|
704
|
+
function usersOfCatalogEntry(context, selfPath, name, catalogName) {
|
|
705
|
+
const users = [];
|
|
706
|
+
for (const module of context.workspace.values()) {
|
|
707
|
+
if (module.path === selfPath) continue;
|
|
708
|
+
let pkg;
|
|
709
|
+
try {
|
|
710
|
+
pkg = JSON.parse(readFileSync4(join5(module.path, "package.json"), "utf8"));
|
|
711
|
+
} catch {
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
const dep = declaredDependencies(pkg).find((d) => d.name === name);
|
|
715
|
+
if (!dep || splitRangeProtocol(dep.range).protocol !== "catalog") continue;
|
|
716
|
+
if ((splitRangeProtocol(dep.range).rest || "default") !== catalogName) continue;
|
|
717
|
+
users.push(module.id);
|
|
718
|
+
}
|
|
719
|
+
return users;
|
|
720
|
+
}
|
|
551
721
|
|
|
552
722
|
// src/adapters/npm/npm-updater.ts
|
|
553
|
-
import { readFileSync as
|
|
554
|
-
import { join as
|
|
723
|
+
import { readFileSync as readFileSync7, writeFileSync } from "fs";
|
|
724
|
+
import { join as join8, relative as relative2 } from "path";
|
|
555
725
|
|
|
556
726
|
// src/domain/ports.ts
|
|
557
727
|
var InstallError = class extends Error {
|
|
@@ -564,13 +734,13 @@ var InstallError = class extends Error {
|
|
|
564
734
|
};
|
|
565
735
|
|
|
566
736
|
// src/adapters/npm/npm-lockfile.ts
|
|
567
|
-
import { existsSync as existsSync3, readFileSync as
|
|
568
|
-
import { join as
|
|
737
|
+
import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
|
|
738
|
+
import { join as join6 } from "path";
|
|
569
739
|
function readLockfile(rootDir) {
|
|
570
|
-
const file =
|
|
740
|
+
const file = join6(rootDir, "package-lock.json");
|
|
571
741
|
if (!existsSync3(file)) return void 0;
|
|
572
742
|
try {
|
|
573
|
-
return JSON.parse(
|
|
743
|
+
return JSON.parse(readFileSync5(file, "utf8"));
|
|
574
744
|
} catch {
|
|
575
745
|
return void 0;
|
|
576
746
|
}
|
|
@@ -591,51 +761,168 @@ function findUnlinkedWorkspaceDependencies(lockfile, moduleRelPath, names) {
|
|
|
591
761
|
return violations;
|
|
592
762
|
}
|
|
593
763
|
|
|
764
|
+
// src/adapters/npm/pnpm-audit.ts
|
|
765
|
+
var AUDIT_TIMEOUT_MS = 6e4;
|
|
766
|
+
var LEVELS = ["info", "low", "moderate", "high", "critical"];
|
|
767
|
+
function unavailable(reason) {
|
|
768
|
+
return { message: `Vulnerability check unavailable: ${reason}.`, severity: "warn" };
|
|
769
|
+
}
|
|
770
|
+
function parsePnpmAudit(output) {
|
|
771
|
+
let json;
|
|
772
|
+
try {
|
|
773
|
+
json = JSON.parse(output);
|
|
774
|
+
} catch {
|
|
775
|
+
return unavailable("pnpm audit returned no result");
|
|
776
|
+
}
|
|
777
|
+
const counts = json?.metadata?.vulnerabilities;
|
|
778
|
+
if (counts && typeof counts === "object") {
|
|
779
|
+
const parts = [];
|
|
780
|
+
let total = 0;
|
|
781
|
+
for (const level of LEVELS) {
|
|
782
|
+
const n = Number(counts[level]) || 0;
|
|
783
|
+
total += n;
|
|
784
|
+
if (n > 0) parts.push(`${n} ${level}`);
|
|
785
|
+
}
|
|
786
|
+
if (total === 0) return { message: "No known vulnerabilities found.", severity: "clean" };
|
|
787
|
+
return {
|
|
788
|
+
message: `${total} ${total === 1 ? "vulnerability" : "vulnerabilities"} (${parts.join(", ")})`,
|
|
789
|
+
severity: "warn"
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
const errorMessage = json?.error?.message;
|
|
793
|
+
if (typeof errorMessage === "string" && errorMessage) return unavailable(errorMessage);
|
|
794
|
+
return unavailable("pnpm audit returned no result");
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// src/adapters/npm/pnpm-lockfile.ts
|
|
798
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
|
|
799
|
+
import { join as join7 } from "path";
|
|
800
|
+
import { parse as parse2 } from "yaml";
|
|
801
|
+
var SECTIONS = ["dependencies", "devDependencies", "optionalDependencies"];
|
|
802
|
+
function readPnpmLockfile(rootDir) {
|
|
803
|
+
const file = join7(rootDir, "pnpm-lock.yaml");
|
|
804
|
+
if (!existsSync4(file)) return { kind: "missing" };
|
|
805
|
+
let data;
|
|
806
|
+
try {
|
|
807
|
+
data = parse2(readFileSync6(file, "utf8"));
|
|
808
|
+
} catch {
|
|
809
|
+
return { kind: "unreadable" };
|
|
810
|
+
}
|
|
811
|
+
if (typeof data !== "object" || data === null || Array.isArray(data)) return { kind: "unreadable" };
|
|
812
|
+
const lockfileVersion = String(data.lockfileVersion ?? "");
|
|
813
|
+
if (!/^9(\.|$)/.test(lockfileVersion)) return { kind: "unsupported", lockfileVersion };
|
|
814
|
+
const importers = data.importers ?? {};
|
|
815
|
+
if (typeof importers !== "object" || Array.isArray(importers)) return { kind: "unreadable" };
|
|
816
|
+
return { kind: "ok", importers };
|
|
817
|
+
}
|
|
818
|
+
function findUnlinkedPnpmDependencies(lockfile, moduleRelPath, names) {
|
|
819
|
+
switch (lockfile.kind) {
|
|
820
|
+
case "missing":
|
|
821
|
+
return ["pnpm-lock.yaml is missing; workspace links cannot be verified"];
|
|
822
|
+
case "unreadable":
|
|
823
|
+
return ["pnpm-lock.yaml could not be read; workspace links cannot be verified"];
|
|
824
|
+
case "unsupported":
|
|
825
|
+
return [
|
|
826
|
+
`pnpm-lock.yaml has lockfileVersion '${lockfile.lockfileVersion}', which Elevate cannot read; workspace links cannot be verified`
|
|
827
|
+
];
|
|
828
|
+
}
|
|
829
|
+
const importer = lockfile.importers[moduleRelPath === "" ? "." : moduleRelPath] ?? {};
|
|
830
|
+
const violations = [];
|
|
831
|
+
for (const name of names) {
|
|
832
|
+
let entry;
|
|
833
|
+
for (const section of SECTIONS) {
|
|
834
|
+
entry = importer[section]?.[name];
|
|
835
|
+
if (entry) break;
|
|
836
|
+
}
|
|
837
|
+
if (!entry) violations.push(`${name}: not present in pnpm-lock.yaml`);
|
|
838
|
+
else if (!String(entry.version).startsWith("link:")) {
|
|
839
|
+
violations.push(`${name}: resolved to ${entry.version} instead of linking the workspace package`);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
return violations;
|
|
843
|
+
}
|
|
844
|
+
|
|
594
845
|
// src/adapters/npm/npm-updater.ts
|
|
595
846
|
var INSTALL_TIMEOUT_MS = 10 * 6e4;
|
|
847
|
+
function failureDetails(manager, result) {
|
|
848
|
+
return manager === "pnpm" ? tail(`${result.stdout}
|
|
849
|
+
${result.stderr}`) : tail(result.stderr || result.stdout);
|
|
850
|
+
}
|
|
596
851
|
var NpmUpdaterAdapter = class {
|
|
597
852
|
affectedFiles(module, rootDir) {
|
|
853
|
+
if (detectPackageManager(rootDir).name === "pnpm") {
|
|
854
|
+
return [
|
|
855
|
+
join8(module.path, "package.json"),
|
|
856
|
+
join8(rootDir, "package.json"),
|
|
857
|
+
join8(rootDir, "pnpm-lock.yaml"),
|
|
858
|
+
join8(rootDir, "pnpm-workspace.yaml")
|
|
859
|
+
];
|
|
860
|
+
}
|
|
598
861
|
return [
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
862
|
+
join8(module.path, "package.json"),
|
|
863
|
+
join8(rootDir, "package.json"),
|
|
864
|
+
join8(rootDir, "package-lock.json"),
|
|
865
|
+
join8(rootDir, "npm-shrinkwrap.json")
|
|
603
866
|
];
|
|
604
867
|
}
|
|
605
868
|
async applyUpdates(module, rootDir, updates, onProgress) {
|
|
869
|
+
let manager;
|
|
606
870
|
try {
|
|
607
|
-
|
|
871
|
+
manager = await prepareSupportedPackageManager(rootDir);
|
|
608
872
|
} catch (err) {
|
|
609
|
-
if (err instanceof UnsupportedPackageManagerError
|
|
873
|
+
if (err instanceof UnsupportedPackageManagerError || err instanceof PackageManagerNotFoundError) {
|
|
874
|
+
throw new InstallError(err.message);
|
|
875
|
+
}
|
|
610
876
|
throw err;
|
|
611
877
|
}
|
|
612
|
-
const
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
878
|
+
const catalogUpdates = updates.filter((u) => u.declaration?.kind === "catalog");
|
|
879
|
+
const manifestUpdates = updates.filter((u) => u.declaration?.kind !== "catalog");
|
|
880
|
+
const pkgPath = join8(module.path, "package.json");
|
|
881
|
+
let manifest;
|
|
882
|
+
if (manifestUpdates.length > 0 || catalogUpdates.length === 0) {
|
|
883
|
+
try {
|
|
884
|
+
const source = readFileSync7(pkgPath, "utf8");
|
|
885
|
+
manifest = { source, pkg: JSON.parse(source) };
|
|
886
|
+
} catch {
|
|
887
|
+
throw new InstallError(`Cannot read package.json in ${module.path}.`);
|
|
888
|
+
}
|
|
620
889
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
890
|
+
if (manifest) {
|
|
891
|
+
onProgress("Writing new versions to package.json\u2026");
|
|
892
|
+
for (const item of manifestUpdates) {
|
|
893
|
+
const name = item.coordinate.artifact;
|
|
894
|
+
for (const section of DEPENDENCY_SECTIONS) {
|
|
895
|
+
if (typeof manifest.pkg[section]?.[name] === "string") manifest.pkg[section][name] = item.newRange;
|
|
896
|
+
}
|
|
626
897
|
}
|
|
898
|
+
writeFileSync(pkgPath, serializeLike(manifest.source, manifest.pkg), "utf8");
|
|
899
|
+
}
|
|
900
|
+
if (catalogUpdates.length > 0) {
|
|
901
|
+
onProgress("Writing new versions to pnpm-workspace.yaml\u2026");
|
|
902
|
+
writeCatalogRanges(catalogUpdates);
|
|
627
903
|
}
|
|
628
|
-
|
|
629
|
-
onProgress(
|
|
630
|
-
const
|
|
631
|
-
if (
|
|
632
|
-
const reason =
|
|
633
|
-
throw new InstallError(reason,
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
const
|
|
638
|
-
|
|
904
|
+
const install = commands.install(manager);
|
|
905
|
+
onProgress(`Running ${manager} install\u2026`);
|
|
906
|
+
const result = await runCommand(install.command, install.args, { cwd: rootDir, timeoutMs: INSTALL_TIMEOUT_MS });
|
|
907
|
+
if (result.exitCode !== 0) {
|
|
908
|
+
const reason = result.timedOut ? `${manager} install timed out` : `${manager} install failed`;
|
|
909
|
+
throw new InstallError(reason, failureDetails(manager, result));
|
|
910
|
+
}
|
|
911
|
+
const lockName = manager === "pnpm" ? "pnpm-lock.yaml" : "package-lock.json";
|
|
912
|
+
onProgress(`Checking workspace links in ${lockName}\u2026`);
|
|
913
|
+
const integrityViolations = verifyAlignedLinks(manager, module, rootDir, updates);
|
|
914
|
+
if (manager === "pnpm") {
|
|
915
|
+
const audit2 = integrityViolations.length > 0 ? void 0 : await runPnpmAudit(rootDir);
|
|
916
|
+
return {
|
|
917
|
+
updatedCount: updates.length,
|
|
918
|
+
auditMessage: audit2?.message ?? "",
|
|
919
|
+
auditSeverity: audit2?.severity ?? "clean",
|
|
920
|
+
fundingMessage: void 0,
|
|
921
|
+
integrityViolations
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
const combined = `${result.stdout}
|
|
925
|
+
${result.stderr}`;
|
|
639
926
|
const audit = parseAuditSummary(combined);
|
|
640
927
|
const fundingMatch = combined.match(/(\d+ packages? (?:is|are) looking for funding)/i);
|
|
641
928
|
return {
|
|
@@ -647,14 +934,42 @@ ${install.stderr}`;
|
|
|
647
934
|
};
|
|
648
935
|
}
|
|
649
936
|
async resync(_module, rootDir, onProgress) {
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
const
|
|
937
|
+
const name = detectPackageManager(rootDir).name;
|
|
938
|
+
if (name !== "npm" && name !== "pnpm") return;
|
|
939
|
+
const restore = commands.restore(name);
|
|
940
|
+
onProgress(`Restoring node_modules (${name} install)\u2026`);
|
|
941
|
+
const result = await runCommand(restore.command, restore.args, { cwd: rootDir, timeoutMs: INSTALL_TIMEOUT_MS });
|
|
653
942
|
if (result.exitCode !== 0) {
|
|
654
|
-
throw new InstallError(
|
|
943
|
+
throw new InstallError(
|
|
944
|
+
`${name} install failed while restoring the previous state`,
|
|
945
|
+
name === "pnpm" ? failureDetails(name, result) : tail(result.stderr)
|
|
946
|
+
);
|
|
655
947
|
}
|
|
656
948
|
}
|
|
657
949
|
};
|
|
950
|
+
function writeCatalogRanges(updates) {
|
|
951
|
+
const sources = /* @__PURE__ */ new Map();
|
|
952
|
+
for (const item of updates) {
|
|
953
|
+
const declaration = item.declaration;
|
|
954
|
+
try {
|
|
955
|
+
let source = sources.get(declaration.file) ?? readFileSync7(declaration.file, "utf8");
|
|
956
|
+
source = setCatalogRange(source, declaration.catalogName ?? "default", item.coordinate.artifact, item.newRange);
|
|
957
|
+
sources.set(declaration.file, source);
|
|
958
|
+
} catch (err) {
|
|
959
|
+
throw new InstallError(err instanceof Error ? err.message : `Cannot update ${declaration.displayPath}.`);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
for (const [file, source] of sources) writeFileSync(file, source, "utf8");
|
|
963
|
+
}
|
|
964
|
+
async function runPnpmAudit(rootDir) {
|
|
965
|
+
try {
|
|
966
|
+
const result = await runCommand("pnpm", ["audit", "--json"], { cwd: rootDir, timeoutMs: AUDIT_TIMEOUT_MS });
|
|
967
|
+
if (result.timedOut) return unavailable(`pnpm audit timed out after ${AUDIT_TIMEOUT_MS / 1e3} seconds`);
|
|
968
|
+
return parsePnpmAudit(result.stdout);
|
|
969
|
+
} catch {
|
|
970
|
+
return unavailable("pnpm audit returned no result");
|
|
971
|
+
}
|
|
972
|
+
}
|
|
658
973
|
var VULNERABILITY_LINE = /^(?:found\s+)?(\d+)\s+(?:(?:info|low|moderate|high|critical)\s+severity\s+)?vulnerabilit(?:y|ies)\b.*$/i;
|
|
659
974
|
function parseAuditSummary(output) {
|
|
660
975
|
for (const line of output.split(/\r?\n/)) {
|
|
@@ -663,12 +978,13 @@ function parseAuditSummary(output) {
|
|
|
663
978
|
}
|
|
664
979
|
return { message: "No known vulnerabilities found.", severity: "clean" };
|
|
665
980
|
}
|
|
666
|
-
function verifyAlignedLinks(module, rootDir, updates) {
|
|
981
|
+
function verifyAlignedLinks(manager, module, rootDir, updates) {
|
|
667
982
|
const aligned = updates.filter((u) => u.origin.kind === "workspace").map((u) => u.coordinate.identifier);
|
|
668
983
|
if (aligned.length === 0) return [];
|
|
984
|
+
const moduleRelPath = relative2(rootDir, module.path).replace(/\\/g, "/");
|
|
985
|
+
if (manager === "pnpm") return findUnlinkedPnpmDependencies(readPnpmLockfile(rootDir), moduleRelPath, aligned);
|
|
669
986
|
const lockfile = readLockfile(rootDir);
|
|
670
987
|
if (!lockfile) return ["package-lock.json is missing; workspace links cannot be verified"];
|
|
671
|
-
const moduleRelPath = relative2(rootDir, module.path).replace(/\\/g, "/");
|
|
672
988
|
return findUnlinkedWorkspaceDependencies(lockfile, moduleRelPath, aligned);
|
|
673
989
|
}
|
|
674
990
|
function serializeLike(original, value) {
|
|
@@ -682,24 +998,28 @@ function serializeLike(original, value) {
|
|
|
682
998
|
var VERIFY_TIMEOUT_MS = 10 * 6e4;
|
|
683
999
|
var NpmVerificationAdapter = class {
|
|
684
1000
|
async verify(_module, rootDir, options = {}) {
|
|
685
|
-
|
|
1001
|
+
let manager = "npm";
|
|
686
1002
|
if (!options.customScript) {
|
|
687
1003
|
try {
|
|
688
|
-
|
|
1004
|
+
manager = await prepareSupportedPackageManager(rootDir);
|
|
689
1005
|
} catch (err) {
|
|
690
|
-
if (err instanceof UnsupportedPackageManagerError
|
|
1006
|
+
if (err instanceof UnsupportedPackageManagerError || err instanceof PackageManagerNotFoundError) {
|
|
1007
|
+
return { status: "warn", details: err.message, label: options.customLabel || "Dependency verification" };
|
|
1008
|
+
}
|
|
691
1009
|
throw err;
|
|
692
1010
|
}
|
|
693
1011
|
}
|
|
694
|
-
const
|
|
1012
|
+
const defaultLabel = manager === "pnpm" ? "Lockfile consistency (pnpm install --frozen-lockfile)" : "Dependency tree integrity (npm ls)";
|
|
1013
|
+
const label = options.customLabel || options.customScript || defaultLabel;
|
|
1014
|
+
const verify = commands.verify(manager);
|
|
1015
|
+
const result = options.customScript ? await runShellScript(options.customScript, { cwd: rootDir, timeoutMs: VERIFY_TIMEOUT_MS }) : await runCommand(verify.command, verify.args, { cwd: rootDir, timeoutMs: VERIFY_TIMEOUT_MS });
|
|
695
1016
|
if (result.exitCode === 0) {
|
|
696
|
-
|
|
1017
|
+
const details = manager === "pnpm" && !options.customScript ? "pnpm-lock.yaml is up to date with all package.json files." : "All dependencies are installed and consistent.";
|
|
1018
|
+
return { status: "clean", details, label };
|
|
697
1019
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
label
|
|
702
|
-
};
|
|
1020
|
+
const output = manager === "pnpm" && !options.customScript ? tail(`${result.stdout}
|
|
1021
|
+
${result.stderr}`) : tail(result.stderr || result.stdout);
|
|
1022
|
+
return { status: "warn", details: result.timedOut ? "Verification timed out." : output, label };
|
|
703
1023
|
}
|
|
704
1024
|
};
|
|
705
1025
|
|
|
@@ -707,6 +1027,12 @@ var NpmVerificationAdapter = class {
|
|
|
707
1027
|
var NpmEcosystemStrategy = class {
|
|
708
1028
|
ecosystem = "npm";
|
|
709
1029
|
displayName = "Node / npm";
|
|
1030
|
+
packageManagerFor(rootDir) {
|
|
1031
|
+
return detectPackageManager(rootDir).name;
|
|
1032
|
+
}
|
|
1033
|
+
displayNameFor(rootDir) {
|
|
1034
|
+
return detectPackageManager(rootDir).name === "pnpm" ? "Node / pnpm" : "Node / npm";
|
|
1035
|
+
}
|
|
710
1036
|
icon = "\u{1F4E6}";
|
|
711
1037
|
manifestFile = "package.json";
|
|
712
1038
|
discovery = new NpmModuleDiscoveryAdapter();
|
|
@@ -724,16 +1050,16 @@ var NpmEcosystemStrategy = class {
|
|
|
724
1050
|
};
|
|
725
1051
|
|
|
726
1052
|
// src/adapters/maven/maven-discovery.ts
|
|
727
|
-
import { existsSync as
|
|
728
|
-
import { dirname as dirname5, join as
|
|
1053
|
+
import { existsSync as existsSync7 } from "fs";
|
|
1054
|
+
import { dirname as dirname5, join as join11, relative as relative3, resolve as resolve6 } from "path";
|
|
729
1055
|
|
|
730
1056
|
// src/adapters/maven/maven-project.ts
|
|
731
|
-
import { existsSync as
|
|
732
|
-
import { join as
|
|
1057
|
+
import { existsSync as existsSync6, readdirSync, statSync as statSync2 } from "fs";
|
|
1058
|
+
import { join as join10, resolve as resolve5 } from "path";
|
|
733
1059
|
|
|
734
1060
|
// src/adapters/maven/maven-pom.ts
|
|
735
|
-
import { existsSync as
|
|
736
|
-
import { dirname as dirname4, join as
|
|
1061
|
+
import { existsSync as existsSync5, readFileSync as readFileSync8, statSync } from "fs";
|
|
1062
|
+
import { dirname as dirname4, join as join9, resolve as resolve4 } from "path";
|
|
737
1063
|
|
|
738
1064
|
// src/adapters/shared/xml.ts
|
|
739
1065
|
import { SaxesParser } from "saxes";
|
|
@@ -868,7 +1194,7 @@ function localName(qualified) {
|
|
|
868
1194
|
|
|
869
1195
|
// src/adapters/maven/maven-pom.ts
|
|
870
1196
|
function readPom(file) {
|
|
871
|
-
const source =
|
|
1197
|
+
const source = readFileSync8(file, "utf8");
|
|
872
1198
|
return parsePom(source, file);
|
|
873
1199
|
}
|
|
874
1200
|
function parsePom(source, file) {
|
|
@@ -917,8 +1243,8 @@ function parentPomFile(pom) {
|
|
|
917
1243
|
const relativePath = pom.parent.relativePath ?? "../pom.xml";
|
|
918
1244
|
if (relativePath === "") return void 0;
|
|
919
1245
|
let candidate = resolve4(pom.dir, relativePath);
|
|
920
|
-
if (
|
|
921
|
-
return
|
|
1246
|
+
if (existsSync5(candidate) && statSync(candidate).isDirectory()) candidate = join9(candidate, "pom.xml");
|
|
1247
|
+
return existsSync5(candidate) ? candidate : void 0;
|
|
922
1248
|
}
|
|
923
1249
|
function builtinProperty(name, module) {
|
|
924
1250
|
switch (name) {
|
|
@@ -994,8 +1320,8 @@ var MavenProject = class _MavenProject {
|
|
|
994
1320
|
poms.push(pom);
|
|
995
1321
|
for (const entry of pom.modules) {
|
|
996
1322
|
const target = resolve5(pom.dir, entry);
|
|
997
|
-
const moduleFile = target.endsWith(".xml") ? target :
|
|
998
|
-
if (!seen.has(moduleFile) &&
|
|
1323
|
+
const moduleFile = target.endsWith(".xml") ? target : join10(target, "pom.xml");
|
|
1324
|
+
if (!seen.has(moduleFile) && existsSync6(moduleFile)) {
|
|
999
1325
|
seen.add(moduleFile);
|
|
1000
1326
|
queue.push(moduleFile);
|
|
1001
1327
|
}
|
|
@@ -1065,7 +1391,7 @@ var MavenProject = class _MavenProject {
|
|
|
1065
1391
|
this.aggregators.set(pom.file, aggregator.dir);
|
|
1066
1392
|
for (const entry of pom.modules) {
|
|
1067
1393
|
const target = resolve5(pom.dir, entry);
|
|
1068
|
-
const file = target.endsWith(".xml") ? target :
|
|
1394
|
+
const file = target.endsWith(".xml") ? target : join10(target, "pom.xml");
|
|
1069
1395
|
const modulePom = this.byFile.get(file);
|
|
1070
1396
|
if (modulePom) queue.push(modulePom);
|
|
1071
1397
|
}
|
|
@@ -1075,23 +1401,23 @@ var MavenProject = class _MavenProject {
|
|
|
1075
1401
|
};
|
|
1076
1402
|
function findPomFiles(rootDir) {
|
|
1077
1403
|
const results = [];
|
|
1078
|
-
const rootPom =
|
|
1079
|
-
if (
|
|
1404
|
+
const rootPom = join10(rootDir, "pom.xml");
|
|
1405
|
+
if (existsSync6(rootPom)) results.push(resolve5(rootPom));
|
|
1080
1406
|
const walk = (dir, depth) => {
|
|
1081
1407
|
if (depth > MAX_DEPTH) return;
|
|
1082
|
-
let
|
|
1408
|
+
let entries2;
|
|
1083
1409
|
try {
|
|
1084
|
-
|
|
1410
|
+
entries2 = readdirSync(dir, { withFileTypes: true });
|
|
1085
1411
|
} catch {
|
|
1086
1412
|
return;
|
|
1087
1413
|
}
|
|
1088
|
-
const isProject =
|
|
1089
|
-
for (const entry of
|
|
1414
|
+
const isProject = existsSync6(join10(dir, "pom.xml"));
|
|
1415
|
+
for (const entry of entries2) {
|
|
1090
1416
|
if (!entry.isDirectory() || entry.name.startsWith(".") || SKIPPED_DIRS.has(entry.name)) continue;
|
|
1091
1417
|
if (isProject && entry.name === "src") continue;
|
|
1092
|
-
const subDir =
|
|
1093
|
-
const pom =
|
|
1094
|
-
if (
|
|
1418
|
+
const subDir = join10(dir, entry.name);
|
|
1419
|
+
const pom = join10(subDir, "pom.xml");
|
|
1420
|
+
if (existsSync6(pom) && statSync2(pom).isFile()) results.push(resolve5(pom));
|
|
1095
1421
|
walk(subDir, depth + 1);
|
|
1096
1422
|
}
|
|
1097
1423
|
};
|
|
@@ -1107,7 +1433,7 @@ var MavenModuleDiscoveryAdapter = class {
|
|
|
1107
1433
|
*/
|
|
1108
1434
|
findRoot(startDir, boundaryDir) {
|
|
1109
1435
|
let dir = resolve6(startDir);
|
|
1110
|
-
if (!
|
|
1436
|
+
if (!existsSync7(join11(dir, "pom.xml"))) return void 0;
|
|
1111
1437
|
const boundary = boundaryDir ? resolve6(boundaryDir) : void 0;
|
|
1112
1438
|
for (; ; ) {
|
|
1113
1439
|
const above = pomAbove(dir, boundary);
|
|
@@ -1154,7 +1480,7 @@ function ancestorsWithin(dir, boundary) {
|
|
|
1154
1480
|
return result;
|
|
1155
1481
|
}
|
|
1156
1482
|
function pomAbove(dir, boundary) {
|
|
1157
|
-
const pom = tryReadPom(
|
|
1483
|
+
const pom = tryReadPom(join11(dir, "pom.xml"));
|
|
1158
1484
|
if (!pom) return void 0;
|
|
1159
1485
|
const ancestors = ancestorsWithin(dir, boundary);
|
|
1160
1486
|
const found = [];
|
|
@@ -1166,9 +1492,9 @@ function pomAbove(dir, boundary) {
|
|
|
1166
1492
|
found.push(dirname5(parentFile));
|
|
1167
1493
|
}
|
|
1168
1494
|
}
|
|
1169
|
-
const ownFile =
|
|
1495
|
+
const ownFile = join11(dir, "pom.xml");
|
|
1170
1496
|
const aggregator = ancestors.find(
|
|
1171
|
-
(ancestor) => tryReadPom(
|
|
1497
|
+
(ancestor) => tryReadPom(join11(ancestor, "pom.xml"))?.modules.some((entry) => {
|
|
1172
1498
|
const target = resolve6(ancestor, entry);
|
|
1173
1499
|
return target === dir || target === ownFile;
|
|
1174
1500
|
})
|
|
@@ -1178,12 +1504,12 @@ function pomAbove(dir, boundary) {
|
|
|
1178
1504
|
}
|
|
1179
1505
|
|
|
1180
1506
|
// src/adapters/maven/maven-registry.ts
|
|
1181
|
-
import { existsSync as
|
|
1182
|
-
import { join as
|
|
1507
|
+
import { existsSync as existsSync10 } from "fs";
|
|
1508
|
+
import { join as join14 } from "path";
|
|
1183
1509
|
|
|
1184
1510
|
// src/adapters/maven/maven-command.ts
|
|
1185
|
-
import { existsSync as
|
|
1186
|
-
import { dirname as dirname6, isAbsolute, join as
|
|
1511
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1512
|
+
import { dirname as dirname6, isAbsolute, join as join12, relative as relative4, resolve as resolve7, sep } from "path";
|
|
1187
1513
|
var MavenUnavailableError = class extends Error {
|
|
1188
1514
|
constructor() {
|
|
1189
1515
|
super("Maven is not available: no Maven Wrapper (mvnw) in the project and no `mvn` on the PATH.");
|
|
@@ -1206,8 +1532,8 @@ function findMavenWrapper(fromDir, rootDir) {
|
|
|
1206
1532
|
let dir = resolve7(fromDir);
|
|
1207
1533
|
const root = resolve7(rootDir);
|
|
1208
1534
|
for (; ; ) {
|
|
1209
|
-
const candidate =
|
|
1210
|
-
if (
|
|
1535
|
+
const candidate = join12(dir, WRAPPER);
|
|
1536
|
+
if (existsSync8(candidate)) return candidate;
|
|
1211
1537
|
if (dir === root || relative4(root, dir).startsWith("..")) return void 0;
|
|
1212
1538
|
const parent = dirname6(dir);
|
|
1213
1539
|
if (parent === dir) return void 0;
|
|
@@ -1240,8 +1566,8 @@ function mavenErrors(output) {
|
|
|
1240
1566
|
|
|
1241
1567
|
// src/adapters/maven/maven-resolution.ts
|
|
1242
1568
|
import { randomUUID } from "crypto";
|
|
1243
|
-
import { existsSync as
|
|
1244
|
-
import { join as
|
|
1569
|
+
import { existsSync as existsSync9, mkdirSync, readFileSync as readFileSync9, readdirSync as readdirSync2, rmSync, writeFileSync as writeFileSync2 } from "fs";
|
|
1570
|
+
import { join as join13 } from "path";
|
|
1245
1571
|
var DEFAULT_MAVEN_PLUGINS = { help: "3.5.2", versions: "2.22.0" };
|
|
1246
1572
|
var HELP_PLUGIN_ID = "org.apache.maven.plugins:maven-help-plugin";
|
|
1247
1573
|
var VERSIONS_PLUGIN_ID = "org.codehaus.mojo:versions-maven-plugin";
|
|
@@ -1273,9 +1599,9 @@ var ScratchDirectory = class {
|
|
|
1273
1599
|
target;
|
|
1274
1600
|
createdTarget;
|
|
1275
1601
|
constructor(moduleDir) {
|
|
1276
|
-
this.target =
|
|
1277
|
-
this.createdTarget = !
|
|
1278
|
-
this.path =
|
|
1602
|
+
this.target = join13(moduleDir, "target");
|
|
1603
|
+
this.createdTarget = !existsSync9(this.target);
|
|
1604
|
+
this.path = join13(this.target, `.elevate-${randomUUID().slice(0, 8)}`);
|
|
1279
1605
|
mkdirSync(this.path, { recursive: true });
|
|
1280
1606
|
}
|
|
1281
1607
|
dispose() {
|
|
@@ -1287,7 +1613,7 @@ var ScratchDirectory = class {
|
|
|
1287
1613
|
}
|
|
1288
1614
|
};
|
|
1289
1615
|
async function readEffectivePoms(pomFile, moduleDir, rootDir, scratch, plugins, recursive = false) {
|
|
1290
|
-
const output =
|
|
1616
|
+
const output = join13(scratch.path, "effective-pom.xml");
|
|
1291
1617
|
const args = [
|
|
1292
1618
|
"-f",
|
|
1293
1619
|
relativeToCwd(moduleDir, pomFile),
|
|
@@ -1299,7 +1625,7 @@ async function readEffectivePoms(pomFile, moduleDir, rootDir, scratch, plugins,
|
|
|
1299
1625
|
rootDir,
|
|
1300
1626
|
timeoutMs: MAVEN_TIMEOUT_MS
|
|
1301
1627
|
});
|
|
1302
|
-
if (result.exitCode !== 0 || !
|
|
1628
|
+
if (result.exitCode !== 0 || !existsSync9(output)) {
|
|
1303
1629
|
const text = `${result.stdout}
|
|
1304
1630
|
${result.stderr}`;
|
|
1305
1631
|
if (isPluginUnavailable(text, HELP_PLUGIN_ID)) {
|
|
@@ -1311,7 +1637,7 @@ ${result.stderr}`;
|
|
|
1311
1637
|
throw new MavenCommandError(`Maven could not build the effective POM:
|
|
1312
1638
|
${mavenErrors(text)}`, text);
|
|
1313
1639
|
}
|
|
1314
|
-
return parseEffectivePoms(
|
|
1640
|
+
return parseEffectivePoms(readFileSync9(output, "utf8"));
|
|
1315
1641
|
}
|
|
1316
1642
|
function parseEffectivePoms(source) {
|
|
1317
1643
|
const root = parseXml(source);
|
|
@@ -1360,7 +1686,7 @@ function readEffectiveDependencies(container) {
|
|
|
1360
1686
|
}
|
|
1361
1687
|
function buildProbePom(artifacts, model) {
|
|
1362
1688
|
const seen = /* @__PURE__ */ new Set();
|
|
1363
|
-
const
|
|
1689
|
+
const entries2 = [];
|
|
1364
1690
|
for (const artifact of artifacts) {
|
|
1365
1691
|
const key = `${artifact.groupId}:${artifact.artifactId}:${artifact.type ?? "jar"}:${artifact.classifier ?? ""}`;
|
|
1366
1692
|
if (seen.has(key)) continue;
|
|
@@ -1372,7 +1698,7 @@ function buildProbePom(artifacts, model) {
|
|
|
1372
1698
|
artifact.type && artifact.type !== "jar" ? `<type>${escapeXml(artifact.type)}</type>` : "",
|
|
1373
1699
|
artifact.classifier ? `<classifier>${escapeXml(artifact.classifier)}</classifier>` : ""
|
|
1374
1700
|
].filter(Boolean);
|
|
1375
|
-
|
|
1701
|
+
entries2.push(` <dependency>${fields.join("")}</dependency>`);
|
|
1376
1702
|
}
|
|
1377
1703
|
const repositories = [model.repositories, model.pluginRepositories].filter((element) => element !== void 0).map((element) => serializeXml(element, " ")).join("\n");
|
|
1378
1704
|
return [
|
|
@@ -1386,7 +1712,7 @@ function buildProbePom(artifacts, model) {
|
|
|
1386
1712
|
repositories,
|
|
1387
1713
|
" <dependencyManagement>",
|
|
1388
1714
|
" <dependencies>",
|
|
1389
|
-
...
|
|
1715
|
+
...entries2,
|
|
1390
1716
|
" </dependencies>",
|
|
1391
1717
|
" </dependencyManagement>",
|
|
1392
1718
|
"</project>",
|
|
@@ -1395,9 +1721,9 @@ function buildProbePom(artifacts, model) {
|
|
|
1395
1721
|
}
|
|
1396
1722
|
async function queryNewerVersions(artifacts, model, moduleDir, rootDir, scratch, plugins) {
|
|
1397
1723
|
if (artifacts.length === 0) return { newerVersions: /* @__PURE__ */ new Map(), failedLookups: /* @__PURE__ */ new Set() };
|
|
1398
|
-
const probeDir =
|
|
1724
|
+
const probeDir = join13(scratch.path, "probe");
|
|
1399
1725
|
mkdirSync(probeDir, { recursive: true });
|
|
1400
|
-
const probePom =
|
|
1726
|
+
const probePom = join13(probeDir, "pom.xml");
|
|
1401
1727
|
writeFileSync2(probePom, buildProbePom(artifacts, model), "utf8");
|
|
1402
1728
|
const result = await runMaven(
|
|
1403
1729
|
[
|
|
@@ -1412,8 +1738,8 @@ async function queryNewerVersions(artifacts, model, moduleDir, rootDir, scratch,
|
|
|
1412
1738
|
);
|
|
1413
1739
|
const log = `${result.stdout}
|
|
1414
1740
|
${result.stderr}`;
|
|
1415
|
-
const reportFile =
|
|
1416
|
-
if (result.exitCode !== 0 || !
|
|
1741
|
+
const reportFile = join13(probeDir, "target", "dependency-updates-report.xml");
|
|
1742
|
+
if (result.exitCode !== 0 || !existsSync9(reportFile)) {
|
|
1417
1743
|
if (isPluginUnavailable(log, VERSIONS_PLUGIN_ID)) {
|
|
1418
1744
|
throw new MavenPluginUnavailableError(
|
|
1419
1745
|
{ id: VERSIONS_PLUGIN_ID, version: plugins.versions, key: "versions", purpose: "look up newer versions" },
|
|
@@ -1424,7 +1750,7 @@ ${result.stderr}`;
|
|
|
1424
1750
|
${mavenErrors(log)}`, log);
|
|
1425
1751
|
}
|
|
1426
1752
|
return {
|
|
1427
|
-
newerVersions: parseUpdatesReport(
|
|
1753
|
+
newerVersions: parseUpdatesReport(readFileSync9(reportFile, "utf8")),
|
|
1428
1754
|
failedLookups: parseFailedLookups(log)
|
|
1429
1755
|
};
|
|
1430
1756
|
}
|
|
@@ -1509,14 +1835,14 @@ var MavenRegistryAdapter = class {
|
|
|
1509
1835
|
}
|
|
1510
1836
|
};
|
|
1511
1837
|
async function projectRepositories(cwd, scratch, plugins) {
|
|
1512
|
-
const rootPom =
|
|
1513
|
-
if (!
|
|
1838
|
+
const rootPom = join14(cwd, "pom.xml");
|
|
1839
|
+
if (!existsSync10(rootPom)) return NO_REPOSITORIES;
|
|
1514
1840
|
const models = await readEffectivePoms(rootPom, cwd, cwd, scratch, plugins);
|
|
1515
1841
|
return [...models.values()][0] ?? NO_REPOSITORIES;
|
|
1516
1842
|
}
|
|
1517
1843
|
|
|
1518
1844
|
// src/adapters/maven/maven-scanner.ts
|
|
1519
|
-
import { join as
|
|
1845
|
+
import { join as join15 } from "path";
|
|
1520
1846
|
import semver4 from "semver";
|
|
1521
1847
|
|
|
1522
1848
|
// src/adapters/maven/maven-locator.ts
|
|
@@ -1661,14 +1987,14 @@ function groupModules(project, modules) {
|
|
|
1661
1987
|
if (module.aggregatorDir) reactorSizes.set(module.aggregatorDir, (reactorSizes.get(module.aggregatorDir) ?? 0) + 1);
|
|
1662
1988
|
}
|
|
1663
1989
|
for (const module of modules) {
|
|
1664
|
-
const pom = project.pomAt(
|
|
1990
|
+
const pom = project.pomAt(join15(module.path, "pom.xml"));
|
|
1665
1991
|
if (!pom) continue;
|
|
1666
1992
|
const reactor = module.aggregatorDir;
|
|
1667
1993
|
const batched = reactor !== void 0 && (reactorSizes.get(reactor) ?? 0) > 1;
|
|
1668
1994
|
const key = batched ? `reactor:${reactor}` : `module:${module.path}`;
|
|
1669
1995
|
const group = groups.get(key) ?? {
|
|
1670
1996
|
dir: batched ? reactor : module.path,
|
|
1671
|
-
aggregatorPom: batched ?
|
|
1997
|
+
aggregatorPom: batched ? join15(reactor, "pom.xml") : void 0,
|
|
1672
1998
|
members: []
|
|
1673
1999
|
};
|
|
1674
2000
|
group.members.push({ module, pom });
|
|
@@ -1836,11 +2162,11 @@ function markSharedDeclarations(candidates) {
|
|
|
1836
2162
|
}
|
|
1837
2163
|
|
|
1838
2164
|
// src/adapters/maven/maven-updater.ts
|
|
1839
|
-
import { readFileSync as
|
|
1840
|
-
import { join as
|
|
2165
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync3 } from "fs";
|
|
2166
|
+
import { join as join16, resolve as resolve8 } from "path";
|
|
1841
2167
|
var MavenUpdaterAdapter = class {
|
|
1842
2168
|
affectedFiles(module, _rootDir, updates) {
|
|
1843
|
-
const files = new Set(updates.map((u) => resolve8(u.declaration?.file ??
|
|
2169
|
+
const files = new Set(updates.map((u) => resolve8(u.declaration?.file ?? join16(module.path, "pom.xml"))));
|
|
1844
2170
|
return [...files];
|
|
1845
2171
|
}
|
|
1846
2172
|
async applyUpdates(module, rootDir, updates, onProgress) {
|
|
@@ -1856,7 +2182,7 @@ var MavenUpdaterAdapter = class {
|
|
|
1856
2182
|
const writes = [];
|
|
1857
2183
|
for (const [file, fileUpdates] of byFile) {
|
|
1858
2184
|
onProgress(`Preparing ${fileUpdates.length} change(s) in ${fileUpdates[0].declaration.displayPath}\u2026`);
|
|
1859
|
-
const source =
|
|
2185
|
+
const source = readFileSync10(file, "utf8");
|
|
1860
2186
|
const pom = parsePom(source, file);
|
|
1861
2187
|
const chain = project.pomAt(file) ? project.chainOf(project.pomAt(file)).poms : [pom];
|
|
1862
2188
|
const edits = collectEdits(pom, [pom, ...chain.slice(1)], fileUpdates);
|
|
@@ -1926,14 +2252,14 @@ function describe(declaration) {
|
|
|
1926
2252
|
}
|
|
1927
2253
|
|
|
1928
2254
|
// src/adapters/maven/maven-verifier.ts
|
|
1929
|
-
import { dirname as dirname7, join as
|
|
2255
|
+
import { dirname as dirname7, join as join17, relative as relative6, resolve as resolve9 } from "path";
|
|
1930
2256
|
var VERIFY_TIMEOUT_MS2 = 30 * 6e4;
|
|
1931
2257
|
var MavenVerificationAdapter = class {
|
|
1932
2258
|
async verify(module, rootDir, options = {}) {
|
|
1933
2259
|
if (options.customScript) {
|
|
1934
2260
|
const label2 = options.customLabel || options.customScript;
|
|
1935
2261
|
const result = await runShellScript(options.customScript, { cwd: module.path, timeoutMs: VERIFY_TIMEOUT_MS2 });
|
|
1936
|
-
return result.exitCode === 0 ? { status: "clean", details: "Verification script succeeded.", label: label2 } : { status: "warn", details:
|
|
2262
|
+
return result.exitCode === 0 ? { status: "clean", details: "Verification script succeeded.", label: label2 } : { status: "warn", details: failureDetails2(result), label: label2 };
|
|
1937
2263
|
}
|
|
1938
2264
|
const plan = planBuild(module, rootDir, options.changedFiles ?? []);
|
|
1939
2265
|
const label = `Maven build (${["test-compile", ...plan.args].join(" ")})`;
|
|
@@ -1946,7 +2272,7 @@ var MavenVerificationAdapter = class {
|
|
|
1946
2272
|
if (result.exitCode === 0) {
|
|
1947
2273
|
return { status: "clean", details: "All affected modules compile, including tests.", label };
|
|
1948
2274
|
}
|
|
1949
|
-
const details = result.timedOut ? "Maven build timed out." :
|
|
2275
|
+
const details = result.timedOut ? "Maven build timed out." : failureDetails2(result);
|
|
1950
2276
|
return { status: "warn", details, label };
|
|
1951
2277
|
} catch (err) {
|
|
1952
2278
|
if (err instanceof MavenUnavailableError) return { status: "warn", details: err.message, label };
|
|
@@ -1954,7 +2280,7 @@ var MavenVerificationAdapter = class {
|
|
|
1954
2280
|
}
|
|
1955
2281
|
}
|
|
1956
2282
|
};
|
|
1957
|
-
function
|
|
2283
|
+
function failureDetails2(result) {
|
|
1958
2284
|
const output = `${result.stdout}
|
|
1959
2285
|
${result.stderr}`.trim();
|
|
1960
2286
|
return output ? mavenErrors(output) : `Exited with code ${result.exitCode}.`;
|
|
@@ -1963,7 +2289,7 @@ function planBuild(module, rootDir, changedFiles) {
|
|
|
1963
2289
|
const aggregatorDir = module.aggregatorDir;
|
|
1964
2290
|
if (!aggregatorDir) return { cwd: module.path, args: [] };
|
|
1965
2291
|
const project = MavenProject.load(rootDir);
|
|
1966
|
-
const files = changedFiles.length > 0 ? changedFiles : [
|
|
2292
|
+
const files = changedFiles.length > 0 ? changedFiles : [join17(module.path, "pom.xml")];
|
|
1967
2293
|
const selected = /* @__PURE__ */ new Set();
|
|
1968
2294
|
for (const file of files) {
|
|
1969
2295
|
const pom = project.pomAt(file);
|
|
@@ -2038,7 +2364,7 @@ function findRepositoryRoot(startDir) {
|
|
|
2038
2364
|
const boundary = findCheckoutBoundary(start);
|
|
2039
2365
|
let dir = start;
|
|
2040
2366
|
for (; ; ) {
|
|
2041
|
-
if (
|
|
2367
|
+
if (existsSync11(join18(dir, "elevate.config.json"))) return dir;
|
|
2042
2368
|
const parent = dirname8(dir);
|
|
2043
2369
|
if (dir === boundary || parent === dir) break;
|
|
2044
2370
|
dir = parent;
|
|
@@ -2104,15 +2430,15 @@ function resolveConfig(rootDir, file, source = "elevate.config.json") {
|
|
|
2104
2430
|
}
|
|
2105
2431
|
function loadConfig2(startDir = process.cwd()) {
|
|
2106
2432
|
const rootDir = findRepositoryRoot(startDir);
|
|
2107
|
-
const configPath =
|
|
2433
|
+
const configPath = join18(rootDir, "elevate.config.json");
|
|
2108
2434
|
let file = {};
|
|
2109
|
-
if (
|
|
2435
|
+
if (existsSync11(configPath)) {
|
|
2110
2436
|
const unreadable = (detail) => new ConfigError(
|
|
2111
2437
|
`Cannot read elevate.config.json: ${detail} (${configPath}). Fix the file or remove it; ${NO_CONTINUE}`
|
|
2112
2438
|
);
|
|
2113
2439
|
let parsed;
|
|
2114
2440
|
try {
|
|
2115
|
-
parsed = JSON.parse(
|
|
2441
|
+
parsed = JSON.parse(readFileSync11(configPath, "utf8").replace(/^/, ""));
|
|
2116
2442
|
} catch (err) {
|
|
2117
2443
|
throw unreadable(err instanceof Error ? err.message : String(err));
|
|
2118
2444
|
}
|
|
@@ -2161,7 +2487,9 @@ var de = {
|
|
|
2161
2487
|
"lookup-failed": "Abfrage der Registry fehlgeschlagen",
|
|
2162
2488
|
"managed-externally": "Version wird au\xDFerhalb dieses Repos verwaltet",
|
|
2163
2489
|
"declaration-mismatch": "Ort der Versionsangabe ist nicht eindeutig",
|
|
2164
|
-
"invalid-name": "ung\xFCltiger Paketname"
|
|
2490
|
+
"invalid-name": "ung\xFCltiger Paketname",
|
|
2491
|
+
"not-linked": "Workspace-Paket, das pnpm ohne das workspace:-Protokoll nicht verlinkt",
|
|
2492
|
+
"catalog-unresolved": "Catalog-Eintrag konnte nicht aufgel\xF6st werden"
|
|
2165
2493
|
})[reason]
|
|
2166
2494
|
},
|
|
2167
2495
|
badges: {
|
|
@@ -2267,7 +2595,9 @@ var en = {
|
|
|
2267
2595
|
"lookup-failed": "registry lookup failed",
|
|
2268
2596
|
"managed-externally": "version is managed outside this repository",
|
|
2269
2597
|
"declaration-mismatch": "version location is ambiguous",
|
|
2270
|
-
"invalid-name": "invalid package name"
|
|
2598
|
+
"invalid-name": "invalid package name",
|
|
2599
|
+
"not-linked": "workspace package that pnpm does not link without the workspace: protocol",
|
|
2600
|
+
"catalog-unresolved": "catalog entry could not be resolved"
|
|
2271
2601
|
})[reason]
|
|
2272
2602
|
},
|
|
2273
2603
|
badges: {
|
|
@@ -2461,7 +2791,12 @@ function candidateToJson(u) {
|
|
|
2461
2791
|
scope: u.scope,
|
|
2462
2792
|
isPreRelease: u.isPreRelease ?? false,
|
|
2463
2793
|
preReleaseTag: u.preReleaseTag,
|
|
2464
|
-
declaredIn: u.declaration ? {
|
|
2794
|
+
declaredIn: u.declaration ? {
|
|
2795
|
+
file: u.declaration.displayPath,
|
|
2796
|
+
kind: u.declaration.kind,
|
|
2797
|
+
property: u.declaration.propertyName,
|
|
2798
|
+
catalog: u.declaration.catalogName
|
|
2799
|
+
} : void 0,
|
|
2465
2800
|
sharedWith: u.sharedWith
|
|
2466
2801
|
};
|
|
2467
2802
|
}
|
|
@@ -2583,24 +2918,24 @@ function usePackages(strategy, module, modules, config, channel = "stable") {
|
|
|
2583
2918
|
import { useState as useState4, useCallback as useCallback4 } from "react";
|
|
2584
2919
|
|
|
2585
2920
|
// src/application/update-workflow.ts
|
|
2586
|
-
import { existsSync as
|
|
2921
|
+
import { existsSync as existsSync13 } from "fs";
|
|
2587
2922
|
import { relative as relative7 } from "path";
|
|
2588
2923
|
|
|
2589
2924
|
// src/adapters/shared/file-snapshot.ts
|
|
2590
|
-
import { existsSync as
|
|
2925
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
2591
2926
|
var FileSnapshot = class _FileSnapshot {
|
|
2592
|
-
constructor(
|
|
2593
|
-
this.entries =
|
|
2927
|
+
constructor(entries2) {
|
|
2928
|
+
this.entries = entries2;
|
|
2594
2929
|
}
|
|
2595
2930
|
entries;
|
|
2596
2931
|
/** Records the current content (or absence) of each file. */
|
|
2597
2932
|
static capture(files) {
|
|
2598
|
-
const
|
|
2933
|
+
const entries2 = /* @__PURE__ */ new Map();
|
|
2599
2934
|
for (const file of files) {
|
|
2600
|
-
if (
|
|
2601
|
-
|
|
2935
|
+
if (entries2.has(file)) continue;
|
|
2936
|
+
entries2.set(file, existsSync12(file) ? readFileSync12(file) : null);
|
|
2602
2937
|
}
|
|
2603
|
-
return new _FileSnapshot(
|
|
2938
|
+
return new _FileSnapshot(entries2);
|
|
2604
2939
|
}
|
|
2605
2940
|
/** The files covered by this snapshot. */
|
|
2606
2941
|
get files() {
|
|
@@ -2610,9 +2945,9 @@ var FileSnapshot = class _FileSnapshot {
|
|
|
2610
2945
|
changedFiles() {
|
|
2611
2946
|
return this.files.filter((file) => {
|
|
2612
2947
|
const before = this.entries.get(file) ?? null;
|
|
2613
|
-
const exists =
|
|
2948
|
+
const exists = existsSync12(file);
|
|
2614
2949
|
if (before === null) return exists;
|
|
2615
|
-
return !exists || !
|
|
2950
|
+
return !exists || !readFileSync12(file).equals(before);
|
|
2616
2951
|
});
|
|
2617
2952
|
}
|
|
2618
2953
|
/** Writes every file back to its captured state; removes files that did not exist. */
|
|
@@ -2660,7 +2995,7 @@ Restoring the installed state failed as well: ${message(err)}`;
|
|
|
2660
2995
|
let baseline;
|
|
2661
2996
|
if (!options.skipVerification) {
|
|
2662
2997
|
progress("Running verification on the current state\u2026");
|
|
2663
|
-
baseline = await verify(snapshot.files.filter((file) =>
|
|
2998
|
+
baseline = await verify(snapshot.files.filter((file) => existsSync13(file)));
|
|
2664
2999
|
}
|
|
2665
3000
|
let applied;
|
|
2666
3001
|
try {
|
|
@@ -2864,7 +3199,8 @@ var Header = ({
|
|
|
2864
3199
|
currentLocale,
|
|
2865
3200
|
strategy,
|
|
2866
3201
|
nextStrategyName,
|
|
2867
|
-
channel
|
|
3202
|
+
channel,
|
|
3203
|
+
rootDir
|
|
2868
3204
|
}) => /* @__PURE__ */ jsxs2(
|
|
2869
3205
|
Box2,
|
|
2870
3206
|
{
|
|
@@ -2885,7 +3221,7 @@ var Header = ({
|
|
|
2885
3221
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.colors.brandLight, bold: true, children: [
|
|
2886
3222
|
strategy.icon,
|
|
2887
3223
|
" ",
|
|
2888
|
-
strategy.displayName
|
|
3224
|
+
rootDir && strategy.displayNameFor?.(rootDir) || strategy.displayName
|
|
2889
3225
|
] }),
|
|
2890
3226
|
/* @__PURE__ */ jsxs2(Text2, { color: theme.colors.muted, children: [
|
|
2891
3227
|
" [",
|
|
@@ -3733,7 +4069,8 @@ var App = ({ config }) => {
|
|
|
3733
4069
|
currentLocale: locale,
|
|
3734
4070
|
strategy,
|
|
3735
4071
|
nextStrategyName: `${nextStrategy.icon} ${nextStrategy.displayName}`,
|
|
3736
|
-
channel: config.channel
|
|
4072
|
+
channel: config.channel,
|
|
4073
|
+
rootDir: config.rootDir
|
|
3737
4074
|
}
|
|
3738
4075
|
),
|
|
3739
4076
|
view === "workspace_modal" && /* @__PURE__ */ jsx14(
|
|
@@ -3931,12 +4268,14 @@ async function handleModulesCommand(options, config) {
|
|
|
3931
4268
|
const ecosystem = options.ecosystem ?? "npm";
|
|
3932
4269
|
const strategy = EcosystemFactory.getStrategy(ecosystem);
|
|
3933
4270
|
const modules = await strategy.discovery.discover(config.rootDir);
|
|
4271
|
+
const displayName = strategy.displayNameFor?.(config.rootDir) ?? strategy.displayName;
|
|
4272
|
+
const packageManager = strategy.packageManagerFor?.(config.rootDir);
|
|
3934
4273
|
if (options.json) {
|
|
3935
|
-
console.log(JSON.stringify({ ecosystem, count: modules.length, modules }, null, 2));
|
|
4274
|
+
console.log(JSON.stringify({ ecosystem, ...packageManager ? { packageManager } : {}, count: modules.length, modules }, null, 2));
|
|
3936
4275
|
return 0;
|
|
3937
4276
|
}
|
|
3938
4277
|
console.log(`
|
|
3939
|
-
\u{1FAB6} Discovered ${strategy.icon} ${
|
|
4278
|
+
\u{1FAB6} Discovered ${strategy.icon} ${displayName} Modules (${modules.length}):
|
|
3940
4279
|
`);
|
|
3941
4280
|
for (const m of modules) {
|
|
3942
4281
|
const isRootBadge = m.isRoot ? " [ROOT]" : "";
|
|
@@ -3960,6 +4299,10 @@ function describeSkip(skipped) {
|
|
|
3960
4299
|
return `version location is ambiguous${detail}`;
|
|
3961
4300
|
case "invalid-name":
|
|
3962
4301
|
return "not a valid package name";
|
|
4302
|
+
case "not-linked":
|
|
4303
|
+
return `workspace package that pnpm does not link unless it is declared with the workspace: protocol${detail}`;
|
|
4304
|
+
case "catalog-unresolved":
|
|
4305
|
+
return `catalog entry could not be resolved${detail}`;
|
|
3963
4306
|
}
|
|
3964
4307
|
}
|
|
3965
4308
|
|
|
@@ -4366,9 +4709,9 @@ function parsePackageArgument(item) {
|
|
|
4366
4709
|
}
|
|
4367
4710
|
|
|
4368
4711
|
// src/cli/command-init.ts
|
|
4369
|
-
import { existsSync as
|
|
4712
|
+
import { existsSync as existsSync14, readFileSync as readFileSync13, writeFileSync as writeFileSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
4370
4713
|
import { homedir, platform } from "os";
|
|
4371
|
-
import { join as
|
|
4714
|
+
import { join as join19, resolve as resolve11 } from "path";
|
|
4372
4715
|
function mcpServerEntry() {
|
|
4373
4716
|
return {
|
|
4374
4717
|
command: "npx",
|
|
@@ -4414,7 +4757,7 @@ automatic rollback.
|
|
|
4414
4757
|
var home = homedir();
|
|
4415
4758
|
var isWin = platform() === "win32";
|
|
4416
4759
|
function appData() {
|
|
4417
|
-
return process.env.APPDATA ||
|
|
4760
|
+
return process.env.APPDATA || join19(home, "AppData", "Roaming");
|
|
4418
4761
|
}
|
|
4419
4762
|
function detectCursor(cwd) {
|
|
4420
4763
|
const env = {
|
|
@@ -4426,25 +4769,25 @@ function detectCursor(cwd) {
|
|
|
4426
4769
|
mcpConfigured: false,
|
|
4427
4770
|
skillConfigured: false
|
|
4428
4771
|
};
|
|
4429
|
-
const workspaceCursorDir =
|
|
4430
|
-
const globalCursorDir =
|
|
4431
|
-
if (
|
|
4772
|
+
const workspaceCursorDir = join19(cwd, ".cursor");
|
|
4773
|
+
const globalCursorDir = join19(home, ".cursor");
|
|
4774
|
+
if (existsSync14(workspaceCursorDir) || existsSync14(globalCursorDir)) {
|
|
4432
4775
|
env.detected = true;
|
|
4433
|
-
env.reason =
|
|
4776
|
+
env.reason = existsSync14(workspaceCursorDir) ? `Found .cursor/ in workspace` : `Found ~/.cursor/ global config`;
|
|
4434
4777
|
}
|
|
4435
|
-
const mcpPath =
|
|
4436
|
-
if (
|
|
4778
|
+
const mcpPath = join19(cwd, ".cursor", "mcp.json");
|
|
4779
|
+
if (existsSync14(mcpPath)) {
|
|
4437
4780
|
try {
|
|
4438
|
-
const content = JSON.parse(
|
|
4781
|
+
const content = JSON.parse(readFileSync13(mcpPath, "utf8"));
|
|
4439
4782
|
if (content?.mcpServers?.elevate) {
|
|
4440
4783
|
env.mcpConfigured = true;
|
|
4441
4784
|
}
|
|
4442
4785
|
} catch {
|
|
4443
4786
|
}
|
|
4444
4787
|
}
|
|
4445
|
-
const rulesPath =
|
|
4446
|
-
if (
|
|
4447
|
-
const content =
|
|
4788
|
+
const rulesPath = join19(cwd, ".cursorrules");
|
|
4789
|
+
if (existsSync14(rulesPath)) {
|
|
4790
|
+
const content = readFileSync13(rulesPath, "utf8");
|
|
4448
4791
|
if (content.includes("elevate") || content.includes("Elevate")) {
|
|
4449
4792
|
env.skillConfigured = true;
|
|
4450
4793
|
}
|
|
@@ -4461,12 +4804,12 @@ function detectClaudeDesktop() {
|
|
|
4461
4804
|
mcpConfigured: false,
|
|
4462
4805
|
skillConfigured: false
|
|
4463
4806
|
};
|
|
4464
|
-
const configPath = isWin ?
|
|
4465
|
-
if (
|
|
4807
|
+
const configPath = isWin ? join19(appData(), "Claude", "claude_desktop_config.json") : join19(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
4808
|
+
if (existsSync14(configPath)) {
|
|
4466
4809
|
env.detected = true;
|
|
4467
4810
|
env.reason = `Found ${configPath}`;
|
|
4468
4811
|
try {
|
|
4469
|
-
const content = JSON.parse(
|
|
4812
|
+
const content = JSON.parse(readFileSync13(configPath, "utf8"));
|
|
4470
4813
|
if (content?.mcpServers?.elevate) {
|
|
4471
4814
|
env.mcpConfigured = true;
|
|
4472
4815
|
}
|
|
@@ -4485,23 +4828,23 @@ function detectClaudeCode(cwd) {
|
|
|
4485
4828
|
mcpConfigured: false,
|
|
4486
4829
|
skillConfigured: false
|
|
4487
4830
|
};
|
|
4488
|
-
const mcpJsonPath =
|
|
4489
|
-
const claudeMdPath =
|
|
4490
|
-
if (
|
|
4831
|
+
const mcpJsonPath = join19(cwd, ".mcp.json");
|
|
4832
|
+
const claudeMdPath = join19(cwd, "CLAUDE.md");
|
|
4833
|
+
if (existsSync14(mcpJsonPath) || existsSync14(claudeMdPath)) {
|
|
4491
4834
|
env.detected = true;
|
|
4492
|
-
env.reason =
|
|
4835
|
+
env.reason = existsSync14(mcpJsonPath) ? `Found .mcp.json in workspace` : `Found CLAUDE.md in workspace`;
|
|
4493
4836
|
}
|
|
4494
|
-
if (
|
|
4837
|
+
if (existsSync14(mcpJsonPath)) {
|
|
4495
4838
|
try {
|
|
4496
|
-
const content = JSON.parse(
|
|
4839
|
+
const content = JSON.parse(readFileSync13(mcpJsonPath, "utf8"));
|
|
4497
4840
|
if (content?.mcpServers?.elevate) {
|
|
4498
4841
|
env.mcpConfigured = true;
|
|
4499
4842
|
}
|
|
4500
4843
|
} catch {
|
|
4501
4844
|
}
|
|
4502
4845
|
}
|
|
4503
|
-
if (
|
|
4504
|
-
const content =
|
|
4846
|
+
if (existsSync14(claudeMdPath)) {
|
|
4847
|
+
const content = readFileSync13(claudeMdPath, "utf8");
|
|
4505
4848
|
if (content.includes("elevate") || content.includes("Elevate")) {
|
|
4506
4849
|
env.skillConfigured = true;
|
|
4507
4850
|
}
|
|
@@ -4518,20 +4861,20 @@ function detectAntigravity(cwd) {
|
|
|
4518
4861
|
mcpConfigured: false,
|
|
4519
4862
|
skillConfigured: false
|
|
4520
4863
|
};
|
|
4521
|
-
const globalGeminiDir =
|
|
4522
|
-
const workspaceAgentsDir =
|
|
4523
|
-
if (
|
|
4864
|
+
const globalGeminiDir = join19(home, ".gemini");
|
|
4865
|
+
const workspaceAgentsDir = join19(cwd, ".agents");
|
|
4866
|
+
if (existsSync14(globalGeminiDir) || existsSync14(workspaceAgentsDir)) {
|
|
4524
4867
|
env.detected = true;
|
|
4525
|
-
env.reason =
|
|
4868
|
+
env.reason = existsSync14(workspaceAgentsDir) ? `Found .agents/ in workspace` : `Found ~/.gemini/ global config`;
|
|
4526
4869
|
}
|
|
4527
4870
|
const mcpPaths = [
|
|
4528
|
-
|
|
4529
|
-
|
|
4871
|
+
join19(cwd, ".agents", "mcp_config.json"),
|
|
4872
|
+
join19(home, ".gemini", "config", "mcp_config.json")
|
|
4530
4873
|
];
|
|
4531
4874
|
for (const p of mcpPaths) {
|
|
4532
|
-
if (
|
|
4875
|
+
if (existsSync14(p)) {
|
|
4533
4876
|
try {
|
|
4534
|
-
const content = JSON.parse(
|
|
4877
|
+
const content = JSON.parse(readFileSync13(p, "utf8"));
|
|
4535
4878
|
if (content?.mcpServers?.elevate) {
|
|
4536
4879
|
env.mcpConfigured = true;
|
|
4537
4880
|
break;
|
|
@@ -4552,24 +4895,24 @@ function detectVSCode(cwd) {
|
|
|
4552
4895
|
mcpConfigured: false,
|
|
4553
4896
|
skillConfigured: false
|
|
4554
4897
|
};
|
|
4555
|
-
const vscodeDir =
|
|
4556
|
-
if (
|
|
4898
|
+
const vscodeDir = join19(cwd, ".vscode");
|
|
4899
|
+
if (existsSync14(vscodeDir)) {
|
|
4557
4900
|
env.detected = true;
|
|
4558
4901
|
env.reason = `Found .vscode/ in workspace`;
|
|
4559
4902
|
}
|
|
4560
|
-
const mcpPath =
|
|
4561
|
-
if (
|
|
4903
|
+
const mcpPath = join19(cwd, ".vscode", "mcp.json");
|
|
4904
|
+
if (existsSync14(mcpPath)) {
|
|
4562
4905
|
try {
|
|
4563
|
-
const content = JSON.parse(
|
|
4906
|
+
const content = JSON.parse(readFileSync13(mcpPath, "utf8"));
|
|
4564
4907
|
if (content?.servers?.elevate) {
|
|
4565
4908
|
env.mcpConfigured = true;
|
|
4566
4909
|
}
|
|
4567
4910
|
} catch {
|
|
4568
4911
|
}
|
|
4569
4912
|
}
|
|
4570
|
-
const copilotInstructions =
|
|
4571
|
-
if (
|
|
4572
|
-
const content =
|
|
4913
|
+
const copilotInstructions = join19(cwd, ".github", "copilot-instructions.md");
|
|
4914
|
+
if (existsSync14(copilotInstructions)) {
|
|
4915
|
+
const content = readFileSync13(copilotInstructions, "utf8");
|
|
4573
4916
|
if (content.includes("elevate") || content.includes("Elevate")) {
|
|
4574
4917
|
env.skillConfigured = true;
|
|
4575
4918
|
}
|
|
@@ -4586,14 +4929,14 @@ function detectWindsurf() {
|
|
|
4586
4929
|
mcpConfigured: false,
|
|
4587
4930
|
skillConfigured: false
|
|
4588
4931
|
};
|
|
4589
|
-
const configPath = isWin ?
|
|
4590
|
-
const altPath =
|
|
4591
|
-
if (
|
|
4932
|
+
const configPath = isWin ? join19(appData(), "Codeium", "Windsurf", "mcp_config.json") : join19(home, ".codeium", "windsurf", "mcp_config.json");
|
|
4933
|
+
const altPath = join19(home, ".codeium", "windsurf", "mcp_config.json");
|
|
4934
|
+
if (existsSync14(configPath) || existsSync14(altPath)) {
|
|
4592
4935
|
env.detected = true;
|
|
4593
4936
|
env.reason = `Found Windsurf config`;
|
|
4594
|
-
const path =
|
|
4937
|
+
const path = existsSync14(configPath) ? configPath : altPath;
|
|
4595
4938
|
try {
|
|
4596
|
-
const content = JSON.parse(
|
|
4939
|
+
const content = JSON.parse(readFileSync13(path, "utf8"));
|
|
4597
4940
|
if (content?.mcpServers?.elevate) {
|
|
4598
4941
|
env.mcpConfigured = true;
|
|
4599
4942
|
}
|
|
@@ -4604,9 +4947,9 @@ function detectWindsurf() {
|
|
|
4604
4947
|
}
|
|
4605
4948
|
function mergeJsonMcpServer(filePath, serverKey, topLevelKey) {
|
|
4606
4949
|
let existing = {};
|
|
4607
|
-
if (
|
|
4950
|
+
if (existsSync14(filePath)) {
|
|
4608
4951
|
try {
|
|
4609
|
-
existing = JSON.parse(
|
|
4952
|
+
existing = JSON.parse(readFileSync13(filePath, "utf8"));
|
|
4610
4953
|
} catch {
|
|
4611
4954
|
existing = {};
|
|
4612
4955
|
}
|
|
@@ -4618,15 +4961,15 @@ function mergeJsonMcpServer(filePath, serverKey, topLevelKey) {
|
|
|
4618
4961
|
servers[serverKey] = mcpServerEntry();
|
|
4619
4962
|
existing[topLevelKey] = servers;
|
|
4620
4963
|
const dir = filePath.substring(0, filePath.lastIndexOf(isWin ? "\\" : "/"));
|
|
4621
|
-
if (dir && !
|
|
4964
|
+
if (dir && !existsSync14(dir)) {
|
|
4622
4965
|
mkdirSync2(dir, { recursive: true });
|
|
4623
4966
|
}
|
|
4624
4967
|
writeFileSync5(filePath, JSON.stringify(existing, null, 2) + "\n", "utf8");
|
|
4625
4968
|
return { written: true, message: `${filePath}: elevate MCP server registered` };
|
|
4626
4969
|
}
|
|
4627
4970
|
function appendSkillToFile(filePath, content, marker) {
|
|
4628
|
-
if (
|
|
4629
|
-
const existing =
|
|
4971
|
+
if (existsSync14(filePath)) {
|
|
4972
|
+
const existing = readFileSync13(filePath, "utf8");
|
|
4630
4973
|
if (existing.includes(marker)) {
|
|
4631
4974
|
return { written: false, message: `${filePath}: skill already present` };
|
|
4632
4975
|
}
|
|
@@ -4634,48 +4977,48 @@ function appendSkillToFile(filePath, content, marker) {
|
|
|
4634
4977
|
return { written: true, message: `${filePath}: skill appended` };
|
|
4635
4978
|
}
|
|
4636
4979
|
const dir = filePath.substring(0, filePath.lastIndexOf(isWin ? "\\" : "/"));
|
|
4637
|
-
if (dir && !
|
|
4980
|
+
if (dir && !existsSync14(dir)) {
|
|
4638
4981
|
mkdirSync2(dir, { recursive: true });
|
|
4639
4982
|
}
|
|
4640
4983
|
writeFileSync5(filePath, content, "utf8");
|
|
4641
4984
|
return { written: true, message: `${filePath}: skill created` };
|
|
4642
4985
|
}
|
|
4643
4986
|
function configureCursor(cwd, result) {
|
|
4644
|
-
const mcpPath =
|
|
4987
|
+
const mcpPath = join19(cwd, ".cursor", "mcp.json");
|
|
4645
4988
|
const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
|
|
4646
4989
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Cursor MCP: ${mcp.message}`);
|
|
4647
|
-
const rulesPath =
|
|
4990
|
+
const rulesPath = join19(cwd, ".cursorrules");
|
|
4648
4991
|
const skill = appendSkillToFile(rulesPath, ELEVATE_SKILL_CONTENT, "elevate_scan");
|
|
4649
4992
|
result.actions.push(`${skill.written ? "\u2705" : "\u23ED\uFE0F"} Cursor Rules: ${skill.message}`);
|
|
4650
4993
|
}
|
|
4651
4994
|
function configureClaudeDesktop(result) {
|
|
4652
|
-
const configPath = isWin ?
|
|
4995
|
+
const configPath = isWin ? join19(appData(), "Claude", "claude_desktop_config.json") : join19(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
4653
4996
|
const mcp = mergeJsonMcpServer(configPath, "elevate", "mcpServers");
|
|
4654
4997
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Claude Desktop MCP: ${mcp.message}`);
|
|
4655
4998
|
}
|
|
4656
4999
|
function configureClaudeCode(cwd, result) {
|
|
4657
|
-
const mcpPath =
|
|
5000
|
+
const mcpPath = join19(cwd, ".mcp.json");
|
|
4658
5001
|
const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
|
|
4659
5002
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Claude Code MCP: ${mcp.message}`);
|
|
4660
|
-
const claudeMdPath =
|
|
5003
|
+
const claudeMdPath = join19(cwd, "CLAUDE.md");
|
|
4661
5004
|
const skill = appendSkillToFile(claudeMdPath, ELEVATE_SKILL_CONTENT, "elevate_scan");
|
|
4662
5005
|
result.actions.push(`${skill.written ? "\u2705" : "\u23ED\uFE0F"} Claude Code Skill: ${skill.message}`);
|
|
4663
5006
|
}
|
|
4664
5007
|
function configureAntigravity(cwd, result) {
|
|
4665
|
-
const mcpPath =
|
|
5008
|
+
const mcpPath = join19(cwd, ".agents", "mcp_config.json");
|
|
4666
5009
|
const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
|
|
4667
5010
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Antigravity MCP: ${mcp.message}`);
|
|
4668
5011
|
}
|
|
4669
5012
|
function configureVSCode(cwd, result) {
|
|
4670
|
-
const mcpPath =
|
|
5013
|
+
const mcpPath = join19(cwd, ".vscode", "mcp.json");
|
|
4671
5014
|
const mcp = mergeJsonMcpServer(mcpPath, "elevate", "servers");
|
|
4672
5015
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} VS Code MCP: ${mcp.message}`);
|
|
4673
|
-
const instructionsPath =
|
|
5016
|
+
const instructionsPath = join19(cwd, ".github", "copilot-instructions.md");
|
|
4674
5017
|
const skill = appendSkillToFile(instructionsPath, ELEVATE_SKILL_CONTENT, "elevate_scan");
|
|
4675
5018
|
result.actions.push(`${skill.written ? "\u2705" : "\u23ED\uFE0F"} Copilot Instructions: ${skill.message}`);
|
|
4676
5019
|
}
|
|
4677
5020
|
function configureWindsurf(result) {
|
|
4678
|
-
const configPath = isWin ?
|
|
5021
|
+
const configPath = isWin ? join19(appData(), "Codeium", "Windsurf", "mcp_config.json") : join19(home, ".codeium", "windsurf", "mcp_config.json");
|
|
4679
5022
|
const mcp = mergeJsonMcpServer(configPath, "elevate", "mcpServers");
|
|
4680
5023
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Windsurf MCP: ${mcp.message}`);
|
|
4681
5024
|
}
|
|
@@ -4708,7 +5051,7 @@ async function handleInitCommand(options) {
|
|
|
4708
5051
|
console.log(` Creating portable .mcp.json for broad compatibility.
|
|
4709
5052
|
`);
|
|
4710
5053
|
}
|
|
4711
|
-
const mcpPath =
|
|
5054
|
+
const mcpPath = join19(cwd, ".mcp.json");
|
|
4712
5055
|
const mcp = mergeJsonMcpServer(mcpPath, "elevate", "mcpServers");
|
|
4713
5056
|
result.actions.push(`${mcp.written ? "\u2705" : "\u23ED\uFE0F"} Generic MCP: ${mcp.message}`);
|
|
4714
5057
|
} else {
|
|
@@ -4917,8 +5260,10 @@ async function startMcpServer(config) {
|
|
|
4917
5260
|
try {
|
|
4918
5261
|
if (name === "elevate_discover_modules") {
|
|
4919
5262
|
const modules = await strategy.discovery.discover(config.rootDir);
|
|
5263
|
+
const packageManager = strategy.packageManagerFor?.(config.rootDir);
|
|
4920
5264
|
return reply({
|
|
4921
5265
|
ecosystem,
|
|
5266
|
+
...packageManager ? { packageManager } : {},
|
|
4922
5267
|
count: modules.length,
|
|
4923
5268
|
modules: modules.map((m) => ({
|
|
4924
5269
|
id: m.id,
|