UNPKG

@zenfs/core

Version:

A filesystem, anywhere

151 lines (150 loc) 3.96 kB
import { Buffer } from 'buffer'; import { withErrno } from 'kerium'; import { warn } from 'kerium/log'; import { parse } from '../path.js'; import { DirType } from '../vfs/dir.js'; import { readdir } from './promises.js'; import { readdirSync } from './sync.js'; export class Dirent { ino; type; _name; get name() { const name = Buffer.from(this._name); return (this._encoding == 'buffer' ? name : name.toString(this._encoding)); } /** * @internal @protected */ _encoding; /** * @internal @protected */ _parentPath; get parentPath() { return this._parentPath; } /** * @deprecated Removed in Node v24, use `parentPath` instead. */ get path() { warn('Dirent.path was removed in Node v24, use parentPath instead'); return this._parentPath; } /** * @internal */ static from(vfs, encoding) { const dirent = new Dirent(); const { base, dir } = parse(vfs.path); dirent._parentPath = dir || '.'; dirent._name = base; dirent.ino = vfs.ino; dirent.type = vfs.type; dirent._encoding = encoding; return dirent; } isFile() { return this.type === DirType.REG; } isDirectory() { return this.type === DirType.DIR; } isBlockDevice() { return this.type === DirType.BLK; } isCharacterDevice() { return this.type === DirType.CHR; } isSymbolicLink() { return this.type === DirType.LNK; } isFIFO() { return this.type === DirType.FIFO; } isSocket() { return this.type === DirType.SOCK; } } /** * A class representing a directory stream. */ export class Dir { path; context; closed = false; checkClosed() { if (this.closed) throw withErrno('EBADF', 'Can not use closed Dir'); } _entries; constructor(path, context) { this.path = path; this.context = context; } close(cb) { this.closed = true; if (!cb) { return Promise.resolve(); } cb(null); } /** * Synchronously close the directory's underlying resource handle. * Subsequent reads will result in errors. */ closeSync() { this.closed = true; } async _read() { this.checkClosed(); this._entries ??= await readdir.call(this.context, this.path, { withFileTypes: true, }); if (!this._entries.length) return null; return this._entries.shift() ?? null; } read(cb) { if (!cb) { return this._read(); } void this._read().then(value => cb(null, 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. */ readSync() { this.checkClosed(); this._entries ??= readdirSync.call(this.context, this.path, { withFileTypes: true }); if (!this._entries.length) return null; return this._entries.shift() ?? null; } async next() { 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. */ [Symbol.asyncIterator]() { return this; } [Symbol.dispose]() { if (this.closed) return; this.closeSync(); } async [Symbol.asyncDispose]() { if (this.closed) return; await this.close(); } }