Oxlint rule limits
These ten rules check syntax within defined limits. They follow literal module sources, lexical import identities, and unchanged local aliases where needed. They cannot resolve TypeScript modules or prove whole-program behavior. Reopen a limit when the policy or runtime changes. Treat an unchanged static-analysis limit as a known boundary rather than repeatedly reporting it as a bypass.
Limits
no-cross-layer-imports:- Covered now: Imports, exports,
import(), TS import-equals/import types, direct and aliasedrequire,module.require, importedcreateRequire, and unchanged local aliases. - Residual limit and why: Dynamic specifiers,
createRequireinitialized from unknown/dynamic sources, custom tsconfig/package aliases, export-condition resolution, and re-export paths across files need a resolver or graph walk.
- Covered now: Imports, exports,
no-browser-server-imports:- Covered now: Browser-zone imports use the shared static-source visitors;
node:and unprefixed Node builtins, server-only markers, Alchemy, Workers, and infra paths are rejected. - Residual limit and why: Dynamic sources and package aliases/conditions are not resolved. The denylist does not classify every server-only module in auth or database packages; expanding that policy is a separate decision.
- Covered now: Browser-zone imports use the shared static-source visitors;
no-feature-transport:- Covered now: Global
fetchreferences and aliases, browser-global fetch members, known client/factory imports and stable aliases, namespace clients, static require/import sources, andnew HttpClient()are reported. - Residual limit and why: Cross-file wrappers/re-exports, dynamic module/key values, mutable aliases, and transport hidden behind unknown factories need dataflow or resolution. The canonical-name
new HttpClient()guard intentionally retains its legacy false positive for a local class with that name.
- Covered now: Global
no-hand-rolled-surface:- Covered now: Known imported surface25 constructors, static computed keys, namespace/require/dynamic-import aliases, destructured API methods, stable aliases, and
.call/.applywrappers are recognized. - Residual limit and why: Dynamic keys, arbitrary local object shapes, re-export chains, and constructors hidden behind helpers in other files need type or graph analysis.
- Covered now: Known imported surface25 constructors, static computed keys, namespace/require/dynamic-import aliases, destructured API methods, stable aliases, and
no-browser-globals-on-server:- Covered now: Bare runtime references, literal/template member keys,
globalThis/self/global/window, unchanged aliases, and destructuring are checked with value-scope34 awareness. Type positions, property names, local runtime shadows, andtypeofguards are excluded intentionally. - Residual limit and why: Computed values, reflection, mutable aliases, and cross-file aliases cannot be resolved syntactically.
- Covered now: Bare runtime references, literal/template member keys,
no-devtools-in-production:- Covered now: Static imports, exports,
import(), TS import-equals/import types, direct/aliased require,module.require, importedcreateRequire, auth distribution paths, and productionsrc/testpaths are checked. - Residual limit and why: Dynamic sources, package resolution, and whole-graph reachability through helper/re-export files are outside the rule. CLI, tests, dev folders, and the devtools package are deliberate permitted roots.
- Covered now: Static imports, exports,
acquire-release-constructs-in-acquire-body:- Covered now: Imported Effect calls and stable aliases, transparent TS wrappers, eager
Effect.succeed, captured identifiers/member values, block returns, and default-parameter captures are checked. - Residual limit and why: Arbitrary helper functions, complex control flow, and Effect values whose resource ownership is hidden behind other abstractions need semantic analysis. A syntax rule cannot prove interruption safety.
- Covered now: Imported Effect calls and stable aliases, transparent TS wrappers, eager
no-module-level-mutable-state:- Covered now: Module-scope
let/varand nested module-scopedvarare reported; immutableusingandawait usingremain allowed. - Residual limit and why: Mutation behind
const, static fields, imported shared state, and block-scoped bindings23 that escape through closures are not binding-levellet/varfindings.
- Covered now: Module-scope
contract-binding-matches-name:- Covered now: Imported
defineContract, stable aliases, static string/template names, transparent wrappers, and later identifier assignments are checked. - Residual limit and why: Dynamic names, destructured bindings, unknown computed members, and aliases/re-exports crossing files need resolution.
- Covered now: Imported
watch-effect-actors:- Covered now: Each assigned
createEffectActorbinding is matched to an identifier passed towatchActor; inline actor calls are matched separately. Stable aliases and direct.call/static.applyforms are recognized. - Residual limit and why: A watcher in dead code still counts, and lint cannot prove execution or coverage on every branch. Dynamic/spread arguments and helper calls in other files require flow analysis.
- Covered now: Each assigned
Copy surface for downstream projects
- From
scripts/oxlint-plugin-boundaries.ts:isStringModule,staticPropertyName,importIdentity,variableOf,isStableVariable,moduleSourceVisitors,isReference,isTypePosition, and the focused visitors innoFeatureTransport,noHandRolledSurface, andnoBrowserGlobalsOnServer. - From
scripts/oxlint-plugin-patterns.ts:importIdentity,isEffectCall,expressionForVariable,variableBinding, andwatchEffectActors’ per-bindingProgram:exitcheck. - Check visitor behavior: the two real-Oxlint fixture files under
packages/core/test/show both rejected syntax and intentional allows. - Do not copy workspace path normalization as a package resolver. It recognizes this template’s paths; package exports, conditions, and custom aliases need their own resolver contract.
Audit evidence
The two fixture suites pass 52 tests. A two-actor/one-watch probe against base 8da815d produced no watch-rule diagnostic; the current fixture requires the unwatched second actor to be reported. Repo-wide Oxlint over scripts packages apps is also part of the validation gate.
Last changed 2026-10-24 in c525d23. Source on GitHub. Change log.