# 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 `typescript` package
- Auto-fix with `--fix`
- Works with your existing ESLint or oxlint config

## Run the linter

```bash terminal icon="terminal"
bun lint
```

```txt
1 | 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.

```bash terminal icon="terminal"
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.

```bash terminal icon="terminal"
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.

```bash terminal icon="terminal"
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`, and `jsx-key`, if `react` is in your `package.json`
- **Tests**: leftover `test.only`, `expect()` without a matcher, un-awaited `.resolves`, and a few more, in files that import from `bun: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 runs `import` and `require()` in the same file.
- A name that starts with `_` may be unused.
- `done && done();` and `ok ? 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 a `case`, and `// fall-through` or `// no break` marks 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 React` counts as used in a file with JSX.

Each rule's page says what differs and how to change it.

- In `.d.ts` files only the errors run.
- An `eslint-disable` comment 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](/lint/plugins#other-languages), which need an ESLint config.

```toml bunfig.toml icon="settings"
[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:

```txt
2 | "no-consle" = "warn"
    ^
error: Unknown rule "no-consle"
  Did you mean "no-console"?
    at bunfig.toml:2:1
```

### Find a rule

Browse [all rules](/lint/rules), or look one up from the terminal. In most terminals you can also click the rule name in a report.

```bash terminal icon="terminal"
bun lint --rules                 # list every rule
bun lint --explain prefer-const  # what it does, examples, options
```

### Add an ESLint plugin

Need a rule that isn't built in? Load any [ESLint plugin](/lint/plugins), from npm or your own. The default rules stay on.

```toml bunfig.toml icon="settings"
[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`.

```toml bunfig.toml icon="settings"
[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`](/runtime/bunfig#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.

```toml bunfig.toml icon="settings"
[lint.rules]
"jsx-a11y/*" = "off"
"unicorn/*" = "off"
"unicorn/no-null" = "warn"
```

## Ignore files

```toml bunfig.toml icon="settings"
[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`](/runtime/bunfig#lint-ignorepatterns) 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:

- `.mjs` and `.mts` files are modules. `.cjs` and `.cts` files are CommonJS.
- Any other file is a module if the nearest `package.json` has `"type": "module"`.
- Otherwise, a file with an `import`, an `export`, `import.meta` or a top-level `await` is 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"`:

```toml bunfig.toml icon="settings"
[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.

```ts
// 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.

```bash terminal icon="terminal"
bun lint --type-aware
```

```txt
5 | 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:3
```

Types come from [`bun check`](/runtime/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:

```toml bunfig.toml icon="settings"
[lint]
typeAware = true
```

Only want one of them? Turn it on like any other rule. You don't need `typeAware`.

```toml bunfig.toml icon="settings"
[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`.

```bash terminal icon="terminal"
bun lint --type-aware --tsconfig tsconfig.lint.json
```

A `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.

```txt
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`.

```sh .git/hooks/pre-commit icon="terminal"
#!/bin/sh
bun lint --staged --fix
```

If 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.

```bash terminal icon="terminal"
bun lint --changed
bun lint --changed=origin/dev
```

## CI

In GitHub Actions, `bun lint` annotates the pull request diff automatically.

```yaml .github/workflows/lint.yml icon="file-code"
steps:
  - uses: actions/checkout@v4
  - uses: oven-sh/setup-bun@v2
  - run: bun lint --max-warnings 0 # fail on warnings too
```

For 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.

```json package.json icon="file-json"
{
  "scripts": {
    "lint": "eslint ." // [!code --]
    "lint": "bun lint" // [!code ++]
  }
}
```

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:

```bash terminal icon="terminal"
bun lint --doctor
```

See the guides: [Migrate from ESLint](/guides/lint/migrate-from-eslint) and [Migrate from oxlint](/guides/lint/migrate-from-oxlint). For custom rules, see [ESLint plugins](/lint/plugins).

# CLI Usage

```bash
bun lint [flags] [files, directories, or globs]
```

### Rules

<ParamField path="--rule" type="string">
  Set a rule for this run: <code>--rule 'eqeqeq: [error, smart]'</code>. Turn off a plugin:{" "}
  <code>--rule 'unicorn/*: off'</code>
</ParamField>

<ParamField path="--type-aware" type="boolean">
  Also run the rules that need type information
</ParamField>

<ParamField path="--rules" type="boolean">
  List every built-in rule. Add <code>-f json</code> for machine-readable output
</ParamField>

<ParamField path="--explain" type="string">
  Print the documentation for a rule
</ParamField>

<ParamField path="--init" type="boolean">
  Add a <code>[lint]</code> section to <code>bunfig.toml</code>
</ParamField>

### Fixing

<ParamField path="--fix" type="boolean">
  Apply fixes and write the files
</ParamField>

<ParamField path="--fix-suggestions" type="boolean">
  Also apply suggestions, which may change what the code does
</ParamField>

<ParamField path="--fix-dry-run" type="boolean">
  Compute fixes without writing them. The <code>json</code> format includes the fixed code
</ParamField>

### Files

<ParamField path="--staged" type="boolean">
  Lint only the files staged in Git
</ParamField>

<ParamField path="--changed" type="string">
  Lint only the files that differ from a Git ref. Defaults to the default branch
</ParamField>

<ParamField path="--ignore-pattern" type="string">
  Skip files that match this glob. Can be repeated
</ParamField>

<ParamField path="--no-ignore" type="boolean">
  Lint ignored files too
</ParamField>

### Output

<ParamField path="-f, --format" type="string" default="pretty">
  <code>pretty</code>, <code>stylish</code>, <code>json</code>, <code>unix</code>, <code>github</code>,{" "}
  <code>junit</code>, <code>checkstyle</code>, <code>gitlab</code>, <code>sarif</code>, or <code>agent</code>
</ParamField>

<ParamField path="-o, --output-file" type="string">
  Write the report to a file
</ParamField>

<ParamField path="--quiet" type="boolean">
  Report errors only
</ParamField>

<ParamField path="--max-warnings" type="number">
  Exit with code 1 if there are more warnings than this
</ParamField>

<ParamField path="--all" type="boolean">
  Show every problem. By default, more than 50 identical problems are grouped
</ParamField>

### Tools

<ParamField path="--doctor" type="boolean">
  Show which rules of your ESLint or oxlint config run natively, and which packages you can remove
</ParamField>

<ParamField path="--print-config" type="string">
  Print the resolved config for a file
</ParamField>

<ParamField path="--no-script" type="boolean">
  Run the linter even if the project has a <code>lint</code> script
</ParamField>

`bun lint` also accepts the flags of ESLint and oxlint. Run `bun lint --help --all` to see them.
