UNPKG

static-fs

Version:

A static filesystem to bundle files and read them using Node.js

1,618 lines (1,285 loc) 88.7 kB
(function(e, a) { for(var i in a) e[i] = a[i]; }(exports, /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 0); /******/ }) /************************************************************************/ /******/ ({ /***/ "./node_modules/webpack/buildin/harmony-module.js": /***/ (function(module, exports) { module.exports = function(originalModule) { if (!originalModule.webpackPolyfill) { var module = Object.create(originalModule); // module.parent = undefined by default if (!module.children) module.children = []; Object.defineProperty(module, "loaded", { enumerable: true, get: function() { return module.l; } }); Object.defineProperty(module, "id", { enumerable: true, get: function() { return module.i; } }); Object.defineProperty(module, "exports", { enumerable: true }); module.webpackPolyfill = 1; } return module; }; /***/ }), /***/ "./src/common/index.js": /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; // EXPORTS __webpack_require__.d(__webpack_exports__, "a", function() { return /* reexport */ INT_SIZE; }); __webpack_require__.d(__webpack_exports__, "d", function() { return /* reexport */ isWindows; }); __webpack_require__.d(__webpack_exports__, "b", function() { return /* reexport */ calculateHash; }); __webpack_require__.d(__webpack_exports__, "c", function() { return /* reexport */ fs_close; }); __webpack_require__.d(__webpack_exports__, "h", function() { return /* reexport */ fs_open; }); __webpack_require__.d(__webpack_exports__, "j", function() { return /* reexport */ readdir; }); __webpack_require__.d(__webpack_exports__, "i", function() { return /* reexport */ readFile; }); __webpack_require__.d(__webpack_exports__, "k", function() { return /* reexport */ stat; }); __webpack_require__.d(__webpack_exports__, "o", function() { return /* reexport */ write; }); __webpack_require__.d(__webpack_exports__, "p", function() { return /* reexport */ writeFile; }); __webpack_require__.d(__webpack_exports__, "f", function() { return /* reexport */ mkdir; }); __webpack_require__.d(__webpack_exports__, "e", function() { return /* reexport */ isWindowsPath; }); __webpack_require__.d(__webpack_exports__, "g", function() { return /* reexport */ nodePathToString; }); __webpack_require__.d(__webpack_exports__, "n", function() { return /* reexport */ unixifyPath; }); __webpack_require__.d(__webpack_exports__, "m", function() { return /* reexport */ stripBOM; }); __webpack_require__.d(__webpack_exports__, "l", function() { return /* reexport */ strToEncoding; }); // UNUSED EXPORTS: lstat, read, copyFile, exists, isDirectory, isFile // CONCATENATED MODULE: ./src/common/constants.js // integers on node are 6B Big-Endian const INT_SIZE = 6; const isWindows = process.platform === 'win32'; // EXTERNAL MODULE: external "crypto" var external_crypto_ = __webpack_require__("crypto"); // CONCATENATED MODULE: ./src/common/crypto.js function calculateHash(content) { return Object(external_crypto_["createHash"])('sha256') .update(JSON.stringify(content)) .digest('base64'); } // EXTERNAL MODULE: external "fs" var external_fs_ = __webpack_require__("fs"); // EXTERNAL MODULE: external "path" var external_path_ = __webpack_require__("path"); // EXTERNAL MODULE: external "util" var external_util_ = __webpack_require__("util"); // CONCATENATED MODULE: ./src/common/fs.js // promisified functions from the real fs async function fs_close(fd) { return Object(external_util_["promisify"])(external_fs_["close"])(fd); } async function lstat(path) { return Object(external_util_["promisify"])(external_fs_["lstat"])(path); } async function fs_open(path, flags, mode) { return Object(external_util_["promisify"])(external_fs_["open"])(path, flags, mode); } async function read(fd, buffer, offset, length, position) { return Object(external_util_["promisify"])(external_fs_["read"])(fd, buffer, offset, length, position || null); } async function readdir(path) { return Object(external_util_["promisify"])(external_fs_["readdir"])(path); } async function readFile(path, options) { return Object(external_util_["promisify"])(external_fs_["readFile"])(path, options); } async function stat(path) { return Object(external_util_["promisify"])(external_fs_["stat"])(path); } async function write(fd, buffer, offset, length, position) { return Object(external_util_["promisify"])(external_fs_["write"])(fd, buffer, offset || 0, length || buffer.length, position || undefined); } async function writeFile(filename, content) { return Object(external_util_["promisify"])(external_fs_["writeFile"])(filename, content); } // custom functions async function copyFile(source, target) { await mkdir(Object(external_path_["dirname"])(target)); return await new Promise((resolve, reject) => { const rd = external_fs_["createReadStream"](source); rd.on('error', rejectCleanup); const wr = external_fs_["createWriteStream"](target); wr.on('error', rejectCleanup); function rejectCleanup(err) { rd.destroy(); wr.end(); reject(err); } wr.on('finish', () => { rd.close(); wr.close(); resolve(); }); rd.pipe(wr); }); } async function exists(path) { try { await stat(path); return true; } catch { /* no-op */ } return false; } async function isDirectory(dirPath) { try { if (await exists(dirPath)) { return (await lstat(dirPath)).isDirectory(); } } catch { /* no-op */ } return false; } async function isFile(filePath) { try { if (await exists(filePath)) { return !(await lstat(filePath)).isDirectory(); } } catch { /* no-op */ } return false; } async function mkdir(dirPath) { if (!(await isDirectory(dirPath))) { const p = Object(external_path_["normalize"])(dirPath + '/'); const parent = Object(external_path_["dirname"])(dirPath); if (!(await isDirectory(parent))) { if (p !== parent) { await mkdir(parent); } } try { await Object(external_util_["promisify"])(external_fs_["mkdir"])(p); } catch (e) { if (!(await isDirectory(p))) { throw new Error(e); } } } } // CONCATENATED MODULE: ./src/common/path.js function getPathFromURLPosix(url) { if (url.hostname !== '') { throw new Error(`file URL host must be "localhost" or empty on ${process.platform}`); } const pathname = url.pathname; for (let n = 0; n < pathname.length; n++) { if (pathname[n] === '%') { const third = pathname.codePointAt(n + 2) | 0x20; if (pathname[n + 1] === '2' && third === 102) { throw new Error(`file URL path must not include encoded / characters`); } } } return decodeURIComponent(pathname); } function isWindowsPath(filePath) { if (!isWindows) return filePath; if (filePath && filePath.length >= 3) { if (filePath.charCodeAt(0) === 92 && filePath.charCodeAt(1) === 92) { return true; } if (filePath.charCodeAt(1) === 58 && filePath.charCodeAt(2) === 92) { const code = filePath.charCodeAt(0); return (code >= 65 && code <= 90) || (code >= 97 && code <= 122); } } return false; } function nodePathToString(path) { if (typeof path !== 'string' && !Buffer.isBuffer(path)) { if (!(path instanceof __webpack_require__("url").URL)) throw new Error(`The "path" argument must be one of type string, Buffer, or URL. Received type ${typeof path}`); path = getPathFromURLPosix(path); } // in case it is a buffer convert const pathString = String(path); // null check if (('' + pathString).indexOf('\u0000') !== -1) { const er = new Error('path must be a string without null bytes'); er.code = 'ENOENT'; throw er; } return pathString; } function unixifyPath(filePath) { if (!isWindows) return filePath; if (filePath && typeof filePath === 'string') { return ( filePath // simplify drive letter .replace(/^\\\\\?\\(.):\\/, '$1:\\') // back slashes -> forward slashes with deduplicate // eslint-disable-next-line no-useless-escape .replace(/[\\\/]+/g, '/') // remove drive letter .replace(/^([a-zA-Z]+:|\.\/)/, '') // remove trailing slash .replace(/(.+?)\/$/, '$1') ); } return filePath; } // CONCATENATED MODULE: ./src/common/string.js function stripBOM(content) { return content && content.charCodeAt(0) === 0xfeff ? content.slice(1) : content; } function strToEncoding(str, encoding = 'utf8') { if (encoding === 'utf8') return str; if (encoding === 'buffer') return new Buffer(str); return Buffer.from(str).toString(encoding); } // CONCATENATED MODULE: ./src/common/index.js /***/ }), /***/ "./src/filesystem/index.js": /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; // EXPORTS __webpack_require__.d(__webpack_exports__, "a", function() { return /* reexport */ filesystem_StaticFilesystem; }); // UNUSED EXPORTS: ReadableStaticVolume, WritableStaticVolume // EXTERNAL MODULE: external "fs" var external_fs_ = __webpack_require__("fs"); // EXTERNAL MODULE: external "path" var external_path_ = __webpack_require__("path"); // EXTERNAL MODULE: external "os" var external_os_ = __webpack_require__("os"); // EXTERNAL MODULE: ./src/common/index.js + 5 modules var common = __webpack_require__("./src/common/index.js"); // EXTERNAL MODULE: external "stream" var external_stream_ = __webpack_require__("stream"); // EXTERNAL MODULE: external "util" var external_util_ = __webpack_require__("util"); var external_util_default = /*#__PURE__*/__webpack_require__.n(external_util_); // CONCATENATED MODULE: ./src/filesystem/streams/read.js // NOTE: this is a raw re-implementation of https://github.com/nodejs/node/blob/v10.x/lib/internal/fs/streams.js#L45 // that is setup to follow our needs for the static filesystem const ERR_STR_OPTS = (typeOfOptions) => `Expected options to be either an object or a string, but got ${typeOfOptions} instead`; function assertEncoding(encoding) { if (encoding && !Buffer.isEncoding(encoding)) throw new Error(`ERR_INVALID_OPT_VALUE_ENCODING ${encoding}`); } function getOptions(options, defaultOptions) { if (options === null || options === undefined || typeof options === 'function') { return defaultOptions; } if (typeof options === 'string') { defaultOptions = external_util_default.a._extend({}, defaultOptions); defaultOptions.encoding = options; options = defaultOptions; } else if (typeof options !== 'object') { throw new ERR_STR_OPTS(typeof options); } if (options.encoding !== 'buffer') assertEncoding(options.encoding); return options; } function copyObject(source) { const target = {}; for (const key in source) target[key] = source[key]; return target; } const kMinPoolSpace = 128; let pool; // It can happen that we expect to read a large chunk of data, and reserve // a large chunk of the pool accordingly, but the read() call only filled // a portion of it. If a concurrently executing read() then uses the same pool, // the "reserved" portion cannot be used, so we allow it to be re-used as a // new pool later. const poolFragments = []; function allocNewPool(poolSize) { if (poolFragments.length > 0) pool = poolFragments.pop(); else pool = Buffer.allocUnsafe(poolSize); pool.used = 0; } function ReadStream(sfs, path, options) { if (!(this instanceof ReadStream)) return new ReadStream(sfs, path, options); this._sfs = sfs; // a little bit bigger buffer and water marks by default options = copyObject(getOptions(options, {})); if (options.highWaterMark === undefined) options.highWaterMark = 64 * 1024; // for backwards compat do not emit close on destroy. options.emitClose = false; external_stream_["Readable"].call(this, options); this.path = path; this.fd = options.fd === undefined ? null : options.fd; this.flags = options.flags === undefined ? 'r' : options.flags; this.mode = options.mode === undefined ? 0o666 : options.mode; this.start = options.start; this.end = options.end; this.autoClose = options.autoClose === undefined ? true : options.autoClose; this.pos = undefined; this.bytesRead = 0; this.closed = false; if (this.start !== undefined) { if (typeof this.start !== 'number' || Number.isNaN(this.start)) { throw new TypeError('"start" option must be a Number'); } if (this.end === undefined) { this.end = Infinity; } else if (typeof this.end !== 'number' || Number.isNaN(this.end)) { throw new TypeError('"end" option must be a Number'); } if (this.start > this.end) { throw new Error('"start" option must be <= "end" option'); } this.pos = this.start; } if (typeof this.end !== 'number') this.end = Infinity; else if (Number.isNaN(this.end)) throw new TypeError('"end" option must be a Number'); if (!this.fd || !this.fd.type || this.fd.type !== 'static_fs_file_descriptor') this.open(); this.on('end', function() { if (this.autoClose) { this.destroy(); } }); } external_util_default.a.inherits(ReadStream, external_stream_["Readable"]); ReadStream.prototype.open = function() { this._sfs.open(this.path, (er, fd) => { if (er) { if (this.autoClose) { this.destroy(); } this.emit('error', er); return; } this.fd = fd; this.emit('open', fd); this.emit('ready'); // start the flow of data. this.read(); }); }; ReadStream.prototype._read = function(n) { if (!this.fd || !this.fd.type || this.fd.type !== 'static_fs_file_descriptor') { return this.once('open', function() { this._read(n); }); } if (this.destroyed) return; if (!pool || pool.length - pool.used < kMinPoolSpace) { // discard the old pool. allocNewPool(this.readableHighWaterMark); } // Grab another reference to the pool in the case that while we're // in the thread pool another read() finishes up the pool, and // allocates a new one. const thisPool = pool; let toRead = Math.min(pool.length - pool.used, n); const start = pool.used; if (this.pos !== undefined) toRead = Math.min(this.end - this.pos + 1, toRead); else toRead = Math.min(this.end - this.bytesRead + 1, toRead); // already read everything we were supposed to read! // treat as EOF. if (toRead <= 0) return this.push(null); // the actual read. this._sfs.read(this.fd, pool, pool.used, toRead, this.pos, (er, bytesRead) => { if (er) { if (this.autoClose) { this.destroy(); } this.emit('error', er); } else { let b = null; // Now that we know how much data we have actually read, re-wind the // 'used' field if we can, and otherwise allow the remainder of our // reservation to be used as a new pool later. if (start + toRead === thisPool.used && thisPool === pool) thisPool.used += bytesRead - toRead; else if (toRead - bytesRead > kMinPoolSpace) poolFragments.push(thisPool.slice(start + bytesRead, start + toRead)); if (bytesRead > 0) { this.bytesRead += bytesRead; b = thisPool.slice(start, start + bytesRead); } this.push(b); } }); // move the pool positions, and internal position for reading. if (this.pos !== undefined) this.pos += toRead; pool.used += toRead; }; ReadStream.prototype._destroy = function(err, cb) { if (!this.fd || !this.fd.type || this.fd.type !== 'static_fs_file_descriptor') { this.once('open', closeFsStream.bind(null, this, cb, err)); return; } closeFsStream(this, cb, err); this.fd = null; }; function closeFsStream(stream, cb, err) { stream._sfs.close(stream.fd, (er) => { er = er || err; cb(er); stream.closed = true; if (!er) stream.emit('close'); }); } ReadStream.prototype.close = function(cb) { this.destroy(null, cb); }; Object.defineProperty(ReadStream.prototype, 'pending', { get() { return this.fd === null; }, configurable: true, }); // CONCATENATED MODULE: ./src/filesystem/streams/index.js // CONCATENATED MODULE: ./src/filesystem/volume/readable.js class readable_ReadableStaticVolume { constructor(sourcePath) { this.sourcePath = sourcePath; this.indexPath = Object(external_path_["resolve"])(Object(external_path_["dirname"])(this.sourcePath), 'static_fs_index.json'); this.moutingRoot = Object(external_path_["resolve"])(Object(external_path_["dirname"])(this.sourcePath), '../'); this.runtimePath = Object(external_path_["resolve"])(Object(external_path_["dirname"])(this.sourcePath), 'static_fs_runtime.js'); this.reset(); } reset() { this.directoriesIndex = {}; this.filesBeingRead = {}; this.filesIndex = {}; this.hash = ''; this.volumeFd = -1; this.volumeStats = {}; } load() { // already load? if (this.volumeFd >= 0) { return; } // read the index this.volumeFd = external_fs_["openSync"](this.sourcePath, 'r'); // read first int into int buffer const intBuffer = Buffer.alloc(common["a" /* INT_SIZE */]); // read hash let hashSize = this.readInt(intBuffer); let hashBuffer = Buffer.alloc(hashSize); this.readBuffer(hashBuffer, hashSize); this.hash = hashBuffer.toString('utf8', 0, hashSize); const indexContent = JSON.parse(external_fs_["readFileSync"](this.indexPath, 'utf8')); this.volumeStats = this.buildVolumeStatsFromJSON(indexContent.volumeStats); this.directoriesIndex = indexContent.directoriesIndex; this.filesIndex = indexContent.filesIndex; // verify hash const hashCheckIndex = new Set(); Object.keys(this.filesIndex).forEach((filePath) => hashCheckIndex.add(filePath)); const hashCheck = Object(common["b" /* calculateHash */])(Array.from(hashCheckIndex.values()).sort()); if (hashCheck !== this.hash) { throw new Error( `Something went wrong loading the volume ${this.sourcePath}. Check hash after loading is different from the one stored in the volume.`, ); } } buildVolumeStatsFromJSON(jsonVolumeStats) { return { isDirectory: () => false, isSymbolicLink: () => false, isBlockDevice: () => false, isCharacterDevice: () => false, isFile: () => true, isFIFO: () => false, isSocket: () => false, ...jsonVolumeStats, atime: new Date(jsonVolumeStats.atime), mtime: new Date(jsonVolumeStats.mtime), ctime: new Date(jsonVolumeStats.ctime), birthtime: new Date(jsonVolumeStats.birthtime), }; } readBuffer(buffer, length) { return external_fs_["readSync"](this.volumeFd, buffer, 0, length || buffer.length, null); } readInt(intBuffer) { external_fs_["readSync"](this.volumeFd, intBuffer, 0, common["a" /* INT_SIZE */], null); return intBuffer.readIntBE(0, 6); } shutdown() { // In case fd is open close it // to release the file if (this.volumeFd > 0) { external_fs_["closeSync"](this.volumeFd); } this.reset(); } getFromFilesIndex(filePath) { return this.filesIndex[this._resolveAndUnmountPath(filePath)]; } getFromDirectoriesIndex(dirPath) { return this.directoriesIndex[this._resolveAndUnmountPath(dirPath)]; } getFromIndex(itemPath) { const fileItem = this.getFromFilesIndex(itemPath); const dirItem = this.getFromDirectoriesIndex(itemPath); if (!fileItem && !dirItem) { return null; } const item = fileItem ? Object.assign({}, fileItem, { isDirectory: () => false, isFile: () => true }) : { isDirectory: () => true, isFile: () => false }; return { ...this.volumeStats, ...item, blocks: fileItem ? 1 : 0, blksize: fileItem ? fileItem.size : this.volumeStats.blksize, }; } getStatsFromFilepath(filePath, bigInt = false) { const baseStats = this.getFromIndex(filePath); if (!bigInt) { return baseStats; } const getBigInt = (num) => { if (typeof BigInt !== 'function') { throw new Error('BigInt is not supported.'); } return BigInt(num); }; const bigIntStats = [ 'size', 'dev', 'mode', 'nlink', 'uid', 'gid', 'rdev', 'blksize', 'ino', 'blocks', 'atimeMs', 'mtimeMs', 'ctimeMs', 'birthtimeMs', ].reduce((newStats, statVal) => { if (Object.prototype.hasOwnProperty.call(baseStats, statVal)) { newStats[statVal] = getBigInt(baseStats[statVal]); } return newStats; }, {}); return { ...baseStats, ...bigIntStats(), }; } getRealpath(filePath, encoding = 'utf8') { if (!this.getFromIndex(filePath)) { return undefined; } return Object(common["l" /* strToEncoding */])(filePath, encoding); } getDirInfo(dirPath, encoding = 'utf8', withFileTypes = false) { const dirIdxData = this.getFromDirectoriesIndex(dirPath); if (!dirIdxData) { return undefined; } return dirIdxData.sort().map((dirInfoElem) => { const encodedDirInfoElem = Object(common["l" /* strToEncoding */])(dirInfoElem, encoding); if (!withFileTypes) { return encodedDirInfoElem; } const isElemDir = !!this.getFromDirectoriesIndex(dirInfoElem); return { name: encodedDirInfoElem, isDirectory: () => isElemDir, isFile: () => !isElemDir, isBlockDevice: () => false, isCharacterDevice: () => false, isSymbolicLink: () => false, isFIFO: () => false, isSocket: () => false, }; }); } _resolveAndUnmountPath(mountedPath) { if (!mountedPath.includes(this.moutingRoot)) { return mountedPath; } // mountRoot path + slash return Object(common["n" /* unixifyPath */])(mountedPath).slice(Object(common["n" /* unixifyPath */])(this.moutingRoot).length + 1); } readFileSync(filePath, options) { const item = this.getFromIndex(filePath); if (!item || !item.isFile()) { return undefined; } const encoding = options ? typeof options === 'string' ? options : typeof options === 'object' ? options.encoding : null : null; const buf = Buffer.alloc(item.size); // read the content into the created buffer external_fs_["readSync"](this.volumeFd, buf, 0, item.size, item.ino); if (!encoding) { return buf; } return buf.toString(encoding, 0, item.size); } _deleteReadFileFromCache(filePath) { if (this.filesBeingRead[filePath]) { delete this.filesBeingRead[filePath]; } } _readFromCache(filePath, buffer, offset, length, position) { const cachedBuffer = this.filesBeingRead[filePath]; if (position >= cachedBuffer.length) { this._deleteReadFileFromCache(filePath); return 0; } const copiedBytes = cachedBuffer.copy(buffer, offset, position, Math.min(position + length, cachedBuffer.length)); if (copiedBytes + position === cachedBuffer.length) { this._deleteReadFileFromCache(filePath); } return copiedBytes; } readSync(filePath, buffer, offset, length, position) { const item = this.getFromIndex(filePath); if (!item || !item.isFile()) { return undefined; } if (position >= item.size) { // it is not possible to read // more than the file size return 0; } if (this.filesBeingRead[filePath]) { return this._readFromCache(filePath, buffer, offset, length, position); } else { const cachedFileBuffer = (this.filesBeingRead[filePath] = Buffer.alloc(item.size)); external_fs_["readSync"](this.volumeFd, cachedFileBuffer, 0, item.size, item.ino); return this._readFromCache(filePath, buffer, offset, length, position); } } } // CONCATENATED MODULE: ./src/filesystem/volume/writable.js class writable_WritableStaticVolume { constructor(mountingRoot) { this.mountingRoot = mountingRoot; this.indexFile = Object(external_path_["resolve"])(this.mountingRoot, '.static_fs/static_fs_index.json'); this.manifestFile = Object(external_path_["resolve"])(this.mountingRoot, '.static_fs/static_fs_manifest.json'); this.outputFile = Object(external_path_["resolve"])(this.mountingRoot, '.static_fs/static_fs_volume.sfsv'); this.reset(); } reset() { this.directoriesIndex = {}; this.hash = ''; this.hashBuffer = Buffer.allocUnsafe(0); this.filesIndex = {}; this.intBuffer = Buffer.alloc(common["a" /* INT_SIZE */]); } async addFolder(sourceFolder, exclusions) { if (this.mountingRoot === sourceFolder) { throw new Error('You cannot add the mounting root of the project to the static filesystem'); } if (!sourceFolder.includes(this.mountingRoot)) { throw new Error( `All the files to include into the static filesystem should has mountingRoot has parent: ${this.mountingRoot}`, ); } const calculatedTargetFolder = Object(external_path_["relative"])(this.mountingRoot, sourceFolder); const isFolder = (await Object(common["k" /* stat */])(sourceFolder)).isDirectory(); if (!isFolder) { throw new Error(`The given path ${sourceFolder} is not a folder.`); } // mark folder without native modules by default // it would be updated in the next walk update function this.directoriesIndex[calculatedTargetFolder] = { hasNativeModules: false, content: new Set(), }; await this.getFileNames(sourceFolder, calculatedTargetFolder, exclusions); } getHeaderLength() { this.hashBuffer = Buffer.from(this.hash, 'utf-8'); let size = common["a" /* INT_SIZE */]; // hashSize integer size += this.hashBuffer.byteLength; // hashString size += common["a" /* INT_SIZE */]; // end of header with a control zero return size; } writeInt(fd, value, position) { this.intBuffer.writeIntBE(value, 0, 6); return Object(common["o" /* write */])(fd, this.intBuffer, 0, common["a" /* INT_SIZE */], position); } async write() { await Object(common["f" /* mkdir */])(Object(external_path_["dirname"])(this.outputFile)); this.hash = Object(common["b" /* calculateHash */])( Object.keys(this.filesIndex) .map((filePath) => Object(common["n" /* unixifyPath */])(filePath)) .sort(), ); // write the main volume await this.writeVolume(); // write the index file await this.writeIndex(); // write the manifest file await this.writeManifest(); return this.hash; } async writeIndex() { const directoriesIndex = Object.keys(this.directoriesIndex).reduce((dirsIdx, dirPath) => { const unixifiedDirPath = Object(common["n" /* unixifyPath */])(dirPath); dirsIdx[unixifiedDirPath] = Array.from(this.directoriesIndex[dirPath].content.values()); return dirsIdx; }, {}); let totalDataSize = this.getHeaderLength(); const filesIndex = Object.keys(this.filesIndex).reduce((filesIdx, filePath) => { const unixifiedFilePath = Object(common["n" /* unixifyPath */])(filePath); filesIdx[unixifiedFilePath] = { ino: totalDataSize, size: this.filesIndex[filePath].size, }; totalDataSize += this.filesIndex[filePath].size; return filesIdx; }, {}); const volumeStats = await Object(common["k" /* stat */])(this.outputFile); await Object(common["p" /* writeFile */])( this.indexFile, JSON.stringify({ directoriesIndex, filesIndex, volumeStats, }), ); } async writeVolume() { const volumeFd = await Object(common["h" /* open */])(this.outputFile, 'w'); let dataOffset = this.getHeaderLength(); let headerPosition = await this.writeInt(volumeFd, this.hashBuffer.byteLength); await Object(common["o" /* write */])(volumeFd, this.hashBuffer, 0, this.hashBuffer.byteLength, headerPosition); const filePaths = Object.keys(this.filesIndex); // write the data for (const each of filePaths) { const entry = this.filesIndex[each]; const position = dataOffset; dataOffset += entry.size; const buf = await this.filesIndex[each].getBuffer(); await Object(common["o" /* write */])(volumeFd, buf, 0, buf.length, position); } await Object(common["c" /* close */])(volumeFd); } async writeManifest() { const baseManifestFileDir = Object(external_path_["dirname"])(this.manifestFile); // gather useful info const manifestContent = { manifest: Object(external_path_["basename"])(this.manifestFile), mountingRoot: Object(external_path_["relative"])(baseManifestFileDir, this.mountingRoot), hash: this.hash, volume: Object(external_path_["relative"])(baseManifestFileDir, this.outputFile), directories: Object.keys(this.directoriesIndex).sort(), files: Object.keys(this.filesIndex).sort(), }; await Object(common["p" /* writeFile */])(this.manifestFile, JSON.stringify(manifestContent, null, 2)); } async getFileNames(sourceFolder, targetFolder, exclusions) { const files = await Object(common["j" /* readdir */])(sourceFolder); const all = []; for (const file of files) { // compute the path names const sourcePath = `${sourceFolder}${external_path_["sep"]}${file}`; const targetPath = `${targetFolder}${external_path_["sep"]}${file}`; // is declared exclusion? const foundExclusion = exclusions[sourceFolder] || exclusions[sourcePath]; if (foundExclusion) { continue; } // is this a directory const ss = await Object(common["k" /* stat */])(sourcePath); if (ss.isDirectory()) { this.directoriesIndex[targetPath] = { hasNativeModules: false, content: new Set(), }; all.push(this.getFileNames(sourcePath, targetPath, exclusions)); continue; } // add native module metadata to folders index const isNativeModuleFile = file.endsWith('.node'); if (isNativeModuleFile) { this.directoriesIndex[targetFolder].hasNativeModules = true; continue; } // adds file to the content this.directoriesIndex[targetFolder].content.add(file); // it's a file. capture the details. this.filesIndex[targetPath] = { size: ss.size, getBuffer: () => Object(common["i" /* readFile */])(sourcePath), }; } // wait for children to finish await Promise.all(all); } getAddedFilesAndFolders() { // Recursive help function to build the parent // hierarchy for a given folder const addParentsForFolder = (folderPath, accum) => { const calculatedParent = Object(external_path_["dirname"])(Object(external_path_["resolve"])(this.mountingRoot, folderPath)); const parent = Object(external_path_["dirname"])(folderPath); if (calculatedParent && calculatedParent !== this.mountingRoot && calculatedParent.includes(this.mountingRoot)) { accum[parent] = true; return addParentsForFolder(parent, accum); } }; // Get the base folders with native modules files on it // and build the complete path with parents const foldersWithNativeModulesIndex = Object.keys(this.directoriesIndex).reduce((accum, folderPath) => { if (this.directoriesIndex[folderPath].hasNativeModules && !accum[folderPath]) { accum[folderPath] = true; addParentsForFolder(folderPath, accum); } return accum; }, {}); // To the entire list of added folders // remove the entire hierarchy for the ones // with native modules const addedFolders = Object.keys(this.directoriesIndex) .filter((folderPath) => !foldersWithNativeModulesIndex[folderPath]) .map((folderPath) => Object(external_path_["resolve"])(this.mountingRoot, folderPath)); const addedFiles = Object.keys(this.filesIndex).map((filePath) => Object(external_path_["resolve"])(this.mountingRoot, filePath)); // Finally return the curated list of filtered folders // and added files return addedFiles.concat(addedFolders).sort((a, b) => b.localeCompare(a)); } } // CONCATENATED MODULE: ./src/filesystem/volume/index.js // CONCATENATED MODULE: ./src/filesystem/filesystem.js class filesystem_StaticFilesystem { static NewError(code, method, data) { switch (code) { case external_os_["constants"].errno.ENOENT: return { ...new Error(`ENOENT: no such file or directory, ${method} '${data}'`), code: 'ENOENT', path: data, errno: external_os_["constants"].errno.ENOENT, }; case external_os_["constants"].errno.EISDIR: return { ...new Error(`EISDIR: illegal operation on a directory, ${method} '${data}'`), code: 'EISDIR', path: data, errno: external_os_["constants"].errno.EISDIR, }; case external_os_["constants"].errno.EBADF: return { ...new Error(`EBADF: bad file number, ${method} ${data}`), code: 'EBADF', info: data, errno: external_os_["constants"].errno.EBADF, }; case external_os_["constants"].errno.ENOTDIR: return { ...new Error(`ENOTDIR: not a directory, ${method} '${data}'`), code: 'ENOTDIR', path: data, errno: external_os_["constants"].errno.ENOTDIR, }; case external_os_["constants"].errno.EROFS: return { ...new Error(`EROFS: Static-Fs is a read-only filesystem, ${method} '${data}'`), code: 'EROFS', path: data, errno: external_os_["constants"].errno.EROFS, }; case external_os_["constants"].errno.EACCES: return { ...new Error(`EACCES: permission denied, ${method} '${data}'`), code: 'EACCES', path: data, errno: external_os_["constants"].errno.EACCES, }; } return { ...new Error(`UNKNOWN_ERROR: Something unexpected happened , ${method} ${data}`), code: 'UNKNOWN_ERROR', info: data, errno: -9999, }; } constructor() { this.fds = {}; this.volumes = {}; } shutdown() { for (const volume of Object.values(this.volumes)) { volume.shutdown(); } } load(sourcePath) { sourcePath = Object(external_path_["resolve"])(sourcePath); if (this.volumes[sourcePath]) { // already loaded? return this; } const volume = new readable_ReadableStaticVolume(sourcePath); volume.load(); this.volumes[volume.sourcePath] = volume; return this; } get loadedVolumes() { return Object.keys(this.volumes); } areFlagsValid(flags) { return !(flags && flags !== 'r'); } isValidFD(fd) { const isCorrectFormat = fd && fd.id && fd.type && fd.type === 'static_fs_file_descriptor'; const isPresent = this.fds[fd.id]; return isCorrectFormat && isPresent; } getValidatedFD(fd) { if (!this.isValidFD(fd)) { throw filesystem_StaticFilesystem.NewError(external_os_["constants"].errno.EBADF, 'getValidatedFD', fd); } return this.fds[fd.id]; } getVolumeForPath(itemPath) { const volKeys = Object.keys(this.volumes); for (let i = 0; i < volKeys.length; i++) { const vol = this.volumes[volKeys[i]]; const exists = vol.getRealpath(itemPath); if (exists) { return volKeys[i]; } } return undefined; } volumeForFilepathSync(itemPath) { const volumePathForFilePath = this.getVolumeForPath(itemPath); if (!volumePathForFilePath) { return undefined; } const volumeForFilePath = this.volumes[volumePathForFilePath]; if (!volumeForFilePath) { return undefined; } return volumeForFilePath; } wrapAsync(method, args, callback) { if (typeof callback !== 'function') throw new Error('Callback is not a function'); process.nextTick(() => { try { callback(null, method.apply(this, args)); } catch (err) { err.message = err.message.replace(/Sync/gi, ''); callback(err); } }); } accessSync(path, mode) { const filePath = Object(common["g" /* nodePathToString */])(path); const volume = this.volumeForFilepathSync(filePath); if (!volume) { throw filesystem_StaticFilesystem.NewError(external_os_["constants"].errno.ENOENT, 'accessSync', filePath); } if (mode !== external_fs_["constants"].F_OK && mode !== external_fs_["constants"].R_OK && mode !== external_fs_["constants"].X_OK) { throw filesystem_StaticFilesystem.NewError(external_os_["constants"].errno.EACCES, 'accessSync', filePath); } } access(path, mode, callback) { this.wrapAsync(this.accessSync, [path, mode], callback); } readFileSync(path, options) { const isFd = this.isValidFD(path); const filePath = isFd ? path.filePath : Object(common["g" /* nodePathToString */])(path); const volume = this.volumeForFilepathSync(filePath); if (!volume) { throw filesystem_StaticFilesystem.NewError(external_os_["constants"].errno.ENOENT, 'readFileSync', filePath); } return volume.readFileSync(filePath, options); } readFile(path, options, callback) { this.wrapAsync(this.readFileSync, [path, options], callback); } readSync(fd, buffer, offset, length, position) { try { this.getValidatedFD(fd); } catch (e) { throw filesystem_StaticFilesystem.NewError(external_os_["constants"].errno.ENOENT, 'readSync', e); } const filePath = fd.filePath; const volume = this.volumeForFilepathSync(filePath); if (!volume) { throw filesystem_StaticFilesystem.NewError(external_os_["constants"].errno.ENOENT, 'readSync', fd); } return volume.readSync(filePath, buffer, offset, length, position); } read(fd, buffer, offset, length, position, callback) { // copied from Node implementation if (length === 0) { return process.nextTick(() => { if (callback) callback(null, 0, buffer); }); } process.nextTick(() => { try { const readBytes = this.readSync(fd, buffer, offset, length, position); callback(null, readBytes, buffer); } catch (err) { err.message = err.message.replace(/Sync/gi, ''); callback(err); } }); } realpathSync(path, options) { const filePath = Object(common["g" /* nodePathToString */])(path); const encoding = options && options.encoding; const volume = this.volumeForFilepathSync(filePath); if (!volume) { throw filesystem_StaticFilesystem.NewError(external_os_["constants"].errno.ENOENT, 'realpathSync', filePath); } return volume.getRealpath(filePath, encoding); } realpath(path, options, callback) { this.wrapAsync(this.realpathSync, [path, options], callback); } statSync(path, options) { const filePath = Object(common["g" /* nodePathToString */])(path); const bigInt = options && options.bigint; const volume = this.volumeForFilepathSync(filePath); if (!volume) { throw filesystem_StaticFilesystem.NewError(external_os_["constants"].errno.ENOENT, 'statSync', filePath); } return volume.getStatsFromFilepath(filePath, bigInt); } stat(path, options, callback) { this.wrapAsync(this.statSync, [path, options], callback); } readdirSync(path, options) { const dirPath = Object(common["g" /* nodePathToString */])(path); const encoding = options && options.encoding; const withFileTypes = options && options.withFileTypes; const volume = this.volumeForFilepathSync(dirPath); if (!volume) { throw filesystem_StaticFilesystem.NewError(external_os_["constants"].errno.ENOENT, 'readdirSync', dirPath); } const dirInfo = volume.getDirInfo(dirPath, encoding, withFileTypes); if (!dirInfo) { throw filesystem_StaticFilesystem.NewError(external_os_["constants"].errno.ENOTDIR, 'readdirSync', dirPath); } return dirInfo; } readdir(path, options, callback) { this.wrapAsync(this.readdirSync, [path, options], callback); } openSync(path, flags) { const filePath = Object(common["g" /* nodePathToString */])(path); const volume = this.volumeForFilepathSync(filePath); if (!volume) { throw filesystem_StaticFilesystem.NewError(external_os_["constants"].errno.ENOENT, 'openSync', filePath); } if (!this.areFlagsValid(flags)) { throw filesystem_StaticFilesystem.NewError(external_os_["constants"].errno.EROFS, 'openSync', filePath); } const fdIdentifier = `${volume.sourcePath}#${Object(common["n" /* unixifyPath */])(filePath)}`; this.fds[fdIdentifier] = { type: 'static_fs_file_descriptor', id: fdIdentifier, volumeSourcePath: volume.sourcePath, filePath: filePath, }; return this.fds[fdIdentifier]; } open(path, flags, callback) { this.wrapAsync(this.openSync, [path, flags], callback); } closeSync(fd) { try { this.getValidatedFD(fd); } catch (e) { throw filesystem_StaticFilesystem.NewError(external_os_["constants"].errno.ENOENT, 'closeSync', e); } delete this.fds[fd.id]; } close(fd, callback) { this.wrapAsync(this.closeSync, [fd], callback); } fstatSync(fd, options) { try { this.getValidatedFD(fd); } catch (e) { throw filesystem_StaticFilesystem.NewError(external_os_["constants"].errno.ENOENT, 'fstatSync', e); } return this.statSync(fd.filePath, options); } fstat(fd, options, callback) { this.wrapAsync(this.fstatSync, [fd, options], callback); } createReadStream(path, options) { const optionsFlags = options && options.flags; const optionsFd = options && options.fd; const filePath = optionsFd ? path : Object(common["g" /* nodePathToString */])(path); if (!this.areFlagsValid(optionsFlags)) { throw filesystem_StaticFilesystem.NewError(external_os_["constants"].errno.EROFS, 'createReadStream', filePath); } return new ReadStream(this, filePath, options); } } // CONCATENATED MODULE: ./src/filesystem/index.js /***/ }), /***/ "./src/runtime/index.js": /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony import */ var _runtime__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/runtime/runtime.js"); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "load", function() { return _runtime__WEBPACK_IMPORTED_MODULE_0__["a"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "shutdown", function() { return _runtime__WEBPACK_IMPORTED_MODULE_0__["b"]; }); /* harmony import */ var _patch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__("./src/runtime/patch/index.js"); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "patchChildProcess", function() { return _patch__WEBPACK_IMPORTED_MODULE_1__["a"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "patchFilesystem", function() { return _patch__WEBPACK_IMPORTED_MODULE_1__["b"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "patchModuleLoader", function() { return _patch__WEBPACK_IMPORTED_MODULE_1__["c"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "patchProcess", function() { return _patch__WEBPACK_IMPORTED_MODULE_1__["d"]; }); /***/ }), /***/ "./src/runtime/patch/index.js": /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; // EXPORTS __webpack_require__.d(__webpack_exports__, "a", function() { return /* reexport */ patchChildProcess; }); __webpack_require__.d(__webpack_exports__, "b", function() { return /* reexport */ patchFilesystem; }); __webpack_require__.d(__webpack_exports__, "c", function() { return /* reexport */ patchModuleLoader; }); __webpack_require__.d(__webpack_exports__, "d", function() { return /* reexport */ patchProcess; }); // EXTERNAL MODULE: external "child_process" var external_child_process_ = __webpack_require__("child_process"); // EXTERNAL MODULE: external "fs" var external_fs_ = __webpack_require__("fs"); // EXTERNAL MODULE: external "util" var external_util_ = __webpack_require__("util"); // CONCATENATED MODULE: ./src/runtime/patch/child_process.js const nodeBinPossibilities = ['node', 'node.exe', process.execPath, process.argv[0]]; // assureCwdOnSanitizedOptions is needed as it is reasonable // to declare cwd in the child_process function's options // for resources bundled inside the static-fs volumes // and in that case will not exist on the real filesystem. // As nodejs uses uv_spawn to launch the child_process ultimately, // that library would throw an error if the cwd does not exist function assureCwdOnSanitizedOptions(options, sfs, existsOnRealFs) { const cwd = options && options.cwd; if (!cwd) { return; } try { const cwdStat = sfs.statSync(cwd); if (!!cwdStat && cwdStat.isDirectory() && !existsOnRealFs(cwd)) { external_fs_["mkdirSync"](cwd, { recursive: true }); } } catch { /* no-op */ } } // Build the args order for the child_process functions // The order is always the same: // 1 - first add the arguments starting with -- or - // 2 - add the main static fs runtime path // 3 - add the static-fs-volumes flag // 4 - add the main entry // 5 - add the rest of the args // // However for fork the order is different as fork don't accept // node args starting by -- or - unless we set the options.execArgs // So for fork (when mainStaticFsRuntimePath === null) the order is // // 1 - add the static-fs-volumes flag // 2 - add the main entry // 3 - add the rest of the args function buildStaticFsArgs(args, mainStaticFsRuntimePath, staticFsVolumesPaths, mainEntry = null) { const sanitizedArgs = Array.isArray(args) ? args : typeof args === 'string' ? args.split(' ') : []; const builtArgs = []; let toAddMetaArgs = true; const iterableArgs = [...sanitizedArgs]; do { const wasArrayEmptyBeforeArg = iterableArgs.length === 0; const arg = iterableArgs.shift(); if (!arg && !wasArrayEmptyBeforeArg) { return; } if ((typeof arg === 'string' && arg.startsWith('-') && mainStaticFsRuntimePath) ||