node-power-user 1.0.19 → 1.0.21

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,43 @@
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 (include peer dependencies)
30
+ const ncuOptions = {
31
+ packageFile: packageJsonPath,
32
+ dep: 'prod,dev,peer,optional',
33
+ };
34
+
35
+ const [patchUpgrades, minorUpgrades, latestUpgrades] = await Promise.all([
36
+ ncu.run({ ...ncuOptions, target: 'patch' }),
37
+ ncu.run({ ...ncuOptions, target: 'minor' }),
38
+ ncu.run({ ...ncuOptions, target: 'latest' }),
39
+ ]);
40
+
41
+ // Get all dependencies
20
42
  const allDependencies = Object.assign(
21
43
  {},
22
44
  projectJson.dependencies,
@@ -24,77 +46,187 @@ module.exports = async function (options) {
24
46
  projectJson.peerDependencies
25
47
  );
26
48
 
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
- }
49
+ // Build unified package data
50
+ const allPackages = new Map();
32
51
 
33
- // Log start
34
- logger.log(`Checking outdated dependencies for ${logger.format.bold(projectJson.name || 'Unknown Project')}...`);
52
+ for (const dep of Object.keys(allDependencies)) {
53
+ const packageVersion = version.clean(allDependencies[dep]);
54
+ const installedPackagePath = path.join(projectPath, 'node_modules', dep, 'package.json');
35
55
 
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);
56
+ let installedVersion = null;
57
+ if (jetpack.exists(installedPackagePath)) {
58
+ const installedJson = jetpack.read(installedPackagePath, 'json');
59
+ installedVersion = installedJson?.version ? version.clean(installedJson.version) : null;
60
+ }
40
61
 
41
- const response = {};
62
+ const patchVersion = patchUpgrades[dep] ? version.clean(patchUpgrades[dep]) : null;
63
+ const minorVersion = minorUpgrades[dep] ? version.clean(minorUpgrades[dep]) : null;
64
+ const latestVersion = latestUpgrades[dep] ? version.clean(latestUpgrades[dep]) : null;
42
65
 
