diff options
Diffstat (limited to 'deps/npm/lib/outdated.js')
-rw-r--r-- | deps/npm/lib/outdated.js | 369 |
1 files changed, 184 insertions, 185 deletions
diff --git a/deps/npm/lib/outdated.js b/deps/npm/lib/outdated.js index c10f63a12e..fc6967faf6 100644 --- a/deps/npm/lib/outdated.js +++ b/deps/npm/lib/outdated.js @@ -9,112 +9,135 @@ const pickManifest = require('npm-pick-manifest') const Arborist = require('@npmcli/arborist') -const npm = require('./npm.js') const output = require('./utils/output.js') const usageUtil = require('./utils/usage.js') const ansiTrim = require('./utils/ansi-trim.js') -const usage = usageUtil('outdated', - 'npm outdated [[<@scope>/]<pkg> ...]' -) +class Outdated { + constructor (npm) { + this.npm = npm + } -function cmd (args, cb) { - outdated(args) - .then(() => cb()) - .catch(cb) -} + /* istanbul ignore next - see test/lib/load-all-commands.js */ + get usage () { + return usageUtil('outdated', + 'npm outdated [[<@scope>/]<pkg> ...]' + ) + } -async function outdated (args) { - const opts = npm.flatOptions - const global = path.resolve(npm.globalDir, '..') - const where = opts.global - ? global - : npm.prefix - - const arb = new Arborist({ - ...opts, - path: where, - }) - - const tree = await arb.loadActual() - const list = await outdated_(tree, args, opts) - - // sorts list alphabetically - const outdated = list.sort((a, b) => a.name.localeCompare(b.name)) - - // return if no outdated packages - if (outdated.length === 0 && !opts.json) - return - - // display results - if (opts.json) - output(makeJSON(outdated, opts)) - else if (opts.parseable) - output(makeParseable(outdated, opts)) - else { - const outList = outdated.map(x => makePretty(x, opts)) - const outHead = ['Package', - 'Current', - 'Wanted', - 'Latest', - 'Location', - 'Depended by', - ] - - if (opts.long) - outHead.push('Package Type', 'Homepage') - const outTable = [outHead].concat(outList) - - if (opts.color) - outTable[0] = outTable[0].map(heading => styles.underline(heading)) - - const tableOpts = { - align: ['l', 'r', 'r', 'r', 'l'], - stringLength: s => ansiTrim(s).length, - } - output(table(outTable, tableOpts)) + exec (args, cb) { + this.outdated(args).then(() => cb()).catch(cb) } -} -async function outdated_ (tree, deps, opts) { - const list = [] + async outdated (args) { + this.opts = this.npm.flatOptions + + const global = path.resolve(this.npm.globalDir, '..') + const where = this.opts.global + ? global + : this.npm.prefix + + const arb = new Arborist({ + ...this.opts, + path: where, + }) - const edges = new Set() - function getEdges (nodes, type) { - const getEdgesIn = (node) => { - for (const edge of node.edgesIn) - edges.add(edge) + this.edges = new Set() + this.list = [] + this.tree = await arb.loadActual() + + if (args.length !== 0) { + // specific deps + for (let i = 0; i < args.length; i++) { + const nodes = this.tree.inventory.query('name', args[i]) + this.getEdges(nodes, 'edgesIn') + } + } else { + if (this.opts.all) { + // all deps in tree + const nodes = this.tree.inventory.values() + this.getEdges(nodes, 'edgesOut') + } + // top-level deps + this.getEdges() } - const getEdgesOut = (node) => { - if (opts.global) { - for (const child of node.children.values()) - edges.add(child) - } else { - for (const edge of node.edgesOut.values()) - edges.add(edge) + await Promise.all(Array.from(this.edges).map((edge) => { + return this.getOutdatedInfo(edge) + })) + + // sorts list alphabetically + const outdated = this.list.sort((a, b) => a.name.localeCompare(b.name)) + + // return if no outdated packages + if (outdated.length === 0 && !this.opts.json) + return + + // display results + if (this.opts.json) + output(this.makeJSON(outdated)) + else if (this.opts.parseable) + output(this.makeParseable(outdated)) + else { + const outList = outdated.map(x => this.makePretty(x)) + const outHead = ['Package', + 'Current', + 'Wanted', + 'Latest', + 'Location', + 'Depended by', + ] + + if (this.opts.long) + outHead.push('Package Type', 'Homepage') + const outTable = [outHead].concat(outList) + + if (this.opts.color) + outTable[0] = outTable[0].map(heading => styles.underline(heading)) + + const tableOpts = { + align: ['l', 'r', 'r', 'r', 'l'], + stringLength: s => ansiTrim(s).length, } + output(table(outTable, tableOpts)) } + } + getEdges (nodes, type) { if (!nodes) - return getEdgesOut(tree) + return this.getEdgesOut(this.tree) for (const node of nodes) { type === 'edgesOut' - ? getEdgesOut(node) - : getEdgesIn(node) + ? this.getEdgesOut(node) + : this.getEdgesIn(node) + } + } + + getEdgesIn (node) { + for (const edge of node.edgesIn) + this.edges.add(edge) + } + + getEdgesOut (node) { + if (this.opts.global) { + for (const child of node.children.values()) + this.edges.add(child) + } else { + for (const edge of node.edgesOut.values()) + this.edges.add(edge) } } - async function getPackument (spec) { + async getPackument (spec) { const packument = await pacote.packument(spec, { - ...npm.flatOptions, - fullMetadata: npm.flatOptions.long, + ...this.npm.flatOptions, + fullMetadata: this.npm.flatOptions.long, preferOnline: true, }) return packument } - async function getOutdatedInfo (edge) { + async getOutdatedInfo (edge) { const spec = npa(edge.name) const node = edge.to || edge const { path, location } = node @@ -125,7 +148,7 @@ async function outdated_ (tree, deps, opts) { : edge.dev ? 'devDependencies' : 'dependencies' - for (const omitType of opts.omit || []) { + for (const omitType of this.opts.omit || []) { if (node[omitType]) return } @@ -136,7 +159,7 @@ async function outdated_ (tree, deps, opts) { return try { - const packument = await getPackument(spec) + const packument = await this.getPackument(spec) const expected = edge.spec // if it's not a range, version, or tag, skip it try { @@ -145,15 +168,15 @@ async function outdated_ (tree, deps, opts) { } catch (err) { return null } - const wanted = pickManifest(packument, expected, npm.flatOptions) - const latest = pickManifest(packument, '*', npm.flatOptions) + const wanted = pickManifest(packument, expected, this.npm.flatOptions) + const latest = pickManifest(packument, '*', this.npm.flatOptions) if ( !current || current !== wanted.version || wanted.version !== latest.version ) { - list.push({ + this.list.push({ name: edge.name, path, type, @@ -167,7 +190,7 @@ async function outdated_ (tree, deps, opts) { } } catch (err) { // silently catch and ignore ETARGET, E403 & - // E404 errors, deps are just skipped { + // E404 errors, deps are just skipped if (!( err.code === 'ETARGET' || err.code === 'E403' || @@ -177,113 +200,89 @@ async function outdated_ (tree, deps, opts) { } } - const p = [] - if (deps.length !== 0) { - // specific deps - for (let i = 0; i < deps.length; i++) { - const nodes = tree.inventory.query('name', deps[i]) - getEdges(nodes, 'edgesIn') - } - } else { - if (opts.all) { - // all deps in tree - const nodes = tree.inventory.values() - getEdges(nodes, 'edgesOut') - } - // top-level deps - getEdges() - } - - for (const edge of edges) - p.push(getOutdatedInfo(edge)) - - await Promise.all(p) - return list -} - -// formatting functions -function makePretty (dep, opts) { - const { - current = 'MISSING', - location = '-', - homepage = '', - name, - wanted, - latest, - type, - dependent, - } = dep - - const columns = [name, current, wanted, latest, location, dependent] - - if (opts.long) { - columns[6] = type - columns[7] = homepage - } - - if (opts.color) { - columns[0] = color[current === wanted ? 'yellow' : 'red'](columns[0]) // current - columns[2] = color.green(columns[2]) // wanted - columns[3] = color.magenta(columns[3]) // latest - } - - return columns -} - -// --parseable creates output like this: -// <fullpath>:<name@wanted>:<name@installed>:<name@latest>:<dependedby> -function makeParseable (list, opts) { - return list.map(dep => { + // formatting functions + makePretty (dep) { const { + current = 'MISSING', + location = '-', + homepage = '', name, - current, wanted, latest, - path, - dependent, type, - homepage, - } = dep - const out = [ - path, - name + '@' + wanted, - current ? (name + '@' + current) : 'MISSING', - name + '@' + latest, dependent, - ] - if (opts.long) - out.push(type, homepage) + } = dep - return out.join(':') - }).join(os.EOL) -} + const columns = [name, current, wanted, latest, location, dependent] -function makeJSON (list, opts) { - const out = {} - list.forEach(dep => { - const { - name, - current, - wanted, - latest, - path, - type, - dependent, - homepage, - } = dep - out[name] = { - current, - wanted, - latest, - dependent, - location: path, + if (this.opts.long) { + columns[6] = type + columns[7] = homepage } - if (opts.long) { - out[name].type = type - out[name].homepage = homepage + + if (this.opts.color) { + columns[0] = color[current === wanted ? 'yellow' : 'red'](columns[0]) // current + columns[2] = color.green(columns[2]) // wanted + columns[3] = color.magenta(columns[3]) // latest } - }) - return JSON.stringify(out, null, 2) -} -module.exports = Object.assign(cmd, { usage }) + return columns + } + + // --parseable creates output like this: + // <fullpath>:<name@wanted>:<name@installed>:<name@latest>:<dependedby> + makeParseable (list) { + return list.map(dep => { + const { + name, + current, + wanted, + latest, + path, + dependent, + type, + homepage, + } = dep + const out = [ + path, + name + '@' + wanted, + current ? (name + '@' + current) : 'MISSING', + name + '@' + latest, + dependent, + ] + if (this.opts.long) + out.push(type, homepage) + + return out.join(':') + }).join(os.EOL) + } + + makeJSON (list) { + const out = {} + list.forEach(dep => { + const { + name, + current, + wanted, + latest, + path, + type, + dependent, + homepage, + } = dep + out[name] = { + current, + wanted, + latest, + dependent, + location: path, + } + if (this.opts.long) { + out[name].type = type + out[name].homepage = homepage + } + }) + return JSON.stringify(out, null, 2) + } +} +module.exports = Outdated |