Skip to content

chore: Migrate to eslint 10 - #1447

Merged
TimothyJones merged 2 commits into
mainfrom
chore/eslint-10-migration
Sep 15, 2026
Merged

TimothyJones merged 2 commits into
mainfrom
chore/eslint-10-migration

Conversation

@TimothyJones

Copy link
Copy Markdown
Member

Migrates the workspace to ESLint 10: the shared eslint-config-case-maintainer package, and each of the 18 packages that consume it.

The blocker: eslint-plugin-import

[email protected] cannot run under ESLint 10. import/order calls SourceCode#getTokenOrCommentBefore/After, which v10 removed, and that rule is error in our config — so it crashes rather than merely warning about peer ranges. Upstream support was merged in June 2026 but is still unreleased.

This PR moves to eslint-plugin-import-x@4, which declares ESLint 10 support and has all 44 import/* rules we use. Every rule and settings key becomes import-x/*, in the config and in eslint-disable comments. no-cycle's disableScc option isn't in import-x's schema; it defaulted to false, so dropping it changes nothing.

If eslint-plugin-import does ship v10 support later, moving back is a mechanical rename plus restoring disableScc — but import-x is actively maintained and already there, so there's no pressure to.

Config changes

  • Dropped @eslint/eslintrc/FlatCompat — the eslintrc format is gone in v10. The two compat.extends() calls now use native flat configs: import-x's flatConfigs.typescript and jest's flat/recommended. We take only settings and rules from the former, since it registers import-x itself and v10 rejects a redefinition.
  • jest is now registered alongside the other plugins, so eslint-disable jest/* comments still resolve in every file the way the global plugin:jest/recommended extend allowed.
  • Bumped @eslint/js to ^10, @typescript-eslint/* to ^8.70, eslint-plugin-jest to ^29.
  • Added globals as an explicit dependency — it had been arriving transitively via @eslint/eslintrc.
  • Added eslint: ^10 as a peer dependency; engines.node>=22.13.0 to match ESLint 10's floor. CI already runs 22.x/24.x/26.x.
  • Deleted the root .eslintrc.json, dead since the flat-config move and unreadable by v10 regardless.

Source fixes

Four, each a real finding rather than a silencing:

  1. no-useless-assignment (new in v10's recommended set) correctly flags let content = '' in readContract — the initialiser is never read.
  2. import-x/no-named-as-default: import supportsColor from 'supports-color' shadowed that module's own named export. Renamed the binding rather than switching import style, since forceColour depends on load order.
  3. jest/no-export on case-core's jest test helper. Upstream change, not ours: plugin v29 broadened the rule to trigger on describe, where v28 only triggered on test. Added the same file-level disable the sibling helper in contract-case-jest already carries.
  4. case-connector-proto/src/index.d.ts: import-x resolves the barrel to the generated .d.ts files and then reports the export type * lines as duplicate exports missing an extension — neither of which eslint-plugin-import did. Scoped both rules off for that one file rather than change a published package's type surface, since a297ce9 added those lines deliberately.

Verification

  • nx run-many -t lint — 18/18 projects pass
  • nx run-many -t build — 20/20 pass
  • nx run-many -t test — full suite passes
  • nx run-many -t format:check — clean

To be sure the migration adds no new lint output, I ran ESLint 9 against a worktree of clean main and compared. The remaining warnings — 1 jest/expect-expect, 1 jest/no-disabled-tests, 8 unused-disable-directives — are present in that baseline at identical counts. That baseline is also how finding 3 was confirmed as upstream rather than a side effect of restructuring the jest config.

Generated docs examples are unaffected: the extractor strips eslint-disable comments, so no regeneration is needed.

🤖 Generated with Claude Code

Replaces eslint-plugin-import with eslint-plugin-import-x, and drops
FlatCompat in favour of the plugins' native flat configs.

eslint-plugin-import cannot run under eslint 10: import/order calls
SourceCode#getTokenOrCommentBefore/After, which v10 removed. Upstream
support was merged in June 2026 but is still unreleased, so we move to
the maintained fork instead. Every import/* rule becomes import-x/*,
including in eslint-disable comments. no-cycle's disableScc option is
not in import-x's schema; it defaulted to false, so dropping it changes
nothing.

@eslint/eslintrc goes with the eslintrc format itself. The two
compat.extends() calls become import-x's flatConfigs.typescript and
jest's flat/recommended. We take only settings and rules from the
former, because it registers import-x itself and v10 rejects a
redefinition. jest is now registered alongside the other plugins so
that eslint-disable jest/* comments still resolve in every file, as the
global plugin:jest/recommended extend used to allow.

globals becomes an explicit dependency, having previously arrived
transitively via @eslint/eslintrc. The root .eslintrc.json was already
dead after the flat config move, and v10 would not read it anyway.

Four source fixes, all verified against an eslint 9 baseline so that
the migration adds no new lint output:

- no-useless-assignment is new in v10's recommended set, and correctly
  flags the unread initialiser in readContract.
- import-x/no-named-as-default: the supports-color default import
  shadowed that module's own named export. Renamed the binding rather
  than the import style, since forceColour depends on load order.
- jest/no-export now fires on case-core's jest test helper. This is
  upstream: plugin v29 broadened the rule to trigger on describe, where
  v28 only triggered on test. Added the same file-level disable the
  sibling helper in contract-case-jest already carries.
- import-x resolves case-connector-proto's barrel to the generated
  .d.ts files and then reports the `export type *` lines as duplicate
  exports missing an extension, neither of which eslint-plugin-import
  did. Scoped both rules off for that file rather than change a
  published package's type surface.

Co-Authored-By: Claude Opus 5 <[email protected]>
@TimothyJones
TimothyJones enabled auto-merge (squash) September 14, 2026 04:44
@TimothyJones
TimothyJones enabled auto-merge (squash) September 15, 2026 01:18
@TimothyJones
TimothyJones merged commit b26b1bf into main Sep 15, 2026
15 checks passed
@TimothyJones
TimothyJones deleted the chore/eslint-10-migration branch September 15, 2026 01:28
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.

1 participant