【问题标题】:Using a shared npm node_modules/ for multiple workspaces on the same server对同一服务器上的多个工作区使用共享的 npm node_modules/
【发布时间】:2026-02-06 22:10:01
【问题描述】:

我们的 Jenkins/CI 服务器每天为我们的 node/js 项目运行数百次构建,我希望能够在完全干净的工作区中运行每个构建。但是,npm install 步骤可能需要超过 10 分钟,这太慢了。相反,由于我们的 npm 依赖项仅针对少量构建(约占所有构建的 1%)发生变化,因此我只想在每次 npm-shrinkwrap.json 更改时运行一次 npm install(每次构建时检查 md5sum)。如果收缩包装文件没有更改,请使用缓存的node_modules/ 目录。

如果我复制缓存的node_modules/,此计划运行良好,但即使该操作也可能需要一分钟。为了进一步优化我们的构建时间,我希望能够符号链接到缓存的node_modules/,这将大大提高我们的整体构建性能。

ln -s /path/to/cache/ /path/to/workspace/node_modules

但是,在依赖关系树的多个级别存在依赖关系的情况下,简单地对缓存路径进行符号链接是行不通的。例如,我们的*项目同时依赖于gulpgulp-util。顶层依赖也依赖于gulp-util。在npm install 之后,gulp-util 将安装在* node_modules/ 中,但不会安装在 node_modules/gulp/node_modules 中。

如果本地工作区中存在依赖项(即真实目录/path/to/workspace/node_modules/),那么node_modules/gulp 中的任何require('gulp-util') 实例都会(我认为)递归依赖关系树,直到找到合适的gulp-util模块。也就是说,它开始在 /path/to/workspace/node_modules/gulp/node_modules/gulp-util 中查找,没有找到任何东西,然后在 /path/to/workspace/node_modules/gulp-util 中查找,找到合适的模块,导入它并继续前进。

但是,当这是一个符号链接时,我会收到如下错误:

module.js:339
    throw err;
    ^

Error: Cannot find module 'gulp-util'
    at Function.Module._resolveFilename (module.js:337:15)
    at Function.Module._load (module.js:287:25)
    at Module.require (module.js:366:17)
    at require (module.js:385:17)
    at Object.<anonymous> (/path/to/cache/gulp/index.js:4:15)
    at Module._compile (module.js:435:26)
    at Object.Module._extensions..js (module.js:442:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:311:12)
    at Module.require (module.js:366:17)
    at require (module.js:385:17)

我认为这会尝试与其他版本相同,但我不明白为什么它无法找到 gulp-util。无论是在/path/to/workspace/node_modules/gulp-util 还是/path/to/cache/gulp-util 中查找,它都应该找到该模块并能够导入它。

我已经尝试通过手动安装模块gulp/node_modules/gulp-util 来解决这个问题,但是我遇到了很多这样的错误,并且在构建服务器上手动处理这个问题是不可行的。编写一些代码来搜索这种类型的依赖项并安装它们是可能的,但感觉这样做是错误的。

npm 必须有某种方式来支持这样的工作流程,对吧?我错过了一些明显的东西吗?我是否掩盖了文档中的某些内容?

【问题讨论】:

  • 您可以通过切换到 NPM 3.x 来实现这一点,这会将 node_modules 结构从 node_modules/parent/node_modules/child 更改为 node_modules/child
  • 呵呵,我用最新版本的 npm 3.x 试过了,我遇到了完全相同的问题。
  • @laffoyb 检查这个答案*.com/a/18974546/1341008 简而言之,尝试将所有模块放在您的 NODE_PATH 上
  • @darko,实际上,阅读该线程中的最佳答案给了我一个想法。它会查找名为 node_modules 的文件夹的路径。我的缓存在/path/to/cache/,如果它在/path/to/cache/node_modules/,也许它可以正常工作?我现在无法测试,但我会在早上检查。

标签: node.js npm npm-shrinkwrap


【解决方案1】:

感谢@amol-m-kulkarni here 发布的答案(上面的@darko-rodic 很有帮助地引用了),我意识到了我的错误。

如果给定的模块不是核心模块(例如http、fs等), 然后 Node.js 将开始搜索名为 node_modules 的目录。

它将从当前目录开始(相对于 Node.JS 中当前正在执行的文件),然后向上工作 文件夹层次结构,检查 node_modules 文件夹的每个级别。一次 Node.JS 找到 node_modules 文件夹,然后它会尝试加载 给定的模块可以是 (.js) JavaScript 文件,也可以是命名的 子目录;如果它找到命名的子目录,它将然后 尝试以各种方式加载文件。所以,例如

我的错误在于我给缓存路径的名称。因为它被命名为/path/to/cache,所以require 在路径中遇到的最后一个node_modules 目录之后停止向上看。在这种情况下,它停在/path/to/cache/gulp/node_modules,并没有意识到应该在搜索中考虑/path/to/cache/gulp-util

我通过将缓存重命名为/path/to/cache/node_modules 来解决此问题,因此require 将继续搜索,直到达到该级别,随后将找到/path/to/cache/node_modules/gulp-util

我将再次参考文档,看看我是否应该清楚这一点。

【讨论】:

  • 我也有同样的问题。在缓存文件夹下创建 node_modules 文件夹后,它可以工作。非常感谢!!!