피어가 해결되는 방법

pnpm의 가장 좋은 기능 중 하나는 한 프로젝트에서 패키지의 특정 버전이 항상 하나의 의존성 세트를 갖는다는 것입니다. 하지만 이 규칙에는 한 가지 예외가 있습니다 - peer dependencies가 있는 패키지입니다.

피어 의존성은 그들의 부모와 동일한 버전을 공유하기 때문에 의존성 그래프에서 더 높게 설치된 의존성으로부터 해결됩니다. 즉, foo@1.0.0에 두 개의 피어(bar@^1과 baz@^1)가 있는 경우, 같은 프로젝트에서 여러 다른 종속성 세트를 가질 수 있습니다.

- foo-parent-1
  - bar@1.0.0
  - baz@1.0.0
  - foo@1.0.0
- foo-parent-2
  - bar@1.0.0
  - baz@1.1.0
  - foo@1.0.0

위의 예시에서 foo@1.0.0은 foo-parent-1과 foo-parent-2를 위해 설치됩니다. 두 패키지 모두 bar와 baz도 가지고 있지만, 서로 다른 버전의 baz에 종속됩니다. 그 결과 foo@1.0.0은 두 가지 다른 종속성 세트를 갖게 됩니다: 하나는 baz@1.0.0, 다른 하나는 baz@1.1.0입니다. 이러한 사용 사례를 지원하기 위해 pnpm은 서로 다른 종속성 세트의 수만큼 foo@1.0.0을 하드 링크해야 합니다.

일반적으로 패키지에 피어 종속성이 없으면, 다음과 같이 해당 종속성의 심볼릭 링크 옆에 있는 node_modules 폴더에 하드 링크됩니다:

node_modules
└── .pnpm
    ├── foo@1.0.0
    │   └── node_modules
    │       ├── foo
    │       ├── qux   -> ../../qux@1.0.0/node_modules/qux
    │       └── plugh -> ../../plugh@1.0.0/node_modules/plugh
    ├── qux@1.0.0
    ├── plugh@1.0.0

그러나 foo에 피어 종속성이 있으면 여러 종속성 세트가 있을 수 있으므로, 서로 다른 피어 종속성 해석마다 다른 세트를 생성합니다:

node_modules
└── .pnpm
    ├── foo@1.0.0_bar@1.0.0_baz@1.0.0
    │   └── node_modules
    │       ├── foo
    │       ├── bar   -> ../../bar@1.0.0/node_modules/bar
    │       ├── baz   -> ../../baz@1.0.0/node_modules/baz
    │       ├── qux   -> ../../qux@1.0.0/node_modules/qux
    │       └── plugh -> ../../plugh@1.0.0/node_modules/plugh
    ├── foo@1.0.0_bar@1.0.0_baz@1.1.0
    │   └── node_modules
    │       ├── foo
    │       ├── bar   -> ../../bar@1.0.0/node_modules/bar
    │       ├── baz   -> ../../baz@1.1.0/node_modules/baz
    │       ├── qux   -> ../../qux@1.0.0/node_modules/qux
    │       └── plugh -> ../../plugh@1.0.0/node_modules/plugh
    ├── bar@1.0.0
    ├── baz@1.0.0
    ├── baz@1.1.0
    ├── qux@1.0.0
    ├── plugh@1.0.0

We create symlinks either to the foo that is inside foo@1.0.0_bar@1.0.0_baz@1.0.0 or to the one in foo@1.0.0_bar@1.0.0_baz@1.1.0. 결과적으로 Node.js 모듈 resolver는 올바른 피어를 찾습니다.

패키지에 피어 종속성은 없지만 그래프의 상위에서 해석되는 피어를 가진 종속성이 있는 경우, 해당 전이적 패키지는 프로젝트에서 서로 다른 종속성 세트로 나타날 수 있습니다. 예를 들어, 단일 종속성 b@1.0.0을 가진 패키지 a@1.0.0이 있습니다. b@1.0.0에는 피어 종속성 c@^1이 있습니다. a@1.0.0은 b@1.0.0의 피어를 절대 해석하지 않으므로, b@1.0.0의 피어에도 종속하게 됩니다.

다음은 node_modules에서 해당 구조가 어떻게 보이는지 보여줍니다. 이 예시에서 a@1.0.0은 프로젝트의 node_modules에 두 번 나타나야 합니다 - 한 번은 c@1.0.0으로, 또 한 번은 c@1.1.0으로 해석됩니다.

node_modules
└── .pnpm
    ├── a@1.0.0_c@1.0.0
    │   └── node_modules
    │       ├── a
    │       └── b -> ../../b@1.0.0_c@1.0.0/node_modules/b
    ├── a@1.0.0_c@1.1.0
    │   └── node_modules
    │       ├── a
    │       └── b -> ../../b@1.0.0_c@1.1.0/node_modules/b
    ├── b@1.0.0_c@1.0.0
    │   └── node_modules
    │       ├── b
    │       └── c -> ../../c@1.0.0/node_modules/c
    ├── b@1.0.0_c@1.1.0
    │   └── node_modules
    │       ├── b
    │       └── c -> ../../c@1.1.0/node_modules/c
    ├── c@1.0.0
    ├── c@1.1.0