CommonJS(.cjs や require() を使うコード)から ESM 専用モジュールを読み込もうとすると、次のエラーで落ちます。
Error [ERR_REQUIRE_ESM]: require() of ES Module ... not supported.
Instead change the require of ... to a dynamic import() which is available in all CommonJS modules.
古い Node(14 系以前)や一部のツール経由では、同じ原因が Must use import to load ES Module という文言で表示されることもあります。
結論から言うと、require() を動的 import() に置き換えるのが最短の直し方です。ただしその前に、あなたの Node のバージョンを確認してください。原因の半分はそこにあります。
まず node -v を確認する
このエラーは Node のバージョンで挙動が変わります。
| Node のバージョン | require('*.mjs') の挙動 |
|---|---|
| 18.x/20.0〜20.18/22.0〜22.11 | ERR_REQUIRE_ESM で落ちる(require(esm) が既定で無効) |
| 20.19.0 以上/22.12.0 以上/23/24 | 落ちない(require(esm) が既定で有効化された) |
注意したいのは中間の帯です。「18 系ではないから大丈夫」ではありません。20.0〜20.18 と 22.0〜22.11 も既定では落ちます(require(esm) の既定有効化は 20.19.0/22.12.0/23.0.0 から)。この差から 「自分のPCでは再現しないのに、CI や本番でだけ出る」 という状況が起こります。手元が 20.19/22.12 以上なら気づけません。まず node -v を実行し、動いている環境のバージョンをそろえて確認してください。
再現(最小構成)
ESM 専用モジュールを 1 つ用意します。
// esm-only.mjs(.mjs は常に ESM として扱われる)
export const greeting = 'hello from an ES module';
これを CommonJS から require() すると落ちます。
// index.cjs
const mod = require('./esm-only.mjs');
console.log(mod.greeting);
Node 18 で実行すると ERR_REQUIRE_ESM が出て、終了コードは 1 になります。
解決:require() を動的 import() に置き換える
ファイルを CommonJS のまま、require() だけを動的 import() に変えます。
// index.cjs(CJS のまま。require() を import() に置き換えただけ)
(async () => {
const mod = await import('./esm-only.mjs');
console.log(mod.greeting);
})();
これで Node 18 でも正常に動きます(終了コード 0)。import() は require() と違い、この記事が扱う Node 18 以降なら、どの版の CommonJS からでも使えます。
仕組み:Node 18 では require で ESM を読めない
Node 18 では、CommonJS の require() から ESM を直接読み込む機能が既定で有効ではありません。だから同期の require() で ESM を読もうとすると拒否されます。
新しい Node(20.19/22.12 以上)は require(esm) が既定有効になり、トップレベル await を含まない同期的な ESM なら require() で読めます。逆にトップレベル await を含む ESM は評価が非同期になるため、同期の require() では待てず ERR_REQUIRE_ASYNC_MODULE になります(詳細は ERR_REQUIRE_ASYNC_MODULE)。
動的 import() は Promise を返す非同期の読み込みなので、版やトップレベル await の有無に関わらず ESM を読み込めます。CommonJS から確実に読むなら import() が無難です。
それでも直したいときの選択肢
- Node を上げて直すなら 20.19.0 以上にする。
require(esm)が有効になり、同期require()でも ESM を読めるようになります。ただし読み込む ESM がトップレベルawaitを含む場合は、上げてもrequire()では読めません(ERR_REQUIRE_ASYNC_MODULEになります)。その場合は動的import()を使ってください。 - プロジェクト全体を ESM にするなら
package.jsonに"type": "module"を足し、require()をimport文に置き換える。 - 依存パッケージが ESM 専用になったことが原因なら、CommonJS 対応の 1 つ前のメジャーバージョンに下げるという手もあります(例: ESM 専用化されたパッケージの直前の版)。ただしこれは一時しのぎで、いずれ移行が必要です。
切り分け(うまくいかないとき)
- 自分のコードに
require()を書いていないのに出る:require()した先(依存パッケージ)が ESM 専用に変わった可能性が高い。読み込み経路を動的import()に変えるか、その依存の CommonJS 対応版に下げる。 - Node を上げたら今度は別のエラーになった:
ERR_REQUIRE_ASYNC_MODULEなら、読み込む ESM がトップレベルawaitを含んでいる。対処は require() of an ES Module with top-level await(ERR_REQUIRE_ASYNC_MODULE) を参照。 import文に変えたらCannot use import statement outside a moduleが出た:そのファイルは CommonJS 扱いのまま。require()のまま動的import()にするか、package.jsonに"type": "module"を足す。