Node は 2 つのモジュールシステム をサポートします。CommonJS (CJS) — require/module.exports を使う元来の方式 — と、ES Modules (ESM) — import/export を使う標準方式 — です。これらは構文、読み込みの挙動、相互運用の点で異なります。
Node は 2 つのモジュールシステム をサポートします。CommonJS (CJS) — require/module.exports を使う元来の方式 — と、ES Modules (ESM) — import/export を使う標準方式 — です。これらは構文、読み込みの挙動、相互運用の点で異なります。
// math.js
function add(a, b) { return a + b; }
module.exports = { add };
// app.js
const { add } = require("./math"); // synchronous, runtime
// math.mjs (or .js with "type": "module")
export function add(a, b) { return a + b; }
export default something;
// app.mjs
import { add } from "./math.mjs"; // static, hoisted
CommonJS ES Modules
Syntax require/module.exports import/export
Loading synchronous, runtime static, hoisted
Top-level await no yes
Tree-shaking hard yes (statically analyzable)
File extension .cjs / .js (default) .mjs / .js with "type":"module"
__dirname/__filename available not available (use import.meta.url)
// package.json
{ "type": "module" } // now .js files are treated as ESM
あるいは .mjs 拡張子を明示的に使います。これがなければ、Node は .js をデフォルトで CommonJS として扱います。
// ESM can import CommonJS:
import pkg from "some-cjs-lib"; // works (default import)
// CommonJS CANNOT require() a pure-ESM package (it's async):
const esm = require("esm-only-pkg"); // ❌ ERR_REQUIRE_ESM
// must use dynamic import: const esm = await import("esm-only-pkg");
難点: CJS は ESM 専用パッケージを require() できません — 移行時によくある悩みの種です。また ESM には __dirname がない点にも注意してください(import.meta.url を使います)。
両方のシステムを知ることは、実際の Node の作業に不可欠です。ESM は未来(標準、ツリーシェイキング可能、トップレベル await、ブラウザと整合)であり、新規プロジェクトの正しい選択ですが、npm エコシステムと既存コードの膨大な部分は CommonJS です。
構文の違い、ESM の有効化方法("type": "module")、そして相互運用の制限(特に CJS は ESM を require できない)を理解することで、それらを混在させたときに起こりがちな混乱とエラーを防げます。