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

자주 묻는 질문

패키지가 global store에 저장되었는데도 node_modules가 디스크 공간을 사용하는 이유가 무엇인가요?

pnpm은 global store에서 프로젝트의 node_modules 폴더로 hard links를 만듭니다. Hard links는 디스크상의 원본 파일이 있는 위치를 가리킵니다. 예를 들어, 프로젝트에 1MB를 잡아 먹는 foo 디펜던시가 있으면, 그게 프로젝트의 node_modules 폴더와 global store 안에서 각각 1MB을 차지하는 것으로 보입니다. 하지만, 그 1MB는 디스크 내에서 동일한 공간이며 서로 다른 두 곳에서 참조할 뿐입니다. 그래서 foo는 2MB가 아니라 총 1MB를 차지합니다.

이 주제에 대해 자세한 내용 (영어):

Windows에서 동작할까요?

짧은 답: 네. 긴 답: Windows에서 symbolic link을 다루는 게 아무래도 문제가 되긴 하는데, pnpm은 해결 방법이 있긴 합니다. Windows에선 junctions을 대신 사용하고 있습니다.

하지만 중첩된 node_modules 방식은 Windows랑 안 맞지 않나요?

npm의 초기 버전은 node_modules을 모두 중첩하는 것에 문제가 있었습니다. (see this issue). 하지만, pnpm은 폴더를 깊이 생성하는 대신, 모든 패키지를 평평하게 저장하고 symbolic link를 사용해 종속성 트리 구조를 만듭니다.

순환 참조는 어떻습니까?

pnpm은 링크를 사용하여 종속성을 node_modules 폴더에 넣지만 상위 패키지가 종속성이 있는 동일한 node_modules 폴더에 배치되기 때문에 순환 참조를 피할 수 있습니다. 따라서 foo의 디펜던시는 foo/node_modules에 있지 않고, foo 와 그 디펜던시와 node_modules에 함께 있습니다.

왜 하드 링크가 있습니까? 글로벌 스토어에 직접 심볼릭 링크하지 않는 이유는 무엇입니까?

하나의 패키지는 하나의 시스템에 대해 서로 다른 의존성 집합을 가질 수 있습니다.

프로젝트 A foo@1.0.0 에서 의존성이 bar@1.0.0 으로 해결될 수 있지만 프로젝트 B 에서 foo 의 동일한 의존성은 bar@1.1.0 을 통해 해결될 수 있습니다. 따라서 pnpm은 foo@1.0.0 를 사용하는 모든 프로젝트에 하드 링크하여 서로 다른 의존성 세트를 생성합니다.

글로벌 스토어에 대한 직접적인 심볼릭 링크는 Node의 --preserve-symlinks 플래그와 함께 작동하지만 해당 접근 방식에는 자체 문제가 너무 많기 때문에 하드 링크를 사용하기로 결정했습니다. 이 결정이 내려진 이유에 대한 자세한 내용은 이 문제 를 참조하십시오

Does pnpm work across multiple drives or filesystems?

The package store should be on the same drive and filesystem as installations, otherwise packages will be copied, not linked. This is due to a limitation in how hard linking works, in that a file on one filesystem cannot address a location in another. See Issue #712 for more details.

pnpm functions differently in the 2 cases below:

저장 경로가 지정된 경우

If the store path is specified via the store config, then copying occurs between the store and any projects that are on a different disk.

If you run pnpm install on disk A, then the pnpm store must be on disk A. If the pnpm store is located on disk B, then all required packages will be directly copied to the project location instead of being linked. This severely inhibits the storage and performance benefits of pnpm.

저장 경로가 지정되지 않은 경우

If the store path is not set, then multiple stores are created (one per drive or filesystem).

If installation is run on disk A, the store will be created on A .pnpm-store under the filesystem root. If later the installation is run on disk B, an independent store will be created on B at .pnpm-store. The projects would still maintain the benefits of pnpm, but each drive may have redundant packages.

What does pnpm stand for?

pnpm stands for performant npm. @rstacruz came up with the name.

pnpm does not work with <YOUR-PROJECT-HERE>?

In most cases it means that one of the dependencies require packages not declared in package.json. It is a common mistake caused by flat node_modules. If this happens, this is an error in the dependency and the dependency should be fixed. That might take time though, so pnpm supports workarounds to make the buggy packages work.

해결 방법 1

In case there are issues, you can use the node-linker=hoisted setting. This creates a flat node_modules structure similar to the one created by npm.

해결 방법 2

In the following example, a dependency does not have the iterall module in its own list of deps.

The easiest solution to resolve missing dependencies of the buggy packages is to add iterall as a dependency to our project's package.json.

You can do so, by installing it via pnpm add iterall, and will be automatically added to your project's package.json.

  "dependencies": {
...
"iterall": "^1.2.2",
...
}

해결 방법 3

One of the solutions is to use hooks for adding the missing dependencies to the package's package.json.

An example was Webpack Dashboard which wasn't working with pnpm. It has since been resolved such that it works with pnpm now.

It used to throw an error:

Error: Cannot find module 'babel-traverse'
at /node_modules/inspectpack@2.2.3/node_modules/inspectpack/lib/actions/parse

The problem was that babel-traverse was used in inspectpack which was used by webpack-dashboard, but babel-traverse wasn't specified in inspectpack's package.json. It still worked with npm and yarn because they create flat node_modules.

The solution was to create a .pnpmfile.cjs with the following contents:

module.exports = {
hooks: {
readPackage: (pkg) => {
if (pkg.name === "inspectpack") {
pkg.dependencies['babel-traverse'] = '^6.26.0';
}
return pkg;
}
}
};

After creating a .pnpmfile.cjs, delete pnpm-lock.yaml only - there is no need to delete node_modules, as pnpm hooks only affect module resolution. Then, rebuild the dependencies & it should be working.