npm script 笔记 (from掘金小册)
1.2 运行多个 npm script
*比 npm 内置的多命令运行机制更好用的解决方案:npm-run-all
- && 串行多个命令 & 并行多个命令
1
2Expected linebreaks to be 'LF' but found 'CRLF' linebreak-style
linebreak-style: ["error", "windows"]
1 | "semi": "error", js语句末尾强制要加分号 如果不加的话删除这一项即可 |
npm-run-all 还支持通配符匹配分组的 npm script1
npm i npm-run-all -D
1 | "test": "npm-run-all lint:* mocha" |
并行执行的时候,我们并不需要在后面增加 & wait,因为 npm-run-all 已经帮我们做了
1.3 给 npm script 传递参数和添加注释
“lint:js”: “eslint *.js”,
“lint:js:fix”: “npm run lint:js – –fix”,
在命令前面加上注释(# … \n):
“test”: “# 运行所有代码检查和单元测试 \n npm-run-all –parallel lint:* mocha”
####调整 npm script 运行时日志输出
npm runn test -s (简略信息,没有信息就是最好的信息)
–loglevel silent,或者 –silent,或者更简单的 -s 来控制
npm t -d (详细信息)
需要使用 –loglevel verbose,或者 –verbose,或者更简单的 -d 来控制
2.1 使用 npm script 的钩子
举例来说,运行 npm run test 的时候,分 3 个阶段:
- 检查 scripts 对象中是否存在 pretest 命令,如果有,先执行该命令;
- 检查是否有 test 命令,有的话运行 test 命令,没有的话报错;
- 检查是否存在 posttest 命令,如果有,执行 posttest 命令;…
增加覆盖率收集
我们需要引入两个新工具:
- 覆盖率收集工具 nyc,是覆盖率收集工具 istanbul 的命令行版本,istanbul 支持生成各种格式的覆盖率报告;
- 打开 html 文件的工具 opn-cli,是能够打开任意程序的工具 opn 的命令行版本,作者是前端社区非常高产的 Sindre Sorhus,它在 npm 上发布了超过 1000 个包,并且质量都很不错…
1
npm i nyc opn-cli -D
然后在 package.json 增加 nyc 的配置,告诉 nyc 该忽略哪些文件。最后是在 scripts 中新增 3 条命令:
- precover,收集覆盖率之前把之前的覆盖率报告目录清理掉;
- cover,直接调用 nyc,让其生成 html 格式的覆盖率报告;
- postcover,清理掉临时文件,并且在浏览器中预览覆盖率报告;
1 | diff --git a/package.json b/package.json |
2.2 在 npm script 中使用变量
- 查看所有的npm环境变量: npm run env | grep npm_package | sort
- 调用环境变量(直接在 npm script 给想要引用的变量前面加上 $ 符号即可):
1
2
3{
"dummy": "echo $npm_package_name"
}
2.3 实现命令行自动补全(Windows下还没支持?)
2.3.1 当npm script太多了,可以查找
- npm run | less (这里的 less 不是 css 领域的 less,而是 linux 里面的工具)
- 输入 / 和 对应的npm script 的关键字 敲回车
- 选中对应的npm script(选中即为复制)
- shift + zz 退出shell
- npm run 上面复制的npm script
2.3.2
- npm run 空格 tab (查看当前需要run的命令)
3.1 实现 npm script 跨平台兼容 (mac Windows)
3.1.1 文件系统操作的跨平台兼容
- rimraf 或 del-cli,用来删除文件和目录,实现类似于 rm -rf 的功能;
- cpr,用于拷贝、复制文件和目录,实现类似于 cp -r 的功能;
- make-dir-cli,用于创建目录,实现类似于 mkdir -p 的功能;
- npm i rimraf cpr make-dir-cli -D
1
2- "cover:cleanup": "rm -rf coverage && rm -rf .nyc_output",
+ "cover:cleanup": "rimraf coverage && rimraf .nyc_output",
3.1.2 用 cross-var 引用变量
Linux 和 Windows 下引用变量的方式是不同的,Linux 下直接可以用 $npm_package_name,而 Windows 下必须使用 %npm_package_name%,我们可以使用 cross-var 实现跨平台的变量引用,具体步骤如下:
第 1 步,安装 cross-var 为开发依赖:1
2npm i cross-var -D
# npm install cross-var --save-dev
第 2 步,改写引用变量 npm script,具体 diff 如下:1
2
3
4"scripts": {
- "cover:archive": "mkdir -p coverage_archive/$npm_package_version && cp -r coverage/* coverage_archive/$npm_package_version",
+ "cover:archive": "cross-var \"mkdir -p coverage_archive/$npm_package_version && cp -r coverage/* coverage_archive/$npm_package_version\""
}
3.1.2 用 cross-env 设置环境变量
第 1 步,添加 cross-env 到开发依赖:1
2npm i cross-env -D
# npm install cross-env --save-dev
第 2 步,改写使用了环境变量的 npm script:1
2
3
4 "scripts": {
- "test": "NODE_ENV=test mocha tests/",
+ "test": "cross-env NODE_ENV=test mocha tests/",
},
3.2 把庞大的 npm script 拆到单独文件中
安装依赖
1
2npm i scripty -D
# npm install scripty --save-dev准备目录和文件
1
2
3
4
5mkdir -p scripts/cover
// 里命名为 scripts 是 scripty 默认的,实际上是可以自定义的
touch scripts/cover.sh
touch scripts/cover/serve.sh
touch scripts/cover/open.sh
创建的文件对应npm script 里面1
2
3
4
5"scripts": {
"cover": "scripty",
"cover:serve": "scripty",
"cover:open": "scripty"
},
给所有脚本增加可执行权限是必须的,否则 scripty 执行时会报错1
chmod -R a+x scripts/**/*.sh
- 对应脚本里面写入相应的命令行操作
1
2
3#!/usr/bin/env bash
http-server coverage_archive/$npm_package_version -p $npm_package_config_port
3.3 用 node.js 脚本替代复杂的 npm script
安装 shelljs 依赖
1
2npm i shelljs -D
# npm install shelljs --save-dev创建 Node.js 脚本
1
touch scripts/cover.js
用 Node.js 实现同等功能
shelljs 为我们提供了各种常见命令的跨平台支持,比如 cp、mkdir、rm、cd 等命令1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16const { rm, cp, mkdir, exec, echo } = require('shelljs');
const chalk = require('chalk');
console.log(chalk.green('1. remove old coverage reports...'));
rm('-rf', 'coverage');
rm('-rf', '.nyc_output');
console.log(chalk.green('2. run test and collect new coverage...'));
exec('nyc --reporter=html npm run test');
console.log(chalk.green('3. archive coverage report by version...'));
mkdir('-p', 'coverage_archive/$npm_package_version');
cp('-r', 'coverage/*', 'coverage_archive/$npm_package_version');
console.log(chalk.green('4. open coverage report for preview...'));
exec('npm-run-all --parallel cover:serve cover:open');对应修改npm script
1
2
3
4
5
6"scripts": {
"test": "cross-env NODE_ENV=test mocha tests/",
- "cover": "scripty",
+ "cover": "node scripts/cover.js",
"cover:open": "scripty"
}
4.1 文件变化时自动运行 npm script
1. 单元测试自动化
幸运的是,mocha 本身支持 –watch 参数
“watch:test”: “npm t – –watch”,
2. 代码检查自动化
安装项目依赖
1
2npm i onchange -D
# npm install onchange --save-dev添加 npm script
1
2
3+ "watch": "npm-run-all --parallel watch:*",
+ "watch:lint": "onchange -i \"**/*.js\" \"**/*.less\" -- npm run lint",
"watch:test": "npm t -- --watch",
关于改动的几点说明:
- watch:lint 里面的文件匹配模式可以使用通配符,但是模式两边使用了转义的双引号,这样是跨平台兼容的;
- watch:lint 里面的 -i 参数是让 onchange 在启动时就运行一次 – 之后的命令,即代码没变化的时候,变化前后的对比大多数时候还是有价值的;
- watch 命令实际上是使用了 npm-run-all 来运行所有的 watch 子命令;
4.2 在 Git Hooks 中执行 npm script
Git 在代码版本管理之外,也提供了类似 npm script 里 pre、post 的钩子机制,叫做 Git Hooks,钩子机制能让我们在代码 commit、push 之前(后)做自己想做的事情
安装项目依赖
1
npm i husky lint-staged -D
添加 npm script
1
2
3
4
5
6"scripts": {
+ "precommit": "npm run lint",
+ "prepush": "npm run test",
"lint": "npm-run-all --parallel lint:*",
"lint:js": "eslint *.js"
}用 lint-staged 改进 pre-commit
1
2
3
4
5
6
7
8
9
10
11
12
13"scripts": {
- "precommit": "npm run lint",
+ "precommit": "lint-staged",
"prepush": "npm run test",
"lint": "npm-run-all --parallel lint:*",
},
+ "lint-staged": {
+ "*.js": "eslint",
+ "*.less": "stylelint",
+ "*.css": "stylelint",
+ "*.json": "jsonlint --quiet",
+ "*.md": "markdownlint --config .markdownlint.json"
+ }
4.4 使用 npm script 实现构建流水线
例如:项目目录结构1
2
3
4
5
6
7
8client
├── images
│ └── schedule.png
├── index.html
├── scripts
│ └── main.js
└── styles
└── main.css
准备构建目录
1
+ "prebuild": "rm -rf dist && mkdir -p dist/{images,styles,scripts}",
准备脚本目录
1
2
3
4mkdir scripts/build
touch scripts/build.sh
touch scripts/build/{images,styles,scripts}.sh
chmod -R a+x scripts
1 | scripts |
- 构建
图片构建
1
2
3
4
5
6// 安装依赖
npm i imagemin-cli -D
// 然后在 scripts/build/images.sh 中添加如下内容:
imagemin client/images/* --out-dir=dist/images
// 然后在 package.json 中添加 build:images 命令:
+ "build:images": "scripty",样式构建
1
2
3
4
5
6
7
8npm i cssmin -D
// scripts/build/styles.sh
for file in client/styles/*.css
do
lessc $file | cssmin > dist/styles/$(basename $file)
done
// 然后在 package.json 中添加 build:styles 命令:
+ "build:styles": "scripty",JS 构建
1
2
3
4
5
6
7
8npm i uglify-es -D
// 然后在 scripts/build/scripts.sh 中添加:
for file in client/scripts/*.js
do
./node_modules/uglify-es/bin/uglifyjs $file --mangle > dist/scripts/$(basename $file)
done
// 然后在 package.json 中添加 build:scripts 命令:
+ "build:scripts": "scripty",
尝试运行 npm run prebuild && npm run build:scripts,然后观察 dist 目录的变化,应该能看到被 uglify-es 压缩后的代码。
- 资源版本号和引用替换
为了实现这个过程,我们需要引入两个小工具:
- hashmark,自动添加版本号
- replaceinfiles,自动完成引用替换,它需要家版本号过程的输出作为输入
首先安装依赖:1
npm i hashmark replaceinfiles -D
然后在 scripts/build/hash.sh 中添加如下内容:1
2
3
4
5# 给图片资源加上版本号,并且替换引用
hashmark -c dist -r -l 8 '**/*.{png,jpg}' '{dir}/{name}.{hash}{ext}' | replaceinfiles -S -s 'dist/**/*.css' -d '{dir}/{base}'
# 给 js、css 资源加上版本号,并且替换引用
hashmark -c dist -r -l 8 '**/*.{css,js}' '{dir}/{name}.{hash}{ext}' | replaceinfiles -S -s 'client/index.html' -d 'dist/index.html'
然后在 package.json 中添加 build:hash 命令:1
+ "build:hash": "scripty",
package.json 中添加 build 命令把所有的步骤串起来,完整的 diff 如下:1
2
3
4
5
6
7
8- "client:static-server": "http-server client/"
+ "client:static-server": "http-server client/",
+ "prebuild": "rm -rf dist && mkdir -p dist/{images,styles,scripts}",
+ "build": "scripty",
+ "build:images": "scripty",
+ "build:scripts": "scripty",
+ "build:styles": "scripty",
+ "build:hash": "scripty"
其中 scripts/build.sh 的内容如下:1
2
3
4for step in 'images' 'scripts' 'styles' 'hash'
do
npm run build:$step
done