# code-lint **Repository Path**: KimSohyunZz/code-lint ## Basic Information - **Project Name**: code-lint - **Description**: 代码规范工具集成 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-01-11 - **Last Updated**: 2023-01-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README #### 前言 最近做公司项目加上维护公司其他项目,观摩了其他同事的代码,发现所有人的代码风格都不相同,包括提交代码的`commit-msg`,在修改某项功能,去代码仓库查询当时写该代码的目的时,偶然还会遇到`commit-msg`为`..`的情况,更甚的是,合并代码还会因为各种文件格式引起的冲突,故有此篇文章。 #### 1、工具列表 - 第三方依赖 > `ESlint` 代码校验工具 > > `Prettier` 代码美化工具 > > `husky` Modern native Git hooks made easy(git hook管理工具) > > `lint-stage` Run linters against staged git files and don't let 💩 slip into your code base!(git 暂存区代码校验工具) > > `commitizen` commit-msg工具,统一管理commit-msg的工具 > > `commitlint` commit-msg 校验工具,本文使用了`commitizen`,所以暂时不使用这个,只做简单介绍 - 开发环境 > `VSCode` 代码编辑器 > > `nvm` node版本管理工具,可能涉及到部分 #### 2、开搞 ##### 1、初始化项目 以`React`项目为例,使用脚手架搭建一个项目,具体方式参考[Vite快速搭建React-ts项目](https://cn.vitejs.dev/guide/#scaffolding-your-first-vite-project),目录如下: ![](http://1.15.103.126:9528/miscellaneous/QQ20230110-211428@2x.png) :star:这里我包管理使用的[pnpm](https://pnpm.io/zh/),使用`npm`也可以。 --- ##### 2、ESlint `Eslint`是非常热门的代码校验工具,也是我们使用初期非常头疼的工具,万事开头难,后面会习惯,[ESlint官网](https://eslint.org/),按照文档就能生成基本的`Elsint`配置: ```shell # 安装依赖 pnpm i eslint @eslint/create-config -D # 初始化eslint 配置文件 npx eslint --init 或者 npm init @eslint/config ``` ![](http://1.15.103.126:9528/miscellaneous/QQ20230110-213458@2x.png) 选择`ESlint`校验的严格程度,直接选择最严格的校验方式,我就是这么牛逼~~:laughing: --- ![](http://1.15.103.126:9528/miscellaneous/QQ20230110-213655@2x.png) 选择项目使用的模块化规范,根据需求选择就行,这里使用的`Vite`搭建的项目,所以也就跟着`Vite`选择`ESM`模块化规范 --- ![](http://1.15.103.126:9528/miscellaneous/QQ20230110-213926@2x.png) 选择项目使用的框架,根据自己项目实际情况选择,这里选择`React` --- ![](http://1.15.103.126:9528/miscellaneous/QQ20230110-214102@2x.png) 选择项目是否使用了`Typescript`,根据自己项目情况选择,这里选择`Yes` --- ![](http://1.15.103.126:9528/miscellaneous/QQ20230110-214239@2x.png) 选择项目运行环境,根据实际需求选择,前端项目大部分都是在浏览器运行,除非像`nestjs、express`这样框架生成的代码,一般运行在`Node`环境下座位服务端代码运行,这里选择`Browser` --- ![](http://1.15.103.126:9528/miscellaneous/QQ20230110-214531@2x.png)使用当前流行的代码规范法则,直接选第一个,第二个没试过,有空试一试:headphones: --- ![](http://1.15.103.126:9528/miscellaneous/QQ20230110-220105@2x.png) 选择一个规则库,选择`Standard`,如果上面是否启用`Typescript`,选择了`No`,这里建议选择`Airbnb`的规范,如果喜欢其他规范也行,看自己以及团队的意见 --- ![](http://1.15.103.126:9528/miscellaneous/QQ20230110-220340@2x.png) 选择生产的`ESlint`配置文件的文件格式,喜欢那种选那种,这里选择`JavaScript` --- ![](http://1.15.103.126:9528/miscellaneous/QQ20230110-220527@2x.png) 上面所有的配置项需要安装新的依赖,是否现在安装,选择`Yes` --- ![](http://1.15.103.126:9528/miscellaneous/QQ20230110-220631@2x.png) 选择报管理工具,依据自己项目而定,最好别几个包管理工具混用,选择`pnpm` --- 完成后会生成`.eslintrc.js`配置文件,需要添加额外配置: ```js module.exports = { env: { browser: true, es2021: true }, extends: [ 'eslint:recommended', 'plugin:react/jsx-runtime', 'plugin:react/recommended', 'standard-with-typescript' ], overrides: [ ], parserOptions: { ecmaVersion: 'latest', parser: '@typescript-eslint/parser', sourceType: 'module', + project: ['./tsconfig.json'] }, plugins: [ 'react' ], rules: { 'no-console': 'off', // 关闭console对象方法校验 'import/no-unresolved': 'off', // 关闭 'import/no-absolute-path': 'off', // 关闭绝对路径校验 'import/extensions': 'off', // 导入文件扩展名 'max-len': ['error', { code: 200 }], 'vue/no-v-model-argument': 'off', // vue代码v-model参数校验 'vue/multi-word-component-names': 'off', // vue组件名校验 'no-undef': 'off', // 关闭未定义校验,ts校验会检测ForStatement 'no-restricted-syntax': ['off', { selector: 'ForStatement' }], 'no-unused-vars': 'off', // 解决ts中全局类型声明错误的校验 '@typescript-eslint/no-unused-vars': ['error'], // 解决ts中全局类型声明报错错误的校验 'import/prefer-default-export': 'off', // 允许一个ts、js只导出单个方法 'no-use-before-define': 'warn', // 允许先使用,后声明 'no-param-reassign': 'off', // 允许对对象进行重新赋值 'import/no-extraneous-dependencies': 'off', // 可导入dev依赖 'consistent-return': 'off', // 关闭函数必须返回一个值 'guard-for-in': 'off', // 关闭for循环需要验证原型上是否存在该属性 'prefer-destructuring': 'off', // 关闭强制使用对象解构 '@typescript-eslint/explicit-function-return-type': 'off' } } ``` - `parserOption`在ts项目中需要加上`project:["./tsconfig.json"]` - `rules` 可以根据项目和团队意见修改部分校验规则,毕竟`Eslint`部分规则反人类 效果: ![](http://1.15.103.126:9528/miscellaneous/QQ20230111-133353@2x.png) `tsx`文件报错了,`Eslint`所有不能自动修复的错误,都可以在相关网站找到错误原因,根据说明修改即可 最后,`VScode`需要安装对应的`Eslint`插件,并且启用。 --- ##### 3、Prettier `Prettier`主要是和`ESlint`搭配使用的,[Prettier官网](https://prettier.io/) ```shell pnpm i prettier eslint-config-prettier eslint-plugin-prettier -D ``` `eslint-config-prettier`用于关闭一部分没要的规则以及处理可能和`prettier`冲突的规则 `eslint-plugin-prettier` 用于处理格式化 在`Eslint`配置文件`.eslintrc.js`添加如下配置: ```js module.exports = { env: { browser: true, es2021: true }, extends: [ + 'prettier', // 第一行 'eslint:recommended', 'plugin:react/jsx-runtime', 'plugin:react/recommended', 'standard-with-typescript' ], overrides: [ ], parserOptions: { ecmaVersion: 'latest', parser: '@typescript-eslint/parser', sourceType: 'module', project: ['./tsconfig.json'] }, plugins: [ 'react', + 'prettier' // '@typescript-eslint' ], rules: { 'no-console': 'off', // 关闭console对象方法校验 'import/no-unresolved': 'off', // 关闭 'import/no-absolute-path': 'off', // 关闭绝对路径校验 'import/extensions': 'off', // 导入文件扩展名 'max-len': ['error', { code: 200 }], 'vue/no-v-model-argument': 'off', // vue代码v-model参数校验 'vue/multi-word-component-names': 'off', // vue组件名校验 'no-undef': 'off', // 关闭未定义校验,ts校验会检测ForStatement 'no-restricted-syntax': ['off', { selector: 'ForStatement' }], 'no-unused-vars': 'off', // 解决ts中全局类型声明错误的校验 '@typescript-eslint/no-unused-vars': ['error'], // 解决ts中全局类型声明报错错误的校验 'import/prefer-default-export': 'off', // 允许一个ts、js只导出单个方法 'no-use-before-define': 'warn', // 允许先使用,后声明 'no-param-reassign': 'off', // 允许对对象进行重新赋值 'import/no-extraneous-dependencies': 'off', // 可导入dev依赖 'consistent-return': 'off', // 关闭函数必须返回一个值 'guard-for-in': 'off', // 关闭for循环需要验证原型上是否存在该属性 'prefer-destructuring': 'off', // 关闭强制使用对象解构 '@typescript-eslint/explicit-function-return-type': 'off' } } ``` - `extends`字段的`prettier`必须在第一行,不然格式化无法生效,官网说明是`prettier`需要放在最后一个,不知道是不是bug:small_airplane: - `eslint-config-prettier`,可以理解为`ESlint`中对`rule`的覆盖,[使用说明](https://github.com/prettier/eslint-config-prettier) - `eslint-plugin-prettier`,可以理解为将`prettier`以`eslint`运行的工具,[使用说明](https://github.com/prettier/eslint-plugin-prettier) --- ##### 4、husky、lint-staged `husky` 让原生`Git`变得简单,介绍`husky`之前需要简单介绍一下`Git Hook`。 `Hooks`就是在特定特定时间点执行的时间,以下为常见`Git Hook`,具体参考[Git Hook](https://git-scm.com/docs/githooks) | hook名 | 说明 | | -------------------- |:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `applypatch-msg` | 它接收单个参数:包含请求合并信息的临时文件的名字。 如果脚本返回非零值,Git 将放弃该补丁。 你可以用该脚本来确保提交信息符合格式,或直接用脚本修正格式错误。 | | `commit-msg` | 钩子在启动提交信息编辑器之前,默认信息被创建之后运行。 它允许你编辑提交者所看到的默认信息。 该钩子接收一些选项:存有当前提交信息的文件的路径、提交类型和修补提交的提交的 SHA-1 校验。 它对一般的提交来说并没有什么用;然而对那些会自动产生默认信息的提交,如提交信息模板、合并提交、压缩提交和修订提交等非常实用。 你可以结合提交模板来使用它,动态地插入信息。 | | `post-update` | 仅在所有的ref被push之后执行一次。它与post-receive很像,但是不接收旧值与新值。主要用于通知。每个被push的repo都会生成一个参数,参数内容是ref的名称。 | | `pre-applypatch` | 实际上的调用时机是应用补丁之后、变更commit之前。如果以非0的状态退出,会导致变更成为uncommitted状态。可用于在实际进行commit之前检查代码树的状态或用它在提交前检查快照。 你可以用这个脚本运行测试或检查工作区。 如果有什么遗漏,或测试未能通过,脚本会以非零值退出,中断 git am 的运行,这样补丁就不会被提交。 | | `pre-commit` | 钩子在键入提交信息前运行。 它用于检查即将提交的快照,例如,检查是否有所遗漏,确保测试运行,以及核查代码。 如果该钩子以非零值退出,Git 将放弃此次提交,不过你可以用 git commit --no-verify 来绕过这个环节。 你可以利用该钩子,来检查代码风格是否一致(运行类似 lint 的程序)、尾随空白字符是否存在(自带的钩子就是这么做的),或新方法的文档是否适当。 | | `prepare-commit-msg` | 钩子在启动提交信息编辑器之前,默认信息被创建之后运行。 它允许你编辑提交者所看到的默认信息。 该钩子接收一些选项:存有当前提交信息的文件的路径、提交类型和修补提交的提交的 SHA-1 校验。 它对一般的提交来说并没有什么用;然而对那些会自动产生默认信息的提交,如提交信息模板、合并提交、压缩提交和修订提交等非常实用。 你可以结合提交模板来使用它,动态地插入信息。 | | `pre-push` | 钩子会在 git push 运行期间, 更新了远程引用但尚未传送对象时被调用。 它接受远程分支的名字和位置作为参数,同时从标准输入中读取一系列待更新的引用。 你可以在推送开始之前,用它验证对引用的更新操作(一个非零的退出码将终止推送过程)。 | | `pre-rebase` | 钩子运行于变基之前,以非零值退出可以中止变基的过程。 你可以使用这个钩子来禁止对已经推送的提交变基。 Git 自带的 pre-rebase 钩子示例就是这么做的,不过它所做的一些假设可能与你的工作流程不匹配。 | | `pre-receive(服务器端)` | 处理来自客户端的推送操作时,最先被调用的脚本是 pre-receive。 它从标准输入获取一系列被推送的引用。如果它以非零值退出,所有的推送内容都不会被接受。 你可以用这个钩子阻止对引用进行非快进(non-fast-forward)的更新,或者对该推送所修改的所有引用和文件进行访问控制。 | | `update(服务器端)` | update 脚本和 pre-receive 脚本十分类似,不同之处在于它会为每一个准备更新的分支各运行一次。 假如推送者同时向多个分支推送内容,pre-receive 只运行一次,相比之下 update 则会为每一个被推送的分支各运行一次。 它不会从标准输入读取内容,而是接受三个参数:引用的名字(分支),推送前的引用指向的内容的 SHA-1 值,以及用户准备推送的内容的 SHA-1 值。 如果 update 脚本以非零值退出,只有相应的那一个引用会被拒绝;其余的依然会被更新。 | | `post-receive(服务器端)` | post-receive 挂钩在整个过程完结以后运行,可以用来更新其他系统服务或者通知用户。 它接受与 pre-receive 相同的标准输入数据。 它的用途包括给某个邮件列表发信,通知持续集成(continous integration)的服务器, 或者更新问题追踪系统(ticket-tracking system) —— 甚至可以通过分析提交信息来决定某个问题(ticket)是否应该被开启,修改或者关闭。 该脚本无法终止推送进程,不过客户端在它结束运行之前将保持连接状态, 所以如果你想做其他操作需谨慎使用它,因为它将耗费你很长的一段时间。 | | `post-checkout` | 更新工作树后调用checkout时调用,或者执行 git clone后调用。主要用于验证环境、显示变更、配置环境。在 git checkout 成功运行后,post-checkout 钩子会被调用。你可以根据你的项目环境用它调整你的工作目录。 其中包括放入大的二进制文件、自动生成文档或进行其他类似这样的操作。 | | `post-rewrite` | 本hook在git命令重写(rewrite)已经被commit的数据时调用。除了其携带的参数之外,本hook还从stdin接收信息,信息格式为” ”。post-rewrite 钩子被那些会替换提交记录的命令调用,比如 git commit --amend 和 git rebase(不过不包括 git filter-branch)。 它唯一的参数是触发重写的命令名,同时从标准输入中接受一系列重写的提交记录。 这个钩子的用途很大程度上跟 post-checkout 和 post-merge 差不多。 | :star:`husky`就是在这些`Hook`中添加事件,例如: - `lint-stage`是在`pre-commit`中触发事件 - `commitlint`是在`commit-msg`中触发事件 ###### 安装husky - 安装依赖 ```shell pnpm i husky -D ``` - 启用husky,这步需要仓库已经初始化,即已经存在.git文件 ```shell npx husky install ``` 执行完成后会生成下面的文件: ![](http://1.15.103.126:9528/miscellaneous/QQ20230111-163715@2x.png) `.husky`文件夹会存放所有的`Git hook`需要调用的事件。 - 生成`.husky`初始化脚本(可选) ```shell npm pkg set scripts.prepare="husky install" ``` 也可以直接在`package.json`中添加: ![](http://1.15.103.126:9528/miscellaneous/QQ20230111-164149@2x.png) --- ###### 添加Hook 使用`husky`创建hook ```shell npx husky add .husky/pre-commit "npx lint-staged" ``` 将hook添加到`git` ```shell git add .husky/pre-commit ``` 更多`husky`用法参考[husky官网](https://typicode.github.io/husky/#/) --- ###### 安装lint-staged `lint-staged`是对git暂存区进行额外的操作,这里就可以使用`prettier`进行再次格式化,防止未格式化的代码被提交到仓库 安装依赖 ```shell pnpm i lint-staged -D ``` 添加配置文件: ![](http://1.15.103.126:9528/miscellaneous/QQ20230111-165804@2x.png) ```json "lint-staged": { "*.{js,jsx,ts,tsx,vue}": "prettier --write --ignore-unknown" } ``` 这里的意思就是执行`lint-staged`时候,会将以`.js、.jsx、.ts、.tsx、.vue`结尾的文件使用`prettier`进行格式化。 执行代码提交,执行`git add . => git commit -m "xxx"` ![](http://1.15.103.126:9528/miscellaneous/QQ20230111-170647@2x.png)可以看到在提交之前,对所有代码进行了一次格式化。 `lint-staged`更多用法和说明,参考[lint-staged逛网](https://github.com/okonet/lint-staged) --- ##### 5、commitizen `commitizen` 相当于对`commit-msg`进行规范的工具,目前主流的规范都是`Angular`提出的规范`@commitlint/config-conventional` | 类型 | 说明 | |:----------:|:---------------------------:| | `build` | 编译相关的修改,例如发布版本、对项目构建或者依赖的改动 | | `chore` | 其他修改, 比如改变构建流程、或者增加依赖库、工具等 | | `ci` | 持续集成修改 | | `docs` | 文档修改 | | `feat` | 新特性、新功能 | | `fix` | 修改BUG | | `perf` | 优化相关,比如提升性能、体验 | | `revert` | 回滚到上一个版本 | | `style` | 代码格式修改, 注意不是 css 修改 | | `refactor` | 代码重构 | | `test` | 测试用例修改 | 项目采用自定义规则,不使用`Angular`的规则 安装依赖(也可以全局安装) ```shell pnpm install commitizen cz-customizable cz-conventional-changelog -D ``` 初始化`cz-conventional-changelog` ```shell npx commitizen init cz-conventional-changelog --save-dev --save-exact ``` - 命令可能执行报错,可以尝试切换到低版本`Nodejs`进行执行,建议`v.14.x` 执行完成,会在`package.json`添加如下配置 ![](http://1.15.103.126:9528/miscellaneous/QQ20230111-195746@2x.png) 根目录创建`.cz-config.js`配置文件 ```js module.exports = { types: [ { value: '✨新增', name: '新增: 新的内容' }, { value: '🐛修复', name: '修复: 修复一个Bug' }, { value: '📝文档', name: '文档: 变更的只有文档' }, { value: '💄格式', name: '格式: 空格, 分号等格式修复' }, { value: '♻️重构', name: '重构: 代码重构,注意和特性、修复区分开' }, { value: '⚡️性能', name: '性能: 提升性能' }, { value: '✅测试', name: '测试: 添加一个测试' }, { value: '🔧工具', name: '工具: 开发工具变动(构建、脚手架工具等)' }, { value: '⏪回滚', name: '回滚: 代码回退' } ], scopes: [ { name: 'javascript' }, { name: 'typescript' }, { name: 'Vue' }, { name: 'node' }, { name: '其他' } ], // 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: '选择一种你的提交类型:', scope: '选择一个scope (可选):', // used if allowCustomScopes is true // customScope: 'Denote the SCOPE of this change:', subject: '短说明:\n', body: '长说明,使用"|"换行(可选):\n', breaking: '非兼容性说明 (可选):\n', footer: '关联关闭的issue,例如:#31, #34(可选):\n', confirmCommit: '确定提交说明?(yes/no)', }, allowCustomScopes: false, allowBreakingChanges: ['特性', '修复'], // limit subject length subjectLimit: 100 } ``` - 根据项目需求,对配置文件进行修,配置参考[cz-customiable](https://github.com/leoforfree/cz-customizable) 配置`package.json`,启用自定义文件 ![](http://1.15.103.126:9528/miscellaneous/QQ20230111-195915@2x.png) ```json "config": { "commitizen": { "path": "./node_modules/cz-customizable" } } ``` 到此,`commit-msg`配置完成,代码提交步骤更改为下面这样: - `git add xxx` - `git commit由以下命令替换`,如果`commitizen`是全局安装,可以使用`git cz`或者直接`cz`命令,若是项目局部安装,使用`npx cz`,若在`package.json`中定义了`script`,例如上面的`commit:git cz`,则可以使用`pnpm run commit`,[参考地址](https://github.com/commitizen/cz-cli) 现在的提交界面: ![](http://1.15.103.126:9528/miscellaneous/QQ20230111-202005@2x.png) 根据提示一步步向下填写即可 --- ##### 问题汇总: - `ESlint`不起作用?检查编译器是否安装了`ESlint`插件,`TS`项目检查`.eslintrc.js`是否配置了`parseOption中的project:["./tsconfig.json"]` - `Prettier`不能再保存的时候自动格式化,检查编译器的默认格式化工具,`VScode`直接搜`Default Formatter`进行设置,`.eslintrc.js`中`extends`配置`prettier`是否处于第一顺位。 ##### 6、commitlint 下次再说吧 :crab: