# JS linter benchmarks

Which JavaScript linter is the fastest and consumes the least energy? We benchmarked different JavaScript linters to give you the answer.

- LSP: incremental-change-wait express-router.js
- LSP: incremental-change-wait react-quickly-ch10.jsx
- LSP: full-change-wait express-router.js
- Methodology
- Comments

## LSP: incremental-change-wait express-router.js

An LSP server is software which plugs into your code editor. It provides diagnostics (error squigglies), go-to-definition, and auto-complete. This benchmark measures how long an LSP server takes to give diagnostics after making a change in a JavaScript file (e.g. after typing a character). In other words, it measures input latency. Lower latency means a faster-feeling editor.

### Results

0 ms 60 FPS 50 ms 100 ms 150 ms 200 ms 250 ms 300 ms 350 ms 0.1 ms 1 ms 10 ms 100 ms 350 ms
quick-lint-js
ESLint
ESLint + Vue plugin
ESLint + React plugin
ESLint + TypeScript plugin
Flow
ESLint + Airbnb rules
Biome
Deno (no linting)
Deno
TypeScript (JSX parser)
TypeScript
response time (lower is better)
log scale
Results data table

linter | response time (milliseconds)
--- | ---
min | avg | max | ÷ qljs quick-lint-js
quick-lint-js | 0.24 | 0.26 | 0.29 | 1.0×
ESLint | 29.31 | 30.67 | 31.63 | 118.7×
ESLint + Vue plugin | 33.24 | 33.87 | 35.20 | 131.1×
ESLint + React plugin | 38.13 | 38.65 | 39.39 | 149.7×
ESLint + TypeScript plugin | 50.76 | 53.04 | 55.78 | 205.4×
Flow | 67.61 | 68.17 | 69.12 | 263.9×
ESLint + Airbnb rules | 79.11 | 79.96 | 80.84 | 309.6×
Biome | 143.48 | 145.70 | 147.95 | 564.1×
Deno (no linting) | 258.08 | 258.73 | 259.89 | 1001.8×
Deno | 258.58 | 259.26 | 259.80 | 1003.8×
TypeScript (JSX parser) | 247.91 | 271.79 | 278.27 | 1052.3×
TypeScript | 248.56 | 274.65 | 278.13 | 1063.4×

### Benchmark details

#### Setup (untimed)

1. Start the LSP server.
2. Wait for initialization to finish.
3. Open one document with contents from express-router.js.
4. Wait for diagnostics.

#### Work (timed)

Repeat the following steps N times:

1. Change a few characters in the document, sending small deltas in an LSP message.
2. Wait for diagnostics.

## LSP: incremental-change-wait react-quickly-ch10.jsx

This benchmark measures how long an LSP server takes to give diagnostics to an editor.

This benchmark is similar to the incremental-change-wait benchmark, except it uses a JSX (React) source file instead of a vanilla JavaScript source file.

### Results

0 ms 60 FPS 50 ms 100 ms 150 ms 200 ms 250 ms 300 ms 350 ms 0.1 ms 1 ms 10 ms 100 ms 350 ms
quick-lint-js
Biome
Flow
ESLint + React plugin
ESLint + TypeScript plugin
Deno
Deno (no linting)
TypeScript (JSX parser)
response time (lower is better)
log scale
Results data table

linter | response time (milliseconds)
--- | ---
min | avg | max | ÷ qljs quick-lint-js
quick-lint-js | 0.08 | 0.11 | 0.13 | 1.0×
Biome | 5.97 | 6.47 | 7.27 | 60.6×
Flow | 27.21 | 27.55 | 27.77 | 258.0×
ESLint + React plugin | 27.98 | 28.43 | 28.97 | 266.2×
ESLint + TypeScript plugin | 26.96 | 28.93 | 33.00 | 270.9×
Deno | 226.97 | 227.86 | 228.56 | 2133.6×
Deno (no linting) | 227.70 | 228.19 | 228.66 | 2136.7×
TypeScript (JSX parser) | 274.45 | 275.37 | 275.96 | 2578.4×

### Benchmark details

#### Setup (untimed)

1. Start the LSP server.
2. Wait for initialization to finish.
3. Open one document with contents from react-quickly-ch10.jsx.
4. Wait for diagnostics.

