【从入门到提桶】package.json 之 npm run scripts 命令传参

3,039 阅读3分钟

问题

问1:如何使以下命令更优雅?

// package.json

{
  "scripts": {
    "build:dev": "vue-tsc --noEmit && vite build --mode development",
    "build:pro": "vue-tsc --noEmit && vite build --mode production",
  }
}

答1:

// package.json

{
  "scripts": {
    "build": "vue-tsc --noEmit && vite build",
    "build:dev": "npm run build -- --mode development",
    "build:pro": "npm run build -- --mode production",
  }
}

问2:如果命令变成下面这样怎么优化呢?

// package.json

{
  "scripts": {
    "build:dev": "vue-tsc --noEmit && vite build --mode development && echo build end",
    "build:pro": "vue-tsc --noEmit && vite build --mode production && echo build end",
  }
}

答2:这样传?

// package.json

{
  "scripts": {
    "build": "vue-tsc --noEmit && vite build && echo build end",
    "build:dev": "npm run build -- --mode development",
    "build:pro": "npm run build -- --mode production",
  }
}

错,vite build 接收不到 --mode 参数

那我调换一下顺序不就行了?

"build": "vue-tsc --noEmit && echo build end && vite build"

真是大聪明,不过要求 echo build end 要在最后执行😏

这种情况有没有优雅的解决方案呢😅

分析

1. 先拜读 阮一峰 大佬的文章 npm scripts 使用指南

npm 脚本的原理非常简单。每当执行npm run,就会自动新建一个 Shell,在这个 Shell 里面执行指定的脚本命令。因此,只要是 Shell(一般是 Bash)可以运行的命令,就可以写在 npm 脚本里面。

向 npm 脚本传入参数,要使用--标明。

2. 分析答1中 npm run build -- --mode development 是怎么执行的:

-- 表示传参,a -- b 表示 b 作为字符串拼接在 a 后面,解析后的命令为 vue-tsc --noEmit && vite build "--mode" "development",所以执行 npm run build:dev 能成功

3. 答2为什么不可以:

根据前面的解释,答2执行 npm run build:dev 解析到的命令为 vue-tsc --noEmit && vite build && echo build end "--mode" "development"

显然这是不行的,vite build 没有正确接收到参数,会以默认 --mode production 执行

当然,调换一下顺序,将 && vite build 放在最后是可行的,"--mode" "development" 参数正好拼接在 vite build 命令后面,但是题目要求不能调换顺序,因为实际就有可能出现这种情况。

类似问题:npm run 执行package.json中的scripts配置时如何参数传递?

4. 简化问题

这个问题本质上就是 npm 命令如何以 -- 之外的方式传参

// 伪代码
// 如果有这样一种传参方式

{
  "scripts": {
    "test": "echo ${参数}",
    "test_": "npm run test 参数",
  }
}

于是这个问题就简化为:执行 npm run test_ 时,能打印出 参数

传参方式

1. npm config 变量

npm Docs: using-npm/config

1.1 第一个 Command Line Flags 就是前面讲到的 -- 方式,我们看第二个 Environment Variables

直接上结果:

// 执行 `npm run test_` 会打印 `123`
{
  "scripts": {
    "test": "echo $npm_config_abc",
    "test_": "npm run test --abc=123",
  }
}

关键在于 --abc=123 会在 npm run 开启的 shell 里创建一个环境变量 npm_config_abc=123

1.2 注意: windows和mac/linux获取变量命令不一样

# windows
echo %npm_config_abc%

# mac/linux
# shell中,变量加{}和不加{}都可以
echo $npm_config_abc
# or
echo ${npm_config_abc}

为消除平台差异,可以使用 cross-var 或者 cross-env 插件

# install
npm i cross-var -D
# or
npm i cross-env -D


# scripts
cross-var echo $npm_config_abc

1.3 注意自定义变量 npm_config_abc 不要和 npm 内置变量冲突

比如 npm_config_mnpm_config_v

1.4 注意和 package.json 中的 config 配置项 区分

// package.json

{
  "config": {
    "qwe": "456"
  }
}

这个 config 变量是通过 npm_package_config_qwe 获取的

2. shell 变量

既然 npm run 执行的是 shell 命令,那么就可以通过创建 shell 变量来传参,

{
  "scripts": {
    "test": "cross-var echo $abc",
    "test_": "abc=123 npm run test",
  }
}

和 npm config 变量不同的是:

  1. 定义变量不需要加 --,并且需要在使用变量之前定义
  2. 使用变量不需要加 npm_config_

3. 其他

3.1 在这个回答中,有提到用shell函数的方式

{
  "scripts": {
    "test": "test(){ echo $1 } test",
    "test_": "npm run test -- 123",
  }
}

实测是不可行的,执行 npm run test_ 会报错

sh: -c: line 1: syntax error: unexpected end of file

按理说思路是对的,可能写法有问题,希望有大佬指教一下🙇

3.2 利用 shell 脚本的 $n 传参方式是否可行

// 打印 123 456
{
  "scripts": {
    "test": "echo $2 $1",
    "test_": "npm run test -- 123 456",
  }
}

打印顺序不对,说明传参失败,而打印出来的 '123 456' 是因为 -- 123 456 被拼接在了 test 后面

Over