从零开发自己的工具库(二)配置 ESLint + Perttier + Husky + Commitlint

上一篇讲了如何使用 TS + Rollup + Jest 创建开发环境。本文和大家分享如何提高代码的规范和质量,让多人协同变得更高效。成品可以参考 utils ,欢迎 star 🤞❤️

ESLint + Perttier 代码规范配置

关于 ESlint 的项目搭建,可以参考 ESLint 非权威配置指北(下),代码规范几乎每个项目都会配置,这里就不再赘述。

最终,我们添加了 .eslintrc.js.eslintignore.prettierrc.js 配置文件,并且添加了以下两条 Scripts 命令:

json 复制代码
{
    "scripts": {
        "lint:eslint": "eslint --cache --max-warnings 0 \"packages/**/*.{js,ts}\" --fix",
        "lint:prettier": "prettier --write \"packages/**/*\""
    }
}

还有一个与 TS 相关的 ESLint 问题,值得拿出来讨论下,请看 FAQ。

FAQ

1. ESLint was configured to run ... However, that TSConfig does not / none of those TSConfigs include this file"

这些错误是由 ESLint 配置请求类型信息引起的,因为在 tsconfig.json 中并未包含 ESLint 的配置文件,所以 TS 并没有对这些文件起作用。

如果你想关闭该错误提示,可以直接在 .eslintignore 中进行忽略,也可以做如下两步操作,解决报错:

  1. 项目目录下新建 tsconfig.eslint.json 文件(与其他 TS 配置区分),将所有配置文件包含进去。

    json 复制代码
    {
        "compilerOptions": {
        "types": ["@types/node"],
        "noEmit": true,
        "allowJs": true
        },
        "extends": "./tsconfig.base.json",
        "include": [
            ".eslintrc.js",
            ".prettier.js",
            "rollup.config.js",
            "jest.config.js",
            "babel.config.js",
        ]
    }
  2. 接着,在 .eslintrc.js 中提供 TS 配置文件的路径。所有需要使用类型信息的规则,都要在此配置:

    js 复制代码
    module.exports = {
        /** ...... */
        parserOptions: {
            /** ...... */
            project: ['./tsconfig.base.json', './tsconfig.eslint.json', './tsconfig.json'],
            tsconfigRootDir: __dirname,
        },
    }

关于该报错的详尽信息,可以查阅 ESLint was configured to run ...

关于 ESLint parserOptions.project 选项的说明,可以查阅 project

关于官方的解决方案示例,可以查阅 tsconfig.eslint.json

配置 Editorconfig

editorconfig 也是用来帮助开发者定义和维护代码风格的。但是它与 Prettier 不同的是,Prettier 是 JS 特有的格式化工具,里面有很多配置项是 JS 语言特有的规范,而 editorconfig 适应性更广泛,它可以跨编辑器(或 )维护统一的代码风格,专注于比较基础的格式化,比如 Tab 缩进、文件编码、末尾换行符等,这些规范与使用哪种编程语言无关。

VSCode 需要安装 EditorConfig 扩展来搭配使用,部分配置如下:

yml 复制代码
# .editorconfig
root = true

[*]
charset = utf-8
indent_style = space              # 空格缩进
indent_size = 4                   # 缩进空格为4个
end_of_line = lf                  # 文件换行符是 linux 的 `\n`
insert_final_newline = true       # 文件末尾添加一个空行
trim_trailing_whitespace = true   # 不保留行末的空格

[*.md]
insert_final_newline = false
trim_trailing_whitespace = false
max_line_length = 0               # 在指定的字符数后强制换行。off 关闭此功能

[package.json]                    # 对 package.json 生效
indent_size = 2

husky + lint-staged + commitlint 提交规范配置

git hooks 前置知识

和其它版本控制系统一样,Git 能在特定的重要动作发生时触发自定义脚本。

有两组这样的钩子:客户端的和服务器端的。 客户端钩子由诸如提交和合并这样的操作所调用,而服务器端钩子作用于诸如接收被推送的提交这样的联网操作。

你可以随心所欲地运用这些钩子。

本文整理部分 hooks 如下:

