文件夹

npm 使用的文件夹结构

选择 CLI 版本

描述

npm 将各种东西放在您的计算机上。 这是它的工作。

本文档将告诉您它将什么放在哪里。

tl;dr

  • 本地安装(默认):将东西放在当前包根目录的 ./node_modules 中。
  • 全局安装(使用 -g):将东西放在 /usr/local 或节点安装的任何地方。
  • 如果您要 require() 它,则将其本地安装。
  • 如果您要在命令行上运行它,则将其全局安装。
  • 如果您两者都需要,则将它安装在两个地方,或者使用 npm link

前缀配置

prefix 配置 默认设置为节点安装的位置。 在大多数系统上,这是 /usr/local。 在 Windows 上,它是 %AppData%\npm。 在类 Unix 系统上,它向上提升一级,因为节点通常安装在 {prefix}/bin/node 而不是 {prefix}/node.exe

当设置了 global 标志时,npm 将东西安装到这个前缀中。 当没有设置它时,它使用当前包的根目录,或者如果不在包中,则使用当前工作目录。

节点模块

包被放到 prefix 下的 node_modules 文件夹中。 当本地安装时,这意味着您可以 require("packagename") 来加载它的主模块,或者 require("packagename/lib/path/to/sub/module") 来加载其他模块。

类 Unix 系统上的全局安装到 {prefix}/lib/node_modules。 Windows 上的全局安装到 {prefix}/node_modules(即,没有 lib 文件夹)。

作用域包的安装方式相同,只是它们在相关 node_modules 文件夹的子文件夹中分组在一起,该子文件夹的名称由 @ 符号前的作用域前缀决定,例如 npm install @myorg/package 会将包放在 {prefix}/node_modules/@myorg/package 中。 有关更多详细信息,请参阅 scope

如果您希望 require() 一个包,那么请将其本地安装。

可执行文件

在全局模式下,可执行文件被链接到类 Unix 系统上的 {prefix}/bin,或者直接链接到 Windows 上的 {prefix}。 确保该路径在您的终端的 PATH 环境中,以便运行它们。

在本地模式下,可执行文件被链接到 ./node_modules/.bin,以便它们可以供通过 npm 运行的脚本使用。 (例如,以便在运行 npm test 时,测试运行器位于路径中)。

手册页

在全局模式下,手册页被链接到 {prefix}/share/man

在本地模式下,不会安装手册页。

Windows 系统上不会安装手册页。

缓存

参阅 npm cache。 缓存文件存储在 Posix 上的 ~/.npm 或 Windows 上的 %LocalAppData%/npm-cache 中。

这由 cache 配置 参数控制。

临时文件

临时文件默认存储在由 tmp 配置 指定的文件夹中,该文件夹默认为 TMPDIR、TMP 或 TEMP 环境变量,或者类 Unix 系统上的 /tmp 和 Windows 上的 c:\windows\temp

临时文件为程序的每次运行在此根目录下分配一个唯一的文件夹,并在成功退出时删除。

更多信息

当本地安装时,npm 首先尝试找到一个合适的 prefix 文件夹。 这样做是为了使 npm install [email protected] 安装到您包的合理根目录,即使您碰巧 cd 进入其他文件夹。

从 $PWD 开始,npm 将向上遍历文件夹树,检查是否存在包含 package.json 文件或 node_modules 文件夹的文件夹。 如果找到这样的东西,那么它被视为运行 npm 命令的有效“当前目录”。 (此行为受 git 的 .git-文件夹查找逻辑的启发,类似于在工作目录中运行 git 命令时)。

如果找不到包根目录,则使用当前文件夹。

当您运行 npm install [email protected] 时,该包会被加载到缓存中,然后解压到 ./node_modules/foo。 之后,foo 的任何依赖项也会以类似的方式解压到 ./node_modules/foo/node_modules/...

任何 bin 文件都会被符号链接到 ./node_modules/.bin/,以便 npm 脚本在需要时可以找到它们。

全局安装

如果 global 配置 设置为 true,则 npm 将“全局”安装包。

对于全局安装,包的安装方式大致相同,但使用上述文件夹。

循环、冲突和文件夹简洁性

循环处理利用了 Node 模块系统的一个特性,它会向上遍历目录寻找 node_modules 文件夹。因此,在每个阶段,如果某个包已安装在祖先 node_modules 文件夹中,那么它就不会安装在当前位置。

考虑上面的情况,其中 foo -> bar -> baz。 想象一下,除了这些之外,baz 还依赖于 bar,所以您将拥有:foo -> bar -> baz -> bar -> baz ...。 但是,由于文件夹结构是:foo/node_modules/bar/node_modules/baz,所以无需在 .../baz/node_modules 中放置另一个 bar 的副本,因为当 baz 调用 require("bar") 时,它会获取安装在 foo/node_modules/bar 中的副本。

此快捷方式仅在多个嵌套的 node_modules 文件夹中安装的是完全相同的版本时使用。 仍然可以有 a/node_modules/b/node_modules/a,如果两个“a”包是不同的版本。 但是,如果没有重复安装完全相同的包多次,那么无限循环总是可以避免的。

另一个优化可以通过在尽可能高的级别(低于本地化的“目标”文件夹)安装依赖项来实现(提升)。 从版本 3 开始,npm 默认提升依赖项。

示例

考虑以下依赖项图

在这种情况下,我们可能会期望一个像这样的文件夹结构(所有依赖项都提升到尽可能高的级别)

foo
+-- node_modules
+-- blerg (1.2.5) <---[A]
+-- bar (1.2.3) <---[B]
| +-- node_modules
| +-- baz (2.0.2) <---[C]
+-- asdf (2.3.4)
+-- baz (1.2.3) <---[D]
+-- quux (3.2.0) <---[E]

由于 foo 直接依赖于 [email protected][email protected],因此它们安装在 foo 的 node_modules 文件夹中。

即使 blerg 的最新版本是 1.3.7,foo 对版本 1.2.5 也有特定的依赖项。 因此,它会安装在 [A] 中。 由于 blerg 的父安装满足了 bar 对 [email protected] 的依赖关系,因此它不会在 [B] 下安装另一个副本。

Bar [B] 也依赖于 baz 和 asdf。 因为它依赖于 [email protected],所以它不能重用安装在父 node_modules 文件夹 [D] 中的 [email protected],并且必须安装自己的副本 [C]。 为了最大程度地减少重复,npm 默认情况下会将依赖项提升到顶层,因此 asdf 安装在 [A] 下。

在 bar 下,baz -> quux -> bar 依赖关系创建了一个循环。 但是,由于 bar 已经在 quux 的祖先 [B] 中,因此它不会将另一个 bar 的副本解压到该文件夹中。 同样,quux 的 [E] 文件夹树是空的,因为其对 bar 的依赖关系由安装在 [B] 中的父文件夹副本满足。

要以图形方式了解安装位置,请使用 npm ls

发布

发布时,npm 将查看 node_modules 文件夹。 如果其中的任何项目不在 bundleDependencies 数组中,那么它们将不会包含在包 tarball 中。

这允许包维护者在本地安装所有依赖项(以及开发依赖项),但仅重新发布那些在其他地方找不到的项目。 有关更多信息,请参见 package.json

另见