1
头图

pnpm workspace monorepo 这篇文章中讲述了一个单仓多包项目应该如何创建管理。但该项目可能会有多人共同开发,而每个人的开发习惯都不尽相同,所以在规范协商后还需要借助工具对项目进行约束,以确保该项目的开发不会有过多的个人风格。

代码格式化与 Git 仓库工具

接下来,我们在项目中将使用以下工具约束管理项目代码:

  • eslint
  • eslint-config-prettier
  • eslint-plugin-prettier
  • prettier
  • husky
  • lint-staged
  • commitzen
  • @commitlint/cli
  • @commitlint/config-conventional
  • cz-conventional-changelog

eslintprettier 大家都知道是用来规范代码和格式化代码的,在 vscode 中很常用,但如果团队中有人不用 vscode,而是用记事本这种软件,你又该如何应对呢?这时候就需要用到这两个工具了。

eslint-config-prettiereslint-plugin-prettier 两个是解决 eslintprettier 配置冲突的,想要详细解释请查看官网。

husky 是用来管理 git hooks 的,比如说在代码上传前对代码进行格式化,又或者对 commit-msg 进行校验等。

lint-staged 是用来过滤文件的。通常我们在项目中只改了一个文件,但运行代码格式化 prettier 时会对整个项目进行格式化,这会造成时间/资源的浪费,因此我们需要用这个工具对文件过滤,只格式化改动的的文件。

commitzen@commitlint/cli@commitlint/config-conventionalcz-conventional-changelog 几个工具都是规范 git commit 内容的。


实操

接着上一篇文章新建的项目,对其进行约束管理。

安装代码格式化工具

仅安装 eslintprettier 并不能让格式化符合我们的预期,因此我们还要安装 eslint 的插件来更好的对代码开发。

pnpm add -w -D eslint prettier eslint-config-prettier eslint-plugin-prettier @typescript-eslint/parser @typescript-eslint/eslint-plugin

新建这几个文件并填入相关配置

  • .eslintrc
  • .prettierrc
  • .eslintignore
  • .prettierignore

.eslintignore.pretterignore 都是用来过滤文件不执行检验和格式化的,因此填入的内容是一样的。

node_modules/
dist/
pnpm-lock.yaml
README.md

.eslintrc

{
  "root": true,
  "parser": "@typescript-eslint/parser",
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:@typescript-eslint/eslint-recommended",
    "prettier"
  ],
  "plugins": ["@typescript-eslint", "prettier"],
  "rules": {
    "prettier/prettier": "error",
    "arrow-body-style": "off",
    "prefer-arrow-callback": "off"
  }
}

.prettierrc

.prettierrc 的配置需要团队之间去协商,这里仅代表我个人,具体的参数配置可以查看 prettier 文档1

{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "printWidth": 120
}

此时代码格式化工具已配置完毕,我们在根目录的 package.json 添加指令尝试一下

{
    ...,
    "scripts": {
        ...,
        "lint": "eslint ./ --ext .ts,.js,.jsx,.tsx,.json --max-warnings=0",
        "format": "prettier --config .prettierrc . --write"
    }
}

指令已经添加,解释一下这两个指令:

  1. lint 指令,使用 eslint 对当前目录及所有子目录中包含 ts,js,jsx,tsx,json 后缀的文件都进行代码检测,具体参数请查看 eslint 官方文档2
  2. format 指令,使用 prettier 的配置文件 .prettierrc 对当前目录及所有子目录中的文件都进行文件格式化,具体参数请查看 prettier 官方文档1

目前我们的代码是这样的

image.png

这里我们单独运行指令看看

pnpm run lint

image.png

通过截图可以看出,代码极其不规范,Eslint 报了一大堆错误,该指令目前符合我们的预期,接下来运行 format 指令

pnpm run format

image.png

很好,文件都进行了格式化,该指令也符合我们的预期,此时再次运行 pnpm run lint 指令进行代码检查。

image.png
image.png

发现位于 packages/space-play/index.ts 文件有个变量没有用到,先删除掉再运行 pnpm run lint 指令。

image.png

此时的代码检查已经完成。

但是有个问题,修改了文件后每次都要单独运行 pnpm run lintpnpm run format 指令,这对我们的开发很不方便,因此我们要引入一个工具 onchange 帮我们监听文件的存储变动,而后自动格式化文件。

pnpm run -w -D onchange

安装好后在根目录的 package.json 文件新增一条指令

{
    ...,
    "scripts": {
        "format-auto": "onchange **/* -- prettier --config .prettierrc --write {{changed}}"
    }
}