git hooks 运行时机 说明 工作流 所属类型
pre-commit 在键入提交信息前运行 以非零值退出,Git 将放弃此次提交, 可以利用该钩子,来检查代码风格是否一致 提交 客户端---提交工作流
prepare-commit-msg 在启动提交信息编辑器之前,默认信息被创建之后运行 提交 客户端
commit-msg 接收一个参数, 以非零值退出,Git 将放弃提交。可以在提交通过前验证项目状态或提交信息 提交 客户端
post-commit 在整个提交过程完成后运行 一般用于通知之类的事情 提交 客户端
applypatch-msg 可以用该脚本来确保提交信息符合格式,或直接用脚本修正格式错误。 email 客户端
pre-applypatch 运行于应用补丁之后,产生提交之前 可以用它在提交前检查快照 email 客户端
post-applypatch 运行于提交产生之后 可以用它把结果通知给一个小组或所拉取的补丁的作者 email 客户端
pre-rebase 运行于变基之前 可以使用这个钩子来禁止对已经推送的提交变基 其他 客户端
post-rewrite 被那些会替换提交记录的命令调用 用途很大程度上跟 post-checkoutpost-merge 差不多 其他 客户端
post-checkout git checkout 成功运行后 可以根据你的项目环境用它调整你的工作目录 其他 客户端
post-merge git merge 成功运行后 可以用它恢复 Git 无法跟踪的工作区数据 其他 客户端
pre-push 会在 git push 运行期间, 更新了远程引用但尚未传送对象时被调用 可以在推送开始之前,用它验证对引用的更新操作 其他 客户端
pre-auto-gc 会在垃圾回收开始之前被调用 可以用它来提醒你现在要回收垃圾了,或者依情形判断是否要中断回收 其他 客户端
pre-receive 客户端的推送操作时,最先执行 可以用这个钩子阻止对引用进行非快进(non-fast-forward)的更新,或者对该推送所修改的所有引用和文件进行访问控制 推送 服务端
update 会为每一个准备更新的分支各运行一次 更新 服务端
post-receive 在整个过程完结以后运行 可以用来更新其他系统服务或者通知用户 完结 服务端

这里,我们通常只关注提交工作流 的几个 hooks,用的最多的一个就是 pre-commit 。当然,你可以利用 git commit --no-verify 来绕过这个环节。

配置 husky

Husky 就是一个创建或修改 Git hooks 的工具,它在内部封装了 git hook,允许我们在提交代码时运行一些额外的脚本。比如校验规范,格式化代码等。

初始化 husky

执行 npm i husky -D 安装 Husky 后,我们在 package.json 中添加一个 script:

json 复制代码
{
    "scripts": {
        // ......
        "prepare": "husky install"
    }
}

执行 npm run prepare,会创建 .husky 文件夹,此时 git hook 已被启用。

创建 git hook

接着,我们使用 husky add <file> [cmd] 命令往里面添加 hook,其中:

  • <file>: 指名为 git hook 的文件,通常放在 .husky/ 目录下。比如:.husky/pre-commit ;
  • [cmd]: 指想要运行的命令。比如:npm run lint:eslint

以下是官方示例:

bash 复制代码
npx husky add .husky/pre-commit "npm run test"
git add .husky/pre-commit

此时,.husky 文件夹中会创建一个名为 pre-commit 的 shell 脚本。当你尝试 commit 提交时,pre-commit hook 会提前执行其中的 npm run test,如果检测失败,那么本次提交会被自动阻止。

当然,你可以根据自己的需求修改这个 shell 脚本。

官方推荐

我们还可以使用官方推荐的方法,一次性快速初始化 husky 。

bash 复制代码
npx husky-init && npm install

该命令会自动初始化并生成 pre-commit 脚本,我们只需直接去里面修改内容即可,很方便。

配置 lint-staged

这里有个问题:npm run lint:eslint 会对你自定义的所有文件进行 ESLint 修改,成本显然很高。

所以 lint-staged 的出现允许你每次 commit 时只对当前添加到暂存区(stage)的代码 进行 lint,效率大大提高。正如它的标题:不要让屎溜进你的代码库!

lint-staged 的配置方式有两大种,支持 package.json 或 JSON 和 YML 风格的 .lintstagedrc

我们以 package 为例,在执行 npm i lint-staged -D 安装依赖后,在 package.json 中添加 lint-staged 字段,格式如下:

json 复制代码
{
  "lint-staged": {
    "<glob-pattern>": "<command>"
  }
}

