前言
书接上回,为 Monorepo 单仓库多应用 项目集成代码规范工具,提高代码质量。
参考: 【从 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:
npm init @eslint/config@latest但在 Monorepo 环境下,很可能会在最后安装依赖时出现如下错误:
√ 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 的工具。
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 文件。
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 参数表示自动修复一些错误。
"scripts": { "lint": "eslint --fix ./",},首次运行,会出现非常多的报错,但不必担心,这些只是 ESLint 对代码的检查,并不是代码出现了不可运行的错误。
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 - 扁平化配置系统【译】。
可以使用如下命令,自动转换传统配置文件,但不保证在不进行进一步修改的情况下立即生效。
npx @eslint/migrate-config .eslintrc.json还可以安装 @eslint/eslintrc 包,导入 FlatCompat() 并创建一个新实例来转换现有的 eslintrc 配置。
import { FlatCompat } from "@eslint/eslintrc";import path from "path";import { fileURLToPath } from "url";// mimic CommonJS variables -- not needed if using CommonJSconst __filename = fileURLToPath(import.meta.url);const __dirname = path.dirname(__filename);const compat = new FlatCompat({ baseDirectory: __dirname});
export default [ // mimic ESLintRC-style extends ...compat.extends("eslint-config-my-config"),];配置提示
安装 eslint-define-config 插件,在编辑配置时提供代码提示。
pnpm add -wD eslint eslint-define-config该插件提供了 defineConfig 和 defineFlatConfig 函数,分别用于传统配置和平面配置。
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 时,配置对象会被合并,如果有冲突时,后面的对象会覆盖前面的对象。
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 的配置对象,则为全局忽略配置,会对默认忽略项进行扩展。
{ ignores: ["**/*.d.ts", "**/dist/*"],},指定了 ignore,最好也要指定 files:
如果有 ignores 而没有 files,但有任何其他配置项(如 rules),那么该配置对象适用于除了 ignores 指定的文件外的所有文件,相当于 files 为 **/*。
通过 ! 取消忽略文件和目录,包括默认忽略项。
{ ignores: [ // 使用 build/** 会忽略整个目录及其内容,因此遍历将完全跳过目录,也就无法取消忽略其中的任何内容。 "build/**/*", "!build/test.js" ]}安装 @eslint/compat,通过 includeIgnoreFile(),可以导入传统的 .gitignore 文件。
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 项目中共享规则、处理器、配置、解析器等等。
可以通过 <插件名>/ 使用插件中特定的规则:
{ 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 可以检查的代码片段。
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。
最新的扁平化配置也使得我们很容易去自定义、覆盖一些独特的配置。
编辑配置
按目前项目需求编辑规则,关闭一些不需要的检查。
// @ts-checkimport 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";
/// <reference types="@eslint-types/typescript-eslint" />
export default defineFlatConfig([ // 插件的预设配置放在最前面 pluginJs.configs.recommended, ...tseslint.configs.recommended, ...pluginVue.configs["flat/recommended"], { languageOptions: { // 配置全局变量 globals: { ...globals.browser, ...globals.node, InstanceType: "readonly", NodeJS: "readonly", }, }, }, { // 因为demo使用了auto-imports插件,所以需要把自动导入的变量加到全局变量配置里 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", }, }, // 匹配 vue、ts 文件,使用 tseslint.parser { files: ["**/*.ts", "**/*.vue"], languageOptions: { parserOptions: { parser: tseslint.parser, project: "./tsconfig.eslint.json", extraFileExtensions: ["vue"], }, }, },]);我们希望 ESLint 能覆盖所有源码文件,一般新创建一个 tsconfig.eslint.json,在其中包含所有希望被规范化的源码文件。
{ // eslint 检查专用,不要包含到 tsconfig.json 中 "extends": "./tsconfig.base.json", "compilerOptions": { "noEmit": true }, // 只检查,不构建,因此要包含所有需要检查的文件 "include": [ "**/*", // .xxx.js 文件需要单独声明,例如 .eslintrc.js "**/.*.*" ], "exclude": [ // 排除产物目录 "**/dist", "**/node_modules" ]}注意,tsconfig 的文件集合范围总是需要 >= ESLint 的 files 范围,否则会出现该 TSConfig 不包括此文件的报错。
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.jsonFound 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.projectSee 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 样式的检查。
安装
pnpm i -wD stylelint继续安装一些项目需要的插件、规则集。
- 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 管理代码风格,则不必使用该插件)
pnpm i -wD stylelint-config-standard-scss stylelint-config-recommended-vue stylelint-config-recess-order @stylistic/stylelint-plugin安装 stylelint-define-config 以获得配置提示。
pnpm i -wD stylelint-define-config配置
Stylelint 的配置文件与 ESLint 的传统配置 .eslintrc 相似。
在根目录创建 stylelint.config.mjs 文件。具体的规则配置项,详见官方文档-rules。
// @ts-checkimport defineConfig from "stylelint-define-config";
/// <reference types="@stylelint-types/stylelint-scss" />
export default defineConfig({ // 继承的预设,这些预设包含了规则集插件 extends: [ // 基本 scss 规则 "stylelint-config-standard-scss", // scss vue 规则 "stylelint-config-recommended-vue/scss", // 样式属性顺序规则 "stylelint-config-recess-order", ], plugins: [ // 代码风格插件 "@stylistic/stylelint-plugin", ], rules: { // 自定义规则集的启用 / 禁用 "@stylistic/max-line-length": 100, "no-empty-source": null, }, // glob 匹配排除的文件 ignoreFiles: ["**/dist/*", "**/node_modules/*"], overrides: [ { files: ["*.html", "**/*.html"], customSyntax: "postcss-html", }, ],});修复 html 报错
在对 html 检查时,可能有如下莫名其妙的报错:
demo/index.html 1:1 ✖ Unknown word CssSyntaxError针对 html 格式文件采用自定义语法解析器:
pnpm i -wD postcss-htmloverrides: [ { files: ["*.html", "**/*.html"], customSyntax: "postcss-html", },],运行
编辑 package.json,添加 lint 命令。
"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 对插件进行配置:
{ // 指定 typescript 的路径,使其能够找到项目中安装的 typescript。 "typescript.tsdk": "node_modules/typescript/lib", // 关闭 IDE 自带的样式验证 "css.validate": false, "less.validate": false, "scss.validate": false, // 指定 stylelint 生效的文件类型(尤其是 vue 文件)。 "stylelint.validate": [ "css", "scss", "postcss", "vue" ], // 启用 eslint 的格式化能力 "eslint.format.enable": true, // eslint 会在检查出错误时,给出对应的文档链接地址 "eslint.codeAction.showDocumentation": { "enable": true }, // 指定 eslint 生效的文件类型(尤其是 vue 文件)。 "eslint.probe": [ "javascript", "typescript", "vue" ], // 指定 eslint 的工作区 // "eslint.workingDirectories": [ // { // "mode": "auto" // } // ], // 指定 eslint 的 node_modules 路径,使其能够找到项目中安装的 eslint。 "eslint.nodePath": "node_modules", // 在保存时,自动修复 lint 错误。 "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 等文件的格式化。它会删除了所有原始代码格式,并确保所有输出的代码符合一致的格式。
安装
pnpm i -wD prettier在 .vscode/setting.json 中设置一些配置。
{ // 设置默认格式化程序 "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 规则冲突。
pnpm install -wD eslint-config-prettier eslint-plugin-prettier按文档操作导入 eslint-plugin-prettier/recommended,该 recommended 已经集成了两者的配置。
注意:将其添加为文件中配置数组的最后一项,以便 eslint-config-prettier 有机会覆盖其他配置。
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";export default defineFlatConfig([ // .... eslintPluginPrettierRecommended, { rules: { // prettier 配置为警告而不是报错 'prettier/prettier': ['warn'], }, },]);与 stylelint 集成
从 Stylelint v15 开始,所有与样式相关的规则都已弃用。如果您使用的是 v15 或更高版本,并且未使用这些弃用的规则,则 stylelint-config-prettier 插件不再是必要的。
配置文件
创建 .prettierrc 文件,并设定一些规则。configuration,options。
我喜欢使用 prettier.config.mjs 配合 prettier-define-config 插件以获得配置提示。
// @ts-checkimport { defineConfig } from '@archoleat/prettier-define-config';export default defineConfig({ arrowParens: 'always', // 箭头函数参数周围加上括号 bracketSameLine: true, // 大括号与代码在同一行 bracketSpacing: true, // 大括号内部加空格 semi: true, // 分号结尾 experimentalTernaries: false, // 不使用实验性三元表达式 singleQuote: true, // 使用单引号 jsxSingleQuote: true, // JSX属性值使用单引号 quoteProps: 'preserve', // 保留引号样式 trailingComma: 'all', // 尾随逗号保留 singleAttributePerLine: false, // 不强制单个属性换行 htmlWhitespaceSensitivity: 'css', // HTML空格敏感性为css vueIndentScriptAndStyle: false, // Vue脚本和样式不缩进 proseWrap: 'never', // 文本不换行 insertPragma: false, // 不插入格式化标记 printWidth: 80, // 打印宽度为80个字符 requirePragma: false, // 不要求格式化标记 useTabs: false, // 不使用Tab缩进 embeddedLanguageFormatting: 'auto', // 嵌入语言格式自动 tabWidth: 2, // Tab宽度为2个空格 endOfLine: 'auto', // 行尾自动});VSCode 响应配置修改有时候非常慢,善用 reload。
在 .vscode\settings.json 中配置自动格式化等。
{ // 保存时自动格式化 "editor.formatOnSave": true, // 指定格式化工具为 prettier "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 的操作。
# 暂时先使用命令行格式化吧npx prettier . --writeReplace ··· with ↹↹
这是因为没有统一好项目的缩进风格。
在 .vscode\settings.json 中配置强制使用2个空格缩进:
{ "editor.tabSize": 2, "editor.insertSpaces": true, "editor.detectIndentation": false,}完整配置
// @ts-checkimport 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';
/// <reference types="@eslint-types/typescript-eslint" />
export default defineFlatConfig([ // 插件的预设配置放在最前面 pluginJs.configs.recommended, ...tseslint.configs.recommended, ...pluginVue.configs['flat/recommended'], { languageOptions: { // 配置全局变量 globals: { ...globals.browser, ...globals.node, InstanceType: 'readonly', NodeJS: 'readonly', }, }, }, { // 因为demo使用了auto-imports插件,所以需要把自动导入的变量加到全局变量配置里 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', }, }, // 匹配 vue、ts 文件,使用 tseslint.parser { files: ['**/*.ts', '**/*.vue'], languageOptions: { parserOptions: { parser: tseslint.parser, project: './tsconfig.eslint.json', extraFileExtensions: ['vue'], }, }, }, eslintPluginPrettierRecommended, { rules: { // prettier 配置为警告而不是报错 'prettier/prettier': ['warn'], }, },]);// @ts-checkimport defineConfig from 'stylelint-define-config';
/// <reference types="@stylelint-types/stylelint-scss" />
export default defineConfig({ // 继承的预设,这些预设包含了规则集插件 extends: [ // 基本 scss 规则 'stylelint-config-standard-scss', // scss vue 规则 'stylelint-config-recommended-vue/scss', // 样式属性顺序规则 'stylelint-config-recess-order', ], plugins: [ // 代码风格插件,应该使用 prettier 管理代码风格 // '@stylistic/stylelint-plugin', ], rules: { // 自定义规则集的启用 / 禁用 // '@stylistic/max-line-length': 100, 'no-empty-source': null, }, // glob 匹配排除的文件 ignoreFiles: ['**/dist/*', '**/node_modules/*'], overrides: [ { files: ['*.html', '**/*.html'], customSyntax: 'postcss-html', }, ],});// @ts-checkimport { defineConfig } from '@archoleat/prettier-define-config';
export default defineConfig({ arrowParens: 'always', // 箭头函数参数周围加上括号 bracketSameLine: true, // 大括号与代码在同一行 bracketSpacing: true, // 大括号内部加空格 semi: true, // 分号结尾 experimentalTernaries: false, // 不使用实验性三元表达式 singleQuote: true, // 使用单引号 jsxSingleQuote: true, // JSX属性值使用单引号 quoteProps: 'preserve', // 保留引号样式 trailingComma: 'all', // 尾随逗号保留 singleAttributePerLine: false, // 不强制单个属性换行 htmlWhitespaceSensitivity: 'css', // HTML空格敏感性为css vueIndentScriptAndStyle: false, // Vue脚本和样式不缩进 proseWrap: 'never', // 文本不换行 insertPragma: false, // 不插入格式化标记 printWidth: 80, // 打印宽度为80个字符 requirePragma: false, // 不要求格式化标记 useTabs: false, // 不使用Tab缩进 embeddedLanguageFormatting: 'auto', // 嵌入语言格式自动 tabWidth: 2, // Tab宽度为2个空格 endOfLine: 'auto', // 行尾自动});{ "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 的路径,使其能够找到项目中安装的 typescript。 "typescript.tsdk": "node_modules/typescript/lib", // 关闭 IDE 自带的样式验证 "css.validate": false, "less.validate": false, "scss.validate": false, // 指定 stylelint 生效的文件类型(尤其是 vue 文件)。 "stylelint.validate": [ "css", "scss", "postcss", "vue" ], // 启用 eslint 的格式化能力 "eslint.format.enable": true, // eslint 会在检查出错误时,给出对应的文档链接地址 "eslint.codeAction.showDocumentation": { "enable": true }, // 指定 eslint 生效的文件类型(尤其是 vue 文件)。 "eslint.probe": [ "javascript", "typescript", "vue", ], // 指定 eslint 的工作区 // "eslint.workingDirectories": [ // { // "mode": "auto" // } // ], // 指定 eslint 的 node_modules 路径,使其能够找到项目中安装的 eslint。 "eslint.nodePath": "node_modules", // 在保存时,自动修复 lint 错误。 "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit", "source.fixAll.stylelint": "explicit" }, // 保存时自动格式化 "editor.formatOnSave": true, // 指定格式化工具为 prettier "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" }, // 行尾默认为 LF 换行符而非 CRLF "files.eol": "\n", // 自动补充闭合的 HTML 标签 "html.autoClosingTags": true,}总结
我们在原先的 Monorepo 项目中集成了最新的 ESLint 和 Stylelint,以对代码质量进行检查。
然后,我们引入了 Prettier,使代码风格保持一致。
最后,我们解决了 ESLint 和 Prettier 之间的冲突,并配合 VSCode 设置了一系列自动化配置。
每次都进行全量 lint 检查是非常耗时的,在下一篇文章中进行解决。
评论