How Linters Traverse AST Nodes in JavaScript
JavaScript code linters detect anti-patterns, security risks, and style violations by transforming raw source code into an Abstract Syntax Tree (AST) and systematically inspecting each node. By utilizing parsing algorithms and tree-traversal patterns—primarily the Visitor Pattern—linters examine structural code syntax, track variable scopes, and evaluate node relationships. This article explains the technical pipeline linters use to parse JavaScript, navigate AST nodes, and report or automatically fix code issues.
1. Parsing Source Code into an AST
Before traversal begins, the linter passes the raw source code string to a parser (such as Espree, Babel, or Acorn). Parsing occurs in two distinct phases:
- Lexical Analysis (Tokenization): The parser reads the character stream and converts it into a flat list of tokens, stripping whitespace and comments unless required for formatting rules.
- Syntactic Analysis: The parser validates the tokens against the JavaScript grammar specification (usually adhering to the ESTree standard) and organizes them into a hierarchical tree structure called an Abstract Syntax Tree (AST).
Each node in the AST represents a syntactic construct (e.g.,
FunctionDeclaration, BinaryExpression,
VariableDeclarator) and contains metadata such as line
numbers, column offsets, and references to child nodes.
2. Tree Traversal with the Visitor Pattern
Linters traverse the generated AST using a depth-first search (DFS) guided by the Visitor Pattern.
During traversal, the traversal engine emits events as it enters and leaves each node:
- Enter: The linter visits the node before processing any of its child nodes.
- Exit: The linter returns to the node after all of its descendants have been fully traversed.
Program (enter)
└── VariableDeclaration (enter)
└── VariableDeclarator (enter)
└── Identifier (enter -> exit)
└── VariableDeclarator (exit)
└── VariableDeclaration (exit)
Program (exit)
Rules register specific “listener” functions that trigger when the traversal engine encounters matching node types.
3. Node Matching and CSS-Style Selectors
Linters like ESLint use selector engines (such as
esquery) to allow rules to target specific structural
patterns in the AST, similar to CSS selectors targeting DOM nodes.
Rules can listen for: * Specific Node Types:
Listening directly to BinaryExpression or
AssignmentExpression. * Attribute Filters:
Matching nodes where specific properties hold true, such as
BinaryExpression[operator="=="]. * Relational
Selectors: Matching structures based on ancestry or siblings,
such as
CallExpression > MemberExpression[property.name="eval"].
4. Scope and Contextual Analysis
Static analysis often requires more than isolated node inspection. Linters maintain an internal state during traversal to track lexical scope, variable declarations, and execution paths.
- Scope Manager: Linters construct a tree of nested scopes (global, function, block). When a variable is defined, the linter records it in the current scope. When an identifier is referenced, the linter links it back to its declaration to flag issues like unused variables or shadow declarations.
- Code Path Analysis: Linters generate control-flow graphs (CFGs) to identify unreachable code, infinite loops, or paths that fail to return a value.
5. Flagging Anti-Patterns and Applying Fixes
When a rule’s conditions are met (for example, identifying a
== operator instead of ===), the rule invokes
a reporting method on the linter’s context object:
- Error Object Creation: The rule emits a diagnostic
report containing the error message, severity level (warning or error),
and precise source code coordinates (
loc). - Fixer Application: If the anti-pattern has an automatic fix, the rule provides a fixer function. The fixer specifies an atomic source code modification—such as replacing, inserting, or removing characters at specific text offsets—without invalidating neighboring code tokens.
Once the traversal completes, the linter aggregates all emitted reports and outputs the results to the terminal, IDE, or CI/CD pipeline.