对于通过 require() 调用目标文件的命令行工具,我们如何安全地避免本地和全局 npm 包之间的冲突?

Blc*_*knx 10 javascript command-line makefile node.js

全局安装和本地安装的冲突

我正在开发 npm 命令行工具和包https://github.com/ecma-make/ecmake。我在该包的全局安装版本和本地安装版本之间遇到了奇怪的冲突。

我可以通过将一个库链接到另一个库来避免这种冲突。那么该库就只有一个实例,不存在冲突。现在我必须考虑用户,他确实将包安装到两个地方,一次是全局安装,以便能够在没有前缀的npx情况下运行命令,一次是本地安装,以便在package.json.

如何重现

# prepare test fixture
mkdir ecmakeTest
cd ecmakeTest/
npm init -y

# install globally
npm install -g @ecmake/ecmake@0.3.1
npm ls -g @ecmake/ecmake

# install locally
npm install --save-dev @ecmake/ecmake@0.3.1
npm ls @ecmake/ecmake

# init ecmakeCode.js
npx ecmake --init

# run with local lib => shows the expected behaviour
npx ecmake all 

# run with global lib => NoRootTaskError
ecmake all

Run Code Online (Sandbox Code Playgroud)

冲突的根源

堆栈跟踪引导我们到全局安装中的行:/usr/local/lib/node_modules/@ecmake/ecmake/lib/runner/reader.js:21:13

    if (!(root instanceof Task)) {
      throw new Reader.NoRootTaskError(this.makefile);
    }

Run Code Online (Sandbox Code Playgroud)

发生了什么?

root根据全局库的类定义检查使用本地库创建的对象。它们具有相同的代码,但它们是相同代码的不同副本。

全局ecmake运行程序需要本地 makefile ecmakeCode.js。该文件又需要Task本地库的定义。

const root = module.exports = require('@ecmake/ecmake').makeRoot();

root.default
  .described('defaults to all')
  .awaits(root.all);

[...]
Run Code Online (Sandbox Code Playgroud)

我们可以通过将日志指令放入两个库中来验证实际上这两个库都已被调用。

其他人如何解决这个问题?

GulpGrunt导出一个函数,通过注入获取实际的依赖关系。虽然依赖注入通常非常聪明,但在这种情况下它并不是那么漂亮。整个文件被包装。我想避免这种包装功能。

请参阅:https ://gulpjs.com/docs/en/getting-started/quick-start#create-a-gulpfile

请参阅: https: //gruntjs.com/getting-started

我已经考虑过的

跑步者可以首先检查是否存在此类冲突。如果它可以通过运行子进程将给予全局的参数委托ecmake给本地。npx ecmake

唉,这会减慢跑步者的速度。至少需要一个子进程,也许还需要更多子进程来检查情况。

问题

您是否有一个通用的解决方案来应对这一挑战(除了我已经列出的缺点之外)?

Blc*_*knx 3

作为我自己答案的第一部分,我确实调查了为什么不可能出现规范的解决方案。

没有规范的解决方案

makefile 创建数据模型。运行程序加载并检查该数据模型并运行存储在其中的指令。跑步者和数据模型都需要一个库。有全局 npm安装和本地安装之分。这已经给出了四种可能的组合。

运行程序ecmake提供了一个--base directory模仿原始make工具的选项。意思是,“在做任何事情之前先进入指定的目录”。这为当地图书馆提供了第二名。我们是三乘三的组合。

我将列出几种解决方案的策略。将所有这些结合起来,就有数十种或多或少合理的选择。这使得规范的答案不太可能出现。尽管如此,此示例的基本挑战通常适用于许多命令行工具。

确切答案

精确解决原始问题的答案是那些将模型和运行器解耦的策略,或者注意只调用库的一个实例。

额外的限制

现实生活并不能准确回答问题。如果ecmake寻找解决方案会带来额外的限制。我想确保模型运行程序的版本与makefile编码所依据的主要版本相匹配。应事先避免重大版本更改时的版本冲突。

因此,makefile应与package.json. 如果只有全局安装,则应尝试运行,但应给出缺少本地安装的警告。