Skip to content

refactor(oxlint): drive hybrid routing from generated capabilities - #656

Merged
christopher-buss merged 2 commits into
mainfrom
feat/oxlint-capability-resolver
Jul 28, 2026
Merged

refactor(oxlint): drive hybrid routing from generated capabilities#656
christopher-buss merged 2 commits into
mainfrom
feat/oxlint-capability-resolver

Conversation

@christopher-buss

@christopher-buss christopher-buss commented Jul 28, 2026

Copy link
Copy Markdown
Owner

What

Replaces the hand-maintained oxlintRuleMapping table (~1100 lines, 742 entries) with a resolver backed by generated capability data, and fixes two behavioural defects found while doing it.

The old table had genuinely drifted — unicorn/no-loop-iterable-mutation, unicorn/isolated-functions, unicorn/consistent-compound-words, sonar/no-trivial-assertions and sonar/no-use-of-empty-return-value were all still mapped despite no longer being enabled anywhere in the preset. Deriving typeAware from meta.docs.requiresTypeChecking instead of a hand-list means a dependency bump now produces a reviewable capability diff rather than silent drift.

Layout

Everything a generator writes moves to src/generated/ (suffix dropped — the directory carries the meaning); hand-written routing moves to src/oxlint/{adapters,routing}.ts, leaving src/rules/ to mean only "rule maps". Both root lint configs ignore the generated directory.

from to
src/rules/oxlint-native-generated.ts src/generated/oxlint-native.ts
src/rules/oxlint-capabilities-generated.ts src/generated/oxlint-capabilities.ts
src/rules/stylistic-generated.ts src/generated/stylistic.ts
src/rules/type-aware-generated.ts src/generated/type-aware.ts
src/rules/oxlint-mapping.ts src/oxlint/routing.ts

Public API is unaffected — package.json exports point at bundles and src/oxlint/index.ts re-exports by name.

Payload

The generated artifacts now hold rule names only. Oxlint names, plugin aliases and package specifiers are derived via jsPluginAdapterFor; the human-facing metadata (categories, fix status, doc links) stays in src/oxlint/typegen.d.ts where an editor can show it. documentationUrl was being shipped twice — once as runtime data nothing read, once as @see JSDoc.

This module loads on every ESLint startup, so it matters: 719 KB → 145 KB.

A test asserts every generated rule name has an adapter, which is the invariant that lets the derivation replace the stored fields.

Two behavioural fixes

