前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[译] 以和为贵!让 ESlint、Prettier 和 EditorConfig 互不冲突

[译] 以和为贵!让 ESlint、Prettier 和 EditorConfig 互不冲突

作者头像
江米小枣
发布2021-06-30 11:02:56
8.7K6
发布2021-06-30 11:02:56
举报
文章被收录于专栏:云前端云前端云前端

原文链接:https://blog.theodo.com/2019/08/empower-your-dev-environment-with-eslint-prettier-and-editorconfig-with-no-conflicts/

ESLint, Prettier and EditorConfig

来由

如果你已经在搭配使用 Prettier 和 ESLint, 可能已经遇到过 代码格式化冲突 的问题了吧。

ESLint - Prettier conflict

我曾在一次把 TypeScript 项目从 TSLint 迁移到 ESLint 的工作中遇到过这些问题。我们打算用 ESLint 和 Prettier 接管语法检查,在添加了一条 ESLint 规则强制规定 2 个空格缩进以解决上图中的问题后,其他问题又像按下葫芦浮起瓢一样纷纷出现了,很明显没法子通过一条条增加规则解决每一个冲突。

网上关于这个话题的确有很多说法,但大部分都是针对某个特定项目给出一个配置,而非深入阐释为什么 ESLint、Prettier 或 EditorConfig 会八字不合。本文的目的就是对这些潜在的问题解惑,以免相关的 bug 再干扰调试。

策略

我们先来明确一下 各司其职 的原则:

  • EditorConfig 将负责统一各种编辑器的配置,所有和编辑器相关的配置 (行尾、缩进样式、缩进距离...) 都交给它
  • Prettier 作为 代码格式化 工具
  • 其余的,也就是 代码质量 方面的语法检查,用 ESLint 来做

ESLint 和 Prettier

假设我们有这么一个 main.js,并同时配置了 ESLint 和 Prettier。

main.js

function printUser(firstName, lastName, number, street, code, city, country) {
    console.log(`${firstName} ${lastName} lives at ${number}, ${street}, ${code} in ${city}, ${country}`);
}
printUser('John', 'Doe', 48, '998 Primrose Lane', 53718, 'Madison', 'United States of America');

eslintrc.json

{
  "extends": ["eslint:recommended"],
  "env": {
    "es6": true,
    "node": true
  }
}

让 Prettier 做它自己的工作

为了能 配合使用 ESLint 和 Prettier,应该 关闭所有可能和 Prettier 冲突的 ESLint 规则 (也就是 代码格式化 那些)。好消息是,eslint-config-prettier 包已经解决了这个问题。

npm install eslint-config-prettier --save-dev

先在 .eslintrc.json 中,将 prettier 加到 extends 数组的最后,并移除任何 代码格式化 相关的规则:

{
  "extends": ["eslint:recommended", "prettier"],
  "env": {
    "es6": true,
    "node": true
  }
}

如此一来, Prettier 的配置将覆盖 extends 数组中先前任何 代码格式化 相关的 ESLint 配置,二者就能并行不悖地工作了。

将 Prettier 整合进 ESLint

分别运行两条命令以检查语法和格式化代码可不太方便,我们可以通过安装 eslint-plugin-prettier 包来解决这个问题。

npm install eslint-plugin-prettier --save-dev

.eslintrc.jsonplugins 数组中加入 prettier 插件,并建立一条指定为 error 的 Prettier 新规则,这样任何格式化错误就也被认为是 ESLint 错误了。

{
  "extends": ["eslint:recommended", "prettier"],
  "env": {
    "es6": true,
    "node": true
  },
  "rules": {
    "prettier/prettier": "error"
  },
  "plugins": [
    "prettier"
  ]
}

main.js 运行 ESLint 试一下:

npx eslint main.js

ESLint no fix

可以看到,那些字符过多或缩进错误的行,都被标以了 prettier/prettier 并作为 ESLint 错误被打印出来。

修复之:

npx eslint --fix main.js

文件将按 Prettier 的方式被正确格式化。

Prettier 和 ESLint 配合中的常见问题

添加 ESLint 插件

以上的配置应付小项目绰绰有余;但当你使用 Vue、React 或其他框架时,还是 很容易让 ESLint 和 Prettier 的配置打架。我遇到的一个常见问题是当开发者增加一个 ESLint 插件后,如何在不同时改动 Prettier 的情况下,也能让后者正常工作。

以 TypeScript 为例

