Enable ESLint errors and suppress existing violations (#10490)

This commit is contained in:
DS Boyce
2026-06-03 11:27:44 -07:00
committed by GitHub
parent cdad723ea1
commit 84d1785e12
16 changed files with 1879 additions and 68 deletions
+3 -23
View File
@@ -21,7 +21,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
job: [lint, prettier] job: [lint, prettier, typecheck]
steps: steps:
- name: Check out Git repository - name: Check out Git repository
uses: actions/checkout@v5 uses: actions/checkout@v5
@@ -62,6 +62,8 @@ jobs:
command: pnpm install --frozen-lockfile command: pnpm install --frozen-lockfile
attempt_limit: 3 attempt_limit: 3
attempt_delay: 2000 attempt_delay: 2000
- name: Check & compile i18n
run: pnpm intl:build
- name: Lint checks - name: Lint checks
run: pnpm ${{ matrix.job }} run: pnpm ${{ matrix.job }}
# Aggregates the matrix results into a single stable check name so branch # Aggregates the matrix results into a single stable check name so branch
@@ -80,28 +82,6 @@ jobs:
run: | run: |
echo "linting result: $RESULT" echo "linting result: $RESULT"
test "$RESULT" = "success" test "$RESULT" = "success"
typechecking:
name: Run typecheck
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v5
- uses: pnpm/action-setup@v6
- name: Install node
uses: actions/setup-node@v6
with:
node-version-file: package.json
cache: pnpm
- name: pnpm install
uses: Wandalen/wretry.action@master
with:
command: pnpm install --frozen-lockfile
attempt_limit: 3
attempt_delay: 2000
- name: Check & compile i18n
run: pnpm intl:build
- name: Type check
run: pnpm typecheck
testing: testing:
name: Run tests name: Run tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
+1833 -1
View File
File diff suppressed because it is too large Load Diff
+18 -20
View File
@@ -134,11 +134,10 @@ export default defineConfig(
'react-native/no-inline-styles': 'off', 'react-native/no-inline-styles': 'off',
...reactNativeA11y.configs.all.rules, ...reactNativeA11y.configs.all.rules,
'react-compiler/react-compiler': 'warn', 'react-compiler/react-compiler': 'warn',
// TODO: Fix these and set to error 'react-hooks/set-state-in-effect': 'error',
'react-hooks/set-state-in-effect': 'warn', 'react-hooks/purity': 'error',
'react-hooks/purity': 'warn', 'react-hooks/refs': 'error',
'react-hooks/refs': 'warn', 'react-hooks/immutability': 'error',
'react-hooks/immutability': 'warn',
/** /**
* Import sorting * Import sorting
@@ -235,9 +234,8 @@ export default defineConfig(
}, },
], ],
/** /**
* Maintain previous behavior - these are stricter in typescript-eslint * Maintain previous behavior via eslint-suppressions.json - these are
* v8 `warn` ones are probably worth fixing. `off` ones are a bit too * stricter in typescript-eslint v8. `off` ones are a bit too nit-picky.
* nit-picky
*/ */
'@typescript-eslint/no-explicit-any': 'error', '@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/ban-ts-comment': 'off',
@@ -247,18 +245,18 @@ export default defineConfig(
'@typescript-eslint/unbound-method': 'off', '@typescript-eslint/unbound-method': 'off',
'@typescript-eslint/no-unsafe-argument': 'off', '@typescript-eslint/no-unsafe-argument': 'off',
'@typescript-eslint/no-unsafe-return': 'off', '@typescript-eslint/no-unsafe-return': 'off',
'@typescript-eslint/no-unsafe-member-access': 'warn', '@typescript-eslint/no-unsafe-member-access': 'error',
'@typescript-eslint/no-unsafe-call': 'warn', '@typescript-eslint/no-unsafe-call': 'error',
'@typescript-eslint/no-floating-promises': 'warn', '@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-misused-promises': 'warn', '@typescript-eslint/no-misused-promises': 'error',
'@typescript-eslint/require-await': 'warn', '@typescript-eslint/require-await': 'error',
'@typescript-eslint/no-unsafe-enum-comparison': 'warn', '@typescript-eslint/no-unsafe-enum-comparison': 'error',
'@typescript-eslint/no-unnecessary-type-assertion': 'warn', '@typescript-eslint/no-unnecessary-type-assertion': 'error',
'@typescript-eslint/no-redundant-type-constituents': 'warn', '@typescript-eslint/no-redundant-type-constituents': 'error',
'@typescript-eslint/no-duplicate-type-constituents': 'warn', '@typescript-eslint/no-duplicate-type-constituents': 'error',
'@typescript-eslint/no-base-to-string': 'warn', '@typescript-eslint/no-base-to-string': 'error',
'@typescript-eslint/prefer-promise-reject-errors': 'warn', '@typescript-eslint/prefer-promise-reject-errors': 'error',
'@typescript-eslint/await-thenable': 'warn', '@typescript-eslint/await-thenable': 'error',
'no-restricted-imports': [ 'no-restricted-imports': [
'error', 'error',
+4 -2
View File
@@ -8,8 +8,10 @@ import {
type UninheritableButtonProps, type UninheritableButtonProps,
} from '#/components/Button' } from '#/components/Button'
import {CircleCheck_Stroke2_Corner0_Rounded as CircleCheck} from '#/components/icons/CircleCheck' import {CircleCheck_Stroke2_Corner0_Rounded as CircleCheck} from '#/components/icons/CircleCheck'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo' import {
import {CircleInfo_Stroke2_Corner0_Rounded as ErrorIcon} from '#/components/icons/CircleInfo' CircleInfo_Stroke2_Corner0_Rounded as CircleInfo,
CircleInfo_Stroke2_Corner0_Rounded as ErrorIcon,
} from '#/components/icons/CircleInfo'
import {type Props as SVGIconProps} from '#/components/icons/common' import {type Props as SVGIconProps} from '#/components/icons/common'
import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning' import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning'
import {dismiss} from '#/components/Toast/sonner' import {dismiss} from '#/components/Toast/sonner'
+6 -6
View File
@@ -58,12 +58,12 @@ export function dateDiff(
if (diffSeconds < NOW) { if (diffSeconds < NOW) {
diff = { diff = {
value: 0, value: 0,
unit: 'now' as DateDiff['unit'], unit: 'now',
} }
} else if (diffSeconds < MINUTE) { } else if (diffSeconds < MINUTE) {
diff = { diff = {
value: diffSeconds, value: diffSeconds,
unit: 'second' as DateDiff['unit'], unit: 'second',
} }
} else if (diffSeconds < HOUR) { } else if (diffSeconds < HOUR) {
const value = const value =
@@ -72,7 +72,7 @@ export function dateDiff(
: Math.floor(diffSeconds / MINUTE) : Math.floor(diffSeconds / MINUTE)
diff = { diff = {
value, value,
unit: 'minute' as DateDiff['unit'], unit: 'minute',
} }
} else if (diffSeconds < DAY) { } else if (diffSeconds < DAY) {
const value = const value =
@@ -81,7 +81,7 @@ export function dateDiff(
: Math.floor(diffSeconds / HOUR) : Math.floor(diffSeconds / HOUR)
diff = { diff = {
value, value,
unit: 'hour' as DateDiff['unit'], unit: 'hour',
} }
} else if (diffSeconds < MONTH_30) { } else if (diffSeconds < MONTH_30) {
const value = const value =
@@ -90,7 +90,7 @@ export function dateDiff(
: Math.floor(diffSeconds / DAY) : Math.floor(diffSeconds / DAY)
diff = { diff = {
value, value,
unit: 'day' as DateDiff['unit'], unit: 'day',
} }
} else { } else {
const value = const value =
@@ -99,7 +99,7 @@ export function dateDiff(
: Math.floor(diffSeconds / MONTH_30) : Math.floor(diffSeconds / MONTH_30)
diff = { diff = {
value, value,
unit: 'month' as DateDiff['unit'], unit: 'month',
} }
} }
+2 -2
View File
@@ -19,8 +19,8 @@ export function makeProfileLink(
export function makeCustomFeedLink( export function makeCustomFeedLink(
did: string, did: string,
rkey: string, rkey: string,
segment?: string | undefined, segment?: string,
feedCacheKey?: 'discover' | 'explore' | undefined, feedCacheKey?: 'discover' | 'explore',
) { ) {
return ( return (
[`/profile`, did, 'feed', rkey, ...(segment ? [segment] : [])].join('/') + [`/profile`, did, 'feed', rkey, ...(segment ? [segment] : [])].join('/') +
@@ -129,7 +129,7 @@ function getAppIconName(icon: string | false): DynamicAppIcon.IconName {
if (!icon || icon === 'DEFAULT') { if (!icon || icon === 'DEFAULT') {
return 'default_light' return 'default_light'
} else { } else {
return icon as DynamicAppIcon.IconName return icon
} }
} }
@@ -151,7 +151,7 @@ function Group({
values={[value]} values={[value]}
maxSelections={1} maxSelections={1}
onChange={vals => { onChange={vals => {
if (vals[0]) onChange(vals[0] as DynamicAppIcon.IconName) if (vals[0]) onChange(vals[0])
}}> }}>
<View style={[a.flex_1, a.rounded_md, a.overflow_hidden]}> <View style={[a.flex_1, a.rounded_md, a.overflow_hidden]}>
{children} {children}
@@ -23,8 +23,7 @@ import {
import {useEventListener} from 'expo' import {useEventListener} from 'expo'
import {type VideoPlayer} from 'expo-video' import {type VideoPlayer} from 'expo-video'
import {tokens} from '#/alf' import {atoms as a, tokens} from '#/alf'
import {atoms as a} from '#/alf'
import {formatTime} from '#/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/utils' import {formatTime} from '#/components/Post/Embed/VideoEmbed/VideoEmbedInner/web-controls/utils'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
+1 -1
View File
@@ -298,7 +298,7 @@ export function* findAllPostsInQueryData(
if (AppBskyFeedDefs.isPostView(item.subject)) { if (AppBskyFeedDefs.isPostView(item.subject)) {
const quotedPost = getEmbeddedPost(item.subject?.embed) const quotedPost = getEmbeddedPost(item.subject?.embed)
if (quotedPost && didOrHandleUriMatches(atUri, quotedPost)) { if (quotedPost && didOrHandleUriMatches(atUri, quotedPost)) {
yield embedViewRecordToPostView(quotedPost!) yield embedViewRecordToPostView(quotedPost)
} }
} }
} }
+1 -1
View File
@@ -34,7 +34,7 @@ export function parseAppNux(nux: AppBskyActorDefs.Nux): AppNux | undefined {
export function serializeAppNux(nux: AppNux): AppBskyActorDefs.Nux { export function serializeAppNux(nux: AppNux): AppBskyActorDefs.Nux {
const {data, ...rest} = nux const {data, ...rest} = nux
const schema = NuxSchemas[nux.id as Nux] const schema = NuxSchemas[nux.id]
const result: AppBskyActorDefs.Nux = { const result: AppBskyActorDefs.Nux = {
...rest, ...rest,
+1 -1
View File
@@ -85,7 +85,7 @@ export function useSuggestedFollowsByActorWithDismiss({
const profiles = useMemo(() => { const profiles = useMemo(() => {
return (data?.suggestions ?? []).map(profile => ({ return (data?.suggestions ?? []).map(profile => ({
actor: profile as bsky.profile.AnyProfileView, actor: profile,
recId: data?.recId, recId: data?.recId,
})) }))
}, [data?.suggestions, data?.recId]) }, [data?.suggestions, data?.recId])
+1 -1
View File
@@ -196,7 +196,7 @@ export function sortAndAnnotateThreadItems(
* `repliesSeenCounter` later on, since `repliesSeenCounter` * `repliesSeenCounter` later on, since `repliesSeenCounter`
* is 1-indexed and `replyIndex` is 0-indexed. * is 1-indexed and `replyIndex` is 0-indexed.
*/ */
childMetadata!.replyIndex = childMetadata.replyIndex =
childParentMetadata.repliesSeenCounter childParentMetadata.repliesSeenCounter
} }
+3 -3
View File
@@ -470,11 +470,11 @@ export async function draftToComposerPosts(
height, height,
mime: 'image/jpeg', mime: 'image/jpeg',
}, },
} as ComposerImage } satisfies ComposerImage
}) })
const images = (await Promise.all(imagePromises)).filter( const images = (await Promise.all(imagePromises)).filter(
(img): img is ComposerImage => img !== null, (img): img is NonNullable<typeof img> => img !== null,
) )
if (images.length > 0) { if (images.length > 0) {
embed.media = {type: 'images', images} embed.media = {type: 'images', images}
@@ -511,7 +511,7 @@ export async function draftToComposerPosts(
tinygif: mediaObject, tinygif: mediaObject,
preview: mediaObject, preview: mediaObject,
}, },
} as Gif, },
alt: gifData.alt, alt: gifData.alt,
} }
break break
+1 -1
View File
@@ -55,7 +55,7 @@ export function TestCtrls() {
accessibilityLabel="Text input field" accessibilityLabel="Text input field"
accessibilityHint="Enter proxy header" accessibilityHint="Enter proxy header"
testID="e2eProxyHeaderInput" testID="e2eProxyHeaderInput"
onChangeText={val => setProxyHeader(val as any)} onChangeText={val => setProxyHeader(val)}
autoComplete="off" autoComplete="off"
autoCorrect={false} autoCorrect={false}
autoCapitalize="none" autoCapitalize="none"
+1 -1
View File
@@ -3,9 +3,9 @@ import {
Pressable, Pressable,
type PressableProps, type PressableProps,
type StyleProp, type StyleProp,
type View,
type ViewStyle, type ViewStyle,
} from 'react-native' } from 'react-native'
import {type View} from 'react-native'
import {addStyle} from '#/lib/styles' import {addStyle} from '#/lib/styles'
import {useInteractionState} from '#/components/hooks/useInteractionState' import {useInteractionState} from '#/components/hooks/useInteractionState'
@@ -171,7 +171,7 @@ function NativeStackNavigator({
} }
// Evicted screens get a lightweight placeholder instead of their full tree // Evicted screens get a lightweight placeholder instead of their full tree
finalDescriptors = {} as typeof descriptors finalDescriptors = {}
for (const key in descriptors) { for (const key in descriptors) {
if (mountSet.has(key)) { if (mountSet.has(key)) {
finalDescriptors[key] = descriptors[key] finalDescriptors[key] = descriptors[key]