Backend Development 14 min read

Understanding Node.js Module System and Implementing a Custom Require Loader

This article explains how Node.js implements module loading using the CommonJS wrapper, demonstrates the role of the built‑in vm module for isolated execution, and walks through building a custom Require function with path resolution, file reading, extension handling, caching, and automatic extension resolution.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Understanding Node.js Module System and Implementing a Custom Require Loader

Node.js is a JavaScript runtime that enables executing JavaScript outside the browser, and each file is treated as an independent CommonJS module.

1. Node module implementation

Node wraps each module in a function to provide scope isolation, exposing exports , module , require , __filename , and __dirname . The wrapper can be observed by running a file that logs an undefined global like window , which throws a ReferenceError.

(function (exports, require, module, __filename, __dirname) { console.log(window); /* ReferenceError: window is not defined */ })

Modules can also be manually wrapped, e.g.:

let moduleA = (function() { module.exports = Promise; return module.exports; })();

2. require loading

The built‑in fs module reads a file as a string, which can be turned into executable code using eval or new Function . Both approaches suffer from variable leakage, so Node uses the vm module to run code in an isolated context.

const name = 'yd'; const str = 'const a = 123; console.log(name)'; eval(str); // yd
const b = 3; const str = 'let a = 1; return a + b'; const fun = new Function('b', str); console.log(fun(b)); // 4

3. Implementing require

Before building a custom loader, the article reviews the path and fs modules and their useful methods ( basename , extname , dirname , join , resolve , __dirname , __filename ).

const path = require('path'); console.log(path.basename('1.js')); console.log(path.extname('2.txt'));
const fs = require('fs'); const buffer = fs.readFileSync('./name.txt', 'utf8'); console.log(buffer);

The custom Require function resolves the absolute path, creates a Module instance, and delegates loading to tryModuleLoad :

function Require(modulePath) { let absPathname = path.resolve(__dirname, modulePath); const module = new Module(absPathname); tryModuleLoad(module); return module.exports; }

The Module constructor stores the file id and an empty exports object.

function Module(id) { this.id = id; this.exports = {}; }

Node’s internal wrapper is reproduced:

Module.wrapper = ["(function(exports, module, Require, __dirname, __filename) {", "})"];

Extension handling is defined in Module._extensions for .js (using vm.runInThisContext ) and .json (using JSON.parse ).

Module._extensions['.js'](module) { const content = fs.readFileSync(module.id, 'utf8'); const fnStr = Module.wrapper[0] + content + Module.wrapper[1]; const fn = vm.runInThisContext(fnStr); fn.call(module.exports, module.exports, module, Require, __filename, __dirname); }
Module._extensions['.json'](module) { const json = fs.readFileSync(module.id, 'utf8'); module.exports = JSON.parse(json); }

tryModuleLoad selects the proper loader based on the file extension.

function tryModuleLoad(module) { const extension = path.extname(module.id); Module._extensions[extension](module); }

4. Adding cache

The loader checks Module._cache before loading; if a module is already cached, its exports are returned directly.

if (Module._cache[absPathname]) { return Module._cache[absPathname].exports; }

5. Automatic extension resolution

If a required path lacks an extension, the loader iterates over known extensions, appends each to the path, and loads the first existing file.

const extNames = Object.keys(Module._extensions); /* iterate and find existing file */

6. Summary of steps

1) Import path , fs , and vm and create Require . 2) Use a wrapper function to isolate module code. 3) Resolve filenames to absolute paths. 4) Cache modules in Module._cache . 5) Load files according to their extension and execute them in the isolated context.

javascriptNode.jsmodule systemRequireCommonJS
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.