Node.js API
The Node.js API lets you run rslint programmatically — lint files or in-memory source from a script, an editor integration, or a build tool. Its surface is aligned with ESLint's v10 API shape, so most ESLint API code ports over with minimal changes.
All config resolution (override config, config-file selection, discovery, normalization) happens in JavaScript; rslint's engine receives only the final resolved config object and never reads config from disk.
Getting started
new Rslint(options) creates a linter instance. Both lintFiles and lintText are async and return an ESLint-shaped LintResult[].
Linting files
lintFiles takes one or more glob patterns (resolved against cwd) and lints every matching file. By default the nearest config is auto-discovered from cwd.
Results are ordered by the linted file's path (deterministic), not by glob-walk order.
If no file matches the patterns, lintFiles returns an empty array rather than throwing — unlike ESLint v10, whose default errorOnUnmatchedPattern throws on an unmatched glob.
Linting a string
lintText lints an in-memory string as if it lived at filePath:
lintText always returns exactly one result — for the linted buffer. If you omit filePath, the result's filePath is the "<text>" sentinel (matching ESLint).
Fully in-memory linting
By default lintText still reads the config and tsconfig from disk. To lint with no disk access at all — source, config, and tsconfig all in memory — combine overrideConfigFile: true (use only the inline config), an inline overrideConfig, and a virtualFiles overlay:
virtualFiles is an in-memory file overlay (path → content) — an rslint extension; ESLint has no in-memory file map. Type-aware rules run against it with no disk access: put the tsconfig.json that parserOptions.project names, plus any dependency files, in the overlay.
Declaring plugins. A rule from a plugin (@typescript-eslint/*, unicorn/*, and so on) runs only when that plugin is listed in plugins — rslint enforces this exactly like ESLint. Core rules (no / prefix) need no declaration.
Type-aware vs syntax-only rules. The tsconfig.json and parserOptions.project matter only for type-aware rules, which need a real TypeScript program (see Type Checking). If your config has only syntax-only rules, you can drop both — no tsconfig, no parserOptions.project.
Use relative paths in virtualFiles keys and inside the tsconfig:
virtualFileskeys: prefer relative paths ('tsconfig.json'). Keys are resolved againstcwd, so a relative key always lines up withparserOptions.project(also resolved againstcwd). An absolute key like'/tsconfig.json'happens to match only whencwdis/; with any othercwdit lands at the filesystem root and won't match the project path.- Inside the tsconfig (
files) and inparserOptions.project: use relative paths. The TypeScript compiler resolves these, and a bare POSIX-absolute path (such as/a.ts) has no drive letter on Windows, so it won't match the overlay.
Pin the tsconfig to explicit files — a broad include glob is expanded against the real filesystem and would scan cwd on disk.
Auto-fixing
Pass fix: true. A result whose file a fix changed then carries an output string — the full fixed source; results with no applied fix have no output.
Write fixes to disk with the static Rslint.outputFixes:
Rslint.outputFixes writes back only results whose filePath is absolute. A lintText result is absolute — and so will be written — when you pass a filePath; only a result with no filePath (the non-absolute "<text>" sentinel) is skipped.
Apply fixes in memory — to fix without touching disk, read output directly and don't call outputFixes:
lintText with fix: true never writes to disk — the fixed source comes back as result.output. For edit-level control, each result.messages[].fix is a { range: [start, end], text } edit (UTF-16 offsets) you can splice into the source yourself; for more than one fix prefer output, which is already the safely merged whole-file result.
Lifecycle
Each Rslint instance owns a long-lived rslint engine child process. You don't need to call close() — like ESLint, a one-off script exits cleanly on its own (the idle child is unref'd, so it never blocks the event loop).
Call close() only in a long-running host (an editor server, a watch process) that creates many instances, to free each child promptly:
Or use await using for automatic disposal at the end of scope:
Native await using needs a runtime with explicit resource management support, which Node 22 lacks (a bare .mjs throws a SyntaxError). Compile with a using-aware toolchain such as TypeScript 5.2+, or use the try / finally form above, which runs everywhere.
Result shape
Both methods resolve to LintResult[]:
Each LintMessage:
Options
new Rslint(options) accepts: