explain-eresolve.js 2.5 KB

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