feat: adopt ESLint 10 by replacing eslint-plugin-react - #133
Merged
neolitec merged 1 commit intoAug 14, 2026
Conversation
ESLint 9 reached end of life on 2026-08-06, and eslint-plugin-react still has no release supporting ESLint 10 — its last release was 7.37.5 in April 2025, and the two compatibility PRs (#3979, #4022 upstream) have been open for months awaiting review. Waiting it out is no longer viable. The block turned out to be narrower than #119 recorded. Only one of the three plugins was actually broken: - eslint-plugin-react calls the removed context.getFilename() unguarded while detecting the React version, so every rule throws. Replaced with @eslint-react/eslint-plugin, which is TypeScript-first and supports ESLint 10. Not a drop-in: react/no-unused-prop-types has no equivalent and is dropped, which costs nothing here since this package is TypeScript-only and ships no PropTypes. The jsx-runtime config is also gone — ESLint 10 tracks JSX references natively. - eslint-plugin-jsx-a11y only lagged in its declared peer range. Swapped for eslint-plugin-jsx-a11y-x, the e18e fork that declares ^9 || ^10. - eslint-plugin-import was never wired into eslint.config.js at all. It was only present as a peer of eslint-import-resolver-typescript, which was equally unused. Both removed rather than migrated. The new recommended ruleset raises 15 warnings, all of which are either inapplicable or wrong for this package, so they are disabled at config level with the reasoning inline. Of note, no-forward-ref is off because peerDependencies still declare react ^17 || ^18 || ^19: passing ref as a plain prop would break every consumer below 19. CI moves to Node 22, the floor declared by @eslint-react/eslint-plugin. Closes #119
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
neolitec
deleted the
neolitec/eslint-10-is-blocked-by-the-plugin-ecosystem
branch
August 14, 2026 16:41
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #119.
ESLint 9 reached end of life on 2026-08-06, so holding at 9 now means running an unsupported linter.
eslint-plugin-reactstill has no ESLint 10 release — last release was 7.37.5 in April 2025, and the compatibility PRs (#3979, #4022) have sat unreviewed for months. So we move instead of waiting.The block was narrower than #119 recorded
Only one of the three plugins was actually broken:
eslint-plugin-reactcontext.getFilename()unguarded during React version detection, so every rule throws@eslint-react/eslint-plugineslint-plugin-jsx-a11yeslint-plugin-jsx-a11y-x(e18e fork, declares^9 || ^10)eslint-plugin-importeslint.config.jsat all — only present as a peer of the equally unusedeslint-import-resolver-typescriptTwo of the three "blockers" were doing nothing. That's the bulk of the lockfile shrink.
Behaviour changes
react/no-unused-prop-typesis dropped. ESLint React has no equivalent (it's a PropTypes-era rule). Costs nothing here — this package is TypeScript-only and ships no PropTypes.jsx-runtimeconfig is gone. ESLint 10 tracks JSX references natively.no-children-map/no-children-to-array/no-clone-element(9×) — this is the library's core mechanism: it reads<Tab>children's props and clones them with the wiring they need.no-forward-ref—peerDependenciesstill declarereact: ^17 \|\| ^18 \|\| ^19. Passingrefas a plain prop would break every consumer below 19. Revisit when the peer range drops <19.naming-convention-ref-name(4×) — assumesuseRefholds a DOM ref; ours hold a timer handle, a focus index and a node array.timeoutIdandtabRefsare the accurate names.use-state— false positive;useStateis already destructured, the rule just doesn't recognise a destructuring pattern in the value position.Also
eslint/@eslint/jsmajor holds fromdependabot.yml(added in ci: hold back the ESLint and TypeScript majors that are blocked upstream #123) — there's nothing left to hold. Thetypescripthold stays; TS 7 is a separate block.@eslint-react/eslint-plugin. Verified it does run on Node 20 in practice, but matching the declared engine avoids surprises.Verification
pnpm lintexits 0 with zero warnings,pnpm buildexits 0, 68/68 tests pass across 5 files. New dependencies are MIT with SLSA provenance attestations.🤖 Generated with Claude Code