Hybrid double-reporting. The drop path read the sampled compatibility view while the emit path read the resolver. A rule the preset enables only under an unsampled option stayed in ESLint while oxlint also ran it — test: { jest: { extended: true } } double-reported all five jest-extended/* rules. Both paths now ask the resolver, so they agree by construction. New oxlint hybrid hand-off suite in test/oxlint-parity.spec.ts reproduces the failure and guards it.

categories opt-in. Off-only entries were skipped wholesale, dropping the explicit "off" the preset relies on to keep deliberately disabled native rules down. Harmless at defaults (DEFAULT_CATEGORIES is all-off), but a consumer setting categories: { correctness: "error" } got native no-unused-vars, getter-return and no-setter-return back against the preset's intent. The skip now applies only to jsPlugin routes — the ones that would load a plugin just to carry a disable. require-await is pinned again, matching pre-change behaviour.

Also fixed along the way

  • flawless/toml-sort-keys was being routed to oxlint, which cannot lint TOML. Making the drop path total exposed it. Non-JS flawless rules are now matched by name shape so a new toml-* rule cannot repeat it.
  • The generator's output depended on the Node version of whoever ran pnpm gen (e18e/prefer-get-or-insert is gated on nodeMajor). Pinned through a shared GENERATOR_NODE_MAJOR.
  • scripts/audit-native-parity.ts asked the oxlint factory for a type-aware run inside a tsconfig-less scratch dir, so tsgolint died before the first diagnostic — it could never complete. Now runs, and reports 5 pre-existing native divergences worth a separate look: jsdoc/require-param, jsdoc/require-returns, max-lines, no-console, unicorn/prefer-includes.
  • staysInEslint (public) had lost its per-rule detail and the oxc#23290 link; restored.
  • Dead code removed: excludedFromOxlint, hasNativeOxlintRule, an unreachable preference === "native" branch, and three generated fields nothing read.

Verification

  • pnpm typecheck clean
  • pnpm lint clean (oxc + fast + typed passes)
  • 503 tests pass
  • pnpm gen byte-stable across consecutive runs (idempotent and deterministic)
  • Runtime payload measured: 719 KB → 145 KB

Notes for review

  • The style/* rules the oxfmt layer disables are now dropped from ESLint in hybrid mode. Behaviour-neutral — both engines disable them — but it moved test/oxlint.spec.ts's "every dropped rule must be enabled in oxlint" invariant, which was measuring raw config unions. Rather than weaken it, formatterDisabledRules excludes rules the isentinel/oxfmt/* layer switches off, using the same discriminator scripts/typegen-oxlint.ts already uses.
  • Squash merge intended; the work is one commit.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Improved hybrid ESLint and Oxlint rule routing, including native, plugin-based, ESLint-only, and type-aware handling.
    • Added generated capability data for more accurate rule selection and suppression of duplicate diagnostics.
    • Added support for fixing JavaScript-plugin rules through Oxlint.
  • Bug Fixes

    • Prevented preset-disabled rules from being unintentionally re-enabled.
    • Improved handling of explicit user overrides and native rule equivalents.
  • Documentation

    • Clarified generated output locations, hybrid rule resolution, and deterministic type generation.

Replace the hand-maintained `oxlintRuleMapping` table with a resolver
backed by generated capability data. The old table had drifted: five
rules were still mapped despite no longer being enabled anywhere in the
preset.

Layout: everything a generator writes now lives in `src/generated/`, and
the hand-written routing modules move to `src/oxlint/{adapters,routing}.ts`,
leaving `src/rules/` to mean only "rule maps". Both root lint configs
ignore the generated directory.

Payload: the generated artifacts hold rule names only. Oxlint names,
plugin aliases and package specifiers are derived via `jsPluginAdapterFor`,
and the human-facing metadata (categories, fix status, doc links) stays in
`src/oxlint/typegen.d.ts` where an editor can show it. This module loads on
every ESLint startup, so the runtime data went 719 KB -> 145 KB.

Two behavioural fixes:

- The drop path read the sampled compatibility view while the emit path
  read the resolver, so a rule the preset enables only under an unsampled
  option stayed in ESLint while oxlint also ran it. `test.jest.extended`
  hit this and double-reported all five `jest-extended/*` rules. Both
  paths now ask the resolver, so they agree by construction.
- Off-only entries were skipped wholesale, dropping the explicit `"off"`
  the preset relies on to keep deliberately disabled native rules down.
  Harmless at defaults, but a consumer setting `categories` got them back.
  The skip now applies only to jsPlugin routes, which are the ones that
  would load a plugin to carry a disable.

Also fixes `flawless/toml-sort-keys` being routed to oxlint, which cannot
lint TOML; matches the other non-JS flawless rules by name shape so a new
`toml-*` rule cannot repeat it.

The generator no longer depends on the host Node version (`nodeMajor` is
pinned through a shared `GENERATOR_NODE_MAJOR`), covers `jest.extended`,
and shares `requiresTypeChecking` with the type-aware split instead of
re-narrowing the metadata. `scripts/audit-native-parity.ts` asked for a
type-aware run in a tsconfig-less scratch dir and could never complete;
it now runs and reports.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@netlify

netlify Bot commented Jul 28, 2026

Copy link
Copy Markdown

Deploy Preview for beamish-daffodil-b0f61d ready!

Name Link
🔨 Latest commit 9a507e9
🔍 Latest deploy log https://app.netlify.com/projects/beamish-daffodil-b0f61d/deploys/6a68eb441e03f20008ecbcfc
😎 Deploy Preview https://deploy-preview-656--beamish-daffodil-b0f61d.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@christopher-buss, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 22 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 568b9b9e-c196-4e03-aaf5-0936b626c90e

📥 Commits

Reviewing files that changed from the base of the PR and between 08204bf and 9a507e9.

⛔ Files ignored due to path filters (2)
  • src/generated/stylistic.ts is excluded by !**/generated/**
  • src/generated/type-aware.ts is excluded by !**/generated/**
📒 Files selected for processing (14)
  • CLAUDE.md
  • package.json
  • scripts/stylistic-gen.ts
  • scripts/type-aware-gen.ts
  • scripts/typegen-oxlint.ts
  • src/eslint/factory.ts
  • src/eslint/oxlint-drop.ts
  • src/eslint/type-aware-split.ts
  • src/oxlint/adapters.ts
  • src/oxlint/configs/typescript.ts
  • src/oxlint/index.ts
  • src/oxlint/routing.ts
  • test/oxlint-helpers.ts
  • test/oxlint-native-only.spec.ts

Walkthrough

The PR replaces static Oxlint mapping with generated capability data and resolver-based routing, updates hybrid ESLint/Oxlint handoff logic, relocates generated snapshots, and expands routing, parity, fixture, and documentation coverage.

Changes

Oxlint routing and generation

Layer / File(s) Summary
Deterministic capability generation
scripts/*gen.ts, scripts/config-factories.ts, src/generated/..., src/eslint/types.ts
Generators now emit relocated, deterministic native, type-aware, stylistic, and jsPlugin capability snapshots.
Adapters and route resolution
src/oxlint/adapters.ts, src/oxlint/routing.ts, src/oxlint/index.ts
Canonical ESLint rules are resolved to native, jsPlugin, eslint-only, or unmanaged Oxlint routes with aliasing and suppression metadata.
Hybrid handoff and splitting
src/eslint/oxlint-drop.ts, src/oxlint/utils.ts, scripts/audit-native-parity.ts, docs/oxlint.md
Hybrid rule removal, Oxlint rule splitting, and parity auditing now consume resolver routes and generated capability views.
Validation and fixtures
test/oxlint-routing.spec.ts, test/oxlint-parity.spec.ts, test/oxlint-fixtures.spec.ts, test/oxlint-run.ts, test/oxlint.spec.ts
Tests cover routing precedence, off-only capabilities, hybrid handoff, executable lookup, formatter filtering, and autofix behavior.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ESLintConfig
  participant OxlintResolver
  participant RuleSplitter
  participant OxlintEngine
  ESLintConfig->>OxlintResolver: resolve rule route
  OxlintResolver->>RuleSplitter: provide target and translation
  RuleSplitter->>OxlintEngine: emit native/jsPlugin rules
  ESLintConfig->>OxlintEngine: remove rules handed to Oxlint
Loading

Possibly related PRs

Poem

A rabbit watched the rules hop through,
From generated paths to resolvers new.
Native or plugin, each found its lane,
Hybrid reports no longer complain.
“Snuffle!” said Bunny, “the handoff is bright!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main change: oxlint hybrid routing now comes from generated capabilities.
Docstring Coverage ✅ Passed Docstring coverage is 83.72% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/oxlint-capability-resolver

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/typegen-oxlint.ts`:
- Around line 507-520: Update the effectivePresetRuleNames construction around
rawRuleStates so every raw-enabled rule is added as well as raw-disabled rules,
excluding only LEGACY_PRESET_DISABLES and the intentional oxfmt/oxfmt removal.
Preserve the existing effectiveVariant handling and ensure rules scoped to
patterns such as GLOB_TYPE_TESTS remain represented in the routing compatibility
view.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: df9c7f3f-1660-4f8f-8fc4-79d94817feca

📥 Commits

Reviewing files that changed from the base of the PR and between a47aace and 08204bf.

⛔ Files ignored due to path filters (6)
  • src/generated/oxlint-capabilities.ts is excluded by !**/generated/**
  • src/generated/oxlint-native.ts is excluded by !**/generated/**
  • src/generated/stylistic.ts is excluded by !**/generated/**
  • src/generated/type-aware.ts is excluded by !**/generated/**
  • test/__snapshots__/oxlint-fixtures.spec.ts.snap is excluded by !**/*.snap
  • test/__snapshots__/oxlint.spec.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (35)
  • CLAUDE.md
  • docs/oxlint.md
  • eslint.config.ts
  • oxlint.config.ts
  • project-words.txt
  • scripts/audit-native-parity.ts
  • scripts/config-factories.ts
  • scripts/stylisticgen.ts
  • scripts/typeawaregen.ts
  • scripts/typegen-oxlint.ts
  • src/eslint/configs/e18e.ts
  • src/eslint/factory.ts
  • src/eslint/oxlint-drop.ts
  • src/eslint/type-aware-split.ts
  • src/eslint/types.ts
  • src/oxlint/adapters.ts
  • src/oxlint/configs/oxfmt.ts
  • src/oxlint/configs/typescript.ts
  • src/oxlint/index.ts
  • src/oxlint/override-diagnostics.ts
  • src/oxlint/routing.ts
  • src/oxlint/utils.ts
  • src/rules/javascript.ts
  • src/rules/oxlint-mapping.ts
  • src/rules/oxlint-native-generated.ts
  • src/rules/typescript.ts
  • test/no-duplicate-imports.spec.ts
  • test/oxlint-fixtures.spec.ts
  • test/oxlint-helpers.ts
  • test/oxlint-native-only.spec.ts
  • test/oxlint-parity.spec.ts
  • test/oxlint-routing.spec.ts
  • test/oxlint-run.ts
  • test/oxlint.spec.ts
  • test/stylistic-rule-names.spec.ts
