Install pnpm: 次世代のJSパッケージマネージャ、npmやyarnより2倍速い
JSパッケージ・マネージャー・トリオの弟分、pnpmを紹介する。 pnpmがnpmやyarnよりも高速に動作することを説明し、インストール方法や使い方を整理してみる。
1. pnpmの紹介
pnpm 公式ホームページ: https://pnpm.io/ja/ (opens in a new tab)
pnpmは、従来のJSパッケージマネージャのパフォーマンス上の制限を克服するために設計された次世代JSパッケージマネージャです。 pnpmという名前はPerformant npmの略で、その名前から効率的なパフォーマンスのために設計されていることがわかります。
使い方は他のパッケージマネージャとほとんど同じです。
pnpmはpackage.json
ファイルに基づいて動作し、npmやyarnのほとんどすべての機能を置き換えることができる。
コマンドラインのmingはよりyarnに近いように作られています。
2. pnpmの3つの利点
pnpmを使うメリットは主に3つある。
ディスクスペースの節約、依存関係のインストールの高速化、そして node_modules
ディレクトリ構造の効率的な整理です。
ひとつずつ見ていきましょう。
2.1. ディスク使用量の削減
pnpm が同じことをより少ないディスク使用量でできる理由は、content-addressable storage (CAS) と呼ばれるセントラルリポジトリを使うからです。 pnpmでパッケージをインストールすると、すべてのパッケージがこのリポジトリに保存されます。 そして、pnpmを使うすべてのプロジェクトは、そのリポジトリから必要なパッケージを取得する。
例えば、npmで3つのReactプロジェクトを作成した場合、Reactパッケージは各プロジェクトのnode_modules
ディレクトリにインストールされる。
つまり、私のマシンには同じパッケージが3つインストールされていることになる。
しかし、pnpmを使うと、CASのリポジトリにはreactパッケージが1つだけインストールされ、3つのプロジェクトはそのパッケージを共有する。
pnpmを使ったCASの仕組みの説明:
必要なパッケージのバージョンが異なる場合、CAS リポジトリはこれを効率的に処理します。 バージョンごとに変更されたファイルだけをダウンロードして保存します。 これはDockerの依存関係管理アルゴリズムに似ています。
JS/TSの開発では、どんなプロジェクトでも重複したパッケージや、同時に複数のパッケージに依存しているパッケージを見つけやすい。 このような場合、pnpmによって節約されるディスク容量は大きなものになります。
上記は、いくつかのパッケージをpnpmでインストールしたときに出力される進捗ログです。最後の行の各数値の意味は
resolved
: 必要なパッケージの総数reused
:resolved
のうち CAS リポジトリにインストール済みのパッケージの数downloaded
:resolved
のうち、新しくダウンロードしたパッケージの数。added
: 結果としてプロジェクトに追加されたパッケージの数。
上記の場合、新たにダウンロードするパッケージがないため、ディスクやネットワークを使用することがなく、インストール時間が短縮されるはずである。 CAS リポジトリにインストールされているパッケージをオフラインでプロジェクトに追加することもできます。
2.2. 依存パッケージのインストールの高速化
pnpm が依存パッケージのインストールを高速化できる最初の理由は、前述の CAS リポジトリです。 まず第一に、すでにリポジトリにあるパッケージを再インストールすることがなく、バージョンアップ時にダウンロードするファイルが大幅に減るので、速くなるのは当然です。
第二に、pnpm はパッケージの並列インストールをサポートしています。
JSのパッケージマネージャは、依存関係のインストールを3段階のプロセスで行います: [必要な依存関係の特定 -> ダウンロード -> パッケージのコンパイル]。 npmの場合、すべてのパッケージは次のステップに進む前に1つのステップを完了しなければなりません。 pnpmはパッケージ同士が干渉しないことを前提としているので、各パッケージは別々にステップ3を行うことができます。
下のグラフの横軸は時間、縦軸は各パッケージです。 すべてのパッケージが前のステップを終了していなくても、最初に終了したパッケージが次のステップに進むので、インストールプロセス全体が速くなります。
2.3. よりタイトで効率的な node_modules ディレクトリ
上記の 3 つの依存関係インストールステップの最初のステップである、必要な依存 関係の特定は、依存関係の解決と呼ばれます。 このステップでは、パッケージマネージャは、このプロジェクトが使用するパッケージと、それらが依存するパッケージを追跡し、それらをすべてインストールする計画を立てます。
npmのバージョン2までは、この段階ですべてのパッケージをインストールしていましたが、これには2つの問題がありました。
まず、パッケージA
とパッケージB
の両方がパッケージC
を依存関係として持っていた場合、重複しているにもかかわらず2回インストールされる。
これは不必要な重複である。
第二に、各パッケージの node_modules
ディレクトリは尻尾を巻いてインストールされた。
これは nested node_modules と呼ばれ、以下のようなディレクトリ構造になっている。
▾ node_modules
▾ package A
▾ node_modules
▸ package C
▾ package B
▾ node_modules
▾ package C
▸ node_modules
npm 3+とyarnでは、この問題を解決するためにflat node_modules構造を使用しています。
これは、依存関係の深さに関係なく、プロジェクト内の node_modules
以下のすべてのパッケージをインストールする方法です。
▾ node_modules
▾ .bin
▾ accepts
▾ array-flatten
...
▾ etag
▾ express
このフラットな構造は重複インストールを防ぐが、別の問題も引き起こす。
それは、私が依存関係としてインストールしたパッケージは、たとえ package.json
によって明示的にインストールされていなくても、このプロジェクトで使用することができるということです。
例えば、私は package.json
に入れずに package A
の依存関係としてインストールした package S
のバージョン 1 を使っています、
package S
のバージョン2にアップグレードする必要がある場合はどうなるでしょうか?
package S
を追跡していないので、自動的にアップグレードすることはできず、実行時エラーになる。
PNPM はこれを防ぎます。
pnpm で作成される node_modules
内のパッケージファイルは、すべて CAS リポジトリ内の実際のファイルへのシンボリックリンクまたはハードリンクであるため、自由に再構築や最適化を行うことができる。
これにより、フラットな構造を使用することなく、パッケージの重複の問題を解決し、明示的にインストールしたパッケージのみにアクセスを制限することができます。
3. 中間組織
おわかりのように、pnpm は CAS リポジトリを使うことで重複を可能な限り排除するだけで、従来のパッケージマネージャの 2 倍以上の効率性を実現しています。 また、その過程で安定性も大幅に向上している。
では、pnpmのインストール方法と使い方を簡単に説明しよう。
インストール方法
pnpmはnpmを使ってグローバルにインストールすることを推奨します。 簡単なコマンドでインストールできます。もちろんWindowsでも使えます。
npm install -g pnpm
実行結果:
Corepack for Node.jsを使ってインストールすることも可能だが、これはまだ実験的なものなので割愛する。
5. 使い方
使い方は npm や yarn とほぼ同じです。
5.1. プロジェクト内のパッケージ管理
-
pnpm init
: pnpmで管理する新しいプロジェクトを開始する。 -
pnpm install
: プロジェクトの依存パッケージのビルド済みリストをインストールする。 -
pnpm add [パッケージ名]
: 1つ以上の新しいパッケージをプロジェクトに追加します。 -
pnpm exec [パッケージ名]
: プロジェクトにインストールしたパッケージをコマンドラインから実行します。 -
pnpm run [script_name]
: プロジェクトで指定したnpmスクリプトを実行します。
5.2. グローバルパッケージ管理
-
pnpm dlx [パッケージ名]
: パッケージをインストールせずに、一時的にダウンロードして実行します。 -
pnpm create [package_name]
: create をサポートしているパッケージのスターターキットを使って新しいプロジェクトを作成します。 -
pnpm add -g [パッケージ名]
: グローバルパッケージを追加します。 -
pnpm list -g
: インストールされている全てのグローバルパッケージを一覧表示する。.
その他のコマンドは pnpm
とタイプする。
