UNPKG

static-fs

Version:

A static filesystem to bundle files and read them using NodeJS

173 lines (132 loc) 5.69 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WritableStaticVolume = void 0; var _path = require("path"); var _common = require("../../common"); class WritableStaticVolume { constructor(mountingRoot) { this.mountingRoot = mountingRoot; this.outputFile = (0, _path.resolve)(this.mountingRoot, 'static_fs/static_fs_volume.sfsv'); this.reset(); } reset() { this.hash = ''; this.hashBuffer = Buffer.allocUnsafe(0); this.index = []; this.directoriesIndex = {}; this.intBuffer = Buffer.alloc(_common.INT_SIZE); } async addFolder(sourceFolder, exclusions) { if (this.mountingRoot === sourceFolder) { throw new Error('You cannot add the mounting root of the project to the static filesystem'); } if (!sourceFolder.includes(this.mountingRoot)) { throw new Error(`All the files to include into the static filesystem should has mountingRoot has parent: ${this.mountingRoot}`); } const calculatedTargetFolder = (0, _path.relative)(this.mountingRoot, sourceFolder); await this.getFileNames(sourceFolder, calculatedTargetFolder, exclusions); } get headerLength() { let size = _common.INT_SIZE; // start of data // put hash size in header this.hashBuffer = Buffer.from(this.hash, 'utf-8'); size += _common.INT_SIZE; size += this.hashBuffer.byteLength; const filePaths = Object.keys(this.index); for (const each of filePaths) { size += _common.INT_SIZE; // name size size += _common.INT_SIZE; // data size const filenameBuffer = Buffer.from(each, 'utf-8'); this.index[each].filename = filenameBuffer; size += filenameBuffer.byteLength; // name itself. } size += _common.INT_SIZE; // trailing zero. return size; } writeInt(fd, value, position) { this.intBuffer.writeIntBE(value, 0, 6); return (0, _common.write)(fd, this.intBuffer, 0, _common.INT_SIZE, position); } async write() { await (0, _common.mkdir)((0, _path.dirname)(this.outputFile)); this.hash = (0, _common.calculateHash)(Object.keys(this.index).sort()); let dataOffset = this.headerLength; const fd = await (0, _common.open)(this.outputFile, 'w'); let headerPosition = await this.writeInt(fd, dataOffset); headerPosition += await this.writeInt(fd, this.hashBuffer.byteLength); headerPosition += await (0, _common.write)(fd, this.hashBuffer, 0, this.hashBuffer.byteLength, headerPosition); const all = []; const filePaths = Object.keys(this.index); // start writing out the data for (const each of filePaths) { const entry = this.index[each]; const position = dataOffset; dataOffset += entry.size; const buf = await this.index[each].getBuffer(); await (0, _common.write)(fd, buf, 0, buf.length, position); } // finish writing all the buffers. await Promise.all(all); // write the header for (const each of filePaths) { const entry = this.index[each]; headerPosition += await this.writeInt(fd, entry.filename.length, headerPosition); headerPosition += await this.writeInt(fd, entry.size, headerPosition); headerPosition += await (0, _common.write)(fd, entry.filename, 0, entry.filename.length, headerPosition); } await (0, _common.close)(fd); return this.hash; } async getFileNames(sourceFolder, targetFolder, exclusions) { const files = await (0, _common.readdir)(sourceFolder); const all = []; for (const file of files) { // compute the path names const sourcePath = `${sourceFolder}${_path.sep}${file}`; const targetPath = `${targetFolder}${_path.sep}${file}`; // is declared exclusion? const foundExclusion = exclusions[sourceFolder] || exclusions[sourcePath]; if (foundExclusion) { continue; } // is this a directory const ss = await (0, _common.stat)(sourcePath); if (ss.isDirectory()) { this.directoriesIndex[sourcePath] = { hasNativeModules: false }; all.push(this.getFileNames(sourcePath, targetPath, exclusions)); continue; } const isNativeModuleFile = file.endsWith('.node'); if (isNativeModuleFile) { this.directoriesIndex[sourcePath] = { hasNativeModules: true }; continue; } // it's a file. capture the details. this.index[targetPath] = { size: ss.size, getBuffer: () => (0, _common.readFile)(sourcePath) }; } // wait for children to finish await Promise.all(all); } getAddedFilesAndFolders() { const addParentsForFolder = (folderPath, accum) => { const parent = (0, _path.dirname)(folderPath); if (parent && parent !== this.mountingRoot && parent.includes(this.mountingRoot)) { accum[parent] = true; return addParentsForFolder(parent, accum); } }; const foldersWithNativeModulesIndex = Object.keys(this.directoriesIndex).reduce((accum, folderPath) => { if (this.directoriesIndex[folderPath].hasNativeModules && !accum[folderPath]) { accum[folderPath] = true; addParentsForFolder(folderPath, accum); } return accum; }, {}); const addedFolders = Object.keys(this.directoriesIndex).filter(folderPath => !foldersWithNativeModulesIndex[folderPath]).map(folderPath => (0, _path.resolve)(this.mountingRoot, folderPath)); const addedFiles = Object.keys(this.index).map(filePath => (0, _path.resolve)(this.mountingRoot, filePath)); return addedFiles.concat(addedFolders).sort((a, b) => b.localeCompare(a)); } } exports.WritableStaticVolume = WritableStaticVolume;