explain-eresolve.js 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. // this is called when an ERESOLVE error is caught in the exit-handler, or when there's a log.warn('eresolve', msg, explanation), to turn it into a human-intelligible explanation of what's wrong and how to fix.
  2. const { explainEdge, explainNode, printNode } = require('./explain-dep.js')
  3. // expl is an explanation object that comes from Arborist.
  4. // Depth is how far we want to want to descend into the object making a report.
  5. // The full report (ie, depth=Infinity) is always written to the cache folder at ${cache}/eresolve-report.txt along with full json.
  6. const explain = (expl, chalk, depth) => {
  7. const { edge, dep, current, peerConflict, currentEdge } = expl
  8. const out = []
  9. const whileInstalling = dep && dep.whileInstalling ||
  10. current && current.whileInstalling ||
  11. edge && edge.from && edge.from.whileInstalling
  12. if (whileInstalling) {
  13. out.push('While resolving: ' + printNode(whileInstalling, chalk))
  14. }
  15. // it "should" be impossible for an ERESOLVE explanation to lack both current and currentEdge, but better to have a less helpful error than a crashing failure.
  16. if (current) {
  17. out.push('Found: ' + explainNode(current, depth, chalk))
  18. } else if (peerConflict && peerConflict.current) {
  19. out.push('Found: ' + explainNode(peerConflict.current, depth, chalk))
  20. } else if (currentEdge) {
  21. out.push('Found: ' + explainEdge(currentEdge, depth, chalk))
  22. } else /* istanbul ignore else - should always have one */ if (edge) {
  23. out.push('Found: ' + explainEdge(edge, depth, chalk))
  24. }
  25. out.push('\nCould not resolve dependency:\n' +
  26. explainEdge(edge, depth, chalk))
  27. if (peerConflict) {
  28. const heading = '\nConflicting peer dependency:'
  29. const pc = explainNode(peerConflict.peer, depth, chalk)
  30. out.push(heading + ' ' + pc)
  31. }
  32. return out.join('\n')
  33. }
  34. // generate a full verbose report and tell the user how to fix it
  35. const report = (expl, chalk, noColorChalk) => {
  36. const flags = [
  37. expl.strictPeerDeps ? '--no-strict-peer-deps' : '',
  38. '--force',
  39. '--legacy-peer-deps',
  40. ].filter(Boolean)
  41. const or = (arr) => arr.length <= 2
  42. ? arr.join(' or ') :
  43. arr.map((v, i, l) => i + 1 === l.length ? `or ${v}` : v).join(', ')
  44. const fix = `Fix the upstream dependency conflict, or retry this command with ${or(flags)} to accept an incorrect (and potentially broken) dependency resolution.`
  45. return {
  46. explanation: `${explain(expl, chalk, 4)}\n\n${fix}`,
  47. file: `# npm resolution error report\n\n${explain(expl, noColorChalk, Infinity)}\n\n${fix}`,
  48. }
  49. }
  50. module.exports = {
  51. explain,
  52. report,
  53. }