Javascript
開発環境設定
Pnpm: パッケージマネージャ

Install pnpm: 次世代のJSパッケージマネージャ、npmやyarnより2倍速い

JSパッケージ・マネージャー・トリオの弟分、pnpmを紹介する。 pnpmがnpmやyarnよりも高速に動作することを説明し、インストール方法や使い方を整理してみる。

1. pnpmの紹介

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の動作説明

必要なパッケージのバージョンが異なる場合、CAS リポジトリはこれを効率的に処理します。 バージョンごとに変更されたファイルだけをダウンロードして保存します。 これはDockerの依存関係管理アルゴリズムに似ています。

JS/TSの開発では、どんなプロジェクトでも重複したパッケージや、同時に複数のパッケージに依存しているパッケージを見つけやすい。 このような場合、pnpmによって節約されるディスク容量は大きなものになります。

pnpm progress log

上記は、いくつかのパッケージをpnpmでインストールしたときに出力される進捗ログです。最後の行の各数値の意味は

  • resolved : 必要なパッケージの総数
  • reused : resolved のうち CAS リポジトリにインストール済みのパッケージの数
  • downloaded : resolved のうち、新しくダウンロードしたパッケージの数。
  • added : 結果としてプロジェクトに追加されたパッケージの数。

上記の場合、新たにダウンロードするパッケージがないため、ディスクやネットワークを使用することがなく、インストール時間が短縮されるはずである。 CAS リポジトリにインストールされているパッケージをオフラインでプロジェクトに追加することもできます。

2.2. 依存パッケージのインストールの高速化

pnpm が依存パッケージのインストールを高速化できる最初の理由は、前述の CAS リポジトリです。 まず第一に、すでにリポジトリにあるパッケージを再インストールすることがなく、バージョンアップ時にダウンロードするファイルが大幅に減るので、速くなるのは当然です。

第二に、pnpm はパッケージの並列インストールをサポートしています。

JSのパッケージマネージャは、依存関係のインストールを3段階のプロセスで行います: [必要な依存関係の特定 -> ダウンロード -> パッケージのコンパイル]。 npmの場合、すべてのパッケージは次のステップに進む前に1つのステップを完了しなければなりません。 pnpmはパッケージ同士が干渉しないことを前提としているので、各パッケージは別々にステップ3を行うことができます。

下のグラフの横軸は時間、縦軸は各パッケージです。 すべてのパッケージが前のステップを終了していなくても、最初に終了したパッケージが次のステップに進むので、インストールプロセス全体が速くなります。

pnpmパッケージの並列インストール

pnpmパッケージの並列インストール例 (source (opens in a new tab))

2.3. よりタイトで効率的な node_modules ディレクトリ

上記の 3 つの依存関係インストールステップの最初のステップである、必要な依存 関係の特定は、依存関係の解決と呼ばれます。 このステップでは、パッケージマネージャは、このプロジェクトが使用するパッケージと、それらが依存するパッケージを追跡し、それらをすべてインストールする計画を立てます。

npmのバージョン2までは、この段階ですべてのパッケージをインストールしていましたが、これには2つの問題がありました。

まず、パッケージAパッケージBの両方がパッケージCを依存関係として持っていた場合、重複しているにもかかわらず2回インストールされる。 これは不必要な重複である。

第二に、各パッケージの node_modules ディレクトリは尻尾を巻いてインストールされた。 これは nested 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 以下のすべてのパッケージをインストールする方法です。

flat 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

実行結果: npmでpnpmをインストール

Corepack for Node.jsを使ってインストールすることも可能だが、これはまだ実験的なものなので割愛する。

5. 使い方

使い方は npm や yarn とほぼ同じです。

5.1. プロジェクト内のパッケージ管理

  • pnpm init : pnpmで管理する新しいプロジェクトを開始する。 pnpm initの例

  • pnpm install : プロジェクトの依存パッケージのビルド済みリストをインストールする。 pnpm installの例

  • pnpm add [パッケージ名]: 1つ以上の新しいパッケージをプロジェクトに追加します。 pnpm add の例

  • pnpm exec [パッケージ名]: プロジェクトにインストールしたパッケージをコマンドラインから実行します。 pnpm execの例

  • pnpm run [script_name] : プロジェクトで指定したnpmスクリプトを実行します。 pnpm run example pnpm run dev result

5.2. グローバルパッケージ管理

  • pnpm dlx [パッケージ名] : パッケージをインストールせずに、一時的にダウンロードして実行します。 phpm dlx の例

  • pnpm create [package_name] : create をサポートしているパッケージのスターターキットを使って新しいプロジェクトを作成します。 pnpm createの例

  • pnpm add -g [パッケージ名] : グローバルパッケージを追加します。 pnpm add -g 例

  • pnpm list -g : インストールされている全てのグローバルパッケージを一覧表示する。 pnpm list -g 例.

その他のコマンドは pnpm とタイプする。

pnpmコマンドリスト

copyright for Javascript pnpm install

© 2023 All rights reserved.