@miniflare/runner-vm
Version:
VM script runner module for Miniflare: a fun, full-featured, fully-local simulator for Cloudflare Workers
331 lines (325 loc) • 13.1 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
var __export = (target, all) => {
__markAsModule(target);
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __reExport = (target, module2, desc) => {
if (module2 && typeof module2 === "object" || typeof module2 === "function") {
for (let key of __getOwnPropNames(module2))
if (!__hasOwnProp.call(target, key) && key !== "default")
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable });
}
return target;
};
var __toModule = (module2) => {
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2);
};
// packages/runner-vm/src/index.ts
__export(exports, {
VMScriptRunner: () => VMScriptRunner,
VMScriptRunnerError: () => VMScriptRunnerError,
defineHasInstances: () => defineHasInstances
});
var import_vm3 = __toModule(require("vm"));
// packages/runner-vm/src/error.ts
var import_shared = __toModule(require("@miniflare/shared"));
var VMScriptRunnerError = class extends import_shared.MiniflareError {
};
// packages/runner-vm/src/instanceof.ts
var import_vm = __toModule(require("vm"));
function ordinaryHasInstance(C, O) {
if (typeof C !== "function")
return false;
if (typeof O !== "object" && typeof O !== "function")
return false;
if (O === null)
return false;
const P = C.prototype;
if (typeof P !== "object" && typeof P !== "function") {
throw new TypeError(`Function has non-object prototype '${P}' in instanceof check`);
}
while ((O = Object.getPrototypeOf(O)) !== null) {
if (P === O)
return true;
}
return false;
}
function instanceOf(V, insideTarget, outsideTarget) {
return ordinaryHasInstance(insideTarget, V) || ordinaryHasInstance(outsideTarget, V);
}
var outsideTargets = {
Object,
Function,
Array,
Promise,
RegExp,
Error,
EvalError,
RangeError,
ReferenceError,
SyntaxError,
TypeError,
URIError
};
function defineHasInstance(insideTarget) {
Object.defineProperty(insideTarget, Symbol.hasInstance, {
value(value) {
const outsideTarget = outsideTargets[this.name];
return instanceOf(value, this, outsideTarget);
}
});
}
var defineHasInstancesScript = new import_vm.default.Script(`(function(defineHasInstance) {
// Only define properties once, would throw if we tried doing this twice
if (Object.hasOwnProperty(Symbol.hasInstance)) return;
defineHasInstance(Object);
defineHasInstance(Function);
defineHasInstance(Array);
defineHasInstance(Promise);
defineHasInstance(RegExp);
defineHasInstance(Error);
})`, { filename: "<defineHasInstancesScript>" });
function defineHasInstances(ctx) {
defineHasInstancesScript.runInContext(ctx)(defineHasInstance);
}
// packages/runner-vm/src/linker.ts
var import_fs = __toModule(require("fs"));
var import_module = __toModule(require("module"));
var import_path = __toModule(require("path"));
var import_vm2 = __toModule(require("vm"));
var import_shared2 = __toModule(require("@miniflare/shared"));
var SUGGEST_BUNDLE = "If you're trying to import an npm package, you'll need to bundle your Worker first.";
var SUGGEST_NODE = "If you're trying to import a Node.js built-in module, or an npm package that uses Node.js built-ins, you'll either need to:\n- Bundle your Worker, configuring your bundler to polyfill Node.js built-ins\n- Configure your bundler to load Workers-compatible builds by changing the main fields/conditions\n- Find an alternative package that doesn't require Node.js built-ins";
var linkingPromises = new WeakMap();
var evaluatingPromises = new WeakMap();
var ModuleLinker = class {
constructor(moduleRules, additionalModules, compat) {
this.moduleRules = moduleRules;
this.additionalModules = additionalModules;
this.compat = compat;
}
#referencedPathSizes = new Map();
#moduleCache = new Map();
#cjsModuleCache = new Map();
#namespaceCache = new WeakMap();
get referencedPaths() {
return this.#referencedPathSizes.keys();
}
get referencedPathsTotalSize() {
const sizes = Array.from(this.#referencedPathSizes.values());
return sizes.reduce((total, size) => total + size, 0);
}
linker = (spec, referencing) => {
const relative = import_path.default.relative("", referencing.identifier);
const errorBase = `Unable to resolve "${relative}" dependency "${spec}"`;
if (referencing.identifier === import_shared2.STRING_SCRIPT_PATH) {
throw new VMScriptRunnerError("ERR_MODULE_STRING_SCRIPT", `${errorBase}: imports unsupported with string script`);
}
const additionalModule = this.additionalModules[spec];
const identifier = additionalModule ? spec : import_path.default.resolve(import_path.default.dirname(referencing.identifier), spec);
const cached = this.#moduleCache.get(identifier);
if (cached)
return cached;
const moduleOptions = { identifier, context: referencing.context };
let module2;
if (additionalModule) {
module2 = new import_vm2.default.SyntheticModule(Object.keys(additionalModule), function() {
for (const [key, value] of Object.entries(additionalModule)) {
this.setExport(key, value);
}
}, moduleOptions);
this.#moduleCache.set(identifier, module2);
return module2;
}
const rule = this.moduleRules.find((rule2) => rule2.include.test(identifier));
if (rule === void 0) {
const isBuiltin = import_module.builtinModules.includes(spec);
const suggestion = isBuiltin ? SUGGEST_NODE : SUGGEST_BUNDLE;
throw new VMScriptRunnerError("ERR_MODULE_RULE", `${errorBase}: no matching module rules.
${suggestion}`);
}
const data = (0, import_fs.readFileSync)(identifier);
this.#referencedPathSizes.set(identifier, data.byteLength);
switch (rule.type) {
case "ESModule":
module2 = new import_vm2.default.SourceTextModule(data.toString("utf8"), {
...moduleOptions,
importModuleDynamically: this.importModuleDynamically
});
break;
case "CommonJS":
const mod = this.loadCommonJSModule(errorBase, identifier, spec, referencing.context);
module2 = new import_vm2.default.SyntheticModule(["default"], function() {
this.setExport("default", mod.exports);
}, moduleOptions);
break;
case "Text":
module2 = new import_vm2.default.SyntheticModule(["default"], function() {
this.setExport("default", data.toString("utf8"));
}, moduleOptions);
break;
case "Data":
module2 = new import_vm2.default.SyntheticModule(["default"], function() {
this.setExport("default", (0, import_shared2.viewToBuffer)(data));
}, moduleOptions);
break;
case "CompiledWasm":
module2 = new import_vm2.default.SyntheticModule(["default"], function() {
this.setExport("default", new WebAssembly.Module(data));
}, moduleOptions);
break;
default:
throw new VMScriptRunnerError("ERR_MODULE_UNSUPPORTED", `${errorBase}: ${rule.type} modules are unsupported`);
}
this.#moduleCache.set(identifier, module2);
return module2;
};
importModuleDynamically = async (spec, referencing) => {
const module2 = this.linker(spec, referencing);
if (module2.status === "unlinked") {
linkingPromises.set(module2, module2.link(this.linker));
}
await linkingPromises.get(module2);
if (module2.status === "linked") {
evaluatingPromises.set(module2, module2.evaluate());
}
await evaluatingPromises.get(module2);
return module2;
};
loadCommonJSModule(errorBase, identifier, spec, context) {
const cached = this.#cjsModuleCache.get(identifier);
if (cached)
return cached;
const additionalModule = this.additionalModules[spec];
const module2 = { exports: {} };
if (additionalModule) {
module2.exports = additionalModule.default;
this.#cjsModuleCache.set(identifier, module2);
return module2;
}
const rule = this.moduleRules.find((rule2) => rule2.include.test(identifier));
if (rule === void 0) {
const isBuiltin = import_module.builtinModules.includes(spec);
const suggestion = isBuiltin ? SUGGEST_NODE : SUGGEST_BUNDLE;
throw new VMScriptRunnerError("ERR_MODULE_RULE", `${errorBase}: no matching module rules.
${suggestion}`);
}
this.#cjsModuleCache.set(identifier, module2);
const data = (0, import_fs.readFileSync)(identifier);
this.#referencedPathSizes.set(identifier, data.byteLength);
switch (rule.type) {
case "ESModule":
throw new VMScriptRunnerError("ERR_CJS_MODULE_UNSUPPORTED", `${errorBase}: CommonJS modules cannot require ES modules`);
case "CommonJS":
const code = data.toString("utf8");
const wrapped = `(function(exports, require, module) {
${code}
});`;
const script = new import_vm2.default.Script(wrapped, {
filename: identifier,
lineOffset: -1
});
const moduleWrapper = script.runInContext(context);
const require2 = this.createRequire(identifier, context);
moduleWrapper(module2.exports, require2, module2);
break;
case "Text":
module2.exports = data.toString("utf8");
break;
case "Data":
module2.exports = (0, import_shared2.viewToBuffer)(data);
break;
case "CompiledWasm":
module2.exports = new WebAssembly.Module(data);
break;
default:
throw new VMScriptRunnerError("ERR_MODULE_UNSUPPORTED", `${errorBase}: ${rule.type} modules are unsupported`);
}
return module2;
}
createRequire(referencingIdentifier, context) {
const relative = import_path.default.relative("", referencingIdentifier);
const referencingDirname = import_path.default.dirname(referencingIdentifier);
return (spec) => {
const errorBase = `Unable to resolve "${relative}" dependency "${spec}"`;
const identifier = import_path.default.resolve(referencingDirname, spec);
const mod = this.loadCommonJSModule(errorBase, identifier, spec, context);
if (this.compat === void 0 || this.compat.isEnabled("export_commonjs_default")) {
return mod.exports;
} else {
let ns = this.#namespaceCache.get(mod);
if (ns !== void 0)
return ns;
ns = Object.defineProperty({}, "default", {
get: () => mod.exports,
enumerable: true
});
this.#namespaceCache.set(mod, ns);
return ns;
}
};
}
};
// packages/runner-vm/src/index.ts
var VMScriptRunner = class {
constructor(context) {
this.context = context;
}
runAsScript(context, blueprint) {
const script = new import_vm3.default.Script(blueprint.code, {
filename: blueprint.filePath
});
script.runInContext(context);
}
async runAsModule(context, blueprint, linker) {
const module2 = new import_vm3.default.SourceTextModule(blueprint.code, {
identifier: blueprint.filePath,
context,
importModuleDynamically: linker.importModuleDynamically
});
await module2.link(linker.linker);
await module2.evaluate();
return module2.namespace;
}
async run(globalScope, blueprint, modulesRules, additionalModules, compat) {
if (modulesRules && !("SourceTextModule" in import_vm3.default)) {
throw new VMScriptRunnerError("ERR_MODULE_DISABLED", "Modules support requires the --experimental-vm-modules flag");
}
const linker = modulesRules && new ModuleLinker(modulesRules, additionalModules ?? {}, compat);
let context = this.context;
if (context) {
Object.assign(context, globalScope);
} else {
context = import_vm3.default.createContext(globalScope, {
codeGeneration: { strings: false, wasm: false }
});
}
defineHasInstances(context);
let exports = {};
let bundleSize = 0;
bundleSize += Buffer.byteLength(blueprint.code);
if (linker) {
exports = await this.runAsModule(context, blueprint, linker);
} else {
this.runAsScript(context, blueprint);
}
if (linker)
bundleSize += linker.referencedPathsTotalSize;
const watch = linker && [...linker.referencedPaths];
return { exports, bundleSize, watch };
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
VMScriptRunner,
VMScriptRunnerError,
defineHasInstances
});
//# sourceMappingURL=index.js.map