如何让 Grunt Deploy 使用全局 NPM 模块而不是本地模块

Posted

技术标签:

【中文标题】如何让 Grunt Deploy 使用全局 NPM 模块而不是本地模块【英文标题】:How to make Grunt Deploy use global NPM modules instead of local ones 【发布时间】:2013-11-16 16:36:20 【问题描述】:

首先,我对 npm 和 grunt 很陌生。我们有一个项目,我们正在使用 Grunt 为其编译和生成输出文件。我正在尝试设置我们的构建服务器以使用 Grunt 生成输出文件。我们正在使用带有 TFS 源代码控制的 Windows,由于 it's 260 character path limit,我们无法将 grunt-bower-task 模块签入源代码控制 (as it alone uses 230 characters in its installed path)。

当我从我的项目目录运行 npm install 时,它工作正常,并将以下所需模块安装到我的项目目录中的 node_modules 文件夹中:

咕噜声 咕噜咕噜任务 grunt-contrib-compass grunt-contrib-connect grunt-contrib-jshint grunt-contrib-requirejs grunt-contrib-watch

然后当我从我的项目目录运行 grunt deploy 时,一切都按预期工作。

虽然我可以简单地将运行 npm install 作为构建过程的一部分,但我不希望这样做,因为下载所有文件需要几分钟,而且我不想要我们的构建依赖于可用的外部网络服务。

我已经看到you can install modules either locally or globally,所以我希望能够在构建服务器上全局安装模块,这样它们就不需要在运行之前直接位于项目目录内的 node_modules 文件夹中 咕噜部署。我已经为上面列出的每个模块运行了 npm install -g,以及 npm install -g [module],以及 npm install - g grunt-cli.

如果我执行 npm prefix -g,它会告诉我全局模块目录是 C:\Users[My User]\AppData\Roaming\npm,当我查看了该目录的 node_modules 文件夹,我确实看到了所有模块。但是,当我运行 grunt deploy 时,它会抱怨:

致命错误:无法找到本地咕噜声

如果我只包含 *node_modules\grunt* 目录,那么我仍然会收到以下错误:

找不到本地 Npm 模块“grunt-contrib-watch”。安装了吗?

找不到本地 Npm 模块“grunt-contrib-jshint”。安装了吗?

...

我也尝试过使用 *grunt deploy --base "C:\Users[My User]\AppData\Roaming\npm",但它抱怨它找不到其他文件,例如 .jshintrc。

那么有没有一种方法可以让我运行 grunt deploy 并让它检查模块的 npm 全局前缀路径,而不是查看项目目录?

一个棘手的解决方法是在构建过程中手动将模块复制到本地项目目录,但如果可能的话,我想避免这种情况。

作为参考,这是我的 package.json 文件的样子:


  "name": "MyProject",
  "version": "0.0.1",
  "scripts": 
    "preinstall": "npm i -g grunt-cli bower"
  ,
  "devDependencies": 
    "grunt": "~0.4.1",
    "grunt-contrib-compass": "~0.2.0",
    "grunt-contrib-watch": "~0.4.4",
    "grunt-contrib-jshint": "~0.6.0",
    "grunt-contrib-requirejs": "~0.4.1",
    "grunt-contrib-connect": "~0.3.0",
    "grunt-bower-task": "~0.2.3"
  

谢谢。

【问题讨论】:

感谢您如此雄辩地详细说明我遇到的问题。这让我发疯了。 【参考方案1】:

解决方法:在您自己的 package.json 中明确列出所有临时依赖项。

例如,假设您依赖于 module_a,而 module_a 依赖于 module_b。在npm install 之后,您将拥有node_modules/module_a/node_modules/module_b/,因为 npm 会将 module_b 安装到 module_a 的本地。但是,如果您在 your package.json 中添加 module_b 作为直接依赖项(并且版本说明符完全匹配),那么 npm 只会安装一次 module_b:在顶层。

这是因为当需要模块时,它们会开始查找最近的 node_modules 目录并向上遍历,直到找到所需的模块。所以 npm 能够通过只在版本匹配的最低级别安装模块来节省磁盘空间。

所以,修改后的例子。您依赖于 module_a@0.1.0,它依赖于 module_b@0.2.0。如果您还依赖于 module_b@0.1.0,您最终会安装两次 module_b。 (0.1.0 版将安装在顶层,0.2.0 版将安装在 module_a 下。)但是,如果您依赖 v0.2.0(使用 package.json 中的确切版本字符串作为 module_a 使用),那么npm 会注意到它可以使用相同版本的 module_b。所以它只会在顶层安装module_b,而不会在module_a下。

长话短说:将您拥有的任何具有深层模块树的临时依赖项直接添加到您自己的 package.json 中,您最终会得到一个更浅的 node_modules 树。

【讨论】:

