husky 和 lint-staged 在 monorepo 中的配置

huskylint-staged 应该算是在基于 node.js 的前后端项目中的标配了,它们可以帮助我们在提交代码时自动执行一些代码检查、格式化等操作,从而保证代码的质量。

在纯粹的前端或后端项目中,配置起来都很简单,但是在 monorepo 中,由于项目结构的复杂性,配置起来相对有些区别。这篇文章主要记录一下我的配置过程。

在我的项目中,使用 pnpm workspace 管理依赖,项目结构举例如下:

.
├── apps
│   ├── backend
│   │   ├── package.json
│   │   └── src
│   └── frontend
│       ├── package.json
│       └── src
├── package.json
├── pnpm-lock.yaml
└── pnpm-workspace.yaml

lint-staged

由于前后端采用的语言、技术和框架大概率不同,比如前端可能是 typescript + react,后端可能是 typescript + express,对于 eslint 等配置必然有所区别,所以并不建议在根目录下安装 lint-staged,而是在各个子项目中安装。

pnpm -F backend add -D lint-staged
pnpm -F frontend add -D lint-staged

然后在各个子项目中配置 lint-stagedbackend 配置举例如下:

// backend/package.json
{
  "lint-staged": {
    "*.ts": ["eslint"]
  }
}

frontend 配置举例如下:

// frontend/package.json
{
  "lint-staged": {
    "*.{ts,tsx}": ["eslint"],
    "*.{css,scss}": ["prettier --write"]
  }
}

husky

由于 husky 的原理是基于 git hooks,而一个 monorepo 中的 git hooks 只会在根目录下的 .git/hooks 中,所以 husky 应该安装在根目录下。

pnpm -w add -D husky

按照 官方文档 配置完成。

lint-staged + husky

我们需要在 .husky -> pre-commit 中执行 lint-staged 相关的操作,为此我们需要在每个子项目的 package.json 中添加一个 script,用于执行 lint-staged,举例如下:

// backend/package.json
{
  "scripts": {
    "lint-staged": "lint-staged"
  }
}
// frontend/package.json
{
  "scripts": {
    "lint-staged": "lint-staged"
  }
}

编辑 .husky -> pre-commit,添加如下内容:

##!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

pnpm -F backend run lint-staged
pnpm -F frontend run lint-staged

至此,无论是在前端还是后端项目中提交代码,都会自动执行 lint-staged,从而保证代码质量。而且 lint-staged 的配置分散在各个子项目中,可以按照各自的需求配置如 prettierstylelint 等,互不干涉,非常灵活。