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