在 Monorepo 中对代码进行规范(husky + lint-staged)

前言

简单讲一下需要使用到的一些工具库

husky

github.com/typicode/hu...

Modern native git hooks made easy

Husky improves your commits and more 🐶 woof!

也就是说,husky为我们提供了一系列的git hooks,用于在进行git操作时的一些预处理等等

lint-staged

github.com/lint-staged...

Run linters against staged git files and don't let 💩 slip into your code base!

专门用于在通过 git 提交代码之前,对暂存区的代码执行一系列的格式化

Biome

个人也非常推荐

不是很喜欢 eslint + prettier 那一套,麻烦,而且要装一堆插件

Biome

One toolchain for your web project

通过一个工具就可以完成包括 format、lint等等的工具链(速度也非常快,内部使用rust编写的)

顺便说一下,之前 prettier 也发起了一个 rust化的竞赛,biome也获得了最终的奖金🎉🎉🎉

Biome formatter wins the Prettier challenge | Biome (biomejs.dev)

monorepo

这边使用的是 pnpm + turbo 组合实现的

目录结构

包含三个分区

vbnet 复制代码
packages:
  - 'apps/*'
  - 'packages/*'
  - 'examples/*'

接下来我们就直接进行主题

lint

在 packages 下创建一个lint包,用于使用以上内容

以下是目录结构

首先我们先安装所需的库

bash 复制代码
pnpm add husky lint-staged -D
# 注意biome需要全局安装
pnpm add --save-dev --save-exact @biomejs/biome -w

接下来我们来依依说明每个文件

biome.json

默认的一些配置,后续可以根据每个项目的不同去扩展,默认配置可以通过 extends 去导入

json 复制代码
{
  "$schema": "<https://biomejs.dev/schemas/1.4.1/schema.json>",
  "files": {
    "ignore": ["dist/*", "node_modules/*", ".vscode/*", "turbo/*"]
  },
  "organizeImports": {
    "enabled": true,
    "ignore": []
  },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": true,
      "a11y": {
        "useKeyWithClickEvents": "off",
        "useKeyWithMouseEvents": "off"
      }
    },
    "ignore": []
  },
  "formatter": {
    "enabled": true,
    "ignore": [],
    "indentStyle": "space",
    "lineWidth": 120,
    "indentWidth": 2,
    "lineEnding": "lf"
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "single",
      "jsxQuoteStyle": "single",
      "semicolons": "asNeeded",
      "trailingComma": "all",
      "arrowParentheses": "asNeeded",
      "bracketSpacing": true,
      "quoteProperties": "asNeeded"
    }
  }
}

在其他项目中,通过如下方式引用(如果是npm安装,则是node_modules)

json 复制代码
{
  "extends": ["./packages/lint/biome.json"]
}

prepare.js

通过npm提供的hook,可以在安装该包的时候执行prepare指令,我们可以给prepare挂载一个指令去执行该文件

需要在 package.json 中添加这条指令

json 复制代码
{
  "name": "@turbo/lint",
	// ...
  "scripts": {
    "prepare": "node prepare.js",
  },
  // ...
}

prepare.js文件的目的就是找到git仓库所在的目录,并为其添加上.husky文件夹

首先我们需要根据目录向上去递归遍历找到最终的git仓库文件夹,找不到的话则报错(注意:husky必须安装在git所在的目录,不然会报错)

javascript 复制代码
const path = require('path')
const husky = require('husky')
const fs = require('fs')

const isDiskRoot = dirPath => {
  const pathObj = path.parse(dirPath)
  return pathObj.root === dirPath
}

const isGitFolder = dirPath => {
  const gitFolderPath = `${dirPath}\\.git`
  try {
    const stat = fs.statSync(gitFolderPath)
    return stat.isDirectory() || stat.isFile()
  } catch (error) {
    return false
  }
}

const findNearestGit = dirname => {
  if (isDiskRoot(dirname)) {
    return null
  }
  if (isGitFolder(dirname)) {
    return dirname
  }
  return findNearestGit(path.resolve(dirname, '..'))
}

