Linter
Bun's fast, built-in linter for JavaScript, TypeScript, and JSX
Bun ships with a fast, built-in linter. There's nothing to install and nothing to configure.
- 1,000+ rules from ESLint, typescript-eslint, React, import, unicorn, jsx-a11y, Jest, Vitest, and more
- Type-aware rules, without the
typescriptpackage - Auto-fix with
--fix - Works with your existing ESLint or oxlint config
Run the linter#
bun lint1 | export function total(prices: number[]) {
2 | let sum = 0;
3 | for (let i = 0; i < prices.length; i--) {
^
error: The update clause in this loop moves the variable in the wrong direction. for-direction
at src/cart.ts:3:3
5 | }
6 |
7 | let tax = 0.2;
^
warn: 'tax' is never reassigned. Use 'const' instead. prefer-const
at src/cart.ts:7:7
2 problems (1 error, 1 warning)
1 can be fixed with --fix
Linted 1 file [5.17ms]bun lint checks every .js, .jsx, .ts, and .tsx file in the current directory (plus the .mjs, .cjs, .mts, .cts variants). It skips node_modules and anything in your .gitignore or .git/info/exclude.
To lint specific files, pass paths or globs.
bun lint src "test/**/*.test.ts"An argument that starts with ! leaves files out of what the other arguments select, as it does for bun fmt. This holds with an ESLint or oxlint config too.
bun lint src "!src/generated" "!**/*.test.ts"The exit code is 1 if there are errors and 0 if there are only warnings.
Fix problems#
Many rules can fix what they report. --fix applies those fixes and writes the files.
bun lint --fix--fix is for changes that keep what your program does and keep your comments. Where a rule's fix is known to alter behavior or drop a comment, the problem is still reported, but only --fix-suggestions or --fix-dangerously applies the fix, even if ESLint or oxlint apply it with --fix. Each rule's page says which flag it needs.
Default rules#
With no config, bun lint turns on the rules that catch real bugs:
- Every file: ESLint's recommended rules
- TypeScript: typescript-eslint's recommended rules
- React:
rules-of-hooks,exhaustive-deps, andjsx-key, ifreactis in yourpackage.json - Tests: leftover
test.only,expect()without a matcher, un-awaited.resolves, and a few more, in files that import frombun:test
Likely bugs are errors. Unused code, style suggestions, and code that people often write on purpose, like while (match = re.exec(text)), are warnings. A warning doesn't fail the run.
A few defaults differ from ESLint's, so that ordinary code isn't reported:
require()is allowed in TypeScript files. Bun runsimportandrequire()in the same file.- A name that starts with
_may be unused. done && done();andok ? resolve() : reject();are fine as statements.- A no-break space or a full-width space is only reported in code, not in text, comments or JSX.
process.exit()ends acase, and// fall-throughor// no breakmarks one that goes on.{false && <Debug />}is a fine way to switch something off for a while.- An array of JSX elements needs keys only where React renders it as a list.
import Reactcounts as used in a file with JSX.
Each rule's page says what differs and how to change it.
- In
.d.tsfiles only the errors run. - An
eslint-disablecomment for a rule that is off is left alone.
Configure rules#
Rules live in bunfig.toml. Every built-in rule works the same way, with nothing to install. The exceptions are the rules for JSON and Markdown, which need an ESLint config.
[lint.rules]
"no-console" = "warn"
"prefer-const" = "off"
"eqeqeq" = ["error", "smart"] # with options
"unicorn/prefer-node-protocol" = "error"
"@typescript-eslint/no-explicit-any" = "off"An option you don't set keeps its default.
To create the section, run bun lint --init.
A typo is an error, not a silent no-op:
2 | "no-consle" = "warn"
^
error: Unknown rule "no-consle"
Did you mean "no-console"?
at bunfig.toml:2:1Find a rule#
Browse all rules, or look one up from the terminal. In most terminals you can also click the rule name in a report.
bun lint --rules # list every rule
bun lint --explain prefer-const # what it does, examples, optionsAdd an ESLint plugin#
Need a rule that isn't built in? Load any ESLint plugin, from npm or your own. The default rules stay on.
[lint]
plugins = ["eslint-plugin-perfectionist", "./lint/plugin.js"]
[lint.rules]
"perfectionist/sort-imports" = "error"
"local/no-todo" = "warn"Turn on a group of rules#
Categories switch on many rules at once: correctness, suspicious, pedantic, perf, style, restriction, nursery.
[lint.categories]
suspicious = "warn"
perf = "warn"A category includes the React, Next.js, Jest and Vitest rules only if your package.json lists that package. See lint.categories.
Turn a plugin off#
Use * as the rule name to turn off every rule of a plugin. A setting for a single rule overrides it.
[lint.rules]
"jsx-a11y/*" = "off"
"unicorn/*" = "off"
"unicorn/no-null" = "warn"Ignore files#
[lint]
ignorePatterns = ["vendor/", "**/*.gen.ts"]The patterns use .gitignore syntax and are relative to bunfig.toml. With an eslint.config.* file they're ESLint ignores instead.
ES modules and CommonJS#
bun lint works out whether a file is an ES module or CommonJS from the file itself, the way Node.js does:
.mjsand.mtsfiles are modules..cjsand.ctsfiles are CommonJS.- Any other file is a module if the nearest
package.jsonhas"type": "module". - Otherwise, a file with an
import, anexport,import.metaor a top-levelawaitis a module. A file with none of them is CommonJS.
A CommonJS file can return at the top level, and it's only in strict mode if it starts with "use strict".
To decide for every file yourself, set sourceType to "module", "commonjs" or "script":
[lint.parserOptions]
sourceType = "commonjs"Next to an eslint.config.js, ESLint's defaults apply instead: .js and .mjs files are modules and .cjs files are CommonJS, unless languageOptions.sourceType says otherwise.
Disable a rule in code#
bun lint uses ESLint's comments, so existing ones keep working. oxlint's oxlint-disable comments mean the same, except next to an ESLint config, where they are ordinary comments as in ESLint.
// eslint-disable-next-line no-console -- the CLI prints its version
console.log(version);
/* eslint-disable no-console */
console.log("off until the end of the file");A comment that no longer disables anything gets a warning, and --fix removes it.
Type-aware rules#
Some bugs only show up with types, like a promise nobody awaits. --type-aware turns on the recommended rules that use them.
bun lint --type-aware5 | export function onClick() {
6 | save("hello");
^
error: Promises must be awaited, end with a call to .catch, ... @typescript-eslint/no-floating-promises
at src/save.ts:6:3Types come from bun check, Bun's built-in type checker. It's slower than a plain run, because your code is type checked first. To make it the default:
[lint]
typeAware = trueOnly want one of them? Turn it on like any other rule. You don't need typeAware.
[lint.rules]
"@typescript-eslint/no-floating-promises" = "error"To report type errors too, like bun check does, set typeCheck = true or pass --type-check.
Choosing a tsconfig.json#
Each file is type checked with the tsconfig.json your editor would use for it. To use one tsconfig.json for the whole run, pass --tsconfig.
bun lint --type-aware --tsconfig tsconfig.lint.jsonA tsconfig.json without "include" covers every file below it. At the root of a monorepo, that can mean loading the whole repository to lint one package. Bun warns you when that happens.
warn: Loaded 10,114 files in 1.4s to lint vite.config.ts
../../tsconfig.json has no "include", so it covers every file under it.
To speed this up, add "include" to it or pass --tsconfig.With an oxlint config, --tsconfig does what it does in oxlint. It sets the tsconfig.json that the import rules resolve imports with, for its paths. It doesn't change which tsconfig.json a file is type checked with, so the warning only suggests "include".
Git hooks#
--staged lints only the files you're about to commit, so you don't need lint-staged.
#!/bin/sh
bun lint --staged --fixIf a file has both staged and unstaged changes, Bun lints the staged version, which is what the commit gets. --fix doesn't write such a file, because staging it again would commit the rest. If it has something to fix, Bun names the file and exits with code 2.
The same goes for the files that a staged file imports, which the type-aware rules and the import rules read. A file you staged and then deleted is still linted. Config files, such as bunfig.toml and eslint.config.js, are read from disk.
--changed lints the files that differ from your default branch. Pass a ref to compare with something else.
bun lint --changed
bun lint --changed=origin/devCI#
In GitHub Actions, bun lint annotates the pull request diff automatically.
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun lint --max-warnings 0 # fail on warnings tooFor other systems, pick a report format with --format: json, junit, checkstyle, gitlab, sarif, unix.
Migrate from ESLint or oxlint#
Already have an eslint.config.js, .eslintrc, or .oxlintrc.json? bun lint reads it and reports what that tool reports. Change the script and you're done.
{
"scripts": {
"lint": "eslint ."
"lint": "bun lint"
}
}If package.json has a lint script, bun lint runs that script. Inside the script, bun lint is the linter. If package.json has "lint" in it but can't be parsed, as in the middle of a merge, bun lint can't tell whether that is a script: it lints nothing and exits with code 2.
Writing a tool that calls Bun in projects you don't control? Pass --no-script: bun lint --no-script is always the linter.
Rules from built-in plugins run natively. Any other ESLint plugin still runs, in JavaScript. To see which is which, and which packages you can uninstall:
bun lint --doctorSee the guides: Migrate from ESLint and Migrate from oxlint. For custom rules, see ESLint plugins.
CLI Usage#
bun lint [flags] [files, directories, or globs]Rules#
Set a rule for this run: --rule 'eqeqeq: [error, smart]'. Turn off a plugin:
--rule 'unicorn/*: off'
Also run the rules that need type information
List every built-in rule. Add -f json for machine-readable output
Print the documentation for a rule
Add a [lint] section to bunfig.toml
Fixing#
Apply fixes and write the files
Also apply suggestions, which may change what the code does
Compute fixes without writing them. The json format includes the fixed code
Files#
Lint only the files staged in Git
Lint only the files that differ from a Git ref. Defaults to the default branch
Skip files that match this glob. Can be repeated
Lint ignored files too
Output#
pretty, stylish, json, unix, github,
junit, checkstyle, gitlab, sarif, or agent
Write the report to a file
Report errors only
Exit with code 1 if there are more warnings than this
Show every problem. By default, more than 50 identical problems are grouped
Tools#
Show which rules of your ESLint or oxlint config run natively, and which packages you can remove
Print the resolved config for a file
Run the linter even if the project has a lint script
bun lint also accepts the flags of ESLint and oxlint. Run bun lint --help --all to see them.