ESLint、Prettier 与 Stylelint:从本地格式化到可治理质量门禁
一次保存为什么会改三遍
你接手一个前端仓库,修了两行业务代码,保存后却出现几十行 diff:Prettier 改了换行,ESLint 又把引号改回来,Stylelint 对 Vue 单文件组件报出一串解析错误。开发机提交前没有任何提示,CI 却在另一个文件上失败。再看依赖树,编辑器使用全局 Prettier,项目使用另一版 ESLint,Stylelint 的 glob 根本没有命中组件目录。
这不是“多装几个插件就更严格”,而是三套执行链互相越界。Prettier 负责确定性排版;ESLint 在 JavaScript、TypeScript 及插件支持的语法上发现可疑模式;Stylelint 检查 CSS 和经过正确语法适配的样式代码。编辑器只负责把反馈提前,本机命令负责复现,CI 才负责裁决,三处都必须读取仓库里的同一组版本、配置和入口。
先在项目根目录确认运行时与工作区状态:
node --version
npm --version
git status --shortNode.js 版本应由项目版本文件或 CI 镜像锁定。当前 ESLint 入门页列出的 Node.js 支持组合会随主版本变化;插件、解析器和共享配置还有各自的 peer dependency。真正可用的基线不是“某台机器安装成功”,而是干净安装后依赖图无冲突,三条检查都能在 CI 使用的 Node.js 上启动。
保存改造前的可复现证据:
npm ci
npm test
git status --short如果仓库还没有 lockfile,先确定唯一包管理器并生成 lockfile。临时全局安装无法证明开发机和 CI 使用同一套工具,也无法可靠回滚。
真正的运行单元是“项目依赖 + lockfile + 仓库配置 + package scripts”。容器若执行了不同命令、挂载了不同目录或重新解析依赖,结果仍然会漂移。ESLint 的 flat config、Prettier 的配置查找和 Stylelint 的配置对象也不是同一种合并算法,不能因为文件名相似就套用同一套直觉。
在升级 PR 和流水线制品中记录实际版本与解析结果:
npm exec -- eslint --version
npm exec -- prettier --version
npm exec -- stylelint --version
npm ls eslint prettier stylelint新项目先安装核心工具、官方推荐配置和用于关闭 ESLint 排版冲突的配置:
npm install --save-dev --save-exact eslint @eslint/js eslint-config-prettier prettier stylelint stylelint-config-standard这里没有故意写死版本号。团队首次引入时应在变更分支选择当日校准并验证过的版本,让 package.json 与 lockfile 一起进入评审;后续 CI 使用 npm ci,不能每次重新解析一个浮动依赖图。
ESLint 可用官方初始化入口生成起点:
npm init @eslint/config@latestStylelint 也提供初始化入口:
npm create stylelint@latest初始化器会根据回答生成配置,但生成结果不是团队标准本身。提交前仍要审查匹配范围、插件来源、规则集、忽略文件和脚本。如果项目已有配置,不要反复运行初始化器覆盖它。
先用 npm ls 证明依赖已经由 lockfile 安装,再使用 npm exec -- 调用本地解析到的命令。不要在缺少项目依赖时把临时下载当成安装入口:
npm ls eslint prettier stylelint
npm exec -- eslint --version
npm exec -- prettier --version
npm exec -- stylelint --version若包管理器不支持相同参数,就使用它提供的本地执行命令,例如 npm exec 或 pnpm exec。关键不是命令长相,而是禁止工具缺失时静默下载一个未经锁定的新版本。
卸载或回退也必须通过依赖清单完成,而不是手工删除 node_modules 中某个目录:
npm uninstall eslint @eslint/js eslint-config-prettier prettier stylelint stylelint-config-standard
npm ci如果同一次引入还增加了 typescript、typescript-eslint 或语法插件,也要删除本次变更新增的依赖;不要误删项目原本就需要的 TypeScript 编译器。正式回退应撤销引入工具链的整组变更,让 package.json、lockfile、eslint.config.mjs、Prettier 与 Stylelint 配置、ignore 文件和 package.json 脚本一起恢复,再执行 npm ci 和原有构建。只卸载包却留下配置导入,下一次编辑器启动或 CI 扫描仍会因找不到模块而失败。
先划清职责
三者的稳定分工如下:
| 工具 | 主要输入 | 负责什么 | 不负责什么 |
|---|---|---|---|
| Prettier | 支持的源码与文档 | 缩进、换行、引号等确定性排版 | 缺陷、安全、复杂语义 |
| ESLint | JS / TS 及插件支持的代码 | 语义规则、可疑模式、团队代码约束 | 全项目统一排版 |
| Stylelint | CSS 及 custom syntax 支持的样式 | CSS 正确性、选择器和样式约束 | JS 语义和通用排版 |
同一件事只交给一个工具。若 ESLint 或 Stylelint 的格式类规则与 Prettier 冲突,应关闭冲突规则,而不是调整保存顺序来掩盖。Prettier 官方建议在 ESLint 体系中使用 eslint-config-prettier 消除冲突;它是关闭冲突规则的配置,不会代替 Prettier执行格式化。
ESLint flat config
一个保守的 eslint.config.mjs 起点如下:
import { defineConfig } from "eslint/config";
import js from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier/flat";
export default defineConfig([
{
ignores: ["dist/**", "coverage/**", "generated/**"],
},
js.configs.recommended,
{
files: ["src/**/*.{js,mjs,cjs}"],
rules: {
"no-unused-vars": "error",
"no-undef": "error",
},
},
eslintConfigPrettier,
]);这里的关键是覆盖顺序:关闭冲突规则的对象必须位于会启用这些规则的配置之后。可用 npm exec -- eslint-config-prettier src/app.js 检查仍在冲突的规则。
Flat config 是有顺序的配置对象数组。files 决定对象适用于谁,ignores 决定排除谁,后续对象可能覆盖前面的规则。不要把它理解成把 .eslintrc 换个文件名。
迁移旧项目时可以使用:
npx @eslint/migrate-config .eslintrc.json迁移器只生成起点,尤其不能完整保留 .eslintrc.js 中的动态函数和条件逻辑。还要复核 .eslintignore、dotfile、eslint-env 注释、旧 CLI 参数、共享配置和插件兼容性。当前主版本已不支持旧配置系统,不应把长期设置环境变量继续使用旧格式当作迁移方案。
TypeScript 类型信息不是免费附赠
只把 *.ts 加进 ESLint glob,并不会自动获得类型级规则。TypeScript 项目还要精确锁定 typescript 与 typescript-eslint:
npm install --save-dev --save-exact typescript typescript-eslint在 flat config 中启用类型感知规则:
import { defineConfig } from "eslint/config";
import tseslint from "typescript-eslint";
export default defineConfig({
files: ["src/**/*.ts"],
extends: [tseslint.configs.recommendedTypeChecked],
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
});projectService: true 会为每个文件寻找最近的 tsconfig.json,并使用与编辑器相近的 TypeScript Project Service 建立类型信息。代价接近一次类型检查:大仓库若同时让多个 package 并发执行,CPU 与内存会成倍放大。出现“文件未被 project service 找到”时,先检查该文件是否真的属于最近的 tsconfig.json;不要用宽泛 allowDefaultProject 把整个仓库塞进默认项目。官方的类型感知 Lint 指南也明确提醒这部分性能成本。
Prettier 配置与忽略
Prettier配置应尽量少:
{
"semi": true,
"singleQuote": false,
"trailingComma": "all"
}将其保存为 .prettierrc.json,同时创建 .prettierignore:
dist/
coverage/
generated/
vendor/
*.min.jsPrettier 可以读取部分 .editorconfig 属性,但项目 Prettier 配置优先。不要同时在个人 IDE、.editorconfig、CLI 参数和 .prettierrc 中重复定义不同值。格式选择进入仓库配置,CLI 只决定“检查还是写入”。
Stylelint 配置与语法边界
普通 CSS 项目可以从官方标准配置起步:
// stylelint.config.mjs
/** @type {import("stylelint").Config} */
export default {
extends: ["stylelint-config-standard"],
ignoreFiles: ["dist/**", "coverage/**", "generated/**"],
reportDescriptionlessDisables: true,
reportInvalidScopeDisables: true,
reportNeedlessDisables: true,
reportUnscopedDisables: true,
};SCSS、Vue SFC、CSS-in-JS 不能只把文件后缀加入 glob。它们通常需要相应共享配置、插件或 customSyntax,并通过 overrides 限定文件范围。解析器选错时,得到的可能是大量伪语法错误,也可能是部分代码根本没有被检查。
大量忽略项优先放入 .stylelintignore:
dist/
coverage/
generated/
vendor/配置中的 ignoreFiles 适合少量明确排除;官方指出大量忽略使用 .stylelintignore 更高效。无论使用哪种方式,都要审查 glob 是否意外排除了整个源码目录。
--version 只能证明二进制能启动。门禁必须同时证明“坏样例会失败”和“修复后会恢复”,否则 glob 为空、配置未加载或 CI 忽略退出码都可能制造假绿。先创建一个不会提交的实验目录:
mkdir -p .quality-smoke写入三份独立配置,避免正式项目的 glob 让实验样例漏检。
// .quality-smoke/eslint.config.mjs
export default [
{
files: ["**/*.js"],
rules: {
"no-unused-vars": "error",
},
},
];// .quality-smoke/stylelint.config.mjs
export default {
rules: {
"color-no-invalid-hex": true,
},
};将下面内容保存为 .quality-smoke/.prettierrc.json:
{"semi":true,"singleQuote":false}再写入三个故意失败的输入文件:
// .quality-smoke/bad.js
const unused = 1/* .quality-smoke/bad.css */
.demo { color: #12zz34; }{"name":"quality-smoke","items":[1,2,3]}在仓库根目录依次验证。下面的脚本会暂时关闭 errexit,逐项保存三个工具的状态,再恢复调用方原有的 set -e 状态;因此第一条预期失败不会提前终止流水线:
restore_errexit=0
case $- in
*e*) restore_errexit=1; set +e ;;
esac
npm exec -- eslint --config .quality-smoke/eslint.config.mjs .quality-smoke/bad.js
eslint_rc=$?
npm exec -- stylelint --config .quality-smoke/stylelint.config.mjs ".quality-smoke/bad.css"
stylelint_rc=$?
npm exec -- prettier --config .quality-smoke/.prettierrc.json ".quality-smoke/*.{js,css,json}" --check
prettier_rc=$?
if [ "$restore_errexit" -eq 1 ]; then set -e; fi
printf 'eslint=%s stylelint=%s prettier=%s\n' \
"$eslint_rc" "$stylelint_rc" "$prettier_rc"
if [ "$eslint_rc" -eq 0 ] || [ "$stylelint_rc" -eq 0 ] || [ "$prettier_rc" -eq 0 ]; then
printf '%s\n' '反向实验失败:至少一个门禁没有拦住故意违规输入' >&2
exit 1
fi三个状态都必须非零:ESLint 报告 no-unused-vars,Stylelint 报告 color-no-invalid-hex,Prettier 列出未格式化文件。脚本自身只有在三个门禁都拦住坏样例时才返回 0;若任一工具返回 0,先检查输入是否命中、配置是否加载和调用脚本是否吞掉退出码,不能继续把它接入 CI。
然后只执行工具能够确定的变换:
npm exec -- prettier --config .quality-smoke/.prettierrc.json ".quality-smoke/*.{js,css,json}" --write
npm exec -- eslint --config .quality-smoke/eslint.config.mjs .quality-smoke/bad.js --fix
npm exec -- stylelint --config .quality-smoke/stylelint.config.mjs ".quality-smoke/bad.css" --fixunused 和非法颜色都涉及语义,工具不应为追求绿灯猜测业务意图;--fix 后仍失败是正确证据。手工删除未使用变量,把颜色改为 #123334,再重新检查:
restore_errexit=0
case $- in
*e*) restore_errexit=1; set +e ;;
esac
npm exec -- prettier --config .quality-smoke/.prettierrc.json ".quality-smoke/*.{js,css,json}" --check
prettier_rc=$?
npm exec -- eslint --config .quality-smoke/eslint.config.mjs .quality-smoke/bad.js
eslint_rc=$?
npm exec -- stylelint --config .quality-smoke/stylelint.config.mjs ".quality-smoke/bad.css"
stylelint_rc=$?
if [ "$restore_errexit" -eq 1 ]; then set -e; fi
printf 'eslint=%s stylelint=%s prettier=%s\n' \
"$eslint_rc" "$stylelint_rc" "$prettier_rc"
if [ "$eslint_rc" -ne 0 ] || [ "$stylelint_rc" -ne 0 ] || [ "$prettier_rc" -ne 0 ]; then
printf '%s\n' '正向实验失败:修复后的输入仍未通过全部门禁' >&2
exit 1
fi此时三个状态都必须为 0,脚本才返回 0。正向和反向证据共同证明配置被加载、输入被命中、修复边界正确、退出码能传到调用方。
最后确认工具只改动预期文件并清理:
git diff -- .quality-smoke
rm -rf .quality-smoke
git status --shortWindows PowerShell 可使用 Remove-Item -LiteralPath .quality-smoke -Recurse。验证目录必须经过绝对路径或工作区边界确认后再递归删除,不能把变量为空的清理命令带进团队脚本。
可修复不等于应该在 CI 自动写回,不可修复也不等于工具失效。CI 只读检查,修复 diff 必须由开发者或独立机器人 PR 接受评审。
把所有人需要记住的命令收敛到 package.json:
{
"scripts": {
"format": "prettier . --write",
"format:check": "prettier . --check",
"lint:js": "eslint . --max-warnings 0",
"lint:js:fix": "eslint . --fix",
"lint:style": "stylelint \"**/*.{css,scss,vue}\" --max-warnings 0",
"lint:style:fix": "stylelint \"**/*.{css,scss,vue}\" --fix",
"quality": "npm run format:check && npm run lint:js && npm run lint:style"
}
}示例 glob 不是所有项目的标准答案。纯 CSS 项目不应扫描不存在的 SCSS / Vue;Vue 或 SCSS 项目必须先安装并配置对应语法支持。引号必须保留,让 glob 由工具处理,减少 Windows、Linux 和不同 shell 的展开差异。
本机执行:
npm run qualityCI 执行同一入口:
npm ci
npm run quality不要在 CI 里另写一套 npx eslint src --fix。CI 使用 --fix 会让工作区与提交内容不同,绿灯也无法证明仓库本身合规。修复命令留给开发者显式执行,CI 只读检查并保存失败报告。
编辑器也必须使用工作区版本和仓库配置。以 VS Code 为例,团队可以提交最小工作区设置,让保存时明确选择格式器,并让 ESLint / Stylelint 扩展处理各自语言;但具体扩展设置应按扩展官方文档校准,不能假设所有 IDE 都读取相同键。
一致性的判断标准不是“大家都打开保存修复”,而是:
关闭所有编辑器后,npm ci && npm run quality 仍得到同样结果。编辑器执行的二进制来自项目依赖,而非全局安装。保存操作不会让 ESLint、Stylelint 和 Prettier循环修改同一段代码。
本机与 CI 的 Node.js、包管理器、lockfile 和命令一致。任意失败都能由开发者在提交前用一条仓库脚本复现。
查看最终配置
当规则来源不明时,不要先猜是哪一个插件:
npx eslint --print-config src/app.js
npx stylelint --print-config src/styles/app.cssESLint 还提供配置检查入口:
npx eslint --inspect-config这些输出适合排查覆盖顺序,不宜长期提交,因为它们会随依赖升级变化。
分离检查与修复
npm run format:check
npm run lint:js
npm run lint:style
npm run format
npm run lint:js:fix
npm run lint:style:fix先运行检查可以知道问题属于哪层。直接连续执行所有修复虽然省事,却容易把大面积无关格式变化混入业务 PR。
只检查改动文件
提交前 Hook 可以只处理暂存文件以缩短反馈时间,但它不是最终门禁。文件重命名、配置变化、跨文件规则和未被暂存的生成物都可能让“只检查改动”漏掉问题。CI 至少应在合并请求上运行完整 npm run quality。
管理禁用注释
ESLint 可通过 linter options 报告无用禁用指令;Stylelint 提供以下治理参数:
npx stylelint "**/*.css" \
--report-descriptionless-disables \
--report-invalid-scope-disables \
--report-needless-disables \
--report-unscoped-disables禁用应精确到规则和最小行数,并带原因。整文件 eslint-disable 或无规则名的 stylelint-disable 应视为需要审计的例外,而不是消除噪声的快捷键。
检查依赖解析
npm ls eslint prettier stylelint
npm explain eslint
npm explain stylelint看到 invalid、peer dep missing 或多套不兼容主版本时,应先解决依赖图,再分析规则结果。
保存后格式反复变化
保存时引号、换行或分号先被一种工具修改,随后又被另一种工具改回。
分别关闭保存格式、ESLint fix 和 Stylelint fix,再单独运行三个 package script;检查是否有两个工具负责同一格式规则。
职责重叠、编辑器使用个人配置,或没有启用关闭冲突规则的共享配置。
让 Prettier独占确定性排版,关闭 lint 中冲突的格式规则;编辑器只读取工作区工具和仓库配置。
连续保存两次后 git diff 不再变化,npm run quality 通过。
本机通过、CI 失败
开发机没有告警,CI 出现不同规则、不同 glob 或格式差异。
对比 node --version、工具 --version、npm ls、lockfile、当前工作目录和实际执行脚本。
本机用全局版本、CI 使用浮动安装、shell 展开 glob 不同、大小写或换行符差异、编辑器只检查打开文件。
锁定 Node.js 和依赖,CI 使用 npm ci,glob 加引号,三处执行同一 package script。
在干净目录重新检出后执行 npm ci && npm run quality。
文件没有被检查
故意加入明显错误,命令仍然通过。
使用 --print-config 检查目标文件;检查 files、ignores、.prettierignore、.stylelintignore 和输入 glob。
flat config glob 不匹配、忽略范围过宽、CSS 扩展语法未配置 custom syntax,或 CI 从错误目录启动。
缩小并明确匹配模式,为不同语法使用 overrides;增加无害失败 smoke test。
故意失败必须被发现,移除失败后必须恢复通过。
插件升级后配置无法加载
出现规则不存在、配置导入失败、ESM / CommonJS 错误、旧规则 API 报错或 peer dependency 冲突。
查看官方迁移文档、插件 peer dependency、npm ls 和配置模块类型。
核心工具、插件、解析器、共享配置、Node.js 不是独立升级单元;主版本改变了 API 或模块系统。
恢复经过验证的依赖组合,在独立升级分支逐项更新。兼容工具只能作为短期过渡,不能永久掩盖无人维护的插件。
重新安装依赖,运行 smoke test、全量质量检查和项目测试。
--fix 后仍失败
命令改了一部分文件,但退出状态仍非零。
读取剩余规则 ID 和具体位置,不要重复盲跑 --fix。
部分问题涉及业务语义,工具无法安全决定修复方式;也可能是修复后触发了另一条规则。
人工理解问题并修改,随后重新运行完整检查。
确认 diff 只包含预期变化,测试与 npm run quality 同时通过。
三者运行时通常不需要业务凭证,但安装依赖可能经过 npm 私服、企业代理和内部 CA。代理应由包管理器和 CI 环境统一配置,不要把带用户名、密码或访问令牌的 registry URL 写入文章、共享配置或流水线日志。
更重要的风险来自代码执行权限。以下对象都不是“纯数据”:
eslint.config.js、prettier.config.js、stylelint.config.js 可以执行 JavaScript。ESLint 插件、解析器、processor 和共享配置是依赖代码。Prettier 插件会解析和打印源码。
Stylelint 插件、custom syntax、共享配置和 formatter 运行在当前进程权限下。
因此,在陌生仓库运行 npm install、npm run lint 或编辑器自动修复,不应被理解为无风险的文本检查。CI 最小权限建议是:
质量任务默认不注入发布 Token、云密钥和生产凭证。外部贡献或 Fork PR 在无 Secret 的隔离 Runner 上执行。使用 lockfile 和确定性安装,依赖升级由审查工具单独发起。
限制工作区写权限;CI 使用检查命令,不运行 --write 或 --fix。报告和日志可能包含源码片段、文件路径与内部规则名,按源码同级保护。
编辑器扩展也有工作区读取和进程执行能力。团队应维护允许列表或最低版本基线,禁止要求开发者安装来源不明的“规则修复增强插件”。
把规则变成工程合同
每条新增规则至少说明:规则 ID、解决的问题、默认严重度、是否可修复、适用文件、owner、误报申诉、临时豁免期限和升级策略。没有 owner 的规则最终只会积累禁用注释。
建议分三层管理:
错误:明确缺陷、危险模式和团队红线,CI 必须阻断。警告:迁移期或观察期规则,必须设置数量预算和收紧日期。关闭:不适用或与其他工具冲突,要记录关闭原因。
--max-warnings 0 适合已经清零的项目。历史项目若无法立即清零,不要偷偷取消门禁;先建立可审计基线,再逐步收紧。
历史基线不是永久忽略
引入新规则时先在报告模式测量:
npm run lint:js -- --format json --output-file artifacts/eslint.json
npm run lint:style -- --formatter json --output-file artifacts/stylelint.json
npm run format:check然后将问题分成三类:自动修复且 diff 可审查、需要人工判断、确属误报或暂不适用。基线至少记录规则、文件、数量、owner、原因和到期日。
推荐采用“新代码零新增、存量按批清偿”:
配置和依赖先进入只报告阶段。记录存量,不把整个目录加入 ignore。合并请求禁止新增违规。
按模块和 owner 清偿历史问题。到期复审豁免,清零后切换为全量阻断。
如果工具没有原生、可靠的基线模型,可以用精确禁用、按文件 override 或外部结果差分实现过渡,但必须可追踪、可到期、可删除。
升级与回滚
核心工具、插件、解析器、共享配置、Node.js 和编辑器扩展构成兼容矩阵。升级应独立成 PR:
阅读所有相关官方迁移文档和 release notes。保存升级前版本、最终配置、问题数量和耗时。更新 package.json 与 lockfile,不夹带业务功能。
删除受影响缓存;Prettier 官方明确说明插件版本不属于缓存键。运行无害失败 smoke test、全量质量检查、构建和测试。将自动修复与依赖升级分成可审查提交,避免规则变化淹没在格式 diff 中。
观察一段约定窗口,再提升规则严重度。
回滚入口必须在升级前就可用:恢复上一版依赖清单、lockfile 和配置,清理缓存后 npm ci,再运行相同验证。不要只降级核心工具却保留新版插件和配置,那不是回滚,而是制造新的未验证组合。
规则升级制造永久红灯
升级后数千条历史问题同时出现,团队开始使用 --no-verify、整目录 ignore 或全局 disable。
升级 PR 是否同时改变核心工具、共享配置和大量业务文件;是否有升级前后的按规则数量对比。
先报告、后阻断;新代码零新增,存量有 owner 和期限。不能以“CI 太吵”为由静默 fail-open,也不能一次性阻断所有存量导致门禁失去信用。
插件供应链进入高权限环境
只增加一个格式或 lint 插件,却在有发布 Token 的 Runner 上执行;插件维护状态、依赖树和来源无人审查。
检查插件是否为官方或社区项目、peer dependency、维护活跃度、安装脚本、锁文件变化和实际权限。
质量 Job 与发布 Job 隔离,不向 lint 注入高权限 Secret;插件进入准入清单,升级走独立 PR。来源不明或长期无人维护的插件宁可不引入。
IDE 绿灯掩盖真实门禁缺口
编辑器只检查打开文件,或扩展使用全局版本;开发者认为没有红线,CI 才发现问题。
关闭 IDE,在干净检出中运行仓库脚本;比较工具版本和最终配置。
IDE 只做反馈,不成为规则权威。新成员入场验证必须包含 npm ci && npm run quality,而不是只安装扩展。
自动修复污染业务变更
一个功能 PR 同时包含全仓格式化,评审无法区分语义变更和机械变换;回滚业务时又带回旧格式。
比较变更文件数量、规则修复类型和业务提交边界。
大规模修复独立提交并冻结短时间合并;先验证测试,再合并业务分支。CI 不自动写回源码,机器人修复也必须生成可审查 PR。
Ignore 与豁免成为不可见盲区
生成目录、迁移目录和整个旧模块被长期排除,新源码误落入这些路径后完全不检查。
定期对输入文件清单做抽样,使用故意失败验证关键目录;审查 ignore 和 disable 的新增 diff。
ignore 只排除真正不可维护的生成物,豁免精确到规则和文件并设置到期日。目录结构变化时同步复审三个工具的匹配范围。
回滚只恢复配置、不恢复依赖图
配置文件回退了,lockfile 仍保留新版插件,结果与升级前不同。
比较 package.json、lockfile、Node.js、工具版本和 npm ls,而不是只看配置 diff。
把“依赖清单 + lockfile + 配置 + 缓存清理 + 验证证据”视为一个回滚单元。若旧版本存在已知安全风险,应选择前滚修复或经批准的短期回退,并明确退出期限。
Prettier、ESLint、Stylelint 的职责没有重叠,格式规则只由一个工具负责。工具安装在项目本地,版本进入 package.json 和 lockfile。ESLint 使用当前 eslint.config.* 模型,没有遗留 .eslintrc* 依赖。
Stylelint 为 CSS、SCSS、Vue 或 CSS-in-JS 配置了正确语法边界。.prettierignore、.stylelintignore 和 ESLint ignores 已审查,没有误排源码。package scripts 明确区分检查与修复,CI 只运行检查入口。
无害失败实验能分别触发三个工具,并在修复后恢复通过。编辑器、本机和 CI 使用同一项目版本、配置与脚本。CI 使用确定性安装,质量 Job 不持有发布或生产凭证。
插件、解析器、共享配置和 custom syntax 已做来源与兼容性审查。warning、禁用注释、ignore 和历史基线都有 owner、原因与到期日。升级 PR 记录规则数量、最终配置、耗时、测试和缓存处理。
回滚能同时恢复依赖清单、lockfile、配置和缓存,并重新运行完整验证。报告和日志未泄漏真实凭证、内部路径或不应外传的源码片段。
