pnp
Plug'n'Playを勝手に雑に翻訳したやつ
2018年9月に発表された Plug'n'Play は、Nodeの新しい革新的なインストール戦略です。他の言語の先行作品(例えば、PHPのautoload)に基づいて、ほぼ完全に後方互換性のある方法で、通常のcommonjsのrequireワークフローの上に構築される興味深い特徴を示しています。
node_modules
が抱える問題
yarn install
を実行すると、Yarnはnode_modules
ディレクトリを生成し、Nodeは内蔵されているNode Resolution Algorithmのおかげでそれを解決することができました。
このコンテキストでは、Nodeは「パッケージ」が何であるかを知る必要はありません。「このファイルはここに存在するか?ない?では、親のnode_modules
を見てみましょう。このファイルはここにありますか?まだない?残念...」と、適切なファイルが見つかるまで繰り返します。
このプロセスは非常に非効率的でしたが、それには多くの理由があります。
-
node_modules
ディレクトリには、通常、膨大な量のファイルが格納されています。それらを生成することで、yarn install
を実行する時間の70%以上を占めることになります。既にインストールされたモジュールがあっても、パッケージマネージャは既存のnode_modules
と本来あるべきものとの差分を取らなければならないので、これらは利用されることはありません。 -
node_modules
生成はIO負荷が高い作業なため、パッケージマネージャは単純なファイルコピーより操作を最適化するための余裕がありませんでした。また可能な限りハードリンクやコピーオンライトを使用することができたとしても、ディスク操作のための大量のシステムコールを実行する前に、ファイルシステムの現在の状態をdiffする必要がありました。 -
Nodeにはパッケージの概念がなかったので、ファイルがアクセスされることを意図しているのか、それとも単なる巻き上げ(hoisting)によって利用可能になっているのかがわかりませんでした。あなたが書いたコードが、ある日の開発では動いていたのに、本番では package.json に依存関係の一つをリストアップし忘れたために壊れてしまった、ということもあり得ます。
-
実行の時も、Nodeは依存解決のために必要なファイルをどこから読み込むかを知るために、statやreaddirを何度も呼び出さなければなりませんでした。これは非常に無駄なことで、Nodeアプリケーションの起動に非常に時間がかかる原因の一つでした。
-
最後に、
node_modules
フォルダの設計は、パッケージマネージャが適切にパッケージの重複を排除することができないという点で、実用的ではありませんでした。ツリーのレイアウトを最適化するためにいくつかのアルゴリズムを採用することができましたが、いくつかの特定のパターンを最適化することができませんでした。そのため、ディスク使用量が必要以上に多くなるだけでなく、一部のパッケージがメモリ上で何度もインスタンス化されてしまいます。
node_modules
Fixing 考えてみると、Yarnはすでにあなたの依存関係ツリーについて知るべきことをすべて知っていて、あなたのためにディスクにインストールまでしてくれます。
そこで疑問に思うのは、なぜパッケージがどこにあるかを探すのがNodeの役目なのか?
ということです。
なぜパッケージマネージャの仕事は、インタプリタにディスク上のパッケージの場所を知らせ、パッケージYによるパッケージXへのrequireコールはバージョンVとして解決されること
を意味するのではないのでしょうか?
Plug'n'Playは、このような仮定のもとに作られました。
このpnpモード(Yarn 2.0以降のデフォルト)では、Yarnは通常のnode_modules
の代わりに、単一の.pnp.cjs
ファイルを生成します。
この .pnp.cjs
ファイルには、インストールされるパッケージのソースコードの代わりに、パッケージ名とバージョンをディスク上の位置と紐づけるマップや、パッケージ名とバージョンを依存関係のリストに紐づけるマップなどが含まれています。
これらの静的に生成されたテーブルを巧妙に利用することで、このファイルがあなたの環境で読み込まれている限り(詳しくは次のセクションで)、Yarnは依存関係ツリーの一部である限り、アクセスする必要のあるパッケージがどこにあるかをNodeに即座に伝えることができます。
このpnpモードのアプローチにはいくつか利点があります。
-
何万ものテキストファイルを生成するのではなく、1つのテキストファイルを生成するだけなので、インストールはほぼ瞬時に完了します。ボトルネックとなるのは、ディスクの性能ではなく、プロジェクト内の依存関係の数です。
-
失敗しがちなIO操作が減るため、インストールの安定性と信頼性が向上します。(特にWindowsでは、バッチでファイルを書き込んだり削除したりすると、Windows Defenderなどとの様々な意図しない相互作用が発生する可能性があります)
-
依存関係ツリーの完全な最適化(別名、perfect hoisting)と、予測可能なパッケージのインスタンス化をしてくれます
-
生成された
.pnp.cjs
ファイルは、ゼロインストールの取り組みの一環として、あなたのリポジトリにコミットすることができ、そもそもyarn install
を実行する必要がなくなります。 -
アプリケーションの起動が速くなりました。なぜならば、Nodeの依存解決時に以前のようにファイルシステム階層を反復する必要がないからです。(近いうちに全く必要なくなるでしょう!)