持续更新中…… 第一次更新 2025-5-23
1. 📝 package.json 的“巧妙”注释
常用于分组或者注释某一条比较难以阅读命令
比如分组:
{
"scripts": {
"========== Linting ==========": "",
"lint": "biome lint && tsc --noEmit",
"lint:fix": "biome check --write --unsafe",
"prepare": "husky",
"lint-staged": "biome check --staged --fix",
"========== Testing ==========": "",
"test": "vitest run --typecheck",
"test:cov": "vitest run --typecheck --coverage",
"ci": "npm run test:cov",
"========== Publishing ==========": "",
"pub:patch": "npm version patch",
"pub:minor": "npm version minor",
"pub:major": "npm version major",
"preversion": "nvm use 22 && git pull && git push origin HEAD && pnpm i && npm run build-test-lint-concurrently",
"postversion": "npm publish && git push origin HEAD && git push --tags",
"build-test-lint-concurrently": "npm run build && concurrently -n 🧪,🔍 -p name \"npm run test:cov\" \"npm run lint\"",
"publishd": "npm publish --dry-run",
}
}
或解释某条 script:
{
"scripts": {
"bun-test-staged": "bun test $(git status -s | awk '{print $2}')",
"// 失败自动 watch": "",
"test:staged": "bun bun-test-staged || bun bun-test-staged --watch",
}
2. 🧪 仅运行修改或新增的测试文件
{
"scripts": {
"test:staged": "bun test $(git status -s | awk '{print $2}')",
}
}
为什么不用 git diff --name-only? 因为新增文件不会输出。
如果你只想测试某次修改或新增的单测文件,执行 bun test:staged 即可,这在本地测试阶段非常有用。比如存在如下修改:
modified: package.json
modified: src/openApp.edit-last-one-multi-modal.test.tsx
modified: src/openApp.edit.test.tsx
modified: tests/formatHTML.ts
Added: demo # git diff 不会列出新增的文件,只会列出文件夹
将只会执行 test 结尾的两个文件 demo 文件夹内的 test 文件。
bun 很聪明,一股脑将文件给他,自己会过滤出符合测试模式的文件,无需 grep 一大堆正则表达式,而且虽然 git status 不会列出新增的文件,只会列出文件夹,但是 bun 也能自动过滤 😎!
能否更完善点?如果测试文件没有改动但是对应的源码动了,也执行那就更好了。
更完善点
比如修改了 foo.ts,foo.test.ts 也自动执行,有个难点,若仅按照文件名规则 *.test.ts[x] 会有遗漏,还是需要检查引用关系且是 test 后缀的文件。但这样速度就很慢了,需要结合 oxc 或 biome 的快速 parser。
为什么不用
--watch,初始化会执行所有测试
那就过于复杂了,能否一行脚本搞定。
假设我们的单测和源码在一个目录:
❯ tree src/NoData
NoData
├── demo
│ ├── basic.less
│ ├── basic.test.tsx // <-
│ └── basic.tsx
├── index.less
├── index.md
├── index.test.tsx // <-
└── index.tsx
当 NoData 内任意文件修改都能执行对应的单测。
更进一步,仅执行新增或修改的单测以及源文件修改涉及的单测 & 失败自动重试以及 watch:
"// 仅执行新增或修改的单测以及源文件修改涉及的单测 & 失败自动重试以及 watch": "",
"test:staged": "bun test:staged:core || bun test:staged:core --watch",
"test:staged:core": "bun test $(git status -s | awk '{print $2}' | xargs -I {} dirname {} | grep -v '^\\.' | sort -u)",
假设修改如下:
❯ git status -s
M .nvmrc
M .vscode/settings.json
M README.md
M package.json
M src/NoData/demo/basic.tsx
M src/NoData/index.md
M src/NoData/index.tsx
?? src/NoData/demo/basic.less
?? src/NoData/index.less
上面虽然没有改动单测文件,但是源文件有变化也需要执行测试。其实我们只需要“喂给” bun test 改动的文件夹即可。即 src/NoData。
❯ git status -s | awk '{print $2}' | xargs -I {} dirname {} | grep -v '^\.' | sort -u
src/NoData
src/NoData/demo
虽然多输出了一个目录但是 bun 足够“聪明”,已经能满足我们的诉求!
"// 仅执行新增或修改的单测以及源文件修改涉及的单测 & 失败自动重试以及 watch": "",
"test:staged": "bun test:staged:core || bun test:staged:core --watch",
"test:staged:core": "bun test $(git status -s | awk '{print $2}' | xargs -I {} dirname {} | grep -v '^\\.' | sort -u)",
3. 🚚 本地一行命令自动发布 npm 包
{
"scripts": {
"========== Publishing ==========": "",
"pub:patch": "npm version patch",
"pub:minor": "npm version minor",
"pub:major": "npm version major",
"preversion": "nvm use 22 && git pull && git push origin HEAD && pnpm i && npm run build",
"postversion": "npm publish && git push origin HEAD && git push --tags",
"prebuild": "concurrently -n 🧪,🔍 -p name \"npm run test:cov\" \"npm run lint\"",
"publishd": "npm publish --dry-run",
}
}
4. 📦 指定特定包管理器
比如某个老项目必须使用 yarn 安装:
"preinstall": "npx only-allow yarn",
上述钩子会在安装前通过 only-allow 校验包管理器是否是 yarn。
若尝试后发现会拖慢包安装的速度,可改成 pnpx 或 bunx。
5. 🕵️♂️ npm run ... 之前检查 Node.js 版本号
虽然我们已经通过脚本在切换项目目录的时候自动切换版本号,但是若存在多个项目,A 项目仍然可能被其他项目“意外”切换版本号,所以仍然需要在每次运行的时候再次确认下版本号。
假设某个项目必须 Node.js v22 (LTS) 以上版本:
{
"scripts": {
"check-node-version": "node -e \"const version = process.versions.node.split('.')[0]; console.log('\\n', version >= 22 ? '✅ ' + require('chalk').green('Node.js 版本号正确') : (process.exitCode = 1, '❌ ' + require('chalk').bgRed('需 >= v22')), '\\n')\"",
"predev": "check-node-version"
}
}
不限于 dev 之前检查,
predev改成pre{target}即可。
上述一行代码展开后:
const version = process.versions.node.split('.')[0];
console.log(
'\n',
version >= 22 ?
'✅ ' + require('chalk').green('Node.js 版本号正确') :
(process.exitCode = 1, '❌ ' + require('chalk').bgRed('需 >= v22')),
'\n'
)
关于为什么使用 process.exitCode 而非“粗暴”的 process.exit(),详见 process.exit([code]),总结来说就是因为太粗暴,而前者更“优雅”。还有一种优雅方式是 throw Error,针对版本号范围检测可以使用 RangeError。