@aurelienbbn/oxlint-config

中文文档
0.8.0发布于 2026/09/30MITTypeScriptES Module零依赖构建来源已验证近 30 天下载 2100

Opinionated strict reusable oxlint config for TypeScript projects.

原始使用文档
文档目录6 节
  1. 1 preset, 5 opt-ins
  2. What the preset sets
  3. Every conflict is settled once
  4. Vague test titles fail
  5. Opt-in layers
  6. Contract & migration

@aurelienbbn/oxlint-config

npm downloads CI license node

Strict oxlint for TypeScript: every stable category at error, type-aware, warnings denied, every rule conflict settled.

pnpm add -D @aurelienbbn/oxlint-config oxlint oxlint-tsgolint
# oxlint >=1.82.0 <2.0.0 · oxlint-tsgolint ^7.0.2001 · Node ^22.19.0 || ^24.11.0
// oxlint.config.ts
import { defineStrictOxlintConfig } from "@aurelienbbn/oxlint-config";
import { defineConfig } from "oxlint";

export default defineConfig(
  defineStrictOxlintConfig({
    ignorePatterns: ["dist/**"],
  }),
);

Returns a plain OxlintConfig: works with oxlint directly or as Vite+'s lint config, no Vite+ dependency.

1 preset, 5 opt-ins

ExportAdds
strictOxlintConfigthe preset object
defineStrictOxlintConfig(overrides?, options?)preset + your overrides, cloned
withTanstackQueryLayer(config?, options?)🔌 official @tanstack/eslint-plugin-query, 7 rules
withEffectTsgoLayer(config, { preset })🔌 official @effect/tsgo preset + 4 owner rules
withImportGraphLayer(config?, { entrypoints? })🔌 import/no-cycle + import/no-self-import
layerDirectionOverride({ files, forbidden, message })🔌 one overrides entry banning cross-layer imports
nurseryCandidateRules🔌 4 nursery rules to trial

Also: testFileGlobs, vagueTestTitlePattern, tanstackQueryRules, tanstackQueryPluginSpecifier, effectTsgoPluginName, effectTsgoOwnerRules, effectTsgoSettledRules, effectIdiomRules, importGraphRules, types OxlintConfig, ImportGraphLayerOptions.

What the preset sets

correctness  ██████  error      style       ██████  error
pedantic     ██████  error      suspicious  ██████  error
perf         ██████  error      nursery     ░░░░░░  off (pending upgrade review)
restriction  ██████  error

vitest never runs on source files, only on testFileGlobs: vitest/require-hook reports ordinary top-level calls.

