前提概要

我们使用<font style="color:rgb(77, 77, 77);">husky</font>检测<font style="color:rgb(77, 77, 77);">git</font>钩子,<font style="color:rgb(77, 77, 77);">lint-staged</font>规范化暂存区代码,<font style="color:rgb(77, 77, 77);">commitlint</font>规范化提交信息。

钩子介绍

git钩子 描述
pre-commit 判断提交的代码是否符合规范
commit-msg 判断 commit 信息是否符合规范
pre-push 执行测试,避免对以前的内容造成影响

工具介绍

  • <font style="color:rgb(51, 51, 51);">husky</font>:操作git钩子的工具
  • <font style="color:rgb(51, 51, 51);">lint-staged</font>:本地暂存代码检查工具
  • <font style="color:rgb(51, 51, 51);">commitlint</font>:提交信息校验工具
  • <font style="color:rgb(51, 51, 51);">commitizen</font>:辅助提交信息 ,全局安装后可以使用<font style="color:rgb(51, 51, 51);">cz</font>命令,选项式提交<font style="color:rgb(51, 51, 51);">git</font>

commitlin

  • 安装依赖
  1. pnpm add @commitlint/config-conventional @commitlint/cli cz-git czg -D
  • 使用的版本
  1. "devDependencies": {
  2. "@commitlint/cli": "^19.5.0",
  3. "@commitlint/config-conventional": "^19.5.0",
  4. "cz-git": "^1.10.1",
  5. "czg": "^1.10.1"
  6. },
  • 说明

@commitlint/config-conventional: 这是一个配置包,提供了一套基于 Angular 规范的提交信息格式规则。它帮助确保提交信息的一致性和可读性。

@commitlint/cli:这是 Commitlint 的命令行工具,用于检查提交信息是否符合指定的规则。通常与 @commitlint/config-conventional 一起使用。

cz-git: 这是一个 Commitizen 适配器,提供了一种交互式的方式来生成符合规范的提交信息。它可以帮助开发者在提交代码时选择合适的提交类型、描述和其他信息。

