29 KiB
OXC 迁移计划
本文档记录将项目中可替代的工具链逐步迁移到 oxc 生态的计划。
当前现状
项目 ESLint 体系依赖约 20+ 个包,包括:
eslint^10.0.3@typescript-eslint/parser^8.57.0@typescript-eslint/eslint-plugin^8.57.0eslint-plugin-unicorn^63.0.0eslint-plugin-import-x^4.16.2eslint-plugin-unused-imports^4.4.1eslint-plugin-regexp^3.1.0eslint-plugin-jsdoc^62.8.0eslint-plugin-n^17.24.0eslint-plugin-no-only-tests^3.3.0eslint-plugin-perfectionist^5.6.0eslint-plugin-prettier^5.5.5eslint-plugin-vue^10.8.0eslint-plugin-better-tailwindcss^4.3.2eslint-plugin-jsonc^3.1.2eslint-plugin-yml^3.3.1eslint-plugin-command^3.5.2eslint-plugin-pnpm^1.6.0@eslint-community/eslint-plugin-eslint-comments^4.7.1@vitest/eslint-plugin^1.6.11vue-eslint-parser^10.4.0yaml-eslint-parser^2.0.0globals^17.4.0prettier^3.8.1esbuild^0.27.4html-minifier-terser^7.2.0
第一阶段:引入 oxlint,与 ESLint 共存
目标:将通用 JS/TS lint 规则交给 oxlint,ESLint 仅保留 oxlint 无法覆盖的特殊规则,大幅减少依赖数量并提升 lint 速度。
1.1 安装 oxlint
pnpm add -Dw oxlint
1.2 可移除的 ESLint 插件
以下插件与规则在 oxlint 中已有对应能力,但“有能力承接”不等于“现在就能删依赖”。这里区分为“已移除 / 已迁出 ESLint”和“仍需保留”两类:
| 插件 | 当前状态 | 说明 |
|---|---|---|
eslint-plugin-regexp |
已移除 | 由 oxlint 内建能力承接 |
eslint-plugin-jsdoc |
已移除 | 由 oxlint 内建能力承接 |
eslint-plugin-no-only-tests |
已移除 | 测试规则已迁到 oxlint / vitest |
eslint-plugin-better-tailwindcss |
已迁出 ESLint | 仍然保留依赖,但已通过 oxlint jsPlugins 运行 |
@typescript-eslint/eslint-plugin |
仍需保留 | 还有一批 TS 规则未迁移 |
@typescript-eslint/parser |
仍需保留 | ESLint 侧 TS / Vue 规则仍依赖它 |
eslint-plugin-unicorn |
仍需保留 | ESLint 侧仍使用其 recommended 规则补充 |
eslint-plugin-import-x |
仍需保留 | import/newline-after-import、import/no-webpack-loader-syntax 尚未迁移 |
eslint-plugin-unused-imports |
仍需保留 | no-unused-imports 仍由 ESLint 负责 |
eslint-plugin-n |
仍需保留 | 多条 Node 规则在 oxlint 中暂无等价承接 |
eslint-plugin-perfectionist |
仍需保留 | 排序规则策略不同,不建议强迁 |
@eslint-community/eslint-plugin-eslint-comments |
仍需保留 | 当前尚未迁移 |
1.3 必须保留的 ESLint 插件
以下插件 oxlint 目前不支持或不完全支持,需继续由 ESLint 处理:
| 插件 | 原因 |
|---|---|
eslint-plugin-vue + vue-eslint-parser |
Vue SFC 模板 lint,oxlint Vue 支持尚未完善 |
eslint-plugin-jsonc + eslint-plugin-yml |
JSON/YAML 文件 lint,oxlint 不支持 |
eslint-plugin-pnpm |
pnpm workspace 规则,oxlint 无对应 |
eslint-plugin-command |
魔法注释命令,oxlint 无对应 |
@vitest/eslint-plugin |
Vitest 特定规则(oxlint vitest 分类可能部分覆盖,需验证) |
yaml-eslint-parser |
YAML 解析,配合 eslint-plugin-yml 使用 |
eslint-plugin-prettier |
Prettier 集成(第二阶段替换 Prettier 后可移除) |
1.4 配置 oxlint
当前实现已调整为与 ESLint 一致的 workspace 包组织方式:
- 根目录使用 oxlint.config.ts 作为薄入口
- 实际规则配置位于
internal/lint-configs/oxlint-config - ESLint 兼容层通过
eslint-plugin-oxlint直接消费@vben/oxlint-config导出的配置对象,确保 oxlint 与 ESLint 共用同一份规则源
示例结构:
// oxlint.config.ts
import { defineConfig } from '@vben/oxlint-config';
export default defineConfig();
// internal/lint-configs/oxlint-config/src/index.ts
import { defineConfig as defineOxlintConfig } from 'oxlint';
import { mergeOxlintConfigs, oxlintConfig } from './configs';
export function defineConfig(config = {}) {
const { extends: extendedConfigs = [], ...restConfig } = config;
return defineOxlintConfig(
mergeOxlintConfigs(oxlintConfig, ...extendedConfigs, restConfig),
);
}
不再使用根目录 .oxlintrc.json。
1.5 当前已完成项
截至目前,第一阶段已完成以下工作:
- 根
lint已调整为先跑oxlint再跑vsh lint lefthook已接入oxlint --fix- 已新增
@vben/oxlint-config,并将 oxlint 规则集中到internal/lint-configs/oxlint-config eslint-plugin-oxlint已接入@vben/eslint-config- 已移除
eslint-plugin-jsdoc、eslint-plugin-regexp、eslint-plugin-no-only-tests better-tailwindcss已迁移到@vben/oxlint-config的jsPlugins方案unicorn插件已在 oxlint 侧显式启用,并补齐仓库内所需的 override- 当前
pnpm run lint:oxc、pnpm run lint:eslint、pnpm run lint均可通过
1.6 当前 oxlint 基线策略
当前 @vben/oxlint-config 采用“先收敛噪音,再逐步接管规则”的策略:
- 先启用
correctness和suspicious - 保留
import、node、typescript、unicorn、vitest、vue插件 - 通过
jsPlugins在 oxlint 侧承接better-tailwindcss - 对当前仓库内高噪音且不准备立即批量整改的规则先显式关闭
- 通过
overrides为.d.ts等特殊文件保留例外配置
1.7 已确认可迁移到 oxlint 的规则
以下规则已确认可由当前版本 oxlint 接管,后续应从 @vben/eslint-config 逐步迁移到 @vben/oxlint-config:
JavaScript / core
eqeqeqno-alertno-array-constructorno-callerno-case-declarationsno-control-regexno-debuggerno-empty-functionno-evalno-iteratorno-new-wrappersno-shadowno-shadow-restricted-namesno-unused-expressionsno-unused-varsprefer-const
import
import/consistent-type-specifier-styleimport/firstimport/no-duplicatesimport/no-mutable-exportsimport/no-named-defaultimport/no-self-import
TypeScript
@typescript-eslint/no-non-null-assertion@typescript-eslint/no-empty-function@typescript-eslint/no-shadow@typescript-eslint/no-unused-expressions@typescript-eslint/no-unused-vars@typescript-eslint/no-var-requires
@typescript-eslint/ban-ts-comment虽然 oxlint 已支持,但当前仓库中存在多处@ts-ignore,直接迁移会新增报错,暂不建议在这一阶段接管。
Vitest
vitest/no-focused-testsvitest/no-identical-titlevitest/prefer-hooks-in-ordervitest/prefer-lowercase-title
Node
n/no-exports-assignn/no-new-requiren/no-path-concat
Unicorn
unicorn/consistent-function-scopingunicorn/no-process-exitunicorn/prefer-global-thisunicorn/prefer-module
Tailwind CSS
better-tailwindcss/enforce-consistent-class-orderbetter-tailwindcss/enforce-consistent-line-wrappingbetter-tailwindcss/no-unknown-classes
Vue
vue/prefer-import-from-vue
1.8 暂时仍需保留在 ESLint 的部分
以下内容当前不建议从 ESLint 侧移除:
- Vue SFC/template 主体规则
perfectionistjsonc/ymlpnpmcommandprettiereslint-commentsunused-imports
其中 Vue 相关规则尤其需要保留,至少包括:
vue/block-ordervue/component-name-in-template-casingvue/component-options-name-casingvue/custom-event-name-casingvue/define-macros-ordervue/no-unused-refsvue/no-useless-v-bindvue/require-default-propvue/require-explicit-emitsvue/v-on-event-hyphenationvue/prefer-separate-static-classvue/prefer-template
1.9 推荐的下一步执行顺序
为降低回归风险,后续规则迁移建议按以下顺序推进:
import.tsjavascript.ts中剩余的低风险 1:1 规则typescript.tsnode.tstest.ts- 最后再评估
import.ts中剩余规则是否值得继续下沉 - 最后处理
vue.ts中的个别确认项
每迁移一组后都执行:
pnpm run lint:oxc
pnpm run lint:eslint
pnpm run lint
1.10 更新 scripts
// package.json
{
"scripts": {
"lint": "pnpm run lint:oxc && pnpm run lint:eslint",
"lint:oxc": "oxlint .",
"lint:eslint": "vsh lint",
},
}
1.11 更新 lefthook
在 pre-commit 中将 oxlint 加入暂存文件检查:
pre-commit:
commands:
lint-vue:
glob: '*.vue'
run: pnpm prettier --write {staged_files} && pnpm oxlint --fix {staged_files} && pnpm eslint --cache --fix {staged_files} && pnpm stylelint --fix --allow-empty-input {staged_files}
lint-js:
glob: '*.{js,jsx,ts,tsx}'
run: pnpm prettier --cache --ignore-unknown --write {staged_files} && pnpm oxlint --fix {staged_files} && pnpm eslint --cache --fix {staged_files}
1.12 验证步骤
- 安装 oxlint 后全量运行
pnpm lint:oxc,记录所有报告 - 对比当前 ESLint 规则覆盖情况,调整 oxlint 配置使两者对齐
- 逐个移除可替代的 ESLint 插件,每次移除后运行完整 lint 验证无遗漏
- CI 中同时运行 oxlint 和 ESLint,确保双重覆盖期间无回归
1.13 待修复问题与改进项
以下问题通过代码审查发现,按优先级排列。
[高] unicorn 插件启用后要同时收敛默认规则与 override
这个问题已经修复,但需要记录经验:
plugins一旦显式声明,必须手动包含unicorn- 启用后不只会打开我们手写的
unicorn/no-process-exit、unicorn/prefer-module - 还会带出 oxlint 默认启用的一部分
unicorn规则,需要按仓库现状显式关闭噪音项 - OXC
overrides.files不要继续使用?([cm])[jt]s?(x)这类 ESLint 风格的 extglob,当前仓库已改为显式文件后缀列表
[高] 使用 @oxlint/migrate 仅做审计,不直接落地配置
官方提供了 @oxlint/migrate 工具,可自动读取 ESLint flat config 生成对应 oxlint 配置:
也可以参考 oxlint-migration-inspector npx oxlint-migration-inspector 命令,查看哪些规则无法迁移。
# 查看哪些规则无法迁移
npx @oxlint/migrate --details
# 与已有配置合并(而非覆盖)
npx @oxlint/migrate --merge
对于当前仓库,@oxlint/migrate 更适合做“审计工具”,不适合直接生成或合并为最终配置,原因是:
- 当前仓库已经不是简单的
.oxlintrc.json形态,而是 workspace 包 + 模块化configs/* @vben/oxlint-config依赖自定义mergeOxlintConfigs(...)来保证settings、ignorePatterns、jsPlugins正确合并- Tailwind 规则依赖手写
jsPlugins和settings - 现有迁移流程还包含 ESLint 兼容层
eslint-plugin-oxlint
因此更合理的用法是:
# 只做对账,不直接采纳输出
npx @oxlint/migrate --details
不建议直接使用 --merge 覆盖当前仓库的真实配置结构。
[中] mergeOxlintConfigs 函数重复定义
mergeOxlintConfigs 在两处有几乎相同的实现:
oxlint-config/src/configs/index.ts— 包含jsPlugins合并逻辑eslint-config/src/configs/oxlint.ts— 缺少jsPlugins合并逻辑
这个问题已经修复。ESLint 侧的 oxlint.ts 已直接导入 @vben/oxlint-config 的 mergeOxlintConfigs,避免实现不一致:
// eslint-config/src/configs/oxlint.ts
import { mergeOxlintConfigs, oxlintConfig } from '@vben/oxlint-config';
import oxlint from 'eslint-plugin-oxlint';
export async function oxcCompat(): Promise<Linter.Config[]> {
return oxlint.buildFromOxlintConfig(mergeOxlintConfigs(oxlintConfig));
}
[中] 验证 oxcCompat() 是否正确关闭 ESLint 重复规则
ESLint 侧 javascript.ts 约有 70+ 条规则,很多已被 oxlint 的 correctness/suspicious category 覆盖(如 no-debugger、no-dupe-keys、no-unreachable 等),但 ESLint 侧并未手动关闭。
eslint-plugin-oxlint 的 buildFromOxlintConfig() 理论上会自动关闭 ESLint 中被 oxlint 覆盖的规则。需要验证 oxcCompat() 确实生效——如果正常工作,则 ESLint 侧无需手动清理这些规则。
验证方法:
# 临时在 oxlint 中 warn 一个 ESLint 也有的规则(如 no-debugger),
# 确认 ESLint 不再报告该规则
[中] eslint-plugin-import-x 迁移不完全
ESLint 侧 import.ts 仍保留 3 条规则:
| 规则 | 状态 |
|---|---|
import/newline-after-import |
未迁移到 oxlint,需检查是否已支持 |
import/no-unresolved |
已 off,无需处理 |
import/no-webpack-loader-syntax |
未迁移到 oxlint,需检查是否已支持 |
如果这两条规则被 oxlint 支持,则 eslint-plugin-import-x 可从 ESLint 依赖中完全移除。
[中] eslint-plugin-n 实际不能完全移除
1.2 表格中标记 eslint-plugin-n 为"可移除",但 ESLint 侧 node.ts 仍有以下规则未被 oxlint 接管:
可参考 https://github.com/oxc-project/oxc/issues/481 查看 Tasks 列表,oxlint 正在逐步接管 ESLint 的规则。
| 规则 | 备注 |
|---|---|
n/handle-callback-err |
oxlint 未支持 |
n/no-deprecated-api |
oxlint 未支持 |
n/no-extraneous-import |
有自定义 allowModules,oxlint 未支持 |
n/no-unsupported-features/es-syntax |
oxlint 未支持 |
n/prefer-global/buffer |
oxlint 未支持 |
n/prefer-global/process |
oxlint 未支持 |
n/process-exit-as-throw |
oxlint 未支持 |
结论:eslint-plugin-n 当前仍需保留在 ESLint 侧,1.2 表格需修正。
[低] 考虑启用 --type-aware 规则
ESLint 侧 typescript.ts 已配置 project: './tsconfig.*.json',即已使用 type-aware linting。oxlint 也支持 --type-aware 选项,启用后可迁移更多依赖类型信息的规则(如 @typescript-eslint/no-floating-promises 等),进一步减少对 ESLint TypeScript 插件的依赖。
# 测试 type-aware 模式
oxlint --type-aware --tsconfig tsconfig.json
[低] --replace-eslint-comments 批量转换注释
代码中存在的 // eslint-disable 注释虽然 oxlint 兼容,但在第四阶段移除 ESLint 后会变得语义不清。官方迁移工具提供了批量转换选项:
npx @oxlint/migrate --replace-eslint-comments
建议在第一阶段完成后或第四阶段开始前执行。
[低] jsPlugins 特性可减少 ESLint 依赖
oxlint 支持通过 jsPlugins 字段加载外部 ESLint 插件。对于 eslint-plugin-pnpm、eslint-plugin-command 等 oxlint 不内置的插件,未来可评估通过 jsPlugins 在 oxlint 中直接加载,从而减少对 ESLint 的依赖:
{
"jsPlugins": ["eslint-plugin-pnpm"],
"rules": {
"pnpm/some-rule": "warn"
}
}
第二阶段:oxfmt 替换 Prettier
前提:oxfmt 已发布,支持 JS/TS/Vue SFC 格式化,CSS/HTML/YAML/Markdown 等通过内部委托 Prettier 处理。
参考文档:oxfmt CLI | Config Reference
2.1 当前 Prettier 引用全景
迁移前需清楚 Prettier 在仓库中的所有触点:
| 位置 | 用途 | 迁移动作 |
|---|---|---|
internal/lint-configs/prettier-config/ |
@vben/prettier-config 包,导出共享配置 |
改写为 @vben/oxfmt-config |
.prettierrc.mjs |
根配置入口,export { default } from '@vben/prettier-config' |
替换为 .oxfmtrc.json |
.prettierignore |
格式化忽略文件 | 迁移到 .oxfmtrc.json 的 ignorePatterns |
eslint-config/src/configs/prettier.ts |
ESLint 中 eslint-plugin-prettier 集成 |
移除整个配置文件 |
eslint-config/src/index.ts |
引用 prettier() 配置 |
移除 prettier() 调用 |
eslint-config/package.json |
依赖 eslint-plugin-prettier |
移除该依赖 |
stylelint-config/index.mjs |
插件 stylelint-prettier,规则 prettier/prettier: true |
移除 stylelint-prettier 插件和规则 |
stylelint-config/package.json |
依赖 prettier、stylelint-prettier |
移除这两个依赖 |
internal/node-utils/src/prettier.ts |
prettierFormat() 函数,供构建工具调用 |
改写为调用 oxfmt |
internal/node-utils/package.json |
依赖 prettier |
替换为 oxfmt |
scripts/vsh/src/lint/index.ts |
prettier . --write --cache / prettier . --check --cache |
替换为 oxfmt / oxfmt --check |
lefthook.yml |
多处 pnpm prettier --write/--cache |
替换为 pnpm oxfmt |
pnpm-workspace.yaml catalog |
prettier: ^3.8.1、eslint-plugin-prettier、stylelint-prettier |
移除这三项 |
package.json 根 |
依赖 @vben/prettier-config |
替换为 @vben/oxfmt-config |
vben-admin.code-workspace |
workspace 引用 prettier-config |
更新 |
2.2 自动迁移配置
oxfmt 内置了从 Prettier 迁移的能力:
npx oxfmt@latest --migrate prettier
该命令会:
- 读取当前
.prettierrc.mjs(通过@vben/prettier-config解析) - 生成
.oxfmtrc.json - 将
.prettierignore的模式迁移到ignorePatterns - 如果检测到
prettier-plugin-tailwindcss会自动启用sortTailwindcss - 如果检测到
prettier-plugin-packagejson会启用sortPackageJson
注意:如果
.oxfmtrc.json已存在会报错,需先删除再重新运行。
2.3 配置对照与预期输出
当前 Prettier 配置(@vben/prettier-config):
{
endOfLine: 'auto',
printWidth: 80,
proseWrap: 'never',
semi: true,
singleQuote: true,
trailingComma: 'all',
overrides: [
{ files: ['*.json5'], options: { quoteProps: 'preserve', singleQuote: false } }
]
}
预期 oxfmt 配置(.oxfmtrc.json):
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"printWidth": 80,
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"proseWrap": "never",
"endOfLine": "lf",
"ignorePatterns": [
"dist",
"dev-dist",
".local",
".claude",
".agent",
".agents",
".codex",
".output.js",
"node_modules",
".nvmrc",
"coverage",
"CODEOWNERS",
".nitro",
".output",
"**/*.svg",
"**/*.sh",
"public",
".npmrc",
"*-lock.yaml",
"skills-lock.json"
],
"overrides": [
{
"files": ["*.json5"],
"options": { "quoteProps": "preserve", "singleQuote": false }
}
]
}
关键差异说明:
| Prettier 选项 | oxfmt 处理 |
|---|---|
endOfLine: 'auto' |
不支持 auto,需显式设为 "lf"(与 .editorconfig 的 end_of_line=lf 一致) |
printWidth: 80 |
直接映射(oxfmt 默认 100,需显式设 80 保持一致) |
overrides |
自动迁移不支持 overrides,需手动编写 |
2.4 @vben/oxfmt-config workspace 包
保持与 @vben/prettier-config 一致的 workspace 包组织模式:
internal/lint-configs/oxfmt-config/
package.json
index.mjs # 导出 oxfmt 配置对象(供程序化调用)
// internal/lint-configs/oxfmt-config/package.json
{
"name": "@vben/oxfmt-config",
"version": "5.6.0",
"private": true,
"type": "module",
"main": "./index.mjs",
"module": "./index.mjs",
"exports": {
".": { "default": "./index.mjs" }
},
"dependencies": {
"oxfmt": "catalog:"
}
}
注意:oxfmt 的配置文件
.oxfmtrc.json不像 Prettier 那样支持 JS 配置的export default。@vben/oxfmt-config包的作用是:
- 集中管理
oxfmt版本依赖- 导出配置对象供
internal/node-utils等程序化场景使用- 根目录直接使用
.oxfmtrc.json文件(oxfmt 会自动读取)
2.5 替换范围详表
| 当前依赖 | 替代方案 |
|---|---|
prettier ^3.8.1 |
oxfmt(JS/TS 原生格式化,CSS/HTML/YAML/MD 内部委托 Prettier) |
eslint-plugin-prettier |
移除(不再需要 ESLint-Prettier 集成) |
stylelint-prettier |
移除(Stylelint 不再需要 Prettier 集成) |
@vben/prettier-config |
改写为 @vben/oxfmt-config |
2.6 操作步骤
第一步:生成 oxfmt 配置
# 自动迁移(生成 .oxfmtrc.json)
npx oxfmt@latest --migrate prettier
# 手动补充 overrides(自动迁移不支持)
# 手动将 endOfLine: 'auto' 改为 "lf"
# 手动添加 ignorePatterns(从 .prettierignore 迁移)
第二步:创建 @vben/oxfmt-config 包
mkdir -p internal/lint-configs/oxfmt-config
# 创建 package.json 和 index.mjs
第三步:全量格式化并对比
# 先用 prettier 格式化一遍作为基线
pnpm prettier . --write
# 再用 oxfmt 格式化
npx oxfmt@latest
# 查看差异
git diff --stat
检查差异是否可接受。已知的可能差异:
- oxfmt 的
sortPackageJson排序算法可能与 Prettier 不同 - 嵌入语言格式化(CSS-in-JS)可能有细微差异
第四步:更新 scripts/vsh/src/lint/index.ts
// Before
await execaCommand(`prettier . --write --cache --log-level warn`, {
stdio: 'inherit',
});
await execaCommand(`prettier . --ignore-unknown --check --cache`, {
stdio: 'inherit',
});
// After
await execaCommand(`oxfmt`, { stdio: 'inherit' });
await execaCommand(`oxfmt --check`, { stdio: 'inherit' });
第五步:更新 lefthook.yml
pre-commit:
parallel: true
commands:
lint-md:
run: pnpm oxfmt {staged_files}
glob: '*.md'
lint-vue:
run: pnpm oxfmt {staged_files} && pnpm oxlint --fix {staged_files} && pnpm eslint --cache --fix {staged_files} && pnpm stylelint --fix --allow-empty-input {staged_files}
glob: '*.vue'
lint-js:
run: pnpm oxfmt {staged_files} && pnpm oxlint --fix {staged_files} && pnpm eslint --cache --fix {staged_files}
glob: '*.{js,jsx,ts,tsx}'
lint-style:
run: pnpm oxfmt {staged_files} && pnpm stylelint --fix --allow-empty-input {staged_files}
glob: '*.{scss,less,styl,html,vue,css}'
lint-package:
run: pnpm oxfmt {staged_files}
glob: 'package.json'
lint-json:
run: pnpm oxfmt {staged_files}
glob: '{!(package)*.json,*.code-snippets,.!(browserslist)*rc}'
第六步:清理 ESLint 中的 Prettier 集成
- 删除
eslint-config/src/configs/prettier.ts - 从
eslint-config/src/configs/index.ts移除export * from './prettier' - 从
eslint-config/src/index.ts移除prettier()调用 - 从
eslint-config/package.json移除eslint-plugin-prettier依赖
第七步:清理 Stylelint 中的 Prettier 集成
- 从
stylelint-config/index.mjs的plugins数组移除'stylelint-prettier' - 从
stylelint-config/index.mjs的rules移除'prettier/prettier': true - 从
stylelint-config/package.json移除prettier和stylelint-prettier依赖
第八步:更新 internal/node-utils/src/prettier.ts
// 重命名为 formatter.ts 或保留文件名
import { execaCommand } from 'execa';
async function formatFile(filepath: string) {
await execaCommand(`oxfmt ${filepath}`, { stdio: 'inherit' });
}
export { formatFile };
或者如果 oxfmt 提供了 Node API,则使用程序化调用。
第九步:移除旧依赖
# 移除根目录文件
rm .prettierrc.mjs
rm .prettierignore
# 从 pnpm-workspace.yaml catalog 移除
# - prettier: ^3.8.1
# - eslint-plugin-prettier: ^5.5.5
# - stylelint-prettier: ^5.0.3
# 移除 @vben/prettier-config 包
rm -rf internal/lint-configs/prettier-config
# 更新 package.json 根,移除 @vben/prettier-config 依赖
# 更新 vben-admin.code-workspace,移除 prettier-config workspace 引用
第十步:全量验证
pnpm install
pnpm run lint
pnpm run lint:oxc
pnpm run lint:eslint
2.7 oxfmt 额外能力评估
oxfmt 提供了 Prettier 没有的扩展特性,可选启用:
| 特性 | 说明 | 建议 |
|---|---|---|
sortImports |
import 语句排序,灵感来自 eslint-plugin-perfectionist/sort-imports |
评估是否可替代 ESLint 侧 perfectionist 的 import 排序 |
sortTailwindcss |
替代 prettier-plugin-tailwindcss |
当前项目已通过 oxlint jsPlugins 处理,需评估是否切换 |
sortPackageJson |
自动排序 package.json 字段 |
默认启用,排序算法与 prettier-plugin-packagejson 不同 |
insertFinalNewline |
文件末尾添加换行 | 默认启用,与 .editorconfig 的 insert_final_newline=true 一致 |
2.8 文件类型覆盖说明
oxfmt 对不同文件类型的处理方式:
| 文件类型 | 格式化方式 |
|---|---|
| JS / TS / JSX / TSX | oxfmt 原生格式化 |
| Vue SFC | oxfmt 原生格式化(<script> / <template>) |
| TOML | oxfmt 原生格式化(via taplo) |
| CSS / SCSS / Less | 内部委托 Prettier |
| HTML | 内部委托 Prettier |
| YAML / Markdown / GraphQL | 内部委托 Prettier |
重要:CSS 等非 JS/TS 文件仍然通过 oxfmt 内部委托 Prettier 处理,因此
npx oxfmt一个命令即可覆盖所有文件类型。不需要单独安装 Prettier——oxfmt 自带内嵌版本。
2.9 迁移风险
- 格式化差异:oxfmt 与 Prettier 的输出可能存在细微差异,首次全量格式化会产生大量 diff,建议单独一个 commit
- 嵌套配置不支持:oxfmt 不支持子目录级别的配置文件,如有需要需用
overrides替代 endOfLine: 'auto'不支持:必须改为显式值,建议统一为"lf"node-utils/prettier.ts:如果有其他包或脚本通过prettierFormat()调用格式化,需逐一排查- IDE 配置:团队成员需安装 oxfmt VS Code 扩展,或配置 LSP(
oxfmt --lsp)
第三阶段:Vite / Rolldown 自动集成 oxc
前提:Vite 通过 Rolldown 自动集成 oxc 能力后,以下依赖将被自然替代,无需手动操作。
| 当前依赖 | 替代方式 |
|---|---|
esbuild ^0.27.4 |
Rolldown 内置 oxc-transform / oxc-parser |
html-minifier-terser ^7.2.0 |
oxc-minifier(或 Rolldown 内置 minify) |
3.1 操作步骤
- 跟踪 Vite / Rolldown 版本更新,确认 oxc 集成进度
- 升级 Vite 到集成 Rolldown 的版本后,移除显式的
esbuild依赖 - 验证构建产物体积和功能无回归
- 评估
html-minifier-terser是否可被 Rolldown 内置能力替代
第四阶段:完全移除 ESLint(远期)
前提:oxlint 完全支持 Vue SFC 模板 lint 及所有保留插件的功能。
4.1 替换范围
| 当前依赖 | 替代方案 |
|---|---|
eslint |
移除 |
eslint-plugin-vue + vue-eslint-parser |
oxlint Vue 支持 |
eslint-plugin-better-tailwindcss |
oxlint tailwindcss 支持或其他方案 |
eslint-plugin-jsonc / eslint-plugin-yml |
oxlint 或独立 linter |
eslint-config-turbo |
移除 |
eslint-plugin-pnpm |
oxlint 或独立检查 |
eslint-plugin-command |
oxlint 或移除 |
@vitest/eslint-plugin |
oxlint vitest 分类 |
@vben/eslint-config |
改写为 @vben/oxlint-config 或移除 |
4.2 操作步骤
- 持续跟踪 oxlint 的 Vue / JSON / YAML 支持进展
- 当 oxlint 规则覆盖度满足要求时,全面切换
- 移除
internal/lint-configs/eslint-config/及所有 ESLint 相关依赖 - 更新 CI 和 lefthook 配置
迁移风险与注意事项
- 规则对齐:oxlint 的规则命名和行为可能与 ESLint 插件不完全一致,需逐条验证
- Vue SFC 支持:oxlint 对
<template>的 lint 能力是最大瓶颈,第一阶段务必保留eslint-plugin-vue - CI 稳定性:共存期间两套工具可能对同一问题重复报告,需通过配置互相排除
- 团队习惯:IDE 插件(如 VS Code ESLint 扩展)需要同步配置 oxlint 扩展
- 版本锁定:oxc 生态仍在快速迭代,建议锁定 minor 版本,升级时做全量验证
预期收益
| 指标 | 预期改善 |
|---|---|
| lint 速度 | 通用规则部分提升 50-100x |
| 依赖数量 | 第一阶段减少约 11 个包,最终可减少 20+ 个包 |
| 安装体积 | 显著减少(ESLint 插件依赖树庞大) |
| CI 时间 | lint 阶段耗时大幅缩短 |
| 维护成本 | 减少 ESLint 插件版本兼容性问题 |