メインコンテンツまでスキップ
Version: 9.x

シンボリックリンクを使用した `node_modules` の構造

情報

This article only describes how pnpm's node_modules are structured when there are no packages with peer dependencies. For the more complex scenario of dependencies with peers, see how peers are resolved.

pnpm's node_modules layout uses symbolic links to create a nested structure of dependencies.

Every file of every package inside node_modules is a hard link to the content-addressable store. Let's say you install [email protected] that depends on [email protected]. pnpm will hard link both packages to node_modules like this:

node_modules
└── .pnpm
├── [email protected]
│ └── node_modules
│ └── bar -> <store>/bar
│ ├── index.js
│ └── package.json
└── [email protected]
└── node_modules
└── foo -> <store>/foo
├── index.js
└── package.json

These are the only "real" files in node_modules. Once all the packages are hard linked to node_modules, symbolic links are created to build the nested dependency graph structure.

As you might have noticed, both packages are hard linked into a subfolder inside a node_modules folder ([email protected]/node_modules/foo). このハードリンクが必要な理由は次のとおりです。

  1. allow packages to import themselves. foo should be able to require('foo/package.json') or import * as package from "foo/package.json".
  2. avoid circular symlinks. Dependencies of packages are placed in the same folder in which the dependent packages are. For Node.js it doesn't make a difference whether dependencies are inside the package's node_modules or in any other node_modules in the parent directories.

インストールの次の段階では、依存関係同士をシンボリックリンクで結びつけます。 bar is going to be symlinked to the [email protected]/node_modules folder:

node_modules
└── .pnpm
├── [email protected]
│ └── node_modules
│ └── bar -> <store>/bar
└── [email protected]
└── node_modules
├── foo -> <store>/foo
└── bar -> ../../[email protected]/node_modules/bar

続いて、直接的な依存関係を処理します。 foo is going to be symlinked into the root node_modules folder because foo is a dependency of the project:

node_modules
├── foo -> ./.pnpm/[email protected]/node_modules/foo
└── .pnpm
├── [email protected]
│ └── node_modules
│ └── bar -> <store>/bar
└── [email protected]
└── node_modules
├── foo -> <store>/foo
└── bar -> ../../[email protected]/node_modules/bar

例としてはあまりにも簡単です。 しかし、依存 関係の数が増えても、依存関係同士のグラフ構造がどれほど深くなっても、基本的にこのような方法でレイアウトを(構造を)管理することは変わりません。

Let's add [email protected] as a dependency of bar and foo. こちらが新しい構造です。

node_modules
├── foo -> ./.pnpm/[email protected]/node_modules/foo
└── .pnpm
├── [email protected]
│ └── node_modules
│ ├── bar -> <store>/bar
│ └── qar -> ../../[email protected]/node_modules/qar
├── [email protected]
│ └── node_modules
│ ├── foo -> <store>/foo
│ ├── bar -> ../../[email protected]/node_modules/bar
│ └── qar -> ../../[email protected]/node_modules/qar
└── [email protected]
└── node_modules
└── qar -> <store>/qar

As you may see, even though the graph is deeper now (foo > bar > qar), the directory depth in the file system is still the same.

一見すると奇妙に見えるかもしれませんが、Node.jsのモジュール解決アルゴリズムと完全に互換性のある構造なのです。 When resolving modules, Node ignores symlinks, so when bar is required from [email protected]/node_modules/foo/index.js, Node does not use bar at [email protected]/node_modules/bar, but instead, bar is resolved to its real location ([email protected]/node_modules/bar). As a consequence, bar can also resolve its dependencies which are in [email protected]/node_modules.

このレイアウトの大きな利点 は、依存関係に含まれるパッケージにのみアクセスできるようになることです。 With a flattened node_modules structure, all hoisted packages are accessible. To read more about why this is an advantage, see "pnpm's strictness helps to avoid silly bugs"

\n ##callToAction##\n
\n\n'})