A high-performance linter for JavaScript and TypeScript, written in Zig and powered by Yuku.
utoo-lint is published on npm as
@utoo/lint. It combines a native
lint engine with familiar configuration, CLI, and JavaScript APIs.
Installation · Quick start · Rules · Configuration · ESLint migration
- Native performance — Zig and Yuku power parsing, semantic analysis, and parallel file linting.
- Broad rule coverage — More than 300 rules cover JavaScript, TypeScript, React, JSX accessibility, and imports.
- Familiar configuration — Use typed
utlint.config.tsor staticutlint.config.json, with ESLint-style rule names and severities. - Practical workflows — Safe autofix, suppression comments, JSON output, and ESM/CommonJS APIs are included.
- Portable installation — Prebuilt binaries are available for macOS, Linux, and Windows on supported architectures.
The benchmark suite compares fresh CLI runs of utoo-lint, Oxlint, Biome, and ESLint on the same generated TypeScript corpus and shared rule set.
See the benchmark methodology to reproduce the results and understand what is included in the comparison.
@utoo/lint requires Node.js 20 or later.
utoo is a fast, npm-compatible package manager written in Rust. Install it once if it is not already available:
npm install -g utooAdd @utoo/lint as a development dependency:
ut install @utoo/lint -DIn an existing project, run ut install without a package name to install all
dependencies declared in package.json.
pnpm add -D @utoo/lint
npm install --save-dev @utoo/lintThe npm package selects the native binary for the current platform. No Zig toolchain is required when using the published package.
Point utoo-lint at a file or directory:
utx @utoo/lint srcWithout a config file, utoo-lint uses its built-in default rules. It supports
.js, .jsx, .ts, .tsx, .mjs, .cjs, .mts, and .cts files.
Create utlint.config.ts:
import { defineConfig, globalIgnores } from "@utoo/lint/config";
import frontend from "@utoo/lint/configs/frontend";
export default defineConfig(
globalIgnores(["dist/", "coverage/"]),
{
...frontend,
files: ["src/**/*.{js,jsx,ts,tsx}"],
rules: {
...frontend.rules,
"no-console": "warn"
}
}
);Run the linter again without repeating the target:
utx @utoo/lintThe CLI discovers the nearest utlint.config.ts or utlint.config.json and
uses its files patterns. See the configuration guide
for flat config arrays, global ignores, rule options, and config precedence.
{
"scripts": {
"lint": "utoo-lint src",
"lint:fix": "utoo-lint --fix src"
}
}With utoo, run these scripts as ut lint and ut lint:fix.
| Task | Command |
|---|---|
| Lint files | utx @utoo/lint src |
| Apply safe fixes | utx @utoo/lint --fix src |
| Preview fixes as JSON | utx @utoo/lint --fix-dry-run --format=json src |
| Select rules for one run | utx @utoo/lint --rules=no-debugger,no-unused-vars src |
| Use an explicit config | utx @utoo/lint --config=utlint.config.json src |
Run utx @utoo/lint --help for all CLI options.
utoo-lint supports two canonical config formats. They are alternative
representations of one active config and are not merged together.
| Config | Best for | Raw native binary |
|---|---|---|
utlint.config.ts |
Typed authoring, imports, presets, and computed values | No |
utlint.config.json |
Static configuration and JSON Schema validation | Yes |
Rule severities follow ESLint conventions: off, warn, error, 0, 1,
and 2. A selected config enables only the rules present in its resolved
rules map.
The npm CLI and JavaScript API load both config formats. The raw native binary loads JSON only. See the configuration guide for the complete behavior.
Apply safe fixes in place:
utx @utoo/lint --fix srcPreview fixes without writing files:
utx @utoo/lint --fix-dry-run --format=json srcFixes run until the source is stable, subject to a safety pass limit. The rule status documents autofix support for each rule.
Use suppression comments for intentional exceptions without disabling a rule in project configuration:
// utlint-ignore no-debugger: generated breakpoint
debugger;| Directive | Scope |
|---|---|
utlint-ignore [rule] |
The next line of code |
utlint-ignore-all [rule] |
The entire file when placed before any code |
utlint-ignore-start [rule] |
The start of a suppressed range |
utlint-ignore-end [rule] |
The end of a suppressed range |
A directive may target one rule ID. Omitting it creates or closes an all-rules suppression. Parse errors are never suppressed, and suppressed fixes are not applied.
See the suppression comments guide for range examples and API behavior.
Convert an existing ESLint config into a native utoo-lint config:
utx @utoo/lint migrate eslint \
--from eslint.config.js \
--output utlint.config.jsonThe package also exposes eslint and fishlint compatibility commands for
incremental replacement workflows. See Migrating from ESLint
for supported mappings and known differences.
The package provides ESM and CommonJS entry points:
import { lintFiles } from "@utoo/lint";
const report = lintFiles(["src"], {
config: "utlint.config.ts"
});
console.log(report.diagnostics);An ESLint-style API is also available:
import { ESLint } from "@utoo/lint";
const eslint = new ESLint({ fix: true });
const results = await eslint.lintFiles(["src"]);
await ESLint.outputFixes(results);Raw reports include active diagnostics, suppressed diagnostics, fixed outputs,
and an exit code. The compatibility layer also exports Linter, CLIEngine,
RuleTester, and SourceCode APIs.
- Rule status — implemented rules and autofix coverage
- Configuration — config discovery, matching, and rule values
- Suppression comments — line, file, and range suppressions
- Migrating from ESLint — migration workflow and compatibility notes
- Contributing — local development, rule work, and releases
- Security policy — privately report a vulnerability
Contributions are welcome. Read CONTRIBUTING.md before opening a pull request. The native engine is built with Zig, and Yuku is pinned as a git submodule for reproducible parser behavior.

