codekingpro/portable-devtools
114k
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 super();
130 this.opt = opt;
131 this.file = opt.file || '';
132 this.cwd = opt.cwd || process.cwd();
133 this.maxReadSize = opt.maxReadSize;
134 this.preservePaths = !!opt.preservePaths;
135 this.strict = !!opt.strict;
136 this.noPax = !!opt.noPax;
137 this.prefix = (0, normalize_windows_path_js_1.normalizeWindowsPath)(opt.prefix || '');
138 this.linkCache = opt.linkCache || new Map();
139 this.statCache = opt.statCache || new Map();
140 this.readdirCache = opt.readdirCache || new Map();
141 this.onWriteEntry = opt.onWriteEntry;
142 this[WRITEENTRYCLASS] = write_entry_js_1.WriteEntry;
143 if (typeof opt.onwarn === 'function') {
144 this.on('warn', opt.onwarn);
145 }
146 this.portable = !!opt.portable;
147 if (opt.gzip || opt.brotli || opt.zstd) {
148 if ((opt.gzip ? 1 : 0) + (opt.brotli ? 1 : 0) + (opt.zstd ? 1 : 0) >
149 1) {
150 throw new TypeError('gzip, brotli, zstd are mutually exclusive');
151 }
152 if (opt.gzip) {
153 if (typeof opt.gzip !== 'object') {
154 opt.gzip = {};
155 }
156 if (this.portable) {
157 opt.gzip.portable = true;
158 }
159 this.zip = new zlib.Gzip(opt.gzip);
160 }
161 if (opt.brotli) {
162 if (typeof opt.brotli !== 'object') {
163 opt.brotli = {};
164 }
165 this.zip = new zlib.BrotliCompress(opt.brotli);
166 }
167 if (opt.zstd) {
168 if (typeof opt.zstd !== 'object') {
169 opt.zstd = {};
170 }
171 this.zip = new zlib.ZstdCompress(opt.zstd);
172 }
173 /* c8 ignore next */
174 if (!this.zip)
175 throw new Error('impossible');
176 const zip = this.zip;
177 zip.on('data', chunk => super.write(chunk));
178 zip.on('end', () => super.end());
179 zip.on('drain', () => this[ONDRAIN]());
180 this.on('resume', () => zip.resume());
181 }
182 else {
183 this.on('drain', this[ONDRAIN]);
184 }
185 this.noDirRecurse = !!opt.noDirRecurse;
186 this.follow = !!opt.follow;
187 this.noMtime = !!opt.noMtime;
188 if (opt.mtime)
189 this.mtime = opt.mtime;
190 this.filter =
191 typeof opt.filter === 'function' ? opt.filter : () => true;
192 this[QUEUE] = new yallist_1.Yallist();
193 this[JOBS] = 0;
194 this.jobs = Number(opt.jobs) || 4;
195 this[PROCESSING] = false;
196 this[ENDED] = false;
197 }
198 [WRITE](chunk) {
199 return super.write(chunk);
200 }
201 add(path) {
202 this.write(path);
203 return this;
204 }
205 end(path, encoding, cb) {
206 /* c8 ignore start */
207 if (typeof path === 'function') {
208 cb = path;
209 path = undefined;
210 }
211 if (typeof encoding === 'function') {
212 cb = encoding;
213 encoding = undefined;
214 }
215 /* c8 ignore stop */
216 if (path) {
217 this.add(path);
218 }
219 this[ENDED] = true;
220 this[PROCESS]();
221 /* c8 ignore next */
222 if (cb)
223 cb();
224 return this;
225 }
226 write(path) {
227 if (this[ENDED]) {
228 throw new Error('write after end');
229 }
230 if (path instanceof read_entry_js_1.ReadEntry) {
231 this[ADDTARENTRY](path);
232 }
233 else {
234 this[ADDFSENTRY](path);
235 }
236 return this.flowing;
237 }
238 [ADDTARENTRY](p) {
239 const absolute = (0, normalize_windows_path_js_1.normalizeWindowsPath)(path_1.default.resolve(this.cwd, p.path));
240 // in this case, we don't have to wait for the stat
241 if (!this.filter(p.path, p)) {
242 p.resume();
243 }
244 else {
245 const job = new PackJob(p.path, absolute);
246 job.entry = new write_entry_js_1.WriteEntryTar(p, this[ENTRYOPT](job));
247 job.entry.on('end', () => this[JOBDONE](job));
248 this[JOBS] += 1;
249 this[QUEUE].push(job);
250 }
251 this[PROCESS]();
252 }
253 [ADDFSENTRY](p) {
254 const absolute = (0, normalize_windows_path_js_1.normalizeWindowsPath)(path_1.default.resolve(this.cwd, p));
255 this[QUEUE].push(new PackJob(p, absolute));
256 this[PROCESS]();
257 }
258 [STAT](job) {
259 job.pending = true;
260 this[JOBS] += 1;
261 const stat = this.follow ? 'stat' : 'lstat';
262 fs_1.default[stat](job.absolute, (er, stat) => {
263 job.pending = false;
264 this[JOBS] -= 1;
265 if (er) {
266 this.emit('error', er);
267 }
268 else {
269 this[ONSTAT](job, stat);
270 }
271 });
272 }
273 [ONSTAT](job, stat) {
274 this.statCache.set(job.absolute, stat);
275 job.stat = stat;
276 // now we have the stat, we can filter it.
277 if (!this.filter(job.path, stat)) {
278 job.ignore = true;
279 }
280 else if (stat.isFile() &&
281 stat.nlink > 1 &&
282 job === this[CURRENT] &&
283 !this.linkCache.get(`${stat.dev}:${stat.ino}`) &&
284 !this.sync) {
285 // if it's not filtered, and it's a new File entry,
286 // jump the queue in case any pending Link entries are about
287 // to try to link to it. This prevents a hardlink from coming ahead
288 // of its target in the archive.
289 this[PROCESSJOB](job);
290 }
291 this[PROCESS]();
292 }
293 [READDIR](job) {
294 job.pending = true;
295 this[JOBS] += 1;
296 fs_1.default.readdir(job.absolute, (er, entries) => {
297 job.pending = false;
298 this[JOBS] -= 1;
299 if (er) {
300 return this.emit('error', er);
301 }
302 this[ONREADDIR](job, entries);
303 });
304 }
305 [ONREADDIR](job, entries) {
306 this.readdirCache.set(job.absolute, entries);
307 job.readdir = entries;
308 this[PROCESS]();
309 }
310 [PROCESS]() {
311 if (this[PROCESSING]) {
312 return;
313 }
314 this[PROCESSING] = true;
315 for (let w = this[QUEUE].head; !!w && this[JOBS] < this.jobs; w = w.next) {
316 this[PROCESSJOB](w.value);
317 if (w.value.ignore) {
318 const p = w.next;
319 this[QUEUE].removeNode(w);
320 w.next = p;
321 }
322 }
323 this[PROCESSING] = false;
324 if (this[ENDED] && this[QUEUE].length === 0 && this[JOBS] === 0) {
325 if (this.zip) {
326 this.zip.end(EOF);
327 }
328 else {
329 super.write(EOF);
330 super.end();
331 }
332 }
333 }
334 get [CURRENT]() {
335 return this[QUEUE] && this[QUEUE].head && this[QUEUE].head.value;
336 }
337 [JOBDONE](_job) {
338 this[QUEUE].shift();
339 this[JOBS] -= 1;
340 this[PROCESS]();
341 }
342 [PROCESSJOB](job) {
343 if (job.pending) {
344 return;
345 }
346 if (job.entry) {
347 if (job === this[CURRENT] && !job.piped) {
348 this[PIPE](job);
349 }
350 return;
351 }
352 if (!job.stat) {
353 const sc = this.statCache.get(job.absolute);
354 if (sc) {
355 this[ONSTAT](job, sc);
356 }
357 else {
358 this[STAT](job);
359 }
360 }
361 if (!job.stat) {
362 return;
363 }
364 // filtered out!
365 if (job.ignore) {
366 return;
367 }
368 if (!this.noDirRecurse && job.stat.isDirectory() && !job.readdir) {
369 const rc = this.readdirCache.get(job.absolute);
370 if (rc) {
371 this[ONREADDIR](job, rc);
372 }
373 else {
374 this[READDIR](job);
375 }
376 if (!job.readdir) {
377 return;
378 }
379 }
380 // we know it doesn't have an entry, because that got checked above
381 job.entry = this[ENTRY](job);
382 if (!job.entry) {
383 job.ignore = true;
384 return;
385 }
386 if (job === this[CURRENT] && !job.piped) {
387 this[PIPE](job);
388 }
389 }
390 [ENTRYOPT](job) {
391 return {
392 onwarn: (code, msg, data) => this.warn(code, msg, data),
393 noPax: this.noPax,
394 cwd: this.cwd,
395 absolute: job.absolute,
396 preservePaths: this.preservePaths,
397 maxReadSize: this.maxReadSize,
398 strict: this.strict,
399 portable: this.portable,
400 linkCache: this.linkCache,
401 statCache: this.statCache,
402 noMtime: this.noMtime,
403 mtime: this.mtime,
404 prefix: this.prefix,
405 onWriteEntry: this.onWriteEntry,
406 };
407 }
408 [ENTRY](job) {
409 this[JOBS] += 1;
410 try {
411 const e = new this[WRITEENTRYCLASS](job.path, this[ENTRYOPT](job));
412 return e
413 .on('end', () => this[JOBDONE](job))
414 .on('error', er => this.emit('error', er));
415 }
416 catch (er) {
417 this.emit('error', er);
418 }
419 }
420 [ONDRAIN]() {
421 if (this[CURRENT] && this[CURRENT].entry) {
422 this[CURRENT].entry.resume();
423 }
424 }
425 // like .pipe() but using super, because our write() is special
426 [PIPE](job) {
427 job.piped = true;
428 if (job.readdir) {
429 job.readdir.forEach(entry => {
430 const p = job.path;
431 const base = p === './' ? '' : p.replace(/\/*$/, '/');
432 this[ADDFSENTRY](base + entry);
433 });
434 }
435 const source = job.entry;
436 const zip = this.zip;
437 /* c8 ignore start */
438 if (!source)
439 throw new Error('cannot pipe without source');
440 /* c8 ignore stop */
441 if (zip) {
442 source.on('data', chunk => {
443 if (!zip.write(chunk)) {
444 source.pause();
445 }
446 });
447 }
448 else {
449 source.on('data', chunk => {
450 if (!super.write(chunk)) {
451 source.pause();
452 }
453 });
454 }
455 }
456 pause() {
457 if (this.zip) {
458 this.zip.pause();
459 }
460 return super.pause();
461 }
462 warn(code, message, data = {}) {
463 (0, warn_method_js_1.warnMethod)(this, code, message, data);
464 }
465}
466exports.Pack = Pack;
467class PackSync extends Pack {
468 sync = true;
469 constructor(opt) {
470 super(opt);
471 this[WRITEENTRYCLASS] = write_entry_js_1.WriteEntrySync;
472 }
473 // pause/resume are no-ops in sync streams.
474 pause() { }
475 resume() { }
476 [STAT](job) {
477 const stat = this.follow ? 'statSync' : 'lstatSync';
478 this[ONSTAT](job, fs_1.default[stat](job.absolute));
479 }
480 [READDIR](job) {
481 this[ONREADDIR](job, fs_1.default.readdirSync(job.absolute));
482 }
483 // gotta get it all in this tick
484 [PIPE](job) {
485 const source = job.entry;
486 const zip = this.zip;
487 if (job.readdir) {
488 job.readdir.forEach(entry => {
489 const p = job.path;
490 const base = p === './' ? '' : p.replace(/\/*$/, '/');
491 this[ADDFSENTRY](base + entry);
492 });
493 }
494 /* c8 ignore start */
495 if (!source)
496 throw new Error('Cannot pipe without source');
497 /* c8 ignore stop */
498 if (zip) {
499 source.on('data', chunk => {
500 zip.write(chunk);
501 });
502 }
503 else {
504 source.on('data', chunk => {
505 super[WRITE](chunk);
506 });
507 }
508 }
509}
510exports.PackSync = PackSync;
511//# sourceMappingURL=pack.js.map