@ezdevlol/memfs
Version:
In-memory file-system with Node's fs API.
188 lines (187 loc) • 7.14 kB
JavaScript
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);
}
}