const gitFolder = findNearestGit(__dirname)

通过以上方式,最终就能找到 git 所在的目录了

接下来,我们根据git目录去执行husky install指令,并添加pre-commit钩子

javascript 复制代码
const { execSync } = require('child_process')

const gitFolder = findNearestGit(__dirname)
if (!gitFolder) {
  throw new Error('postinstall husky but cannot find .git file, please git init first!')
}

// 执行指令
execSync(`cd ${gitFolder} && husky install`)

// 找到创建好的.husky所在的目录
const preCommitFile = path.join(gitFolder, '.husky', 'pre-commit')
// 找到runLintStaged.mjs文件位置,后续需要执行该文件,进行项目格式化
const lintStagedJs = path.join(__dirname, 'runLintStaged.mjs').replace(/\\/g, '/')
// 最后将钩子设置到husky中
husky.set(
  preCommitFile,
  `echo ''\nnode "${lintStagedJs}"`,
)

通过这种方式,我们就可以在安装lint包的同时,帮我们创建好.husky文件夹,以及pre-commit钩子,最后在钩子中添加 node lintStagedJs 来实现格式化

runLintStaged.mjs

同理,我们需要在该文件中也要找到git仓库所在目录,然后在根目录去执行lint来进行整体的格式化

注意,有一点不一样,因为使用的是mjs,所以需要手动设置一下__dirname

javascript 复制代码
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const gitFolder = findNearestGit(__dirname)

找到之后,就可以去执行格式化命令了

javascript 复制代码
import lintStaged from 'lint-staged'

lintStaged({
  config: { '*.{js,ts,jsx,tsx,json,css,md}': `biome format --write ${gitFolder}\\.` },
}).then(res => {
  if (res) {
    console.log('🚀 ~ set lintStaged successfully!')
  } else {
    throw new Error('🚀 ~ biome failed! please fix and retry!')
  }
})

成效

最终,我们就可以在其他项目中去引用lint包了

json 复制代码
{
	"devDependencies": {
		"@turbolc/lint": "workspace:*",
	}
}

或者也可以将库发送到npm仓库中,去直接下载

安装lint库之后,我们就会看见如下界面

在pre-commit文件中,也能看到对应的hook


内容

shell 复制代码
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
node "...(目录)/turbo-web/packages/lint/runLintStaged.mjs"

后面,在你提交commit的时候,就会触发该钩子了

相关推荐
广州华水科技5 分钟前
深度测评2026年单北斗GNSS位移监测系统推荐,与高口碑变形监测设备一同引领行业新风尚
前端
生成论实验室21 分钟前
《事件关系阴阳博弈动力学:识势应势之道》第四篇:降U动力学——认知确定度的自驱演化
人工智能·科技·神经网络·算法·架构
SamDeepThinking40 分钟前
并发量就算只有2,该上锁还得上呀
java·后端·架构
Alice-YUE1 小时前
【js高频八股】防抖与节流
开发语言·前端·javascript·笔记·学习·ecmascript
Sam_Deep_Thinking1 小时前
如何让订单系统和营销系统解耦
java·架构·系统架构
ting94520001 小时前
Micro1 超详细深度解析:架构原理、部署实战、性能评测与落地应用全指南
人工智能·架构
该昵称用户已存在2 小时前
从边缘计量到碳足迹追踪:MyEMS 开源一体化架构的全栈拆解
架构·开源
是上好佳佳佳呀2 小时前
【前端(十一)】JavaScript 语法基础笔记(多语言对比)
前端·javascript·笔记
福大大架构师每日一题2 小时前
ollama v0.22.1 重大更新全解析:新增Poolside集成、模型推荐机制与多架构适配
架构·ollama
该昵称用户已存在3 小时前
以开源筑基,架构先行——深度拆解 MyEMS 微服务能源管理系统的技术内核
微服务·架构·开源