将命令行参数传递给 package.json 中的 npm 脚本

Posted

技术标签:

【中文标题】将命令行参数传递给 package.json 中的 npm 脚本【英文标题】:Pass command line args to npm scripts in package.json 【发布时间】:2018-12-25 14:25:05 【问题描述】:

我的 package.json 中有以下脚本:

"scripts": 
    "vumper": "node node_modules/vumper/index.js",
    "format": "prettier --single-quote -width=80 --write package.json"
 ,

“vumper”包接受命令行参数(例如“dv”)。我想做的是有一个命令可以连续运行这两个命令。

基本上,我希望能够运行:

npm run vumber dv

然后

npm run format

但在一个命令中,类似于

npm run my-build dv

它将运行上述两个命令,正确接受命令行参数“dv”并将其传递给第一个 npm run vumper。这可能吗?

【问题讨论】:

【参考方案1】:

简答:

基本上,您想要的是有一个类似这样的 npm 脚本,其中 <arg-here> 是通过 CLI 提供的;

...
"scripts": 
  "my-build": "npm run vumper <arg-here> && npm run format",
  ...
,
...

但是,不幸的是,npm 没有内置功能来实现这一点。

特殊的 npm 选项 --,(有关此选项的更多信息,请参阅下面解决方案 1 的末尾),只能用于将参数传递给 END 的脚本,但不进入中间。因此,如果您的两个命令的顺序相反,则可以像这样使用 -- 选项:

...
"scripts": 
  "my-build": "npm run format && npm run vumper --",
  ...
,
...

要克服没有内置功能将参数传递到脚本的 MIDDLE 的限制,请考虑以下解决方案:

    对于仅 Bash 的解决方案,请参阅 “解决方案 1” 部分。

    如果需要跨平台支持,请遵循“解决方案 2” 部分中描述的解决方案。


解决方案 1 - Bash(MacOS/Linux/ 等)

package.jsonscripts 部分中配置您的 my-build 脚本以调用 Bash shell function,如下所示:

package.json

...
"scripts": 
  "my-build": "func()  npm run vumper \"$1\" && npm run format; ; func",
  "vumper": "node node_modules/vumper/index.js",
  "format": "prettier --single-quote -width=80 --write package.json"
,
...

说明:

名为 func 的 Bash 函数执行以下操作:

    首先运行npm run vumper &lt;arg&gt;。其中&lt;arg&gt; 将是通过 CLI 传递的 shell 参数。它在脚本中使用$1 引用(即第一个positional parameter/argument)。 随后它通过命令npm run format 运行名为format 的脚本。

这两个npm run 命令使用&amp;&amp; 运算符链接,因此第二个npm run format 命令只有在初始npm run vumper &lt;arg&gt; 命令成功完成时才会运行(即返回0 退出代码)。

正在运行my-build 脚本:

要通过 CLI 调用 my-build,您需要运行:

npm run my-build -- dv

注意:

    在这种情况下,结尾的 dv 部分是将传递给您的 vumper 脚本的参数。

    必须在参数之前指定特殊选项--。 docs 将 -- 选项描述为:

    ...getopt 使用特殊选项-- 来分隔选项的结尾。 npm 会将-- 之后的所有参数直接传递给你的脚本:...参数只会传递给npm run 之后指定的脚本,而不是任何前置或后置脚本。


解决方案 2 - 跨平台

对于跨平台解决方案(与 Bash、Windows 命令提示符/cmd.exe 和 PowerShell 等一起成功运行的解决方案),您需要使用 nodejs 帮助脚本,如下所示。

run.js

让我们将nodejs脚本命名为run.js并将其保存在项目根目录中,与package.json处于同一级别。

const execSync = require('child_process').execSync;

const arg = process.argv[2] || 'dv'; // Default value `dv` if no args provided via CLI.

execSync('npm run vumper ' + arg, stdio:[0, 1, 2]);
execSync('npm run format', stdio:[0, 1, 2]);

package.json

配置您的 my-build 脚本以调用 run.js,如下所示:

...
"scripts": 
  "my-build": "node run",
  "vumper": "node node_modules/vumper/index.js",
  "format": "prettier --single-quote -width=80 --write package.json"
,
...

正在运行my-build 脚本:

根据解决方案 1,要通过 CLI 调用 my-build,您需要运行:

npm run my-build -- dv

说明:

run.js 利用process.argv 获取通过CLI 传递的参数(例如dv)。如果在运行npm run my-build 时未提供参数,则默认值(即dv)将传递给vumper npm-script。

