Skip to content

Commit

Permalink
Tighten up the output of dep node explanations
Browse files Browse the repository at this point in the history
Reduce visual noise, make the more important information more obvious.
  • Loading branch information
isaacs committed Sep 7, 2020
1 parent 58c2155 commit 7418970
Show file tree
Hide file tree
Showing 5 changed files with 1,227 additions and 878 deletions.
101 changes: 101 additions & 0 deletions lib/utils/explain-dep.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
const chalk = require('chalk')
const nocolor = {
bold: s => s,
dim: s => s,
red: s => s,
yellow: s => s,
cyan: s => s,
magenta: s => s
}

const explainNode = (node, depth, color) =>
printNode(node, color) +
explainDependents(node, depth, color)

const colorType = (type, color) => {
const { red, yellow, cyan, magenta } = color ? chalk : nocolor
const style = type === 'extraneous' ? red
: type === 'dev' ? yellow
: type === 'optional' ? cyan
: type === 'peer' ? magenta
: /* istanbul ignore next */ s => s
return style(type)
}

const printNode = (node, color) => {
const {
name,
version,
location,
extraneous,
dev,
optional,
peer
} = node
const { bold, dim } = color ? chalk : nocolor
const extra = []
if (extraneous) {
extra.push(' ' + bold(colorType('extraneous', color)))
}
if (dev) {
extra.push(' ' + bold(colorType('dev', color)))
}
if (optional) {
extra.push(' ' + bold(colorType('optional', color)))
}
if (peer) {
extra.push(' ' + bold(colorType('peer', color)))
}

return `${bold(name)}@${bold(version)}${extra.join('')}` +
(location ? dim(`\n${location}`) : '')
}

const explainDependents = ({ name, dependents }, depth, color) => {
if (!dependents || !dependents.length || depth <= 0) {
return ''
}

const max = Math.ceil(depth / 2)
const messages = dependents.slice(0, max)
.map(dep => explainDependency(name, dep, depth, color))

// show just the names of the first 5 deps that overflowed the list
if (dependents.length > max) {
let len = 0
const maxLen = 30
const showNames = []
for (let i = max; i < dependents.length; i++) {
const { from: { name } } = dependents[i]
len += name.length
if (len >= maxLen && i < dependents.length - 1) {
showNames.push('...')
break
}
showNames.push(name)
}
const show = `(${showNames.join(', ')})`
messages.push(`${dependents.length - max} more ${show}`)
}

const str = '\n' + messages.join('\n')
return str.split('\n').join('\n ')
}

const explainDependency = (name, { type, from, spec }, depth, color) => {
const { bold } = color ? chalk : nocolor
return (type === 'prod' ? '' : `${colorType(type, color)} `) +
`${bold(name)}@"${bold(spec)}" from ` +
explainFrom(from, depth, color)
}

const explainFrom = (from, depth, color) => {
if (!from.name && !from.version) {
return 'the root project'
}

return printNode(from, color) +
explainDependents(from, depth - 1, color)
}

module.exports = { explainNode, printNode }
66 changes: 6 additions & 60 deletions lib/utils/explain-eresolve.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,7 @@
const npm = require('../npm.js')
const { writeFileSync } = require('fs')
const { resolve } = require('path')

const chalk = require('chalk')
const nocolor = {
bold: s => s,
dim: s => s
}
const { explainNode, printNode } = require('./explain-dep.js')

// expl is an explanation object that comes from Arborist. It looks like:
// {
Expand Down Expand Up @@ -50,69 +45,20 @@ const explainEresolve = (expl, color, depth) => {
out.push('While resolving: ' + printNode(dep.whileInstalling, color))
}

out.push(explainNode('Found:', current, depth, color))
out.push('Found: ' + explainNode(current, depth, color))

out.push(explainNode('\nCould not add conflicting dependency:', dep, depth, color))
out.push('\nCould not add conflicting dependency: ' +
explainNode(dep, depth, color))

if (peerConflict) {
const heading = '\nConflicting peer dependency:'
const pc = explainNode(heading, peerConflict, depth, color)
out.push(pc)
const pc = explainNode(peerConflict, depth, color)
out.push(heading + ' ' + pc)
}

return out.join('\n')
}

