文件夹
npm 使用的文件夹结构描述
npm 在你的计算机上放置各种东西。 这就是它的工作。
这份文件会告诉你它把什么放在哪里。
tl;dr
- 本地安装(默认): 将内容放入当前包根目录的
./node_modules
中。 - 全局安装(使用
-g
): 将东西放在 /usr/local 或安装节点的任何地方。 - 如果要安装
require()
,请安装 locally。 - 如果要在命令行上运行它,请安装它 globally。
- 如果两者都需要,那么在两个地方都安装,或者使用
npm link
。
前缀配置
prefix
配置默认为安装 node 的位置。
在大多数系统上,这是 /usr/local
。 在 Windows 上,它是 %AppData%\npm
。
在 Unix 系统上,它是上一级的,因为 node 通常安装在 {prefix}/bin/node
而不是 {prefix}/node.exe
。
当设置 global
标志时,npm 会将内容安装到此前缀中。
如果未设置,则使用当前包的根目录,如果不在包中,则使用当前工作目录。
Node 模块
包被放到 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}
。
在本地模式下,可执行文件被链接到 ./node_modules/.bin
,以便它们可用于通过 npm 运行的脚本。 (例如,当你运行 npm test
时,测试运行程序将在路径中。)
手册页
在全局模式下,手册页链接到 {prefix}/share/man
。
在本地模式下,不安装手册页。
手册页未安装在 Windows 系统上。
缓存
见 npm cache
。 缓存文件存储在 Posix 上的 ~/.npm
或 Windows 上的 %AppData%/npm-cache
中。
这由 cache
配置参数控制。
临时文件
临时文件默认存储在 tmp
配置指定的文件夹中,默认为 TMPDIR、TMP 或 TEMP 环境变量,或者 Unix 上的 /tmp
和 Windows 上的 c:\windows\temp
。
每次运行程序时,都会在此根目录下为临时文件分配一个唯一文件夹,并在成功退出后删除。
更多信息
在本地安装时,npm 首先尝试找到合适的 prefix
文件夹。 这样 npm install foo@1.2.3
将安装到你的包的合理根目录,即使你碰巧将 cd
安装到其他文件夹中。
从 $PWD 开始,npm 将遍历文件夹树,检查包含 package.json
文件或 node_modules
文件夹的文件夹。 如果找到这样的东西,那么为了运行 npm 命令,它被视为有效的 "当前目录"。 (在工作目录中运行 git 命令时,此行为受到 git 的 .git-folder 搜索逻辑的启发并与之类似。)
如果没有找到包根目录,则使用当前文件夹。
当你运行 npm install foo@1.2.3
时,包被加载到缓存中,然后解压到 ./node_modules/foo
中。 然后,任何 foo 的依赖都被类似地解包到 ./node_modules/foo/node_modules/...
中。
任何 bin 文件都符号链接到 ./node_modules/.bin/
,以便在必要时可以通过 npm 脚本找到它们。
全局安装
如果 global
配置设置为 true,则 npm 将安装包 "globally"。
对于全局安装,包的安装方式大致相同,但使用上述文件夹。
循环、冲突和文件夹简约
循环使用 node 模块系统的属性处理,它遍历目录查找 node_modules
文件夹。 因此,在每个阶段,如果一个包已经安装在祖级 node_modules
文件夹中,那么它不会安装在当前位置。
考虑上面的情况,其中 foo -> bar -> baz
。 想象一下,如果除此之外,baz 还依赖于 bar,那么你将拥有:
foo -> bar -> baz -> bar -> baz ...
。 但是,由于文件夹结构是: foo/node_modules/bar/node_modules/baz
,不需要将 bar 的另一个副本放入 .../baz/node_modules
,因为当它调用 require("bar") 时,它将获得安装在 foo/node_modules/bar
中的副本。
只有在多个嵌套的 node_modules
文件夹中安装完全相同的版本时,才使用此快捷方式。 如果两个 "a" 包是不同的版本,仍然可以有 a/node_modules/b/node_modules/a
。 但是,如果不多次重复完全相同的包,将始终防止无限倒退。
可以通过在本地化 "target" 文件夹下的最高级别安装依赖来进行另一项优化。
示例
考虑这个依赖图:
foo+-- blerg@1.2.5+-- bar@1.2.3| +-- blerg@1.x (latest=1.3.7)| +-- baz@2.x| | `-- quux@3.x| | `-- bar@1.2.3 (cycle)| `-- asdf@*`-- baz@1.2.3`-- quux@3.x`-- bar
在这种情况下,我们可能期望这样的文件夹结构:
foo+-- node_modules+-- blerg (1.2.5) <---[A]+-- bar (1.2.3) <---[B]| `-- node_modules| +-- baz (2.0.2) <---[C]| | `-- node_modules| | `-- quux (3.2.0)| `-- asdf (2.3.4)`-- baz (1.2.3) <---[D]`-- node_modules`-- quux (3.2.0) <---[E]
由于 foo 直接依赖于 bar@1.2.3
和 baz@1.2.3
,因此它们安装在 foo 的 node_modules
文件夹中。
尽管 blerg 的最新版本是 1.3.7,但 foo 对 1.2.5 版本有特定的依赖。 所以,它被安装在 [A]。 由于 blerg 的父安装满足 bar 对 blerg@1.x
的依赖,所以没有在 [B]下安装另一个副本。
Bar [B] 也依赖于 baz 和 asdf,所以它们安装在 bar 的 node_modules
文件夹中。 因为它依赖于 baz@2.x
,所以它不能重复使用安装在父 node_modules
文件夹 [D]中的 baz@1.2.3
,必须安装自己的副本 [C]。
在条形下方,baz -> quux -> bar
依赖创建了一个循环。
但是,因为 bar 已经在 quux 的祖级 [B] 中,所以它不会将 bar 的另一个副本解压缩到该文件夹中。
在 foo -> baz
[D] 下面,quux 的 [E] 文件夹树是空的,因为它对 bar 的依赖由安装在 [B] 的父文件夹副本满足。
有关安装位置的图形细分,请使用 npm ls
。
发布
发布后,npm 将在 node_modules
文件夹中查找。 如果有任何项目不在 bundledDependencies
数组中,则它们将不会包含在包 tarball 中。
这允许包维护者在本地安装他们所有的依赖(和开发依赖),但只重新发布那些在其他地方找不到的项目。 有关详细信息,请参阅 package.json
。