UNPKG

static-fs

Version:

A static filesystem to bundle files and read them using NodeJS

313 lines (245 loc) 7.37 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.StaticFilesystem = void 0; var _path = require("path"); var _os = require("os"); var _common = require("../common"); var _streams = require("./streams"); var _volume = require("./volume"); class StaticFilesystem { static NewError(code, method, filepath) { switch (code) { case _os.constants.errno.ENOENT: return { ...new Error(`ENOENT: no such file or directory, ${method} '${filepath}'`), code: 'ENOENT', path: filepath, errno: _os.constants.errno.ENOENT }; case _os.constants.errno.EISDIR: return { ...new Error(`EISDIR: illegal operation on a directory, ${method} '${filepath}'`), code: 'EISDIR', path: filepath, errno: _os.constants.errno.EISDIR }; } return { ...new Error(`UNKNOWN: Error, ${method} '${filepath}'`), code: 'UNKNOWN', path: filepath, errno: -10000 }; } constructor() { this.volumes = {}; this.fds = {}; this.pathVolumeMap = {}; } shutdown() { for (const volume of Object.values(this.volumes)) { volume.shutdown(); } } load(sourcePath) { sourcePath = (0, _path.resolve)(sourcePath); if (this.volumes[sourcePath]) { // already loaded? return this; } const volume = new _volume.ReadableStaticVolume(sourcePath); const pathVolumeIndex = volume.load(); this.pathVolumeMap = { ...this.pathVolumeMap, ...pathVolumeIndex }; this.volumes[volume.sourcePath] = volume; return this; } get loadedVolumes() { return Object.keys(this.volumes); } unload(sourcePath) { sourcePath = (0, _path.resolve)(sourcePath); if (!this.volumes[sourcePath]) { return this; } const volumeToUnload = this.volumes[sourcePath]; if (volumeToUnload.sourcePath !== sourcePath) { return this; } volumeToUnload.shutdown(); return this; } get entries() { return Object.keys(this.pathVolumeMap); } readFileSync(filePath, options) { const volume = this.volumeForFilepathSync(filePath); if (!volume) { throw StaticFilesystem.NewError(_os.constants.errno.ENOENT, 'readFileSync', filePath); } return volume.readFileSync(filePath, options); } readFile(filePath, options, callback) { const volume = this.volumeForFilepathSync(filePath); if (!volume) { process.nextTick(() => { callback(StaticFilesystem.NewError(_os.constants.errno.ENOENT, 'readFile', filePath)); }); return; } const foundFile = volume.readFileSync(filePath, options); process.nextTick(() => { callback(undefined, foundFile); }); } read(fd, buffer, offset, length, position, callback) { try { this.getValidatedFD(fd); } catch (e) { process.nextTick(() => { callback(e); }); return; } const filePath = fd.filePath; const volume = this.volumeForFilepathSync(filePath); if (!volume) { process.nextTick(() => { callback(StaticFilesystem.NewError(_os.constants.errno.ENOENT, 'read', fd)); }); return; } process.nextTick(() => { volume.read(filePath, buffer, offset, length, position, callback); }); } realpathSync(filePath) { const volume = this.volumeForFilepathSync(filePath); if (!volume) { throw StaticFilesystem.NewError(_os.constants.errno.ENOENT, 'realpathSync', filePath); } return volume.getFromIndex(filePath) ? (0, _common.sanitizePath)(filePath) : undefined; } realpath(filePath, callback) { const volume = this.volumeForFilepathSync(filePath); if (!volume) { process.nextTick(() => { callback(StaticFilesystem.NewError(_os.constants.errno.ENOENT, 'realpath', filePath)); }); return; } const foundPath = volume.getFromIndex(filePath) ? (0, _common.sanitizePath)(filePath) : undefined; process.nextTick(() => { callback(undefined, foundPath); }); } volumeForFilepathSync(filePath) { const targetPath = (0, _common.sanitizePath)(filePath); const volumePathForFilePath = this.pathVolumeMap[targetPath]; if (!volumePathForFilePath) { return undefined; } const volumeForFilePath = this.volumes[volumePathForFilePath]; if (!volumeForFilePath) { return undefined; } return volumeForFilePath; } statSync(filePath) { const volume = this.volumeForFilepathSync(filePath); if (!volume) { StaticFilesystem.NewError(_os.constants.errno.ENOENT, 'statSync', filePath); } return volume.getFromIndex(filePath); } stat(filePath, callback) { const volume = this.volumeForFilepathSync(filePath); if (!volume) { process.nextTick(() => { callback(StaticFilesystem.NewError(_os.constants.errno.ENOENT, 'stat', filePath)); }); return; } const foundStat = volume.getFromIndex(filePath); process.nextTick(() => { callback(undefined, foundStat); }); } readdirSync(filePath) { const volume = this.volumeForFilepathSync(filePath); if (!volume) { StaticFilesystem.NewError(_os.constants.errno.ENOENT, 'readdirSync', filePath); } return Object.keys(volume.getFromDirectoriesIndex(filePath)) || []; } readdir(filePath, callback) { const volume = this.volumeForFilepathSync(filePath); if (!volume) { process.nextTick(() => { callback(StaticFilesystem.NewError(_os.constants.errno.ENOENT, 'readdir', filePath)); }); return; } process.nextTick(() => { callback(undefined, Object.keys(volume.getFromDirectoriesIndex(filePath)) || []); }); } getValidatedFD(fd) { if (!fd || !fd.type || fd.type !== 'static_fs_file_descriptor') { throw StaticFilesystem.NewError(_os.constants.errno.EBADF, 'getValidatedFD', fd); } const sfsFd = this.fds[fd.id]; if (!sfsFd) { throw StaticFilesystem.NewError(_os.constants.errno.EEXIST, 'getValidatedFD', fd); } return sfsFd; } open(filePath, callback) { const volume = this.volumeForFilepathSync(filePath); if (!volume) { process.nextTick(() => { callback(StaticFilesystem.NewError(_os.constants.errno.ENOENT, 'open', filePath)); }); return; } const fdIdentifier = `${volume.sourcePath}#${(0, _common.sanitizePath)(filePath)}`; this.fds[fdIdentifier] = { type: 'static_fs_file_descriptor', id: fdIdentifier, volumeSourcePath: volume.sourcePath, filePath: filePath }; process.nextTick(() => { callback(undefined, this.fds[fdIdentifier]); }); } close(fd, callback) { try { this.getValidatedFD(fd); } catch (e) { process.nextTick(() => { callback(e); }); return; } delete this.fds[fd.id]; process.nextTick(() => { callback(); }); } fstat(fd, callback) { try { this.getValidatedFD(fd); } catch (e) { process.nextTick(() => { callback(e); }); return; } this.stat(fd.filePath, callback); } createReadStream(filePath, options) { return new _streams.ReadStream(this, filePath, options); } } exports.StaticFilesystem = StaticFilesystem;