본문으로 건너뛰기
버전: Next

symlink된 `node_modules` 구조

info

이 기사는 피어 의존성이 있는 패키지가 없을 때 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). 이것은 다음을 위해 필요합니다:

  1. 패키지가 자기 자신에 대한 import를 허용합니다. foorequire('foo/package.json') 또는 import * as package from "foo/package.json" 를 할 수 있어야 합니다.
  2. 순환 심볼릭 링크를 피합니다. 패키지의 의존성은 의존하는 패키지와 동일한 폴더에 있습니다. Node.js의 경우, 의존성이 패키지의 node_modules 내부에 있는지 또는 상위 디렉토리의 다른 node_modules 에 이 있는지 여부로 차이를 두지 않습니다.

설치의 다음 단계는 의존성을 심볼릭 링크하는 것입니다. barfoo@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

다음으로, 직접 의존성이 처리됩니다. foo 는 루트 node_modules 폴더에 심볼릭 링크됩니다. 그 이유는 foo 가 프로젝트의 의존성이기 때문입니다.

node_modules
├── foo -> ./.pnpm/foo@1.0.0/node_modules/foo
└── .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

이것은 매우 간단한 예입니다. 그러나 이 레이아웃은 의존성 수와 의존성 그래프의 깊이에 관계없이 이 구조를 유지합니다.

barfoo의 의존성으로 qar@2.0.0 을 추가해 보겠습니다. 새로운 구조는 다음과 같습니다.

node_modules
├── foo -> ./.pnpm/foo@1.0.0/node_modules/foo
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ ├── bar -> <store>/bar
│ └── qar -> ../../qar@2.0.0/node_modules/qar
├── foo@1.0.0
│ └── node_modules
│ ├── foo -> <store>/foo
│ ├── bar -> ../../bar@1.0.0/node_modules/bar
│ └── qar -> ../../qar@2.0.0/node_modules/qar
└── qar@2.0.0
└── node_modules
└── qar -> <store>/qar

보시다시피 그래프가 더 깊어지더라도 (foo > bar > qar), 파일 시스템의 디렉토리 깊이는 여전히 동일합니다.

이 레이아웃은 언뜻 보기에는 이상해보일 수 있지만, Node의 모듈 resolution 알고리즘과 완벽하게 호환됩니다. 모듈을 확인할 때, Node는 심볼릭 링크를 무시하므로, foo@1.0.0/node_modules/foo/index.js 에서 bar 가 필요할 때 Node는 foo@1.0.0/node_modules/bar 에서 bar 를 사용하지 않습니다. 대신, bar 는 실제 위치로 확인됩니다(bar@1.0.0/node_modules/bar). 결과적으로, barbar@1.0.0/node_modules에 있는 의존성을 해결할 수도 있습니다.

이 레이아웃의 큰 장점은 실제로 의존성에 있는 패키지에만 액세스할 수 있다는 것입니다. 평탄한 node_modules 구조를 사용하면, 호이스트 패키지 모두에 접근할 수 있습니다. 이것이 장점인 이유에 대한 자세한 내용은 pnpm's strictness helps to avoid silly bugs 를 참조하십시오.