Element Plus 源码手写: Element Plus 的 commitlint配置

一、前言

Element Plus 中有严格的提交信息规范,commitlint 是作为它的首选的提交信息工具,这个工具通常和 husky 工具共同使用,以确保所有的提交信息符合指定的提交规范。这篇文章应该和 ESlint 配置那篇文章是一起的,但由于篇幅太长单独写在这篇了。

这篇文章你可以收获,在项目开发中搭建一套完美的提交校验,让你知道更改项目代码应该如何书写提交信息。

二、一般项目中的 commitlint 基本使用

一)、初始化 commitlint

1. 安装 commitlint

项目内安装下面依赖:

bash 复制代码
npm install @commitlint/config-conventional @commitlint/cli --save-dev

2. 配置 commitlint

在项目根目录创建 commitlint.config.js 文件,用于定义 commitlint 的规则配置:

javascript 复制代码
module.exports = {
  extends: ['@commitlint/config-conventional'],
};

这里的 @commitlint/config-conventional 是一种标准化的提交规范,基于 Angular的提交规范

3. 提交信息格式解释

通过配置后,commitlint 会按照上面的格式校验提交信息,例如要求以下格式:

plain 复制代码
<type>(<scope>): <subject>

type:提交类型,表示提交的目的或类别。

  • feat:新功能(feature)
  • fix:修复 bug
  • docs:文档修改
  • style:代码格式修改(不影响代码逻辑,如空格、格式化等)
  • refactor:重构(既不是新功能也不是修 bug)
  • test:增加测试
  • chore: 用于不修改源代码或测试代码的更改,通常是项目维护中的一些杂项任务,比如修改配置文件、更新依赖、清理代码 。

scope:影响的范围,例如模块或功能名称(可选)

subject:对提交内容的简要描述

参照下面提交格式

plain 复制代码
feat(user-auth): add login functionality
fix(api): resolve issue with timeout error
docs(readme): update installation guide

二)、自定义 commitlint.config.js 配置

1.常用自定义规则配置

如果需要自定义提交信息规则,可以在 commitlint.config.js 中修改默认配置。

javascript 复制代码
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore']],
    'scope-empty': [2, 'never'], // scope 不可为空
    'subject-case': [2, 'never', ['start-case', 'pascal-case']], // subject 不允许使用大写开头
    'header-max-length': [2, 'always', 72], // 限制提交信息的长度
  },
};
  • type-enum:指定 type 的枚举值
  • scope-empty:不允许 scope 为空
  • subject-casesubject 不允许使用 PascalCaseStartCase

2.提交信息示例

新增功能feat

plain 复制代码
feat(auth): add support for two-factor authentication

修复bug ( **fix** )

plain 复制代码
fix(api): handle network errors in data fetch function

更新文档docs

plain 复制代码
docs(contributing): update code of conduct section

代码格式调整style

plain 复制代码
style(header): adjust header spacing and font size

重构代码refactor

plain 复制代码
refactor(user-service): split large function into smaller ones

增加测试test

plain 复制代码
test(auth): add unit tests for login component

构建/工具变动chore

plain 复制代码
chore(deps): update dependencies to latest versions

3. 配置 huskycommitlint 集成

为了确保每次提交时都自动进行校验,可以使用 husky 配合 commitlint。安装 husky

bash 复制代码
npm install husky --save-dev
npm husky init 

npm husky init初始化 husky会创建一个 pre-commit 脚本在 .husky/文件夹下,而且还会再 package.json 中更新一个脚本 prepare

如果是需要配置 commitlint 自动化校验提交信息,需要再.husky/目录下添加一个 commit-msg文件名来执行 commitlint 校验:

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

这会在每次提交信息时,自动执行 commitlint 检查。

三、Element Plus 中的 commitlint 的使用

一)、项目初始化 commitlint

1.安装 commitlint

bash 复制代码
pnpm npm install @commitlint/config-conventional @commitlint/cli --save-dev -w

2.创建配置文件

