vm-browserify

Emulates Node's vm module in the browser by evaluating code inside a hidden, sandboxed iframe.

Library
npm
v1.1.2
205stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
39/100Needs Attention
Development Activity0
Maintenance20
Community56
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
50/100Fair
Architecture58
Code Quality35
Innovation62
Learning Curve45

vm-browserify is the tiny shim browserify swaps in for Node’s built-in vm module whenever bundled browser code calls require(‘vm’). It creates a hidden iframe, copies the requested context properties and standard JavaScript globals onto that iframe’s window, evaluates the code inside that isolated realm, and copies the resulting bindings back onto the caller’s context object — reproducing Node’s runInNewContext, runInContext, runInThisContext, createContext, and isContext API surface without a real V8 sandbox to lean on.

Because it is a browserify-only replacement module with zero runtime dependencies, most projects never install it directly — it is pulled in automatically through browserify’s browser-field module mapping whenever some bundled dependency references Node’s vm module. It has shipped with essentially the same implementation since 2012, making it one of the most widely distributed but rarely-read pieces of the browserify ecosystem.

What You Get

  • Drop-in vm module replacement - Implements runInNewContext, runInContext, runInThisContext, createContext, and isContext so require(‘vm’) resolves to a working shim under browserify.
  • iframe-based sandboxing - Evaluates code inside a detached, invisible iframe’s window rather than the host page’s own global scope.
  • Automatic global forwarding - Copies standard JS globals (Array, Object, RegExp, JSON, etc.) and the properties on your context object onto the iframe before evaluating.
  • Zero runtime dependencies - Ships as a single ~150-line index.js with nothing third-party to install or audit.

Common Use Cases

  • Bundling libraries that call Node’s vm module so they work unmodified in the browser via browserify’s browser-field substitution.
  • Running user-supplied or plugin code in a script-runner tool without polluting the host page’s own global scope.
  • Powering browser-side REPLs and code playgrounds that need a semi-isolated evaluation context.
  • Satisfying the vm shim that older webpack/browserify build chains expect when bundling server-oriented code for the browser.

Under The Hood

Architecture The entire module is one file, index.js (~150 lines), with no build step or internal layering: a Script “class” built from prototype methods (runInContext, runInNewContext, runInThisContext), a marker Context class, and hand-rolled ES3-compatible helpers (indexOf, Object_keys, forEach, defineProp) that stand in for Array/Object methods on older browsers. The data flow is linear — runInNewContext creates a Context via createContext, delegates to runInContext, which appends a hidden iframe to document.body, copies the caller’s context properties plus a fixed list of JS globals onto the iframe’s window, evaluates the code with win.eval (falling back to win.execScript for old IE), copies any new or changed window keys back onto the original context, then removes the iframe. Because everything lives behind one exports object with no abstraction boundary, changing the sandboxing strategy (e.g. swapping iframes for something else) would mean rewriting this single file end to end.

Tech Stack Plain, dependency-free JavaScript (package.json declares “dependencies”: {}) written to ES3-era conventions rather than assuming ES5 built-ins are present. Dev tooling is limited to browserify ^16.1.1 (to bundle test/vm.js for browser execution), tape ^4.11.0 (assertions), and tape-run ^6.0.1 (running the bundled tests in an actual browser), wired up as a single npm test script. There is no TypeScript, no bundler config of its own, and no linter/formatter config in the repo; CI is a legacy Travis badge referencing .travis.yml.

Code Quality A single test file, test/vm.js, uses tape to cover four scenarios (arithmetic evaluation via runInNewContext, variable mutation propagating back to the caller’s context object, multi-statement evaluation, and createContext/runInContext round-tripping via deepEqual), executed in a real browser through the browserify+tape-run pipeline rather than a Node-only test runner. Error handling is minimal — the only explicit throw is a TypeError when runInContext receives a non-Context argument; failure modes like a missing iframe, an eval exception, or an IE execScript fallback are not separately tested. Naming leans on terse, ES3-era conventions (indexOf, Object_keys, defineProp) rather than modern idiomatic JavaScript, and there is no type annotation layer, linter config, or CI beyond a Travis badge.

API Design The public API deliberately mirrors Node’s native vm module method names and signatures (runInNewContext, runInContext, runInThisContext, createContext, isContext), so code that already calls require(‘vm’) needs no changes once browserify substitutes this shim in — API-compatibility with Node is the entire point of the design. Getting started requires no configuration: call the same methods Node users already know, with context objects as plain JS objects. Documentation is limited to a short README example and a CHANGELOG, with no dedicated docs site, JSDoc, or guidance on caveats such as which globals are forwarded automatically, the lack of async/Promise support, or the IE-only execScript fallback path.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search