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
JavaScript
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 };