const explainNode = (heading, node, depth, color) =>
`${heading} ${printNode(node, color)}` +
explainDependents(node, depth, color)

const printNode = ({ name, version, location }, color) => {
const { bold, dim } = color ? chalk : nocolor
return `${bold(name)}@${bold(version)}` +
(location ? dim(` at ${location}`) : '')
}

const explainDependents = ({ name, dependents }, depth, color) => {
if (!dependents || !dependents.length || depth <= 0) {
return ''
}

const max = Math.ceil(depth / 2)
const messages = dependents.slice(0, max)
.map(dep => explainDependency(name, dep, depth, color))

// show just the names of the first 5 deps that overflowed the list
if (dependents.length > max) {
const names = dependents.slice(max).map(d => d.from.name)
const showNames = names.slice(0, 5)
if (showNames.length < names.length) {
showNames.push('...')
}
const show = `(${showNames.join(', ')})`
messages.push(`${names.length} more ${show}`)
}

const str = '\nfor: ' + messages.join('\nand: ')
return str.split('\n').join('\n ')
}

const explainDependency = (name, { type, from, spec }, depth, color) => {
const { bold } = color ? chalk : nocolor
return `${type} dependency ` +
`${bold(name)}@"${bold(spec)}"\nfrom: ` +
explainFrom(from, depth, color)
}

const explainFrom = (from, depth, color) => {
if (!from.name && !from.version) {
return 'the root project'
}

return printNode(from, color) +
explainDependents(from, depth - 1, color)
}

