diff --git a/docs/content/cli-commands/npm-outdated.md b/docs/content/cli-commands/npm-outdated.md index 3dae28c465f4e..75e122973ce7a 100644 --- a/docs/content/cli-commands/npm-outdated.md +++ b/docs/content/cli-commands/npm-outdated.md @@ -1,5 +1,5 @@ --- -section: cli-commands +section: cli-commands title: npm-outdated description: Check for outdated packages --- @@ -116,6 +116,14 @@ project. Max depth for checking dependency tree. +#### outdated-level + +* Default: 'latest' +* Type: 'latest', 'wanted', 'none' + +The minimum level of outdated dependency to exit with a non-zero +exit code. + ### See Also * [npm update](/cli-commands/update) diff --git a/lib/config/defaults.js b/lib/config/defaults.js index e07da3aaf97f4..9c862620c5572 100644 --- a/lib/config/defaults.js +++ b/lib/config/defaults.js @@ -189,6 +189,7 @@ Object.defineProperty(exports, 'defaults', {get: function () { only: null, optional: true, otp: null, + 'outdated-level': 'latest', 'package-lock': true, 'package-lock-only': false, parseable: false, @@ -331,8 +332,9 @@ exports.types = { 'onload-script': [null, String], only: [null, 'dev', 'development', 'prod', 'production'], optional: Boolean, - 'package-lock': Boolean, otp: [null, String], + 'outdated-level': ['none', 'wanted', 'latest'], + 'package-lock': Boolean, 'package-lock-only': Boolean, parseable: Boolean, 'prefer-offline': Boolean, diff --git a/lib/outdated.js b/lib/outdated.js index 5b84ae35587c8..ecc41dc0ab60a 100644 --- a/lib/outdated.js +++ b/lib/outdated.js @@ -54,6 +54,7 @@ const OutdatedConfig = figgyPudding({ global: {}, json: {}, only: {}, + 'outdated-level': {}, parseable: {}, prod: 'production', production: {}, @@ -132,7 +133,12 @@ function outdated (args, silent, cb) { } output(table(outTable, tableOpts)) } - process.exitCode = list.length ? 1 : 0 + const levels = ['none', 'wanted', 'latest'] + const minLevel = levels.indexOf(opts['outdated-level']) + const outdatedCount = list.reduce((count, dependency) => { + return semver.lt(dependency[2], dependency[2 + minLevel]) ? count + 1 : count + }, 0) + process.exitCode = outdatedCount ? 1 : 0 cb(null, list.map(function (item) { return [item[0].parent.path].concat(item.slice(1, 7)) })) }) })) diff --git a/test/tap/outdated-level-none.js b/test/tap/outdated-level-none.js new file mode 100644 index 0000000000000..7c8b508131fc9 --- /dev/null +++ b/test/tap/outdated-level-none.js @@ -0,0 +1,89 @@ +var fs = require('graceful-fs') +var path = require('path') + +var mr = require('npm-registry-mock') +var test = require('tap').test + +var npm = require('../../lib/npm') +var common = require('../common-tap') + +var pkg = common.pkg + +var json = { + name: 'outdated-level', + version: '1.2.3', + dependencies: { + request: '^0.9.5' + } +} + +test('setup', function (t) { + fs.writeFileSync( + path.join(pkg, 'package.json'), + JSON.stringify(json, null, 2) + ) + process.chdir(pkg) + + t.end() +}) + +var expected = { + latest: [ + pkg, + 'request', + '0.9.5', + '0.9.5', + '2.27.0', + '^0.9.5', + null + ], + wanted: [ + pkg, + 'request', + '0.9.0', + '0.9.5', + '2.27.0', + '^0.9.0', + null + ] +} + +test('outdated level none', function (t) { + mr({ port: common.port }, function (er, s) { + npm.load( + { + 'outdated-level': 'none', + loglevel: 'silent', + registry: common.registry + }, + function () { + npm.install('request@0.9.5', function (er) { + if (er) throw new Error(er) + npm.outdated(function (err, d) { + if (err) { + throw err + } + t.is(process.exitCode, 0, 'exit code set to 0') + // process.exitCode = 0 + t.deepEqual(d[0], expected.latest) + t.equal(d.length, 1) + npm.install('request@0.9.0', function (er) { + if (er) throw new Error(er) + npm.outdated(function (err, d) { + if (err) { + throw err + } + t.is(process.exitCode, 0, 'exit code set to 0') + // process.exitCode = 0 + t.deepEqual(d[0], expected.wanted) + t.equal(d.length, 1) + s.close() + t.end() + }) + }) + }) + }) + } + ) + }) +}) diff --git a/test/tap/outdated-level-wanted.js b/test/tap/outdated-level-wanted.js new file mode 100644 index 0000000000000..4cdec37ff3fba --- /dev/null +++ b/test/tap/outdated-level-wanted.js @@ -0,0 +1,89 @@ +var fs = require('graceful-fs') +var path = require('path') + +var mr = require('npm-registry-mock') +var test = require('tap').test + +var npm = require('../../lib/npm') +var common = require('../common-tap') + +var pkg = common.pkg + +var json = { + name: 'outdated-level', + version: '1.2.3', + dependencies: { + request: '^0.9.5' + } +} + +var expected = { + latest: [ + pkg, + 'request', + '0.9.5', + '0.9.5', + '2.27.0', + '^0.9.5', + null + ], + wanted: [ + pkg, + 'request', + '0.9.0', + '0.9.5', + '2.27.0', + '^0.9.0', + null + ] +} + +test('setup', function (t) { + fs.writeFileSync( + path.join(pkg, 'package.json'), + JSON.stringify(json, null, 2) + ) + process.chdir(pkg) + + t.end() +}) + +test('outdated level wanted', function (t) { + mr({ port: common.port }, function (er, s) { + npm.load( + { + 'outdated-level': 'wanted', + loglevel: 'silent', + registry: common.registry + }, + function () { + npm.install('request@0.9.5', function (er) { + if (er) throw new Error(er) + npm.outdated(function (err, d) { + if (err) { + throw err + } + t.is(process.exitCode, 0, 'exit code set to 0') + // process.exitCode = 0 + t.deepEqual(d[0], expected.latest) + t.equal(d.length, 1) + npm.install('request@0.9.0', function (er) { + if (er) throw new Error(er) + npm.outdated(function (err, d) { + if (err) { + throw err + } + t.is(process.exitCode, 1, 'exit code set to 1') + process.exitCode = 0 + t.deepEqual(d[0], expected.wanted) + t.equal(d.length, 1) + s.close() + t.end() + }) + }) + }) + }) + } + ) + }) +})