depfresh 0.9.2
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 -0
- package/README.md +279 -0
- package/dist/chunks/config.mjs +126 -0
- package/dist/chunks/index.mjs +21 -0
- package/dist/chunks/index2.mjs +624 -0
- package/dist/chunks/index3.mjs +145 -0
- package/dist/chunks/interactive.mjs +85 -0
- package/dist/chunks/normalize-args.mjs +59 -0
- package/dist/cli.d.mts +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.mjs +202 -0
- package/dist/index.d.mts +204 -0
- package/dist/index.d.ts +204 -0
- package/dist/index.mjs +21 -0
- package/dist/shared/depfresh.B1o7OHO_.mjs +63 -0
- package/dist/shared/depfresh.ClIHCCxD.mjs +1938 -0
- package/package.json +86 -0
|
@@ -0,0 +1,1938 @@
|
|
|
1
|
+
import c from 'ansis';
|
|
2
|
+
import { C as CacheError, a as ConfigError, c as createLogger, R as RegistryError, b as ResolveError, W as WriteError } from './depfresh.B1o7OHO_.mjs';
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { findUpSync } from 'find-up-simple';
|
|
6
|
+
import { parse } from 'ini';
|
|
7
|
+
import { join, dirname, resolve } from 'pathe';
|
|
8
|
+
import { execSync } from 'node:child_process';
|
|
9
|
+
import detectIndent from 'detect-indent';
|
|
10
|
+
import * as semver from 'semver';
|
|
11
|
+
import { parsePnpmWorkspaceYaml } from 'pnpm-workspace-yaml';
|
|
12
|
+
import YAML from 'yaml';
|
|
13
|
+
import pLimit from 'p-limit';
|
|
14
|
+
import Database from 'better-sqlite3';
|
|
15
|
+
import { glob } from 'tinyglobby';
|
|
16
|
+
|
|
17
|
+
function detectPackageManager(cwd, packages) {
|
|
18
|
+
for (const pkg of packages) {
|
|
19
|
+
if (pkg.packageManager?.name) {
|
|
20
|
+
return pkg.packageManager.name;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (existsSync(join(cwd, "bun.lock")) || existsSync(join(cwd, "bun.lockb"))) return "bun";
|
|
24
|
+
if (existsSync(join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
25
|
+
if (existsSync(join(cwd, "yarn.lock"))) return "yarn";
|
|
26
|
+
return "npm";
|
|
27
|
+
}
|
|
28
|
+
async function runInstall(cwd, packages, logger) {
|
|
29
|
+
const pm = detectPackageManager(cwd, packages);
|
|
30
|
+
try {
|
|
31
|
+
logger.info(`Running ${pm} install...`);
|
|
32
|
+
execSync(`${pm} install`, { cwd, stdio: "inherit" });
|
|
33
|
+
} catch {
|
|
34
|
+
logger.error(`${pm} install failed`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function runUpdate(cwd, packages, logger) {
|
|
38
|
+
const pm = detectPackageManager(cwd, packages);
|
|
39
|
+
try {
|
|
40
|
+
logger.info(`Running ${pm} update...`);
|
|
41
|
+
execSync(`${pm} update`, { cwd, stdio: "inherit" });
|
|
42
|
+
} catch {
|
|
43
|
+
logger.error(`${pm} update failed`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const CACHE_DIR = join(homedir(), ".depzy");
|
|
48
|
+
const CACHE_DB = join(CACHE_DIR, "cache.db");
|
|
49
|
+
const SCHEMA = `
|
|
50
|
+
CREATE TABLE IF NOT EXISTS registry_cache (
|
|
51
|
+
package TEXT NOT NULL,
|
|
52
|
+
data TEXT NOT NULL,
|
|
53
|
+
fetched_at INTEGER NOT NULL,
|
|
54
|
+
expires_at INTEGER NOT NULL,
|
|
55
|
+
PRIMARY KEY (package)
|
|
56
|
+
)
|
|
57
|
+
`;
|
|
58
|
+
const INDEX = `CREATE INDEX IF NOT EXISTS idx_expires ON registry_cache(expires_at)`;
|
|
59
|
+
function createSqliteCache() {
|
|
60
|
+
mkdirSync(CACHE_DIR, { recursive: true });
|
|
61
|
+
let db;
|
|
62
|
+
try {
|
|
63
|
+
db = new Database(CACHE_DB);
|
|
64
|
+
} catch {
|
|
65
|
+
return createMemoryFallback();
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
69
|
+
db.exec("PRAGMA synchronous = NORMAL");
|
|
70
|
+
db.exec(SCHEMA);
|
|
71
|
+
db.exec(INDEX);
|
|
72
|
+
} catch {
|
|
73
|
+
db.close();
|
|
74
|
+
return createMemoryFallback();
|
|
75
|
+
}
|
|
76
|
+
const getStmt = db.prepare("SELECT data FROM registry_cache WHERE package = ? AND expires_at > ?");
|
|
77
|
+
const setStmt = db.prepare(
|
|
78
|
+
"INSERT OR REPLACE INTO registry_cache (package, data, fetched_at, expires_at) VALUES (?, ?, ?, ?)"
|
|
79
|
+
);
|
|
80
|
+
const hasStmt = db.prepare("SELECT 1 FROM registry_cache WHERE package = ? AND expires_at > ?");
|
|
81
|
+
const clearStmt = db.prepare("DELETE FROM registry_cache");
|
|
82
|
+
const countStmt = db.prepare("SELECT COUNT(*) as count FROM registry_cache WHERE expires_at > ?");
|
|
83
|
+
const pruneStmt = db.prepare("DELETE FROM registry_cache WHERE expires_at <= ?");
|
|
84
|
+
const deleteStmt = db.prepare("DELETE FROM registry_cache WHERE package = ?");
|
|
85
|
+
let hits = 0;
|
|
86
|
+
let misses = 0;
|
|
87
|
+
pruneStmt.run(Date.now());
|
|
88
|
+
return {
|
|
89
|
+
get(key) {
|
|
90
|
+
let row;
|
|
91
|
+
try {
|
|
92
|
+
row = getStmt.get(key, Date.now());
|
|
93
|
+
} catch (error) {
|
|
94
|
+
throw new CacheError(`Failed to read cache entry for ${key}`, { cause: error });
|
|
95
|
+
}
|
|
96
|
+
if (row) {
|
|
97
|
+
try {
|
|
98
|
+
const parsed = JSON.parse(row.data);
|
|
99
|
+
hits++;
|
|
100
|
+
return parsed;
|
|
101
|
+
} catch {
|
|
102
|
+
deleteStmt.run(key);
|
|
103
|
+
misses++;
|
|
104
|
+
return void 0;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
misses++;
|
|
108
|
+
return void 0;
|
|
109
|
+
},
|
|
110
|
+
set(key, data, ttl) {
|
|
111
|
+
const now = Date.now();
|
|
112
|
+
try {
|
|
113
|
+
setStmt.run(key, JSON.stringify(data), now, now + ttl);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
throw new CacheError(`Failed to write cache entry for ${key}`, { cause: error });
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
has(key) {
|
|
119
|
+
try {
|
|
120
|
+
return !!hasStmt.get(key, Date.now());
|
|
121
|
+
} catch (error) {
|
|
122
|
+
throw new CacheError(`Failed to check cache entry for ${key}`, { cause: error });
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
clear() {
|
|
126
|
+
try {
|
|
127
|
+
clearStmt.run();
|
|
128
|
+
} catch (error) {
|
|
129
|
+
throw new CacheError("Failed to clear cache", { cause: error });
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
close() {
|
|
133
|
+
try {
|
|
134
|
+
db.close();
|
|
135
|
+
} catch (error) {
|
|
136
|
+
throw new CacheError("Failed to close cache database", { cause: error });
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
stats() {
|
|
140
|
+
let row;
|
|
141
|
+
try {
|
|
142
|
+
row = countStmt.get(Date.now());
|
|
143
|
+
} catch (error) {
|
|
144
|
+
throw new CacheError("Failed to read cache stats", { cause: error });
|
|
145
|
+
}
|
|
146
|
+
return { hits, misses, size: row.count };
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function createMemoryFallback() {
|
|
151
|
+
const store = /* @__PURE__ */ new Map();
|
|
152
|
+
let hits = 0;
|
|
153
|
+
let misses = 0;
|
|
154
|
+
return {
|
|
155
|
+
get(key) {
|
|
156
|
+
const entry = store.get(key);
|
|
157
|
+
if (entry && entry.expiresAt > Date.now()) {
|
|
158
|
+
hits++;
|
|
159
|
+
return entry.data;
|
|
160
|
+
}
|
|
161
|
+
if (entry) store.delete(key);
|
|
162
|
+
misses++;
|
|
163
|
+
return void 0;
|
|
164
|
+
},
|
|
165
|
+
set(key, data, ttl) {
|
|
166
|
+
store.set(key, { data, expiresAt: Date.now() + ttl });
|
|
167
|
+
},
|
|
168
|
+
has(key) {
|
|
169
|
+
const entry = store.get(key);
|
|
170
|
+
return !!entry && entry.expiresAt > Date.now();
|
|
171
|
+
},
|
|
172
|
+
clear() {
|
|
173
|
+
store.clear();
|
|
174
|
+
},
|
|
175
|
+
close() {
|
|
176
|
+
store.clear();
|
|
177
|
+
},
|
|
178
|
+
stats() {
|
|
179
|
+
return { hits, misses, size: store.size };
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function loadCatalogs(cwd, options) {
|
|
185
|
+
const catalogs = [];
|
|
186
|
+
const loaders = await Promise.all([
|
|
187
|
+
Promise.resolve().then(function () { return pnpm; }).then((m) => m.pnpmCatalogLoader),
|
|
188
|
+
Promise.resolve().then(function () { return bun; }).then((m) => m.bunCatalogLoader),
|
|
189
|
+
Promise.resolve().then(function () { return yarn; }).then((m) => m.yarnCatalogLoader)
|
|
190
|
+
]);
|
|
191
|
+
const detected = await Promise.all(
|
|
192
|
+
loaders.map(async (loader) => {
|
|
193
|
+
if (await loader.detect(cwd)) {
|
|
194
|
+
return loader.load(cwd, options);
|
|
195
|
+
}
|
|
196
|
+
return [];
|
|
197
|
+
})
|
|
198
|
+
);
|
|
199
|
+
for (const result of detected) {
|
|
200
|
+
catalogs.push(...result);
|
|
201
|
+
}
|
|
202
|
+
return catalogs;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const PREFIX_RE = /^(\^|~|>=?|<=?|=)?/;
|
|
206
|
+
function getVersionPrefix(version) {
|
|
207
|
+
const match = version.match(PREFIX_RE);
|
|
208
|
+
return match?.[1] ?? "";
|
|
209
|
+
}
|
|
210
|
+
function isRange(version) {
|
|
211
|
+
return /[~^>=<|*x ]/.test(version);
|
|
212
|
+
}
|
|
213
|
+
function isLocked(version) {
|
|
214
|
+
return !isRange(version) && !!semver.valid(version);
|
|
215
|
+
}
|
|
216
|
+
function getMaxSatisfying(versions, range) {
|
|
217
|
+
const valid = versions.filter((v) => semver.satisfies(v, range));
|
|
218
|
+
if (valid.length === 0) return null;
|
|
219
|
+
return valid.reduce((max, v) => semver.gt(v, max) ? v : max, valid[0]);
|
|
220
|
+
}
|
|
221
|
+
function getMaxVersion(versions) {
|
|
222
|
+
if (versions.length === 0) return null;
|
|
223
|
+
return versions.reduce((max, v) => semver.gt(v, max) ? v : max, versions[0]);
|
|
224
|
+
}
|
|
225
|
+
function getDiff(current, target) {
|
|
226
|
+
const c = semver.coerce(current);
|
|
227
|
+
const t = semver.coerce(target);
|
|
228
|
+
if (!(c && t)) return "error";
|
|
229
|
+
if (semver.eq(c, t)) return "none";
|
|
230
|
+
const diff = semver.diff(c, t);
|
|
231
|
+
if (!diff) return "none";
|
|
232
|
+
if (diff.startsWith("major") || diff === "premajor") return "major";
|
|
233
|
+
if (diff.startsWith("minor") || diff === "preminor") return "minor";
|
|
234
|
+
if (diff.startsWith("patch") || diff === "prepatch" || diff === "prerelease") return "patch";
|
|
235
|
+
return "patch";
|
|
236
|
+
}
|
|
237
|
+
function applyVersionPrefix(version, prefix) {
|
|
238
|
+
if (!prefix) return version;
|
|
239
|
+
return `${prefix}${version}`;
|
|
240
|
+
}
|
|
241
|
+
function resolveTargetVersion(currentVersion, versions, distTags, mode) {
|
|
242
|
+
switch (mode) {
|
|
243
|
+
case "latest":
|
|
244
|
+
return distTags.latest ?? null;
|
|
245
|
+
case "newest": {
|
|
246
|
+
return getMaxVersion(versions);
|
|
247
|
+
}
|
|
248
|
+
case "next":
|
|
249
|
+
return distTags.next ?? distTags.latest ?? null;
|
|
250
|
+
case "major":
|
|
251
|
+
return getMaxVersion(versions);
|
|
252
|
+
case "minor": {
|
|
253
|
+
const current = semver.coerce(currentVersion);
|
|
254
|
+
if (!current) return null;
|
|
255
|
+
const minor = versions.filter((v) => {
|
|
256
|
+
const parsed = semver.parse(v);
|
|
257
|
+
return parsed && parsed.major === current.major;
|
|
258
|
+
});
|
|
259
|
+
return getMaxVersion(minor);
|
|
260
|
+
}
|
|
261
|
+
case "patch": {
|
|
262
|
+
const current = semver.coerce(currentVersion);
|
|
263
|
+
if (!current) return null;
|
|
264
|
+
const patch = versions.filter((v) => {
|
|
265
|
+
const parsed = semver.parse(v);
|
|
266
|
+
return parsed && parsed.major === current.major && parsed.minor === current.minor;
|
|
267
|
+
});
|
|
268
|
+
return getMaxVersion(patch);
|
|
269
|
+
}
|
|
270
|
+
default:
|
|
271
|
+
return getMaxSatisfying(versions, currentVersion) ?? distTags.latest ?? null;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function parseProtocol(version) {
|
|
276
|
+
const npmMatch = version.match(/^npm:(.+)@(.+)$/);
|
|
277
|
+
if (npmMatch) {
|
|
278
|
+
return { protocol: "npm", currentVersion: npmMatch[2] };
|
|
279
|
+
}
|
|
280
|
+
const jsrMatch = version.match(/^jsr:(.+)@(.+)$/);
|
|
281
|
+
if (jsrMatch) {
|
|
282
|
+
return { protocol: "jsr", currentVersion: jsrMatch[2] };
|
|
283
|
+
}
|
|
284
|
+
return { currentVersion: version };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function parseOverrideKey(key) {
|
|
288
|
+
if (key.startsWith("@")) {
|
|
289
|
+
const secondAt = key.indexOf("@", 1);
|
|
290
|
+
if (secondAt !== -1) {
|
|
291
|
+
return key.slice(0, secondAt);
|
|
292
|
+
}
|
|
293
|
+
return key;
|
|
294
|
+
}
|
|
295
|
+
const atIndex = key.indexOf("@");
|
|
296
|
+
if (atIndex !== -1) {
|
|
297
|
+
return key.slice(0, atIndex);
|
|
298
|
+
}
|
|
299
|
+
return key;
|
|
300
|
+
}
|
|
301
|
+
function getNestedField(obj, path) {
|
|
302
|
+
const parts = path.split(".");
|
|
303
|
+
let current = obj;
|
|
304
|
+
for (const part of parts) {
|
|
305
|
+
if (!current || typeof current !== "object") return void 0;
|
|
306
|
+
current = current[part];
|
|
307
|
+
}
|
|
308
|
+
return current;
|
|
309
|
+
}
|
|
310
|
+
function flattenOverrides(obj, source, deps, options, parents, includePatterns, excludePatterns, skipFn) {
|
|
311
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
312
|
+
if (typeof value === "string") {
|
|
313
|
+
const name = parseOverrideKey(key);
|
|
314
|
+
if (skipFn(name, value, options, includePatterns, excludePatterns)) continue;
|
|
315
|
+
const protocol = parseProtocol(value);
|
|
316
|
+
deps.push({
|
|
317
|
+
name,
|
|
318
|
+
currentVersion: protocol.currentVersion,
|
|
319
|
+
source,
|
|
320
|
+
update: !isLocked(protocol.currentVersion) || options.includeLocked,
|
|
321
|
+
parents: [...parents, key],
|
|
322
|
+
protocol: protocol.protocol
|
|
323
|
+
});
|
|
324
|
+
} else if (typeof value === "object" && value !== null) {
|
|
325
|
+
flattenOverrides(
|
|
326
|
+
value,
|
|
327
|
+
source,
|
|
328
|
+
deps,
|
|
329
|
+
options,
|
|
330
|
+
[...parents, key],
|
|
331
|
+
includePatterns,
|
|
332
|
+
excludePatterns,
|
|
333
|
+
skipFn
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function isGlob(pattern) {
|
|
340
|
+
return pattern.includes("*") && !/[\^$[\]()\\|+?]/.test(pattern);
|
|
341
|
+
}
|
|
342
|
+
function patternToRegex(pattern) {
|
|
343
|
+
const slashMatch = pattern.match(/^\/(.+)\/([gimsuy]*)$/);
|
|
344
|
+
if (slashMatch) {
|
|
345
|
+
return new RegExp(slashMatch[1], slashMatch[2]);
|
|
346
|
+
}
|
|
347
|
+
if (isGlob(pattern)) {
|
|
348
|
+
const escaped = pattern.replace(/[.@/]/g, "\\$&").replace(/\*/g, "[^/]*");
|
|
349
|
+
return new RegExp(`^${escaped}$`);
|
|
350
|
+
}
|
|
351
|
+
return new RegExp(pattern);
|
|
352
|
+
}
|
|
353
|
+
function compilePatternsStrict(patterns) {
|
|
354
|
+
const compiled = [];
|
|
355
|
+
for (const p of patterns) {
|
|
356
|
+
try {
|
|
357
|
+
compiled.push(patternToRegex(p));
|
|
358
|
+
} catch (error) {
|
|
359
|
+
throw new ConfigError(`Invalid dependency filter pattern: ${p}`, { cause: error });
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return compiled;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const DEP_FIELDS = [
|
|
366
|
+
"dependencies",
|
|
367
|
+
"devDependencies",
|
|
368
|
+
"peerDependencies",
|
|
369
|
+
"optionalDependencies"
|
|
370
|
+
];
|
|
371
|
+
const OVERRIDE_FIELDS = ["overrides", "resolutions", "pnpm.overrides"];
|
|
372
|
+
function isDepFieldEnabled(field, options) {
|
|
373
|
+
if (options.depFields?.[field] === false) return false;
|
|
374
|
+
if (field === "peerDependencies" && !options.peer) return false;
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
function shouldSkipDependency(name, version, options, includePatterns = [], excludePatterns = []) {
|
|
378
|
+
if (version.startsWith("workspace:") && !options.includeWorkspace) return true;
|
|
379
|
+
if (version.startsWith("catalog:")) return true;
|
|
380
|
+
if (/^(link|file|git|github|https?):/.test(version)) return true;
|
|
381
|
+
if (includePatterns.length && !includePatterns.some((re) => re.test(name))) {
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
if (excludePatterns.length && excludePatterns.some((re) => re.test(name))) {
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
function parseDependencies(raw, options) {
|
|
390
|
+
const deps = [];
|
|
391
|
+
const includePatterns = options.include?.length ? compilePatternsStrict(options.include) : [];
|
|
392
|
+
const excludePatterns = options.exclude?.length ? compilePatternsStrict(options.exclude) : [];
|
|
393
|
+
for (const field of DEP_FIELDS) {
|
|
394
|
+
if (!isDepFieldEnabled(field, options)) continue;
|
|
395
|
+
const section = raw[field];
|
|
396
|
+
if (!section || typeof section !== "object") continue;
|
|
397
|
+
for (const [name, version] of Object.entries(section)) {
|
|
398
|
+
if (shouldSkipDependency(name, version, options, includePatterns, excludePatterns)) continue;
|
|
399
|
+
const protocol = parseProtocol(version);
|
|
400
|
+
deps.push({
|
|
401
|
+
name,
|
|
402
|
+
currentVersion: protocol.currentVersion,
|
|
403
|
+
source: field,
|
|
404
|
+
update: !isLocked(protocol.currentVersion) || options.includeLocked,
|
|
405
|
+
parents: [],
|
|
406
|
+
protocol: protocol.protocol
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
for (const field of OVERRIDE_FIELDS) {
|
|
411
|
+
if (!isDepFieldEnabled(field, options)) continue;
|
|
412
|
+
const section = getNestedField(raw, field);
|
|
413
|
+
if (!section || typeof section !== "object") continue;
|
|
414
|
+
flattenOverrides(
|
|
415
|
+
section,
|
|
416
|
+
field,
|
|
417
|
+
deps,
|
|
418
|
+
options,
|
|
419
|
+
[],
|
|
420
|
+
includePatterns,
|
|
421
|
+
excludePatterns,
|
|
422
|
+
shouldSkipDependency
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
return deps;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function parsePackageManagerField(raw) {
|
|
429
|
+
const match = raw.match(/^(npm|pnpm|yarn|bun)@([^+]+)(?:\+(.+))?$/);
|
|
430
|
+
if (!match) return void 0;
|
|
431
|
+
return {
|
|
432
|
+
name: match[1],
|
|
433
|
+
version: match[2],
|
|
434
|
+
hash: match[3],
|
|
435
|
+
raw
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function belongsToNestedWorkspace(filepath, rootDir) {
|
|
440
|
+
const pkgDir = dirname(filepath);
|
|
441
|
+
const normalizedRoot = resolve(rootDir);
|
|
442
|
+
if (resolve(pkgDir) === normalizedRoot) return false;
|
|
443
|
+
const pnpmWs = findUpSync("pnpm-workspace.yaml", { cwd: pkgDir, stopAt: normalizedRoot });
|
|
444
|
+
if (pnpmWs && resolve(dirname(pnpmWs)) !== normalizedRoot) return true;
|
|
445
|
+
const yarnRc = findUpSync(".yarnrc.yml", { cwd: pkgDir, stopAt: normalizedRoot });
|
|
446
|
+
if (yarnRc && resolve(dirname(yarnRc)) !== normalizedRoot) return true;
|
|
447
|
+
try {
|
|
448
|
+
const content = JSON.parse(readFileSync(filepath, "utf-8"));
|
|
449
|
+
if (content.workspaces) return true;
|
|
450
|
+
} catch {
|
|
451
|
+
}
|
|
452
|
+
const parentDir = dirname(pkgDir);
|
|
453
|
+
if (resolve(parentDir) !== normalizedRoot) {
|
|
454
|
+
const nestedPkg = findUpSync("package.json", { cwd: parentDir, stopAt: normalizedRoot });
|
|
455
|
+
if (nestedPkg && resolve(dirname(nestedPkg)) !== normalizedRoot) {
|
|
456
|
+
try {
|
|
457
|
+
const content = JSON.parse(readFileSync(nestedPkg, "utf-8"));
|
|
458
|
+
if (content.workspaces) return true;
|
|
459
|
+
} catch {
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
const gitDir = findUpSync(".git", {
|
|
464
|
+
cwd: pkgDir,
|
|
465
|
+
stopAt: normalizedRoot,
|
|
466
|
+
type: "directory"
|
|
467
|
+
});
|
|
468
|
+
if (gitDir && resolve(dirname(gitDir)) !== normalizedRoot) return true;
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
async function loadPackages(options) {
|
|
473
|
+
const logger = createLogger(options.loglevel);
|
|
474
|
+
if (options.global) {
|
|
475
|
+
const { loadGlobalPackages } = await import('../chunks/index3.mjs').then(function (n) { return n.g; });
|
|
476
|
+
const packages2 = loadGlobalPackages();
|
|
477
|
+
logger.info(
|
|
478
|
+
`Found ${packages2.length} packages with ${packages2.reduce((sum, p) => sum + p.deps.length, 0)} dependencies`
|
|
479
|
+
);
|
|
480
|
+
return packages2;
|
|
481
|
+
}
|
|
482
|
+
const packages = [];
|
|
483
|
+
let jsonFiles = await glob(["**/package.json"], {
|
|
484
|
+
cwd: options.cwd,
|
|
485
|
+
ignore: options.ignorePaths,
|
|
486
|
+
absolute: true
|
|
487
|
+
});
|
|
488
|
+
if (options.ignoreOtherWorkspaces) {
|
|
489
|
+
const rootDir = resolve(options.cwd);
|
|
490
|
+
const before = jsonFiles.length;
|
|
491
|
+
jsonFiles = jsonFiles.filter((f) => !belongsToNestedWorkspace(f, rootDir));
|
|
492
|
+
const skipped = before - jsonFiles.length;
|
|
493
|
+
if (skipped > 0) {
|
|
494
|
+
logger.debug(`Skipped ${skipped} package(s) from nested workspaces`);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
for (const filepath of jsonFiles) {
|
|
498
|
+
try {
|
|
499
|
+
const content = readFileSync(filepath, "utf-8");
|
|
500
|
+
const raw = JSON.parse(content);
|
|
501
|
+
const indent = detectIndent(content).indent || " ";
|
|
502
|
+
const deps = parseDependencies(raw, options);
|
|
503
|
+
const meta = {
|
|
504
|
+
name: raw.name ?? dirname(filepath),
|
|
505
|
+
type: "package.json",
|
|
506
|
+
filepath,
|
|
507
|
+
deps,
|
|
508
|
+
resolved: [],
|
|
509
|
+
raw,
|
|
510
|
+
indent
|
|
511
|
+
};
|
|
512
|
+
if (raw.packageManager && typeof raw.packageManager === "string") {
|
|
513
|
+
meta.packageManager = parsePackageManagerField(raw.packageManager);
|
|
514
|
+
}
|
|
515
|
+
packages.push(meta);
|
|
516
|
+
logger.debug(`Loaded ${filepath} (${deps.length} deps)`);
|
|
517
|
+
} catch (error) {
|
|
518
|
+
logger.warn(`Failed to load ${filepath}:`, error);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
try {
|
|
522
|
+
const catalogs = await loadCatalogs(options.cwd, options);
|
|
523
|
+
for (const catalog of catalogs) {
|
|
524
|
+
const catalogTypeName = catalog.type === "pnpm" ? "pnpm-workspace" : catalog.type === "bun" ? "bun-workspace" : "yarn-workspace";
|
|
525
|
+
const displayName = catalog.name === "default" ? `${catalog.type} catalog` : `${catalog.type} catalog:${catalog.name}`;
|
|
526
|
+
packages.push({
|
|
527
|
+
name: displayName,
|
|
528
|
+
type: catalogTypeName,
|
|
529
|
+
filepath: catalog.filepath,
|
|
530
|
+
deps: catalog.deps,
|
|
531
|
+
resolved: [],
|
|
532
|
+
raw: catalog.raw,
|
|
533
|
+
indent: catalog.indent,
|
|
534
|
+
catalogs: [catalog]
|
|
535
|
+
});
|
|
536
|
+
logger.debug(`Loaded catalog ${displayName} (${catalog.deps.length} deps)`);
|
|
537
|
+
}
|
|
538
|
+
} catch (error) {
|
|
539
|
+
logger.warn("Failed to load workspace catalogs:", error);
|
|
540
|
+
}
|
|
541
|
+
logger.info(
|
|
542
|
+
`Found ${packages.length} packages with ${packages.reduce((sum, p) => sum + p.deps.length, 0)} dependencies`
|
|
543
|
+
);
|
|
544
|
+
return packages;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const DEFAULT_REGISTRY = "https://registry.npmjs.org/";
|
|
548
|
+
function loadNpmrc(cwd) {
|
|
549
|
+
const config = {
|
|
550
|
+
registries: /* @__PURE__ */ new Map(),
|
|
551
|
+
defaultRegistry: DEFAULT_REGISTRY,
|
|
552
|
+
strictSsl: true
|
|
553
|
+
};
|
|
554
|
+
const globalPath = process.env.npm_config_userconfig || join(homedir(), ".npmrc");
|
|
555
|
+
const projectFile = findUpSync(".npmrc", { cwd });
|
|
556
|
+
loadNpmrcFile(globalPath, config);
|
|
557
|
+
if (projectFile) {
|
|
558
|
+
loadNpmrcFile(projectFile, config);
|
|
559
|
+
}
|
|
560
|
+
applyEnvOverrides(config);
|
|
561
|
+
return config;
|
|
562
|
+
}
|
|
563
|
+
function loadNpmrcFile(filepath, config) {
|
|
564
|
+
let content;
|
|
565
|
+
try {
|
|
566
|
+
content = readFileSync(filepath, "utf-8");
|
|
567
|
+
} catch {
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
const parsed = parse(content);
|
|
571
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
572
|
+
if (key === "registry" && typeof value === "string") {
|
|
573
|
+
config.defaultRegistry = ensureTrailingSlash(value);
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
if (key === "proxy" && typeof value === "string") {
|
|
577
|
+
config.proxy = value;
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
if (key === "https-proxy" && typeof value === "string") {
|
|
581
|
+
config.httpsProxy = value;
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
if (key === "strict-ssl" && typeof value === "string") {
|
|
585
|
+
config.strictSsl = value !== "false";
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
if (key === "cafile" && typeof value === "string") {
|
|
589
|
+
config.cafile = resolve(filepath, "..", value);
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
const scopedMatch = key.match(/^(@[^:]+):registry$/);
|
|
593
|
+
if (scopedMatch && typeof value === "string") {
|
|
594
|
+
const scope = scopedMatch[1];
|
|
595
|
+
const url = ensureTrailingSlash(value);
|
|
596
|
+
const existing = config.registries.get(scope) ?? { url };
|
|
597
|
+
existing.url = url;
|
|
598
|
+
config.registries.set(scope, existing);
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
const tokenMatch = key.match(/^\/\/(.+)\/:_authToken$/);
|
|
602
|
+
if (tokenMatch && typeof value === "string") {
|
|
603
|
+
const host = tokenMatch[1];
|
|
604
|
+
for (const [_scope, reg] of config.registries) {
|
|
605
|
+
if (reg.url.includes(host)) {
|
|
606
|
+
reg.token = value;
|
|
607
|
+
reg.authType = "bearer";
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
if (config.defaultRegistry.includes(host)) {
|
|
611
|
+
const defaultReg = config.registries.get("default") ?? {
|
|
612
|
+
url: config.defaultRegistry
|
|
613
|
+
};
|
|
614
|
+
defaultReg.token = value;
|
|
615
|
+
defaultReg.authType = "bearer";
|
|
616
|
+
config.registries.set("default", defaultReg);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
function applyEnvOverrides(config) {
|
|
622
|
+
const envRegistry = process.env.npm_config_registry || process.env.NPM_CONFIG_REGISTRY;
|
|
623
|
+
if (envRegistry) {
|
|
624
|
+
config.defaultRegistry = ensureTrailingSlash(envRegistry);
|
|
625
|
+
}
|
|
626
|
+
const envProxy = process.env.npm_config_proxy || process.env.HTTP_PROXY || process.env.http_proxy;
|
|
627
|
+
if (envProxy) {
|
|
628
|
+
config.proxy = envProxy;
|
|
629
|
+
}
|
|
630
|
+
const envHttpsProxy = process.env.npm_config_https_proxy || process.env.HTTPS_PROXY || process.env.https_proxy;
|
|
631
|
+
if (envHttpsProxy) {
|
|
632
|
+
config.httpsProxy = envHttpsProxy;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
function getRegistryForPackage(name, config) {
|
|
636
|
+
const scopeMatch = name.match(/^(@[^/]+)\//);
|
|
637
|
+
if (scopeMatch) {
|
|
638
|
+
const scoped = config.registries.get(scopeMatch[1]);
|
|
639
|
+
if (scoped) return scoped;
|
|
640
|
+
}
|
|
641
|
+
const defaultReg = config.registries.get("default");
|
|
642
|
+
if (defaultReg) return defaultReg;
|
|
643
|
+
return { url: config.defaultRegistry };
|
|
644
|
+
}
|
|
645
|
+
function ensureTrailingSlash(url) {
|
|
646
|
+
return url.endsWith("/") ? url : `${url}/`;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function buildJsonPackage(name, updates) {
|
|
650
|
+
return {
|
|
651
|
+
name,
|
|
652
|
+
updates: updates.map((u) => ({
|
|
653
|
+
name: u.name,
|
|
654
|
+
current: u.currentVersion,
|
|
655
|
+
target: u.targetVersion,
|
|
656
|
+
diff: u.diff,
|
|
657
|
+
source: u.source,
|
|
658
|
+
...u.deprecated ? { deprecated: u.deprecated } : {},
|
|
659
|
+
...u.publishedAt ? { publishedAt: u.publishedAt } : {},
|
|
660
|
+
...u.currentVersionTime ? { currentVersionTime: u.currentVersionTime } : {}
|
|
661
|
+
}))
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
function outputJsonEnvelope(packages, options) {
|
|
665
|
+
const allUpdates = packages.flatMap((p) => p.updates);
|
|
666
|
+
const count = (diff) => allUpdates.filter((u) => u.diff === diff).length;
|
|
667
|
+
const output = {
|
|
668
|
+
packages,
|
|
669
|
+
summary: {
|
|
670
|
+
total: allUpdates.length,
|
|
671
|
+
major: count("major"),
|
|
672
|
+
minor: count("minor"),
|
|
673
|
+
patch: count("patch"),
|
|
674
|
+
packages: packages.length
|
|
675
|
+
},
|
|
676
|
+
meta: {
|
|
677
|
+
cwd: options.cwd,
|
|
678
|
+
mode: options.mode,
|
|
679
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
console.log(JSON.stringify(output, null, 2));
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
async function runExecute(command, cwd, logger) {
|
|
686
|
+
try {
|
|
687
|
+
logger.info(`Running: ${command}`);
|
|
688
|
+
execSync(command, { cwd, stdio: "inherit" });
|
|
689
|
+
} catch {
|
|
690
|
+
logger.error(`Command failed: ${command}`);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
function renderUpToDate(packageName) {
|
|
694
|
+
const log = console.log;
|
|
695
|
+
log();
|
|
696
|
+
log(c.cyan.bold(packageName));
|
|
697
|
+
log(` ${c.green("All dependencies are up to date")}`);
|
|
698
|
+
log();
|
|
699
|
+
}
|
|
700
|
+
async function selectInteractiveUpdates(updates, explain) {
|
|
701
|
+
const { runInteractive } = await import('../chunks/interactive.mjs');
|
|
702
|
+
return runInteractive(updates, { explain });
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function getPackageMode(packageName, packageMode, defaultMode) {
|
|
706
|
+
if (!packageMode) return defaultMode;
|
|
707
|
+
if (packageMode[packageName]) {
|
|
708
|
+
return packageMode[packageName];
|
|
709
|
+
}
|
|
710
|
+
for (const [pattern, mode] of Object.entries(packageMode)) {
|
|
711
|
+
if (pattern === packageName) continue;
|
|
712
|
+
try {
|
|
713
|
+
const regex = patternToRegex(pattern);
|
|
714
|
+
if (regex.test(packageName)) {
|
|
715
|
+
return mode;
|
|
716
|
+
}
|
|
717
|
+
} catch {
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return defaultMode;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
async function fetchPackageData(name, options) {
|
|
724
|
+
const registry = getRegistryForPackage(name, options.npmrc);
|
|
725
|
+
if (name.startsWith("jsr:")) {
|
|
726
|
+
return fetchJsrPackage(name.slice(4), options);
|
|
727
|
+
}
|
|
728
|
+
return fetchNpmPackage(name, registry, options);
|
|
729
|
+
}
|
|
730
|
+
async function fetchNpmPackage(name, registry, options) {
|
|
731
|
+
const encodedName = name.startsWith("@") ? `@${encodeURIComponent(name.slice(1))}` : encodeURIComponent(name);
|
|
732
|
+
const url = `${registry.url}${encodedName}`;
|
|
733
|
+
const headers = {
|
|
734
|
+
accept: "application/vnd.npm.install-v1+json"
|
|
735
|
+
};
|
|
736
|
+
if (registry.token) {
|
|
737
|
+
headers.authorization = registry.authType === "basic" ? `Basic ${registry.token}` : `Bearer ${registry.token}`;
|
|
738
|
+
}
|
|
739
|
+
const json = await fetchWithRetry(url, headers, options);
|
|
740
|
+
const versionsObj = json.versions ?? {};
|
|
741
|
+
const versions = Object.keys(versionsObj).filter((v) => semver.valid(v));
|
|
742
|
+
const distTags = json["dist-tags"] ?? {};
|
|
743
|
+
const time = json.time ?? {};
|
|
744
|
+
const deprecated = {};
|
|
745
|
+
const provenance = {};
|
|
746
|
+
const engines = {};
|
|
747
|
+
for (const [ver, data] of Object.entries(versionsObj)) {
|
|
748
|
+
if (data.deprecated) {
|
|
749
|
+
deprecated[ver] = String(data.deprecated);
|
|
750
|
+
}
|
|
751
|
+
provenance[ver] = data.hasSignatures ? "attested" : "none";
|
|
752
|
+
const enginesObj = data.engines;
|
|
753
|
+
if (enginesObj?.node) {
|
|
754
|
+
engines[ver] = enginesObj.node;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
return {
|
|
758
|
+
name,
|
|
759
|
+
versions,
|
|
760
|
+
distTags,
|
|
761
|
+
time,
|
|
762
|
+
deprecated: Object.keys(deprecated).length > 0 ? deprecated : void 0,
|
|
763
|
+
provenance: Object.keys(provenance).length > 0 ? provenance : void 0,
|
|
764
|
+
engines: Object.keys(engines).length > 0 ? engines : void 0,
|
|
765
|
+
description: json.description,
|
|
766
|
+
homepage: json.homepage,
|
|
767
|
+
repository: typeof json.repository === "string" ? json.repository : json.repository?.url
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
async function fetchJsrPackage(name, options) {
|
|
771
|
+
const url = `https://jsr.io/${name}/meta.json`;
|
|
772
|
+
const json = await fetchWithRetry(url, {}, options);
|
|
773
|
+
const versionsObj = json.versions ?? {};
|
|
774
|
+
const versions = Object.keys(versionsObj);
|
|
775
|
+
const latest = json.latest ?? versions[versions.length - 1] ?? "";
|
|
776
|
+
return {
|
|
777
|
+
name: `jsr:${name}`,
|
|
778
|
+
versions,
|
|
779
|
+
distTags: { latest }
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
async function fetchWithRetry(url, headers, options, attempt = 0) {
|
|
783
|
+
const controller = new AbortController();
|
|
784
|
+
const timer = setTimeout(() => controller.abort(), options.timeout);
|
|
785
|
+
try {
|
|
786
|
+
const response = await fetch(url, {
|
|
787
|
+
headers,
|
|
788
|
+
signal: controller.signal
|
|
789
|
+
});
|
|
790
|
+
if (!response.ok) {
|
|
791
|
+
throw new RegistryError(
|
|
792
|
+
`HTTP ${response.status}: ${response.statusText} for ${url}`,
|
|
793
|
+
response.status,
|
|
794
|
+
url
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
return await response.json();
|
|
798
|
+
} catch (error) {
|
|
799
|
+
if (error instanceof RegistryError && error.status >= 400 && error.status < 500) {
|
|
800
|
+
throw error;
|
|
801
|
+
}
|
|
802
|
+
if (attempt < options.retries) {
|
|
803
|
+
const delay = Math.min(1e3 * 2 ** attempt, 5e3);
|
|
804
|
+
options.logger.debug(`Retry ${attempt + 1}/${options.retries} for ${url} in ${delay}ms`);
|
|
805
|
+
await sleep(delay);
|
|
806
|
+
return fetchWithRetry(url, headers, options, attempt + 1);
|
|
807
|
+
}
|
|
808
|
+
if (error instanceof RegistryError) {
|
|
809
|
+
throw error;
|
|
810
|
+
}
|
|
811
|
+
if (error instanceof ResolveError) {
|
|
812
|
+
throw error;
|
|
813
|
+
}
|
|
814
|
+
if (isAbortError(error)) {
|
|
815
|
+
throw new ResolveError(`Request timeout after ${options.timeout}ms for ${url}`, {
|
|
816
|
+
cause: error
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
const causeMessage = error instanceof Error ? `: ${error.message}` : "";
|
|
820
|
+
throw new ResolveError(`Network failure while fetching ${url}${causeMessage}`, { cause: error });
|
|
821
|
+
} finally {
|
|
822
|
+
clearTimeout(timer);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
function isAbortError(error) {
|
|
826
|
+
if (!error || typeof error !== "object") return false;
|
|
827
|
+
const named = error;
|
|
828
|
+
return named.name === "AbortError";
|
|
829
|
+
}
|
|
830
|
+
function sleep(ms) {
|
|
831
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
function filterVersions(pkgData, dep, options) {
|
|
835
|
+
const currentPrerelease = semver.prerelease(dep.currentVersion);
|
|
836
|
+
const currentChannel = currentPrerelease?.[0];
|
|
837
|
+
let filtered = pkgData.versions.filter((v) => {
|
|
838
|
+
if (pkgData.deprecated?.[v] && !pkgData.deprecated?.[dep.currentVersion]) {
|
|
839
|
+
return false;
|
|
840
|
+
}
|
|
841
|
+
const vPrerelease = semver.prerelease(v);
|
|
842
|
+
if (vPrerelease?.length && !currentPrerelease?.length) {
|
|
843
|
+
return false;
|
|
844
|
+
}
|
|
845
|
+
if (vPrerelease?.length && currentPrerelease?.length) {
|
|
846
|
+
const vChannel = vPrerelease[0];
|
|
847
|
+
if (typeof currentChannel === "string" && typeof vChannel === "string") {
|
|
848
|
+
if (vChannel !== currentChannel) {
|
|
849
|
+
return false;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
return true;
|
|
854
|
+
});
|
|
855
|
+
if (options?.cooldown && options.cooldown > 0) {
|
|
856
|
+
filtered = filterVersionsByMaturityPeriod(filtered, pkgData.time, options.cooldown);
|
|
857
|
+
}
|
|
858
|
+
return filtered;
|
|
859
|
+
}
|
|
860
|
+
function filterVersionsByMaturityPeriod(versions, time, days) {
|
|
861
|
+
if (!time || days <= 0) return versions;
|
|
862
|
+
const cutoff = Date.now() - days * 864e5;
|
|
863
|
+
const filtered = versions.filter((v) => {
|
|
864
|
+
const published = time[v];
|
|
865
|
+
if (!published) return true;
|
|
866
|
+
return new Date(published).getTime() <= cutoff;
|
|
867
|
+
});
|
|
868
|
+
return filtered.length > 0 ? filtered : versions;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
async function resolveDependency(dep, options, cache, npmrc, logger, privatePackages) {
|
|
872
|
+
const packageName = dep.aliasName ?? dep.name;
|
|
873
|
+
if (privatePackages?.has(packageName)) {
|
|
874
|
+
logger.debug(`Skipping private workspace package: ${packageName}`);
|
|
875
|
+
return null;
|
|
876
|
+
}
|
|
877
|
+
const mode = getPackageMode(packageName, options.packageMode, options.mode);
|
|
878
|
+
if (mode === "ignore") {
|
|
879
|
+
logger.debug(`Ignoring ${packageName} (mode: ignore)`);
|
|
880
|
+
return null;
|
|
881
|
+
}
|
|
882
|
+
let pkgData = cache.get(packageName);
|
|
883
|
+
if (!pkgData) {
|
|
884
|
+
try {
|
|
885
|
+
pkgData = await fetchPackageData(packageName, {
|
|
886
|
+
npmrc,
|
|
887
|
+
timeout: options.timeout,
|
|
888
|
+
retries: options.retries,
|
|
889
|
+
logger
|
|
890
|
+
});
|
|
891
|
+
cache.set(packageName, pkgData, options.cacheTTL);
|
|
892
|
+
} catch (error) {
|
|
893
|
+
logger.debug(`Failed to fetch ${packageName}: ${error}`);
|
|
894
|
+
return {
|
|
895
|
+
...dep,
|
|
896
|
+
targetVersion: dep.currentVersion,
|
|
897
|
+
diff: "error",
|
|
898
|
+
pkgData: { name: packageName, versions: [], distTags: {} }
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
const versions = filterVersions(pkgData, dep, options);
|
|
903
|
+
const targetVersion = resolveTargetVersion(dep.currentVersion, versions, pkgData.distTags, mode);
|
|
904
|
+
if (!targetVersion) {
|
|
905
|
+
return null;
|
|
906
|
+
}
|
|
907
|
+
const prefix = getVersionPrefix(dep.currentVersion);
|
|
908
|
+
const prefixedTarget = applyVersionPrefix(targetVersion, prefix);
|
|
909
|
+
const diff = getDiff(dep.currentVersion, targetVersion);
|
|
910
|
+
if (diff === "none" && !options.force) {
|
|
911
|
+
return null;
|
|
912
|
+
}
|
|
913
|
+
const cleanCurrent = semver.coerce(dep.currentVersion)?.version ?? void 0;
|
|
914
|
+
const currentProvenance = cleanCurrent ? pkgData.provenance?.[cleanCurrent] : void 0;
|
|
915
|
+
const targetProvenance = pkgData.provenance?.[targetVersion];
|
|
916
|
+
const nodeCompat = pkgData.engines?.[targetVersion];
|
|
917
|
+
const nodeCompatible = nodeCompat ? semver.satisfies(process.version, nodeCompat) : void 0;
|
|
918
|
+
return {
|
|
919
|
+
...dep,
|
|
920
|
+
targetVersion: prefixedTarget,
|
|
921
|
+
diff,
|
|
922
|
+
pkgData,
|
|
923
|
+
deprecated: pkgData.deprecated?.[targetVersion],
|
|
924
|
+
latestVersion: pkgData.distTags.latest,
|
|
925
|
+
publishedAt: pkgData.time?.[targetVersion],
|
|
926
|
+
currentVersionTime: cleanCurrent ? pkgData.time?.[cleanCurrent] : void 0,
|
|
927
|
+
provenance: targetProvenance,
|
|
928
|
+
currentProvenance,
|
|
929
|
+
nodeCompat,
|
|
930
|
+
nodeCompatible
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
async function resolvePackage(pkg, options, externalCache, externalNpmrc, privatePackages, onDependencyProcessed) {
|
|
935
|
+
const logger = createLogger(options.loglevel);
|
|
936
|
+
const npmrc = externalNpmrc ?? loadNpmrc(options.cwd);
|
|
937
|
+
const cache = externalCache ?? createSqliteCache();
|
|
938
|
+
const ownCache = !externalCache;
|
|
939
|
+
const limit = pLimit(options.concurrency);
|
|
940
|
+
try {
|
|
941
|
+
const results = await Promise.allSettled(
|
|
942
|
+
pkg.deps.filter((dep) => dep.update).map(
|
|
943
|
+
(dep) => limit(async () => {
|
|
944
|
+
try {
|
|
945
|
+
return await resolveDependency(dep, options, cache, npmrc, logger, privatePackages);
|
|
946
|
+
} finally {
|
|
947
|
+
await onDependencyProcessed?.(pkg, dep);
|
|
948
|
+
}
|
|
949
|
+
})
|
|
950
|
+
)
|
|
951
|
+
);
|
|
952
|
+
const resolved = [];
|
|
953
|
+
for (const result of results) {
|
|
954
|
+
if (result.status === "fulfilled" && result.value) {
|
|
955
|
+
resolved.push(result.value);
|
|
956
|
+
options.onDependencyResolved?.(pkg, result.value);
|
|
957
|
+
} else if (result.status === "rejected") {
|
|
958
|
+
logger.debug(`Resolution failed: ${result.reason}`);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
return resolved;
|
|
962
|
+
} finally {
|
|
963
|
+
if (ownCache) {
|
|
964
|
+
const stats = cache.stats();
|
|
965
|
+
cache.close();
|
|
966
|
+
logger.debug(`Cache stats: ${stats.hits} hits, ${stats.misses} misses, ${stats.size} entries`);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
function parseCatalogDeps(catalog, parentPath, options) {
|
|
972
|
+
const deps = [];
|
|
973
|
+
for (const [name, version] of Object.entries(catalog)) {
|
|
974
|
+
deps.push({
|
|
975
|
+
name,
|
|
976
|
+
currentVersion: version,
|
|
977
|
+
source: "catalog",
|
|
978
|
+
update: !isLocked(version) || options.includeLocked,
|
|
979
|
+
parents: [parentPath]
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
return deps;
|
|
983
|
+
}
|
|
984
|
+
const bunCatalogLoader = {
|
|
985
|
+
async detect(cwd) {
|
|
986
|
+
const pkgPath = join(cwd, "package.json");
|
|
987
|
+
if (!existsSync(pkgPath)) return false;
|
|
988
|
+
try {
|
|
989
|
+
const raw = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
990
|
+
return !!raw.workspaces?.catalog || !!raw.workspaces?.catalogs;
|
|
991
|
+
} catch {
|
|
992
|
+
return false;
|
|
993
|
+
}
|
|
994
|
+
},
|
|
995
|
+
async load(cwd, options) {
|
|
996
|
+
const filepath = join(cwd, "package.json");
|
|
997
|
+
const content = readFileSync(filepath, "utf-8");
|
|
998
|
+
const raw = JSON.parse(content);
|
|
999
|
+
const indent = detectIndent(content).indent || " ";
|
|
1000
|
+
const sources = [];
|
|
1001
|
+
if (raw.workspaces?.catalog) {
|
|
1002
|
+
const catalog = raw.workspaces.catalog;
|
|
1003
|
+
sources.push({
|
|
1004
|
+
type: "bun",
|
|
1005
|
+
name: "default",
|
|
1006
|
+
filepath,
|
|
1007
|
+
deps: parseCatalogDeps(catalog, "workspaces.catalog", options),
|
|
1008
|
+
raw,
|
|
1009
|
+
indent
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
if (raw.workspaces?.catalogs) {
|
|
1013
|
+
const catalogs = raw.workspaces.catalogs;
|
|
1014
|
+
for (const [catalogName, catalog] of Object.entries(catalogs)) {
|
|
1015
|
+
sources.push({
|
|
1016
|
+
type: "bun",
|
|
1017
|
+
name: catalogName,
|
|
1018
|
+
filepath,
|
|
1019
|
+
deps: parseCatalogDeps(catalog, `workspaces.catalogs.${catalogName}`, options),
|
|
1020
|
+
raw,
|
|
1021
|
+
indent
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
return sources;
|
|
1026
|
+
},
|
|
1027
|
+
write(catalog, changes) {
|
|
1028
|
+
const content = readFileSync(catalog.filepath, "utf-8");
|
|
1029
|
+
const raw = JSON.parse(content);
|
|
1030
|
+
const indent = detectIndent(content).indent || catalog.indent;
|
|
1031
|
+
let section;
|
|
1032
|
+
if (catalog.name === "default") {
|
|
1033
|
+
section = raw.workspaces?.catalog;
|
|
1034
|
+
} else {
|
|
1035
|
+
section = raw.workspaces?.catalogs?.[catalog.name];
|
|
1036
|
+
}
|
|
1037
|
+
if (!section) return;
|
|
1038
|
+
for (const [name, version] of changes) {
|
|
1039
|
+
if (name in section) {
|
|
1040
|
+
section[name] = version;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
const lineEnding = detectLineEnding(content);
|
|
1044
|
+
const newContent = JSON.stringify(raw, null, indent);
|
|
1045
|
+
const withTrailing = content.endsWith("\n") ? `${newContent}
|
|
1046
|
+
` : newContent;
|
|
1047
|
+
const final = lineEnding === "\r\n" ? withTrailing.replace(/\n/g, "\r\n") : withTrailing;
|
|
1048
|
+
writeFileSync(catalog.filepath, final, "utf-8");
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
|
|
1052
|
+
const bun = {
|
|
1053
|
+
__proto__: null,
|
|
1054
|
+
bunCatalogLoader: bunCatalogLoader
|
|
1055
|
+
};
|
|
1056
|
+
|
|
1057
|
+
const pnpmCatalogLoader = {
|
|
1058
|
+
async detect(cwd) {
|
|
1059
|
+
return !!findUpSync("pnpm-workspace.yaml", { cwd });
|
|
1060
|
+
},
|
|
1061
|
+
async load(cwd, options) {
|
|
1062
|
+
const filepath = findUpSync("pnpm-workspace.yaml", { cwd });
|
|
1063
|
+
if (!filepath) return [];
|
|
1064
|
+
const content = readFileSync(filepath, "utf-8");
|
|
1065
|
+
const workspace = parsePnpmWorkspaceYaml(content);
|
|
1066
|
+
const schema = workspace.toJSON();
|
|
1067
|
+
const catalogs = [];
|
|
1068
|
+
if (schema.catalog && typeof schema.catalog === "object") {
|
|
1069
|
+
catalogs.push(parseCatalogSection(schema.catalog, "default", filepath, options, content));
|
|
1070
|
+
}
|
|
1071
|
+
if (schema.catalogs && typeof schema.catalogs === "object") {
|
|
1072
|
+
for (const [name, deps] of Object.entries(schema.catalogs)) {
|
|
1073
|
+
catalogs.push(parseCatalogSection(deps, name, filepath, options, content));
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
return catalogs;
|
|
1077
|
+
},
|
|
1078
|
+
write(catalog, changes) {
|
|
1079
|
+
const content = readFileSync(catalog.filepath, "utf-8");
|
|
1080
|
+
const workspace = parsePnpmWorkspaceYaml(content);
|
|
1081
|
+
for (const [name, version] of changes) {
|
|
1082
|
+
workspace.setPackage(catalog.name === "default" ? "default" : catalog.name, name, version);
|
|
1083
|
+
}
|
|
1084
|
+
writeFileSync(catalog.filepath, workspace.toString(), "utf-8");
|
|
1085
|
+
}
|
|
1086
|
+
};
|
|
1087
|
+
function parseCatalogSection(deps, name, filepath, options, rawContent) {
|
|
1088
|
+
const parsed = [];
|
|
1089
|
+
for (const [depName, version] of Object.entries(deps)) {
|
|
1090
|
+
parsed.push({
|
|
1091
|
+
name: depName,
|
|
1092
|
+
currentVersion: version,
|
|
1093
|
+
source: "catalog",
|
|
1094
|
+
update: !isLocked(version) || options.includeLocked,
|
|
1095
|
+
parents: [name === "default" ? "catalog" : `catalogs.${name}`]
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
return {
|
|
1099
|
+
type: "pnpm",
|
|
1100
|
+
name,
|
|
1101
|
+
filepath,
|
|
1102
|
+
deps: parsed,
|
|
1103
|
+
raw: rawContent,
|
|
1104
|
+
indent: " "
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
const pnpm = {
|
|
1109
|
+
__proto__: null,
|
|
1110
|
+
pnpmCatalogLoader: pnpmCatalogLoader
|
|
1111
|
+
};
|
|
1112
|
+
|
|
1113
|
+
const yarnCatalogLoader = {
|
|
1114
|
+
async detect(cwd) {
|
|
1115
|
+
const rcFile = findUpSync(".yarnrc.yml", { cwd });
|
|
1116
|
+
return !!rcFile;
|
|
1117
|
+
},
|
|
1118
|
+
async load(cwd, options) {
|
|
1119
|
+
const filepath = findUpSync(".yarnrc.yml", { cwd });
|
|
1120
|
+
if (!filepath) return [];
|
|
1121
|
+
const content = readFileSync(filepath, "utf-8");
|
|
1122
|
+
const doc = YAML.parseDocument(content);
|
|
1123
|
+
const raw = doc.toJSON();
|
|
1124
|
+
if (!raw?.catalog) return [];
|
|
1125
|
+
const deps = [];
|
|
1126
|
+
const catalog = raw.catalog;
|
|
1127
|
+
for (const [name, version] of Object.entries(catalog)) {
|
|
1128
|
+
deps.push({
|
|
1129
|
+
name,
|
|
1130
|
+
currentVersion: version,
|
|
1131
|
+
source: "catalog",
|
|
1132
|
+
update: !isLocked(version) || options.includeLocked,
|
|
1133
|
+
parents: ["catalog"]
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
return [
|
|
1137
|
+
{
|
|
1138
|
+
type: "yarn",
|
|
1139
|
+
name: "default",
|
|
1140
|
+
filepath,
|
|
1141
|
+
deps,
|
|
1142
|
+
raw: doc,
|
|
1143
|
+
indent: " "
|
|
1144
|
+
}
|
|
1145
|
+
];
|
|
1146
|
+
},
|
|
1147
|
+
write(catalog, changes) {
|
|
1148
|
+
const doc = catalog.raw;
|
|
1149
|
+
const catalogNode = doc.get("catalog");
|
|
1150
|
+
if (!catalogNode) return;
|
|
1151
|
+
for (const [name, version] of changes) {
|
|
1152
|
+
if (catalogNode.has(name)) {
|
|
1153
|
+
catalogNode.set(name, version);
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
writeFileSync(catalog.filepath, doc.toString(), "utf-8");
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1159
|
+
|
|
1160
|
+
const yarn = {
|
|
1161
|
+
__proto__: null,
|
|
1162
|
+
yarnCatalogLoader: yarnCatalogLoader
|
|
1163
|
+
};
|
|
1164
|
+
|
|
1165
|
+
const catalogWriters = {
|
|
1166
|
+
pnpm: pnpmCatalogLoader,
|
|
1167
|
+
bun: bunCatalogLoader,
|
|
1168
|
+
yarn: yarnCatalogLoader
|
|
1169
|
+
};
|
|
1170
|
+
function writeCatalogPackage(pkg, changes, logger) {
|
|
1171
|
+
if (!pkg.catalogs?.length) return;
|
|
1172
|
+
for (const catalog of pkg.catalogs) {
|
|
1173
|
+
const changeMap = /* @__PURE__ */ new Map();
|
|
1174
|
+
for (const change of changes) {
|
|
1175
|
+
if (catalog.deps.some((d) => d.name === change.name)) {
|
|
1176
|
+
changeMap.set(change.name, change.targetVersion);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
if (changeMap.size === 0) continue;
|
|
1180
|
+
const writer = catalogWriters[catalog.type];
|
|
1181
|
+
if (writer) {
|
|
1182
|
+
try {
|
|
1183
|
+
writer.write(catalog, changeMap);
|
|
1184
|
+
} catch (error) {
|
|
1185
|
+
throw new WriteError(
|
|
1186
|
+
`Failed to write ${catalog.type} catalog "${catalog.name}" (${catalog.filepath})`,
|
|
1187
|
+
{ cause: error }
|
|
1188
|
+
);
|
|
1189
|
+
}
|
|
1190
|
+
logger.success(
|
|
1191
|
+
`Updated ${catalog.type} catalog "${catalog.name}" (${changeMap.size} changes)`
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
function detectLineEnding(content) {
|
|
1198
|
+
return content.includes("\r\n") ? "\r\n" : "\n";
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
function writePackageJson(pkg, changes, logger) {
|
|
1202
|
+
let content;
|
|
1203
|
+
try {
|
|
1204
|
+
content = readFileSync(pkg.filepath, "utf-8");
|
|
1205
|
+
} catch (error) {
|
|
1206
|
+
throw new WriteError(`Failed to read ${pkg.filepath}`, { cause: error });
|
|
1207
|
+
}
|
|
1208
|
+
const indent = detectIndent(content).indent || pkg.indent;
|
|
1209
|
+
let raw;
|
|
1210
|
+
try {
|
|
1211
|
+
raw = JSON.parse(content);
|
|
1212
|
+
} catch (error) {
|
|
1213
|
+
throw new WriteError(`Failed to parse JSON in ${pkg.filepath}`, { cause: error });
|
|
1214
|
+
}
|
|
1215
|
+
const bySource = /* @__PURE__ */ new Map();
|
|
1216
|
+
for (const change of changes) {
|
|
1217
|
+
const group = bySource.get(change.source) ?? [];
|
|
1218
|
+
group.push(change);
|
|
1219
|
+
bySource.set(change.source, group);
|
|
1220
|
+
}
|
|
1221
|
+
for (const [source, sourceChanges] of bySource) {
|
|
1222
|
+
const section = getSection(raw, source);
|
|
1223
|
+
if (!section) continue;
|
|
1224
|
+
for (const change of sourceChanges) {
|
|
1225
|
+
if (change.name in section) {
|
|
1226
|
+
const oldVersion = section[change.name];
|
|
1227
|
+
section[change.name] = rebuildVersion(oldVersion, change.targetVersion);
|
|
1228
|
+
logger.debug(` ${change.name}: ${oldVersion} -> ${section[change.name]}`);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
const pmChange = changes.find((c) => c.source === "packageManager");
|
|
1233
|
+
if (pmChange && pkg.packageManager) {
|
|
1234
|
+
const newPm = pkg.packageManager.hash ? `${pkg.packageManager.name}@${pmChange.targetVersion}+${pkg.packageManager.hash}` : `${pkg.packageManager.name}@${pmChange.targetVersion}`;
|
|
1235
|
+
raw.packageManager = newPm;
|
|
1236
|
+
}
|
|
1237
|
+
const lineEnding = detectLineEnding(content);
|
|
1238
|
+
const newContent = JSON.stringify(raw, null, indent);
|
|
1239
|
+
const withTrailing = content.endsWith("\n") ? `${newContent}
|
|
1240
|
+
` : newContent;
|
|
1241
|
+
const finalContent = lineEnding === "\r\n" ? withTrailing.replace(/\n/g, "\r\n") : withTrailing;
|
|
1242
|
+
try {
|
|
1243
|
+
writeFileSync(pkg.filepath, finalContent, "utf-8");
|
|
1244
|
+
} catch (error) {
|
|
1245
|
+
throw new WriteError(`Failed to write ${pkg.filepath}`, { cause: error });
|
|
1246
|
+
}
|
|
1247
|
+
logger.success(`Updated ${pkg.filepath} (${changes.length} changes)`);
|
|
1248
|
+
}
|
|
1249
|
+
function getSection(raw, source) {
|
|
1250
|
+
if (source.includes(".")) {
|
|
1251
|
+
const parts = source.split(".");
|
|
1252
|
+
let current = raw;
|
|
1253
|
+
for (const part of parts) {
|
|
1254
|
+
if (!current || typeof current !== "object") return null;
|
|
1255
|
+
current = current[part];
|
|
1256
|
+
}
|
|
1257
|
+
return current;
|
|
1258
|
+
}
|
|
1259
|
+
return raw[source] ?? null;
|
|
1260
|
+
}
|
|
1261
|
+
function rebuildVersion(original, newVersion) {
|
|
1262
|
+
const npmMatch = original.match(/^(npm:.+@)/);
|
|
1263
|
+
if (npmMatch) return `${npmMatch[1]}${newVersion}`;
|
|
1264
|
+
const jsrMatch = original.match(/^(jsr:.+@)/);
|
|
1265
|
+
if (jsrMatch) return `${jsrMatch[1]}${newVersion}`;
|
|
1266
|
+
return newVersion;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
function backupPackageFiles(pkg) {
|
|
1270
|
+
const backups = [];
|
|
1271
|
+
let mainContent;
|
|
1272
|
+
try {
|
|
1273
|
+
mainContent = readFileSync(pkg.filepath, "utf-8");
|
|
1274
|
+
} catch (error) {
|
|
1275
|
+
throw new WriteError(`Failed to backup file ${pkg.filepath}`, { cause: error });
|
|
1276
|
+
}
|
|
1277
|
+
backups.push({
|
|
1278
|
+
filepath: pkg.filepath,
|
|
1279
|
+
content: mainContent
|
|
1280
|
+
});
|
|
1281
|
+
if (pkg.catalogs?.length) {
|
|
1282
|
+
for (const catalog of pkg.catalogs) {
|
|
1283
|
+
let catalogContent;
|
|
1284
|
+
try {
|
|
1285
|
+
catalogContent = readFileSync(catalog.filepath, "utf-8");
|
|
1286
|
+
} catch (error) {
|
|
1287
|
+
throw new WriteError(`Failed to backup file ${catalog.filepath}`, { cause: error });
|
|
1288
|
+
}
|
|
1289
|
+
backups.push({
|
|
1290
|
+
filepath: catalog.filepath,
|
|
1291
|
+
content: catalogContent
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
return backups;
|
|
1296
|
+
}
|
|
1297
|
+
function restorePackageFiles(backups) {
|
|
1298
|
+
for (const backup of backups) {
|
|
1299
|
+
try {
|
|
1300
|
+
writeFileSync(backup.filepath, backup.content, "utf-8");
|
|
1301
|
+
} catch (error) {
|
|
1302
|
+
throw new WriteError(`Failed to restore file ${backup.filepath}`, { cause: error });
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
function writePackage(pkg, changes, loglevel = "info") {
|
|
1308
|
+
const logger = createLogger(loglevel);
|
|
1309
|
+
if (changes.length === 0) return;
|
|
1310
|
+
if (pkg.type === "package.json") {
|
|
1311
|
+
writePackageJson(pkg, changes, logger);
|
|
1312
|
+
} else if (pkg.catalogs?.length) {
|
|
1313
|
+
writeCatalogPackage(pkg, changes, logger);
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
async function verifyAndWrite(pkg, changes, verifyCommand, logger) {
|
|
1318
|
+
let applied = 0;
|
|
1319
|
+
let reverted = 0;
|
|
1320
|
+
for (const change of changes) {
|
|
1321
|
+
const backups = backupPackageFiles(pkg);
|
|
1322
|
+
writePackage(pkg, [change], "silent");
|
|
1323
|
+
try {
|
|
1324
|
+
execSync(verifyCommand, { cwd: pkg.filepath.replace(/\/[^/]+$/, ""), stdio: "pipe" });
|
|
1325
|
+
applied++;
|
|
1326
|
+
logger.success(` ${change.name} ${change.currentVersion} \u2192 ${change.targetVersion} \u2713`);
|
|
1327
|
+
} catch {
|
|
1328
|
+
restorePackageFiles(backups);
|
|
1329
|
+
reverted++;
|
|
1330
|
+
logger.warn(
|
|
1331
|
+
` ${change.name} ${change.currentVersion} \u2192 ${change.targetVersion} \u2717 (reverted)`
|
|
1332
|
+
);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
return { applied, reverted };
|
|
1336
|
+
}
|
|
1337
|
+
async function applyPackageWrite(pkg, changes, options, logger) {
|
|
1338
|
+
if (changes.length === 0) return false;
|
|
1339
|
+
if (options.verifyCommand) {
|
|
1340
|
+
const result = await verifyAndWrite(pkg, changes, options.verifyCommand, logger);
|
|
1341
|
+
logger.info(` Verify: ${result.applied} applied, ${result.reverted} reverted`);
|
|
1342
|
+
return result.applied > 0;
|
|
1343
|
+
}
|
|
1344
|
+
if (pkg.type === "global") {
|
|
1345
|
+
const { writeGlobalPackage } = await import('../chunks/index3.mjs').then(function (n) { return n.g; });
|
|
1346
|
+
const pmName = pkg.filepath.replace("global:", "");
|
|
1347
|
+
for (const change of changes) {
|
|
1348
|
+
writeGlobalPackage(pmName, change.name, change.targetVersion);
|
|
1349
|
+
}
|
|
1350
|
+
return true;
|
|
1351
|
+
}
|
|
1352
|
+
writePackage(pkg, changes, options.loglevel);
|
|
1353
|
+
return true;
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
async function processPackage(pkg, options, hooks) {
|
|
1357
|
+
await options.beforePackageStart?.(pkg);
|
|
1358
|
+
try {
|
|
1359
|
+
pkg.resolved = await resolvePackage(
|
|
1360
|
+
pkg,
|
|
1361
|
+
options,
|
|
1362
|
+
hooks.cache,
|
|
1363
|
+
hooks.npmrc,
|
|
1364
|
+
hooks.workspacePackageNames,
|
|
1365
|
+
hooks.onDependencyProcessed
|
|
1366
|
+
);
|
|
1367
|
+
const updates = pkg.resolved.filter((d) => d.diff !== "none" && d.diff !== "error");
|
|
1368
|
+
if (updates.length === 0) {
|
|
1369
|
+
hooks.onAllModeNoUpdates();
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
hooks.onHasUpdates(updates);
|
|
1373
|
+
const selected = options.interactive ? await selectInteractiveUpdates(updates, options.explain) : updates;
|
|
1374
|
+
if (!options.write || selected.length === 0) return;
|
|
1375
|
+
const shouldWrite = await options.beforePackageWrite?.(pkg) ?? true;
|
|
1376
|
+
if (!shouldWrite) return;
|
|
1377
|
+
if (await applyPackageWrite(pkg, selected, options, hooks.logger)) {
|
|
1378
|
+
hooks.onDidWrite();
|
|
1379
|
+
}
|
|
1380
|
+
await options.afterPackageWrite?.(pkg);
|
|
1381
|
+
} finally {
|
|
1382
|
+
await options.afterPackageEnd?.(pkg);
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
const ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-?]*[ -/]*[@-~]`, "g");
|
|
1387
|
+
function stripAnsi(str) {
|
|
1388
|
+
return str.replace(ANSI_PATTERN, "");
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
const DIFF_COLORS = {
|
|
1392
|
+
major: c.red,
|
|
1393
|
+
minor: c.yellow,
|
|
1394
|
+
patch: c.green,
|
|
1395
|
+
none: c.gray,
|
|
1396
|
+
error: c.red
|
|
1397
|
+
};
|
|
1398
|
+
function colorDiff(diff) {
|
|
1399
|
+
const color = DIFF_COLORS[diff];
|
|
1400
|
+
return color(diff);
|
|
1401
|
+
}
|
|
1402
|
+
function colorVersion(version, diff) {
|
|
1403
|
+
const color = DIFF_COLORS[diff];
|
|
1404
|
+
return color(version);
|
|
1405
|
+
}
|
|
1406
|
+
function colorizeVersionDiff(from, to, diff) {
|
|
1407
|
+
if (diff === "none" || diff === "error") return colorVersion(to, diff);
|
|
1408
|
+
const color = DIFF_COLORS[diff];
|
|
1409
|
+
const prefixMatch = to.match(/^([^\d]*)(.*)$/);
|
|
1410
|
+
const prefix = prefixMatch?.[1] ?? "";
|
|
1411
|
+
const toVersion = prefixMatch?.[2] ?? to;
|
|
1412
|
+
const fromPrefixMatch = from.match(/^([^\d]*)(.*)$/);
|
|
1413
|
+
const fromVersion = fromPrefixMatch?.[2] ?? from;
|
|
1414
|
+
const fromParts = fromVersion.split(".");
|
|
1415
|
+
const toParts = toVersion.split(".");
|
|
1416
|
+
let diffIdx = -1;
|
|
1417
|
+
for (let i = 0; i < toParts.length; i++) {
|
|
1418
|
+
if (fromParts[i] !== toParts[i]) {
|
|
1419
|
+
diffIdx = i;
|
|
1420
|
+
break;
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
if (diffIdx === -1) return colorVersion(to, diff);
|
|
1424
|
+
const unchanged = toParts.slice(0, diffIdx).join(".");
|
|
1425
|
+
const changed = toParts.slice(diffIdx).join(".");
|
|
1426
|
+
const separator = diffIdx > 0 ? "." : "";
|
|
1427
|
+
return prefix + unchanged + separator + color(changed);
|
|
1428
|
+
}
|
|
1429
|
+
function arrow() {
|
|
1430
|
+
return c.gray(" -> ");
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
function timeDifference(dateStr) {
|
|
1434
|
+
if (!dateStr) return void 0;
|
|
1435
|
+
const date = new Date(dateStr);
|
|
1436
|
+
if (Number.isNaN(date.getTime())) return void 0;
|
|
1437
|
+
const now = Date.now();
|
|
1438
|
+
const diffMs = now - date.getTime();
|
|
1439
|
+
if (diffMs < 0) return { text: "~0d", color: "green" };
|
|
1440
|
+
const days = diffMs / (1e3 * 60 * 60 * 24);
|
|
1441
|
+
if (days < 90) {
|
|
1442
|
+
const d = Math.max(1, Math.round(days));
|
|
1443
|
+
return { text: `~${d}d`, color: "green" };
|
|
1444
|
+
}
|
|
1445
|
+
if (days < 365) {
|
|
1446
|
+
const months = Math.round(days / 30);
|
|
1447
|
+
return { text: `~${months}mo`, color: "yellow" };
|
|
1448
|
+
}
|
|
1449
|
+
const years = days / 365;
|
|
1450
|
+
const formatted = years >= 10 ? `~${Math.round(years)}y` : `~${years.toFixed(1)}y`;
|
|
1451
|
+
return { text: formatted, color: "red" };
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
function visualLength(str) {
|
|
1455
|
+
const plain = stripAnsi(str);
|
|
1456
|
+
let width = 0;
|
|
1457
|
+
for (const char of plain) {
|
|
1458
|
+
const codePoint = char.codePointAt(0);
|
|
1459
|
+
if (codePoint === void 0) continue;
|
|
1460
|
+
if (isZeroWidthChar(char, codePoint)) continue;
|
|
1461
|
+
width += isWideCodePoint(codePoint) ? 2 : 1;
|
|
1462
|
+
}
|
|
1463
|
+
return width;
|
|
1464
|
+
}
|
|
1465
|
+
function visualPadEnd(str, len) {
|
|
1466
|
+
const diff = len - visualLength(str);
|
|
1467
|
+
return diff > 0 ? str + " ".repeat(diff) : str;
|
|
1468
|
+
}
|
|
1469
|
+
function visualPadStart(str, len) {
|
|
1470
|
+
const diff = len - visualLength(str);
|
|
1471
|
+
return diff > 0 ? " ".repeat(diff) + str : str;
|
|
1472
|
+
}
|
|
1473
|
+
function visualTruncate(str, maxLen) {
|
|
1474
|
+
if (maxLen <= 0) return "";
|
|
1475
|
+
const plain = stripAnsi(str);
|
|
1476
|
+
if (visualLength(plain) <= maxLen) return plain;
|
|
1477
|
+
if (maxLen === 1) return "\u2026";
|
|
1478
|
+
let out = "";
|
|
1479
|
+
let used = 0;
|
|
1480
|
+
const target = maxLen - 1;
|
|
1481
|
+
for (const char of plain) {
|
|
1482
|
+
const codePoint = char.codePointAt(0);
|
|
1483
|
+
if (codePoint === void 0) continue;
|
|
1484
|
+
const charWidth = isZeroWidthChar(char, codePoint) ? 0 : isWideCodePoint(codePoint) ? 2 : 1;
|
|
1485
|
+
if (used + charWidth > target) break;
|
|
1486
|
+
out += char;
|
|
1487
|
+
used += charWidth;
|
|
1488
|
+
}
|
|
1489
|
+
return `${out}\u2026`;
|
|
1490
|
+
}
|
|
1491
|
+
function padEnd(str, len) {
|
|
1492
|
+
return visualPadEnd(str, len);
|
|
1493
|
+
}
|
|
1494
|
+
function truncate(str, maxLen) {
|
|
1495
|
+
return visualTruncate(str, maxLen);
|
|
1496
|
+
}
|
|
1497
|
+
function isZeroWidthChar(char, codePoint) {
|
|
1498
|
+
if (codePoint === 0) return true;
|
|
1499
|
+
if (codePoint >= 0 && codePoint <= 31 || codePoint >= 127 && codePoint <= 159) {
|
|
1500
|
+
return true;
|
|
1501
|
+
}
|
|
1502
|
+
if (codePoint === 8204 || codePoint === 8205 || codePoint === 173) {
|
|
1503
|
+
return true;
|
|
1504
|
+
}
|
|
1505
|
+
if (/\p{Mark}/u.test(char)) {
|
|
1506
|
+
return true;
|
|
1507
|
+
}
|
|
1508
|
+
return codePoint >= 65024 && codePoint <= 65039;
|
|
1509
|
+
}
|
|
1510
|
+
function isWideCodePoint(codePoint) {
|
|
1511
|
+
if (codePoint < 4352) return false;
|
|
1512
|
+
return codePoint <= 4447 || // Hangul Jamo
|
|
1513
|
+
codePoint === 9001 || codePoint === 9002 || codePoint >= 11904 && codePoint <= 42191 && codePoint !== 12351 || codePoint >= 44032 && codePoint <= 55203 || // Hangul Syllables
|
|
1514
|
+
codePoint >= 63744 && codePoint <= 64255 || // CJK Compatibility Ideographs
|
|
1515
|
+
codePoint >= 65040 && codePoint <= 65049 || codePoint >= 65072 && codePoint <= 65135 || codePoint >= 65280 && codePoint <= 65376 || // Fullwidth Forms
|
|
1516
|
+
codePoint >= 65504 && codePoint <= 65510 || codePoint >= 127744 && codePoint <= 128591 || // Emoji
|
|
1517
|
+
codePoint >= 129280 && codePoint <= 129535 || codePoint >= 131072 && codePoint <= 262141;
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
const BAR_WIDTH = 24;
|
|
1521
|
+
const LINES = 2;
|
|
1522
|
+
function createCheckProgress(options, packages) {
|
|
1523
|
+
if (!shouldRenderProgress(options)) {
|
|
1524
|
+
return null;
|
|
1525
|
+
}
|
|
1526
|
+
const state = {
|
|
1527
|
+
totalPackages: packages.length,
|
|
1528
|
+
totalDeps: packages.reduce((sum, p) => sum + p.deps.filter((d) => d.update).length, 0),
|
|
1529
|
+
completedPackages: 0,
|
|
1530
|
+
completedDeps: 0,
|
|
1531
|
+
currentPackageName: "",
|
|
1532
|
+
currentPackageTotalDeps: 0,
|
|
1533
|
+
currentPackageResolvedDeps: 0,
|
|
1534
|
+
renderedLines: 0
|
|
1535
|
+
};
|
|
1536
|
+
const progress = {
|
|
1537
|
+
onPackageStart(pkg) {
|
|
1538
|
+
state.currentPackageName = pkg.name || "(unnamed)";
|
|
1539
|
+
state.currentPackageTotalDeps = pkg.deps.filter((d) => d.update).length;
|
|
1540
|
+
state.currentPackageResolvedDeps = 0;
|
|
1541
|
+
render(state);
|
|
1542
|
+
},
|
|
1543
|
+
onDependencyProcessed() {
|
|
1544
|
+
state.currentPackageResolvedDeps = Math.min(
|
|
1545
|
+
state.currentPackageResolvedDeps + 1,
|
|
1546
|
+
state.currentPackageTotalDeps
|
|
1547
|
+
);
|
|
1548
|
+
state.completedDeps = Math.min(state.completedDeps + 1, state.totalDeps);
|
|
1549
|
+
render(state);
|
|
1550
|
+
},
|
|
1551
|
+
onPackageEnd() {
|
|
1552
|
+
state.completedPackages = Math.min(state.completedPackages + 1, state.totalPackages);
|
|
1553
|
+
state.currentPackageResolvedDeps = state.currentPackageTotalDeps;
|
|
1554
|
+
render(state);
|
|
1555
|
+
},
|
|
1556
|
+
done() {
|
|
1557
|
+
clear(state);
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1560
|
+
return progress;
|
|
1561
|
+
}
|
|
1562
|
+
function shouldRenderProgress(options) {
|
|
1563
|
+
return process.stdout.isTTY && options.output === "table" && options.loglevel !== "silent";
|
|
1564
|
+
}
|
|
1565
|
+
function render(state) {
|
|
1566
|
+
const maxLabel = getMaxLabelWidth();
|
|
1567
|
+
const pkgLabel = clampLabel("Packages", maxLabel);
|
|
1568
|
+
const depLabel = clampLabel(`Deps (${state.currentPackageName || "-"})`, maxLabel);
|
|
1569
|
+
const pkgBar = formatBar(state.completedPackages, state.totalPackages);
|
|
1570
|
+
const depBar = formatBar(state.currentPackageResolvedDeps, state.currentPackageTotalDeps);
|
|
1571
|
+
const pkgLine = `${pkgLabel} ${pkgBar}`;
|
|
1572
|
+
const depLine = `${depLabel} ${depBar} total ${state.completedDeps}/${state.totalDeps}`;
|
|
1573
|
+
if (state.renderedLines > 0) {
|
|
1574
|
+
moveCursorUp(state.renderedLines);
|
|
1575
|
+
}
|
|
1576
|
+
writeLine(pkgLine);
|
|
1577
|
+
writeLine(depLine);
|
|
1578
|
+
state.renderedLines = LINES;
|
|
1579
|
+
}
|
|
1580
|
+
function clear(state) {
|
|
1581
|
+
if (state.renderedLines === 0) return;
|
|
1582
|
+
moveCursorUp(state.renderedLines);
|
|
1583
|
+
for (let i = 0; i < state.renderedLines; i++) {
|
|
1584
|
+
process.stdout.write("\r\x1B[2K\n");
|
|
1585
|
+
}
|
|
1586
|
+
moveCursorUp(state.renderedLines);
|
|
1587
|
+
state.renderedLines = 0;
|
|
1588
|
+
}
|
|
1589
|
+
function formatBar(value, total) {
|
|
1590
|
+
if (total <= 0) {
|
|
1591
|
+
return `[${"-".repeat(BAR_WIDTH)}] 0/0`;
|
|
1592
|
+
}
|
|
1593
|
+
const clamped = Math.min(value, total);
|
|
1594
|
+
const ratio = clamped / total;
|
|
1595
|
+
const filled = Math.round(BAR_WIDTH * ratio);
|
|
1596
|
+
const empty = Math.max(0, BAR_WIDTH - filled);
|
|
1597
|
+
return `[${"=".repeat(filled)}${"-".repeat(empty)}] ${clamped}/${total}`;
|
|
1598
|
+
}
|
|
1599
|
+
function getMaxLabelWidth() {
|
|
1600
|
+
const columns = typeof process.stdout.columns === "number" ? process.stdout.columns : 80;
|
|
1601
|
+
return Math.max(12, Math.floor(columns / 2) - 22);
|
|
1602
|
+
}
|
|
1603
|
+
function clampLabel(label, max) {
|
|
1604
|
+
const clean = visualLength(label) > max ? visualTruncate(label, max) : label;
|
|
1605
|
+
const pad = max - visualLength(clean);
|
|
1606
|
+
return `${clean}${" ".repeat(Math.max(0, pad))}`;
|
|
1607
|
+
}
|
|
1608
|
+
function moveCursorUp(lines) {
|
|
1609
|
+
if (lines > 0) {
|
|
1610
|
+
process.stdout.write(`\x1B[${lines}A`);
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
function writeLine(text) {
|
|
1614
|
+
process.stdout.write(`\r\x1B[2K${text}
|
|
1615
|
+
`);
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
const DIFF_ORDER = {
|
|
1619
|
+
major: 0,
|
|
1620
|
+
minor: 1,
|
|
1621
|
+
patch: 2,
|
|
1622
|
+
error: 3,
|
|
1623
|
+
none: 4
|
|
1624
|
+
};
|
|
1625
|
+
function byDiff(a, b) {
|
|
1626
|
+
return (DIFF_ORDER[a.diff] ?? 4) - (DIFF_ORDER[b.diff] ?? 4);
|
|
1627
|
+
}
|
|
1628
|
+
function byTime(a, b) {
|
|
1629
|
+
const aTime = a.publishedAt ? new Date(a.publishedAt).getTime() : 0;
|
|
1630
|
+
const bTime = b.publishedAt ? new Date(b.publishedAt).getTime() : 0;
|
|
1631
|
+
return aTime - bTime;
|
|
1632
|
+
}
|
|
1633
|
+
function byName(a, b) {
|
|
1634
|
+
return a.name.localeCompare(b.name);
|
|
1635
|
+
}
|
|
1636
|
+
function sortDeps(deps, sort) {
|
|
1637
|
+
if (deps.length === 0) return deps;
|
|
1638
|
+
const sorted = [...deps];
|
|
1639
|
+
switch (sort) {
|
|
1640
|
+
case "diff-asc":
|
|
1641
|
+
sorted.sort(byDiff);
|
|
1642
|
+
break;
|
|
1643
|
+
case "diff-desc":
|
|
1644
|
+
sorted.sort((a, b) => byDiff(b, a));
|
|
1645
|
+
break;
|
|
1646
|
+
case "time-asc":
|
|
1647
|
+
sorted.sort(byTime);
|
|
1648
|
+
break;
|
|
1649
|
+
case "time-desc":
|
|
1650
|
+
sorted.sort((a, b) => byTime(b, a));
|
|
1651
|
+
break;
|
|
1652
|
+
case "name-asc":
|
|
1653
|
+
sorted.sort(byName);
|
|
1654
|
+
break;
|
|
1655
|
+
case "name-desc":
|
|
1656
|
+
sorted.sort((a, b) => byName(b, a));
|
|
1657
|
+
break;
|
|
1658
|
+
}
|
|
1659
|
+
return sorted;
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
const MIN_NAME_COL = 8;
|
|
1663
|
+
const MIN_VERSION_COL = 6;
|
|
1664
|
+
const MIN_SOURCE_COL = 6;
|
|
1665
|
+
function buildColumnLayout(deps, showSource, showTimediff, terminalWidth) {
|
|
1666
|
+
const layout = {
|
|
1667
|
+
nameWidth: Math.max(4, ...deps.map((u) => visualLength(u.name))),
|
|
1668
|
+
sourceWidth: showSource ? Math.max(6, ...deps.map((u) => visualLength(u.source))) : 0,
|
|
1669
|
+
currentWidth: Math.max(7, ...deps.map((u) => visualLength(u.currentVersion))),
|
|
1670
|
+
targetWidth: Math.max(6, ...deps.map((u) => visualLength(u.targetVersion))),
|
|
1671
|
+
diffWidth: Math.max(4, ...deps.map((u) => visualLength(u.diff))),
|
|
1672
|
+
ageWidth: showTimediff ? 8 : 0,
|
|
1673
|
+
showSource,
|
|
1674
|
+
showTimediff
|
|
1675
|
+
};
|
|
1676
|
+
if (!terminalWidth || terminalWidth <= 0) {
|
|
1677
|
+
return layout;
|
|
1678
|
+
}
|
|
1679
|
+
while (totalRowWidth(layout) > terminalWidth) {
|
|
1680
|
+
if (layout.nameWidth > MIN_NAME_COL) {
|
|
1681
|
+
layout.nameWidth--;
|
|
1682
|
+
continue;
|
|
1683
|
+
}
|
|
1684
|
+
if (layout.currentWidth > MIN_VERSION_COL) {
|
|
1685
|
+
layout.currentWidth--;
|
|
1686
|
+
continue;
|
|
1687
|
+
}
|
|
1688
|
+
if (layout.targetWidth > MIN_VERSION_COL) {
|
|
1689
|
+
layout.targetWidth--;
|
|
1690
|
+
continue;
|
|
1691
|
+
}
|
|
1692
|
+
if (layout.showSource && layout.sourceWidth > MIN_SOURCE_COL) {
|
|
1693
|
+
layout.sourceWidth--;
|
|
1694
|
+
continue;
|
|
1695
|
+
}
|
|
1696
|
+
break;
|
|
1697
|
+
}
|
|
1698
|
+
return layout;
|
|
1699
|
+
}
|
|
1700
|
+
function totalRowWidth(layout) {
|
|
1701
|
+
const base = 4 + // indentation
|
|
1702
|
+
layout.nameWidth + 2 + layout.currentWidth + visualLength(arrow()) + layout.targetWidth + 2 + layout.diffWidth;
|
|
1703
|
+
const source = layout.showSource ? layout.sourceWidth + 2 : 0;
|
|
1704
|
+
const timediff = layout.showTimediff ? layout.ageWidth + 2 : 0;
|
|
1705
|
+
return base + source + timediff;
|
|
1706
|
+
}
|
|
1707
|
+
function fitCell(value, width) {
|
|
1708
|
+
return visualLength(value) > width ? visualTruncate(value, width) : value;
|
|
1709
|
+
}
|
|
1710
|
+
function getTerminalWidth() {
|
|
1711
|
+
if (!process.stdout.isTTY) return void 0;
|
|
1712
|
+
return typeof process.stdout.columns === "number" ? process.stdout.columns : void 0;
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
function renderRows(deps, options, log, showSource, terminalWidth) {
|
|
1716
|
+
const showTimediff = options.timediff;
|
|
1717
|
+
const showNodecompat = options.nodecompat;
|
|
1718
|
+
const showLong = options.long;
|
|
1719
|
+
const layout = buildColumnLayout(deps, showSource, showTimediff, terminalWidth);
|
|
1720
|
+
const header = buildHeader(layout);
|
|
1721
|
+
log(header);
|
|
1722
|
+
const separatorLen = Math.max(0, totalRowWidth(layout) - 4);
|
|
1723
|
+
log(c.gray(` ${"-".repeat(separatorLen)}`));
|
|
1724
|
+
for (const dep of deps) {
|
|
1725
|
+
const name = visualPadEnd(fitCell(dep.name, layout.nameWidth), layout.nameWidth);
|
|
1726
|
+
const currentRaw = fitCell(dep.currentVersion, layout.currentWidth);
|
|
1727
|
+
const current = visualPadEnd(currentRaw, layout.currentWidth);
|
|
1728
|
+
const targetRaw = fitCell(dep.targetVersion, layout.targetWidth);
|
|
1729
|
+
const target = visualPadEnd(
|
|
1730
|
+
colorizeVersionDiff(dep.currentVersion, targetRaw, dep.diff),
|
|
1731
|
+
layout.targetWidth
|
|
1732
|
+
);
|
|
1733
|
+
const diff = visualPadEnd(colorDiff(dep.diff), layout.diffWidth);
|
|
1734
|
+
const deprecated = dep.deprecated ? c.red(" (deprecated)") : "";
|
|
1735
|
+
let line = ` ${name} `;
|
|
1736
|
+
if (showSource) {
|
|
1737
|
+
const source = visualPadEnd(
|
|
1738
|
+
c.gray(fitCell(dep.source, layout.sourceWidth)),
|
|
1739
|
+
layout.sourceWidth
|
|
1740
|
+
);
|
|
1741
|
+
line += `${source} `;
|
|
1742
|
+
}
|
|
1743
|
+
line += `${current}${arrow()}${target} ${diff}`;
|
|
1744
|
+
if (showTimediff) {
|
|
1745
|
+
line += ` ${renderTimediff(dep.publishedAt, layout.ageWidth)}`;
|
|
1746
|
+
}
|
|
1747
|
+
if (hasProvenanceDowngrade(dep)) {
|
|
1748
|
+
line += ` ${c.yellow("\u26A0")}`;
|
|
1749
|
+
}
|
|
1750
|
+
if (showNodecompat) {
|
|
1751
|
+
if (dep.nodeCompatible === true) {
|
|
1752
|
+
line += ` ${c.green.dim("\u2713")}`;
|
|
1753
|
+
} else if (dep.nodeCompatible === false) {
|
|
1754
|
+
line += ` ${c.red("\u2717node")}`;
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
line += deprecated;
|
|
1758
|
+
log(line);
|
|
1759
|
+
if (showLong && dep.pkgData.homepage) {
|
|
1760
|
+
log(c.gray(` \u21B3 ${dep.pkgData.homepage}`));
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
function buildHeader(layout) {
|
|
1765
|
+
let header = ` ${visualPadEnd(c.gray("name"), layout.nameWidth)} `;
|
|
1766
|
+
if (layout.showSource) {
|
|
1767
|
+
header += `${visualPadEnd(c.gray("source"), layout.sourceWidth)} `;
|
|
1768
|
+
}
|
|
1769
|
+
header += `${visualPadEnd(c.gray("current"), layout.currentWidth)}`;
|
|
1770
|
+
header += `${arrow()}${visualPadEnd(c.gray("target"), layout.targetWidth)}`;
|
|
1771
|
+
header += ` ${visualPadEnd(c.gray("diff"), layout.diffWidth)}`;
|
|
1772
|
+
if (layout.showTimediff) {
|
|
1773
|
+
header += ` ${visualPadStart(c.gray("age"), layout.ageWidth)}`;
|
|
1774
|
+
}
|
|
1775
|
+
return header;
|
|
1776
|
+
}
|
|
1777
|
+
function renderTimediff(publishedAt, ageWidth) {
|
|
1778
|
+
const td = timeDifference(publishedAt);
|
|
1779
|
+
if (!td) return " ".repeat(ageWidth);
|
|
1780
|
+
const colorFn = td.color === "green" ? c.green : td.color === "yellow" ? c.yellow : c.red;
|
|
1781
|
+
return visualPadStart(colorFn(td.text), ageWidth);
|
|
1782
|
+
}
|
|
1783
|
+
function hasProvenanceDowngrade(dep) {
|
|
1784
|
+
return (dep.currentProvenance === "trusted" || dep.currentProvenance === "attested") && dep.provenance === "none";
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
function renderSummary(updates, log) {
|
|
1788
|
+
const major = updates.filter((u) => u.diff === "major").length;
|
|
1789
|
+
const minor = updates.filter((u) => u.diff === "minor").length;
|
|
1790
|
+
const patch = updates.filter((u) => u.diff === "patch").length;
|
|
1791
|
+
const parts = [];
|
|
1792
|
+
if (major) parts.push(c.red(`${major} major`));
|
|
1793
|
+
if (minor) parts.push(c.yellow(`${minor} minor`));
|
|
1794
|
+
if (patch) parts.push(c.green(`${patch} patch`));
|
|
1795
|
+
log(` ${parts.join(c.gray(" | "))} ${c.gray(`(${updates.length} total)`)}`);
|
|
1796
|
+
log();
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
const DEP_SOURCE_SHORT_NAMES = {
|
|
1800
|
+
dependencies: "dependencies",
|
|
1801
|
+
devDependencies: "devDependencies",
|
|
1802
|
+
peerDependencies: "peerDependencies",
|
|
1803
|
+
optionalDependencies: "optionalDependencies",
|
|
1804
|
+
overrides: "overrides",
|
|
1805
|
+
resolutions: "resolutions",
|
|
1806
|
+
"pnpm.overrides": "pnpm.overrides",
|
|
1807
|
+
catalog: "catalog",
|
|
1808
|
+
packageManager: "packageManager"
|
|
1809
|
+
};
|
|
1810
|
+
function renderTable(packageName, updates, options) {
|
|
1811
|
+
const log = console.log;
|
|
1812
|
+
const terminalWidth = getTerminalWidth();
|
|
1813
|
+
const title = terminalWidth ? fitCell(packageName, Math.max(1, terminalWidth - 2)) : packageName;
|
|
1814
|
+
log();
|
|
1815
|
+
log(c.cyan.bold(title));
|
|
1816
|
+
log();
|
|
1817
|
+
const sorted = sortDeps(updates, options.sort);
|
|
1818
|
+
if (options.group) {
|
|
1819
|
+
renderGrouped(sorted, options, log, terminalWidth);
|
|
1820
|
+
} else {
|
|
1821
|
+
renderFlat(sorted, options, log, terminalWidth);
|
|
1822
|
+
}
|
|
1823
|
+
renderSummary(updates, log);
|
|
1824
|
+
}
|
|
1825
|
+
function renderGrouped(sorted, options, log, terminalWidth) {
|
|
1826
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1827
|
+
for (const dep of sorted) {
|
|
1828
|
+
const source = dep.source;
|
|
1829
|
+
const existing = groups.get(source);
|
|
1830
|
+
if (existing) {
|
|
1831
|
+
existing.push(dep);
|
|
1832
|
+
} else {
|
|
1833
|
+
groups.set(source, [dep]);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
for (const [source, deps] of groups) {
|
|
1837
|
+
const label = DEP_SOURCE_SHORT_NAMES[source] ?? source;
|
|
1838
|
+
const outLabel = terminalWidth ? fitCell(label, Math.max(1, terminalWidth - 4)) : label;
|
|
1839
|
+
log(c.gray(` ${outLabel}`));
|
|
1840
|
+
renderRows(deps, options, log, false, terminalWidth);
|
|
1841
|
+
log();
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
function renderFlat(sorted, options, log, terminalWidth) {
|
|
1845
|
+
renderRows(sorted, options, log, true, terminalWidth);
|
|
1846
|
+
log();
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
async function check(options) {
|
|
1850
|
+
const logLevel = options.output === "json" ? "silent" : options.loglevel;
|
|
1851
|
+
const logger = createLogger(logLevel);
|
|
1852
|
+
try {
|
|
1853
|
+
const packages = await loadPackages(options);
|
|
1854
|
+
if (packages.length === 0) {
|
|
1855
|
+
logger.warn("No packages found");
|
|
1856
|
+
if (options.output === "json") {
|
|
1857
|
+
outputJsonEnvelope([], options);
|
|
1858
|
+
}
|
|
1859
|
+
return 0;
|
|
1860
|
+
}
|
|
1861
|
+
await options.afterPackagesLoaded?.(packages);
|
|
1862
|
+
let hasUpdates = false;
|
|
1863
|
+
let didWrite = false;
|
|
1864
|
+
const jsonPackages = [];
|
|
1865
|
+
const progress = createCheckProgress(options, packages);
|
|
1866
|
+
const cache = createSqliteCache();
|
|
1867
|
+
const npmrc = loadNpmrc(options.cwd);
|
|
1868
|
+
const workspacePackageNames = new Set(packages.map((p) => p.name).filter(Boolean));
|
|
1869
|
+
try {
|
|
1870
|
+
for (const pkg of packages) {
|
|
1871
|
+
progress?.onPackageStart(pkg);
|
|
1872
|
+
await processPackage(pkg, options, {
|
|
1873
|
+
cache,
|
|
1874
|
+
npmrc,
|
|
1875
|
+
workspacePackageNames,
|
|
1876
|
+
onDependencyProcessed: () => progress?.onDependencyProcessed(),
|
|
1877
|
+
onHasUpdates: (updates) => {
|
|
1878
|
+
hasUpdates = true;
|
|
1879
|
+
if (options.output === "json") {
|
|
1880
|
+
jsonPackages.push(buildJsonPackage(pkg.name, updates));
|
|
1881
|
+
} else {
|
|
1882
|
+
renderTable(pkg.name, updates, options);
|
|
1883
|
+
}
|
|
1884
|
+
},
|
|
1885
|
+
onAllModeNoUpdates: () => {
|
|
1886
|
+
if (!options.all) return;
|
|
1887
|
+
if (options.output === "json") {
|
|
1888
|
+
jsonPackages.push(buildJsonPackage(pkg.name, []));
|
|
1889
|
+
} else {
|
|
1890
|
+
renderUpToDate(pkg.name);
|
|
1891
|
+
}
|
|
1892
|
+
},
|
|
1893
|
+
onDidWrite: () => {
|
|
1894
|
+
didWrite = true;
|
|
1895
|
+
},
|
|
1896
|
+
logger
|
|
1897
|
+
});
|
|
1898
|
+
progress?.onPackageEnd();
|
|
1899
|
+
}
|
|
1900
|
+
await options.afterPackagesEnd?.(packages);
|
|
1901
|
+
} finally {
|
|
1902
|
+
progress?.done();
|
|
1903
|
+
const stats = cache.stats();
|
|
1904
|
+
cache.close();
|
|
1905
|
+
logger.debug(`Cache stats: ${stats.hits} hits, ${stats.misses} misses, ${stats.size} entries`);
|
|
1906
|
+
}
|
|
1907
|
+
if (options.execute && options.write && didWrite) {
|
|
1908
|
+
await runExecute(options.execute, options.cwd, logger);
|
|
1909
|
+
}
|
|
1910
|
+
if (options.write && didWrite) {
|
|
1911
|
+
if (options.update) {
|
|
1912
|
+
await runUpdate(options.cwd, packages, logger);
|
|
1913
|
+
} else if (options.install) {
|
|
1914
|
+
await runInstall(options.cwd, packages, logger);
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
if (options.output === "json") {
|
|
1918
|
+
outputJsonEnvelope(jsonPackages, options);
|
|
1919
|
+
}
|
|
1920
|
+
if (!hasUpdates) {
|
|
1921
|
+
logger.success("All dependencies are up to date");
|
|
1922
|
+
}
|
|
1923
|
+
if (hasUpdates && options.output === "table") {
|
|
1924
|
+
if (options.mode === "default") {
|
|
1925
|
+
logger.info(c.gray("Tip: Run `depzy major` to check for major updates"));
|
|
1926
|
+
}
|
|
1927
|
+
if (!options.write) {
|
|
1928
|
+
logger.info(c.gray("Tip: Add `-w` to write changes to package files"));
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
return hasUpdates && !options.write && options.failOnOutdated ? 1 : 0;
|
|
1932
|
+
} catch (error) {
|
|
1933
|
+
logger.error("Check failed:", error instanceof Error ? error.message : String(error));
|
|
1934
|
+
return 2;
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
export { arrow as a, colorizeVersionDiff as b, check as c, detectPackageManager as d, colorDiff as e, applyVersionPrefix as f, getVersionPrefix as g, getDiff as h, truncate as i, padEnd as j, loadPackages as l, parseDependencies as p, resolvePackage as r, stripAnsi as s, timeDifference as t, writePackage as w };
|