⚠更新:
目前antfu佬的项目已经不再使用monorepo进行eslint的分包了,但是local-pkg仍然是有用到的。
本文目标:
- 了解如何Anthony Fu大佬是如何用Monorepo的方式撰写eslint配置的
- 接触local-pkg,了解如何一套eslint适配不同项目(react/vue/vanillaJS/ts)
前言
我是一个普通的 antfu(Anthony Fu) 的迷弟。某一天,在研究他的 Vitesse 模板的时候,我看到了他的 eslint 配置竟然如此简单
{
"extends": [
"@antfu",
"@unocss"
]
}
我们都知道 extends 配置是继承已经写好了的eslint的配置,但是不同于我经常在网上看到的一大堆extends、rules还有parser之类的配置,antfu的 extends 只有这一点,并且没有任何其它的配置,这就让我不禁想要了解一下他的具体实现。
目录结构
让我们排除掉一些无关的文件和目录,专注于我们需要的部分
eslint-config
├─ packages # 多个eslint配置包
│ ├─ eslint-config # 项目的入口
│ │ ├─ index.js # extends: "@antfu"时找到的文件
│ │ └─ package.json
│ ├─ eslint-config-basic # 基础、通用的eslint配置
│ │ ├─ index.js
│ │ ├─ package.json
│ │ └─ standard.js
│ ├─ eslint-config-ts # ts项目专用的eslint配置
│ │ ├─ index.js
│ │ └─ package.json
│ ├─ eslint-config-vue # Vue专用的eslint配置
│ │ ├─ index.js
│ │ └─ package.json
│ ├─ eslint-config-react # React专用的eslint配置
│ │ ├─ index.js
│ │ └─ package.json
│ └─ eslint-plugin-antfu # antfu自己写的eslint插件,文章分享中将不涉及
├─ .eslintrc.json # eslint配置
├─ package.json # 项目整体的package.json
├─ pnpm-workspace.yaml # pnpm
├─ README.md
└─ tsconfig.json # typescipt配置
我们点进 packages/eslint-config/index.js进行查看,发现里面的代码无它,只有一个短短的 extends
module.exports = {
extends: [
'@antfu/eslint-config-vue',
],
}
那么这个 @antfu/eslint-config-vue 是从哪儿来的呢,我们进入到 package.json就能看到这其实就是一个包
"dependencies": {
"@antfu/eslint-config-vue": "workspace:*",
...
},
不了解 workspace 的朋友也不用担心,我们之后会讲的。接下来我们直接进入 packages/eslint-config-vue 开始分析吧!
适配js/ts
Anthony Fu的eslint是会自动判断项目是基于js还是ts,然后引入对应的配置的,那么具体是怎么实现的呢?
区分项目类型 - local-pkg 的妙用
进入到 package.json,我们会看到该包入口即为index.js
"main": "index.js", // https://docs.npmjs.com/cli/v9/configuring-npm/package-json#files
"files": [ // https://docs.npmjs.com/cli/v9/configuring-npm/package-json#files
"index.js"
],
然后我们来看下 index.js的代码,大概内容如下
const { isPackageExists } = require('local-pkg') // 引入local-pkg
const TS = isPackageExists('typescript') // 判断
module.exports = {
overrides: [
{
files: ['*.vue'], // 覆盖 .vue 文件的eslint配置,比如parser, rules
...
},
],
extends: [
'plugin:vue/vue3-recommended',
TS
? '@antfu/eslint-config-ts' // 如果是ts项目,extends ts配置
: '@antfu/eslint-config-basic', // 如果不是ts项目, extends 基础配置
],
rules: { ... } // vue项目的规则
}
在这里我们发现他首先从 local-pkg 中引入了 isPackageExists,然后用 isPackageExists 去判断项目中是否使用了ts。
如果使用了ts,就引入 @antfu/eslint-config-ts 配置,没有的话,就引入 @antfu/eslint-config-basic 配置。
antfu佬是只做了ts/js的适配,但是我们知道了原理就可以自己去写一套eslint适配各个技术栈的项目。
简化 extends 写法
那从这里我们也大概知道了只要合理利用 locak-pkg,我们就能够适配多个项目了。那么为什么可以直接继承 @antfu 而不是 @antfu/eslint-config呢,这个其实是归功于eslint自身的extends的设定,比如 extends: 'eslint-config-test' 可以被简化为 extends: 'test',那么简化的规则有哪些呢
- "extends": "eslint-config-test" => "extends": "test"
- "extends": "@scoped/eslint-config" => "extends": "@scoped"
- "extends": "@scoped/eslint-config-test" => "extends": "@scoped/test"
(详情可以参考 eslint官方文档的说明)
项目中的monorepo是如何实践的
package.json中的 workspaces (工作区)
如果设置了 workspaces (也就是工作区),那么你在项目根目录进行 npm install的时候,npm会自动找到 workspaces 中的目录下的package.json,将该package.json下的依赖安装到项目根目录,然后将该目录符号链接到node_modules下。
比如说我现在有一个项目目录结构如下
test-workspaces
├─ packages
│ ├─ child
│ | | index.js
│ └─ └─ package.json
├─ index.js
└─ package.json
执行npm install之后安装对应依赖(express),然后进行符号链接
注意!pnpm不适用package.json下的 workspaces
pnpm-workspace 有什么作用
如果你只在package.json中设置了 workspaces,会发现执行 pnpm install 的时候有一行提示
pnpm天生支持monorepo,你可以在项目根目录下创建 pnpm-workspace.yaml 去指定工作区
packages:
- 'packages/*'
版本号是 workspace: * 意味着什么?
我们在 packages/eslint-config-vue/package.json 中可以看到,两个eslint配置包并没有指定版本,而是 workspace: *
这样是为了告诉npm,使用工作区中的这个包作为依赖项。
换句话来说,在 @antfu/eslint-config-vue 这个项目中,引入 @antfu/eslint-config-basic 其实是从工作区引入的,* 代表了使用该包最新的本地版本。
如何发布到线上
pnpm publish? pnpm -r publish!
适配原理讲完了,工作区的概念也有了,那么接下来就剩下发布了。
我们不能只发布 eslint-config,其它的依赖比如 eslint-config-basic、eslint-config-ts、eslint-config-...这些都需要进行发布,那难道我们需要直接进入到packges里面一个个发布吗?
其实我们只需要一行代码,pnpm就能自动帮我们递归地进行发布
pnpm -r publish
-r 代表 recursive,也就是递归。
手动更改版本号做版本控制? bumpp!
我们不希望每次都手动去修改版本号,antfu佬必然也不希望,那么他是怎么做的呢?项目根目录下的package.json有这样一行script
"scripts": {
"release": "bumpp -r && pnpm -r publish"
},
可以看到在进行publish之前会先执行执行 bumpp -r。其实 bumpp -r就是递归地将一个Monorepo中的项目都进行版本控制。执行 pnpm add -D bumpp 之后再在package.json中添加一句scripts
"scripts": {
"bump": "bumpp -r",
},
执行pnpm bump就可以看到下面的画面(确保你的项目是个git仓库并且已经设置了远程origin)
选择要更新的版本,然后它就会自动进行版本更新、commit、tag、push到远程仓库,遵循 conventional commits 的规范。
如果这篇文章对你有帮助的话,还请动动小手点个免费的赞,这会让我创作更有动力,谢谢你🥳
如果这篇文章哪里有问题的话,也欢迎你指出,我们可以友好讨论🤓
参考资料:
如果你正好在写简历,毛遂自荐一下自己的简历生成器: