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"
}
相关推荐
Avan_菜菜4 小时前
Nuxt3 中使用 pnpm 安装的 NuxtImg 使用会提示找不到图片
前端·npm·nuxt.js
抠脚小弟6 小时前
实现vue组件库并发布npm上使用流程
前端·vue.js·npm
HarryHY7 小时前
检查项目中的依赖是否有更新——npm outdated
前端·npm·node.js
持久的棒棒君19 小时前
npm安装electron下载太慢,导致报错
前端·electron·npm
陪我一起学编程1 天前
关于nvm与node.js
vue.js·后端·npm·node.js
前端百草阁2 天前
从npm库 Vue 组件到独立SDK:打包与 CDN 引入的最佳实践
前端·vue.js·npm
夏日米米茶2 天前
Windows系统下npm报错node-gyp configure got “gyp ERR“解决方法
前端·windows·npm
蓝胖子的多啦A梦2 天前
npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚
前端·npm·node.js
qziovv2 天前
Pnpm的使用
npm
XI锐真的烦2 天前
横向对比npm和yarn
前端·npm·node.js