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