Skip to main content

格式规范和语法检测实践

editorConfig

在项目中引入 editorconfig 是为了在多人协作开发中保持代码的风格和一致性。不同的开发者使用不同的编辑器或 IDE,可能会有不同的缩进(比如有的人喜欢 4 个空格,有的喜欢 2 个空格)、换行符、编码格式等。甚至相同的编辑器因为开发者自定义配置的不同也会导致不同风格的代码,这会导致代码的可读性降低,增加代码冲突的可能性,降低了代码的可维护性。

EditorConfig 使不同编辑器可以保持同样的配置。因此,我们得以无需在每次编写新代码时,再依靠 Prettier 来按照团队约定格式化一遍(出现保存时格式化突然改变的情况) 。当然这需要在你的 IDE 上安装了必要的 EditorConfig 插件或扩展。

安装 editorConfig 插件

添加到工作区建议

然后在插件的介绍页上点击设置的齿轮,并且选择 Add to Workspace Recommendations,就可以将其加入清单。也可以直接开启 .vscode/extensions.json 进行编辑:

向团队每一位成员建议安装此插件 eslint 与 prettier 我们也进行这一配置

// .vscode/extensions.json
{
"recommendations": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}

在根目录新建.editorconfig

# https://editorconfig.org
root = true # 设置为true表示根目录,控制配置文件 .editorconfig 是否生效的字段

[*] # 匹配全部文件,匹配除了 `/` 路径分隔符之外的任意字符串
charset = utf-8 # 设置字符编码,取值为 latin1,utf-8,utf-8-bom,utf-16be 和 utf-16le,当然 utf-8-bom 不推荐使用
end_of_line = lf # 设置使用的换行符,取值为 lf,cr 或者 crlf
indent_size = 2 # 设置缩进的大小,即缩进的列数,当 indexstyle 取值 tab 时,indentsize 会使用 tab_width 的值
indent_style = space # 缩进风格,可选space|tab
insert_final_newline = true # 设为true表示使文件以一个空白行结尾
trim_trailing_whitespace = true # 删除一行中的前后空格

[*.md] # 匹配全部 .md 文件
trim_trailing_whitespace = false

可以规范本项目中文件的缩进风格,和缩进空格数等,会覆盖 vscode 的配置,来达到不同编辑器中代码默认行为一致的作用。 VS Code 的 EditorConfig 目前支持下列属性:

indent_style indent_size tab_width end_of_line(on save) insert_final_newline(on save) trim_trailing_whitespace(on save)

eslint

关注代码质量,检查代码风格

ESLint 是什么呢?一个开源的 JavaScript 的 linting 工具,是一个在 JavaScript 代码中通过规则模式匹配作代码识别和报告的插件化的检测工具,它的目的是保证代码规范的一致性和及时发现代码问题、提前避免错误发生。它使用 espree 将 JavaScript 代码解析成抽象语法树 (AST),然后通过 AST 来分析我们代码,从而给予我们两种提示:

代码质量问题:使用方式有可能有问题,eslint 可以发现代码中存在的可能错误,如使用未声明变量、声明而未使用的变量、修改 const 变量、代码中使用 debugger 等等 代码风格问题:风格不符合一定规则,eslint 也可以用来统一团队的代码风格,比如加不加分号、使用 tab 还是空格、字符串使用单引号 等等

npm i -D eslint eslint-plugin-react-hooks eslint-plugin-react-refresh @typescript-eslint/parser @typescript-eslint/parser

需要告诉 eslint 使用的 react 版本,在 .eslintrc.js 和 rules 平级添加 settings 配置,让 eslint 自己检测 react 版本,

// .eslintrc.cjs
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
/*使用eslint推荐的规则作为基础配置,可以在rules中覆盖*/
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended",
],
ignorePatterns: ["dist", ".eslintrc.cjs"],
parser: "@typescript-eslint/parser",
// 引入插件,作用类似 require
plugins: ["react-refresh"],
rules: {
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
],
},
// // 当访问当前源文件内未定义的变量时,no-undef 规则将发出警告,
// // 可以通过定义全局变量来解决。env提供了多个环境选择字段
// //一个环境定义了一组预定义的全局变量。globals可以自定义单个的全局变量。
// globals: {
// "$": "readonly"
// },
settings: {
react: {
version: "detect",
},
},
};

增加.eslintignore

