1const EOL = '\n' 2 3const colorizeDiff = require('@npmcli/disparity-colors') 4const jsDiff = require('diff') 5 6const shouldPrintPatch = require('./should-print-patch.js') 7 8const formatDiff = ({ files, opts = {}, refs, versions }) => { 9 let res = '' 10 const srcPrefix = opts.diffNoPrefix ? '' : opts.diffSrcPrefix || 'a/' 11 const dstPrefix = opts.diffNoPrefix ? '' : opts.diffDstPrefix || 'b/' 12 13 for (const filename of files.values()) { 14 const names = { 15 a: `${srcPrefix}${filename}`, 16 b: `${dstPrefix}${filename}`, 17 } 18 19 let fileMode = '' 20 const filenames = { 21 a: refs.get(`a/${filename}`), 22 b: refs.get(`b/${filename}`), 23 } 24 const contents = { 25 a: filenames.a && filenames.a.content, 26 b: filenames.b && filenames.b.content, 27 } 28 const modes = { 29 a: filenames.a && filenames.a.mode, 30 b: filenames.b && filenames.b.mode, 31 } 32 33 if (contents.a === contents.b && modes.a === modes.b) { 34 continue 35 } 36 37 if (opts.diffNameOnly) { 38 res += `${filename}${EOL}` 39 continue 40 } 41 42 let patch = '' 43 let headerLength = 0 44 const header = str => { 45 headerLength++ 46 patch += `${str}${EOL}` 47 } 48 49 // manually build a git diff-compatible header 50 header(`diff --git ${names.a} ${names.b}`) 51 if (modes.a === modes.b) { 52 fileMode = filenames.a.mode 53 } else { 54 if (modes.a && !modes.b) { 55 header(`deleted file mode ${modes.a}`) 56 } else if (!modes.a && modes.b) { 57 header(`new file mode ${modes.b}`) 58 } else { 59 header(`old mode ${modes.a}`) 60 header(`new mode ${modes.b}`) 61 } 62 } 63 /* eslint-disable-next-line max-len */ 64 header(`index ${opts.tagVersionPrefix || 'v'}${versions.a}..${opts.tagVersionPrefix || 'v'}${versions.b} ${fileMode}`) 65 66 if (shouldPrintPatch(filename)) { 67 patch += jsDiff.createTwoFilesPatch( 68 names.a, 69 names.b, 70 contents.a || '', 71 contents.b || '', 72 '', 73 '', 74 { 75 context: opts.diffUnified === 0 ? 0 : opts.diffUnified || 3, 76 ignoreWhitespace: opts.diffIgnoreAllSpace, 77 } 78 ).replace( 79 '===================================================================\n', 80 '' 81 ).replace(/\t\n/g, '\n') // strip trailing tabs 82 headerLength += 2 83 } else { 84 header(`--- ${names.a}`) 85 header(`+++ ${names.b}`) 86 } 87 88 res += (opts.color 89 ? colorizeDiff(patch, { headerLength }) 90 : patch) 91 } 92 93 return res.trim() 94} 95 96module.exports = formatDiff 97