• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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