1. Eslint
yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-config-prettier eslint-config-standard-with-typescript eslint-plugin-import eslint-plugin-n eslint-plugin-prettier eslint-plugin-promise eslint-plugin-react eslint-plugin-react-hooks -D
.eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
},
extends: ["plugin:react/recommended", "standard-with-typescript", "prettier"],
overrides: [],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaFeatures: {
impliedStrict: true,
jsx: true,
},
ecmaVersion: "latest",
sourceType: "module",
project: "tsconfig.json",
},
plugins: ["react", "prettier"],
/**
* off或者0 => 关闭规则
* warn或者1 => 开启规则,警告(不影响代码执行)
* error或者2 => 开启规则,报错(代码不能执行,界面报错)
*/
rules: {
// @fixable 必须使用 === 或 !==,禁止使用 == 或 !=,与 null 比较时除外
eqeqeq: [
"error",
"always",
{
null: "ignore",
},
],
semi: [2, "always"],
// 引号风格,使用单引号,需要转义时忽略,反斜号忽略
// quotes: [
// 2,
// 'single',
// {
// avoidEscape: true,
// allowTemplateLiterals: true,
// },
// ],
"@typescript-eslint/strict-boolean-expressions": 0,
"@typescript-eslint/prefer-optional-chain": 0,
"@typescript-eslint/restrict-template-expressions": [
2,
{
allowNumber: true,
allowBoolean: true,
allowAny: true,
allowNullish: true,
allowRegExp: true,
},
],
"@typescript-eslint/semi": [2, "always"],
"@typescript-eslint/no-useless-constructor": 2,
"@typescript-eslint/no-empty-function": 1, // 禁止空函数
"@typescript-eslint/no-var-requires": 0, // 不允许在 import 语句中使用 require 语句
"@typescript-eslint/explicit-function-return-type": 0, // 不允许对初始化为数字、字符串或布尔值的变量或参数进行显式类型声明
"@typescript-eslint/explicit-module-boundary-types": 0, // 要求导出函数和类的公共类方法的显式返回和参数类型
"@typescript-eslint/no-explicit-any": 0, // 禁止使用 any 类型
"@typescript-eslint/no-use-before-define": 2,
"@typescript-eslint/no-unused-vars": 1, // 禁止定义未使用的变量
"@typescript-eslint/naming-convention": [
2,
{
selector: "variable",
format: ["camelCase", "UPPER_CASE", "snake_case", "PascalCase"],
leadingUnderscore: "forbid",
filter: {
// 忽略部分无法控制的命名
regex: "^(__html|zh_CN|en_US)$",
match: false,
},
},
{
selector: "property",
format: ["camelCase", "UPPER_CASE", "snake_case", "PascalCase"],
leadingUnderscore: "forbid",
filter: {
// 忽略部分无法控制的命名
regex: "^(__html|zh_CN|en_US)$",
match: false,
},
},
{
selector: "class",
format: ["PascalCase"],
leadingUnderscore: "forbid",
},
{
selector: "interface",
format: ["PascalCase"],
leadingUnderscore: "forbid",
},
{
selector: "parameter",
format: ["camelCase", "UPPER_CASE", "snake_case"],
leadingUnderscore: "allow",
filter: {
regex: "^(Child|WrappedComponent)$",
match: false,
},
},
{
selector: "memberLike",
format: ["PascalCase", "camelCase", "snake_case"],
leadingUnderscore: "forbid",
filter: {
// 忽略部分无法控制的命名
regex: "^(__html|zh_TW|zh_CN|en_US|_ga|SignUp_.*|Crm_.*|UNSAFE_.*)$",
match: false,
},
},
{
selector: "typeLike",
format: ["PascalCase", "camelCase", "snake_case"],
leadingUnderscore: "forbid",
},
],
// 类和接口的命名必须遵守帕斯卡命名法,比如 PersianCat
"@typescript-eslint/class-name-casing": 0,
"@typescript-eslint/camelcase": 0,
"@typescript-eslint/ban-types": [
// 禁止使用特定类型
0,
{
extendDefaults: true,
types: {
"{}": false,
Function: false,
},
},
],
"n/no-callback-literal": 0,
"linebreak-style": [2, "unix"],
"no-unused-expressions": [2, { allowShortCircuit: true, allowTernary: true }],
"no-plusplus": 0,
"no-console": 1, //console.log
"no-debugger": 2,
"linkbreak-style": ["off", "windows"],
"no-fallthrough": [
2,
{
commentPattern: "fallthrough",
},
],
"no-const-assign": 2, //禁止修改const声明的变量
"no-unused-vars": 1,
"class-methods-use-this": 0,
camelcase: 0,
"global-require": 0,
"no-use-before-define": 0,
"no-restricted-syntax": 0,
"no-continue": 0,
"consistent-return": 0,
"prefer-const": [
2,
{
destructuring: "all",
},
],
"key-spacing": [0, { beforeColon: false, afterColon: true }], // 对象字面量中冒号的前后空格
"object-curly-spacing": [0, "never"], // 大括号内是否允许不必要的空格
"generator-star-spacing": 0, // 生成器函数*的前后空格
"comma-spacing": 0, // 逗号前后的空格
"array-bracket-spacing": [2, "never"], // 是否允许非空数组里面有多余的空格
"no-trailing-spaces": 1, // 一行结束后面不要有空格
"no-multiple-empty-lines": 1, // 删除多余的空行
"no-spaced-func": 2, // 函数调用时 函数名与()之间不能有空格
"no-regex-spaces": 2, // 禁止在正则表达式字面量中使用多个空格 /foo bar/
"no-multi-spaces": 1, // 不能用多余的空格
"no-mixed-spaces-and-tabs": [2, false], // 禁止混用tab和空格
"no-irregular-whitespac": 0, // 不能有不规则的空格
"require-render-return": 0, // 确保在 render 方法中存在返回值
"react/react-in-jsx-scope": 0, // 可用空标签<></>
// 自闭合的标签前要加一个空格
"no-multi-spaces": 1,
"react/jsx-tag-spacing": 1,
"react/self-closing-comp": 2, // 没有子元素的标签请自闭合
"react/jsx-closing-bracket-location": 1, // 如果组件包含多行属性,在新的一行闭合标签
"react/no-array-index-key": 0, // 避免使用数组的索引作为 key 属性值, 建议使用稳定的ID
"react/jsx-boolean-value": 1, // 当属性值为true时可以省略
"react/jsx-curly-spacing": 1, // 不要在 JSX 的花括号里边加空格
"react/jsx-closing-bracket-location": 1, // 对齐:遵循以下JSX语法的对齐风格
"react/no-multi-comp": 2, // 每个文件只包含一个 React 类组件,但是多个函数式组件可以放到一个文件中
"react/jsx-pascal-case": 2, // 引用命名:React 组件使用 大驼峰,组件实例使用 小驼峰
"react/jsx-filename-extension": [2, { extensions: [".tsx", "ts", ".jsx", "js"] }],
"react/jsx-indent-props": [2, 2],
"react/jsx-indent": [2, 2],
"react/jsx-one-expression-per-line": 0,
"react/destructuring-assignment": 0,
"react/state-in-constructor": 0,
"react/require-default-props": 0,
"react/jsx-props-no-spreading": 0,
"react/prop-types": 0,
"jsx-quotes": [2, "prefer-single"],
},
};
Ignore check .eslintignore
# 忽略不必要检查的文件
*.sh
*.md
*.woff
*.ttf
.vscode
.idea
.husky
.local
.nyc_output
/mock/*
/dist/*
/static
/public
/docs
/node_modules
/bin
/test
/unittest
/**/build
/**/dist
/**/static
.eslintrc.js
.prettierrc.js
vscode install eslint
2. Stylelint
yarn add stylelint stylelint-config-prettier stylelint-config-rational-order stylelint-config-recess-order stylelint-config-standard stylelint-declaration-block-no-ignored-properties stylelint-less stylelint-prettier -D
.stylelintrc.js
module.exports = {
extends: [
"stylelint-config-standard", // 配置stylelint拓展插件
"stylelint-config-recess-order", // 配置stylelint css属性书写顺序插件,
"stylelint-config-rational-order",
"stylelint-config-prettier", // 配置stylelint和prettier兼容
],
plugins: [
"stylelint-order", //指定事物的排序,例如声明块中的属性(插件包)。
"stylelint-declaration-block-no-ignored-properties", //禁止由于同一规则中的另一个属性值而被忽略的属性值。
"stylelint-less", // 配置stylelint less拓展插件
"stylelint-prettier", //将 Prettier 作为 stylelint 规则运行。
],
rules: {
"function-calc-no-unspaced-operator": true, //禁止在 calc 函数中使用没有间隔的运算符。
"function-linear-gradient-no-nonstandard-direction": null, //禁止在 linear-gradient() 中调用不符合标准语法的无效方
"plugin/declaration-block-no-ignored-properties": true,
"comment-empty-line-before": null,
"declaration-empty-line-before": null,
"function-name-case": "lower",
"no-descending-specificity": null, // 禁止在具有较高优先级的选择器后出现被其覆盖的较低优先级的选择器
"function-url-quotes": "always", // 要求或禁止 URL 的引号 "always(必须加上引号)"|"never(没有引号)"
"string-quotes": "double", // 指定字符串使用单引号或双引号
"unit-case": null, // 指定单位的大小写 "lower(全小写)"|"upper(全大写)"
"color-hex-case": "lower", // 指定 16 进制颜色的大小写 "lower(全小写)"|"upper(全大写)"
"color-hex-length": "long", // 指定 16 进制颜色的简写或扩写 "short(16进制简写)"|"long(16进制扩写)"
"no-invalid-double-slash-comments": null,
"block-no-empty": true, //禁止空块
"comment-no-empty": true, // 禁止空注释
"no-extra-semicolons": true, //禁止额外的分号(可自动修复)
"value-keyword-case": null,
"rule-empty-line-before": ["always", { except: ["after-single-line-comment", "first-nested"] }], // 要求或禁止在规则之前的空行 "always(规则之前必须始终有一个空行)"|"never(规则前绝不能有空行)"|"always-multi-line(多行规则之前必须始终有一个空行)"|"never-multi-line(多行规则之前绝不能有空行。)"
"font-family-no-missing-generic-family-keyword": null, // 禁止在字体族名称列表中缺少通用字体族关键字
"block-opening-brace-space-before": "always", // 要求在块的开大括号之前必须有一个空格或不能有空白符 "always(大括号前必须始终有一个空格)"|"never(左大括号之前绝不能有空格)"|"always-single-line(在单行块中的左大括号之前必须始终有一个空格)"|"never-single-line(在单行块中的左大括号之前绝不能有空格)"|"always-multi-line(在多行块中,左大括号之前必须始终有一个空格)"|"never-multi-line(多行块中的左大括号之前绝不能有空格)"
"property-no-unknown": null, // 禁止未知的属性(true 为不允许)
"property-no-unknown": [
true,
{
ignoreProperties: ["box-flex"], // 忽略某些未知属性的检测
},
],
"selector-pseudo-element-no-unknown": [
true,
{
ignorePseudoElements: ["ng-deep", "input-placeholder"], // 忽略ng-deep这种合法的伪元素选择器报警
},
],
"declaration-colon-newline-after": null, //一个属性过长的话可以写成多行
"media-feature-name-no-unknown": null, // 关闭禁止未知的媒体功能名
"no-empty-source": null, // 禁止空源码
"declaration-block-trailing-semicolon": null, // 要求或不允许在声明块中使用尾随分号 string:"always(必须始终有一个尾随分号)"|"never(不得有尾随分号)"
"at-rule-no-unknown": null, //禁止未知的@规则
"property-no-vendor-prefix": null, // 保留各大浏览器不兼容的样式属性名前缀, 如 -moz-user-select: auto
"value-no-vendor-prefix": null, // 保留各大浏览器不兼容的样式属性值前缀,display: -webkit-box;
"selector-no-vendor-prefix": null, // 保留各大浏览器不兼容的选择器前缀,如input::-webkit-input-placeholder
"color-function-notation": "legacy", // 屏蔽background-color: rgba(0, 0, 0, 0.5);这种写法引起的警告
"alpha-value-notation": "number", // 屏蔽background-color: rgba(0, 0, 0, 0.5);中0.5引起的警告
"block-opening-brace-newline-after": ["always"], //禁用单行代码块
"block-closing-brace-newline-before": ["always"],
"number-max-precision": 10, // css属性值中小数点之后数字的最大位数
indentation: 2, // 指定缩进空格
"at-rule-no-vendor-prefix": null, // 动画名称前,可以加浏览器前缀 如@-webkit-keyframes bounce
// 动画名称命名规则
// html不区分大小写,推荐 kebab-case(短横线)风格命名
// todo:现阶段仅提示,后续强制为 kebab-case 风格命名
// 短横线命名(kebab-case): ^([a-z][a-z0-9]*)(-[a-z0-9]+)*$
// 小驼峰命名(lowerCamelCase): ^[a-z][a-zA-Z0-9]+$
// 蛇形命名(snake_case): ^([a-z][a-z0-9]*)(_[a-z0-9]+)*$
// 大驼峰命名(UpperCamelCase): ^[A-Z][a-zA-Z0-9]+$
"keyframes-name-pattern": [
"^([a-z][a-z0-9]*)(-[a-z0-9]+)*$",
{
message: "Expected keyframe name to be kebab-case",
severity: "warning",
},
],
// 小驼峰命名类名选择器命名规则
"selector-class-pattern": [
"^([a-z][a-zA-Z0-9]+)*$",
{
message: "Expected class selector to be kebab-case",
severity: "error",
},
],
// 小驼峰命名id选择器命名规则
"selector-id-pattern": [
"^([a-z][a-zA-Z0-9]+)*$",
{
message: "Expected id selector to be kebab-case",
severity: "error",
},
],
"selector-pseudo-class-no-unknown": [
true,
{
ignorePseudoClasses: ["global", "v-deep", "deep"],
},
],
"no-duplicate-selectors": true, // 选择器不允许重复写
"no-duplicate-at-import-rules": true, // 禁止在样式表中使用重复的 @import 规则。
"declaration-block-no-duplicate-properties": true, // 禁止声明块的重复属性。
"declaration-block-no-shorthand-property-overrides": true, //禁止简写属性覆盖相关的扩写属性。
"no-descending-specificity": true, //禁止在具有较高优先级的选择器后出现被其覆盖的较低优先级的选择器。
// 如果是小程序允许rpx
// 'unit-no-unknown': [true, { ignoreUnits: ['rpx'] }],
// 'selector-type-no-unknown': [true, { ignoreTypes: ['page'] }],
// "prettier/prettier": ["error", {"singleQuote": true, "parser": "flow"}] 这里的配置会覆盖.prettierrc.js的配置
// "prettier/prettier": ["error", {}, {
// "usePrettierrc": true
// }] // 开启这个配置,可以指定使用.prettierrc.js配置,不会和其他配置冲突
},
ignoreFiles: ["node_modules/**/*", "./public/**/*"],
customSyntax: "postcss-less",
};
Ignore file .stylelintignore
# 忽略不需要检查的文件或目录
/dist/*
/public/*
public/*
/node_modules
/test
/unittest
/**/build
/**/dist
/**/static
global.css
global.less
vscode install stylelint
3. Prettier
npm install prettier -D
.prettierrc.js
/**
* always 强制
* never 不检查
*/
module.exports = {
// 不使用缩进符,而使用空格
useTabs: false,
// 是否在代码语句结尾添加分号
semi: true,
// 'all':尽可能在结尾处加上尾逗号
trailingComma: 'es5',
// 使用 4 个空格缩进
// tabWidth: 4, // 用eslint的
// js css 是否使用单引号,不包含JSX
singleQuote: false,
// 是否在JSX中使用单引号
jsxSingleQuote: true,
// 代码行的宽度,通用建议每行最大长度建议为100/120,但最好不超过这两个数
printWidth: 120,
// 'preserve':使用默认的折行标准
proseWrap: 'never',
// 大括号内的首尾需要空格
bracketSpacing: true,
// 箭头函数,只有一个参数的时候,也需要括号
arrowParens: 'always',
// 设置换行风格,避免不同操作系统造成的大量代码diff
// lf / crlf / cr / auto
endOfLine: 'lf',
// 对象的 key 仅在必要时用引号
quoteProps: 'as-needed',
// jsx 标签的反尖括号需要换行
jsxBracketSameLine: false,
// 是否格式化一些文件中被嵌入的代码片段的风格,如果插件可以识别。
// off / auto
embeddedLanguageFormatting: 'off',
// 为 HTML,Vue,Angular 和 Suppherbars 指定全局空白的灵敏度
// “css” - 尊重 CSS display属性的默认值
// “strict” - 所有空格都被认为是重要的
// “ignore” - 所有空格都被认为是微不足道的
htmlWhitespaceSensitivity: 'ignore',
"properties": "always", // (默认)为属性名称强制执行 小驼峰 样式
// 每个文件格式化的范围是文件的全部内容
rangeStart: 0,
rangeEnd: Infinity,
// 不需要写文件开头的 @prettier
requirePragma: false,
// 不需要自动在文件开头插入 @prettier
insertPragma: false,
};
ignore.prettierignore
# 忽略不需要的检查的代码
/node_modules
/test
/unittest
/**/build
/**/dist
/**/static
vscoe install prettier
四、lint-staged、husky
npm install lint-staged husky -D
npm install -g npx
npx husky install
npx husky add .husky/pre-commit "yarn lint-staged --allow-empty"
或
npx husky add .husky/pre-commit "npx lint-staged"
Five, package.json configuration
"scripts": {
"lint:eslint": "eslint --config .eslintrc.js --cache --fix --ext .ts,.tsx,.js ./src",
"lint:prettier": "prettier --config .prettierrc.js --write ./src/**/*.{js,ts,tsx,less,css,md,json}",
"lint:stylelint": "stylelint --allow-empty-input --config .stylelintrc.js --cache --fix ./src/**/*.{less,css}"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"commit-msg": "commitlint --config .commitlintrc.js -E HUSKY_GIT_PARAMS"
}
},
"lint-staged": {
"./src/**/*.{js,jsx,ts,tsx}": [
"eslint --quiet --config .eslintrc.js --fix",
"prettier --config .prettierrc.js --write"
],
"./src/**/*.{css,less,scss,postcss}": [
"stylelint --quiet --config .stylelintrc.js --fix",
"prettier --config .prettierrc.js --write"
],
"./src/**/*.{js,jsx,ts,tsx,json,html,md}": [
"prettier --config .prettierrc.js --write"
]
},
Six, commitlint
npm install @commitlint/cli @commitlint/config-conventional -D
npm install -g yarn
yarn husky add .husky/commit-msg 'yarn commitlint --edit "$1"'
或
npx husky add .husky/commit-msg "npx --no -- commitlint --edit $1"
.commitlintrc.js
const checkType = (header) => {
header = `${header}`;
const enumType = [ 'feat', 'fix', 'style', 'ci', 'chore', 'build', 'docs', 'test', 'merge', 'revert', 'refactor', 'reformat' ];
const realType = header.split(':')[ 0 ];
return enumType.includes(realType);
};
const checkSubject = (header) => {
header = `${header}`;
const realSubject = header.split(':')[ 1 ];
if (!realSubject) {
return false;
}
return realSubject.length > 0;
};
/*
* @Description: commit-msg提交信息格式规范
*
* commit-msg格式: <type>: <subject>
* - feat: 新增功能
* - fix: 修改、修复bug
* - style: 代码格式修改,不影响程序逻辑的代码修改(修改空白字符,格式缩进,补全缺失的分号等,没有改变代码逻辑)
* - ci: 更新项目持续集成配置(ci/cd)
* - chore: 其他修改, 比如改变构建流程、或者增加依赖库、工具等
* - build: 更新项目构建配置(webpack)
* - docs: 文档修改
* - test: 新增或者修改测试用例
* - merge: 合并分支
* - revert: 回滚某个更早之前的提交
* - refactor: 重构代码(既没有新增功能,也没有修复bug)
* - perf: 优化相关,比如提升性能、体验
*/
module.exports = {
extends: [ '@commitlint/config-conventional' ],
rules: {
'type-enum-rule': [ 2, 'never' ],
'subject-enum-rule': [ 2, 'never' ],
'type-enum': [ 0, 'never' ],
'type-empty': [ 0, 'always' ],
'subject-empty': [ 0, 'always' ],
},
plugins: [
{
rules: {
'type-enum-rule': ({ header }) => {
return [
checkType(header),
`
commit-msg 提交信息格式,需要包含提交类型,类型后面紧跟英文冒号分隔主题信息
格式: <type>: <subject>
- feat: 新增功能
- fix: 修改、修复bug
- style: 代码格式修改,不影响程序逻辑的代码修改(修改空白字符,格式缩进,补全缺失的分号等,没有改变代码逻辑)
- ci: 更新项目持续集成配置(ci/cd)
- chore: 其他修改, 比如改变构建流程、或者增加依赖库、工具等
- build: 更新项目构建配置(webpack)
- docs: 文档修改
- test: 新增或者修改测试用例
- merge: 合并分支
- revert: 回滚某个更早之前的提交
- refactor: 重构代码(既没有新增功能,也没有修复bug)
- perf: 优化相关,比如提升性能、体验
`
];
},
'subject-enum-rule': ({ header }) => {
return [ checkSubject(header), '需要包含提交主题, 格式如: "feat: 开发新功能" 中的 开发新功能' ];
},
},
},
],
};
7. Format code when vscode configuration is saved (eslint, stylelint)
Open the vscode settings and find settings.json
{
"workbench.colorTheme": "One Dark Pro",
"eslint.enable": true,
"eslint.options": {
"extensions": [".js", ".vue", ".ts", ".tsx"]
},
//关闭VSCode在Save时候自动格式化,因为VSCode自带的格式化和ESlint规范并不兼容
"editor.formatOnSave": true,
//代码保存时,自动执行ESlint格式化代码
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.organizeImports": true, //vscode 保存时自动去掉无效引用import
"source.fixAll.eslint": true, //eslint修复检测
"eslint.autoFixOnSave": true //eslint保存时格式化
},
// 配置 ESLint 检查的文件类型
"eslint.validate": [
"vue",
"html",
"javascript",
"typescript",
"javascriptreact",
"typescriptreact"
],
//保存自动格式化css
"stylelint.validate": ["css", "less", "postcss", "scss", "vue", "sass"],
"vetur.format.defaultFormatter.html": "prettier",
"files.autoSave": "onFocusChange",
"emmet.includeLanguages": {
"editor.formatOnType": "true",
"editor.formatOnSave": "true"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.formatOnType": true,
"editor.formatOnPaste": true,
"eslint.codeActionsOnSave.rules": null,
"color-highlight.sass.includePaths": [],
"css.enabledLanguages": ["html"],
"editor.tabSize": 2
}
Note: This should be consistent with eslint and stylelint
code submission
git add .
git commit -m "test commit"