Documentation 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.
effect-oxlint is built on Effect v4 idioms throughout. Understanding the handful of patterns the library relies on will make reading and writing rules feel natural. This page walks through each pattern with examples drawn directly from the source.
Effect.gen with function*
Thecreate field of Rule.define is a generator function. Inside it you can yield* any Effect to retrieve services, allocate Ref state, or run effectful combinators before returning the visitor map.
function* syntax is required — arrow-function generators (() => {}) are not valid generator syntax, and Effect.gen expects function*.
Option for absence
Every AST matcher ineffect-oxlint returns Option<T> rather than T | null. This eliminates null-check branches and makes chains composable.
Ref for mutable state
Replacelet depth = 0 counters with Ref.make and Ref.update. A Ref allocated in create is closed over by all handler functions, so state persists across the entire file traversal.
pipe for composition
pipe from effect/Function threads a value through a sequence of functions left to right. It is the idiomatic way to chain Option matchers and build visitor logic without deep nesting.
AST.matchMember('JSON', 'parse') with no node argument) return a curried function that pipe can pass the node through. See Dual API below.
Effect.runSync at boundaries
Effect.runSync is the bridge between oxlint’s synchronous plugin API and the Effect world. In Rule.define, it is called in two places:
- Once per file — to run the
creategenerator, allocateRefstate, and build the visitor map. - Once per node — inside each visitor handler, to execute the Effect returned by the handler.
Effect.runSync yourself. It is an internal detail of Rule.define. The consequence, however, is important: because Effect.runSync cannot surface typed failures, both create and every handler must have error channel never. See Handler error channel and fallible effects for how to handle fallible sub-effects.
Context.Service for RuleContext
RuleContext is defined as a Context.Service. Inside any create generator or visitor handler, yield* RuleContext retrieves the current lint context — the file name, source code, and report function.
Rule.define before Effect.runSync is called, so it is always available inside create and handlers without any extra setup.
Dual API pattern
Public combinators inAST and Visitor expose two calling styles through Effect’s dual function:
- Data-first — pass the subject node as the first argument (useful for one-off calls)
- Data-last — omit the node argument and receive a curried function (useful inside
pipe)
dual helper in effect/Function inspects the argument count at runtime and routes to the correct overload, so both forms share a single implementation.
Effect.void for no-ops
When anonNone branch (or any branch) has nothing to do, return Effect.void rather than Effect.succeed(undefined). This is the idiomatic no-op in Effect v4 and signals clearly that the result is intentionally discarded.
Module import aliases
The canonical import aliases used throughouteffect-oxlint are:
Why aliases instead of Array.prototype methods?
Why aliases instead of Array.prototype methods?
Arr.map, Arr.filter, and Arr.reduce are pure functions that operate on ReadonlyArray values. They compose cleanly inside pipe chains and align with the rest of the Effect ecosystem. Native Array.prototype methods require a method receiver, cannot be used as curried arguments to pipe, and may mutate in-place.The same reasoning applies to R.* over Object.*, Str.* over String.prototype.*, and P.isString over raw typeof checks.Handler error channel
Why handlers must have error channel
never and how to deal with fallible sub-effects.Assembling a plugin
How to collect your rules into an oxlint plugin with
Plugin.define and Plugin.merge.