在跟目录下创建 commitlint.config.js 文件和 commitlint.config.ts文件。

🤷‍♂️🤷‍♂️为什么需要创建者两个文件而不是创建这其中的一个?

commitlint.config.ts在开发环境下,我们需要 TypeScript提供更加安全的类型校验,他可以更好的维护项目。

commitlint.config.js是一个 JavaScript文件,通常在运行时执行,因为 JS不需要类型检查,执行更快。在执行 钩子需要在终端中运行,因为在 Node.js 环境下 **JavaScript**文件可以直接执行不需要额外的转译,这样可以提高运行速度。

所以保留两个配置文件是:保留类型校验,提高 husky运行速度。

3.配置 commitlint 文件

配置 commit.config.js 文件,默认导入 commit.config.ts文件,这里使用 esbuild-kit/cjs-loader来代替 ts-node 来加载 typescript配置。这个加载速度更快。

javascript 复制代码
require('@esbuild-kit/cjs-loader')
module.exports = require('./commitlint.config.ts').default
1)、配置 commitlint.config.ts 文件

这个配置主要需要实现以下功能:

  • 自动获取提交范围 :动态获取 packagesinternal 目录下的文件夹名,生成提交信息的 scope 列表。
  • **自动匹配默认 scope 和 **subject:通过 Git 状态检测当前修改的文件,自动提取默认的 scope(文件夹名)和 subject(组件名),帮助生成符合规范的默认提交信息。
  • 规范提交信息格式 :定义了提交信息的多项格式规则(如 typescopesubject 的大小写、长度限制等),确保提交信息遵循统一的风格。
  • 个性化提示 :在提交信息编辑时,使用提取的 scopesubject 作为默认值,提供提示以便快速填写符合规范的提交信息。
typescript 复制代码
import { execSync } from 'child_process'
import fg from 'fast-glob'

const getPackages = (packagePath) =>
  fg.sync('*', { cwd: packagePath, onlyDirectories: true })

execSync 是 Node.js 提供的一个用于执行系统命令的同步方法。fgfast-glob 包的导入,用于快速处理文件和目录的匹配。

getPackages这个函数主要利用 fast-glob 工具来实现获取指定路径下的所有文件夹名称。**onlyDirectories: true**** 选项使得只匹配文件夹**。

如果 packages 目录下有 componentstheme-chalk 两个文件夹,则 getPackages('packages') 返回 ['components', 'theme-chalk'] 数组。

typescript 复制代码
const scopes = [
  ...getPackages('packages'),
  ...getPackages('internal'),
  'docs',
  'play',
  'project',
  'core',
  'style',
  'ci',
  'dev',
  'deploy',
  'other',
  'typography',
  'color',
  'border',
  'var',
  'ssr',
]

获取提交信息格式里的 <type>(scope): subject 中的 scope 数组列表。 如果 packages 目录包含 buttoninput 两个文件夹,则 scopes 可能变成 ['button', 'input', 'docs', 'play', ...]

typescript 复制代码
const gitStatus = execSync('git status --porcelain || true')
  .toString()
  .trim()
  .split('\n')

execSync('git status --porcelain || true') 命令执行 git status,并将其输出以字符串形式返回.toString() 将结果转换为字符串,.trim() 去除空白,.split('\n') 将结果按行分割成数组 。它返回的是修改过的文件 , 这段代码将返回一个包含这些文件路径的数组,如 ['M packages/button/index.ts', 'M packages/input/style.css']

typescript 复制代码
const scopeComplete = gitStatus
  .find((r) => ~r.indexOf('M  packages'))
  ?.replace(/\//g, '%%')
  ?.match(/packages%%((\w|-)*)/)?.[1]

const subjectComplete = gitStatus
  .find((r) => ~r.indexOf('M  packages/components'))
  ?.replace(/\//g, '%%')
  ?.match(/packages%%components%%((\w|-)*)/)?.[1]

遍历 gitStatus 返回的数组,找到这个数组中第一条包含 M packages的记录,**~r.indexOf('M packages')**会返回找到这个字符串在数组中的某项所在的下标的取反值。比如返回下标为 2,则这个取反返回 -2。如果没找到为 indexOf的值为-1,取反则为 0find函数获取的就是 false。这样就能找到通过 indexOf() 方法找到的包含的那项。主要作用就是将下标值转换成布尔值

在将数组中的子项如果存在/则用 %%来取代。'M packages/example-path/file.ts'** 会被替换为 **'M packages%%example-path%%file.ts'

最后通过 **match**** 来获取正则表达式的捕获项。比如 'M packages%%example-path%%file.ts'.match(/packages%%((\w|-)*)/) 这个首先找到 packages%%,所以这一部分符合正则表达式的开始部分。然后 ((\w|-)*) 开始匹配 example-path%%file.ts 中的 example-pathexample-path 符合 (\w|-) 的规则(字母和 - 组成的组合)。匹配到 %% 时停止,因为后面的%% 不符合 (\w|-) 的要求。最终,捕获组 ((\w|-)*) 匹配到了 example-pathmatch执行这段代码将返回一个数组 **['packages%%example-path', 'example-path']


typescript 复制代码
export default {
  rules: {
    /**
     * type[scope]: [function] description
     *      ^^^^^
     * 'scope-enum' 限定了 scope(范围)的值必须在 `scopes` 数组中,以确保提交信息的 scope 有效。
     */
    'scope-enum': [2, 'always', scopes],

    /**
     * type[scope]: [function] description
     *
     * ^^^^^^^^^^^^^^ 空行.
     * - 提交信息的 body 部分之前必须有一个空行。
     */
    'body-leading-blank': [1, 'always'],

    /**
     * type[scope]: [function] description
     *
     * - something here
     *
     * ^^^^^^^^^^^^^^
     * - 提交信息的 footer 部分之前必须有一个空行。
     ep:
     fix: 修复用户登录问题

     增加了登录失败时的错误提示
     关闭了连接泄漏的问题
                        ->这里要有空行
     Related to issue #123
     */
    'footer-leading-blank': [1, 'always'],

    /**
     * type[scope]: [function] description [不超过 72 个字符]
     *      ^^^^^
     * - 限制提交信息的标题(header)的最大长度为 72 个字符。
     */
    'header-max-length': [2, 'always', 72],

    /**
     * 限制 scope 的格式为小写。
     */
    'scope-case': [2, 'always', 'lower-case'],

    /**
     * 限制 subject 的格式,不能为以下格式:
     * - sentence-case(首字母大写)
     * - start-case(每个单词首字母大写)
     * - pascal-case(首字母大写的驼峰命名)
     * - upper-case(全大写)
     */
    'subject-case': [
      1,
      'never',
      ['sentence-case', 'start-case', 'pascal-case', 'upper-case'],
    ],

    /**
     * 确保 subject(描述)部分不为空。
     */
    'subject-empty': [2, 'never'],

    /**
     * 确保 subject 部分不以句号 (.) 结尾。
     */
    'subject-full-stop': [2, 'never', '.'],

    /**
     * 限制 type 的格式为小写。
     */
    'type-case': [2, 'always', 'lower-case'],

    /**
     * 确保 type(类型)部分不为空。
     */
    'type-empty': [2, 'never'],

    /**
     * type[scope]: [function] description
     * ^^^^
     * 限定 type 的值必须在以下选项中,以确保提交类型统一:
     * - build、chore、ci、docs、feat、fix、perf、refactor、revert、release、style、test、improvement
     */
    'type-enum': [
      2,
      'always',
      [
        'build',
        'chore',
        'ci',
        'docs',
        'feat',
        'fix',
        'perf',
        'refactor',
        'revert',
        'release',
        'style',
        'test',
        'improvement',
      ],
    ],
  },
  prompt: {
    /**
     * 设置提交信息的默认 scope(范围),使用 scopeComplete 的值。
     */
    defaultScope: scopeComplete,

    /**
     * 自定义 scope 的位置:如果 scopeComplete 存在,则将自定义 scope 选项放在底部,否则放在顶部。
     */
    customScopesAlign: !scopeComplete ? 'top' : 'bottom',

    /**
     * 设置默认的 subject(主题),如果 subjectComplete 存在则在其前面加上 `[` 和 `]`。
     */
    defaultSubject: subjectComplete && `[${subjectComplete}] `,

    /**
     * 禁止自定义 issue 前缀。
     */
    allowCustomIssuePrefixs: false,

    /**
     * 禁止空的 issue 前缀。
     */
    allowEmptyIssuePrefixs: false,
  },
}
2)、type 的使用场景介绍
  • build: 与构建系统或外部依赖相关的更改。例如,更新编译工具、打包配置或版本控制的设置。
  • chore: 与代码库维护相关的常规更改,不影响代码运行和功能,比如更新构建脚本或清理文件 。
  • ci: 修改了持续集成(CI)配置和脚本,例如更新 GitHub Actions、Jenkins 或 Travis 配置文件。
  • docs: 仅限文档的更改,比如添加、更新或删除文档内容。
  • feat: 增加新功能或特性,通常会直接影响用户体验。
  • fix: 修复问题或 bug,改善当前功能的稳定性。
  • perf: 提升性能的更改,比如优化算法或减少加载时间。
  • refactor: 重构代码,但不影响外部行为的更改,通常用于提升代码可读性和可维护性。
  • revert: 撤销以前的提交,通常是在发现更改带来问题后将其还原。
  • release: 用于版本发布的更改或标记。
  • style: 与代码格式、样式、注释相关的更改,不会影响功能和逻辑,比如代码缩进或删除多余空行。
  • test: 添加或修改测试代码,以确保代码功能的正确性。
  • improvement: 对现有功能的改进或增强,但不引入全新功能。
3)、commitlint 和 husky 结合

.husky/目录下创建 commit-msg文件,配置以下配置,就可以完成自动提交校验提交信息。

typescript 复制代码
pnpm exec commitlint --config commitlint.config.js --edit "${1}"

4.配置执行脚本

在 项目根目录下的package.json 文件中配置执行脚本。

json 复制代码
"lint:commit": "commitlint --from $(git merge-base origin/dev HEAD) --to HEAD > ./commit-lint.txt",

检查从特定起点到当前 HEAD 之间的所有提交信息是否符合 commitlint 的规则,并将结果输出到 commit-lint.txt 文件中。

在合并代码前,可以执行这个脚本,确保从开发分支分离后创建的所有提交信息符合规范。

四、总结

  • commitlint 的基本使用
  • Element Plus 中 commitllint 的使用

看完这一篇,相信你应该可以给自己的项目配置提交校验了。 愿诸君越来越好,一起加油。

相关推荐
m0_7482463523 分钟前
前端通过new Blob下载文档流(下载zip或excel)
前端·excel
半糖112235 分钟前
将本地项目提交到远程仓库
前端·vue.js
web150850966415 小时前
【React&前端】大屏适配解决方案&从框架结构到实现(超详细)(附代码)
前端·react.js·前端框架
理想不理想v5 小时前
前端项目性能优化(详细)
前端·性能优化
CodeToGym5 小时前
使用 Vite 和 Redux Toolkit 创建 React 项目
前端·javascript·react.js·redux
Cachel wood6 小时前
Vue.js前端框架教程8:Vue消息提示ElMessage和ElMessageBox
linux·前端·javascript·vue.js·前端框架·ecmascript
桃园码工7 小时前
4_使用 HTML5 Canvas API (3) --[HTML5 API 学习之旅]
前端·html5·canvas
桃园码工7 小时前
9_HTML5 SVG (5) --[HTML5 API 学习之旅]
前端·html5·svg
人才程序员8 小时前
QML z轴(z-order)前后层级
c语言·前端·c++·qt·软件工程·用户界面·界面
m0_548514778 小时前
前端三大主流框架:React、Vue、Angular
前端·vue.js·react.js