⬆️(frontend) upgrade frontend to ESLint 9

Manually upgrade the frontend codebase to ESLint 9.

Update the linting configuration and related code where required to
maintain compatibility with the new major version.
This commit is contained in:
lebaudantoine
2026-06-03 17:39:51 +02:00
committed by aleb_the_flash
parent 73a7841b96
commit 6ccc9ef0bf
18 changed files with 1696 additions and 775 deletions
-21
View File
@@ -1,21 +0,0 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'plugin:@tanstack/eslint-plugin-query/recommended',
'plugin:jsx-a11y/recommended',
'prettier'
],
ignorePatterns: ['dist', '.eslintrc.cjs', 'styled-system'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
+78
View File
@@ -0,0 +1,78 @@
// src/frontend/eslint.config.js
import js from '@eslint/js'
import tseslint from 'typescript-eslint'
import reactPlugin from 'eslint-plugin-react'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import jsxA11y from 'eslint-plugin-jsx-a11y'
import globals from 'globals'
export default tseslint.config(
// Global ignores (replaces .eslintignore)
{
ignores: [
'dist/**',
'node_modules/**',
'coverage/**',
'src/styled-system/**',
],
},
// Base JS recommended rules
js.configs.recommended,
// TypeScript recommended rules (parser + rules bundled together)
...tseslint.configs.recommended,
// React-specific config
{
files: ['**/*.{ts,tsx}'],
plugins: {
react: reactPlugin,
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
'jsx-a11y': jsxA11y,
},
languageOptions: {
globals: {
...globals.browser,
},
parserOptions: {
ecmaFeatures: { jsx: true },
},
},
settings: {
react: { version: 'detect' },
},
rules: {
// React rules
...reactPlugin.configs.recommended.rules,
...reactHooks.configs.recommended.rules,
'react/react-in-jsx-scope': 'off', // not needed with React 17+
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
// jsx-a11y recommended rules
...jsxA11y.configs.recommended.rules,
// TypeScript rules you may want to adjust
'@typescript-eslint/no-unused-vars': [
'error',
{ argsIgnorePattern: '^_' },
],
'@typescript-eslint/no-explicit-any': 'warn',
},
},
// CJS and root-level JS config files (postcss.config.js, etc.)
{
files: ['**/*.cjs', '*.config.js'],
languageOptions: {
globals: {
...globals.node,
},
},
}
)
+1594 -736
View File
File diff suppressed because it is too large Load Diff
+6 -3
View File
@@ -8,6 +8,7 @@
"build": "panda codegen && tsc -b && vite build", "build": "panda codegen && tsc -b && vite build",
"build:debug": "VITE_ANALYZE=true npm run build -- --debug", "build:debug": "VITE_ANALYZE=true npm run build -- --debug",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"lint:fix": "eslint . --fix",
"preview": "vite preview", "preview": "vite preview",
"i18n:extract": "npx i18next -c i18next-parser.config.json", "i18n:extract": "npx i18next -c i18next-parser.config.json",
"format": "prettier --write ./src", "format": "prettier --write ./src",
@@ -45,6 +46,7 @@
"wouter": "3.9.0" "wouter": "3.9.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "9.39.4",
"@pandacss/dev": "1.11.1", "@pandacss/dev": "1.11.1",
"@tanstack/eslint-plugin-query": "5.91.4", "@tanstack/eslint-plugin-query": "5.91.4",
"@tanstack/react-query-devtools": "5.91.3", "@tanstack/react-query-devtools": "5.91.3",
@@ -52,18 +54,19 @@
"@types/node": "22.16.0", "@types/node": "22.16.0",
"@types/react": "18.3.12", "@types/react": "18.3.12",
"@types/react-dom": "18.3.1", "@types/react-dom": "18.3.1",
"@typescript-eslint/eslint-plugin": "8.35.1",
"@typescript-eslint/parser": "8.35.1",
"@vitejs/plugin-react": "5.1.4", "@vitejs/plugin-react": "5.1.4",
"eslint": "8.57.0", "eslint": "9.39.4",
"eslint-config-prettier": "10.1.5", "eslint-config-prettier": "10.1.5",
"eslint-plugin-jsx-a11y": "6.10.2", "eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-react": "7.37.5",
"eslint-plugin-react-hooks": "5.2.0", "eslint-plugin-react-hooks": "5.2.0",
"eslint-plugin-react-refresh": "0.4.20", "eslint-plugin-react-refresh": "0.4.20",
"globals": "17.6.0",
"postcss": "8.5.14", "postcss": "8.5.14",
"prettier": "3.8.1", "prettier": "3.8.1",
"rollup-plugin-visualizer": "7.0.1", "rollup-plugin-visualizer": "7.0.1",
"typescript": "5.8.3", "typescript": "5.8.3",
"typescript-eslint": "8.60.1",
"vite": "7.3.2", "vite": "7.3.2",
"vite-plugin-svgr": "5.2.0", "vite-plugin-svgr": "5.2.0",
"vite-tsconfig-paths": "6.1.1" "vite-tsconfig-paths": "6.1.1"
@@ -29,7 +29,6 @@ export const useUser = (
}, [data, opts, isConfigLoading]) }, [data, opts, isConfigLoading])
const query = useQuery({ const query = useQuery({
// eslint-disable-next-line @tanstack/query/exhaustive-deps
queryKey: [keys.user], queryKey: [keys.user],
queryFn: () => fetchUser(options), queryFn: () => fetchUser(options),
staleTime: Infinity, staleTime: Infinity,
@@ -116,7 +116,7 @@ export const PipOptionsMenu = ({ overflowControls }: PipOptionsMenuProps) => {
zIndex: 10, zIndex: 10,
})} })}
> >
{/* eslint-disable-next-line jsx-a11y/no-autofocus */} {/* eslint-disable-next-line jsx-a11y/no-autofocus*/}
<FocusScope autoFocus> <FocusScope autoFocus>
<Box size="sm" type="popover" variant="dark"> <Box size="sm" type="popover" variant="dark">
<PipOptionsMenuItems overflowControls={overflowControls} /> <PipOptionsMenuItems overflowControls={overflowControls} />
@@ -12,7 +12,9 @@ export const useLowerHandParticipants = () => {
return Promise.all(promises) return Promise.all(promises)
} catch (error) { } catch (error) {
console.error('An error occurred while lowering hands :', error) console.error('An error occurred while lowering hands :', error)
throw new Error('An error occurred while lowering hands.') throw new Error('An error occurred while lowering hands.', {
cause: error,
})
} }
} }
return { lowerHandParticipants } return { lowerHandParticipants }
@@ -12,7 +12,9 @@ export const useMuteParticipants = () => {
return Promise.all(promises) return Promise.all(promises)
} catch (error) { } catch (error) {
console.error('An error occurred while muting participants :', error) console.error('An error occurred while muting participants :', error)
throw new Error('An error occurred while muting participants.') throw new Error('An error occurred while muting participants.', {
cause: error,
})
} }
} }
return { muteParticipants } return { muteParticipants }
@@ -16,7 +16,9 @@ export const useUpdateParticipantsPermissions = () => {
return Promise.all(promises) return Promise.all(promises)
} catch (error) { } catch (error) {
console.error('An error occurred while updating permissions :', error) console.error('An error occurred while updating permissions :', error)
throw new Error('An error occurred while updating permissions.') throw new Error('An error occurred while updating permissions.', {
cause: error,
})
} }
} }
return { updateParticipantsPermissions } return { updateParticipantsPermissions }
@@ -72,7 +72,6 @@ export const Conference = ({
isError: isFetchError, isError: isFetchError,
data, data,
} = useQuery({ } = useQuery({
/* eslint-disable @tanstack/query/exhaustive-deps */
queryKey: fetchKey, queryKey: fetchKey,
staleTime: 6 * 60 * 60 * 1000, // By default, LiveKit access tokens expire 6 hours after generation staleTime: 6 * 60 * 60 * 1000, // By default, LiveKit access tokens expire 6 hours after generation
initialData: initialRoomData, initialData: initialRoomData,
@@ -301,7 +301,6 @@ export const Join = ({
isError, isError,
refetch: refetchRoom, refetch: refetchRoom,
} = useQuery({ } = useQuery({
/* eslint-disable @tanstack/query/exhaustive-deps */
queryKey: [keys.room, roomId], queryKey: [keys.room, roomId],
queryFn: () => fetchRoom({ roomId, username }), queryFn: () => fetchRoom({ roomId, username }),
staleTime: 6 * 60 * 60 * 1000, // By default, LiveKit access tokens expire 6 hours after generation staleTime: 6 * 60 * 60 * 1000, // By default, LiveKit access tokens expire 6 hours after generation
@@ -582,7 +581,7 @@ export const Join = ({
transform: 'scale(1.02)', transform: 'scale(1.02)',
})} })}
> >
{/* eslint-disable-next-line jsx-a11y/media-has-caption */} {/* eslint-disable jsx-a11y/media-has-caption */}
<video <video
ref={videoEl} ref={videoEl}
width="1280" width="1280"
@@ -37,7 +37,6 @@ export const useLobby = ({
}, [clearWaitingTimeout]) }, [clearWaitingTimeout])
const { data: waitingData } = useQuery({ const { data: waitingData } = useQuery({
/* eslint-disable @tanstack/query/exhaustive-deps */
queryKey: [keys.requestEntry, roomId], queryKey: [keys.requestEntry, roomId],
queryFn: async () => { queryFn: async () => {
const response = await requestEntry({ const response = await requestEntry({
@@ -23,7 +23,6 @@ export const useVideoResolutionSubscription = () => {
if (!room) return if (!room) return
const handleTrackPublished = ( const handleTrackPublished = (
publication: RemoteTrackPublication, publication: RemoteTrackPublication,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_participant: RemoteParticipant _participant: RemoteParticipant
) => { ) => {
// By default, the maximum quality is set to high // By default, the maximum quality is set to high
@@ -24,7 +24,7 @@ export const SettingsDialog = (props: SettingsDialogProps) => {
<Trans <Trans
i18nKey="settings:account.currentlyLoggedAs" i18nKey="settings:account.currentlyLoggedAs"
values={{ user: userDisplay }} values={{ user: userDisplay }}
components={[<Badge />]} components={[<Badge key="user-badge" />]}
/> />
</P> </P>
<P> <P>
@@ -61,7 +61,7 @@ export const AccountTab = ({ id, onOpenChange }: AccountTabProps) => {
<Trans <Trans
i18nKey="settings:account.currentlyLoggedAs" i18nKey="settings:account.currentlyLoggedAs"
values={{ user: userDisplay }} values={{ user: userDisplay }}
components={[<Badge />]} components={[<Badge key="user-badge" />]}
/> />
</P> </P>
<P> <P>
@@ -180,7 +180,7 @@ export const VideoTab = ({ id }: VideoTabProps) => {
> >
{localParticipant.isCameraEnabled ? ( {localParticipant.isCameraEnabled ? (
<> <>
{/* eslint-disable-next-line jsx-a11y/media-has-caption */} {/* eslint-disable jsx-a11y/media-has-caption */}
<video <video
ref={videoCallbackRef} ref={videoCallbackRef}
width="160px" width="160px"
+2
View File
@@ -38,3 +38,5 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
) )
} }
) )
Button.displayName = 'Button'
+2 -2
View File
@@ -2,9 +2,9 @@
"compilerOptions": { "compilerOptions": {
"composite": true, "composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020", "target": "ES2022",
"useDefineForClassFields": true, "useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"], "lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext", "module": "ESNext",
"skipLibCheck": true, "skipLibCheck": true,