Mastering Node.js ES Modules: From CommonJS to Native Imports

This article explains how Node.js 12 introduced native ECMAScript Modules, compares them with CommonJS, shows how to enable the feature, configure package.json, and adapt code using import/export, import.meta, and other module‑specific changes.

Node Underground
Node Underground
Node Underground
Mastering Node.js ES Modules: From CommonJS to Native Imports

In April 2019, the Node.js team released Node.js 12 with native ECMAScript Modules (ESM) support.

ESM is no longer new; tools like Babel and TypeScript have allowed its use for years, but Node.js’s implementation differs significantly from those compile‑time solutions.

Babel/TypeScript Design Philosophy

Babel’s slogan is “Use next generation JavaScript, today,” focusing on compiling new features for current engines. TypeScript’s slogan describes it as a typed superset that compiles to plain JavaScript. Both ultimately produce code limited by the target engine, usually converting ESM to CommonJS for Node.js.

Because Node.js 12 enforces stricter ESM rules, developers need to understand the native implementation before adopting it.

Enabling the Feature

Use the --experimental-modules flag to activate ESM parsing. In this mode, file extensions determine module type:

.mjs – ESM files using import/export.cjs – CommonJS files

.js – Treated as ESM when package.json has

"type": "module"

Distinguishing Module Types with package.json

The type field in package.json can be "module" or "commonjs". If omitted, commonjs is assumed. This field affects how .js files are parsed, while .cjs and .mjs are explicit.

{
  "type": "module" | "commonjs"
}

Depending on the type value, imports are resolved as ESM or CommonJS, allowing seamless inter‑operation.

// package.json "type" is "module"
import './startup/init.js'; // ESM
import 'commonjs-package'; // CommonJS fallback
import './legacy-file.cjs'; // .cjs forces CommonJS
import 'commonjs-package/src/index.mjs'; // .mjs forces ESM

Key Differences Between ES Modules and CommonJS

File extensions must be explicit

CommonJS allowed omitting extensions; ESM requires the exact filename, enabling static analysis of dependencies.

Removal of require , exports , module.exports , __filename , __dirname

These CommonJS globals disappear in ESM. Node provides alternatives, such as module.createRequire() for require and the import.meta API for filename and dirname.

import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

import.meta

import.meta

supplies module‑specific metadata, like its URL.

import.meta is an object exposing context‑specific metadata for a JavaScript module, such as its URL.
console.log(import.meta);
// { url: "file:///home/user/my-module.mjs" }

No require.extensions or require.cache

These CommonJS features are unavailable in ESM.

URL‑based file paths

ESM resolves modules using URL semantics, allowing query parameters to affect caching.

import './foo.mjs?query=1'; // loads with query=1
import './foo.mjs?query=2'; // loads with query=2

Conclusion

Node.js’s ESM implementation is still experimental (stability level 1) and may evolve, especially regarding inter‑op with CommonJS. Nonetheless, the stricter module system promises better optimization and a unified JavaScript ecosystem across browsers and Node.js.

References

ECMAScript Modules – Node.js official docs

Plan for New Modules Implementation – Node.js roadmap

The new ECMAScript module support in Node.js 12 – 2ality

import.meta – MDN

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Node.jspackage.jsonCommonJSModulesES modules
Node Underground
Written by

Node Underground

No language is immortal—Node.js isn’t either—but thoughtful reflection is priceless. This underground community for Node.js enthusiasts was started by Taobao’s Front‑End Team (FED) to share our original insights and viewpoints from working with Node.js. Follow us. BTW, we’re hiring.

0 followers
Reader feedback

How this landed with the community

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.