💤 Files with no reviewable changes (3)
  • src/rules/oxlint-native-generated.ts
  • src/eslint/configs/e18e.ts
  • src/rules/oxlint-mapping.ts

Comment thread scripts/typegen-oxlint.ts
Comment on lines +507 to +520
// Legacy compatibility disables name rules no current ESLint implementation
// owns. They remain useful as raw preset relaxations, but are not capabilities
// and must not enter the generated ownership view.
const LEGACY_PRESET_DISABLES = new Set(["no-new-symbol", "ts/no-dupe-class-members"]);
const presetRuleNames = new Set(effectiveEnabledRuleNames);
// Formatting has its own hybrid hand-off and is intentionally outside the
// rule resolver's public compatibility view.
presetRuleNames.delete("oxfmt/oxfmt");
for (const [rule, enabled] of rawRuleStates) {
if (!enabled && !LEGACY_PRESET_DISABLES.has(rule)) {
presetRuleNames.add(rule);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n -C3 'effectivePresetRuleNames|enabledPresetRuleNames' --type=ts

Repository: christopher-buss/eslint-config

Length of output: 168


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Changed/mentioned files:"
git diff --name-only HEAD~1..HEAD 2>/dev/null || git diff --name-only 2>/dev/null || true

echo
echo "Locate typegen file and relevant symbols:"
fd -a 'typegen-oxlint\.ts|config-factories\.ts|configs/test\.ts|routing\.ts|native.*parity.*ts' . | sed 's#^\./##'

echo
echo "Search preset-related symbols:"
rg -n 'effectivePresetRuleNames|enabledPresetRuleNames|presetRuleNames|presetEnabledRuleNames|effectiveEnabledRuleNames|unknownFamilies|GLOB_TYPE_TESTS|typeTestRules' .

Repository: christopher-buss/eslint-config

Length of output: 4792


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "scripts/typegen-oxlint.ts relevant section:"
sed -n '400,575p' scripts/typegen-oxlint.ts | cat -n

echo
echo "src/generated/oxlint-capabilities.ts line ranges around generated exports:"
sed -n '1930,1955p;2745,2770p' src/generated/oxlint-capabilities.ts | cat -n

echo
echo "scripts/audit-native-parity.ts consumers:"
sed -n '1,150p' scripts/audit-native-parity.ts | cat -n

echo
echo "src/oxlint/routing.ts consumers:"
sed -n '1,35p;270,310p' src/oxlint/routing.ts | cat -n

echo
echo "src/oxlint/utils.ts relevant area:"
sed -n '305,335p' src/oxlint/utils.ts | cat -n

echo
echo "test.ts config/rules relevant sections:"
sed -n '1,240p' src/eslint/configs/test.ts | cat -n

echo
echo "globs exports:"
sed -n '90,125p' src/globs.ts | cat -n

echo
echo "Check typeTestRules contents:"
rg -n -C3 'function typeTestRules|const typeTestRules|export function typeTestRules|typeTestRules\(' src/rules/test.ts scripts/typegen-oxlint.ts src/oxlint/configs/test.ts

Repository: christopher-buss/eslint-config

Length of output: 28651


🌐 Web query:

TypeScript tsconfig composite project types rootDir outDir type roots typecheck source files

💡 Result:

In TypeScript, the "composite" configuration (enabled via "composite": true) allows a project to function as part of a larger, multi-project build orchestrated by tsc --build [1][2]. Enabling this flag enforces specific constraints designed to ensure that build outputs are stable and discoverable by other projects [3][1]. Key requirements and behaviors for composite projects include: 1. Declaration Generation: You must enable "declaration": true [3][4]. This is necessary because downstream projects consume the declarations (.d.ts files) rather than the source code of the referenced project [5][6]. 2. rootDir and outDir Configuration: - When "composite": true is set, the default for "rootDir" changes to the directory containing the tsconfig.json file (if not explicitly set otherwise) [3][7]. - It is standard practice to explicitly set "rootDir" (often to "src") and "outDir" (often to "dist") to ensure a stable, predictable output structure [5][6][2]. - TypeScript enforces that all files intended for emission must reside within the directory specified by "rootDir" [8]. 3. Input File Specification: All implementation files must be explicitly included via the "include" pattern or the "files" array [3][4]. Implicit file discovery is disabled; if a file is not matched by these configurations, TypeScript will report an error [3][9]. 4. Build Orchestration: - Composite projects are built using tsc --build (or tsc -b) [1][2]. This mode acts as a build orchestrator that computes a dependency graph and only rebuilds projects when necessary [1][6]. - A common pattern is to create a "solution" tsconfig.json file at the root of a monorepo. This file typically has "files": [] and a "references" array pointing to the individual project configuration files [1][9][2]. This root file serves only to define the build graph and does not contain source files itself [5][2]. Regarding "typeRoots," while it is not strictly constrained by the composite flag, it remains a standard compiler option [7]. In a composite/monorepo environment, ensure that any custom type declarations are properly resolved across project boundaries, often by using path aliases or workspace-level configuration if using a monorepo tool [10]. To verify your setup, ensure all referenced projects have "composite": true and that you are using tsc --build to trigger the compilation process rather than standard tsc [6][2].

Citations:


Don’t drop raw-enabled rules from effectivePresetRuleNames.

enabledPresetRuleNames keeps every raw-enabled rule, but effectivePresetRuleNames only adds raw-named rules when they are disabled unless they were already captured by effectiveVariant paths. Add the enabled ones too so a rule scoped to a glob like GLOB_TYPE_TESTS doesn’t appear in the “enabled somewhere” artifact while being omitted from the routing compatibility view.

🐛 Proposed fix
 for (const [rule, enabled] of rawRuleStates) {
-	if (!enabled && !LEGACY_PRESET_DISABLES.has(rule)) {
+	if (!LEGACY_PRESET_DISABLES.has(rule)) {
 		presetRuleNames.add(rule);
	}
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Legacy compatibility disables name rules no current ESLint implementation
// owns. They remain useful as raw preset relaxations, but are not capabilities
// and must not enter the generated ownership view.
const LEGACY_PRESET_DISABLES = new Set(["no-new-symbol", "ts/no-dupe-class-members"]);
const presetRuleNames = new Set(effectiveEnabledRuleNames);
// Formatting has its own hybrid hand-off and is intentionally outside the
// rule resolver's public compatibility view.
presetRuleNames.delete("oxfmt/oxfmt");
for (const [rule, enabled] of rawRuleStates) {
if (!enabled && !LEGACY_PRESET_DISABLES.has(rule)) {
presetRuleNames.add(rule);
}
}
// Legacy compatibility disables name rules no current ESLint implementation
// owns. They remain useful as raw preset relaxations, but are not capabilities
// and must not enter the generated ownership view.
const LEGACY_PRESET_DISABLES = new Set(["no-new-symbol", "ts/no-dupe-class-members"]);
const presetRuleNames = new Set(effectiveEnabledRuleNames);
// Formatting has its own hybrid hand-off and is intentionally outside the
// rule resolver's public compatibility view.
presetRuleNames.delete("oxfmt/oxfmt");
for (const [rule, enabled] of rawRuleStates) {
if (!LEGACY_PRESET_DISABLES.has(rule)) {
presetRuleNames.add(rule);
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/typegen-oxlint.ts` around lines 507 - 520, Update the
effectivePresetRuleNames construction around rawRuleStates so every raw-enabled
rule is added as well as raw-disabled rules, excluding only
LEGACY_PRESET_DISABLES and the intentional oxfmt/oxfmt removal. Preserve the
existing effectiveVariant handling and ensure rules scoped to patterns such as
GLOB_TYPE_TESTS remain represented in the routing compatibility view.

Follow-ups from the cleanup review.

The generated capability data (~145 KB of Sets) was reachable from
`src/eslint/factory.ts` through a static import chain, so every plain
ESLint run parsed it even though nothing reads it with `oxlint: false`.
Two links to cut: `oxlint-drop.ts` is now imported dynamically inside the
`oxlintMode !== "off"` branch that already guards its only use, and the
two hand-written Sets the type-aware split wanted moved to `adapters.ts`,
which has no generated imports. Verified with a module-resolution hook:
importing the factory now loads neither generated module; `oxlint: true`
loads them on demand.

`oxlintRuleMapping` was also built at module scope — ~800 resolves for a
view the drop path never consults. The build moves behind
`buildOxlintRuleMapping`, called once from `src/oxlint/index.ts` (which
the ESLint factory never loads) for the public export, and memoized for
the preset-scoped predicates.

Those predicates are renamed to say what they are: `isPresetRuleOxlintCovered`
and `isPresetRuleJsPlugin` read the sampled view, while `resolveOxlintRule`
beside them is total, and nothing in the old names said so. That ambiguity
had already bitten — `src/oxlint/configs/typescript.ts` gated *emission* on
the sampled `isTsgolintRule`, the same resolver-vs-view asymmetry the drop
path was just fixed to avoid. It now uses the resolver-backed
`runsInTsgolint`.

Also:

- `isPruningViewConfig` is shared between the generator and the test
  helper instead of two hand-rolled name-prefix filters. They were not
  actually the same predicate: the generator skipped
  `isentinel/markdown/disables` too, and the helper's comment wrongly
  claimed they matched.
- The vitest sampling variant re-derived four effective configs identical
  to the jest variant's; it now samples only the paths where the two
  differ. Generated output is byte-identical, ~360 ms off every `pnpm gen`.
- `typeawaregen.ts` -> `type-aware-gen.ts` and `stylisticgen.ts` ->
  `stylistic-gen.ts`, so `typeaware` and `stylisticgen` come back out of
  the spell-check dictionary rather than being pinned there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@christopher-buss
christopher-buss merged commit 1c804f5 into main Jul 28, 2026
8 checks passed
@christopher-buss
christopher-buss deleted the feat/oxlint-capability-resolver branch July 28, 2026 18:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant