Deep Dive into ESLint: How Rules, CLI, and AST Processing Work
This article explains why ESLint is essential for frontend development, details how its rule configuration works, walks through the CLI execution flow, and reveals the internal call stack that parses code into an AST, applies custom rules, and performs auto‑fixes.
ESLint Overview
In frontend development, ESLint is essential for enforcing code style and preventing bugs.
1. ESLint Rules
Rules are configured with numeric levels 0 (off), 1 (warn), 2 (error). Example configuration:
{
"rules": {
"arrow-body-style": 0,
"quotes": ["error", "single"]
}
}Each rule key corresponds to a specific check.
ESLint Core Rules
Understanding a rule’s structure (meta and create) is key to writing custom rules.
Example of a simple custom rule no-with:
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "disallow `with` statements",
recommended: true,
url: "https://eslint.org/docs/rules/no-with"
},
messages: {
unexpectedWith: "Unexpected use of 'with' statement."
}
},
create(context) {
return {
WithStatement(node) {
context.report({ node, messageId: "unexpectedWith" });
}
};
}
};A rule consists of meta (metadata) and create (function that returns visitor methods).
2. ESLint CLI Execution
Define a bin entry in package.json and create the executable file.
"bin": {
"eslint": "bin/eslint.js"
}
#!/usr/bin/env node
console.log("console.log output")The main CLI function parses arguments, creates a CLIEngine instance, and runs engine.executeOnFiles or engine.executeOnText.
execute(args, text) {
// parse options
const engine = new CLIEngine(translateOptions(currentOptions));
const report = useStdin
? engine.executeOnText(text, currentOptions.stdinFilename, true)
: engine.executeOnFiles(files);
return 0;
}3. Execution Call Stack
The execute flow leads to engine.executeOnFiles, which iterates files, reads content, and calls verifyText.
executeOnFiles(patterns) {
for (const { config, filePath, ignored } of fileEnumerator.iterateFiles(patterns)) {
const result = verifyText({ /* ... */ });
results.push(result);
}
} verifyTextinvokes verifyAndFix, which repeatedly applies fixes until no more changes or a maximum of ten passes.
do {
messages = this.verify(currentText, config, options);
fixedResult = SourceCodeFixer.applyFixes(currentText, messages, shouldFix);
if (messages.length === 1 && messages[0].fatal) break;
fixed = fixed || fixedResult.fixed;
currentText = fixedResult.output;
} while (fixedResult.fixed && passNumber < MAX_AUTOFIX_PASSES);During verification, the code is parsed into an AST (default parser espree), which can be replaced (e.g., @typescript-eslint/parser).
const espree = require("espree");
let parserName = DEFAULT_PARSER_NAME; // 'espree'
let parser = espree;4. Rule Execution Engine
The engine creates an event emitter, traverses the AST, registers rule listeners, and emits events for matching selectors.
const emitter = createEmitter();
Traverser.traverse(sourceCode.ast, {
enter(node, parent) { node.parent = parent; nodeQueue.push({ isEntering: true, node }); },
leave(node) { nodeQueue.push({ isEntering: false, node }); },
visitorKeys: sourceCode.visitorKeys
});
Object.keys(configuredRules).forEach(ruleId => {
const rule = ruleMapper(ruleId);
const ruleContext = { /* ... */ };
const ruleListeners = createRuleListeners(rule, ruleContext);
Object.keys(ruleListeners).forEach(selector => {
emitter.on(selector, addRuleErrorHandler(ruleListeners[selector]));
});
});The emitter triggers visitor callbacks during a second pass over nodeQueue, allowing rules to report problems.
5. Overall Mechanism
ESLint walks the AST, fires events at specific nodes or traversal moments, and rules can report errors or auto‑fix code.
Tips
Replace the default parser when ESLint cannot understand TypeScript syntax:
{
"parser": "@typescript-eslint/parser"
}The article provides a comprehensive view of ESLint’s internal workflow, useful for developers who want to extend or customize linting behavior.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
