配置提交规范
npm i -D husky
npx husky install
// @see: https://cz-git.qbenben.com/zh/guide
/** @type {import('cz-git').UserConfig} */
module.exports = {
ignores: [(commit) => commit.includes('init')],
extends: ['@commitlint/config-conventional'],
rules: {
// @see: https://commitlint.js.org/#/reference-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'],
'subject-case': [0],
'type-enum': [
2,
'always',
[
'feat',
'fix',
'docs',
'style',
'refactor',
'perf',
'test',
'build',
'ci',
'chore',
'revert',
'wip',
'workflow',
'types',
'release',
],
],
},
prompt: {
messages: {
type: "Select the type of change that you're committing:",
scope: 'Denote the SCOPE of this change (optional):',
customScope: 'Denote the SCOPE of this change:',
subject: 'Write a SHORT, IMPERATIVE tense description of the change:\n',
body: 'Provide a LONGER description of the change (optional). Use "|" to break new line:\n',
breaking:
'List any BREAKING CHANGES (optional). Use "|" to break new line:\n',
footerPrefixsSelect:
'Select the ISSUES type of changeList by this change (optional):',
customFooterPrefixs: 'Input ISSUES prefix:',
footer: 'List any ISSUES by this change. E.g.: #31, #34:\n',
confirmCommit: 'Are you sure you want to proceed with the commit above?',
// 中文版
// type: "选择你要提交的类型 :",
// scope: "选择一个提交范围(可选):",
// customScope: "请输入自定义的提交范围 :",
// subject: "填写简短精炼的变更描述 :\n",
// body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
// breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
// footerPrefixsSelect: "选择关联issue前缀(可选):",
// customFooterPrefixs: "输入自定义issue前缀 :",
// footer: "列举关联issue (可选) 例如: #31, #I3244 :\n",
// confirmCommit: "是否提交或修改commit ?"
},
types: [
{
value: 'feat',
name: 'feat: 🚀 A new feature',
emoji: '🚀',
},
{
value: 'fix',
name: 'fix: 🧩 A bug fix',
emoji: '🧩',
},
{
value: 'docs',
name: 'docs: 📚 Documentation only changes',
emoji: '📚',
},
{
value: 'style',
name: 'style: 🎨 Changes that do not affect the meaning of the code',
emoji: '🎨',
},
{
value: 'refactor',
name: 'refactor: ♻️ A code change that neither fixes a bug nor adds a feature',
emoji: '♻️',
},
{
value: 'perf',
name: 'perf: ⚡️ A code change that improves performance',
emoji: '⚡️',
},
{
value: 'test',
name: 'test: ✅ Adding missing tests or correcting existing tests',
emoji: '✅',
},
{
value: 'build',
name: 'build: 📦️ Changes that affect the build system or external dependencies',
emoji: '📦️',
},
{
value: 'ci',
name: 'ci: 🎡 Changes to our CI configuration files and scripts',
emoji: '🎡',
},
{
value: 'chore',
name: "chore: 🔨 Other changes that don't modify src or test files",
emoji: '🔨',
},
{
value: 'revert',
name: 'revert: ⏪️ Reverts a previous commit',
emoji: '⏪️',
},
// 中文版
// { value: "特性", name: "特性: 🚀 新增功能", emoji: "🚀" },
// { value: "修复", name: "修复: 🧩 修复缺陷", emoji: "🧩" },
// { value: "文档", name: "文档: 📚 文档变更", emoji: "📚" },
// { value: "格式", name: "格式: 🎨 代码格式(不影响功能,例如空格、分号等格式修正)", emoji: "🎨" },
// { value: "重构", name: "重构: ♻️ 代码重构(不包括 bug 修复、功能新增)", emoji: "♻️" },
// { value: "性能", name: "性能: ⚡️ 性能优化", emoji: "⚡️" },
// { value: "测试", name: "测试: ✅ 添加疏漏测试或已有测试改动", emoji: "✅" },
// { value: "构建", name: "构建: 📦️ 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)", emoji: "📦️" },
// { value: "集成", name: "集成: 🎡 修改 CI 配置、脚本", emoji: "🎡" },
// { value: "回退", name: "回退: ⏪️ 回滚 commit", emoji: "⏪️" },
// { value: "其他", name: "其他: 🔨 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)", emoji: "🔨" }
],
useEmoji: true,
themeColorCode: '',
scopes: [],
allowCustomScopes: true,
allowEmptyScopes: true,
customScopesAlign: 'bottom',
customScopesAlias: 'custom',
emptyScopesAlias: 'empty',
upperCaseSubject: false,
allowBreakingChanges: ['feat', 'fix'],
breaklineNumber: 100,
breaklineChar: '|',
skipQuestions: [],
issuePrefixs: [
{ value: 'closed', name: 'closed: ISSUES has been processed' },
],
customIssuePrefixsAlign: 'top',
emptyIssuePrefixsAlias: 'skip',
customIssuePrefixsAlias: 'custom',
allowCustomIssuePrefixs: true,
allowEmptyIssuePrefixs: true,
confirmColorize: true,
maxHeaderLength: Infinity,
maxSubjectLength: Infinity,
minSubjectLength: 0,
scopeOverrides: undefined,
defaultBody: '',
defaultIssues: '',
defaultScope: '',
defaultSubject: '',
},
};
然后使用 Husky 启用 commitlint:
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
配置代码检查
npm install lint-staged --save-dev
npx husky add .husky/pre-commit "npm run lint"
"scripts: {
...
"lint": "lint-staged"
}
在项目根目录下新建 lint-staged.config.js
module.exports = {
'*.{js,jsx,ts,tsx}': ['eslint --fix', 'prettier --write'],
'{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': [
'prettier --write--parser json',
],
'package.json': ['prettier --write'],
'*.{css,scss,less,styl}': ['stylelint --fix', 'prettier --write'],
'*.md': ['prettier --write'],
};
代码格式化和质量工具
为了设定一个标准,供项目的所有贡献者使用,以保持代码风格一致并遵循基本的最佳实践,我们将使用两个工具:
eslint - 代码规范的最佳实践
prettier - 自动格式化代码文件
### ESLint
我们从 ESLint 开始,它非常简单因为在我们创建 Next.js 项目的时候已经自动安装好并且有了默认配置。
我们仅需要添加少部分额外的配置就可以让它比默认配置更加严格。如果你不同意其中的任何一条规则配置,不用担心,我们可以非常简单的手动关闭这些规则。我们将所有的 ESLint 配置都写在 .eslintrc.json 文件中,这个文件已经存在于我们项目的根目录。
.eslintrc.json
{ "globals": { "React": "readonly" }, "settings": { "react": { "version": "detect" } }, "root": true, "env": { "browser": true, "node": true, "es6": true }, /* 指定如何解析语法 / "parser": "@typescript-eslint/parser", / 优先级低于 parse 的语法解析配置 / "parserOptions": { "ecmaVersion": 2020, "sourceType": "module", "jsxPragma": "React", "ecmaFeatures": { "jsx": true } }, "plugins": ["react", "@typescript-eslint", "react-hooks", "prettier"], /
- "off" 或 0 ==> 关闭规则
- "warn" 或 1 ==> 打开的规则作为警告(不影响代码执行)
- "error" 或 2 ==> 规则作为一个错误(代码不能执行,界面报错) */ "rules": { // eslint (http://eslint.cn/docs/rules) "no-var": "error", // 要求使用 let 或 const 而不是 var "no-multiple-empty-lines": ["error", { "max": 1 }], // 不允许多个空行 "no-use-before-define": "error", // 禁止在 函数/类/变量 定义之前使用它们 "prefer-const": "error", // 此规则旨在标记使用 let 关键字声明但在初始分配后从未重新分配的变量,要求使用 const "no-irregular-whitespace": "error", // 禁止不规则的空白
// typeScript (https://typescript-eslint.io/rules)
"@typescript-eslint/no-unused-vars": "warn", // 禁止定义未使用的变量
"@typescript-eslint/no-inferrable-types": "warn", // 可以轻松推断的显式类型可能会增加不必要的冗长
"@typescript-eslint/no-namespace": "warn", // 禁止使用自定义 TypeScript 模块和命名空间。
"@typescript-eslint/no-explicit-any": "warn", // 禁止使用 any 类型
"@typescript-eslint/ban-ts-ignore": "off", // 禁止使用 @ts-ignore
"@typescript-eslint/ban-types": "warn", // 禁止使用特定类型
"@typescript-eslint/explicit-function-return-type": "warn", // 不允许对初始化为数字、字符串或布尔值的变量或参数进行显式类型声明
"@typescript-eslint/no-var-requires": "warn", // 不允许在 import 语句中使用 require 语句
"@typescript-eslint/no-empty-function": "warn", // 禁止空函数
"@typescript-eslint/no-use-before-define": "warn", // 禁止在变量定义之前使用它们
"@typescript-eslint/ban-ts-comment": "warn", // 禁止 @ts-<directive> 使用注释或要求在指令后进行描述
"@typescript-eslint/no-non-null-assertion": "warn", // 不允许使用后缀运算符的非空断言(!)
"@typescript-eslint/explicit-module-boundary-types": "warn", // 要求导出函数和类的公共类方法的显式返回和参数类型
// react (https://github.com/jsx-eslint/eslint-plugin-react)
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "error",
"no-debugger": "error",
"no-console": "warn"
} }
npm run lint
你会得到类型的提示
✔ No ESLint warnings or errors
✨ Done in 3.48s.
## Prettier
npm i -D prettier
同时我也推荐你安装 Prettier VS Code 插件 ,这样你不用依赖命令行工具就可以在 VS Code 中进行文件格式化。在你的项目中安装和配置它意味着 VSCode 将使用你项目的设置,因此仍然有必要在此处添加它。
我们将在根目录添加两个文件:
.prettierrc.js
// @see: https://www.prettier.cn
module.exports = { // 超过最大值换行 printWidth: 130, // 缩进字节数 tabWidth: 2, // 使用制表符而不是空格缩进行 useTabs: true, // 结尾不用分号(true有,false没有) semi: true, // 使用单引号(true单双引号,false双引号) singleQuote: false, // 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>" quoteProps: "as-needed", // 在对象,数组括号与文字之间加空格 "{ foo: bar }" bracketSpacing: true, // 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none trailingComma: "none", // 在JSX中使用单引号而不是双引号 jsxSingleQuote: false, // (x) => {} 箭头函数参数只有一个时是否要有小括号。avoid:省略括号 ,always:不省略括号 arrowParens: "avoid", // 如果文件顶部已经有一个 doclock,这个选项将新建一行注释,并打上@format标记。 insertPragma: false, // 指定要使用的解析器,不需要写文件开头的 @prettier requirePragma: false, // 默认值。因为使用了一些折行敏感型的渲染器(如GitHub comment)而按照markdown文本样式进行折行 proseWrap: "preserve", // 在html中空格是否是敏感的 "css" - 遵守CSS显示属性的默认值, "strict" - 空格被认为是敏感的 ,"ignore" - 空格被认为是不敏感的 htmlWhitespaceSensitivity: "css", // 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>" endOfLine: "auto", // 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码 rangeStart: 0, rangeEnd: Infinity, // Vue文件脚本和样式标签缩进 vueIndentScriptAndStyle: false };
.prettierignore
.prettierignore .next dist node_modules
在这个文件中我列了一些目录,我不希望 prettier 在这些目录中浪费任何资源去进行格式化。你也可以使用类似 *.html 这样的方式去忽略你选择的文件类似
现在我们在 package.json 添加新的 script ,然后我们就可以运行 Prettier:
package.json
"scripts: { ... "prettier": "prettier --write ." }
你可以运行
npm run prettier
chmod ug+x .husky/*