UNPKG

tar-iterator

Version:

Extract contents from tar archive type using an iterator API using streams or paths. Use stream interface and pipe transforms to add decompression algorithms

77 lines (76 loc) 3.09 kB
import once from 'call-once-fn'; import { FileEntry, waitForAccess } from 'extract-base-iterator'; import fs from 'graceful-fs'; import oo from 'on-one'; let TarFileEntry = class TarFileEntry extends FileEntry { create(dest, options, callback) { callback = typeof options === 'function' ? options : callback; options = typeof options === 'function' ? {} : options || {}; if (typeof callback === 'function') { FileEntry.prototype.create.call(this, dest, options, (err)=>{ callback(err); if (this.lock) { this.lock.release(); this.lock = null; } }); return; } return new Promise((resolve, reject)=>this.create(dest, options, (err, done)=>err ? reject(err) : resolve(done))); } _writeFile(fullPath, _options, callback) { if (!this.stream) { callback(new Error('FileEntry missing stream. Check for calling create multiple times')); return; } const stream = this.stream; this.stream = null; // Prevent reuse // Use once since errors can come from either stream const cb = once((err)=>{ err ? callback(err) : waitForAccess(fullPath, callback); // gunzip stream returns prematurely occasionally }); try { const writeStream = fs.createWriteStream(fullPath); // Listen for errors on source stream (errors don't propagate through pipe) stream.on('error', (streamErr)=>{ // Destroy the write stream on source error. // On Node 0.8, destroy() emits 'close' before 'error'. Since on-one is listening // for ['error', 'close', 'finish'], it catches 'close' first, calls our callback, // and removes ALL listeners - including the 'error' listener. The subsequent EBADF // error then fires with no handler, causing an uncaught exception. // Adding a no-op error handler ensures there's always a listener for any error. const ws = writeStream; writeStream.on('error', ()=>{}); if (typeof ws.destroy === 'function') ws.destroy(); cb(streamErr); }); // Pipe and listen for write stream completion/errors stream.pipe(writeStream); oo(writeStream, [ 'error', 'close', 'finish' ], cb); } catch (err) { cb(err); } } destroy() { FileEntry.prototype.destroy.call(this); if (this.stream) { this.stream.resume(); // drain stream this.stream = null; } if (this.lock) { this.lock.release(); this.lock = null; } } constructor(attributes, stream, lock){ super(attributes); this.stream = stream; this.lock = lock; this.lock.retain(); } }; export { TarFileEntry as default };