Linting tools like ESLint have become an essential part of modern JavaScript development. They help enforce code quality, prevent bugs, and maintain consistent codebases across large teams. But few developers understand how lint rules work behind the scenes. At the core of this powerful system is a data structure known as the Abstract Syntax Tree (AST).
This article will demystify lint rules by showing how they interact with ASTs, provide examples, and even demonstrate how to write your own custom lint rule using ESLint.
What Is a Linter?
A linter is a static code analysis tool. It scans your code without executing it, looking for issues like:
- Syntax errors
- Bad practices
- Style violations
- Code smells
- Security flaws
Popular JavaScript linters:
- ESLint – Highly configurable and plugin-based.
- Prettier – An opinionated code formatter.
- JSHint – Lightweight and older alternative.
Example of ESLint in action:
var name = 'John'
console.log(name)
If your ESLint config enforces semicolons and disallows var
, it would report:
1:1 error Unexpected var, use let or const instead
2:17 error Missing semicolon
What Is an Abstract Syntax Tree (AST)?
An Abstract Syntax Tree is a structured, tree-like representation of your source code. It breaks your code into nested nodes, each representing a piece of syntax.
Code example:
const result = 4 + 6;
The AST will parse this into:
-
A
VariableDeclaration
node- With a
VariableDeclarator
node -
Identifier
:result
-
BinaryExpression
node-
left
:Literal
(4
) -
operator
:+
-
right
:Literal
(6
)
-
- With a
ASTs ignore formatting. Whether you use tabs, spaces, semicolons, or line breaks, the AST focuses solely on syntax and structure.
To visualize this, you can use AST Explorer.
How ESLint Uses ASTs to Enforce Rules
Here’s what happens when you run ESLint:
- Parsing: Your code is parsed into an AST using a parser like Espree.
- Traversing: ESLint walks the tree node by node.
- Rule Checking: ESLint applies each rule to relevant nodes.
- Reporting: If a rule detects a violation, it reports it (and may fix it).
Example: no-console
Rule
Code:
console.log('Debugging this line...');
ESLint:
- Finds a
CallExpression
node. - Sees that the callee is
console.log
. - Applies the
no-console
rule. - Returns a warning or error if it's disallowed.
Writing Your Own ESLint Rule
You can define a custom lint rule using the ESLint plugin system. Let’s write a simple rule that disallows identifiers named foo
.
1. Rule Definition
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'disallow variable named foo',
},
messages: {
noFoo: 'Avoid using "foo" as a variable name.',
}
},
create(context) {
return {
Identifier(node) {
if (node.name === 'foo') {
context.report({
node,
messageId: 'noFoo',
});
}
}
};
}
};
2. Sample Input That Triggers It
const foo = 10; // ESLint will report this line
3. ESLint Output
1:7 error Avoid using "foo" as a variable name.
Real AST Node Types You’ll Encounter
Here’s a cheat sheet of common node types:
Node Type | Description |
---|---|
Identifier |
Variable or function name |
Literal |
Strings, numbers, booleans |
BinaryExpression |
Math/logical ops like a + b
|
CallExpression |
Function calls like fn()
|
VariableDeclaration |
let , const , or var
|
FunctionDeclaration |
function foo() {} |
ArrowFunctionExpression |
const a = () => {} |
By targeting these node types, you can write ESLint rules to enforce or restrict just about anything.
Use Cases for Custom Lint Rules
- Enforce architectural conventions (e.g., prevent direct API calls in UI components)
-
Catch framework-specific mistakes (e.g., disallow
setState
in ReactuseEffect
) -
Create security checks (e.g., prevent use of
eval
,innerHTML
)
Writing custom rules ensures your entire team writes code that conforms to your organization’s expectations, beyond standard linting.
Experimenting With ASTs
Try this at AST Explorer:
Input:
const total = price + tax;
You'll see:
-
VariableDeclaration
at the top - Inside:
VariableDeclarator
with nametotal
- Right-hand side:
BinaryExpression
with leftprice
, operator+
, righttax
This shows exactly how tools see and navigate your code.
Summary
Linting tools are more than just static checkers — they're syntax tree traversers. With ASTs, they understand the structure of your code, not just the characters on the screen.
Key Takeaways:
- ASTs convert code into meaningful structures.
- ESLint rules walk through these structures and apply checks.
- You can create your own lint rules by targeting specific node types.
- AST tools empower you to build better, safer, and more maintainable JavaScript.
Learn JavaScript in Depth — Including ASTs, Tools & Projects
Want to dive even deeper into JavaScript development, including topics like ASTs, tooling, and advanced code techniques?
Explore my in-depth ebook:
JavaScript Complete Guide: Mastering Modern JavaScript With Projects
This book includes:
- Beginner to advanced JavaScript concepts
- Code samples and visual guides
- Hands-on projects to practice and solidify your skills
- Deep dives into how tools like ESLint, Babel, and Prettier work internally
Perfect for developers who want to move from just writing JavaScript to understanding it deeply.
Top comments (0)