microvium
Version:
A compact, embeddable scripting engine for microcontrollers for executing small scripts written in a subset of JavaScript.
125 lines • 6.24 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.nodeStyleImporter = void 0;
const utils_1 = require("./utils");
const resolve_1 = __importDefault(require("resolve"));
const minimatch_1 = __importDefault(require("minimatch"));
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const module_1 = require("module");
/**
* Create a node-style module importer for the given VM and options.
*
* The term "node-style" here refers to the fact that the importer primarily
* works around filenames. Module caching is done on the full path
*/
function nodeStyleImporter(vm, options = {}) {
options = {
extensions: ['.mvm.js', '.js', '.json'],
...options
};
const coreModules = options.coreModules || {};
const rootDir = options.basedir === undefined ? process.cwd() : options.basedir;
const fileSystemAccess = options.fileSystemAccess || 'subdir-only';
const moduleCache = new Map();
const rootImporter = makeNestedImporter(rootDir);
return rootImporter;
// An importer that works relative to a different basedir (e.g. for a nested dependency)
function makeNestedImporter(basedir) {
return (specifier) => {
if (specifier in coreModules) {
const coreModule = coreModules[specifier];
// The value can be a specifier for the core module
if (typeof coreModule === 'string') {
return rootImporter(coreModule);
}
(0, utils_1.hardAssert)(typeof coreModule === 'object' && coreModule !== null);
return coreModule;
}
// If it's not in the core module list and we can't access the file
// system, then we can't import the module
if (fileSystemAccess === 'none') {
throw new Error(`Module not found: ${(0, utils_1.stringifyIdentifier)(specifier)}`);
}
// References to files start with `/`, `./` or `../`
// https://nodejs.org/api/modules.html#modules_file_modules
const isFilePath = /^((\/)|(\.\/)|(\.\.\/))/.test(specifier);
const resolved = resolve_1.default.sync(specifier, {
...options,
basedir
});
const isNodeCoreModule = module_1.builtinModules.includes(resolved);
if (isNodeCoreModule) {
if (options.allowNodeCoreModules) {
return require(resolved);
}
else {
throw new Error(`Module not found: ${(0, utils_1.stringifyIdentifier)(specifier)}`);
}
}
// If it didn't resolve to a file path, and we've already checked
// specified core modules and node core modules, then it's not valid
if (!isFilePath) {
throw new Error(`Module not found: ${(0, utils_1.stringifyIdentifier)(specifier)}`);
}
const fullModulePath = resolved;
const relativePath = path_1.default.relative(rootDir, fullModulePath);
if (options.fileSystemAccess === 'subdir-only') {
if (path_1.default.posix.normalize(relativePath).startsWith('../')) {
throw new Error(`Module not found: ${(0, utils_1.stringifyIdentifier)(specifier)}`);
}
}
if (options.includes) {
const isIncluded = options.includes.some(include => (0, minimatch_1.default)(relativePath, include));
if (!isIncluded) {
throw new Error(`Module not found: ${(0, utils_1.stringifyIdentifier)(specifier)}`);
}
}
if (options.excludes) {
const isIncluded = options.excludes.some(exclude => (0, minimatch_1.default)(relativePath, exclude));
if (!isIncluded) {
throw new Error(`Module not found: ${(0, utils_1.stringifyIdentifier)(specifier)}`);
}
}
const fileExtension = path_1.default.extname(fullModulePath).toLowerCase();
if (fileExtension === '.js') {
let source;
if (moduleCache.has(fullModulePath)) {
source = moduleCache.get(fullModulePath);
}
else {
const moduleDir = path_1.default.dirname(fullModulePath);
if (!fs_1.default.existsSync(fullModulePath)) {
throw new utils_1.MicroviumUsageError(`File not found: "${fullModulePath}"`);
}
const sourceText = fs_1.default.readFileSync(fullModulePath, 'utf8');
const debugFilename = fullModulePath;
const importDependency = makeNestedImporter(moduleDir);
(0, utils_1.hardAssert)(typeof sourceText === 'string');
source = { sourceText, debugFilename, importDependency };
moduleCache.set(fullModulePath, source);
}
const module = vm.evaluateModule(source);
return module;
}
else if (fileExtension === '.json') {
const sourceText = fs_1.default.readFileSync(fullModulePath, 'utf8');
const debugFilename = fullModulePath;
const value = JSON.parse(sourceText);
const importedValue = (0, utils_1.importPodValueRecursive)(vm, value);
const source = { sourceText, debugFilename };
moduleCache.set(fullModulePath, source);
return importedValue;
}
else {
// Other resources, e.g. JSON
return require(fullModulePath);
}
};
}
}
exports.nodeStyleImporter = nodeStyleImporter;
//# sourceMappingURL=node-style-importer.js.map