本文へ移動

フラットなnode_modulesは唯一の方法ではない

·読了時間3分

pnpmの新規ユーザーは、pnpmが作成するnode_modulesの奇妙な構造についてよく質問します。なぜフラットではないのでしょうか?すべてのサブ依存関係はどこにあるのでしょうか?

この記事の読者は、npmとYarnによって作成されたフラットなnode_modulesに既に精通していると仮定します。npm 3がv3でフラットなnode_modulesを使用し始めた理由がわからない場合は、なぜpnpmを使うべきなのか?でいくつかの前史を見つけることができます。

では、なぜpnpmのnode_modulesは普通ではないのでしょうか?2つのディレクトリを作成し、一方ではnpm add expressを、もう一方ではpnpm add expressを実行してみましょう。最初のディレクトリのnode_modulesの上部には次のようなものがあります。

.bin
accepts
array-flatten
body-parser
bytes
content-disposition
cookie-signature
cookie
debug
depd
destroy
ee-first
encodeurl
escape-html
etag
express

完全なディレクトリ構造はこちらで確認できます。

そして、これがpnpmによって作成されたnode_modulesです。

.pnpm
.modules.yaml
express

こちらで確認できます こちら

では、すべての依存関係はどこにあるのでしょうか?node_modulesには.pnpmというフォルダとexpressというシンボリックリンクしかありません。expressしかインストールしていないため、アプリケーションがアクセスする必要があるのはこのパッケージだけです。

pnpmの厳格さがなぜ良いことなのかについては、こちらで詳しく説明しています。

expressの中身を見てみましょう。

▾ node_modules
▸ .pnpm
▾ express
▸ lib
History.md
index.js
LICENSE
package.json
Readme.md
.modules.yaml

expressnode_modulesがありません!expressのすべての依存関係はどこにあるのでしょうか?

コツは、expressが単なるシンボリックリンクであることです。Node.jsは依存関係を解決する際に、実際の場所を使用するため、シンボリックリンクは保持しません。しかし、expressの実際の場所はどこにあるのでしょうか?

ここにあります: node_modules/.pnpm/express@4.17.1/node_modules/express.

わかりました。これで.pnpm/フォルダの目的がわかりました。.pnpm/はすべてのパッケージをフラットなフォルダ構造で格納するため、すべてのパッケージはこのパターンで名前付けされたフォルダにあります。

.pnpm/<name>@<version>/node_modules/<name>

これを仮想ストアディレクトリと呼びます。

このフラットな構造により、npm v2によって作成されたネストされたnode_modulesによって発生した長いパス問題を回避しますが、npm v3,4,5,6またはYarn v1によって作成されたフラットなnode_modulesとは異なり、パッケージを分離したままにします。

それでは、expressの実際の場所を見てみましょう。

  ▾ express
▸ lib
History.md
index.js
LICENSE
package.json
Readme.md

詐欺でしょうか?それでもnode_modulesがありません!pnpmのnode_modules構造の2つ目のコツは、パッケージの依存関係が、依存パッケージの実際の場所と同じディレクトリレベルにあることです。そのため、expressの依存関係は.pnpm/express@4.17.1/node_modules/express/node_modules/にはありませんが、.pnpm/express@4.17.1/node_modules/にあります。

▾ node_modules
▾ .pnpm
▸ accepts@1.3.5
▸ array-flatten@1.1.1
...
▾ express@4.16.3
▾ node_modules
▸ accepts
▸ array-flatten
▸ body-parser
▸ content-disposition
...
▸ etag
▾ express
▸ lib
History.md
index.js
LICENSE
package.json
Readme.md

expressのすべての依存関係は、node_modules/.pnpm/内の適切なディレクトリへのシンボリックリンクです。expressの依存関係を一つ上のレベルに配置することで、循環シンボリックリンクを回避できます。

ご覧のように、pnpmのnode_modules構造は最初は変わって見えますが、

  1. 完全にNode.jsと互換性があります。
  2. パッケージは依存関係とともに適切にグループ化されています。

ピア依存関係を持つパッケージについては、構造はもう少し複雑ですが、フラットなディレクトリ構造でネストを作成するためのシンボリックリンクを使用するという考え方は同じです。