当我们的代码库有很多人维护时,经常会出现代码风格不一致或者代码质量不过关,提交信息杂乱的情况,当然啦,即使是一个人的代码库,有的时候,自己写代码时不太注意细节,也会出现风格不一致的情况。
本文正是为了解决这个问题而生,阅读本篇文章并不需要很长时间,如果你的代码库还没有进行这些配置,正是你大展身手的好时机,武装一下你的代码库。
1. 规范 commit 信息
首先,看下 angular 的代码库的 commit 记录,如图:
我们可以利用 commitizen 和 husky 来规范代码库的 commit。
安装以下依赖:
npm install @commitlint/cli @commitlint/config-conventional husky -D
如果你还没有安装过 commitizen,那么先全局安装:
npm install commitizen -g
在 package.json 中增加 husky 字段。
{ "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 文件
module .exports = { extends : ["@commitlint/config-conventional" ], };
此刻开始,请使用 git cz 来替代 git commit 提交信息,我们来看看,假设我们随便写一个 git commit -m 'fixbug' 会提示什么?
使用 git cz 来进行填写 commit 的内容。
git cz 的 type 说明:
虽然,我们现在已经可以规范提交信息了,但是我们可能不喜欢默认的交互,例如,一个精简的描述就可以了,不希望再提示我去写详细的描述,那么就可以使用 cz-customizable 来进行定制。
自定义提交说明
安装 cz-customizable
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
//.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 code\n (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 tools\n 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. 代码提交前检查
安装依赖
npm install lint-staged -D
使用 pre-commit 的 hook
"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_modulespublic
新建 .eslintrc.js 文件
以下是我的配置:
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 代码库配置