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