inup 1.4.9 → 1.4.12
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 +4 -1
- package/dist/cli.js +10 -2
- package/dist/config/constants.js +1 -2
- package/dist/core/package-detector.js +184 -96
- package/dist/core/upgrade-runner.js +62 -13
- package/dist/interactive-ui.js +116 -62
- package/dist/services/changelog-fetcher.js +59 -34
- package/dist/services/index.js +0 -2
- package/dist/services/jsdelivr-registry.js +92 -176
- package/dist/services/npm-registry.js +97 -27
- package/dist/ui/input-handler.js +1 -1
- package/dist/ui/renderer/index.js +2 -2
- package/dist/ui/renderer/package-list.js +32 -5
- package/dist/ui/state/state-manager.js +7 -5
- package/dist/utils/filesystem.js +149 -22
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -50,14 +50,17 @@ inup [options]
|
|
|
50
50
|
|
|
51
51
|
-d, --dir <path> Run in specific directory
|
|
52
52
|
-e, --exclude <patterns> Skip directories (comma-separated regex)
|
|
53
|
+
-i, --ignore <packages> Ignore packages (comma-separated, glob supported)
|
|
54
|
+
--max-depth <number> Maximum scan depth for package discovery (default: 10)
|
|
53
55
|
--package-manager <name> Force package manager (npm, yarn, pnpm, bun)
|
|
56
|
+
--debug Write verbose debug logs
|
|
54
57
|
```
|
|
55
58
|
|
|
56
59
|
## 🔒 Privacy
|
|
57
60
|
|
|
58
61
|
We don't track anything. Ever.
|
|
59
62
|
|
|
60
|
-
|
|
63
|
+
Version checks and package metadata are fetched from the npm registry. When needed for immutable exact-version manifests, inup may also fetch a pinned `package.json` from jsDelivr. Weekly download counts come from the npm downloads API.
|
|
61
64
|
|
|
62
65
|
## 📄 License
|
|
63
66
|
|
package/dist/cli.js
CHANGED
|
@@ -21,12 +21,11 @@ program
|
|
|
21
21
|
.option('-d, --dir <directory>', 'specify directory to run in', process.cwd())
|
|
22
22
|
.option('-e, --exclude <patterns>', 'exclude paths matching regex patterns (comma-separated)', '')
|
|
23
23
|
.option('-i, --ignore <packages>', 'ignore packages (comma-separated, supports glob patterns like @babel/*)')
|
|
24
|
+
.option('--max-depth <number>', 'maximum directory depth for package.json discovery', '10')
|
|
24
25
|
.option('--package-manager <name>', 'manually specify package manager (npm, yarn, pnpm, bun)')
|
|
25
26
|
.option('--debug', 'write verbose debug log to /tmp/inup-debug-YYYY-MM-DD.log')
|
|
26
27
|
.action(async (options) => {
|
|
27
28
|
console.log(chalk_1.default.bold.blue(`🚀 `) + chalk_1.default.bold.red(`i`) + chalk_1.default.bold.yellow(`n`) + chalk_1.default.bold.blue(`u`) + chalk_1.default.bold.magenta(`p`) + `\n`);
|
|
28
|
-
// Check for updates in the background (non-blocking)
|
|
29
|
-
const updateCheckPromise = (0, services_1.checkForUpdateAsync)('inup', packageJson.version);
|
|
30
29
|
const cwd = (0, path_1.resolve)(options.dir);
|
|
31
30
|
if (options.debug || process.env.INUP_DEBUG === '1') {
|
|
32
31
|
(0, utils_1.enableDebugLogging)();
|
|
@@ -49,6 +48,14 @@ program
|
|
|
49
48
|
.filter(Boolean)
|
|
50
49
|
: [];
|
|
51
50
|
const ignorePackages = [...new Set([...cliIgnorePatterns, ...(projectConfig.ignore || [])])];
|
|
51
|
+
const maxDepth = Number.parseInt(options.maxDepth, 10);
|
|
52
|
+
if (!Number.isInteger(maxDepth) || maxDepth < 0) {
|
|
53
|
+
console.error(chalk_1.default.red(`Invalid max depth: ${options.maxDepth}`));
|
|
54
|
+
console.error(chalk_1.default.yellow('Expected a non-negative integer, for example: --max-depth 10'));
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
// Check for updates in the background (non-blocking)
|
|
58
|
+
const updateCheckPromise = (0, services_1.checkForUpdateAsync)('inup', packageJson.version);
|
|
52
59
|
// Validate package manager if provided
|
|
53
60
|
let packageManager;
|
|
54
61
|
if (options.packageManager) {
|
|
@@ -63,6 +70,7 @@ program
|
|
|
63
70
|
const upgrader = new index_1.UpgradeRunner({
|
|
64
71
|
cwd,
|
|
65
72
|
excludePatterns,
|
|
73
|
+
maxDepth,
|
|
66
74
|
ignorePackages,
|
|
67
75
|
packageManager,
|
|
68
76
|
debug: options.debug || process.env.INUP_DEBUG === '1',
|
package/dist/config/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.JSDELIVR_POOL_TIMEOUT = exports.JSDELIVR_RETRY_DELAYS = exports.JSDELIVR_RETRY_TIMEOUTS = exports.REQUEST_TIMEOUT = exports.CACHE_TTL = exports.MAX_CONCURRENT_REQUESTS = exports.JSDELIVR_CDN_URL = exports.NPM_REGISTRY_URL = exports.PACKAGE_NAME = void 0;
|
|
4
4
|
exports.PACKAGE_NAME = 'inup';
|
|
5
5
|
exports.NPM_REGISTRY_URL = 'https://registry.npmjs.org';
|
|
6
6
|
exports.JSDELIVR_CDN_URL = 'https://cdn.jsdelivr.net/npm';
|
|
@@ -10,5 +10,4 @@ exports.REQUEST_TIMEOUT = 60000; // 60 seconds in milliseconds
|
|
|
10
10
|
exports.JSDELIVR_RETRY_TIMEOUTS = [2000, 3500]; // short retry budget to keep fallback fast
|
|
11
11
|
exports.JSDELIVR_RETRY_DELAYS = [150]; // tiny backoff between jsDelivr retries in ms
|
|
12
12
|
exports.JSDELIVR_POOL_TIMEOUT = 60000; // keep-alive/connect lifecycle should be looser than per-request timeouts
|
|
13
|
-
exports.DEFAULT_REGISTRY = 'jsdelivr';
|
|
14
13
|
//# sourceMappingURL=constants.js.map
|
|
@@ -44,9 +44,12 @@ class PackageDetector {
|
|
|
44
44
|
constructor(options) {
|
|
45
45
|
this.packageJsonPath = null;
|
|
46
46
|
this.packageJson = null;
|
|
47
|
+
this.batchSizes = [10, 15, 20, 25];
|
|
48
|
+
this.batchConcurrency = 5;
|
|
47
49
|
this.cwd = options?.cwd || process.cwd();
|
|
48
50
|
this.excludePatterns = options?.excludePatterns || [];
|
|
49
51
|
this.ignorePackages = options?.ignorePackages || [];
|
|
52
|
+
this.maxDepth = options?.maxDepth ?? 10;
|
|
50
53
|
this.packageJsonPath = (0, utils_1.findPackageJson)(this.cwd);
|
|
51
54
|
if (this.packageJsonPath) {
|
|
52
55
|
this.packageJson = (0, utils_1.readPackageJson)(this.packageJsonPath);
|
|
@@ -56,21 +59,87 @@ class PackageDetector {
|
|
|
56
59
|
return this.packageJsonPath !== null && this.packageJson !== null;
|
|
57
60
|
}
|
|
58
61
|
async getOutdatedPackages() {
|
|
62
|
+
const packages = [];
|
|
63
|
+
await this.streamOutdatedPackages((event) => {
|
|
64
|
+
if (event.type === 'batch') {
|
|
65
|
+
event.payload.batch.forEach((item) => {
|
|
66
|
+
packages.push(...item.packageInfo);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
else if (event.type === 'complete') {
|
|
70
|
+
packages.splice(0, packages.length, ...event.payload.packages);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return packages;
|
|
74
|
+
}
|
|
75
|
+
async streamOutdatedPackages(onEvent) {
|
|
59
76
|
if (!this.packageJson) {
|
|
60
77
|
throw new Error('No package.json found in current directory');
|
|
61
78
|
}
|
|
62
|
-
const packages = [];
|
|
63
79
|
const t0 = Date.now();
|
|
64
80
|
utils_3.debugLog.info('PackageDetector', `Starting scan in ${this.cwd}`);
|
|
65
|
-
|
|
81
|
+
const prepared = await this.prepareDependencies();
|
|
82
|
+
const initialPayload = {
|
|
83
|
+
allDependencies: prepared.allDependencies,
|
|
84
|
+
uniquePackages: prepared.uniquePackages,
|
|
85
|
+
currentVersions: prepared.currentVersions,
|
|
86
|
+
progress: this.createProgressSnapshot(prepared.uniquePackages.length, 0, 0, true),
|
|
87
|
+
};
|
|
88
|
+
onEvent({ type: 'initial', payload: initialPayload });
|
|
89
|
+
const packageLookup = new Map();
|
|
90
|
+
let resolved = 0;
|
|
91
|
+
let failed = 0;
|
|
92
|
+
const tFetch = Date.now();
|
|
93
|
+
utils_3.debugLog.info('PackageDetector', 'fetching version data via npm registry in batches');
|
|
94
|
+
await (0, services_1.getAllPackageDataBatched)(prepared.uniquePackages, (batch) => {
|
|
95
|
+
const batchItems = batch.map((batchItem) => {
|
|
96
|
+
const packageInfo = this.resolvePackageGroup(batchItem.packageName, prepared.allDependencies, batchItem.data);
|
|
97
|
+
packageLookup.set(batchItem.packageName, packageInfo);
|
|
98
|
+
resolved++;
|
|
99
|
+
const isFailed = batchItem.data.latestVersion === 'unknown';
|
|
100
|
+
if (isFailed) {
|
|
101
|
+
failed++;
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
packageName: batchItem.packageName,
|
|
105
|
+
packageInfo,
|
|
106
|
+
failed: isFailed,
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
const progress = this.createProgressSnapshot(prepared.uniquePackages.length, resolved, failed, resolved < prepared.uniquePackages.length);
|
|
110
|
+
onEvent({
|
|
111
|
+
type: 'batch',
|
|
112
|
+
payload: {
|
|
113
|
+
batch: batchItems,
|
|
114
|
+
progress,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
}, prepared.currentVersions, {
|
|
118
|
+
batchSizes: this.batchSizes,
|
|
119
|
+
concurrency: this.batchConcurrency,
|
|
120
|
+
});
|
|
121
|
+
utils_3.debugLog.perf('PackageDetector', `registry fetch (${resolved}/${prepared.uniquePackages.length} resolved)`, tFetch);
|
|
122
|
+
const finalPackages = prepared.uniquePackages.flatMap((packageName) => packageLookup.get(packageName) ?? []);
|
|
123
|
+
const progress = this.createProgressSnapshot(prepared.uniquePackages.length, resolved, failed, false);
|
|
124
|
+
utils_3.debugLog.perf('PackageDetector', `total scan complete (${finalPackages.filter((p) => p.isOutdated).length} outdated of ${finalPackages.length} deps)`, t0);
|
|
125
|
+
onEvent({
|
|
126
|
+
type: 'complete',
|
|
127
|
+
payload: {
|
|
128
|
+
packages: finalPackages,
|
|
129
|
+
progress,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
utils_2.ConsoleUtils.clearProgress();
|
|
133
|
+
return finalPackages;
|
|
134
|
+
}
|
|
135
|
+
async prepareDependencies() {
|
|
66
136
|
this.showProgress('🔍 Scanning repository for package.json files...');
|
|
67
137
|
const tScan = Date.now();
|
|
68
|
-
const allPackageJsonFiles = this.findPackageJsonFilesWithTimeout(30000);
|
|
138
|
+
const allPackageJsonFiles = await this.findPackageJsonFilesWithTimeout(30000);
|
|
69
139
|
utils_3.debugLog.perf('PackageDetector', `file scan (${allPackageJsonFiles.length} files)`, tScan, {
|
|
70
140
|
files: allPackageJsonFiles,
|
|
71
141
|
});
|
|
72
142
|
this.showProgress(`🔍 Found ${allPackageJsonFiles.length} package.json file${allPackageJsonFiles.length === 1 ? '' : 's'}`);
|
|
73
|
-
// Step 2: Collect all dependencies from package.json files (parallelized)
|
|
74
143
|
this.showProgress('🔍 Reading dependencies from package.json files...');
|
|
75
144
|
const tDeps = Date.now();
|
|
76
145
|
const allDepsRaw = await (0, utils_1.collectAllDependenciesAsync)(allPackageJsonFiles, {
|
|
@@ -78,10 +147,9 @@ class PackageDetector {
|
|
|
78
147
|
includeOptionalDeps: true,
|
|
79
148
|
});
|
|
80
149
|
utils_3.debugLog.perf('PackageDetector', `dependency collection (${allDepsRaw.length} raw deps)`, tDeps);
|
|
81
|
-
// Step 3: Get unique package names while filtering out workspace references and ignored packages
|
|
82
150
|
this.showProgress('🔍 Identifying unique packages...');
|
|
83
151
|
const uniquePackageNames = new Set();
|
|
84
|
-
const
|
|
152
|
+
const allDependencies = [];
|
|
85
153
|
let ignoredCount = 0;
|
|
86
154
|
const seenWorkspaceRefs = new Set();
|
|
87
155
|
const seenIgnored = new Set();
|
|
@@ -102,119 +170,139 @@ class PackageDetector {
|
|
|
102
170
|
}
|
|
103
171
|
continue;
|
|
104
172
|
}
|
|
105
|
-
|
|
173
|
+
allDependencies.push({
|
|
174
|
+
name: dep.name,
|
|
175
|
+
version: dep.version,
|
|
176
|
+
type: dep.type,
|
|
177
|
+
packageJsonPath: dep.packageJsonPath,
|
|
178
|
+
});
|
|
106
179
|
uniquePackageNames.add(dep.name);
|
|
107
180
|
}
|
|
108
181
|
if (ignoredCount > 0) {
|
|
109
182
|
this.showProgress(`🔍 Skipped ${ignoredCount} ignored package(s)`);
|
|
110
183
|
}
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
184
|
+
const uniquePackages = Array.from(uniquePackageNames).sort((a, b) => {
|
|
185
|
+
const aIsScoped = a.startsWith('@');
|
|
186
|
+
const bIsScoped = b.startsWith('@');
|
|
187
|
+
if (aIsScoped && !bIsScoped)
|
|
188
|
+
return -1;
|
|
189
|
+
if (!aIsScoped && bIsScoped)
|
|
190
|
+
return 1;
|
|
191
|
+
return a.localeCompare(b);
|
|
192
|
+
});
|
|
193
|
+
utils_3.debugLog.info('PackageDetector', `${uniquePackages.length} unique packages to check, ${ignoredCount} ignored`);
|
|
115
194
|
const currentVersions = new Map();
|
|
116
|
-
for (const dep of
|
|
117
|
-
// Use the first occurrence of each package's version
|
|
195
|
+
for (const dep of allDependencies) {
|
|
118
196
|
if (!currentVersions.has(dep.name)) {
|
|
119
197
|
currentVersions.set(dep.name, dep.version);
|
|
120
198
|
}
|
|
121
199
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
});
|
|
131
|
-
utils_3.debugLog.perf('PackageDetector', `registry fetch (${allPackageData.size}/${packageNames.length} resolved)`, tFetch);
|
|
132
|
-
const loggedOutdated = new Set();
|
|
200
|
+
return {
|
|
201
|
+
allDependencies,
|
|
202
|
+
uniquePackages,
|
|
203
|
+
currentVersions,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
resolvePackageGroup(packageName, allDependencies, packageData) {
|
|
207
|
+
const dependencies = allDependencies.filter((dep) => dep.name === packageName);
|
|
133
208
|
const loggedNoData = new Set();
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (!
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
utils_3.debugLog.warn('PackageDetector', `no data returned for ${dep.name} — skipping`);
|
|
142
|
-
}
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
const { latestVersion, allVersions } = packageData;
|
|
146
|
-
// Find closest minor version (same major, higher minor) that satisfies the current range
|
|
147
|
-
// Falls back to patch updates if no minor updates are available
|
|
148
|
-
const closestMinorVersion = (0, utils_1.findClosestMinorVersion)(dep.version, allVersions);
|
|
149
|
-
const installedClean = semver.coerce(dep.version)?.version || dep.version;
|
|
150
|
-
const minorClean = closestMinorVersion
|
|
151
|
-
? semver.coerce(closestMinorVersion)?.version || closestMinorVersion
|
|
152
|
-
: null;
|
|
153
|
-
const latestClean = semver.coerce(latestVersion)?.version || latestVersion;
|
|
154
|
-
const hasRangeUpdate = minorClean !== null && minorClean !== installedClean;
|
|
155
|
-
const hasMajorUpdate = semver.major(latestClean) > semver.major(installedClean);
|
|
156
|
-
const isOutdated = hasRangeUpdate || hasMajorUpdate;
|
|
157
|
-
if (isOutdated) {
|
|
158
|
-
const outdatedKey = `${dep.name}@${dep.version}`;
|
|
159
|
-
if (!loggedOutdated.has(outdatedKey)) {
|
|
160
|
-
loggedOutdated.add(outdatedKey);
|
|
161
|
-
utils_3.debugLog.info('PackageDetector', `outdated: ${dep.name} ${dep.version} → range:${closestMinorVersion ?? '-'} latest:${latestVersion}`);
|
|
162
|
-
}
|
|
209
|
+
const loggedOutdated = new Set();
|
|
210
|
+
return dependencies.map((dep) => {
|
|
211
|
+
try {
|
|
212
|
+
if (!packageData || packageData.latestVersion === 'unknown') {
|
|
213
|
+
if (!loggedNoData.has(dep.name)) {
|
|
214
|
+
loggedNoData.add(dep.name);
|
|
215
|
+
utils_3.debugLog.warn('PackageDetector', `no data returned for ${dep.name} — marking unavailable`);
|
|
163
216
|
}
|
|
164
|
-
|
|
165
|
-
name: dep.name,
|
|
166
|
-
currentVersion: dep.version, // Keep original version specifier with prefix
|
|
167
|
-
rangeVersion: closestMinorVersion || dep.version,
|
|
168
|
-
latestVersion,
|
|
169
|
-
type: dep.type,
|
|
170
|
-
packageJsonPath: dep.packageJsonPath,
|
|
171
|
-
isOutdated,
|
|
172
|
-
hasRangeUpdate,
|
|
173
|
-
hasMajorUpdate,
|
|
174
|
-
});
|
|
217
|
+
return this.createFailedPackageInfo(dep);
|
|
175
218
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
219
|
+
const { latestVersion, allVersions } = packageData;
|
|
220
|
+
const closestMinorVersion = (0, utils_1.findClosestMinorVersion)(dep.version, allVersions);
|
|
221
|
+
const installedClean = semver.coerce(dep.version)?.version || dep.version;
|
|
222
|
+
const minorClean = closestMinorVersion
|
|
223
|
+
? semver.coerce(closestMinorVersion)?.version || closestMinorVersion
|
|
224
|
+
: null;
|
|
225
|
+
const latestClean = semver.coerce(latestVersion)?.version || latestVersion;
|
|
226
|
+
const hasRangeUpdate = minorClean !== null && minorClean !== installedClean;
|
|
227
|
+
const hasMajorUpdate = semver.valid(latestClean) !== null &&
|
|
228
|
+
semver.valid(installedClean) !== null &&
|
|
229
|
+
semver.major(latestClean) > semver.major(installedClean);
|
|
230
|
+
const isOutdated = hasRangeUpdate || hasMajorUpdate;
|
|
231
|
+
if (isOutdated) {
|
|
232
|
+
const outdatedKey = `${dep.name}@${dep.version}`;
|
|
233
|
+
if (!loggedOutdated.has(outdatedKey)) {
|
|
234
|
+
loggedOutdated.add(outdatedKey);
|
|
235
|
+
utils_3.debugLog.info('PackageDetector', `outdated: ${dep.name} ${dep.version} → range:${closestMinorVersion ?? '-'} latest:${latestVersion}`);
|
|
236
|
+
}
|
|
190
237
|
}
|
|
238
|
+
return {
|
|
239
|
+
name: dep.name,
|
|
240
|
+
currentVersion: dep.version,
|
|
241
|
+
rangeVersion: closestMinorVersion || dep.version,
|
|
242
|
+
latestVersion,
|
|
243
|
+
type: dep.type,
|
|
244
|
+
packageJsonPath: dep.packageJsonPath,
|
|
245
|
+
isOutdated,
|
|
246
|
+
hasRangeUpdate,
|
|
247
|
+
hasMajorUpdate,
|
|
248
|
+
};
|
|
191
249
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
250
|
+
catch (error) {
|
|
251
|
+
utils_3.debugLog.error('PackageDetector', `error processing ${dep.name}`, error);
|
|
252
|
+
return this.createFailedPackageInfo(dep);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
createFailedPackageInfo(dep) {
|
|
257
|
+
return {
|
|
258
|
+
name: dep.name,
|
|
259
|
+
currentVersion: dep.version,
|
|
260
|
+
rangeVersion: 'unknown',
|
|
261
|
+
latestVersion: 'unknown',
|
|
262
|
+
type: dep.type,
|
|
263
|
+
packageJsonPath: dep.packageJsonPath,
|
|
264
|
+
isOutdated: false,
|
|
265
|
+
hasRangeUpdate: false,
|
|
266
|
+
hasMajorUpdate: false,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
createProgressSnapshot(total, resolved, failed, isLoading) {
|
|
270
|
+
return {
|
|
271
|
+
discovered: total,
|
|
272
|
+
resolved,
|
|
273
|
+
total,
|
|
274
|
+
failed,
|
|
275
|
+
isLoading,
|
|
276
|
+
};
|
|
201
277
|
}
|
|
202
|
-
findPackageJsonFilesWithTimeout(timeoutMs) {
|
|
203
|
-
// Synchronous file search with depth limiting and symlink protection
|
|
204
|
-
// The timeout parameter is kept for future async implementation
|
|
278
|
+
async findPackageJsonFilesWithTimeout(timeoutMs) {
|
|
205
279
|
try {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
280
|
+
let timeoutId;
|
|
281
|
+
try {
|
|
282
|
+
return await Promise.race([
|
|
283
|
+
(0, utils_1.findAllPackageJsonFilesAsync)(this.cwd, this.excludePatterns, this.maxDepth, (currentDir, foundCount) => {
|
|
284
|
+
const truncatedDir = currentDir.length > 50 ? '...' + currentDir.slice(-47) : currentDir;
|
|
285
|
+
this.showProgress(`🔍 Scanning ${truncatedDir} (found ${foundCount})`);
|
|
286
|
+
}),
|
|
287
|
+
new Promise((_, reject) => {
|
|
288
|
+
timeoutId = setTimeout(() => {
|
|
289
|
+
reject(new Error(`Scan timed out after ${timeoutMs}ms`));
|
|
290
|
+
}, timeoutMs);
|
|
291
|
+
timeoutId.unref?.();
|
|
292
|
+
}),
|
|
293
|
+
]);
|
|
294
|
+
}
|
|
295
|
+
finally {
|
|
296
|
+
if (timeoutId) {
|
|
297
|
+
clearTimeout(timeoutId);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
211
300
|
}
|
|
212
301
|
catch (err) {
|
|
213
302
|
throw new Error(`Failed to scan for package.json files: ${err}. Try using --exclude patterns to skip problematic directories.`);
|
|
214
303
|
}
|
|
215
304
|
}
|
|
216
305
|
isWorkspaceReference(version) {
|
|
217
|
-
// Check for common workspace reference patterns
|
|
218
306
|
return (version.includes('workspace:') ||
|
|
219
307
|
version === '*' ||
|
|
220
308
|
version.startsWith('file:') ||
|
|
@@ -31,27 +31,72 @@ class UpgradeRunner {
|
|
|
31
31
|
try {
|
|
32
32
|
// Check prerequisites
|
|
33
33
|
this.checkPrerequisites();
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
const progress = {
|
|
35
|
+
discovered: 0,
|
|
36
|
+
resolved: 0,
|
|
37
|
+
total: 0,
|
|
38
|
+
failed: 0,
|
|
39
|
+
isLoading: true,
|
|
40
|
+
};
|
|
41
|
+
let selectionStates = [];
|
|
42
|
+
let refreshUI;
|
|
43
|
+
let latestPackages = [];
|
|
44
|
+
let previousSelections;
|
|
45
|
+
const selectionPromise = new Promise((resolve, reject) => {
|
|
46
|
+
const streamPromise = this.detector.streamOutdatedPackages((event) => {
|
|
47
|
+
if (event.type === 'initial') {
|
|
48
|
+
progress.discovered = event.payload.progress.discovered;
|
|
49
|
+
progress.resolved = event.payload.progress.resolved;
|
|
50
|
+
progress.total = event.payload.progress.total;
|
|
51
|
+
progress.failed = event.payload.progress.failed;
|
|
52
|
+
progress.isLoading = event.payload.progress.isLoading;
|
|
53
|
+
selectionStates = [];
|
|
54
|
+
this.ui
|
|
55
|
+
.selectPackagesToUpgradeProgressive(selectionStates, progress, (refresh) => {
|
|
56
|
+
refreshUI = refresh;
|
|
57
|
+
})
|
|
58
|
+
.then(resolve)
|
|
59
|
+
.catch(reject);
|
|
60
|
+
}
|
|
61
|
+
if (event.type === 'batch') {
|
|
62
|
+
latestPackages = latestPackages
|
|
63
|
+
.filter((pkg) => !event.payload.batch.some((item) => item.packageName === pkg.name))
|
|
64
|
+
.concat(event.payload.batch.flatMap((item) => item.packageInfo));
|
|
65
|
+
progress.discovered = event.payload.progress.discovered;
|
|
66
|
+
progress.resolved = event.payload.progress.resolved;
|
|
67
|
+
progress.total = event.payload.progress.total;
|
|
68
|
+
progress.failed = event.payload.progress.failed;
|
|
69
|
+
progress.isLoading = event.payload.progress.isLoading;
|
|
70
|
+
this.ui.appendOutdatedBatchToSelectionStates(selectionStates, event.payload.batch, previousSelections);
|
|
71
|
+
refreshUI?.();
|
|
72
|
+
}
|
|
73
|
+
if (event.type === 'complete') {
|
|
74
|
+
latestPackages = event.payload.packages;
|
|
75
|
+
progress.discovered = event.payload.progress.discovered;
|
|
76
|
+
progress.resolved = event.payload.progress.resolved;
|
|
77
|
+
progress.total = event.payload.progress.total;
|
|
78
|
+
progress.failed = event.payload.progress.failed;
|
|
79
|
+
progress.isLoading = event.payload.progress.isLoading;
|
|
80
|
+
refreshUI?.();
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
streamPromise.catch(reject);
|
|
84
|
+
});
|
|
85
|
+
let selectedChoices = await selectionPromise;
|
|
86
|
+
const outdatedPackages = this.detector.getOutdatedPackagesOnly(latestPackages);
|
|
87
|
+
if (outdatedPackages.length === 0 && selectedChoices.length === 0) {
|
|
88
|
+
console.log(chalk_1.default.green('✅ All packages are up to date!'));
|
|
40
89
|
return;
|
|
41
90
|
}
|
|
42
91
|
// Interactive selection and confirmation loop
|
|
43
|
-
let selectedChoices = [];
|
|
44
92
|
let shouldProceed = false;
|
|
45
|
-
let previousSelections;
|
|
46
93
|
while (true) {
|
|
47
|
-
// Interactive selection
|
|
48
|
-
selectedChoices = await this.ui.selectPackagesToUpgrade(packages, previousSelections);
|
|
49
94
|
if (selectedChoices.length === 0) {
|
|
50
95
|
console.log(chalk_1.default.yellow('No packages selected. Exiting...'));
|
|
51
96
|
return;
|
|
52
97
|
}
|
|
53
98
|
// Validate selected choices before confirmation
|
|
54
|
-
this.validateSelectedChoices(selectedChoices,
|
|
99
|
+
this.validateSelectedChoices(selectedChoices, latestPackages);
|
|
55
100
|
// Store current selections for potential return to selection
|
|
56
101
|
previousSelections = new Map();
|
|
57
102
|
// Convert selectedChoices back to selection state format
|
|
@@ -71,7 +116,11 @@ class UpgradeRunner {
|
|
|
71
116
|
// User pressed N or ESC - go back to selection with current selections preserved
|
|
72
117
|
console.clear();
|
|
73
118
|
console.log(chalk_1.default.bold.blue('🚀 inup\n'));
|
|
74
|
-
|
|
119
|
+
selectedChoices = progress.isLoading
|
|
120
|
+
? await this.ui.selectPackagesToUpgradeProgressive(selectionStates, progress, (refresh) => {
|
|
121
|
+
refreshUI = refresh;
|
|
122
|
+
})
|
|
123
|
+
: await this.ui.selectPackagesToUpgrade(latestPackages, previousSelections);
|
|
75
124
|
continue;
|
|
76
125
|
}
|
|
77
126
|
if (!shouldProceed) {
|
|
@@ -82,7 +131,7 @@ class UpgradeRunner {
|
|
|
82
131
|
break;
|
|
83
132
|
}
|
|
84
133
|
// Perform upgrade
|
|
85
|
-
await this.upgrader.upgradePackages(selectedChoices,
|
|
134
|
+
await this.upgrader.upgradePackages(selectedChoices, latestPackages);
|
|
86
135
|
}
|
|
87
136
|
catch (error) {
|
|
88
137
|
console.error(chalk_1.default.red(`Error: ${error}`));
|