シンボリックリンクを使用した `node_modules` の構造
情報
この記事では、ピア依存関係のパッケージが存在しない場合、pnpm が node_modules
をどのように構成するのか説明します。 ピア依存関係を含むより複雑な状況については、ピア依存関係の解決方法を参照してください。
pnpmは入れ子になった依存関係をシンボリックリンクを使用してnode_modules
に配置します。
node_modules
に存在する全てのパッケージに含まれるそれぞれのファイルは、コンテンツストアへのハードリンクです。 依存関係にbar@1.0.0
を持つfoo@1.0.0
をインストールするところを見ていきましょう。 pnpmはnode_modules
にそれぞれのパッケージのハードリンクを作成します。
node_modules
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ └── bar -> <store>/bar
│ ├── index.js
│ └── package.json
└── foo@1.0.0
└── node_modules
└── foo -> <store>/foo
├── index.js
└── package.json
これらのファイルはnode_modules
において唯一の「実体のある」ファイルです。 すべてのパッケージについてnode_modules
にハードリンクを作成したら、入れ子になった依存関係のグラフ構造を反映するシンボリックリンクを作成します。
お気づきのとおり、どちらのパッケージもそれぞれのサブディレクトリnode_modules
へ自身のハードリンクを作成しています (foo@1.0.0/node_modules/foo
) 。 このハードリンクが必要な理由は次のとおりです。
- **パッケージが自分自身をインポートできるようにするため。**たとえばパッケージ
foo
でrequire('foo/package.json')
あるいはimport * as package from "foo/package.json"
のように記述できるようにするためです。 - **シンボリックリンクの循環参照を避けるため。**あるパッケージの依存パッケージは、同 じディレクトリ階層に並んでいます。 Node.jsの場合、依存パッケージがパッケージ自身の
node_modules
にあっても、上位階層のどこかのnode_modules
にあっても違いはありません。
インストールの次の段階では、依存関係同士をシンボリックリンクで結びつけます。 たとえば、bar
のシンボリックリンクをfoo@1.0.0/node_modules
に作成します。
node_modules
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ └── bar -> <store>/bar
└── foo@1.0.0
└── node_modules
├── foo -> <store>/foo
└── bar -> ../../bar@1.0.0/node_modules/bar