isolation
Version:
How often do you see libraries which mutates global variables Or how often do you check libraries actions ? This library provides script isolation in custom contexts to solve this kind of issues.
66 lines (56 loc) • 2.31 kB
JavaScript
;
const { basename, join, dirname, parse } = require('node:path');
const { readFile, readdir, stat } = require('node:fs').promises;
const { checkAccess, isObject } = require('./utils');
const isAllowed = checkAccess.bind(null, 'reader');
const read = (src, opts = {}) => stat(src).then(s => (s.isDirectory() ? dir : file)(src, opts));
Object.assign(read, { file, dir });
var init, execute;
module.exports = Script => {
execute = Script.execute;
init = Script.prepare;
return read;
};
async function file(path, options = {}) {
if (typeof path !== 'string') throw new TypeError('Non-string path is not supported');
if (!isObject(options)) throw new TypeError('Non-object options are not supported');
const src = await readFile(path, 'utf8');
if (src.length === 0) throw new SyntaxError(`File is empty '${path}'`);
return (options.prepare ? init : execute)(src, {
...options,
filename: basename(path),
dir: dirname(path),
});
}
async function dir(src, options = {}) {
if (typeof src !== 'string') throw new TypeError('Non-string path is not supported');
if (!isObject(options)) throw new TypeError('Non-object options are not supported');
const result = await pull(options, src);
return result || {};
}
const further = (max, depth) => max === true || (typeof max === 'number' && depth + 1 < max);
async function pull(options, dir, root = {}, depth = 0) {
const { depth: max = true, flat = false, access } = options;
const createScript = options.prepare ? init : execute;
const files = await readdir(dir, { withFileTypes: true });
const storage = flat ? root : {};
const promises = files.map(async file => {
const path = join(dir, file.name);
if (!isAllowed(access, path)) return;
const isDir = file.isDirectory();
const { base: filename, name: key } = parse(path);
if (!isDir) {
var src = await readFile(path, 'utf8');
if (src.length === 0) return;
storage[key] = createScript(src, { ...options, filename, dir });
return;
}
if (!further(max, depth)) return;
const result = await pull(options, path, root, depth + 1);
if (flat || result === null) return;
storage[key] = result;
});
await Promise.all(promises);
if (Object.keys(storage) === 0) return null;
return storage;
}