UNPKG

@storm-stack/core

Version:

A build toolkit and runtime used by Storm Software in TypeScript applications

1,143 lines (1,136 loc) 43.5 kB
'use strict'; var chunkRD3O7HS7_cjs = require('./chunk-RD3O7HS7.cjs'); var chunkGRNJVY7I_cjs = require('./chunk-GRNJVY7I.cjs'); var types = require('@storm-software/config-tools/types'); var bufferToString = require('@stryke/convert/buffer-to-string'); var toArray = require('@stryke/convert/to-array'); var hash = require('@stryke/hash/hash'); var filePathFns = require('@stryke/path/file-path-fns'); var isParentPath = require('@stryke/path/is-parent-path'); var isType = require('@stryke/path/is-type'); var joinPaths = require('@stryke/path/join-paths'); var prettyBytes = require('@stryke/string-format/pretty-bytes'); var isBuffer = require('@stryke/type-checks/is-buffer'); var isFunction = require('@stryke/type-checks/is-function'); var isSetString = require('@stryke/type-checks/is-set-string'); var defu = require('defu'); var memfs = require('memfs'); var buffer = require('buffer'); var fs = require('fs'); var prettier = require('prettier'); var unionfs = require('unionfs'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var defu__default = /*#__PURE__*/_interopDefault(defu); var fs__default = /*#__PURE__*/_interopDefault(fs); // src/types/vfs.ts var __VFS_INIT__ = "__VFS_INIT__"; var __VFS_REVERT__ = "__VFS_REVERT__"; var __VFS_CACHE__ = "__VFS_CACHE__"; var __VFS_RESOLVER__ = "__VFS_RESOLVER__"; var __VFS_VIRTUAL__ = "__VFS_VIRTUAL__"; var __VFS_UNIFIED__ = "__VFS_UNIFIED__"; var FILE_PREFIX = "file://"; function toFilePath(pathOrUrl) { if (!pathOrUrl) { throw new Error("No Path or URL provided to Virtual File System"); } let result = pathOrUrl.toString(); if (result.startsWith(FILE_PREFIX)) { result = result.slice(FILE_PREFIX.length); } return result; } chunkGRNJVY7I_cjs.__name(toFilePath, "toFilePath"); var FS_METHODS = [ "mkdir", "mkdirSync", "rmdir", "rmdirSync", "unlink", "unlinkSync", "existsSync", "realpathSync", "writeFileSync", "readFileSync", "readdirSync", "createWriteStream", "WriteStream", "createReadStream", "ReadStream" ]; var FS_PROMISE_METHODS = [ "mkdir", "rm", "rmdir", "unlink", "writeFile", "readFile", "readdir", "stat", "lstat" ]; function cloneFS(originalFS) { const clonedFS = { ...originalFS, promises: { ...originalFS.promises ?? {} } }; for (const method of FS_METHODS) { if (originalFS[method]) { clonedFS[method] = originalFS[method]; } } originalFS.promises ??= {}; for (const method of FS_PROMISE_METHODS) { if (originalFS.promises[method]) { clonedFS.promises ??= {}; clonedFS.promises[method] = originalFS.promises[method]; clonedFS[method] = originalFS.promises[method]; } } for (const prop in clonedFS) { if (isFunction.isFunction(clonedFS[prop])) { clonedFS[prop] = clonedFS[prop].bind(originalFS); if (isFunction.isFunction(clonedFS.promises[prop])) { clonedFS.promises[prop] = clonedFS.promises[prop].bind(originalFS); } } } for (const prop in clonedFS.promises) { if (isFunction.isFunction(clonedFS.promises[prop])) { clonedFS.promises[prop] = clonedFS.promises[prop].bind(originalFS); } } return clonedFS; } chunkGRNJVY7I_cjs.__name(cloneFS, "cloneFS"); function patchFS(originalFS, vfs) { const clonedFS = cloneFS(originalFS); originalFS.mkdirSync = (file, options) => vfs.mkdirSync(toFilePath(file), options); originalFS.mkdir = (file, options, callback) => vfs.mkdir(toFilePath(file), options, callback); originalFS.promises.mkdir = async (file, options) => vfs.mkdir(toFilePath(file), options); originalFS.unlinkSync = (file) => vfs.unlinkSync(toFilePath(file)); originalFS.promises.rm = async (file, options) => vfs.rm(toFilePath(file), options); originalFS.promises.unlink = async (file) => vfs.unlink(toFilePath(file)); originalFS.existsSync = (file) => vfs.existsSync(toFilePath(file)); Object.defineProperty(originalFS, "realpathSync", { value: /* @__PURE__ */ chunkGRNJVY7I_cjs.__name((file, options) => vfs.realpathSync(toFilePath(file), options), "value") }); originalFS.writeFileSync = (file, data, options) => vfs.writeFileSync(toFilePath(file), data, options); originalFS.promises.writeFile = async (file, data, options) => vfs.writeFile(toFilePath(file), data, options); originalFS.readFileSync = (file, options) => vfs.readFileSync(toFilePath(file), options); originalFS.promises.readFile = (file, options) => vfs.readFile(toFilePath(file), options); originalFS.readdirSync = (file, options) => vfs.readdirSync(toFilePath(file), options); originalFS.promises.readdir = (file, options) => vfs.readdir(toFilePath(file), options); Object.defineProperty(originalFS, "statSync", { value: /* @__PURE__ */ chunkGRNJVY7I_cjs.__name((file, options) => vfs.statSync(toFilePath(file), options), "value") }); originalFS.stat = (file, options) => vfs.statSync(toFilePath(file), options); originalFS.promises.stat = (file, options) => vfs.stat(toFilePath(file), options); Object.defineProperty(originalFS, "lstatSync", { value: /* @__PURE__ */ chunkGRNJVY7I_cjs.__name((file, options) => vfs.lstatSync(toFilePath(file), options), "value") }); originalFS.lstat = (file, options) => vfs.lstatSync(toFilePath(file), options); originalFS.promises.lstat = (file, options) => vfs.lstat(toFilePath(file), options); return () => { originalFS.mkdirSync = clonedFS.mkdirSync; originalFS.mkdir = clonedFS.mkdir; originalFS.promises.mkdir = clonedFS.promises.mkdir; originalFS.unlinkSync = clonedFS.unlinkSync; originalFS.promises.rm = clonedFS.promises.rm; originalFS.promises.unlink = clonedFS.promises.unlink; originalFS.existsSync = clonedFS.existsSync; originalFS.realpathSync = clonedFS.realpathSync; originalFS.writeFileSync = clonedFS.writeFileSync; originalFS.promises.writeFile = clonedFS.promises.writeFile; originalFS.readFileSync = clonedFS.readFileSync; originalFS.promises.readFile = clonedFS.promises.readFile; originalFS.readdirSync = clonedFS.readdirSync; originalFS.promises.readdir = clonedFS.promises.readdir; Object.defineProperty(originalFS, "statSync", { value: clonedFS.statSync }); originalFS.stat = clonedFS.stat; originalFS.promises.stat = clonedFS.promises.stat; Object.defineProperty(originalFS, "lstatSync", { value: clonedFS.lstatSync }); originalFS.lstat = clonedFS.lstat; originalFS.promises.lstat = clonedFS.promises.lstat; }; } chunkGRNJVY7I_cjs.__name(patchFS, "patchFS"); function checkVariants(request, vfs, parentPath) { const path = parentPath ? joinPaths.joinPaths(parentPath, request) : request; let file = checkExtensions(path, vfs); if (file) { return file; } file = checkIndex(path, vfs); if (file) { return file; } return false; } chunkGRNJVY7I_cjs.__name(checkVariants, "checkVariants"); function checkIndex(request, vfs) { let file = joinPaths.joinPaths(request, "index"); if (vfs.fileExistsSync(file)) { return file; } file = checkExtensions(file, vfs); if (file) { return file; } return false; } chunkGRNJVY7I_cjs.__name(checkIndex, "checkIndex"); function checkExtensions(request, vfs) { let file = `${request}.ts`; if (vfs.fileExistsSync(file)) { return file; } file = `${request}.mts`; if (vfs.fileExistsSync(file)) { return file; } file = `${request}.cts`; if (vfs.fileExistsSync(file)) { return file; } file = `${request}.tsx`; if (vfs.fileExistsSync(file)) { return file; } file = `${request}.js`; if (vfs.fileExistsSync(file)) { return file; } file = `${request}.mjs`; if (vfs.fileExistsSync(file)) { return file; } file = `${request}.cjs`; if (vfs.fileExistsSync(file)) { return file; } file = `${request}.jsx`; if (vfs.fileExistsSync(file)) { return file; } file = `${request}.json`; if (vfs.fileExistsSync(file)) { return file; } file = `${request}.d.ts`; if (vfs.fileExistsSync(file)) { return file; } return false; } chunkGRNJVY7I_cjs.__name(checkExtensions, "checkExtensions"); // src/base/vfs/virtual-file-system.ts var RUNTIME_PREFIX = "storm:"; var VirtualFileSystem = class { static { chunkGRNJVY7I_cjs.__name(this, "VirtualFileSystem"); } /** * The internal map of virtual files. */ #runtimeIdMap = /* @__PURE__ */ new Map(); /** * A map of virtual file paths to their underlying file content. */ #cachedFS = /* @__PURE__ */ new Map(); /** * A map of virtual file paths to their underlying file content. */ #cachedResolver = /* @__PURE__ */ new Map(); /** * The internal map of virtual files. */ #virtualFS = new memfs.Volume(); /** * The physical file system. */ #fs = cloneFS(fs__default.default); /** * The unified volume that combines the virtual file system with the real file system. * * @remarks * This volume allows for seamless access to both virtual and real files. */ #unifiedFS = new unionfs.Union(); /** * Indicator specifying if the file system module is patched */ #isPatched = false; /** * Function to revert require patch */ #revert; /** * The context of the virtual file system. */ #context; /** * The file system's logging function. */ #log; /** * Exposes the internal VFS map for advanced usage. */ get [__VFS_CACHE__]() { return this.#cachedFS; } /** * Exposes the internal VFS resolver cache for advanced usage. */ get [__VFS_RESOLVER__]() { return this.#cachedResolver; } /** * Exposes the internal VFS map for advanced usage. */ get [__VFS_VIRTUAL__]() { return this.#virtualFS; } /** * Exposes the internal UFS map for advanced usage. */ get [__VFS_UNIFIED__]() { return this.#unifiedFS; } /** * Creates a new instance of the VirtualFileSystem. * * @param context - The context of the virtual file system, typically containing options and logging functions. * @param serialized - A map of files/file contents to populate in cache */ constructor(context, serialized) { this.#context = context; this.#cachedFS = /* @__PURE__ */ new Map(); this.#runtimeIdMap = new Map(Object.entries(serialized?.runtimeIdMap ?? {})); if (!this.#fs.existsSync(this.#context.dataPath)) { this.#fs.mkdirSync(this.#context.dataPath, { recursive: true }); } if (!this.#fs.existsSync(this.#context.cachePath)) { this.#fs.mkdirSync(this.#context.cachePath, { recursive: true }); } if (!this.#fs.existsSync(joinPaths.joinPaths(this.#context.options.workspaceRoot, this.#context.options.output.outputPath))) { this.#fs.mkdirSync(joinPaths.joinPaths(this.#context.options.workspaceRoot, this.#context.options.output.outputPath), { recursive: true }); } this.#unifiedFS = this.#unifiedFS.use(this.#fs); if (this.#context.options.output.outputMode !== "fs") { if (serialized?.virtualFiles && Object.keys(serialized.virtualFiles).length > 0) { this.#virtualFS = memfs.Volume.fromJSON(serialized.virtualFiles); } if (!this.#virtualFS.existsSync(this.#context.artifactsPath)) { this.#virtualFS.mkdirSync(this.#context.artifactsPath, { recursive: true }); } if (!this.#virtualFS.existsSync(this.#context.runtimePath)) { this.#virtualFS.mkdirSync(this.#context.runtimePath, { recursive: true }); } if (!this.#virtualFS.existsSync(this.#context.entryPath)) { this.#virtualFS.mkdirSync(this.#context.entryPath, { recursive: true }); } if (!this.#virtualFS.existsSync(this.#context.dtsPath)) { this.#virtualFS.mkdirSync(this.#context.dtsPath, { recursive: true }); } this.#unifiedFS = this.#unifiedFS.use(this.#virtualFS); } else { if (!this.#fs.existsSync(this.#context.artifactsPath)) { this.#fs.mkdirSync(this.#context.artifactsPath, { recursive: true }); } if (!this.#fs.existsSync(this.#context.runtimePath)) { this.#fs.mkdirSync(this.#context.runtimePath, { recursive: true }); } if (!this.#fs.existsSync(this.#context.entryPath)) { this.#fs.mkdirSync(this.#context.entryPath, { recursive: true }); } if (!this.#fs.existsSync(this.#context.dtsPath)) { this.#fs.mkdirSync(this.#context.dtsPath, { recursive: true }); } } this.#log = chunkRD3O7HS7_cjs.extendLog(this.#context.log, "virtual-file-system"); } [__VFS_INIT__]() { if (!this.#isPatched && this.#context.options.output.outputMode !== "fs") { this.#revert = patchFS(fs__default.default, this); this.#isPatched = true; } } [__VFS_REVERT__]() { if (this.#isPatched && this.#context.options.output.outputMode !== "fs") { if (!this.#revert) { throw new Error("Attempting to revert File System patch prior to calling `__init__` function"); } this.#revert?.(); this.#isPatched = false; } } /** * Returns a Map of all runtime file IDs and their corresponding paths in the virtual file system. * * @returns A Map where the keys are runtime file IDs (strings) and the values are their corresponding paths (strings). */ get runtimeIdMap() { return this.#runtimeIdMap; } /** * Lists all runtime IDs in the virtual file system. * * @returns An array of formatted runtime IDs. */ get runtimeIds() { return Array.from(this.runtimeIdMap.keys()).map((id) => this.formatRuntimeId(id)); } /** * Checks if a given path or ID corresponds to a runtime file. * * @param pathOrId - The path or ID to check. * @param options - Options for resolving the path, such as paths to check. * @returns `true` if the path or ID corresponds to a runtime file, otherwise `false`. */ isRuntimeFile(pathOrId, options) { return !!this.runtimeIdMap.values().find((path) => path === this.resolvePath(pathOrId, { ...options, type: "file" })); } /** * Checks if a provided string is a valid runtime ID (does not need to already be created in the file system). * * @param id - The ID to check. * @returns Whether the ID is a valid runtime ID. */ isValidRuntimeId(id) { return id.startsWith(RUNTIME_PREFIX); } /** * Check if a path or ID corresponds to a virtual file. * * @param pathOrId - The path or ID to check. * @param options - Options for resolving the path, such as paths to check. * @returns Whether the path or ID corresponds to a virtual file. */ isVirtualFile(pathOrId, options = {}) { if (!pathOrId) { return false; } const resolvedPath = this.resolvePath(pathOrId, { ...options, type: "file" }); if (!resolvedPath) { return false; } if (this.runtimeIdMap.values().find((path) => path === resolvedPath)) { return true; } return this.#virtualFS.existsSync(resolvedPath); } /** * Check if a path exists within one of the directories specified in the tsconfig.json's `path` field. * * @see https://www.typescriptlang.org/tsconfig#paths * * @param pathOrId - The path or ID to check. * @returns Whether the path or ID corresponds to a virtual file. */ isTsconfigPath(pathOrId) { return !!this.#context.tsconfig.options.paths && Object.keys(this.#context.tsconfig.options.paths).some((path) => pathOrId.startsWith(path.replaceAll("*", ""))); } /** * Checks if a given ID corresponds to a runtime file path. * * @param id - The unique identifier for the runtime file. * @param pathOrId - The path or ID to check. * @returns `true` if the ID corresponds to the path or ID of a runtime file, otherwise `false`. */ isMatchingRuntimeId(id, pathOrId) { const resolvedPath = this.resolvePath(pathOrId); const resolvedId = this.resolveId(pathOrId); return !!(this.isRuntimeFile(pathOrId) && (resolvedPath && (resolvedPath === this.runtimeIdMap.get(id) || resolvedPath === this.runtimeIdMap.get(this.formatRuntimeId(id))) || resolvedId && (resolvedId === this.runtimeIdMap.get(id) || resolvedId === this.runtimeIdMap.get(this.formatRuntimeId(id))))); } /** * Lists all runtime files in the virtual file system. * * @returns A promise that resolves to an array of runtime files. */ async listRuntimeFiles() { const runtimeFiles = []; for (const [id, path] of this.runtimeIdMap.entries()) { const contents = await this.readFile(path); if (contents) { runtimeFiles.push({ id: this.formatRuntimeId(id), path, contents }); } } return runtimeFiles; } /** * Lists files in a given path. * * @param path - The path to list files from. * @param options - Options for listing files, such as encoding and recursion. * @returns An array of file names in the specified path. */ readdirSync(path, options = "utf8") { return this.resolveFS(path).readdirSync(toFilePath(path), options); } /** * Removes a file in the virtual file system (VFS). * * @param path - The path to create the directory at. */ unlinkSync(path, options) { const formattedPath = toFilePath(path); if (!this.fileExistsSync(path)) { return; } this.#log(types.LogLevelLabel.TRACE, `Synchronously removing file: ${formattedPath}`); this.resolveFS(path, options).unlinkSync(formattedPath); this.#cachedFS.delete(formattedPath); this.clearResolverCache(formattedPath); } /** * Removes a file in the virtual file system (VFS). * * @param path - The path to create the directory at. */ async unlink(path, options) { const formattedPath = toFilePath(path); if (!this.fileExistsSync(path)) { return; } this.#log(types.LogLevelLabel.TRACE, `Removing file: ${formattedPath}`); if (isFunction.isFunction(this.resolveFS(path, options).promises.unlink)) { await this.resolveFS(path, options).promises.unlink(formattedPath); this.#cachedFS.delete(formattedPath); this.clearResolverCache(formattedPath); } else { this.unlinkSync(formattedPath, options); } } /** * Removes a directory in the virtual file system (VFS). * * @param path - The path to create the directory at. * @param options - Options for creating the directory. */ rmdirSync(path, options = {}) { const formattedPath = toFilePath(path); if (!this.directoryExistsSync(path)) { return; } this.#log(types.LogLevelLabel.TRACE, `Synchronously removing directory: ${formattedPath}`); this.resolveFS(path, options).rmdirSync(formattedPath, defu__default.default(options, { recursive: true })); this.#cachedFS.delete(formattedPath); this.clearResolverCache(formattedPath); } /** * Removes a directory in the virtual file system (VFS). * * @param path - The path to create the directory at. * @param options - Options for creating the directory. * @returns A promise that resolves to the path of the created directory, or undefined if the directory could not be created. */ async rmdir(path, options = {}) { const formattedPath = toFilePath(path); if (!this.directoryExistsSync(path)) { return; } this.#log(types.LogLevelLabel.TRACE, `Removing directory: ${formattedPath}`); if (isFunction.isFunction(this.resolveFS(path, options).promises.rm)) { await this.resolveFS(path, options).promises.rm(formattedPath, defu__default.default(options, { force: true, recursive: true })); this.#cachedFS.delete(formattedPath); this.clearResolverCache(formattedPath); } else { this.rmdirSync(formattedPath, defu__default.default(options ?? {}, { force: true, recursive: true })); } } /** * Removes a file in the virtual file system (VFS). * * @param path - The path to the file to remove. * @param options - Options for removing the file. * @returns A promise that resolves when the file is removed. */ async rm(path, options = {}) { this.#log(types.LogLevelLabel.TRACE, `Removing: ${toFilePath(path)}`); if (this.directoryExistsSync(path)) { return this.rmdir(path, options); } return this.unlink(path, options); } /** * Creates a directory in the virtual file system (VFS). * * @param path - The path to create the directory at. * @param options - Options for creating the directory. * @returns A promise that resolves to the path of the created directory, or undefined if the directory could not be created. */ mkdirSync(path, options = {}) { const filePath = toFilePath(path); this.clearResolverCache(filePath); return this.resolveFS(filePath, options).mkdirSync(filePath, defu__default.default(options ?? {}, { recursive: true })); } /** * Creates a directory in the virtual file system (VFS). * * @param path - The path to create the directory at. * @param options - Options for creating the directory. * @returns A promise that resolves to the path of the created directory, or undefined if the directory could not be created. */ async mkdir(path, options = {}) { let result; const filePath = toFilePath(path); if (isFunction.isFunction(this.resolveFS(filePath, options).promises.mkdir)) { result = await this.resolveFS(filePath, options).promises.mkdir(filePath, defu__default.default(options ?? {}, { recursive: true })); } else { result = this.resolveFS(filePath, options).mkdirSync(filePath, defu__default.default(options ?? {}, { recursive: true })); } this.clearResolverCache(filePath); return result; } /** * Lists files in a given path. * * @param path - The path to list files from. * @param options - Options for listing files, such as encoding and recursion. * @returns An array of file names in the specified path. */ async readdir(path, options = "utf8") { return this.resolveFS(path).promises.readdir(toFilePath(path), options); } /** * Asynchronously reads a file from the virtual file system (VFS). * * @param pathOrId - The path or ID of the file to read. * @returns A promise that resolves to the contents of the file as a string, or undefined if the file does not exist. */ async readFile(pathOrId, options = "utf8") { if (!pathOrId) { return void 0; } const filePath = this.resolvePath(toFilePath(pathOrId), { type: "file" }); if (filePath) { if (this.#cachedFS.has(filePath)) { return this.#cachedFS.get(filePath); } let result; if (isFunction.isFunction(this.resolveFS(filePath).promises.readFile)) { result = (await this.resolveFS(filePath).promises.readFile(filePath, options))?.toString("utf8"); } else { result = this.resolveFS(filePath).readFileSync(filePath, options); } const content = isBuffer.isBuffer(result) ? bufferToString.bufferToString(result) : result; this.#cachedFS.set(filePath, content); return content; } return void 0; } /** * Synchronously reads a file from the virtual file system (VFS). * * @param pathOrId - The path or ID of the file to read. * @returns The contents of the file as a string, or undefined if the file does not exist. */ readFileSync(pathOrId, options = "utf8") { if (!pathOrId) { return void 0; } const filePath = this.resolvePath(toFilePath(pathOrId), { type: "file" }); if (filePath) { if (this.#cachedFS.has(filePath)) { return this.#cachedFS.get(filePath); } const result = this.resolveFS(filePath).readFileSync(filePath, options); const content = isBuffer.isBuffer(result) ? bufferToString.bufferToString(result) : result; this.#cachedFS.set(filePath, content); return content; } return void 0; } /** * Writes a file to the virtual file system (VFS). * * @param file - The path to the file. * @param data - The contents of the file. * @param options - Optional parameters for writing the file. * @returns A promise that resolves when the file is written. */ async writeFile(file, data = "", options = "utf8") { const filePath = this.formatAbsoluteFilePath(toFilePath(file)); if (!this.directoryExistsSync(filePathFns.findFilePath(filePath))) { await this.mkdir(filePathFns.findFilePath(filePath), options); } this.#log(types.LogLevelLabel.TRACE, `Writing ${filePath} file to virtual file system (size: ${prettyBytes.prettyBytes(new buffer.Blob(toArray.toArray(data)).size)})`); this.#cachedFS.set(filePath, data.toString()); this.clearResolverCache(filePath); const ifs = this.resolveFS(filePath, options); if (isFunction.isFunction(ifs.promises.writeFile)) { return ifs.promises.writeFile(filePath, data, options); } return ifs.writeFileSync(filePath, data, options); } /** * Synchronously writes a file to the virtual file system (VFS). * * @param file - The file to write. * @param data - The contents of the file. * @param options - Optional parameters for writing the file. */ writeFileSync(file, data = "", options = "utf8") { const filePath = this.formatAbsoluteFilePath(toFilePath(file)); if (!this.directoryExistsSync(filePathFns.findFilePath(filePath))) { this.mkdirSync(filePathFns.findFilePath(filePath)); } this.#log(types.LogLevelLabel.TRACE, `Writing ${filePath} file to virtual file system (size: ${prettyBytes.prettyBytes(new buffer.Blob(toArray.toArray(data)).size)})`); this.#cachedFS.set(filePath, data.toString()); this.clearResolverCache(filePath); const writeStream = this.resolveFS(filePath, options).createWriteStream(filePath); try { writeStream.write(data); } finally { writeStream.close(); } } /** * Writes a runtime file to the virtual file system (VFS). * * @param id - The unique identifier for the runtime file. * @param path - The path to the runtime file. * @param contents - The contents of the runtime file. * @param options - Optional parameters for writing the runtime file. * @returns A promise that resolves when the file is written. */ async writeRuntimeFile(id, path, contents, options = {}) { const formattedId = this.formatRuntimeId(id); const absolutePath = this.formatAbsoluteFilePath(toFilePath(path)); this.runtimeIdMap.set(formattedId, absolutePath); let data = contents; if (!options.skipFormat) { data = await prettier.format(contents, { absolutePath, ...await prettier.resolveConfig(absolutePath) }); } const _options = defu__default.default(isSetString.isSetString(options) ? {} : options ?? {}, { encoding: isSetString.isSetString(options) ? options : "utf8", outputMode: "virtual" }); this.#log(types.LogLevelLabel.DEBUG, `Writing runtime file ${absolutePath} (size: ${prettyBytes.prettyBytes(new buffer.Blob(toArray.toArray(data)).size)}) to ${this.resolveOutputMode(absolutePath, _options) === "fs" ? "disk" : "memory"}`); return this.writeFile(absolutePath, data, _options); } /** * Adds an entry file to the virtual file system. * * @param name - The file name or absolute path of the entry module. * @param contents - The contents of the entry file. * @param options - Optional parameters for writing the entry file. */ async writeEntryFile(name, contents, options = {}) { const absolutePath = this.formatAbsoluteFilePath(isType.isAbsolutePath(toFilePath(name)) ? toFilePath(name) : toFilePath(joinPaths.joinPaths(this.#context.entryPath, name))); let data = contents; if (!options.skipFormat) { data = await prettier.format(contents, { absolutePath, ...await prettier.resolveConfig(absolutePath) }); } const _options = defu__default.default(isSetString.isSetString(options) ? {} : options ?? {}, { encoding: isSetString.isSetString(options) ? options : "utf8", outputMode: "virtual" }); this.#log(types.LogLevelLabel.DEBUG, `Writing entry file ${absolutePath} (size: ${prettyBytes.prettyBytes(new buffer.Blob(toArray.toArray(data)).size)}) to ${this.resolveOutputMode(absolutePath, _options) === "fs" ? "disk" : "memory"}`); return this.writeFile(absolutePath, data, _options); } /** * Writes a file to disk from the physical file system (on disk). * * @param path - The path to the file to write. * @param contents - The contents of the file to write. * @param options - Optional parameters for writing the file. * @returns A promise that resolves when the file is written. */ async writeFileToDisk(path, contents, options = {}) { const absolutePath = this.formatAbsoluteFilePath(toFilePath(path)); let data = contents; if (!options.skipFormat) { const resolvedConfig = await prettier.resolveConfig(absolutePath); if (resolvedConfig) { data = await prettier.format(contents, { absolutePath, ...resolvedConfig }); } } return this.writeFile(absolutePath, data, defu__default.default({ outputMode: "fs" }, isSetString.isSetString(options) ? {} : options ?? {}, { encoding: isSetString.isSetString(options) ? options : "utf8" })); } /** * Synchronously checks if a file exists in the virtual file system (VFS). * * @param pathOrId - The path or ID of the file to check. * @returns `true` if the file exists, otherwise `false`. */ existsSync(pathOrId) { return this.pathExistsSync(this.resolvePath(toFilePath(pathOrId)) || toFilePath(pathOrId)); } /** * Checks if a file exists in the virtual file system (VFS). * * @remarks * This is a base method used by {@link existsSync} - it does not try to resolve the path prior to checking if it exists or not. * * @param path - The path of the file to check. * @returns `true` if the file exists, otherwise `false`. */ fileExistsSync(path) { const formattedPath = this.formatAbsoluteFilePath(toFilePath(path)); return this.isValidRuntimeId(formattedPath) || this.#virtualFS.existsSync(formattedPath) && this.#virtualFS.lstatSync(formattedPath).isFile() || this.#fs.existsSync(formattedPath) && this.#fs.lstatSync(formattedPath).isFile() || this.resolveFS(path).existsSync(formattedPath) && this.resolveFS(path).lstatSync(formattedPath).isFile(); } /** * Checks if a directory exists in the virtual file system (VFS). * * @param path - The path of the directory to check. * @returns `true` if the directory exists, otherwise `false`. */ directoryExistsSync(path) { const formattedPath = this.formatAbsoluteFilePath(toFilePath(path)); return this.#virtualFS.existsSync(formattedPath) && this.#virtualFS.lstatSync(formattedPath).isDirectory() || this.#fs.existsSync(formattedPath) && this.#fs.lstatSync(formattedPath).isDirectory() || this.resolveFS(path).existsSync(formattedPath) && this.resolveFS(path).lstatSync(formattedPath).isDirectory(); } /** * Checks if a path exists in the virtual file system (VFS). * * @param path - The path to check. * @returns `true` if the path exists, otherwise `false`. */ pathExistsSync(path) { const formattedPath = this.formatAbsoluteFilePath(toFilePath(path)); return this.isValidRuntimeId(formattedPath) || this.#virtualFS.existsSync(formattedPath) || this.#fs.existsSync(formattedPath) || this.resolveFS(path).existsSync(formattedPath); } /** * Retrieves the status of a file in the virtual file system (VFS). * * @param pathOrId - The path or ID of the file to retrieve status for. * @returns A promise that resolves to the file's status information, or false if the file does not exist. */ async stat(pathOrId, options) { return this.resolveFS(pathOrId).promises.stat(this.resolvePath(toFilePath(pathOrId)) || toFilePath(pathOrId), options); } /** * Synchronously retrieves the status of a file in the virtual file system (VFS). * * @param pathOrId - The path or ID of the file to retrieve status for. * @returns The file's status information, or false if the file does not exist. */ statSync(pathOrId) { return this.resolveFS(pathOrId).statSync(this.resolvePath(toFilePath(pathOrId)) || toFilePath(pathOrId)); } /** * Retrieves the status of a symbolic link in the virtual file system (VFS). * * @param pathOrId - The path or ID of the symbolic link to retrieve status for. * @returns A promise that resolves to the symbolic link's status information, or false if the link does not exist. */ async lstat(pathOrId, options) { return this.resolveFS(pathOrId).promises.lstat(this.resolvePath(toFilePath(pathOrId)) || toFilePath(pathOrId), options); } /** * Synchronously retrieves the status of a symbolic link in the virtual file system (VFS). * * @param pathOrId - The path or ID of the symbolic link to retrieve status for. * @returns The symbolic link's status information, or false if the link does not exist. */ lstatSync(pathOrId, options) { return this.resolveFS(pathOrId).lstatSync(this.resolvePath(toFilePath(pathOrId)) || toFilePath(pathOrId), options); } /** * Resolves a path or ID to a runtime file id in the virtual file system. * * @param pathOrId - The path or id of the file to resolve. * @returns The resolved id of the runtime file if it exists, otherwise false. */ resolveId(pathOrId) { if (this.runtimeIdMap.has(this.formatRuntimeId(toFilePath(pathOrId)))) { return this.formatRuntimeId(toFilePath(pathOrId)); } const filePath = this.resolvePath(toFilePath(pathOrId)); if (filePath) { return this.runtimeIdMap.keys().find((id) => this.runtimeIdMap.get(id) === filePath) || false; } return false; } /** * Resolves a path based on TypeScript's `tsconfig.json` paths. * * @see https://www.typescriptlang.org/tsconfig#paths * * @param path - The path to check. * @returns The resolved file path if it exists, otherwise undefined. */ resolveTsconfigPath(path) { if (this.#context.tsconfig.options.paths) { for (const tsconfigPathKey of Object.keys(this.#context.tsconfig.options.paths).filter((tsconfigPath) => path.startsWith(tsconfigPath.replaceAll("*", "")))) { const resolvedPath = this.#context.tsconfig.options.paths[tsconfigPathKey]?.find((tsconfigPath) => this.resolvePathName(joinPaths.joinPaths(this.#context.options.workspaceRoot, tsconfigPath.replaceAll("*", ""), path.replace(tsconfigPathKey.replaceAll("*", ""), ""))) || this.formatAbsoluteFilePath(tsconfigPath) === this.formatAbsoluteFilePath(path)); if (resolvedPath) { return this.formatAbsoluteFilePath(resolvedPath) === this.formatAbsoluteFilePath(path) ? this.formatAbsoluteFilePath(resolvedPath) : this.resolvePathName(joinPaths.joinPaths(this.#context.options.workspaceRoot, resolvedPath.replaceAll("*", ""), path.replace(tsconfigPathKey.replaceAll("*", ""), ""))); } } } return false; } /** * Resolves a path based on TypeScript's `tsconfig.json` paths. * * @see https://www.typescriptlang.org/tsconfig#paths * * @param path - The path to check. * @returns The resolved file path if it exists, otherwise undefined. */ resolveTsconfigPathPackage(path) { if (this.#context.tsconfig.options.paths) { const tsconfigPathKeys = Object.keys(this.#context.tsconfig.options.paths).filter((tsconfigPath) => path.startsWith(tsconfigPath.replaceAll("*", ""))); if (tsconfigPathKeys.length > 0 && tsconfigPathKeys[0]) { return tsconfigPathKeys[0].replace(/\/\*$/, ""); } } return false; } /** * Resolves a path or ID to its real path in the virtual file system (VFS). * * @param pathOrId - The path or ID to resolve. * @returns The resolved real path if it exists, otherwise undefined. */ realpathSync(pathOrId) { const filePath = this.resolvePath(toFilePath(pathOrId)); if (!filePath) { throw new Error(`File not found: ${toFilePath(pathOrId)}`); } return filePath; } /** * Resolves a path or ID parameter to a corresponding virtual file path in the virtual file system (VFS). * * @param pathOrId - The path or ID to resolve. * @param options - Optional parameters for resolving the path, such as whether to include the file extension. * @returns The resolved file path if it exists, otherwise undefined. */ resolvePath(pathOrId, options = {}) { const formattedPath = toFilePath(pathOrId); const resolverKey = `${formattedPath}${options.withExtension ? "-ext" : ""}${options.paths ? `-${hash.hash(options.paths)}` : ""}${options.type ? `-${options.type}` : ""}`; if (this.#cachedResolver.has(resolverKey)) { return this.#cachedResolver.get(resolverKey); } else if (this.#cachedFS.has(formattedPath)) { return formattedPath; } let result = false; if (this.isValidRuntimeId(formattedPath)) { result = this.runtimeIdMap.get(this.formatRuntimeId(formattedPath)); } else { result = this.resolvePathName(formattedPath, options); } if (!result) { result = false; } else { result = toFilePath(result); } if (result && options.withExtension === false) { return result.replace(/\.[m|c]?[t|j]sx?$/, ""); } this.#cachedResolver.set(resolverKey, result); return result; } /** * Formats a file path by removing the runtime prefix and leading null character. * * @param path - The file path to format. * @returns The formatted file path. */ formatFilePath(path) { if (!isSetString.isSetString(path)) { throw new Error(`Invalid path provided. Expected a string or a valid file path.`); } return path.replace(new RegExp(`^${RUNTIME_PREFIX}`), "").replace(/^\\0/, ""); } /** * Converts a relative path to an absolute path based on the workspace and project root. * * @param path - The relative path to convert. * @returns The absolute path. */ formatAbsoluteFilePath = /* @__PURE__ */ chunkGRNJVY7I_cjs.__name((path) => { const formattedPath = this.formatFilePath(path); if (isType.isAbsolutePath(formattedPath) || formattedPath.startsWith(this.#context.options.workspaceRoot)) { return formattedPath; } else if (formattedPath.startsWith(this.#context.options.projectRoot)) { return joinPaths.joinPaths(this.#context.options.workspaceRoot, formattedPath); } return formattedPath; }, "formatAbsoluteFilePath"); /** * Formats a runtime ID by removing the file extension and prepending the runtime prefix. * * @param id - The runtime ID to format. * @returns The formatted runtime ID. */ formatRuntimeId(id) { return `${RUNTIME_PREFIX}${this.formatFilePath(id).replace(/\.[m|c]?[t|j]sx?$/, "")}`; } /** * Resolves a path or ID parameter to a corresponding virtual file path in the virtual file system (VFS). * * @param pathOrId - The path or ID to resolve. * @returns The resolved file path if it exists, otherwise undefined. */ resolvePathName(pathOrId, options = {}) { if (pathOrId.startsWith(RUNTIME_PREFIX)) { return false; } if (isType.isAbsolutePath(pathOrId)) { if (options.type === "file" ? this.fileExistsSync(pathOrId) : this.pathExistsSync(pathOrId)) { return pathOrId; } const result = checkVariants(pathOrId, this); if (result) { return result; } } for (const path of this.resolveParentPaths(pathOrId, options.paths)) { const request = joinPaths.joinPaths(path, pathOrId); if (options.type === "file" ? this.fileExistsSync(pathOrId) : this.pathExistsSync(pathOrId)) { return request; } const result = checkVariants(request, this); if (result) { return result; } } return false; } resolveParentPaths(request, current = []) { let paths = [ this.#context.options.workspaceRoot, joinPaths.joinPaths(this.#context.options.workspaceRoot, this.#context.options.projectRoot) ]; if (this.#context.tsconfig.options.paths) { paths = this.#context.tsconfig.options.paths ? Object.keys(this.#context.tsconfig.options.paths).filter((tsconfigPath) => request.startsWith(tsconfigPath.replaceAll("*", ""))).map((tsconfigPath) => this.#context.tsconfig.options.paths?.[tsconfigPath]).flat().reduce((ret, path) => { if (path && !ret.includes(joinPaths.joinPaths(this.#context.options.workspaceRoot, path))) { ret.push(joinPaths.joinPaths(this.#context.options.workspaceRoot, path)); } return ret; }, paths) : paths; } return paths.reduce((ret, path) => { if (!ret.includes(path)) { ret.push(path); } return ret; }, current.filter(Boolean).map((p) => this.formatAbsoluteFilePath(toFilePath(p)))); } /** * Select the file system module to use for the operation based on the path or URL. * * @param pathOrUrl - The path to perform the file system operation on. * @param options - Options for the operation, such as output mode. * @returns The file system module used for the operation. */ resolveFS(pathOrUrl, options = {}) { const outputMode = this.resolveOutputMode(pathOrUrl, options); if (outputMode === "virtual") { return this.#virtualFS; } else if (outputMode === "fs") { return this.#fs; } return this.#unifiedFS; } /** * Select the file system module to use for the operation based on the path or URL. * * @param pathOrUrl - The path to perform the file system operation on. * @param options - Options for the operation, such as output mode. * @returns The file system module used for the operation. */ resolveOutputMode(pathOrUrl, options = {}) { if (options.outputMode === "virtual" && this.#context.options.output.outputMode !== "fs" && isParentPath.isParentPath(toFilePath(pathOrUrl), this.#context.artifactsPath)) { return "virtual"; } else if (options.outputMode === "fs" || this.#context.options.output.outputMode === "fs" || isParentPath.isParentPath(toFilePath(pathOrUrl), this.#context.dataPath) || isParentPath.isParentPath(toFilePath(pathOrUrl), this.#context.cachePath) || isParentPath.isParentPath(toFilePath(pathOrUrl), joinPaths.joinPaths(this.#context.options.workspaceRoot, this.#context.options.output.outputPath))) { return "fs"; } return null; } /** * Clears the resolver cache for a given path. * * @param path - The path to clear the resolver cache for. */ clearResolverCache(path) { this.#cachedResolver.keys().filter((key) => key.startsWith(toFilePath(path))).forEach((key) => this.#cachedResolver.delete(key)); } }; function createVfs(context) { const vfs = new VirtualFileSystem(context); return vfs; } chunkGRNJVY7I_cjs.__name(createVfs, "createVfs"); function restoreVfs(context, serialized) { const vfs = new VirtualFileSystem(context, serialized); return vfs; } chunkGRNJVY7I_cjs.__name(restoreVfs, "restoreVfs"); exports.RUNTIME_PREFIX = RUNTIME_PREFIX; exports.__VFS_VIRTUAL__ = __VFS_VIRTUAL__; exports.createVfs = createVfs; exports.restoreVfs = restoreVfs;