node_modules
dist
env
.gitignore
pnpm-lock.yaml
README.md
src/assets/*

增加 eslint 检测脚本

// --fix:此项指示 ESLint 尝试修复尽可能多的问题。这些修复是对实际文件本身进行的,只有剩余的未修复的问题才会被输出。
{
"lint:eslint": "eslint --fix --ext .j,.ts,.tsx ./src",
}

prettier

npm i -D prettier

vscode 安装 prettier 插件 其实安装完插件已经具有 prettier 的能力了 关键是项目中需要有统一配置

// .prettierrc.js
module.exports = {
tabWidth: 2, // 一个tab代表几个空格数,默认就是2
useTabs: false, // 是否启用tab取代空格符缩进,.editorconfig设置空格缩进,所以设置为false
printWidth: 100, // 一行的字符数,如果超过会进行换行
semi: false, // 行尾是否使用分号,默认为true
singleQuote: true, // 字符串是否使用单引号
trailingComma: "none", // 对象或数组末尾是否添加逗号 none| es5| all
jsxSingleQuote: true, // 在jsx里是否使用单引号,你看着办
bracketSpacing: true, // 对象大括号直接是否有空格,默认为true,效果:{ foo: bar }
arrowParens: "avoid", // 箭头函数如果只有一个参数则省略括号
};
// .prettierignore
node_modules
dist
env
.gitignore
pnpm-lock.yaml
README.md

vscode 保存格式化

配置.vscode/settings.json

配置前两步后,虽然已经配置 prettier 格式化规则,但还需要让 vscode 来支持保存后触发格式化,在项目根目录新建 .vscode 文件夹,内部新建 settings.json 文件配置文件,代码如下:

{
"search.exclude": {
"/node_modules": true,
"dist": true,
"pnpm-lock.yaml": true
},
"files.autoSave": "onFocusChange",
"editor.formatOnSave": true,
"editor.formatOnType": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[markdown]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"javascript.validate.enable": false
}

增加 prettier 脚本

"lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,scss,stylus,html,md}\""

"--write": 表示将格式化后的结果直接写回原文件中,而不是输出到控制台。 "--loglevel warn": 表示只输出警告级别的日志信息。 "src/**/*.{js,ts,json,tsx,css,less,scss,stylus,html,md}": 是要格式化的文件的路径,这里指定了在 src 目录下,所有扩展名为 .js、.ts、.json、.tsx、.css、.less、.scss、.stylus、.html、.md 的文件。

解决 eslint 与 prettier 冲突

二者搭配使用,ESLint 关注代码质量,Prettier 关注代码格式。但是二者在格式化上面的功能有所交叉,所以 Prettier 和 ESLint 一起使用的时候会有冲突,这需要我们进行一些配置

冲突的本质在于 eslint 既负责了代码质量检测,又负责了一部分的格式美化工作,格式化部分的部分规则和 prettier 不兼容。 能不能让 eslint 只负责代码质量检测而让 prettier 负责美化呢? 好在社区有了非常好的成熟方案,即 eslint-config-prettier + eslint-plugin-prettier。

eslint-config-prettier 的作用是关闭 eslint 中与 prettier 相互冲突的规则。 eslint-plugin-prettier 的作用是赋予 eslint 用 prettier 格式化代码的能力。

安装依赖

pnpm add eslint-config-prettier eslint-plugin-prettier -D

.eslintrc.js 的 extends 中加入:

  extends: [
// ...
'plugin:prettier/recommended', // <==== 增加一行
],
note

当我们使用 Prettier + ESLint 的时候,其实格式问题两个都有参与,disable ESLint 之后,其实格式的问题已经全部由 prettier 接手了。那我们为什么还要这个 plugin?其实是因为我们期望报错的来源依旧是 ESLint ,使用这个,相当于把 Prettier 推荐的格式问题的配置以 ESLint rules 的方式写入,这样相当于可以统一代码问题的来源。

最后再配置一下 .vscode/settings.json

markdownlint

pnpm add markdownlint-cli -D

配置 markdownlint(可选)

新增.markdownlint.js MD034 和 mdx 有冲突,所以禁用掉

一些规范型规则无法自动修复,需要手动解决,因此也禁掉

{
"default": true,
"MD034": false,
"MD013": false,
"MD040":false,
"MD025":false,
"MD001":false,
"MD033": false,
"MD041": false,
"MD029": false,
"MD045": false,
"MD024": false,
"MD036": false,
"MD028": false
}

