Node.js 22の革命的進化:CommonJSとESMの「壁」を壊す同期読み込みの真実
こんにちは。長年、複雑なレガシーシステムの近代化やCI/CDパイプラインの設計に携わってきたアーキテクトです。
Node.jsの世界には、長年開発者を悩ませてきた「CommonJS (CJS) と ECMAScript Modules (ESM) の断絶」という大きな壁がありました。これまで `require()` はCJSしか扱えず、ESMを読み込むには非同期の `import()` を使う必要がありました。この「非同期の壁」が、設定ファイルやビルドスクリプトの記述をどれほど複雑にしてきたことか。
しかし、Node.js 22で導入された `–experimental-require-module` によって、ついにこの歴史的な不整合に終止符が打たれようとしています。今日は、この新機能を理解し、あなたの開発環境を次世代へ引き上げるための戦略をお話しします。
—
1. なぜ「同期的なESM読み込み」が重要なのか
これまでのNode.jsでは、`require()` は同期的に動作し、`import` は非同期的に動作するという決定的な違いがありました。
- CJS (CommonJS): `require()` はプログラムの実行を止め、即座にモジュールをロードします。設定ファイル(`webpack.config.js` 等)や初期化処理で重宝されてきました。
- ESM: `import` はトップレベルawaitなどを可能にする非同期設計です。これが原因で、従来の同期的なJS環境からESMを呼び出す際に、コールバックやPromiseの連鎖を強制されてきました。
Node.js 22の同期的なESM読み込みは、「CJSのコンテキストから、ESMのコードを同期的に呼び出せるようにする」という、極めて実用的な解決策です。これにより、既存の膨大なCJS資産を捨てずに、最新のESMライブラリを「今まで通りの感覚」で統合できるようになります。
—
2. 準備:Node.js 22のセットアップと動作確認
まずは、この機能を試すための環境を整えましょう。Node.jsのバージョン管理には `nvm` や `fnm` を使うのがプロの鉄則です。
Node.js 22.x系をインストールして利用開始
fnm install 22
fnm use 22
バージョンの確認
node -v
出力例: v22.x.x
HelloWorld:CJSからESMを呼び出す
ディレクトリを作成し、プロジェクトを初期化します。
mkdir node22-esm-demo && cd node22-esm-demo
npm init -y
ここで、あえて `package.json` に `”type”: “module”` を記述しない(つまりCJSモードの)プロジェクトを作成します。
1. ESM形式のファイル (`math.mjs`)
// 名前付きエクスポートを定義
export const add = (a, b) => a + b;
2. CJS形式のファイル (`index.js`)
// 通常のrequireではESMを読み込めないが、フラグで解決する
const { add } = require(‘./math.mjs’);
console.log(‘計算結果:’, add(10, 20));
3. 実行コマンド
–experimental-require-module フラグを立てて実行
node –experimental-require-module index.js
もしフラグなしで実行すると、Node.jsは「ESMをrequireできません」というエラーを返しますが、フラグをつけることでスムーズに実行されます。この「同期的に解決できる」という一点が、ビルドツールやプラグイン開発においてどれほど大きな利益をもたらすか、想像してみてください。
—
3. 実務にどう計り知れない利益をもたらすか
この機能が現場にもたらすメリットは、単なる「書きやすさ」だけではありません。
① 移行コストの劇的な低減
既存のCJSベースのCIツールや設定ファイルをすべてESMに書き換える必要はありません。必要な部分だけESMで書き、レガシーな枠組みの中で安全に呼び出すという「段階的な近代化」が可能です。
② ツールチェーンの統一
多くのビルドツール(Webpack, Rollup, Jestなど)は、内部的にCJSのロードロジックを多用しています。これらがESMをネイティブに同期ロードできるようになれば、複雑な変換(Babelやts-nodeによるトランスパイル)を介さず、Node.jsが直接ESMを解釈できるようになり、ビルド速度とデバッグの容易さが劇的に向上します。
—
4. アーキテクトからのアドバイス:安全な移行プラン
この機能はまだ実験的(Experimental)です。プロダクション環境で利用する際は、以下のステップを踏むことを強く推奨します。
1. まずはテスト・ビルド環境から導入: アプリケーションコード本体ではなく、テストスクリプトやビルドツール環境でこのフラグを有効にし、依存関係が正しく解決されるかを確認してください。
2. `package.json` の `exports` を活用: モジュール解決戦略を「フラグ頼み」にするのではなく、`package.json` の `exports` フィールドを使用して、CJSとESMのデュアルパッケージを構築するのが、モダンなNode.js開発のベストプラクティスです。
3. TypeScriptとの併用: `ts-node` 等を使用する場合も、Node.jsのローダー設定を最新に合わせることで、より型安全かつ高速なモジュール解決が可能になります。
最後に
Node.js 22は、モジュールシステムの「分断」という長年の課題に対する明確な回答を示しました。これまでの「CJSとESMは混ぜるな危険」という教条的なルールを鵜呑みにせず、技術の進歩に合わせてアーキテクチャを最適化していく姿勢こそが、エンジニアの価値を最大化します。
この機能を使って、あなたのプロジェクトをよりクリーンで、よりモダンなものに進化させてみてください。もし環境構築や複雑な依存関係で躓いたら、いつでもまた聞いてください。一緒に解決しましょう。