@zenfs/core
Version:
A filesystem, anywhere
139 lines (121 loc) • 3.66 kB
text/typescript
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;
}
}