惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Proofpoint News Feed
H
Hacker News: Front Page
C
CXSECURITY Database RSS Feed - CXSecurity.com
C
Cisco Blogs
P
Palo Alto Networks Blog
Know Your Adversary
Know Your Adversary
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Cybersecurity and Infrastructure Security Agency CISA
AWS News Blog
AWS News Blog
Spread Privacy
Spread Privacy
S
Schneier on Security
The Hacker News
The Hacker News
Cyberwarzone
Cyberwarzone
T
Tenable Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
T
Tailwind CSS Blog
S
Secure Thoughts
N
Netflix TechBlog - Medium
T
The Exploit Database - CXSecurity.com
I
Intezer
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Help Net Security
Help Net Security
K
Kaspersky official blog
Google Online Security Blog
Google Online Security Blog
L
LangChain Blog
Martin Fowler
Martin Fowler
L
LINUX DO - 热门话题
Hacker News: Ask HN
Hacker News: Ask HN
www.infosecurity-magazine.com
www.infosecurity-magazine.com
有赞技术团队
有赞技术团队
P
Privacy International News Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Recent Announcements
Recent Announcements
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
The Register - Security
The Register - Security
云风的 BLOG
云风的 BLOG
Google DeepMind News
Google DeepMind News
阮一峰的网络日志
阮一峰的网络日志
WordPress大学
WordPress大学
Recorded Future
Recorded Future
The Last Watchdog
The Last Watchdog
G
Google Developers Blog
T
Threatpost
小众软件
小众软件
S
Securelist
Recent Commits to openclaw:main
Recent Commits to openclaw:main
O
OpenAI News

轻笑Chuckle

NestJS实践杂记 React-记-3 React-记-2 React-记-1 闭包与内存泄漏 Vue3.5 更新了啥 Git lint 相关 Monorepo 单仓库多应用 Vue+TS 实现虚拟列表 开发了一个 Canvas 2D 渲染引擎 Canvas 2D 事件 Canvas 2D 贝塞尔曲线 Canvas 2D 进阶 Canvas 2D 基础 Git 规范与实践 Git 熟知熟用 解决System占CPU过高 浏览器HTTP缓存 浏览器渲染流程
集成 lint 代码规范工具
『轻笑Chuckle』 · 2024-09-08 · via 轻笑Chuckle

前言

书接上回,为 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 为我们添加了五个开发依赖。

  1. @eslint/js 插件的 recommended 配置包含了一组被 ESLint 社区推荐的规则。
  2. eslint 是 ESLint 的核心库。
  3. eslint-plugin-vue 提供了一些针对 Vue 的 ESLint 规则。
  4. globals 提供了一个包含各种 JS 环境全局变量的对象,在 ESLint 中,使用 globals 来配置哪些全局变量是允许在你的代码中使用的。
  5. 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

该插件提供了 defineConfigdefineFlatConfig 函数,分别用于传统配置和平面配置。

1
2
3
4
5
import { defineFlatConfig } from "eslint-define-config";
export default defineFlatConfig([
{ files: ["**/*.{js,mjs,cjs,ts,vue}"] },

]);

配置对象简介

Flat config(扁平化配置) 导出一个包含若干配置对象的数组。官方文档:配置 ESLint

配置对象由以下属性组成:

  1. files:glob 数组,表示配置适用的文件,未指定时适用于所有与其他配置对象匹配的文件
  2. ignores:glob 数组,表示忽略检查的文件,
  3. 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 所需的额外配置选项,
  4. linterOptions:对象,包含与 linting 过程相关的设置
    • noInlineConfig :boolean,表示是否允许内联配置
    • reportUnusedDisableDirectives:boolean,表示是否应该跟踪和报告未用的禁用指令
  5. processor:包含 preprocess() 和 postprocess() 方法的对象,或者指示插件中处理器的名称的字符串(例如 “pluginName/processorName”)。
  6. plugins:包含插件名称与对应的插件对象的名值对对象。如果指定了 files,则只适用于与之匹配的文件。
  7. rules:包含具体的规则配置,当指定了 files 或 ignores 时,这些规则配置仅对匹配的文件可用。内置规则。规则值: warn(警告) | error(报错) | off
  8. 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

专门用来配置检查过程的选项,对文件源码的解析方式没有影响。它有两个选项:

  1. noInlineConfig:默认为 false,表示是否禁用内联配置,内联配置是通过 /*eslint*/ 注释实现的,例如 /*eslint semi: error*/。设置为 true,则会忽略所有内联配置。
  2. reportUnusedDisableDirectives/*eslint-disable*//*eslint-disable-next-line*/ 这样的禁用指令是用来禁用 ESLint 规则的,围绕代码的某些部分。随着代码的变化,这些指令有可能不再需要,因为代码的变化使规则不再被触发。通过设置该选项为 true,可以把未用的禁用指令被报告为警告。

languageOptions

languageOptions 是配置语言选项。

  1. ecmaVersion 指定支持的 ES 语法版本,确定语法和可用的全局变量,可以是年份(如 2022)或者版本号(如 5),默认为 latest,表示最新版本。
  2. sourceType 表示正在使用的 JavaScript 文件的模式。有以下三种值:
    • module ESM 模块(当ecmaVersion为3或5时无效)。代码具有模块范围并以严格模式运行。
    • commonjs CommonJS 模块。代码具有顶级函数作用域,并以非严格模式运行。
    • script 非模块。代码具有共享的全局范围并以非严格模式运行。
    • 默认情况下,.js 和 .mjs 文件的 sourceType 是 “module”,而 .cjs 文件则是 “commonjs”。
  3. parser 官方文档,自定义解析器将 JavaScript 代码转换为抽象语法树,以供 ESLint 检查。
  4. parserOptions 键值对对象,用于向自定义解析器传递选项。
  5. 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 样式的检查。

安装

1
pnpm i -wD stylelint

继续安装一些项目需要的插件、规则集。

  1. stylelint-config-standard-scss:集成完整的 sass 规则集。它本身继承了很多东西,包括 sass 规则实现的插件、css 标准规则集 stylelint-config-standard 等。
  2. stylelint-config-recommended-vue:使 Stylelint 支持对 .vue 文件的 style 标签部分进行检查。
  3. stylelint-config-recess-order:一种推荐的 css 属性排序的规则(先写定位,再写盒模型,再写内容区样式,最后写 CSS3 相关属性)。
  4. @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 格式文件采用自定义语法解析器:

1
pnpm i -wD postcss-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 插件 ESLintStylelintPrettier 使用,可以在编辑器中实时检查代码规范。

如果插件没有实时提示,可以查看面板->输出,找到对应插件查看问题,多半是 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 等文件的格式化。它会删除了所有原始代码格式,并确保所有输出的代码符合一致的格式。

安装

1
pnpm i -wD prettier

在 .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 之间的冲突。

两个插件:

  1. eslint-plugin-prettier:基于 prettier 代码风格的 eslint 规则。即 eslint 使用 prettier 规则来格式化代码,并将差异报告为单个ESLint问题
  2. 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 文件,并设定一些规则。configurationoptions
我喜欢使用 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
1
2

npx prettier . --write

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 检查是非常耗时的,在下一篇文章中进行解决。