运行时包管理器打包器测试运行器代码检查格式化指南参考博客反馈

从 ESLint 迁移

将现有 ESLint 配置迁移到 bun lint

此页面尚未完成本地化

当前显示英文原文。内容会随官方文档同步更新。

阅读英文原文
内容摘要

把 eslint 命令替换为 bun lint,保留参数、规则与退出码。本页列出迁移步骤与行为差异。

bun lint reads your eslint.config.js and prints the same report as ESLint 10. In most projects you only change the command.

package.json
{
  "scripts": {
    "lint": "eslint --max-warnings 0", // [!code --]
    "lint": "bun lint --max-warnings 0" // [!code ++]
  }
}

Flags and arguments stay the same: eslint --fix src becomes bun lint --fix src. Change CI, lint-staged, and Git hooks too.

Edit the script first. If package.json has a lint script, bun lint runs that script, so you'd still get ESLint.


Run it. You get the same rules, messages, and exit code as with eslint.

terminal
bun run lint
Text
$ bun lint --max-warnings 0

/app/src/index.ts
  1:1   warning  Open an issue instead of leaving a TODO              local/no-todo
  3:7   error    'fallback' is never reassigned. Use 'const' instead  prefer-const
  4:27  error    Invalid typeof comparison value                      valid-typeof

✖ 3 problems (2 errors, 1 warning)
  1 error and 0 warnings potentially fixable with the `--fix` option.

Linted 3 files [108.90ms]
error: script "lint" exited with code 1

Run --doctor to see what Bun does with your config. It doesn't lint or write anything.

terminal
bun lint --doctor
Text
Using eslint.config.js (ESLint flat config)

    3 files to lint

   25 rules run natively
    1 rule runs in JavaScript
        lint/plugin.js    1

Still needed:   eslint (typescript-eslint needs it)
                typescript-eslint (eslint.config.js loads it)
  • Natively: the rule is built into Bun. That covers ESLint's core rules, typescript-eslint, react, react-hooks, import, 15 rules of n, and prettier/prettier.
  • In JavaScript: Bun loads the plugin and runs it like ESLint does. It works, but it's slower. See ESLint plugins.

The last lines tell you which packages you still need. Bun runs your eslint.config.js, so everything that file imports has to stay. Here that's typescript-eslint, which needs eslint. Packages you no longer need are listed under You can remove.


You can drop lint-staged. --staged lints only the staged files, and with --fix it stages the fixed files again.

.husky/pre-commit
npx lint-staged # [!code --]
bun lint --staged --fix # [!code ++]

Type-aware rules need no changes. If your config asks for types, they run.

eslint.config.js
import tseslint from "typescript-eslint";

export default tseslint.config(...tseslint.configs.recommendedTypeChecked, {
  languageOptions: { parserOptions: { projectService: true } },
});

The types come from Bun's built-in type checker (bun check), not from the typescript package. It behaves like TypeScript 7.


Still on ESLint 8? .eslintrc.*, eslintConfig in package.json, and .eslintignore work when there is no eslint.config.js. So do ESLint 8's flags.

terminal
eslint --ext .js,.ts,.tsx src # [!code --]
bun lint --ext .js,.ts,.tsx src # [!code ++]

If your config asks for something Bun can't do, Bun lints everything else, tells you what it skipped, and exits with code 2.

Text
error: 1 rule did not run because rules that run in JavaScript cannot use type information: local/typed
  Everything else was linted. Use --allow-unsupported to make this a warning.

There are two such cases: a JavaScript rule that needs types, and files in other languages when the eslint package isn't installed. Pass --allow-unsupported to turn the error into a warning.


If the project also has an oxlint config, Bun uses that one and tells you.

Text
note: Using .oxlintrc.json. eslint.config.js is ignored. Use --config to pick it.

Pick the ESLint config with --config.

package.json
{
  "scripts": {
    "lint": "oxlint && eslint", // [!code --]
    "lint": "bun lint && bun lint --config eslint.config.js" // [!code ++]
  }
}

Differences from ESLint

If bun lint and ESLint 10 disagree about a rule, that's a bug. Open an issue. These differences are intentional:

  • Built-in rules have a fixed version, whatever you have installed: ESLint 10.12, typescript-eslint 8.71, eslint-plugin-react 7.37, react-hooks 7.1, import 2.32, n 18.4. --no-native-plugin-rules runs your installed plugins instead.
  • Deprecated formatting rules don't run: semi, quotes, indent, and so on. Use bun fmt or @stylistic/eslint-plugin.
  • No custom formatters. -f only takes built-in formats such as stylish, json, unix, junit, and sarif.
  • No --inspect-config or --mcp.
  • --cache does nothing. Every run lints every file.
  • No config file is not an error. Bun uses its defaults.
  • Syntax errors can be worded differently. Bun's parser replaces espree, @typescript-eslint/parser, and @babel/eslint-parser.
  • A type-aware rule without type information is skipped, with a warning. typescript-eslint throws.
  • A file that no tsconfig.json includes is still linted, with the nearest tsconfig.json. ESLint stops.
  • --fix never writes a file that no longer parses. Bun drops those fixes and warns.
  • console.log in your config file is hidden unless the config fails to load.
  • One extra line on stderr with the number of files and the time.
  • In GitHub Actions, ::error and ::warning lines follow the stylish report on stdout, so problems show up in the pull request diff. If a problem matcher such as the one from actions/setup-node already does that, you get each annotation twice. -f json and the other machine-readable formats don't have these lines.

See also: