Skip to content

ESLint v10 support#3230

Open
rasmi wants to merge 10 commits intoimport-js:mainfrom
rasmi:eslint-update
Open

ESLint v10 support#3230
rasmi wants to merge 10 commits intoimport-js:mainfrom
rasmi:eslint-update

Conversation

@rasmi
Copy link
Copy Markdown

@rasmi rasmi commented Feb 19, 2026

Summary

Add ESLint v10 support while maintaining backward compatibility with ESLint v2–v9. The v10 compat changes in shipped code use feature detection (e.g. checking if context.languageOptions exists) rather than version checks, and fix two pre-existing bugs in export * as handling exposed by modern parsers.

Closes #3227 (ESLint v10 support) (and maybe #3079).

Fixes #2002, #1883, #1834, #1845, & #2289 (pre-existing export * as bugs exposed by modern parsers).

What changed

Removed API replacements

ESLint v10 removed several deprecated context properties and SourceCode methods. Each call site now feature-detects the new API and falls back to the old one:

  • context.parserOptionscontext.languageOptions.parserOptions (affects sourceType.js, childContext.js, typescript.js, scc.js, parse.js)
  • context.parserPathcontext.languageOptions used for cache hashing instead (scc.js)
  • context.getPhysicalFilename()context.physicalFilename property (contextCompat.js)
  • context.getScope() → already had a compat wrapper, but one call site in declaredScope.js bypassed it — now fixed
  • sourceCode.getTokenOrCommentAfter/Before()sourceCode.getTokenAfter/Before(node, { includeComments: true }) (order.js)

no-unused-modules file enumeration fallback

ESLint v10 removed FileEnumerator and the internal glob-utils module. The existing cascade (FileEnumerator → legacy glob-utils) now has a third tier: a Node.js fs + minimatch fallback that walks directories and matches globs.

  • Only reached when both prior methods throw MODULE_NOT_FOUND or ERR_PACKAGE_PATH_NOT_EXPORTED
  • Uses minimatch and is-glob (existing direct dependencies)
  • On v10, this resolves #3079 (flat config requiring a dummy .eslintrc) since FileEnumerator is no longer used. The issue remains on v9.

ExportMap export * as fix

Fixed two pre-existing bugs in ExportAllDeclaration handling that affected modern parsers (espree ES2020+, @babel/eslint-parser) but not babel-eslint. These were latent because babel-eslint represents export * as as ExportNamedDeclaration with ExportNamespaceSpecifier (a working code path); modern parsers use ExportAllDeclaration with exported, which hit the broken path.

  • specifier.js: export * as ns passed a string to namespace.add() (expects AST node), so the namespace getter was never set up. Fixed by defining the getter directly, matching the ExportNamespaceSpecifier pattern.
  • visitor.js: export * as ns unconditionally added to the star-export dependencies set, incorrectly flattening the target module's exports. Fixed by only adding to dependencies for plain export * (without as).

False positives fixed (incorrect errors that will stop being reported): #2002, #1883, #1834, #1845, #2289.

False negatives fixed (behavior change on v9+ with espree): the namespace rule will now correctly flag invalid deep references through export * as re-exports that were previously missed.

Test infrastructure (test files only)

