Skip to content

INTER-2222: Upgrade to ESLint v10 flat config#199

Draft
erayaydin wants to merge 5 commits into
mainfrom
chore/eslint-upgrade-inter-2222
Draft

INTER-2222: Upgrade to ESLint v10 flat config#199
erayaydin wants to merge 5 commits into
mainfrom
chore/eslint-upgrade-inter-2222

Conversation

@erayaydin

@erayaydin erayaydin commented Jun 20, 2026

Copy link
Copy Markdown
Member

Use @fingerprintjs/eslint-config-dx-team@3.0.0 which requires ESLint v10, exports a flat config array, and ships a type-aware config via its /type-checked export.

Dependency changes

Package Before After Reason
eslint 8.57.0 ^10.4.1 Required by dx-team config v3
@fingerprintjs/eslint-config-dx-team ^0.1.0 ^3.0.0 ESLint upgrade, flat config, and type-aware rules (peer-declares eslint@^10 + typescript-eslint@^8)
eslint-config-next 14.1.3 ^16.2.0 v14 uses @rushstack/eslint-patch which fails on ESLint v10; v16 is flat-config native
eslint-plugin-react-hooks ^4.6.0 ^7.0.0 Required peer of eslint-config-next@16
eslint-plugin-prettier ^5.5.5 removed Bundled in dx-team config
@eslint/eslintrc - ^3.0.0 ⚠️ Needed for FlatCompat in examples/preact. We can consider moving this to the example package.json file
typescript-eslint - ^8.60.0 Explicit dep for parser override in root config

Config notes

settings.react.version: '18.2' instead of 'detect': the detect path in eslint-plugin-react@7.37.x calls context.getFilename() which was removed in ESLint v10.

Parser override for .js files: eslint-config-next@16 sets @babel/eslint-parser for all files, then restores @typescript-eslint/parser only for .ts/.tsx. The babel parser's eslint-scope@5.x scope manager lacks the addGlobals() method ESLint v10 requires, so we re-apply @typescript-eslint/parser for .js/.jsx files.

examples/preact — uses FlatCompat to wrap eslint-config-preact (no flat config support). plugin:@typescript-eslint/recommended is replaced with tseslint.configs.recommended (unified package v8) to support TypeScript 5.9

Type-aware linting (dx-team v3)

Switched the root config to the /type-checked export, which layers strictTypeChecked plus strict-boolean-expressions, prefer-nullish-coalescing, restrict-template-expressions, etc. on top of the base rules.

  • Added tsconfig.eslint.json covering src, __tests__, and the root config files. The build tsconfig.json still only includes src, so tests/config would otherwise fail the type-aware parser with "not in project".
  • The parserOptions.project block is ordered after nextConfig so its parser swap doesn't strip the typed parser off .tsx files; pure-JS config/scripts get disableTypeChecked.
  • Most src fixes are mechanical (?? [], ??=, String(...) in templates, explicit nullish checks). Full pass verified: eslint --max-warnings 0, tsc --noEmit, 21/21 tests, and vite build all green.

Discussion point: test rule relaxation scoped to one rule

Tests only turn off @typescript-eslint/no-unsafe-assignment, because vitest's expect.any() matcher returns any. I measured what actually fires rather than copying the broader relaxation set from the node SDK — only this one triggers. Push back if you'd rather pre-emptively relax the full no-unsafe-* set for tests.

Discussion point: assertIsTruthyassertIsDefined

strict-boolean-expressions flagged the !value check. Since the function asserts NonNullable<T> and throws "must not be null or undefined", I changed it to an explicit value === null || value === undefined and renamed it to match its real contract. Behaviour-adjacent but safe: both callers pass objects, never falsy primitives.

Discussion point: removed always-true if (env) guard

In fingerprint-provider.tsx env is typed non-nullable, so no-unnecessary-condition flagged the guard. Removed it and kept explicit empty/undefined handling for env.version. The react-sdk/.../react integration-info assertion exercises this path.

Code changes (ESLint v10 base rules)

