diff --git a/src/config.ts b/src/config.ts index 3cb7c303..f96f33e0 100644 --- a/src/config.ts +++ b/src/config.ts @@ -181,7 +181,7 @@ export async function configure(configuration: Partial { + public isEnabled: boolean = false; + + protected sync = new Map(); + + protected async = new Map>(); + + /** + * Gets data from the cache, if is exists and the cache is enabled. + */ + getSync(path: string): T | undefined { + if (!this.isEnabled) return; + + return this.sync.get(path); + } + + /** + * Adds data if the cache is enabled + */ + setSync(path: string, value: T): void { + if (!this.isEnabled) return; + + this.sync.set(path, value); + } + + /** + * Clears the cache if it is enabled + */ + clearSync(): void { + if (!this.isEnabled) return; + + this.sync.clear(); + } + + /** + * Gets data from the cache, if it exists and the cache is enabled. + */ + get(path: string): Promise | undefined { + if (!this.isEnabled) return; + + return this.async.get(path); + } + + /** + * Adds data if the cache is enabled + */ + set(path: string, value: Promise): void { + if (!this.isEnabled) return; + + this.async.set(path, value); + } + + /** + * Clears the cache if it is enabled + */ + clear(): void { + if (!this.isEnabled) return; + + this.async.clear(); + } } -const statsSync = new Map(); - -/** - * Gets stats from the cache, if they exist and the cache is enabled. - */ -export function getStatsSync(path: string): Stats | undefined { - if (!isEnabled) return; - - return statsSync.get(path); -} - -/** - * Adds stats if the cache is enabled - */ -export function setStatsSync(path: string, value: Stats): void { - if (!isEnabled) return; - - statsSync.set(path, value); -} - -/** - * Clears the cache if it is enabled - */ -export function clearStatsSync(): void { - if (!isEnabled) return; - - statsSync.clear(); -} - -const stats = new Map>(); - -/** - * Gets stats from the cache, if they exist and the cache is enabled. - */ -export function getStats(path: string): Promise | undefined { - if (!isEnabled) return; - - return stats.get(path); -} - -/** - * Adds stats if the cache is enabled - */ -export function setStats(path: string, value: Promise): void { - if (!isEnabled) return; - - stats.set(path, value); -} - -/** - * Clears the cache if it is enabled - */ -export function clearStats(): void { - if (!isEnabled) return; - - stats.clear(); -} +export const stats = new Cache(); diff --git a/src/emulation/promises.ts b/src/emulation/promises.ts index ab06243d..b05620de 100644 --- a/src/emulation/promises.ts +++ b/src/emulation/promises.ts @@ -471,7 +471,7 @@ export async function unlink(path: fs.PathLike): Promise { path = normalizePath(path); const { fs, path: resolved } = resolveMount(path); try { - if (config.checkAccess && !(await (cache.getStats(path) || fs.stat(resolved)))!.hasAccess(constants.W_OK)) { + if (config.checkAccess && !(await (cache.stats.get(path) || fs.stat(resolved)))!.hasAccess(constants.W_OK)) { throw ErrnoError.With('EACCES', resolved, 'unlink'); } await fs.unlink(resolved); @@ -623,7 +623,7 @@ export async function rmdir(path: fs.PathLike): Promise { path = await realpath(path); const { fs, path: resolved } = resolveMount(path); try { - const stats = await (cache.getStats(path) || fs.stat(resolved)); + const stats = await (cache.stats.get(path) || fs.stat(resolved)); if (!stats) { throw ErrnoError.With('ENOENT', path, 'rmdir'); } @@ -718,8 +718,8 @@ export async function readdir( const { fs, path: resolved } = resolveMount(path); - const _stats = cache.getStats(path) || fs.stat(resolved).catch(handleError); - cache.setStats(path, _stats); + const _stats = cache.stats.get(path) || fs.stat(resolved).catch(handleError); + cache.stats.set(path, _stats); const stats = await _stats; if (!stats) { @@ -740,8 +740,8 @@ export async function readdir( const addEntry = async (entry: string) => { let entryStats: Stats | undefined; if (options?.recursive || options?.withFileTypes) { - const _entryStats = cache.getStats(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError); - cache.setStats(join(path, entry), _entryStats); + const _entryStats = cache.stats.get(join(path, entry)) || fs.stat(join(resolved, entry)).catch(handleError); + cache.stats.set(join(path, entry), _entryStats); entryStats = await _entryStats; } if (options?.withFileTypes) { @@ -768,7 +768,7 @@ export async function readdir( }; await Promise.all(entries.map(addEntry)); if (!options?._isIndirect) { - cache.clearStats(); + cache.stats.clear(); } return values as string[] | Dirent[]; @@ -966,13 +966,13 @@ export async function rm(path: fs.PathLike, options?: fs.RmOptions & InternalOpt path = normalizePath(path); const _stats = - cache.getStats(path) || + cache.stats.get(path) || stat(path).catch((error: ErrnoError) => { if (error.code == 'ENOENT' && options?.force) return undefined; throw error; }); - cache.setStats(path, _stats); + cache.stats.set(path, _stats); const stats = await _stats; if (!stats) { @@ -998,12 +998,12 @@ export async function rm(path: fs.PathLike, options?: fs.RmOptions & InternalOpt case constants.S_IFIFO: case constants.S_IFSOCK: default: - cache.clearStats(); + cache.stats.clear(); throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm'); } if (!options?._isIndirect) { - cache.clearStats(); + cache.stats.clear(); } } rm satisfies typeof promises.rm; diff --git a/src/emulation/sync.ts b/src/emulation/sync.ts index 8362178b..bbdbde75 100644 --- a/src/emulation/sync.ts +++ b/src/emulation/sync.ts @@ -106,7 +106,7 @@ export function unlinkSync(path: fs.PathLike): void { path = normalizePath(path); const { fs, path: resolved } = resolveMount(path); try { - if (config.checkAccess && !(cache.getStatsSync(path) || fs.statSync(resolved)).hasAccess(constants.W_OK)) { + if (config.checkAccess && !(cache.stats.getSync(path) || fs.statSync(resolved)).hasAccess(constants.W_OK)) { throw ErrnoError.With('EACCES', resolved, 'unlink'); } fs.unlinkSync(resolved); @@ -386,7 +386,7 @@ export function rmdirSync(path: fs.PathLike): void { path = normalizePath(path); const { fs, path: resolved } = resolveMount(realpathSync(path)); try { - const stats = cache.getStatsSync(path) || fs.statSync(resolved); + const stats = cache.stats.getSync(path) || fs.statSync(resolved); if (!stats.isDirectory()) { throw ErrnoError.With('ENOTDIR', resolved, 'rmdir'); } @@ -459,8 +459,8 @@ export function readdirSync( const { fs, path: resolved } = resolveMount(realpathSync(path)); let entries: string[]; try { - const stats = cache.getStatsSync(path) || fs.statSync(resolved); - cache.setStatsSync(path, stats); + const stats = cache.stats.getSync(path) || fs.statSync(resolved); + cache.stats.setSync(path, stats); if (config.checkAccess && !stats.hasAccess(constants.R_OK)) { throw ErrnoError.With('EACCES', resolved, 'readdir'); } @@ -475,8 +475,8 @@ export function readdirSync( // Iterate over entries and handle recursive case if needed const values: (string | Dirent | Buffer)[] = []; for (const entry of entries) { - const entryStat = cache.getStatsSync(join(path, entry)) || fs.statSync(join(resolved, entry)); - cache.setStatsSync(join(path, entry), entryStat); + const entryStat = cache.stats.getSync(join(path, entry)) || fs.statSync(join(resolved, entry)); + cache.stats.setSync(join(path, entry), entryStat); if (options?.withFileTypes) { values.push(new Dirent(entry, entryStat)); @@ -500,7 +500,7 @@ export function readdirSync( } if (!options?._isIndirect) { - cache.clearStatsSync(); + cache.stats.clearSync(); } return values as string[] | Dirent[] | Buffer[]; } @@ -658,7 +658,7 @@ export function rmSync(path: fs.PathLike, options?: fs.RmOptions & InternalOptio let stats: Stats | undefined; try { - stats = cache.getStatsSync(path) || statSync(path); + stats = cache.stats.getSync(path) || statSync(path); } catch (error) { if ((error as ErrnoError).code != 'ENOENT' || !options?.force) throw error; } @@ -667,7 +667,7 @@ export function rmSync(path: fs.PathLike, options?: fs.RmOptions & InternalOptio return; } - cache.setStatsSync(path, stats); + cache.stats.setSync(path, stats); switch (stats.mode & constants.S_IFMT) { case constants.S_IFDIR: @@ -688,12 +688,12 @@ export function rmSync(path: fs.PathLike, options?: fs.RmOptions & InternalOptio case constants.S_IFIFO: case constants.S_IFSOCK: default: - cache.clearStatsSync(); + cache.stats.clearSync(); throw new ErrnoError(Errno.EPERM, 'File type not supported', path, 'rm'); } if (!options?._isIndirect) { - cache.clearStatsSync(); + cache.stats.clearSync(); } } rmSync satisfies typeof fs.rmSync;