UNPKG

@ezdevlol/memfs

Version:

In-memory file-system with Node's fs API.

188 lines (187 loc) 7.14 kB
import { createError, isFd, pathToFilename } from '../node/util'; import { pathToLocation } from './util'; import { ERRSTR } from '../node/constants'; import { FsaNodeFsOpenFile } from './FsaNodeFsOpenFile'; import * as util from '../node/util'; export class FsaNodeCore { root; syncAdapter; static fd = 0x7fffffff; fds = new Map(); constructor(root, syncAdapter) { this.root = root; this.syncAdapter = syncAdapter; if (root instanceof Promise) { root .then(root => { this.root = root; }) .catch(error => { }); } } getSyncAdapter() { const adapter = this.syncAdapter; if (!adapter) throw new Error('No sync adapter'); return adapter; } /** * A list of reusable (opened and closed) file descriptors, that should be * used first before creating a new file descriptor. */ releasedFds = []; newFdNumber() { const releasedFd = this.releasedFds.pop(); return typeof releasedFd === 'number' ? releasedFd : FsaNodeCore.fd--; } /** * @param path Path from root to the new folder. * @param create Whether to create the folders if they don't exist. */ async getDir(path, create, funcName) { let curr = await this.root; const options = { create }; try { for (const name of path) { curr = await curr.getDirectoryHandle(name, options); } } catch (error) { if (error && typeof error === 'object') { switch (error.name) { case 'TypeMismatchError': throw createError('ENOTDIR', funcName, path.join("/" /* FsaToNodeConstants.Separator */)); case 'NotFoundError': throw createError('ENOENT', funcName, path.join("/" /* FsaToNodeConstants.Separator */)); } } throw error; } return curr; } async getFile(path, name, funcName, create) { const dir = await this.getDir(path, false, funcName); const file = await dir.getFileHandle(name, { create }); return file; } async getFileOrDir(path, name, funcName) { const dir = await this.getDir(path, false, funcName); if (!name) return dir; try { const file = await dir.getFileHandle(name); return file; } catch (error) { if (error && typeof error === 'object') { switch (error.name) { case 'TypeMismatchError': try { return await dir.getDirectoryHandle(name); } catch (error2) { if (error2 && typeof error2 === 'object') { switch (error2.name) { case 'TypeMismatchError': throw createError('ENOTDIR', funcName, path.join("/" /* FsaToNodeConstants.Separator */)); case 'NotFoundError': throw createError('ENOENT', funcName, path.join("/" /* FsaToNodeConstants.Separator */)); } } } case 'NotFoundError': throw createError('ENOENT', funcName, path.join("/" /* FsaToNodeConstants.Separator */)); } } throw error; } } getFileByFd(fd, funcName) { if (!isFd(fd)) throw TypeError(ERRSTR.FD); const file = this.fds.get(fd); if (!file) throw createError('EBADF', funcName); return file; } async getFileByFdAsync(fd, funcName) { return this.getFileByFd(fd, funcName); } async __getFileById(id, funcName) { if (typeof id === 'number') return (await this.getFileByFd(id, funcName)).file; const filename = pathToFilename(id); const [folder, name] = pathToLocation(filename); return await this.getFile(folder, name, funcName); } async getFileByIdOrCreate(id, funcName) { if (typeof id === 'number') return (await this.getFileByFd(id, funcName)).file; const filename = pathToFilename(id); const [folder, name] = pathToLocation(filename); const dir = await this.getDir(folder, false, funcName); return await dir.getFileHandle(name, { create: true }); } async __open(filename, flags, mode) { const [folder, name] = pathToLocation(filename); const throwIfExists = !!(flags & 128 /* FLAG.O_EXCL */); if (throwIfExists) { try { await this.getFile(folder, name, 'open', false); throw util.createError('EEXIST', 'writeFile'); } catch (error) { const file404 = error && typeof error === 'object' && (error.code === 'ENOENT' || error.name === 'NotFoundError'); if (!file404) { if (error && typeof error === 'object') { switch (error.name) { case 'TypeMismatchError': throw createError('ENOTDIR', 'open', filename); case 'NotFoundError': throw createError('ENOENT', 'open', filename); } } throw error; } } } try { const createIfMissing = !!(flags & 64 /* FLAG.O_CREAT */); const fsaFile = await this.getFile(folder, name, 'open', createIfMissing); return this.__open2(fsaFile, filename, flags, mode); } catch (error) { if (error && typeof error === 'object') { switch (error.name) { case 'TypeMismatchError': throw createError('ENOTDIR', 'open', filename); case 'NotFoundError': throw createError('ENOENT', 'open', filename); } } throw error; } } __open2(fsaFile, filename, flags, mode) { const fd = this.newFdNumber(); const file = new FsaNodeFsOpenFile(fd, mode, flags, fsaFile, filename); this.fds.set(fd, file); return file; } async __close(fd) { const openFile = await this.getFileByFdAsync(fd, 'close'); await openFile.close(); const deleted = this.fds.delete(fd); if (deleted) this.releasedFds.push(fd); } getFileName(id) { if (typeof id === 'number') { const openFile = this.fds.get(id); if (!openFile) throw createError('EBADF', 'readFile'); return openFile.filename; } return pathToFilename(id); } }