Node-Modules 与 Hoisting 设置

node_modules settings#

modulesDir#

  • 默认值:node_modules
  • 类型:路径

将安装依赖项的目录(而不是 node_modules)。

nodeLinker#

  • 默认值:isolated
  • 类型:isolated、hoisted、pnp

定义应该使用什么链接器来安装 Node 包。

  • isolated - 依赖项从虚拟存储 node_modules/.pnpm 中建立符号链接
  • hoisted - 创建一个没有符号链接的扁平的 node_modules。与 npm 或 Yarn Classic 创建的 node_modules 一致。当使用此设置时,Yarn 的一个库用于提升。使用此设置的正当理由:
    1. 你的工具无法很好地与符号链接配合使用。
    2. 你的项目会被部署到 serverless 服务提供商。一些 serverless 提供商(例如 AWS Lambda)不支持符号链接。此问题的另一种解决方案是在部署之前打包你的应用程序。
    3. 如果你想使用 "bundledDependencies" 发你的包。
    4. 如果你使用 --preserve-symlinks 标志运行 Node.js。
  • pnp — 没有 node_modules。 Plug'n'Play 是一种 Yarn Berry 使用的创新的 Node 依赖策略。当使用 pnp 作为你的链接器时,建议同时将 symlink 设置为 false。

nodeExperimentalPackageMap#

添加于:v11.8.0

  • 默认值: false
  • 类型:Boolean

When true, pnpm writes node_modules/.package-map.json during isolated and hoisted installs and injects it into pnpm-managed Node.js script environments by adding Node's --experimental-package-map option to NODE_OPTIONS.

Nothing reads the map without this setting, so pnpm does not write one. An install that stops writing the map also removes one an earlier install left.

CLI 和环境变量配置使用 kebab-case 风格的名称 node-experimental-package-map。

pnpm-workspace.yaml
nodeExperimentalPackageMap: true

nodePackageMapType#

添加于:v11.8.0

  • 默认:standard
  • 类型:standard,loose

控制 node_modules/.package-map.json 的生成方式。

  • standard - 只有已声明的依赖项可通过包映射访问。
  • loose —— 还会映射那些可通过已安装的 node_modules 布局访问到的包,这使得未显式声明但被提升的依赖项能够被解析。

CLI 和环境变量配置使用 kebab-case 风格的名称 node-package-map-type。

pnpm-workspace.yaml
nodePackageMapType: loose

符号链接#

  • 默认值:true
  • 类型:Boolean

当 symlink 设置为 false 时,pnpm 创建一个没有任何符号链接的虚拟存储目录。这与 node-linker=pnp 一起是一个有用的设置。

enableModulesDir#

  • 默认值:true
  • 类型:Boolean

当为 false 时,pnpm 不会将任何文件写入模块目录 (node_modules)。这对于在用户空间的文件系统 (FUSE) 中挂载模块目录时很有用。有一个实验性 CLI 允许你在 FUSE 中挂载模块目录:@pnpm/mount-modules。

confirmModulesPurge#

  • 默认值:true
  • 类型:Boolean

Controls whether pnpm asks for confirmation before removing the contents of an incompatible modules directory and reinstalling it from scratch. When false, pnpm recreates the modules directory without prompting. In CI, confirmation is disabled automatically. If confirmation is enabled without a TTY and CI is not detected, pnpm aborts with ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY.

virtualStoreDir#

  • 默认值:node_modules/.pnpm
  • 类型:路径

带有指向存储的链接的目录。所有直接和间接依赖项都链接到此目录中。

Dependencies installed from a URL or local path get a hash suffix on their directory name when the part after the scheme contains +, #, :, ?, or another character that is not allowed in file names. This keeps distinct URLs in separate directories.

这是一个有用的设置,可以解决 Windows 上长路径的问题。如果你有一些路径很长的依赖项,你可以选择将虚拟存储放在驱动器的根目录中(例如 C:\my-project-store)。

或者你可以将虚拟存储设置为 .pnpm 并将其添加到 .gitignore。这将使堆栈跟踪更清晰,因为依赖项的路径将会提高一个目录层级。

**注意:**虚拟存储不能在多个项目之间共享。每个项目都应该有自己的虚拟存储(除了在工作空间中被共享的根目录)。

virtualStoreDirMaxLength#

  • 默认值:
    • 在 Linux/macOS 上:120
    • 在 Windows 上:60
  • 类型:number

设置虚拟存储目录 (node_modules/.pnpm) 中目录名称的最大允许长度。如果你在 Windows 上遇到长路径问题,你可以将其设置为较低的数字。

virtualStoreOnly#

添加于:v11.0.0

  • 默认值: false
  • 类型:Boolean

当设置为 true 时,pnpm 会装入虚拟存储店,而不会创建导入器的符号链接、钩子、二进制链接或运行生命周期脚本。这对于预先填充存储(例如,在 Nix 构建中)非常有用,而不会创建不必要的项目级工件。 pnpm fetch 内部使用此模式。