Plugins, options, pinned rules
KeyValue
pluginseslint, typescript, unicorn, oxc, node, promise
optionstypeAware: true, typeCheck: true, denyWarnings: true, reportUnusedDisableDirectives: "error"
envbuiltin: true, node: true
overrides[0]vitest plugin on testFileGlobs = **/*.{test,spec}.{ts,tsx,mts,cts,js,jsx,mjs,cjs}

plugins replaces oxlint's default set, so the four defaults (eslint, typescript, unicorn, oxc) are re-listed before node and promise. Other test layout: add an overrides entry with plugins: ["vitest"].

RuleSettingWhy
typescript/ban-ts-commentts-ignore banned, ts-expect-error needs descriptiona suppression says why
typescript/no-explicit-any, typescript/no-non-null-assertionerrorpinned by name
unicorn/filename-casekebabCaseone naming scheme
eslint/no-underscore-dangleallow _tag, _tree, __dirnametagged unions, Node globals
eslint/max-lines300, blank and comment lines skippedmeasures code, not docs or formatter reflow
eslint/sort-keysoffkey order carries meaning: schema fields, table columns, read-in-order config
eslint/one-varnevergrouping makes later edits noisier
oxc/no-async-await, oxc/no-optional-chaining, oxc/no-rest-spread-propertiesoffban modern syntax, not defects
vitest/no-standalone-expectoff (test files)
typescript/no-unnecessary-conditionerror by name (nursery off)needs strictNullChecks; noUncheckedIndexedAccess keeps indexed access quiet
typescript/switch-exhaustiveness-checkallowDefaultCaseForExhaustiveSwitch: false, requireDefaultForNonUnion: trueunion switch: every member, no default; other switch: default required

nurseryCandidateRules (not in the preset; spread into rules to trial): typescript/prefer-optional-chain, eslint/no-useless-assignment, eslint/no-unreachable-loop, promise/no-return-in-finally.

Every conflict is settled once

❌ Off✅ KeptWhy
eslint/no-ternaryunicorn/prefer-ternary, eslint/no-nested-ternaryno-let code needs expressions; nesting stays banned
node/no-top-level-awaitunicorn/prefer-top-level-awaitapp entry points; libraries re-enable the node rule
vitest/no-importing-vitest-globalsvitest/prefer-importing-vitest-globalsimports stay visible to import-keyed rules
vitest/prefer-to-be-truthy, vitest/prefer-to-be-falsyvitest/prefer-strict-boolean-matcherstoBe(true) is stricter
eslint/no-undefinedunicorn/no-null, unicorn/no-useless-undefinedbanning both null and undefined leaves no way to express absence
eslint/no-warning-comments—rejects tracked TODO(#123):; core/no-dead-comments reports untracked only
eslint/max-lines-per-functioneslint/max-statementscounts physical lines, so a narrower print width fails it; statements don't
eslint/default-casetypescript/switch-exhaustiveness-checkdefault hides new union members
eslint/require-awaittypescript/require-awaittyped extension
eslint/no-implied-evaltypescript/no-implied-evaltyped extension
eslint/prefer-promise-reject-errorstypescript/prefer-promise-reject-errorstyped extension
eslint/no-throw-literaltypescript/only-throw-errortyped extension
unicorn/prefer-includestypescript/prefer-includestyped duplicate
unicorn/prefer-string-starts-ends-withtypescript/prefer-string-starts-ends-withtyped duplicate
unicorn/prefer-array-findtypescript/prefer-findtyped duplicate
eslint/no-magic-numbers (test files only)vitest/prefer-expect-assertionsexpected values are the oracle and stay literal; expect.assertions(n) too

[!WARNING] The typed side wins only because typeAware: true. Turn it off: re-enable the untyped rules.

Vague test titles fail

it("works", …)                            // ❌ bare "works" / "should work", any case
it("adds items correctly", …)             // ❌ correctly / properly / as expected
it("rejects improperly signed tokens", …) // ✅
Test-file rules pinned by name, and the oxlint 1.82.0 mustNotMatch probe

Pinned so an upstream category move can't drop them:

RuleOwns
vitest/require-to-throw-messagebare toThrow() / rejects.toThrow() (core/no-weak-test-assertions skips this)
vitest/prefer-called-withbare toHaveBeenCalled(): makes existing interaction assertions exact, doesn't create them
vitest/valid-titlemustNotMatch: vagueTestTitlePattern
vitest/require-hooktop-level setup; allowedFunctionCalls admits the four Harness *Conformance({ root }) registrars

Whether to assert a call at all: agentlint core/test-behavior-coverage. toHaveBeenCalledWith(expect.anything()): core/no-weak-test-assertions.

mustNotMatch form (oxlint 1.82.0)Fires?
plain string✅
{ it: "<regex>" }✅
[regex, message] tuple✅
any form + non-empty disallowedWords❌ ignored
pattern with \b or \W❌ never matches

So filler words share one pattern with [^a-z] word edges. Overriding: keep one mustNotMatch, leave disallowedWords unset. src/index.test.ts runs the installed oxlint against these cases.

Opt-in layers

import {
  defineStrictOxlintConfig,
  layerDirectionOverride,
  withImportGraphLayer,
  withTanstackQueryLayer,
} from "@aurelienbbn/oxlint-config";
import { defineConfig } from "oxlint";

export default defineConfig(
  withImportGraphLayer(
    withTanstackQueryLayer(
      defineStrictOxlintConfig({
        overrides: [
          layerDirectionOverride({
            files: ["**/domain/**"],
            forbidden: ["**/infra/**", "express"],
            message: "Domain never imports infrastructure.",
          }),
        ],
      }),
    ),
  ),
);

Rules already set on config win over any layer.

[!WARNING] oxlint replaces rule options, it doesn't merge them. Two overrides matching one file: the later eslint/no-restricted-imports wins, and either replaces a root-level one. One entry per layer glob, listing everything it must not reach.

TanStack Query layer: install, probe results, companion plugins
pnpm add -D @tanstack/eslint-plugin-query   # not a dependency of this package

Appends the plugin to jsPlugins, sets tanstackQueryRules to error.

@tanstack/query/…Reported in probe¹
exhaustive-deps✅
mutation-property-order✅
no-rest-destructuring✅ (needs type info under oxlint? unconfirmed)
prefer-query-options✅
stable-query-client✅
infinite-query-property-order⚠️ not exercised
no-unstable-deps⚠️ not exercised
no-void-query-fn❌ left out: needs the TS checker, not exposed to oxlint JS plugins

¹ @tanstack/eslint-plugin-query 5.103.1 under oxlint 1.82.0, one-off. Wiring, not verified coverage: no repo fixture installs the official plugin; oxlint JS plugins are alpha. Re-check after upgrading either.

options.companionPlugins: { specifier, rules }[]. Specifier appended to jsPlugins after the official plugin; rules merge after the official rules.

@effect/tsgo layer: install, version lock, owner rules, settled conflicts
pnpm add -D @effect/[email protected] [email protected] [email protected] [email protected]   # not dependencies of this package
// package.json: registers the native `effecttsgo` plugin in oxlint-tsgolint
{ "scripts": { "prepare": "effect-tsgo patch --oxlint" } }
// oxlint.config.ts
import { recommended } from "@effect/tsgo/oxlint-presets";
import { defineStrictOxlintConfig, withEffectTsgoLayer } from "@aurelienbbn/oxlint-config";
import { defineConfig } from "oxlint";

export default defineConfig(withEffectTsgoLayer(defineStrictOxlintConfig(), { preset: recommended }));

Merges the preset's rules, adds effecttsgo to plugins, then effectTsgoOwnerRules, effectTsgoSettledRules and effectIdiomRules. Config rules and options win. The preset reports at warn; the strict preset's denyWarnings makes those blocking. Editor LSP (@effect/language-service in tsconfig.json plugins): set "diagnostics": false, or every finding shows twice.

[!WARNING] Version lock. @effect/tsgo 0.45.0 supports oxlint 1.81.0 / 1.82.0, oxlint-tsgolint 7.0.2001, TypeScript 7.0.2, and effect-tsgo patch refuses anything else. That is the compatibility baseline row; the current row (oxlint 1.83.0, oxlint-tsgolint 7.0.2002) is outside it. Pin the baseline row until a tsgo release widens its matrix.

effectTsgoOwnerRules: off in recommended, on here because they own checks @aurelienbbn/oxlint-plugin-effect removed.

effecttsgo/…Owns the removed rule
any-unknown-in-error-contextno-unsafe-error-channel
unsafe-effect-type-assertionno-effect-type-assertion
deterministic-keysmatching-identifier (with class-self-mismatch, already in recommended)
strict-effect-provide⚠️ partial: Effect.provide outside entry points, not the removed Layer.provide nesting

Every overlap with @aurelienbbn/oxlint-plugin-effect, settled once:

effecttsgo/…Harness ruleSettingWhy
catch-die-to-or-dieno-effect-ordieoffits rewrite is Effect.orDie, which the Harness rule bans; the dying catch already reports
redundant-or-dieno-effect-ordieoffhoists an orDie the Harness rule already reports
catch-to-or-else-succeedno-swallowed-failureoffits rewrite Effect.orElseSucceed(() => placeholder) is reported too
catch-to-ignoreno-swallowed-failure, no-catch-all-causeoffsuggests bare Effect.ignore (needs { log }) or Effect.ignoreCause (banned)
global-fetch, global-fetch-in-effectrequire-abort-signalboth on, tsgo firstreplace fetch with HttpClient (interruption built in) and both go quiet; forwarding signal alone leaves tsgo firing. Keeping fetch: turn the tsgo pair off
global-error-in-effect-failure, extends-native-errorrequire-tagged-effect-failboth onsame fix (a tagged error); Effect.fail(new Error(…)) reports twice. Literals and strings: Harness only
try-catch-in-effect-gen, global-timers-in-effectno-unsafe-effect-bodytsgo ownsthe Harness rule keeps only its throw check

Not running @aurelienbbn/oxlint-plugin-effect? The four off rules lose their reason: set them back in rules.

effectIdiomRules: strict-preset rules that contradict @effect/tsgo or an idiom Effect's API requires. The Effect side wins; each rule still reports outside that idiom.

Strict ruleSettingWhy
typescript/promise-function-asyncoffits autofix adds async, which effecttsgo/async-function reports
eslint/new-capcapIsNew: falseEffect constructors are PascalCase functions: Schema.Struct(…)
eslint/func-namesgenerators: "never"Effect.gen(function* () { … }) takes an anonymous generator
node/no-syncignores: ["runSync"]Effect.runSync runs an Effect at an edge; fs.*Sync still reports
eslint/max-classes-per-fileofftagged errors, Schema.Class models and services are classes; a domain module holds several
eslint/no-redeclareoffa Schema and its type share a name (const User + type User); TypeScript rejects real redeclarations
unicorn/throw-new-erroroffreports the Schema.TaggedError<Self>()(…) factory; its autofix emits code that does not compile
typescript/prefer-readonly-parameter-typesmethods readonly, inferred types ignored, Effect's immutable types allowedthe deep check rejects every Effect, Option, DateTime.Utc, Redacted, SQL Fragment and Migrator.Loader parameter; mutable arrays and objects still report
unicorn/no-array-callback-reference, unicorn/no-array-for-each, unicorn/no-array-method-this-argument, unicorn/no-array-sortoffwith namespace imports they take Effect modules for arrays (Option.some(value), Effect.forEach, Arr.filter(xs, f), Arr.sort(order)); the effect/no-array-* rules of @aurelienbbn/oxlint-plugin-effect report the same misuse on real arrays. Not running that plugin: set the four back in rules

Credit: preset shape, the effecttsgo plugin name, and every diagnostic name come from Effect-TS/tsgo (MIT).

Layer direction: fields

One overrides entry using eslint/no-restricted-imports patterns. No default layer map: no layers, no rule.

FieldTakes
filesglobs of the protected layer
forbiddengitignore-style globs for paths, bare names for packages; relative and package imports both report
messagethe fix direction: the rule can't know which of the three moves is right
Import graph layer: why opt-in, upgrade checklist

Cycles creep in through convenience imports between siblings, then surface as undefined at module init and untestable modules.

import rules in oxlint 1.82.0   33
  error  ██                      2   import/no-cycle, import/no-self-import
  off    ███████████████████████ 31  pinned off in importGraphRules

Loading the import plugin also builds the module graph, which lets oxc/no-barrel-file count what a barrel loads: a package's src/index.ts over Effect modules loads hundreds. Pass its entry points as entrypoints (withImportGraphLayer(config, { entrypoints: ["packages/*/src/index.ts"] })) to exempt them.