出于某些考虑,我们决定在项目中使用 TypeScript。鉴于 TSLint 将被废弃,自然要用 ESLint 取而代之。这里就使用 TypeScript 作为一个例子,来展示 对于有一个适用的 ESLint 插件的框架,该如何处理

这是进行了 TypeScript 改造后的 main.ts 文件:

function printUser(firstName: string, lastName: string, number: number, street: string, code: number, city: string, country: string): void {
 console.log(`${firstName} ${lastName} lives at ${number}, ${street}, ${code} in ${city}, ${country}`);
}
printUser('John', 'Doe', 48, '998 Primrose Lane', 53718, 'Madison', 'United States of America');

要想让 ESLint 兼容 TypeScript 或是其他什么特殊语法的框架,需要增加一个 parser 以使 ESLint 可以读取新的代码和相关的一系列规则。

npm install typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin --save-dev

然后修改 .eslintrc.json,分别向 parser 选项和 extends 数组中包含 TypeScript 插件:

{
  "parser": "@typescript-eslint/parser",
  "extends": ["plugin:@typescript-eslint/recommended", "eslint:recommended", "prettier"],
  "env": {
    "es6": true,
    "node": true
  },
  "rules": {
    "prettier/prettier": "error"
  },
  "plugins": [
    "prettier"
  ]
}

带上 --fix 选项,再来试试 ESLint:

eslint-fix-option

这时如果我们多次执行这条命令,每次都将得到同样的报错 -- 尽管控制台里面说错误是可以被修复的。此时文件被修改为了这样:

function printUser(
  firstName: string,
  lastName: string,
  number: number,
  street: string,
  code: number,
  city: string,
  country: string
): void {
  console.log(
    `${firstName} ${lastName} lives at ${number}, ${street}, ${code} in ${city}, ${country}`
  );
}

printUser(
  'John',
  'Doe',
  48,
  '998 Primrose Lane',
  53718,
  'Madison',
  'United States of America'
);

所以 Prettier 的确格式化了我们的代码,但 ESLint 期望的 4 个空格没有被满足。错误看起来和 @typescript-eslint 规则有关。

如果你像我一样在使用 VSCode 并开启了保存时自动执行 ESLint 修复,可能会看到这种情况:

Conflict between typescript eslint and prettier

通过禁用新增插件的所有 ESLint 格式化规则解决冲突

很多人的一个常见错误就是头疼医头、脚疼医脚。比如对于这个 @typescript-eslint 插件里面的缩进规则,他们会往 rules 数组中添加一条这样的规则:

"@typescript-eslint/indent": ["error", 2]

这当然解决了具体冲突,但有两个问题出现了:

  • 无法保证 typescript-eslint 插件中的其他规则今后不和 Prettier 冲突
  • ESLint 和 Prettier 又开始同时负责代码格式化了,这违背了我们的分工策略

按照之前的整合方法,通过在 extends 数组中增加 prettier/@typescript-eslint 来禁用相关插件中所有关乎 代码格式化 的规则。

{
  "parser": "@typescript-eslint/parser",
  "extends": ["plugin:@typescript-eslint/recommended", "eslint:recommended", "prettier", "prettier/@typescript-eslint"],
  "env": {
    "es6": true,
    "node": true
  },
  "rules": {
    "prettier/prettier": "error"
  },
  "plugins": [
    "prettier"
  ]
}

谨记 .eslintrc.jsonextends 数组的顺序非常重要。基本上每次向数组添加新配置时,都将覆盖之前的配置。因此 prettierprettier/@typescript-eslint 待在数组末尾至关重要。

这样配置后就没问题了,ESLint 将不会再越俎代庖。

