エラーメッセージプロジェクトを最近 ES Modules に切り替えた場合、ターミナルに次のような大量のテキストが表示されたことがあるかもしれません:
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/path/to/project/utils' imported from /path/to/project/index.js
at new NodeError (node:internal/errors:371:5)
at finalizeResolution (node:internal/modules/esm/resolve:1018:11)
at moduleResolve (node:internal/modules/esm/resolve:983:10)
... {
code: 'ERR_MODULE_NOT_FOUND'
}
なぜこのエラーが発生するのかCommonJS(require)から ES Modules(import)への移行により、ルールの内容が変わりました。以前の CommonJS の時代、Node.js は親切すぎたと言えるかもしれません。const utils = require('./utils') と書けば、Node.js が自動的に utils.js や utils.json、あるいはフォルダ内の index.js を探し出してくれました。
ES Modules は、より厳格でブラウザ準拠の仕様に従っています。これらは**完全な指定子(full specifiers)**を必要とします。Node.js は、どのファイル拡張子が必要かを推測しなくなりました。ファイルが utils.js であれば、コード内でも utils.js と記述しなければなりません。このドット1つと2文字の欠落が、ESM 移行における悩みの約90%を占めています。
即効性のある解決策:明示的な拡張子エラーを解消するには、すべての相対インポートに .js 拡張子を追加します。標準の JavaScript ファイルをインポートしているか、コンパイル済みの資産であるかは関係ありません。拡張子は必須です。
誤った方法```
// index.js import { helper } from './utils'; // ERR_MODULE_NOT_FOUND がスローされる
### 正しい方法```
// index.js
import { helper } from './utils.js'; // 正常に動作する
TypeScript ユーザーへの注意点: 違和感があるかもしれませんが、ディスク上の物理ファイルが utils.ts であっても、import 文では .js 拡張子を使用する必要があります。TypeScript コンパイラ(TSC)は、ESM をターゲットにする場合、コンパイル中にインポートパスを書き換えないため、この挙動を前提としています。
「クイックフィックス」フラグ500以上のファイルがある大規模なコードベースを一度に更新するのは、必ずしも現実的ではありません。急いでいる場合は、実験的なフラグを使用して Node.js に CommonJS のような動作を強制させることができます。ただし、これは根本的な解決策ではなく、あくまで応急処置として控えめに使用してください。
node --experimental-specifier-resolution=node index.js
このフラグは従来の解決アルゴリズムを復元し、拡張子やディレクトリのインデックスを省略できるようにします。ただし、Node.js 19 以降などの新しいバージョンでは、カスタムローダーが推奨されるようになり、このフラグは削除されていることに注意してください。
ディレクトリインポートの処理以前はフォルダを指定すれば、Node.js がその中の index.js を見つけてくれました。ESM ではそうはいきません。具体的に指定する必要があります。
CommonJS スタイル(失敗)```
import { api } from './services';
### ESM スタイル(正解)```
import { api } from './services/index.js';
VS Code で作業を自動化する毎回手動で拡張子を入力する必要はありません。エディタの自動インポートロジックにこの要件を組み込むことができます。.vscode/settings.json を開き、以下の行を追加してください:
{
"javascript.preferences.importModuleSpecifierEnding": "js",
"typescript.preferences.importModuleSpecifierEnding": "js"
}
これで、関数を自動インポートするために「Enter」を押すと、VS Code が自動的に .js を末尾に付加してくれるようになります。
作業内容の再確認コードをプッシュする前に、以下のチェックリストを確認してください:
node index.jsでアプリを起動します。スタックトレースが出ずに起動すれば成功です。-./や../で始まる相対パスのインポートを確認します。そのすべてに拡張子が必要です。-node_modulesは無視して構いません。import express from 'express'のようなインポートは、Node.js がローカルファイルとは異なる方法でパッケージを解決するため、拡張子は不要です。## ESLint で再発を防止するこのエラーを確実に防ぐ最善の方法は、ビルドエラーにすることです。eslint-plugin-importを使用して、開発中に欠落している拡張子を検出します。設定に以下のルールを追加してください:
"rules": {
"import/extensions": ["error", "always", { "js": "always", "mjs": "always" }]
}
このルールを有効にすると、不完全なインポートにはエディタ上で赤い下線が表示されるようになり、壊れたコードがリポジトリに混入するのを防ぐことができます。