run.js 还利用child_process.execSync(...)shell-out/调用两个npm run 命令。

【讨论】:

难道没有一个cli npm包可以简单地做类似const args = process.argv.slice(3);const command = args.reduce((res, arg, index) =&gt; res.replace(new RegExp(`\\$$ index + 1 `, 'g'), arg),process.argv[2]);const execSync = require('child_process').execSync;execSync(command, stdio: [0, 1, 2] );之类的事情吗?这样我就可以用 \" 包装任何脚本,并在它前面加上这样的 cli 并运行任何带有有序参数的脚本......【参考方案2】:

Npm 现在 内置选项可将 cli 参数直接传递给脚本。 cli 参数存储在带有前缀npm_config_&lt;flagname&gt; 的环境变量中,它们需要非常严格的语法,格式为--&lt;flagname&gt;=&lt;flagvalue&gt;

例子:

 "my-build": "npm run vumper %npm_config_myflag% && npm run format",

在终端中,运行npm run my-build --myflag=my_value执行npm run vumper my_value &amp;&amp; npm run format

注意:

要在 npm 脚本中引用环境变量,您必须使用特定于平台的语法,即 Windows 中的 %npm_config_myflag% 或 Linux 中的 $npm_config_myflag

更新:

为避免与用于配置 npm 本身的 npm_config 变量发生冲突风险,只需为您的参数添加一个唯一前缀,例如您的应用程序名称。

潜在冲突是一个非常常见的问题,它适用于许多情况:任何应用程序都可以使用其他应用程序已经使用的环境变量;因此,环境变量通常以应用程序名称为前缀(例如 NVM_HOME、JAVA_HOME)。但是这种潜在的冲突并不是避免使用环境变量的好理由。我认为这同样适用于 npm params / npm_config env vars。 The doc 没有提及冲突的风险,暗示我想他们应该像往常一样管理。

【讨论】:

平台特定对我来说很关键。我在 Windows 上尝试 $npm_config_myflag 并失去了理智 npm 也为内部目的创建了自己的名为 npm_config_* 的环境变量。请务必为标志/选项选择一个名称(例如--myflag=*),该名称不会覆盖内部使用的名称,因为这可能会产生不需要的结果。您可以 cd 到您的项目目录并运行 npm run env 以列出 npm 添加的环境变量。例如。传递--tag=foo 不是一个好主意,因为npm 添加了npm_config_tag 环境变量(它的值通常设置为latest)。通过使用这种方法,不确定将来可能会添加哪个内部 npm_config_* vars npm,而您最终可能会被覆盖。 您可以参考这种方法,但正如@RobC 在评论中所解释的那样,使用这种方法可能很危险。所以不是关于这个问题的解决方案,但一般来说,如果某人的要求是在命令末尾附加参数,那么在这种情况下不要使用环境变量方法,他/她可以使用 npm run -- .【参考方案3】:

我的首选方法是使用环境变量:


  "scripts": 
    "ncc-build": "ncc build $ACTION/src/index.ts -o $ACTION/dist",
    "build:pr-changelog": "ACTION=pr-changelog npm run ncc-build",
  

它应该在 UNIX 系统中工作。我不确定 Windows 平台的兼容性。

【讨论】:

是的,它在 windows 平台上不起作用【参考方案4】:

执行此操作的不同方法 - 深入您的依赖链:

npm 脚本部分:

"test:local": "cross-env-shell UPDATE_BASELINE=false UPDATE_MODULE=%npm_config_vizdifsingle% run-p koa:ci wdio:local",
"test:remote": "cross-env-shell UPDATE_BASELINE=false  UPDATE_MODULE=%npm_config_vizdifsingle% run-p localtunnel:start koa:ci wdio:remote"

通过使用 crossenv 和 npm 的值放置,您可以将 args 传递给 env.args

像这样:

npm run test:local --vizdifsingle=some,value,or,values

它将提供给您

process.env.npm_config_update_module

【讨论】:

以上是关于将命令行参数传递给 package.json 中的 npm 脚本的主要内容,如果未能解决你的问题,请参考以下文章

将相同的参数传递给 package.json 中多个脚本的节点

如何将带有前导斜杠的参数传递给 npm 脚本

通过 npm 命令将参数传递给 Angular 6 应用程序 [重复]

将命令行参数传递给函数

将命令行参数传递给调用带有装饰器参数的装饰函数的函数

如何将命令行参数传递给 WinForms 应用程序?