60秒で解決する方法
依存関係から特定のユーティリティをインポートしようとして、Node.jsに拒否されたことはありませんか?このエラーは、パッケージの作者が "exports" フィールドを使用して内部ファイルをロックしているために発生します。ファイルが「公開」リストに含まれていない場合、Node.jsはアクセスを許可しません。
作業を再開するために、以下の手順を試してみてください:
- ドキュメントを確認する: 公開を意図されていない内部ファイルを取得しようとしている可能性があります。その機能にアクセスするための公式な方法を探してください。
- インポートを短縮する:
import { helper } from 'my-lib/dist/utils'の代わりに、よりクリーンなimport { helper } from 'my-lib/utils'や、単にimport { helper } from 'my-lib'を試してみてください。 - アップデートを確認する: メンテナはESM(ECMAScript Modules)に切り替える際、サブパスを隠すことがよくあります。パッケージをアップデートしたばかりの場合は、バージョン履歴で破壊的変更がないか確認してください。
なぜNode.jsはインポートをブロックするのか?
Node.js 12.17.0より前は、npmパッケージ内のあらゆるファイルに自由にアクセスできました。それは、レストランに入って勝手に厨房に行き、フォークを手に取るようなものでした。便利ではありましたが、メンテナがフォルダ構造を変更すると、何千ものアプリが壊れてしまうことを意味していました。
Node.jsは**パッケージのカプセル化(Package Encapsulation)**によってこれを解決しました。package.json に "exports" ブロックを追加することで、作者はどのファイルがパブリックAPIの一部であるかを正確に定義できるようになりました。これは門番のような役割を果たします。リストにないものは、外部の世界からは存在しないものとして扱われます。
// node_modules/some-package/package.json 内
{
"name": "some-package",
"exports": {
".": "./index.js",
"./feature": "./src/feature.js"
}
}
このシナリオでは、some-package/src/feature.js を直接インポートしようとすると ERR_PACKAGE_PATH_NOT_EXPORTED エラーで失敗します。代わりにエイリアスである some-package/feature を使用する必要があります。
解決するための3つの方法
1. 定義されたエントリポイントを使用する
node_modules フォルダ内にあるライブラリの package.json ファイルを開き、"exports" セクションまでスクロールしてください。そこに記載されているキーだけが、コードをインポートする有効な方法です。
パッケージが以下のように定義している場合:
"exports": {
".": "./dist/main.js",
"./utils": "./dist/utils.js"
}
dist フォルダを直接指定するのはやめましょう。コードを以下のように変更します:
import { formatDate } from 'some-package/dist/utils.js'; // これはクラッシュします
次のクリーンなバージョンに変更します:
import { formatDate } from 'some-package/utils'; // これは正常に動作します
2. サブパスの公開(パッケージ作者向け)
もしあなたがエラーの原因となっているパッケージの所有者であるなら、不足しているパスをホワイトリストに追加する必要があります。すべてのファイルを個別にリストする必要はありません。ワイルドカードを使用して、ディレクトリ全体を一度に公開できます:
{
"exports": {
".": "./index.js",
"./lib/*": "./lib/*.js"
}
}
これにより、ユーザーは lib フォルダ内の任意のJSファイルをインポートできるようになります。これはセキュリティと柔軟性の優れたバランスです。
3. 「緊急用」パッチ
ライブラリの作者がプルリクエストをマージするのを待っていられない緊急時もあります。そのような場合は、patch-package を使用してローカルで修正します。
node_modules/the-package/内のpackage.jsonを手動で編集します。- 不足しているパスを
"exports"オブジェクトに追加します。 npx patch-package the-packageを実行します。
これにより patches/ フォルダに永続的な修正が作成され、依存関係を再インストールした後でもプロジェクトに保持されます。
修正内容をテストする方法
簡単なターミナルコマンドを使えば、デバッグ時間を大幅に短縮できます。アプリ全体を実行せずに、Nodeがパスを解決できるかどうかを次のワンライナーで確認してください:
node --input-type=module -e "import('some-package/utils').then(() => console.log('Resolved!')).catch(console.error)"
スタックトレースではなく "Resolved!" と表示されれば、そのインポートパスは正式に有効です。
次回からこの問題を回避するために
設定エラーは起こりやすいものです。package.json にカンマが1つ足りないだけで、Nodeは exports ブロック全体を無視してしまいます。私は通常、公開前に設定を JSON Validator に通して、目に見えない構文バグをキャッチするようにしています。後で失敗したCIビルドをデバッグするよりもずっと早いです。
また、ESMはファイル拡張子に厳しいことにも注意してください。exports が .js や .mjs 拡張子のないファイルを指している場合、Nodeは別の、しかし同様に厄介なエラーをスローする可能性があります。モジュールローダーを正常に動作させるために、常にパスを明示的に指定しましょう。

