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