#### Work (timed)

Repeat the following steps N times:

1. Add or remove a few characters in the document, sending small deltas in an LSP message.
2. Wait for diagnostics.

## LSP: full-change-wait express-router.js

This benchmark measures how long an LSP server takes to give diagnostics to an editor.

This benchmark differs from the incremental-change-wait benchmark in that the editor gives the new version of the file entirely rather than just the incremental changes. This is for compatibility with linters such as RSLint which do not support incremental changes.

### Results

0 ms 60 FPS 50 ms 100 ms 150 ms 200 ms 250 ms 300 ms 350 ms 0.1 ms 1 ms 10 ms 100 ms 350 ms
quick-lint-js
RSLint
ESLint
ESLint + Vue plugin
Flow
ESLint + React plugin
ESLint + TypeScript plugin
ESLint + Airbnb rules
Biome
TypeScript (JSX parser)
Deno (no linting)
Deno
TypeScript
response time (lower is better)
log scale
Results data table

linter | response time (milliseconds)
--- | ---
min | avg | max | ÷ qljs quick-lint-js
quick-lint-js | 0.29 | 0.32 | 0.38 | 1.0×
RSLint | 20.12 | 20.17 | 20.23 | 63.9×
ESLint | 29.27 | 30.10 | 31.11 | 95.4×
ESLint + Vue plugin | 32.72 | 33.42 | 34.12 | 106.0×
Flow | 32.80 | 33.45 | 34.36 | 106.0×
ESLint + React plugin | 37.50 | 38.45 | 39.11 | 121.9×
ESLint + TypeScript plugin | 48.87 | 50.98 | 53.81 | 161.6×
ESLint + Airbnb rules | 79.09 | 80.24 | 81.72 | 254.4×
Biome | 143.88 | 145.63 | 148.62 | 461.7×
TypeScript (JSX parser) | 224.56 | 257.80 | 285.38 | 817.4×
Deno (no linting) | 258.04 | 258.99 | 259.62 | 821.2×
Deno | 257.61 | 259.03 | 260.01 | 821.3×
TypeScript | 224.95 | 272.06 | 309.69 | 862.6×

### Benchmark details

#### Setup (untimed)

1. Start the LSP server.
2. Wait for initialization to finish.
3. Open one document with contents from express-router.js.
4. Wait for diagnostics.

#### Work (timed)

Repeat the following steps N times:

1. Change a few characters in the document, sending the entire new document in an LSP message.
2. Wait for diagnostics.

## Methodology

These benchmarks measure the following linters:

- quick-lint-js version 2.17.0 (Debian package)
- Biome version 1.3.1 (npm package)
- RSLint version v0.3.2 (with rustc version 1.73.0)
- Flow version 0.219.5
- ESLint version v8.52.0
  - @typescript-eslint/eslint-plugin version 6.9.0
  - @typescript-eslint/parser version 6.9.0
  - eslint-plugin-vue version 9.18.0
  - vscode-eslint commit d3c7703cd11dc6752c96edf0f140d50156d72dd7
  - Node version v21.1.0
- Deno version 1.37.2 (with V8 version 11.8.172.13 and TypeScript version 5.2.2)
- TypeScript version 5.2.2 (with language server version 4.0.0 and Node version v21.1.0)

These benchmarks were measured on the following machine:

- strager's "straglum"; Purism Librem 13 Version 4 laptop
- CPU: Intel Core i7-7500U CPU @ 2.70 GHz (2 cores, 4 threads)
- OS: Linux Mint 20 Ulyana; 5.4.0-165-generic #182-Ubuntu SMP Mon Oct 2 19:43:28 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
- Performance governor: performance; 400 MHz - 3.50 GHz

## Comments

### Deno

Deno's LSP server (and thus its Visual Studio Code extension) delays processing by 200 milliseconds. This means that Deno appears to be much slower than it actually is, but this artificial latency does affect the editing experience.

### TypeScript

TypeScript's LSP server (but not its Visual Studio Code extension) delays processing by 200 milliseconds + 50 milliseconds. This means that TypeScript appears to be much slower than it actually is, but this artificial latency does affect the editing experience (in all editors except Visual Studio Code).