static-fs
Version:
A static filesystem to bundle files and read them using NodeJS
173 lines (132 loc) • 5.69 kB
JavaScript
"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;