@zenfs/core
Version:
A filesystem, anywhere
151 lines (150 loc) • 3.96 kB
JavaScript
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();
}
}