Addenda: Package manager tips
Node.js require()
函数的语义被设计为足够通用以支持合理的目录结构。
诸如 dpkg
、rpm
和 npm
之类的包管理器程序有望发现无需修改即可从 Node.js 模块构建本机包。
下面给出了一个可行的建议目录结构:
假设想让位于 /usr/lib/node/<some-package>/<some-version>
的文件夹保存特定版本包的内容。
包可以相互依赖。
为了安装包 foo
,可能需要安装包 bar
的特定版本。
bar
包本身可能存在依赖关系,在某些情况下,这些甚至可能发生冲突或形成循环依赖关系。
因为 Node.js 查找它加载的任何模块的 realpath
(即,它解析符号链接)然后在 node_modules
文件夹中查找它们的依赖项,所以这种情况可以通过以下架构解决:
/usr/lib/node/foo/1.2.3/
:foo
包的内容,版本 1.2.3。/usr/lib/node/bar/4.3.2/
:foo
依赖的bar
包的内容。/usr/lib/node/foo/1.2.3/node_modules/bar
:/usr/lib/node/bar/4.3.2/
的符号链接。/usr/lib/node/bar/4.3.2/node_modules/*
:bar
依赖的包的符号链接。
因此,即使遇到循环,或者如果存在依赖冲突,每个模块都将能够获得它可以使用的依赖版本。
当 foo
包中的代码执行 require('bar')
时,它将获得符号链接到 /usr/lib/node/foo/1.2.3/node_modules/bar
的版本。
然后,当 bar
包中的代码调用 require('quux')
时,它将获得符号链接到 /usr/lib/node/bar/4.3.2/node_modules/quux
的版本。
此外,为了使模块查找过程更加优化,与其将包直接放在 /usr/lib/node
中,还可以将它们放在 /usr/lib/node_modules/<name>/<version>
中。
这样 Node.js 就不会费心寻找 /usr/node_modules
或 /node_modules
中缺失的依赖项了。
为了使模块可用于 Node.js 交互式解释器,将 /usr/lib/node_modules
文件夹添加到 $NODE_PATH
环境变量可能会很有用。
由于使用 node_modules
文件夹的模块查找都是相对的,并且基于调用 require()
的文件的真实路径,因此包本身可以位于任何位置。
The semantics of the Node.js require()
function were designed to be general
enough to support reasonable directory structures. Package manager programs
such as dpkg
, rpm
, and npm
will hopefully find it possible to build
native packages from Node.js modules without modification.
Below we give a suggested directory structure that could work:
Let's say that we wanted to have the folder at
/usr/lib/node/<some-package>/<some-version>
hold the contents of a
specific version of a package.
Packages can depend on one another. In order to install package foo
, it
may be necessary to install a specific version of package bar
. The bar
package may itself have dependencies, and in some cases, these may even collide
or form cyclic dependencies.
Because Node.js looks up the realpath
of any modules it loads (that is, it
resolves symlinks) and then looks for their dependencies in node_modules
folders,
this situation can be resolved with the following architecture:
/usr/lib/node/foo/1.2.3/
: Contents of thefoo
package, version 1.2.3./usr/lib/node/bar/4.3.2/
: Contents of thebar
package thatfoo
depends on./usr/lib/node/foo/1.2.3/node_modules/bar
: Symbolic link to/usr/lib/node/bar/4.3.2/
./usr/lib/node/bar/4.3.2/node_modules/*
: Symbolic links to the packages thatbar
depends on.
Thus, even if a cycle is encountered, or if there are dependency conflicts, every module will be able to get a version of its dependency that it can use.
When the code in the foo
package does require('bar')
, it will get the
version that is symlinked into /usr/lib/node/foo/1.2.3/node_modules/bar
.
Then, when the code in the bar
package calls require('quux')
, it'll get
the version that is symlinked into
/usr/lib/node/bar/4.3.2/node_modules/quux
.
Furthermore, to make the module lookup process even more optimal, rather
than putting packages directly in /usr/lib/node
, we could put them in
/usr/lib/node_modules/<name>/<version>
. Then Node.js will not bother
looking for missing dependencies in /usr/node_modules
or /node_modules
.
In order to make modules available to the Node.js REPL, it might be useful to
also add the /usr/lib/node_modules
folder to the $NODE_PATH
environment
variable. Since the module lookups using node_modules
folders are all
relative, and based on the real path of the files making the calls to
require()
, the packages themselves can be anywhere.