@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
JavaScript
'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;