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 ADDED
@@ -0,0 +1,142 @@
1
+ # inup
2
+
3
+ [![npm version](https://img.shields.io/npm/v/inup?logo=npm&logoColor=%23CB3837&style=for-the-badge&color=crimson)](https://www.npmjs.com/package/inup)
4
+ [![Downloads](https://img.shields.io/npm/dm/inup?style=for-the-badge&color=646CFF&logoColor=white)](https://www.npmjs.com/package/inup)
5
+ [![Total downloads](https://img.shields.io/npm/dt/inup?style=for-the-badge&color=informational)](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
+ ![Interactive Upgrade Demo](docs/demo/interactive-upgrade.gif)
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