🐻

JavaScriptパッケージ管理ツール「pnpm」の紹介

2023/12/25に公開

こんにちは、クラウドエース SRE ディビジョン所属の荒木です。
この記事では、JavaScript のパッケージ管理ツール「pnpm」についてご紹介します。

pnpm とは

pnpm は Performant NPM を略したもので、JavaScript のパッケージ管理ツールです。npm や yarn と似ていますが、主にディスク容量の節約インストール速度の向上node_modules の厳格さに焦点を当てています。

alt

これは pnpm の公式サイトのトップをスクリーンショットしたものなのですが、ページリロードのたびに "p"、"n"、"p"、"m" の大文字小文字がランダムで入れ替わるようになっています。
この仕様が原因で、「PnPm が正式名称だ!」「いや、pnpM が正しい!」といった論争が起きたり起きなかったりします。

npm と比較して pnpm が優れている点

ストレージが効率的

npm と pnpm のストレージの使い方には大きな違いがあります。

npm では、同じ依存関係を使う複数のプロジェクトが、それぞれにコピーを保存します。例えば、hoge という 1 MB のパッケージを使用するプロジェクトを 100 個ローカル環境に用意すると、 100 回 hoge がコピーされるため、コンピューターのストレージを 100 MB 使用します。

一方で pnpm では、これらの依存関係を一箇所に集約し、各プロジェクトはその場所へのリンクを作成することでディスク容量を節約します。hoge という 1 MB パッケージを使用するプロジェクトを 100 個ローカル環境に用意した場合、pnpm のストアに保存されている hoge へのリンクが 100 個作成されるだけで hoge 自体は増えないため、コンピュータのストレージは 1 MB のみ使用されます。

alt
(画像引用:https://medium.com/@jesuva/node-package-managers-cab41450c2da

この図では、LICENSE.md を project_1 と project_2 で共有しています。このとき、それぞれのプロジェクトの node_modules に実際に配置されているのは LICENSE.md へのリンクです。このように、同じパッケージを繰り返し保存せずプロジェクト間で共有することで、ディスク容量を節約しています。

インストールが高速

JavaScript のパッケージ管理ツールでは、次の 3 ステップでインストールが行われます。

  1. 依存関係の解決(Resolving)
  2. ディレクトリ構造の計算(Fetching)
  3. 依存関係をリンク(Linking)

従来のパッケージ管理ツールは、すべてのパッケージでこれらの各プロセスを共有していました。

どういうことかイメージしづらいので hogefugapiyo という 3 つのパッケージをインストールする場合を考えてみます。もし、hogefuga の Resolving が終わっても、piyo の Resolving が終わるまでは hogefuga は次のステージの Fetching に進むことができません。つまり、処理に時間のかかるパッケージがあると、他のパッケージの処理が待ち状態になってしまうということです。

alt
(画像引用:https://pbs.twimg.com/media/Eg_YM2lXcAEZn3B?format=png&name=large

pnpm では、下の図のようにパッケージごとに個別にステージを実行することで、待ち状態を生み出さないようにし、インストールのプロセスを効率化しています。

alt
(画像引用:https://pbs.twimg.com/media/Eg_YM29XcAIJMIn?format=jpg&name=large

この説明の参考にした pnpm 公式の X(旧 Twitter)のポストを載せておきます。
https://twitter.com/pnpmjs/status/1301496539265851392

node_modules が厳格

npm を使う場合、プロジェクトの package.json に記載されていないパッケージにアクセスできることがあります。例えば、express というパッケージをインストールしたとき、node_modules フォルダには以下のように関連パッケージが配置されます。

$ cd node_modules
$ ls -1

accepts
array-flatten
content-disposition
content-type
cookie
cookie-signature
debug
depd
destroy
ee-first
encodeurl
escape-html
etag
express
finalhandler
forwarded
fresh
http-errors
inherits
ipaddr.js
media-typer
merge-descriptors
methods
mime
mime-db
mime-types
ms
negotiator
on-finished
parseurl
path-to-regexp
proxy-addr
qs
range-parser
send
serve-static
setprototypeof
statuses
type-is
unpipe
utils-merge
vary

一方で pnpm では、express パッケージのシンボリックリンクのみが配置されます。これにより、プロジェクトの package.json に記載されていないパッケージにはアクセスできなくなり、不要な依存関係による問題やバグの発生を防ぐことができます。

詳しくは公式ドキュメントの Symlinked `node_modules` structure を参照してください。
https://pnpm.io/symlinked-node-modules-structure

機能の比較

pnpm は npm や yarn と比較して、機能面で以下のような違いがあります。

機能 pnpm Yarn npm
ワークスペースのサポート ✔️ ✔️ ✔️
分離されたnode_modules ✔️ - デフォルト ✔️ ✔️
ホイストされた node_modules ✔️ ✔️ ✔️ - デフォルト
Peerの自動インストール ✔️ ✔️
Plug'n'Play ✔️ ✔️ - デフォルト
Zero-Installs ✔️
依存関係のパッチ ✔️ ✔️
Node.jsバージョンの管理 ✔️
ロックファイル ✔️ - pnpm-lock.yaml ✔️ - yarn.lock ✔️ - package-lock.json
オーバーライドのサポート ✔️ ✔️ - resolutionsによって ✔️
連想ストレージ ✔️
パッケージの直接実行 ✔️ - pnpm dlx によって ✔️ - yarn dlx によって ✔️ - npx によって
副作用キャッシュ ✔️
ライセンスの一覧表示 ✔️ - pnpm licenses listによって ✔️ - プラグインによって

(引用:https://pnpm.io/ja/feature-comparison

まとめ

pnpm は従来のパッケージ管理ツールと比較して、以下のメリットがありました。

  • ストレージが効率的
  • パッケージのインストールが高速
  • 依存関係によるバグを防ぎやすい

npm に不満を持っている方は、ぜひ pnpm を試してみてください。

ただし、npm や yarn と比較してネット上に情報が少なく、ある程度自分でトラブルに対処しなくてはならないという点に注意が必要かなと思いました。

Discussion