Skip to content

feat(oxlint): add prefer-explicit-variants rule#705

Merged
aidenybai merged 2 commits into
refactor/boolean-prefixed-prop-utilfrom
feat/prefer-explicit-variants
Jun 6, 2026
Merged

feat(oxlint): add prefer-explicit-variants rule#705
aidenybai merged 2 commits into
refactor/boolean-prefixed-prop-utilfrom
feat/prefer-explicit-variants

Conversation

@aidenybai
Copy link
Copy Markdown
Member

@aidenybai aidenybai commented Jun 6, 2026

Summary

Adds prefer-explicit-variants (architecture, warn, app-only) — the one net-new lintable pattern from the vercel-labs composition-patterns skill (the rest are already shipped or aren't statically lintable).

Catches a component that picks which component to render from 2+ boolean-prefixed props, each the test of a two-sided JSX ternary:

// flagged
function Composer({ isThread, isEditing }) {
  return (
    <div>
      {isThread ? <ThreadHeader /> : <ChannelHeader />}
      {isEditing ? <EditActions /> : <DefaultActions />}
    </div>
  );
}

Stays quiet on: single boolean switch (isMobile ? <A/> : <B/>), visibility toggles (? <X/> : null), cross-cutting state/responsive/auth booleans (isLoading, isError, isMobile, …), non-prefixed props, local useState booleans, and nested-function branches.

Precision: requires 2 distinct props; both arms must be JSX with parens/TS-wrappers stripped (so Prettier's multi-line cond ? (<A/>) : (<B/>) is caught — thanks to the thermo review); curated state-boolean denylist for false-positive control.

Stacked on #704 (reuses isBooleanPrefixedPropName). Review/merge #704 first.

Test plan

  • 17 adversarial rule tests (incl. parenthesized arms, state-boolean exclusion, nested-fn pruning, renamed/defaulted destructuring)
  • integration run-oxlint/architecture.test.ts + rule meta-tests (registry/metadata/tags/docs-url)
  • typecheck / lint / format:check

Follow-ups (rule-validate stage)

  • ESLint mirror in eslint-plugin-react-doctor
  • RDE noise eval against OSS (opinionated test-noise tier; threshold may tune)

Note

Low Risk
New optional maintainability lint at warn severity with deliberate false-positive guards; no runtime or security impact.

Overview
Adds react-doctor/prefer-explicit-variants, an architecture warn that nudges apps away from one component whose render path is driven by two or more boolean-prefixed props, each used as the test of a two-sided JSX ternary (both arms must be element/fragment; parens stripped for Prettier-shaped arms).

The rule is registered in the oxlint plugin, tagged test-noise / react-jsx-only, and listed in APP_ONLY_RULE_KEYS so it runs on app/unknown packages but stays off confidently classified libraries. BOOLEAN_PROP_VARIANT_BRANCH_THRESHOLD (2) gates firing; a curated denylist skips cross-cutting booleans (isLoading, isMobile, etc.), and nested inline functions are not walked.

Supporting change: shared isJsxElementOrFragment helper plus a focused unit test suite and a minor changeset for oxlint-plugin-react-doctor.

Reviewed by Cursor Bugbot for commit 003de9c. Bugbot is set up for automated code reviews on this repo. Configure here.

Flags a component that selects which component to render from 2+ boolean
props, each used as the test of a two-sided JSX ternary
(isThread ? <A/> : <B/> plus isEditing ? <C/> : <D/>) — the "explicit
variants" smell from the composition-patterns guidance. Conservative by
design: requires 2 distinct props, both ternary arms must be JSX (parens
stripped for the Prettier multi-line shape), cross-cutting state/
responsive/auth booleans are excluded, and nested-function branches are
pruned. App-only, warn severity.
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Jun 6, 2026

Open in StackBlitz

npm i https://pkg.pr.new/eslint-plugin-react-doctor@705
npm i https://pkg.pr.new/oxlint-plugin-react-doctor@705
npm i https://pkg.pr.new/react-doctor@705

commit: 003de9c

@aidenybai
Copy link
Copy Markdown
Member Author

/rde parity

@react-doctor-evals
Copy link
Copy Markdown

react-doctor-evals Bot commented Jun 6, 2026

❌ Parity failed — trace 92a6ac043fe8e396cadd5ec56e17aee9. Check server logs for that trace ID.

@aidenybai
Copy link
Copy Markdown
Member Author

/rde parity

Replace the rule-local JSX-arm check with a reusable
utils/is-jsx-element-or-fragment.ts type-guard — the
`isNodeOfType(x, "JSXElement") || isNodeOfType(x, "JSXFragment")` pattern
inlined across ~20 rules. The rule strips parens at the call site and
delegates the node check to the shared guard. No behavior change.
@aidenybai aidenybai merged commit d942131 into refactor/boolean-prefixed-prop-util Jun 6, 2026
4 checks passed
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