Adding "import" to plugins alone switches on all 33 (whole categories are enabled), including contradicting pairs: import/no-default-export vs import/prefer-default-export, import/no-named-export, import/group-exports. Want another: set it in rules.

BehaviorDetail
type-only cyclesignored (ignoreTypes): gone at runtime
costresolves files project-wide, the plugin's most expensive rule
path aliasesresolve only when oxlint finds the declaring tsconfig

After every oxlint upgrade, a new import/* rule missing from importGraphRules turns on via its category:

  • pnpm exec oxlint --rules (or --rules -f json)
  • compare import rows with importGraphRules
  • pin each new rule off

Here src/index.test.ts fails until the list matches the installed oxlint. Consumers on another oxlint check by hand.

Contract & migration

TopicContract
targetNode/TypeScript. Browser: override env.node, enable the browser env
nurseryoff pending upgrade review, except typescript/no-unnecessary-condition
Jest-namespace disables⚠️ removed
listsplugins, jsPlugins, ignorePatterns, overrides merge deduplicated; { replaceLists: true } replaces any list you supply
objectscategories, env, options, rules, settings merge key by key
isolationnested rule options are independent clones
typesfrom oxlint; Vite+ re-exports the same contract
compatibilitymatrix tests the packed config on baseline and current runners, type-aware included

版本与发布记录

@aurelienbbn/oxlint-config 共发布 8 个版本,首次发布于 2026/09/25;过去 12 个月发布了 8 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.8.0latest2026/09/301 个月前
0.6.02026/09/291 个月前
0.5.02026/09/291 个月前
0.4.02026/09/291 个月前
0.3.02026/09/281 个月前
0.2.02026/09/281 个月前

关于 @aurelienbbn/oxlint-config 的常见问题

@aurelienbbn/oxlint-config 是什么?

Opinionated strict reusable oxlint config for TypeScript projects.

如何安装 @aurelienbbn/oxlint-config?

在项目目录运行 npm install @aurelienbbn/oxlint-config,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:oxlint(>=1.82.0 <2.0.0)、oxlint-tsgolint(^7.0.2001)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@aurelienbbn/oxlint-config 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@aurelienbbn/oxlint-config 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@aurelienbbn/oxlint-config 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js ^22.19.0 || ^24.11.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@aurelienbbn/oxlint-config 可以商用吗?

@aurelienbbn/oxlint-config 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@aurelienbbn/oxlint-config 的下载量有多少?

根据 npm 官方下载统计,@aurelienbbn/oxlint-config 在 2026/10/07 – 2026/11/05 这 30 天内被下载 2,100 次,日均约 70 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@aurelienbbn/oxlint-config 还在维护吗?

最新版本 0.8.0 发布于 2026/09/30(1 个月前);至今共发布 8 个版本,其中过去 12 个月发布了 8 个;首次发布于 2026/09/25。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。