@typescript-eslint/no-empty-object-type

Empty object type {} is now flagged; object is the correct replacement when you mean "no specific props".

no-promise-executor-return

__tests__/helpers.tsx

// before
new Promise(resolve => setTimeout(resolve, ms))
// after
new Promise(resolve => { setTimeout(resolve, ms) })

@typescript-eslint/no-non-null-assertion

DOM querySelector calls in tests suppressed with eslint-disable-next-line. The elements are asserted to exist by the test setup.

@typescript-eslint/no-explicit-any

  • Test props typed as { env?: { name: string } } instead of any.
  • render(...args: any[]) -> render(...args: unknown[]).

@typescript-eslint/ban-ts-comment

The rule now requires a description when suppressing comments.

@typescript-eslint/consistent-type-assertions

  • const mock = fn as FunctionComponent -> const mock: FunctionComponent = fn
  • Unavoidable widening/narrowing casts suppressed with eslint-disable-next-line.

no-useless-constructor

Empty constructor removed. It provided no behaviour and is redundant when the parent's constructor signature is identical.

@typescript-eslint/no-require-imports

CJS file using require() suppressed with a file-level disable. The script runs in Node
before any bundler, and cannot use ESM (depends on Node.js version).

Use `@fingerprintjs/eslint-config-dx-team@2.0.0` which requires ESLint
v10.

Related-Task: INTER-2222
@erayaydin erayaydin self-assigned this Jun 20, 2026
@github-actions

github-actions Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Coverage report

St.
Category Percentage Covered / Total
🟢 Statements
82.68% (-0.53% 🔻)
105/127
🟡 Branches 63.77% 44/69
🟢 Functions
80% (-0.49% 🔻)
32/40
🟢 Lines
82.26% (-0.55% 🔻)
102/124
Show new covered files 🐣
St.
File Statements Branches Functions Lines
🔴
... / assert-is-defined.ts
50% 75% 100% 50%
Show files with reduced coverage 🔻
St.
File Statements Branches Functions Lines
🟢
... / fingerprint-provider.tsx
90.32% (-0.85% 🔻)
54.55% (-3.79% 🔻)
90%
90% (-0.91% 🔻)

Test suite run success

21 tests passing in 15 suites.

Report generated by 🧪jest coverage report action from d7ba0fb

Show full coverage report
St File % Stmts % Branch % Funcs % Lines Uncovered Line #s
🟢 All files 82.67 63.76 80 82.25
🟢  src 88.7 75.86 83.33 88.33
🟢   detect-env.ts 90 83.33 100 90 15,87
🟢   env.ts 100 100 100 100
🟢   env.types.ts 85.71 40 50 83.33 14
🟡   ...nt-context.ts 75 100 0 75 8
🟡   get-env.ts 60 0 100 60 8-9
🟢   ssr.ts 100 100 100 100
🟢   ...sitor-data.ts 95.65 100 80 95.65 101
🟢  src/components 91.66 58.33 90.9 91.42
🟢   ...-provider.tsx 90.32 54.54 90 90 54,59,92
🟢   ...vironment.tsx 100 100 100 100
🟡  src/utils 58.62 50 63.63 58.62
🟡   ...is-defined.ts 50 75 100 50 3
🟡   to-error.ts 66.66 50 100 66.66 6
🟢   use-const.ts 100 50 100 100 5
🟢   ...mise-store.ts 92.3 75 100 92.3 7
🔴   wait-until.ts 0 0 0 0 8-19

@erayaydin erayaydin marked this pull request as ready for review June 22, 2026 18:15
@erayaydin

Copy link
Copy Markdown
Member Author

Moving the PR to draft since we're planning to upgrade @fingerprintjs/eslint-config-dx-team to ^3.0.0 instead of ^2.0.0.

@erayaydin erayaydin marked this pull request as draft July 9, 2026 12:42
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ This PR doesn't contain any changesets. If there are user-facing changes, don't forget to run:

pnpm exec changeset

to create a changeset.

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.

2 participants