UNPKG

@zenfs/core

Version:

A filesystem, anywhere

139 lines (121 loc) 3.66 kB
import type { Dir as _Dir, Dirent as _Dirent } from 'node:fs'; import { Errno, ErrnoError } from '../error.js'; import type { Stats } from '../stats.js'; import type { Callback } from '../utils.js'; import { basename } from './path.js'; import { readdir } from './promises.js'; import { readdirSync } from './sync.js'; export class Dirent implements _Dirent { public get name(): string { return basename(this.path); } public constructor( public path: string, protected stats: Stats ) {} get parentPath(): string { return this.path; } isFile(): boolean { return this.stats.isFile(); } isDirectory(): boolean { return this.stats.isDirectory(); } isBlockDevice(): boolean { return this.stats.isBlockDevice(); } isCharacterDevice(): boolean { return this.stats.isCharacterDevice(); } isSymbolicLink(): boolean { return this.stats.isSymbolicLink(); } isFIFO(): boolean { return this.stats.isFIFO(); } isSocket(): boolean { return this.stats.isSocket(); } } /** * A class representing a directory stream. */ export class Dir implements _Dir { protected closed = false; protected checkClosed(): void { if (this.closed) { throw new ErrnoError(Errno.EBADF, 'Can not use closed Dir'); } } protected _entries?: Dirent[]; public constructor(public readonly path: string) {} /** * Asynchronously close the directory's underlying resource handle. * Subsequent reads will result in errors. */ public close(): Promise<void>; public close(cb: Callback): void; public close(cb?: Callback): void | Promise<void> { this.closed = true; if (!cb) { return Promise.resolve(); } cb(); } /** * Synchronously close the directory's underlying resource handle. * Subsequent reads will result in errors. */ public closeSync(): void { this.closed = true; } protected async _read(): Promise<Dirent | null> { this.checkClosed(); this._entries ??= await readdir(this.path, { withFileTypes: true }); if (!this._entries.length) { return null; } return this._entries.shift() ?? null; } /** * Asynchronously read the next directory entry via `readdir(3)` as an `Dirent`. * After the read is completed, a value is returned that will be resolved with an `Dirent`, or `null` if there are no more directory entries to read. * Directory entries returned by this function are in no particular order as provided by the operating system's underlying directory mechanisms. */ public read(): Promise<Dirent | null>; public read(cb: Callback<[Dirent | null]>): void; public read(cb?: Callback<[Dirent | null]>): void | Promise<Dirent | null> { if (!cb) { return this._read(); } void this._read().then(value => cb(undefined, value)); } /** * Synchronously read the next directory entry via `readdir(3)` as a `Dirent`. * If there are no more directory entries to read, null will be returned. * Directory entries returned by this function are in no particular order as provided by the operating system's underlying directory mechanisms. */ public readSync(): Dirent | null { this.checkClosed(); this._entries ??= readdirSync(this.path, { withFileTypes: true }); if (!this._entries.length) { return null; } return this._entries.shift() ?? null; } async next(): Promise<IteratorResult<Dirent>> { const value = await this._read(); if (value) { return { done: false, value }; } await this.close(); return { done: true, value: undefined }; } /** * Asynchronously iterates over the directory via `readdir(3)` until all entries have been read. */ public [Symbol.asyncIterator](): AsyncIterableIterator<Dirent> { return this; } }