Frontend Development 11 min read

Building a Front‑End JavaScript Module Executor Using Node's Module._compile Logic

This article explains how to dynamically load and execute a CommonJS‑style JavaScript module in a browser by reproducing Node's internal Module._compile workflow, creating a sandbox with with, Proxy, and Symbol.unscopables, and providing a complete ES6 class implementation with examples.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Building a Front‑End JavaScript Module Executor Using Node's Module._compile Logic

Dynamic Module Loading in the Front‑End

If you are given a code snippet as a string and need to import it as a module at runtime in the browser, you must emulate the Node.js module system.

module.exports = {
  name: 'ConardLi',
  action: function(){
    console.log(this.name);
  }
};

Node Environment Execution

In Node we usually use the Module class, whose private _compile method can dynamically load a module:

export function getRuleFromString(code) {
  const myModule = new Module('my-module');
  myModule._compile(code, 'my-module');
  return myModule.exports;
}

The _compile source shows three steps: create a wrapper function, compile it with vm.runInThisContext , and invoke the wrapper with the proper arguments.

Module.prototype._compile = function(content, filename) {
  // strip shebang
  content = internalModule.stripShebang(content);
  // 1. create wrapper
  var wrapper = Module.wrap(content);
  // 2. compile wrapper in current context
  var compiledWrapper = vm.runInThisContext(wrapper, {
    filename: filename,
    lineOffset: 0,
    displayErrors: true
  });
  var dirname = path.dirname(filename);
  var require = internalModule.makeRequireFunction(this);
  var depth = internalModule.requireDepth;
  // 3. run wrapper
  var result = compiledWrapper.call(this.exports, this.exports, require, this, filename, dirname);
  return result;
};

Creating the Wrapper

The wrapper simply surrounds the module code with a function that receives exports, require, module, __filename, __dirname :

Module.wrap = function(script) {
  return Module.wrapper[0] + script + Module.wrapper[1];
};

Module.wrapper = [
  '(function (exports, require, module, __filename, __dirname) { ',
  '\n});'
];

This isolates each module's scope, solving JavaScript's global‑scope problem.

Implementing the Module in the Browser

Because browsers lack the vm module, we must build our own sandbox. Several techniques are combined:

eval – executes code in the current scope but is unsafe.

new Function() – compiles code without closing over the surrounding scope.

with – creates a semi‑sandbox by preferring properties from a supplied object.

Proxy – intercepts property access to make every lookup succeed.

Symbol.unscopables – prevents the sandbox from hiding built‑in identifiers.

Sandbox via with and new Function

function compileCode(src) {
  src = 'with (sandbox) {' + src + '}';
  return new Function('sandbox', src);
}

Proxy to Force Property Presence

function compileCode(code) {
  code = 'with (sandbox) {' + code + '}';
  const fn = new Function('sandbox', code);
  return (sandbox) => {
    const proxy = new Proxy(sandbox, {
      has() { return true; }
    });
    return fn(proxy);
  };
}

Handling Symbol.unscopables

function compileCode(code) {
  code = 'with (sandbox) {' + code + '}';
  const fn = new Function('sandbox', code);
  return (sandbox) => {
    const proxy = new Proxy(sandbox, {
      has() { return true; },
      get(target, key, receiver) {
        if (key === Symbol.unscopables) return undefined;
        return Reflect.get(target, key, receiver);
      }
    });
    return fn(proxy);
  };
}

Global‑Variable Whitelist

const ALLOW_LIST = ['console'];

function compileCode(code) {
  code = 'with (sandbox) {' + code + '}';
  const fn = new Function('sandbox', code);
  return (sandbox) => {
    const proxy = new Proxy(sandbox, {
      has(target, key) {
        if (!ALLOW_LIST.includes(key)) return true;
        return false;
      },
      get(target, key, receiver) {
        if (key === Symbol.unscopables) return undefined;
        return Reflect.get(target, key, receiver);
      }
    });
    return fn(proxy);
  };
}

Final Implementation

const ALLOW_LIST = ['console'];

export default class Module {
  exports = {};
  wrapper = [
    'return (function (exports, module) { ',
    '\n});'
  ];

  wrap(script) {
    return `${this.wrapper[0]}${script}${this.wrapper[1]}`;
  }

  runInContext(code) {
    code = `with (sandbox) { ${code} }`;
    const fn = new Function('sandbox', code);
    return (sandbox) => {
      const proxy = new Proxy(sandbox, {
        has(target, key) {
          if (!ALLOW_LIST.includes(key)) return true;
          return false;
        },
        get(target, key, receiver) {
          if (key === Symbol.unscopables) return undefined;
          return Reflect.get(target, key, receiver);
        }
      });
      return fn(proxy);
    };
  }

  compile(content) {
    const wrapper = this.wrap(content);
    const compiledWrapper = this.runInContext(wrapper)({});
    compiledWrapper.call(this.exports, this.exports, this);
  }
}

Test Execution

function getModuleFromString(code) {
  const scanModule = new Module();
  scanModule.compile(code);
  return scanModule.exports;
}

const module = getModuleFromString(`
module.exports = {
  name: 'ConardLi',
  action: function(){
    console.log(this.name);
  }
};
`);

module.action(); // ConardLi

The example demonstrates that a string‑based CommonJS module can be safely evaluated in the browser with isolated scope, mimicking Node's module system.

frontendJavaScriptNode.jsSandboxmodule-loadingdynamic-import
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.