inup 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -0
- package/dist/cli.js +109 -0
- package/dist/constants.js +28 -0
- package/dist/core/index.js +23 -0
- package/dist/core/package-detector.js +182 -0
- package/dist/core/upgrade-runner.js +135 -0
- package/dist/core/upgrader.js +139 -0
- package/dist/index.js +10 -0
- package/dist/interactive-ui.js +411 -0
- package/dist/services/changelog-fetcher.js +193 -0
- package/dist/services/index.js +24 -0
- package/dist/services/jsdelivr-registry.js +232 -0
- package/dist/services/npm-registry.js +145 -0
- package/dist/services/package-manager-detector.js +172 -0
- package/dist/services/version-checker.js +92 -0
- package/dist/types.js +3 -0
- package/dist/ui/index.js +13 -0
- package/dist/ui/input-handler.js +112 -0
- package/dist/ui/renderer/confirmation.js +34 -0
- package/dist/ui/renderer/index.js +77 -0
- package/dist/ui/renderer/modal.js +184 -0
- package/dist/ui/renderer/package-list.js +243 -0
- package/dist/ui/state.js +250 -0
- package/dist/ui/utils.js +66 -0
- package/dist/utils/exec.js +38 -0
- package/dist/utils/filesystem.js +225 -0
- package/dist/utils/index.js +28 -0
- package/dist/utils/version.js +130 -0
- package/package.json +73 -0
package/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# inup
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/inup)
|
|
4
|
+
[](https://www.npmjs.com/package/inup)
|
|
5
|
+
[](https://www.npmjs.com/package/inup)
|
|
6
|
+
|
|
7
|
+
A powerful interactive CLI tool for upgrading dependencies with ease. **Auto-detects and works with npm, yarn, pnpm, and bun**. Inspired by `yarn upgrade-interactive`, this tool makes dependency management a breeze for any project. Perfect for monorepos, workspaces, and batch upgrades ❤️
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
## What it does
|
|
12
|
+
|
|
13
|
+
Ever found yourself staring at a wall of outdated packages, wondering which ones to upgrade? This tool helps you:
|
|
14
|
+
|
|
15
|
+
- **Scans your entire project** - finds all package.json files in your workspace
|
|
16
|
+
- **Auto-detects your package manager** - works seamlessly with npm, yarn, pnpm, or bun
|
|
17
|
+
- **Checks for updates** - compares your current versions against the latest available
|
|
18
|
+
- **Lets you pick what to upgrade** - interactive interface to select exactly what you want
|
|
19
|
+
- **Does the heavy lifting** - updates your package.json files and runs the appropriate install command
|
|
20
|
+
|
|
21
|
+
## Why choose inup?
|
|
22
|
+
|
|
23
|
+
If you miss the convenience of `yarn upgrade-interactive` but want it to work with **any package manager**, this tool is perfect for you!
|
|
24
|
+
|
|
25
|
+
- **🚀 Fast & Efficient** - Batch upgrade multiple packages at once
|
|
26
|
+
- **🔒 Safe Updates** - Choose between minor updates or major version jumps
|
|
27
|
+
- **🏢 Monorepo Friendly** - Works seamlessly with workspaces
|
|
28
|
+
- **📦 Registry Aware** - Checks npm registry for latest versions
|
|
29
|
+
- **🎯 Selective Upgrades** - Pick exactly which packages to upgrade
|
|
30
|
+
- **⚡ Zero Config** - Works out of the box with sensible defaults
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
### With npx (no installation needed)
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx inup
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Install globally with pnpm
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pnpm add -g inup
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Alternative: npm
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install -g inup
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Usage
|
|
53
|
+
|
|
54
|
+
Just run it in your project directory:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
inup
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The tool will scan your entire workspace (including monorepos), find outdated packages, and let you choose which ones to upgrade interactively.
|
|
61
|
+
|
|
62
|
+
### Command line options
|
|
63
|
+
|
|
64
|
+
- `-d, --dir <directory>`: Run in a specific directory (default: current directory)
|
|
65
|
+
- `-e, --exclude <patterns>`: Skip directories matching these regex patterns (comma-separated)
|
|
66
|
+
- `-p, --peer`: Include peer dependencies in upgrade process (default: false)
|
|
67
|
+
- `-o, --optional`: Include optional dependencies in upgrade process (default: false)
|
|
68
|
+
- `--package-manager <name>`: Manually specify package manager (npm, yarn, pnpm, bun) - overrides auto-detection
|
|
69
|
+
|
|
70
|
+
**Note:** By default, the tool only processes `dependencies` and `devDependencies`. Both `peerDependencies` and `optionalDependencies` are excluded by default and must be explicitly included with their respective flags.
|
|
71
|
+
|
|
72
|
+
Examples:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Basic usage - scans only dependencies and devDependencies
|
|
76
|
+
inup
|
|
77
|
+
|
|
78
|
+
# Include peer dependencies in the upgrade process
|
|
79
|
+
inup --peer
|
|
80
|
+
|
|
81
|
+
# Include optional dependencies
|
|
82
|
+
inup --optional
|
|
83
|
+
|
|
84
|
+
# Include both peer and optional dependencies
|
|
85
|
+
inup --peer --optional
|
|
86
|
+
|
|
87
|
+
# Skip example and test directories
|
|
88
|
+
inup --exclude "example,test"
|
|
89
|
+
|
|
90
|
+
# Skip specific paths with regex
|
|
91
|
+
inup -e "example/.*,.*\.test\..*"
|
|
92
|
+
|
|
93
|
+
# Run in a different directory
|
|
94
|
+
inup --dir ../my-other-project
|
|
95
|
+
|
|
96
|
+
# Combine multiple options
|
|
97
|
+
inup --dir ./packages --peer --exclude "test,dist"
|
|
98
|
+
|
|
99
|
+
# Force a specific package manager
|
|
100
|
+
inup --package-manager npm
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### How it works
|
|
104
|
+
|
|
105
|
+
1. **Detects your package manager** - Auto-detects from lock files (package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb) or package.json
|
|
106
|
+
2. **Scans your project** - Finds all package.json files recursively (respects exclude patterns)
|
|
107
|
+
3. **Collects dependencies** - Gathers dependencies based on your options (dependencies, devDependencies, and optionally peerDependencies/optionalDependencies)
|
|
108
|
+
4. **Checks for updates** - Queries npm registry for latest versions
|
|
109
|
+
5. **Shows you options** - Interactive UI lets you pick what to upgrade (minor updates or latest versions)
|
|
110
|
+
6. **Updates safely** - Modifies package.json files and runs the appropriate install command (`npm install`, `yarn install`, `pnpm install`, or `bun install`)
|
|
111
|
+
|
|
112
|
+
### Package Manager Detection
|
|
113
|
+
|
|
114
|
+
inup automatically detects which package manager you're using by:
|
|
115
|
+
|
|
116
|
+
1. **Checking package.json** - Looks for the `packageManager` field
|
|
117
|
+
2. **Checking lock files** - Scans for:
|
|
118
|
+
- `pnpm-lock.yaml` → pnpm
|
|
119
|
+
- `bun.lockb` → bun
|
|
120
|
+
- `yarn.lock` → yarn
|
|
121
|
+
- `package-lock.json` → npm
|
|
122
|
+
3. **Fallback to npm** - If nothing is detected, defaults to npm with a warning
|
|
123
|
+
|
|
124
|
+
You can override auto-detection using the `--package-manager` flag.
|
|
125
|
+
|
|
126
|
+
### FAQ
|
|
127
|
+
|
|
128
|
+
**Q: How does inup detect my package manager?**
|
|
129
|
+
A: It checks your `package.json` `packageManager` field first, then looks for lock files. You can manually override with `--package-manager`.
|
|
130
|
+
|
|
131
|
+
**Q: What if I have multiple lock files?**
|
|
132
|
+
A: inup will use the most recently modified lock file and show a warning. Consider cleaning up unused lock files.
|
|
133
|
+
|
|
134
|
+
**Q: Can I force a specific package manager?**
|
|
135
|
+
A: Yes! Use `--package-manager npm` (or yarn, pnpm, bun) to override auto-detection.
|
|
136
|
+
|
|
137
|
+
**Q: What if the detected package manager isn't installed?**
|
|
138
|
+
A: inup will still update your package.json files but skip the install step. It will show you the manual install command to run.
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
MIT
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const fs_1 = require("fs");
|
|
10
|
+
const path_1 = require("path");
|
|
11
|
+
const index_1 = require("./index");
|
|
12
|
+
const services_1 = require("./services");
|
|
13
|
+
const packageJson = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '../package.json'), 'utf-8'));
|
|
14
|
+
const program = new commander_1.Command();
|
|
15
|
+
program
|
|
16
|
+
.name('inup')
|
|
17
|
+
.description('Interactive upgrade tool for package managers. Auto-detects and works with npm, yarn, pnpm, and bun.')
|
|
18
|
+
.version(packageJson.version)
|
|
19
|
+
.option('-d, --dir <directory>', 'specify directory to run in', process.cwd())
|
|
20
|
+
.option('-e, --exclude <patterns>', 'exclude paths matching regex patterns (comma-separated)', '')
|
|
21
|
+
.option('-p, --peer', 'include peer dependencies in upgrade process')
|
|
22
|
+
.option('-o, --optional', 'include optional dependencies in upgrade process')
|
|
23
|
+
.option('--package-manager <name>', 'manually specify package manager (npm, yarn, pnpm, bun)')
|
|
24
|
+
.action(async (options) => {
|
|
25
|
+
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`);
|
|
26
|
+
// Check for updates in the background (non-blocking)
|
|
27
|
+
const updateCheckPromise = (0, services_1.checkForUpdateAsync)('inup', packageJson.version);
|
|
28
|
+
const excludePatterns = options.exclude
|
|
29
|
+
? options.exclude
|
|
30
|
+
.split(',')
|
|
31
|
+
.map((p) => p.trim())
|
|
32
|
+
.filter(Boolean)
|
|
33
|
+
: [];
|
|
34
|
+
// Commander.js: boolean flags are undefined if not provided, true if provided
|
|
35
|
+
// Both flags default to false (opt-in)
|
|
36
|
+
const includePeerDeps = options.peer === true;
|
|
37
|
+
const includeOptionalDeps = options.optional === true;
|
|
38
|
+
// Validate package manager if provided
|
|
39
|
+
let packageManager;
|
|
40
|
+
if (options.packageManager) {
|
|
41
|
+
const validPMs = ['npm', 'yarn', 'pnpm', 'bun'];
|
|
42
|
+
if (!validPMs.includes(options.packageManager)) {
|
|
43
|
+
console.error(chalk_1.default.red(`Invalid package manager: ${options.packageManager}`));
|
|
44
|
+
console.error(chalk_1.default.yellow(`Valid options: ${validPMs.join(', ')}`));
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
packageManager = options.packageManager;
|
|
48
|
+
}
|
|
49
|
+
const upgrader = new index_1.UpgradeRunner({
|
|
50
|
+
cwd: options.dir,
|
|
51
|
+
excludePatterns,
|
|
52
|
+
includePeerDeps,
|
|
53
|
+
includeOptionalDeps,
|
|
54
|
+
packageManager,
|
|
55
|
+
});
|
|
56
|
+
await upgrader.run();
|
|
57
|
+
// After the main flow completes, check if there's an update available
|
|
58
|
+
const updateCheck = await updateCheckPromise;
|
|
59
|
+
if (updateCheck?.isOutdated) {
|
|
60
|
+
console.log('');
|
|
61
|
+
console.log(chalk_1.default.yellow('┌' + '─'.repeat(78) + '┐'));
|
|
62
|
+
console.log(chalk_1.default.yellow('│') +
|
|
63
|
+
' ' +
|
|
64
|
+
chalk_1.default.bold.yellow('Update available! ') +
|
|
65
|
+
chalk_1.default.gray(`${updateCheck.currentVersion}`) +
|
|
66
|
+
' → ' +
|
|
67
|
+
chalk_1.default.green(`${updateCheck.latestVersion}`) +
|
|
68
|
+
' '.repeat(78 - 19 - updateCheck.currentVersion.length - 3 - updateCheck.latestVersion.length - 1) +
|
|
69
|
+
chalk_1.default.yellow('│'));
|
|
70
|
+
console.log(chalk_1.default.yellow('│') +
|
|
71
|
+
' ' +
|
|
72
|
+
chalk_1.default.gray('Run: ') +
|
|
73
|
+
chalk_1.default.cyan(updateCheck.updateCommand) +
|
|
74
|
+
' '.repeat(78 - 6 - updateCheck.updateCommand.length - 1) +
|
|
75
|
+
chalk_1.default.yellow('│'));
|
|
76
|
+
console.log(chalk_1.default.yellow('└' + '─'.repeat(78) + '┘'));
|
|
77
|
+
console.log('');
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
// Handle uncaught errors gracefully
|
|
81
|
+
process.on('uncaughtException', (error) => {
|
|
82
|
+
console.error(chalk_1.default.red('Uncaught Exception:'), error.message);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
});
|
|
85
|
+
process.on('unhandledRejection', (reason) => {
|
|
86
|
+
console.error(chalk_1.default.red('Unhandled Rejection:'), reason);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
});
|
|
89
|
+
// Handle Ctrl+C gracefully
|
|
90
|
+
let sigintReceived = false;
|
|
91
|
+
process.on('SIGINT', () => {
|
|
92
|
+
if (sigintReceived) {
|
|
93
|
+
// Force exit on second Ctrl+C
|
|
94
|
+
console.log(chalk_1.default.red('\n\nForce exiting...'));
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
sigintReceived = true;
|
|
99
|
+
console.log(chalk_1.default.yellow('\n\nOperation cancelled by user. Press Ctrl+C again to force exit.'));
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
// Also handle SIGTERM
|
|
104
|
+
process.on('SIGTERM', () => {
|
|
105
|
+
console.log(chalk_1.default.yellow('\n\nOperation cancelled.'));
|
|
106
|
+
process.exit(0);
|
|
107
|
+
});
|
|
108
|
+
program.parse();
|
|
109
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Constants for npm registry queries and configuration
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.WORKSPACE_FILES = exports.LOCK_FILES = exports.REQUEST_TIMEOUT = exports.CACHE_TTL = exports.MAX_CONCURRENT_REQUESTS = exports.JSDELIVR_CDN_URL = exports.NPM_REGISTRY_URL = void 0;
|
|
7
|
+
exports.NPM_REGISTRY_URL = 'https://registry.npmjs.org';
|
|
8
|
+
exports.JSDELIVR_CDN_URL = 'https://cdn.jsdelivr.net/npm';
|
|
9
|
+
exports.MAX_CONCURRENT_REQUESTS = 80;
|
|
10
|
+
exports.CACHE_TTL = 5 * 60 * 1000; // 5 minutes in milliseconds
|
|
11
|
+
exports.REQUEST_TIMEOUT = 30000; // 30 seconds in milliseconds
|
|
12
|
+
/**
|
|
13
|
+
* Package manager lock files
|
|
14
|
+
*/
|
|
15
|
+
exports.LOCK_FILES = {
|
|
16
|
+
npm: 'package-lock.json',
|
|
17
|
+
yarn: 'yarn.lock',
|
|
18
|
+
pnpm: 'pnpm-lock.yaml',
|
|
19
|
+
bun: 'bun.lockb',
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Package manager workspace files
|
|
23
|
+
*/
|
|
24
|
+
exports.WORKSPACE_FILES = {
|
|
25
|
+
pnpm: 'pnpm-workspace.yaml',
|
|
26
|
+
// npm, yarn, and bun use package.json workspaces field
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Core business logic
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
17
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
__exportStar(require("./upgrade-runner"), exports);
|
|
21
|
+
__exportStar(require("./package-detector"), exports);
|
|
22
|
+
__exportStar(require("./upgrader"), exports);
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.PackageDetector = void 0;
|
|
37
|
+
const semver = __importStar(require("semver"));
|
|
38
|
+
const utils_1 = require("../utils");
|
|
39
|
+
const services_1 = require("../services");
|
|
40
|
+
class PackageDetector {
|
|
41
|
+
constructor(options) {
|
|
42
|
+
this.packageJsonPath = null;
|
|
43
|
+
this.packageJson = null;
|
|
44
|
+
this.cwd = options?.cwd || process.cwd();
|
|
45
|
+
this.excludePatterns = options?.excludePatterns || [];
|
|
46
|
+
// Explicitly check for true to ensure false/undefined both become false (opt-in)
|
|
47
|
+
this.includePeerDeps = options?.includePeerDeps === true;
|
|
48
|
+
this.includeOptionalDeps = options?.includeOptionalDeps === true;
|
|
49
|
+
this.packageJsonPath = (0, utils_1.findPackageJson)(this.cwd);
|
|
50
|
+
if (this.packageJsonPath) {
|
|
51
|
+
this.packageJson = (0, utils_1.readPackageJson)(this.packageJsonPath);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
hasPackageJson() {
|
|
55
|
+
return this.packageJsonPath !== null && this.packageJson !== null;
|
|
56
|
+
}
|
|
57
|
+
async getOutdatedPackages() {
|
|
58
|
+
if (!this.packageJson) {
|
|
59
|
+
throw new Error('No package.json found in current directory');
|
|
60
|
+
}
|
|
61
|
+
const packages = [];
|
|
62
|
+
// Always check all package.json files recursively with timeout protection
|
|
63
|
+
this.showProgress('🔍 Scanning repository for package.json files...');
|
|
64
|
+
const allPackageJsonFiles = this.findPackageJsonFilesWithTimeout(30000); // 30 second timeout
|
|
65
|
+
this.showProgress(`🔍 Found ${allPackageJsonFiles.length} package.json file${allPackageJsonFiles.length === 1 ? '' : 's'}`);
|
|
66
|
+
// Step 2: Collect all dependencies from package.json files (parallelized)
|
|
67
|
+
this.showProgress('🔍 Reading dependencies from package.json files...');
|
|
68
|
+
const allDepsRaw = await (0, utils_1.collectAllDependenciesAsync)(allPackageJsonFiles, {
|
|
69
|
+
includePeerDeps: this.includePeerDeps,
|
|
70
|
+
includeOptionalDeps: this.includeOptionalDeps,
|
|
71
|
+
});
|
|
72
|
+
// Step 3: Get unique package names while filtering out workspace references
|
|
73
|
+
this.showProgress('🔍 Identifying unique packages...');
|
|
74
|
+
const uniquePackageNames = new Set();
|
|
75
|
+
const allDeps = [];
|
|
76
|
+
for (const dep of allDepsRaw) {
|
|
77
|
+
if (!this.isWorkspaceReference(dep.version)) {
|
|
78
|
+
allDeps.push(dep);
|
|
79
|
+
uniquePackageNames.add(dep.name);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const packageNames = Array.from(uniquePackageNames);
|
|
83
|
+
// Step 4: Fetch all package data in one call per package using jsdelivr CDN
|
|
84
|
+
// Create a map of package names to their current versions for major version optimization
|
|
85
|
+
const currentVersions = new Map();
|
|
86
|
+
for (const dep of allDeps) {
|
|
87
|
+
// Use the first occurrence of each package's version
|
|
88
|
+
if (!currentVersions.has(dep.name)) {
|
|
89
|
+
currentVersions.set(dep.name, dep.version);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const allPackageData = await (0, services_1.getAllPackageDataFromJsdelivr)(packageNames, currentVersions, (currentPackage, completed, total) => {
|
|
93
|
+
const percentage = Math.round((completed / total) * 100);
|
|
94
|
+
const truncatedPackage = currentPackage.length > 40 ? currentPackage.substring(0, 37) + '...' : currentPackage;
|
|
95
|
+
this.showProgress(`🌐 Fetching ${percentage}% (${truncatedPackage})`);
|
|
96
|
+
});
|
|
97
|
+
try {
|
|
98
|
+
for (const dep of allDeps) {
|
|
99
|
+
try {
|
|
100
|
+
const packageData = allPackageData.get(dep.name);
|
|
101
|
+
if (!packageData)
|
|
102
|
+
continue;
|
|
103
|
+
const { latestVersion, allVersions } = packageData;
|
|
104
|
+
// Find closest minor version (same major, higher minor) that satisfies the current range
|
|
105
|
+
// Falls back to patch updates if no minor updates are available
|
|
106
|
+
const closestMinorVersion = (0, utils_1.findClosestMinorVersion)(dep.version, allVersions);
|
|
107
|
+
const installedClean = semver.coerce(dep.version)?.version || dep.version;
|
|
108
|
+
const minorClean = closestMinorVersion
|
|
109
|
+
? semver.coerce(closestMinorVersion)?.version || closestMinorVersion
|
|
110
|
+
: null;
|
|
111
|
+
const latestClean = semver.coerce(latestVersion)?.version || latestVersion;
|
|
112
|
+
const hasRangeUpdate = minorClean !== null && minorClean !== installedClean;
|
|
113
|
+
const hasMajorUpdate = semver.major(latestClean) > semver.major(installedClean);
|
|
114
|
+
const isOutdated = hasRangeUpdate || hasMajorUpdate;
|
|
115
|
+
packages.push({
|
|
116
|
+
name: dep.name,
|
|
117
|
+
currentVersion: dep.version, // Keep original version specifier with prefix
|
|
118
|
+
rangeVersion: closestMinorVersion || dep.version,
|
|
119
|
+
latestVersion,
|
|
120
|
+
type: dep.type,
|
|
121
|
+
packageJsonPath: dep.packageJsonPath,
|
|
122
|
+
isOutdated,
|
|
123
|
+
hasRangeUpdate,
|
|
124
|
+
hasMajorUpdate,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
// Skip packages that can't be checked (private packages, etc.)
|
|
129
|
+
packages.push({
|
|
130
|
+
name: dep.name,
|
|
131
|
+
currentVersion: dep.version,
|
|
132
|
+
rangeVersion: 'unknown',
|
|
133
|
+
latestVersion: 'unknown',
|
|
134
|
+
type: dep.type,
|
|
135
|
+
packageJsonPath: dep.packageJsonPath,
|
|
136
|
+
isOutdated: false,
|
|
137
|
+
hasRangeUpdate: false,
|
|
138
|
+
hasMajorUpdate: false,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return packages;
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
this.showProgress('❌ Failed to check packages\n');
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
findPackageJsonFilesWithTimeout(timeoutMs) {
|
|
150
|
+
// Synchronous file search with depth limiting and symlink protection
|
|
151
|
+
// The timeout parameter is kept for future async implementation
|
|
152
|
+
try {
|
|
153
|
+
return (0, utils_1.findAllPackageJsonFiles)(this.cwd, this.excludePatterns, 10, (currentDir, foundCount) => {
|
|
154
|
+
// Show scanning progress with current directory and count
|
|
155
|
+
const truncatedDir = currentDir.length > 50 ? '...' + currentDir.slice(-47) : currentDir;
|
|
156
|
+
this.showProgress(`🔍 Scanning ${truncatedDir} (found ${foundCount})`);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
throw new Error(`Failed to scan for package.json files: ${err}. Try using --exclude patterns to skip problematic directories.`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
isWorkspaceReference(version) {
|
|
164
|
+
// Check for common workspace reference patterns
|
|
165
|
+
return (version.includes('workspace:') ||
|
|
166
|
+
version === '*' ||
|
|
167
|
+
version.startsWith('file:') ||
|
|
168
|
+
version.startsWith('link:') ||
|
|
169
|
+
version.startsWith('github:') ||
|
|
170
|
+
version.startsWith('gitlab:') ||
|
|
171
|
+
version.startsWith('bitbucket:'));
|
|
172
|
+
}
|
|
173
|
+
showProgress(message) {
|
|
174
|
+
// Clear current line and show new message
|
|
175
|
+
process.stdout.write(`\r${' '.repeat(80)}\r${message}`);
|
|
176
|
+
}
|
|
177
|
+
getOutdatedPackagesOnly(packages) {
|
|
178
|
+
return packages.filter((pkg) => pkg.isOutdated);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
exports.PackageDetector = PackageDetector;
|
|
182
|
+
//# sourceMappingURL=package-detector.js.map
|
|
@@ -0,0 +1,135 @@
|
|
|
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.PnpmUpgradeInteractive = exports.UpgradeRunner = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const package_detector_1 = require("./package-detector");
|
|
9
|
+
const interactive_ui_1 = require("../interactive-ui");
|
|
10
|
+
const upgrader_1 = require("./upgrader");
|
|
11
|
+
const package_manager_detector_1 = require("../services/package-manager-detector");
|
|
12
|
+
/**
|
|
13
|
+
* Main orchestrator for the inup upgrade process
|
|
14
|
+
*/
|
|
15
|
+
class UpgradeRunner {
|
|
16
|
+
constructor(options) {
|
|
17
|
+
this.options = options;
|
|
18
|
+
// Detect package manager
|
|
19
|
+
const cwd = options?.cwd || process.cwd();
|
|
20
|
+
if (options?.packageManager) {
|
|
21
|
+
this.packageManager = package_manager_detector_1.PackageManagerDetector.getInfo(options.packageManager);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
this.packageManager = package_manager_detector_1.PackageManagerDetector.detect(cwd);
|
|
25
|
+
}
|
|
26
|
+
this.detector = new package_detector_1.PackageDetector(options);
|
|
27
|
+
this.ui = new interactive_ui_1.InteractiveUI(this.packageManager);
|
|
28
|
+
this.upgrader = new upgrader_1.PackageUpgrader(this.packageManager);
|
|
29
|
+
}
|
|
30
|
+
async run() {
|
|
31
|
+
try {
|
|
32
|
+
// Check prerequisites
|
|
33
|
+
this.checkPrerequisites();
|
|
34
|
+
// Detect packages
|
|
35
|
+
const packages = await this.detector.getOutdatedPackages();
|
|
36
|
+
// Display packages table
|
|
37
|
+
await this.ui.displayPackagesTable(packages);
|
|
38
|
+
const outdatedPackages = this.detector.getOutdatedPackagesOnly(packages);
|
|
39
|
+
if (outdatedPackages.length === 0) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// Interactive selection and confirmation loop
|
|
43
|
+
let selectedChoices = [];
|
|
44
|
+
let shouldProceed = false;
|
|
45
|
+
let previousSelections;
|
|
46
|
+
while (true) {
|
|
47
|
+
// Interactive selection (pass options for filtering)
|
|
48
|
+
selectedChoices = await this.ui.selectPackagesToUpgrade(packages, previousSelections, {
|
|
49
|
+
includePeerDeps: this.options?.includePeerDeps,
|
|
50
|
+
includeOptionalDeps: this.options?.includeOptionalDeps,
|
|
51
|
+
});
|
|
52
|
+
if (selectedChoices.length === 0) {
|
|
53
|
+
console.log(chalk_1.default.yellow('No packages selected. Exiting...'));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// Validate selected choices before confirmation
|
|
57
|
+
this.validateSelectedChoices(selectedChoices, packages);
|
|
58
|
+
// Store current selections for potential return to selection
|
|
59
|
+
previousSelections = new Map();
|
|
60
|
+
// Convert selectedChoices back to selection state format
|
|
61
|
+
// Group by package name and version specifier
|
|
62
|
+
const choiceMap = new Map();
|
|
63
|
+
selectedChoices.forEach((choice) => {
|
|
64
|
+
const key = `${choice.name}@${choice.currentVersionSpecifier}`;
|
|
65
|
+
choiceMap.set(key, choice.upgradeType);
|
|
66
|
+
});
|
|
67
|
+
// Convert to the format expected by selectPackagesToUpgrade
|
|
68
|
+
choiceMap.forEach((upgradeType, key) => {
|
|
69
|
+
previousSelections.set(key, upgradeType);
|
|
70
|
+
});
|
|
71
|
+
// Confirm upgrade
|
|
72
|
+
shouldProceed = await this.ui.confirmUpgrade(selectedChoices);
|
|
73
|
+
if (shouldProceed === null) {
|
|
74
|
+
// User pressed N or ESC - go back to selection with current selections preserved
|
|
75
|
+
console.clear();
|
|
76
|
+
console.log(chalk_1.default.bold.blue('🚀 inup\n'));
|
|
77
|
+
// previousSelections is already set from above
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (!shouldProceed) {
|
|
81
|
+
console.log(chalk_1.default.yellow('Upgrade cancelled.'));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// User confirmed - break out of loop and proceed
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
// Perform upgrade
|
|
88
|
+
await this.upgrader.upgradePackages(selectedChoices, packages);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
console.error(chalk_1.default.red(`Error: ${error}`));
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
checkPrerequisites() {
|
|
96
|
+
// Check if package.json exists
|
|
97
|
+
if (!this.detector.hasPackageJson()) {
|
|
98
|
+
throw new Error('No package.json found in current directory');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
validateSelectedChoices(selectedChoices, allPackages) {
|
|
102
|
+
// Validate that all selected packages have valid target versions
|
|
103
|
+
const invalidChoices = selectedChoices.filter((choice) => {
|
|
104
|
+
const packageInfo = allPackages.find((pkg) => pkg.name === choice.name && pkg.packageJsonPath === choice.packageJsonPath);
|
|
105
|
+
return !packageInfo || !choice.targetVersion;
|
|
106
|
+
});
|
|
107
|
+
if (invalidChoices.length > 0) {
|
|
108
|
+
throw new Error(`Invalid selections detected: ${invalidChoices.map((c) => c.name).join(', ')}. Please review your selections.`);
|
|
109
|
+
}
|
|
110
|
+
// Print summary of what will be upgraded
|
|
111
|
+
const packageJsonPaths = new Set(selectedChoices.map((c) => c.packageJsonPath));
|
|
112
|
+
const uniquePackages = new Set(selectedChoices.map((c) => c.name));
|
|
113
|
+
console.log('\n' + chalk_1.default.bold('📋 Upgrade Summary'));
|
|
114
|
+
console.log(chalk_1.default.gray('─'.repeat(50)));
|
|
115
|
+
console.log(`${chalk_1.default.cyan(uniquePackages.size.toString())} package(s) will be upgraded`);
|
|
116
|
+
console.log(`${chalk_1.default.cyan(packageJsonPaths.size.toString())} package.json file(s) will be modified`);
|
|
117
|
+
const rangeUpgrades = selectedChoices.filter((c) => c.upgradeType === 'range').length;
|
|
118
|
+
const majorUpgrades = selectedChoices.filter((c) => c.upgradeType === 'latest').length;
|
|
119
|
+
if (rangeUpgrades > 0) {
|
|
120
|
+
console.log(` ${chalk_1.default.yellow('●')} ${rangeUpgrades} minor/patch upgrade(s)`);
|
|
121
|
+
}
|
|
122
|
+
if (majorUpgrades > 0) {
|
|
123
|
+
console.log(` ${chalk_1.default.red('●')} ${majorUpgrades} major upgrade(s)`);
|
|
124
|
+
}
|
|
125
|
+
console.log(chalk_1.default.gray('─'.repeat(50)));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
exports.UpgradeRunner = UpgradeRunner;
|
|
129
|
+
/**
|
|
130
|
+
* @deprecated Use UpgradeRunner instead
|
|
131
|
+
*/
|
|
132
|
+
class PnpmUpgradeInteractive extends UpgradeRunner {
|
|
133
|
+
}
|
|
134
|
+
exports.PnpmUpgradeInteractive = PnpmUpgradeInteractive;
|
|
135
|
+
//# sourceMappingURL=upgrade-runner.js.map
|