The Module Wrapper Function
Here is a question that puzzles most Node beginners: where do require, module, exports, __dirname, and __filename come from? You never declared them, yet they exist in every CommonJS file — and they are not globals. The answer is the module wrapper: before running your code, Node wraps it in a function whose parameters are exactly those five names.
What Node actually executes
Your file is never run as-is. Node reads the source and wraps it like this before handing it to V8:
The wrapper Node adds around every CJS module
(function (exports, require, module, __filename, __dirname) {
// ─── your module's code goes here ───
});Then Node calls that function, passing in the right values for each parameter. So those five names are ordinary function arguments — which immediately explains their behaviour.
The five injected values
Name | What it is |
|---|---|
| Shorthand reference to |
| This module's import function (carries |
| The module object describing this file |
| Absolute path of this file |
| Absolute path of the directory containing this file |
Why this explains everything
Why each file has its own scope — top-level
const/letare locals of the wrapper function, so they never leak between files or ontoglobal.Why
__dirnamediffers per file — it is an argument Node computes fresh for each module from its own path.Why
exportsandmodule.exportsstart equal — Node passesmodule.exportsin as theexportsargument (see module.exports & exports).Why
requireknows where you are — it is a per-module function bound to this file, so relative paths resolve against it.
Prove it to yourself
Node even exposes the wrapper template. You can print the array of parameter names and the surrounding strings:
const Module = require('node:module')
console.log(Module.wrapper)[
'(function (exports, require, module, __filename, __dirname) { ',
'\n});'
]ES Modules use a different mechanism
The ESM equivalents
import { fileURLToPath } from 'node:url'
import { dirname } from 'node:path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
// Node 20.11+ also offers these directly:
console.log(import.meta.dirname, import.meta.filename)A practical consequence: resolve files relative to the module
__dirname matters because the current working directory (process.cwd()) is wherever the user ran node from — not where your file lives. Always build paths to bundled assets from __dirname, never from a bare relative string:
const path = require('node:path')
// FRAGILE — breaks if launched from another directory
const bad = './config/default.json'
// ROBUST — always relative to THIS file, regardless of cwd
const good = path.join(__dirname, 'config', 'default.json')