czg: 这是一个命令行工具,类似于 cz-git,提供了一个交互式的界面来生成提交信息。它通常用于简化和标准化提交信息的创建过程。

  • package.json更新相关代码
  1. {
  2. "scripts": {
  3. "commit": "czg"
  4. },
  5. "commitizen": {
  6. "path": "node_modules/cz-git"
  7. }
  8. }
  • 简洁版本 - commitlint.config.cjs
  1. const fs = require('fs');
  2. const path = require('path');
  3. /** @type {import('cz-git').UserConfig} */
  4. module.exports = {
  5. ignores: [(commit) => commit.includes('init')],
  6. extends: ['@commitlint/config-conventional'],
  7. rules: {
  8. 'body-leading-blank': [2, 'always'],
  9. 'footer-leading-blank': [1, 'always'],
  10. 'header-max-length': [2, 'always', 108],
  11. 'subject-empty': [2, 'never'],
  12. 'type-empty': [2, 'never'],
  13. 'subject-case': [0],
  14. 'type-enum': [
  15. 2,
  16. 'always',
  17. [
  18. 'fix',
  19. 'docs',
  20. 'style',
  21. 'refactor',
  22. 'perf',
  23. 'test',
  24. 'build',
  25. 'ci',
  26. 'chore',
  27. 'revert',
  28. 'wip',
  29. 'workflow',
  30. 'types',
  31. 'release'
  32. ]
  33. ]
  34. },
  35. prompt: {
  36. messages: {
  37. type: '选择你要提交的类型 :',
  38. scope: '选择一个提交范围[可选]:',
  39. customScope: '请输入自定义的提交范围 :',
  40. subject: '请简要描述提交 :\n',
  41. body: '填写更加详细的变更描述[可选]。使用 "|" 换行 :\n',
  42. breaking: '列举非兼容性重大的变更[可选]。使用 "|" 换行 :\n',
  43. footerPrefixsSelect: '选择关联issue前缀[可选]:',
  44. customFooterPrefixs: '输入自定义issue前缀 :',
  45. footer: '列举关联issue [可选] 例如: #31, #I3244 :\n',
  46. confirmCommit: '是否提交或修改commit ?'
  47. },
  48. types: [
  49. { value: 'feat', name: 'feat: ✨ 新增功能', emoji: '✨' },
  50. { value: 'fix', name: 'fix: 🐛 修复缺陷', emoji: '🐛' },
  51. { value: 'docs', name: 'docs: 📝 文档变更', emoji: '📝' },
  52. {
  53. value: 'style',
  54. name: 'style: 💄 代码格式',
  55. emoji: '💄'
  56. },
  57. {
  58. value: 'refactor',
  59. name: 'refactor: 🔨 代码重构',
  60. emoji: '🔨'
  61. },
  62. { value: 'perf', name: 'perf: ⚡️ 性能优化', emoji: '⚡️' },
  63. {
  64. value: 'test',
  65. name: 'test: ✅ 测试',
  66. emoji: '✅'
  67. },
  68. {
  69. value: 'build',
  70. name: 'build: 📦️ 打包构建',
  71. emoji: '📦️'
  72. },
  73. { value: 'ci', name: 'ci: 👷 CI 配置变更', emoji: '👷' },
  74. { value: 'revert', name: 'revert: ⏪️ 代码回退', emoji: '⏪️' },
  75. {
  76. value: 'chore',
  77. name: 'chore: 🚀 构建/工程依赖/工具',
  78. emoji: '🚀'
  79. },
  80. { value: 'wip', name: 'wip: 🚧 正在开发中', emoji: '🚧' },
  81. { value: 'workflow', name: 'workflow: 🎯 工作流程改进', emoji: '🎯' }
  82. ],
  83. useEmoji: true,
  84. scopes: [],
  85. customScopesAlign: 'bottom',
  86. emptyScopesAlias: 'empty',
  87. customScopesAlias: 'custom',
  88. allowBreakingChanges: ['feat', 'fix'],
  89. skipQuestions: ['scope', 'body', 'breaking', 'footerPrefix', 'footer'] // 自定义选择指定的问题不显示
  90. }
  91. };
  • 注释版本 - commitlint.config.cjs
  1. // @see: https://cz-git.qbenben.com/zh/guide
  2. const fs = require('fs');
  3. const path = require('path');
  4. const scopes = fs
  5. .readdirSync(path.resolve(__dirname, 'src'), { withFileTypes: true })
  6. .filter((dirent) => dirent.isDirectory())
  7. .map((dirent) => dirent.name.replace(/s$/, ''));
  8. /** @type {import('cz-git').UserConfig} */
  9. module.exports = {
  10. ignores: [(commit) => commit.includes('init')],
  11. extends: ['@commitlint/config-conventional'],
  12. rules: {
  13. // @see: https://commitlint.js.org/#/reference-rules
  14. 'body-leading-blank': [2, 'always'], // 正文以空行开头
  15. 'footer-leading-blank': [1, 'always'], // 脚注以空行开头
  16. 'header-max-length': [2, 'always', 108], // 头部最大长度
  17. 'subject-empty': [2, 'never'], // 主题不能为空
  18. 'type-empty': [2, 'never'], // 类型不能为空
  19. 'subject-case': [0], // 主题格式
  20. 'type-enum': [
  21. 2,
  22. 'always',
  23. [
  24. 'fix',
  25. 'docs',
  26. 'style',
  27. 'refactor',
  28. 'perf',
  29. 'test',
  30. 'build',
  31. 'ci',
  32. 'chore',
  33. 'revert',
  34. 'wip',
  35. 'workflow',
  36. 'types',
  37. 'release'
  38. ]
  39. ]
  40. },
  41. prompt: {
  42. messages: {
  43. type: '选择你要提交的类型 :',
  44. scope: '选择一个提交范围[可选]:',
  45. customScope: '请输入自定义的提交范围 :',
  46. subject: '填写简短精炼的变更描述 :\n',
  47. body: '填写更加详细的变更描述[可选]。使用 "|" 换行 :\n',
  48. breaking: '列举非兼容性重大的变更[可选]。使用 "|" 换行 :\n',
  49. footerPrefixsSelect: '选择关联issue前缀[可选]:',
  50. customFooterPrefixs: '输入自定义issue前缀 :',
  51. footer: '列举关联issue [可选] 例如: #31, #I3244 :\n',
  52. confirmCommit: '是否提交或修改commit ?'
  53. },
  54. types: [
  55. { value: 'feat', name: 'feat: ✨ 新增功能', emoji: '✨' },
  56. { value: 'fix', name: 'fix: 🐛 修复缺陷', emoji: '🐛' },
  57. { value: 'docs', name: 'docs: 📝 文档变更', emoji: '📝' },
  58. {
  59. value: 'style',
  60. name: 'style: 💄 代码格式',
  61. emoji: '💄'
  62. },
  63. {
  64. value: 'refactor',
  65. name: 'refactor: 🔨 代码重构',
  66. emoji: '🔨'
  67. },
  68. { value: 'perf', name: 'perf: ⚡️ 性能优化', emoji: '⚡️' },
  69. {
  70. value: 'test',
  71. name: 'test: ✅ 测试',
  72. emoji: '✅'
  73. },
  74. {
  75. value: 'build',
  76. name: 'build: 📦️ 打包构建',
  77. emoji: '📦️'
  78. },
  79. { value: 'ci', name: 'ci: 👷 CI 配置变更', emoji: '👷' },
  80. { value: 'revert', name: 'revert: ⏪️ 代码回退', emoji: '⏪️' },
  81. {
  82. value: 'chore',
  83. name: 'chore: 🚀 构建/工程依赖/工具',
  84. emoji: '🚀'
  85. },
  86. { value: 'wip', name: 'wip: 🚧 正在开发中', emoji: '🚧' },
  87. { value: 'workflow', name: 'workflow: 🎯 工作流程改进', emoji: '🎯' },
  88. { value: 'types', name: 'types: 🏡 类型定义文件修改', emoji: '🏡' }
  89. ],
  90. useEmoji: true, // 是否开启 commit message 带有 Emoji 字符。
  91. emojiAlign: 'center', // 设置 Emoji 字符 的 位于头部位置
  92. themeColorCode: '', // 设置提示查询器主题颜色, cyan青色
  93. scopes: [...scopes], // 自定义选择 模块范围 命令行显示信息
  94. allowCustomScopes: true, // 是否在选择 模块范围 显示自定义选项(custom)
  95. allowEmptyScopes: true, // 是否在选择 模块范围 显示为空选项(empty)
  96. customScopesAlign: 'bottom', // 设置 选择范围 中 为空选项(empty) 和 自定义选项(custom) 的 位置
  97. customScopesAlias: 'custom', // 自定义 选择范围 中 自定义选项(custom) 在命令行中显示的 名称
  98. emptyScopesAlias: 'empty', // 自定义 选择范围 中 为空选项(empty) 在命令行中显示的 名称
  99. upperCaseSubject: false, // 是否自动将简短描述(subject)第一个字符进行大写处理
  100. markBreakingChangeMode: false, // 添加额外的问题重大变更(BREAKING CHANGES)提问,询问是否需要添加 "!" 标识于
  101. allowBreakingChanges: ['feat', 'fix'], // 允许出现 重大变更(BREAKING CHANGES)的特定 type
  102. breaklineNumber: 100, // 详细描述(body)和重大变更(BREAKING CHANGES)中根据字符超过该数值自动换行
  103. breaklineChar: '|', // 详细描述(body)和重大变更(BREAKING CHANGES)中换行字符
  104. skipQuestions: ['scope', 'body', 'breaking', 'footerPrefix', 'footer'], // 自定义选择指定的问题不显示
  105. // 自定义选择issue前缀
  106. issuePrefixs: [
  107. // 如果使用 gitee 作为开发管理
  108. { value: 'link', name: 'link: 链接 ISSUES 进行中' },
  109. { value: 'closed', name: 'closed: 标记 ISSUES 已完成' }
  110. ],
  111. customIssuePrefixsAlign: 'top', // 设置 选择 issue 前缀 中 跳过选项(skip) 和 自定义选项(custom) 的 位置
  112. emptyIssuePrefixsAlias: 'skip', // 自定义 选择 issue 前缀 中 跳过选项(skip) 在命令行中显示的 名称
  113. customIssuePrefixsAlias: 'custom', // 自定义 选择 issue 前缀 中 自定义选项(custom) 在命令行中显示的 名称
  114. allowCustomIssuePrefixs: true, // 是否在选择 ISSUE 前缀 显示自定义选项(custom)
  115. allowEmptyIssuePrefixs: true, // 是否在选择 ISSUE 前缀 显示为跳过选项(skip)
  116. confirmColorize: true, // 确定提交中模板 commit message 是否着色
  117. // maxHeaderLength: Infinity, // 定义commit message中的 header 长度, 给予在命令行中的校验信息
  118. // maxSubjectLength: Infinity, // 定义commit message中的 subject 长度, 给予在命令行中的校验信息
  119. minSubjectLength: 0, // 定义commit message中的 subject 长度, 给予在命令行中的校验信息
  120. scopeOverrides: undefined, // 自定义选择了特定类型后 覆盖模块范围 命令行显示信息
  121. defaultBody: '', // 在 详细描述 中是否使用显示默认值
  122. defaultIssues: '', // 在 输入ISSUE 中是否使用显示默认值
  123. defaultScope: '', // 如果 defaultScope 与在选择范围列表项中的 value 相匹配就会进行星标置顶操作。
  124. defaultSubject: '' // 在 简短描述 中是否使用显示默认值
  125. }
  126. };

husky & lint-staged

  • 安装依赖
  1. pnpm add husky lint-staged -D
  • 使用的版本
  1. "devDependencies": {
  2. "husky": "^9.1.6",
  3. "lint-staged": "^15.2.10"
  4. },
  • 更新package.json
  1. "scripts": {
  2. "prepare": "husky",
  3. },
  • 添加文件.lintstagedrc
  1. {
  2. "*.{js,jsx,ts,tsx}": [
  3. "pnpm run lint:fix"
  4. ],
  5. "*.{css,scss}": [
  6. "pnpm run stylelint:fix",
  7. "git add"
  8. ]
  9. }
  • 或者lint-staged.config.cjs
  1. module.exports = {
  2. "*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
  3. "{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": ["prettier --write--parser json"],
  4. "package.json": ["prettier --write"],
  5. "*.vue": ["eslint --fix", "prettier --write", "stylelint --fix"],
  6. "*.{scss,less,styl,html}": ["stylelint --fix", "prettier --write"],
  7. "*.md": ["prettier --write"]
  8. };

参考