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"
}
相关推荐
奔跑吧邓邓子2 小时前
npm包管理深度探索:从基础到进阶全面教程!
前端·npm·node.js
朗朗乾坤.py20 小时前
Hadoop HDFS命令操作实例
hadoop·hdfs·npm
哇咔咔哇咔1 天前
【科普】什么是npm和pip?它们之间有什么异同?
前端·npm·pip
太阳火神的美丽人生1 天前
Vant WeApp 开启 NPM 遇到的问题总结
前端·npm·node.js
Minyy113 天前
小程序-使用npm包
前端·小程序·npm·node.js
Mrs_Lupin3 天前
npm与包
前端·npm·node.js
潜心专研的小张同学3 天前
pnpm依赖安装失败解决|pnpm项目从一个文件夹复制到另一个文件夹运行失败问题解决-以vbenAdmin项目为例
前端·javascript·vscode·npm·vue·pnpm
西西o4 天前
Linux服务安装node,npm与yarn
linux·运维·npm
世俗ˊ4 天前
npm、yarn、pnpm对比
前端·npm·node.js
qq_427506084 天前
前端项目npm install报错解决的解决办法
前端·npm·node.js