zachleat/javascript-eval-modules
Dynamic Script Evaluation in JavaScript
You have a String of JavaScript code. How can you execute it? This is a playground for testing various dynamic script execution methods in Node.js and what features they support.
This is research for import-module-string (the approach I currently use and recommend for ESM code) and node-retrieve-globals (legacy but more CommonJS friendly), whose features I have documented in a separate compatibility table below.
vmNode.js documentation- MDN docs for
eval import("data:…")approach from2ality.comimport("blob:…")approach suggested by David Bushnell (not currently supported in Node.js but works in Deno!)
Runtime Native Methods
| JavaScript Feature | Module#_compile | Function() | vm.Script | vm.Module | import("data:…") (or blob:…) |
|---|---|---|---|---|---|
Assign module.exports (CommonJS-only) | Yes | No | Yes | No5 | No |
export (ESM-only) | No | No | No | Yes1 | Yes |
require | Yes | Yes | Yes | Yes1 | No6 |
import (ESM-only) | No4 | No4 | No4 | Yes1 | No7 |
Dynamic import() | Yes | Yes | Yes2 | Yes1 | No7 |
Top level async or await | Faux3 | Faux3 | Faux3 | Yes1 | Yes |
| Can leak to global scope | Yes | Yes | No | No | Yes |
| Runtime Compatibility | Node.js | Any | Node.js | Node.js | Any |
Notes:
- Requires
--experimental-vm-modules. Use outputs anExperimentalWarningto the console. - Requires
vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADERan experimental Node feature added in v21.7.0, v20.12.0. Any use outputs anExperimentalWarningto the console. - Requires the code to be wrapped in an
(async () => {})()IIFE wrapper. - Can use
esm-import-transformerto transform staticimportto dynamicimport()orrequire: https://github.com/zachleat/esm-import-transformer - Probably shimmable but not worth it.
requireis shimmable in Node.js vianode:module#createRequireimportof runtime built-ins (e.g.node:prefixed modules in Node.js) are allowed.
npm Packages
| JavaScript Feature | npm:node-retrieve-globals | npm:import-module-string |
|---|---|---|
Assign module.exports (CommonJS-only) | Yes | No |
export (ESM-only) | No | Yes |
require | Yes | Yes |
import (ESM-only) | No | Yes |
Dynamic import() | Yes | Yes |
Top level async or await | Faux3 | Yes |
| Can leak to global scope | Yes | Yes |
| Runtime Compatibility | Node.js | Any |
Alternate methods
- A lot of the pain here is due to an experimental
vm.Module. If you already have access to a bundler (e.g.esbuild), use that to output CommonJS code and run it throughModule#_compileto bypass current limitations with dynamic ESM in Node.js.- Vite writes a temporary file to the filesystem to workaround this issue.
- Some more discussion on Mastodon.