npm script基础

本文总结了npm script的原理和基础用法,包括执行多个命令、生命周期钩子、npm变量等等......

[What] 什么是npm script

package.json里面定义的scripts字段就是npm script

json 复制代码
{
  // ...
  "scripts": {
    "build": "node build.js"
  }
}

scripts字段是一个对象,它的每一个属性,对应一段脚本

原理

当我们在项目里面运行npm run xxx的时候,实际步骤如下

  1. package.json里面读取scripts对象

  2. 以传给npm run的第一个参数作为键,在scripts对象里面获取对应的值作为接下来要执行的命令,如果没找到就直接报错

  3. 新建一个shell,将当前目录的node_modules/.bin子目录加入PATH变量(这就意味着,当前目录的node_modules/.bin子目录里面的所有脚本,都可以直接用脚本名调用,而不需要加上路径)

  4. 在这个shell中执行上述命令

npm script的退出码也遵守shell脚本的规则,如果退出码不是0,则视为脚本执行失败

[Why] 为什么需要npm script

使用npm script主要有以下几个好处:

  • 编写单一职责的命令,提高代码复用性

  • 不同项目的脚本,只要功能相同,就可以有相同的对外接口,提升可读性、降低项目的上手门槛

  • 通过连接多个命令,可以打造自动化的工作流

    • 减少重复地手动执行命令的频率

    • 避免人为因素(例如:单词拼写错误/参数输入错误/遗漏某个命令等)导致命令执行失败,提高命令执行的成功率

[How] 如何使用npm script

执行多个命令

在当前目录下执行多个不同命令

  1. 串行执行

使用&&符号连接多个命令,当前序命令失败时,后续命令都不会执行

json 复制代码
"script": {
  "lint:js": "eslint *.js",
  "lint:css": "eslint *.less",
  "lint:all": "npm run lint:js && npm run lint:css"
}
  1. 并行执行

使用单个&符号来连接多个命令

json 复制代码
"script": {
  "lint:js": "eslint *.js",
  "lint:css": "eslint *.less",
  "lint:all": "npm run lint:js & npm run lint:css"
}

在不同目录下执行多个相同命令

对于monorepo项目,一个项目的目录下可能会有多个workspace(工作区),可以在项目根目录的package.json里面进行如下配置

json 复制代码
"workspaces": [
   "packages/a",
   "packages/b",
   "packages/c"
]

运行npm script时可以通过-w来指定工作区,在(单个、多个或者所有 )工作区的上下文中串行运行指定的命令

几个简单的例子

  • packages/a目录下执行npm build
  • packages/apackages/b目录下执行npm build
  • 在所有workspaces下执行npm build
bash 复制代码
# 在packages/a目录下执行npm build,以下2条命令完全等价
npm run build -w=packages/a
cd packages/a && npm run build

# 在packages/a和packages/b目录下执行npm build
npm run build -w=packages/a -w=packages/b

# 在所有workspace下执行npm build
npm run build -ws

传递参数

向npm脚本传递参数需要用 -- 标明

json 复制代码
"script": {
  "lint:js": "eslint *.js",
  "lint:js:fix": "npm run lint:js -- --fix",
}

当执行npm run lint:js:fix的时候,实际运行的命令是eslint *.js --fix

添加注释

可以直接在命令前面以#开头加上注释

json 复制代码
"script": {
  "test": "# 这是一条注释 \n exit1 "
}

调整日志输出

执行npm script的过程中会产生一些日志输出,我们可以通过--loglevel来设置日志输出级别

从简洁到详细的排序如下:默认的输出级别为notice

输出级别 完整命令 简写 备注
silent --loglevel silent -s 或 --slient "没有消息就是最好的消息"
error --loglevel error
warn --loglevel warn -q 或 --quiet
notice --loglevel notice
http --loglevel http
timing --loglevel timing
info --loglevel info -d
verbose --loglevel verbose --d 或 --verbose 详细打印出每个步骤的信息,有利于排查问题
silly --loglevel silly -ddd

我们也可以通过npm config set loglevel来修改默认的输出级别

除常规的notice级别以外,推荐根据实际情况使用silent和verbose这两种模式

例如:执行命令npm view @arco-desing/web-react查看包的信息

输出级别 输出截图
verbose
notice
silent 没有任何输出

生命周期钩子

npm script是具有生命周期机制的,具体来说就是prepost

  • pre:用于在某些动作之前执行其他的动作
  • post:用于在某些动作之后执行其他的动作

npm默认提供以下钩子

  • prepublish,postpublish
  • preinstall,postinstall
  • preuninstall,postuninstall
  • preversion,postversion
  • pretest,posttest
  • prestop,poststop
  • prestart,poststart
  • prerestart,postrestart

