How ESLint Detects Unnecessary Lodash Chaining
Lodash chaining provides a fluent API for sequencing data
transformations, but wrapping values in a Lodash wrapper for solitary
operations introduces runtime overhead and prevents effective
tree-shaking. This article examines how
eslint-plugin-lodash parses the Abstract Syntax Tree (AST)
to identify redundant chaining, evaluates call depth, and alerts
developers to simplify their code into direct method invocations.
Abstract Syntax Tree Traversal
ESLint operates by parsing JavaScript source code into an Abstract
Syntax Tree (AST) via parsers like Espree.
eslint-plugin-lodash hooks into this process using visitor
functions that inspect specific AST node types, primarily
CallExpression and MemberExpression.
When code executes a chained operation such as
_chain(items).map(fn).value() or
_(items).filter(fn).value(), the AST represents this
structure as a nested hierarchy of method calls:
- The root
CallExpressionrepresents.value(). - The
calleeof that call is aMemberExpressionpointing to the output of the preceding operation (e.g.,.map(fn)). - The chain continues recursively down to the initial wrapper call
(
_.chain(...)or_(...)).
Scope and Identifier Resolution
Before checking chain length, eslint-plugin-lodash
verifies that the invoked object actually originates from the Lodash
library. The plugin uses ESLint's built-in Scope manager to
trace identifiers. It inspects:
- Import declarations, such as
import _ from 'lodash'orimport { chain } from 'lodash'. - CommonJS require statements, such as
const _ = require('lodash'). - Global variable declarations if Lodash is configured as a global.
If an identifier does not resolve to the designated Lodash import or variable, the plugin ignores the call, preventing false positives on native method chains or other libraries with similar chaining patterns.
Counting Intermediate Transformations
The core detection logic relies on analyzing the depth of the chain
between the initialization and the terminal .value() or
unwrapping step. Rules such as lodash/chain-style enforce
specific architectural patterns (explicit, implicit, or no
chaining).
To catch unnecessary chaining:
- Step Identification: When the linter encounters a
terminal call (like
.value()), it traverses up the AST branch, collecting each intermediateMemberExpressionandCallExpression. - Operation Counting: The plugin counts how many transformation steps occur between the creation of the wrapper and the extraction of the value.
- Threshold Evaluation: If the chain consists of only
zero or one transformation (for example,
_.chain(users).map(getName).value()), the chain wrapper provides no functional utility over the direct call_.map(users, getName).
Warning Generation and Autofixing
When the AST walker identifies a chain that violates the configured
threshold or rule constraints, eslint-plugin-lodash calls
ESLint's context.report() API.
The report contains:
- The precise line and column numbers of the redundant wrapper.
- A message explaining that the single chained call should be replaced with a direct method invocation.
- An optional fixer function that rewrites the AST node by unwrapping
the initial argument and passing it as the first parameter to the direct
method, transforming
_.chain(data).filter(predicate).value()into_.filter(data, predicate).