node-power-user 2.1.2 → 2.1.4
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 +5 -0
- package/bin/node-power-user +7 -0
- package/dist/commands/install.js +22 -0
- package/dist/commands/outdated.js +55 -7
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -106,6 +106,10 @@ npu outdated
|
|
|
106
106
|
npu out --force # bypass Socket protection
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
+
When discrepancies are found between `package.json` and `node_modules`, the menu offers context-aware actions:
|
|
110
|
+
- **Sync** — when `node_modules` is *behind* `package.json`, installs packages to match what `package.json` declares.
|
|
111
|
+
- **Reconcile** — when `node_modules` is *ahead* of `package.json`, updates `package.json` to match installed versions.
|
|
112
|
+
|
|
109
113
|
### List Packages
|
|
110
114
|
List all packages in your project.
|
|
111
115
|
```shell
|
|
@@ -137,6 +141,7 @@ npu wait <ms>
|
|
|
137
141
|
```
|
|
138
142
|
|
|
139
143
|
### Global flags
|
|
144
|
+
* `-C <dir>`, `--cwd <dir>`: Run as if invoked from `<dir>` (e.g. `npu -C /path/to/project out`)
|
|
140
145
|
* `--debug`: Log the commands and flags before they are executed
|
|
141
146
|
|
|
142
147
|
## 🛠️ Development
|
package/bin/node-power-user
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const { hideBin } = require('yargs/helpers');
|
|
3
3
|
const argv = require('yargs')(hideBin(process.argv)).parseSync();
|
|
4
|
+
|
|
5
|
+
// --cwd / -C: run as if invoked from a different directory
|
|
6
|
+
const targetDir = argv.cwd || argv.C;
|
|
7
|
+
if (targetDir) {
|
|
8
|
+
process.chdir(targetDir);
|
|
9
|
+
}
|
|
10
|
+
|
|
4
11
|
const cli = new (require('../dist/cli.js'))();
|
|
5
12
|
(async function() {
|
|
6
13
|
'use strict';
|
package/dist/commands/install.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Libraries
|
|
2
2
|
const logger = new (require('../lib/logger'))('node-power-user');
|
|
3
3
|
const socket = require('../lib/socket');
|
|
4
|
+
const jetpack = require('fs-jetpack');
|
|
5
|
+
const path = require('path');
|
|
4
6
|
|
|
5
7
|
// Module
|
|
6
8
|
module.exports = async function (options) {
|
|
@@ -22,6 +24,26 @@ module.exports = async function (options) {
|
|
|
22
24
|
flags.push('--global');
|
|
23
25
|
}
|
|
24
26
|
|
|
27
|
+
// When installing specific packages with a version/tag (e.g. @latest, @^2.0.0),
|
|
28
|
+
// remove existing node_modules copies first so npm actually re-fetches them
|
|
29
|
+
// instead of reporting "up to date" with a stale cached version.
|
|
30
|
+
if (packages.length > 0 && !flags.includes('--global')) {
|
|
31
|
+
for (const pkg of packages) {
|
|
32
|
+
// For scoped packages like @scope/name@version, skip the leading @
|
|
33
|
+
const searchFrom = pkg.startsWith('@') ? 1 : 0;
|
|
34
|
+
const versionIdx = pkg.indexOf('@', searchFrom);
|
|
35
|
+
if (versionIdx <= 0) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const pkgName = pkg.substring(0, versionIdx);
|
|
40
|
+
const pkgDir = path.join(process.cwd(), 'node_modules', pkgName);
|
|
41
|
+
if (jetpack.exists(pkgDir)) {
|
|
42
|
+
jetpack.remove(pkgDir);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
25
47
|
const command = packages.length > 0
|
|
26
48
|
? `npm install ${packages.join(' ')} ${flags.join(' ')}`.trim()
|
|
27
49
|
: `npm install ${flags.join(' ')}`.trim();
|
|
@@ -143,8 +143,19 @@ module.exports = async function (options) {
|
|
|
143
143
|
logger.log(chalk.dim('Legend: ') + chalk.magenta('⚠ = major version (breaking changes)'));
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
//
|
|
146
|
+
// Separate discrepancies into two categories:
|
|
147
|
+
// - "behind": node_modules has an OLDER version than package.json wants (needs npm install)
|
|
148
|
+
// - "ahead": node_modules has a NEWER version than package.json specifies (reconcile package.json)
|
|
147
149
|
const discrepancies = [...allPackages.values()].filter(pkg => pkg.hasDiscrepancy);
|
|
150
|
+
const behindPackages = discrepancies.filter(pkg =>
|
|
151
|
+
pkg.installedVersion !== '?' && version.is(pkg.installedVersion, '<', pkg.packageVersion)
|
|
152
|
+
);
|
|
153
|
+
const aheadPackages = discrepancies.filter(pkg =>
|
|
154
|
+
pkg.installedVersion !== '?' && version.is(pkg.installedVersion, '>', pkg.packageVersion)
|
|
155
|
+
);
|
|
156
|
+
const unknownPackages = discrepancies.filter(pkg => pkg.installedVersion === '?');
|
|
157
|
+
|
|
158
|
+
// Get counts for menu (only show a tier if it offers upgrades beyond the tier below)
|
|
148
159
|
const patchCount = Object.keys(patchUpgrades).length;
|
|
149
160
|
const minorCount = Object.keys(minorUpgrades).length;
|
|
150
161
|
const majorCount = Object.keys(latestUpgrades).length;
|
|
@@ -180,9 +191,19 @@ module.exports = async function (options) {
|
|
|
180
191
|
if (!action) {
|
|
181
192
|
const choices = [];
|
|
182
193
|
|
|
183
|
-
|
|
194
|
+
// "Sync" installs packages where node_modules is behind package.json
|
|
195
|
+
const syncCount = behindPackages.length + unknownPackages.length;
|
|
196
|
+
if (syncCount > 0) {
|
|
197
|
+
choices.push({
|
|
198
|
+
name: `Sync (${syncCount}) - install packages to match package.json`,
|
|
199
|
+
value: 'sync',
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// "Reconcile" updates package.json where node_modules is ahead
|
|
204
|
+
if (aheadPackages.length > 0) {
|
|
184
205
|
choices.push({
|
|
185
|
-
name: `Reconcile (${
|
|
206
|
+
name: `Reconcile (${aheadPackages.length}) - sync package.json to installed versions`,
|
|
186
207
|
value: 'reconcile',
|
|
187
208
|
});
|
|
188
209
|
}
|
|
@@ -220,12 +241,39 @@ module.exports = async function (options) {
|
|
|
220
241
|
return { allPackages };
|
|
221
242
|
}
|
|
222
243
|
|
|
223
|
-
// Handle
|
|
244
|
+
// Handle sync — run npm install for packages where node_modules is behind package.json
|
|
245
|
+
if (action === 'sync') {
|
|
246
|
+
const toSync = [...behindPackages, ...unknownPackages];
|
|
247
|
+
const installCmd = `npm install ${toSync.map(pkg => `${pkg.name}@${pkg.packageVersion}`).join(' ')}`;
|
|
248
|
+
logger.log(logger.format.cyan(`\nRunning ${installCmd}...`));
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
await socket.wrap(installCmd, { force: options.force });
|
|
252
|
+
} catch (e) {
|
|
253
|
+
if (e.reason === 'npm-failed') {
|
|
254
|
+
logger.log('');
|
|
255
|
+
logger.log('Fix the npm error above and retry.');
|
|
256
|
+
return { allPackages, updated: false };
|
|
257
|
+
}
|
|
258
|
+
throw e;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
try {
|
|
262
|
+
await socket.audit({ force: options.force });
|
|
263
|
+
} catch (e) {
|
|
264
|
+
logger.error(`Audit warning: ${e.message}`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
logger.log(logger.format.green(`\nSynced ${toSync.length} package(s) to match package.json.`));
|
|
268
|
+
return { allPackages, synced: true };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Handle reconcile — update package.json for packages where node_modules is ahead
|
|
224
272
|
if (action === 'reconcile') {
|
|
225
|
-
// Re-read in case it changed
|
|
226
273
|
projectJson = jetpack.read(packageJsonPath, 'json');
|
|
227
274
|
|
|
228
|
-
|
|
275
|
+
const toReconcile = aheadPackages.length > 0 ? aheadPackages : discrepancies;
|
|
276
|
+
for (const pkg of toReconcile) {
|
|
229
277
|
const depType = pkg.type === 'dev' ? 'devDependencies'
|
|
230
278
|
: pkg.type === 'peer' ? 'peerDependencies'
|
|
231
279
|
: 'dependencies';
|
|
@@ -234,7 +282,7 @@ module.exports = async function (options) {
|
|
|
234
282
|
}
|
|
235
283
|
|
|
236
284
|
jetpack.write(packageJsonPath, projectJson);
|
|
237
|
-
logger.log(logger.format.green(`\nReconciled ${
|
|
285
|
+
logger.log(logger.format.green(`\nReconciled ${toReconcile.length} package(s) in package.json.`));
|
|
238
286
|
|
|
239
287
|
return { allPackages, reconciled: true };
|
|
240
288
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-power-user",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.4",
|
|
4
4
|
"description": "Easy tools for every Node.js developer!",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"input": "./src",
|
|
23
23
|
"output": "./dist",
|
|
24
24
|
"replace": {},
|
|
25
|
-
"type": "copy"
|
|
25
|
+
"type": "copy",
|
|
26
|
+
"hooks": {}
|
|
26
27
|
},
|
|
27
28
|
"repository": {
|
|
28
29
|
"type": "git",
|
|
@@ -40,20 +41,20 @@
|
|
|
40
41
|
},
|
|
41
42
|
"homepage": "https://itwcreativeworks.com",
|
|
42
43
|
"dependencies": {
|
|
43
|
-
"@inquirer/prompts": "^8.3
|
|
44
|
+
"@inquirer/prompts": "^8.4.3",
|
|
44
45
|
"chalk": "^5.6.2",
|
|
45
46
|
"cli-progress": "^3.12.0",
|
|
46
47
|
"fs-jetpack": "^5.1.0",
|
|
47
48
|
"itwcw-package-analytics": "^1.0.8",
|
|
48
49
|
"node-powertools": "^3.0.0",
|
|
49
50
|
"npm-api": "^1.0.1",
|
|
50
|
-
"npm-check-updates": "^
|
|
51
|
+
"npm-check-updates": "^22.2.0",
|
|
51
52
|
"table": "^6.9.0",
|
|
52
53
|
"wonderful-version": "^1.3.2",
|
|
53
54
|
"yargs": "^18.0.0"
|
|
54
55
|
},
|
|
55
56
|
"devDependencies": {
|
|
56
57
|
"mocha": "^11.7.5",
|
|
57
|
-
"prepare-package": "^2.0
|
|
58
|
+
"prepare-package": "^2.1.0"
|
|
58
59
|
}
|
|
59
60
|
}
|