【问题标题】:Achieve "npm run x" behavior without a "scripts" entry?在没有“脚本”条目的情况下实现“npm run x”行为?
【发布时间】:2016-12-20 03:16:18
【问题描述】:

要在您安装的node_modules 的“上下文”中运行节点命令,您可以在package.jsonscripts 字段中输入一个条目。例如:

...
  "scripts": {
    "test": "mocha --recursive test/**/*.js --compilers js:babel-register"
  }
...

然后我可以在我的项目根目录中输入 npm run test,然后 mocha 测试将运行(通过调用安装在 node_modules/mocha/bin 中的 mocha 二进制文件)。

有没有办法在不创建脚本条目的情况下实现完全相同的行为?例如,对于一次性的“脚本”?

我想像下面这样的东西,相当于npm run test

npm cmd mocha --recursive test/**/*.js --compilers js:babel-register

有什么办法可以做到吗?

注意:我应该澄清一下,我正在寻找真正的对等。也就是说,我的命令应该能够访问其他脚本命令等。我知道您始终可以使用 node 和 node_modules 中的二进制文件路径来调用二进制文件,但这不是一个适当的解决方案。

【问题讨论】:

  • 没有特定的用例。用例是我想做一些我可以在包脚本中做的事情,但这是一次性的,我不想在脚本中添加一个条目。它出现在很多场合,我想要一个通用的解决方案。假设您向我寻求有关您的项目的帮助,然后我将其拉下来,并且想运行其中的一个二进制文件。说我应该全局安装它,尤其是当它可能不是我什至想要全局安装的东西并且它已经安装在你的项目中时似乎被误导了

标签: javascript node.js npm command-line-interface


【解决方案1】:

注意:这个答案解决了 OP 的特定用例:在给定项目的上下文中调用 依赖包的 CLI; 不是关于使 CLI全球可用 - 请参阅底部的讨论。

tl;博士:

Unix 类平台上npm run env -- 添加到您的命令中;例如:

npm run env -- mocha --recursive test/**/*.js --compilers js:babel-register

不仅可以仅通过名称调用依赖 CLI,还可以在您使用 npm testnpm run-script <script-defined-in-package.json> 时完全复制 npm 在幕后设置的环境

遗憾的是,这种方法不适用于 Windows。

对于 Windows 解决方案、便利别名(包括每会话一次的环境配置命令)和背景信息,请继续阅读。


有两种(不相互排斥的)方法可以使 npm 项目的依赖项的 CLI 可以通过 shell 的名称调用

  • (a) 使用 per-invocation 辅助命令,将命令传递给
  • (b) 运行 once-per-session 命令(临时)修改您的环境

Frxstrem's helpful answer 为 (a) 在类 Unix 平台上提供了一个不完整的解决方案;但是,可能就足够了,具体取决于您的具体需求。
它是不完整的,因为它只是将包含依赖 CLI(符号链接)的目录添加到 $PATH,而不执行调用 npm testnpm run-script <script-defined-in-package.json 时发生的所有其他环境修改。


Unix 便利性和 Windows 解决方案

请注意,以下所有解决方案均基于 npm run env,这可确保设置所有必要的环境变量,就像在项目的 package.json 文件中使用 @ 预定义的运行脚本时一样987654333@ 或 npm run-script <script>.
这些环境修改包括:

  • $(npm prefix -g)/node_modules/npm/bin/node-gyp-bin 和项目目录的./node_modules/.bin 子目录(临时)添加到$PATH 环境变量,这是依赖项的CLI 的符号链接所在的位置。
  • 定义多个反映项目设置的npm_* 环境变量,例如npm_package_version 以及npm / node 环境。

类 Unix 平台的便捷解决方案:

以下两种解决方案均基于别名,在 (a) 的情况下,它是使用 脚本的更轻量级的替代方案>,在 (b) 的情况下,是允许修改当前 shell 环境的先决条件(尽管也可以使用 shell 函数)。

为方便起见,将这些别名添加到您的 shell 配置文件/初始化中 文件

