codekingpro/portable-devtools
115k
1"use strict";
2// A readable tar stream creator
3// Technically, this is a transform stream that you write paths into,
4// and tar format comes out of.
5// The `add()` method is like `write()` but returns this,
6// and end() return `this` as well, so you can
7// do `new Pack(opt).add('files').add('dir').end().pipe(output)
8// You could also do something like:
9// streamOfPaths().pipe(new Pack()).pipe(new fs.WriteStream('out.tar'))
10var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11 if (k2 === undefined) k2 = k;
12 var desc = Object.getOwnPropertyDescriptor(m, k);
13 if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14 desc = { enumerable: true, get: function() { return m[k]; } };
15 }
16 Object.defineProperty(o, k2, desc);
17}) : (function(o, m, k, k2) {
18 if (k2 === undefined) k2 = k;
19 o[k2] = m[k];
20}));
21var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22 Object.defineProperty(o, "default", { enumerable: true, value: v });
23}) : function(o, v) {
24 o["default"] = v;
25});
26var __importStar = (this && this.__importStar) || (function () {
27 var ownKeys = function(o) {
28 ownKeys = Object.getOwnPropertyNames || function (o) {
29 var ar = [];
30 for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31 return ar;
32 };
33 return ownKeys(o);
34 };
35 return function (mod) {
36 if (mod && mod.__esModule) return mod;
37 var result = {};
38 if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39 __setModuleDefault(result, mod);
40 return result;
41 };
42})();
43var __importDefault = (this && this.__importDefault) || function (mod) {
44 return (mod && mod.__esModule) ? mod : { "default": mod };
45};
46Object.defineProperty(exports, "__esModule", { value: true });
47exports.PackSync = exports.Pack = exports.PackJob = void 0;
48const fs_1 = __importDefault(require("fs"));
49const write_entry_js_1 = require("./write-entry.js");
50class PackJob {
51 path;
52 absolute;
53 entry;
54 stat;
55 readdir;
56 pending = false;
57 ignore = false;
58 piped = false;
59 constructor(path, absolute) {
60 this.path = path || './';
61 this.absolute = absolute;
62 }
63}
64exports.PackJob = PackJob;
65const minipass_1 = require("minipass");
66const zlib = __importStar(require("minizlib"));
67const yallist_1 = require("yallist");
68const read_entry_js_1 = require("./read-entry.js");
69const warn_method_js_1 = require("./warn-method.js");
70const EOF = Buffer.alloc(1024);
71const ONSTAT = Symbol('onStat');
72const ENDED = Symbol('ended');
73const QUEUE = Symbol('queue');
74const CURRENT = Symbol('current');
75const PROCESS = Symbol('process');
76const PROCESSING = Symbol('processing');
77const PROCESSJOB = Symbol('processJob');
78const JOBS = Symbol('jobs');
79const JOBDONE = Symbol('jobDone');
80const ADDFSENTRY = Symbol('addFSEntry');
81const ADDTARENTRY = Symbol('addTarEntry');
82const STAT = Symbol('stat');
83const READDIR = Symbol('readdir');
84const ONREADDIR = Symbol('onreaddir');
85const PIPE = Symbol('pipe');
86const ENTRY = Symbol('entry');
87const ENTRYOPT = Symbol('entryOpt');
88const WRITEENTRYCLASS = Symbol('writeEntryClass');
89const WRITE = Symbol('write');
90const ONDRAIN = Symbol('ondrain');
91const path_1 = __importDefault(require("path"));
92const normalize_windows_path_js_1 = require("./normalize-windows-path.js");
93class Pack extends minipass_1.Minipass {
94 sync = false;
95 opt;
96 cwd;
97 maxReadSize;
98 preservePaths;
99 strict;
100 noPax;
101 prefix;
102 linkCache;
103 statCache;
104 file;
105 portable;
106 zip;
107 readdirCache;
108 noDirRecurse;
109 follow;
110 noMtime;
111 mtime;
112 filter;
113 jobs;
114 [WRITEENTRYCLASS];
115 onWriteEntry;
116 // Note: we actually DO need a linked list here, because we
117 // shift() to update the head of the list where we start, but still
118 // while that happens, need to know what the next item in the queue
119 // will be. Since we do multiple jobs in parallel, it's not as simple
120 // as just an Array.shift(), since that would lose the information about
121 // the next job in the list. We could add a .next field on the PackJob
122 // class, but then we'd have to be tracking the tail of the queue the
123 // whole time, and Yallist just does that for us anyway.
124 [QUEUE];
125 [JOBS] = 0;
126 [PROCESSING] = false;
127 [ENDED] = false;
128 constructor(opt = {}) {
129 //@ts-ignore
130 super();
131 this.opt = opt;
132 this.file = opt.file || '';
133 this.cwd = opt.cwd || process.cwd();
134 this.maxReadSize = opt.maxReadSize;
135 this.preservePaths = !!opt.preservePaths;
136 this.strict = !!opt.strict;
137 this.noPax = !!opt.noPax;
138 this.prefix = (0, normalize_windows_path_js_1.normalizeWindowsPath)(opt.prefix || '');
139 this.linkCache = opt.linkCache || new Map();
140 this.statCache = opt.statCache || new Map();
141 this.readdirCache = opt.readdirCache || new Map();
142 this.onWriteEntry = opt.onWriteEntry;
143 this[WRITEENTRYCLASS] = write_entry_js_1.WriteEntry;
144 if (typeof opt.onwarn === 'function') {
145 this.on('warn', opt.onwarn);
146 }
147 this.portable = !!opt.portable;
148 if (opt.gzip || opt.brotli || opt.zstd) {
149 if ((opt.gzip ? 1 : 0) +
150 (opt.brotli ? 1 : 0) +
151 (opt.zstd ? 1 : 0) >
152 1) {
153 throw new TypeError('gzip, brotli, zstd are mutually exclusive');
154 }
155 if (opt.gzip) {
156 if (typeof opt.gzip !== 'object') {
157 opt.gzip = {};
158 }
159 if (this.portable) {
160 opt.gzip.portable = true;
161 }
162 this.zip = new zlib.Gzip(opt.gzip);
163 }
164 if (opt.brotli) {
165 if (typeof opt.brotli !== 'object') {
166 opt.brotli = {};
167 }
168 this.zip = new zlib.BrotliCompress(opt.brotli);
169 }
170 if (opt.zstd) {
171 if (typeof opt.zstd !== 'object') {
172 opt.zstd = {};
173 }
174 this.zip = new zlib.ZstdCompress(opt.zstd);
175 }
176 /* c8 ignore next */
177 if (!this.zip)
178 throw new Error('impossible');
179 const zip = this.zip;
180 zip.on('data', chunk => super.write(chunk));
181 zip.on('end', () => super.end());
182 zip.on('drain', () => this[ONDRAIN]());
183 this.on('resume', () => zip.resume());
184 }
185 else {
186 this.on('drain', this[ONDRAIN]);
187 }
188 this.noDirRecurse = !!opt.noDirRecurse;
189 this.follow = !!opt.follow;
190 this.noMtime = !!opt.noMtime;
191 if (opt.mtime)
192 this.mtime = opt.mtime;
193 this.filter =
194 typeof opt.filter === 'function' ? opt.filter : () => true;
195 this[QUEUE] = new yallist_1.Yallist();
196 this[JOBS] = 0;
197 this.jobs = Number(opt.jobs) || 4;
198 this[PROCESSING] = false;
199 this[ENDED] = false;
200 }
201 [WRITE](chunk) {
202 return super.write(chunk);
203 }
204 add(path) {
205 this.write(path);
206 return this;
207 }
208 end(path, encoding, cb) {
209 /* c8 ignore start */
210 if (typeof path === 'function') {
211 cb = path;
212 path = undefined;
213 }
214 if (typeof encoding === 'function') {
215 cb = encoding;
216 encoding = undefined;
217 }
218 /* c8 ignore stop */
219 if (path) {
220 this.add(path);
221 }
222 this[ENDED] = true;
223 this[PROCESS]();
224 /* c8 ignore next */
225 if (cb)
226 cb();
227 return this;
228 }
229 write(path) {
230 if (this[ENDED]) {
231 throw new Error('write after end');
232 }
233 if (path instanceof read_entry_js_1.ReadEntry) {
234 this[ADDTARENTRY](path);
235 }
236 else {
237 this[ADDFSENTRY](path);
238 }
239 return this.flowing;
240 }
241 [ADDTARENTRY](p) {
242 const absolute = (0, normalize_windows_path_js_1.normalizeWindowsPath)(path_1.default.resolve(this.cwd, p.path));
243 // in this case, we don't have to wait for the stat
244 if (!this.filter(p.path, p)) {
245 p.resume();
246 }
247 else {
248 const job = new PackJob(p.path, absolute);
249 job.entry = new write_entry_js_1.WriteEntryTar(p, this[ENTRYOPT](job));
250 job.entry.on('end', () => this[JOBDONE](job));
251 this[JOBS] += 1;
252 this[QUEUE].push(job);
253 }
254 this[PROCESS]();
255 }
256 [ADDFSENTRY](p) {
257 const absolute = (0, normalize_windows_path_js_1.normalizeWindowsPath)(path_1.default.resolve(this.cwd, p));
258 this[QUEUE].push(new PackJob(p, absolute));
259 this[PROCESS]();
260 }
261 [STAT](job) {
262 job.pending = true;
263 this[JOBS] += 1;
264 const stat = this.follow ? 'stat' : 'lstat';
265 fs_1.default[stat](job.absolute, (er, stat) => {
266 job.pending = false;
267 this[JOBS] -= 1;
268 if (er) {
269 this.emit('error', er);
270 }
271 else {
272 this[ONSTAT](job, stat);
273 }
274 });
275 }
276 [ONSTAT](job, stat) {
277 this.statCache.set(job.absolute, stat);
278 job.stat = stat;
279 // now we have the stat, we can filter it.
280 if (!this.filter(job.path, stat)) {
281 job.ignore = true;
282 }
283 else if (stat.isFile() &&
284 stat.nlink > 1 &&
285 job === this[CURRENT] &&
286 !this.linkCache.get(`${stat.dev}:${stat.ino}`) &&
287 !this.sync) {
288 // if it's not filtered, and it's a new File entry,
289 // jump the queue in case any pending Link entries are about
290 // to try to link to it. This prevents a hardlink from coming ahead
291 // of its target in the archive.
292 this[PROCESSJOB](job);
293 }
294 this[PROCESS]();
295 }
296 [READDIR](job) {
297 job.pending = true;
298 this[JOBS] += 1;
299 fs_1.default.readdir(job.absolute, (er, entries) => {
300 job.pending = false;
301 this[JOBS] -= 1;
302 if (er) {
303 return this.emit('error', er);
304 }
305 this[ONREADDIR](job, entries);
306 });
307 }
308 [ONREADDIR](job, entries) {
309 this.readdirCache.set(job.absolute, entries);
310 job.readdir = entries;
311 this[PROCESS]();
312 }
313 [PROCESS]() {
314 if (this[PROCESSING]) {
315 return;
316 }
317 this[PROCESSING] = true;
318 for (let w = this[QUEUE].head; !!w && this[JOBS] < this.jobs; w = w.next) {
319 this[PROCESSJOB](w.value);
320 if (w.value.ignore) {
321 const p = w.next;
322 this[QUEUE].removeNode(w);
323 w.next = p;
324 }
325 }
326 this[PROCESSING] = false;
327 if (this[ENDED] && !this[QUEUE].length && this[JOBS] === 0) {
328 if (this.zip) {
329 this.zip.end(EOF);
330 }
331 else {
332 super.write(EOF);
333 super.end();
334 }
335 }
336 }
337 get [CURRENT]() {
338 return this[QUEUE] && this[QUEUE].head && this[QUEUE].head.value;
339 }
340 [JOBDONE](_job) {
341 this[QUEUE].shift();
342 this[JOBS] -= 1;
343 this[PROCESS]();
344 }
345 [PROCESSJOB](job) {
346 if (job.pending) {
347 return;
348 }
349 if (job.entry) {
350 if (job === this[CURRENT] && !job.piped) {
351 this[PIPE](job);
352 }
353 return;
354 }
355 if (!job.stat) {
356 const sc = this.statCache.get(job.absolute);
357 if (sc) {
358 this[ONSTAT](job, sc);
359 }
360 else {
361 this[STAT](job);
362 }
363 }
364 if (!job.stat) {
365 return;
366 }
367 // filtered out!
368 if (job.ignore) {
369 return;
370 }
371 if (!this.noDirRecurse &&
372 job.stat.isDirectory() &&
373 !job.readdir) {
374 const rc = this.readdirCache.get(job.absolute);
375 if (rc) {
376 this[ONREADDIR](job, rc);
377 }
378 else {
379 this[READDIR](job);
380 }
381 if (!job.readdir) {
382 return;
383 }
384 }
385 // we know it doesn't have an entry, because that got checked above
386 job.entry = this[ENTRY](job);
387 if (!job.entry) {
388 job.ignore = true;
389 return;
390 }
391 if (job === this[CURRENT] && !job.piped) {
392 this[PIPE](job);
393 }
394 }
395 [ENTRYOPT](job) {
396 return {
397 onwarn: (code, msg, data) => this.warn(code, msg, data),
398 noPax: this.noPax,
399 cwd: this.cwd,
400 absolute: job.absolute,
401 preservePaths: this.preservePaths,
402 maxReadSize: this.maxReadSize,
403 strict: this.strict,
404 portable: this.portable,
405 linkCache: this.linkCache,
406 statCache: this.statCache,
407 noMtime: this.noMtime,
408 mtime: this.mtime,
409 prefix: this.prefix,
410 onWriteEntry: this.onWriteEntry,
411 };
412 }
413 [ENTRY](job) {
414 this[JOBS] += 1;
415 try {
416 const e = new this[WRITEENTRYCLASS](job.path, this[ENTRYOPT](job));
417 return e
418 .on('end', () => this[JOBDONE](job))
419 .on('error', er => this.emit('error', er));
420 }
421 catch (er) {
422 this.emit('error', er);
423 }
424 }
425 [ONDRAIN]() {
426 if (this[CURRENT] && this[CURRENT].entry) {
427 this[CURRENT].entry.resume();
428 }
429 }
430 // like .pipe() but using super, because our write() is special
431 [PIPE](job) {
432 job.piped = true;
433 if (job.readdir) {
434 job.readdir.forEach(entry => {
435 const p = job.path;
436 const base = p === './' ? '' : p.replace(/\/*$/, '/');
437 this[ADDFSENTRY](base + entry);
438 });
439 }
440 const source = job.entry;
441 const zip = this.zip;
442 /* c8 ignore start */
443 if (!source)
444 throw new Error('cannot pipe without source');
445 /* c8 ignore stop */
446 if (zip) {
447 source.on('data', chunk => {
448 if (!zip.write(chunk)) {
449 source.pause();
450 }
451 });
452 }
453 else {
454 source.on('data', chunk => {
455 if (!super.write(chunk)) {
456 source.pause();
457 }
458 });
459 }
460 }
461 pause() {
462 if (this.zip) {
463 this.zip.pause();
464 }
465 return super.pause();
466 }
467 warn(code, message, data = {}) {
468 (0, warn_method_js_1.warnMethod)(this, code, message, data);
469 }
470}
471exports.Pack = Pack;
472class PackSync extends Pack {
473 sync = true;
474 constructor(opt) {
475 super(opt);
476 this[WRITEENTRYCLASS] = write_entry_js_1.WriteEntrySync;
477 }
478 // pause/resume are no-ops in sync streams.
479 pause() { }
480 resume() { }
481 [STAT](job) {
482 const stat = this.follow ? 'statSync' : 'lstatSync';
483 this[ONSTAT](job, fs_1.default[stat](job.absolute));
484 }
485 [READDIR](job) {
486 this[ONREADDIR](job, fs_1.default.readdirSync(job.absolute));
487 }
488 // gotta get it all in this tick
489 [PIPE](job) {
490 const source = job.entry;
491 const zip = this.zip;
492 if (job.readdir) {
493 job.readdir.forEach(entry => {
494 const p = job.path;
495 const base = p === './' ? '' : p.replace(/\/*$/, '/');
496 this[ADDFSENTRY](base + entry);
497 });
498 }
499 /* c8 ignore start */
500 if (!source)
501 throw new Error('Cannot pipe without source');
502 /* c8 ignore stop */
503 if (zip) {
504 source.on('data', chunk => {
505 zip.write(chunk);
506 });
507 }
508 else {
509 source.on('data', chunk => {
510 super[WRITE](chunk);
511 });
512 }
513 }
514}
515exports.PackSync = PackSync;
516//# sourceMappingURL=pack.js.map