Removed the Requirement to Install Python and NodeJS (Now Bundled with Borealis)
This commit is contained in:
282
Dependencies/NodeJS/node_modules/npm/lib/commands/outdated.js
generated
vendored
Normal file
282
Dependencies/NodeJS/node_modules/npm/lib/commands/outdated.js
generated
vendored
Normal file
@ -0,0 +1,282 @@
|
||||
const { resolve } = require('node:path')
|
||||
const { stripVTControlCharacters } = require('node:util')
|
||||
const pacote = require('pacote')
|
||||
const table = require('text-table')
|
||||
const npa = require('npm-package-arg')
|
||||
const pickManifest = require('npm-pick-manifest')
|
||||
const { output } = require('proc-log')
|
||||
const localeCompare = require('@isaacs/string-locale-compare')('en')
|
||||
const ArboristWorkspaceCmd = require('../arborist-cmd.js')
|
||||
|
||||
const safeNpa = (spec) => {
|
||||
try {
|
||||
return npa(spec)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// This string is load bearing and is shared with Arborist
|
||||
const MISSING = 'MISSING'
|
||||
|
||||
class Outdated extends ArboristWorkspaceCmd {
|
||||
static description = 'Check for outdated packages'
|
||||
static name = 'outdated'
|
||||
static usage = ['[<package-spec> ...]']
|
||||
static params = [
|
||||
'all',
|
||||
'json',
|
||||
'long',
|
||||
'parseable',
|
||||
'global',
|
||||
'workspace',
|
||||
]
|
||||
|
||||
#tree
|
||||
#list = []
|
||||
#edges = new Set()
|
||||
#filterSet
|
||||
|
||||
async exec (args) {
|
||||
const Arborist = require('@npmcli/arborist')
|
||||
const arb = new Arborist({
|
||||
...this.npm.flatOptions,
|
||||
path: this.npm.global ? resolve(this.npm.globalDir, '..') : this.npm.prefix,
|
||||
})
|
||||
this.#tree = await arb.loadActual()
|
||||
|
||||
if (this.workspaceNames?.length) {
|
||||
this.#filterSet = arb.workspaceDependencySet(
|
||||
this.#tree,
|
||||
this.workspaceNames,
|
||||
this.npm.flatOptions.includeWorkspaceRoot
|
||||
)
|
||||
} else if (!this.npm.flatOptions.workspacesEnabled) {
|
||||
this.#filterSet = arb.excludeWorkspacesDependencySet(this.#tree)
|
||||
}
|
||||
|
||||
if (args.length) {
|
||||
for (const arg of args) {
|
||||
// specific deps
|
||||
this.#getEdges(this.#tree.inventory.query('name', arg), 'edgesIn')
|
||||
}
|
||||
} else {
|
||||
if (this.npm.config.get('all')) {
|
||||
// all deps in tree
|
||||
this.#getEdges(this.#tree.inventory.values(), 'edgesOut')
|
||||
}
|
||||
// top-level deps
|
||||
this.#getEdges()
|
||||
}
|
||||
|
||||
await Promise.all([...this.#edges].map((e) => this.#getOutdatedInfo(e)))
|
||||
|
||||
// sorts list alphabetically by name and then dependent
|
||||
const outdated = this.#list
|
||||
.sort((a, b) => localeCompare(a.name, b.name) || localeCompare(a.dependent, b.dependent))
|
||||
|
||||
if (outdated.length) {
|
||||
process.exitCode = 1
|
||||
}
|
||||
|
||||
if (this.npm.config.get('json')) {
|
||||
output.buffer(this.#json(outdated))
|
||||
return
|
||||
}
|
||||
|
||||
const res = this.npm.config.get('parseable')
|
||||
? this.#parseable(outdated)
|
||||
: this.#pretty(outdated)
|
||||
|
||||
if (res) {
|
||||
output.standard(res)
|
||||
}
|
||||
}
|
||||
|
||||
#getEdges (nodes, type) {
|
||||
// when no nodes are provided then it should only read direct deps
|
||||
// from the root node and its workspaces direct dependencies
|
||||
if (!nodes) {
|
||||
this.#getEdgesOut(this.#tree)
|
||||
this.#getWorkspacesEdges()
|
||||
return
|
||||
}
|
||||
|
||||
for (const node of nodes) {
|
||||
if (type === 'edgesOut') {
|
||||
this.#getEdgesOut(node)
|
||||
} else {
|
||||
this.#getEdgesIn(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#getEdgesIn (node) {
|
||||
for (const edge of node.edgesIn) {
|
||||
this.#trackEdge(edge)
|
||||
}
|
||||
}
|
||||
|
||||
#getEdgesOut (node) {
|
||||
// TODO: normalize usage of edges and avoid looping through nodes here
|
||||
const edges = this.npm.global ? node.children.values() : node.edgesOut.values()
|
||||
for (const edge of edges) {
|
||||
this.#trackEdge(edge)
|
||||
}
|
||||
}
|
||||
|
||||
#trackEdge (edge) {
|
||||
if (edge.from && this.#filterSet?.size > 0 && !this.#filterSet.has(edge.from.target)) {
|
||||
return
|
||||
}
|
||||
this.#edges.add(edge)
|
||||
}
|
||||
|
||||
#getWorkspacesEdges () {
|
||||
if (this.npm.global) {
|
||||
return
|
||||
}
|
||||
|
||||
for (const edge of this.#tree.edgesOut.values()) {
|
||||
if (edge?.to?.target?.isWorkspace) {
|
||||
this.#getEdgesOut(edge.to.target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async #getPackument (spec) {
|
||||
return pacote.packument(spec, {
|
||||
...this.npm.flatOptions,
|
||||
fullMetadata: this.npm.config.get('long'),
|
||||
preferOnline: true,
|
||||
})
|
||||
}
|
||||
|
||||
async #getOutdatedInfo (edge) {
|
||||
const alias = safeNpa(edge.spec)?.subSpec
|
||||
const spec = npa(alias ? alias.name : edge.name)
|
||||
const node = edge.to || edge
|
||||
const { path, location, package: { version: current } = {} } = node
|
||||
|
||||
const type = edge.optional ? 'optionalDependencies'
|
||||
: edge.peer ? 'peerDependencies'
|
||||
: edge.dev ? 'devDependencies'
|
||||
: 'dependencies'
|
||||
|
||||
for (const omitType of this.npm.flatOptions.omit) {
|
||||
if (node[omitType]) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// deps different from prod not currently
|
||||
// on disk are not included in the output
|
||||
if (edge.error === MISSING && type !== 'dependencies') {
|
||||
return
|
||||
}
|
||||
|
||||
// if it's not a range, version, or tag, skip it
|
||||
if (!safeNpa(`${edge.name}@${edge.spec}`)?.registry) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
const packument = await this.#getPackument(spec)
|
||||
const expected = alias ? alias.fetchSpec : edge.spec
|
||||
const wanted = pickManifest(packument, expected, this.npm.flatOptions)
|
||||
const latest = pickManifest(packument, '*', this.npm.flatOptions)
|
||||
if (!current || current !== wanted.version || wanted.version !== latest.version) {
|
||||
this.#list.push({
|
||||
name: alias ? edge.spec.replace('npm', edge.name) : edge.name,
|
||||
path,
|
||||
type,
|
||||
current,
|
||||
location,
|
||||
wanted: wanted.version,
|
||||
latest: latest.version,
|
||||
workspaceDependent: edge.from?.isWorkspace ? edge.from.pkgid : null,
|
||||
dependent: edge.from?.name ?? 'global',
|
||||
homepage: packument.homepage,
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
// silently catch and ignore ETARGET, E403 &
|
||||
// E404 errors, deps are just skipped
|
||||
if (!['ETARGET', 'E404', 'E404'].includes(err.code)) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// formatting functions
|
||||
|
||||
#pretty (list) {
|
||||
if (!list.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const long = this.npm.config.get('long')
|
||||
const { bold, yellow, red, cyan, blue } = this.npm.chalk
|
||||
|
||||
return table([
|
||||
[
|
||||
'Package',
|
||||
'Current',
|
||||
'Wanted',
|
||||
'Latest',
|
||||
'Location',
|
||||
'Depended by',
|
||||
...long ? ['Package Type', 'Homepage'] : [],
|
||||
].map(h => bold.underline(h)),
|
||||
...list.map((d) => [
|
||||
d.current === d.wanted ? yellow(d.name) : red(d.name),
|
||||
d.current ?? 'MISSING',
|
||||
cyan(d.wanted),
|
||||
blue(d.latest),
|
||||
d.location ?? '-',
|
||||
d.workspaceDependent ? blue(d.workspaceDependent) : d.dependent,
|
||||
...long ? [d.type, blue(d.homepage ?? '')] : [],
|
||||
]),
|
||||
], {
|
||||
align: ['l', 'r', 'r', 'r', 'l'],
|
||||
stringLength: s => stripVTControlCharacters(s).length,
|
||||
})
|
||||
}
|
||||
|
||||
// --parseable creates output like this:
|
||||
// <fullpath>:<name@wanted>:<name@installed>:<name@latest>:<dependedby>
|
||||
#parseable (list) {
|
||||
return list.map(d => [
|
||||
d.path,
|
||||
`${d.name}@${d.wanted}`,
|
||||
d.current ? `${d.name}@${d.current}` : 'MISSING',
|
||||
`${d.name}@${d.latest}`,
|
||||
d.dependent,
|
||||
...this.npm.config.get('long') ? [d.type, d.homepage] : [],
|
||||
].join(':')).join('\n')
|
||||
}
|
||||
|
||||
#json (list) {
|
||||
// TODO(BREAKING_CHANGE): this should just return an array. It's a list and
|
||||
// turing it into an object with keys is lossy since multiple items in the
|
||||
// list could have the same key. For now we hack that by only changing
|
||||
// top level values into arrays if they have multiple outdated items
|
||||
return list.reduce((acc, d) => {
|
||||
const dep = {
|
||||
current: d.current,
|
||||
wanted: d.wanted,
|
||||
latest: d.latest,
|
||||
dependent: d.dependent,
|
||||
location: d.path,
|
||||
...this.npm.config.get('long') ? { type: d.type, homepage: d.homepage } : {},
|
||||
}
|
||||
acc[d.name] = acc[d.name]
|
||||
// If this item alread has an outdated dep then we turn it into an array
|
||||
? (Array.isArray(acc[d.name]) ? acc[d.name] : [acc[d.name]]).concat(dep)
|
||||
: dep
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Outdated
|
Reference in New Issue
Block a user