名称可以是 default、 rule、 alias 或 tags:

default:特殊属性,设为  true  会启用所有规则,反之设为  false  则关闭所有规则 rule:规则名称,对应至各个规则,像是范例中的  MD001  与 MD003 alias:规则别称,对应至各个规则的别名,例如范例中的  first-line-heading  是规则  MD041  的别名 tags:作用的标签,对应至各规则的所属标签,可以将其视为规则的群组,例如范例中的  atx_closed  包括了  MD020  与  MD021  两个规则

新建 .markdownlintignore

node_modules
dist
env
.gitignore
pnpm-lock.yaml
src/assets/*
.vscode
public
.github

加入 scripts

{
"lint:md": "markdownlint --config -fix .markdownlint.js ."
}

vscode 自动保存

{
// ...
"editor.codeActionsOnSave": {
"source.fixAll.markdownlint": true
}
}

stylelint

格式化 css

安装插件及依赖

新建.stylelintrc.js

module.exports = {
extends: [
"stylelint-config-standard",
"stylelint-prettier/recommended",
"stylelint-config-rational-order",
],
rules: {
"function-name-case": ["lower"],
"function-no-unknown": [
true,
{
ignoreFunctions: [
"fade",
"fadeout",
"tint",
"darken",
"ceil",
"fadein",
"floor",
"unit",
"shade",
"lighten",
"percentage",
"-",
],
},
],
"import-notation": null,
"no-descending-specificity": null,
"no-invalid-position-at-import-rule": null,
"declaration-empty-line-before": null,
"keyframes-name-pattern": null,
"custom-property-pattern": null,
"number-max-precision": 8,
"alpha-value-notation": "number",
"color-function-notation": "legacy",
"selector-class-pattern": null,
"selector-id-pattern": null,
"selector-not-notation": null,
},
};

新建.stylelintignore

dist
public
env
build
.vscode
.husky
.DS_Store
.github
typings
README.md
node_modules


vscode 保存格式化

{
// ...
"editor.codeActionsOnSave": {
"source.fixAll.stylelint": true
},
"stylelint.validate": ["css", "less", "sass", "stylus", "postcss"]
}

husky + lint-staged

使用 lint-staged 优化检测速度

让 eslint 和 prettier 只检测暂存区的文件

pnpm add lint-staged -D

在 package.json 添加 lint-staged 配置 (适用于 react)

检测是全部检测,但是修改只对暂存区文件做处理

"lint-staged": {
"src/**/*.{ts,tsx}": [
"pnpm run lint:eslint",
"pnpm run lint:prettier"
]
},

tsc

一些类型错误可能 eslint 检测不出来,可能需要安装 tsc

关闭 tsc 的 emit 功能,只做类型检测和修复,不编译

配置 husky

为了避免把不规范的代码提交到远程仓库,一般会在 git 提交代码时对代码语法进行检测,只有检测通过时才能被提交,git 提供了一系列的 githooks,而我们需要其中的 pre-commit 钩子,它会在 git commit 把代码提交到本地仓库之前执行,可以在这个阶段检测代码,如果检测不通过就退出命令行进程停止 commit。

而 husky 就是可以监听 githooks 的工具,可以借助它来完成这件事情。

pnpm add husky -D

为什么不直接在提交时执行脚本,而是采用 husky?

采用 husky 只是在提交前进行检测,而不是修复操作

如果检测到未修复的地方,会终止提交

直接在提交时执行脚本,提交上去的还是未修复的代码

pre-commit 钩子

新版的 husky 使用有些变化,不再是直接在 package.json 中进行配置。

npx husky install

在.husky 目录下新增 pre-commit 文件

(注意不是.husky/_目录)

. "$(dirname "$0")/_/husky.sh"

npx --no-install lint-staged

关于 husky install 官网推荐的是在 packgae.json 中添加 prepare 脚本 ,prepare 脚本会在 npm install(不带参数)之后自动执行。

这样少输一次命令,相当于把脚手架初始化命令放到 npm install 中

{
"scripts": {
"prepare": "husky install"
}
}

commitlint

对 commit message 进行规范检测

commit conventions 规范

https://www.conventionalcommits.org/en/v1.0.0/

pnpm add @commitlint/cli -D

新建commitlint.config.js

安装 commitlint 的预设规则包

pnpm add @commitlint/config-conventional -D

使用规则包

module.exports = {
extends: ["@commitlint/config-conventional"],
// ...
};

涉及以下规则包

https://github.com/conventional-changelog/commitlint/blob/5403f0b5bcab43803708997c482a44a7d1151480/@commitlint/config-conventional/index.js

在 husky 中为 commitlint 注册 git hooks 监听

npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

修改完后,要重新注册 Git hooks:

npx husky install

commitizen 规范提交

commitizen 可以用轮询交互的方式帮我们生成符合规范的commit message

全局安装

  1. 全局下载两个包 npm install -g commitizen cz-conventional-changelog

  2. 创建 ~/.czrc 文件,写入如下内容 { "path": "cz-conventional-changelog" }

  3. 这时就可以全局使用 git cz 命令来代替 git commit 命令了

项目内安装

  1. 安装 commitizen, dev 依赖 npm install --save-dev commitizen npm install --save-dev cz-conventional-changelog

  2. 配置,打开项目的 package.json 文件,配置如下

 {
"scripts": {
"commit": "git-cz",
},
"config": {
"commitizen": {
"path": "node_modules/cz-conventional-changelog"
}
}
}
  1. 使用 npm run commit 代替 git commit

change-log

安装 standard-version

https://github.com/conventional-changelog/standard-version

pnpm add standard-version -D

自动化升级版本号、生成 changelog 及 tag

添加到 package.json 脚本命令

"scripts": {
// ...
"release": "standard-version"
}

通过 pnpm run release,生成日志。

自动升级版本

  1. 版本构成 版本号 major.minor.patch

主版本号.次版本号.修订号,版本号递增规则如下:

主版本号(major):当你做了不兼容的 API 修改, 次版本号(minor):当你做了向下兼容的功能性新增,可以理解为 Feature 版本, 修订号(patch):当你做了向下兼容的问题修正,可以理解为 Bug fix 版本。

先行版本号可以加到 “主版本号.次版本号.修订号” 的后面,作为延伸。

先行版本 当即将发布大版本改动前,但是又不能保证这个版本的功能 100% 正常,这个时候可以发布先行版本:

alpha: 内部版本 beta: 公测版本 rc: 候选版本(Release candiate)

比如:1.0.0-alpha.0、1.0.0-alpha.1、1.0.0-rc.0、1.0.0-rc.1 等。

standard-verstion 生成的 CHANGELOG 只会包含 feat、fix、BREACK-CHANGE 类型的提交记录

{
"scripts": {
"release": "standard-version",
// 手动控制版本更新
"release:alpha": "standard-version --prerelease alpha",
"release:rc": "standard-version --prerelease rc",
"release:major": "pnpm run release -- --release-as major",
"release:minor": "pnpm run release -- --release-as minor",
"release:patch": "pnpm run release -- --release-as patch"
}
}

第一个版本(该方式不会升级版本号)

pnpm run release -- --first-release

配置哪些 commit 消息写入 changelog

hidden 属性值控制是否将该类型的 commit 消息写入 changlog, 不填的情况下默认是: false,在根目录下新建 .versionrc.js:

module.exports = {
types: [
{ type: "feat", section: "✨ Features | 新功能" },
{ type: "fix", section: "🐛 Bug Fixes | Bug 修复" },
{ type: "init", section: "🎉 Init | 初始化" },
{ type: "docs", section: "✏️ Documentation | 文档" },
{ type: "style", section: "💄 Styles | 风格" },
{ type: "refactor", section: "♻️ Code Refactoring | 代码重构" },
{ type: "perf", section: "⚡ Performance Improvements | 性能优化" },
{ type: "test", section: "✅ Tests | 测试" },
{ type: "revert", section: "⏪ Revert | 回退", hidden: true },
{ type: "build", section: "📦‍ Build System | 打包构建" },
{ type: "chore", section: "🚀 Chore | 构建/工程依赖/工具" },
{ type: "ci", section: "👷 Continuous Integration | CI 配置" },
],
};

配置跳过生成 changelog 这个步骤

所有可配置跳过的有: bump, changelog, commit, tag:

{
"standard-version": {
"skip": {
"changelog": true
}
}
}

作者:_你当像鸟飞往你的山 链接:https://juejin.cn/post/7207374216126922809 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

扩展