npm v7で`ERESOLVE unable to resolve dependency tree Could not resolve dependency:`

はじめに

この記事はテスト中に遭遇した表題のエラーの、原因と解決方法を記録/するためのものです。

先に結論だけ

  • npm v7で破壊的な変更が導入されました。
  • peerDependenciesのバージョン解決がより厳密になりました。
  • 上流モジュールのpeerDependenciesを修正すると解決します。
  • 上流モジュールの修正が出来ない場合、npm installに--legacy-peer-depsオプションをつけると解決します。

再現条件と手順

問題が発生する条件は以下の通りです。

  • テストにはTravis CIを利用している
  • 環境はnode.js
  • node.jsバージョン16をテスト対象に加えた
  • バージョン16のテストのみ失敗する

並列でバージョン12と14のテストも行っていますが、そちらはエラーが発生しません。

環境

Travis CIから出力される環境情報は以下の通りです。

$ node --version
v16.3.0
$ npm --version
7.15.1
$ nvm --version
0.37.2
$ yarn --version
1.22.4

エラーログ

出力されたエラーログは以下の通りです。

npm install
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! Found: jest@27.0.4
npm ERR! node_modules/jest
npm ERR!   dev jest@"^27.0.4" from the root project
npm ERR!   peer jest@"^27.0.0" from ts-jest@27.0.3
npm ERR!   node_modules/ts-jest
npm ERR!     dev ts-jest@"^27.0.3" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer jest@"^26.4.2" from fake-mouse-event@0.0.4
npm ERR! node_modules/fake-mouse-event
npm ERR!   dev fake-mouse-event@"https://github.com/MasatoMakino/fake-mouse-event.git" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.

原因

この問題の原因は、npm v7の変更です。

https://github.com/npm/rfcs/blob/latest/implemented/0025-install-peer-deps.md

npm v7ではpeerDependenciesの挙動が変更されました。npm v6以前では、peerDependenciesはあくまでユーザーに対するヒントでしかありません。パッケージの依存関係を解決したり、インストールしたりという機能はありませんでした。v7から、peerDependenciesに指定したパッケージを自動でインストールするようになりました。

その結果、npm v7はpeerDependenciesのセマンティックバージョニングを厳密に解釈するようになりました。dependenciesやdevDependenciesに、peerDependenciesと矛盾するバージョンのパッケージがインストールされていると、npm v7は依存関係の解決に失敗したとしてインストール時にエラーを返します。

node.jsのv12やv14のテスト環境でこの問題が発生しなかったのは、npmのv6を使用しているためです。

解決方法

解決方法は以下の2つです。

上流モジュールの修正

もしあなたが上流モジュールを修正できる場合、上流モジュールのpeerDependenciesフィールドを修正しましょう。

今回のエラーは自作した上流モジュールに、以下のpeerDependenciesを設定していたため引き起こされました。

"peerDependencies": {
  "jest": "^26.4.2",
}

この設定を残したまま、下流リポジトリのjestをv27に更新すると、依存関係の解決に失敗してエラーが返ります。

"peerDependencies": {
  "jest": ">=26.4.2 <28.0.0"
}

上流モジュールがjest v27で正常に動作するかテストし、バージョンの範囲を広げます。

--legacy-peer-depsオプション

上流モジュールの修正が不可能な場合、npm installコマンドに--legacy-peer-depsオプションをつけましょう。このオプションはpeerDependenciesの依存解決をv6以前と同じ処理にします。

Travis CIの場合、travis.ymlのinstallオプションを省略すると、デフォルトnpm installが挿入されます。オプションをつける場合は、installを明示的に指定する必要があります。

▼travis.yml

install:
    - npm install --legacy-peer-deps

以上、ありがとうございました。