<%= meta.title %>
+<%= meta.description %>
+ + +
+ Written by strager on
+
+ When people hear about quick-lint-js, they ask me one of two questions: +
+-
+
What makes quick-lint-js so fast?
+ -
+
Why did you make another JavaScript linter? Why not use ESLint?
+
+
+ ESLint is the de-facto standard JavaScript linter. It is highly + customizable and readily available. What's not to like about ESLint? +
+ +Here is my list:
+ + ++ After a discussion of my ESLint grievances, I will evaluate new + JavaScript linters: +
+-
+
- Deno Lint +
- Biome +
- quick-lint-js +
Finally, I will share my predictions:
+ + +ESLint is editor-last
+ ++ ESLint is command-line first, CI second, and editor last. + ESLint focuses on checking your code after you write it, not while + you write it. Programmers typically run ESLint as a batch job + pre-commit: +
+@@@ picture of ESLint CLI
+ ++ I want a JavaScript code assistant; I want to catch issues while I'm + typing. I miss the days when JetBrains Resharper had my back when I + hacked away in C#. +
+ +@@@ picture of Resharper?
+ ++ While you can run ESLint in your editor, the experience + isn't great. I teach JavaScript a lot, so I often make small + programs showcasing different JavaScript, Node.js, or browser + features. ESLint only really works inside projects, so it + fights you when you're writing one-off scripts. +
+ ++ I use Vim. ESLint's Vim plugins are weak. The ALE plugin, for + example, starts a new Node.js process each time linting occurs. + ESLint takes over 150 milliseconds to just start up (on my + Zen 3 5950X CPU!); ESLint can't even keep up with typing at 80 WPM! + But let's talk about speed later. +
+ +
+ When typing, you often introduce syntax errors. You type
+ (
but don't write the closing )
. ESLint
+ breaks. ESLint should at least give a helpful error message, but it
+ just tells me "unexpected token }":
+
@@@ video
+ +
+ Apparently ESLint's designers want you to use ESLint only after
+ you've
+ tested your code. But at that point, ESLint is useless to me: it
+ only is for telling me that row_index
is an evil
+ variable name, not that I have serious bugs in my code.
+
+ I want a tool I can recommend to beginners. Unfortunately, the + ESLint Visual Studio Code extension does not bundle ESLint, so it + does not work out of the box. The extension doesn't tell you what's + wrong if you're missing ESLint or a config file. ESLint in VS Code + is bad for beginners. +
+ ++ I want ESLint to work well in my editor. + Editor support should be a first-class feature. +
+ESLint is plugin & config hell
+ ++ Want to use ESLint to lint some JavaScript code? Great! Just run + ESLint: +
+@@@ pic
+ ++ Nope. ESLint requires you to create a configuration file, even if + you want a default well-established configuration. As I mentioned + earlier, this makes it annoying to use ESLint for one-off scripts I + make while teaching. +
+ ++ Want to use ESLint with a Node.js project? Great! ESLint works out + of the box. Not! You need to convince ESLint that you're in + a Node.js project. By default, ESLint complains: +
+ + ++ Want to use ESLint for ES modules? Great! But it doesn't work out of + the box; you gotta configure it:
+ + ++ Want to use ESLint for JavaScript code from 2015, using advanced + features like arrow functions and async functions? Great! But it + doesn't work out of the box; you must configure the parser's + ecmaVersion. +
+ ++ Want to use ESLint with a JSX/React project? Great! There's a plugin + for that. But you can't just install the React plugin; you also need + to configure ESLint's parser, and you should probably specify the + React version too. To make matters worse, the specifics for doing + these things have changed over the years, so old StackOverflow + answers don't work anymore. +
+ +@@@ picture of minimal ESLint config for JSX
+ ++ Want to use ESLint with a TypeScript project? Great! There's a + plugin for that. @@@ but stuff. +
+ ++ Want to use ESLint for bug-finding only and leave formatting to + Prettier? Great! There's a plugin for that. But why do you need a + plugin? Shouldn't I be able to just turn off ESLint's formatting + rules? Well, now + most ESLint formatting rules are gone + (deprecated as of ESLint v8.53.0), but a few (such as no-extra-semi + and no-irregular-whitespace) remain as default-enabled rules. +
+ ++ Configuring ESLint only needs to be done when you make major changes + to the project, or when you're just starting the project. There is + even an init tool to create an ESLint config for you. ESLint doesn't + make setup easy enough for me to recommend it to beginners, + and it's not easy enough for me to use for all my projects. +
+ ++ I want my batteries included. No required config + file for the basics. No plugins for the basics. + Easy to beginners, please. +
+ESLint is slow
+ ++ Anyone who has used ESLint in a big project knows that ESLint's + performance sucks. But I work on smaller projects, so performance + shouldn't be too bad, right? +
+ +
+ On one 15k SLOC project, ESLint takes around 950 milliseconds
+ (16,000 lines per second). The --cache
option helps,
+ bringing linting down to around 400 milliseconds if I change one
+ file. But even with lots of caching, that's only 38,000 lines per
+ second! C++ linters run at over 100,000 lines per second without
+ caching, and C++ is notoriously uglier to deal with than JavaScript.
+ And in bigger C++ projects, parallelization kicks in to improve
+ throughput more.
+
+ Why does linting speed matter to me? Half a second isn't that bad. + But as I mentioned in the ESLint is editor-last section, I + need ESLint to keep up with my typing. ESLint being slow is + disorienting: +
+@@@ video of lag
+ ++ ESLint is distracting. "But strager, why are you linting while + typing? Just add debouncing so it doesn't flicker as much." No! + Unacceptable. The debouncing is less distracting, but it's still + distracting! In contrast, a fast linter like quick-lint-js is + not distracting to me: +
+@@@ video of no lag
+ ++ ESLint performance has been improving. + eslint_d and + the ESLint LSP server (hidden inside the Visual Studio Code extension) help with the latency. ESLint's core has gotten faster; I had to + adjust my "over 130× faster than ESLint" messaging to just "over 90× + faster". And Node.js, V8, and computer hardware are getting faster + too. +
+ ++ Despite these improvements, + I want a 100× faster ESLint. +
+New JavaScript linters
+ ++ I don't like ESLint. What other options are there, and how do they + address the problems with ESLint? Let's look at three ESLint + alternatives: Deno Lint, + Biome, and + quick-lint-js. +
+ +@@@ picture: Deno vs Biome vs qljs
+ + Deno Lint
+
+ + Deno Lint + is a part of the Deno project. Created in March 2020, it features + better performance than ESLint and integration with the rest of + Deno. +
+ +
+ Compatibility: Because Deno Lint is designed for Deno
+ projects, it does not work well for non-Deno projects. For example,
+ Deno Lint recognizes JSX syntax, but only in
+ .jsx
files. But most React-based projects use
+ .js
files, so Deno Lint explodes on JSX. Deno Lint
+ probably won't ever support frameworks such as Vue and Svelte
+ either.
+
+ Editor integration: Deno has an LSP server that can run the + linter. Editor support is not prominently advertised, but it does + seem to be well-supported (including in Vim). The Visual Studio Code + extension does not bundle Deno, though, making it harder to use for + beginners. The LSP server also includes the TypeScript type checker, + which is handy (or annoying, depending on your perspective). @@@ try + it out. does it need a project to work? +
+ ++ Plugin & configs: Deno Lint works out of the box with no + configuration. No plugins are necessary for JSX and TypeScript. + Nice! +
+ ++ Speed: Deno Lint's command-line interface is decently fast. + Sadly, the LSP server is slow. The slowness is caused by intentional + debouncing. I could fork Deno and patch it out, of course. +
+ Biome
+
+ + Biome is a linter and formatter + for JavaScript and other languages. Biome's history is a bit weird: + Rome was created in February 2020. It was rewritten from scratch in + September 2021 based on + RSLint + (which was created in September 2020). The Rome dev team forked Rome + and called it Biome in August 2023. +
+ ++ Compatibility: Biome is designed to meet developers where + they are. As such, it supports JavaScript, JSX, and TypeScript + today, and will support Vue and Svelte in the future. +
+ ++ Editor integration: Biome has an LSP server that works in Vim + with coc.nvim. In my experience, the LSP server is a bit flaky + because it talks to a separate Biome daemon. Biome's Visual Studio + Code extension requires Biome to be installed separately, making it + harder to use for beginners. +
+ +
+ Plugin & configs: Biome does not suffer from plugin hell
+ for basic features. Biome also works out of the box without configs.
+ However, Biome is opinionated by default; it disallows code such as
+ this.icons["zoomIn"]
and sometimes
+ requires blocks inside
+ switch
statements. Therefore, I need
+ to configure Biome to shut it up.
+
+ Speed: Biome started fast when it was a light fork of RSLint. + As new lint rules have been added over time, + Biome's linting performance has 50× gotten worse. In my testing, Biome's LSP server is slower than ESLint's! I'm + sure some of this can be fixed, but it looks like linting + performance is not a key feature of Biome. +
+
+ quick-lint-js
+
+
+ + quick-lint-js is my own JavaScript linter. I + started writing it in March 2020 after being annoyed by ESLint's + poor editor integration and + Flow being too picky. +
+ ++ Compatibility: Like Biome, quick-lint-js is designed to meet + developers where they are. It supports JavaScript and JSX today, + with experimental support for TypeScript. Vue and Svelte support + will come eventually. +
+ ++ Editor integration: quick-lint-js was designed to work with + editors from the beginning. It has first-party support for various + editors, including different Vim and Emacs plugins. The Visual + Studio Code extension comes with the linter bundled, simplifying + installation. +
+ ++ Plugin & configs: quick-lint-js is like Biome and Deno + Lint: Your code is correctly linted out of the box. quick-lint-js + focuses on correctness issues and avoids stylistic nitpicks, making + it usable in any project without configuration. However, + quick-lint-js's lack of configuration means that it cannot enforce + style rules in a team. +
+ ++ Speed: As its name implies, quick-lint-js is fast. Low + latency is a goal because quick-lint-js lints as you type and does + not want to be distracting. Other linters struggle to hit 30 FPS, + and quick-lint-js easily passes 1000 FPS. quick-lint-js isn't + perfect, though; it currently cannot lint directories and does not + lint files in parallel. +
+ +@@@ benchmark chart
+ +When I created quick-lint-js, Deno Lint and Rome/Biome/RSLint didn't really + exist. If they had existed, I might have contributed to one of those + projects (probably Deno Lint). Today, it would be hard for me to + abandon quick-lint-js. Just thinking about it makes me sad, so let's + move on. 😆
+The future of JavaScript linters
+ +Biome started in February 2020. Deno Lint and quick-lint-js both + started in March 2020. RSLint (merged into Biome) started in September + 2020. Worldwide lockdowns sure gave people the time to start their + ESLint rewrites. 😅
+ +But what about 2024 and beyond? + Will the industry-standard ESLint prevail, or will + Deno Lint, Biome, or quick-lint-js dethrone ESLint? Or will even newer linters, + like OXC's + oxlint from February 2023, take the crown?
+ +The different linters of today have different strengths:
+-
+
- ESLint is highly extensible and allows teams to tune their + codebase however they want. +
- Deno Lint is the go-to option for developers making + Deno-based servers and scripts. +
- Biome is a tool to stop the fighting and make codebases + consistent. +
- quick-lint-js lets you find bugs in any codebase without + being overbearing or complicated. +
I predict that some of these linters will converge. Biome might adopt + an 'easy mode', removing the need for quick-lint-js. Deno might replace + Deno Lint with a pre-configured ESLint. quick-lint-js might add plugins + and complicated configs and eventually replace ESLint. oxlint might + obsolete everything else!
+ +It's hard to say what will happen exactly. But I suspect that the + linter landscape will change in the coming years. I hope that my + project (quick-lint-js) is useful today and will push other linters + forward.
+