• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1const tar = require('tar')
2const { minimatch } = require('minimatch')
3
4const normalizeMatch = str => str
5  .replace(/\\+/g, '/')
6  .replace(/^\.\/|^\./, '')
7
8// files and refs are mutating params
9// filterFiles, item, prefix and opts are read-only options
10const untar = ({ files, refs }, { filterFiles, item, prefix }) => {
11  tar.list({
12    filter: (path, entry) => {
13      const fileMatch = () =>
14        (!filterFiles.length ||
15          filterFiles.some(f => {
16            const pattern = normalizeMatch(f)
17            return minimatch(
18              normalizeMatch(path),
19              `{package/,}${pattern}`,
20              { matchBase: pattern.startsWith('*') }
21            )
22          }))
23
24      // expands usage of simple path filters, e.g: lib or src/
25      const folderMatch = () =>
26        filterFiles.some(f =>
27          normalizeMatch(path).startsWith(normalizeMatch(f)) ||
28          normalizeMatch(path).startsWith(`package/${normalizeMatch(f)}`))
29
30      if (
31        entry.type === 'File' &&
32        (fileMatch() || folderMatch())
33      ) {
34        const key = path.replace(/^[^/]+\/?/, '')
35        files.add(key)
36
37        // should skip reading file when using --name-only option
38        let content
39        try {
40          entry.setEncoding('utf8')
41          content = entry.concat()
42        } catch (e) {
43          /* istanbul ignore next */
44          throw Object.assign(
45            new Error('failed to read files'),
46            { code: 'EDIFFUNTAR' }
47          )
48        }
49
50        refs.set(`${prefix}${key}`, {
51          content,
52          mode: `100${entry.mode.toString(8)}`,
53        })
54        return true
55      }
56    },
57  })
58    .on('error', /* istanbul ignore next */ e => {
59      throw e
60    })
61    .end(item)
62}
63
64const readTarballs = async (tarballs, opts = {}) => {
65  const files = new Set()
66  const refs = new Map()
67  const arr = [].concat(tarballs)
68
69  const filterFiles = opts.diffFiles || []
70
71  for (const i of arr) {
72    untar({
73      files,
74      refs,
75    }, {
76      item: i.item,
77      prefix: i.prefix,
78      filterFiles,
79    })
80  }
81
82  // await to read all content from included files
83  const allRefs = [...refs.values()]
84  const contents = await Promise.all(allRefs.map(async ref => ref.content))
85
86  contents.forEach((content, index) => {
87    allRefs[index].content = content
88  })
89
90  return {
91    files,
92    refs,
93  }
94}
95
96module.exports = readTarballs
97