管控前端代码质量,必要配置一览
技术tools, frontend-dev, DX, lint

管控前端代码质量,必要配置一览

Published On
| 更新于
Updated On
31分钟阅读概述:作者分享了,在开发项目中,为了前端代码质量的可管控和健壮性,有必要引入的多个相关配置项,和简略配置细节。

最前

本文涉及配置项一览表

配置项(文件(目录)/扩展等)作用概述
.editorconfig跨编辑器平台的格式化规则约定
editorconfig
.prettierrc.js简洁格式化工具
prettier
.eslintrc.jsJS 等 JS 变体语言(TS,JSX,TSX)格式化工具
eslint
.husky介入 git hook
husky
.lintstagedrc.js可关联 gitHook#pre-commit,代码提交门禁
lint-staged
.commitlintrc.js可关联 gitHook#commit-msg,代码提交门禁
commitlint
扩展:Prettier - Code formattervscode 生态插件,配合 prettier 配置使用
扩展:Prettier
扩展:ESLintvscode 生态插件,配合 eslint 配置使用
扩展:ESLint
.stylelintrc.js样式表文件(CSS / SCSS / LESS)格式化工具
stylelint
tsconfig.json配置 prettier、 eslint 格式化生效范围
setting.json以 vscode 编辑器为例,.vscode/ 目录下配置文件

特别说明

附上该表格在这里,意在说明,本文所述/罗列相关配置的简要分享,是从吾辈日常工作,及实际项目配置出发,比如相关配置是从项目整体背景是 React + TypeScript 出发,所以不可避免的存在一定片面性、局限性。权威的配置应直接跳转官网索引参考。

背景

前端工程化

前端工程化,网络上有着各家的理解和阐述,这里不再无脑 CV 贴到这里,但大致肯定都契合着这样一个主题:所谓工程化,一定是从(业务)项目本身的健壮性和可长期维护性出发。也不局限于开发代码本身,将“工程化”概念抬升一点,实际开发前的需求评审,开发测试完成后,CI/CD/打包部署,都可以看作工程化可涉及的要点。

而这当中,对代码本身、git 提交作规范限制,也是尤其重要的一环。

规范化一环

不言而喻的,如果是独立开发,代码天马行空的来都行,甚至于最原始的目的:能跑能上线也 OK。

但是立足于团队配合的代码开发项目,严格的规范限制、统一的代码风格、语义化 Commit 提交记录,才能让代码无论随着团队的成员迭代,人去人留,项目本身始终保持着其长期可维护性

代码风格/偏好的约定

显然的,不同的开发从业人员大致有着不同的代码风格偏好,比如这么个浅显的问题,“在前端代码里,究竟该不该使用分号”。

这个问题下,如果我们共同遵守一套规范,规范限制了代码使用强分号风格,不用也用。而规范声明应使用 JS 下支持的无分号风格,用也不用。

总而言之,开发人员约定并遵守相同的规范,并在开发过程中辅以 Lint 格式化工具,长远受益。

正题

1. 跨不同编辑器平台的统一格式化约定

虽然本文主要从吾辈日常使用的编辑器 vscode 出发,但是我们依然有手段实现跨不同编辑器平台的统一格式化约定 —— 尝试使用 EditorConfig 配置文件

editor config

一些技术分享博文略去了该配置文件,诚然,其声明的约定格式都可以通过后续的 eslint 或者 prettier 来配置实现。

不过吾辈抱持着这样一个观点,只要靶向的是同一个功能需求,能够接受多个不同配置间是可以冗余重复的,只要合理配置,不让其相互冲突就行,另个角度来说,也是种兜底措施,一个配置出了问题,或者放弃不用,还有另个配置支持着相同功能。

一个典型的 editor config 配置文件内容如下:

.editorconfig
# https://editorconfig.org/
root = true

