All files / tar/lib extract.js

100% Statements 61/61
100% Branches 45/45
100% Functions 12/12
100% Lines 60/60
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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113      2x 2x 2x 2x 2x   2x 21x 1x 20x 1x   21x 3x   21x 13x   8x   21x   21x 2x   19x 1x   18x 7x   18x               2x 13x 7x   7x 87x 87x       87x 87x     7x 9x 9x     2x 6x   6x 6x   6x     5x 5x       4x     2x 9x 9x   9x 9x 9x 9x       9x 9x 1x   8x       8x 8x       9x     2x 1x     2x 2x    
'use strict'
 
// tar -x
const hlo = require('./high-level-opt.js')
const Unpack = require('./unpack.js')
const fs = require('fs')
const fsm = require('fs-minipass')
const path = require('path')
 
const x = module.exports = (opt_, files, cb) => {
  if (typeof opt_ === 'function')
    cb = opt_, files = null, opt_ = {}
  else if (Array.isArray(opt_))
    files = opt_, opt_ = {}
 
  if (typeof files === 'function')
    cb = files, files = null
 
  if (!files)
    files = []
  else
    files = Array.from(files)
 
  const opt = hlo(opt_)
 
  if (opt.sync && typeof cb === 'function')
    throw new TypeError('callback not supported for sync tar functions')
 
  if (!opt.file && typeof cb === 'function')
    throw new TypeError('callback only supported with file option')
 
  if (files.length)
    filesFilter(opt, files)
 
  return opt.file && opt.sync ? extractFileSync(opt)
    : opt.file ? extractFile(opt, cb)
    : opt.sync ? extractSync(opt)
    : extract(opt)
}
 
// construct a filter that limits the file entries listed
// include child entries if a dir is included
const filesFilter = (opt, files) => {
  const map = new Map(files.map(f => [f.replace(/\/+$/, ''), true]))
  const filter = opt.filter
 
  const mapHas = (file, r) => {
    const root = r || path.parse(file).root || '.'
    const ret = file === root ? false
      : map.has(file) ? map.get(file)
      : mapHas(path.dirname(file), root)
 
    map.set(file, ret)
    return ret
  }
 
  opt.filter = filter
    ? (file, entry) => filter(file, entry) && mapHas(file.replace(/\/+$/, ''))
    : file => mapHas(file.replace(/\/+$/, ''))
}
 
const extractFileSync = opt => {
  const u = new Unpack.Sync(opt)
 
  const file = opt.file
  let threw = true
  let fd
  const stat = fs.statSync(file)
  // This trades a zero-byte read() syscall for a stat
  // However, it will usually result in less memory allocation
  const readSize = opt.maxReadSize || 16*1024*1024
  const stream = new fsm.ReadStreamSync(file, {
    readSize: readSize,
    size: stat.size
  })
  stream.pipe(u)
}
 
const extractFile = (opt, cb) => {
  const u = new Unpack(opt)
  const readSize = opt.maxReadSize || 16*1024*1024
 
  const file = opt.file
  const p = new Promise((resolve, reject) => {
    u.on('error', reject)
    u.on('close', resolve)
 
    // This trades a zero-byte read() syscall for a stat
    // However, it will usually result in less memory allocation
    fs.stat(file, (er, stat) => {
      if (er)
        reject(er)
      else {
        const stream = new fsm.ReadStream(file, {
          readSize: readSize,
          size: stat.size
        })
        stream.on('error', reject)
        stream.pipe(u)
      }
    })
  })
  return cb ? p.then(cb, cb) : p
}
 
const extractSync = opt => {
  return new Unpack.Sync(opt)
}
 
const extract = opt => {
  return new Unpack(opt)
}