"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs"); const util_1 = require("util"); const path_1 = require("path"); const stream_1 = require("stream"); const MultiStream = require("multistream"); const lstat = util_1.promisify(fs.lstat), realpath = util_1.promisify(fs.realpath), createReadStream = fs.createReadStream, stat = util_1.promisify(fs.stat); function makeRelative(cwd, path) { return './' + path_1.relative(cwd, path); } function sortEntry(a, b) { return a[0] > b[0] ? 1 : -1; } function toStream(content) { const readable = new stream_1.Readable({ read() { this.push(content); this.push(null); }, }); return readable; } exports.toStream = toStream; function createFile(absoluteFileName) { return __awaiter(this, void 0, void 0, function* () { const stats = yield lstat(absoluteFileName), file = { size: stats.size, contents: '', absPath: absoluteFileName, deps: {}, }; if (stats.isSymbolicLink()) { file.size = stats.size; const [realPath, realStat] = yield Promise.all([ realpath(absoluteFileName), stat(absoluteFileName), ]); file.realPath = realPath; file.realSize = realStat.size; } return file; }); } class Bundle { constructor({ cwd } = { cwd: process.cwd() }) { this.size = 0; this.rendered = false; this.offset = 0; this.index = {}; this.files = {}; this.streams = []; this.cwd = cwd; } get list() { return Object.keys(this.files); } addResource(absoluteFileName, content) { return __awaiter(this, void 0, void 0, function* () { if (this.files[absoluteFileName]) { return this.size; } if (typeof content === 'string' || Buffer.isBuffer(content)) { this.files[absoluteFileName] = { size: Buffer.byteLength(content), contents: content, deps: {}, absPath: absoluteFileName, }; } else if (content) { this.files[content.absPath] = content; } else { this.files[absoluteFileName] = { absPath: absoluteFileName, contents: '', deps: {}, size: 0, }; this.files[absoluteFileName] = yield createFile(absoluteFileName); } return (this.size += this.files[absoluteFileName].size); }); } /** * De-dupe files by absolute path, partition by symlink/real * Iterate over real, add entries * Iterate over symlinks, add symlinks */ renderIndex() { if (this.rendered) { throw new Error('Bundle index already rendered'); } const files = Object.entries(this.files), realFiles = [], symLinks = []; for (const entry of files) { if (entry[1].realPath) { symLinks.push(entry); } else { realFiles.push(entry); } } realFiles.sort(sortEntry); symLinks.sort(sortEntry); for (const [absPath, file] of realFiles) { this.addEntry(absPath, file); } for (const [absPath, file] of symLinks) { this.addEntry(file.realPath, file); this.addEntry(absPath, file, file.realPath); } this.rendered = true; return this.index; } /** * Add a stream if needed and an entry with the required offset and size * Ensure the calling order of this method is idempotent (eg, while iterating a sorted set) * @param entryPath * @param file * @param useEntry */ addEntry(entryPath, file, useEntry) { var _a; const existingName = useEntry && makeRelative(this.cwd, useEntry), name = makeRelative(this.cwd, entryPath), size = (_a = file.realSize) !== null && _a !== void 0 ? _a : file.size, existingEntry = this.index[existingName !== null && existingName !== void 0 ? existingName : name]; this.index[name] = existingEntry || [this.offset, size]; if (!existingEntry) { this.streams.push(() => file.contents ? toStream(file.contents) : createReadStream(file.absPath)); this.offset += size; } } toStream() { return new MultiStream(this.streams); } } exports.Bundle = Bundle;