Moduleshigh

CommonJS Modules

CommonJS (CJS) is Node.js's original module system. Files use require() to import and module.exports or exports to export. All require() calls are synchronous.

Memory anchor

CommonJS = sending a package via snail mail. You pack it up (module.exports), it ships synchronously, and the recipient gets a snapshot (copy). 'exports' is just a sticky note on the box—rip it off (reassign) and the box ships empty.

Expected depth

CJS modules are loaded synchronously: require() blocks until the module is parsed, executed, and its exports are returned. Modules are cached after first load—subsequent require() calls return the cached exports object. This caching means module-level state is shared across all importers. The exports shorthand is a reference to module.exports; reassigning exports breaks the link (exports = {} does nothing useful; module.exports = {} works).

Deep — senior internals

The CJS module wrapper function is how Node provides module-level scope: `(function(exports, require, module, __filename, __dirname) { /* module code */ })`. This is why __filename and __dirname are available without explicit import. The require() function resolves modules through an algorithm: exact file path → .js/.json/.node extension → index.js/index.json/index.node → node_modules lookup up the directory tree. Loaded module source is cached at require.cache[filename]; deleting an entry from require.cache causes the next require() to re-execute the module. This technique is used in hot-reload implementations but can cause memory leaks if not managed carefully.

🎤Interview-ready answer

CommonJS loads modules synchronously via require(), wrapping each module in a function that provides exports, require, module, __filename, __dirname. Modules are cached post-load, making module-level singletons reliable. The critical behavior: reassigning exports doesn't affect module.exports—only mutations to the exports object or direct assignment to module.exports work for export.

Common trap

exports.foo = bar works because it mutates the object that module.exports points to. But exports = { foo: bar } reassigns the local variable, breaking the reference. The module still exports whatever module.exports holds (the original empty object). This silently exports nothing and is a classic footgun.

Related concepts