[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 2
trim_trailing_whitespace = true

具体的配置含义不作赘述。

vscode 编辑器下,可配合编辑器扩展EditorConfig for VS Code 使用。

2. 格式化流行工具

当下流行的两个格式化工具: prettier 和 eslint;

  • 前者能够支持对各种格式文件作简单格式化需求,但是可以说只能做“死板的格式化”,不具备代码逻辑/合法性校验功能。
  • 后者聚焦于对 JS、TS 等变体语言的代码逻辑/合法性校验,及一定的自动修正功能,同时可以取代 prettier,作格式化功能。

配置文件格式选择

通常的,配置文件支持多种格式,比如广泛使用的 json 格式,以及 jsyaml,甚至于直接将配置内容书写在 package.json 文件下。

使用何种配置文件,看各家偏好。

吾辈的偏向来说,更偏向于/推荐使用 .js.cjs, .mjs) 后缀格式的配置文件,因为:

  1. 可以友好的添加一些注释备忘 (原始 json 文件无法做到这一点,当然 json 本身就是为了去除注释,还配置文件一份清爽);

  2. 对一些冗长的配置作单独文件导出;

    (比如 Prettier 下,基于相关插件如 prettier-plugin-sort-importsimport 语句导入顺序的格式化配置声明)可以单独声明一个变量并导出,而后在主配置文件下导入。如下的例子:

import-order-for-prettier-plugin-sort-imports.cjs

而在 prettier 主配置文件下,则可以想这样导入:

.prettierrc.cjs

而这是使用 json 格式作为配置文件做不到的,配置文件或许因为一些插件的具体配置声明,而显得过长(当然本身不是什么问题,还是看开发者个人喜好)

2.1 prettier

前文提到:prettier 能够支持对各种格式文件作简单格式化需求,包括不限于 JS、 CSS、 Markdown 等文件内容,但不具备代码逻辑/合法性校验功能。

其专注于代码的格式化。当然,同 eslint 两者都支持对 JS 等语言的格式化处理,所以两“工具”都有在同一项目中工作时,需注意兼容性处理。会在 eslint 章节提及对兼容性的配置细节。

prettier 也提供了丰富的插件生态,使用 prettier 插件时的相关细节应主要参考: prettier 官方插件本身的官方说明页((以 prettier-plugin-sort-imports 为例))来作进一步的自定义配置。

prettier 依赖安装的版本应明确指定

prettier 不同版本下,可能存在格式化结果的不同,所以为了确保开发团队的格式化结果总是保持一致,package.json 文件下的版本声明、包管理器单独安装时都应对版本作明确指定:

pnpm add -D --save-exact prettier@2.8.8

辩证看待:你是否需要 prettier

不同的“工具”间,可能存在功能交叉的地方,比如:

  • prettiereslint 都可以处理 JS 等代码格式化;
  • prettierstylelint 都可以处理样式表文件格式化;

一篇优秀的博文: Why I don't use Prettier 提出 prettier 的“死板格式化”可能给 git 版本控制带来的一些痛点。(能给出一定参考性)

另外,吾辈的观点,前面已经陈述过:“只要靶向的是同一个功能需求,吾辈能够接受多个不同配置间是可以冗余重复的,只要合理配置,不让其相互冲突就行”。

但如果开发者是另一种观点:少即是多(Less is More),其实舍弃 prettier,全面拥抱 eslint 单独工作:代码合法性校验,以及格式化工作,也是可行并推荐方案。

概念理清:第三方库&编辑器扩展各自职责

这里有必要理清第三方库 prettier 和编辑器扩展(插件)prettier 两者的角色职能。

这里为了阅读区分,后文总是将编辑器扩展称为extension@[somename],而将第三方库称为lib@[somename]

首先,须知道,编辑器扩展extension@prettier依赖于第三方库 lib@prettier的前置存在, 当然 prettier 可以不安装在项目 node_modules/ 下,而存在于 npm install -g 全局安装目录下。

但是 extension@prettier 要正常工作,底层是依赖于的lib@prettier对外暴露的功能 api。

  1. 先来看没有extension@prettier 而已经装上 lib@prettier 的情况下:

