环境准备
- node v20.13.0
- pnpm v9.1.3
- vite v5.2.0
- vscode 插件
- ESLint
- Stylelint
- Prettier - Code formatter
使用vite创建一个react-ts项目
pnpm create vite react-code-lint --template react-ts
- 可以看到默认是已经帮我们生成了
.eslintrc.cjs文件,可以先删除此文件(或者备份一个),自己通过eslint命令来创建 - 根目录下创建.vscode文件夹
- extensions.json - 用于放vscode扩展插件
- settings.json - 用于设置vscode的基本配置(项目配置会覆盖本地)
- 删除
.gitignore里的.vscode/*和!.vscode/extensions.json- 目的:做到同一项目相同配置,保证风格统一
Eslint
ESLint 能够帮助开发者发现代码中的潜在错误和不良实践,确保代码符合团队或项目的编码规范,从而提高代码质量和可维护性。
这是使用的版本是eslint@8.57.0,和v9的版本有一定的区别
- 终端输入命令行
npx eslint --init
- 依次选中
√ How would you like to use ESLint? · problems√ What type of modules does your project use? · esm√ Which framework does your project use? · react√ The React plugin doesn't officially support ESLint v9 yet. What would you like to do? · 8.x√ Does your project use TypeScript? · typescript√ Where does your code run? · browserThe config that you've selected requires the following dependencies:eslint@8.x, globals, @eslint/js, typescript-eslint, eslint-plugin-react√ Would you like to install them now? · No / Yes√ Which package manager do you want to use? · pnpm
- 安装成功后生成
eslint.config.js文件 - 配置规则发现不一样,还是用回之前
.eslintrc.cjs的吧 - 配置写入 - 保存的时候会自动修复
{"editor.codeActionsOnSave": {"source.fixAll": "explicit","source.fixAll.eslint": "explicit"}}
- 根目录下添加
.eslintignore文件
node_modulesbuilddist
- 添加脚本命令
// ..."scripts": {// ..."lint": "eslint . --ext .js,.ts,.jsx,.tsx --report-unused-disable-directives --max-warnings 0","lint:fix": "eslint . --ext .js,.ts,.jsx,.tsx --ignore-path .gitignore --fix",}// ...
- 新增插件排序
pnpm add eslint-plugin-import eslint-import-resolver-alias eslint-import-resolver-typescript -D
- 在.eslintrc.js 文件中增加配置
module.export = {// ...extends: [// ..."plugin:import/recommended",],settings: {'import/resolver': {// eslint-import-resolver-typescript 可以解决别名报错的问题typescript: {alwaysTryTypes: true,},// eslint-import-resolver-alias 可以解决绝对路径的问题alias: {map: [['', './public'], // <-- this line],extensions: ['.js', '.jsx', '.ts', '.tsx', '.svg'],},},},rules: {//..."import/order": ["error",{// 对导入模块进行分组,分组排序规则如下groups: ["builtin", // 内置模块"external", // 外部模块"parent", //父节点依赖"sibling", //兄弟依赖"internal", //内部引用"index", // index文件"type", //类型文件"unknown",],//通过路径自定义分组pathGroups: [{pattern: "@/**", // 把@开头的应用放在external分组后面group: "external",position: "after",},],// 是否开启独特组,用于区分自定义规则分组和其他规则分组distinctGroup: true,// 每个分组之间换行"newlines-between": "always",// 相同分组排列规则 按字母升序排序alphabetize: { order: "asc" },},],},};
Prettier
- 安装依赖
pnpm add prettier eslint-config-prettier eslint-plugin-prettier -D
- 添加
.prettierrc.cjs文件
module.exports = {// 一行最多多少个字符printWidth: 90,// 指定每个缩进级别的空格数tabWidth: 2,// 使用制表符而不是空格缩进行useTabs: false,// 在语句末尾是否需要分号semi: true,// 是否使用单引号singleQuote: true,// 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"quoteProps: 'as-needed',// 在JSX中使用单引号而不是双引号jsxSingleQuote: false,// 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认nonetrailingComma: 'es5',// 在对象文字中的括号之间打印空格bracketSpacing: true,// jsx 标签的反尖括号需要换行jsxBracketSameLine: false,// 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => xarrowParens: 'always',// 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码rangeStart: 0,rangeEnd: Infinity,// 指定要使用的解析器,不需要写文件开头的 @prettierrequirePragma: false,// 不需要自动在文件开头插入 @prettierinsertPragma: false,// 使用默认的折行标准 always\never\preserveproseWrap: 'preserve',// 指定HTML文件的全局空格敏感度 css\strict\ignorehtmlWhitespaceSensitivity: 'css',// Vue文件脚本和样式标签缩进vueIndentScriptAndStyle: false,//在 windows 操作系统中换行符通常是回车 (CR) 加换行分隔符 (LF),也就是回车换行(CRLF),//然而在 Linux 和 Unix 中只使用简单的换行分隔符 (LF)。//对应的控制字符为 "\n" (LF) 和 "\r\n"(CRLF)。auto意为保持现有的行尾// 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"endOfLine: 'crlf',};
- 修改
.eslintrc.cjs文件,注入prettier相关插件和配置
module.exports = {root: true,env: { browser: true, es2020: true },extends: ['eslint:recommended','plugin:@typescript-eslint/recommended','plugin:react-hooks/recommended','plugin:prettier/recommended',],ignorePatterns: ['dist', '.eslintrc.cjs'],parser: '@typescript-eslint/parser',plugins: ['react-refresh'],rules: {'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],'@typescript-eslint/no-unused-vars': ['warn'],},};
- 配置好可以看到
App.tsx文件里的内容标红了- 使用配置好的脚本
pnpm run lint:fix进行修复
- 使用配置好的脚本
- .vscode/settings.json中配置自动保存格式化,然后使用保存快捷键
"editor.formatOnSave": false,"editor.defaultFormatter": "esbenp.prettier-vscode",
- Tip:
.prettierrc.cjs修改了相关格式化配置规则,ESLint代码不会立即去检查报红,Ctrl+Shift+P 打开命令面板,输入Restart Extension Host重启扩展宿主,等待加载一会儿,就能同步更新了。也可以暴力直接重启打开vscode软件。
Stylelint
- 安装相关依赖(使用Scss样式预处理器)
pnpm add stylelint stylelint-scss stylelint-config-recommended-scss stylelint-config-standard stylelint-config-standard-scss stylelint-order -D
- 根目录新增文件
.stylelintignore、.stylelintrc.cjs
# .stylelintignore# 旧的不需打包的样式库*.min.css# 其他类型文件*.js*.jpg*.png*.eot*.ttf*.woff*.json# 测试和打包目录/test//dist//node_modules//lib/
module.exports = {extends: ['stylelint-config-standard','stylelint-config-standard-scss','stylelint-config-recommended-scss',],plugins: ['stylelint-scss', 'stylelint-order'],rules: {'no-irregular-whitespace': true,// 指定样式的排序'order/properties-order': [['content','position','top','right','bottom','left','z-index','display','vertical-align','flex','flex-grow','flex-shrink','flex-basis','flex-direction','flex-flow','flex-wrap','grid','grid-area','grid-template','grid-template-areas','grid-template-rows','grid-template-columns','grid-row','grid-row-start','grid-row-end','grid-column','grid-column-start','grid-column-end','grid-auto-rows','grid-auto-columns','grid-auto-flow','grid-gap','grid-row-gap','grid-column-gap','gap','row-gap','column-gap','align-content','align-items','align-self','justify-content','justify-items','justify-self','order','float','clear','object-fit','overflow','overflow-x','overflow-y','overflow-scrolling','clip',//'box-sizing','width','min-width','max-width','height','min-height','max-height','margin','margin-top','margin-right','margin-bottom','margin-left','padding','padding-top','padding-right','padding-bottom','padding-left','border','border-spacing','border-collapse','border-width','border-style','border-color','border-top','border-top-width','border-top-style','border-top-color','border-right','border-right-width','border-right-style','border-right-color','border-bottom','border-bottom-width','border-bottom-style','border-bottom-color','border-left','border-left-width','border-left-style','border-left-color','border-radius','border-top-left-radius','border-top-right-radius','border-bottom-right-radius','border-bottom-left-radius','border-image','border-image-source','border-image-slice','border-image-width','border-image-outset','border-image-repeat','border-top-image','border-right-image','border-bottom-image','border-left-image','border-corner-image','border-top-left-image','border-top-right-image','border-bottom-right-image','border-bottom-left-image',//'background','background-color','background-image','background-attachment','background-position','background-position-x','background-position-y','background-clip','background-origin','background-size','background-repeat','color','box-decoration-break','box-shadow','outline','outline-width','outline-style','outline-color','outline-offset','table-layout','caption-side','empty-cells','list-style','list-style-position','list-style-type','list-style-image',//'font','font-weight','font-style','font-variant','font-size-adjust','font-stretch','font-size','font-family','src','line-height','letter-spacing','quotes','counter-increment','counter-reset','-ms-writing-mode','text-align','text-align-last','text-decoration','text-emphasis','text-emphasis-position','text-emphasis-style','text-emphasis-color','text-indent','text-justify','text-outline','text-transform','text-wrap','text-overflow','text-overflow-ellipsis','text-overflow-mode','text-shadow','white-space','word-spacing','word-wrap','word-break','overflow-wrap','tab-size','hyphens','interpolation-mode',//'opacity','visibility','filter','resize','cursor','pointer-events','user-select',//'unicode-bidi','direction','columns','column-span','column-width','column-count','column-fill','column-gap','column-rule','column-rule-width','column-rule-style','column-rule-color','break-before','break-inside','break-after','page-break-before','page-break-inside','page-break-after','orphans','widows','zoom','max-zoom','min-zoom','user-zoom','orientation','fill','stroke',//'transition','transition-delay','transition-timing-function','transition-duration','transition-property','transform','transform-origin','animation','animation-name','animation-duration','animation-play-state','animation-timing-function','animation-delay','animation-iteration-count','animation-direction','animation-fill-mode',],{unspecified: 'bottom',severity: 'error',},],},}
- 打开命令面板重启一下扩展宿主,可以看到
App.css文件报红 - 添加脚本命令行
// ..."scripts": {// ..."stylelint": "stylelint ./**/*.{css,scss}","stylelint:fix": "stylelint ./**/*.{css,scss} --fix",},// ...
- 使用
pnpm run stylelint:fix进行样式格式化修复 - .vscode/settings.json中配置自动保存格式化,然后使用保存快捷键
{"editor.codeActionsOnSave": {"source.fixAll": "explicit","source.fixAll.eslint": "explicit","source.fixAll.stylelint": "explicit"},"editor.formatOnSave": true,"editor.defaultFormatter": "esbenp.prettier-vscode","stylelint.validate": ["css", "scss", "sass"],"stylelint.enable": true,"css.validate": false,"scss.validate": false,"[scss]": {"editor.defaultFormatter": "esbenp.prettier-vscode"},"[css]": {"editor.defaultFormatter": "esbenp.prettier-vscode"}}
Husky
- 安装依赖
pnpm add husky@8.0.3 -D
- 添加脚本命令
// ..."scripts": {// ..."prepare": "husky install"},// ...
- 执行
pnpm run prepare命令,根目录会多一个.husky的目录 - 在
.husky文件下添加pre-commit文件
就可以当我们在使用 git commit 代码提交的时候会先触发以下命令进行代码格式化修复
pnpm husky add .husky/pre-commit "pnpm run lint:fix && pnpm run stylelint:fix"
Commitlint
- 安装依赖
pnpm add @commitlint/config-conventional @commitlint/cli commitizen@4.2.4 cz-customizable -D
- 为什么使用指定版本
- 根目录新增文件
.commitlintrc.cjs、.cz-config.cjs
module.exports = {extends: ['@commitlint/config-conventional'],rules: {'type-enum': [// type枚举2,'always',[':sparkles:',':bug:',':construction:',':memo:',':lipstick:',':zap:',':hammer:',':white_check_mark:',':rewind:',':package:',':rocket:',':construction_worker:',],],// 'type-empty': [2, 'never'], // 提交类型不能为空// 'type-case': [0, 'always', 'lower-case'], // 提交类型的大小写(0表示不检查)// 'scope-empty': [0], // 提交范围(scope)是否为空(0表示不检查)// 'scope-case': [0], // 提交范围的大小写(0表示不检查)// 'subject-empty': [2, 'never'], // 提交说明(subject)不能为空// 'subject-case': [0], // 提交说明的大小写(0表示不检查)// 'subject-full-stop': [0, 'never', '.'], // 提交说明的结尾不能有句号// 'header-max-length': [2, 'always', 72], // 提交信息的头部最长72个字符// 'body-leading-blank': [0], // 提交信息的主体开头不强制为空行// 'footer-leading-blank': [0, 'always'], // 提交信息的脚注开头需要有空行},parserPreset: {parserOpts: {headerPattern: /^(:\w*:)(?:\((.*?)\))?\s((?:.*(?=\())|.*)(?:\(#(\d*)\))?/,headerCorrespondence: ['type', 'scope', 'subject', 'ticket'],},},}
module.exports = {types: [{value: ':sparkles: feat',name: '✨ feat: 新功能',},{value: ':bug: fix',name: '🐛 fix: 修复bug',},{value: ':construction: WIP',name: '🚧 WIP: 工作进行中',},{value: ':memo: docs',name: '📝 docs: 文档变更',},{value: ':lipstick: style',name: '💄 style: 代码风格变更',},{value: ':zap: perf',name: '⚡ perf: 性能优化',},{value: ':hammer: refactor',name: '🔨 refactor: 重构',},{value: ':white_check_mark: test',name: '✅ test: 测试',},{value: ':rewind: revert',name: '⏪️ revert: 代码回退',},{value: ':package: build',name: '📦 build: 打包构建',},{value: ':rocket: chore',name: '🚀 chore: 构建/工程依赖/工具',},{value: ':construction_worker: ci',name: '👷 CI 配置变更',},],scopes: [],scopeOverrides: {},// override the messages, defaults are as followsmessages: {type: '请选择提交类型(必填):',scope: '请输入文件修改范围(可选):',// used if allowCustomScopes is truecustomScope: '请输入文件修改范围(可选):',subject: '请简要描述提交(必填):\n',body: '请输入详细描述,使用 "|" 实现换行输入(可选):\n',breaking: '列出所有BREAKING CHANGES(可选):\n',footer: '请输入要关联的 YouTrack Issue ID,例如: #5, #30 (可选):\n',confirmCommit: '确定提交此说明吗?',},// 跳过空的 scopeskipEmptyScopes: false,skipQuestions: ['breaking', 'body'],// 设置为 true,在 scope 选择的时候,会有 empty 和 custom 可以选择// 顾名思义,选择 empty 表示 scope 缺省,如果选择 custom,则可以自己输入信息allowCustomScopes: true,// 只有我们 type 选择了 feat 或者是 fix,才会询问我们 breaking message.allowBreakingChanges: ['feat', 'fix'],}
package.json
"config": {"commitizen": {"path": "./node_modules/cz-customizable"},"cz-customizable": {"config": ".cz-config.cjs"}}
- 安装依赖
pnpm add @commitlint/config-conventional @commitlint/cli -D
- 添加commit-msg钩子
pnpm husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
lint-staged
- 安装依赖
pnpm add lint-staged -D
- 在根目录新增文件
.lintstagedrc
{"*.{js,jsx,ts,tsx}": ["pnpm run lint:fix"],"*.{css,scss}": ["pnpm run stylelint:fix","git add"]}
- 或者
package.json下新增
"lint-staged": {"*.{js,jsx,ts,tsx}": ["pnpm run lint:fix"],"*.{css,scss}": ["pnpm run stylelint:fix","git add"]}
- 修改.husky/pre-commit文件
#!/usr/bin/env sh. "$(dirname -- "$0")/_/husky.sh"npx lint-staged
参考
