npm包的package.json文件中特定于操作系统的依赖项

80

有没有一种方法可以在npm的package.json文件中指定特定于操作系统的依赖关系?

例如,如果用户正在运行Linux,我只想安装'dbus'(https://npmjs.org/package/dbus)作为我的模块的依赖项。 我将拥有Mac和Windows的不同依赖项。


5
好问题。我知道在package.json中有os字段,但它不能让你基于当前平台交换依赖项 - 它只是声明一个包在哪些平台上是白名单/黑名单。例如,package.json中的这个属性:"os": ["!win32", "darwin"]意味着“此包将无法在Windows上运行,但可以在Mac上运行”。不幸的是,这并不能真正实现你所要求的。 - smithclay
这正是问题所在,如果dbus模块具有本地绑定,只能在特定的操作系统上编译(如下评论中所述),那么它的package.json应该包括该“os”字段。 - Chris Vandevelde
有没有一种方法可以忽略操作系统要求来安装软件包? - kinger6621
4个回答

47

这个问题有一个可能的好办法,取决于你的设置。

npm package.json 支持一个 os 键,

还有 optionalDependencies

  • os 可以用来指定一个模块可以安装在哪个操作系统上。
  • optionalDependencies 是模块的依赖项,如果它们无法安装,npm 将跳过它们并继续安装。

通过这种方式,你的模块可以为每个操作系统设置一个可选的依赖项,只有适用的依赖项才会被加载/安装 ^.^

编辑:正如 @Sebastien 在下面提到的,这种方法是危险的。 对于任何给定的操作系统,至少有一个依赖项是“必需的”,其余的是“可选的”。将所有版本的依赖项都设置为可选意味着如果你的安装由于合理的原因失败,它将悄悄地跳过安装,而你将缺少一个你真正需要的依赖项。


2
刚看到这个回答(在回复你关于反模式的评论后)。这看起来比使用安装脚本要好得多!既然我知道这是可用的,你可以忽略我的评论(如果还不晚的话,我会尝试删除/编辑它)。 - Metalskin
你能给我展示一个完整的例子吗?比如说,在OSX上,fsevents是可选依赖项,但我并不关心它,因为我的构建脚本在LINUX上运行。 - chen bin
@chenbin 我在这个 package.json 文件中使用它:https://github.com/appium/sample-apps/blob/master/package.json - TinyTimZamboni
9
这是一种方式,可能是目前由npm支持的更好的方式,但并不是一个好的方式。 如果你在正确的操作系统上运行npm install,但其中一个可选的依赖项因为未知原因而失败,npm将跳过它并不会抛出任何错误。结果将是一个不工作的环境。 可选依赖项是为那些可以选择使用的依赖项设计的,这里我们想管理在特定操作系统上强制执行的依赖项。 - Sébastien BATEZAT
啊,就像@Sebastien指出的那样:现在将您的依赖项设置为“可选”意味着如果安装由于合法原因出错,您的代码将忽略它。 - TinyTimZamboni
显示剩余3条评论

9
我认为简短的答案是不可以。不过我能想到一些解决方法——最简单的方法就是将所有东西添加到package.json中,无论操作系统如何,然后在运行时require()正确的文件。
如果这个方法行不通,你可能可以使用安装脚本来达到你想要的结果-请参考https://docs.npmjs.com/misc/scripts 虽然我没有测试过,但我认为这将有效:将以下内容添加到你的package.json中:
,"scripts": {
  "install": "node install_dependencies.js"
}

然后添加一个install_dependencies.js文件,该文件检查操作系统并运行适当的npm install ...命令来安装依赖项。


6
问题在于我的依赖项具有本地绑定,只能在特定操作系统上编译,因此我不能将它们作为显式依赖项。你对npm安装脚本的建议很有效,我使用了os.platform()来检测用户正在使用哪个平台。 - sandeepmistry
2
安装脚本现在被认为是一种“反模式”来源。相反,应该使用.gyp编译文件。 - TinyTimZamboni
2
以上内容中有一个观察点并不是很清楚。如果使用 package.json 来管理项目的安装,而且你不需要发布,那么使用 .gyp 并不是解决方案。请参考 TinyTimZamboni 在下面的帖子,它更适合这种情况(https://dev59.com/mGUp5IYBdhLWcg3wmIUt#26069595)。 - Metalskin
@TinyTimZamboni 当使用gyp编译某些内容时,基于平台需要不同的编译器选项或环境变量,这是一个问题。 - Michael
不要使用install。使用.gyp文件进行编译,并使用prepublish处理其他事项。您几乎永远不需要显式设置preinstall或install脚本。如果您正在这样做,请考虑是否有另一种选择。 install或preinstall脚本的唯一有效用途是必须在目标架构上完成的编译。 - TarranJones
关于“安装脚本”的“反模式”,根据NPM:“install或preinstall脚本的唯一有效用途是必须在目标架构上进行编译。”这正是这个问题所问的用例。使用optionalDependencies曾经让我吃过亏。请遵循此答案中的建议。如果可能,请在运行时解决,否则请使用安装脚本。 - gilly3

1
还有一个名为 bindings-shyp 的模块:

https://www.npmjs.com/package/bindings-shyp

加载本地模块 .node 文件的帮助模块

这是针对 Node.js 本地插件模块作者的一个帮助模块,可以理解为 "瑞士军刀",用于 require() 您的本地模块 .node 文件。

在 Node 的本地插件历史中,插件编译到不同的位置,取决于使用的构建工具和 Node 的版本。更糟糕的是,现在 gyp 构建工具可以生成 Release 或 Debug 构建,每个构建都编译到不同的位置。

这个模块检查本地插件可能被编译的所有位置,并返回首次成功加载的位置。


0

引用 @npm_support:

https://twitter.com/npm_support/status/968195526989512705

如果您想避免与依赖项相关的安装问题,其中一种方法是编写一个需要作为常规依赖项的包装器,并确保它具有optionalDeps(还要确保包装器验证您拥有完成所需的所有内容)。
但在我看来,这更像是一个解决问题的变通方法。
我可以理解npm希望保持可移植性并避免处理特定于平台的问题,但无论如何都必须这样做,并且在我的观点中,在运行时执行此操作并不是最佳选择(特别是如果要优化代码大小)。
因此,今天我没有最佳解决方案要分享,但提出了对建议的开放讨论。
不能在npm中支持“条件依赖项”吗?
我想到的第一件事是添加一个“覆盖”部分,它将更改(+添加,-删除,=替换)当前解析的部分。
例如: dependencies: { "common-stuff": "*" } overrides: { "os: { linux: { dependencies: { "+best-linux-module" } } } }

我认识的一位开发人员建议另一种选择,即引入一个提供关键字,然后几个模块可以提供与解析器相同的语义(类似于Debian),但这会产生类似的开销。

我正在寻找一种通用方法,不仅专注于操作系统支持,还包括其他软件包的变体(例如取决于引擎)。

你是否知道NPM跟踪器中的任何相关问题?如果没有,我考虑提交一个要在以下位置跟踪的错误:

https://github.com/npm/npm/issues?q=dependencies+conditional

欢迎对这个想法提出反馈。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接