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