packageImportMethod#

  • 默认值: auto
  • 类型:auto、hardlink、copy、clone、clone-copy

控制从存储中导入包的方式(如果要禁用 node_modules 中的符号链接,则需要更改 nodeLinker 设置,而不是此设置)。

  • auto - 尝试从存储克隆包。如果不支持克隆则从存储硬链接包。如果克隆和链接都不支持,则回退到复制
  • hardlink - 从存储硬链接包
  • clone-or-copy - 尝试从存储中克隆包。如果不支持克隆则回退到复制。
  • copy - 从存储中复制包
  • clone - 从存储中克隆(也称为 copy-on-write 或参考链接)包

克隆是将包写入 node_modules 的最佳方式。这是最快的方式,也是最安全的方式。当使用克隆时,你可以在 node_modules 中编辑文件,并且它们不会在中央内容可寻址存储中被修改。

不幸的是,并非所有文件系统都支持克隆。我们建议使用写时复制 (CoW) 文件系统(例如,在 Linux 上使用 Btrfs 而不是 Ext4)以获得最佳的 pnpm 体验。

modulesCacheMaxAge#

  • 默认值:10080 (以分钟为单位的 7 天)
  • 类型:number

孤立包应该从模块目录中被删除的时间(以分钟为单位)。 pnpm 在模块目录中保存了一个包的缓存。切换分支或降级依赖项时,这会提高安装速度。

dlxCacheMaxAge#

  • 默认值:1440 (以分钟为单位的 1 天)
  • 类型:number

Dlx 缓存过期的时间(以分钟为单位)。执行 dlx 命令后,pnpm 会保留一个缓存,该缓存会省略后续调用同一 dlx 命令的安装步骤。

virtualStoreType#

添加于:v11.23.0

  • Default: project
  • Type: project, global

Names where the virtual store lives: one store per project, or one store per machine.

pnpm-workspace.yaml
virtualStoreType: global

project is the default layout: every project gets its own virtual store inside node_modules/.pnpm. global is the global virtual store: a single store shared by every project on the machine, with each project's node_modules holding only symlinks into it.

This is the canonical spelling of enableGlobalVirtualStore, which keeps working; when a project sets both, virtualStoreType wins. It can also be set through the PNPM_CONFIG_VIRTUAL_STORE_TYPE env variable and read back with pnpm config get virtualStoreType.

The setting is independent of nodeLinker: isolated and pnp both work with either store type, and hoisted writes no virtual store at all, so it is unaffected.

enableGlobalVirtualStore#

添加于:v10.12.1

  • 默认值: false
  • 类型:Boolean

注意

Since v11.23.0, virtualStoreType is the canonical spelling of this setting: enableGlobalVirtualStore: true is virtualStoreType: global. Both work, and virtualStoreType wins when a project sets both.

注意

在 pnpm v11 中,全局安装(pnpm add -g)和 pnpm dlx 默认使用全局虚拟存储。

如果启用,node_modules 只包含到一个中心虚拟存储的符号链接,而不是 node_modules/.pnpm。默认情况下,此中央存储位于 STORE_PATH/links(使用 pnpm store path 来查找 STORE_PATH)。

在中央虚拟存储中,每个包都被硬链接到一个目录中,该目录的名称是其依赖关系图的哈希值。因此,系统上的所有项目都可以从磁盘上的这个共享位置符号链接它们的依赖项。这种方法在概念上类似于 NixOS 管理包的方式,使用依赖图哈希在 Nix 存储中创建隔离且可共享的包目录。

这不应与全局内容可寻址存储混淆。 The actual package files are still hard linked from the content-addressable store, but instead of being linked directly into node_modules/.pnpm, they are linked into the global virtual store.

Using a global virtual store can speed up installations when a warm cache is available. 然而,在 CI 环境中(通常不存在缓存),它可能会减慢安装速度。 If pnpm detects that it is running in CI and this setting is not set, the global virtual store is off.

important

为了在使用全局虚拟存储时支持提升的依赖项,pnpm 依赖于 NODE_PATH 环境变量。这允许 Node.js 解析来自提升的 node_modules 目录的包。 Node.js does not respect NODE_PATH for ESM imports, though, so before v11.23.0 a dependency that imports a package not declared in its own package.json (which is considered bad practice) failed to resolve under ESM.

