node-power-user 1.0.19 → 1.0.20

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/dist/cli.js CHANGED
@@ -9,7 +9,7 @@ const ALIASES = {
9
9
  clean: ['-c', '--clean'],
10
10
  global: ['-g', '--global'],
11
11
  open: ['--open', 'repo', '--repo'],
12
- outdated: ['-o', 'out', '--outdated'],
12
+ outdated: ['-o', 'out', '--outdated', '-u', '--update', 'up', 'update'],
13
13
  packages: ['-p', 'pack', '--packages'],
14
14
  version: ['-v', '--version'],
15
15
  sync: ['-s', '--sync', 'push', '--push'],
@@ -2,21 +2,38 @@
2
2
  const logger = new (require('../lib/logger'))('node-power-user');
3
3
  const chalk = require('chalk');
4
4
  const { table } = require('table');
5
- const ProgressBar = require('cli-progress');
6
- const Npm = require('npm-api');
7
- const version = require('wonderful-version');
8
5
  const jetpack = require('fs-jetpack');
9
6
  const path = require('path');
10
-
11
- const npm = new Npm();
7
+ const version = require('wonderful-version');
8
+ const inquirer = require('@inquirer/prompts');
9
+ const ncu = require('npm-check-updates');
10
+ const { execute } = require('node-powertools');
12
11
 
13
12
  // Load package.json
14
13
  const projectPath = process.cwd();
15
- const projectJson = jetpack.read(path.join(projectPath, 'package.json'), 'json') || {};
16
14
 
17
15
  // Module
18
16
  module.exports = async function (options) {
19
- // Combine all dependencies
17
+ const packageJsonPath = path.join(projectPath, 'package.json');
18
+ let projectJson = jetpack.read(packageJsonPath, 'json');
19
+
20
+ // Check if package.json exists
21
+ if (!projectJson) {
22
+ logger.error('No package.json found in the current directory.');
23
+ return {};
24
+ }
25
+
26
+ // Log start
27
+ logger.log(`Checking packages for ${logger.format.bold(projectJson.name || 'Unknown Project')}...`);
28
+
29
+ // Run ncu for patch, minor, and latest
30
+ const [patchUpgrades, minorUpgrades, latestUpgrades] = await Promise.all([
31
+ ncu.run({ packageFile: packageJsonPath, target: 'patch' }),
32
+ ncu.run({ packageFile: packageJsonPath, target: 'minor' }),
33
+ ncu.run({ packageFile: packageJsonPath, target: 'latest' }),
34
+ ]);
35
+
36
+ // Get all dependencies
20
37
  const allDependencies = Object.assign(
21
38
  {},
22
39
  projectJson.dependencies,
@@ -24,77 +41,187 @@ module.exports = async function (options) {
24
41
  projectJson.peerDependencies
25
42
  );
26
43
 
27
- // Check if there are any dependencies
28
- if (Object.keys(allDependencies).length === 0) {
29
- logger.warn('No dependencies found in package.json.');
30
- return {};
31
- }
44
+ // Build unified package data
45
+ const allPackages = new Map();
32
46
 
33
- // Log start
34
- logger.log(`Checking outdated dependencies for ${logger.format.bold(projectJson.name || 'Unknown Project')}...`);
47
+ for (const dep of Object.keys(allDependencies)) {
48
+ const packageVersion = version.clean(allDependencies[dep]);
49
+ const installedPackagePath = path.join(projectPath, 'node_modules', dep, 'package.json');
35
50
 
36
- // Initialize data table
37
- const data = [['Name', 'Package', 'Installed', 'Latest']];
38
- const progress = new ProgressBar.SingleBar({}, ProgressBar.Presets.shades_classic);
39
- progress.start(Object.keys(allDependencies).length, 0);
51
+ let installedVersion = null;
52
+ if (jetpack.exists(installedPackagePath)) {
53
+ const installedJson = jetpack.read(installedPackagePath, 'json');
54
+ installedVersion = installedJson?.version ? version.clean(installedJson.version) : null;
55
+ }
40
56
 
41
- const response = {};
57
+ const patchVersion = patchUpgrades[dep] ? version.clean(patchUpgrades[dep]) : null;
58
+ const minorVersion = minorUpgrades[dep] ? version.clean(minorUpgrades[dep]) : null;
59
+ const latestVersion = latestUpgrades[dep] ? version.clean(latestUpgrades[dep]) : null;
42
60
 
43
- // Loop through dependencies
44
- for (const dep of Object.keys(allDependencies)) {
45
- try {
46
- progress.increment();
61
+ // Check if this package needs attention
62
+ const hasDiscrepancy = installedVersion && !version.is(installedVersion, '==', packageVersion);
63
+ const hasUpdate = latestVersion && latestVersion !== packageVersion;
47
64
 
48
- // Get version info
49
- const packageVersion = version.clean(allDependencies[dep]); // Version from package.json
50
- const installedPackagePath = path.join(projectPath, 'node_modules', dep, 'package.json');
65
+ if (hasDiscrepancy || hasUpdate) {
66
+ allPackages.set(dep, {
67
+ name: dep,
68
+ packageVersion,
69
+ installedVersion: installedVersion || '?',
70
+ patchVersion,
71
+ minorVersion,
72
+ latestVersion,
73
+ type: getDependencyType(projectJson, dep),
74
+ hasDiscrepancy,
75
+ hasMajorUpdate: latestVersion && minorVersion !== latestVersion,
76
+ });
77
+ }
78
+ }
51
79
 
52
- let installedVersion = '?';
53
- if (jetpack.exists(installedPackagePath)) {
54
- const installedJson = jetpack.read(installedPackagePath, 'json');
55
- installedVersion = installedJson?.version ? version.clean(installedJson.version) : '?';
80
+ // Display unified table
81
+ if (allPackages.size === 0) {
82
+ logger.log(logger.format.green('\nAll packages are up to date!'));
83
+ return { allPackages };
84
+ }
85
+
86
+ const dataTable = [['Package', 'package.json', 'Installed', 'Latest', 'Type']];
87
+
88
+ for (const pkg of allPackages.values()) {
89
+ const pkgVersionColor = pkg.hasDiscrepancy ? 'red' : 'green';
90
+
91
+ // Format latest version - show major updates in magenta
92
+ let latestDisplay;
93
+ if (pkg.latestVersion) {
94
+ if (pkg.hasMajorUpdate) {
95
+ latestDisplay = chalk.magenta(pkg.latestVersion + ' ⚠');
96
+ } else if (pkg.latestVersion !== pkg.installedVersion) {
97
+ latestDisplay = chalk.cyan(pkg.latestVersion);
98
+ } else {
99
+ latestDisplay = chalk.green(pkg.latestVersion);
56
100
  }
101
+ } else {
102
+ latestDisplay = chalk.dim('-');
103
+ }
57
104
 
58
- // Get latest published version
59
- const latestVersion = await npm.repo(dep).package().then(pkg => version.clean(pkg.version)).catch(() => '?');
60
-
61
- // Check version statuses
62
- const isInstalledCurrent = version.is(installedVersion, '==', packageVersion);
63
- const isUpToDate = version.is(packageVersion, '>=', latestVersion);
64
-
65
- // Format version colors
66
- const installedColor = isInstalledCurrent ? 'green' : 'yellow';
67
- const latestColor = isUpToDate ? 'green' : 'red';
68
-
69
- // Store response data
70
- response[dep] = {
71
- package: packageVersion,
72
- installed: installedVersion,
73
- latest: latestVersion,
74
- isInstalledCurrent,
75
- isUpToDate
76
- };
77
-
78
- // Add row to table
79
- data.push([
80
- dep,
81
- packageVersion,
82
- chalk[installedColor](installedVersion),
83
- chalk[latestColor](latestVersion)
84
- ]);
85
- } catch (e) {
86
- logger.error(`Error checking ${dep}: ${e.message}`);
105
+ dataTable.push([
106
+ pkg.name,
107
+ chalk[pkgVersionColor](pkg.packageVersion),
108
+ chalk.green(pkg.installedVersion),
109
+ latestDisplay,
110
+ pkg.type,
111
+ ]);
112
+ }
113
+
114
+ console.log(table(dataTable));
115
+ logger.log(chalk.dim('Legend: ') + chalk.magenta('⚠ = major version (breaking changes)'));
116
+
117
+ // Get counts for menu
118
+ const discrepancies = [...allPackages.values()].filter(pkg => pkg.hasDiscrepancy);
119
+ const patchCount = Object.keys(patchUpgrades).length;
120
+ const minorCount = Object.keys(minorUpgrades).length;
121
+ const majorCount = Object.keys(latestUpgrades).length;
122
+
123
+ // Check for shortcut flags (skip menu)
124
+ let action = null;
125
+ if (options.r || options.reconcile) {
126
+ action = 'reconcile';
127
+ } else if (options.P || options.patch) {
128
+ action = 'patch';
129
+ } else if (options.m || options.minor) {
130
+ action = 'minor';
131
+ } else if (options.M || options.major) {
132
+ action = 'latest';
133
+ }
134
+
135
+ // If no shortcut, show menu
136
+ if (!action) {
137
+ const choices = [];
138
+
139
+ if (discrepancies.length > 0) {
140
+ choices.push({
141
+ name: `Reconcile (${discrepancies.length}) - sync package.json to installed versions`,
142
+ value: 'reconcile',
143
+ });
87
144
  }
145
+
146
+ if (patchCount > 0) {
147
+ choices.push({
148
+ name: `Patch (${patchCount}) - safe bug fixes only`,
149
+ value: 'patch',
150
+ });
151
+ }
152
+
153
+ if (minorCount > 0) {
154
+ choices.push({
155
+ name: `Minor (${minorCount}) - new features, no breaking changes`,
156
+ value: 'minor',
157
+ });
158
+ }
159
+
160
+ if (majorCount > 0) {
161
+ choices.push({
162
+ name: `Major (${majorCount}) - all updates including breaking changes ⚠`,
163
+ value: 'latest',
164
+ });
165
+ }
166
+
167
+ choices.push({ name: 'Exit', value: 'exit' });
168
+
169
+ action = await inquirer.select({
170
+ message: 'What would you like to do?',
171
+ choices,
172
+ });
88
173
  }
89
174
 
90
- // Stop progress bar
91
- progress.stop();
175
+ if (action === 'exit') {
176
+ return { allPackages };
177
+ }
92
178
 
93
- // Display table
94
- console.log(table(data));
179
+ // Handle reconcile
180
+ if (action === 'reconcile') {
181
+ // Re-read in case it changed
182
+ projectJson = jetpack.read(packageJsonPath, 'json');
95
183
 
96
- // Log completion
97
- logger.log(logger.format.green('Outdated package check completed successfully!'));
184
+ for (const pkg of discrepancies) {
185
+ const depType = pkg.type === 'dev' ? 'devDependencies'
186
+ : pkg.type === 'peer' ? 'peerDependencies'
187
+ : 'dependencies';
98
188
 
99
- return response;
189
+ projectJson[depType][pkg.name] = `^${pkg.installedVersion}`;
190
+ }
191
+
192
+ jetpack.write(packageJsonPath, projectJson);
193
+ logger.log(logger.format.green(`\nReconciled ${discrepancies.length} package(s) in package.json.`));
194
+
195
+ return { allPackages, reconciled: true };
196
+ }
197
+
198
+ // Handle patch/minor/major updates
199
+ const upgrades = action === 'patch' ? patchUpgrades
200
+ : action === 'minor' ? minorUpgrades
201
+ : latestUpgrades;
202
+
203
+ await ncu.run({
204
+ packageFile: packageJsonPath,
205
+ target: action,
206
+ upgrade: true,
207
+ });
208
+
209
+ logger.log(logger.format.green(`\nUpdated ${Object.keys(upgrades).length} package(s) in package.json.`));
210
+
211
+ // Run npm install
212
+ logger.log(logger.format.cyan('\nRunning npm install...'));
213
+ await execute('npm install', { log: true });
214
+
215
+ return { allPackages, updated: true, target: action };
100
216
  };
217
+
218
+ // Helper to determine dependency type
219
+ function getDependencyType(packageJson, dep) {
220
+ if (packageJson.devDependencies?.[dep]) {
221
+ return 'dev';
222
+ }
223
+ if (packageJson.peerDependencies?.[dep]) {
224
+ return 'peer';
225
+ }
226
+ return 'prod';
227
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-power-user",
3
- "version": "1.0.19",
3
+ "version": "1.0.20",
4
4
  "description": "Easy tools for every Node.js developer!",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -36,19 +36,20 @@
36
36
  "replace": {}
37
37
  },
38
38
  "dependencies": {
39
- "@inquirer/prompts": "^7.9.0",
39
+ "@inquirer/prompts": "^7.10.1",
40
40
  "chalk": "^4.1.2",
41
41
  "cli-progress": "^3.12.0",
42
42
  "fs-jetpack": "^5.1.0",
43
43
  "itwcw-package-analytics": "^1.0.6",
44
- "node-powertools": "^2.3.1",
44
+ "node-powertools": "^2.3.2",
45
45
  "npm-api": "^1.0.1",
46
+ "npm-check-updates": "^19.3.2",
46
47
  "table": "^6.9.0",
47
48
  "wonderful-version": "^1.3.2",
48
49
  "yargs": "^16.2.0"
49
50
  },
50
51
  "devDependencies": {
51
52
  "mocha": "^8.4.0",
52
- "prepare-package": "^1.2.5"
53
+ "prepare-package": "^1.2.6"
53
54
  }
54
55
  }