运行指令看看会有什么变化

pnpm run format-auto

image.png

可以看到 onchange 工具已经在监听,并且手动修改了 apps/play1/index.ts 文件后,执行了 prettier 格式化。

至此,代码检查和格式化已经完成。

Git 仓库相关

项目中采用 husky 工具对 git hooks 进行管理,这里用到的钩子为 pre-commitcommit-msg

pnpm add -w -D husky

添加 husky 钩子有两种方式,一种是通过命令添加,一种是通过 .huskyrc 文件添加。

文件添加的方式是在根目录新建 .huskyrc 文件,并在里面写入钩子,钩子的值时指令运行,这里不做叙述。

{
    "hooks": {
        "pre-commit: "",
        "commit-msg": ""
    }
}

Git Hook: Pre-Commit

我们采用的方式是通过命令添加,这里先添加 pre-commit 钩子用于在 git commit 前执行 eslint 代码检查,如果不通过则取消 git commit

npx husky add .husky/pre-commit "pnpm run lint"

指令已经添加,试一试添加一条 git commit:

image.png

好的,通过截图可以看到,在添加 git commit 时已经运行了 pnpm run lint 指令,下面我们将通过新增一个工具 lint-staged 在进行 git-commit 时对文件进行过滤,仅对本次修改的文件进行代码检查。

pnpm add -w -D lint-staged

新建一个文件 .lintstagedrc.js,为什么这个文件要添加一个 .js 的后缀?因为在这个文件里需要写一点代码对文件进行过滤:

const { ESLint } = require('eslint');

const removeIgnoredFiles = async (files) => {
  const eslint = new ESLint();
  const ignoredFiles = await Promise.all(files.map((file) => eslint.isPathIgnored(file)));
  const filteredFiles = files.filter((_, i) => !ignoredFiles[i]);
  return filteredFiles.join(' ');
};

module.exports = {
  '*': async (files) => {
    const filesToLint = await removeIgnoredFiles(files);
    return [`eslint ${filesToLint} --max-warnings=0`];
  },
};

添加了代码后,打开 .husky/pre-commit 文件,将里面的内容替换:

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

pnpm run lint # 这条指令删掉
npx lint-staged # 新增该指令

在进行完上面的操作后,我们再次添加一条 git commit:

image.png

可以看到,lint-staged 已成功过滤文件并执行了 prettier 文件格式化和 eslint 代码检查。

Git Hook: Commit-Msg

为什么要添加 commit-msg 钩子?设想一下,某天你团队中的某个人在提交代码的时候仅写了个 ... ,你应该如何知道他修改了什么文件?时间长了之后,你再问他,他还记得这次的提交修改了什么东西吗?

所以,为了避免上述类似问题和规范化,我们需要对 commit-msg 进行限制,并借助 cz 工具帮我们问答式提交。

commit-msg 进行限制,我们需要安装这些包 @commitlint/cli, @commitlint/config-conventional

pnpm add -w -D @commitlint/cli @commitlint/config-conventional

安装后新建 .commitlintrc.json 文件并写入相关规则,具体规则请自行搜索

{
  "extends": ["@commitlint/config-conventional"],
  "rules": {
    "scope-empty": [2, "never"]
  }
}

然后添加 husky 钩子在进行 git commit 时对提交内容校验。

npx husky .husky/commit-msg 'npx --no -- commitlint --edit "$1"'

此时,限制 git commit -m 的钩子已经准备完毕,我们来添加一条 git commit 试试:

git add .
git commit -m "feat: 添加 commitlint 对 git commit 提交内容限制"

image.png

可以看到这条 git commit 已经被阻止了,原因就是在内容中我没有指定范围。
现在 commit-msg 已经被加了限制,那么我们安装 cz 工具对提交内容规范化,看看能不能通过。

需要安装的工具有 commitizen, cz-conventional-changelog:

pnpm add -w -D commitizen cz-conventional-changelog

安装完后新建 .czrc 文件并添加内容:

{
  "path": "cz-conventional-changelog"
}

然后再到根目录 package.json 添加一条新的指令:

{
    ...,
    "scripts": {
        ...,
        "cz": "cz"
    }
}

每次执行完 git add . 之后,我们可以通过运行 pnpm run cz 命令进行问答式的 commit-msg
image.png

从截图中可以看到,commit-msg 的内容已经规范化并通过了 commitlint 拦截。

至此,代码检查、文件格式化、版本控制提交消息规范已完成。


项目代码 - Monorepo Example


观炎
23 声望1 粉丝

每天都在摸鱼.....