【大きいプロジェクトやモノレポ向き?】pnpmの特徴とnpm、yarnとの比較

Node.js のパッケージ管理ツールとしてはnpm が有名ですが、最近はpnpm という選択肢も出てきています。本記事では、それらの違いを比較しつつ、主にpnpm について紹介します。

目次

各パッケージ管理ツールの概要

npm

npm はNode.js のデフォルトのパッケージ管理ツールです。Node.js をインストールする際には、通常は一緒にインストールされるはずです。

ここで、npm のようなパッケージ管理ツールの役目としてはその名の通りパッケージを管理するのですが、ここでパッケージとは、例えばアプリケーションを作るときに、データベースとやり取りするためのコードをはじめから作るのではなく、誰かが前もって作っていたソースコードをインストールして、自分のアプリの中で呼び出してそれを使うようなときに使うソースコードを指します。そうしたソースコードはプログラムの中で使うことを意図されていて、誰もが簡単に使えるように作成・メンテナンスされていますが、それをパッケージと呼びます。Javascript を触ったことがある方は、import やrequire を使ってモジュールを呼び出したことがあると思いますが、npm からインストールして使えるモジュール=パッケージと思って一般的には差支えないです。もう少し詳しい説明はコチラ

また、パッケージ"管理" なので、単にパッケージをインストールするだけではなく、アンインストールや更新、バージョンの管理などを実行できます。

特にこのバージョン管理というのが大きな強みになります。ある古い参考書を使ってコードを写経しているときに、その中で使われているパッケージのバージョンが異なりうまく動かない、ということは多くの方が経験しているかもしれませんが、そのアプリの依存関係でもあるパッケージがまとめられたpackage.json というファイルを使って、そのアプリで動作が確認されているパッケージをまとめてインストールすることで、そのような環境差分の失敗のリスクを削減することができます。

npx

厳密にはパッケージ管理ツールではないですが、npm と似ているコマンドとしてnpx があります。過去の記事にまとめられていますので、詳細は以下を参照してください。

yarn

yarn もnpm と同様Node.js で利用できるパッケージ管理ツールです。npm と比較して、パッケージの高速インストールやセキュリティに焦点を当てて設計されています。npm との互換性もあります。

速度についてはいろいろと議論がありますが、この後ベンチマークを紹介します。

pnpm

pnpm もyarn と同じく、パッケージインストールの高速化が1つのメリットではありますが、ディスクスペースの効率化や厳格なパッケージ管理などのメリットも提供します。

ディスクスペースの効率化方法として、npm のようにプロジェクトごとにパッケージの本体をインストールするのではなく、ディスク上のあるフォルダの中にまとめて配置され、各プロジェクトからハードリンクが張られるため、ディスク容量を大きく節約できます。なお、ハードリンクとは、ディスク上に保存されている実データ(データブロック)を管理するためのメタデータであるinode に対してのリンクであり、実データをコピーするわけではないので、容量を節約できるというわけです。

https://pnpm.io/motivation#creating-a-non-flat-node_modules-directory

また、厳格なパッケージ管理の例として、node_modules の構造が大きく異なります。npm の場合はパッケージがホストされているフォルダはすべてルートに配置されるため、パッケージごとの依存関係が明確ではありません。npm でexpress パッケージをインストールした場合、node_modules 配下にはexpress 以外の、例えばcookie など数多くのパッケージフォルダがホストされます。したがって、package.json と同期がとれず、package.json ファイルで定義されていないけれど、あるパッケージをインストールする際に付属してきたパッケージも普通に呼び出せることになります。このようなpackage.json の信用性の問題が潜在的なバグを生み出す可能性があります。

一応、npm にはglobal-style というオプションがあり、このようなフラットな構造のnode_modules を使わないこともできたのですが、今はDepriciated 扱いになっています。フラットでないということは、パッケージの依存関係が複雑になった場合、必然的にフォルダ構成も複雑になり、重複したパッケージのインストールが発生するなど、フラットにする以上の問題が発生します(がゆえにDepriciated になっていると思います)。

https://docs.npmjs.com/cli/v10/using-npm/config#global-style

pnpm は、インストールされたパッケージストアへのハードリンクとシンボリックリンクをうまく使ってこのような問題を回避しています。つまり、たとえばexpress をインストールすると、.pnpm 配下のexpress へのシンボリックリンクが張られ、express が依存しているcookie も同様にシンボリックリンクが張られることで、各パッケージごとの依存関係をnode_modules 内で表現しつつも、シンプルなフォルダ構成になっていると言えます。下記でnode_modules 配下にはexpress しか見えないことに注目してください。

https://pnpm.io/motivation#creating-a-non-flat-node_modules-directory

ちなみに、各パッケージ管理ツールのパッケージインストール速度の目安として、pnpm の公式サイトにベンチマーク情報があります。

https://pnpm.io/benchmarks

これだけ見るとクリーンインストール以外の場合はどれもそこまで大差ないように見えます。パッケージインストール速度だけを見てパッケージ管理ツールを選択するのは現時点ではあまり意味がないかもしれません。

どれを使うか?

pnpm の紹介記事で元も子もないですが、私はnpm をずっと使っています。何か事象が発生したときに、標準でないパッケージ管理ツールを使うことが原因である可能性を削減できるメリットの方が、高速化やディスク容量節約のメリットよりも現時点では大きいと感じているからです。Github に買収されたことで、今後もMicrosoft による継続的な投資がされるであろう安心感もあります。

ただ、プロジェクトが大きくなればなるほど、数が増えれば増えるほど、pnpm が提供するインストールの高速化、ディスク容量効率化、パッケージの厳格管理のメリットは大きくなるので、それらを天秤にかけたうえで判断すべきです。

まとめ

本記事では、最近勢いのあるpnpm を中心に、Node.js で利用できる各パッケージ管理ツールについて紹介しました。プロジェクトが大きくなりすぎて既存のパッケージ管理のシステムに課題が出てきていると感じている方はぜひ試してみてください。