这是我们最好的解决方案;我们有一个非常相似的问题,这是我们非常想要的 npm-native 解决方案。谢谢!【参考方案2】:

您应该在模块上使用symbolic links,而不是使用npm 的全局选项,以获得类似的结果。

全局npm 安装只是为了方便命令行实用程序,例如jshintgrunt-cli

【讨论】:

所以您是说不要将模块复制/粘贴到项目的 node_modules 目录中,而是使用符号链接?这是对我的“hacky workaround”的一个很好的改进,但我希望 npm/grunt 有一个我可以使用的功能。至于 TFS,它确实支持 Git 源代码控制,我们确实在一些项目中使用了 git;虽然我不确定它是否具有相同的路径长度限制。 TFS 不仅仅是源代码控制(实际上非​​常棒),所以我们不会很快放弃它;我们是一家拥有 150 多名开发人员的开发商店。 是的,我知道 TFS 很不错,但它也有很多缺点——比如这个。是的,符号链接更可取,因为您仍然需要“本地”安装。有点。 我测试了使用 mklink.exe 创建一个符号链接,它似乎工作得很好,所以如果我必须使用解决方法,这将避免我必须复制/粘贴 node_modules 文件夹,保存时间和空间是美好的。哦,仅供参考,260 个字符的路径限制是 Windows 的限制,而不是 TFS (msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx)。 很高兴为您解决了问题。对 Windows 上的 260 限制感到厌烦。 +1 用于优化我的符号链接解决方法。虽然我坚持使用 npm/grunt 内置解决方案,但没有标记为答案。谢谢!【参考方案3】:

我知道这个帖子很旧,但我终于找到了自己的个人答案,使用的是 Mac,但我认为 PC 也可以这样说/做。

跟进 bevacqua 的回答:

您应该在模块上使用符号链接,而不是使用 npm 的全局选项,以获得类似的结果。

全局 npm 安装只是为了方便命令行实用程序,例如 jshint 或 grunt-cli。

我做了一些挖掘,也许可以提供更多的澄清:

我最终在我的用户目录的根目录中创建了一个全局文件夹。在那个目录中,我使用npm install 添加了我需要的所有包。例如,我运行了npm install gruntnpm install grunt-contrib-watchnpm install grunt-contrib-less 等。您还可以在同一个文件夹中添加一个package.json 文件,然后运行npm install 一次将它们全部添加。现在我的全局目录具有以下结构:

.global_grunt_modules
    node_modules
        grunt
        grunt-contrib-watch
        grunt-contrib-less

然后我转到需要运行 grunt 的任何工作项目目录,并在该文件夹的根目录中运行命令:

ln -s ~/.global_grunt_modules/node_modules .

ln-s(符号链接)标志接受两个参数:

[source_file] [target_dir]

所以该命令基本上是说,“从我的全局 node_modules 文件夹创建一个符号链接,并将其链接到我的当前目录”。 . 指定当前工作目录。

希望这会有所帮助,我也遇到了麻烦。然后,每当我有一个需要新 grunt 模块的项目时,我只需通过我的全局目录安装它,然后在创建符号链接的任何地方都可以使用它。

【讨论】:

【参考方案4】:

我使用fenestrate 来解决Windows 上的此类问题。 即使它适用于 Git,如果您在团队资源管理器中使用 Visual Studio 中的 Git 集成,如果长文件路径位于您的 node_modules 文件夹中,它仍然会崩溃 - 即使您没有将该文件夹添加到源代码控制。

通常 Grunt 和 Bower 依赖结构会导致这种情况。

    我建议的第一件事是在你的包上运行npm dedupe。这样做的目的是扫描您已经安装的软件包并查看是否有一些依赖项重复。如果在更高级别上找到,它将删除经过深入测试的。

    其次,如果重复数据删除无法解决问题,如果您能找到导致此问题的嵌套太深的依赖项,请尝试将其直接安装到您的解决方案中,然后再次运行重复数据删除。

    如果有更多包依赖项导致此问题,fenestrate 将解决此问题。另外,它真的很酷,因为您可以将它挂接到您的 npm install 上,并且它总是会在安装新软件包后运行。而且,它是完全可逆的。

我希望这会有所帮助。

【讨论】:

【参考方案5】:

当 npm 链接不够好(网络文件系统,...)时,您可以使用 'requireg' 包。这是唯一一种以某种方式使其“内置”的干净解决方案。 'requireg' 包有一个函数 globalize,它使后续的 require 调用也可以在全局范围内查找。

【讨论】:

以上是关于如何让 Grunt Deploy 使用全局 NPM 模块而不是本地模块的主要内容,如果未能解决你的问题,请参考以下文章

grunt的使用方法,环境配置和插件安装

Grunt 入门

[转] npm install 本地安装与全局安装的区别

如何使用 grunt-run 执行 npm 脚本?

Grunt是什么,以及它的一些使用方法

Grunt