Build1 distinct publisher3 min readUpdated
Same declared ESLint range, opposite outcomes: 39 jsx-a11y rules run clean on 10.9.0 while 38 of eslint-plugin-react's 101 throw, and one line of config moves 32 of them.
The Engineer · Build desk
Compiled by The EngineerSomething wrong?How this is made
`resolveBasedir` is where both react numbers come from. It accepts either a string or a rule context, and when it gets the context it calls `contextOrFilename.getFilename()`, a method ESLint 10 removed [7]. Every rule that wants to know which React version you are running walks through that function, which is why the casualty list looks arbitrary next to what the rules actually check: `prop-types`, `no-multi-comp`, `sort-comp`, `hook-use-state` [8]. They are not version-sensitive checks. They just ask a question that no longer has an answer.
The arithmetic is worth doing by hand. The author of the eslint10-matrix survey reports 38 crashing rules with `settings: { react: { version: 'detect' } }` and 6 with no `settings.react.version` at all, same fixtures otherwise [9]. Thirty-eight minus thirty-two is six, and the six named survivors of the second run are precisely the ones failing on something other than version lookup: four on `isSpaceBetweenTokens`, one on `getSourceCode`, one on a `getFilename` call of its own [10][1]. So version detection accounts for the whole config-dependent surface, and one line in a shared config decides whether your CI reports 38 broken rules or 6 [9].
Then the harness, which is the part most people will get wrong. ESLint aborts the run at the first rule that throws, so "enable everything and count the failures" returns 1 no matter how bad the state is [11]. The fix used here is to lint with every rule on and, when that explodes, re-lint one rule at a time, 101 passes for a single plugin, affordable because `Linter.verify` on a handful of files takes milliseconds [12]. The first pass through that harness named `@typescript-eslint/eslint-plugin` the worst result in the set with 64 crashing rules [13]. It was wrong. Those rules were refusing to start because `parserOptions` was not configured to generate type information, and they failed identically on ESLint 9 [14]. The same control caught `typescript@latest` now being TS 7.0, which typescript-eslint hard-refuses, breaking four plugins in a shape that did not look like a version problem [16], and `--legacy-peer-deps` silently skipping peers, which left `@angular-eslint` without `@typescript-eslint/utils` and looking broken when it was fine [17].
The failure mode that no manifest can warn you about is vitest and eslint-plugin-deprecation, which never load at all: `Class extends value undefined is not a constructor or null` [18]. Neither plugin's own code is involved. `@typescript-eslint/utils` declares `class LegacyESLint extends eslint.LegacyESLint`, and ESLint 10 removed `LegacyESLint` when it removed eslintrc [19]. A declared ceiling of 9 can mean a clean run on 10 [1][3], and a plugin that declares nothing amiss can be dead two levels down its dependency tree [18]. The survey behind these numbers installs 54 plugins into clean temp directories against 9.39.5 and 10.9.0 nightly with every rule enabled [20], which is roughly the minimum shape of an answer that is not a guess about when someone last edited a JSON field.
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
Installed with --legacy-peer-deps against ESLint 10 with all 39 rules the plugin exports enabled and pointed at ordinary JSX, eslint-plugin-jsx-a11y produced zero crashes.
The survey installs 54 plugins into clean temp directories against ESLint 9.39.5 and 10.9.0 with every rule enabled, nightly, published as a matrix.
[email protected] declares peerDependencies of eslint "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9".
ESLint's current release is 10.9.0, so npm refuses to resolve [email protected] into the same tree, reporting a peer dependency resolution error.
[email protected] declares an almost identical peer range, and under the same treatment 38 of its 101 rules threw.
The reported error is: Error while loading rule 'react/display-name': contextOrFilename.getFilename is not a function.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Detailed and self-correcting, but single-source and unreproduced
The post supplies verbatim error strings, the offending source function (resolveBasedir in lib/util/version.js), the specific removed APIs (context.getFilename, LegacyESLint), a named transitive cause for the vitest/deprecation load failures, and internally consistent counts (38 minus 32 getFilename failures equals the 6 config-independent failures). It also documents a method, retracts a wrong result, and enumerates its own limits. Against that, everything rests on one self-reported harness from one publisher, with no maintainer confirmation, no per-plugin data for the other 52 plugins, and an acknowledged fixture corpus that can mark a broken rule clean.
Real upstream release, lagging plugin support, unmeasured tooling uptake
There is genuine adoption-side signal on the ecosystem: ESLint 10.9.0 is the shipped current release with breaking API removals, while latest-version plugins still declare peer ranges capped at ^9 and at least two plugins fail to load through a transitive dependency. There is no usage evidence for the author's own matrix or npx checker — no downloads, stars, dependents, or third-party users — and no data on how many teams have actually attempted the ESLint 10 upgrade, so the measurable adoption is upstream release status plus documented downstream lag, not diffusion of this story's tooling.
Claims sit slightly below what the evidence would allow
The headline framing is punchy but literally what the test showed, and the article repeatedly narrows rather than inflates: it caps the result at two ESLint versions, latest plugin versions, flat config, and a fixture corpus; it flags that its own strongest number was wrong once; it explains why a colleague's count may differ; and it notes the react count should be obsoleted by a pending upstream PR. The mild negative reflects that a promoted tool is presented with fewer guarantees than the underlying evidence would support, offset by the fact that all numbers remain unverified by anyone else.
Author promotes the tool the findings justify
The write-up is a personal developer-platform post whose findings lead directly to the author's own artifacts: a GitHub Pages compatibility matrix and an npx eslint10-matrix check command, with a call for PRs to his fixtures. That is a clear self-promotional incentive to make plugin incompatibility feel urgent and manifests feel untrustworthy. Mitigating factors are the disclosed limits, the publicly retracted false positive, and the absence of any commercial, vendor, or funding relationship disclosed or implied in the source; no sponsorship, employer, or paid-product interest is evidenced.
Mechanisms convincing, counts single-sourced
Confidence is moderate: the causal explanations (removed context.getFilename, removed LegacyESLint reached through @typescript-eslint/utils, ESLint aborting on first throw) are specific and checkable against the quoted code and error text, so the qualitative story is likely right. The quantitative story depends entirely on one unaudited harness by one author, at one point in time, over a fixture corpus he calls the weakest part, on plugin versions that a pending upstream PR is expected to change. One publisher in the cluster means no cross-source corroboration is possible.
build
263 green tests, every value undefined: the schema mirror that agreed with itself1 distinct publisher
build
Agent reliability is a harness problem, not a prompt problem1 distinct publisher
build
Punctuation is a three-way classification, not a model call1 distinct publisher
build
A build step instead of a backend: 1,025 records, 8 locales, no runtime API1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 22, 2026