背景
因为业务需求需要开发一个npm包,但没有一个npm开发规范,也没有一个开发npm的模板,看了已有的npm包。是一种百花齐放的状态,所以这里想完成一个npm开发套件,让开发者可以开箱即用。
目标
完成一个npm套件,让开发者开箱即用。那么这个套件应有包含以下功能:
- 统一的代码规范
- 统一的协作规范
- 零配置
- 自动化npm包创建【下篇实现】
- 一键发布发布
- 发布套件-cli 【下篇实现】
根据上面目标,这里给出了整个套件的架构
其实看到这里,你可能发现,npm和web或者其他应用其实类似,这里看完了你都了解了,配置其他类型的应用也很容易了。
认识NPM
虽然天天用这个,但是不是真的都完全了解了呢,不是吧!哈哈,这里有一篇很好的文章,还有这篇
包管理 – lerna
因为要开发一系列相关npm包, 这里用的是lerna,作为包管理工具。这里有篇文章介绍lerna最佳实践。
初始化项目
yarn add global lerna
mkdir <dir-name>
cd dir-name
lerna init
复制代码
经过上面步骤,会在自定义目录里生成,以下目录和文件
packages/
lerna.json
package.json
复制代码
安装依赖
对于新项目,安装包,独立模式下都需要加上 -W
lerna add husky # 给所有包都安装husky
lerna add husky -D --scope=@xxxx/package-name # 指定给 @xxxx/package-name 包安装husky
# 更多信息, 查看 lerna add -h
复制代码
对于老项目
lerna bootstrap # 安装所有包的依赖
lerna bootstrap --scope @xxxx/package-name # 安装自定包下面的所有依赖
# 更多信息,查看 lerna bootstrap -h
复制代码
由于包之间可能存在共同的包,不需要我们重复安装,我们可以这样
lerna bootstrap --hoist # 把公共包装在根目录下的node_modules,这样降低依赖安装、管理成本
复制代码
还可以简化使用成本,修改配置文件
// lerna.json 这样 lerna bootstrap 等价于 lerna bootstrap --hoist
{
"command": {
"bootstrap": {
"hoist": true
}
}
}
复制代码
统一代码规范
因为项目中可能用到TS 或 JS,所以 eslint 需要支持两种语法的lint检查。而这些检查是针对所有项目的,所以在根目录安装ESlint相关包
ESlint 检查JS
安装依赖
yarn add -D eslint
复制代码
新增配置.eslintrc.js
, 完成eslint 对JS的语法检查
// .eslintrc.js
module.exports = {
extends: ['eslint:recommended'],
env: {
browser: true,
node: true,
},
parserOptions: {
ecmaVersion: 2019,
sourceType: 'module',
}
};
复制代码
ESLint 检查TS
安装依赖
yarn add -D typescript @typescript-eslint/eslint-plugin @typescript-eslint/parser
复制代码
配置.eslintrc.js
, 完成对eslint对TS的检查
// .eslintrc.js
module.exports = {
parser: '@typescript-eslint/parser',
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
plugins: ['@typescript-eslint'],
parserOptions: {
ecmaVersion: 2019,
sourceType: 'module',
},
env: {
browser: true,
node: true,
}
};
复制代码
Prittier
安装依赖
yarn add -D prettier eslint-config-prettier eslint-plugin-prettier
复制代码
修改.eslintrc.js
// .eslintrc.js
module.exports = {
parser: '@typescript-eslint/parser',
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'],
plugins: ['@typescript-eslint', 'prettier'],
parserOptions: {
ecmaVersion: 2019,
sourceType: 'module',
},
env: {
browser: true,
node: true,
}
};
复制代码
需要注意的是:由于eslint和prettier 都会对代码的格式做校验。那么就会有冲突
- eslint-config-prettier 插件,可以让eslint使用prettier规则进行检查,并使用–fix选项。
- eslint-config-prettier 插件,之前说了eslint也会检查代码的格式,这个插件就是关闭所有不必要或可能跟prettier产生冲突的规则。
新增.prettierrc
{
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 120,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"overrides": [
{
"files": ".prettierrc",
"options": { "parser": "json" }
}
]
}
复制代码
统一协作规范
对改动文件做ESLint检查
首先安装可以提供各种git钩子的husky
yarn add -D husky
复制代码
注意默认你安装的应该是V6,husky配置发生了很大的改变,现在市面大都是V6以下的配置方式,所以你的husky配置不生效
npx husky install # 会在根目录生成 .husky文件夹,里面存放 githook 文件
复制代码
安装lint-staged
, 顾名思义,他使用来对git add
后的代码做lint检查。
yarn add -D lint-staged
复制代码
修改package.json
{
// other config
"lint-staged": {
"*.{js,ts}": [
"eslint --fix",
]
}
}
复制代码
新增pre-commit hook
,对staged
代码做eslint
校验
npx husky add .husky/pre-commit "npx lint-staged"
复制代码
Commit 信息规范化
这里要对每个人提交信息规范化,同时也方便后续自动化生成changelog
安装规范化commit的依赖
yarn add -D @commitlint/cli @commitlint/config-conventional
复制代码
新建commitlint.config.js
文件。
module.exports = {
extends: ['@commitlint/config-conventional']
}
复制代码
新增commit-msg hook
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
复制代码
自动生成changelog
前面我们规范化了我们的提交信息,这里我们希望利用我们的提交信息,在发布版本后,能自动生成changelog
。
安装依赖
yarn add -D commitizen cz-lerna-changelog
复制代码
修改package.json
// package.json 新增config 配置
{
"config": {
"commitizen": {
"path": "./node_modules/cz-lerna-changelog"
}
}
}
复制代码
至此我们完成了,所有代码规范和协作规范上的配置。我们可以正常开发npm包了。
打包工具 – rollup
我们一般都是用TS,或者ES6来开发npm包,但大部分应用都还是需要es5才能正常运行,所以我们需要一款打包工具。这里是为了从来没用过rollup,所以选择了rollup
看社区还有个基于rollup的、零配置的Bili,看起来更香
a word on Bili. Bili is just a nice, Rollup-based and zero-config (by default) bundler with built-in support for ES-Next and CSS
进入子包,安装依赖
cd packages/your-subpackage
yarn add -D bili rollup-plugin-typescript2
复制代码
在子包根目录下新建配置文件,点击了解更多 Bili 官网
// config/bili.base.js
module.exports = {
input: 'src/index.ts',
output: {
moduleName: 'Package',
fileName: '[name][ext]',
},
plugins: {
typescript2: {
useTsconfigDeclarationDir: true,
},
// 这里是fix @rollup/plugin-replace 剔出的warning
replace: {
preventAssignment: true,
},
},
};
// config/bili.dev.js
import merge from 'rollup-merge-config';
import baseConfig from './bili.base';
module.exports = merge(baseConfig, {
output: {
// 这里输入dev 模式下配置
},
env: {
ENV: 'development',
},
});
// config/bili.prod.js
import merge from 'rollup-merge-config';
import baseConfig from './bili.base';
module.exports = merge(baseConfig, {
output: {
// 这里输入prod 模式下配置
minify: true,
sourceMap: false,
},
env: {
ENV: 'production',
},
});
复制代码
开发生产模式的配置都在这了,是不是炒鸡简单。
修改子包的package.json
{
"scripts": {
// other npm scripts
"build": "rm -rf dist/ && bili --config config/bili.prod.js",
"start": "bili --watch --config config/bili.dev.js"
},
}
复制代码
至此,所有项目配置完成。可以尽情的开发npm包了。
发布
配置发布脚本
由于bili
或者rollup
只是负责打包,如果我们需要发布npm包,需要做以下几件事:
- 将包目录下
package.json
拷贝到dist
- 将
changelog.md
和readme.md
拷贝到dist
ok,很容易,子包根目录新建build.sh
文件。
cp package.json dist/
cp CHANGELOG.md dist/
cp README.md dist/
# 如果npm是ts项目,则将typings拷到dist
tsconfig=$(cd `dirname $0`; pwd)"/tsconfig.json"
typingsDir=$(cd `dirname $0`; pwd)"/typings/"
if [ -f "$tsconfig" ] && [ -d "$typingsDir" ]; then
cp -r typings dist/
fi
复制代码
这里typings
是啥?
这里存放npm自己的声明文件。
在根目录下增加命令行
// package.json
// 这里要做三件事:
// 1. 更新版本号
// 2. 执行子包下面的build.sh,【因为这里要是最终的版本号,所以要在lerna version 后】
// 3. 发布
{
"scripts": {
// other npm scripts
"prePublish": "npx lerna exec ./build.sh",
"pub:test": "npx lerna version prerelease --preid=beta --yes && npm run prePublish && npx lerna publish from-git --yes", // 发beta包
"pub:patch": "npx lerna version patch --yes && npm run prePublish && npx lerna publish from-git --yes", // 发 patch 包
"pub:minor": "npx lerna version minor --yes && npm run prePublish && npx lerna publish from-git --yes", // 发 minor 包
"pub:major": "npx lerna version major --yes && npm run prePublish && npx lerna publish from-git --yes" // 发 major 包
},
}
复制代码
执行发布命令
npm run pub:test # 发beta
npm run pub:xxx # 发其他正式包
复制代码
补充
lerna.json
这里给出lerna.json
完整代码
{
"packages": [
"packages/*"
],
"command": {
"bootstrap": {
"hoist": true
},
"version": {
"conventionalCommits": true,
"noCommitHooks": true
},
"publish": {
"conventionalCommits": true,
"message": "chore(release): publish %s"
}
},
"ignoreChanges": ["**/__tests__/**", "**/*.md"],
"version": "independent"
}
复制代码
提取ts相同配置
在根目录配置一份tsconfig.base.json
{
"compilerOptions": {
"module": "esnext",
"lib": ["esnext", "dom"],
"strict": true,
"declaration": true,
"esModuleInterop": true,
"moduleResolution": "Node"
},
"files": []
}
复制代码
在子包根目录配置子包的tsconfig
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"baseUrl": "./",
"declarationDir": "./typings"
},
"include": ["src/**/*"],
"exclude": [
"node_modules",
"**/__tests__/*",
"**/__e2e__/*"
],
}
复制代码