Last active
March 17, 2023 07:07
-
-
Save AriPerkkio/50562378aef3448a234e9ce6658c4c37 to your computer and use it in GitHub Desktop.
isolated vm + dynamic import
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
$ node --watch --no-warnings --experimental-vm-modules --experimental-import-meta-resolve vm.mjs | |
*/ | |
import vm from "node:vm"; | |
import { readFileSync } from "node:fs"; | |
import { fileURLToPath } from "node:url"; | |
import { JSDOM } from "jsdom"; | |
const server = { | |
async fetchModule(filename) { | |
if (filename.startsWith("node:")) return "externalize"; | |
const code = readFileSync(fileURLToPath(filename), "utf8"); | |
return this.transform(code); | |
}, | |
async transform(code) { | |
return `// @transformed\n${code}`; | |
}, | |
}; | |
const html = `<!DOCTYPE html> | |
<html> | |
<body> | |
<div>Initial HTML</div> | |
</body> | |
</html>`; | |
const dom1 = new JSDOM(html, { runScripts: "dangerously" }); | |
const dom2 = new JSDOM(html, { runScripts: "dangerously" }); | |
const script = ` | |
'use strict'; | |
(async () => { | |
await import("./setup-file.js"); | |
const { default: chalk } = await import("chalk"); | |
console.log(chalk.blue('Running chalk!')); | |
})() | |
`.trim(); | |
const context1 = dom1.getInternalVMContext(); | |
const context2 = dom2.getInternalVMContext(); | |
context1.console = { log: (...args) => console.log("[Context 1]:", ...args) }; | |
context2.console = { log: (...args) => console.log("[Context 2]:", ...args) }; | |
await vm.runInContext(script, context1, createOptions(context1)); | |
await vm.runInContext(script, context2, createOptions(context2)); | |
await vm.runInContext(script, context1, createOptions(context1)); | |
console.log("\ncontext1\n", context1.document.body.outerHTML); | |
console.log("\ncontext2\n", context2.document.body.outerHTML); | |
console.log("\nglobalThis\n", globalThis.document); | |
/* | |
*/ | |
function createOptions(context) { | |
const moduleCache = new Map(); | |
async function load( | |
filename, | |
{ identifier } = { identifier: import.meta.url } | |
) { | |
if (moduleCache.has(filename)) { | |
return moduleCache.get(filename); | |
} | |
const name = await import.meta.resolve(filename, identifier); | |
const options = { | |
context, | |
identifier: name, | |
importModuleDynamically, | |
}; | |
const code = await server.fetchModule(name); | |
const mod = | |
code === "externalize" | |
? await externalize(options) | |
: new vm.SourceTextModule(code, options); | |
moduleCache.set(filename, mod); | |
return mod; | |
} | |
async function externalize(options) { | |
const mod = await import(options.identifier); | |
const externalized = new vm.SyntheticModule( | |
Object.keys(mod), | |
() => | |
Object.keys(mod).forEach((key) => | |
externalized.setExport(key, mod[key]) | |
), | |
options | |
); | |
return externalized; | |
} | |
async function importModuleDynamically(specifier) { | |
const m = await load(specifier); | |
if (m.status === "unlinked") { | |
await m.link(load); | |
} | |
if (m.status === "linked") { | |
await m.evaluate(); | |
} | |
return m; | |
} | |
return { importModuleDynamically }; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "repro", | |
"version": "1.0.0", | |
"main": "index.js", | |
"dependencies": { | |
"chalk": "^5.2.0", | |
"jsdom": "^21.1.1" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const div = document.createElement("div"); | |
div.appendChild(document.createTextNode("Setup complete!")); | |
document.body.appendChild(div); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment