You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					975 lines
				
				24 KiB
			
		
		
			
		
	
	
					975 lines
				
				24 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Archiver Core
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @ignore
							 | 
						||
| 
								 | 
							
								 * @license [MIT]{@link https://github.com/archiverjs/node-archiver/blob/master/LICENSE}
							 | 
						||
| 
								 | 
							
								 * @copyright (c) 2012-2014 Chris Talkington, contributors.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								var fs = require('fs');
							 | 
						||
| 
								 | 
							
								var glob = require('readdir-glob');
							 | 
						||
| 
								 | 
							
								var async = require('async');
							 | 
						||
| 
								 | 
							
								var path = require('path');
							 | 
						||
| 
								 | 
							
								var util = require('archiver-utils');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var inherits = require('util').inherits;
							 | 
						||
| 
								 | 
							
								var ArchiverError = require('./error');
							 | 
						||
| 
								 | 
							
								var Transform = require('readable-stream').Transform;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var win32 = process.platform === 'win32';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * @constructor
							 | 
						||
| 
								 | 
							
								 * @param {String} format The archive format to use.
							 | 
						||
| 
								 | 
							
								 * @param {(CoreOptions|TransformOptions)} options See also {@link ZipOptions} and {@link TarOptions}.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								var Archiver = function(format, options) {
							 | 
						||
| 
								 | 
							
								  if (!(this instanceof Archiver)) {
							 | 
						||
| 
								 | 
							
								    return new Archiver(format, options);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (typeof format !== 'string') {
							 | 
						||
| 
								 | 
							
								    options = format;
							 | 
						||
| 
								 | 
							
								    format = 'zip';
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  options = this.options = util.defaults(options, {
							 | 
						||
| 
								 | 
							
								    highWaterMark: 1024 * 1024,
							 | 
						||
| 
								 | 
							
								    statConcurrency: 4
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  Transform.call(this, options);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._format = false;
							 | 
						||
| 
								 | 
							
								  this._module = false;
							 | 
						||
| 
								 | 
							
								  this._pending = 0;
							 | 
						||
| 
								 | 
							
								  this._pointer = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._entriesCount = 0;
							 | 
						||
| 
								 | 
							
								  this._entriesProcessedCount = 0;
							 | 
						||
| 
								 | 
							
								  this._fsEntriesTotalBytes = 0;
							 | 
						||
| 
								 | 
							
								  this._fsEntriesProcessedBytes = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._queue = async.queue(this._onQueueTask.bind(this), 1);
							 | 
						||
| 
								 | 
							
								  this._queue.drain(this._onQueueDrain.bind(this));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._statQueue = async.queue(this._onStatQueueTask.bind(this), options.statConcurrency);
							 | 
						||
| 
								 | 
							
								  this._statQueue.drain(this._onQueueDrain.bind(this));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._state = {
							 | 
						||
| 
								 | 
							
								    aborted: false,
							 | 
						||
| 
								 | 
							
								    finalize: false,
							 | 
						||
| 
								 | 
							
								    finalizing: false,
							 | 
						||
| 
								 | 
							
								    finalized: false,
							 | 
						||
| 
								 | 
							
								    modulePiped: false
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._streams = [];
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								inherits(Archiver, Transform);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Internal logic for `abort`.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @return void
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype._abort = function() {
							 | 
						||
| 
								 | 
							
								  this._state.aborted = true;
							 | 
						||
| 
								 | 
							
								  this._queue.kill();
							 | 
						||
| 
								 | 
							
								  this._statQueue.kill();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (this._queue.idle()) {
							 | 
						||
| 
								 | 
							
								    this._shutdown();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Internal helper for appending files.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @param  {String} filepath The source filepath.
							 | 
						||
| 
								 | 
							
								 * @param  {EntryData} data The entry data.
							 | 
						||
| 
								 | 
							
								 * @return void
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype._append = function(filepath, data) {
							 | 
						||
| 
								 | 
							
								  data = data || {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var task = {
							 | 
						||
| 
								 | 
							
								    source: null,
							 | 
						||
| 
								 | 
							
								    filepath: filepath
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!data.name) {
							 | 
						||
| 
								 | 
							
								    data.name = filepath;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  data.sourcePath = filepath;
							 | 
						||
| 
								 | 
							
								  task.data = data;
							 | 
						||
| 
								 | 
							
								  this._entriesCount++;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (data.stats && data.stats instanceof fs.Stats) {
							 | 
						||
| 
								 | 
							
								    task = this._updateQueueTaskWithStats(task, data.stats);
							 | 
						||
| 
								 | 
							
								    if (task) {
							 | 
						||
| 
								 | 
							
								      if (data.stats.size) {
							 | 
						||
| 
								 | 
							
								        this._fsEntriesTotalBytes += data.stats.size;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      this._queue.push(task);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    this._statQueue.push(task);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Internal logic for `finalize`.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @return void
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype._finalize = function() {
							 | 
						||
| 
								 | 
							
								  if (this._state.finalizing || this._state.finalized || this._state.aborted) {
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._state.finalizing = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._moduleFinalize();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._state.finalizing = false;
							 | 
						||
| 
								 | 
							
								  this._state.finalized = true;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Checks the various state variables to determine if we can `finalize`.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @return {Boolean}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype._maybeFinalize = function() {
							 | 
						||
| 
								 | 
							
								  if (this._state.finalizing || this._state.finalized || this._state.aborted) {
							 | 
						||
| 
								 | 
							
								    return false;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (this._state.finalize && this._pending === 0 && this._queue.idle() && this._statQueue.idle()) {
							 | 
						||
| 
								 | 
							
								    this._finalize();
							 | 
						||
| 
								 | 
							
								    return true;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return false;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Appends an entry to the module.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @fires  Archiver#entry
							 | 
						||
| 
								 | 
							
								 * @param  {(Buffer|Stream)} source
							 | 
						||
| 
								 | 
							
								 * @param  {EntryData} data
							 | 
						||
| 
								 | 
							
								 * @param  {Function} callback
							 | 
						||
| 
								 | 
							
								 * @return void
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype._moduleAppend = function(source, data, callback) {
							 | 
						||
| 
								 | 
							
								  if (this._state.aborted) {
							 | 
						||
| 
								 | 
							
								    callback();
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._module.append(source, data, function(err) {
							 | 
						||
| 
								 | 
							
								    this._task = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (this._state.aborted) {
							 | 
						||
| 
								 | 
							
								      this._shutdown();
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (err) {
							 | 
						||
| 
								 | 
							
								      this.emit('error', err);
							 | 
						||
| 
								 | 
							
								      setImmediate(callback);
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Fires when the entry's input has been processed and appended to the archive.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @event Archiver#entry
							 | 
						||
| 
								 | 
							
								     * @type {EntryData}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    this.emit('entry', data);
							 | 
						||
| 
								 | 
							
								    this._entriesProcessedCount++;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (data.stats && data.stats.size) {
							 | 
						||
| 
								 | 
							
								      this._fsEntriesProcessedBytes += data.stats.size;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * @event Archiver#progress
							 | 
						||
| 
								 | 
							
								     * @type {ProgressData}
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    this.emit('progress', {
							 | 
						||
| 
								 | 
							
								      entries: {
							 | 
						||
| 
								 | 
							
								        total: this._entriesCount,
							 | 
						||
| 
								 | 
							
								        processed: this._entriesProcessedCount
							 | 
						||
| 
								 | 
							
								      },
							 | 
						||
| 
								 | 
							
								      fs: {
							 | 
						||
| 
								 | 
							
								        totalBytes: this._fsEntriesTotalBytes,
							 | 
						||
| 
								 | 
							
								        processedBytes: this._fsEntriesProcessedBytes
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    setImmediate(callback);
							 | 
						||
| 
								 | 
							
								  }.bind(this));
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Finalizes the module.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @return void
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype._moduleFinalize = function() {
							 | 
						||
| 
								 | 
							
								  if (typeof this._module.finalize === 'function') {
							 | 
						||
| 
								 | 
							
								    this._module.finalize();
							 | 
						||
| 
								 | 
							
								  } else if (typeof this._module.end === 'function') {
							 | 
						||
| 
								 | 
							
								    this._module.end();
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    this.emit('error', new ArchiverError('NOENDMETHOD'));
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Pipes the module to our internal stream with error bubbling.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @return void
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype._modulePipe = function() {
							 | 
						||
| 
								 | 
							
								  this._module.on('error', this._onModuleError.bind(this));
							 | 
						||
| 
								 | 
							
								  this._module.pipe(this);
							 | 
						||
| 
								 | 
							
								  this._state.modulePiped = true;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Determines if the current module supports a defined feature.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @param  {String} key
							 | 
						||
| 
								 | 
							
								 * @return {Boolean}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype._moduleSupports = function(key) {
							 | 
						||
| 
								 | 
							
								  if (!this._module.supports || !this._module.supports[key]) {
							 | 
						||
| 
								 | 
							
								    return false;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this._module.supports[key];
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Unpipes the module from our internal stream.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @return void
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype._moduleUnpipe = function() {
							 | 
						||
| 
								 | 
							
								  this._module.unpipe(this);
							 | 
						||
| 
								 | 
							
								  this._state.modulePiped = false;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Normalizes entry data with fallbacks for key properties.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @param  {Object} data
							 | 
						||
| 
								 | 
							
								 * @param  {fs.Stats} stats
							 | 
						||
| 
								 | 
							
								 * @return {Object}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype._normalizeEntryData = function(data, stats) {
							 | 
						||
| 
								 | 
							
								  data = util.defaults(data, {
							 | 
						||
| 
								 | 
							
								    type: 'file',
							 | 
						||
| 
								 | 
							
								    name: null,
							 | 
						||
| 
								 | 
							
								    date: null,
							 | 
						||
| 
								 | 
							
								    mode: null,
							 | 
						||
| 
								 | 
							
								    prefix: null,
							 | 
						||
| 
								 | 
							
								    sourcePath: null,
							 | 
						||
| 
								 | 
							
								    stats: false
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (stats && data.stats === false) {
							 | 
						||
| 
								 | 
							
								    data.stats = stats;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var isDir = data.type === 'directory';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (data.name) {
							 | 
						||
| 
								 | 
							
								    if (typeof data.prefix === 'string' && '' !== data.prefix) {
							 | 
						||
| 
								 | 
							
								      data.name = data.prefix + '/' + data.name;
							 | 
						||
| 
								 | 
							
								      data.prefix = null;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    data.name = util.sanitizePath(data.name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (data.type !== 'symlink' && data.name.slice(-1) === '/') {
							 | 
						||
| 
								 | 
							
								      isDir = true;
							 | 
						||
| 
								 | 
							
								      data.type = 'directory';
							 | 
						||
| 
								 | 
							
								    } else if (isDir) {
							 | 
						||
| 
								 | 
							
								      data.name += '/';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // 511 === 0777; 493 === 0755; 438 === 0666; 420 === 0644
							 | 
						||
| 
								 | 
							
								  if (typeof data.mode === 'number') {
							 | 
						||
| 
								 | 
							
								    if (win32) {
							 | 
						||
| 
								 | 
							
								      data.mode &= 511;
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      data.mode &= 4095
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } else if (data.stats && data.mode === null) {
							 | 
						||
| 
								 | 
							
								    if (win32) {
							 | 
						||
| 
								 | 
							
								      data.mode = data.stats.mode & 511;
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      data.mode = data.stats.mode & 4095;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // stat isn't reliable on windows; force 0755 for dir
							 | 
						||
| 
								 | 
							
								    if (win32 && isDir) {
							 | 
						||
| 
								 | 
							
								      data.mode = 493;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } else if (data.mode === null) {
							 | 
						||
| 
								 | 
							
								    data.mode = isDir ? 493 : 420;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (data.stats && data.date === null) {
							 | 
						||
| 
								 | 
							
								    data.date = data.stats.mtime;
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    data.date = util.dateify(data.date);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return data;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Error listener that re-emits error on to our internal stream.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @param  {Error} err
							 | 
						||
| 
								 | 
							
								 * @return void
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype._onModuleError = function(err) {
							 | 
						||
| 
								 | 
							
								  /**
							 | 
						||
| 
								 | 
							
								   * @event Archiver#error
							 | 
						||
| 
								 | 
							
								   * @type {ErrorData}
							 | 
						||
| 
								 | 
							
								   */
							 | 
						||
| 
								 | 
							
								  this.emit('error', err);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Checks the various state variables after queue has drained to determine if
							 | 
						||
| 
								 | 
							
								 * we need to `finalize`.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @return void
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype._onQueueDrain = function() {
							 | 
						||
| 
								 | 
							
								  if (this._state.finalizing || this._state.finalized || this._state.aborted) {
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (this._state.finalize && this._pending === 0 && this._queue.idle() && this._statQueue.idle()) {
							 | 
						||
| 
								 | 
							
								    this._finalize();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Appends each queue task to the module.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @param  {Object} task
							 | 
						||
| 
								 | 
							
								 * @param  {Function} callback
							 | 
						||
| 
								 | 
							
								 * @return void
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype._onQueueTask = function(task, callback) {
							 | 
						||
| 
								 | 
							
								  var fullCallback = () => {
							 | 
						||
| 
								 | 
							
								    if(task.data.callback) {
							 | 
						||
| 
								 | 
							
								      task.data.callback();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    callback();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (this._state.finalizing || this._state.finalized || this._state.aborted) {
							 | 
						||
| 
								 | 
							
								    fullCallback();
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._task = task;
							 | 
						||
| 
								 | 
							
								  this._moduleAppend(task.source, task.data, fullCallback);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Performs a file stat and reinjects the task back into the queue.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @param  {Object} task
							 | 
						||
| 
								 | 
							
								 * @param  {Function} callback
							 | 
						||
| 
								 | 
							
								 * @return void
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype._onStatQueueTask = function(task, callback) {
							 | 
						||
| 
								 | 
							
								  if (this._state.finalizing || this._state.finalized || this._state.aborted) {
							 | 
						||
| 
								 | 
							
								    callback();
							 | 
						||
| 
								 | 
							
								    return;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  fs.lstat(task.filepath, function(err, stats) {
							 | 
						||
| 
								 | 
							
								    if (this._state.aborted) {
							 | 
						||
| 
								 | 
							
								      setImmediate(callback);
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (err) {
							 | 
						||
| 
								 | 
							
								      this._entriesCount--;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      /**
							 | 
						||
| 
								 | 
							
								       * @event Archiver#warning
							 | 
						||
| 
								 | 
							
								       * @type {ErrorData}
							 | 
						||
| 
								 | 
							
								       */
							 | 
						||
| 
								 | 
							
								      this.emit('warning', err);
							 | 
						||
| 
								 | 
							
								      setImmediate(callback);
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    task = this._updateQueueTaskWithStats(task, stats);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (task) {
							 | 
						||
| 
								 | 
							
								      if (stats.size) {
							 | 
						||
| 
								 | 
							
								        this._fsEntriesTotalBytes += stats.size;
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      this._queue.push(task);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    setImmediate(callback);
							 | 
						||
| 
								 | 
							
								  }.bind(this));
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Unpipes the module and ends our internal stream.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @return void
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype._shutdown = function() {
							 | 
						||
| 
								 | 
							
								  this._moduleUnpipe();
							 | 
						||
| 
								 | 
							
								  this.end();
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Tracks the bytes emitted by our internal stream.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @param  {Buffer} chunk
							 | 
						||
| 
								 | 
							
								 * @param  {String} encoding
							 | 
						||
| 
								 | 
							
								 * @param  {Function} callback
							 | 
						||
| 
								 | 
							
								 * @return void
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype._transform = function(chunk, encoding, callback) {
							 | 
						||
| 
								 | 
							
								  if (chunk) {
							 | 
						||
| 
								 | 
							
								    this._pointer += chunk.length;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  callback(null, chunk);
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Updates and normalizes a queue task using stats data.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @param  {Object} task
							 | 
						||
| 
								 | 
							
								 * @param  {fs.Stats} stats
							 | 
						||
| 
								 | 
							
								 * @return {Object}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype._updateQueueTaskWithStats = function(task, stats) {
							 | 
						||
| 
								 | 
							
								  if (stats.isFile()) {
							 | 
						||
| 
								 | 
							
								    task.data.type = 'file';
							 | 
						||
| 
								 | 
							
								    task.data.sourceType = 'stream';
							 | 
						||
| 
								 | 
							
								    task.source = util.lazyReadStream(task.filepath);
							 | 
						||
| 
								 | 
							
								  } else if (stats.isDirectory() && this._moduleSupports('directory')) {
							 | 
						||
| 
								 | 
							
								    task.data.name = util.trailingSlashIt(task.data.name);
							 | 
						||
| 
								 | 
							
								    task.data.type = 'directory';
							 | 
						||
| 
								 | 
							
								    task.data.sourcePath = util.trailingSlashIt(task.filepath);
							 | 
						||
| 
								 | 
							
								    task.data.sourceType = 'buffer';
							 | 
						||
| 
								 | 
							
								    task.source = Buffer.concat([]);
							 | 
						||
| 
								 | 
							
								  } else if (stats.isSymbolicLink() && this._moduleSupports('symlink')) {
							 | 
						||
| 
								 | 
							
								    var linkPath = fs.readlinkSync(task.filepath);
							 | 
						||
| 
								 | 
							
								    var dirName = path.dirname(task.filepath);
							 | 
						||
| 
								 | 
							
								    task.data.type = 'symlink';
							 | 
						||
| 
								 | 
							
								    task.data.linkname = path.relative(dirName, path.resolve(dirName, linkPath));
							 | 
						||
| 
								 | 
							
								    task.data.sourceType = 'buffer';
							 | 
						||
| 
								 | 
							
								    task.source = Buffer.concat([]);
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    if (stats.isDirectory()) {
							 | 
						||
| 
								 | 
							
								      this.emit('warning', new ArchiverError('DIRECTORYNOTSUPPORTED', task.data));
							 | 
						||
| 
								 | 
							
								    } else if (stats.isSymbolicLink()) {
							 | 
						||
| 
								 | 
							
								      this.emit('warning', new ArchiverError('SYMLINKNOTSUPPORTED', task.data));
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      this.emit('warning', new ArchiverError('ENTRYNOTSUPPORTED', task.data));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return null;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  task.data = this._normalizeEntryData(task.data, stats);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return task;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Aborts the archiving process, taking a best-effort approach, by:
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * - removing any pending queue tasks
							 | 
						||
| 
								 | 
							
								 * - allowing any active queue workers to finish
							 | 
						||
| 
								 | 
							
								 * - detaching internal module pipes
							 | 
						||
| 
								 | 
							
								 * - ending both sides of the Transform stream
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * It will NOT drain any remaining sources.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return {this}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype.abort = function() {
							 | 
						||
| 
								 | 
							
								  if (this._state.aborted || this._state.finalized) {
							 | 
						||
| 
								 | 
							
								    return this;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._abort();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Appends an input source (text string, buffer, or stream) to the instance.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * When the instance has received, processed, and emitted the input, the `entry`
							 | 
						||
| 
								 | 
							
								 * event is fired.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @fires  Archiver#entry
							 | 
						||
| 
								 | 
							
								 * @param  {(Buffer|Stream|String)} source The input source.
							 | 
						||
| 
								 | 
							
								 * @param  {EntryData} data See also {@link ZipEntryData} and {@link TarEntryData}.
							 | 
						||
| 
								 | 
							
								 * @return {this}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype.append = function(source, data) {
							 | 
						||
| 
								 | 
							
								  if (this._state.finalize || this._state.aborted) {
							 | 
						||
| 
								 | 
							
								    this.emit('error', new ArchiverError('QUEUECLOSED'));
							 | 
						||
| 
								 | 
							
								    return this;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  data = this._normalizeEntryData(data);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (typeof data.name !== 'string' || data.name.length === 0) {
							 | 
						||
| 
								 | 
							
								    this.emit('error', new ArchiverError('ENTRYNAMEREQUIRED'));
							 | 
						||
| 
								 | 
							
								    return this;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (data.type === 'directory' && !this._moduleSupports('directory')) {
							 | 
						||
| 
								 | 
							
								    this.emit('error', new ArchiverError('DIRECTORYNOTSUPPORTED', { name: data.name }));
							 | 
						||
| 
								 | 
							
								    return this;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  source = util.normalizeInputSource(source);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (Buffer.isBuffer(source)) {
							 | 
						||
| 
								 | 
							
								    data.sourceType = 'buffer';
							 | 
						||
| 
								 | 
							
								  } else if (util.isStream(source)) {
							 | 
						||
| 
								 | 
							
								    data.sourceType = 'stream';
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    this.emit('error', new ArchiverError('INPUTSTEAMBUFFERREQUIRED', { name: data.name }));
							 | 
						||
| 
								 | 
							
								    return this;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._entriesCount++;
							 | 
						||
| 
								 | 
							
								  this._queue.push({
							 | 
						||
| 
								 | 
							
								    data: data,
							 | 
						||
| 
								 | 
							
								    source: source
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Appends a directory and its files, recursively, given its dirpath.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param  {String} dirpath The source directory path.
							 | 
						||
| 
								 | 
							
								 * @param  {String} destpath The destination path within the archive.
							 | 
						||
| 
								 | 
							
								 * @param  {(EntryData|Function)} data See also [ZipEntryData]{@link ZipEntryData} and
							 | 
						||
| 
								 | 
							
								 * [TarEntryData]{@link TarEntryData}.
							 | 
						||
| 
								 | 
							
								 * @return {this}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype.directory = function(dirpath, destpath, data) {
							 | 
						||
| 
								 | 
							
								  if (this._state.finalize || this._state.aborted) {
							 | 
						||
| 
								 | 
							
								    this.emit('error', new ArchiverError('QUEUECLOSED'));
							 | 
						||
| 
								 | 
							
								    return this;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (typeof dirpath !== 'string' || dirpath.length === 0) {
							 | 
						||
| 
								 | 
							
								    this.emit('error', new ArchiverError('DIRECTORYDIRPATHREQUIRED'));
							 | 
						||
| 
								 | 
							
								    return this;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._pending++;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (destpath === false) {
							 | 
						||
| 
								 | 
							
								    destpath = '';
							 | 
						||
| 
								 | 
							
								  } else if (typeof destpath !== 'string'){
							 | 
						||
| 
								 | 
							
								    destpath = dirpath;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var dataFunction = false;
							 | 
						||
| 
								 | 
							
								  if (typeof data === 'function') {
							 | 
						||
| 
								 | 
							
								    dataFunction = data;
							 | 
						||
| 
								 | 
							
								    data = {};
							 | 
						||
| 
								 | 
							
								  } else if (typeof data !== 'object') {
							 | 
						||
| 
								 | 
							
								    data = {};
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var globOptions = {
							 | 
						||
| 
								 | 
							
								    stat: true,
							 | 
						||
| 
								 | 
							
								    dot: true
							 | 
						||
| 
								 | 
							
								  };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function onGlobEnd() {
							 | 
						||
| 
								 | 
							
								    this._pending--;
							 | 
						||
| 
								 | 
							
								    this._maybeFinalize();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function onGlobError(err) {
							 | 
						||
| 
								 | 
							
								    this.emit('error', err);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function onGlobMatch(match){
							 | 
						||
| 
								 | 
							
								    globber.pause();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var ignoreMatch = false;
							 | 
						||
| 
								 | 
							
								    var entryData = Object.assign({}, data);
							 | 
						||
| 
								 | 
							
								    entryData.name = match.relative;
							 | 
						||
| 
								 | 
							
								    entryData.prefix = destpath;
							 | 
						||
| 
								 | 
							
								    entryData.stats = match.stat;
							 | 
						||
| 
								 | 
							
								    entryData.callback = globber.resume.bind(globber);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    try {
							 | 
						||
| 
								 | 
							
								      if (dataFunction) {
							 | 
						||
| 
								 | 
							
								        entryData = dataFunction(entryData);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (entryData === false) {
							 | 
						||
| 
								 | 
							
								          ignoreMatch = true;
							 | 
						||
| 
								 | 
							
								        } else if (typeof entryData !== 'object') {
							 | 
						||
| 
								 | 
							
								          throw new ArchiverError('DIRECTORYFUNCTIONINVALIDDATA', { dirpath: dirpath });
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    } catch(e) {
							 | 
						||
| 
								 | 
							
								      this.emit('error', e);
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (ignoreMatch) {
							 | 
						||
| 
								 | 
							
								      globber.resume();
							 | 
						||
| 
								 | 
							
								      return;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this._append(match.absolute, entryData);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var globber = glob(dirpath, globOptions);
							 | 
						||
| 
								 | 
							
								  globber.on('error', onGlobError.bind(this));
							 | 
						||
| 
								 | 
							
								  globber.on('match', onGlobMatch.bind(this));
							 | 
						||
| 
								 | 
							
								  globber.on('end', onGlobEnd.bind(this));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Appends a file given its filepath using a
							 | 
						||
| 
								 | 
							
								 * [lazystream]{@link https://github.com/jpommerening/node-lazystream} wrapper to
							 | 
						||
| 
								 | 
							
								 * prevent issues with open file limits.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * When the instance has received, processed, and emitted the file, the `entry`
							 | 
						||
| 
								 | 
							
								 * event is fired.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param  {String} filepath The source filepath.
							 | 
						||
| 
								 | 
							
								 * @param  {EntryData} data See also [ZipEntryData]{@link ZipEntryData} and
							 | 
						||
| 
								 | 
							
								 * [TarEntryData]{@link TarEntryData}.
							 | 
						||
| 
								 | 
							
								 * @return {this}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype.file = function(filepath, data) {
							 | 
						||
| 
								 | 
							
								  if (this._state.finalize || this._state.aborted) {
							 | 
						||
| 
								 | 
							
								    this.emit('error', new ArchiverError('QUEUECLOSED'));
							 | 
						||
| 
								 | 
							
								    return this;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (typeof filepath !== 'string' || filepath.length === 0) {
							 | 
						||
| 
								 | 
							
								    this.emit('error', new ArchiverError('FILEFILEPATHREQUIRED'));
							 | 
						||
| 
								 | 
							
								    return this;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._append(filepath, data);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Appends multiple files that match a glob pattern.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param  {String} pattern The [glob pattern]{@link https://github.com/isaacs/minimatch} to match.
							 | 
						||
| 
								 | 
							
								 * @param  {Object} options See [node-readdir-glob]{@link https://github.com/yqnn/node-readdir-glob#options}.
							 | 
						||
| 
								 | 
							
								 * @param  {EntryData} data See also [ZipEntryData]{@link ZipEntryData} and
							 | 
						||
| 
								 | 
							
								 * [TarEntryData]{@link TarEntryData}.
							 | 
						||
| 
								 | 
							
								 * @return {this}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype.glob = function(pattern, options, data) {
							 | 
						||
| 
								 | 
							
								  this._pending++;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  options = util.defaults(options, {
							 | 
						||
| 
								 | 
							
								    stat: true,
							 | 
						||
| 
								 | 
							
								    pattern: pattern
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function onGlobEnd() {
							 | 
						||
| 
								 | 
							
								    this._pending--;
							 | 
						||
| 
								 | 
							
								    this._maybeFinalize();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function onGlobError(err) {
							 | 
						||
| 
								 | 
							
								    this.emit('error', err);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  function onGlobMatch(match){
							 | 
						||
| 
								 | 
							
								    globber.pause();
							 | 
						||
| 
								 | 
							
								    var entryData = Object.assign({}, data);
							 | 
						||
| 
								 | 
							
								    entryData.callback = globber.resume.bind(globber);
							 | 
						||
| 
								 | 
							
								    entryData.stats = match.stat;
							 | 
						||
| 
								 | 
							
								    entryData.name = match.relative;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this._append(match.absolute, entryData);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var globber = glob(options.cwd || '.', options);
							 | 
						||
| 
								 | 
							
								  globber.on('error', onGlobError.bind(this));
							 | 
						||
| 
								 | 
							
								  globber.on('match', onGlobMatch.bind(this));
							 | 
						||
| 
								 | 
							
								  globber.on('end', onGlobEnd.bind(this));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Finalizes the instance and prevents further appending to the archive
							 | 
						||
| 
								 | 
							
								 * structure (queue will continue til drained).
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * The `end`, `close` or `finish` events on the destination stream may fire
							 | 
						||
| 
								 | 
							
								 * right after calling this method so you should set listeners beforehand to
							 | 
						||
| 
								 | 
							
								 * properly detect stream completion.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return {Promise}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype.finalize = function() {
							 | 
						||
| 
								 | 
							
								  if (this._state.aborted) {
							 | 
						||
| 
								 | 
							
								    var abortedError = new ArchiverError('ABORTED');
							 | 
						||
| 
								 | 
							
								    this.emit('error', abortedError);
							 | 
						||
| 
								 | 
							
								    return Promise.reject(abortedError);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (this._state.finalize) {
							 | 
						||
| 
								 | 
							
								    var finalizingError = new ArchiverError('FINALIZING');
							 | 
						||
| 
								 | 
							
								    this.emit('error', finalizingError);
							 | 
						||
| 
								 | 
							
								    return Promise.reject(finalizingError);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._state.finalize = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (this._pending === 0 && this._queue.idle() && this._statQueue.idle()) {
							 | 
						||
| 
								 | 
							
								    this._finalize();
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var self = this;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return new Promise(function(resolve, reject) {
							 | 
						||
| 
								 | 
							
								    var errored;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    self._module.on('end', function() {
							 | 
						||
| 
								 | 
							
								      if (!errored) {
							 | 
						||
| 
								 | 
							
								        resolve();
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    self._module.on('error', function(err) {
							 | 
						||
| 
								 | 
							
								      errored = true;
							 | 
						||
| 
								 | 
							
								      reject(err);
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								  })
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Sets the module format name used for archiving.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {String} format The name of the format.
							 | 
						||
| 
								 | 
							
								 * @return {this}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype.setFormat = function(format) {
							 | 
						||
| 
								 | 
							
								  if (this._format) {
							 | 
						||
| 
								 | 
							
								    this.emit('error', new ArchiverError('FORMATSET'));
							 | 
						||
| 
								 | 
							
								    return this;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._format = format;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Sets the module used for archiving.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param {Function} module The function for archiver to interact with.
							 | 
						||
| 
								 | 
							
								 * @return {this}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype.setModule = function(module) {
							 | 
						||
| 
								 | 
							
								  if (this._state.aborted) {
							 | 
						||
| 
								 | 
							
								    this.emit('error', new ArchiverError('ABORTED'));
							 | 
						||
| 
								 | 
							
								    return this;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (this._state.module) {
							 | 
						||
| 
								 | 
							
								    this.emit('error', new ArchiverError('MODULESET'));
							 | 
						||
| 
								 | 
							
								    return this;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._module = module;
							 | 
						||
| 
								 | 
							
								  this._modulePipe();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Appends a symlink to the instance.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * This does NOT interact with filesystem and is used for programmatically creating symlinks.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @param  {String} filepath The symlink path (within archive).
							 | 
						||
| 
								 | 
							
								 * @param  {String} target The target path (within archive).
							 | 
						||
| 
								 | 
							
								 * @param  {Number} mode Sets the entry permissions.
							 | 
						||
| 
								 | 
							
								 * @return {this}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype.symlink = function(filepath, target, mode) {
							 | 
						||
| 
								 | 
							
								  if (this._state.finalize || this._state.aborted) {
							 | 
						||
| 
								 | 
							
								    this.emit('error', new ArchiverError('QUEUECLOSED'));
							 | 
						||
| 
								 | 
							
								    return this;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (typeof filepath !== 'string' || filepath.length === 0) {
							 | 
						||
| 
								 | 
							
								    this.emit('error', new ArchiverError('SYMLINKFILEPATHREQUIRED'));
							 | 
						||
| 
								 | 
							
								    return this;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (typeof target !== 'string' || target.length === 0) {
							 | 
						||
| 
								 | 
							
								    this.emit('error', new ArchiverError('SYMLINKTARGETREQUIRED', { filepath: filepath }));
							 | 
						||
| 
								 | 
							
								    return this;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!this._moduleSupports('symlink')) {
							 | 
						||
| 
								 | 
							
								    this.emit('error', new ArchiverError('SYMLINKNOTSUPPORTED', { filepath: filepath }));
							 | 
						||
| 
								 | 
							
								    return this;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var data = {};
							 | 
						||
| 
								 | 
							
								  data.type = 'symlink';
							 | 
						||
| 
								 | 
							
								  data.name = filepath.replace(/\\/g, '/');
							 | 
						||
| 
								 | 
							
								  data.linkname = target.replace(/\\/g, '/');
							 | 
						||
| 
								 | 
							
								  data.sourceType = 'buffer';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (typeof mode === "number") {
							 | 
						||
| 
								 | 
							
								    data.mode = mode;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._entriesCount++;
							 | 
						||
| 
								 | 
							
								  this._queue.push({
							 | 
						||
| 
								 | 
							
								    data: data,
							 | 
						||
| 
								 | 
							
								    source: Buffer.concat([])
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Returns the current length (in bytes) that has been emitted.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @return {Number}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype.pointer = function() {
							 | 
						||
| 
								 | 
							
								  return this._pointer;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * Middleware-like helper that has yet to be fully implemented.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * @private
							 | 
						||
| 
								 | 
							
								 * @param  {Function} plugin
							 | 
						||
| 
								 | 
							
								 * @return {this}
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								Archiver.prototype.use = function(plugin) {
							 | 
						||
| 
								 | 
							
								  this._streams.push(plugin);
							 | 
						||
| 
								 | 
							
								  return this;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = Archiver;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * @typedef {Object} CoreOptions
							 | 
						||
| 
								 | 
							
								 * @global
							 | 
						||
| 
								 | 
							
								 * @property {Number} [statConcurrency=4] Sets the number of workers used to
							 | 
						||
| 
								 | 
							
								 * process the internal fs stat queue.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * @typedef {Object} TransformOptions
							 | 
						||
| 
								 | 
							
								 * @property {Boolean} [allowHalfOpen=true] If set to false, then the stream
							 | 
						||
| 
								 | 
							
								 * will automatically end the readable side when the writable side ends and vice
							 | 
						||
| 
								 | 
							
								 * versa.
							 | 
						||
| 
								 | 
							
								 * @property {Boolean} [readableObjectMode=false] Sets objectMode for readable
							 | 
						||
| 
								 | 
							
								 * side of the stream. Has no effect if objectMode is true.
							 | 
						||
| 
								 | 
							
								 * @property {Boolean} [writableObjectMode=false] Sets objectMode for writable
							 | 
						||
| 
								 | 
							
								 * side of the stream. Has no effect if objectMode is true.
							 | 
						||
| 
								 | 
							
								 * @property {Boolean} [decodeStrings=true] Whether or not to decode strings
							 | 
						||
| 
								 | 
							
								 * into Buffers before passing them to _write(). `Writable`
							 | 
						||
| 
								 | 
							
								 * @property {String} [encoding=NULL] If specified, then buffers will be decoded
							 | 
						||
| 
								 | 
							
								 * to strings using the specified encoding. `Readable`
							 | 
						||
| 
								 | 
							
								 * @property {Number} [highWaterMark=16kb] The maximum number of bytes to store
							 | 
						||
| 
								 | 
							
								 * in the internal buffer before ceasing to read from the underlying resource.
							 | 
						||
| 
								 | 
							
								 * `Readable` `Writable`
							 | 
						||
| 
								 | 
							
								 * @property {Boolean} [objectMode=false] Whether this stream should behave as a
							 | 
						||
| 
								 | 
							
								 * stream of objects. Meaning that stream.read(n) returns a single value instead
							 | 
						||
| 
								 | 
							
								 * of a Buffer of size n. `Readable` `Writable`
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * @typedef {Object} EntryData
							 | 
						||
| 
								 | 
							
								 * @property {String} name Sets the entry name including internal path.
							 | 
						||
| 
								 | 
							
								 * @property {(String|Date)} [date=NOW()] Sets the entry date.
							 | 
						||
| 
								 | 
							
								 * @property {Number} [mode=D:0755/F:0644] Sets the entry permissions.
							 | 
						||
| 
								 | 
							
								 * @property {String} [prefix] Sets a path prefix for the entry name. Useful
							 | 
						||
| 
								 | 
							
								 * when working with methods like `directory` or `glob`.
							 | 
						||
| 
								 | 
							
								 * @property {fs.Stats} [stats] Sets the fs stat data for this entry allowing
							 | 
						||
| 
								 | 
							
								 * for reduction of fs stat calls when stat data is already known.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * @typedef {Object} ErrorData
							 | 
						||
| 
								 | 
							
								 * @property {String} message The message of the error.
							 | 
						||
| 
								 | 
							
								 * @property {String} code The error code assigned to this error.
							 | 
						||
| 
								 | 
							
								 * @property {String} data Additional data provided for reporting or debugging (where available).
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * @typedef {Object} ProgressData
							 | 
						||
| 
								 | 
							
								 * @property {Object} entries
							 | 
						||
| 
								 | 
							
								 * @property {Number} entries.total Number of entries that have been appended.
							 | 
						||
| 
								 | 
							
								 * @property {Number} entries.processed Number of entries that have been processed.
							 | 
						||
| 
								 | 
							
								 * @property {Object} fs
							 | 
						||
| 
								 | 
							
								 * @property {Number} fs.totalBytes Number of bytes that have been appended. Calculated asynchronously and might not be accurate: it growth while entries are added. (based on fs.Stats)
							 | 
						||
| 
								 | 
							
								 * @property {Number} fs.processedBytes Number of bytes that have been processed. (based on fs.Stats)
							 | 
						||
| 
								 | 
							
								 */
							 |