Upgrade ESLint to v9 with flat config (#9680)

* Upgrade ESLint to v9 with flat config

- Upgrade eslint from v8 to v9.18.0
- Migrate from .eslintrc.js to eslint.config.mjs (flat config)
- Upgrade typescript-eslint to v8.20.0 (unified package)
- Replace eslint-plugin-import with eslint-plugin-import-x for flat config support
- Add globals package for environment globals
- Update eslint-plugin-bsky-internal with proper meta objects for ESLint v9
- Fix deprecated context.getScope() API usage
- Update bskyembed to use flat config
- Remove deprecated --ext flag from lint scripts
- Configure rules to maintain previous behavior while using new ESLint version

* Fix varsIgnorePattern to require character after underscore

Restore the original pattern `^_.+` instead of `^_` so that lingui's
`const { _ } = useLingui()` will still be flagged when unused.

* Update ESLint rule tests for flat config format

- Update RuleTester to use flat config languageOptions instead of
  eslintrc parser format
- Remove duplicate test case that ESLint v9 now detects
- Add Jest globals for test files

* update eslint package versions

* lint android a11y

* enable typechecked rules, switch them to warn

* fix yarn lock ci

* Fix CI failure

* Remove unused globals?

* Organize a bit, add quiet to main lint command

* Allow ternary

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Eric Bailey <git@esb.lol>
This commit is contained in:
Samuel Newman
2026-01-17 00:24:28 +02:00
committed by GitHub
parent acc26c965d
commit bd510d8468
38 changed files with 1914 additions and 2139 deletions
@@ -215,18 +215,18 @@ export function Controls({
const seekLeft = useCallback(() => {
if (!videoRef.current) return
// eslint-disable-next-line @typescript-eslint/no-shadow
const currentTime = videoRef.current.currentTime
// eslint-disable-next-line @typescript-eslint/no-shadow
const duration = videoRef.current.duration || 0
onSeek(clamp(currentTime - 5, 0, duration))
}, [onSeek, videoRef])
const seekRight = useCallback(() => {
if (!videoRef.current) return
// eslint-disable-next-line @typescript-eslint/no-shadow
const currentTime = videoRef.current.currentTime
// eslint-disable-next-line @typescript-eslint/no-shadow
const duration = videoRef.current.duration || 0
onSeek(clamp(currentTime + 5, 0, duration))
}, [onSeek, videoRef])
+1
View File
@@ -35,6 +35,7 @@ export function Text({
if (__DEV__) {
if (!emoji && childHasEmoji(children)) {
logger.warn(
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions, @typescript-eslint/no-base-to-string
`Text: emoji detected but emoji not enabled: "${children}"\n\nPlease add <Text emoji />'`,
)
}
+1 -1
View File
@@ -98,7 +98,7 @@ function DialogInner({
} = useRemoveLiveStatusMutation()
const {minutesUntilExpiry, expiryDateTime} = useMemo(() => {
tick!
void tick
const expiry = new Date(status.expiresAt ?? new Date())
return {
+6 -3
View File
@@ -13,7 +13,11 @@ import {Admonition} from '#/components/Admonition'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import * as TextField from '#/components/forms/TextField'
import {getLiveServiceNames} from '#/components/live/utils'
import {
displayDuration,
getLiveServiceNames,
useDebouncedValue,
} from '#/components/live/utils'
import {Loader} from '#/components/Loader'
import * as ProfileCard from '#/components/ProfileCard'
import * as Select from '#/components/Select'
@@ -21,7 +25,6 @@ import {Text} from '#/components/Typography'
import type * as bsky from '#/types/bsky'
import {LinkPreview} from './LinkPreview'
import {useLiveLinkMetaQuery, useUpsertLiveStatusMutation} from './queries'
import {displayDuration, useDebouncedValue} from './utils'
export function GoLiveDialog({
control,
@@ -57,7 +60,7 @@ function DialogInner({profile}: {profile: bsky.profile.AnyProfileView}) {
const time = useCallback(
(offset: number) => {
tick!
void tick
const date = new Date()
date.setMinutes(date.getMinutes() + offset)
@@ -211,8 +211,8 @@ function Inner(props: ReportDialogProps) {
logger.metric(
'reportDialog:success',
{
reason: state.selectedOption?.reason!,
labeler: state.selectedLabeler?.creator.handle!,
reason: state.selectedOption?.reason ?? '',
labeler: state.selectedLabeler?.creator.handle ?? '',
details: !!state.details,
},
{statsig: false},