UNPKG

@socketsupply/socket

Version:

A Cross-Platform, Native Runtime for Desktop and Mobile Apps — Create apps using HTML, CSS, and JavaScript. Written from the ground up to be small and maintainable.

367 lines (313 loc) 8.1 kB
import { DirectoryHandle } from './handle.js' import { Buffer } from '../buffer.js' import { clamp } from '../util.js' import { UV_DIRENT_UNKNOWN, UV_DIRENT_FILE, UV_DIRENT_DIR, UV_DIRENT_LINK, UV_DIRENT_FIFO, UV_DIRENT_SOCKET, UV_DIRENT_CHAR, UV_DIRENT_BLOCK } from './constants.js' import fds from './fds.js' import ipc from '../ipc.js' import * as exports from './dir.js' export const kType = Symbol.for('fs.Dirent.type') function checkDirentType (dirent, property) { return dirent.type === property } /** * Sorts directory entries * @param {string|Dirent} a * @param {string|Dirent} b * @return {number} */ export function sortDirectoryEntries (a, b) { if (a instanceof Dirent) { return sortDirectoryEntries(a.name, b.name) } return a < b ? -1 : a > b ? 1 : 0 } /** * A containerr for a directory and its entries. This class supports scanning * a directory entry by entry with a `read()` method. The `Symbol.asyncIterator` * interface is exposed along with an AsyncGenerator `entries()` method. * @see {@link https://nodejs.org/dist/latest-v20.x/docs/api/fs.html#class-fsdir} */ export class Dir { static from (fdOrHandle, options) { if (fdOrHandle instanceof DirectoryHandle) { return new this(fdOrHandle, options) } return new this(DirectoryHandle.from(fdOrHandle, options), options) } /** * `Dir` class constructor. * @param {DirectoryHandle} handle * @param {object=} options */ constructor (handle, options) { this.path = handle?.path ?? null this.handle = handle this.encoding = options?.encoding || 'utf8' this.withFileTypes = options?.withFileTypes !== false } /** * `true` if closed, otherwise `false`. * @ignore * @type {boolean} */ get closed () { return Boolean(this.handle?.closed) } /** * `true` if closing, otherwise `false`. * @ignore * @type {boolean} */ get closing () { return Boolean(this.handle?.closing) } /** * Closes container and underlying handle. * @param {object|function} options * @param {function=} callback */ async close (options = null, callback = null) { if (typeof options === 'function') { callback = options options = {} } if (typeof callback === 'function') { try { await this.handle?.close(options) } catch (err) { return callback(err) } return callback(null) } return await this.handle?.close(options) } /** * Closes container and underlying handle * synchronously. * @param {object=} [options] */ closeSync (options = null) { const { id } = this.handle const result = ipc.sendSync('fs.closedir', { id }, options) if (result.err) { throw result.err } fds.release(id, false) } /** * Reads and returns directory entry. * @param {object|function} options * @param {function=} callback * @return {Promise<Dirent[]|string[]>} */ async read (options, callback) { if (typeof options === 'function') { callback = options options = {} } let results = [] try { results = await this.handle?.read(options) } catch (err) { if (typeof callback === 'function') { callback(err) return null } else { throw err } } results = results.map((result) => { if (this.withFileTypes) { result = Dirent.from(result) if (this.encoding === 'buffer') { result.name = Buffer.from(result.name) } else { result.name = Buffer.from(result.name).toString(this.encoding) } } else { result = result.name if (this.encoding === 'buffer') { result = Buffer.from(result) } else { result = Buffer.from(result).toString(this.encoding) } } return result }) if (results.length <= 1) { results = results[0] || null } if (typeof callback === 'function') { callback(null, results) return } return results } /** * Reads and returns directory entry synchronously. * @param {object|function} options * @return {Dirent[]|string[]} */ readSync (options = null) { const { encoding } = this const { id } = this.handle const entries = clamp( options?.entries || DirectoryHandle.MAX_ENTRIES, 1, // MIN_ENTRIES DirectoryHandle.MAX_ENTRIES ) const result = ipc.sendSync('fs.readdir', { id, entries }, options) if (result.err) { throw result.err } const results = result.data .map((entry) => ({ type: entry.type, name: decodeURIComponent(entry.name) })) .map((result) => { const { name } = result if (this.withFileTypes) { result = Dirent.from(result) if (encoding === 'buffer') { result.name = Buffer.from(name) } else { result.name = Buffer.from(name).toString(encoding) } return result } if (encoding === 'buffer') { return Buffer.from(name) } else { return Buffer.from(name).toString(encoding) } }) if (results.length === 1) { return results[0] } else if (results.length === 0) { return null } return results } /** * AsyncGenerator which yields directory entries. * @param {object=} options */ async * entries (options) { try { while (true) { const results = await this.read(options) if (results === null || results?.length === 0) { break } if (Array.isArray(results)) { for (let i = 0; i < results.length; ++i) { yield results[i] } } else { yield results } } } finally { await this.close() } } /** * `for await (...)` AsyncGenerator support. */ get [Symbol.asyncIterator] () { return this.entries } } /** * A container for a directory entry. * @see {@link https://nodejs.org/dist/latest-v20.x/docs/api/fs.html#class-fsdirent} */ export class Dirent { static get UNKNOWN () { return UV_DIRENT_UNKNOWN } static get FILE () { return UV_DIRENT_FILE } static get DIR () { return UV_DIRENT_DIR } static get LINK () { return UV_DIRENT_LINK } static get FIFO () { return UV_DIRENT_FIFO } static get SOCKET () { return UV_DIRENT_SOCKET } static get CHAR () { return UV_DIRENT_CHAR } static get BLOCK () { return UV_DIRENT_BLOCK } /** * Creates `Dirent` instance from input. * @param {object|string} name * @param {(string|number)=} type */ static from (name, type) { if (typeof name === 'object') { return new this(name?.name, name?.type) } return new this(name, type ?? Dirent.UNKNOWN) } /** * `Dirent` class constructor. * @param {string} name * @param {string|number} type */ constructor (name, type) { this.name = name ?? null this[kType] = parseInt(type ?? Dirent.UNKNOWN) } /** * Read only type. */ get type () { return this[kType] } /** * `true` if `Dirent` instance is a directory. */ isDirectory () { return checkDirentType(this, Dirent.DIR) } /** * `true` if `Dirent` instance is a file. */ isFile () { return checkDirentType(this, Dirent.FILE) } /** * `true` if `Dirent` instance is a block device. */ isBlockDevice () { return checkDirentType(this, Dirent.BLOCK) } /** * `true` if `Dirent` instance is a character device. */ isCharacterDevice () { return checkDirentType(this, Dirent.CHAR) } /** * `true` if `Dirent` instance is a symbolic link. */ isSymbolicLink () { return checkDirentType(this, Dirent.LINK) } /** * `true` if `Dirent` instance is a FIFO. */ isFIFO () { return checkDirentType(this, Dirent.FIFO) } /** * `true` if `Dirent` instance is a socket. */ isSocket () { return checkDirentType(this, Dirent.SOCKET) } } export default exports