此时如果我们需要对代码作格式化处理,需要借助 lib@prettier 提供的命令,如在命令行窗口如下的输入:

pnpm prettier --check --plugin-search-dir=. **/*.{ts,tsx,js,jsx,cjs,mjs,md,mdx,json,scss,less,yaml,yml} --ignore-path .prettierignore --ignore-unknown --no-error-on-unmatched-pattern

如上的命令来完成对指定目录下的文件做格式化处理。

哪怕将上面的命令声明在 packageJson#scripts 字段下,但显然的,日常开发中,不能总依赖命令行的手动输入。

这时就需要编辑器扩展来完成一些自动化工作

  1. 已经装上 lib@prettier 的情况下,配合extension@prettier 扩展:

安装对应扩展后,就允许我们通过快捷键来调用 lib@prettier 提供的 api 来执行格式化动作、或者保存文件时自动格式化当前文件

prettier 编辑器扩展

vscode 编辑器下,插件地址

细节:虽然一般不会出现问题,但还是应注意安装同 lib@prettier 兼容版本。在其官网文档中有说明。

在编辑器本身配置文件 setting.json 的修改

前文提到:安装对应扩展 (extension@prettier)后,就允许我们通过快捷键来调用 prettier api 来执行格式化动作、保存时自动格式化当前文件。但是需要我们对编辑器本身配置做一定配置,视开发者自身情况,确定修改的是编辑器全局配置文件,还是当前项目/用户配置文件

其中,我们可以显式声明 prettier.configPath 字段,指定配置文件地址。

修改片段参考:

.vscode/setting.json

scripts 下声明相关脚本命令

突发的,如果extension@prettiersetting.json 文件中声明何时使用的配置错误,或扩展本身异常,导致该扩展本身未能正常工作

我们除了依赖lib@prettier 提供的 api,在命令行窗口手动输入命令。

或着另个场景:对工程目录下的所有文件进行批量的格式化操作,都应该在 scripts 下声明相关脚本命令,参考如下:

package.json

则允许命令行中如下调用:

# 进行格式检验,但并不格式化处理
pnpm pt-format:check

# 格式化处理
pnpm pt-format

另,eslint 同样适用以上两点。后文同样给出参考代码片段。

2.2 eslint

eslint 聚焦于对 JS、TS 等语言的代码逻辑/合法性校验,及简单的修正功能,同时可以替代 prettier,作格式化功能。总之,在 JS 处理上功能更加强大。

其也提供了丰富的插件生态,使用 eslint 插件时的相关细节应主要参考: eslint 官方插件本身的官方说明页(以 eslint-plugin-import 为例)来作进一步的自定义配置。

各类知名开源仓库下都有着各自完善的 eslint 配置文件直接拿来参考:

比如来自 Adobe 的 react-spectrum 项目的 eslint 配置文件

官方约定规则, config & plugin

首先,需说明,对于 eslint 生态下的 eslint-config-[somename]eslint-plugin-[somename] 的使用细节,来自于官方的规则定义,且随着版本可能有所调整。

总的来说,eslint-plugin-[somename]eslint-config-[somename] 提供更强大的功能:除了可以如同后者可以引入配置集合,也可以新增 ESLint 自定义规则

另外,当前引入规则,可以去除 eslint-plugin-eslint-config- 这样的公共前缀,总之:声明规则上不确定的地方直接索引官网文档

.eslintrc.cjs
{
  // ...
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended", // 直接引入来自 eslint-plugin-react 的“推荐的”配置集合
  ],
  plugins: ["import"], // -> "import" 即 eslint-plugin-import | 该插件作用场景 - import 语句调序 | 与 prettier 下生态插件 `prettier-plugin-sort-imports` 作用有一定重叠
}

eslint 兼容 prettier | 接管 prettier 预警及报错

虽然我们可以不依赖 prettier , 完全依赖 eslint 本身完成格式化工作。但如果想要让两者:eslint + prettier 配合工作,需要在 eslint 配置文件下做必要配置。

首先,需要安装依赖如下

pnpm add -D eslint eslint-config-prettier eslint-plugin-prettier

而在配置文件中,最简洁的声明如下:

.eslintrc.cjs
{
  // ...
  "extends": [
    "eslint:recommended",
    "plugin:prettier/recommended",
  ],
  plugins: [/* ... */],
}