执行npm run build时,会分3个阶段串行执行

  1. 检查是否存在prebuild命令,如果有,就执行该命令,否则进入第2阶段
  2. 检查是否存在build命令,如果有,就执行运行build命令,若执行成功则进入第3阶段,否则就会报错
  3. 检查是否存在postbuild命令,如果有,就执行该命令

使用npm变量

预定义变量

npm内置了很多变量,可以通过执行npm run env来查看完整的预定义变量列表,例如:

  • npm_package_name:当前项目名称
  • npm_package_version:当前项目版本号

在bash中读取变量,只需要加一个前缀$即可(变量不区分大小写)

在windows平台下需要使用%前缀

json 复制代码
"script": {
  "echo:package:name": "echo $npm_package_name",
}

在js文件中读取变量,需要通过process.env对象来读取(必须是全小写)

json 复制代码
"script": {
  "echo:package:name": "node test.js",
}

// test.js
console.log(process.env.npm_package_name)

自定义变量

我们可以通过在package.json里面配置自定义属性来添加自定义变量

例如,在package.json里面配置默认的端口号

json 复制代码
{
  "name": "app",
  "version": "1.0.0",
  "config": { 
    "port": "9000" 
  }, 
  "scripts": {
    "echo:port": "echo $npm_package_config_port"
  }
}      

这样我们就可以通过npm_package_config_port来读取端口号

把npm script拆分到单独文件中

当npm script不断累积的时候,全部放在package.json里面会导致可读性大大降低

因此,我们可以把npm script剥离到单独的文件中

shelljs

shelljs提供了各种常见命令的跨平台支持,例如cd、mkdir、exec等命令

通过npm install shelljs -D安装以后,我们就可以在 . js文件中编写shell命令

javascript 复制代码
const { env } = require('shelljs')

const npm_package_version = env['npm_package_version']

console.log('package verson is:', npm_package_version)

zx

zx是Google推出的、可用于以javascript编写脚本的工具

通过npm install zx -D安装以后,我们就可以在 .mjs文件中使用javascript的语法来编写shell命令

  • 使用``await $`xxx```的方式来执行shell命令
  • 提供常用shell命令对应的内置函数(例如:cd、chalk、fs)
  • 所有同步的逻辑(语句)都可以使用原生的javascript语法

例如:将"本地分支关联到远程分支"这个功能封装成一个script命令

git push --set-upstream remoteGitNam currentBranchName

要实现这个功能,我们就需要获取当前分支的名称和远程仓库的名称

获取当前分支的名称直接使用git symbolic-ref --short HEAD即可

而远程仓库的名默认是origin,但用户可以通过git remote rename命令进行修改

因此我们可以通过git remote -v列举出已进行关联的所有远程仓库,再通过正则表达式去匹配我们想要关联的那个远程仓库即可

代码示例如下:

javascript 复制代码
// setUpStreamBranch.mjs
#!/usr/bin/env zx

// 当前分支的名称
const currentBranchName = await $`git symbolic-ref --short HEAD`

// 远程仓库的名称,用默认值兜底
let remoteName = 'origin'

const result = await $`git remote -v`

const lines = result.stdout.split('\n')

const regExp = new RegExp('\(push\)$')

// 远程仓库中,当前项目的具体名称
const gitName = 'xxx'

for (const line of lines) {
  if (line.includes(gitName) && regExp.test(line)) {
    remoteName = line.split('\t')[0]
  }
}

// 本地分支关联远程分支
await $`git push --set-upstream ${remoteName} ${currentBranchName}`

package.json里面添加命令

json 复制代码
"scripts": {
  "set:upstream:branch": "zx setUpstreamBranch.mjs"
}
相关推荐
ac.char5 小时前
在 Ubuntu 系统上安装 npm 环境以及 nvm(Node Version Manager)
linux·ubuntu·npm
ZBY520316 小时前
【Vue】 npm install amap-js-api-loader指南
javascript·vue.js·npm
RAY_CHEN.10 小时前
vue3 pinia 中actions修改状态不生效
vue.js·typescript·npm
Ztiddler1 天前
【npm设置代理-解决npm网络连接error network失败问题】
前端·后端·npm·node.js·vue
三天不学习1 天前
前端工程化-node/npm/babel/polyfill/webpack 一文速通
前端·webpack·npm
水w2 天前
Node.js windows版本 下载和安装(详细步骤)
开发语言·前端·windows·npm·node
guokanglun2 天前
npm镜像查看和修改
前端·npm·node.js
你的代码我的心2 天前
解决 npm xxx was blocked, reason: xx bad guy, steal env and delete files
npm
明天最后2 天前
由于Two-Factor Authentication导致github发布npm包失败
前端·npm·github
平芜尽处是春山-3 天前
vue3项目执行npm install下载依赖报错问题排查方法
前端·npm·node.js