(a) 每次调用帮助器:

定义

alias nx='npm run-script env --'

允许您通过添加 nx 来临时调用您的命令;例如:

nx mocha --recursive test/**/*.js --compilers js:babel-register

(b) 每会话一次配置命令:

alias npmenv='npm run env -- $SHELL'

运行 npmenv 进入设置了 npm 环境的子 shell,允许在该子 shell 中直接(仅按名称)调用相关 CLI。
换句话说,按如下方式使用:

cd ~/some-npm-project
npmenv # after this, you can run dependent CLIs by name alone; e.g., `mocha ...`
# ... run your project-specific commands
exit  # exit the child shell before you switch to a different project

Windows 解决方案:

(a) and (b):请注意,Windows(与类 Unix 平台上的类 POSIX shell 不同)不(直接)支持传递环境变量范围仅限于单个命令,因此下面的命令,即使通过特定命令执行(案例(a)),也总是修改会话的环境(案例(b))。

PowerShell(也适用于 Unix 版本):

将以下函数添加到您的$PROFILE(特定于用户的配置文件脚本):

function npmenv($commandIfAny) {
  npm run env -- |
    ? { $_ -and $_ -notmatch '^>' -and $_ -match '^[a-z_][a-z0-9_]+=' } | 
      % { $name, $val = $_ -split '='; set-item -path "env:$name" -value $val }
  if ($?) {
    if ($commandIfAny) {
      & $commandIfAny $Args
    }
  } 
}

cmd.exe(常规命令提示符,常被误称为“DOS 提示符”):

创建一个名为npmenv.cmd的批处理文件,将其放在%PATH%的文件夹中,并定义如下:

@echo off
:: Set all environment variables that `npm run env` reports.
for /f "delims==; tokens=1,*" %%i in ('npm run env ^| findstr /v "^>"') do set "%%i=%%j"
:: Invoke a specified command, if any.
%*

用法cmd.exe 和 PowerShell):

对于用例 (b),简单地调用 npmenv 不带参数;之后,您可以仅通过名称 (mocha ...) 调用依赖的 CLI。

对于用例 (a),在您的命令前添加 npmenv;例如:

 npmenv mocha --recursive test/**/*.js --compilers js:babel-register

警告:如前所述,npmenv第一次 调用 - 无论是否有参数 - 都会在会话的剩余部分修改 %PATH% / $env:PATH 变量。

如果您在同一会话中切换到不同的项目,请务必再次运行 npmenv(至少一次),但请注意,这会将 附加 目录添加到 %PATH%,因此您如果它不是现在当前项目的已安装依赖项,您仍然可能最终意外运行以前项目的可执行文件。

在 PowerShell 中,您实际上可以结合这两种解决方案来获得不同的 (a) 和 (b) 功能:将上面的 *.cmd 文件定义为不同的命令(使用不同的名称例如nx.cmd),您只使用带参数 (a),并重新定义 PowerShell 函数以用作无参数环境仅修改补充 (b)。
这是可行的,因为 PowerShell 总是在不能影响当前 PowerShell 会话环境的 进程中运行 *.cmd 文件。


关于问题范围和此答案的说明:

OP 的问题是关于在给定项目的上下文中调用 已安装的依赖包的 CLI,仅通过可执行名称来临时调用 - 正如 npm 允许您在添加到package.json 文件中scripts 键的命令。

问题不是关于使 CLI全球可用(通过使用 npm install -g 安装它们)。

事实上,如果你想编写模块化、自包含的包,不要依赖于全局安装的包。相反,让所有依赖包成为你项目的一部分:使用npm install --save(用于运行时依赖)和npm install --save-dev(用于仅开发时间的依赖) - 请参阅https://docs.npmjs.com/cli/install

特别是,如果给定的 CLI 已作为 依赖项 安装,则全局安装它以及(可能是不同的版本)不仅是多余的,而且要求何时执行哪个版本的混淆。

【讨论】:

  • @halfer:公平点 - 我已经删除了请不要投反对票,除非你理解用例说明(这是由推定的报复投票提示的) ,但我保留了 this-is-not-about-global-installation 说明,因为我认为这是一个有用的前期说明。
