npm installでのCould not resolve dependencyエラーと--legacy-peer-depsについて
はじめに
npm install
を実行したときに以下のような依存解決エラーが出ることがあります。このエラーは--legacy-peer-deps
オプションを付与することで解消できるのですが、エラーの具体的な意味や本オプションの内容をちゃんと理解してこなかったので、すこし調べてみました。
npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR!
npm ERR! While resolving: react-alert@7.0.3
npm ERR! Found: react@18.2.0
npm ERR! node_modules/react
npm ERR! react@"^18.2.0" from the root project
npm ERR! peerOptional react@"^16.8.0 || ^17.0.0 || ^18.0.0" from @apollo/client@3.7.2
npm ERR! node_modules/@apollo/client
npm ERR! @apollo/client@"^3.7.2" from the root project
npm ERR! 25 more (@emotion/react, @emotion/styled, ...)
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react@"^16.8.1 || ^17" from react-alert@7.0.3
npm ERR! node_modules/react-alert
npm ERR! react-alert@"^7.0.3" from the root project
npm ERR!
npm ERR! Conflicting peer dependency: react@17.0.2
npm ERR! node_modules/react
npm ERR! peer react@"^16.8.1 || ^17" from react-alert@7.0.3
npm ERR! node_modules/react-alert
npm ERR! react-alert@"^7.0.3" 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.
Could not resolve dependencyエラーとは
まずはこのエラーの内容を確認します。"Could not resolve dependency"というエラーメッセージから分かるように、ライブラリの依存関係を解決できなかった、つまりライブラリ同士でなんらかの不整合が発生している、というのが本エラーの概要です。
具体的にみてみる
npm ERR! node_modules/react
npm ERR! react@"^18.2.0" from the root project
npm ERR! peerOptional react@"^16.8.0 || ^17.0.0 || ^18.0.0" from @apollo/client@3.7.2
ここに注目すると、react
ライブラリの依存解決に失敗しているようです。
プロジェクトルートではreact@^18.2.0
を指定しているところ、@apollo/client@3.7.2
のpeerOptionalではreact@"^16.8.0 || ^17.0.0 || ^18.0.0"
が指定されている、と言っていますね。
peerOptionalってなに?という疑問は後回しにして、@
以降のバージョン指定の部分を見ていきましょう。
バージョン指定を確認する
バージョンはmainバージョン.minorバージョン.patchバージョン
という形式で表されますが、エラーメッセージでは先頭に^(キャレット)
がついています。これは、ゼロ以外の一番左の数値が同じバージョンを許容するという印です。
具体的には以下のようになります。
-
^1.0.0
:1.0.0
以上、2.0.0
未満 -
^0.1.1
:0.1.1
以上、0.2.0
未満
他に~(チルダ)
を利用した指定方法もありますが、ここでは割愛します。
では上記を踏まえて再度エラーを確認してみましょう。
-
react@"^18.2.0"
:18.2.0
以上19.0.0
未満 -
react@"^16.8.0 || ^17.0.0 || ^18.0.0"
:16.8.0
以上19.0.0
未満
この部分の依存関係が矛盾しているのかなと思っていたのですが、18.2.0
などは両方の指定を満たすことができます。つまりエラーの原因は他にあるということですね。
npm ERR! Could not resolve dependency:
npm ERR! peer react@"^16.8.1 || ^17" from react-alert@7.0.3
エラーメッセージをみると、さきほどと似たエラーがあります。ここではreact-alert@7.0.3
のpeerはreact@"^16.8.1 || ^17"
である、と書かれています。
react@"^16.8.1 || ^17"
: 16.8.1
以上18.0.0
未満です。ここまでで指定されたReactの依存バージョンをまとめると以下のようになります。
- プロジェクトルート:
18.2.0
以上19.0.0
未満 -
@apollo/client@3.7.2
:16.8.0
以上19.0.0
未満 -
react-alert@7.0.3
:16.8.1
以上18.0.0
未満
これらをすべて同時に満たすバージョンは存在しないため、依存解決ができずにエラーが表示されたんですね。
dependencyとpeerDependency
エラーの内容は分かりましたが、エラーメッセージに登場したpeerOptional
とかpeer
とはなんでしょうか。これらはいずれもpeerDependency
というもので、dependency
とはちょっと違う概念です。
dependencyとは
まずdependencyとは、そのNPMモジュールが動作するのに必要なモジュールやライブラリです。たとえばアプリケーションのUI構築にReactを利用するとき、npm install react
を実行しますが、これはアプリケーションのdependencyにreact
を追加することと同義です。Reactがなければそのアプリケーションは動かないため、そのアプリケーションはReactに対してdependencyをもっています。
peerDependencyとは
peerDependencyは、そのNPMモジュールが一緒に利用することを想定しているモジュールやライブラリです。たとえば車とタイヤを考えると、タイヤは車にとりつけて使われる前提で作られています。このとき、タイヤにとってのpeerDependency
は車になります。車がなくてもタイヤとしては存在できるけど、想定される使い方をするには車にとりつけてください、というスタンスです。
以上を踏まえてもういちどエラーにおけるReactの依存関係をまとめると以下になります。
- プロジェクトルート:
18.2.0
以上19.0.0
未満が必ず必要 -
@apollo/client@3.7.2
:16.8.0
以上19.0.0
未満で動作する前提 -
react-alert@7.0.3
:16.8.1
以上18.0.0
未満で動作する前提
--legacy-peer-depsとは
依存解決エラーを解消するひとつの手段として--legacy-peer-deps
オプションがありますが、これはpeerDependencyの依存解決をせずにインストールを続行せよというオプションです。注意したいのは、これがNPMに「追加でなにかをさせる」のではなく「処理(=peerDependencyの解決)をスキップさせる」オプションであるという点です。
この一見分かりづらい挙動にはnpm
の歴史的背景が関係しています。
v6以前は実行していなかったpeerDependencyの解決処理がv7で追加され、デフォルトで有効になりました。つまり--legacy-peer-deps
オプションはv7以降のnpm
において、v6と同じ動作をするように矯正するものです。
v6以前ではpeerDependencyの依存解決はしないため、バージョンの矛盾が発生せず(というよりチェックされず)npm install
が正常終了する、という挙動になります。
結局、--legacy-peer-depsは使っていいの?
エラーメッセージにも"accept an incorrect (and potentially broken) dependency resolution."とある通り、バージョンの不整合をノーチェックで通しているだけなので、目先のエラーは消えても実際にはバージョン差分による不具合などが発生する可能性があります。
基本的には利用するライブラリのバージョンを変えたり別ライブラリを検討したりして整合性を合わせる方法を探し、どうしようもない場合に--legacy-peer-deps
を使うというのが良さそうです。
任意のライブラリのpeerDependencyを調べるには以下コマンドを利用するようです。
npm info name-of-module peerDependencies
参考:
Discussion