forked from npm/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexplain.js
112 lines (96 loc) · 3.12 KB
/
explain.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
const { explainNode } = require('./utils/explain-dep.js')
const completion = require('./utils/completion/installed-deep.js')
const Arborist = require('@npmcli/arborist')
const npa = require('npm-package-arg')
const semver = require('semver')
const { relative, resolve } = require('path')
const validName = require('validate-npm-package-name')
const BaseCommand = require('./base-command.js')
class Explain extends BaseCommand {
static get description () {
return 'Explain installed packages'
}
/* istanbul ignore next - see test/lib/load-all-commands.js */
static get name () {
return 'explain'
}
/* istanbul ignore next - see test/lib/load-all-commands.js */
static get usage () {
return ['<folder | specifier>']
}
/* istanbul ignore next - see test/lib/load-all-commands.js */
async completion (opts) {
return completion(this.npm, opts)
}
exec (args, cb) {
this.explain(args).then(() => cb()).catch(cb)
}
async explain (args) {
if (!args.length)
throw this.usage
const arb = new Arborist({ path: this.npm.prefix, ...this.npm.flatOptions })
const tree = await arb.loadActual()
const nodes = new Set()
for (const arg of args) {
for (const node of this.getNodes(tree, arg))
nodes.add(node)
}
if (nodes.size === 0)
throw `No dependencies found matching ${args.join(', ')}`
const expls = []
for (const node of nodes) {
const { extraneous, dev, optional, devOptional, peer, inBundle } = node
const expl = node.explain()
if (extraneous)
expl.extraneous = true
else {
expl.dev = dev
expl.optional = optional
expl.devOptional = devOptional
expl.peer = peer
expl.bundled = inBundle
}
expls.push(expl)
}
if (this.npm.flatOptions.json)
this.npm.output(JSON.stringify(expls, null, 2))
else {
this.npm.output(expls.map(expl => {
return explainNode(expl, Infinity, this.npm.color)
}).join('\n\n'))
}
}
getNodes (tree, arg) {
// if it's just a name, return packages by that name
const { validForOldPackages: valid } = validName(arg)
if (valid)
return tree.inventory.query('name', arg)
// if it's a location, get that node
const maybeLoc = arg.replace(/\\/g, '/').replace(/\/+$/, '')
const nodeByLoc = tree.inventory.get(maybeLoc)
if (nodeByLoc)
return [nodeByLoc]
// maybe a path to a node_modules folder
const maybePath = relative(this.npm.prefix, resolve(maybeLoc))
.replace(/\\/g, '/').replace(/\/+$/, '')
const nodeByPath = tree.inventory.get(maybePath)
if (nodeByPath)
return [nodeByPath]
// otherwise, try to select all matching nodes
try {
return this.getNodesByVersion(tree, arg)
} catch (er) {
return []
}
}
getNodesByVersion (tree, arg) {
const spec = npa(arg, this.npm.prefix)
if (spec.type !== 'version' && spec.type !== 'range')
return []
return tree.inventory.filter(node => {
return node.package.name === spec.name &&
semver.satisfies(node.package.version, spec.rawSpec)
})
}
}
module.exports = Explain