TIP: 如上的,也能说明当前 eslint 版本下,使用 eslint-plugin-[somename]eslint-config-[somename] 插件的细节地方:

如上的,我们安装依赖时包含 eslint-config-prettier,但是 eslint 配置文件里没有出现该插件名称,但实际仍然是使用了该插件的,因为该 extends - "plugin:prettier/recommended" 声明语句实际等效为如下 :

{
  "extends": ["prettier"], // here: "prettier" -> eslint-config-prettier
  "plugins": ["prettier"], // here: "prettier" -> eslint-plugin-prettier
  "rules": {
    "prettier/prettier": "error", // 🎈 turns on the rule provided by this plugin, which runs Prettier from within ESLint.
    "arrow-body-style": "off",
    "prefer-arrow-callback": "off"
  }
}

更详细说明见: eslint-plugin-prettier Recommended Configuration

当然,再次重复:以上这样的配置细节始终是 eslint 官方随版本迭代可能随时改变的使用规则,开发者只需遵守并按官网或插件的说明文档来作配置即可,无需过分关注。

接下来,来具体看看:eslint 接管 prettier 后预警及报错是如何体现的:

来看这样的截图示例tailwind 原子类名排序格式化在 prettier 、 eslint 的配置文件中分别声明依赖于:

  1. prettier 插件:prettier-plugin-tailwindcss
  2. eslint 插件:eslint-plugin-tailwindcss

则在原子类排序“错误”情况下给出的预警/报错情况:

原子类排序未格式化的 eslint 预警

其中第一个红框预警来自:eslint-plugin-tailwindcss, 第二个预警来自: prettier-plugin-tailwindcss原 prettier 预警报错由 eslint 接管后此时统一表示为 eslint(prettier/prettier);


另个图例: 对 import 语句排序格式化在 prettier 、 eslint 的配置文件中分别声明依赖于:

  1. prettier 插件:@ianvs/prettier-plugin-sort-imports
  2. eslint 插件:eslint-plugin-import

则在import 语句顺序错误情况下,给出的报错情况:

import 语句顺序未格式化的 eslint 预警

其中第一个红框预警来自:@ianvs/prettier-plugin-sort-imports, 第二个预警来自: eslint-plugin-import;

由两个截图示例可以直观看出,在 eslint 配置文件中声明了兼容/接管 prettier 格式化后,报警输出呈现 eslint(prettier/prettier) 这样的统一输出。

当然的,上面仅是为了说明eslint 接管 prettier 后 的代码校验预警报错的表现,实际 eslint 和 prettier 两者生态下功能相同或近似的插件, 选择其一安装即可。

所以上面提到的,不妨直接使用 eslint-plugin-tailwindcss 来管理原子类名排序;使用 eslint-plugin-import 来管理import 语句排序。无需冗余安装 prettier 生态下同类型插件。

eslint 编辑器扩展

vscode 编辑器下,插件地址

同编辑器扩展 extension@prettier 的角色一样,如果不安装extension@eslint,我们要使用 eslint 提供的强大语法校验及格式化功能,只能依赖其提供的命令行命令,大致形如:

eslint . --report-unused-disable-directives --fix

extension@eslint 扩展就允许我们通过快捷键来调用 lib@eslint 提供的 api 来执行代码语法逻辑/合法性校验,(包括接管 prettier 后、及其他各插件引入的一众格式化配置)格式化动作、或者保存文件时自动格式化当前文件。

也是该扩展extension@eslint 为我们带来了在编辑器窗口,实时的代码行底红色波浪线报错、黄色波浪线预警。

在编辑器本身配置文件 setting.json 的修改

修改片段参考:

.vscode/setting.json

scripts 下声明相关脚本命令

