@alxwlw/oxlint-config
The alxwlw lint canon for oxlint 1.x + oxlint-tsgolint, built for repos on
TypeScript 7 (typescript-eslint has no TS7-compatible compiler API until
TS 7.1 and upstream will not support 7.0; tsgolint embeds typescript-go, so
the repo's typescript version is irrelevant to linting).
Presets
| Preset | Extends | Adds |
|---|---|---|
base | — | correctness category, full type-aware set (tsgolint), simple-import-sort |
node | base | stack/naming-convention (canon selector set) |
nest | node | stack/require-nest-di-decorator; typescript/require-await off (Nest guard contract); typescript/consistent-type-imports off (DI-safe imports) |
react | base | react-hooks classic pair + eslint-plugin-react recommended + jsx-a11y recommended ports |
next | react | oxlint nextjs plugin |
library | — (overlay) | stack/tsdoc-syntax for TS/TSX; reads the nearest tsdoc.json |
base deliberately carries a curated strict subset beyond
typescript-eslint's recommendedTypeChecked defaults, proven at error level
in production (no-unnecessary-condition, no-deprecated,
restrict-template-expressions, …). Prettier remains a
separate gate (oxlint does not format); dependency-cruiser remains the
architecture/cycle engine (import/no-cycle stays off).
Usage
// .oxlintrc.json (repo root)
{
"extends": ["./node_modules/@alxwlw/oxlint-config/nest.jsonc"],
"ignorePatterns": ["**/dist/**", "**/*.generated.*"],
}
oxlint --type-aware --report-unused-disable-directives --config .oxlintrc.json .
Packages that publish an API layer the library overlay after their preset (not
usable on its own — the stack plugin comes from the preset):
// .oxlintrc.json of the publishing package
{
"extends": [
"./node_modules/@alxwlw/oxlint-config/node.jsonc",
"./node_modules/@alxwlw/oxlint-config/library.jsonc",
],
}
Declare your own TSDoc tags in tsdoc.json instead of switching the rule off; the file needs the standard "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json" (without it tsdoc-config reports Unsupported JSON "$schema" value).
The overlay goes after the preset on purpose: it does not extend base, so it cannot
switch back on what nest or react turned off.
For browser code in a mixed repo, drop a nested .oxlintrc.json extending
react.jsonc into the frontend root — oxlint auto-discovers nested configs per
subtree (a nested config REPLACES the root one for that subtree, which is why
react.jsonc extends base.jsonc itself).
Sharp edges (all empirically verified)
--type-awareis load-bearing. Without the flag everytypescript/*type-aware rule is SILENTLY skipped — no error, no warning. Keep the flag in the repo's lint task;oxlint-tsgolintmust be installed (optional peer).- Build
.d.tsbefore type-aware lint in project-reference monorepos (tsc --buildfirst; the canon moon-taskslinthas no typecheck dependency, so this applies to project-reference repos): unresolved imports produceerror-typed values that fire theno-unsafe-*rules as phantoms. pluginsmerge acrossextends— a consumer that adds a plugin lists only the new one. The presets restate their full list anyway, so each reads as one file (seereact.jsonc);oxlint-config.test.tskeeps them complete.- Never run
--fix-suggestionsin automation: tsgolint'srequire-await"suggestion" rewrites public signatures (Promise<T>→T). Plain--fixis safe; outsidenest(which turnsconsistent-type-importsoff), theconsistent-type-imports×import/no-duplicatesfixers can collide on one import pair — a second--fixpass converges. - Out-of-program files (tests, tool config files, plain JS) are linted under an
inferred strict program by tsgolint;
base.jsoncmirrors tseslint'sdisableTypeCheckedfor the universal globs — extend that override in the consuming repo for repo-specific out-of-program trees (scripts/,e2e/, bundler-frontend subdirs). Tool config files are an explicit list (vite/vitest/playwright/…); productionsrc/**/*.config.tsstays type-checked. import typeconversions can break NestJS DI:nest.jsoncturns offtypescript/consistent-type-imports— oxlint does not modelemitDecoratorMetadata, and the rule's autofix used to convert the import of a class injected by ctor-param type intoimport type(Nest DI then fails at startup while tsc and lint stay green). A Nest consumer no longer needs its own override; animport typeof an injected class in Nest code is an error only a startup catches. An explicit@Inject(Class)(this canon's convention) remains an optional way to make the dependency explicit.
The stack JS plugin
./plugin exports the in-house rules (ESLint rule API, run by oxlint's
jsPlugins host — requires running oxlint via the npm CLI, which every moon
task does):
stack/require-nest-di-decorator— ported from a private ESLint-rule original; seeNOTICEfor provenance. The two are no longer kept in lockstep and will drift.stack/naming-convention— port of the canon naming selector set; option{ "leadingUnderscore": "none" | "allow" | "allowSingleOrDouble" }for repos with a_-prefix private-by-convention marker.stack/tsdoc-syntax— enabled by thelibraryoverlay; reads the nearesttsdoc.json(like eslint-plugin-tsdoc).@microsoft/tsdoc-backed TSDoc validation (eslint-plugin-tsdoc ≥0.5 requires theeslintpackage at load time, which oxlint consumers no longer install).