当我们的代码库有很多人维护时,经常会出现代码风格不一致或者代码质量不过关,提交信息杂乱的情况,当然啦,即使是一个人的代码库,有的时候,自己写代码时不太注意细节,也会出现风格不一致的情况。
本文正是为了解决这个问题而生,阅读本篇文章并不需要很长时间,如果你的代码库还没有进行这些配置,正是你大展身手的好时机,武装一下你的代码库。
1. 规范 commit 信息
首先,看下 angular
的代码库的 commit
记录,如图:
我们可以利用 commitizen
和 husky
来规范代码库的 commit
。
安装以下依赖:
代码语言:javascript复制npm install @commitlint/cli @commitlint/config-conventional husky -D
如果你还没有安装过 commitizen
,那么先全局安装:
npm install commitizen -g
在 package.json 中增加 husky 字段。
代码语言:javascript复制{
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
}
husky
是 git hook
工具,使用 husky
,我们可以方便的在 package.json
中配置 git hook
脚本,例如: pre-commit
、 pre-push
、 commit-msg
等的。
创建 commitlint.config.js 文件
代码语言:javascript复制module.exports = {
extends: ["@commitlint/config-conventional"],
};
此刻开始,请使用 git cz
来替代 git commit
提交信息,我们来看看,假设我们随便写一个 git commit -m 'fixbug'
会提示什么?
使用 git cz
来进行填写 commit
的内容。
git cz
的 type
说明:
虽然,我们现在已经可以规范提交信息了,但是我们可能不喜欢默认的交互,例如,一个精简的描述就可以了,不希望再提示我去写详细的描述,那么就可以使用 cz-customizable
来进行定制。
自定义提交说明
安装 cz-customizable
代码语言:javascript复制npm install cz-customizable -D
cz-customizable
是可自定义的 Commitizen
插件,可帮助实现一致的 commit message
。
cz-customizable
适合大型团队去自定义 scope
,和 commit type
。
新建 .cz-config.js
在项目根目录下创建 .cz-config.js
文件:
官方提供了一份配置信息,可以去这个地址查看:https://github.com/leoforfree/cz-customizable/blob/master/cz-config-EXAMPLE.js
代码语言:javascript复制//.cz-config.js
module.exports = {
types: [
{ value: 'feat', name: 'feat: A new feature' },
{ value: 'fix', name: 'fix: A bug fix' },
{ value: 'docs', name: 'docs: Documentation only changes' },
{
value: 'style',
name:
'style: Changes that do not affect the meaning of the coden (white-space, formatting, missing semi-colons, etc)',
},
{
value: 'refactor',
name: 'refactor: A code change that neither fixes a bug nor adds a feature',
},
{
value: 'perf',
name: 'perf: A code change that improves performance',
},
{ value: 'test', name: 'test: Adding missing tests' },
{
value: 'chore',
name:
'chore: Changes to the build process or auxiliary toolsn and libraries such as documentation generation',
},
{ value: 'revert', name: 'revert: Revert to a commit' },
{ value: 'WIP', name: 'WIP: Work in progress' },
],
scopes: [{ name: 'accounts' }, { name: 'admin' }, { name: 'exampleScope' }, { name: 'changeMe' }],
allowTicketNumber: false,
isTicketNumberRequired: false,
ticketNumberPrefix: 'TICKET-',
ticketNumberRegExp: '\d{1,5}',
// it needs to match the value for field type. Eg.: 'fix'
/*
scopeOverrides: {
fix: [
{name: 'merge'},
{name: 'style'},
{name: 'e2eTest'},
{name: 'unitTest'}
]
},
*/
// override the messages, defaults are as follows
messages: {
type: "Select the type of change that you're committing:",
scope: 'nDenote the SCOPE of this change (optional):',
// used if allowCustomScopes is true
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):n',
footer: 'List any ISSUES CLOSED by this change (optional). E.g.: #31, #34:n',
confirmCommit: 'Are you sure you want to proceed with the commit above?',
},
allowCustomScopes: true,
allowBreakingChanges: ['feat', 'fix'],
// skip any questions you want
skipQuestions: ['body'],
// limit subject length
subjectLimit: 100,
};
- types: 描述修改的性质是什么,是bugfix还是feat,在这里进行定义。
- scopes: 定义之后,我们就可以通过上下键去选择
scope
- scopeOverrides: 针对每一个type去定义scope
- allowBreakingChanges: 如上设置为
['feat', 'fix']
,只有我们type选择了feat
或者是fix
,才会询问我们 breaking message. - allowCustomScopes: 设置为 true,在 scope 选择的时候,会有
empty
和custom
可以选择,顾名思义,选择empty
表示scope
缺省,如果选择custom
,则可以自己输入信息 - skipQuestions: 指定跳过哪些步骤,例如跳过我们刚刚说的详细描述,设置其为
scope: ['body']
,假设我们的项目也不会涉及到关联 issue,我们可以设置其为scope: ['body', 'footer']
- subjectLimit: 描述的长度限制
这里我就不一一演示每个字段修改之后的情况了,根据字段的说明,建议如果想自定义提交规则,在本地进行修改验证,公司内部的代码库不需要管理 issue
,另外,我不喜欢写长描述,所以我把 body
和 footer
给 skip
掉了。
cz-customizable
会首先在项目根目录下寻找: .cz-config.js
或 .config/cz-config.js
,如果找不到,会去主目录寻找。我们也可以在 package.json
中手动去指定配置文件的路径。
"config": {
"commitizen": {
"path": "node_modules/cz-customizable"
},
"cz-customizable": {
"config": "config/path/to/my/config.js"
}
}
现在,我们已经规范了 commit
信息,但是没有对提交的代码进行规范,在一个代码库中,经常出现2个空格/4个空格混用,有些地方写 ;
,有些不写 ;
,风格不统一。例如,我们希望提交到git库的代码,都能够通过 eslint
检查或者是通过测试。我们可以借助于 pre-commit
这个钩子来做这些事情。
2. 代码提交前检查
安装依赖
代码语言:javascript复制npm install lint-staged -D
使用 pre-commit 的 hook
代码语言:javascript复制"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"**/*.js": [
"prettier --write",
"eslint"
]
}
这样配置之后,每次提交的时候,都会对要提交的文件(并不是对整个项目)进行 prettier
格式化和 eslint
检查,都通过之后,才能 commit
成功。
eslint 和 prettier 配置
我的项目是 react
项目,下面是我进行的配置。
安装 eslint
和 prettier
相关依赖:
npm install eslint eslint-config-prettier eslint-plugin-promise eslint-plugin-react eslint-plugin-react-hooks prettier babel-eslint -D
新建
.prettierrc.js
当然啦,你也可以在 package.json
的 prettier
字段中配置,这里我配置成了独立的文件,以便后期维护。
module.exports = {
printWidth: 100, //长度超过100断行
singleQuote: true,//使用单引号
};
如果你有一些文件不需要 prettier
进行格式化,那么可以新建一个 .prettierignore
文件,如下:
dist
node_modules
public
新建
.eslintrc.js
文件
以下是我的配置:
代码语言:javascript复制module.exports = {
settings: {
react: {
pragma: 'React',
version: 'detect'
}
},
// babel parser to support ES6/7 features
parser: 'babel-eslint',
parserOptions: {
ecmaVersion: 7,
ecmaFeatures: {
experimentalObjectRestSpread: true,
jsx: true
},
sourceType: 'module'
},
extends: [
'prettier',
'prettier/react'
],
plugins: [
'promise',
'react',
'react-hooks'
],
env: {
browser: true,
es6: true,
node: true
},
rules: {
'no-compare-neg-zero': 2, //禁止与 -0 进行比较
'no-cond-assign': 2, //禁止条件表达式中出现赋值操作符
'no-console': 1, //禁用 console
'no-constant-condition': 1, //禁止在条件中使用常量表达式
'no-control-regex': 1, //禁止在正则表达式中使用控制字符
'no-debugger': 2, //禁用 debugger
'no-dupe-args': 2, //禁止 function 定义中出现重名参数
'no-dupe-keys': 2, //禁止对象字面量中出现重复的 key
'no-duplicate-case': 2, //禁止出现重复的 case 标签
'no-const-assign': 1, //禁止修改const声明的变量
'no-empty': 1, //禁止出现空语句块
'no-empty-character-class': 2, //禁止在正则表达式中使用空字符集
'no-ex-assign': 2, //禁止对 catch 子句的异常参数重新赋值
'no-extra-boolean-cast': 1, //禁止不必要的布尔转换
'no-extra-semi': 1, //禁止不必要的分号
'no-func-assign': 2, //禁止对 function 声明重新赋值
'no-inner-declarations': 0, //禁止在嵌套的块中出现变量声明或 function 声明,ES6中无需禁止
'no-invalid-regexp': 2, //禁止 RegExp 构造函数中存在无效的正则表达式字符串
'no-irregular-whitespace': 1, //禁止在字符串和注释之外不规则的空白
'no-obj-calls': 2, //禁止把全局对象作为函数调用,比如Math() JSON()
'no-regex-spaces': 1, //禁止正则表达式字面量中出现多个空格
'no-sparse-arrays': 1, //禁用稀疏数组
'no-unexpected-multiline': 1, //禁止出现令人困惑的多行表达式
'no-unreachable': 1, //禁止在return、throw、continue 和 break 语句之后出现不可达代码
'no-unsafe-finally': 2, //禁止在 finally 语句块中出现控制流语句
'no-unsafe-negation': 1, //禁止对关系运算符的左操作数使用否定操作符
'use-isnan': 2, //要求使用 isNaN() 检查 NaN,如 isNaN(foo),而非foo == NaN
'valid-typeof': 2, //强制 typeof 表达式与有效的字符串(如: 'undefined', 'object', 'boolean', 'number', 'string', 'function','symbol')进行比较
'no-case-declarations': 1, //不允许在 case 子句中使用词法声明
'no-empty-pattern': 2, //禁止使用空解构模式
'no-fallthrough': 2, //禁止 case 语句落空
'no-global-assign': 2, //禁止对原生对象或只读的全局对象进行赋值
'no-octal': 1, //禁用八进制字面量
'no-redeclare': 1, //禁止多次声明同一变量
'no-self-assign': 1, //禁止自我赋值
'no-unused-labels': 1, //禁用出现未使用过的标
'no-useless-escape': 1, //禁用不必要的转义字符
'no-delete-var': 2, //禁止删除变量
'no-undef': 2, //禁用使用未声明的变量,除非它们在 /*global */ 注释中被提到
'no-unused-vars': 1, //禁止出现未使用过的变量
'constructor-super': 2, //要求在构造函数中有 super() 的调用
'no-class-assign': 2, //禁止给类赋值
'no-dupe-class-members': 2, //禁止类成员中出现重复的名称
'no-new-symbol': 2, //禁止 Symbol 和 new 操作符一起使用
'no-this-before-super': 2, //禁止在构造函数中,在调用 super() 之前使用 this 或 super
'require-yield': 2, //要求 generator 函数内有 yield
'no-mixed-spaces-and-tabs': 1, //要求不适用space,tab混用
'react/forbid-prop-types': [1, { forbid: ['any'] }], //禁止某些propTypes
'react/prop-types': 1, //没用对props类型进行校验
'react/jsx-closing-bracket-location': 1, //在JSX中验证右括号位置
'react/jsx-curly-spacing': [1, { when: 'never', children: true }], //在JSX属性和表达式中加强或禁止大括号内的空格。
'react/jsx-key': 2, //在数组或迭代器中验证JSX具有key属性
'react/jsx-max-props-per-line': [1, { maximum: 1 }], // 限制JSX中单行上的props的最大数量
'react/jsx-no-duplicate-props': 2, //防止在JSX中重复的props
'react/jsx-no-undef': 1, //在JSX中禁止未声明的变量
'react/no-string-refs': 1, //Using string literals in ref attributes is deprecated
'react/jsx-uses-react': 1, //防止反应被错误地标记为未使用
'react/jsx-uses-vars': 1, //防止在JSX中使用的变量被错误地标记为未使用
'react/no-danger': 1, //防止使用危险的JSX属性
'react/no-did-update-set-state': 2, //防止在componentDidUpdate中使用setState
'react/no-did-mount-set-state': 0, //防止在componentDidUpdate中使用setState
'react/no-direct-mutation-state': 2, //防止this.state赋值
'react/no-unknown-property': 2, //防止使用未知的DOM属性
'react/prefer-es6-class': 1, //为React组件强制执行ES5或ES6类
'react/react-in-jsx-scope': 0, //使用JSX时,必须要引入React
'react/sort-comp': 0, //强制组件方法顺序
'react/sort-prop-types': 0, //强制组件属性顺序
'react/jsx-sort-props': 1,
'react/no-deprecated': 1, //不使用弃用的方法
'react/jsx-equals-spacing': 1, //在JSX属性中强制或禁止等号周围的空格
'react/wrap-multilines': 0,
'comma-dangle': 1, //对象字面量项尾不能有逗号
'react/no-multi-comp': 0, //防止每个文件有多个组件定义
'flowtype/generic-spacing': 0, //泛型对象的尖括号中类型前后的空格规范
'flowtype/space-after-type-colon': 0, //类型注解分号后的空格规范
// react-hooks
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn'
}
};
现在,再也不能随心所欲往你的代码库提交文件啦,不过 eslint
和 prettier
的规则要和团队的成员协商制定哈~
参考资料:
- https://juejin.im/post/6844903831893966856
- react-native-web 代码库配置