본문으로 건너뛰기
버전: 7.x

자주 묻는 질문

패키지가 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랑 안 맞지 않나요?

Early versions of npm had issues because of nesting all node_modules (see this issue). However, pnpm does not create deep folders, it stores all packages flatly and uses symbolic links to create the dependency tree structure.

Although pnpm uses linking to put dependencies into node_modules folders, circular symlinks are avoided because parent packages are placed into the same node_modules folder in which their dependencies are. So foo's dependencies are not in foo/node_modules, but foo is in node_modules together with its own dependencies.

One package can have different sets of dependencies on one machine.

In project A foo@1.0.0 can have a dependency resolved to bar@1.0.0, but in project B the same dependency of foo might resolve to bar@1.1.0; so, pnpm hard links foo@1.0.0 to every project where it is used, in order to create different sets of dependencies for it.

Direct symlinking to the global store would work with Node's --preserve-symlinks flag, however, that approach comes with a plethora of its own issues, so we decided to stick with hard links. For more details about why this decision was made, see this issue.

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:

Store path is specified

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.

Store path is NOT specified

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.

Solution 1

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",
...
}

Solution 2

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.

Solution 3

In case there are too many issues, you can use the shamefully-hoist option. This creates a flat node_modules structure similar to the one created by npm or yarn, which is not recommended as avoiding this structure is the primary purpose of pnpm's node_modules approach.

To use it, try pnpm install --shamefully-hoist.