Visitors in effect-oxlint are plainDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/mpsuesser/effect-oxlint/llms.txt
Use this file to discover all available pages before exploring further.
Record<string, (node: ESTree.Node) => Effect<void>> maps. Instead of writing handlers as standalone callbacks with hidden mutable state, the Visitor module gives you composable combinators that wire up Ref-based counters, file-conditional gating, and collect-then-analyze patterns — all expressible as pure data transformations.
What visitors are
AnEffectVisitor is a record that maps AST node type names (e.g. 'CallExpression') and exit variants (e.g. 'CallExpression:exit') to effectful handlers:
never. The runtime boundary in Rule.define executes handlers synchronously via Effect.runSync, so failures must be caught inside the handler. See the EffectHandler JSDoc in src/Visitor.ts for the recommended Effect.catch pattern.
Visitor.on — single enter-phase handler
Visitor.on(nodeType, handler) creates a one-entry visitor for the enter phase of a node visit. When the nodeType string is a known oxlint visitor key, the handler parameter is automatically narrowed to the corresponding ESTree node type.
Visitor.onExit — single exit-phase handler
Visitor.onExit(nodeType, handler) is identical to Visitor.on but registers the handler under "NodeType:exit", which runs after all child nodes have been visited.
Visitor.merge — combining multiple visitors
Visitor.merge(...visitors) merges an arbitrary number of EffectVisitor objects into one. When two visitors handle the same node type, both handlers run sequentially (left to right).
Rule.banMultiple combines its per-pattern sub-visitors internally, and how you can compose independently-written rule fragments without coordination.
Handler order within a merged visitor is deterministic: left-to-right in the argument list. Both handlers always run — there is no short-circuit on the first match.
Visitor.tracked — Ref-based depth counter
Visitor.tracked(nodeType, predicate, ref) replaces the common let depth = 0 mutable counter pattern. It creates a matching enter/exit visitor pair: the Ref<number> is incremented on enter when the predicate returns true, and decremented on exit.
node is ESTree.CallExpression inside the 'CallExpression' tracker. The ref value is typically merged with other visitors:
Visitor.filter — conditional visitors by filename
Visitor.filter(predicate, visitor) evaluates a predicate against the current filename at create time. If the predicate returns false, an empty visitor is returned — the handlers are never registered for that file.
filter supports the dual API: pass both arguments for data-first, or pass only the predicate to get a data-last function suitable for pipe.
Visitor.accumulate — collect then analyze
Visitor.accumulate(nodeType, extract, analyze) implements the collect-then-analyze pattern: values are gathered during traversal, then passed to an analyzer at Program:exit. This is useful for rules that need to see all occurrences before deciding whether to report.
extract: called for each node ofnodeType; returnsOption<A>.Option.none()is silently skipped.analyze: an Effect generator receiving the collectedReadonlyArray<A>atProgram:exit.
accumulate creates a Ref<ReadonlyArray<A>> and wires it into an enter-phase visitor plus a Program:exit handler via Visitor.merge.
TypedEffectVisitor vs EffectVisitor
When you return an object literal fromcreate, TypeScript treats it as a TypedEffectVisitor. Known visitor keys (those present in the oxlint Visitor type, such as 'MemberExpression' or 'CallExpression') automatically narrow the handler’s node parameter to the matching ESTree type:
EffectVisitor is the internal representation used by combinators — it accepts ESTree.Node for all keys. The variance is safe at runtime because oxlint guarantees each handler receives the node type that matches its key.