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
@@ -14,7 +14,9 @@ import {
import {AppBskyRichtextFacet, RichText} from '@atproto/api'
import PasteInput, {
type PastedFile,
type PasteInputRef, // @ts-expect-error no types when installing from github
type PasteInputRef,
// @ts-expect-error no types when installing from github
// eslint-disable-next-line import-x/no-unresolved
} from '@mattermost/react-native-paste-input'
import {POST_IMG_MAX} from '#/lib/constants'
-1
View File
@@ -437,7 +437,6 @@ let PostFeed = ({
for (const page of data.pages) {
for (const slice of page.slices) {
const item = slice.items.find(
// eslint-disable-next-line @typescript-eslint/no-shadow
item => item.uri === slice.feedPostUri,
)
if (
+4 -2
View File
@@ -1,9 +1,9 @@
import {useState} from 'react'
import {LogBox, Pressable, View, TextInput} from 'react-native'
import {LogBox, Pressable, TextInput, View} from 'react-native'
import {useQueryClient} from '@tanstack/react-query'
import {BLUESKY_PROXY_HEADER} from '#/lib/constants'
import {useSessionApi, useAgent} from '#/state/session'
import {useAgent, useSessionApi} from '#/state/session'
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
import {useOnboardingDispatch} from '#/state/shell/onboarding'
import {navigate} from '../../../Navigation'
@@ -50,6 +50,8 @@ export function TestCtrls() {
return (
<View style={{position: 'absolute', top: 100, right: 0, zIndex: 100}}>
<TextInput
accessibilityLabel="Text input field"
accessibilityHint="Enter proxy header"
testID="e2eProxyHeaderInput"
onChangeText={val => setProxyHeader(val as any)}
onSubmitEditing={() => {
-2
View File
@@ -3,12 +3,10 @@ import {type AlertButton, type AlertStatic} from 'react-native'
class WebAlert implements Pick<AlertStatic, 'alert'> {
public alert(title: string, message?: string, buttons?: AlertButton[]): void {
if (buttons === undefined || buttons.length === 0) {
// eslint-disable-next-line no-alert
window.alert([title, message].filter(Boolean).join('\n'))
return
}
// eslint-disable-next-line no-alert
const result = window.confirm([title, message].filter(Boolean).join('\n'))
if (result === true) {
+2 -2
View File
@@ -153,9 +153,9 @@ export function Button({
async (event: GestureResponderEvent) => {
event.stopPropagation()
event.preventDefault()
withLoading && setIsLoading(true)
if (withLoading) setIsLoading(true)
await onPress?.(event)
withLoading && setIsLoading(false)
if (withLoading) setIsLoading(false)
},
[onPress, withLoading],
)
+1
View File
@@ -51,6 +51,7 @@ function Text_DEPRECATED({
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 />'`,
)
}