为 Node 模块强制安装 yarn 而不是 npm install?

Force yarn install instead of npm install for Node module?

我想强制使用 yarn install 而不是 npm install。我想在 npm install 中提出错误。我应该在 package.json 做什么?

您可以使用 preinstall 挂钩以及一些 shell 脚本来实现此目的。

样本package.json:

  "scripts": {
    "preinstall": "pgrep npm && exit 1"
  }

UPDATE: is the better solution and uses the same technique I describe here. I am leaving my answer in tact for posterity. The original point of my answer was to show that you can execute a small node script which should work on all platforms.

在您的预安装脚本中,您可以 运行 一个迷你节点脚本,它应该适用于所有平台,而 pgrep(以及其他常见的 *nix 命令和运算符)之类的东西将不适用于Windows 直到 Windows 10 得到广泛采用。

我在 Node v4.7.0 (npm v2.15.11) 和 Node v7.2.1 (npm v3.10.10) 上测试了以下脚本。我认为它适用于介于两者之间的所有内容。它通过检查当前 运行ning 进程的环境变量来工作 - npm_execpath 是当前 运行ning "npm" 脚本的路径。如果是yarn,应该指向/path/to/yarn/on/your/machine/yarn.js.

"scripts": {
    "preinstall": "node -e \"if(process.env.npm_execpath.indexOf('yarn') === -1) throw new Error('You must use Yarn to install, not NPM')\""
}

您可以在此处阅读有关 npm 脚本的更多信息:https://docs.npmjs.com/misc/scripts

至于 npm_execpath 环境变量,虽然没有记录在案,但我怀疑它永远不会改变。它已经出现在 npm 的多个主要版本中,但它并没有真正通过 "there's a better name for this" 测试。

与其他答案一样,我建议使用 preinstall script 并检查您的环境。如果另一个 npm 进程恰好是 运行,对于不会有误报的便携式解决方案,使用 node -e 'JS_CODE' 可能是最好的选择。

在该 JS 代码中,您可以使用以下方法检查包管理器的路径:

process.env.npm_execpath

Yarn 的二进制文件是 yarn.js,而 NPM 使用的是 npm-cli.js。我们可以使用如下正则表达式来检查此字符串是否以 yarn.js.

结尾
/yarn\.js$/

通过使用此正则表达式,我们可以确保它不会意外匹配文件系统中较早的位置。 yarn 很可能不会出现在文件路径中,但您永远无法确定。

这是一个最小的例子:

{
  "name": "test",
  "version": "1.0.0",
  "scripts": {
    "preinstall": "node -e 'if(!/yarn\.js$/.test(process.env.npm_execpath))throw new Error(\"Use yarn\")'"
  }
}

当然,用户仍然可以通过编辑 JSON 或使用 --ignore-scripts 选项绕过此检查:

npm install --ignore-scripts

我刚刚发布了一个包含 CLI 的模块(对 npm preinstall 脚本很有用):https://github.com/adjohnson916/use-yarn

此外,我刚刚为 Danger 发布了一个帮助程序来检查 CI 上缺少的 yarn.lock 更改: https://github.com/adjohnson916/danger-yarn-lock

另请参阅此处的讨论:

如果你想简单地测试包是安装在 yarn 还是 npm 下,我稍微调整了 因为它在 OS X:

上对我有用
"scripts": {
    "preinstall": "if node -e \"process.exitCode=!/yarn\.js$/.test(process.env.npm_execpath)\" ; then echo yarn ; else echo npm ; fi",
    "postinstall": ""
}

这个简短的片段中出现了很多概念:

  • \. 部分被转义,因此 \ 变为 \ 并导致正确转义的 \. 以检测正则表达式中的句点.

  • process.exitCode= 可用于设置进程的退出代码,并且由于 Node.js.[=46= 的异步性质,比调用 process.exit(N) 更安全]

  • 中,throw new Error(\"Use yarn\") 导致节点以代码 1 退出并将堆栈跟踪打印到 stderr。您可以在控制台上尝试 运行ning 以查看其工作原理:node -e 'throw new Error("Oops")'node -e 'throw new Error("Oops")' 2> /dev/null(将 stderr 流定向到 /dev/null)。然后您可以使用 echo $?(打印最后一个退出代码)验证退出代码是否为 1。

  • shell 的 if XXXX ; then YYYY ; else ZZZZ ; fi 条件逻辑检查 XXXX 的退出代码并转到 then 情况下的 0(任何其他值转到 else 案例)。因此,如果正则表达式在 process.env.npm_execpath 末尾检测到 yarn.js,则它 returns 为真。这必须被否定,以便节点进程以代码 0 退出并满足 if.

  • 您还可以 console.log() 正则表达式结果并比较 shell 中的输出(这有点冗长)。以下是如何执行此操作的一些示例:https://unix.stackexchange.com/a/52801 and https://superuser.com/a/688902

  • 您可以将 true ;false ; 附加到任何 shell 语句以手动设置退出代码。例如,您可以尝试 true ; echo $?false ; echo $?.

  • 如果不需要,您也可以完全省略 else echo npm ; 部分。

完成所有这些后,您可以用其他命令替换 echo yarnecho npm 部分。例如,您可以将多个命令放在子 shell 中,例如 (echo yarn)echo $(echo yarn).

就我而言,我需要解决一个问题,其中一个软件包已安装但在 yarn 下存在错误,因此我不得不 运行 一个 npm install --ignore-scripts 在成功案例中。请注意,这可能永远不会在生产中完成,但如果您只需要完成某事或无法控制将在未来使用哪个包管理器,则可以成为救命稻草。

我还没有在 Windows 上尝试过这个,所以如果有人可以在那里测试语法,我会用有效的方法更新我的答案。如果 preinstall 脚本在 Windows 和 Mac/Linux shell.

下是相同的,那将是最好的

这里的大部分答案都涉及到 hacky 脚本,但有一种内置的方法可以实现这一点,我在 Yarn github issue 上发布了该方法。与其他方式不同,这适用于所有 NPM 命令。

您在 package.json 中添加了一个伪造的引擎版本(您可能想要调整 yarn 和节点条目):

  "engines": {
    "npm": "please-use-yarn",
    "yarn": ">= 1.17.3",
    "node": ">= 12.5.0"
  }

然后将 .npmrc 文件添加到项目根目录:

engine-strict = true

运行 NPM 然后引发错误:

npm ERR! code ENOTSUP
npm ERR! notsup Unsupported engine for root@: wanted: {"npm":"please-use-yarn","yarn":">= 1.17.3","node":">= 12.5.0"} (current: {"node":"12.9.1","npm":"6.10.2"})
npm ERR! notsup Not compatible with your version of node/npm: root@

在尝试了这些选项但不是很满意之后,我推荐only-allow

只需添加:

{
  "scripts": {
    "preinstall": "npx only-allow yarn"
  }
}

我喜欢它提供了清晰的警告信息,以及如何安装 yarn 的说明:

归功于 回答提供了推荐这个话题的答案。

Reddit 上找到了替代解决方案。我将此添加到我的 .zshenv 文件的末尾:

NPM_PATH=$(which npm)
npm () {
  if [ -e yarn.lock ]
  then
    echo "Please use yarn with this project"
  else
    $NPM_PATH "$@"
  fi
}

它现在阻止我在我的 Mac.

上的任何 yarn 项目上心不在焉地 运行 命令,例如 npm i