各 lockfile は依存グラフに再構成されます。npm では各エントリの node_modules パスがインストール位置を表し、依存の解決は Node と同じ規則で行われます。まず自分の node_modules を探し、見つからなければルートに向かって上へ辿ります。pnpm のグラフは元から平坦で、各インストール実体は名前とバージョンをキーとし、依存は他のキーを指します。2 つのグラフはパッケージ名で比較され、各バージョン変更は major/minor/patch/prerelease に分類されます。各変更についてルートから幅優先探索を行い、そのパッケージへ至る最短の依存チェーンを求めます。チェーン上で 2 つの lockfile 間でバージョンが異なる最初のノードが、その変更を持ち込んだものです。
major 更新は先頭のバージョン番号が変わった更新を示します。慣例上、破壊的変更が許される唯一のレベルなので、間接依存であっても確認する価値があります。新規の間接依存は、誰も package.json で宣言していないのに現れたパッケージで、他の依存を通じて入ってきたものです。N バージョン共存は、リゾルバがすべての範囲を 1 つのコピーで満たせず、同じパッケージが複数回インストールされていることを意味します。多くは無害ですが、状態の重複や instanceof の不一致の原因になることがあります。取得元の変更は tarball の取得先レジストリが変わったことを示し、マージ前に確認する価値があります。
lockfile にはプライベートレジストリの URL や内部パッケージ名が含まれることがよくあります。解析、比較、チェーン追跡はすべてブラウザ内で実行され、アップロード・保存・分析送信は一切行いません。貼り付け時にネットワークタブを開けば確認できます。
このテーマに関するよくある疑問と回答をまとめました。
左に古い lockfile、右に新しい lockfile を貼り付けます。プルリクエストでは base ブランチと head ブランチのファイルをそれぞれ開くか、git show main:package-lock.json と git show HEAD:package-lock.json を使います。ローカルのインストールで lockfile が変わった場合は、git show HEAD:package-lock.json が変更前、作業ツリーのファイルが変更後です。両側は同じ種類の lockfile である必要があります。npm には npm、pnpm には pnpm です。
直接依存は package.json で宣言したものです。間接依存は lockfile にあるそれ以外のすべてで、直接依存やその依存が必要とするために存在します。lockfile の大半は間接依存なので、package.json の 1 行の変更が数百行の変更を生むことがあります。表の direct ラベルは自分で宣言したパッケージを示し、ラベルのないものはいずれかのチェーンを通じて入ってきたもので、チェーン表示がどれかを示します。
npm の package-lock.json(lockfileVersion 1、2、3)と pnpm-lock.yaml(lockfileVersion 5、6、9)に対応しています。npm-shrinkwrap.json は package-lock.json と同じ形式なので利用できます。yarn.lock(旧来の v1 テキスト形式と Berry の YAML 形式のどちらも)は検出されますが未対応で、推測せずにその旨を表示します。なお npm lockfileVersion 1 にはルートの依存リストがないため、direct ラベルは package.json ではなくトップレベルの位置から推定しています。
git diff はパッケージではなく行を見ます。1 つのパッケージの更新は version、resolved、integrity の各行と、それを参照するすべてのエントリに触れるため、同じ変更がファイル全体に散らばります。GitHub の依存関係ビューは追加・削除・変更されたパッケージを列挙しますが、GitHub 上でしか使えず、平坦な一覧に過ぎません。rimraf の更新が原因で minimatch が major を跨いだ、といったことは教えてくれません。このツールは貼り付けられる任意の 2 つの lockfile に対して、変更をパッケージ単位にまとめ、変更幅を分類し、それを説明するチェーンを表示します。
変更された各パッケージについて、変更後の lockfile(削除の場合は変更前)をルートから外側に向かって探索し、そのパッケージに到達する最短チェーンを保持します。次に最短チェーンをルートから辿り、もう一方の lockfile とバージョンが異なる最初のノードで止まります。そのノードが原因として報告されます。例えば「rimraf が 3.0.2 から 6.0.1 に変わったことによる」といった具合です。パッケージが直接宣言されている場合、原因は package.json そのものです。どのノードも変わっていなければ、リゾルバの再解決や重複排除による配置変更の可能性が高く、ツールは原因を作り出さずにその旨を表示します。
それ自体はバグではありません。2 つの依存が要求する範囲を単一のバージョンで満たせない場合、パッケージマネージャは両方をインストールし、一方をネストして配置します。lockfile は各コピーを個別に記録し、このツールはそれを「N バージョン共存」として表示します。これは正常で、多くの場合無害です。そのパッケージが共有状態を持つ、instanceof で検査されるクラスをエクスポートする、あるいは重複がバンドルサイズに影響するほど大きい場合には対処する価値があります。チェーンを見れば、どの依存が古い範囲を要求しているか分かります。