Skip to content

Upgrade ESLint v9 → v10 and migrate JSX spacing rules to @stylistic#28

Open
AlbertSmit wants to merge 1 commit into
mainfrom
migrate-to-eslint-v9
Open

Upgrade ESLint v9 → v10 and migrate JSX spacing rules to @stylistic#28
AlbertSmit wants to merge 1 commit into
mainfrom
migrate-to-eslint-v9

Conversation

@AlbertSmit

@AlbertSmit AlbertSmit commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

What changed

Incremental upgrade from ESLint v9 (already on main via #29) to ESLint v10.

Dependencies

Package Before After Why
eslint ^9.39.4 ^10.0.0 Major version upgrade
@eslint/js ^9.39.4 ^10.0.0 Aligned with eslint
@babel/core ^7.11.6 ^7.26.0 Modernize
@babel/eslint-parser ^7.16.5 ^7.26.0 Modernize
eslint-plugin-react-hooks ^7.1.1 ^7.0.0 Widen range
eslint-import-resolver-node ^0.4.0 ^0.3.9 Compat
globals ^16.0.0 Replaces inlined machinery/globals.json

Rule migration

3 JSX spacing rules moved from eslint-plugin-react to @stylistic/eslint-plugin:

  • react/jsx-curly-spacing@stylistic/jsx-curly-spacing
  • react/jsx-equals-spacing@stylistic/jsx-equals-spacing
  • react/jsx-tag-spacing@stylistic/jsx-tag-spacing

These rules crash on ESLint v10 because eslint-plugin-react uses sourceCode.isSpaceBetweenTokens(), which was removed in v10. The @stylistic versions are forked from the same source but updated for the new API.

Test suite (ESLint v10 RuleTester)

  • Removed type property from error assertions (~20 files) — v10 RuleTester rejects it
  • Updated no-eval error message (backtick quoting change in v10)
  • no-object-constructor now requires suggestions assertion
  • Restored 7 previously-stubbed tests with real test cases

Cleanup

  • Removed migration CLI (bin/migrate-to-flat-config.js) — one-time tool, no longer needed
  • Removed inlined machinery/globals.json (1273 lines) in favor of globals npm package
  • Code style: template literals to string literals where no interpolation, removed unnecessary else branches

Test results

tests 183 | pass 182 | fail 0 | todo 1 (no-mixed-operators, pre-existing)

Known blockers for clean v10 adoption

1. @babel/eslint-parser — scopeManager.addGlobals crash

ESLint v10 ships with eslint-scope 9.x which requires ScopeManager.addGlobals(). Stable @babel/eslint-parser 7.x does not implement this. Fix exists in @babel/eslint-parser 8.0.0-rc.2 but is not yet stable.

Impact: prop-types.test.js is commented out — the RuleTester + babel parser combination crashes. The rule itself still works at runtime.

2. eslint-plugin-react — removed internal APIs

eslint-plugin-react 7.37.5 does not declare ESLint v10 in its peerDependencies. The 3 JSX spacing rules above are the concrete breakage; worked around via @stylistic.

3. eslint-plugin-jsx-a11y — peer dep mismatch

eslint-plugin-jsx-a11y 6.10.2 peers on eslint 3-9, not v10. Functionally works but produces a pnpm warning.

When to merge

This PR can be merged as-is (everything works with the workarounds), or we can wait until:

  • @babel/eslint-parser 8.x ships stable → uncomment prop-types.test.js
  • eslint-plugin-react and eslint-plugin-jsx-a11y release v10-compatible versions → clean peer deps

@coderabbitai

coderabbitai Bot commented Apr 24, 2026

Copy link
Copy Markdown

Walkthrough

This PR removes the ESLint config migration CLI, adopts the npm globals package, migrates JSX formatting rules to @stylistic/eslint-plugin, normalizes test fixtures across the suite, refactors rule implementations for modern JavaScript patterns, and updates dependencies for ESLint v10 compatibility with a beta version bump.

Changes

Globals Dependency Migration

Layer / File(s) Summary
Config and source migration to globals package
eslint.config.js
The main config now imports and spreads multiple global environments (commonjs, es2015, jest, node, browser) from the external globals npm package instead of a local JSON file.
Test harness updates for globals package
rules/core/no-global-assign/test.js, rules/core/no-implied-eval/test.js, rules/core/no-loop-func/test.js, rules/core/no-native-reassign/test.js
Test files import globals from npm and adjust RuleTester languageOptions to use globals.browser or globals.node instead of loading a local JSON file.

JSX Rule Migration to @stylistic Plugin

Layer / File(s) Summary
Config and rule wiring for @stylistic rules
eslint.config.js
Updates eslint.config.js to use @stylistic/jsx-curly-spacing, @stylistic/jsx-equals-spacing, and @stylistic/jsx-tag-spacing instead of the equivalent react/jsx-* rules.
JSX spacing rule test migrations
rules/third-party/react/jsx-curly-spacing.test.js, rules/third-party/react/jsx-equals-spacing.test.js, rules/third-party/react/jsx-tag-spacing.test.js
Test files import and execute the @stylistic/eslint-plugin versions of JSX formatting rules, with test fixtures adjusted to single-quoted strings.

Migration CLI Removal

Layer / File(s) Summary
Remove migration tool infrastructure
README.md, package.json
Deletes migration CLI documentation from README, removes the scripts.migrate npm script, and removes the bin.kaliber-eslint-migrate executable entry. The bin/migrate-to-flat-config.js and bin/migrate-to-flat-config.test.js files are removed entirely.

Rule Implementation Improvements

Layer / File(s) Summary
AST utility and helper refactoring
machinery/ast.js, machinery/filename.js
Improves getPropertyName to handle TemplateLiteral nodes, throws errors for unhandled types instead of warning, refactors getRootFunctionScope error handling, simplifies getParentJSXElements recursion, and clarifies control flow in getBaseFilename.
Rule visitor and selector refactoring
rules/component-properties/index.js, rules/import-sort/index.js, rules/jsx-key/index.js, rules/layout-class-name/index.js, rules/naming-policy/index.js
Updates ESLint visitor selectors from computed array keys to direct string keys and object-method syntax; introduces optional chaining for safer property access; reformats validation messages into template literals.
Path utility and string formatting updates
lib/absolute-path-resolver-plugin.js
Switches from string concatenation ('.' + source) to template literal interpolation for path resolution.

Test Fixture Normalization

Layer / File(s) Summary
Core ESLint rule test normalization
rules/core/*.test.js
Standardizes test case string formatting from backticks to single quotes and removes type field assertions from error objects across comma-dangle, comma-spacing, comma-style, dot-location, eol-last, eqeqeq, key-spacing, keyword-spacing, new-parens, no-array-constructor, no-caller, no-class-assign, no-compare-neg-zero, no-cond-assign, no-const-assign, no-constant-condition, no-control-regex, no-debugger, no-delete-var, no-dupe-args, no-dupe-class-members, no-dupe-keys, no-duplicate-case, no-empty-character-class, no-empty-pattern, no-eval, no-ex-assign, no-extend-native, no-extra-bind, and no-extra-boolean-cast tests.
Built-in rule test activation and placeholder replacement
rules/core/no-extra-label.test.js, rules/core/no-iterator.test.js, rules/core/no-new-object.test.js, rules/core/no-new-symbol.test.js, rules/core/no-proto.test.js
Replaces skipped node:test placeholders with active ESLint RuleTester implementations for deprecated/renamed built-in rules (no-extra-label, no-iterator, no-object-constructor, no-new-native-nonconstructor, no-proto).
Custom rule test fixture updates
rules/component-properties/policy-*.js, rules/layout-class-name/test.js, rules/naming-policy/policy-*.js, rules/position-center/test.js, rules/no-default-export/test.js
Normalizes test strings to single quotes and removes type assertions from error objects in component-properties, layout-class-name, naming-policy, position-center, and no-default-export rule tests.
Third-party plugin rule test normalization
rules/third-party/import-*.test.js, rules/third-party/jsx-a11y-*.test.js, rules/third-party/react/*.test.js
Standardizes string literals and error assertions across all import, JSX a11y, and React plugin rule tests; removes type fields from error expectations and updates to single-quoted strings; disables prop-types test due to ESLint v10 compatibility issue.

Package and Infrastructure Updates

Layer / File(s) Summary
Version bump and dependency refresh
package.json
Bumps version to 2.0.0-beta.1, adds the globals package, and updates ESLint/Babel-related dependencies (@babel/core, @babel/eslint-parser, @eslint/js, eslint, eslint-import-resolver-node, eslint-plugin-react-hooks).
Workspace configuration simplification
pnpm-workspace.yaml
Removes workspace-level settings (allowBuilds, minimumReleaseAge, minimumReleaseAgeExclude) and keeps only nodeLinker: hoisted and autoInstallPeers: false.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • kaliberjs/eslint-plugin#27: Shares modifications to rule implementations like rules/import-sort/index.js and rules/position-center/index.js, indicating overlapping refactoring work.

Suggested reviewers

  • guid-ooo
  • tsharms
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately and specifically summarizes the primary changes: upgrading ESLint from v9 to v10 and migrating JSX spacing rules to @stylistic, both major aspects evident throughout the changeset.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch migrate-to-eslint-v9

@socket-security

socket-security Bot commented Apr 24, 2026

Copy link
Copy Markdown

@AlbertSmit AlbertSmit self-assigned this Apr 24, 2026
@AlbertSmit AlbertSmit changed the title Migrate ESLint configuration to flat format and update tests Migrate to ESLint v9 Apr 24, 2026
@AlbertSmit AlbertSmit changed the title Migrate to ESLint v9 Migrate to ESLint v10, flat config, and pnpm May 28, 2026
ESLint v10 breaking changes addressed:
- Bump eslint and @eslint/js to ^10.0.0
- Migrate react/jsx-{curly,equals,tag}-spacing to @Stylistic equivalents
  (eslint-plugin-react uses removed sourceCode.isSpaceBetweenTokens API)
- Remove type property from error assertions (RuleTester v10 rejects it)
- Update no-eval error message (backtick-quoted in v10)
- Comment out prop-types test (scopeManager.addGlobals not in babel/eslint-parser stable)

Other improvements:
- Switch from inlined globals.json to globals npm package
- Bump babel/core and babel/eslint-parser to ^7.26.0
- Bump eslint-plugin-react-hooks to ^7.0.0
- Remove migration CLI (bin/migrate-to-flat-config.js) and related README docs
- Restore 7 previously-stubbed tests with real test cases
- Clean up ast.js: throw on unknown node types, remove unnecessary else branches
- Convert template literals to string literals where no interpolation needed

Known issue: prop-types.test.js is commented out -- babel/eslint-parser@7.x
does not implement scopeManager.addGlobals() required by ESLint v10.
Fix available in babel/eslint-parser@8.0.0-rc.2 (not yet stable).
@AlbertSmit AlbertSmit force-pushed the migrate-to-eslint-v9 branch from b85c1d5 to 0ae0399 Compare May 29, 2026 14:08

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
rules/naming-policy/index.js (1)

107-117: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix reportUnderscoreProperties to avoid crashing on non-string styles[...] keys.

MemberExpression[object.name = 'styles'] calls reportUnderscoreProperties, which does const name = getPropertyName(property) and then name.startsWith(...). getPropertyName returns property.value for Literal, so name can be non-string (null, numbers, etc.), making .startsWith throw. Add a string/type guard (e.g., typeof name === 'string') before using .startsWith, while keeping the '_root' exemption.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rules/naming-policy/index.js` around lines 107 - 117, The function
reportUnderscoreProperties can crash when getPropertyName(property) returns a
non-string; add a type guard so you only call .startsWith on strings: call const
name = getPropertyName(property); if (typeof name !== 'string') return; then
keep the existing checks (if (!name.startsWith('_') || name.startsWith('_root'))
return) and the context.report using messages['no styles properties with
_'](name) and node: property; this ensures MemberExpression handling for
styles[...] keys won't throw while preserving the '_root' exemption.
🧹 Nitpick comments (1)
rules/third-party/import-export.test.js (1)

6-17: ⚖️ Poor tradeoff

Optional: restoring invalid coverage needs new cross-file fixtures (current mocks don’t model re-export conflicts).

The same-file duplicate-export exclusion is consistent with the ESLint v10 parse-error limitation, but rules/third-party/mocks only includes export-a.js, export-default-a.js, and my-component.js—none re-export from another module—so there’s no existing cross-file conflicting re-export fixture to reuse. Adding real negative coverage would require introducing new re-exporting mock modules and addressing the fatal parsing issue documented in rules/third-party/import-export-retry-failure.md.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rules/third-party/import-export.test.js` around lines 6 - 17, The test suite
for the "import/export" rule only asserts valid cases and omits cross-file
invalid fixtures because the mocks folder lacks re-exporting modules; to restore
negative coverage add new mock modules under rules/third-party/mocks that create
cross-file re-export conflicts (e.g., create a module that re-exports from
another mock to clash with export-a.js or export-default-a.js), update the
RuleTester invocation in rules/third-party/import-export.test.js (the
ruleTester.run('import/export', rule, { valid: [...], invalid: [] }) block) to
include cases that reference those new mock paths, and ensure you handle the
ESLint v10 parse-fatal issue described in
rules/third-party/import-export-retry-failure.md (either by adjusting the
fixtures to avoid same-file duplicate exports or by adding retry/fatal handling
as documented) so the tests exercise cross-file re-export conflicts without
causing fatal parse errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rules/third-party/react/prop-types.test.js`:
- Around line 1-48: Replace the large commented RuleTester block with an
explicit skipped test so it remains visible in coverage and easy to re-enable:
restore the RuleTester setup (RuleTester import, rule =
require('eslint-plugin-react').rules['prop-types'], and the ruleTester.run(...)
call) but wrap it in a top-level describe.skip (or it.skip) named
"react/prop-types" and include the original TODO comment about
scopeManager.addGlobals/ESLint v10 + `@babel/eslint-parser`; ensure the
identifiers RuleTester, rule, and ruleTester.run are used exactly as before and
keep the same valid/invalid test cases unchanged inside the skipped block.

---

Outside diff comments:
In `@rules/naming-policy/index.js`:
- Around line 107-117: The function reportUnderscoreProperties can crash when
getPropertyName(property) returns a non-string; add a type guard so you only
call .startsWith on strings: call const name = getPropertyName(property); if
(typeof name !== 'string') return; then keep the existing checks (if
(!name.startsWith('_') || name.startsWith('_root')) return) and the
context.report using messages['no styles properties with _'](name) and node:
property; this ensures MemberExpression handling for styles[...] keys won't
throw while preserving the '_root' exemption.

---

Nitpick comments:
In `@rules/third-party/import-export.test.js`:
- Around line 6-17: The test suite for the "import/export" rule only asserts
valid cases and omits cross-file invalid fixtures because the mocks folder lacks
re-exporting modules; to restore negative coverage add new mock modules under
rules/third-party/mocks that create cross-file re-export conflicts (e.g., create
a module that re-exports from another mock to clash with export-a.js or
export-default-a.js), update the RuleTester invocation in
rules/third-party/import-export.test.js (the ruleTester.run('import/export',
rule, { valid: [...], invalid: [] }) block) to include cases that reference
those new mock paths, and ensure you handle the ESLint v10 parse-fatal issue
described in rules/third-party/import-export-retry-failure.md (either by
adjusting the fixtures to avoid same-file duplicate exports or by adding
retry/fatal handling as documented) so the tests exercise cross-file re-export
conflicts without causing fatal parse errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 13e26615-27f3-49ec-b22b-6f68100058cd

📥 Commits

Reviewing files that changed from the base of the PR and between 3d40fc4 and 0ae0399.

📒 Files selected for processing (101)
  • README.md
  • bin/migrate-to-flat-config.js
  • bin/migrate-to-flat-config.test.js
  • eslint.config.js
  • lib/absolute-path-resolver-plugin.js
  • machinery/ast.js
  • machinery/filename.js
  • machinery/globals.json
  • package.json
  • pnpm-workspace.yaml
  • rules/component-properties/index.js
  • rules/component-properties/policy-destructure-props.js
  • rules/component-properties/policy-no-setters.js
  • rules/component-properties/policy-variable-passing.js
  • rules/core/comma-dangle.test.js
  • rules/core/comma-spacing.test.js
  • rules/core/comma-style.test.js
  • rules/core/dot-location.test.js
  • rules/core/eol-last.test.js
  • rules/core/eqeqeq.test.js
  • rules/core/key-spacing.test.js
  • rules/core/keyword-spacing.test.js
  • rules/core/new-parens.test.js
  • rules/core/no-array-constructor.test.js
  • rules/core/no-caller.test.js
  • rules/core/no-class-assign.test.js
  • rules/core/no-compare-neg-zero.test.js
  • rules/core/no-cond-assign.test.js
  • rules/core/no-const-assign.test.js
  • rules/core/no-constant-condition.test.js
  • rules/core/no-control-regex.test.js
  • rules/core/no-debugger.test.js
  • rules/core/no-delete-var.test.js
  • rules/core/no-dupe-args.test.js
  • rules/core/no-dupe-class-members.test.js
  • rules/core/no-dupe-keys.test.js
  • rules/core/no-duplicate-case.test.js
  • rules/core/no-empty-character-class.test.js
  • rules/core/no-empty-pattern.test.js
  • rules/core/no-eval.test.js
  • rules/core/no-ex-assign.test.js
  • rules/core/no-extend-native.test.js
  • rules/core/no-extra-bind.test.js
  • rules/core/no-extra-boolean-cast.test.js
  • rules/core/no-extra-label.test.js
  • rules/core/no-global-assign/test.js
  • rules/core/no-implied-eval/test.js
  • rules/core/no-iterator.test.js
  • rules/core/no-loop-func/test.js
  • rules/core/no-native-reassign/test.js
  • rules/core/no-new-object.test.js
  • rules/core/no-new-symbol.test.js
  • rules/core/no-proto.test.js
  • rules/data-x-unique-id/index.js
  • rules/import-sort/index.js
  • rules/jsx-key/index.js
  • rules/layout-class-name/index.js
  • rules/layout-class-name/test.js
  • rules/naming-policy/index.js
  • rules/naming-policy/policy-component-name.js
  • rules/naming-policy/policy-css-file-and-variable-name.js
  • rules/naming-policy/policy-css-variable-properties.js
  • rules/naming-policy/policy-ref-name.js
  • rules/naming-policy/policy-root-element-class-name.js
  • rules/no-default-export/test.js
  • rules/position-center/index.js
  • rules/position-center/test.js
  • rules/third-party/import-default.test.js
  • rules/third-party/import-export.test.js
  • rules/third-party/import-first.test.js
  • rules/third-party/import-named.test.js
  • rules/third-party/import-no-amd.test.js
  • rules/third-party/import-no-unresolved.test.js
  • rules/third-party/jsx-a11y-alt-text.test.js
  • rules/third-party/jsx-a11y-anchor-has-content.test.js
  • rules/third-party/jsx-a11y-anchor-is-valid.test.js
  • rules/third-party/jsx-a11y-aria-activedescendant-has-tabindex.test.js
  • rules/third-party/jsx-a11y-aria-props.test.js
  • rules/third-party/jsx-a11y-aria-proptypes.test.js
  • rules/third-party/jsx-a11y-aria-role.test.js
  • rules/third-party/jsx-a11y-aria-unsupported-elements.test.js
  • rules/third-party/jsx-a11y-heading-has-content.test.js
  • rules/third-party/jsx-a11y-html-has-lang.test.js
  • rules/third-party/jsx-a11y-iframe-has-title.test.js
  • rules/third-party/jsx-a11y-img-redundant-alt.test.js
  • rules/third-party/jsx-a11y-no-access-key.test.js
  • rules/third-party/jsx-a11y-no-distracting-elements.test.js
  • rules/third-party/jsx-a11y-no-redundant-roles.test.js
  • rules/third-party/jsx-a11y-scope.test.js
  • rules/third-party/react/jsx-boolean-value.test.js
  • rules/third-party/react/jsx-curly-spacing.test.js
  • rules/third-party/react/jsx-equals-spacing.test.js
  • rules/third-party/react/jsx-indent.test.js
  • rules/third-party/react/jsx-no-comment-textnodes.test.js
  • rules/third-party/react/jsx-no-undef.test.js
  • rules/third-party/react/jsx-pascal-case.test.js
  • rules/third-party/react/jsx-tag-spacing.test.js
  • rules/third-party/react/no-danger-with-children.test.js
  • rules/third-party/react/prop-types.test.js
  • rules/third-party/react/self-closing-comp.test.js
  • rules/third-party/react/void-dom-elements-no-children.test.js
💤 Files with no reviewable changes (5)
  • README.md
  • rules/position-center/test.js
  • bin/migrate-to-flat-config.test.js
  • machinery/globals.json
  • bin/migrate-to-flat-config.js

Comment on lines +1 to +48
// TODO: eslint-plugin-react@7.37.5 prop-types rule triggers scopeManager.addGlobals error
// with @babel/eslint-parser in ESLint v10. This test is skipped until eslint-plugin-react
// is updated for ESLint v10 compatibility, or until we migrate to @eslint-react/eslint-plugin.
//
// const { RuleTester } = require('eslint')
// const rule = require('eslint-plugin-react').rules['prop-types']
//
// const ruleTester = new RuleTester({
// languageOptions: {
// parser: require('@babel/eslint-parser'),
// ecmaVersion: 2020,
// sourceType: 'module',
// parserOptions: {
// ecmaFeatures: { jsx: true },
// requireConfigFile: false,
// babelOptions: {
// presets: [require.resolve('@babel/preset-react')]
// },
// },
// },
// })
//
// ruleTester.run('react/prop-types', rule, {
// valid: [
// `
// class Hello extends React.Component {
// static propTypes = {
// name: require('prop-types').string.isRequired,
// };
// render() {
// return <div>Hello {this.props.name}</div>;
// }
// }
// `,
// ],
// invalid: [
// {
// code: `
// class Hello extends React.Component {
// render() {
// return <div>Hello {this.props.name}</div>;
// }
// }
// `,
// errors: [{ message: "'name' is missing in props validation" }],
// },
// ],
// })

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Has eslint-plugin-react (after 7.37.0) fixed prop-typesRuleTester compatibility with ESLint v10 +@babel/eslint-parser (scopeManager.addGlobals-related errors)?

💡 Result:

No clear evidence that eslint-plugin-react (specifically after v7.37.0) fixed prop-types RuleTester compatibility with ESLint v10 + @babel/eslint-parser for scopeManager.addGlobals-related errors. What the sources show: 1) ESLint v10 introduced a requirement for ScopeManager#addGlobals, and custom ScopeManager implementations must provide it. This is the API change behind errors like “scopeManager.addGlobals is not a function.” [1] 2) A separate, concrete failure report exists for @babel/eslint-parser + ESLint 10: tests throw “TypeError: scopeManager.addGlobals is not a function,” and the resolution involved adjusting the eslint-scope dependency. [2] 3) The eslint-plugin-react repo has ongoing ESLint v10 compatibility work (e.g., PRs addressing ESLint v10 RuleTester/schema changes), but the surfaced PR excerpt is about updating/removing RuleTester error assertion fields for ESLint 10, not about the specific scopeManager.addGlobals runtime failure. [3] 4) An open issue states the plugin was unable to use with ESLint v10 at least at the time of that report, and explicitly says it wasn’t compatible until the peer dependency range is updated. [4] 5) The eslint-plugin-react changelog entries around v7.37.0 include fixes in the prop-types rule (including a “null-check rootNode before calling getScope” change), but the changelog snippet does not mention ESLint v10, @babel/eslint-parser, RuleTester compatibility, or scopeManager.addGlobals. [5] Conclusion: - The “scopeManager.addGlobals” error is tied to ESLint v10’s ScopeManager API change [1] and has been reported with @babel/eslint-parser [2]. - While eslint-plugin-react has ESLint v10-related test fixes [3] and prop-types-specific fixes in/around v7.37.0 [5], the provided authoritative sources do not demonstrate that eslint-plugin-react’s later changes specifically resolved the addGlobals-related RuleTester incompatibility with @babel/eslint-parser. If you hit this error today, the most evidence-backed fix path is to ensure your @babel/eslint-parser / eslint-scope dependency chain provides the expected ScopeManager#addGlobals behavior (as the @babel/eslint-parser report indicates via overriding eslint-scope). [2]

Citations:


Convert the commented-out RuleTester into an explicit skipped test

The file currently comments out the entire RuleTester run (dropping coverage and making re-enablement easy to miss). Rework it into the repo’s idiomatic “intentionally pending” skip (e.g., it.skip/describe.skip or a conditional guard) while preserving the existing TODO rationale.

The scopeManager.addGlobals failure is tied to ESLint v10’s ScopeManager API change and has been reported with @babel/eslint-parser; there isn’t clear evidence that eslint-plugin-react changes after ~v7.37.0 specifically resolved this exact incompatibility, so keeping it skipped is reasonable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rules/third-party/react/prop-types.test.js` around lines 1 - 48, Replace the
large commented RuleTester block with an explicit skipped test so it remains
visible in coverage and easy to re-enable: restore the RuleTester setup
(RuleTester import, rule = require('eslint-plugin-react').rules['prop-types'],
and the ruleTester.run(...) call) but wrap it in a top-level describe.skip (or
it.skip) named "react/prop-types" and include the original TODO comment about
scopeManager.addGlobals/ESLint v10 + `@babel/eslint-parser`; ensure the
identifiers RuleTester, rule, and ruleTester.run are used exactly as before and
keep the same valid/invalid test cases unchanged inside the skipped block.

@AlbertSmit AlbertSmit changed the title Migrate to ESLint v10, flat config, and pnpm Upgrade ESLint v9 → v10 and migrate JSX spacing rules to @stylistic May 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant