Skip to content

feat(eslint-config)!: upgrade @eslint-react/eslint-plugin to v4#3107

Merged
marcusrbrown merged 2 commits intomainfrom
feat/eslint-react-v4
Apr 15, 2026
Merged

feat(eslint-config)!: upgrade @eslint-react/eslint-plugin to v4#3107
marcusrbrown merged 2 commits intomainfrom
feat/eslint-react-v4

Conversation

@marcusrbrown
Copy link
Copy Markdown
Member

Summary

Upgrades @eslint-react/eslint-plugin from v2 to v4, adopting the unified plugin architecture. Follows the same migration pattern as antfu/eslint-config PR #839.

Breaking Changes

  • Peer dependency: @eslint-react/eslint-plugin now requires ^4.2.3 (was ^2.2.3)
  • Rule names changed: v4 uses dash-joined names under a single @eslint-react namespace instead of separate sub-plugins with / separators
  • Rules removed in v4: no-default-props, no-prop-types, jsx-no-duplicate-props, jsx-uses-vars, no-string-refs, no-useless-forward-ref, prefer-use-state-lazy-initialization

What Changed

Area Before (v2) After (v4)
Plugin registration 6 sub-plugins (react-dom, react-hooks-extra, etc.) 1 unified plugin (@eslint-react) + react-refresh
Rule configuration ~40 manually listed rules pluginReact.configs.recommended.rules spread
Hooks rules Separate eslint-plugin-react-hooks import + spread Included in v4's recommended config (rules-of-hooks, exhaustive-deps)
Type-aware rules Separate config block Unchanged — no-implicit-key, no-leaked-conditional-rendering

Verification

  • pnpm install — clean
  • pnpm type-check — passes
  • pnpm lint — passes
  • pnpm build — passes (including generate-types for eslint-typegen)
  • Changeset included

Related

Copy link
Copy Markdown
Contributor

@fro-bot fro-bot left a comment

Choose a reason for hiding this comment

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

Verdict: CONDITIONAL

This migration is structurally sound — the unified plugin architecture is correctly adopted and the spread of pluginReact.configs.all.plugins works as expected (the v4 all config exposes a single '@eslint-react' key). However, there is one blocking bug introduced by the migration.


Blocking issues

ReactTypeAwareRules uses the wrong plugin prefix (packages/eslint-config/src/configs/react.ts, lines 26–29)

const ReactTypeAwareRules: Config['rules'] = {
  'react/no-implicit-key': 'warn',                      // ← stale v2 prefix
  'react/no-leaked-conditional-rendering': 'warn',      // ← stale v2 prefix
}

In v2 the plugin was explicitly aliased as react (react: plugins['@eslint-react']). After this migration the spread of pluginReact.configs.all.plugins registers the plugin under its native key '@eslint-react', not 'react'. The rules resolver will fail to find any rule under the old namespace — ESLint either silently drops or errors on unknown rule names at runtime.

The correct v4 prefixes are:

const ReactTypeAwareRules: Config['rules'] = {
  '@eslint-react/no-implicit-key': 'warn',
  '@eslint-react/no-leaked-conditional-rendering': 'warn',
}

Both rules exist in the v4 plugin (confirmed via node_modules/@eslint-react/eslint-plugin inspection). They are also absent from the default recommended spread, so the type-aware block remains necessary — it just needs the correct prefix.


Non-blocking concerns

  1. Changeset bump type.changeset/eslint-react-v4.md declares minor, but the PR title uses ! (breaking change marker) and the description explicitly calls out breaking changes: peer dep range jumps from ^2.2.3 to ^4.2.3, rule names change from react/* to @eslint-react/*, and several rules are removed entirely. Consumers who override any of those removed rules or who pin overrides to old react/ prefixed rule names will see breakage on upgrade. Per the repo's own AGENTS.md convention («Major: breaking API changes»), this warrants a major bump. This is a judgment call the author can make intentionally, but it should be conscious.

  2. Stale JSDoc — The function-level JSDoc in react.ts (lines 44–51) still enumerates «React DOM rules», «React Hooks rules», «React Web API rules» as separate configuration categories. These are now all delivered via the single pluginReact.configs.recommended.rules spread. The bullet list is technically still accurate as a description of what rules are applied but implies a structural separation that no longer exists in code. Low-priority cleanup.

  3. eslint-plugin-react-hooks peer dependency retained — The package remains in peerDependencies (and devDependencies) but is no longer imported or used in react.ts. The changeset notes «eslint-plugin-react-hooks remains as a peer dependency for consumers using it directly.» If that intent is correct, a code comment on the peer entry would help future readers understand why. If it's unintentional, the peer dep and its peerDependenciesMeta entry can be removed.


Missing tests

The eslint-config package does not appear to have tests exercising the type-aware rule resolution path (the ReactTypeAwareRules block). The blocking bug above would have been caught by an integration test that instantiates the config with tsconfigPath set and asserts the resulting rule names contain @eslint-react/ prefixes. Not required to block merge, but worth adding to close the gap.


Risk assessment: MED

  • The react/ prefix bug means the two type-aware rules (no-implicit-key, no-leaked-conditional-rendering) are silently dropped for every consumer who enables type-aware linting. This is a correctness regression — the rules disappear without error, making it easy to miss in CI.
  • The broader migration (plugin registration, recommended spread, hooks coverage) is correct and mirrors the antfu/eslint-config reference PR faithfully.
  • Blast radius is limited to the React type-aware linting path, which requires explicit opt-in via tsconfigPath.

Run Summary
Field Value
Event pull_request
Repository bfra-me/works
Run ID 24437992496
Cache hit
Session ses_270650720ffei5Tyq5Lat0XDJL

…prefix for v4 compatibility

In @eslint-react/eslint-plugin v4, the unified plugin architecture registers rules
under the `@eslint-react/` namespace rather than the legacy `react/` namespace.
Update rule references from `react/no-implicit-key` and
`react/no-leaked-conditional-rendering` to `@eslint-react/no-implicit-key`
and `@eslint-react/no-leaked-conditional-rendering` to fix the ts-strict-with-react
fixture test failure.
@fro-bot
Copy link
Copy Markdown
Contributor

fro-bot commented Apr 15, 2026

Autohealing Fix

Failing check: Test@bfra.me/eslint-config test/fixtures.test.ts > ts-strict-with-react

Root cause: The ReactTypeAwareRules constant in packages/eslint-config/src/configs/react.ts referenced rules using the old v2/v3 react/ plugin namespace:

  • react/no-implicit-key
  • react/no-leaked-conditional-rendering

In @eslint-react/eslint-plugin v4, the unified plugin architecture registers all core rules under the @eslint-react/ namespace (not react/). ESLint couldn't find the react plugin when applying these rules, producing:

A configuration object specifies rule "react/no-implicit-key", but could not find plugin "react".

Fix applied (commit dba1f8fc): Updated both rule references to use the v4 @eslint-react/ prefix:

  • react/no-implicit-key@eslint-react/no-implicit-key
  • react/no-leaked-conditional-rendering@eslint-react/no-leaked-conditional-rendering

All tests now pass locally (146 test files, 3385 tests).

Copy link
Copy Markdown
Contributor

@fro-bot fro-bot left a comment

Choose a reason for hiding this comment

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

Verdict: PASS

The blocking issue from the previous review has been addressed in commit dba1f8f: ReactTypeAwareRules now correctly uses the @eslint-react/ prefix for both type-aware rules. Verified in the current file:

const ReactTypeAwareRules: Config['rules'] = {
  '@eslint-react/no-implicit-key': 'warn',
  '@eslint-react/no-leaked-conditional-rendering': 'warn',
}

The migration is correct end-to-end:

  • Unified plugin registered via pluginReact.configs.all.plugins spread (key: '@eslint-react')
  • Recommended ruleset replaces ~40 hand-listed rules
  • Hooks rules (rules-of-hooks, exhaustive-deps) covered via v4 recommended config
  • DOM and Web API rules covered via recommended spread
  • Type-aware rules retain correct v4 prefixes

Blocking issues

None — all resolved.

Non-blocking concerns

The concerns from the previous review remain open for author's discretion:

  1. Changeset bump type — declared minor despite peer dep major bump and rule-name breaking changes. Reasonable call if the package treats peer dep changes as non-breaking, but worth a note in the changelog.
  2. eslint-plugin-react-hooks peer dep — retained but no longer used in react.ts. Intentional per the changeset note; a code comment on the peer entry would help future readers.

Missing tests

None that are blocking. A test exercising the type-aware path with tsconfigPath set would have caught the prefix bug earlier. Non-blocking.

Risk assessment: LOW

Both commits are correct. The fix addresses the only regression path (type-aware rules silently dropping). The broader migration matches the upstream reference (antfu/eslint-config PR #839). All CI checks pass.


Run Summary
Field Value
Event pull_request
Repository bfra-me/works
Run ID 24438596334
Cache hit
Session ses_270650720ffei5Tyq5Lat0XDJL

Copy link
Copy Markdown
Contributor

@fro-bot fro-bot left a comment

Choose a reason for hiding this comment

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

Verdict: PASS

The blocking issue from the previous review has been addressed in commit dba1f8f: ReactTypeAwareRules now correctly uses the @eslint-react/ prefix for both type-aware rules. Verified in the current file:

const ReactTypeAwareRules: Config['rules'] = {
  '@eslint-react/no-implicit-key': 'warn',
  '@eslint-react/no-leaked-conditional-rendering': 'warn',
}

The migration is correct end-to-end:

  • Unified plugin registered via pluginReact.configs.all.plugins spread (key: '@eslint-react')
  • Recommended ruleset replaces ~40 hand-listed rules
  • Hooks rules (rules-of-hooks, exhaustive-deps) covered via v4 recommended config
  • DOM and Web API rules covered via recommended spread
  • Type-aware rules retain correct v4 prefixes

Blocking issues

None — all resolved.

Non-blocking concerns

The concerns from the previous review remain open for author's discretion:

  1. Changeset bump type — declared minor despite peer dep major bump and rule-name breaking changes. Reasonable call if the package treats peer dep changes as non-breaking, but worth a note in the changelog.
  2. eslint-plugin-react-hooks peer dep — retained but no longer used in react.ts. Intentional per the changeset note; a code comment on the peer entry would help future readers.

Missing tests

None that are blocking. A test exercising the type-aware path with tsconfigPath set would have caught the prefix bug earlier. Non-blocking.

Risk assessment: LOW

Both commits are correct. The fix addresses the only regression path (type-aware rules silently dropping). The broader migration matches the upstream reference (antfu/eslint-config PR #839). All CI checks pass.


Run Summary
Field Value
Event pull_request
Repository bfra-me/works
Run ID 24438596334
Cache hit
Session ses_270650720ffei5Tyq5Lat0XDJL

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.

2 participants