Node.js プロジェクトを開発していると、次のようなエラーメッセージに遭遇することがあります。
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /path/to/module.js
これは、CommonJS と ES Modules (ESM) の混在 によって発生する典型的なエラーです。近年の Node.js では、require
と import
の使い分け、package.json
の type
設定、Babel や TypeScript のトランスパイル結果など、さまざまな要因で混乱が生じます。
本記事では、原因と解決方法を整理し、実運用でのベストプラクティスをまとめます。
1. ERR_REQUIRE_ESMエラーの発生背景
Node.js はバージョン 12 以降から正式に ES Modules をサポートしています。しかし、従来の CommonJS 形式(require / module.exports)と、ESM 形式(import / export)の互換性は完全ではありません。
そのため、以下の状況でエラーが発生します。
package.json
に"type": "module"
を設定しているのにrequire()
を使用- ESM として公開されているライブラリを
require()
で読み込もうとした - トランスパイル設定(Babel / TypeScript)と Node.js 実行環境のモジュール解釈が一致していない
このエラーは、モジュール解決のルールを正しく理解していないと何度もハマる落とし穴です。
2. 再現例と典型的なエラーメッセージ
再現しやすい最小例は次のとおりです。
// package.json
{
"name": "esm-error-example",
"type": "module"
}
// index.js
const lib = require('./lib.js')
実行すると以下のエラーが表示されます。
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /path/to/lib.js
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1219:13)
このエラーは「あなたのプロジェクトは ESM として動作しているのに、require() を使おうとしている」と警告しています。
逆に、type: "commonjs"
(デフォルト)環境で import
を使用してもエラーになります。
import fs from 'fs'
// SyntaxError: Cannot use import statement outside a module
3. 解決策1: ESMへの統一
もっとも推奨されるのは、プロジェクト全体を ESM に統一する方法です。
package.json
に"type": "module"
を設定- 全てのモジュールを
import
/export
に書き換える __dirname
,__filename
,require
が必要な場合はimport.meta.url
やcreateRequire
を使用
// 修正例
import { readFile } from 'fs/promises'
const data = await readFile(new URL('./data.json', import.meta.url))
console.log(data.toString())
もし CommonJS のモジュールを読み込む必要がある場合は、createRequire
を利用できます。
import { createRequire } from 'module'
const require = createRequire(import.meta.url)
const cjsLib = require('./legacy-lib.cjs')
4. 解決策2: CommonJS環境に統一
既存プロジェクトが大きく、ESM への移行が困難な場合は CommonJS 環境に固定する 方法もあります。
package.json
から"type": "module"
を削除- すべてのファイルで
require
とmodule.exports
を利用 - ESM ライブラリを読み込む場合は
dynamic import()
を使用
(async () => {
const esmLib = await import('./esm-only-lib.js')
esmLib.default()
})()
この方法は既存資産を維持したまま少しずつ移行する際に有効です。
5. 実運用上の落とし穴とCI/CDでの注意点
実際の開発では、ローカルでは動くのに CI/CD で落ちるケースが多発します。
- CI 環境が古い Node.js バージョンを使用している
- Babel や tsc が CommonJS 出力をしているのに、
type: module
を付けてしまう node --experimental-specifier-resolution=node
の有無で挙動が変わる
対策としては以下がおすすめです。
.nvmrc
やvolta
を使い Node.js バージョンを固定- ビルド設定と runtime の module 解釈を揃える
- CI で
node -p "process.versions.node"
を実行しバージョンをログ出力 - package.json の
"exports"
フィールドを正しく設定
また、混在環境を避けるため、ESLint で no-restricted-imports
を使い、特定のモジュールシステムの利用を制限するのも有効です。
6. まとめ
「ERR_REQUIRE_ESM」エラーは、Node.js のモジュール解釈がプロジェクト設定と一致していないときに発生します。根本解決には以下のいずれかの選択が必要です。
- プロジェクト全体を ESM に統一し、import/export を利用
- もしくは CommonJS に固定し、必要な箇所のみ dynamic import() を使う
移行中のプロジェクトでは、トランスパイル後のコードと package.json の設定が整合しているかを必ず確認しましょう。CI/CD 環境での実行結果もチェックし、バージョンや実行オプションの差異による事故を防止することが重要です。
参考
- Node.js Modules documentation [https://nodejs.org/api/modules.html]
- Node.js ESM and CommonJS interoperability [https://nodejs.org/api/esm.html#interoperability]
- MDN Modules guide [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules]