前言 书接上回,为 Monorepo 单仓库多应用 项目集成代码规范工具,提高代码质量。
示例仓库:qxchuckle/monorepo-test
参考:【从 0 到 1 搭建 Vue 组件库框架】3. 集成 lint 代码规范工具 【前端工程化】项目搭建篇-项目初始化&prettier、eslint、stylelint、lint-staged、husky 前端工程:ESLint - eslint.config.js 配置详解 一文搞懂eslint、prettier以及vscode插件之间的弯弯绕绕 stylelint 规范你的 css 你不能再说你不会配置ESLint和prettier了 3分钟入门 StyleLint 使用 ESLint、Prettier 和 Stylelint 来规范代码 2024年|ESlint9+Prettier从0开始配置教程 通过引入eslint-config包的方式解决项目代码风格和规范
ESLint ESLint 是在 ECMAScript/JavaScript 代码中识别和报告模式匹配的工具,它的目标是保证代码的一致性和避免错误。
安装 仅需一行代码即可安装 ESLint:
1 npm init @eslint/config@latest
但在 Monorepo 环境下,很可能会在最后安装依赖时出现如下错误:
1 2 3 √ Which package manager do you want to use? · pnpm ☕️Installing... ERR_PNPM_ADDING_TO_ROOT Running this command will add the dependency to the workspace root, which might not be what you want - if you really meant it, make it explicit by running this command again with the -w flag (or --workspace-root). If you don't want to see this warning anymore, you may set the ignore-workspace-root-check setting to true.
需要在 .npmrc
添加 ignore-workspace-root-check=true
配置。
默认情况下,如果你在工作区根目录下运行 npm i 命令,npm 会显示一个警告,提示你这可能不是你想要的,因为这会将依赖添加到所有的工作区项目中。如果你想在工作区根目录下添加依赖,你需要使用-w或—workspace-root标志来明确指定。如果设置了ignore-workspace-root-check=true,那么 npm 将不会显示这个警告,而是默认在工作区根目录下添加依赖。
再次运行,cli 为我们添加了五个开发依赖。
@eslint/js 插件的 recommended 配置包含了一组被 ESLint 社区推荐的规则。
eslint 是 ESLint 的核心库。
eslint-plugin-vue 提供了一些针对 Vue 的 ESLint 规则。
globals 提供了一个包含各种 JS 环境全局变量的对象,在 ESLint 中,使用 globals 来配置哪些全局变量是允许在你的代码中使用的。
typescript-eslint 使 ESLint 和 Prettier 支持 TypeScript 的工具。
1 2 3 4 5 6 devDependencies: + @eslint/js ^9.10.0 + eslint ^9.10.0 + eslint-plugin-vue ^9.28.0 + globals ^15.9.0 + typescript-eslint ^8.4.0
还在根目录输出了 eslint.config.mjs
文件。
eslint.config.mjs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import globals from "globals" ;import pluginJs from "@eslint/js" ;import tseslint from "typescript-eslint" ;import pluginVue from "eslint-plugin-vue" ;export default [ { files : ["**/*.{js,mjs,cjs,ts,vue}" ] }, { languageOptions : { globals : globals.browser } }, pluginJs.configs .recommended , ...tseslint.configs .recommended , ...pluginVue.configs ["flat/essential" ], { files : ["**/*.vue" ], languageOptions : { parserOptions : { parser : tseslint.parser } }, }, ];
运行 在 package.json 中添加 lint 命令:--fix
参数表示自动修复一些错误。
package.json 1 2 3 "scripts" : { "lint" : "eslint --fix ./" , } ,
首次运行,会出现非常多的报错,但不必担心,这些只是 ESLint 对代码的检查,并不是代码出现了不可运行的错误。
1 2 3 4 5 6 7 8 9 10 C:\chuckle\qx\study_demo\Monorepo\pnpmWorkspaceTest\demo\src\views\List.vue 1:1 error Component name "List" should always be multi-word vue/multi-word-component-names 6:11 error Custom elements in iteration require 'v-bind:key' directives vue/valid-v-for C:\chuckle\qx\study_demo\Monorepo\pnpmWorkspaceTest\demo\src\views\list\Estimated.vue 1:1 error Component name "Estimated" should always be multi-word vue/multi-word-component-names 31:36 error '_' is defined but never used @typescript-eslint/no-unused-vars 31:39 error 'index' is defined but never used @typescript-eslint/no-unused-vars
配置文件变化 ESLint v8.x 的生命周期终止日期为 2024-10-05,此后将不再维护。 在最新的 v9.x 版本中,使用了新的 eslint.config.js
Flat config(扁平化配置) ,而不再使用传统的 .eslintrc 配置。迁移至 v9.x 。
关于 eslint 作出这一改变的原因,可以参阅 前端工程:ESLint - 扁平化配置系统【译】 。
可以使用如下命令,自动转换传统配置文件,但不保证在不进行进一步修改的情况下立即生效。
1 npx @eslint/migrate-config .eslintrc.json
还可以安装 @eslint/eslintrc
包,导入 FlatCompat()
并创建一个新实例来转换现有的 eslintrc 配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { FlatCompat } from "@eslint/eslintrc" ;import path from "path" ;import { fileURLToPath } from "url" ;const __filename = fileURLToPath (import .meta .url );const __dirname = path.dirname (__filename);const compat = new FlatCompat ({ baseDirectory : __dirname }); export default [ ...compat.extends ("eslint-config-my-config" ), ];
配置提示 安装 eslint-define-config 插件,在编辑配置时提供代码提示。
1 pnpm add -wD eslint eslint-define-config
该插件提供了 defineConfig 和 defineFlatConfig 函数,分别用于传统配置和平面配置。
1 2 3 4 5 import { defineFlatConfig } from "eslint-define-config" ;export default defineFlatConfig ([ { files : ["**/*.{js,mjs,cjs,ts,vue}" ] }, ]);
配置对象简介 Flat config(扁平化配置) 导出一个包含若干配置对象 的数组。官方文档:配置 ESLint
配置对象由以下属性组成:
files :glob 数组,表示配置适用的文件,未指定时适用于所有与其他配置对象匹配的文件 。
ignores :glob 数组,表示忽略检查的文件,
languageOptions :配置如何检查 js 代码
ecmaVersion :strIng,表示支持的 ES 语法版本(只是语法,不包括 ES 的全局变量,全局变量要在 globals 中指定),可以是年份(如 2022)或者版本号(如 5),默认为 latest,表示最新版本
sourceType :string,表示js 源码类型,”script”表示传统 script 标签引入,”module”表示 esm,”commonjs”表示CommonJS 文件。默认情况下,.js 和 .mjs 文件使用 “module”;.cjs 文件使用 “commonjs”
globals :对象,指定添加到全局作用域中的其他对象,避免从第三方库中引入的全局变量不能被识别
parser :默认为 espree,指定解析器(包含 parse() 方法或 parseForESLint() 方法的对象)
parserOptions :对象,指定 parser 所需的额外配置选项,
linterOptions :对象,包含与 linting 过程相关的设置
noInlineConfig :boolean,表示是否允许内联配置
reportUnusedDisableDirectives :boolean,表示是否应该跟踪和报告未用的禁用指令
processor :包含 preprocess() 和 postprocess() 方法的对象,或者指示插件中处理器的名称的字符串(例如 “pluginName/processorName”)。
plugins :包含插件名称与对应的插件对象的名值对对象。如果指定了 files,则只适用于与之匹配的文件。
rules :包含具体的规则配置,当指定了 files 或 ignores 时,这些规则配置仅对匹配的文件可用。内置规则 。规则值: warn(警告) | error(报错) | off 。
settings :包含名称值对的对象,用于所有规则都可以访问的信息。
级联配置 当多个配置对象同时匹配一个给定的 glob 时,配置对象会被合并,如果有冲突时,后面的对象会覆盖前面的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 export default [ { files : ["**/*.js" ], languageOptions : { globals : { MY_CUSTOM_GLOBAL : "readonly" , }, }, }, { files : ["tests/**/*.js" ], languageOptions : { globals : { it : "readonly" , describe : "readonly" , }, }, }, ];
上述配置中,对于所有的 js 文件,都只有一个自定义全局对象 MY_CUSTOM_GLOBAL,而对于 tests 目录下的 js 文件,还会有 it 和 describe 全局对象。
files 默认情况下,files 会匹配 **/*.js
、**/*.cjs
和 **/*.mjs
。 一般使用 files 和 ignores 的组合来决定配置对象的文件适用范围。
当一个配置对象没有 files 字段,则该配置对象为共享配置 ,供其他配置对象使用。
ignores ignores 默认忽略 **/node_modules/**
和 .git/**
。
对于仅有 ignores 的配置对象,则为全局忽略配置 ,会对默认忽略项进行扩展。
1 2 3 { ignores : ["**/*.d.ts" , "**/dist/*" ], },
指定了 ignore,最好也要指定 files: 如果有 ignores 而没有 files,但有任何其他配置项(如 rules),那么该配置对象适用于除了 ignores 指定的文件外的所有文件 ,相当于 files 为 **/*
。
通过 !
取消忽略文件和目录,包括默认忽略项。
忽略 build 目录中除 build/test.js 之外的所有内容。 1 2 3 4 5 6 7 { ignores : [ "build/**/*" , "!build/test.js" ] }
安装 @eslint/compat ,通过 includeIgnoreFile()
,可以导入传统的 .gitignore 文件。
1 2 3 4 5 6 7 import { includeIgnoreFile } from "@eslint/compat" ;const gitignorePath = path.resolve (__dirname, ".gitignore" );export default [ includeIgnoreFile (gitignorePath), ];
linterOptions 专门用来配置检查过程的选项,对文件源码的解析方式没有影响。它有两个选项:
noInlineConfig :默认为 false,表示是否禁用内联配置,内联配置是通过 /*eslint*/
注释实现的,例如 /*eslint semi: error*/
。设置为 true,则会忽略所有内联配置。
reportUnusedDisableDirectives 像 /*eslint-disable*/
和 /*eslint-disable-next-line*/
这样的禁用指令是用来禁用 ESLint 规则的,围绕代码的某些部分。随着代码的变化,这些指令有可能不再需要,因为代码的变化使规则不再被触发。通过设置该选项为 true,可以把未用的禁用指令被报告为警告。
languageOptions languageOptions 是配置语言选项。
ecmaVersion 指定支持的 ES 语法版本,确定语法和可用的全局变量,可以是年份(如 2022)或者版本号(如 5),默认为 latest,表示最新版本。
sourceType 表示正在使用的 JavaScript 文件的模式。有以下三种值:
module ESM 模块(当ecmaVersion为3或5时无效)。代码具有模块范围并以严格模式运行。
commonjs CommonJS 模块。代码具有顶级函数作用域,并以非严格模式运行。
script 非模块。代码具有共享的全局范围并以非严格模式运行。
默认情况下,.js 和 .mjs 文件的 sourceType 是 “module”,而 .cjs 文件则是 “commonjs”。
parser 官方文档 ,自定义解析器将 JavaScript 代码转换为抽象语法树,以供 ESLint 检查。
parserOptions 键值对对象,用于向自定义解析器传递选项。
globals 配置可用的全局变量,键是全局变量名,值为 readonly、writable、off 之一,通常配合 globals 库使用。
plugins plugins 插件是 eslint 最重要的配置,用于在 ESLint 项目中共享规则、处理器、配置、解析器等等。
可以通过 <插件名>/
使用插件中特定的规则:
1 2 3 4 5 6 7 8 9 10 { rules : { "@typescript-eslint/no-unused-vars" : "off" , "@typescript-eslint/no-explicit-any" : "off" , "@typescript-eslint/no-unused-expressions" : "off" , "@typescript-eslint/no-unsafe-function-type" : "off" , "vue/multi-word-component-names" : "off" , "vue/no-unused-vars" : "off" , }, },
插件本质是一个向 ESLint 公开某些属性的 JavaScript 对象,创建插件详见官方文档-Create Plugins 。
processor processor 用于将非 js 文件转化为 eslint 可以检查的代码片段。
处理 markdown 文件 1 2 3 4 5 6 7 8 9 10 import markdown from "@eslint/markdown" ;export default [ { files : ["**/*.md" ], plugins : { markdown }, processor : "markdown/markdown" } ];
工作流程 ESLint 通过 parser
指定的解析器,如 @typescript-eslint/parser
,将源码转换为抽象语法树(AST),供后续流程使用,而 parserOptions
可以对解析器的能力进行详细设置。
ESLint 提供了自定义规则 的接口,开发者需要遵照接口,根据分析器的 AST 产物,实现规范检查逻辑,再将实现的多条规范聚合为 plugin 插件的形式,形成规则集 。
有了规则集,能够理解规范,还需要进一步在 rules
字段中对这些规则进行开启或者关闭的声明,只有开启的规则才会生效。
规则集中有大量的规则,完全在项目中一条条配置是不可取的,社区逐渐演进出了许多配置预设,从而减少绝大多数手动配置的工作量。大部分插件本身也自带推荐预设,如 eslint:recommended
、@typescript-eslint/recommended
。
最新的扁平化配置也使得我们很容易去自定义、覆盖一些独特的配置。
编辑配置 按目前项目需求编辑规则,关闭一些不需要的检查。
eslint.config.mjs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 import globals from "globals" ;import pluginJs from "@eslint/js" ;import tseslint from "typescript-eslint" ;import pluginVue from "eslint-plugin-vue" ;import { defineFlatConfig } from "eslint-define-config" ;export default defineFlatConfig ([ pluginJs.configs .recommended , ...tseslint.configs .recommended , ...pluginVue.configs ["flat/recommended" ], { languageOptions : { globals : { ...globals.browser , ...globals.node , InstanceType : "readonly" , NodeJS : "readonly" , }, }, }, { files : ["demo/src/**/*" ], languageOptions : { globals : { ref : "readonly" , onMounted : "readonly" , nextTick : "readonly" , }, }, }, { ignores : ["**/*.d.ts" , "**/dist/*" ], }, { rules : { "@typescript-eslint/no-unused-vars" : "off" , "@typescript-eslint/no-explicit-any" : "off" , "@typescript-eslint/no-unused-expressions" : "off" , "@typescript-eslint/no-unsafe-function-type" : "off" , "vue/multi-word-component-names" : "off" , "vue/no-unused-vars" : "off" , "no-unused-vars" : "off" , semi : "error" , "prefer-const" : "error" , }, }, { files : ["**/*.ts" , "**/*.vue" ], languageOptions : { parserOptions : { parser : tseslint.parser , project : "./tsconfig.eslint.json" , extraFileExtensions : ["vue" ], }, }, }, ]);
我们希望 ESLint 能覆盖所有源码文件,一般新创建一个 tsconfig.eslint.json
,在其中包含所有希望被规范化的源码文件。
tsconfig.eslint.json 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 { "extends" : "./tsconfig.base.json" , "compilerOptions" : { "noEmit" : true } , "include" : [ "**/*" , "**/.*.*" ] , "exclude" : [ "**/dist" , "**/node_modules" ] }
注意,tsconfig 的文件集合范围总是需要 >= ESLint 的 files 范围,否则会出现该 TSConfig 不包括此文件的报错。
该 TSConfig 不包括此文件的报错 1 2 3 4 5 6 7 8 C:\chuckle\qx\study_demo\Monorepo\pnpmWorkspaceTest\scripts\utils.ts 0:0 error Parsing error: ESLint was configured to run on `<tsconfigRootDir>/scripts\utils.ts` using `parserOptions.project`: <tsconfigRootDir>/tsconfig.eslint.json Found unexpected extension `vue` specified with the `parserOptions.extraFileExtensions` option. Did you mean `.vue`? However, that TSConfig does not include this file. Either: - Change ESLint's list of included files to not include this file - Change that TSConfig to include this file - Create a new TSConfig that includes this file and include it in your parserOptions.project See the typescript-eslint docs for more info: https://typescript-eslint.io/troubleshooting/typed-linting#i-get-errors-telling-me-eslint-was-configured-to-run--however-that-tsconfig-does-not--none-of-those-tsconfigs-include-this-file
社区规则集 ESLint 拥有许多成熟的规则集,这些规则集都是成熟团队的实践。它们通过 plugin
实现了很多个性化的规则,又内置了海量的 rules
配置预设。
eslint-config-airbnb Airbnb 规则集eslint-config-alloy 腾讯 AlloyTeam 的规则集eslint-config-standard StandardJS 规则集
Stylelint Stylelint 是 CSS 样式的 Lint 工具,用于在代码中发现并报告样式代码中的问题。
它与 ESLint 非常相似,包括配置文件、规则等,只是它专注于 CSS 样式的检查。
安装
继续安装一些项目需要的插件、规则集。
stylelint-config-standard-scss :集成完整的 sass 规则集。它本身继承了很多东西,包括 sass 规则实现的插件、css 标准规则集 stylelint-config-standard 等。
stylelint-config-recommended-vue :使 Stylelint 支持对 .vue 文件的 style 标签部分进行检查。
stylelint-config-recess-order :一种推荐的 css 属性排序的规则(先写定位,再写盒模型,再写内容区样式,最后写 CSS3 相关属性)。
@stylistic/stylelint-plugin :Stylelint 升级到 15.0.0 大版本后,计划废弃风格相关的规则,这部分内容分离出来由社区维护,需要单独安装。(若使用 prettier 管理代码风格,则不必使用该插件)
1 pnpm i -wD stylelint-config-standard-scss stylelint-config-recommended-vue stylelint-config-recess-order @stylistic/stylelint-plugin
安装 stylelint-define-config 以获得配置提示。
1 pnpm i -wD stylelint-define-config
配置 Stylelint 的配置文件与 ESLint 的传统配置 .eslintrc 相似。
在根目录创建 stylelint.config.mjs 文件。具体的规则配置项,详见官方文档-rules 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import defineConfig from "stylelint-define-config" ;export default defineConfig ({ extends : [ "stylelint-config-standard-scss" , "stylelint-config-recommended-vue/scss" , "stylelint-config-recess-order" , ], plugins : [ "@stylistic/stylelint-plugin" , ], rules : { "@stylistic/max-line-length" : 100 , "no-empty-source" : null , }, ignoreFiles : ["**/dist/*" , "**/node_modules/*" ], overrides : [ { files : ["*.html" , "**/*.html" ], customSyntax : "postcss-html" , }, ], });
修复 html 报错 在对 html 检查时,可能有如下莫名其妙的报错:
1 2 demo/index.html 1:1 ✖ Unknown word CssSyntaxError
针对 html 格式文件采用自定义语法解析器:
stylelint.config.mjs 1 2 3 4 5 6 overrides : [ { files : ["*.html" , "**/*.html" ], customSyntax : "postcss-html" , }, ],
运行 编辑 package.json,添加 lint 命令。
package.json 1 2 3 4 "scripts" : { "lint:style" : "stylelint --fix ./**/*.{css,scss,vue,html}" , "build:ui" : "pnpm run lint:fix && pnpm run lint:style && pnpm run type:src && pnpm --filter ./packages/** run build && pnpm run dts-mv && pnpm run css-mv" } ,
Stylelint 将自动按规则格式化 CSS,并报告了需要手动修复的问题。
VSCode 插件 配合 VSCode 插件 ESLint 、Stylelint 、Prettier 使用,可以在编辑器中实时检查代码规范。
如果插件没有实时提示,可以查看面板->输出,找到对应插件查看问题,多半是 VSCode 版本需要升级了。
在 .vscode\settings.json
对插件进行配置:
.vscode\settings.json 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 { "typescript.tsdk" : "node_modules/typescript/lib" , "css.validate" : false , "less.validate" : false , "scss.validate" : false , "stylelint.validate" : [ "css" , "scss" , "postcss" , "vue" ] , "eslint.format.enable" : true , "eslint.codeAction.showDocumentation" : { "enable" : true } , "eslint.probe" : [ "javascript" , "typescript" , "vue" ] , "eslint.nodePath" : "node_modules" , "editor.codeActionsOnSave" : { "source.fixAll.eslint" : "explicit" , "source.fixAll.stylelint" : "explicit" } , "editor.formatOnSave" : true , }
代码风格 我们通过 Stylelint 管理了 CSS 的代码质量 ,且 ESLint 更多是用来管理代码质量。
ESLint 在 v8.53.0 版本弃用了代码格式化规则,deprecating-formatting-rules ,但这些规则仍然能用,真正的移除预计会在 v10 版本。
仍要使用 ESLint 去管理代码风格,可以使用 @stylistic/eslint-plugin-js 或 @stylistic/eslint-plugin-ts 插件,这些插件由 Anthony Fu 维护,包含了 ESLint 核心和 typescript-eslint 中废弃的格式化规则。
代码风格应该交给专业的插件去做,比如 Prettier 。
Prettier Prettier 是一个代码格式化工具,它会自动格式化代码,使代码风格保持一致。
并且支持 vue、react 等框架的 SFC 文件,以及 markdown、json 等文件的格式化。它会删除了所有原始代码格式,并确保所有输出的代码符合一致的格式。
安装
在 .vscode/setting.json 中设置一些配置。
1 2 3 4 5 6 { "editor.defaultFormatter" : "esbenp.prettier-vscode" , "editor.formatOnSave" : true , }
.prettierignore
为 Prettier 的忽略文件。
eslint 集成 prettier eslint 及其相关插件目前仍然管理着一部分代码风格,我们需要将 prettier 的规则集成覆盖到 eslint 中,并解决 eslint 和 prettier 之间的冲突。
两个插件:
eslint-plugin-prettier :基于 prettier 代码风格的 eslint 规则。即 eslint 使用 prettier 规则来格式化代码,并将差异报告为单个ESLint问题
eslint-config-prettier :禁用所有与格式相关的 eslint 规则,解决 prettier 与 eslint 规则冲突。
1 pnpm install -wD eslint-config-prettier eslint-plugin-prettier
按文档 操作导入 eslint-plugin-prettier/recommended
,该 recommended 已经集成了两者的配置。
注意:将其添加为文件中配置数组的最后一项,以便 eslint-config-prettier 有机会覆盖其他配置。
eslint.config.mjs 1 2 3 4 5 6 7 8 9 10 11 import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended" ;export default defineFlatConfig ([ eslintPluginPrettierRecommended, { rules : { 'prettier/prettier' : ['warn' ], }, }, ]);
与 stylelint 集成
从 Stylelint v15 开始,所有与样式相关的规则都已弃用 。如果您使用的是 v15 或更高版本,并且未使用这些弃用的规则,则 stylelint-config-prettier 插件不再是必要的。
配置文件 创建 .prettierrc
文件,并设定一些规则。configuration ,options 。 我喜欢使用 prettier.config.mjs
配合 prettier-define-config 插件以获得配置提示。
prettier.config.mjs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import { defineConfig } from '@archoleat/prettier-define-config' ;export default defineConfig ({ arrowParens : 'always' , bracketSameLine : true , bracketSpacing : true , semi : true , experimentalTernaries : false , singleQuote : true , jsxSingleQuote : true , quoteProps : 'preserve' , trailingComma : 'all' , singleAttributePerLine : false , htmlWhitespaceSensitivity : 'css' , vueIndentScriptAndStyle : false , proseWrap : 'never' , insertPragma : false , printWidth : 80 , requirePragma : false , useTabs : false , embeddedLanguageFormatting : 'auto' , tabWidth : 2 , endOfLine : 'auto' , });
VSCode 响应配置修改有时候非常慢,善用 reload。
在 .vscode\settings.json
中配置自动格式化等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 { "editor.formatOnSave" : true , "editor.defaultFormatter" : "esbenp.prettier-vscode" , "[vue]" : { "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" : "esbenp.prettier-vscode" } , "[html]" : { "editor.defaultFormatter" : "esbenp.prettier-vscode" } , "[markdown]" : { "editor.defaultFormatter" : "esbenp.prettier-vscode" } , "[css]" : { "editor.defaultFormatter" : "esbenp.prettier-vscode" } , "[less]" : { "editor.defaultFormatter" : "esbenp.prettier-vscode" } , "[scss]" : { "editor.defaultFormatter" : "esbenp.prettier-vscode" } , }
运行 除了在保存时自动格式化(这个也看个人喜好开启),每当 git 提交代码前,也应该格式化代码。
暂时不在这里展开,后续在 git hook 上集成 prettier 的操作。
https://prettier.nodejs.cn/docs/en/cli.html
Replace ··· with ↹↹ 这是因为没有统一好项目的缩进风格。
在 .vscode\settings.json
中配置强制使用2个空格缩进:
1 2 3 4 5 { "editor.tabSize" : 2 , "editor.insertSpaces" : true , "editor.detectIndentation" : false , }
完整配置 qxchuckle/monorepo-test
eslint.config.mjs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 import globals from 'globals' ;import pluginJs from '@eslint/js' ;import tseslint from 'typescript-eslint' ;import pluginVue from 'eslint-plugin-vue' ;import { defineFlatConfig } from 'eslint-define-config' ;import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended' ;export default defineFlatConfig ([ pluginJs.configs .recommended , ...tseslint.configs .recommended , ...pluginVue.configs ['flat/recommended' ], { languageOptions : { globals : { ...globals.browser , ...globals.node , InstanceType : 'readonly' , NodeJS : 'readonly' , }, }, }, { files : ['demo/src/**/*' ], languageOptions : { globals : { ref : 'readonly' , onMounted : 'readonly' , nextTick : 'readonly' , }, }, }, { ignores : ['**/*.d.ts' , '**/dist/*' ], }, { rules : { '@typescript-eslint/no-unused-vars' : 'off' , '@typescript-eslint/no-explicit-any' : 'off' , '@typescript-eslint/no-unused-expressions' : 'off' , '@typescript-eslint/no-unsafe-function-type' : 'off' , 'vue/multi-word-component-names' : 'off' , 'vue/no-unused-vars' : 'off' , 'no-unused-vars' : 'off' , semi : 'error' , 'prefer-const' : 'error' , }, }, { files : ['**/*.ts' , '**/*.vue' ], languageOptions : { parserOptions : { parser : tseslint.parser , project : './tsconfig.eslint.json' , extraFileExtensions : ['vue' ], }, }, }, eslintPluginPrettierRecommended, { rules : { 'prettier/prettier' : ['warn' ], }, }, ]);
stylelint.config.mjs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import defineConfig from 'stylelint-define-config' ;export default defineConfig ({ extends : [ 'stylelint-config-standard-scss' , 'stylelint-config-recommended-vue/scss' , 'stylelint-config-recess-order' , ], plugins : [ ], rules : { 'no-empty-source' : null , }, ignoreFiles : ['**/dist/*' , '**/node_modules/*' ], overrides : [ { files : ['*.html' , '**/*.html' ], customSyntax : 'postcss-html' , }, ], });
prettier.config.mjs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import { defineConfig } from '@archoleat/prettier-define-config' ;export default defineConfig ({ arrowParens : 'always' , bracketSameLine : true , bracketSpacing : true , semi : true , experimentalTernaries : false , singleQuote : true , jsxSingleQuote : true , quoteProps : 'preserve' , trailingComma : 'all' , singleAttributePerLine : false , htmlWhitespaceSensitivity : 'css' , vueIndentScriptAndStyle : false , proseWrap : 'never' , insertPragma : false , printWidth : 80 , requirePragma : false , useTabs : false , embeddedLanguageFormatting : 'auto' , tabWidth : 2 , endOfLine : 'auto' , });
.vscode\settings.json 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 { "editor.tabSize" : 2 , "editor.insertSpaces" : true , "editor.detectIndentation" : false , "files.exclude" : { "**/.git" : true , "**/.svn" : true , "**/.hg" : true , "**/CVS" : true , "**/.DS_Store" : true , "**/Thumbs.db" : true , "**/node_modules" : false , "**/.deploy_git" : true , "**/.history" : true } , "search.exclude" : { "**/node_modules" : true , "dist" : true , "build" : true , "yarn.lock" : true } , "typescript.tsdk" : "node_modules/typescript/lib" , "css.validate" : false , "less.validate" : false , "scss.validate" : false , "stylelint.validate" : [ "css" , "scss" , "postcss" , "vue" ] , "eslint.format.enable" : true , "eslint.codeAction.showDocumentation" : { "enable" : true } , "eslint.probe" : [ "javascript" , "typescript" , "vue" , ] , "eslint.nodePath" : "node_modules" , "editor.codeActionsOnSave" : { "source.fixAll.eslint" : "explicit" , "source.fixAll.stylelint" : "explicit" } , "editor.formatOnSave" : true , "editor.defaultFormatter" : "esbenp.prettier-vscode" , "[vue]" : { "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" : "esbenp.prettier-vscode" } , "[html]" : { "editor.defaultFormatter" : "esbenp.prettier-vscode" } , "[markdown]" : { "editor.defaultFormatter" : "esbenp.prettier-vscode" } , "[css]" : { "editor.defaultFormatter" : "esbenp.prettier-vscode" } , "[less]" : { "editor.defaultFormatter" : "esbenp.prettier-vscode" } , "[scss]" : { "editor.defaultFormatter" : "esbenp.prettier-vscode" } , "files.eol" : "\n" , "html.autoClosingTags" : true , }
总结 我们在原先的 Monorepo 项目中集成了最新的 ESLint 和 Stylelint,以对代码质量进行检查。
然后,我们引入了 Prettier,使代码风格保持一致。
最后,我们解决了 ESLint 和 Prettier 之间的冲突,并配合 VSCode 设置了一系列自动化配置。
每次都进行全量 lint 检查是非常耗时的,在下一篇文章中进行解决。