ReviseAlgo Logo

Modules & Bundling

Module Resolution & Node.js ESM

Master module resolution rules in JavaScript. Learn how Node.js resolves ES Modules, matches configurations, and handles file extensions.

Last Updated: July 15, 2026 12 min read

1. Introduction

Module Resolution is the process the JavaScript engine or bundler uses to locate module files based on the import path strings. In Node.js, resolving ES Modules behaves differently than CommonJS, enforcing stricter paths and file extension rules.

2. Why It Matters

Node.js supports both CommonJS and ES Modules. Importing CommonJS modules from ES Modules, or importing ES Modules from CommonJS files, can result in resolution errors if you don't configure module settings (like package.json tags) correctly.

3. Real-World Analogy

Think of a Warehouse Navigation Map:

  • CommonJS Resolution (Informal Map): You tell a worker: "Go fetch the toolbox in folder B". The worker checks folder B. If they don't find it, they check for files named B.js, B.json, or check if folder B has an index file inside automatically. The search is dynamic.
  • ES Modules Resolution (Strict Coordinates): You must provide exact coordinates: "Go to aisle 3, shelf 2, box B.js". If you omit the ".js" extension label, the system refuses to search, throwing an error immediately.

4. Node.js Module Resolution

Node.js determines whether to parse a file as CommonJS or ES Modules based on file extensions and package.json properties:

1. File Extensions:

Node.js parses modules based on their extension:
.mjs: Always parsed as an ES Module.
.cjs: Always parsed as a CommonJS module.
.js: Parsed based on the nearest package.json's type field.

2. package.json type Field:

Setting "type": "module" in package.json tells Node.js to treat all .js files in that directory scope as ES Modules. Omitting the type field defaults to CommonJS.

5. ES Modules Strictness

When running ES Modules in Node.js, resolution rules are stricter:
Mandatory Extensions: Relative import paths must specify file extensions explicitly (e.g. import './db.js'). Node.js will not append extensions automatically.
No Directory Indexes: Importing a directory containing an index.js file (e.g. import './components') is not supported in ES Modules. You must specify the full file path.

6. Practical Example

This configuration demonstrates setting up exports definitions inside a package.json file to support both CommonJS and ES Module consumers (Dual Packages):

7. Common Mistakes

  • Trying to use CommonJS global variables inside ES Modules: Global variables like __dirname, __filename, or require are not available in ES Modules. Accessing them throws a ReferenceError. Use import.meta.url to resolve local paths instead.

8. Quick Quiz

Q1: Which configuration setting tells Node.js to parse all '.js' files as ES Modules?

A) "mode": "esm" inside package.json

B) "type": "module" inside package.json

Answer: B — Setting "type": "module" inside package.json tells Node.js to treat all .js files in that scope as ES Modules.

9. Scenario-Based Challenge

The Dual-Module Package Exporter:

You write a library that will be published to npm. You want developers to be able to load the library using both CommonJS: require("lib") and ES Modules: import "lib". Write the conditional export mapping block inside your package.json config.

10. Debugging Exercise

Explain why this import statement crashes in Node.js, and how to fix it:

// app.js (package.json has "type": "module")
// Objective: load directory utility
import { format } from './utils'; // crashes with ERR_MODULE_NOT_FOUND! Why?
View Solution

Diagnosis: Under Node.js ESM mode, the resolver does not append file extensions or check for directory index files automatically, throwing an ERR_MODULE_NOT_FOUND error.

Fix: Specify the exact file path with its extension explicitly:

import { format } from './utils/index.js'; // Specifies full path

11. Interview Questions

🟢 Q1: Why are global variables like __dirname or require not available in ES Modules, and what are their replacements?

Answer: ES Modules are designed to be platform-independent, allowing them to run in both browsers and Node.js. Since __dirname and require are Node.js-specific global variables, they are omitted from ES Modules to ensure compatibility.
To resolve paths in ES Modules, use the standard import.meta.url property instead:
Replacement: import.meta.url returns the module's file URL. You can convert it to a file system path using Node.js's fileURLToPath utility:
const __filename = fileURLToPath(import.meta.url);

12. Production Considerations

  • Explicit Imports: Always write explicit imports with file extensions when developing Node.js backend projects using ESM. This avoids issues with bundler resolution and ensures compatibility with native runtime environments.