feat: port oxc React + jsx-a11y rules and add scope analysis infrastructure#279
feat: port oxc React + jsx-a11y rules and add scope analysis infrastructure#279devin-ai-integration[bot] wants to merge 8 commits into
Conversation
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| const isJavascriptUrl = (value: string): boolean => JAVASCRIPT_URL_PATTERN.test(value); | ||
|
|
||
| const MESSAGE = | ||
| "A]javascript:` URL is a form of `eval()` — use an event handler instead of a URL to execute JavaScript"; |
Port all 12 built-in oxc React rules to TypeScript as react-doctor plugin rules: - no-children-prop - no-danger - no-is-mounted - jsx-no-duplicate-props - jsx-no-script-url - no-render-return-value - no-string-refs - no-direct-mutation-state - jsx-key - require-render-return - no-unknown-property - rules-of-hooks Remove oxc's built-in 'react' plugin from config and BUILTIN_REACT_RULES from external-plugin-rules.ts. Update rules-of-hooks-local-use tests to match new plugin source. Add fixture and test file for all 12 ported rules. Note: bare use() (React 19) detection requires import resolution not available in the AST-only TS rule — 7 related tests skipped. Co-Authored-By: Aiden Bai <aiden.bai05@gmail.com>
Port all 14 jsx-a11y accessibility rules from oxc's built-in plugin to react-doctor's TypeScript rule system: - a11y-alt-text - a11y-anchor-is-valid - a11y-click-events-have-key-events - a11y-heading-has-content - a11y-html-has-lang - a11y-iframe-has-title - a11y-label-has-associated-control - a11y-no-autofocus - a11y-no-distracting-elements - a11y-no-redundant-roles - a11y-no-static-element-interactions - a11y-role-has-required-aria-props - a11y-scope - a11y-tabindex-no-positive Also adds shared utilities for jsx-a11y rules: - find-jsx-attribute-ignore-case.ts - get-jsx-element-name.ts - jsx-a11y-helpers.ts Removes the jsx-a11y built-in plugin dependency and BUILTIN_A11Y_RULES from config, replacing them with native react-doctor rules. Includes test fixture and test file for all 14 rules. Co-Authored-By: Aiden Bai <aiden.bai05@gmail.com>
Co-Authored-By: Aiden Bai <aiden.bai05@gmail.com>
…you-might-not-need-an-effect rules - Add scope analysis types (Scope, ScopeVariable, ScopeReference, ScopeManager) - Add scope traversal utilities (ascend, descend, getRef, resolveVariable) - Add React scope helpers (isState, isProp, isRef, isStableSetter, etc.) - Extend RuleContext with optional sourceCode for scope analysis - Port exhaustive-deps from eslint-plugin-react-hooks (918 lines) - Port 8 scope-based you-might-not-need-an-effect rules: - scope-no-derived-state - scope-no-event-handler - scope-no-chain-state-updates - scope-no-initialize-state - scope-no-pass-data-to-parent - scope-no-pass-live-state-to-parent - scope-no-reset-all-state-on-prop-change - scope-no-adjust-state-on-prop-change - Register all new rules in rule registry (214 total) Co-Authored-By: Aiden Bai <aiden.bai05@gmail.com>
Co-Authored-By: Aiden Bai <aiden.bai05@gmail.com>
658f3b7 to
2eb7219
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
There are 6 total unresolved issues (including 4 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2eb7219. Configure here.
| }, | ||
| rules: { | ||
| ...(customRulesOnly ? {} : BUILTIN_REACT_RULES), | ||
| ...(customRulesOnly ? {} : BUILTIN_A11Y_RULES), |
There was a problem hiding this comment.
Ported rules ignore customRulesOnly flag entirely
Medium Severity
Previously, customRulesOnly gated BUILTIN_REACT_RULES and BUILTIN_A11Y_RULES so they were excluded when true. Now those rules are ported into the reactDoctorRules registry and flow through enabledReactDoctorRules, which does not check customRulesOnly. This means customRulesOnly: true no longer suppresses the ported React and a11y rules (e.g., rules-of-hooks, jsx-key, all a11y-* rules).
Reviewed by Cursor Bugbot for commit 2eb7219. Configure here.
…eck, remove dead code Co-Authored-By: Aiden Bai <aiden.bai05@gmail.com>
Co-Authored-By: Aiden Bai <aiden.bai05@gmail.com>
Co-Authored-By: Aiden Bai <aiden.bai05@gmail.com>


Summary
Replaces oxc's built-in
reactandjsx-a11yplugins with native TypeScript rule implementations insideoxlint-plugin-react-doctor, and adds a scope analysis infrastructure enabling porting of rules that require variable/reference tracking.12 React rules ported (previously
react/*):rules-of-hooks,no-children-prop,no-danger,jsx-key,jsx-no-duplicate-props,jsx-no-script-url,no-string-refs,no-direct-mutation-state,no-render-return-value,no-unknown-property,no-is-mounted,require-render-return.14 jsx-a11y rules ported (previously
jsx-a11y/*):a11y-alt-text,a11y-anchor-is-valid,a11y-click-events-have-key-events,a11y-heading-has-content,a11y-html-has-lang,a11y-iframe-has-title,a11y-label-has-associated-control,a11y-no-autofocus,a11y-no-distracting-elements,a11y-no-redundant-roles,a11y-no-static-element-interactions,a11y-role-has-required-aria-props,a11y-scope,a11y-tabindex-no-positive.Scope analysis infrastructure — new utilities ported from ESLint's scope system:
scope-types.ts—Scope,ScopeVariable,ScopeReference,ScopeManagerinterfacesscope-traversal.ts—ascend,descend,getRef,resolveVariableutilitiesreact-scope-helpers.ts— React-specific helpers (isState,isProp,isRef,isStableSetter, etc.)RuleContextextended with optionalsourceCodefield for scope analysis (backward-compatible)1 exhaustive-deps rule ported (from
eslint-plugin-react-hooks):scope-exhaustive-deps— 918-line faithful port of React'sExhaustiveDeps.ts. UsesnodeType()helper for string-based AST type comparisons to handle legacy ESLint node types (OptionalMemberExpression,OptionalCallExpression,AsExpression) not present in TypeScript ESTree.8 you-might-not-need-an-effect rules ported (scope-based):
scope-no-derived-state,scope-no-event-handler,scope-no-chain-state-updates,scope-no-initialize-state,scope-no-pass-data-to-parent,scope-no-pass-live-state-to-parent,scope-no-reset-all-state-on-prop-change,scope-no-adjust-state-on-prop-change.Config changes:
pluginsis now always[](was["react", "jsx-a11y"])BUILTIN_REACT_RULESandBUILTIN_A11Y_RULESno longer spread into the oxlint configreact-doctor/*namespace (214 total rules)react-hooksandyou-might-not-need-an-effectremain loaded alongside the new internal scope rulesNew shared utilities:
find-jsx-attribute-ignore-case.ts,get-jsx-element-name.ts,jsx-a11y-helpers.ts(constants for HTML tags, ARIA roles, interactive elements; helpers for attribute value extraction, accessible content detection, etc.)Review & Testing Checklist for Human
scope-types.ts,scope-traversal.ts,react-scope-helpers.ts) is a new TypeScript reimplementation of ESLint's scope system. It has no dedicated unit tests — correctness is only verified transitively through the rules that use it. Consider adding targeted tests for variable resolution, scope hierarchy, and reference tracking.scope-exhaustive-depsfidelity: The 918-line port usesnodeType()(returnsnode.typeasstring) andnodeRecord()(casts toRecord<string, unknown>) to bypass TypeScript ESTree type narrowing. This effectively disables type safety for AST access throughout the rule. Spot-check the dependency collection logic (gatherDependenciesRecursively,collectRecommendations) against React's original.BUILTIN_REACT_RULESandBUILTIN_A11Y_RULESmaps had explicit severities (e.g.,rules-of-hooks: "error",jsx-key: "error",alt-text: "error"). Verify the ported rules'severityin eachdefineRule()call matches. Mismatches could change CI pass/fail behavior via--fail-on error.scope-no-*rules andscope-exhaustive-depscoexist with the external JS plugins (react-hooks,you-might-not-need-an-effect). Verify there's no double-reporting for the same violations. If both are active, users may see duplicate diagnostics.rules-of-hookstests:rules-of-hooks-local-use.test.tshas 6 tests.skip-ed — these cover alias resolution edge cases (CommonJSrequire, namespace destructuring, computed destructuring, loop-scoped bindings) that oxc's Rust implementation handled but the ported TS version does not. Confirm this behavioral gap is acceptable.Suggested test plan: Run
react-doctoron a medium-sized React app and verify: (a) no false-positive regressions from the old builtin rules, (b) the new a11y rules fire where expected, (c)scope-exhaustive-depsproduces the same diagnostics as eslint-plugin-react-hooks on a component with missing/unnecessary deps, (d) no duplicate diagnostics between scope rules and external JS plugins.Notes
customRulesOnlytest still passes — it asserts noreactorjsx-a11yplugin diagnostics, which is now trivially true since those plugins are no longer loaded. The test may need updating to verify thereact-doctor/a11y-*rules are excluded instead.Link to Devin session: https://app.devin.ai/sessions/ff7fa51481ed4325906ce93f45f71bb6
Requested by: @aidenybai
Note
Medium Risk
Adds a large set of new lint rules (including scope-aware React Hooks analysis) and changes oxlint config to stop loading the upstream
react/jsx-a11yplugins, which may change diagnostics and CI fail/pass behavior across projects.Overview
Replaces oxlint’s use of upstream
react/jsx-a11yplugins by forcingplugins: []inpackages/coreconfig and removing the spread ofBUILTIN_REACT_RULES/BUILTIN_A11Y_RULES, so React/a11y diagnostics now come fromreact-doctor/*rules.Adds many new native TypeScript rules to
oxlint-plugin-react-doctor, including ports of common React correctness/a11y checks (e.g.,rules-of-hooks,jsx-key,no-danger, severala11y-*rules) and registers them in the generatedrule-registry.ts.Introduces scope-aware linting infrastructure (new helpers + optional
RuleContext.sourceCode) to support rules that require reference/scope tracking, including a largescope-exhaustive-depsport and severalscope-no-*“you might not need an effect” rules.Reviewed by Cursor Bugbot for commit c2a9535. Bugbot is set up for automated code reviews on this repo. Configure here.