npm-check-updates 3.1.25 → 3.2.2

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 CHANGED
@@ -90,6 +90,8 @@ Options
90
90
  --cwd Used as current working directory for `spawn` in npm listing
91
91
  --dep check only a specific section(s) of dependencies:
92
92
  prod|dev|peer|optional|bundle (comma-delimited)
93
+ --engines-node include only packages that satisfy engines.node as
94
+ specified in the package file
93
95
  -e, --error-level set the error-level. 1: exits with error code 0 if no
94
96
  errors occur. 2: exits with error code 0 if no
95
97
  packages need updating (useful for continuous
@@ -97,7 +99,8 @@ Options
97
99
  -f, --filter include only package names matching the given string,
98
100
  comma-or-space-delimited list, or /regex/
99
101
  -g, --global check global packages instead of in the current project
100
- -i, --interactive Enable interactive prompts for each dependency
102
+ -i, --interactive Enable interactive prompts for each dependency;
103
+ Implies -u unless one of the json options are set
101
104
  -j, --jsonAll output new package file instead of human-readable
102
105
  message
103
106
  --jsonDeps Will return output like `jsonAll` but only lists
package/bin/ncu CHANGED
@@ -23,10 +23,11 @@ program
23
23
  .option('--cwd <path>', 'Used as current working directory for `spawn` in npm listing')
24
24
  .option('--dep <dep>', 'check only a specific section(s) of dependencies: prod|dev|peer|optional|bundle (comma-delimited)')
25
25
  .option('-e, --error-level <n>', 'set the error-level. 1: exits with error code 0 if no errors occur. 2: exits with error code 0 if no packages need updating (useful for continuous integration). Default is 1.', cint.partialAt(parseInt, 1, 10), 1)
26
+ .option('--engines-node', 'upgrade to version which satisfies engines.node range')
26
27
  .option('-f, --filter <matches>', 'include only package names matching the given string, comma-or-space-delimited list, or /regex/')
27
28
  .option('-g, --global', 'check global packages instead of in the current project')
28
29
  // program.json is set to true in programInit if any options that begin with 'json' are true
29
- .option('-i, --interactive', 'Enable interactive prompts for each dependency')
30
+ .option('-i, --interactive', 'Enable interactive prompts for each dependency; implies -u unless one of the json options are set')
30
31
  .option('-j, --jsonAll', 'output new package file instead of human-readable message')
31
32
  .option('--jsonDeps', 'Will return output like `jsonAll` but only lists `dependencies`, `devDependencies`, and `optionalDependencies` of the new package data.')
32
33
  .option('--jsonUpgraded', 'output upgraded dependencies in json')
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- require('node-alias')('ncu', __dirname, { message: false });
4
+ require('node-alias')('ncu', __dirname, {message: false});
@@ -100,8 +100,10 @@ function createDependencyTable() {
100
100
  }
101
101
 
102
102
  /**
103
- * @param args.from
104
- * @param args.to
103
+ * @param {object} args
104
+ * @param {object} args.from
105
+ * @param {object} args.to
106
+ * @returns {Table}
105
107
  */
106
108
  function toDependencyTable(args) {
107
109
  const table = createDependencyTable();
@@ -144,7 +146,7 @@ function analyzeGlobalPackages(options) {
144
146
  print(options, latest, 'silly');
145
147
 
146
148
  const upgradedPackageNames = Object.keys(upgraded);
147
- const upgradePromise = printUpgrades(options, {
149
+ printUpgrades(options, {
148
150
  current: globalPackages,
149
151
  upgraded,
150
152
  latest,
@@ -164,8 +166,6 @@ function analyzeGlobalPackages(options) {
164
166
  } else if (instruction.length) {
165
167
  print(options, '\n' + chalk.cyan('ncu') + ' itself cannot upgrade global packages. Run the following to upgrade all global packages: \n\n' + chalk.cyan('npm -g install ' + instruction) + '\n');
166
168
  }
167
-
168
- return upgradePromise;
169
169
  });
170
170
  });
171
171
  }
@@ -188,6 +188,10 @@ function analyzeProjectDependencies(options, pkgData, pkgFile) {
188
188
 
189
189
  print(options, `Fetching ${vm.getVersionTarget(options)} versions...`, 'verbose');
190
190
 
191
+ if (options.enginesNode) {
192
+ options.enginesNode = _.get(pkg, 'engines.node');
193
+ }
194
+
191
195
  return vm.upgradePackageDefinitions(current, options).then(async ([upgraded, latest]) => {
192
196
  const {newPkgData, selectedNewDependencies} = await vm.upgradePackageData(pkgData, current, upgraded, latest, options);
193
197
 
@@ -255,6 +259,7 @@ function analyzeProjectDependencies(options, pkgData, pkgFile) {
255
259
  * @param {Object} args.upgraded - The packages that should be upgraded.
256
260
  * @param {number} args.numUpgraded - The number of upgraded packages
257
261
  * @param {number} args.total - The total number of all possible upgrades
262
+ * @returns {void}
258
263
  */
259
264
  function printUpgrades(options, {current, upgraded, numUpgraded, total}) {
260
265
  print(options, '');
@@ -290,6 +295,11 @@ function printUpgrades(options, {current, upgraded, numUpgraded, total}) {
290
295
  /** Initializes and consolidates options from the cli. */
291
296
  function initOptions(options) {
292
297
 
298
+ const json = _(options)
299
+ .keys()
300
+ .filter(_.partial(_.startsWith, _, 'json', 0))
301
+ .some(_.propertyOf(options));
302
+
293
303
  return Object.assign({}, options, {
294
304
  filter: options.args.join(' ') || options.filter,
295
305
  // convert silent option to loglevel silent
@@ -298,10 +308,9 @@ function initOptions(options) {
298
308
  // default to 0, except when newest or greatest are set
299
309
  pre: options.pre ? Boolean(Number(options.pre)) : options.newest || options.greatest,
300
310
  // add shortcut for any keys that start with 'json'
301
- json: _(options)
302
- .keys()
303
- .filter(_.partial(_.startsWith, _, 'json', 0))
304
- .some(_.propertyOf(options))
311
+ json,
312
+ // imply upgrade in interactive mode when json is not specified as the output
313
+ upgrade: options.interactive && options.upgrade === undefined ? !json : options.upgrade
305
314
  });
306
315
  }
307
316
 
@@ -382,14 +391,13 @@ async function findPackage(options) {
382
391
  return Promise.all([pkgData, pkgFile]);
383
392
  }
384
393
 
385
- /** main entry point */
386
- async function run(options={}) {
387
-
388
- // exit with non-zero error code when there is an unhandled promise rejection
389
- process.on('unhandledRejection', err => {
390
- throw err;
391
- });
394
+ // exit with non-zero error code when there is an unhandled promise rejection
395
+ process.on('unhandledRejection', err => {
396
+ throw err;
397
+ });
392
398
 
399
+ /** main entry point */
400
+ async function run(options = {}) {
393
401
  // if not executed on the command-line (i.e. executed as a node module), set some defaults
394
402
  if (!options.cli) {
395
403
  options = _.defaults({}, options, {
@@ -2,10 +2,12 @@
2
2
  const _ = require('lodash');
3
3
  const cint = require('cint');
4
4
  const semver = require('semver');
5
- const versionUtil = require('../version-util.js');
6
- const spawn = require('spawn-please');
5
+ const versionUtil = require('../version-util.js');
6
+ const spawn = require('spawn-please');
7
7
  const pacote = require('pacote');
8
8
 
9
+ const TIME_FIELDS = ['modified', 'created'];
10
+
9
11
  // needed until pacote supports full npm config compatibility
10
12
  // See: https://github.com/zkat/pacote/issues/156
11
13
  const npmConfig = {};
@@ -43,48 +45,78 @@ function parseJson(result, data) {
43
45
  }
44
46
 
45
47
  /**
48
+ * Returns the value of one of the properties retrieved by npm view.
46
49
  * @param {string} packageName Name of the package
47
50
  * @param {string} field Field such as "versions" or "dist-tags.latest" are parsed from the pacote result (https://www.npmjs.com/package/pacote#packument)
48
51
  * @param {string} currentVersion
49
- * @returns {Promise} Promised result
52
+ * @returns {Promise} Promised result
53
+ */
54
+ function viewOne(packageName, field, currentVersion) {
55
+ return viewMany(packageName, [field], currentVersion)
56
+ .then(result => result && result[field]);
57
+ }
58
+
59
+ /**
60
+ * Returns an object of specified values retrieved by npm view.
61
+ * @param {string} packageName Name of the package
62
+ * @param {string[]} fields Array of fields like versions, time, version
63
+ * @param {string} currentVersion
64
+ * @returns {Promise} Promised result
50
65
  */
51
- function view(packageName, field, currentVersion) {
66
+ function viewMany(packageName, fields, currentVersion) {
52
67
  if (currentVersion && (!semver.validRange(currentVersion) || versionUtil.isWildCard(currentVersion))) {
53
- return Promise.resolve();
68
+ return Promise.resolve({});
54
69
  }
55
70
 
56
- npmConfig['full-metadata'] = field === 'time';
71
+ npmConfig['full-metadata'] = _.includes(fields, 'time');
57
72
 
58
- return pacote.packument(packageName, npmConfig).then(result => {
59
- if (field.startsWith('dist-tags.')) {
60
- const [tagName, version] = field.split('.');
61
- if (result[tagName]) {
62
- return result[tagName][version];
73
+ return pacote.packument(packageName, npmConfig).then(result =>
74
+ fields.reduce((accum, field) => Object.assign(
75
+ accum,
76
+ {
77
+ [field]: field.startsWith('dist-tags.') ?
78
+ result.versions[_.get(result, field)] :
79
+ result[field]
63
80
  }
64
- } else if (field === 'versions') {
65
- return Object.keys(result[field]);
66
- } else {
67
- return result[field];
68
- }
69
- });
81
+ ), {})
82
+ );
70
83
  }
71
84
 
72
85
  /**
73
86
  * @param {Array} versions Array of all available versions
87
+ * @param {Boolean} pre Enabled prerelease?
74
88
  * @returns {Array} An array of versions with the release versions filtered out
75
89
  */
76
- function filterOutPrereleaseVersions(versions) {
77
- return _.filter(versions, _.negate(isPre));
90
+ function filterOutPrereleaseVersions(versions, pre) {
91
+ return _.filter(versions, version => pre || !isPre(version));
78
92
  }
79
93
 
80
94
  /**
81
- * @param version
82
- * @returns {boolean} True if the version is any kind of prerelease: alpha, beta, rc, pre
95
+ * @param {String} version
96
+ * @returns {boolean} True if the version is any kind of prerelease: alpha, beta, rc, pre
83
97
  */
84
98
  function isPre(version) {
85
99
  return versionUtil.getPrecision(version) === 'release';
86
100
  }
87
101
 
102
+ /**
103
+ * @param {{}} versions Object with all versions
104
+ * @param {String} enginesNode Package engines.node range
105
+ * @returns {Array} An array of versions which satisfies engines.node range
106
+ */
107
+ function doesSatisfyEnginesNode(versions, enginesNode) {
108
+ if (!enginesNode) {
109
+ return _.keys(versions);
110
+ }
111
+ const minVersion = _.get(semver.minVersion(enginesNode), 'version');
112
+ if (!minVersion) {
113
+ return _.keys(versions);
114
+ }
115
+ return _.keys(versions).filter(version => {
116
+ let versionEnginesNode = _.get(versions[version], 'engines.node');
117
+ return versionEnginesNode && semver.satisfies(minVersion, versionEnginesNode);
118
+ });
119
+ }
88
120
 
89
121
  /**
90
122
  * Spawn npm requires a different command on Windows.
@@ -159,22 +191,25 @@ module.exports = {
159
191
  /**
160
192
  * @param {string} packageName
161
193
  * @param {string} currentVersion
162
- * @param {boolean} pre
194
+ * @param {{}} options
163
195
  * @returns {Promise}
164
196
  */
165
- latest(packageName, currentVersion, pre) {
166
- return view(packageName, 'dist-tags.latest', currentVersion)
167
- .then(version => {
168
- // if latest is not a prerelease version, return it
169
- // if latest is a prerelease version and --pre is specified, return it
170
- if (!isPre(version) || pre) {
171
- return version;
197
+ latest(packageName, currentVersion, options) {
198
+ return viewOne(packageName, 'dist-tags.latest', currentVersion)
199
+ .then(latest => {
200
+ // if latest exists and latest is not a prerelease version, return it
201
+ // if latest exists and latest is a prerelease version and --pre is specified, return it
202
+ // if latest exists and latest not satisfies min version of engines.node
203
+ if (latest && (!isPre(latest.version) || options.pre) && doesSatisfyEnginesNode({[latest.version]: latest}, options.enginesNode).length) {
204
+ return latest.version;
172
205
  // if latest is a prerelease version and --pre is not specified, find the next
173
206
  // version that is not a prerelease
174
207
  } else {
175
- return view(packageName, 'versions', currentVersion)
176
- .then(filterOutPrereleaseVersions)
177
- .then(_.last);
208
+ return viewOne(packageName, 'versions', currentVersion)
209
+ .then(versions => {
210
+ versions = doesSatisfyEnginesNode(versions, options.enginesNode);
211
+ return _.last(filterOutPrereleaseVersions(versions, options.pre));
212
+ });
178
213
  }
179
214
  });
180
215
  },
@@ -182,55 +217,77 @@ module.exports = {
182
217
  /**
183
218
  * @param {string} packageName
184
219
  * @param {string} currentVersion
185
- * @param {boolean} pre
220
+ * @param {{}} options
186
221
  * @returns {Promise}
187
222
  */
188
- newest(packageName, currentVersion, pre) {
189
- return view(packageName, 'time', currentVersion)
190
- .then(_.keys)
191
- .then(_.partialRight(_.pullAll, ['modified', 'created']))
192
- .then(versions => {
193
- return _.last(pre ? versions : filterOutPrereleaseVersions(versions));
194
- });
223
+ newest(packageName, currentVersion, options) {
224
+ return viewMany(packageName, ['time', 'versions'], currentVersion)
225
+ .then(result => {
226
+ const versions = doesSatisfyEnginesNode(result.versions, options.enginesNode);
227
+ return _.keys(result.time).reduce((accum, key) =>
228
+ accum.concat(_.includes(TIME_FIELDS, key) || _.includes(versions, key) ? key : []), []
229
+ );
230
+ })
231
+ .then(_.partialRight(_.pullAll, TIME_FIELDS))
232
+ .then(versions =>
233
+ _.last(filterOutPrereleaseVersions(versions, options.pre))
234
+ );
195
235
  },
196
236
 
197
237
  /**
198
238
  * @param {string} packageName
199
239
  * @param {string} currentVersion
200
- * @param {boolean} pre
240
+ * @param {{}} options
201
241
  * @returns {Promise}
202
242
  */
203
- greatest(packageName, currentVersion, pre) {
204
- return view(packageName, 'versions', currentVersion)
205
- .then(versions => {
206
- return _.last(pre ? versions : filterOutPrereleaseVersions(versions));
207
- });
243
+ greatest(packageName, currentVersion, options) {
244
+ return viewOne(packageName, 'versions', currentVersion)
245
+ .then(versions =>
246
+ _.last(filterOutPrereleaseVersions(
247
+ doesSatisfyEnginesNode(versions, options.enginesNode),
248
+ options.pre
249
+ ))
250
+ );
208
251
  },
209
252
 
210
253
  /**
211
254
  * @param {string} packageName
212
255
  * @param {string} currentVersion
213
- * @param {boolean} pre
256
+ * @param {{}} options
214
257
  * @returns {Promise}
215
258
  */
216
- greatestMajor(packageName, currentVersion, pre) {
217
- return view(packageName, 'versions', currentVersion).then(versions => {
218
- const resultVersions = pre ? versions : filterOutPrereleaseVersions(versions);
219
- return versionUtil.findGreatestByLevel(resultVersions, currentVersion, 'major');
220
- });
259
+ greatestMajor(packageName, currentVersion, options) {
260
+ return viewOne(packageName, 'versions', currentVersion)
261
+ .then(versions =>
262
+ versionUtil.findGreatestByLevel(
263
+ filterOutPrereleaseVersions(
264
+ doesSatisfyEnginesNode(versions, options.enginesNode),
265
+ options.pre
266
+ ),
267
+ currentVersion,
268
+ 'major'
269
+ )
270
+ );
221
271
  },
222
272
 
223
273
  /**
224
274
  * @param {string} packageName
225
275
  * @param {string} currentVersion
226
- * @param {boolean} pre
276
+ * @param {{}} options
227
277
  * @returns {Promise}
228
278
  */
229
- greatestMinor(packageName, currentVersion, pre) {
230
- return view(packageName, 'versions', currentVersion).then(versions => {
231
- const resultVersions = pre ? versions : filterOutPrereleaseVersions(versions);
232
- return versionUtil.findGreatestByLevel(resultVersions, currentVersion, 'minor');
233
- });
279
+ greatestMinor(packageName, currentVersion, options) {
280
+ return viewOne(packageName, 'versions', currentVersion)
281
+ .then(versions =>
282
+ versionUtil.findGreatestByLevel(
283
+ filterOutPrereleaseVersions(
284
+ doesSatisfyEnginesNode(versions, options.enginesNode),
285
+ options.pre
286
+ ),
287
+ currentVersion,
288
+ 'minor'
289
+ )
290
+ );
234
291
  },
235
292
 
236
293
  defaultPrefix
@@ -253,7 +253,8 @@ function upgradePackageDefinitions(currentDependencies, options) {
253
253
  pre: options.pre,
254
254
  packageManager: options.packageManager,
255
255
  json: options.json,
256
- loglevel: options.loglevel
256
+ loglevel: options.loglevel,
257
+ enginesNode: options.enginesNode
257
258
  }).then(latestVersions => {
258
259
 
259
260
  const upgradedDependencies = upgradeDependencies(currentDependencies, latestVersions, {
@@ -422,7 +423,7 @@ function queryVersions(packageMap, options = {}) {
422
423
  * @returns {Promise}
423
424
  */
424
425
  function getPackageVersionProtected(dep) {
425
- return getPackageVersion(dep, packageMap[dep], options.pre).catch(err => {
426
+ return getPackageVersion(dep, packageMap[dep], options).catch(err => {
426
427
  if (err && (err.message || err).toString().match(/E404|ENOTFOUND|404 Not Found/i)) {
427
428
  return null;
428
429
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "npm-check-updates",
3
- "version": "3.1.25",
3
+ "version": "3.2.2",
4
4
  "author": "Tomas Junnonen <tomas1@gmail.com>",
5
5
  "license": "Apache-2.0",
6
6
  "contributors": [
@@ -69,11 +69,11 @@
69
69
  "chai": "^4.2.0",
70
70
  "chai-as-promised": "^7.1.1",
71
71
  "chai-string": "^1.5.0",
72
- "chokidar-cli": "^2.0.0",
73
- "eslint": "^6.5.1",
74
- "mocha": "^6.2.1",
72
+ "chokidar-cli": "^2.1.0",
73
+ "eslint": "^6.6.0",
74
+ "mocha": "^6.2.2",
75
75
  "should": "^13.2.3",
76
- "snyk": "^1.231.0",
76
+ "snyk": "^1.239.5",
77
77
  "tmp": "0.1.0"
78
78
  },
79
79
  "files": [