其中 <command> 可以是原生的工具库命令,也可以是 Scripts 里的自定义命令。原生命令可以省略 npm run 前缀,但自定义命令必须是完整的,比如 eslint --fixnpm run lint:eslint

命令的类型可以是单个命令的字符串,也可以多个命令组成的数组。

json 复制代码
{
    "lint-staged": {
        "packages/**/__test__/*.ts": "npm run test",
        "packages/**/*.ts": [
            "npm run lint:eslint", // scripts 自定义的命令
            "prettier --write" // prettier 原生的命令
        ]
    }
}

上述 lint-staged 我们配了两个操作:

  • 所有 packages 下的单元测试文件在提交前,都要执行 jest 测试;
  • 所有 packages 下的 .ts 文件都要执行 eslint 校验以及 prettier 格式化。

不过目前它还不能自动生效,要在 .husky/pre-commit 脚本中修改并添加 npx --no-install lint-staged 命令,这样就生效了。

bash 复制代码
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

echo -e "\033[33m ------------------- 正在对提交的代码执行操作 -------------------- \033[0m"
npx --no-install lint-staged

配置 commitlint

commitlint 用来约束提交信息,规范提交格式。

  1. 安装 npm i @commitlint/config-conventional @commitlint/cli -D

  2. 自定义 commitlint.config.js 规范

    js 复制代码
    module.exports = {
        ignores: [commit => commit.includes('init')],
        extends: ['@commitlint/config-conventional'],
        rules: {
            'body-leading-blank': [2, 'always'], // 主体前有空行
            'footer-leading-blank': [1, 'always'], // 末行前有空行
            'header-max-length': [2, 'always', 108], // 首行最大长度
            'subject-empty': [2, 'never'], // 标题不可为空
            'type-empty': [2, 'never'], // 类型不可为空
            'type-enum': [ // 允许的类型
                2,
                'always',
                [
                    'wip', // 开发中
                    'feat', // 新增功能
                    'merge', // 代码合并
                    'fix', // bug 修复
                    'test', // 测试
                    'refactor', // 重构
                    'build', // 构造工具、外部依赖(webpack、npm)
                    'docs', // 文档
                    'perf', // 性能优化
                    'style', // 代码风格(不影响代码含义)
                    'ci', // 修改项目继续集成流程(Travis,Jenkins,GitLab CI,Circle等)
                    'chore', // 不涉及 src、test 的其他修改(构建过程或辅助工具的变更)
                    'workflow', // 流水线
                    'revert', // 回退
                    'types', // 类型声明
                    'release', // 版本发布
                ],
            ],
        },
    };
  3. 使用 husky 继续添加 commit-msg git hook:

    bash 复制代码
    npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

现在,你的 .husky 目录应该长这样:

你可以使用 git add . && git commit -m'aaa: bbb' 来测试了,正常会出现报错。

总结

  1. ESLint 和 Prettier 配置;
  2. Editorconfig 的配置;
  3. Git hooks 与 Husky;
  4. lint-staged 的配置;
  5. FAQ 问题梳理;

下篇将分享如何为我们的 README 添加 badge 徽章,以及重头戏:白嫖 Github Actions 实现一个简单的 CI/CD 来部署并自动发布 NPM。

往期文章

《从零开发自己的工具库(一)配置 TS + Rollup + Jest》

参考资料

ESLint 非权威配置指北(下)

EditorConfig

自定义 Git - Git 钩子

项目规范的基石------husky 与 lint-staged

husky | github

lint-staged | github

相关推荐
唔知小罗7 小时前
git config是做什么的?
git
不是鱼12 小时前
新人程序猿必备的git技能(超实用基础版)
git·github
Exclusive_Cat16 小时前
Git的使用(基础语句)
git
江上清风山间明月16 小时前
git撤销、回退某个commit的修改
git·commit·版本·撤销·回退·特定
cui_win16 小时前
Redis高可用-主从复制
redis·git·github·主从复制·哨兵
Anlici19 小时前
大厂怎么用Git命令
git
谎言西西里19 小时前
Git入门指南:掌握基本概念与日常操作
git·github
程序员buddha20 小时前
git版本工具使用教程
git
tian-ming1 天前
技术栈2:Git分布式版本控制工具
git
算你狠 - ZGX1 天前
Git - 日志
git