// generate a full verbose report and tell the user how to fix it
const report = (expl, depth = 4) => {
const fullReport = resolve(npm.cache, 'eresolve-report.txt')
Expand Down
178 changes: 178 additions & 0 deletions tap-snapshots/test-lib-utils-explain-dep.js-TAP.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/* IMPORTANT
* This snapshot file is auto-generated, but designed for humans.
* It should be checked into source control and tracked carefully.
* Re-generate by setting TAP_SNAPSHOT=1 and running tests.
* Make sure to inspect the output below. Do not ignore changes!
*/
'use strict'
exports[`test/lib/utils/explain-dep.js TAP > ellipses test one 1`] = `
manydep@1.0.0
manydep@"1.0.0" from prod-dep@1.2.3
node_modules/prod-dep
prod-dep@"1.x" from the root project
6 more (optdep, extra-neos, deep-dev, peer, ...)
`

exports[`test/lib/utils/explain-dep.js TAP > ellipses test two 1`] = `
manydep@1.0.0
manydep@"1.0.0" from prod-dep@1.2.3
node_modules/prod-dep
prod-dep@"1.x" from the root project
5 more (optdep, extra-neos, deep-dev, peer, a package with a pretty long name)
`

exports[`test/lib/utils/explain-dep.js TAP deepDev > explain color deep 1`] = `
deep-dev@2.3.4 dev
node_modules/deep-dev
deep-dev@"2.x" from metadev@3.4.5
node_modules/dev/node_modules/metadev
metadev@"3.x" from topdev@4.5.6
node_modules/topdev
dev topdev@"4.x" from the root project
`

exports[`test/lib/utils/explain-dep.js TAP deepDev > explain nocolor shallow 1`] = `
deep-dev@2.3.4 dev
node_modules/deep-dev
deep-dev@"2.x" from metadev@3.4.5
node_modules/dev/node_modules/metadev
metadev@"3.x" from topdev@4.5.6
node_modules/topdev
`

exports[`test/lib/utils/explain-dep.js TAP deepDev > print color 1`] = `
deep-dev@2.3.4 dev
node_modules/deep-dev
`

exports[`test/lib/utils/explain-dep.js TAP deepDev > print nocolor 1`] = `
deep-dev@2.3.4 dev
node_modules/deep-dev
`

exports[`test/lib/utils/explain-dep.js TAP extraneous > explain color deep 1`] = `
extra-neos@1337.420.69-lol extraneous
node_modules/extra-neos
`

exports[`test/lib/utils/explain-dep.js TAP extraneous > explain nocolor shallow 1`] = `
extra-neos@1337.420.69-lol extraneous
node_modules/extra-neos
`

exports[`test/lib/utils/explain-dep.js TAP extraneous > print color 1`] = `
extra-neos@1337.420.69-lol extraneous
node_modules/extra-neos
`

exports[`test/lib/utils/explain-dep.js TAP extraneous > print nocolor 1`] = `
extra-neos@1337.420.69-lol extraneous
node_modules/extra-neos
`

exports[`test/lib/utils/explain-dep.js TAP manyDeps > explain color deep 1`] = `
manydep@1.0.0
manydep@"1.0.0" from prod-dep@1.2.3
node_modules/prod-dep
prod-dep@"1.x" from the root project
optional manydep@"1.x" from optdep@1.0.0 optional
node_modules/optdep
optdep optdep@"1.0.0" from the root project
manydep@"1.0.x" from extra-neos@1337.420.69-lol extraneous
node_modules/extra-neos
dev manydep@"*" from deep-dev@2.3.4 dev
node_modules/deep-dev
deep-dev@"2.x" from metadev@3.4.5
node_modules/dev/node_modules/metadev
metadev@"3.x" from topdev@4.5.6
node_modules/topdev
dev topdev@"4.x" from the root project
peer manydep@">1.0.0-beta <1.0.1" from peer@1.0.0 peer
node_modules/peer
peer peer@"1.0.0" from the root project
manydep@"1" from a package with a pretty long name@1.2.3
manydep@"1" from another package with a pretty long name@1.2.3
manydep@"1" from yet another a package with a pretty long name@1.2.3
`

exports[`test/lib/utils/explain-dep.js TAP manyDeps > explain nocolor shallow 1`] = `
manydep@1.0.0
manydep@"1.0.0" from prod-dep@1.2.3
node_modules/prod-dep
prod-dep@"1.x" from the root project
7 more (optdep, extra-neos, deep-dev, peer, ...)
`

exports[`test/lib/utils/explain-dep.js TAP manyDeps > print color 1`] = `
manydep@1.0.0
`

exports[`test/lib/utils/explain-dep.js TAP manyDeps > print nocolor 1`] = `
manydep@1.0.0
`

exports[`test/lib/utils/explain-dep.js TAP optional > explain color deep 1`] = `
optdep@1.0.0 optional
node_modules/optdep
optdep optdep@"1.0.0" from the root project
`

exports[`test/lib/utils/explain-dep.js TAP optional > explain nocolor shallow 1`] = `
optdep@1.0.0 optional
node_modules/optdep
optdep optdep@"1.0.0" from the root project
`

exports[`test/lib/utils/explain-dep.js TAP optional > print color 1`] = `
optdep@1.0.0 optional
node_modules/optdep
`

exports[`test/lib/utils/explain-dep.js TAP optional > print nocolor 1`] = `
optdep@1.0.0 optional
node_modules/optdep
`

exports[`test/lib/utils/explain-dep.js TAP peer > explain color deep 1`] = `
peer@1.0.0 peer
node_modules/peer
peer peer@"1.0.0" from the root project
`

exports[`test/lib/utils/explain-dep.js TAP peer > explain nocolor shallow 1`] = `
peer@1.0.0 peer
node_modules/peer
peer peer@"1.0.0" from the root project
`

exports[`test/lib/utils/explain-dep.js TAP peer > print color 1`] = `
peer@1.0.0 peer
node_modules/peer
`

exports[`test/lib/utils/explain-dep.js TAP peer > print nocolor 1`] = `
peer@1.0.0 peer
node_modules/peer
`

exports[`test/lib/utils/explain-dep.js TAP prodDep > explain color deep 1`] = `
prod-dep@1.2.3
node_modules/prod-dep
prod-dep@"1.x" from the root project
`

exports[`test/lib/utils/explain-dep.js TAP prodDep > explain nocolor shallow 1`] = `
prod-dep@1.2.3
node_modules/prod-dep
prod-dep@"1.x" from the root project
`

exports[`test/lib/utils/explain-dep.js TAP prodDep > print color 1`] = `
prod-dep@1.2.3
node_modules/prod-dep
`

exports[`test/lib/utils/explain-dep.js TAP prodDep > print nocolor 1`] = `
prod-dep@1.2.3
node_modules/prod-dep
`
Loading

0 comments on commit 7418970

Please sign in to comment.