总结一下这种常见问题:

  • 每当你添加一个 ESLint 插件,针对 它引入的 代码格式化 规则 添加一个 prettier 配置 (如果有的话) 。在我们的例子中,使用了 prettier/@typescript-eslint,但其实我们也可以用 prettier/reactprettier/vue。检查 eslint\-config\-prettier 文档(https://github.com/prettier/eslint-config-prettier) 获取可用的 ESLint 插件。
  • 不要尝试自己覆盖 eslintrc 中的格式化规则
  • 每当你见到这种 Prettier 和 ESLint 对同一种格式化的冲突,就以为着你有一条无用的 ESLint 格式化规则,也意味着你没有遵守以上两条

添加一条自定义规则

项目团队中的 TypeScript 开发者对 2 个空格缩进浑身不舒服,非要改成 4 个。一个常见的错误是把我们的 ESLint-Prettier 整合策略抛之脑后,并在 .eslintrc.json 中直接添加规则,就像这样:

{
  "parser": "@typescript-eslint/parser",
  "extends": ["plugin:@typescript-eslint/recommended", "eslint:recommended", "prettier", "prettier/@typescript-eslint"],
  "env": {
    "es6": true,
    "node": true
  },
  "rules": {
    "prettier/prettier": "error",
    "@typescript-eslint/indent": ["error", 4]
  },
  "plugins": [
    "prettier"
  ]
}

熟悉的错误毫无意外地又出现了,和我们之前解决 Prettier 协同 ESLint 时遇到的一摸一样:

Conflict with custom rule

rules 数组中自定义的规则会覆盖 prettier/@typescript-eslint 配置。

按照正确的策略,代码格式化 规则应该在 .prettierrc 中配置。对于例子来说,应该是:

.prettierrc

{
  "tabWidth": 4
}

这样 Prettier 将以 4 个空格格式化代码,而 .eslintrc.json 应该不关心任何缩进规则。

总结一下这种常见问题:

  • 当你想添加一条规则时,分清其属于 代码质量 还是 代码格式化 类别。简单地做法是,检查这条规则在 Prettier 中是不是可行的
  • 不要在 .eslintrc.json 中添加格式化规则,这样做将不可避免的和 Prettier 冲突

Prettier 和 EditorConfig

设置编辑器配置

EditorConfig 使不同编辑器可以保持同样的配置。因此,我们得以无需在每次编写新代码时,再依靠 Prettier 来按照团队约定格式化一遍(译注:出现保存时格式化突然改变的情况)。当然这需要在你的 IDE 上安装了必要的 EditorConfig 插件或扩展。

本文以 VSCode 为例,但 EditorConfig 支持很多编辑器。

在项目中增加自定义的编辑器配置:

.editorconfig

[*]
end_of_line = lf
charset = utf-8
indent_style = space

如果安装了 the EditorConfig VSCode extension,编辑器将自动获知该如何格式化你的文件。你也能在编辑器右下角看到相应的信息:

vscode

避免 EditorConfig 和 Prettier 的重复配置

但是,这意味着 Prettier 和 EditorConfig 共享了相同的配置选项,而我们不希望同步维护两份重复的配置 (比如关于行尾的配置)。Prettier 的最新版本通过处理 .editorconfig 文件来决定使用的配置。

限于以下几种选项:

end_of_line
indent_style
indent_size/tab_width
max_line_length

这些选项会被映射为 Prettier 的相关选项 (如果没有在 .prettierrc 再被定义的话):

"endOfLine"
"useTabs"
"tabWidth"
"printWidth"

就像 ESLint 配合 Prettier 时的策略那样,在你改变 EditorConfig 或 Prettier 配置时,根据这些限定选项来决定在哪边改。上面例子中的选项就应该只在 .editorconfig 中存在。

据此再检查我们上面做过的所有配置,还能发现一个配置错误。我们在 Prettier 配置中指定了缩进距离。因为这也是一个浏览器相关的配置,所以应该将其移至 .editorconfig

.editorConfig

tab_width 4

现在我们删除了已经空白的 .prettierrc.json,并对未格式化的代码运行 ESLint:

function printUser(
    firstName: string,
    lastName: string,
    number: number,
    street: string,
    code: number,
    city: string,
    country: string
): void {
    console.log(
        `${firstName} ${lastName} lives at ${number}, ${street}, ${code} in ${city}, ${country}`
    );
}

printUser(
    "John",
    "Doe",
    48,
    "998 Primrose Lane",
    53718,
    "Madison",
    "United States of America"
);

代码缩进变成了 4 个空格。现在,无论你在何时用编辑器打开一个新文件,都会应用这个配置,Prettier 同样也会遵循。

--End--

查看更多前端好文 请搜索 云前端 或 fewelife 关注公众号 转载请注明出处

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-06-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 云前端 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 来由
    • 策略
    • ESLint 和 Prettier
      • 让 Prettier 做它自己的工作
        • 将 Prettier 整合进 ESLint
        • Prettier 和 ESLint 配合中的常见问题
          • 添加 ESLint 插件
            • 以 TypeScript 为例
            • 通过禁用新增插件的所有 ESLint 格式化规则解决冲突
            • 添加一条自定义规则
        • Prettier 和 EditorConfig
          • 设置编辑器配置
            • 避免 EditorConfig 和 Prettier 的重复配置
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档