Software zum Installieren eines Smart-Mirror Frameworks , zum Nutzen von hochschulrelevanten Informationen, auf einem Raspberry-Pi.
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.

core.js 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974
  1. /**
  2. * Archiver Core
  3. *
  4. * @ignore
  5. * @license [MIT]{@link https://github.com/archiverjs/node-archiver/blob/master/LICENSE}
  6. * @copyright (c) 2012-2014 Chris Talkington, contributors.
  7. */
  8. var fs = require('fs');
  9. var glob = require('readdir-glob');
  10. var async = require('async');
  11. var path = require('path');
  12. var util = require('archiver-utils');
  13. var inherits = require('util').inherits;
  14. var ArchiverError = require('./error');
  15. var Transform = require('readable-stream').Transform;
  16. var win32 = process.platform === 'win32';
  17. /**
  18. * @constructor
  19. * @param {String} format The archive format to use.
  20. * @param {(CoreOptions|TransformOptions)} options See also {@link ZipOptions} and {@link TarOptions}.
  21. */
  22. var Archiver = function(format, options) {
  23. if (!(this instanceof Archiver)) {
  24. return new Archiver(format, options);
  25. }
  26. if (typeof format !== 'string') {
  27. options = format;
  28. format = 'zip';
  29. }
  30. options = this.options = util.defaults(options, {
  31. highWaterMark: 1024 * 1024,
  32. statConcurrency: 4
  33. });
  34. Transform.call(this, options);
  35. this._format = false;
  36. this._module = false;
  37. this._pending = 0;
  38. this._pointer = 0;
  39. this._entriesCount = 0;
  40. this._entriesProcessedCount = 0;
  41. this._fsEntriesTotalBytes = 0;
  42. this._fsEntriesProcessedBytes = 0;
  43. this._queue = async.queue(this._onQueueTask.bind(this), 1);
  44. this._queue.drain(this._onQueueDrain.bind(this));
  45. this._statQueue = async.queue(this._onStatQueueTask.bind(this), options.statConcurrency);
  46. this._statQueue.drain(this._onQueueDrain.bind(this));
  47. this._state = {
  48. aborted: false,
  49. finalize: false,
  50. finalizing: false,
  51. finalized: false,
  52. modulePiped: false
  53. };
  54. this._streams = [];
  55. };
  56. inherits(Archiver, Transform);
  57. /**
  58. * Internal logic for `abort`.
  59. *
  60. * @private
  61. * @return void
  62. */
  63. Archiver.prototype._abort = function() {
  64. this._state.aborted = true;
  65. this._queue.kill();
  66. this._statQueue.kill();
  67. if (this._queue.idle()) {
  68. this._shutdown();
  69. }
  70. };
  71. /**
  72. * Internal helper for appending files.
  73. *
  74. * @private
  75. * @param {String} filepath The source filepath.
  76. * @param {EntryData} data The entry data.
  77. * @return void
  78. */
  79. Archiver.prototype._append = function(filepath, data) {
  80. data = data || {};
  81. var task = {
  82. source: null,
  83. filepath: filepath
  84. };
  85. if (!data.name) {
  86. data.name = filepath;
  87. }
  88. data.sourcePath = filepath;
  89. task.data = data;
  90. this._entriesCount++;
  91. if (data.stats && data.stats instanceof fs.Stats) {
  92. task = this._updateQueueTaskWithStats(task, data.stats);
  93. if (task) {
  94. if (data.stats.size) {
  95. this._fsEntriesTotalBytes += data.stats.size;
  96. }
  97. this._queue.push(task);
  98. }
  99. } else {
  100. this._statQueue.push(task);
  101. }
  102. };
  103. /**
  104. * Internal logic for `finalize`.
  105. *
  106. * @private
  107. * @return void
  108. */
  109. Archiver.prototype._finalize = function() {
  110. if (this._state.finalizing || this._state.finalized || this._state.aborted) {
  111. return;
  112. }
  113. this._state.finalizing = true;
  114. this._moduleFinalize();
  115. this._state.finalizing = false;
  116. this._state.finalized = true;
  117. };
  118. /**
  119. * Checks the various state variables to determine if we can `finalize`.
  120. *
  121. * @private
  122. * @return {Boolean}
  123. */
  124. Archiver.prototype._maybeFinalize = function() {
  125. if (this._state.finalizing || this._state.finalized || this._state.aborted) {
  126. return false;
  127. }
  128. if (this._state.finalize && this._pending === 0 && this._queue.idle() && this._statQueue.idle()) {
  129. this._finalize();
  130. return true;
  131. }
  132. return false;
  133. };
  134. /**
  135. * Appends an entry to the module.
  136. *
  137. * @private
  138. * @fires Archiver#entry
  139. * @param {(Buffer|Stream)} source
  140. * @param {EntryData} data
  141. * @param {Function} callback
  142. * @return void
  143. */
  144. Archiver.prototype._moduleAppend = function(source, data, callback) {
  145. if (this._state.aborted) {
  146. callback();
  147. return;
  148. }
  149. this._module.append(source, data, function(err) {
  150. this._task = null;
  151. if (this._state.aborted) {
  152. this._shutdown();
  153. return;
  154. }
  155. if (err) {
  156. this.emit('error', err);
  157. setImmediate(callback);
  158. return;
  159. }
  160. /**
  161. * Fires when the entry's input has been processed and appended to the archive.
  162. *
  163. * @event Archiver#entry
  164. * @type {EntryData}
  165. */
  166. this.emit('entry', data);
  167. this._entriesProcessedCount++;
  168. if (data.stats && data.stats.size) {
  169. this._fsEntriesProcessedBytes += data.stats.size;
  170. }
  171. /**
  172. * @event Archiver#progress
  173. * @type {ProgressData}
  174. */
  175. this.emit('progress', {
  176. entries: {
  177. total: this._entriesCount,
  178. processed: this._entriesProcessedCount
  179. },
  180. fs: {
  181. totalBytes: this._fsEntriesTotalBytes,
  182. processedBytes: this._fsEntriesProcessedBytes
  183. }
  184. });
  185. setImmediate(callback);
  186. }.bind(this));
  187. };
  188. /**
  189. * Finalizes the module.
  190. *
  191. * @private
  192. * @return void
  193. */
  194. Archiver.prototype._moduleFinalize = function() {
  195. if (typeof this._module.finalize === 'function') {
  196. this._module.finalize();
  197. } else if (typeof this._module.end === 'function') {
  198. this._module.end();
  199. } else {
  200. this.emit('error', new ArchiverError('NOENDMETHOD'));
  201. }
  202. };
  203. /**
  204. * Pipes the module to our internal stream with error bubbling.
  205. *
  206. * @private
  207. * @return void
  208. */
  209. Archiver.prototype._modulePipe = function() {
  210. this._module.on('error', this._onModuleError.bind(this));
  211. this._module.pipe(this);
  212. this._state.modulePiped = true;
  213. };
  214. /**
  215. * Determines if the current module supports a defined feature.
  216. *
  217. * @private
  218. * @param {String} key
  219. * @return {Boolean}
  220. */
  221. Archiver.prototype._moduleSupports = function(key) {
  222. if (!this._module.supports || !this._module.supports[key]) {
  223. return false;
  224. }
  225. return this._module.supports[key];
  226. };
  227. /**
  228. * Unpipes the module from our internal stream.
  229. *
  230. * @private
  231. * @return void
  232. */
  233. Archiver.prototype._moduleUnpipe = function() {
  234. this._module.unpipe(this);
  235. this._state.modulePiped = false;
  236. };
  237. /**
  238. * Normalizes entry data with fallbacks for key properties.
  239. *
  240. * @private
  241. * @param {Object} data
  242. * @param {fs.Stats} stats
  243. * @return {Object}
  244. */
  245. Archiver.prototype._normalizeEntryData = function(data, stats) {
  246. data = util.defaults(data, {
  247. type: 'file',
  248. name: null,
  249. date: null,
  250. mode: null,
  251. prefix: null,
  252. sourcePath: null,
  253. stats: false
  254. });
  255. if (stats && data.stats === false) {
  256. data.stats = stats;
  257. }
  258. var isDir = data.type === 'directory';
  259. if (data.name) {
  260. if (typeof data.prefix === 'string' && '' !== data.prefix) {
  261. data.name = data.prefix + '/' + data.name;
  262. data.prefix = null;
  263. }
  264. data.name = util.sanitizePath(data.name);
  265. if (data.type !== 'symlink' && data.name.slice(-1) === '/') {
  266. isDir = true;
  267. data.type = 'directory';
  268. } else if (isDir) {
  269. data.name += '/';
  270. }
  271. }
  272. // 511 === 0777; 493 === 0755; 438 === 0666; 420 === 0644
  273. if (typeof data.mode === 'number') {
  274. if (win32) {
  275. data.mode &= 511;
  276. } else {
  277. data.mode &= 4095
  278. }
  279. } else if (data.stats && data.mode === null) {
  280. if (win32) {
  281. data.mode = data.stats.mode & 511;
  282. } else {
  283. data.mode = data.stats.mode & 4095;
  284. }
  285. // stat isn't reliable on windows; force 0755 for dir
  286. if (win32 && isDir) {
  287. data.mode = 493;
  288. }
  289. } else if (data.mode === null) {
  290. data.mode = isDir ? 493 : 420;
  291. }
  292. if (data.stats && data.date === null) {
  293. data.date = data.stats.mtime;
  294. } else {
  295. data.date = util.dateify(data.date);
  296. }
  297. return data;
  298. };
  299. /**
  300. * Error listener that re-emits error on to our internal stream.
  301. *
  302. * @private
  303. * @param {Error} err
  304. * @return void
  305. */
  306. Archiver.prototype._onModuleError = function(err) {
  307. /**
  308. * @event Archiver#error
  309. * @type {ErrorData}
  310. */
  311. this.emit('error', err);
  312. };
  313. /**
  314. * Checks the various state variables after queue has drained to determine if
  315. * we need to `finalize`.
  316. *
  317. * @private
  318. * @return void
  319. */
  320. Archiver.prototype._onQueueDrain = function() {
  321. if (this._state.finalizing || this._state.finalized || this._state.aborted) {
  322. return;
  323. }
  324. if (this._state.finalize && this._pending === 0 && this._queue.idle() && this._statQueue.idle()) {
  325. this._finalize();
  326. }
  327. };
  328. /**
  329. * Appends each queue task to the module.
  330. *
  331. * @private
  332. * @param {Object} task
  333. * @param {Function} callback
  334. * @return void
  335. */
  336. Archiver.prototype._onQueueTask = function(task, callback) {
  337. var fullCallback = () => {
  338. if(task.data.callback) {
  339. task.data.callback();
  340. }
  341. callback();
  342. }
  343. if (this._state.finalizing || this._state.finalized || this._state.aborted) {
  344. fullCallback();
  345. return;
  346. }
  347. this._task = task;
  348. this._moduleAppend(task.source, task.data, fullCallback);
  349. };
  350. /**
  351. * Performs a file stat and reinjects the task back into the queue.
  352. *
  353. * @private
  354. * @param {Object} task
  355. * @param {Function} callback
  356. * @return void
  357. */
  358. Archiver.prototype._onStatQueueTask = function(task, callback) {
  359. if (this._state.finalizing || this._state.finalized || this._state.aborted) {
  360. callback();
  361. return;
  362. }
  363. fs.lstat(task.filepath, function(err, stats) {
  364. if (this._state.aborted) {
  365. setImmediate(callback);
  366. return;
  367. }
  368. if (err) {
  369. this._entriesCount--;
  370. /**
  371. * @event Archiver#warning
  372. * @type {ErrorData}
  373. */
  374. this.emit('warning', err);
  375. setImmediate(callback);
  376. return;
  377. }
  378. task = this._updateQueueTaskWithStats(task, stats);
  379. if (task) {
  380. if (stats.size) {
  381. this._fsEntriesTotalBytes += stats.size;
  382. }
  383. this._queue.push(task);
  384. }
  385. setImmediate(callback);
  386. }.bind(this));
  387. };
  388. /**
  389. * Unpipes the module and ends our internal stream.
  390. *
  391. * @private
  392. * @return void
  393. */
  394. Archiver.prototype._shutdown = function() {
  395. this._moduleUnpipe();
  396. this.end();
  397. };
  398. /**
  399. * Tracks the bytes emitted by our internal stream.
  400. *
  401. * @private
  402. * @param {Buffer} chunk
  403. * @param {String} encoding
  404. * @param {Function} callback
  405. * @return void
  406. */
  407. Archiver.prototype._transform = function(chunk, encoding, callback) {
  408. if (chunk) {
  409. this._pointer += chunk.length;
  410. }
  411. callback(null, chunk);
  412. };
  413. /**
  414. * Updates and normalizes a queue task using stats data.
  415. *
  416. * @private
  417. * @param {Object} task
  418. * @param {fs.Stats} stats
  419. * @return {Object}
  420. */
  421. Archiver.prototype._updateQueueTaskWithStats = function(task, stats) {
  422. if (stats.isFile()) {
  423. task.data.type = 'file';
  424. task.data.sourceType = 'stream';
  425. task.source = util.lazyReadStream(task.filepath);
  426. } else if (stats.isDirectory() && this._moduleSupports('directory')) {
  427. task.data.name = util.trailingSlashIt(task.data.name);
  428. task.data.type = 'directory';
  429. task.data.sourcePath = util.trailingSlashIt(task.filepath);
  430. task.data.sourceType = 'buffer';
  431. task.source = Buffer.concat([]);
  432. } else if (stats.isSymbolicLink() && this._moduleSupports('symlink')) {
  433. var linkPath = fs.readlinkSync(task.filepath);
  434. var dirName = path.dirname(task.filepath);
  435. task.data.type = 'symlink';
  436. task.data.linkname = path.relative(dirName, path.resolve(dirName, linkPath));
  437. task.data.sourceType = 'buffer';
  438. task.source = Buffer.concat([]);
  439. } else {
  440. if (stats.isDirectory()) {
  441. this.emit('warning', new ArchiverError('DIRECTORYNOTSUPPORTED', task.data));
  442. } else if (stats.isSymbolicLink()) {
  443. this.emit('warning', new ArchiverError('SYMLINKNOTSUPPORTED', task.data));
  444. } else {
  445. this.emit('warning', new ArchiverError('ENTRYNOTSUPPORTED', task.data));
  446. }
  447. return null;
  448. }
  449. task.data = this._normalizeEntryData(task.data, stats);
  450. return task;
  451. };
  452. /**
  453. * Aborts the archiving process, taking a best-effort approach, by:
  454. *
  455. * - removing any pending queue tasks
  456. * - allowing any active queue workers to finish
  457. * - detaching internal module pipes
  458. * - ending both sides of the Transform stream
  459. *
  460. * It will NOT drain any remaining sources.
  461. *
  462. * @return {this}
  463. */
  464. Archiver.prototype.abort = function() {
  465. if (this._state.aborted || this._state.finalized) {
  466. return this;
  467. }
  468. this._abort();
  469. return this;
  470. };
  471. /**
  472. * Appends an input source (text string, buffer, or stream) to the instance.
  473. *
  474. * When the instance has received, processed, and emitted the input, the `entry`
  475. * event is fired.
  476. *
  477. * @fires Archiver#entry
  478. * @param {(Buffer|Stream|String)} source The input source.
  479. * @param {EntryData} data See also {@link ZipEntryData} and {@link TarEntryData}.
  480. * @return {this}
  481. */
  482. Archiver.prototype.append = function(source, data) {
  483. if (this._state.finalize || this._state.aborted) {
  484. this.emit('error', new ArchiverError('QUEUECLOSED'));
  485. return this;
  486. }
  487. data = this._normalizeEntryData(data);
  488. if (typeof data.name !== 'string' || data.name.length === 0) {
  489. this.emit('error', new ArchiverError('ENTRYNAMEREQUIRED'));
  490. return this;
  491. }
  492. if (data.type === 'directory' && !this._moduleSupports('directory')) {
  493. this.emit('error', new ArchiverError('DIRECTORYNOTSUPPORTED', { name: data.name }));
  494. return this;
  495. }
  496. source = util.normalizeInputSource(source);
  497. if (Buffer.isBuffer(source)) {
  498. data.sourceType = 'buffer';
  499. } else if (util.isStream(source)) {
  500. data.sourceType = 'stream';
  501. } else {
  502. this.emit('error', new ArchiverError('INPUTSTEAMBUFFERREQUIRED', { name: data.name }));
  503. return this;
  504. }
  505. this._entriesCount++;
  506. this._queue.push({
  507. data: data,
  508. source: source
  509. });
  510. return this;
  511. };
  512. /**
  513. * Appends a directory and its files, recursively, given its dirpath.
  514. *
  515. * @param {String} dirpath The source directory path.
  516. * @param {String} destpath The destination path within the archive.
  517. * @param {(EntryData|Function)} data See also [ZipEntryData]{@link ZipEntryData} and
  518. * [TarEntryData]{@link TarEntryData}.
  519. * @return {this}
  520. */
  521. Archiver.prototype.directory = function(dirpath, destpath, data) {
  522. if (this._state.finalize || this._state.aborted) {
  523. this.emit('error', new ArchiverError('QUEUECLOSED'));
  524. return this;
  525. }
  526. if (typeof dirpath !== 'string' || dirpath.length === 0) {
  527. this.emit('error', new ArchiverError('DIRECTORYDIRPATHREQUIRED'));
  528. return this;
  529. }
  530. this._pending++;
  531. if (destpath === false) {
  532. destpath = '';
  533. } else if (typeof destpath !== 'string'){
  534. destpath = dirpath;
  535. }
  536. var dataFunction = false;
  537. if (typeof data === 'function') {
  538. dataFunction = data;
  539. data = {};
  540. } else if (typeof data !== 'object') {
  541. data = {};
  542. }
  543. var globOptions = {
  544. stat: true,
  545. dot: true
  546. };
  547. function onGlobEnd() {
  548. this._pending--;
  549. this._maybeFinalize();
  550. }
  551. function onGlobError(err) {
  552. this.emit('error', err);
  553. }
  554. function onGlobMatch(match){
  555. globber.pause();
  556. var ignoreMatch = false;
  557. var entryData = Object.assign({}, data);
  558. entryData.name = match.relative;
  559. entryData.prefix = destpath;
  560. entryData.stats = match.stat;
  561. entryData.callback = globber.resume.bind(globber);
  562. try {
  563. if (dataFunction) {
  564. entryData = dataFunction(entryData);
  565. if (entryData === false) {
  566. ignoreMatch = true;
  567. } else if (typeof entryData !== 'object') {
  568. throw new ArchiverError('DIRECTORYFUNCTIONINVALIDDATA', { dirpath: dirpath });
  569. }
  570. }
  571. } catch(e) {
  572. this.emit('error', e);
  573. return;
  574. }
  575. if (ignoreMatch) {
  576. globber.resume();
  577. return;
  578. }
  579. this._append(match.absolute, entryData);
  580. }
  581. var globber = glob(dirpath, globOptions);
  582. globber.on('error', onGlobError.bind(this));
  583. globber.on('match', onGlobMatch.bind(this));
  584. globber.on('end', onGlobEnd.bind(this));
  585. return this;
  586. };
  587. /**
  588. * Appends a file given its filepath using a
  589. * [lazystream]{@link https://github.com/jpommerening/node-lazystream} wrapper to
  590. * prevent issues with open file limits.
  591. *
  592. * When the instance has received, processed, and emitted the file, the `entry`
  593. * event is fired.
  594. *
  595. * @param {String} filepath The source filepath.
  596. * @param {EntryData} data See also [ZipEntryData]{@link ZipEntryData} and
  597. * [TarEntryData]{@link TarEntryData}.
  598. * @return {this}
  599. */
  600. Archiver.prototype.file = function(filepath, data) {
  601. if (this._state.finalize || this._state.aborted) {
  602. this.emit('error', new ArchiverError('QUEUECLOSED'));
  603. return this;
  604. }
  605. if (typeof filepath !== 'string' || filepath.length === 0) {
  606. this.emit('error', new ArchiverError('FILEFILEPATHREQUIRED'));
  607. return this;
  608. }
  609. this._append(filepath, data);
  610. return this;
  611. };
  612. /**
  613. * Appends multiple files that match a glob pattern.
  614. *
  615. * @param {String} pattern The [glob pattern]{@link https://github.com/isaacs/minimatch} to match.
  616. * @param {Object} options See [node-readdir-glob]{@link https://github.com/yqnn/node-readdir-glob#options}.
  617. * @param {EntryData} data See also [ZipEntryData]{@link ZipEntryData} and
  618. * [TarEntryData]{@link TarEntryData}.
  619. * @return {this}
  620. */
  621. Archiver.prototype.glob = function(pattern, options, data) {
  622. this._pending++;
  623. options = util.defaults(options, {
  624. stat: true,
  625. pattern: pattern
  626. });
  627. function onGlobEnd() {
  628. this._pending--;
  629. this._maybeFinalize();
  630. }
  631. function onGlobError(err) {
  632. this.emit('error', err);
  633. }
  634. function onGlobMatch(match){
  635. globber.pause();
  636. var entryData = Object.assign({}, data);
  637. entryData.callback = globber.resume.bind(globber);
  638. entryData.stats = match.stat;
  639. entryData.name = match.relative;
  640. this._append(match.absolute, entryData);
  641. }
  642. var globber = glob(options.cwd || '.', options);
  643. globber.on('error', onGlobError.bind(this));
  644. globber.on('match', onGlobMatch.bind(this));
  645. globber.on('end', onGlobEnd.bind(this));
  646. return this;
  647. };
  648. /**
  649. * Finalizes the instance and prevents further appending to the archive
  650. * structure (queue will continue til drained).
  651. *
  652. * The `end`, `close` or `finish` events on the destination stream may fire
  653. * right after calling this method so you should set listeners beforehand to
  654. * properly detect stream completion.
  655. *
  656. * @return {Promise}
  657. */
  658. Archiver.prototype.finalize = function() {
  659. if (this._state.aborted) {
  660. var abortedError = new ArchiverError('ABORTED');
  661. this.emit('error', abortedError);
  662. return Promise.reject(abortedError);
  663. }
  664. if (this._state.finalize) {
  665. var finalizingError = new ArchiverError('FINALIZING');
  666. this.emit('error', finalizingError);
  667. return Promise.reject(finalizingError);
  668. }
  669. this._state.finalize = true;
  670. if (this._pending === 0 && this._queue.idle() && this._statQueue.idle()) {
  671. this._finalize();
  672. }
  673. var self = this;
  674. return new Promise(function(resolve, reject) {
  675. var errored;
  676. self._module.on('end', function() {
  677. if (!errored) {
  678. resolve();
  679. }
  680. })
  681. self._module.on('error', function(err) {
  682. errored = true;
  683. reject(err);
  684. })
  685. })
  686. };
  687. /**
  688. * Sets the module format name used for archiving.
  689. *
  690. * @param {String} format The name of the format.
  691. * @return {this}
  692. */
  693. Archiver.prototype.setFormat = function(format) {
  694. if (this._format) {
  695. this.emit('error', new ArchiverError('FORMATSET'));
  696. return this;
  697. }
  698. this._format = format;
  699. return this;
  700. };
  701. /**
  702. * Sets the module used for archiving.
  703. *
  704. * @param {Function} module The function for archiver to interact with.
  705. * @return {this}
  706. */
  707. Archiver.prototype.setModule = function(module) {
  708. if (this._state.aborted) {
  709. this.emit('error', new ArchiverError('ABORTED'));
  710. return this;
  711. }
  712. if (this._state.module) {
  713. this.emit('error', new ArchiverError('MODULESET'));
  714. return this;
  715. }
  716. this._module = module;
  717. this._modulePipe();
  718. return this;
  719. };
  720. /**
  721. * Appends a symlink to the instance.
  722. *
  723. * This does NOT interact with filesystem and is used for programmatically creating symlinks.
  724. *
  725. * @param {String} filepath The symlink path (within archive).
  726. * @param {String} target The target path (within archive).
  727. * @param {Number} mode Sets the entry permissions.
  728. * @return {this}
  729. */
  730. Archiver.prototype.symlink = function(filepath, target, mode) {
  731. if (this._state.finalize || this._state.aborted) {
  732. this.emit('error', new ArchiverError('QUEUECLOSED'));
  733. return this;
  734. }
  735. if (typeof filepath !== 'string' || filepath.length === 0) {
  736. this.emit('error', new ArchiverError('SYMLINKFILEPATHREQUIRED'));
  737. return this;
  738. }
  739. if (typeof target !== 'string' || target.length === 0) {
  740. this.emit('error', new ArchiverError('SYMLINKTARGETREQUIRED', { filepath: filepath }));
  741. return this;
  742. }
  743. if (!this._moduleSupports('symlink')) {
  744. this.emit('error', new ArchiverError('SYMLINKNOTSUPPORTED', { filepath: filepath }));
  745. return this;
  746. }
  747. var data = {};
  748. data.type = 'symlink';
  749. data.name = filepath.replace(/\\/g, '/');
  750. data.linkname = target.replace(/\\/g, '/');
  751. data.sourceType = 'buffer';
  752. if (typeof mode === "number") {
  753. data.mode = mode;
  754. }
  755. this._entriesCount++;
  756. this._queue.push({
  757. data: data,
  758. source: Buffer.concat([])
  759. });
  760. return this;
  761. };
  762. /**
  763. * Returns the current length (in bytes) that has been emitted.
  764. *
  765. * @return {Number}
  766. */
  767. Archiver.prototype.pointer = function() {
  768. return this._pointer;
  769. };
  770. /**
  771. * Middleware-like helper that has yet to be fully implemented.
  772. *
  773. * @private
  774. * @param {Function} plugin
  775. * @return {this}
  776. */
  777. Archiver.prototype.use = function(plugin) {
  778. this._streams.push(plugin);
  779. return this;
  780. };
  781. module.exports = Archiver;
  782. /**
  783. * @typedef {Object} CoreOptions
  784. * @global
  785. * @property {Number} [statConcurrency=4] Sets the number of workers used to
  786. * process the internal fs stat queue.
  787. */
  788. /**
  789. * @typedef {Object} TransformOptions
  790. * @property {Boolean} [allowHalfOpen=true] If set to false, then the stream
  791. * will automatically end the readable side when the writable side ends and vice
  792. * versa.
  793. * @property {Boolean} [readableObjectMode=false] Sets objectMode for readable
  794. * side of the stream. Has no effect if objectMode is true.
  795. * @property {Boolean} [writableObjectMode=false] Sets objectMode for writable
  796. * side of the stream. Has no effect if objectMode is true.
  797. * @property {Boolean} [decodeStrings=true] Whether or not to decode strings
  798. * into Buffers before passing them to _write(). `Writable`
  799. * @property {String} [encoding=NULL] If specified, then buffers will be decoded
  800. * to strings using the specified encoding. `Readable`
  801. * @property {Number} [highWaterMark=16kb] The maximum number of bytes to store
  802. * in the internal buffer before ceasing to read from the underlying resource.
  803. * `Readable` `Writable`
  804. * @property {Boolean} [objectMode=false] Whether this stream should behave as a
  805. * stream of objects. Meaning that stream.read(n) returns a single value instead
  806. * of a Buffer of size n. `Readable` `Writable`
  807. */
  808. /**
  809. * @typedef {Object} EntryData
  810. * @property {String} name Sets the entry name including internal path.
  811. * @property {(String|Date)} [date=NOW()] Sets the entry date.
  812. * @property {Number} [mode=D:0755/F:0644] Sets the entry permissions.
  813. * @property {String} [prefix] Sets a path prefix for the entry name. Useful
  814. * when working with methods like `directory` or `glob`.
  815. * @property {fs.Stats} [stats] Sets the fs stat data for this entry allowing
  816. * for reduction of fs stat calls when stat data is already known.
  817. */
  818. /**
  819. * @typedef {Object} ErrorData
  820. * @property {String} message The message of the error.
  821. * @property {String} code The error code assigned to this error.
  822. * @property {String} data Additional data provided for reporting or debugging (where available).
  823. */
  824. /**
  825. * @typedef {Object} ProgressData
  826. * @property {Object} entries
  827. * @property {Number} entries.total Number of entries that have been appended.
  828. * @property {Number} entries.processed Number of entries that have been processed.
  829. * @property {Object} fs
  830. * @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)
  831. * @property {Number} fs.processedBytes Number of bytes that have been processed. (based on fs.Stats)
  832. */