Prettier 与 Linter 的分工边界:格式化交给 Prettier,Bug 检测交给 Linter
【免费下载链接】prettierPrettier is an opinionated code formatter.项目地址: https://gitcode.com/gh_mirrors/pr/prettier
Prettier 是"有主见"(opinionated)的代码格式化器,它从不试图替代 ESLint、TSLint、stylelint 等 linter,两者分属完全不同的职责。本文基于仓库中 comparison.md(即网站版本化文档 website/versioned_docs/version-stable/comparison.md)展开,先拆解 linter 规则的两大类别,再从 Prettier 源码层面解释"从头重印整个程序"为何能让格式化规则彻底"失业",最后给出--check落地 CI 以及与 ESLint/stylelint 集成的最佳实践。读完你将明确团队中 Prettier 与 linter 各自该管什么、如何配置才不会互相冲突。
Linter 规则的两大类别
文档开篇就给出了一个非常清晰的框架:linter 的规则可以分成两类,而 Prettier 只与其中一类发生关系。
格式化规则(Formatting rules)
这类规则关心的是"代码长得怎么样",例如:
max-len:限制单行最大长度;no-mixed-spaces-and-tabs:禁止混用空格与制表符缩进;keyword-spacing:统一关键字(如if、return)两侧的空格;comma-style:统一逗号的书写位置(行尾还是行首)。
Prettier 让这一整类规则全部变得不再必要。因为 Prettier 会把整个程序从头重新打印一遍(reprint the entire program from scratch),并以完全一致的方式输出,程序员根本不可能再在这种地方犯错——空格放错位置、换行不一致、逗号风格不统一这类问题,在 Prettier 的模型里压根不会产生。
这里的关键在于 Prettier 的定位:
Prettier is not a rule-based tool: it has no individual formatting rules that you can enable or disable. It reprints your whole program from scratch instead.
Prettier 不是基于规则的工具,它没有一条条可以单独开启或关闭的格式化规则。这也正是它与其他所有"样式检查器"在架构层面最根本的分野。
代码质量规则(Code-quality rules)
这类规则关心的是"代码有没有问题",例如:
no-unused-vars:发现声明了却从未使用的变量;no-extra-bind:检测多余的bind调用;no-implicit-globals:防止在全局作用域意外创建隐式全局变量;prefer-promise-reject-errors:要求Promise.reject必须传Error对象。
Prettier 对这类规则毫无帮助,也无意帮助。文档明确指出,这类规则恰恰是 linter 提供的最重要的能力——因为它们很可能真的帮你抓到代码里的 Bug。格式化类规则抓不到逻辑错误,而代码质量规则能。
所以结论可以浓缩成一句话:
用 Prettier 管格式化,用 linter 抓 Bug!
"从头重印"不是口号:看 Prettier 源码里的真实管线
要理解为什么 Prettier 能让格式化规则"集体失业",最好的方式是从源码看它到底是怎么工作的。Prettier 的格式化主流程位于 src/main/core.js 的coreFormat函数,其核心三步恰好就是"从头重印"的实现证据:
- 解析:
parseText(originalText, opts)将源码解析成抽象语法树(AST); - 转 Doc:
printAstToDoc(ast, opts)遍历 AST,把它转换成一种称为 Doc 的中间表示。Doc 由 src/document/builders/ 目录下的group、indent、line、if-break等构建器拼装而成,它描述的是"这段代码在不同宽度下应该怎么断行、怎么缩进"; - 打印:
printDocToStringWithoutNormalizeOptions(doc, opts)由 src/document/printer/printer.js 负责,把 Doc 最终渲染成字符串输出。
注意第三步的输入不再是你写的原始文本,而是由 AST 生成的 Doc。换句话说,你的输入只是被"解析"后当作语义来源,最终输出的样式完全由 Prettier 的打印器决定——这就是文档所说"reprints your whole program from scratch"的落地实现。
由此可以引申出几个推论:
- 只要 AST 相同,写法千奇百怪的代码都会被重印成同一种样子,因此
max-len、keyword-spacing、comma-style这类规则永远"无案可查"; - Prettier 没有"规则开关"的概念,因为它根本没有逐条生效的规则表——整个打印策略是一体的,这也决定了你无法只保留某一种格式化行为而关闭另一种;
- 正因为是整体重印,Prettier 能保证输出再次格式化后保持不变(幂等性)。CLI 的
--debug-check正是利用这一性质做自检:它会把结果再格式化一遍并与第一次输出比较,若不一致即报错(见 src/cli/format.js)。
代码质量检测:Linter 不可替代的职责
既然 Prettier 对代码质量规则"什么都不做",那么 lint 仍然是你工程体系里的刚需。no-unused-vars能抓住删除重构后遗留的死变量,no-extra-bind能提示不必要的性能开销,prefer-promise-reject-errors能阻止吞掉错误堆栈的坏习惯——这些都是格式化工具永远无法提供的能力。
文档对此的措辞非常坚定:代码质量规则是 linter 提供的最重要的规则,因为它们"likely to catch real bugs"。所以不要试图用 Prettier 的配置文件去"模拟"这些检查,Prettier 的选项空间里也根本没有它们的位置——这恰好与 option-philosophy.md 中"Prettier 选项已被冻结、不再接受新增选项请求"的原则一致。格式化争议应该被消除,而不是被转移到"选哪个选项"上;而 Bug 检测则始终是 ESLint 这类工具的专属战场。
落地实践:CI 中如何用--check守好格式化这条线
分工清楚了,剩下的问题就是怎么执行。在 CI 里,你不应该让 Prettier"改"代码,而应该让它"检查"代码并让构建失败——这正是prettier --check的用途,配合prettier --write在本地一键修复。
以仓库中的 CLI 实现为例,src/cli/format.js 的listDifferent函数会调用prettier.check(input, options),而 API 层的实现极简,见 src/index.js:
async function check(text, options) { return (await format(text, options)) === text; }即"格式化后与原文本是否完全相等"。由此可总结出--check的完整行为特征(依据 src/cli/format.js):
| 场景 | CLI 输出 | 进程退出码 |
|---|---|---|
| 所有文件都已符合 Prettier 风格 | All matched files use Prettier code style! | 0 |
| 存在未格式化的文件 | 逐行列出文件名,并提示Code style issues found in X files. Run Prettier with --write to fix. | 1 |
| 存在解析错误 / 无法识别的文件 | 输出错误信息 | 2 |
基于此,一个典型的 CI 检查命令是:
npx prettier --check .而本地修复则运行:
npx prettier --write .配合.prettierignore(见 ignore.md)排除不需要格式化的目录,以及 configuration.md 中的配置文件(如.prettierrc或prettier.config.js),Prettier 就能在你打开 PR 之前把格式问题全部拦截在本地。更完整的 CLI 参数说明可参考 cli.md。
与 Linter 的三种集成姿势
文档在 integrating-with-linters.md 中系统梳理了让 Prettier 与 linter 共存的三种方案,按推荐程度排序:
1. 官方推荐:eslint-config-prettier(关闭冲突规则)
linter 里绝大多数风格类规则在使用 Prettier 后不仅多余,更糟糕的是它们可能与 Prettier 冲突——Prettier 刚把代码重印成自己的风格,linter 的样式规则可能又判它违规,形成互相打架的死循环。eslint-config-prettier的作用就是一次性关闭所有与 Prettier 冲突或已无必要的规则。这是官方推荐路线,配置一次即可长期省心。
2. 不推荐但存在:eslint-plugin-prettier / stylelint-prettier(把 Prettier 当 lint 规则跑)
这类插件让你在 linter 内部"以 lint 规则的形式"运行 Prettier。在 Prettier 诞生初期,这种方案可以复用已有的 linter 编辑器集成、不用搭新基础设施,确实有用;但如今可以直接运行prettier --check .,且大多数编辑器已原生支持 Prettier,这类插件的价值已经大打折扣。文档明确指出它们的缺点:
- 编辑器里会出现大量红色波浪线,非常烦人——Prettier 的本意是让你忘记格式化的存在,而不是成天提醒你;
- 比直接运行 Prettier 慢;
- 多了一层间接层,多了一个可能出错的地方。
3. 兜底方案:prettier-eslint / prettier-stylelint(先格式化再 lint)
这类工具先跑prettier,紧接着对输出再跑eslint --fix。如果你对 Prettier 输出的某些方面实在无法接受,可以用eslint --fix再修一轮。缺点是这类工具比单纯运行 Prettier慢得多,应视为特殊情况下的兜底而非常规路线。
总结:一条清晰的分工边界
回到 comparison.md 的核心结论:Prettier 负责格式化,linter 负责抓 Bug。
- 格式化规则(
max-len、no-mixed-spaces-and-tabs、keyword-spacing、comma-style等):交给 Prettier,从源码管线看,它会解析成 AST、生成 Doc 并整体重印输出,让你无从犯错; - 代码质量规则(
no-unused-vars、no-extra-bind、no-implicit-globals、prefer-promise-reject-errors等):交给 linter,因为它们能抓到真实 Bug; - 落地时用
prettier --check进 CI、prettier --write修本地,用eslint-config-prettier关闭冲突的风格规则,避免两套工具互相打架。
这条边界背后,是 Prettier "选项越少越好、格式化战争越少越好"的 option-philosophy.md 设计哲学,以及"为团队终结无休止的风格争论"的初心(见 why-prettier.md)。理解了它,你就理解了 Prettier 在整套工程质量体系中的准确位置——它不抢 linter 的饭碗,它只是让 linter 更专注地去做真正重要的事。
【免费下载链接】prettierPrettier is an opinionated code formatter.项目地址: https://gitcode.com/gh_mirrors/pr/prettier
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考