• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1const { relative } = require('path')
2
3const explainNode = (node, depth, chalk) =>
4  printNode(node, chalk) +
5  explainDependents(node, depth, chalk) +
6  explainLinksIn(node, depth, chalk)
7
8const colorType = (type, chalk) => {
9  const { red, yellow, cyan, magenta, blue, green, gray } = chalk
10  const style = type === 'extraneous' ? red
11    : type === 'dev' ? yellow
12    : type === 'optional' ? cyan
13    : type === 'peer' ? magenta
14    : type === 'bundled' ? blue
15    : type === 'workspace' ? green
16    : type === 'overridden' ? gray
17    : /* istanbul ignore next */ s => s
18  return style(type)
19}
20
21const printNode = (node, chalk) => {
22  const {
23    name,
24    version,
25    location,
26    extraneous,
27    dev,
28    optional,
29    peer,
30    bundled,
31    isWorkspace,
32    overridden,
33  } = node
34  const { bold, dim, green } = chalk
35  const extra = []
36  if (extraneous) {
37    extra.push(' ' + bold(colorType('extraneous', chalk)))
38  }
39
40  if (dev) {
41    extra.push(' ' + bold(colorType('dev', chalk)))
42  }
43
44  if (optional) {
45    extra.push(' ' + bold(colorType('optional', chalk)))
46  }
47
48  if (peer) {
49    extra.push(' ' + bold(colorType('peer', chalk)))
50  }
51
52  if (bundled) {
53    extra.push(' ' + bold(colorType('bundled', chalk)))
54  }
55
56  if (overridden) {
57    extra.push(' ' + bold(colorType('overridden', chalk)))
58  }
59
60  const pkgid = isWorkspace
61    ? green(`${name}@${version}`)
62    : `${bold(name)}@${bold(version)}`
63
64  return `${pkgid}${extra.join('')}` +
65    (location ? dim(`\n${location}`) : '')
66}
67
68const explainLinksIn = ({ linksIn }, depth, chalk) => {
69  if (!linksIn || !linksIn.length || depth <= 0) {
70    return ''
71  }
72
73  const messages = linksIn.map(link => explainNode(link, depth - 1, chalk))
74  const str = '\n' + messages.join('\n')
75  return str.split('\n').join('\n  ')
76}
77
78const explainDependents = ({ name, dependents }, depth, chalk) => {
79  if (!dependents || !dependents.length || depth <= 0) {
80    return ''
81  }
82
83  const max = Math.ceil(depth / 2)
84  const messages = dependents.slice(0, max)
85    .map(edge => explainEdge(edge, depth, chalk))
86
87  // show just the names of the first 5 deps that overflowed the list
88  if (dependents.length > max) {
89    let len = 0
90    const maxLen = 50
91    const showNames = []
92    for (let i = max; i < dependents.length; i++) {
93      const { from: { name: depName = 'the root project' } } = dependents[i]
94      len += depName.length
95      if (len >= maxLen && i < dependents.length - 1) {
96        showNames.push('...')
97        break
98      }
99      showNames.push(depName)
100    }
101    const show = `(${showNames.join(', ')})`
102    messages.push(`${dependents.length - max} more ${show}`)
103  }
104
105  const str = '\n' + messages.join('\n')
106  return str.split('\n').join('\n  ')
107}
108
109const explainEdge = ({ name, type, bundled, from, spec, rawSpec, overridden }, depth, chalk) => {
110  const { bold } = chalk
111  let dep = type === 'workspace'
112    ? bold(relative(from.location, spec.slice('file:'.length)))
113    : `${bold(name)}@"${bold(spec)}"`
114  if (overridden) {
115    dep = `${colorType('overridden', chalk)} ${dep} (was "${rawSpec}")`
116  }
117
118  const fromMsg = ` from ${explainFrom(from, depth, chalk)}`
119
120  return (type === 'prod' ? '' : `${colorType(type, chalk)} `) +
121    (bundled ? `${colorType('bundled', chalk)} ` : '') +
122    `${dep}${fromMsg}`
123}
124
125const explainFrom = (from, depth, chalk) => {
126  if (!from.name && !from.version) {
127    return 'the root project'
128  }
129
130  return printNode(from, chalk) +
131    explainDependents(from, depth - 1, chalk) +
132    explainLinksIn(from, depth - 1, chalk)
133}
134
135module.exports = { explainNode, printNode, explainEdge }
136