Since v11.23.0, every process pnpm spawns for the project (pnpm run, pnpm exec, lifecycle scripts, and the tools pnpm dlx runs) receives both NODE_PATH and a NODE_OPTIONS --import flag that registers a resolve hook restoring NODE_PATH lookups for ESM. Such imports now resolve for CommonJS and ESM alike, without the @pnpm/plugin-esm-node-path config dependency that used to be needed (#9618).

Two things fall outside that: a node process you start yourself rather than through pnpm, and a project that sets extendNodePath to false, which turns the whole NODE_PATH mechanism off and takes the resolve hook with it. Use packageExtensions to declare the missing dependencies if you need them to resolve in either case.

Dependency hoisting settings#

hoist#

  • 默认值:true
  • 类型:Boolean

当为 true 时,所有依赖项都会被提升到 node_modules/.pnpm/node_modules。这使得 node_modules 中的所有包都可以访问未列出的依赖项。

hoistPattern#

  • 默认值:['*']
  • 类型:string[]

告诉 pnpm 哪些包应该被提升到 node_modules/.pnpm/node_modules。默认情况下,所有包都被提升 — 但是,如果你知道只有某些有缺陷的包具有幻影依赖,你可以使用此选项专门提升幻影依赖(推荐做法)。

例如:

pnpm-workspace.yaml
hoistPattern:
- "*eslint*"
- "*babel*"

你还可以在模式前面添加 ! 来避免提升。

例如:

pnpm-workspace.yaml
hoistPattern:
- "*types*"
- "!@types/react"

In a workspace, the pattern is matched against the names of the workspace projects too, unless hoistWorkspacePackages is turned off.

publicHoistPattern#

  • 默认值:[]
  • 类型:string[]

不同于 hoist-pattern 会把依赖提升到一个虚拟存储中的隐藏的模块目录中,publicHoistPattern 将匹配的依赖提升至根模块目录中。提升至根模块目录中意味着应用代码可以访问到幻影依赖,即使它们对解析策略做了不当的修改。

当处理一些不能正确解析依赖关系的有缺陷可插拔工具时,此设置很有用。

例如:

pnpm-workspace.yaml
publicHoistPattern:
- "*plugin*"

注意:设置 shamefully-hoist 为 true 与设置 public-hoist-pattern 为 * 是一样的。

你还可以在模式前面添加 ! 来避免提升。

例如:

pnpm-workspace.yaml
publicHoistPattern:
- "*types*"
- "!@types/react"

In a workspace, the pattern is matched against the names of the workspace projects too, unless hoistWorkspacePackages is turned off.

hoistWorkspacePackages#

  • 默认值:true
  • 类型:Boolean

When true, the projects of the workspace are hoisting candidates too, not only the packages that come from a registry.

hoistPattern and publicHoistPattern then decide where each project lands, exactly as they do for a registry dependency. publicHoistPattern is checked first, so a project matching both patterns goes to the root only:

  • A project whose name matches publicHoistPattern is symlinked into <workspace_root>/node_modules.
  • A project whose name matches only hoistPattern is symlinked into <workspace_root>/node_modules/.pnpm/node_modules.
  • A project whose name matches neither is not hoisted.

Because hoistPattern defaults to ['*'] and publicHoistPattern to [], the default is that every workspace project is symlinked into <workspace_root>/node_modules/.pnpm/node_modules.

This is how a tool that resolves packages by name from the workspace root finds a project of the workspace. The usual case is ESLint loading a plugin that is developed in the same monorepo:

pnpm-workspace.yaml
publicHoistPattern:
  - "*eslint*"

With that setting, a workspace project named eslint-plugin-acme appears at <workspace_root>/node_modules/eslint-plugin-acme, even if no package.json lists it as a dependency.

Details worth knowing:

  • A project is matched by the name in its package.json, not by its directory name.
  • Every project of the workspace is a candidate, including a project that nothing else depends on. The workspace root project is never hoisted.
  • The symlink points at the project's own directory in the workspace, so there is nothing to keep in sync.
  • A direct dependency wins a name conflict. If a package with the same name is a direct dependency of any workspace project, that package is hoisted and the workspace project is not. Frozen installs preserve this choice even when the dependency is unchanged.
  • The setting only adds candidates. When hoistPattern and publicHoistPattern are both empty, nothing is hoisted at all, workspace projects included. Setting hoist to false empties hoistPattern.

shamefullyHoist#

  • 默认值: false
  • 类型:Boolean

默认情况下,pnpm 创建一个半严格的 node_modules,这意味着依赖项可以访问未声明的依赖项,但 node_modules 之外的模块不行。通过这种布局,生态系统中的大多数的包都可以正常工作。但是,如果某些工具仅在提升的依赖项位于根目录的 node_modules 时才有效,你可以将其设置为 true 来提升它们。

hoistingLimits#

添加于:v11.5.0

  • 默认: none
  • 类型:none, workspaces, dependencies

控制在使用 nodeLinker: hoisted 时依赖项的提升层级。此设置对应于 Yarn 的 nmHoistingLimits。

  • none - 尽可能向上提升(默认值)。
  • workspaces —— 仅将依赖提升至各自的工作区包层级,防止依赖被提升至依赖它们的那个工作区包之上。
  • dependencies —— 仅向上提升至各工作区包的直接依赖,从而防止传递依赖被提升到该工作区包的 node_modules 中。