2f5acc0f2ede6658c04ac09d3af9e39b065e6596fc638cd73509dcb7ce62e3410a6bdbb11cebd64748148fbc90926411d4cb7129d74ac2c6a9bfea6c1f15f4 14 KB

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