On v10, the unmaintained babel-eslint (Babel 6) is replaced with @babel/eslint-parser v8 for test coverage. This restores ~475 babel-related tests that would otherwise be skipped on v10.

  • FlatCompatRuleTester extended to auto-inject @babel/eslint-parser config (requireConfigFile: false, babelrc: false, syntax plugins) and handle v10 RuleTester strictness
  • 5 no-duplicates tests skipped: duplicate import identifiers correctly rejected by @babel/eslint-parser
  • TypeScript syntax tests gated for @typescript-eslint/parser v8 (import type Default, { Named } removed in TS 5.0)
  • Angular template parser upgraded to v21 on v10 (v13 does not support ESLint 10; angular-eslint#2903)
  • eslint-plugin-json test skipped on v10 pending azeemba/eslint-plugin-json#97

Dependency changes

Package Change Why
eslint || ^10 added to peerDeps and devDeps v10 support

v10-specific parser deps are installed via dep-time-travel.sh (not in devDeps) to avoid peer dep conflicts with typescript@4.5.5 during npm install:

  • @typescript-eslint/parser@8 — on all ESLint 10 Node versions
    -@angular-eslint/template-parser@21 — on all ESLint 10 Node versions (v13 does not support ESLint 10)
  • @babel/eslint-parser@8 + @babel/core@8 (ESM, RC) — on Node >= 20
  • No babel parser on Node 18/19/21 (v8 is ESM-only, engines ^20.19.0 || >=22.12.0; v7 will not get ESLint 10 support). Babel tests are skipped; all other tests run.

CI changes

  • eslint-8+.yml: added ESLint 10 to the matrix, excluded Node < 18
  • dep-time-travel.sh: installs v10-specific parser deps (@typescript-eslint/parser@8 and @angular-eslint/template-parser@21 on all nodes, @babel/eslint-parser@8 on Node >= 20 only)
  • See also Fix resolve install in old npm version on CI. #3237 to fix tests failing in main.

Test results

2991 passing, 0 failing, 3 pending.

The 3 pending (skipped) tests:

Files modified

Source (12 files):
package.json, src/core/sourceType.js, src/exportMap/childContext.js, src/exportMap/specifier.js, src/exportMap/visitor.js, src/exportMap/typescript.js, src/rules/no-unused-modules.js, src/rules/order.js, src/scc.js, utils/contextCompat.js, utils/declaredScope.js, utils/parse.js

Tests (10 files):
tests/src/rule-tester.js, tests/src/utils.js, tests/src/cli.js, tests/src/rules/first.js, tests/src/rules/group-exports.js, tests/src/rules/order.js, tests/src/rules/namespace.js, tests/src/rules/no-duplicates.js, tests/src/rules/no-empty-named-blocks.js, tests/src/rules/no-unused-modules.js

CI (2 files):
tests/dep-time-travel.sh, .github/workflows/eslint-8+.yml

@socket-security
Copy link
Copy Markdown

socket-security bot commented Feb 19, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​typescript-eslint/​parser@​7.18.01001007198100
Addedcross-env@​7.0.310010010082100
Addedeslint@​10.0.38910010097100
Addedtypescript@​5.9.31001009010090

View full report

@codecov
Copy link
Copy Markdown

codecov bot commented Feb 19, 2026

Codecov Report

❌ Patch coverage is 32.78689% with 41 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.62%. Comparing base (9222532) to head (9b88695).

Files with missing lines Patch % Lines
src/rules/no-unused-modules.js 2.50% 39 Missing ⚠️
src/rules/order.js 80.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3230      +/-   ##
==========================================
- Coverage   95.39%   94.62%   -0.77%     
==========================================
  Files          83       83              
  Lines        3689     3741      +52     
  Branches     1332     1347      +15     
==========================================
+ Hits         3519     3540      +21     
- Misses        170      201      +31     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@StephanTLavavej
Copy link
Copy Markdown

@rasmi Do you want to disclose your usage of LLMs to generate this PR description and/or PR, which @ljharb objected to in another repo jsx-eslint/eslint-plugin-jsx-a11y#1077 ?

@rasmi
Copy link
Copy Markdown
Author

rasmi commented Feb 19, 2026

Was not aware, thanks @StephanTLavavej! It is a bit disappointing, as I spent a few hours of my day working on this, even with LLM tools. I will close this PR regardless out of respect for the maintainer (I just want to make sure the tests pass for my own edification).

@rasmi rasmi force-pushed the eslint-update branch 2 times, most recently from efa4b8b to 069a70f Compare February 19, 2026 02:07
@rasmi rasmi closed this Feb 19, 2026
@dirkluijk
Copy link
Copy Markdown

dirkluijk commented Feb 19, 2026

Just curious (and with all respect), what is the real issue of LLM-generated code? What matters is the code, right?

If the PR is too big, or too hard to review, we could just improve that?

@ljharb
Copy link
Copy Markdown
Member

ljharb commented Feb 19, 2026

@StephanTLavavej please do not take it upon yourself to act as if you're a maintainer. Each project can have different policies regardless of who's maintaining them. Please do not attempt to police projects you aren't even a significant contributor to.

@rasmi if you're using an LLM to automate applying changes you're writing, I'm fine with that - however if you're just prompting one to "add eslint v10 support", i have my own LLM subscription for that kind of thing :-)

@dirkluijk no, "what matters" has never been, and never will be, just "the code". Any code contribution represents a legal assignment of rights, as well as an eternal maintenance burden on that code, and as such, the human contributor is an important factor.

@rasmi
Copy link
Copy Markdown
Author

rasmi commented Feb 19, 2026

@ljharb -- point well-taken! I did go through a more rigorous process here -- it really did take me hours of focused work, thinking about implementation approaches, iterating, fixing tests, etc. It is not my intention to submit slop, I wanted to submit a PR that I hoped would meet the standard of the library, at least as a first pass. I lack decision-making insight/context on things like "Is it okay to just use glob, if not let's do a custom file search" and "exactly how should node vs. eslint-parser vs. core library version compatibility be handled" -- but this is what the review process is for!

I also understand that big changes like this may be simpler for the maintainer to just take on directly, as you have all the proper context and expertise on all the nuances of the library and ecosystem, to say the least. Apologies again, I didn't mean to burden you at all, I know it is hard to maintain core libraries like this. Please do with this what you wish!

beforeEach(function () {
if (semver.satisfies(eslintPkg.version, '< 6')) {
if (semver.satisfies(eslintPkg.version, '< 6') || semver.satisfies(eslintPkg.version, '>= 10')) {
// TODO: re-enable when eslint-plugin-json supports v10 (https://github.com/azeemba/eslint-plugin-json/issues/97)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor

@fernandopasik fernandopasik Feb 23, 2026

Choose a reason for hiding this comment

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

good idea, last commit in https://github.com/azeemba/eslint-plugin-json was 17 months ago

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Defer to @ljharb.

@A-Loot
Copy link
Copy Markdown

A-Loot commented Mar 7, 2026

What is the current status of this PR?

@rasmi
Copy link
Copy Markdown
Author

rasmi commented Mar 11, 2026

Hi @ljharb -- let me know if you have any feedback / next steps on this that I can address. Sorry to ping you, but there is quite a crowd on this PR and #3227. If the issue is codecoverage, I can add new test coverage. I think my main concern was whether some of the design decisions are appropriate for the project. (just rebasing onto main & force-pushing now)

- 9
- 8
exclude:
- node-version: 17
Copy link
Copy Markdown
Contributor

@fernandopasik fernandopasik Mar 11, 2026

Choose a reason for hiding this comment

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

probably need to exclude node 18 as well since eslint 10 supports from node 20
https://github.com/eslint/eslint/releases/tag/v10.0.0

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The rest of the test suite still runs and passes on Node 18 & 19 + eslint 10, so I left these in, but defer to @ljharb! See e68ea07, it's just the @babel/eslint-parser-dependent ones that are skipped.

@rasmi
Copy link
Copy Markdown
Author

rasmi commented Mar 12, 2026

#3237 should fix the tests currently failing in main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

8 participants