以及,对工程目录下的所有文件进行批量的 eslint 格式化操作,可在 scripts 下声明相关脚本命令,如下参考:

package.json

对 JS/TS 两套“独立”的 eslint 配置

本小节依然是对 eslint 配置细节的展开:TypeScript 技术栈开发下,我们有必要引入 @typescript-eslint/eslint-plugin 这样的配置规则,按照最佳实践来规范 TS 代码。

pnpm add -D @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint typescript

但是如果按照 @typescript-eslint/eslint-plugin 的初始文档说明操作,可能会全盘将 JS/TS 等格式文件都交由 @typescript-eslint/parser@typescript-eslint/eslint-plugin 作校验处理了。

而开发者或许并不需要如同对 .ts 文件同级别的校验规则对 .js 文件处理,所以我们可以使用 eslint 配置文件下提供的 overrides 字段

则 eslint 配置文件大致形如:

.eslintrc.cjs

喘口气,歇息一下 ~

the silent view

3. 提交门禁

前文所述,都是聚焦于代码书写环节,实现规范化的相关配置,但团队协作下,我们还需将完成无误的代码提交到指定远程仓库,而在提交这一环节,同样有必要引入相关规范或者说是限制。

husky 扮演角色

这当中,husky 为我们提供了可靠的介入 git hook 的功能。

# 安装 husky
pnpm add -D husky
# 启用 Git hook
npx husky install
# 配置 prepare 命令,用于依赖安装后自动运行特定脚本
# 注意,使用 pkg 需要 npm 7 以上版本
pnpm pkg set scripts.prepare="husky install"

在提交这一环节,我们可使用 husky 介入 git-hook,多数情况下,我们主要处理 pre-commitcommit-msg 钩子,前者可用于提交前对代码进行合法性的最终检查,后者见名知义,是对提交信息本身的规范性检查。

lint-staged | 渐进式引入 lint

前文提及,可以使用 packageJson#scripts 下声明的 pnpm lint:fix 对全局代码做代码合法性校验及格式化处理。

但现实情况里,如果我们接触到的是半途引入 eslint 的项目,则力有所逮,更应考虑渐进式处理,只 lint 校验格式化本次提交的代码。这就可以使用到依赖 lint-staged,其使用场景:

Linting makes more sense when run before committing your code. By doing so you can ensure no errors go into the repository and enforce code style. But running a lint process on a whole project is slow, and linting results can be irrelevant. Ultimately you only want to lint files that will be committed.

pnpm add -D lint-staged

安装好该依赖后,配置文件初始化示例:

.lintstagedrc.cjs

pre-commit hook 触发,关联 lint-staged 配置文件

初始化 husky 介入 pre-commit 钩子,执行 npx lint-staged

npx husky add .husky/pre-commit "npx lint-staged"

commit lint

git commit msg 提交信息的规范,我们可安装依赖 @commitlint

pnpm add -D @commitlint/cli @commitlint/config-conventional

简单的配置文件示例:

.commitlintrc.cjs

或者跳转官方参考实例

commit-msg hook 触发,关联 @commitlint 配置文件

初始化 husky 介入 commit-msg 钩子,执行 npx commitlint

npx husky add .husky/commit-msg 'npx --no -- commitlint --edit ${1}'

引擎版本

一般而言,多个独立项目间因为新旧原因,创建时间的不同,或许 node 依赖版本存在差异,建议在 packageJson 文件下显式声明当前项目所用的 node 版本(也可包含使用的包管理器的版本),便于接手项目的开发人员能够顺利的在对应 node 版本下,将项目运行起来。

package.json
{
  "engines": {
    "node": "^16.0.0 || >=18.0.0"
  },
  "packageManager": "pnpm@8.6.11",

}

总结

全文冗长,却仍然片面,更全面的/整体的关于代码质量主题推荐阅读该开发者博文:前端代码质量与团队协作终极指南 👍

