🟢 Node.js Q13 / 110

What is ES Module in Node.js?

AI-Powered Answer ✓ Answered

ES Modules (ECMAScript Modules), often referred to as ESM, are the standardized module system for JavaScript. Node.js has progressively adopted ESM, offering a modern, standardized way to organize and reuse code, aligning its module system more closely with browsers.

What are ES Modules?

ES Modules use import and export statements to share code between files. This syntax is standardized across the JavaScript ecosystem, including browsers, and provides several benefits over Node.js's traditional CommonJS (CJS) module system, such as static analysis capabilities, better tree-shaking potential, and explicit dependency management.

Prior to ESM adoption, Node.js primarily used CommonJS with require() to import modules and module.exports or exports to expose them. While CommonJS is still widely used and supported, ESM represents the future of JavaScript module development.

Key Features and Benefits

  • Standardized Syntax: Uses the official import and export syntax, making code more portable between Node.js and browsers.
  • Static Analysis: Module dependencies can be determined at parse time, which enables tools to perform optimizations like tree-shaking (removing unused code).
  • Asynchronous Loading: ESM supports top-level await, allowing modules to resolve asynchronous dependencies at the top level without wrapping them in an async function.
  • Clearer Dependencies: Explicitly declares what a module exports and what it imports, leading to better-structured codebases.
  • Scoped Variables: Each module has its own scope, preventing global variable pollution.

How to Use ES Modules in Node.js

Node.js offers two primary ways to signal that a file should be treated as an ES Module:

  • Using the .mjs extension: Files with a .mjs extension are always treated as ES Modules.
  • Using "type": "module" in package.json: If your package.json file contains "type": "module", all .js files within that package (unless explicitly overridden by "type": "commonjs" in a sub-package.json or a .cjs extension) will be treated as ES Modules.

Here's an example of an ES Module:

javascript
// utils.mjs or utils.js (if "type": "module" is set)
export function add(a, b) {
  return a + b;
}

export const PI = 3.14159;

And how to import it:

javascript
// app.mjs or app.js (if "type": "module" is set)
import { add, PI } from './utils.js';

console.log(add(5, 3)); // Output: 8
console.log(PI);      // Output: 3.14159

Interoperability with CommonJS

Node.js provides mechanisms for interoperability between ESM and CJS modules:

  • ESM can import CJS: An ES Module can import a CommonJS module. The default import will be the module.exports object from the CJS module.
  • CJS cannot require() ESM (directly): A CommonJS module cannot directly require() an ES Module. However, dynamic import() can be used within a CJS module to load an ESM module asynchronously.

Key Differences and Considerations

  • No __filename or __dirname: These CommonJS globals are not available in ESM. Use import.meta.url to get the current module's URL, and then use the Node.js url and path modules to construct file paths.
  • No require global: The require function is not available in ESM. Use import for modules and createRequire from the module module if you absolutely need to dynamically require a CJS module from ESM.
  • JSON Modules: Direct import of JSON files in ESM might not work as intuitively as require() in CJS; it often requires a specific import assertion in newer Node.js versions or a custom loader. Consider importing them as text or using fs.readFile and JSON.parse.
  • File Extensions: Always use full file extensions (e.g., .js, .mjs, .json) when importing modules in ESM. Node.js does not perform extension resolution or directory index resolution like it does for CommonJS.