123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.ZipOpenFS = exports.getArchivePart = void 0;
- const tslib_1 = require("tslib");
- const fs_1 = require("fs");
- const FakeFS_1 = require("./FakeFS");
- const NodeFS_1 = require("./NodeFS");
- const ZipFS_1 = require("./ZipFS");
- const watchFile_1 = require("./algorithms/watchFile");
- const errors = tslib_1.__importStar(require("./errors"));
- const path_1 = require("./path");
- // Only file descriptors prefixed by those values will be forwarded to the ZipFS
- // instances. Note that the highest ZIP_MAGIC bit MUST NOT be set, otherwise the
- // resulting fd becomes a negative integer, which isn't supposed to happen per
- // the unix rules (caused problems w/ Go).
- //
- // Those values must be synced with packages/yarnpkg-pnp/sources/esm-loader/fspatch.ts
- //
- const ZIP_MASK = 0xff000000;
- const ZIP_MAGIC = 0x2a000000;
- /**
- * Extracts the archive part (ending in the first instance of `extension`) from a path.
- *
- * The indexOf-based implementation is ~3.7x faster than a RegExp-based implementation.
- */
- const getArchivePart = (path, extension) => {
- let idx = path.indexOf(extension);
- if (idx <= 0)
- return null;
- let nextCharIdx = idx;
- while (idx >= 0) {
- nextCharIdx = idx + extension.length;
- if (path[nextCharIdx] === path_1.ppath.sep)
- break;
- // Disallow files named ".zip"
- if (path[idx - 1] === path_1.ppath.sep)
- return null;
- idx = path.indexOf(extension, nextCharIdx);
- }
- // The path either has to end in ".zip" or contain an archive subpath (".zip/...")
- if (path.length > nextCharIdx && path[nextCharIdx] !== path_1.ppath.sep)
- return null;
- return path.slice(0, nextCharIdx);
- };
- exports.getArchivePart = getArchivePart;
- class ZipOpenFS extends FakeFS_1.BasePortableFakeFS {
- static async openPromise(fn, opts) {
- const zipOpenFs = new ZipOpenFS(opts);
- try {
- return await fn(zipOpenFs);
- }
- finally {
- zipOpenFs.saveAndClose();
- }
- }
- get libzip() {
- if (typeof this.libzipInstance === `undefined`)
- this.libzipInstance = this.libzipFactory();
- return this.libzipInstance;
- }
- constructor({ libzip, baseFs = new NodeFS_1.NodeFS(), filter = null, maxOpenFiles = Infinity, readOnlyArchives = false, useCache = true, maxAge = 5000, fileExtensions = null }) {
- super();
- this.fdMap = new Map();
- this.nextFd = 3;
- this.isZip = new Set();
- this.notZip = new Set();
- this.realPaths = new Map();
- this.limitOpenFilesTimeout = null;
- this.libzipFactory = typeof libzip !== `function`
- ? () => libzip
- : libzip;
- this.baseFs = baseFs;
- this.zipInstances = useCache ? new Map() : null;
- this.filter = filter;
- this.maxOpenFiles = maxOpenFiles;
- this.readOnlyArchives = readOnlyArchives;
- this.maxAge = maxAge;
- this.fileExtensions = fileExtensions;
- }
- getExtractHint(hints) {
- return this.baseFs.getExtractHint(hints);
- }
- getRealPath() {
- return this.baseFs.getRealPath();
- }
- saveAndClose() {
- (0, watchFile_1.unwatchAllFiles)(this);
- if (this.zipInstances) {
- for (const [path, { zipFs }] of this.zipInstances.entries()) {
- zipFs.saveAndClose();
- this.zipInstances.delete(path);
- }
- }
- }
- discardAndClose() {
- (0, watchFile_1.unwatchAllFiles)(this);
- if (this.zipInstances) {
- for (const [path, { zipFs }] of this.zipInstances.entries()) {
- zipFs.discardAndClose();
- this.zipInstances.delete(path);
- }
- }
- }
- resolve(p) {
- return this.baseFs.resolve(p);
- }
- remapFd(zipFs, fd) {
- const remappedFd = this.nextFd++ | ZIP_MAGIC;
- this.fdMap.set(remappedFd, [zipFs, fd]);
- return remappedFd;
- }
- async openPromise(p, flags, mode) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.openPromise(p, flags, mode);
- }, async (zipFs, { subPath }) => {
- return this.remapFd(zipFs, await zipFs.openPromise(subPath, flags, mode));
- });
- }
- openSync(p, flags, mode) {
- return this.makeCallSync(p, () => {
- return this.baseFs.openSync(p, flags, mode);
- }, (zipFs, { subPath }) => {
- return this.remapFd(zipFs, zipFs.openSync(subPath, flags, mode));
- });
- }
- async opendirPromise(p, opts) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.opendirPromise(p, opts);
- }, async (zipFs, { subPath }) => {
- return await zipFs.opendirPromise(subPath, opts);
- }, {
- requireSubpath: false,
- });
- }
- opendirSync(p, opts) {
- return this.makeCallSync(p, () => {
- return this.baseFs.opendirSync(p, opts);
- }, (zipFs, { subPath }) => {
- return zipFs.opendirSync(subPath, opts);
- }, {
- requireSubpath: false,
- });
- }
- async readPromise(fd, buffer, offset, length, position) {
- if ((fd & ZIP_MASK) !== ZIP_MAGIC)
- return await this.baseFs.readPromise(fd, buffer, offset, length, position);
- const entry = this.fdMap.get(fd);
- if (typeof entry === `undefined`)
- throw errors.EBADF(`read`);
- const [zipFs, realFd] = entry;
- return await zipFs.readPromise(realFd, buffer, offset, length, position);
- }
- readSync(fd, buffer, offset, length, position) {
- if ((fd & ZIP_MASK) !== ZIP_MAGIC)
- return this.baseFs.readSync(fd, buffer, offset, length, position);
- const entry = this.fdMap.get(fd);
- if (typeof entry === `undefined`)
- throw errors.EBADF(`readSync`);
- const [zipFs, realFd] = entry;
- return zipFs.readSync(realFd, buffer, offset, length, position);
- }
- async writePromise(fd, buffer, offset, length, position) {
- if ((fd & ZIP_MASK) !== ZIP_MAGIC) {
- if (typeof buffer === `string`) {
- return await this.baseFs.writePromise(fd, buffer, offset);
- }
- else {
- return await this.baseFs.writePromise(fd, buffer, offset, length, position);
- }
- }
- const entry = this.fdMap.get(fd);
- if (typeof entry === `undefined`)
- throw errors.EBADF(`write`);
- const [zipFs, realFd] = entry;
- if (typeof buffer === `string`) {
- return await zipFs.writePromise(realFd, buffer, offset);
- }
- else {
- return await zipFs.writePromise(realFd, buffer, offset, length, position);
- }
- }
- writeSync(fd, buffer, offset, length, position) {
- if ((fd & ZIP_MASK) !== ZIP_MAGIC) {
- if (typeof buffer === `string`) {
- return this.baseFs.writeSync(fd, buffer, offset);
- }
- else {
- return this.baseFs.writeSync(fd, buffer, offset, length, position);
- }
- }
- const entry = this.fdMap.get(fd);
- if (typeof entry === `undefined`)
- throw errors.EBADF(`writeSync`);
- const [zipFs, realFd] = entry;
- if (typeof buffer === `string`) {
- return zipFs.writeSync(realFd, buffer, offset);
- }
- else {
- return zipFs.writeSync(realFd, buffer, offset, length, position);
- }
- }
- async closePromise(fd) {
- if ((fd & ZIP_MASK) !== ZIP_MAGIC)
- return await this.baseFs.closePromise(fd);
- const entry = this.fdMap.get(fd);
- if (typeof entry === `undefined`)
- throw errors.EBADF(`close`);
- this.fdMap.delete(fd);
- const [zipFs, realFd] = entry;
- return await zipFs.closePromise(realFd);
- }
- closeSync(fd) {
- if ((fd & ZIP_MASK) !== ZIP_MAGIC)
- return this.baseFs.closeSync(fd);
- const entry = this.fdMap.get(fd);
- if (typeof entry === `undefined`)
- throw errors.EBADF(`closeSync`);
- this.fdMap.delete(fd);
- const [zipFs, realFd] = entry;
- return zipFs.closeSync(realFd);
- }
- createReadStream(p, opts) {
- if (p === null)
- return this.baseFs.createReadStream(p, opts);
- return this.makeCallSync(p, () => {
- return this.baseFs.createReadStream(p, opts);
- }, (zipFs, { archivePath, subPath }) => {
- const stream = zipFs.createReadStream(subPath, opts);
- // This is a very hacky workaround. `ZipOpenFS` shouldn't have to work with `NativePath`s.
- // Ref: https://github.com/yarnpkg/berry/pull/3774
- // TODO: think of a better solution
- stream.path = path_1.npath.fromPortablePath(this.pathUtils.join(archivePath, subPath));
- return stream;
- });
- }
- createWriteStream(p, opts) {
- if (p === null)
- return this.baseFs.createWriteStream(p, opts);
- return this.makeCallSync(p, () => {
- return this.baseFs.createWriteStream(p, opts);
- }, (zipFs, { subPath }) => {
- return zipFs.createWriteStream(subPath, opts);
- });
- }
- async realpathPromise(p) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.realpathPromise(p);
- }, async (zipFs, { archivePath, subPath }) => {
- let realArchivePath = this.realPaths.get(archivePath);
- if (typeof realArchivePath === `undefined`) {
- realArchivePath = await this.baseFs.realpathPromise(archivePath);
- this.realPaths.set(archivePath, realArchivePath);
- }
- return this.pathUtils.join(realArchivePath, this.pathUtils.relative(path_1.PortablePath.root, await zipFs.realpathPromise(subPath)));
- });
- }
- realpathSync(p) {
- return this.makeCallSync(p, () => {
- return this.baseFs.realpathSync(p);
- }, (zipFs, { archivePath, subPath }) => {
- let realArchivePath = this.realPaths.get(archivePath);
- if (typeof realArchivePath === `undefined`) {
- realArchivePath = this.baseFs.realpathSync(archivePath);
- this.realPaths.set(archivePath, realArchivePath);
- }
- return this.pathUtils.join(realArchivePath, this.pathUtils.relative(path_1.PortablePath.root, zipFs.realpathSync(subPath)));
- });
- }
- async existsPromise(p) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.existsPromise(p);
- }, async (zipFs, { subPath }) => {
- return await zipFs.existsPromise(subPath);
- });
- }
- existsSync(p) {
- return this.makeCallSync(p, () => {
- return this.baseFs.existsSync(p);
- }, (zipFs, { subPath }) => {
- return zipFs.existsSync(subPath);
- });
- }
- async accessPromise(p, mode) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.accessPromise(p, mode);
- }, async (zipFs, { subPath }) => {
- return await zipFs.accessPromise(subPath, mode);
- });
- }
- accessSync(p, mode) {
- return this.makeCallSync(p, () => {
- return this.baseFs.accessSync(p, mode);
- }, (zipFs, { subPath }) => {
- return zipFs.accessSync(subPath, mode);
- });
- }
- async statPromise(p, opts) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.statPromise(p, opts);
- }, async (zipFs, { subPath }) => {
- return await zipFs.statPromise(subPath, opts);
- });
- }
- statSync(p, opts) {
- return this.makeCallSync(p, () => {
- return this.baseFs.statSync(p, opts);
- }, (zipFs, { subPath }) => {
- return zipFs.statSync(subPath, opts);
- });
- }
- async fstatPromise(fd, opts) {
- if ((fd & ZIP_MASK) !== ZIP_MAGIC)
- return this.baseFs.fstatPromise(fd, opts);
- const entry = this.fdMap.get(fd);
- if (typeof entry === `undefined`)
- throw errors.EBADF(`fstat`);
- const [zipFs, realFd] = entry;
- return zipFs.fstatPromise(realFd, opts);
- }
- fstatSync(fd, opts) {
- if ((fd & ZIP_MASK) !== ZIP_MAGIC)
- return this.baseFs.fstatSync(fd, opts);
- const entry = this.fdMap.get(fd);
- if (typeof entry === `undefined`)
- throw errors.EBADF(`fstatSync`);
- const [zipFs, realFd] = entry;
- return zipFs.fstatSync(realFd, opts);
- }
- async lstatPromise(p, opts) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.lstatPromise(p, opts);
- }, async (zipFs, { subPath }) => {
- return await zipFs.lstatPromise(subPath, opts);
- });
- }
- lstatSync(p, opts) {
- return this.makeCallSync(p, () => {
- return this.baseFs.lstatSync(p, opts);
- }, (zipFs, { subPath }) => {
- return zipFs.lstatSync(subPath, opts);
- });
- }
- async fchmodPromise(fd, mask) {
- if ((fd & ZIP_MASK) !== ZIP_MAGIC)
- return this.baseFs.fchmodPromise(fd, mask);
- const entry = this.fdMap.get(fd);
- if (typeof entry === `undefined`)
- throw errors.EBADF(`fchmod`);
- const [zipFs, realFd] = entry;
- return zipFs.fchmodPromise(realFd, mask);
- }
- fchmodSync(fd, mask) {
- if ((fd & ZIP_MASK) !== ZIP_MAGIC)
- return this.baseFs.fchmodSync(fd, mask);
- const entry = this.fdMap.get(fd);
- if (typeof entry === `undefined`)
- throw errors.EBADF(`fchmodSync`);
- const [zipFs, realFd] = entry;
- return zipFs.fchmodSync(realFd, mask);
- }
- async chmodPromise(p, mask) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.chmodPromise(p, mask);
- }, async (zipFs, { subPath }) => {
- return await zipFs.chmodPromise(subPath, mask);
- });
- }
- chmodSync(p, mask) {
- return this.makeCallSync(p, () => {
- return this.baseFs.chmodSync(p, mask);
- }, (zipFs, { subPath }) => {
- return zipFs.chmodSync(subPath, mask);
- });
- }
- async fchownPromise(fd, uid, gid) {
- if ((fd & ZIP_MASK) !== ZIP_MAGIC)
- return this.baseFs.fchownPromise(fd, uid, gid);
- const entry = this.fdMap.get(fd);
- if (typeof entry === `undefined`)
- throw errors.EBADF(`fchown`);
- const [zipFs, realFd] = entry;
- return zipFs.fchownPromise(realFd, uid, gid);
- }
- fchownSync(fd, uid, gid) {
- if ((fd & ZIP_MASK) !== ZIP_MAGIC)
- return this.baseFs.fchownSync(fd, uid, gid);
- const entry = this.fdMap.get(fd);
- if (typeof entry === `undefined`)
- throw errors.EBADF(`fchownSync`);
- const [zipFs, realFd] = entry;
- return zipFs.fchownSync(realFd, uid, gid);
- }
- async chownPromise(p, uid, gid) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.chownPromise(p, uid, gid);
- }, async (zipFs, { subPath }) => {
- return await zipFs.chownPromise(subPath, uid, gid);
- });
- }
- chownSync(p, uid, gid) {
- return this.makeCallSync(p, () => {
- return this.baseFs.chownSync(p, uid, gid);
- }, (zipFs, { subPath }) => {
- return zipFs.chownSync(subPath, uid, gid);
- });
- }
- async renamePromise(oldP, newP) {
- return await this.makeCallPromise(oldP, async () => {
- return await this.makeCallPromise(newP, async () => {
- return await this.baseFs.renamePromise(oldP, newP);
- }, async () => {
- throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { code: `EEXDEV` });
- });
- }, async (zipFsO, { subPath: subPathO }) => {
- return await this.makeCallPromise(newP, async () => {
- throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { code: `EEXDEV` });
- }, async (zipFsN, { subPath: subPathN }) => {
- if (zipFsO !== zipFsN) {
- throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { code: `EEXDEV` });
- }
- else {
- return await zipFsO.renamePromise(subPathO, subPathN);
- }
- });
- });
- }
- renameSync(oldP, newP) {
- return this.makeCallSync(oldP, () => {
- return this.makeCallSync(newP, () => {
- return this.baseFs.renameSync(oldP, newP);
- }, () => {
- throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { code: `EEXDEV` });
- });
- }, (zipFsO, { subPath: subPathO }) => {
- return this.makeCallSync(newP, () => {
- throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { code: `EEXDEV` });
- }, (zipFsN, { subPath: subPathN }) => {
- if (zipFsO !== zipFsN) {
- throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { code: `EEXDEV` });
- }
- else {
- return zipFsO.renameSync(subPathO, subPathN);
- }
- });
- });
- }
- async copyFilePromise(sourceP, destP, flags = 0) {
- const fallback = async (sourceFs, sourceP, destFs, destP) => {
- if ((flags & fs_1.constants.COPYFILE_FICLONE_FORCE) !== 0)
- throw Object.assign(new Error(`EXDEV: cross-device clone not permitted, copyfile '${sourceP}' -> ${destP}'`), { code: `EXDEV` });
- if ((flags & fs_1.constants.COPYFILE_EXCL) && await this.existsPromise(sourceP))
- throw Object.assign(new Error(`EEXIST: file already exists, copyfile '${sourceP}' -> '${destP}'`), { code: `EEXIST` });
- let content;
- try {
- content = await sourceFs.readFilePromise(sourceP);
- }
- catch (error) {
- throw Object.assign(new Error(`EINVAL: invalid argument, copyfile '${sourceP}' -> '${destP}'`), { code: `EINVAL` });
- }
- await destFs.writeFilePromise(destP, content);
- };
- return await this.makeCallPromise(sourceP, async () => {
- return await this.makeCallPromise(destP, async () => {
- return await this.baseFs.copyFilePromise(sourceP, destP, flags);
- }, async (zipFsD, { subPath: subPathD }) => {
- return await fallback(this.baseFs, sourceP, zipFsD, subPathD);
- });
- }, async (zipFsS, { subPath: subPathS }) => {
- return await this.makeCallPromise(destP, async () => {
- return await fallback(zipFsS, subPathS, this.baseFs, destP);
- }, async (zipFsD, { subPath: subPathD }) => {
- if (zipFsS !== zipFsD) {
- return await fallback(zipFsS, subPathS, zipFsD, subPathD);
- }
- else {
- return await zipFsS.copyFilePromise(subPathS, subPathD, flags);
- }
- });
- });
- }
- copyFileSync(sourceP, destP, flags = 0) {
- const fallback = (sourceFs, sourceP, destFs, destP) => {
- if ((flags & fs_1.constants.COPYFILE_FICLONE_FORCE) !== 0)
- throw Object.assign(new Error(`EXDEV: cross-device clone not permitted, copyfile '${sourceP}' -> ${destP}'`), { code: `EXDEV` });
- if ((flags & fs_1.constants.COPYFILE_EXCL) && this.existsSync(sourceP))
- throw Object.assign(new Error(`EEXIST: file already exists, copyfile '${sourceP}' -> '${destP}'`), { code: `EEXIST` });
- let content;
- try {
- content = sourceFs.readFileSync(sourceP);
- }
- catch (error) {
- throw Object.assign(new Error(`EINVAL: invalid argument, copyfile '${sourceP}' -> '${destP}'`), { code: `EINVAL` });
- }
- destFs.writeFileSync(destP, content);
- };
- return this.makeCallSync(sourceP, () => {
- return this.makeCallSync(destP, () => {
- return this.baseFs.copyFileSync(sourceP, destP, flags);
- }, (zipFsD, { subPath: subPathD }) => {
- return fallback(this.baseFs, sourceP, zipFsD, subPathD);
- });
- }, (zipFsS, { subPath: subPathS }) => {
- return this.makeCallSync(destP, () => {
- return fallback(zipFsS, subPathS, this.baseFs, destP);
- }, (zipFsD, { subPath: subPathD }) => {
- if (zipFsS !== zipFsD) {
- return fallback(zipFsS, subPathS, zipFsD, subPathD);
- }
- else {
- return zipFsS.copyFileSync(subPathS, subPathD, flags);
- }
- });
- });
- }
- async appendFilePromise(p, content, opts) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.appendFilePromise(p, content, opts);
- }, async (zipFs, { subPath }) => {
- return await zipFs.appendFilePromise(subPath, content, opts);
- });
- }
- appendFileSync(p, content, opts) {
- return this.makeCallSync(p, () => {
- return this.baseFs.appendFileSync(p, content, opts);
- }, (zipFs, { subPath }) => {
- return zipFs.appendFileSync(subPath, content, opts);
- });
- }
- async writeFilePromise(p, content, opts) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.writeFilePromise(p, content, opts);
- }, async (zipFs, { subPath }) => {
- return await zipFs.writeFilePromise(subPath, content, opts);
- });
- }
- writeFileSync(p, content, opts) {
- return this.makeCallSync(p, () => {
- return this.baseFs.writeFileSync(p, content, opts);
- }, (zipFs, { subPath }) => {
- return zipFs.writeFileSync(subPath, content, opts);
- });
- }
- async unlinkPromise(p) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.unlinkPromise(p);
- }, async (zipFs, { subPath }) => {
- return await zipFs.unlinkPromise(subPath);
- });
- }
- unlinkSync(p) {
- return this.makeCallSync(p, () => {
- return this.baseFs.unlinkSync(p);
- }, (zipFs, { subPath }) => {
- return zipFs.unlinkSync(subPath);
- });
- }
- async utimesPromise(p, atime, mtime) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.utimesPromise(p, atime, mtime);
- }, async (zipFs, { subPath }) => {
- return await zipFs.utimesPromise(subPath, atime, mtime);
- });
- }
- utimesSync(p, atime, mtime) {
- return this.makeCallSync(p, () => {
- return this.baseFs.utimesSync(p, atime, mtime);
- }, (zipFs, { subPath }) => {
- return zipFs.utimesSync(subPath, atime, mtime);
- });
- }
- async mkdirPromise(p, opts) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.mkdirPromise(p, opts);
- }, async (zipFs, { subPath }) => {
- return await zipFs.mkdirPromise(subPath, opts);
- });
- }
- mkdirSync(p, opts) {
- return this.makeCallSync(p, () => {
- return this.baseFs.mkdirSync(p, opts);
- }, (zipFs, { subPath }) => {
- return zipFs.mkdirSync(subPath, opts);
- });
- }
- async rmdirPromise(p, opts) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.rmdirPromise(p, opts);
- }, async (zipFs, { subPath }) => {
- return await zipFs.rmdirPromise(subPath, opts);
- });
- }
- rmdirSync(p, opts) {
- return this.makeCallSync(p, () => {
- return this.baseFs.rmdirSync(p, opts);
- }, (zipFs, { subPath }) => {
- return zipFs.rmdirSync(subPath, opts);
- });
- }
- async linkPromise(existingP, newP) {
- return await this.makeCallPromise(newP, async () => {
- return await this.baseFs.linkPromise(existingP, newP);
- }, async (zipFs, { subPath }) => {
- return await zipFs.linkPromise(existingP, subPath);
- });
- }
- linkSync(existingP, newP) {
- return this.makeCallSync(newP, () => {
- return this.baseFs.linkSync(existingP, newP);
- }, (zipFs, { subPath }) => {
- return zipFs.linkSync(existingP, subPath);
- });
- }
- async symlinkPromise(target, p, type) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.symlinkPromise(target, p, type);
- }, async (zipFs, { subPath }) => {
- return await zipFs.symlinkPromise(target, subPath);
- });
- }
- symlinkSync(target, p, type) {
- return this.makeCallSync(p, () => {
- return this.baseFs.symlinkSync(target, p, type);
- }, (zipFs, { subPath }) => {
- return zipFs.symlinkSync(target, subPath);
- });
- }
- async readFilePromise(p, encoding) {
- return this.makeCallPromise(p, async () => {
- // This weird switch is required to tell TypeScript that the signatures are proper (otherwise it thinks that only the generic one is covered)
- switch (encoding) {
- case `utf8`:
- return await this.baseFs.readFilePromise(p, encoding);
- default:
- return await this.baseFs.readFilePromise(p, encoding);
- }
- }, async (zipFs, { subPath }) => {
- return await zipFs.readFilePromise(subPath, encoding);
- });
- }
- readFileSync(p, encoding) {
- return this.makeCallSync(p, () => {
- // This weird switch is required to tell TypeScript that the signatures are proper (otherwise it thinks that only the generic one is covered)
- switch (encoding) {
- case `utf8`:
- return this.baseFs.readFileSync(p, encoding);
- default:
- return this.baseFs.readFileSync(p, encoding);
- }
- }, (zipFs, { subPath }) => {
- return zipFs.readFileSync(subPath, encoding);
- });
- }
- async readdirPromise(p, opts) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.readdirPromise(p, opts);
- }, async (zipFs, { subPath }) => {
- return await zipFs.readdirPromise(subPath, opts);
- }, {
- requireSubpath: false,
- });
- }
- readdirSync(p, opts) {
- return this.makeCallSync(p, () => {
- return this.baseFs.readdirSync(p, opts);
- }, (zipFs, { subPath }) => {
- return zipFs.readdirSync(subPath, opts);
- }, {
- requireSubpath: false,
- });
- }
- async readlinkPromise(p) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.readlinkPromise(p);
- }, async (zipFs, { subPath }) => {
- return await zipFs.readlinkPromise(subPath);
- });
- }
- readlinkSync(p) {
- return this.makeCallSync(p, () => {
- return this.baseFs.readlinkSync(p);
- }, (zipFs, { subPath }) => {
- return zipFs.readlinkSync(subPath);
- });
- }
- async truncatePromise(p, len) {
- return await this.makeCallPromise(p, async () => {
- return await this.baseFs.truncatePromise(p, len);
- }, async (zipFs, { subPath }) => {
- return await zipFs.truncatePromise(subPath, len);
- });
- }
- truncateSync(p, len) {
- return this.makeCallSync(p, () => {
- return this.baseFs.truncateSync(p, len);
- }, (zipFs, { subPath }) => {
- return zipFs.truncateSync(subPath, len);
- });
- }
- async ftruncatePromise(fd, len) {
- if ((fd & ZIP_MASK) !== ZIP_MAGIC)
- return this.baseFs.ftruncatePromise(fd, len);
- const entry = this.fdMap.get(fd);
- if (typeof entry === `undefined`)
- throw errors.EBADF(`ftruncate`);
- const [zipFs, realFd] = entry;
- return zipFs.ftruncatePromise(realFd, len);
- }
- ftruncateSync(fd, len) {
- if ((fd & ZIP_MASK) !== ZIP_MAGIC)
- return this.baseFs.ftruncateSync(fd, len);
- const entry = this.fdMap.get(fd);
- if (typeof entry === `undefined`)
- throw errors.EBADF(`ftruncateSync`);
- const [zipFs, realFd] = entry;
- return zipFs.ftruncateSync(realFd, len);
- }
- watch(p, a, b) {
- return this.makeCallSync(p, () => {
- return this.baseFs.watch(p,
- // @ts-expect-error
- a, b);
- }, (zipFs, { subPath }) => {
- return zipFs.watch(subPath,
- // @ts-expect-error
- a, b);
- });
- }
- watchFile(p, a, b) {
- return this.makeCallSync(p, () => {
- return this.baseFs.watchFile(p,
- // @ts-expect-error
- a, b);
- }, () => {
- return (0, watchFile_1.watchFile)(this, p, a, b);
- });
- }
- unwatchFile(p, cb) {
- return this.makeCallSync(p, () => {
- return this.baseFs.unwatchFile(p, cb);
- }, () => {
- return (0, watchFile_1.unwatchFile)(this, p, cb);
- });
- }
- async makeCallPromise(p, discard, accept, { requireSubpath = true } = {}) {
- if (typeof p !== `string`)
- return await discard();
- const normalizedP = this.resolve(p);
- const zipInfo = this.findZip(normalizedP);
- if (!zipInfo)
- return await discard();
- if (requireSubpath && zipInfo.subPath === `/`)
- return await discard();
- return await this.getZipPromise(zipInfo.archivePath, async (zipFs) => await accept(zipFs, zipInfo));
- }
- makeCallSync(p, discard, accept, { requireSubpath = true } = {}) {
- if (typeof p !== `string`)
- return discard();
- const normalizedP = this.resolve(p);
- const zipInfo = this.findZip(normalizedP);
- if (!zipInfo)
- return discard();
- if (requireSubpath && zipInfo.subPath === `/`)
- return discard();
- return this.getZipSync(zipInfo.archivePath, zipFs => accept(zipFs, zipInfo));
- }
- findZip(p) {
- if (this.filter && !this.filter.test(p))
- return null;
- let filePath = ``;
- while (true) {
- const pathPartWithArchive = p.substring(filePath.length);
- let archivePart;
- if (!this.fileExtensions) {
- archivePart = (0, exports.getArchivePart)(pathPartWithArchive, `.zip`);
- }
- else {
- for (const ext of this.fileExtensions) {
- archivePart = (0, exports.getArchivePart)(pathPartWithArchive, ext);
- if (archivePart) {
- break;
- }
- }
- }
- if (!archivePart)
- return null;
- filePath = this.pathUtils.join(filePath, archivePart);
- if (this.isZip.has(filePath) === false) {
- if (this.notZip.has(filePath))
- continue;
- try {
- if (!this.baseFs.lstatSync(filePath).isFile()) {
- this.notZip.add(filePath);
- continue;
- }
- }
- catch {
- return null;
- }
- this.isZip.add(filePath);
- }
- return {
- archivePath: filePath,
- subPath: this.pathUtils.join(path_1.PortablePath.root, p.substring(filePath.length)),
- };
- }
- }
- limitOpenFiles(max) {
- if (this.zipInstances === null)
- return;
- const now = Date.now();
- let nextExpiresAt = now + this.maxAge;
- let closeCount = max === null ? 0 : this.zipInstances.size - max;
- for (const [path, { zipFs, expiresAt, refCount }] of this.zipInstances.entries()) {
- if (refCount !== 0 || zipFs.hasOpenFileHandles()) {
- continue;
- }
- else if (now >= expiresAt) {
- zipFs.saveAndClose();
- this.zipInstances.delete(path);
- closeCount -= 1;
- continue;
- }
- else if (max === null || closeCount <= 0) {
- nextExpiresAt = expiresAt;
- break;
- }
- zipFs.saveAndClose();
- this.zipInstances.delete(path);
- closeCount -= 1;
- }
- if (this.limitOpenFilesTimeout === null && ((max === null && this.zipInstances.size > 0) || max !== null)) {
- this.limitOpenFilesTimeout = setTimeout(() => {
- this.limitOpenFilesTimeout = null;
- this.limitOpenFiles(null);
- }, nextExpiresAt - now).unref();
- }
- }
- async getZipPromise(p, accept) {
- const getZipOptions = async () => ({
- baseFs: this.baseFs,
- libzip: this.libzip,
- readOnly: this.readOnlyArchives,
- stats: await this.baseFs.statPromise(p),
- });
- if (this.zipInstances) {
- let cachedZipFs = this.zipInstances.get(p);
- if (!cachedZipFs) {
- const zipOptions = await getZipOptions();
- // We need to recheck because concurrent getZipPromise calls may
- // have instantiated the zip archive while we were waiting
- cachedZipFs = this.zipInstances.get(p);
- if (!cachedZipFs) {
- cachedZipFs = {
- zipFs: new ZipFS_1.ZipFS(p, zipOptions),
- expiresAt: 0,
- refCount: 0,
- };
- }
- }
- // Removing then re-adding the field allows us to easily implement
- // a basic LRU garbage collection strategy
- this.zipInstances.delete(p);
- this.limitOpenFiles(this.maxOpenFiles - 1);
- this.zipInstances.set(p, cachedZipFs);
- cachedZipFs.expiresAt = Date.now() + this.maxAge;
- cachedZipFs.refCount += 1;
- try {
- return await accept(cachedZipFs.zipFs);
- }
- finally {
- cachedZipFs.refCount -= 1;
- }
- }
- else {
- const zipFs = new ZipFS_1.ZipFS(p, await getZipOptions());
- try {
- return await accept(zipFs);
- }
- finally {
- zipFs.saveAndClose();
- }
- }
- }
- getZipSync(p, accept) {
- const getZipOptions = () => ({
- baseFs: this.baseFs,
- libzip: this.libzip,
- readOnly: this.readOnlyArchives,
- stats: this.baseFs.statSync(p),
- });
- if (this.zipInstances) {
- let cachedZipFs = this.zipInstances.get(p);
- if (!cachedZipFs) {
- cachedZipFs = {
- zipFs: new ZipFS_1.ZipFS(p, getZipOptions()),
- expiresAt: 0,
- refCount: 0,
- };
- }
- // Removing then re-adding the field allows us to easily implement
- // a basic LRU garbage collection strategy
- this.zipInstances.delete(p);
- this.limitOpenFiles(this.maxOpenFiles - 1);
- this.zipInstances.set(p, cachedZipFs);
- cachedZipFs.expiresAt = Date.now() + this.maxAge;
- return accept(cachedZipFs.zipFs);
- }
- else {
- const zipFs = new ZipFS_1.ZipFS(p, getZipOptions());
- try {
- return accept(zipFs);
- }
- finally {
- zipFs.saveAndClose();
- }
- }
- }
- }
- exports.ZipOpenFS = ZipOpenFS;
|