diff --git a/lib/outdated.js b/lib/outdated.js index 660f1cd05704e..38cdb0aaf40c3 100644 --- a/lib/outdated.js +++ b/lib/outdated.js @@ -22,7 +22,7 @@ const usage = usageUtil('outdated', const completion = require('./utils/completion/none.js') function cmd (args, cb) { - outdated(args, cb) + outdated(args) .then(() => cb()) .catch(cb) } @@ -78,14 +78,12 @@ async function outdated (args) { } output(table(outTable, tableOpts)) } - - process.exitCode = outdated.length ? 1 : 0 } async function outdated_ (tree, deps, opts) { const list = [] - const edges = new Set() + const edges = new Set() function getEdges (nodes, type) { const getEdgesIn = (node) => { for (const edge of node.edgesIn) { @@ -94,8 +92,14 @@ async function outdated_ (tree, deps, opts) { } const getEdgesOut = (node) => { - for (const edge of node.edgesOut.values()) { - edges.add(edge) + if (opts.global) { + for (const child of node.children.values()) { + edges.add(child) + } + } else { + for (const edge of node.edgesOut.values()) { + edges.add(edge) + } } } @@ -107,30 +111,19 @@ async function outdated_ (tree, deps, opts) { } } - // packument fetching memoizing - const packuments = new Map() async function getPackument (spec) { - if (packuments.has(spec)) { - return packuments.get(spec) - } const packument = await pacote.packument(spec, { fullMetadata: npm.flatOptions.long, preferOnline: true }) - packuments.set(spec, packument) return packument } async function getOutdatedInfo (edge) { const spec = npa(edge.name) - const node = edge.to - const { - path, - location, - package: { - version: current - } - } = node || { package: {} } + const node = edge.to || edge + const { path, location } = node + const { version: current } = node.package || {} const type = edge.optional ? 'optionalDependencies' : edge.peer ? 'peerDependencies' @@ -160,7 +153,7 @@ async function outdated_ (tree, deps, opts) { location, wanted: wanted.version, latest: latest.version, - dependent: edge.from.name, + dependent: edge.from ? edge.from.name : 'global', homepage: packument.homepage }) } diff --git a/tap-snapshots/test-lib-outdated.js-TAP.test.js b/tap-snapshots/test-lib-outdated.js-TAP.test.js new file mode 100644 index 0000000000000..8ae5419a2738f --- /dev/null +++ b/tap-snapshots/test-lib-outdated.js-TAP.test.js @@ -0,0 +1,129 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/lib/outdated.js TAP should display outdated deps outdated --all > must match snapshot 1`] = ` + +Package Current Wanted Latest Location Depended by +alpha 1.0.0 1.0.1 1.0.1 node_modules/alpha outdated-should-display-outdated-deps +beta 1.0.0 1.0.1 1.0.1 node_modules/beta outdated-should-display-outdated-deps +gamma 1.0.1 1.0.1 2.0.0 node_modules/gamma outdated-should-display-outdated-deps +theta MISSING 1.0.1 1.0.1 - outdated-should-display-outdated-deps +` + +exports[`test/lib/outdated.js TAP should display outdated deps outdated --json --long > must match snapshot 1`] = ` + +{ + "alpha": { + "current": "1.0.0", + "wanted": "1.0.1", + "latest": "1.0.1", + "dependent": "outdated-should-display-outdated-deps", + "location": "/cli/test/lib/outdated-should-display-outdated-deps/node_modules/alpha", + "type": "dependencies" + }, + "beta": { + "current": "1.0.0", + "wanted": "1.0.1", + "latest": "1.0.1", + "dependent": "outdated-should-display-outdated-deps", + "location": "/cli/test/lib/outdated-should-display-outdated-deps/node_modules/beta", + "type": "peerDependencies" + }, + "gamma": { + "current": "1.0.1", + "wanted": "1.0.1", + "latest": "2.0.0", + "dependent": "outdated-should-display-outdated-deps", + "location": "/cli/test/lib/outdated-should-display-outdated-deps/node_modules/gamma", + "type": "dependencies" + }, + "theta": { + "wanted": "1.0.1", + "latest": "1.0.1", + "dependent": "outdated-should-display-outdated-deps", + "type": "dependencies" + } +} +` + +exports[`test/lib/outdated.js TAP should display outdated deps outdated --json > must match snapshot 1`] = ` + +{ + "alpha": { + "current": "1.0.0", + "wanted": "1.0.1", + "latest": "1.0.1", + "dependent": "outdated-should-display-outdated-deps", + "location": "/cli/test/lib/outdated-should-display-outdated-deps/node_modules/alpha" + }, + "beta": { + "current": "1.0.0", + "wanted": "1.0.1", + "latest": "1.0.1", + "dependent": "outdated-should-display-outdated-deps", + "location": "/cli/test/lib/outdated-should-display-outdated-deps/node_modules/beta" + }, + "gamma": { + "current": "1.0.1", + "wanted": "1.0.1", + "latest": "2.0.0", + "dependent": "outdated-should-display-outdated-deps", + "location": "/cli/test/lib/outdated-should-display-outdated-deps/node_modules/gamma" + }, + "theta": { + "wanted": "1.0.1", + "latest": "1.0.1", + "dependent": "outdated-should-display-outdated-deps" + } +} +` + +exports[`test/lib/outdated.js TAP should display outdated deps outdated --long > must match snapshot 1`] = ` + +Package Current Wanted Latest Location Depended by Package Type Homepage +alpha 1.0.0 1.0.1 1.0.1 node_modules/alpha outdated-should-display-outdated-deps dependencies +beta 1.0.0 1.0.1 1.0.1 node_modules/beta outdated-should-display-outdated-deps peerDependencies +gamma 1.0.1 1.0.1 2.0.0 node_modules/gamma outdated-should-display-outdated-deps dependencies +theta MISSING 1.0.1 1.0.1 - outdated-should-display-outdated-deps dependencies +` + +exports[`test/lib/outdated.js TAP should display outdated deps outdated --parseable --long > must match snapshot 1`] = ` + +/cli/test/lib/outdated-should-display-outdated-deps/node_modules/alpha:alpha@1.0.1:alpha@1.0.0:alpha@1.0.1:outdated-should-display-outdated-deps:dependencies: +/cli/test/lib/outdated-should-display-outdated-deps/node_modules/beta:beta@1.0.1:beta@1.0.0:beta@1.0.1:outdated-should-display-outdated-deps:peerDependencies: +/cli/test/lib/outdated-should-display-outdated-deps/node_modules/gamma:gamma@1.0.1:gamma@1.0.1:gamma@2.0.0:outdated-should-display-outdated-deps:dependencies: +:theta@1.0.1:MISSING:theta@1.0.1:outdated-should-display-outdated-deps:dependencies: +` + +exports[`test/lib/outdated.js TAP should display outdated deps outdated --parseable > must match snapshot 1`] = ` + +/cli/test/lib/outdated-should-display-outdated-deps/node_modules/alpha:alpha@1.0.1:alpha@1.0.0:alpha@1.0.1:outdated-should-display-outdated-deps +/cli/test/lib/outdated-should-display-outdated-deps/node_modules/beta:beta@1.0.1:beta@1.0.0:beta@1.0.1:outdated-should-display-outdated-deps +/cli/test/lib/outdated-should-display-outdated-deps/node_modules/gamma:gamma@1.0.1:gamma@1.0.1:gamma@2.0.0:outdated-should-display-outdated-deps +:theta@1.0.1:MISSING:theta@1.0.1:outdated-should-display-outdated-deps +` + +exports[`test/lib/outdated.js TAP should display outdated deps outdated > must match snapshot 1`] = ` + +Package Current Wanted Latest Location Depended by +alpha 1.0.0 1.0.1 1.0.1 node_modules/alpha outdated-should-display-outdated-deps +beta 1.0.0 1.0.1 1.0.1 node_modules/beta outdated-should-display-outdated-deps +gamma 1.0.1 1.0.1 2.0.0 node_modules/gamma outdated-should-display-outdated-deps +theta MISSING 1.0.1 1.0.1 - outdated-should-display-outdated-deps +` + +exports[`test/lib/outdated.js TAP should display outdated deps outdated global > must match snapshot 1`] = ` + +Package Current Wanted Latest Location Depended by +alpha 1.0.0 1.0.1 1.0.1 node_modules/alpha global +` + +exports[`test/lib/outdated.js TAP should display outdated deps outdated specific dep > must match snapshot 1`] = ` + +Package Current Wanted Latest Location Depended by +alpha 1.0.0 1.0.1 1.0.1 node_modules/alpha outdated-should-display-outdated-deps +` diff --git a/test/lib/outdated.js b/test/lib/outdated.js new file mode 100644 index 0000000000000..038ac926a77ee --- /dev/null +++ b/test/lib/outdated.js @@ -0,0 +1,336 @@ +const t = require('tap') +const requireInject = require('require-inject') + +const packument = spec => { + const mocks = { + 'alpha': { + 'name': 'alpha', + 'dist-tags': { + 'latest': '1.0.1' + }, + 'versions': { + '1.0.1': { + 'version': '1.0.1', + 'dependencies': { + 'gamma': '2.0.0' + } + } + } + }, + 'beta': { + 'name': 'beta', + 'dist-tags': { + 'latest': '1.0.1' + }, + 'versions': { + '1.0.1': { + 'version': '1.0.1' + } + } + }, + 'gamma': { + 'name': 'gamma', + 'dist-tags': { + 'latest': '2.0.0' + }, + 'versions': { + '1.0.1': { + 'version': '1.0.1', + }, + '2.0.0': { + 'version': '2.0.0' + } + } + }, + 'theta': { + 'name': 'theta', + 'dist-tags': { + 'latest': '1.0.1' + }, + 'versions': { + '1.0.1': { + 'version': '1.0.1' + } + } + } + } + + if (spec.name === 'eta') { + throw new Error('There is an error with this package.') + } + + if (!mocks[spec.name]) { + const err = new Error() + err.code = 'E404' + throw err + } + + return mocks[spec.name] +} + +let logs +const cleanLogs = (done) => { + logs = '' + const fn = (...args) => { + logs += '\n' + args.map(el => logs += el) + } + console.log = fn + done() +} + +const globalDir = t.testdir({ + 'node_modules': { + 'alpha': { + 'package.json': JSON.stringify({ + name: 'alpha', + version: '1.0.0' + }, null, 2) + } + } +}) + +const outdated = (dir, opts) => requireInject( + '../../lib/outdated.js', + { + '../../lib/npm.js': { + prefix: dir, + globalDir: `${globalDir}/node_modules`, + flatOptions: opts + }, + 'pacote': { + packument + } + } +) + +t.beforeEach(cleanLogs) +t.cleanSnapshot = s => { + return s.replace( + /(\/.*)(cli\/test\/lib\/)|(D:\\.*)(cli(\\\\|\\)test(\\\\|\\)lib(\\\\|\\))/g, + '/cli/test/lib/' + ).replace(/(\\\\|\\)/g, '/') +} + +t.test('should display outdated deps', t => { + const testDir = t.testdir({ + 'package.json': JSON.stringify({ + name: 'delta', + version: '1.0.0', + dependencies: { + alpha: '^1.0.0', + gamma: '^1.0.0', + theta: '^1.0.0' + }, + devDependencies: { + zeta: '^1.0.0' + }, + peerDependencies: { + beta: '^1.0.0' + } + }, null, 2), + 'node_modules': { + 'alpha': { + 'package.json': JSON.stringify({ + name: 'alpha', + version: '1.0.0', + dependencies: { + gamma: '2.0.0' + } + }, null, 2), + 'node_modules': { + 'gamma': { + 'package.json': JSON.stringify({ + name: 'gamma', + version: '2.0.0' + }, null, 2) + } + } + }, + 'beta': { + 'package.json': JSON.stringify({ + name: 'beta', + version: '1.0.0' + }, null, 2) + }, + 'gamma': { + 'package.json': JSON.stringify({ + name: 'gamma', + version: '1.0.1' + }, null, 2) + }, + 'zeta': { + 'package.json': JSON.stringify({ + name: 'zeta', + version: '1.0.0' + }, null, 2) + } + } + }) + + t.test('outdated global', t => { + outdated(null, { + global: true, + })([], () => { + t.matchSnapshot(logs) + t.end() + }) + }) + + t.test('outdated', t => { + outdated(testDir, { + global: false, + color: true + })([], () => { + t.matchSnapshot(logs) + t.end() + }) + }) + + t.test('outdated --long', t => { + outdated(testDir, { + global: false, + long: true, + })([], () => { + t.matchSnapshot(logs) + t.end() + }) + }) + + t.test('outdated --json', t => { + outdated(testDir, { + global: false, + json: true, + })([], () => { + t.matchSnapshot(logs) + t.end() + }) + }) + + t.test('outdated --json --long', t => { + outdated(testDir, { + global: false, + json: true, + long: true + })([], () => { + t.matchSnapshot(logs) + t.end() + }) + }) + + t.test('outdated --parseable', t => { + outdated(testDir, { + global: false, + parseable: true, + })([], () => { + t.matchSnapshot(logs) + t.end() + }) + }) + + t.test('outdated --parseable --long', t => { + outdated(testDir, { + global: false, + parseable: true, + long: true + })([], () => { + t.matchSnapshot(logs) + t.end() + }) + }) + + t.test('outdated --all', t => { + outdated(testDir, { + all: true, + })([], () => { + t.matchSnapshot(logs) + t.end() + }) + }) + + t.test('outdated specific dep', t => { + outdated(testDir, { + global: false, + })(['alpha'], () => { + t.matchSnapshot(logs) + t.end() + }) + }) + + t.end() + +}) + +t.test('should return if no outdated deps', t => { + const testDir = t.testdir({ + 'package.json': JSON.stringify({ + name: 'delta', + version: '1.0.0', + dependencies: { + alpha: '^1.0.0' + } + }, null, 2), + 'node_modules': { + 'alpha': { + 'package.json': JSON.stringify({ + name: 'alpha', + version: '1.0.1' + }, null, 2) + } + } + }) + + outdated(testDir, { + global: false, + })([], () => { + t.equals(logs.length, 0, 'no logs') + t.end() + }) +}) + +t.test('throws if error with a dep', t => { + const testDir = t.testdir({ + 'package.json': JSON.stringify({ + name: 'delta', + version: '1.0.0', + dependencies: { + eta: '^1.0.0' + } + }, null, 2), + 'node_modules': { + 'eta': { + 'package.json': JSON.stringify({ + name: 'eta', + version: '1.0.1' + }, null, 2) + } + } + }) + + outdated(testDir, { + global: false, + })([], (err) => { + t.equals(err.message, 'There is an error with this package.') + t.end() + }) +}) + +t.test('should skip missing non-prod deps', t => { + const testDir = t.testdir({ + 'package.json': JSON.stringify({ + name: 'delta', + version: '1.0.0', + devDependencies: { + beta: '^1.0.0' + } + }, null, 2), + 'node_modules': {} + }) + + outdated(testDir, { + global: false, + })([], () => { + //t.equals(logs.length, 0, 'no logs') + t.end() + }) +})