43
- // Loop through dependencies
44
- for (const dep of Object.keys(allDependencies)) {
45
- try {
46
- progress.increment();
66
+ // Check if this package needs attention
67
+ const hasDiscrepancy = installedVersion && !version.is(installedVersion, '==', packageVersion);
68
+ const hasUpdate = latestVersion && latestVersion !== packageVersion;
47
69
 
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');
70
+ if (hasDiscrepancy || hasUpdate) {
71
+ allPackages.set(dep, {
72
+ name: dep,
73
+ packageVersion,
74
+ installedVersion: installedVersion || '?',
75
+ patchVersion,
76
+ minorVersion,
77
+ latestVersion,
78
+ type: getDependencyType(projectJson, dep),
79
+ hasDiscrepancy,
80
+ hasMajorUpdate: latestVersion && minorVersion !== latestVersion,
81
+ });
82
+ }
83
+ }
51
84
 
52
- let installedVersion = '?';
53
- if (jetpack.exists(installedPackagePath)) {
54
- const installedJson = jetpack.read(installedPackagePath, 'json');
55
- installedVersion = installedJson?.version ? version.clean(installedJson.version) : '?';
85
+ // Display unified table
86
+ if (allPackages.size === 0) {
87
+ logger.log(logger.format.green('\nAll packages are up to date!'));
88
+ return { allPackages };
89
+ }
90
+
91
+ const dataTable = [['Package', 'package.json', 'Installed', 'Latest', 'Type']];
92
+
93
+ for (const pkg of allPackages.values()) {
94
+ const pkgVersionColor = pkg.hasDiscrepancy ? 'red' : 'green';
95
+
96
+ // Format latest version - show major updates in magenta
97
+ let latestDisplay;
98
+ if (pkg.latestVersion) {
99
+ if (pkg.hasMajorUpdate) {
100
+ latestDisplay = chalk.magenta(pkg.latestVersion + ' ⚠');
101
+ } else if (pkg.latestVersion !== pkg.installedVersion) {
102
+ latestDisplay = chalk.cyan(pkg.latestVersion);
103
+ } else {
104
+ latestDisplay = chalk.green(pkg.latestVersion);
56
105
  }
106
+ } else {
107
+ latestDisplay = chalk.dim('-');
108
+ }
57
109
 
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}`);
110
+ dataTable.push([
111
+ pkg.name,
112
+ chalk[pkgVersionColor](pkg.packageVersion),
113
+ chalk.green(pkg.installedVersion),
114
+ latestDisplay,
115
+ pkg.type,
116
+ ]);
117
+ }
118
+
119
+ console.log(table(dataTable));
120
+ logger.log(chalk.dim('Legend: ') + chalk.magenta('⚠ = major version (breaking changes)'));
121
+
122
+ // Get counts for menu
123
+ const discrepancies = [...allPackages.values()].filter(pkg => pkg.hasDiscrepancy);
124
+ const patchCount = Object.keys(patchUpgrades).length;
125
+ const minorCount = Object.keys(minorUpgrades).length;
126
+ const majorCount = Object.keys(latestUpgrades).length;
127
+
128
+ // Check for shortcut flags (skip menu)
129
+ let action = null;
130
+ if (options.r || options.reconcile) {
131
+ action = 'reconcile';
132
+ } else if (options.P || options.patch) {
133
+ action = 'patch';
134
+ } else if (options.m || options.minor) {
135
+ action = 'minor';
136
+ } else if (options.M || options.major) {
137
+ action = 'latest';
138
+ }
139
+
140
+ // If no shortcut, show menu
141
+ if (!action) {
142
+ const choices = [];
143
+
144
+ if (discrepancies.length > 0) {
145
+ choices.push({
146
+ name: `Reconcile (${discrepancies.length}) - sync package.json to installed versions`,
147
+ value: 'reconcile',
148
+ });
149
+ }
150
+
151
+ if (patchCount > 0) {
152
+ choices.push({
153
+ name: `Patch (${patchCount}) - safe bug fixes only`,
154
+ value: 'patch',
155
+ });
156
+ }
157
+
158
+ if (minorCount > 0) {
159
+ choices.push({
160
+ name: `Minor (${minorCount}) - new features, no breaking changes`,
161
+ value: 'minor',
162
+ });
163
+ }
164
+
165
+ if (majorCount > 0) {
166
+ choices.push({
167
+ name: `Major (${majorCount}) - all updates including breaking changes ⚠`,
168
+ value: 'latest',
169
+ });
170
+ }
171
+
172
+ choices.push({ name: 'Exit', value: 'exit' });
173
+
174
+ action = await inquirer.select({
175
+ message: 'What would you like to do?',
176
+ choices,
177
+ });
178
+ }
179
+
180
+ if (action === 'exit') {
181
+ return { allPackages };
182
+ }
183
+
184
+ // Handle reconcile
185
+ if (action === 'reconcile') {
186
+ // Re-read in case it changed
187
+ projectJson = jetpack.read(packageJsonPath, 'json');
188
+
189
+ for (const pkg of discrepancies) {
190
+ const depType = pkg.type === 'dev' ? 'devDependencies'
191
+ : pkg.type === 'peer' ? 'peerDependencies'
192
+ : 'dependencies';
193
+
194
+ projectJson[depType][pkg.name] = `^${pkg.installedVersion}`;
87
195
  }
196
+
197
+ jetpack.write(packageJsonPath, projectJson);
198
+ logger.log(logger.format.green(`\nReconciled ${discrepancies.length} package(s) in package.json.`));
199
+
200
+ return { allPackages, reconciled: true };
88
201
  }
89
202
 
90
- // Stop progress bar
91
- progress.stop();
203
+ // Handle patch/minor/major updates
204
+ const upgrades = action === 'patch' ? patchUpgrades
205
+ : action === 'minor' ? minorUpgrades
206
+ : latestUpgrades;
207
+
208
+ await ncu.run({
209
+ packageFile: packageJsonPath,
210
+ target: action,
211
+ upgrade: true,
212
+ });
92
213
 
93
- // Display table
94
- console.log(table(data));
214
+ logger.log(logger.format.green(`\nUpdated ${Object.keys(upgrades).length} package(s) in package.json.`));
95
215
 
96
- // Log completion
97
- logger.log(logger.format.green('Outdated package check completed successfully!'));
216
+ // Run npm install
217
+ logger.log(logger.format.cyan('\nRunning npm install...'));
218
+ await execute('npm install', { log: true });
98
219
 
99
- return response;
220
+ return { allPackages, updated: true, target: action };
100
221
  };
222
+
223
+ // Helper to determine dependency type
224
+ function getDependencyType(packageJson, dep) {
225
+ if (packageJson.devDependencies?.[dep]) {
226
+ return 'dev';
227
+ }
228
+ if (packageJson.peerDependencies?.[dep]) {
229
+ return 'peer';
230
+ }
231
+ return 'prod';
232
+ }
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.21",
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
  }