Your boyfriend has messed up. Royally. He knows it. You know it. The neighbor knows it. Everyone and their mother knows it!
The words form in your head -
I. Told. You. So.
And then, this other voice, "Don't be a dick, Anju" seemingly comes out of nowhere!
And then, dang! You swallow the sentence.
Congratulations. You’re basically Mother Teresa.
Just… hotter.
Next scenario: The man cuts you off in traffic.
Your hand is already on the horn.
A very specific two-word insult involving his mother... is loading.
And again: Don't be a dick, Anju.
Hand moves away, curse dies in your throat. You’re Buddha baby! You are practically levitating.
Or when you’ve crafted the perfect insult.
You know the kind: Elegant. Perfect. A gift from the universe. Frame-worthy!
And right before you say it... you get a vision of future-you; rocking back and forth at 2:30 a.m., ruminating, feeling guilty. Forever. Until eternity.
The insult is swallowed. The masterpiece lost to history. Did I mention already? Yes. I am Jesus.
The Vatican has been notified.
That voice? That sanctimonious, holier-than-thou voice?
That's the linter for you!
Fixing legacy lint errors at work. You know how it goes... you fix one error, you write a blog.
For frontend code, ESLint is the dominant choice.
It's the Obi-Wan Kenobi. Strict older brother energy. But secretly caring.
Obi-Wan doesn’t live inside the IDE. He lives away from Luke but in the same desert.
Let's talk: Job Description.
In VS Code, ESLint parses every debounced keystroke and converts javascript into AST. It takes your tiny little
const x = 1
and turns it into AST (Nodes. Relationships. Structure... a complete ancestry report). A whole crime scene - over 1 variable. Iconic! 😏
Before it starts traversing the AST, ESLint's already mapped your codebase into a scope tree (basically tracking which variables live where, and who can access what).
Each rule registers for the only node type it truly cares about. Very committed. And voila! The listener map is ready.
During the walk, the listener map applies the rules and these rules with the help of the scope tree decide whether to call report().
Think of it as:
If a CallExpression appears, wake up only RuleA. Joker walks in, only Batman stands up.
{
CallExpression: [ruleA],
Identifier: [ruleC],
VariableDeclaration: [ruleD]
}
You might already know this part... remember Leetcode? DFS? Visitor pattern? That's the algorithm ESLint uses to walk AST.
(Your little const x = 1 you ask... triggers nothing.)
ESLint walks past it like: Not today baby!
Plugins - the Jedis who do the actual sparring
ESLint itself is more like a sitting member of the jedi council. The Jedis that participate in the clone war, that’s all plugins!
eslint-plugin-react (Anakin), eslint-plugin-unicorn (Mace Windu), etc...
Bienvenido Grogu.
Today, let's write a linter... for the heck of it.
Not the Obi-Wan. He's seen wars. Just a bite-sized 'baby Yoda'.
Our force can catch every rogue console.log left behind in code.
Here's the thing about Grogu though - he’s not the sharpest tool in the shed.
Grogu logic goes something like - see a frog → eat the frog.
It visits every node in the AST and calls report() whenever it finds console.log.
Here's the whole traversal - super simple
// The traversal accepts AST and the map only bothers itself with 'CallExpression' node. The only Jedi assigned to that node is 'noConsoleLog'.
walk(ast, {
CallExpression(node) {
noConsoleLog(node, report);
},
});
function walk(node, visitor) {
// Base case: if this isn't a real AST node, return early
if (!node || typeof node !== "object") return;
// If we have a Jedi registered for this node type, fire it
if (visitor[node.type]) {
visitor[node.type](node);
}
// Recurse through all it's children just to be sure
for (const key of Object.keys(node)) {
walk(node[key], visitor);
}
}
Grogu only complains. ESLint however is the adult and so takes care of the clean up if urged.
Pass --fix and it gathers all the fixes and applies them at the end.
Calling npm link makes it globally accessible on your machine.
And... that's it. Your very own prickly little conscience is ready to harass your repo.
Word travels fast 'round these parts. And the word is, there's new kids in town.
Biome and Oxlint are written in Rust. On a 1,000-file repo, ESLint might take 40 seconds. Oxlint does it in under 2s flat! Reason being ESLint is JavaScript. So, single-threaded.
These new plugin ecosystems are however still catching up. I tried Biome on a personal project and liked it. It is worth your time. YouTube has solid tutorials if you want to go deeper. I like this one - https://www.youtube.com/watch?v=x5kwmIaPty8&t=48s
Just fyi, ESLint did hold the fort down for 13 years (So that kind of time does help you load up on ammunition)
In a nutshell:
Linters don't cut you off at the bar. They just hold your hair back while you vomit.
Baby linter repo is here.
Grogu is thriving.
I sent the drunk text.
We don't talk about it.
Linter agrees.
Ship the code!👩💻
Top comments (0)