inup 1.5.5 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -10
- package/dist/cli.js +53 -31
- package/dist/config/constants.js +4 -6
- package/dist/config/package-meta.js +10 -0
- package/dist/config/project-config.js +11 -1
- package/dist/core/package-detector.js +37 -5
- package/dist/core/upgrade-runner.js +8 -10
- package/dist/core/upgrader.js +15 -11
- package/dist/features/changelog/clients/github-client.js +5 -6
- package/dist/features/changelog/services/changelog-service.js +2 -4
- package/dist/features/changelog/services/package-metadata-service.js +7 -23
- package/dist/features/changelog/services/release-notes-service.js +4 -29
- package/dist/features/debug/services/performance-tracker.js +6 -8
- package/dist/features/headless/headless-runner.js +53 -0
- package/dist/features/headless/index.js +27 -0
- package/dist/features/headless/report.js +64 -0
- package/dist/features/headless/types.js +6 -0
- package/dist/features/headless/vulnerability-audit.js +106 -0
- package/dist/index.js +1 -2
- package/dist/interactive-ui.js +15 -606
- package/dist/services/background-audit.js +3 -5
- package/dist/services/cache-manager.js +5 -7
- package/dist/services/http/inflight.js +22 -0
- package/dist/services/http/retry.js +30 -0
- package/dist/services/index.js +0 -1
- package/dist/services/npm-registry.js +20 -97
- package/dist/services/package-manager-detector.js +6 -3
- package/dist/services/persistent-cache.js +8 -13
- package/dist/types/domain.js +3 -0
- package/dist/types/streaming.js +3 -0
- package/dist/types/ui.js +3 -0
- package/dist/types.js +17 -0
- package/dist/ui/controllers/package-info-modal-controller.js +22 -31
- package/dist/ui/controllers/vulnerability-audit-controller.js +17 -8
- package/dist/ui/index.js +3 -0
- package/dist/ui/input-handler.js +58 -75
- package/dist/ui/keymap.js +208 -0
- package/dist/ui/modal/package-info-sections/release-notes.js +161 -0
- package/dist/ui/modal/package-info-sections/sections.js +174 -0
- package/dist/ui/modal/package-info-sections/text.js +75 -0
- package/dist/ui/modal/package-info-sections.js +15 -369
- package/dist/ui/modal/package-info.js +1 -1
- package/dist/ui/presenters/health.js +24 -0
- package/dist/ui/renderer/help-modal.js +75 -0
- package/dist/ui/renderer/index.js +2 -5
- package/dist/ui/renderer/package-list/interface.js +184 -0
- package/dist/ui/renderer/package-list/rows.js +177 -0
- package/dist/ui/renderer/package-list.js +15 -455
- package/dist/ui/session/action-dispatcher.js +233 -0
- package/dist/ui/session/index.js +13 -0
- package/dist/ui/session/interactive-session.js +280 -0
- package/dist/ui/session/selection-state-builder.js +149 -0
- package/dist/ui/state/filter-manager.js +17 -6
- package/dist/ui/state/modal-manager.js +35 -0
- package/dist/ui/state/navigation-manager.js +33 -4
- package/dist/ui/state/state-manager.js +68 -3
- package/dist/ui/state/theme-manager.js +1 -0
- package/dist/ui/themes-colors.js +16 -4
- package/dist/ui/themes.js +11 -19
- package/dist/ui/utils/cursor.js +12 -7
- package/dist/ui/utils/index.js +6 -1
- package/dist/ui/utils/text.js +3 -2
- package/dist/ui/utils/version.js +60 -86
- package/dist/utils/color.js +38 -0
- package/dist/utils/config.js +13 -1
- package/dist/utils/engines.js +63 -0
- package/dist/utils/filesystem/io.js +103 -0
- package/dist/utils/filesystem/paths.js +19 -0
- package/dist/utils/filesystem/scan.js +280 -0
- package/dist/utils/filesystem.js +17 -335
- package/dist/utils/index.js +3 -0
- package/dist/utils/manifest.js +35 -0
- package/dist/utils/version.js +38 -1
- package/package.json +8 -7
- package/dist/services/jsdelivr-registry.js +0 -395
package/README.md
CHANGED
|
@@ -49,23 +49,66 @@ inup [options]
|
|
|
49
49
|
-i, --ignore <packages> Ignore packages (comma-separated, glob supported)
|
|
50
50
|
--max-depth <number> Maximum scan depth for package discovery (default: 10)
|
|
51
51
|
--package-manager <name> Force package manager (npm, yarn, pnpm, bun)
|
|
52
|
+
--json Print a machine-readable JSON report and exit (read-only)
|
|
53
|
+
-c, --check Exit non-zero if updates exist, without writing (for CI)
|
|
52
54
|
--debug Write verbose debug logs
|
|
53
55
|
```
|
|
54
56
|
|
|
57
|
+
## CI & Scripting
|
|
58
|
+
|
|
59
|
+
`inup` runs headless automatically when stdout isn't a TTY or `$CI` is set, so it never hangs in a
|
|
60
|
+
pipeline waiting on the interactive UI. Both `--json` and `--check` are **read-only** — they report,
|
|
61
|
+
they never edit `package.json` or install.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
inup --check # exit 1 if anything is outdated → fails the build
|
|
65
|
+
inup --json | jq # structured drift report for dashboards/bots
|
|
66
|
+
inup | cat # plain line-based report when piped to a log
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Each reported package carries its health signals: `deprecated` (npm deprecation message), `enginesNode`
|
|
70
|
+
(declared `engines.node`), and `vulnerability` (known advisories on the currently-installed version,
|
|
71
|
+
from one bulk `npm audit`-style request). Every advisory is **cross-referenced against the upgrade
|
|
72
|
+
targets**, so you know whether the upgrade actually fixes it:
|
|
73
|
+
|
|
74
|
+
- `vulnerability.advisories[].fixedByRange` / `fixedByLatest` — does the in-range / latest target escape
|
|
75
|
+
this advisory's affected range?
|
|
76
|
+
- `vulnerability.fixedByRange` / `fixedByLatest` — does the target clear **every** advisory?
|
|
77
|
+
|
|
78
|
+
The summary includes a `vulnerable` count, and the payload carries a `schemaVersion` so scripts and
|
|
79
|
+
agents can pin to a known shape.
|
|
80
|
+
|
|
81
|
+
Output hygiene: with `--json`, stdout carries **only** the JSON document; all progress and warnings go
|
|
82
|
+
to stderr. Exit codes: `0` up to date, `1` updates exist (`--check`), `2` error.
|
|
83
|
+
|
|
55
84
|
## Keyboard Shortcuts
|
|
56
85
|
|
|
86
|
+
<!-- KEYS:START -->
|
|
57
87
|
| Key | Action |
|
|
58
88
|
|-----|--------|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
89
|
+
| `↑ / k` | Move up |
|
|
90
|
+
| `↓ / j` | Move down |
|
|
91
|
+
| `g` | Jump to the first package |
|
|
92
|
+
| `G` | Jump to the last package |
|
|
93
|
+
| `←` | Cycle selection left (none → range → latest) |
|
|
94
|
+
| `→` | Cycle selection right (none → range → latest) |
|
|
95
|
+
| `Space` | Toggle the current package on/off |
|
|
96
|
+
| `m` | Select all minor/patch updates |
|
|
97
|
+
| `l` | Select all latest updates (including major) |
|
|
98
|
+
| `u` | Unselect all packages |
|
|
99
|
+
| `Enter` | Confirm selection and upgrade |
|
|
100
|
+
| `/` | Search packages by name |
|
|
101
|
+
| `d` | Toggle devDependencies |
|
|
102
|
+
| `p` | Toggle peerDependencies |
|
|
103
|
+
| `o` | Toggle optionalDependencies |
|
|
104
|
+
| `s` | Run the vulnerability audit |
|
|
105
|
+
| `v` | Show only vulnerable packages |
|
|
106
|
+
| `Esc` | Clear the active search filter |
|
|
107
|
+
| `i` | View package details and changelog |
|
|
108
|
+
| `t` | Change the color theme |
|
|
109
|
+
| `?` | Show this help |
|
|
110
|
+
| `!` | Show the performance/debug panel |
|
|
111
|
+
<!-- KEYS:END -->
|
|
69
112
|
|
|
70
113
|
## Privacy
|
|
71
114
|
|
package/dist/cli.js
CHANGED
|
@@ -7,27 +7,34 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
exports.runCli = runCli;
|
|
8
8
|
const commander_1 = require("commander");
|
|
9
9
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
-
const fs_1 = require("fs");
|
|
11
10
|
const path_1 = require("path");
|
|
12
11
|
const index_1 = require("./index");
|
|
12
|
+
const headless_1 = require("./features/headless");
|
|
13
13
|
const services_1 = require("./services");
|
|
14
14
|
const config_1 = require("./config");
|
|
15
15
|
const utils_1 = require("./utils");
|
|
16
16
|
const git_1 = require("./utils/git");
|
|
17
|
-
const
|
|
18
|
-
const packageJson = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '../package.json'), 'utf-8'));
|
|
17
|
+
const ui_1 = require("./ui");
|
|
19
18
|
const program = new commander_1.Command();
|
|
20
19
|
async function runCli(options) {
|
|
20
|
+
// Resolve colored-output intent before anything renders.
|
|
21
|
+
(0, utils_1.applyColorSetting)(options.color);
|
|
21
22
|
const cwd = (0, path_1.resolve)(options.dir);
|
|
22
23
|
if (options.debug || process.env.INUP_DEBUG === '1') {
|
|
23
24
|
(0, utils_1.enableDebugLogging)();
|
|
24
25
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
// Headless when piped, in CI, or when a non-interactive flag is set. The TUI only renders in
|
|
27
|
+
// interactive mode; everything else routes through the read-only headless report path.
|
|
28
|
+
const interactive = !!process.stdout.isTTY && !process.env.CI && !options.json && !options.check;
|
|
29
|
+
// The dirty-tree prompt would hang without a TTY; headless is read-only anyway, so skip it.
|
|
30
|
+
if (interactive) {
|
|
31
|
+
const gitState = (0, git_1.getGitWorkingTreeState)(cwd);
|
|
32
|
+
if (gitState.isRepo && gitState.isDirty) {
|
|
33
|
+
const shouldProceed = await ui_1.TerminalInput.promptForImmediateConfirmation(`${chalk_1.default.yellow('Warning:')} dirty working tree. Proceed anyway? ${chalk_1.default.dim('[y/N]')} `, false);
|
|
34
|
+
if (!shouldProceed) {
|
|
35
|
+
console.log(chalk_1.default.yellow('Upgrade cancelled.'));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
31
38
|
}
|
|
32
39
|
}
|
|
33
40
|
// Load project config from .inuprc
|
|
@@ -54,8 +61,11 @@ async function runCli(options) {
|
|
|
54
61
|
console.error(chalk_1.default.yellow('Expected a non-negative integer, for example: --max-depth 10'));
|
|
55
62
|
process.exit(1);
|
|
56
63
|
}
|
|
57
|
-
// Check for updates in the background (non-blocking)
|
|
58
|
-
|
|
64
|
+
// Check for updates in the background (non-blocking). Interactive only — keeps headless stdout
|
|
65
|
+
// clean and avoids a lingering fetch handle in CI.
|
|
66
|
+
const updateCheckPromise = interactive
|
|
67
|
+
? (0, services_1.checkForUpdateAsync)(config_1.PACKAGE_NAME, config_1.PACKAGE_VERSION)
|
|
68
|
+
: undefined;
|
|
59
69
|
// Validate package manager if provided
|
|
60
70
|
let packageManager;
|
|
61
71
|
if (options.packageManager) {
|
|
@@ -67,50 +77,62 @@ async function runCli(options) {
|
|
|
67
77
|
}
|
|
68
78
|
packageManager = options.packageManager;
|
|
69
79
|
}
|
|
70
|
-
const
|
|
80
|
+
const runnerOptions = {
|
|
71
81
|
cwd,
|
|
72
82
|
excludePatterns,
|
|
83
|
+
scanDirs: projectConfig.scanDirs,
|
|
73
84
|
maxDepth,
|
|
74
85
|
ignorePackages,
|
|
75
86
|
packageManager,
|
|
76
87
|
showPeerDependencyVulnerabilities: projectConfig.showPeerDependencyVulnerabilities ?? false,
|
|
77
88
|
showOptionalDependencyVulnerabilities: projectConfig.showOptionalDependencyVulnerabilities ?? false,
|
|
78
89
|
debug: options.debug || process.env.INUP_DEBUG === '1',
|
|
79
|
-
|
|
80
|
-
|
|
90
|
+
saveExact: options.saveExact ?? false,
|
|
91
|
+
};
|
|
92
|
+
// Non-interactive (piped / CI / --json / --check) routes to the read-only headless feature;
|
|
93
|
+
// only the interactive path builds the full TUI runner.
|
|
94
|
+
if (!interactive) {
|
|
95
|
+
await new headless_1.HeadlessRunner(runnerOptions).run({ json: options.json, check: options.check });
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
await new index_1.UpgradeRunner(runnerOptions).run();
|
|
81
99
|
// After the main flow completes, check if there's an update available
|
|
82
100
|
const updateCheck = await updateCheckPromise;
|
|
83
101
|
if (updateCheck?.isOutdated) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
102
|
+
const columns = process.stdout.columns && process.stdout.columns > 0 ? process.stdout.columns : 80;
|
|
103
|
+
const innerWidth = Math.max(40, Math.min(columns, 100) - 2); // chars between the │ borders
|
|
104
|
+
const border = chalk_1.default.yellow;
|
|
105
|
+
const padTo = (visibleLength) => ' '.repeat(Math.max(0, innerWidth - visibleLength));
|
|
106
|
+
const line1Plain = ` Update available! ${updateCheck.currentVersion} → ${updateCheck.latestVersion}`;
|
|
107
|
+
const line1 = ' ' +
|
|
88
108
|
chalk_1.default.bold.yellow('Update available! ') +
|
|
89
|
-
chalk_1.default.gray(
|
|
109
|
+
chalk_1.default.gray(updateCheck.currentVersion) +
|
|
90
110
|
' → ' +
|
|
91
|
-
chalk_1.default.green(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
console.log(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
chalk_1.default.yellow('│'));
|
|
100
|
-
console.log(chalk_1.default.yellow('└' + '─'.repeat(78) + '┘'));
|
|
111
|
+
chalk_1.default.green(updateCheck.latestVersion);
|
|
112
|
+
const line2Plain = ` Run: ${updateCheck.updateCommand}`;
|
|
113
|
+
const line2 = ' ' + chalk_1.default.gray('Run: ') + chalk_1.default.cyan(updateCheck.updateCommand);
|
|
114
|
+
console.log('');
|
|
115
|
+
console.log(border('┌' + '─'.repeat(innerWidth) + '┐'));
|
|
116
|
+
console.log(border('│') + line1 + padTo(line1Plain.length) + border('│'));
|
|
117
|
+
console.log(border('│') + line2 + padTo(line2Plain.length) + border('│'));
|
|
118
|
+
console.log(border('└' + '─'.repeat(innerWidth) + '┘'));
|
|
101
119
|
console.log('');
|
|
102
120
|
}
|
|
103
121
|
}
|
|
104
122
|
program
|
|
105
|
-
.name(
|
|
123
|
+
.name(config_1.PACKAGE_NAME)
|
|
106
124
|
.description('Interactive upgrade tool for package managers. Auto-detects and works with npm, yarn, pnpm, and bun.')
|
|
107
|
-
.version(
|
|
125
|
+
.version(config_1.PACKAGE_VERSION)
|
|
108
126
|
.option('-d, --dir <directory>', 'specify directory to run in', process.cwd())
|
|
109
127
|
.option('-e, --exclude <patterns>', 'exclude paths matching regex patterns (comma-separated)', '')
|
|
110
128
|
.option('-i, --ignore <packages>', 'ignore packages (comma-separated, supports glob patterns like @babel/*)')
|
|
111
129
|
.option('--max-depth <number>', 'maximum directory depth for package.json discovery', '10')
|
|
112
130
|
.option('--package-manager <name>', 'manually specify package manager (npm, yarn, pnpm, bun)')
|
|
113
131
|
.option('--debug', 'write verbose debug log to /tmp/inup-debug-YYYY-MM-DD.log')
|
|
132
|
+
.option('--no-color', 'disable colored output (also respects NO_COLOR / FORCE_COLOR)')
|
|
133
|
+
.option('--save-exact', 'write exact versions instead of preserving the range prefix (^/~)')
|
|
134
|
+
.option('--json', 'print a machine-readable JSON report and exit (non-interactive, read-only)')
|
|
135
|
+
.option('-c, --check', 'exit non-zero if updates exist, without writing (for CI; read-only)')
|
|
114
136
|
.action(runCli);
|
|
115
137
|
// Handle uncaught errors gracefully
|
|
116
138
|
process.on('uncaughtException', (error) => {
|
package/dist/config/constants.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
3
|
+
exports.REQUEST_TIMEOUT = exports.CACHE_TTL = exports.MAX_CONCURRENT_REQUESTS = exports.NPM_REGISTRY_URL = exports.PACKAGE_VERSION = exports.PACKAGE_NAME = void 0;
|
|
4
|
+
var package_meta_1 = require("./package-meta");
|
|
5
|
+
Object.defineProperty(exports, "PACKAGE_NAME", { enumerable: true, get: function () { return package_meta_1.PACKAGE_NAME; } });
|
|
6
|
+
Object.defineProperty(exports, "PACKAGE_VERSION", { enumerable: true, get: function () { return package_meta_1.PACKAGE_VERSION; } });
|
|
5
7
|
exports.NPM_REGISTRY_URL = 'https://registry.npmjs.org';
|
|
6
|
-
exports.JSDELIVR_CDN_URL = 'https://cdn.jsdelivr.net/npm';
|
|
7
8
|
exports.MAX_CONCURRENT_REQUESTS = 150;
|
|
8
9
|
exports.CACHE_TTL = 5 * 60 * 1000; // 5 minutes in milliseconds
|
|
9
10
|
exports.REQUEST_TIMEOUT = 60000; // 60 seconds in milliseconds
|
|
10
|
-
exports.JSDELIVR_RETRY_TIMEOUTS = [2000, 3500]; // short retry budget to keep fallback fast
|
|
11
|
-
exports.JSDELIVR_RETRY_DELAYS = [150]; // tiny backoff between jsDelivr retries in ms
|
|
12
|
-
exports.JSDELIVR_POOL_TIMEOUT = 60000; // keep-alive/connect lifecycle should be looser than per-request timeouts
|
|
13
11
|
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PACKAGE_VERSION = exports.PACKAGE_NAME = void 0;
|
|
7
|
+
const package_json_1 = __importDefault(require("../../package.json"));
|
|
8
|
+
exports.PACKAGE_NAME = package_json_1.default.name;
|
|
9
|
+
exports.PACKAGE_VERSION = package_json_1.default.version;
|
|
10
|
+
//# sourceMappingURL=package-meta.js.map
|
|
@@ -4,7 +4,12 @@ exports.loadProjectConfig = loadProjectConfig;
|
|
|
4
4
|
exports.isPackageIgnored = isPackageIgnored;
|
|
5
5
|
const fs_1 = require("fs");
|
|
6
6
|
const path_1 = require("path");
|
|
7
|
-
const
|
|
7
|
+
const package_meta_1 = require("./package-meta");
|
|
8
|
+
const CONFIG_FILES = [
|
|
9
|
+
`.${package_meta_1.PACKAGE_NAME}rc`,
|
|
10
|
+
`.${package_meta_1.PACKAGE_NAME}rc.json`,
|
|
11
|
+
`${package_meta_1.PACKAGE_NAME}.config.json`,
|
|
12
|
+
];
|
|
8
13
|
/**
|
|
9
14
|
* Load project configuration from .inuprc, .inuprc.json, or inup.config.json
|
|
10
15
|
* Searches in the specified directory and parent directories up to root
|
|
@@ -49,6 +54,11 @@ function normalizeConfig(config) {
|
|
|
49
54
|
normalized.exclude = config.exclude.filter((item) => typeof item === 'string');
|
|
50
55
|
}
|
|
51
56
|
}
|
|
57
|
+
if (config.scanDirs) {
|
|
58
|
+
if (Array.isArray(config.scanDirs)) {
|
|
59
|
+
normalized.scanDirs = config.scanDirs.filter((item) => typeof item === 'string');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
52
62
|
if (typeof config.showPeerDependencyVulnerabilities === 'boolean') {
|
|
53
63
|
normalized.showPeerDependencyVulnerabilities = config.showPeerDependencyVulnerabilities;
|
|
54
64
|
}
|
|
@@ -32,8 +32,12 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
35
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
39
|
exports.PackageDetector = void 0;
|
|
40
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
37
41
|
const semver = __importStar(require("semver"));
|
|
38
42
|
const utils_1 = require("../utils");
|
|
39
43
|
const services_1 = require("../services");
|
|
@@ -42,13 +46,19 @@ const utils_2 = require("../ui/utils");
|
|
|
42
46
|
const utils_3 = require("../utils");
|
|
43
47
|
const debug_1 = require("../features/debug");
|
|
44
48
|
class PackageDetector {
|
|
49
|
+
packageJsonPath = null;
|
|
50
|
+
packageJson = null;
|
|
51
|
+
cwd;
|
|
52
|
+
excludePatterns;
|
|
53
|
+
scanDirs;
|
|
54
|
+
ignorePackages;
|
|
55
|
+
maxDepth;
|
|
56
|
+
batchSize = 10;
|
|
57
|
+
maxConcurrency = 10;
|
|
45
58
|
constructor(options) {
|
|
46
|
-
this.packageJsonPath = null;
|
|
47
|
-
this.packageJson = null;
|
|
48
|
-
this.batchSize = 10;
|
|
49
|
-
this.maxConcurrency = 10;
|
|
50
59
|
this.cwd = options?.cwd || process.cwd();
|
|
51
60
|
this.excludePatterns = options?.excludePatterns || [];
|
|
61
|
+
this.scanDirs = options?.scanDirs || [];
|
|
52
62
|
this.ignorePackages = options?.ignorePackages || [];
|
|
53
63
|
this.maxDepth = options?.maxDepth ?? 10;
|
|
54
64
|
this.packageJsonPath = (0, utils_1.findPackageJson)(this.cwd);
|
|
@@ -278,6 +288,8 @@ class PackageDetector {
|
|
|
278
288
|
hasRangeUpdate,
|
|
279
289
|
hasMajorUpdate,
|
|
280
290
|
allVersions,
|
|
291
|
+
deprecated: packageData.deprecated,
|
|
292
|
+
enginesNode: packageData.enginesNode,
|
|
281
293
|
};
|
|
282
294
|
}
|
|
283
295
|
catch (error) {
|
|
@@ -309,13 +321,17 @@ class PackageDetector {
|
|
|
309
321
|
};
|
|
310
322
|
}
|
|
311
323
|
async findPackageJsonFilesWithTimeout(timeoutMs) {
|
|
324
|
+
const skippedPackageDirs = new Set();
|
|
312
325
|
try {
|
|
313
326
|
let timeoutId;
|
|
314
327
|
try {
|
|
315
|
-
|
|
328
|
+
const files = await Promise.race([
|
|
316
329
|
(0, utils_1.findAllPackageJsonFilesAsync)(this.cwd, this.excludePatterns, this.maxDepth, (currentDir, foundCount) => {
|
|
317
330
|
const truncatedDir = currentDir.length > 50 ? '...' + currentDir.slice(-47) : currentDir;
|
|
318
331
|
this.showProgress(`🔍 Scanning ${truncatedDir} (found ${foundCount})`);
|
|
332
|
+
}, {
|
|
333
|
+
scanDirs: this.scanDirs,
|
|
334
|
+
onSkippedPackageDir: (relativePath) => skippedPackageDirs.add(relativePath),
|
|
319
335
|
}),
|
|
320
336
|
new Promise((_, reject) => {
|
|
321
337
|
timeoutId = setTimeout(() => {
|
|
@@ -324,6 +340,8 @@ class PackageDetector {
|
|
|
324
340
|
timeoutId.unref?.();
|
|
325
341
|
}),
|
|
326
342
|
]);
|
|
343
|
+
this.warnSkippedPackageDirs(skippedPackageDirs);
|
|
344
|
+
return files;
|
|
327
345
|
}
|
|
328
346
|
finally {
|
|
329
347
|
if (timeoutId) {
|
|
@@ -335,6 +353,20 @@ class PackageDetector {
|
|
|
335
353
|
throw new Error(`Failed to scan for package.json files: ${err}. Try using --exclude patterns to skip problematic directories.`);
|
|
336
354
|
}
|
|
337
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* Warn (once per directory, after the scan) about package.json-bearing directories that the
|
|
358
|
+
* default skip list pruned, so the user can re-include them via `.inuprc`'s `scanDirs`.
|
|
359
|
+
* Emitted after scanning so it does not corrupt the progress spinner output.
|
|
360
|
+
*/
|
|
361
|
+
warnSkippedPackageDirs(skippedPackageDirs) {
|
|
362
|
+
if (skippedPackageDirs.size === 0) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const list = Array.from(skippedPackageDirs).sort();
|
|
366
|
+
console.warn(chalk_1.default.yellow(`⚠️ Skipped ${list.length} package.json-bearing director${list.length === 1 ? 'y' : 'ies'} matching the default ignore list:\n` +
|
|
367
|
+
list.map((dir) => ` - ${dir}`).join('\n') +
|
|
368
|
+
`\n Add the directory name(s) to "scanDirs" in .inuprc to include them.`));
|
|
369
|
+
}
|
|
338
370
|
isWorkspaceReference(version) {
|
|
339
371
|
return (version.includes('workspace:') ||
|
|
340
372
|
version === '*' ||
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.UpgradeRunner = void 0;
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
8
|
const package_detector_1 = require("./package-detector");
|
|
9
9
|
const interactive_ui_1 = require("../interactive-ui");
|
|
@@ -15,8 +15,11 @@ const debug_1 = require("../features/debug");
|
|
|
15
15
|
* Main orchestrator for the inup upgrade process
|
|
16
16
|
*/
|
|
17
17
|
class UpgradeRunner {
|
|
18
|
+
detector;
|
|
19
|
+
ui;
|
|
20
|
+
upgrader;
|
|
21
|
+
packageManager;
|
|
18
22
|
constructor(options) {
|
|
19
|
-
this.options = options;
|
|
20
23
|
// Detect package manager
|
|
21
24
|
const cwd = options?.cwd || process.cwd();
|
|
22
25
|
if (options?.packageManager) {
|
|
@@ -29,6 +32,7 @@ class UpgradeRunner {
|
|
|
29
32
|
this.ui = new interactive_ui_1.InteractiveUI(this.packageManager, {
|
|
30
33
|
showPeerDependencyVulnerabilities: options?.showPeerDependencyVulnerabilities ?? false,
|
|
31
34
|
showOptionalDependencyVulnerabilities: options?.showOptionalDependencyVulnerabilities ?? false,
|
|
35
|
+
saveExact: options?.saveExact ?? false,
|
|
32
36
|
});
|
|
33
37
|
this.upgrader = new upgrader_1.PackageUpgrader(this.packageManager);
|
|
34
38
|
}
|
|
@@ -96,14 +100,14 @@ class UpgradeRunner {
|
|
|
96
100
|
let selectedChoices = await selectionPromise;
|
|
97
101
|
const outdatedPackages = this.detector.getOutdatedPackagesOnly(latestPackages);
|
|
98
102
|
if (outdatedPackages.length === 0 && selectedChoices.length === 0) {
|
|
99
|
-
console.log(chalk_1.default.green('✅
|
|
103
|
+
console.log(chalk_1.default.green('✅ Everything is up to date — no upgrades needed.'));
|
|
100
104
|
return;
|
|
101
105
|
}
|
|
102
106
|
// Interactive selection and confirmation loop
|
|
103
107
|
let shouldProceed = false;
|
|
104
108
|
while (true) {
|
|
105
109
|
if (selectedChoices.length === 0) {
|
|
106
|
-
console.log(chalk_1.default.yellow('
|
|
110
|
+
console.log(chalk_1.default.yellow('Nothing selected — no changes made.'));
|
|
107
111
|
return;
|
|
108
112
|
}
|
|
109
113
|
// Validate selected choices before confirmation
|
|
@@ -184,10 +188,4 @@ class UpgradeRunner {
|
|
|
184
188
|
}
|
|
185
189
|
}
|
|
186
190
|
exports.UpgradeRunner = UpgradeRunner;
|
|
187
|
-
/**
|
|
188
|
-
* @deprecated Use UpgradeRunner instead
|
|
189
|
-
*/
|
|
190
|
-
class PnpmUpgradeInteractive extends UpgradeRunner {
|
|
191
|
-
}
|
|
192
|
-
exports.PnpmUpgradeInteractive = PnpmUpgradeInteractive;
|
|
193
191
|
//# sourceMappingURL=upgrade-runner.js.map
|
package/dist/core/upgrader.js
CHANGED
|
@@ -11,16 +11,17 @@ const path_1 = require("path");
|
|
|
11
11
|
const child_process_1 = require("child_process");
|
|
12
12
|
const utils_1 = require("../utils");
|
|
13
13
|
class PackageUpgrader {
|
|
14
|
+
packageManager;
|
|
14
15
|
constructor(packageManager) {
|
|
15
16
|
this.packageManager = packageManager;
|
|
16
17
|
}
|
|
17
|
-
async upgradePackages(choices,
|
|
18
|
+
async upgradePackages(choices, _packageInfos) {
|
|
18
19
|
if (choices.length === 0) {
|
|
19
20
|
console.log(chalk_1.default.yellow('No packages to upgrade.'));
|
|
20
21
|
return;
|
|
21
22
|
}
|
|
22
23
|
// Group choices by package.json path and dependency type
|
|
23
|
-
const choicesByFileAndType = this.groupChoicesByFileAndType(choices
|
|
24
|
+
const choicesByFileAndType = this.groupChoicesByFileAndType(choices);
|
|
24
25
|
for (const [fileAndType, choiceList] of Object.entries(choicesByFileAndType)) {
|
|
25
26
|
if (choiceList.length === 0)
|
|
26
27
|
continue;
|
|
@@ -32,9 +33,9 @@ class PackageUpgrader {
|
|
|
32
33
|
const uniquePackages = new Set(choices.map((c) => c.name));
|
|
33
34
|
console.log(chalk_1.default.green(`\n✅ Successfully upgraded ${uniquePackages.size} package(s)!`));
|
|
34
35
|
// Execute package manager install after all upgrades are complete
|
|
35
|
-
await this.runInstall(choices
|
|
36
|
+
await this.runInstall(choices);
|
|
36
37
|
}
|
|
37
|
-
async runInstall(choices
|
|
38
|
+
async runInstall(choices) {
|
|
38
39
|
if (choices.length === 0) {
|
|
39
40
|
return;
|
|
40
41
|
}
|
|
@@ -71,7 +72,7 @@ class PackageUpgrader {
|
|
|
71
72
|
throw new Error(`${this.packageManager.installCommand} exited with code ${result.status}`);
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
|
-
groupChoicesByFileAndType(choices
|
|
75
|
+
groupChoicesByFileAndType(choices) {
|
|
75
76
|
const groups = {};
|
|
76
77
|
choices.forEach((choice) => {
|
|
77
78
|
const key = `${choice.packageJsonPath}|${choice.dependencyType}`;
|
|
@@ -91,11 +92,9 @@ class PackageUpgrader {
|
|
|
91
92
|
const packageDir = packageJsonPath.replace('/package.json', '');
|
|
92
93
|
const spinner = (0, nanospinner_1.createSpinner)(`Upgrading ${type} in ${packageDir}...`).start();
|
|
93
94
|
try {
|
|
94
|
-
// Read the current package.json
|
|
95
|
+
// Read the current package.json — keep the raw text so we can round-trip its formatting
|
|
96
|
+
const rawContent = (0, fs_1.readFileSync)(packageJsonPath, 'utf-8');
|
|
95
97
|
const packageJson = (0, utils_1.readPackageJson)(packageJsonPath);
|
|
96
|
-
// Find workspace root
|
|
97
|
-
const workspaceRoot = (0, utils_1.findWorkspaceRoot)(packageDir, this.packageManager.name);
|
|
98
|
-
const isWorkspaceRoot = packageDir === workspaceRoot;
|
|
99
98
|
// Group by upgrade type (range vs latest)
|
|
100
99
|
const rangeChoices = choices.filter((c) => c.upgradeType === 'range');
|
|
101
100
|
const latestChoices = choices.filter((c) => c.upgradeType === 'latest');
|
|
@@ -120,9 +119,14 @@ class PackageUpgrader {
|
|
|
120
119
|
modified = true;
|
|
121
120
|
});
|
|
122
121
|
}
|
|
123
|
-
// Write back the modified package.json
|
|
122
|
+
// Write back the modified package.json, preserving the original indentation and
|
|
123
|
+
// trailing-newline style. Skip the write entirely when nothing actually changed.
|
|
124
124
|
if (modified) {
|
|
125
|
-
(0,
|
|
125
|
+
const format = (0, utils_1.detectJsonFormat)(rawContent);
|
|
126
|
+
const nextContent = JSON.stringify(packageJson, null, format.indent) + (format.trailingNewline ? '\n' : '');
|
|
127
|
+
if (nextContent !== rawContent) {
|
|
128
|
+
(0, fs_1.writeFileSync)(packageJsonPath, nextContent);
|
|
129
|
+
}
|
|
126
130
|
}
|
|
127
131
|
spinner.success({ text: `Upgraded ${choices.length} ${type} in ${packageDir}` });
|
|
128
132
|
// Show which packages were upgraded
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.GitHubClient = void 0;
|
|
4
4
|
const repository_ref_1 = require("../parsers/repository-ref");
|
|
5
|
+
const config_1 = require("../../../config");
|
|
5
6
|
const GITHUB_RELEASES_PAGE_LIMIT = 3;
|
|
6
7
|
class GitHubClient {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
this.rawChangelogCache = new Map();
|
|
10
|
-
}
|
|
8
|
+
releasesCache = new Map();
|
|
9
|
+
rawChangelogCache = new Map();
|
|
11
10
|
clearCache() {
|
|
12
11
|
this.releasesCache.clear();
|
|
13
12
|
this.rawChangelogCache.clear();
|
|
@@ -21,7 +20,7 @@ class GitHubClient {
|
|
|
21
20
|
method: 'GET',
|
|
22
21
|
headers: {
|
|
23
22
|
accept: 'application/vnd.github.v3+json',
|
|
24
|
-
'user-agent':
|
|
23
|
+
'user-agent': `${config_1.PACKAGE_NAME}-cli`,
|
|
25
24
|
},
|
|
26
25
|
signal,
|
|
27
26
|
});
|
|
@@ -72,7 +71,7 @@ class GitHubClient {
|
|
|
72
71
|
method: 'GET',
|
|
73
72
|
headers: {
|
|
74
73
|
accept: 'application/vnd.github.v3+json',
|
|
75
|
-
'user-agent':
|
|
74
|
+
'user-agent': `${config_1.PACKAGE_NAME}-cli`,
|
|
76
75
|
},
|
|
77
76
|
signal,
|
|
78
77
|
});
|
|
@@ -4,10 +4,8 @@ exports.changelogFetcher = exports.ChangelogFetcher = void 0;
|
|
|
4
4
|
const package_metadata_service_1 = require("./package-metadata-service");
|
|
5
5
|
const release_notes_service_1 = require("./release-notes-service");
|
|
6
6
|
class ChangelogFetcher {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
this.releaseNotesService = new release_notes_service_1.ReleaseNotesService(this.metadataService);
|
|
10
|
-
}
|
|
7
|
+
metadataService = new package_metadata_service_1.PackageMetadataService();
|
|
8
|
+
releaseNotesService = new release_notes_service_1.ReleaseNotesService(this.metadataService);
|
|
11
9
|
async fetchPackageMetadata(packageName, version, signal) {
|
|
12
10
|
return await this.metadataService.fetchPackageMetadata(packageName, version, signal);
|
|
13
11
|
}
|
|
@@ -3,13 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.PackageMetadataService = void 0;
|
|
4
4
|
const npm_registry_client_1 = require("../clients/npm-registry-client");
|
|
5
5
|
const package_metadata_1 = require("../parsers/package-metadata");
|
|
6
|
-
const
|
|
6
|
+
const inflight_1 = require("../../../services/http/inflight");
|
|
7
7
|
class PackageMetadataService {
|
|
8
|
-
|
|
8
|
+
npmRegistryClient;
|
|
9
|
+
cache = new Map();
|
|
10
|
+
inFlight = new inflight_1.InflightMap();
|
|
11
|
+
constructor(npmRegistryClient = new npm_registry_client_1.NpmRegistryClient()) {
|
|
9
12
|
this.npmRegistryClient = npmRegistryClient;
|
|
10
|
-
this.exactManifestFetcher = exactManifestFetcher;
|
|
11
|
-
this.cache = new Map();
|
|
12
|
-
this.inFlight = new Map();
|
|
13
13
|
}
|
|
14
14
|
clearCache() {
|
|
15
15
|
this.cache.clear();
|
|
@@ -28,15 +28,7 @@ class PackageMetadataService {
|
|
|
28
28
|
if (this.cache.has(cacheKey)) {
|
|
29
29
|
return this.cache.get(cacheKey) ?? null;
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
if (inFlight) {
|
|
33
|
-
return await inFlight;
|
|
34
|
-
}
|
|
35
|
-
const lookupPromise = this.fetchAndCachePackageMetadata(packageName, version, signal).finally(() => {
|
|
36
|
-
this.inFlight.delete(cacheKey);
|
|
37
|
-
});
|
|
38
|
-
this.inFlight.set(cacheKey, lookupPromise);
|
|
39
|
-
return await lookupPromise;
|
|
31
|
+
return this.inFlight.dedupe(cacheKey, () => this.fetchAndCachePackageMetadata(packageName, version, signal));
|
|
40
32
|
}
|
|
41
33
|
cacheMetadata(packageName, rawData) {
|
|
42
34
|
const metadata = this.buildMetadata(packageName, rawData);
|
|
@@ -90,15 +82,7 @@ class PackageMetadataService {
|
|
|
90
82
|
}
|
|
91
83
|
async fetchPackageManifest(packageName, version, signal) {
|
|
92
84
|
signal?.throwIfAborted();
|
|
93
|
-
|
|
94
|
-
if (normalizedVersion) {
|
|
95
|
-
const jsdelivrManifest = await this.exactManifestFetcher(packageName, normalizedVersion);
|
|
96
|
-
if (jsdelivrManifest) {
|
|
97
|
-
return jsdelivrManifest;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
signal?.throwIfAborted();
|
|
101
|
-
return await this.npmRegistryClient.fetchPackageManifest(packageName, normalizedVersion || 'latest', signal);
|
|
85
|
+
return await this.npmRegistryClient.fetchPackageManifest(packageName, version?.trim() || 'latest', signal);
|
|
102
86
|
}
|
|
103
87
|
buildMetadata(packageName, rawData) {
|
|
104
88
|
return (0, package_metadata_1.mapPackageManifestToMetadata)(packageName, rawData);
|