inup 1.6.6 → 1.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/{interactive-ui.js → app/interactive-ui.js} +32 -21
- package/dist/{core → app}/upgrade-runner.js +12 -11
- package/dist/cli.js +12 -10
- package/dist/{services → features/audit}/background-audit.js +2 -2
- package/dist/features/{headless/vulnerability-audit.js → audit/headless-audit.js} +9 -5
- package/dist/features/audit/index.js +36 -0
- package/dist/{ui/presenters/vulnerability.js → features/audit/presenter.js} +1 -1
- package/dist/{ui/controllers → features/audit}/vulnerability-audit-controller.js +12 -6
- package/dist/{services → features/audit}/vulnerability-checker.js +11 -8
- package/dist/features/changelog/clients/github-client.js +35 -17
- package/dist/features/changelog/clients/npm-registry-client.js +16 -2
- package/dist/features/changelog/index.js +1 -1
- package/dist/features/changelog/services/package-metadata-service.js +6 -2
- package/dist/features/changelog/services/release-notes-service.js +6 -10
- package/dist/features/changelog/types.js +3 -0
- package/dist/features/debug/index.js +3 -4
- package/dist/features/debug/{services/perf-logger.js → perf-logger.js} +1 -1
- package/dist/features/debug/types.js +3 -0
- package/dist/features/headless/headless-runner.js +11 -11
- package/dist/features/headless/index.js +1 -4
- package/dist/features/headless/report.js +2 -0
- package/dist/features/interactive/controllers/index.js +6 -0
- package/dist/{ui → features/interactive}/controllers/package-info-modal-controller.js +3 -3
- package/dist/features/interactive/index.js +21 -0
- package/dist/{ui → features/interactive}/input-handler.js +3 -3
- package/dist/{ui → features/interactive}/modal/layout.js +2 -2
- package/dist/{ui/renderer/package-list.js → features/interactive/modal/package-info-sections/index.js} +3 -3
- package/dist/{ui → features/interactive}/modal/package-info-sections/sections.js +53 -22
- package/dist/{ui → features/interactive}/modal/package-info-sections/text.js +5 -5
- package/dist/{ui → features/interactive}/modal/package-info.js +31 -13
- package/dist/features/interactive/modal/types.js +3 -0
- package/dist/{ui → features/interactive}/presenters/health.js +2 -2
- package/dist/{ui → features/interactive}/renderer/index.js +0 -1
- package/dist/{types.js → features/interactive/renderer/package-list/index.js} +3 -4
- package/dist/{ui → features/interactive}/renderer/package-list/interface.js +9 -10
- package/dist/{ui → features/interactive}/renderer/package-list/rows.js +24 -17
- package/dist/features/{debug → interactive}/renderer/performance-modal.js +1 -1
- package/dist/{ui/utils/version.js → features/interactive/renderer/version-format.js} +6 -10
- package/dist/{ui → features/interactive}/session/index.js +2 -1
- package/dist/{ui → features/interactive}/session/interactive-session.js +39 -19
- package/dist/{ui → features/interactive}/session/selection-state-builder.js +20 -5
- package/dist/{ui → features/interactive}/state/filter-manager.js +2 -2
- package/dist/{ui → features/interactive}/state/state-manager.js +6 -4
- package/dist/{ui → features/interactive}/state/theme-manager.js +8 -4
- package/dist/{ui → features/interactive}/themes-colors.js +5 -2
- package/dist/features/upgrade/index.js +8 -0
- package/dist/{core → features/upgrade}/package-detector.js +74 -33
- package/dist/{core → features/upgrade}/upgrader.js +72 -14
- package/dist/index.js +2 -2
- package/dist/{config → shared/config}/package-meta.js +1 -1
- package/dist/{config → shared/config}/project-config.js +4 -1
- package/dist/{utils/config.js → shared/config/user-config.js} +3 -3
- package/dist/{ui/modal/package-info-sections.js → shared/fs/index.js} +4 -3
- package/dist/{utils/filesystem → shared/fs}/paths.js +3 -3
- package/dist/{utils/filesystem → shared/fs}/scan.js +5 -2
- package/dist/{services → shared}/http/etag-store.js +56 -4
- package/dist/{utils → shared}/local-env.js +1 -1
- package/dist/{services/package-manager-detector.js → shared/package-manager.js} +1 -1
- package/dist/shared/pnpm-catalogs.js +138 -0
- package/dist/{services → shared/registry}/npm-registry.js +48 -29
- package/dist/shared/registry/registry-config.js +76 -0
- package/dist/{services → shared/registry}/version-checker.js +24 -25
- package/dist/{ui/utils → shared/terminal}/index.js +4 -6
- package/dist/{ui/utils → shared/terminal}/terminal-input.js +2 -1
- package/dist/shared/terminal/text.js +48 -0
- package/dist/shared/types/index.js +20 -0
- package/dist/{utils/version.js → shared/versions.js} +12 -1
- package/package.json +15 -6
- package/dist/core/index.js +0 -23
- package/dist/features/changelog/types/changelog.types.js +0 -3
- package/dist/features/debug/types/debug.types.js +0 -3
- package/dist/services/index.js +0 -25
- package/dist/ui/controllers/index.js +0 -8
- package/dist/ui/index.js +0 -27
- package/dist/ui/presenters/index.js +0 -11
- package/dist/ui/utils/text.js +0 -76
- package/dist/utils/filesystem.js +0 -20
- package/dist/utils/index.js +0 -34
- /package/dist/{ui/modal → features/audit}/types.js +0 -0
- /package/dist/features/debug/{services/performance-tracker.js → performance-tracker.js} +0 -0
- /package/dist/{ui → features/interactive}/keymap.js +0 -0
- /package/dist/{ui → features/interactive}/modal/index.js +0 -0
- /package/dist/{ui → features/interactive}/modal/package-info-sections/release-notes.js +0 -0
- /package/dist/{ui → features/interactive}/modal/theme-selector.js +0 -0
- /package/dist/{ui → features/interactive}/renderer/confirmation.js +0 -0
- /package/dist/{ui → features/interactive}/renderer/help-modal.js +0 -0
- /package/dist/{ui → features/interactive}/session/action-dispatcher.js +0 -0
- /package/dist/{ui → features/interactive}/state/index.js +0 -0
- /package/dist/{ui → features/interactive}/state/modal-manager.js +0 -0
- /package/dist/{ui → features/interactive}/state/navigation-manager.js +0 -0
- /package/dist/{ui → features/interactive}/themes.js +0 -0
- /package/dist/{config → shared/config}/constants.js +0 -0
- /package/dist/{config → shared/config}/index.js +0 -0
- /package/dist/{utils → shared}/debug-logger.js +0 -0
- /package/dist/{utils → shared}/engines.js +0 -0
- /package/dist/{utils → shared}/exec.js +0 -0
- /package/dist/{utils/filesystem → shared/fs}/io.js +0 -0
- /package/dist/{utils → shared}/git.js +0 -0
- /package/dist/{services → shared}/http/adaptive-controller.js +0 -0
- /package/dist/{services → shared}/http/inflight.js +0 -0
- /package/dist/{services → shared}/http/resizable-semaphore.js +0 -0
- /package/dist/{services → shared}/http/retry.js +0 -0
- /package/dist/{utils → shared}/manifest.js +0 -0
- /package/dist/{utils → shared/terminal}/color.js +0 -0
- /package/dist/{ui/utils → shared/terminal}/cursor.js +0 -0
- /package/dist/{types → shared/types}/domain.js +0 -0
- /package/dist/{types → shared/types}/streaming.js +0 -0
- /package/dist/{types → shared/types}/ui.js +0 -0
|
@@ -39,12 +39,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
exports.PackageDetector = void 0;
|
|
40
40
|
const chalk_1 = __importDefault(require("chalk"));
|
|
41
41
|
const semver = __importStar(require("semver"));
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
const
|
|
42
|
+
const fs_1 = require("../../shared/fs");
|
|
43
|
+
const versions_1 = require("../../shared/versions");
|
|
44
|
+
const pnpm_catalogs_1 = require("../../shared/pnpm-catalogs");
|
|
45
|
+
const npm_registry_1 = require("../../shared/registry/npm-registry");
|
|
46
|
+
const config_1 = require("../../shared/config");
|
|
47
|
+
const terminal_1 = require("../../shared/terminal");
|
|
48
|
+
const debug_logger_1 = require("../../shared/debug-logger");
|
|
49
|
+
const debug_1 = require("../debug");
|
|
48
50
|
class PackageDetector {
|
|
49
51
|
packageJsonPath = null;
|
|
50
52
|
packageJson = null;
|
|
@@ -63,9 +65,9 @@ class PackageDetector {
|
|
|
63
65
|
this.ignorePackages = options?.ignorePackages || [];
|
|
64
66
|
this.maxDepth = options?.maxDepth ?? 10;
|
|
65
67
|
this.adaptive = options?.adaptive ?? true;
|
|
66
|
-
this.packageJsonPath = (0,
|
|
68
|
+
this.packageJsonPath = (0, fs_1.findPackageJson)(this.cwd);
|
|
67
69
|
if (this.packageJsonPath) {
|
|
68
|
-
this.packageJson = (0,
|
|
70
|
+
this.packageJson = (0, fs_1.readPackageJson)(this.packageJsonPath);
|
|
69
71
|
}
|
|
70
72
|
}
|
|
71
73
|
hasPackageJson() {
|
|
@@ -103,7 +105,7 @@ class PackageDetector {
|
|
|
103
105
|
throw new Error('No package.json found in current directory');
|
|
104
106
|
}
|
|
105
107
|
const t0 = Date.now();
|
|
106
|
-
|
|
108
|
+
debug_logger_1.debugLog.info('PackageDetector', `Starting scan in ${this.cwd}`);
|
|
107
109
|
const prepared = await this.prepareDependencies();
|
|
108
110
|
const initialPayload = {
|
|
109
111
|
allDependencies: prepared.allDependencies,
|
|
@@ -119,8 +121,8 @@ class PackageDetector {
|
|
|
119
121
|
let batchIndex = 0;
|
|
120
122
|
let lastBatchEndAt = Date.now();
|
|
121
123
|
const tFetch = Date.now();
|
|
122
|
-
|
|
123
|
-
await (0,
|
|
124
|
+
debug_logger_1.debugLog.info('PackageDetector', 'fetching version data via npm registry in batches');
|
|
125
|
+
await (0, npm_registry_1.fetchPackageVersions)(prepared.uniquePackages, {
|
|
124
126
|
currentVersions: prepared.currentVersions,
|
|
125
127
|
batchSize: this.batchSize,
|
|
126
128
|
maxConcurrency: this.maxConcurrency,
|
|
@@ -167,11 +169,11 @@ class PackageDetector {
|
|
|
167
169
|
});
|
|
168
170
|
},
|
|
169
171
|
});
|
|
170
|
-
|
|
172
|
+
debug_logger_1.debugLog.perf('PackageDetector', `registry fetch (${resolved}/${prepared.uniquePackages.length} resolved)`, tFetch);
|
|
171
173
|
performanceTracker.recordPhaseDuration('registryFetch', Date.now() - tFetch);
|
|
172
174
|
const finalPackages = prepared.uniquePackages.flatMap((packageName) => packageLookup.get(packageName) ?? []);
|
|
173
175
|
const progress = this.createProgressSnapshot(prepared.uniquePackages.length, resolved, failed, false);
|
|
174
|
-
|
|
176
|
+
debug_logger_1.debugLog.perf('PackageDetector', `total scan complete (${finalPackages.filter((p) => p.isOutdated).length} outdated of ${finalPackages.length} deps)`, t0);
|
|
175
177
|
onEvent({
|
|
176
178
|
type: 'complete',
|
|
177
179
|
payload: {
|
|
@@ -179,7 +181,7 @@ class PackageDetector {
|
|
|
179
181
|
progress,
|
|
180
182
|
},
|
|
181
183
|
});
|
|
182
|
-
|
|
184
|
+
terminal_1.ConsoleUtils.clearProgress();
|
|
183
185
|
return finalPackages;
|
|
184
186
|
}
|
|
185
187
|
async prepareDependencies() {
|
|
@@ -187,7 +189,7 @@ class PackageDetector {
|
|
|
187
189
|
this.showProgress('🔍 Scanning repository for package.json files...');
|
|
188
190
|
const tScan = Date.now();
|
|
189
191
|
const allPackageJsonFiles = await this.findPackageJsonFilesWithTimeout(30000);
|
|
190
|
-
|
|
192
|
+
debug_logger_1.debugLog.perf('PackageDetector', `file scan (${allPackageJsonFiles.length} files)`, tScan, {
|
|
191
193
|
files: allPackageJsonFiles,
|
|
192
194
|
});
|
|
193
195
|
performanceTracker.recordPhaseDuration('discovery', Date.now() - tScan);
|
|
@@ -195,11 +197,11 @@ class PackageDetector {
|
|
|
195
197
|
this.showProgress(`🔍 Found ${allPackageJsonFiles.length} package.json file${allPackageJsonFiles.length === 1 ? '' : 's'}`);
|
|
196
198
|
this.showProgress('🔍 Reading dependencies from package.json files...');
|
|
197
199
|
const tDeps = Date.now();
|
|
198
|
-
const allDepsRaw = await (0,
|
|
200
|
+
const allDepsRaw = await (0, fs_1.collectAllDependenciesAsync)(allPackageJsonFiles, {
|
|
199
201
|
includePeerDeps: true,
|
|
200
202
|
includeOptionalDeps: true,
|
|
201
203
|
});
|
|
202
|
-
|
|
204
|
+
debug_logger_1.debugLog.perf('PackageDetector', `dependency collection (${allDepsRaw.length} raw deps)`, tDeps);
|
|
203
205
|
performanceTracker.recordPhaseDuration('depCollection', Date.now() - tDeps);
|
|
204
206
|
performanceTracker.recordCounts({ rawDependencies: allDepsRaw.length });
|
|
205
207
|
this.showProgress('🔍 Identifying unique packages...');
|
|
@@ -209,12 +211,50 @@ class PackageDetector {
|
|
|
209
211
|
let ignoredCount = 0;
|
|
210
212
|
const seenWorkspaceRefs = new Set();
|
|
211
213
|
const seenIgnored = new Set();
|
|
212
|
-
|
|
214
|
+
// pnpm catalogs: `"react": "catalog:"` gets its real range from
|
|
215
|
+
// pnpm-workspace.yaml. Each catalog entry becomes ONE upgradable dependency
|
|
216
|
+
// sourced from that file, no matter how many workspace packages reference it.
|
|
217
|
+
const catalogs = pnpm_catalogs_1.PnpmCatalogs.load(this.cwd);
|
|
218
|
+
const seenCatalogEntries = new Map();
|
|
219
|
+
for (const rawDep of allDepsRaw) {
|
|
220
|
+
let dep = {
|
|
221
|
+
name: rawDep.name,
|
|
222
|
+
version: rawDep.version,
|
|
223
|
+
type: rawDep.type,
|
|
224
|
+
packageJsonPath: rawDep.packageJsonPath,
|
|
225
|
+
};
|
|
226
|
+
if ((0, pnpm_catalogs_1.isCatalogReference)(rawDep.version)) {
|
|
227
|
+
const resolution = catalogs?.resolve(rawDep.name, rawDep.version) ?? null;
|
|
228
|
+
if (!resolution) {
|
|
229
|
+
debug_logger_1.debugLog.warn('PackageDetector', `skipping unresolvable catalog ref: ${rawDep.name}@${rawDep.version}`);
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const catalogKey = `${resolution.catalog}:${rawDep.name}`;
|
|
233
|
+
const existing = seenCatalogEntries.get(catalogKey);
|
|
234
|
+
if (existing) {
|
|
235
|
+
// Same catalog entry, another referencing package: remember who uses
|
|
236
|
+
// it (for the info modal's Used-by tab) but keep the single entry.
|
|
237
|
+
if (!existing.catalogReferencedBy.includes(rawDep.packageJsonPath)) {
|
|
238
|
+
existing.catalogReferencedBy.push(rawDep.packageJsonPath);
|
|
239
|
+
}
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
dep = {
|
|
243
|
+
name: rawDep.name,
|
|
244
|
+
version: resolution.range,
|
|
245
|
+
type: rawDep.type,
|
|
246
|
+
packageJsonPath: catalogs.path,
|
|
247
|
+
catalog: resolution.catalog,
|
|
248
|
+
catalogEntries: catalogs.entriesOf(resolution.catalog),
|
|
249
|
+
catalogReferencedBy: [rawDep.packageJsonPath],
|
|
250
|
+
};
|
|
251
|
+
seenCatalogEntries.set(catalogKey, dep);
|
|
252
|
+
}
|
|
213
253
|
if (this.isWorkspaceReference(dep.version)) {
|
|
214
254
|
const key = `${dep.name}@${dep.version}`;
|
|
215
255
|
if (!seenWorkspaceRefs.has(key)) {
|
|
216
256
|
seenWorkspaceRefs.add(key);
|
|
217
|
-
|
|
257
|
+
debug_logger_1.debugLog.info('PackageDetector', `skipping workspace ref: ${key}`);
|
|
218
258
|
}
|
|
219
259
|
continue;
|
|
220
260
|
}
|
|
@@ -222,16 +262,11 @@ class PackageDetector {
|
|
|
222
262
|
ignoredCount++;
|
|
223
263
|
if (!seenIgnored.has(dep.name)) {
|
|
224
264
|
seenIgnored.add(dep.name);
|
|
225
|
-
|
|
265
|
+
debug_logger_1.debugLog.info('PackageDetector', `ignoring package: ${dep.name}`);
|
|
226
266
|
}
|
|
227
267
|
continue;
|
|
228
268
|
}
|
|
229
|
-
allDependencies.push(
|
|
230
|
-
name: dep.name,
|
|
231
|
-
version: dep.version,
|
|
232
|
-
type: dep.type,
|
|
233
|
-
packageJsonPath: dep.packageJsonPath,
|
|
234
|
-
});
|
|
269
|
+
allDependencies.push(dep);
|
|
235
270
|
uniquePackageNames.add(dep.name);
|
|
236
271
|
}
|
|
237
272
|
if (ignoredCount > 0) {
|
|
@@ -246,7 +281,7 @@ class PackageDetector {
|
|
|
246
281
|
return 1;
|
|
247
282
|
return a.localeCompare(b);
|
|
248
283
|
});
|
|
249
|
-
|
|
284
|
+
debug_logger_1.debugLog.info('PackageDetector', `${uniquePackages.length} unique packages to check, ${ignoredCount} ignored`);
|
|
250
285
|
performanceTracker.recordPhaseDuration('filter', Date.now() - tFilter);
|
|
251
286
|
performanceTracker.recordCounts({
|
|
252
287
|
uniquePackages: uniquePackages.length,
|
|
@@ -274,12 +309,12 @@ class PackageDetector {
|
|
|
274
309
|
if (!packageData || packageData.latestVersion === 'unknown') {
|
|
275
310
|
if (!loggedNoData.has(dep.name)) {
|
|
276
311
|
loggedNoData.add(dep.name);
|
|
277
|
-
|
|
312
|
+
debug_logger_1.debugLog.warn('PackageDetector', `no data returned for ${dep.name} — marking unavailable`);
|
|
278
313
|
}
|
|
279
314
|
return this.createFailedPackageInfo(dep);
|
|
280
315
|
}
|
|
281
316
|
const { latestVersion, allVersions } = packageData;
|
|
282
|
-
const closestMinorVersion = (0,
|
|
317
|
+
const closestMinorVersion = (0, versions_1.findClosestMinorVersion)(dep.version, allVersions);
|
|
283
318
|
const installedClean = semver.coerce(dep.version)?.version || dep.version;
|
|
284
319
|
const minorClean = closestMinorVersion
|
|
285
320
|
? semver.coerce(closestMinorVersion)?.version || closestMinorVersion
|
|
@@ -294,7 +329,7 @@ class PackageDetector {
|
|
|
294
329
|
const outdatedKey = `${dep.name}@${dep.version}`;
|
|
295
330
|
if (!loggedOutdated.has(outdatedKey)) {
|
|
296
331
|
loggedOutdated.add(outdatedKey);
|
|
297
|
-
|
|
332
|
+
debug_logger_1.debugLog.info('PackageDetector', `outdated: ${dep.name} ${dep.version} → range:${closestMinorVersion ?? '-'} latest:${latestVersion}`);
|
|
298
333
|
}
|
|
299
334
|
}
|
|
300
335
|
return {
|
|
@@ -304,6 +339,9 @@ class PackageDetector {
|
|
|
304
339
|
latestVersion,
|
|
305
340
|
type: dep.type,
|
|
306
341
|
packageJsonPath: dep.packageJsonPath,
|
|
342
|
+
catalog: dep.catalog,
|
|
343
|
+
catalogEntries: dep.catalogEntries,
|
|
344
|
+
catalogReferencedBy: dep.catalogReferencedBy,
|
|
307
345
|
isOutdated,
|
|
308
346
|
hasRangeUpdate,
|
|
309
347
|
hasMajorUpdate,
|
|
@@ -313,7 +351,7 @@ class PackageDetector {
|
|
|
313
351
|
};
|
|
314
352
|
}
|
|
315
353
|
catch (error) {
|
|
316
|
-
|
|
354
|
+
debug_logger_1.debugLog.error('PackageDetector', `error processing ${dep.name}`, error);
|
|
317
355
|
return this.createFailedPackageInfo(dep);
|
|
318
356
|
}
|
|
319
357
|
});
|
|
@@ -326,6 +364,9 @@ class PackageDetector {
|
|
|
326
364
|
latestVersion: 'unknown',
|
|
327
365
|
type: dep.type,
|
|
328
366
|
packageJsonPath: dep.packageJsonPath,
|
|
367
|
+
catalog: dep.catalog,
|
|
368
|
+
catalogEntries: dep.catalogEntries,
|
|
369
|
+
catalogReferencedBy: dep.catalogReferencedBy,
|
|
329
370
|
isOutdated: false,
|
|
330
371
|
hasRangeUpdate: false,
|
|
331
372
|
hasMajorUpdate: false,
|
|
@@ -346,7 +387,7 @@ class PackageDetector {
|
|
|
346
387
|
let timeoutId;
|
|
347
388
|
try {
|
|
348
389
|
const files = await Promise.race([
|
|
349
|
-
(0,
|
|
390
|
+
(0, fs_1.findAllPackageJsonFilesAsync)(this.cwd, this.excludePatterns, this.maxDepth, (currentDir, foundCount) => {
|
|
350
391
|
const truncatedDir = currentDir.length > 50 ? '...' + currentDir.slice(-47) : currentDir;
|
|
351
392
|
this.showProgress(`🔍 Scanning ${truncatedDir} (found ${foundCount})`);
|
|
352
393
|
}, {
|
|
@@ -397,7 +438,7 @@ class PackageDetector {
|
|
|
397
438
|
version.startsWith('bitbucket:'));
|
|
398
439
|
}
|
|
399
440
|
showProgress(message) {
|
|
400
|
-
|
|
441
|
+
terminal_1.ConsoleUtils.showProgress(message);
|
|
401
442
|
}
|
|
402
443
|
getOutdatedPackagesOnly(packages) {
|
|
403
444
|
return packages.filter((pkg) => pkg.isOutdated);
|
|
@@ -9,7 +9,9 @@ const nanospinner_1 = require("nanospinner");
|
|
|
9
9
|
const fs_1 = require("fs");
|
|
10
10
|
const path_1 = require("path");
|
|
11
11
|
const child_process_1 = require("child_process");
|
|
12
|
-
const
|
|
12
|
+
const exec_1 = require("../../shared/exec");
|
|
13
|
+
const fs_2 = require("../../shared/fs");
|
|
14
|
+
const pnpm_catalogs_1 = require("../../shared/pnpm-catalogs");
|
|
13
15
|
class PackageUpgrader {
|
|
14
16
|
packageManager;
|
|
15
17
|
quiet;
|
|
@@ -25,15 +27,25 @@ class PackageUpgrader {
|
|
|
25
27
|
this.log(chalk_1.default.yellow('No packages to upgrade.'));
|
|
26
28
|
return;
|
|
27
29
|
}
|
|
30
|
+
// Catalog entries live in pnpm-workspace.yaml, not a package.json — they
|
|
31
|
+
// take the YAML write path below.
|
|
32
|
+
const catalogChoices = choices.filter((choice) => choice.catalog);
|
|
33
|
+
const fileChoices = choices.filter((choice) => !choice.catalog);
|
|
28
34
|
// Group choices by package.json path and dependency type
|
|
29
|
-
const choicesByFileAndType = this.groupChoicesByFileAndType(
|
|
35
|
+
const choicesByFileAndType = this.groupChoicesByFileAndType(fileChoices);
|
|
30
36
|
for (const [fileAndType, choiceList] of Object.entries(choicesByFileAndType)) {
|
|
37
|
+
// groupChoicesByFileAndType only creates a group when it has a member.
|
|
38
|
+
/* v8 ignore start */
|
|
31
39
|
if (choiceList.length === 0)
|
|
32
40
|
continue;
|
|
41
|
+
/* v8 ignore stop */
|
|
33
42
|
const [packageJsonPath, type] = fileAndType.split('|');
|
|
34
43
|
this.log(`Processing ${type} in ${packageJsonPath}`);
|
|
35
44
|
await this.upgradeChoiceGroup(choiceList, packageJsonPath, type);
|
|
36
45
|
}
|
|
46
|
+
if (catalogChoices.length > 0) {
|
|
47
|
+
await this.upgradeCatalogChoices(catalogChoices);
|
|
48
|
+
}
|
|
37
49
|
// Count unique packages upgraded
|
|
38
50
|
const uniquePackages = new Set(choices.map((c) => c.name));
|
|
39
51
|
this.log(chalk_1.default.green(`\n✅ Successfully upgraded ${uniquePackages.size} package(s)!`));
|
|
@@ -41,18 +53,22 @@ class PackageUpgrader {
|
|
|
41
53
|
await this.runInstall(choices);
|
|
42
54
|
}
|
|
43
55
|
async runInstall(choices) {
|
|
56
|
+
// upgradePackages returns early for an empty selection, so this guard only
|
|
57
|
+
// protects future direct callers.
|
|
58
|
+
/* v8 ignore start */
|
|
44
59
|
if (choices.length === 0) {
|
|
45
60
|
return;
|
|
46
61
|
}
|
|
62
|
+
/* v8 ignore stop */
|
|
47
63
|
// Determine the directory to run install in
|
|
48
64
|
// Use workspace root if it exists, otherwise use the directory of the first package.json
|
|
49
65
|
const firstPackageJsonPath = choices[0].packageJsonPath;
|
|
50
66
|
const firstPackageDir = (0, path_1.dirname)(firstPackageJsonPath);
|
|
51
|
-
const workspaceRoot = (0,
|
|
67
|
+
const workspaceRoot = (0, fs_2.findWorkspaceRoot)(firstPackageDir, this.packageManager.name);
|
|
52
68
|
const installDir = workspaceRoot || firstPackageDir;
|
|
53
69
|
// Check if package manager is installed
|
|
54
70
|
try {
|
|
55
|
-
(0,
|
|
71
|
+
(0, exec_1.executeCommand)(`${this.packageManager.name} --version`, installDir);
|
|
56
72
|
}
|
|
57
73
|
catch (error) {
|
|
58
74
|
this.log(chalk_1.default.yellow(`\n⚠️ ${this.packageManager.displayName} is detected but not installed on your system.\n` +
|
|
@@ -84,6 +100,53 @@ class PackageUpgrader {
|
|
|
84
100
|
throw new Error(`${installCommand} exited with code ${result.status}`);
|
|
85
101
|
}
|
|
86
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Apply catalog upgrades by rewriting the referenced ranges inside
|
|
105
|
+
* pnpm-workspace.yaml (comment- and format-preserving). One write per file.
|
|
106
|
+
*/
|
|
107
|
+
async upgradeCatalogChoices(choices) {
|
|
108
|
+
const choicesByFile = new Map();
|
|
109
|
+
choices.forEach((choice) => {
|
|
110
|
+
const group = choicesByFile.get(choice.packageJsonPath) ?? [];
|
|
111
|
+
group.push(choice);
|
|
112
|
+
choicesByFile.set(choice.packageJsonPath, group);
|
|
113
|
+
});
|
|
114
|
+
for (const [workspaceFilePath, fileChoices] of choicesByFile) {
|
|
115
|
+
if (!(0, fs_1.existsSync)(workspaceFilePath)) {
|
|
116
|
+
this.log(chalk_1.default.yellow(`⚠️ Skipping catalog entries in ${workspaceFilePath} - file not found`));
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
this.log(`Processing catalog entries in ${workspaceFilePath}`);
|
|
120
|
+
const spinner = this.quiet
|
|
121
|
+
? null
|
|
122
|
+
: (0, nanospinner_1.createSpinner)(`Upgrading catalog entries in ${workspaceFilePath}...`).start();
|
|
123
|
+
try {
|
|
124
|
+
(0, pnpm_catalogs_1.writeCatalogUpdates)(workspaceFilePath, fileChoices.map((choice) => ({
|
|
125
|
+
catalog: choice.catalog,
|
|
126
|
+
name: choice.name,
|
|
127
|
+
range: choice.targetVersion,
|
|
128
|
+
})));
|
|
129
|
+
const message = `Upgraded ${fileChoices.length} catalog entr${fileChoices.length === 1 ? 'y' : 'ies'} in ${workspaceFilePath}`;
|
|
130
|
+
if (spinner)
|
|
131
|
+
spinner.success({ text: message });
|
|
132
|
+
else
|
|
133
|
+
this.log(chalk_1.default.green(`✔ ${message}`));
|
|
134
|
+
fileChoices.forEach((choice) => {
|
|
135
|
+
const upgradeTypeColor = choice.upgradeType === 'range' ? chalk_1.default.yellow : chalk_1.default.red;
|
|
136
|
+
const catalogLabel = choice.catalog === 'default' ? 'catalog' : `catalog:${choice.catalog}`;
|
|
137
|
+
this.log(` ${chalk_1.default.green('✓')} ${chalk_1.default.cyan(choice.name)} (${catalogLabel}) → ${upgradeTypeColor(choice.targetVersion)}`);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
if (spinner)
|
|
142
|
+
spinner.error({ text: `Failed to upgrade catalog entries in ${workspaceFilePath}` });
|
|
143
|
+
else
|
|
144
|
+
this.log(chalk_1.default.red(`✖ Failed to upgrade catalog entries in ${workspaceFilePath}`));
|
|
145
|
+
console.error(chalk_1.default.red(`Error: ${error}`));
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
87
150
|
groupChoicesByFileAndType(choices) {
|
|
88
151
|
const groups = {};
|
|
89
152
|
choices.forEach((choice) => {
|
|
@@ -109,11 +172,10 @@ class PackageUpgrader {
|
|
|
109
172
|
try {
|
|
110
173
|
// Read the current package.json — keep the raw text so we can round-trip its formatting
|
|
111
174
|
const rawContent = (0, fs_1.readFileSync)(packageJsonPath, 'utf-8');
|
|
112
|
-
const packageJson = (0,
|
|
175
|
+
const packageJson = (0, fs_2.readPackageJson)(packageJsonPath);
|
|
113
176
|
// Group by upgrade type (range vs latest)
|
|
114
177
|
const rangeChoices = choices.filter((c) => c.upgradeType === 'range');
|
|
115
178
|
const latestChoices = choices.filter((c) => c.upgradeType === 'latest');
|
|
116
|
-
let modified = false;
|
|
117
179
|
// Upgrade range versions by directly modifying package.json
|
|
118
180
|
if (rangeChoices.length > 0) {
|
|
119
181
|
if (!packageJson[type]) {
|
|
@@ -121,7 +183,6 @@ class PackageUpgrader {
|
|
|
121
183
|
}
|
|
122
184
|
rangeChoices.forEach((choice) => {
|
|
123
185
|
packageJson[type][choice.name] = choice.targetVersion;
|
|
124
|
-
modified = true;
|
|
125
186
|
});
|
|
126
187
|
}
|
|
127
188
|
// Upgrade to latest versions by directly modifying package.json
|
|
@@ -131,17 +192,14 @@ class PackageUpgrader {
|
|
|
131
192
|
}
|
|
132
193
|
latestChoices.forEach((choice) => {
|
|
133
194
|
packageJson[type][choice.name] = choice.targetVersion;
|
|
134
|
-
modified = true;
|
|
135
195
|
});
|
|
136
196
|
}
|
|
137
197
|
// Write back the modified package.json, preserving the original indentation and
|
|
138
198
|
// trailing-newline style. Skip the write entirely when nothing actually changed.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
(0, fs_1.writeFileSync)(packageJsonPath, nextContent);
|
|
144
|
-
}
|
|
199
|
+
const format = (0, fs_2.detectJsonFormat)(rawContent);
|
|
200
|
+
const nextContent = JSON.stringify(packageJson, null, format.indent) + (format.trailingNewline ? '\n' : '');
|
|
201
|
+
if (nextContent !== rawContent) {
|
|
202
|
+
(0, fs_1.writeFileSync)(packageJsonPath, nextContent);
|
|
145
203
|
}
|
|
146
204
|
if (spinner)
|
|
147
205
|
spinner.success({ text: `Upgraded ${choices.length} ${type} in ${packageDir}` });
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.UpgradeRunner = void 0;
|
|
7
|
-
var
|
|
8
|
-
Object.defineProperty(exports, "UpgradeRunner", { enumerable: true, get: function () { return
|
|
7
|
+
var upgrade_runner_1 = require("./app/upgrade-runner");
|
|
8
|
+
Object.defineProperty(exports, "UpgradeRunner", { enumerable: true, get: function () { return upgrade_runner_1.UpgradeRunner; } });
|
|
9
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.PACKAGE_VERSION = exports.PACKAGE_NAME = void 0;
|
|
7
|
-
const package_json_1 = __importDefault(require("
|
|
7
|
+
const package_json_1 = __importDefault(require("../../../package.json"));
|
|
8
8
|
exports.PACKAGE_NAME = package_json_1.default.name;
|
|
9
9
|
exports.PACKAGE_VERSION = package_json_1.default.version;
|
|
10
10
|
//# sourceMappingURL=package-meta.js.map
|
|
@@ -31,10 +31,13 @@ function loadProjectConfig(cwd) {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
// Move to parent directory
|
|
34
|
+
// Move to parent directory. The self-parent check only trips on Windows
|
|
35
|
+
// drive roots ('C:\'); on POSIX the loop condition exits at '/' first.
|
|
35
36
|
const parentDir = (0, path_1.join)(currentDir, '..');
|
|
37
|
+
/* v8 ignore start */
|
|
36
38
|
if (parentDir === currentDir)
|
|
37
39
|
break;
|
|
40
|
+
/* v8 ignore stop */
|
|
38
41
|
currentDir = parentDir;
|
|
39
42
|
}
|
|
40
43
|
return {};
|
|
@@ -7,14 +7,14 @@ exports.configManager = void 0;
|
|
|
7
7
|
const fs_1 = require("fs");
|
|
8
8
|
const path_1 = require("path");
|
|
9
9
|
const env_paths_1 = __importDefault(require("env-paths"));
|
|
10
|
-
const
|
|
10
|
+
const package_meta_1 = require("./package-meta");
|
|
11
11
|
class ConfigManager {
|
|
12
12
|
configDir;
|
|
13
13
|
configPath;
|
|
14
14
|
constructor() {
|
|
15
15
|
// Use env-paths for cross-platform config directory support
|
|
16
16
|
// Mac/Linux: ~/.config/inup, Windows: %APPDATA%/inup
|
|
17
|
-
const paths = (0, env_paths_1.default)(
|
|
17
|
+
const paths = (0, env_paths_1.default)(package_meta_1.PACKAGE_NAME);
|
|
18
18
|
this.configDir = paths.config;
|
|
19
19
|
this.configPath = (0, path_1.join)(this.configDir, 'config.json');
|
|
20
20
|
}
|
|
@@ -66,4 +66,4 @@ class ConfigManager {
|
|
|
66
66
|
}
|
|
67
67
|
// Export singleton instance
|
|
68
68
|
exports.configManager = new ConfigManager();
|
|
69
|
-
//# sourceMappingURL=config.js.map
|
|
69
|
+
//# sourceMappingURL=user-config.js.map
|
|
@@ -14,6 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./
|
|
18
|
-
__exportStar(require("./
|
|
19
|
-
|
|
17
|
+
__exportStar(require("./paths"), exports);
|
|
18
|
+
__exportStar(require("./io"), exports);
|
|
19
|
+
__exportStar(require("./scan"), exports);
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -4,16 +4,16 @@ exports.findPackageJson = findPackageJson;
|
|
|
4
4
|
exports.findWorkspaceRoot = findWorkspaceRoot;
|
|
5
5
|
const fs_1 = require("fs");
|
|
6
6
|
const path_1 = require("path");
|
|
7
|
-
const
|
|
7
|
+
const package_manager_1 = require("../package-manager");
|
|
8
8
|
function findPackageJson(cwd = process.cwd()) {
|
|
9
9
|
const packageJsonPath = (0, path_1.join)(cwd, 'package.json');
|
|
10
10
|
return (0, fs_1.existsSync)(packageJsonPath) ? packageJsonPath : null;
|
|
11
11
|
}
|
|
12
12
|
function findWorkspaceRoot(cwd = process.cwd(), packageManager) {
|
|
13
13
|
if (!packageManager) {
|
|
14
|
-
const detected =
|
|
14
|
+
const detected = package_manager_1.PackageManagerDetector.detect(cwd);
|
|
15
15
|
packageManager = detected.name;
|
|
16
16
|
}
|
|
17
|
-
return
|
|
17
|
+
return package_manager_1.PackageManagerDetector.findWorkspaceRoot(cwd, packageManager);
|
|
18
18
|
}
|
|
19
19
|
//# sourceMappingURL=paths.js.map
|
|
@@ -263,16 +263,19 @@ async function findAllPackageJsonFilesAsync(rootDir = process.cwd(), excludePatt
|
|
|
263
263
|
}
|
|
264
264
|
function pump() {
|
|
265
265
|
while (activeTasks < concurrency && pending.length > 0 && !failedError) {
|
|
266
|
+
// Non-null: pending.length > 0 was just checked and nothing runs in between.
|
|
266
267
|
const next = pending.shift();
|
|
267
|
-
if (!next)
|
|
268
|
-
break;
|
|
269
268
|
activeTasks++;
|
|
270
269
|
void processDirectory(next.dir, next.depth)
|
|
271
270
|
.catch((error) => {
|
|
271
|
+
// First error wins; a second in-flight task rejecting in the same
|
|
272
|
+
// tick is a race window that cannot be scheduled deterministically.
|
|
273
|
+
/* v8 ignore start */
|
|
272
274
|
if (!failedError) {
|
|
273
275
|
failedError = error;
|
|
274
276
|
rejectDone?.(error);
|
|
275
277
|
}
|
|
278
|
+
/* v8 ignore stop */
|
|
276
279
|
})
|
|
277
280
|
.finally(() => {
|
|
278
281
|
activeTasks--;
|
|
@@ -1,34 +1,64 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.setEtagCacheEnabled = setEtagCacheEnabled;
|
|
7
|
+
exports.setEtagCacheRoot = setEtagCacheRoot;
|
|
4
8
|
exports.readEtag = readEtag;
|
|
5
9
|
exports.writeEtag = writeEtag;
|
|
6
10
|
exports.etagCacheDir = etagCacheDir;
|
|
7
11
|
const node_crypto_1 = require("node:crypto");
|
|
8
12
|
const node_fs_1 = require("node:fs");
|
|
9
|
-
const node_os_1 = require("node:os");
|
|
10
13
|
const node_path_1 = require("node:path");
|
|
14
|
+
const env_paths_1 = __importDefault(require("env-paths"));
|
|
15
|
+
const config_1 = require("../config");
|
|
11
16
|
/** Bump when the on-disk entry shape changes; old generations are ignored. */
|
|
12
17
|
const SCHEMA = 'v1';
|
|
13
18
|
/** Entries untouched for longer than this are swept on first access. */
|
|
14
19
|
const MAX_AGE_MS = 14 * 24 * 60 * 60 * 1000; // 14 days
|
|
15
20
|
let enabled = true;
|
|
16
21
|
let sweptThisProcess = false;
|
|
22
|
+
let cacheRootOverride = null;
|
|
23
|
+
let resolvedSchemaDir = null;
|
|
17
24
|
/** Allow tests/callers to toggle the store without touching call sites. */
|
|
18
25
|
function setEtagCacheEnabled(value) {
|
|
19
26
|
enabled = value;
|
|
20
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Test hook: point the store at an isolated root instead of the user's real
|
|
30
|
+
* cache directory (null restores the default). Tests must never wipe or race
|
|
31
|
+
* on the persistent per-user cache.
|
|
32
|
+
*/
|
|
33
|
+
function setEtagCacheRoot(root) {
|
|
34
|
+
cacheRootOverride = root;
|
|
35
|
+
resolvedSchemaDir = null;
|
|
36
|
+
sweptThisProcess = false;
|
|
37
|
+
}
|
|
38
|
+
/** The root holding one subdirectory per schema generation. */
|
|
39
|
+
function cacheRoot() {
|
|
40
|
+
// Version data is not project-specific, so it lives in the per-user cache
|
|
41
|
+
// directory (env-paths, like the config dir), shared across every project the
|
|
42
|
+
// user scans. It used to live in the OS temp dir, but macOS clears that on
|
|
43
|
+
// reboot and Linux distros sweep it periodically — throwing the cache away
|
|
44
|
+
// exactly when it is most useful.
|
|
45
|
+
return cacheRootOverride ?? (0, node_path_1.join)((0, env_paths_1.default)(config_1.PACKAGE_NAME).cache, 'etag-cache');
|
|
46
|
+
}
|
|
21
47
|
/** Resolve (and lazily create) the cache directory, sweeping stale entries once. */
|
|
22
48
|
function cacheDir() {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
49
|
+
if (resolvedSchemaDir === null) {
|
|
50
|
+
// The location is process-invariant; derive it once instead of running
|
|
51
|
+
// env-paths on every cache read/write of the registry hot path.
|
|
52
|
+
resolvedSchemaDir = (0, node_path_1.join)(cacheRoot(), SCHEMA);
|
|
53
|
+
}
|
|
54
|
+
const dir = resolvedSchemaDir;
|
|
26
55
|
if (!(0, node_fs_1.existsSync)(dir)) {
|
|
27
56
|
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
28
57
|
}
|
|
29
58
|
if (!sweptThisProcess) {
|
|
30
59
|
sweptThisProcess = true;
|
|
31
60
|
sweepStale(dir);
|
|
61
|
+
sweepOldGenerations(cacheRoot());
|
|
32
62
|
}
|
|
33
63
|
return dir;
|
|
34
64
|
}
|
|
@@ -51,6 +81,28 @@ function sweepStale(dir) {
|
|
|
51
81
|
/* sweeping is optional; never fail a run over it */
|
|
52
82
|
}
|
|
53
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Remove schema generations other than the active one. The cache directory is
|
|
86
|
+
* persistent (unlike the old tmpdir location, which the OS reclaimed), so after
|
|
87
|
+
* a SCHEMA bump the previous generation would otherwise live on disk forever.
|
|
88
|
+
*/
|
|
89
|
+
function sweepOldGenerations(root) {
|
|
90
|
+
try {
|
|
91
|
+
for (const name of (0, node_fs_1.readdirSync)(root)) {
|
|
92
|
+
if (name === SCHEMA)
|
|
93
|
+
continue;
|
|
94
|
+
try {
|
|
95
|
+
(0, node_fs_1.rmSync)((0, node_path_1.join)(root, name), { recursive: true, force: true });
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
/* skip generations we can't remove */
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
/* sweeping is optional; never fail a run over it */
|
|
104
|
+
}
|
|
105
|
+
}
|
|
54
106
|
/** Stable, filesystem-safe filename for a cache key (the registry path). */
|
|
55
107
|
function fileFor(key) {
|
|
56
108
|
const hash = (0, node_crypto_1.createHash)('sha1').update(key).digest('hex');
|
|
@@ -9,7 +9,7 @@ const path_1 = require("path");
|
|
|
9
9
|
* project — without committing anything or touching the shell profile.
|
|
10
10
|
*
|
|
11
11
|
* The repo is located by walking UP from this file's own directory until a
|
|
12
|
-
* `.env.local` is found, so it works regardless of nesting depth (src/
|
|
12
|
+
* `.env.local` is found, so it works regardless of nesting depth (src/shared when
|
|
13
13
|
* type-stripped, dist/utils when compiled) and resolves the inup repo even when
|
|
14
14
|
* the binary is linked and invoked from a different project directory.
|
|
15
15
|
*
|