下面也对本文全篇涉及要点做个简要总结:

  1. 使用 editor config 完成跨编辑器平台的格式化约定;
  2. 使用 prettier 做基本格式化约定; 也可略去 prettier 使用,全面拥抱 eslint
  3. 使用 eslint 处理 JS 、TS 文件的代码合法性校验及格式化工作; 如果协同 prettier 格式化,则需做好兼容性处理,完整接管 prettier 格式化/预警报错规则。
  4. 安装 eslint 、 prettier 配套的 vscode 扩展,让代码合法性校验及格式化工作更加便捷。
  5. 使用 husky 介入 gitHook,实现提交门禁。
  6. 依赖 lint-staged,处理提交时的:提交代码的合法性检查。
  7. 依赖 @commitlint,处理提交时的:提交信息的规范性检查。
  8. 显式声明引擎版本,利于接手维护。

延伸

ignore 文件下的“反选”

一般的,各配置文件都有着自己配套的 ignore 文件,过滤掉指定文件群。比如:

  • eslint 下: .eslintignore 文件。
  • prettier 下: .prettierignore 文件。

但如果有对:比如配置文件本身、或其他 eslint、 prettier 原本默认过滤的文件,也有格式化的需求:则可以如下反选声明:

.prettierignore
# 约定 | 为了对如下的几个配置文件也适用 prettier 的约定客制化格式
!.eslintrc.cjs
!.prettierrc.cjs
!.lintstagedrc.cjs
!.commitlintrc.cjs

为项目声明一份“推荐扩展”

以编辑器 vscode 为例,在编辑器本身的 .vscode/ 配置目录下,除了推荐同步向远程仓库的 settings.json 文件。我们也可以初始化并同步 extensions.json 文件,旨在声明该项目推荐/或必要安装的扩展清单:

参考例:

extensions.json
{
  "recommendations": [
    "esbenp.prettier-vscode",
    "dbaeumer.vscode-eslint",
    "bradlc.vscode-tailwindcss",
    "unifiedjs.vscode-mdx",
    "yoavbls.pretty-ts-errors",
    "editorconfig.editorconfig",
  ]
}

显示效果如下:

项目推荐扩展声明

配置独立发包

可以考虑对项目下 eslint 的配置文件二次封装为 npm 包,增强不同项目间的复用性。如下的风格参考:

  1. @liuli-util/eslint-config-ts

  2. @antfu/eslint-config

关注编辑器扩展是否正常工作

该问题的具体举例:比如:settings.json 下配置了文件保存时即刻使用 prettier 格式化当前文件, ——但是发现并未生效。

(或者生效了,但实际是 extension@eslint 接管了 extension@prettier 的格式化工作,所以保存文件动作下,extension@prettier 扩展实际一直处于静默状态(?存疑)。我们可以手动调用 extension@prettier,尝试让其工作 :使用 vscode 的 Ctrl+Shift+P 呼出菜单下的Format Document 菜单项,如此操作下表现在 vscode 编辑器界面上,就是底部状态栏的扩展 prettier 一栏被高亮标红,点击可查看该扩展的报错信息)

此时不一定是自己的配置出了问题,而可能就简单的是: lib@prettier 版本,和 extension@prettier 的版本存在不兼容问题,也就导致了在 settting.json 下声明的extension@prettier 相关配置最终失效。

配置未生效或仍然是旧有配置

某些情况下,如果 eslint / prettier 的配置未生效或校验及格式化使用的仍然是旧有配置,可尝试 Reload Window 命令重启编辑器窗口,(重启extension@eslintextension@prettier 等扩展)

THX

😘 强烈的爱与支持来自瓷器: 前端代码质量与团队协作终极指南

管控前端代码质量,必要配置一览

https://blog.ninoh.cc/blog/a6-frontend-code-quality[Copy]
本文作者
ninohx96
创建/发布于
Published On
更新/发布于
Updated On
许可协议
CC BY-NC-SA 4.0

转载或引用本文时请遵守“署名-非商业性使用-相同方式共享 4.0 国际”许可协议,注明出处、不得用于商业用途!分发衍生作品时必须采用相同的许可协议。