【解决方案2】:

我已将这个小脚本作为~/bin/npm-cmd 保存在我的计算机上:

#!/bin/bash
PATH="$(npm bin):$PATH" "$@"

然后运行npm-cmd <i>PROGRAM</i> …<i>ARGS</i> 应该首先在./node_modules/.bin 中查找<i>PROGRAM</i>,然后再返回正常查找。

【讨论】:

  • 这是一个实用但不完整的解决方案,但是在许多情况下可能仍然有效。它是不完整的,因为它仅部分复制了npm 在调用npm testnpm run-script 时设置的环境:npm 不仅在$PATH 前面附加了一个文件夹,而且还设置了许多npm_* 环境基于 package.json 内容和 npm / Node.js 环境的变量; npm run env 展示了完整的环境。
【解决方案3】:

注意:此答案适用于所有平台。

npm i -g mocha

mocha --recursive test/**/*.js --compilers js:babel-register

Mocha CLI documentation(滚动到使用部分)。


如果您想从 CLI 将 npm 包作为可执行文件运行,正确的方法是通过 package.json bin 条目。它允许您将项目中的文件映射为可执行文件。与普通脚本一样,文件的第一行应该以shebang 开头,如果是节点脚本,则应以#!/usr/bin/env node 开头。

通过将这些文件映射到您的 package.json bin 中,您将能够从其他包脚本部分调用它们,或者在使用 npm install -g &lt;package&gt; 全局安装您的包时从 cli 调用它们。 bin 节点可以是一个对象,它将脚本名称映射到项目中的脚本文件,如果您想将多个脚本作为可执行文件添加到您的路径,或者您想要添加的单个文件的字符串路径充当您的包的可执行文件。

这实际上是您的示例中 mocha 使用的 exact same mechanism

没有这个,mocha 在全局安装时永远不会出现在您的路径中,并且无法通过您的脚本部分访问。

【讨论】:

  • bin 键描述了项目的自己的可执行文件(当项目本身安装时要“导出”哪些可执行文件)。相比之下,这个问题是关于调用项目的 dependencies 的 CLI,它们被符号链接到 ./node_modules/.bin 子目录中,同时 developing 一个包。
  • 如果您想从 cli 调用 mocha,许多节点专家会不同意您的方法。正确的做法是npm i -g mocha。这样做的目的是完成这个问题,将 mocha 作为可执行文件添加到您的路径中。之所以可行,是因为 mocha 在其自己的 package.json 的 bin 部分中进行了标记。依靠您建议的方法根本无法跨平台。
  • 确实,我的方法仅适用于类 Unix 平台(我已添加注释),但这是一个单独的问题。 OP 希望仅通过名称直接从外壳调用给定项目的现有依赖项的 CLI,无论它们可能是什么。在全球范围内重新安装依赖项不仅是繁重的工作,而且还会带来麻烦(而且,如前所述,bin 键用于不同的目的)。
  • 这实际上是 bin 的 exact 目的 - 将脚本作为可执行文件添加到您的路径(路径上下文由您是否传递全局标志表示,省略添加到项目脚本中的路径,并将添加传递到您的 shell)。在要添加到路径中的包上使用 npm i -g 并不比在 bash 配置文件中为其添加别名更麻烦。如果您不再希望它出现在您的路径中,解决方法很简单npm rm -g mocha
  • 不,bin 键并不是为了帮助您调用包的 依赖项 的 CLI,它用于定义包 实现的 CLI (出口)。包的依赖项,包括它们的 CLI,安装在子树 node_modules 中,这是 npm runscripts 键中定义的任务中仅按名称引用它们时找到它们的位置。 OP的问题是关于如何在shell中直接拥有同样方便的调用,而不需要使用预定义的任务和npm run
猜你喜欢
  • 2014-04-03
  • 1970-01-01
  • 2016-04-17
  • 2020-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-08
  • 1970-01-01
相关资源
最近更新 更多