diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index e3710503c..1d2584b46 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -32,6 +32,10 @@ jobs: - name: Type-check test-app run: cd packages/${{ matrix.adapter == 'vue' && 'vue3' || matrix.adapter }}/test-app && pnpm run type-check + - name: ESLint test-app + run: cd packages/${{ matrix.adapter == 'vue' && 'vue3' || matrix.adapter }}/test-app && pnpm run lint + if: matrix.adapter == 'react' + - name: Install Playwright Browsers run: pnpm playwright install chromium diff --git a/package.json b/package.json index 0d89b3fee..76e606851 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "dev:test-app:svelte": "pnpx concurrently -c \"#c4b5fd,#ffa800\" \"cd tests/app && PACKAGE=svelte pnpm serve:watch\" \"cd packages/svelte/test-app && pnpm run dev\" --names=server,vite", "dev:test-app:vue": "pnpx concurrently -c \"#c4b5fd,#ffa800\" \"cd tests/app && PACKAGE=vue3 pnpm serve:watch\" \"cd packages/vue3/test-app && pnpm run dev\" --names=server,vite", "es2020-check": "pnpm -r --filter './packages/*' es2020-check", + "lint:test-app:react": "cd packages/react/test-app && pnpm run lint", "type-check:test-app": "pnpm -r --filter './packages/*/test-app' type-check", "type-check:test-app:react": "cd packages/react/test-app && pnpm run type-check", "type-check:test-app:svelte": "cd packages/svelte/test-app && pnpm run type-check", @@ -28,13 +29,13 @@ "format": "prettier --write ." }, "dependencies": { - "@playwright/test": "^1.55.1", + "@playwright/test": "^1.56.0", "prettier": "^3.6.2", "prettier-plugin-organize-imports": "^4.3.0", "prettier-plugin-svelte": "^3.4.0", "prettier-plugin-tailwindcss": "^0.6.14" }, "optionalDependencies": { - "@rollup/rollup-linux-x64-gnu": "^4.52.2" + "@rollup/rollup-linux-x64-gnu": "^4.52.4" } } diff --git a/packages/react/test-app/Layouts/NestedLayout.tsx b/packages/react/test-app/Layouts/NestedLayout.tsx index ce99e1d9a..3bcf8654c 100644 --- a/packages/react/test-app/Layouts/NestedLayout.tsx +++ b/packages/react/test-app/Layouts/NestedLayout.tsx @@ -2,7 +2,7 @@ import { usePage } from '@inertiajs/react' import { useId, useState } from 'react' export default ({ children }: { children: React.ReactNode }) => { - const [createdAt, setCreatedAt] = useState(Date.now()) + const [createdAt] = useState(Date.now()) window._inertia_nested_layout_id = useId() window._inertia_nested_layout_props = usePage().props diff --git a/packages/react/test-app/Layouts/SiteLayout.tsx b/packages/react/test-app/Layouts/SiteLayout.tsx index 04cd640d7..8f0e381bd 100644 --- a/packages/react/test-app/Layouts/SiteLayout.tsx +++ b/packages/react/test-app/Layouts/SiteLayout.tsx @@ -2,7 +2,7 @@ import { usePage } from '@inertiajs/react' import { useId, useState } from 'react' export default ({ children }: { children: React.ReactNode }) => { - const [createdAt, setCreatedAt] = useState(Date.now()) + const [createdAt] = useState(Date.now()) window._inertia_layout_id = useId() window._inertia_site_layout_props = usePage().props diff --git a/packages/react/test-app/Pages/DeepMergeProps.tsx b/packages/react/test-app/Pages/DeepMergeProps.tsx index 4c8ce6fa6..e6dad4f79 100644 --- a/packages/react/test-app/Pages/DeepMergeProps.tsx +++ b/packages/react/test-app/Pages/DeepMergeProps.tsx @@ -1,15 +1,13 @@ import { router } from '@inertiajs/react' import { useState } from 'react' -export default ({ - bar, - foo, - baz, -}: { +type PageProps = { bar: number[] foo: { page: number; data: number[]; per_page: number; meta: { label: string } } baz: number[] -}) => { +} + +export default ({ bar, foo, baz }: PageProps) => { const [page, setPage] = useState(foo.page) const reloadIt = () => { @@ -19,7 +17,7 @@ export default ({ }, only: ['foo', 'baz'], onSuccess(visit) { - setPage((visit.props as any).foo.page) + setPage((visit.props as unknown as PageProps).foo.page) }, }) } diff --git a/packages/react/test-app/Pages/Dump.tsx b/packages/react/test-app/Pages/Dump.tsx index ad606a4b0..3728f8a72 100644 --- a/packages/react/test-app/Pages/Dump.tsx +++ b/packages/react/test-app/Pages/Dump.tsx @@ -1,6 +1,6 @@ import type { Method } from '@inertiajs/core' import { usePage } from '@inertiajs/react' -import { useEffect } from 'react' +import { useEffect, useMemo } from 'react' import type { MulterFile } from '../types' export default ({ @@ -20,19 +20,22 @@ export default ({ }) => { const page = usePage() - const dump = { - headers, - method, - form, - files: files ? files : {}, - query, - url, - $page: page, - } + const dump = useMemo( + () => ({ + headers, + method, + form, + files: files ? files : {}, + query, + url, + $page: page, + }), + [headers, method, form, files, query, url, page], + ) useEffect(() => { window._inertia_request_dump = dump - }, []) + }, [dump]) return (
diff --git a/packages/react/test-app/Pages/Events.tsx b/packages/react/test-app/Pages/Events.tsx index 6ae27ec83..724e0925d 100644 --- a/packages/react/test-app/Pages/Events.tsx +++ b/packages/react/test-app/Pages/Events.tsx @@ -2,7 +2,7 @@ import { Link, router, usePage } from '@inertiajs/react' declare global { interface Window { - messages: string[] + messages: unknown[] } } @@ -15,7 +15,7 @@ export default () => { const page = usePage() - const internalAlert = (...args: any[]) => { + const internalAlert = (...args: unknown[]) => { args.forEach((arg) => window.messages.push(arg)) } @@ -87,7 +87,7 @@ export default () => { const beforeVisitPreventGlobalInertia = (e: React.MouseEvent) => { e.preventDefault() document.addEventListener('inertia:before', () => internalAlert('addEventListener(inertia:before)')) - router.on('before', (visit) => { + router.on('before', () => { internalAlert('Inertia.on(before)') return false }) @@ -502,7 +502,7 @@ export default () => { { + onBefore={() => { internalAlert('linkOnBefore') return false }} diff --git a/packages/react/test-app/Pages/FormComponent/Reset.tsx b/packages/react/test-app/Pages/FormComponent/Reset.tsx index 8e103bcc9..c3702266d 100644 --- a/packages/react/test-app/Pages/FormComponent/Reset.tsx +++ b/packages/react/test-app/Pages/FormComponent/Reset.tsx @@ -1,3 +1,4 @@ +import { FormComponentRef } from '@inertiajs/core' import { Form } from '@inertiajs/react' import { useRef } from 'react' @@ -8,11 +9,10 @@ declare global { } export default function Reset() { - const formRef = useRef(null) + const formRef = useRef(null) // Expose reset function to window for testing window.resetForm = (...fields: string[]) => { - // @ts-expect-error formRef.current?.reset(...fields) } diff --git a/packages/react/test-app/Pages/FormHelper/Data.tsx b/packages/react/test-app/Pages/FormHelper/Data.tsx index a0306eaa1..688b86611 100644 --- a/packages/react/test-app/Pages/FormHelper/Data.tsx +++ b/packages/react/test-app/Pages/FormHelper/Data.tsx @@ -1,6 +1,6 @@ import { useForm, usePage } from '@inertiajs/react' -export default ({ errors }: { errors?: { name?: string; handle?: string } }) => { +export default () => { const form = useForm({ name: 'foo', handle: 'example', diff --git a/packages/react/test-app/Pages/FormHelper/EffectCount.tsx b/packages/react/test-app/Pages/FormHelper/EffectCount.tsx index 2efb35019..06d0cadb3 100644 --- a/packages/react/test-app/Pages/FormHelper/EffectCount.tsx +++ b/packages/react/test-app/Pages/FormHelper/EffectCount.tsx @@ -9,7 +9,7 @@ export default () => { useEffect(() => { setData('count', count) setDefaults() - setEffectCount(effectCount + 1) + setEffectCount((e) => e + 1) }, [count, setData, setDefaults]) return ( diff --git a/packages/react/test-app/Pages/FormHelper/Errors.tsx b/packages/react/test-app/Pages/FormHelper/Errors.tsx index c67643f3d..9aba8b624 100644 --- a/packages/react/test-app/Pages/FormHelper/Errors.tsx +++ b/packages/react/test-app/Pages/FormHelper/Errors.tsx @@ -1,6 +1,6 @@ import { useForm } from '@inertiajs/react' -export default ({ errors }: { errors?: { name?: string; handle?: string } }) => { +export default () => { const form = useForm({ name: 'foo', handle: 'example', remember: false }) const submit = () => { diff --git a/packages/react/test-app/Pages/FormHelper/Events.tsx b/packages/react/test-app/Pages/FormHelper/Events.tsx index 281176426..f7d0076da 100644 --- a/packages/react/test-app/Pages/FormHelper/Events.tsx +++ b/packages/react/test-app/Pages/FormHelper/Events.tsx @@ -1,6 +1,6 @@ import type { Errors, Page, PendingVisit } from '@inertiajs/core' import { useForm, usePage } from '@inertiajs/react' -import type { CancelTokenSource } from 'axios' +import type { AxiosProgressEvent, CancelTokenSource } from 'axios' import { useEffect } from 'react' declare global { @@ -39,7 +39,7 @@ const callbacks = (overrides = {}) => ({ ...overrides, }) -export default ({ errors }: { errors?: { name?: string; handle?: string } }) => { +export default () => { const form = useForm({ name: 'foo', remember: false }) const page = usePage() @@ -95,7 +95,7 @@ export default ({ errors }: { errors?: { name?: string; handle?: string } }) => const onBeforeVisitCancelled = () => { form.post('/sleep', { ...callbacks({ - onBefore: (visit: PendingVisit) => { + onBefore: () => { pushEvent('onBefore') return false }, @@ -121,7 +121,7 @@ export default ({ errors }: { errors?: { name?: string; handle?: string } }) => form.post('/dump/post', { ...callbacks({ - onProgress: (event: any) => { + onProgress: (event: AxiosProgressEvent) => { pushEvent('onProgress') pushData('progressEvent', event) }, @@ -157,7 +157,7 @@ export default ({ errors }: { errors?: { name?: string; handle?: string } }) => const onSuccessPromiseVisit = () => { form.post('/dump/post', { ...callbacks({ - onSuccess: (page: Page) => { + onSuccess: () => { pushEvent('onSuccess') setTimeout(() => pushEvent('onFinish should have been fired by now if Promise functionality did not work'), 5) return new Promise((resolve) => setTimeout(resolve, 20)) @@ -180,7 +180,7 @@ export default ({ errors }: { errors?: { name?: string; handle?: string } }) => const onErrorPromiseVisit = () => { form.post('/form-helper/events/errors', { ...callbacks({ - onError: (errors: Errors) => { + onError: () => { pushEvent('onError') setTimeout(() => pushEvent('onFinish should have been fired by now if Promise functionality did not work'), 5) return new Promise((resolve) => setTimeout(resolve, 20)) @@ -196,7 +196,7 @@ export default ({ errors }: { errors?: { name?: string; handle?: string } }) => const onSuccessResetValue = () => { form.post(page.url, { ...callbacks({ - onSuccess: (page: Page) => { + onSuccess: () => { form.reset() }, }), diff --git a/packages/react/test-app/Pages/FormHelper/TypeScript/DynamicInputName.tsx b/packages/react/test-app/Pages/FormHelper/TypeScript/DynamicInputName.tsx index acc2681ec..0f3994d70 100644 --- a/packages/react/test-app/Pages/FormHelper/TypeScript/DynamicInputName.tsx +++ b/packages/react/test-app/Pages/FormHelper/TypeScript/DynamicInputName.tsx @@ -3,7 +3,7 @@ import { useForm } from '@inertiajs/react' interface ClientForm { name: string - [key: string]: any + [key: string]: any // eslint-disable-line @typescript-eslint/no-explicit-any } export default function DynamicInputName() { diff --git a/packages/react/test-app/Pages/FormHelper/TypeScript/Errors.tsx b/packages/react/test-app/Pages/FormHelper/TypeScript/Errors.tsx index 3afd7410d..ec33db96c 100644 --- a/packages/react/test-app/Pages/FormHelper/TypeScript/Errors.tsx +++ b/packages/react/test-app/Pages/FormHelper/TypeScript/Errors.tsx @@ -17,8 +17,8 @@ export default function Errors() { const form = useForm(defaultData) // Get Errors - form.errors.name - form.errors['company.name'] + console.log(form.errors.name) + console.log(form.errors['company.name']) // Clear Errors form.clearErrors('name') @@ -53,9 +53,9 @@ export default function Errors() { form.setError({ 'users.0.name': 'Validation error' }) // @ts-expect-error - Form has no email field - form.errors.email + console.log(form.errors.email) // @ts-expect-error - Company has no email field - form.errors['company.email'] + console.log(form.errors['company.email']) // @ts-expect-error - Form has no email field form.clearErrors('email') @@ -83,6 +83,4 @@ export default function Errors() { form.setError('users.0.email', 'Validation error') // @ts-expect-error - A user has no email field form.setError({ 'users.0.email': 'Validation error' }) - - return null } diff --git a/packages/react/test-app/Pages/FormHelper/TypeScript/Generic.tsx b/packages/react/test-app/Pages/FormHelper/TypeScript/Generic.tsx index 59c5c5591..3a183d073 100644 --- a/packages/react/test-app/Pages/FormHelper/TypeScript/Generic.tsx +++ b/packages/react/test-app/Pages/FormHelper/TypeScript/Generic.tsx @@ -9,5 +9,6 @@ interface GenericProps> { export default function Generic>({ form, }: GenericProps) { + console.log(form) return
{/* Generic form component */}
} diff --git a/packages/react/test-app/Pages/FormHelper/TypeScript/ValidationKey.tsx b/packages/react/test-app/Pages/FormHelper/TypeScript/ValidationKey.tsx index 2e38e9afb..0ff12fbd4 100644 --- a/packages/react/test-app/Pages/FormHelper/TypeScript/ValidationKey.tsx +++ b/packages/react/test-app/Pages/FormHelper/TypeScript/ValidationKey.tsx @@ -21,5 +21,7 @@ const validation = >(errors: () => } export default function ValidationKey() { + validation(() => ({ name: 'Validation error' })) + return
{/* ValidationKey component */}
} diff --git a/packages/react/test-app/Pages/Head.tsx b/packages/react/test-app/Pages/Head.tsx index b9b28a74f..20ce0952f 100644 --- a/packages/react/test-app/Pages/Head.tsx +++ b/packages/react/test-app/Pages/Head.tsx @@ -8,14 +8,14 @@ export default () => { {/* @ts-expect-error - The content attribute must be a string, we support passing other types for backwards compatibility */} - {/* @ts-expect-error */} + {/* @ts-expect-error - same as above */} - {/* @ts-expect-error */} + {/* @ts-expect-error - same as above */} - {/* @ts-expect-error */} + {/* @ts-expect-error - same as above */} - {/* @ts-expect-error */} + {/* @ts-expect-error - same as above */} diff --git a/packages/react/test-app/Pages/InfiniteScroll/Filtering.tsx b/packages/react/test-app/Pages/InfiniteScroll/Filtering.tsx index b419c3eab..35bc84800 100644 --- a/packages/react/test-app/Pages/InfiniteScroll/Filtering.tsx +++ b/packages/react/test-app/Pages/InfiniteScroll/Filtering.tsx @@ -1,6 +1,6 @@ import { InfiniteScroll, Link, useForm } from '@inertiajs/react' import { debounce } from 'lodash-es' -import { useCallback, useEffect } from 'react' +import { useEffect, useMemo } from 'react' import UserCard, { User } from './UserCard' interface Props { @@ -17,23 +17,24 @@ export default ({ users, preserveState, filter, search }: Props) => { search: search, }) - const debouncedSearch = useCallback( - debounce(() => { - get( - '', - preserveState - ? { - preserveState: true, - replace: true, - only: ['users', 'search', 'filter'], - reset: ['users'], - } - : { - replace: true, - }, - ) - }, 250), - [get], + const debouncedSearch = useMemo( + () => + debounce(() => { + get( + '', + preserveState + ? { + preserveState: true, + replace: true, + only: ['users', 'search', 'filter'], + reset: ['users'], + } + : { + replace: true, + }, + ) + }, 250), + [get, preserveState], ) useEffect(() => { diff --git a/packages/react/test-app/Pages/InfiniteScroll/Grid.tsx b/packages/react/test-app/Pages/InfiniteScroll/Grid.tsx index ef8ddf20c..387083424 100644 --- a/packages/react/test-app/Pages/InfiniteScroll/Grid.tsx +++ b/packages/react/test-app/Pages/InfiniteScroll/Grid.tsx @@ -10,7 +10,7 @@ export default ({ users }: { users: { data: User[] } }) => {
Loading more users...
)} > - {({ loading, loadingPrevious, loadingNext }) => users.data.map((user) => )} + {() => users.data.map((user) => )} ) } diff --git a/packages/react/test-app/Pages/InfiniteScroll/ProgrammaticRef.tsx b/packages/react/test-app/Pages/InfiniteScroll/ProgrammaticRef.tsx index 8b8edda88..5e0578639 100644 --- a/packages/react/test-app/Pages/InfiniteScroll/ProgrammaticRef.tsx +++ b/packages/react/test-app/Pages/InfiniteScroll/ProgrammaticRef.tsx @@ -1,6 +1,6 @@ import { InfiniteScrollRef } from '@inertiajs/core' import { InfiniteScroll } from '@inertiajs/react' -import { useEffect, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import UserCard, { User } from './UserCard' export default ({ users }: { users: { data: User[] } }) => { @@ -8,10 +8,10 @@ export default ({ users }: { users: { data: User[] } }) => { const [hasPrevious, setHasMoreBefore] = useState(false) const [hasNext, setHasMoreAfter] = useState(false) - const updateStates = () => { + const updateStates = useCallback(() => { setHasMoreBefore(infRef?.hasPrevious() || false) setHasMoreAfter(infRef?.hasNext() || false) - } + }, [infRef]) const fetchNext = () => { if (infRef) { @@ -27,7 +27,7 @@ export default ({ users }: { users: { data: User[] } }) => { useEffect(() => { updateStates() - }, [infRef]) + }, [infRef, updateStates]) return (
diff --git a/packages/react/test-app/Pages/InfiniteScroll/ShortContent.tsx b/packages/react/test-app/Pages/InfiniteScroll/ShortContent.tsx index 90d20c4af..ea9f2217c 100644 --- a/packages/react/test-app/Pages/InfiniteScroll/ShortContent.tsx +++ b/packages/react/test-app/Pages/InfiniteScroll/ShortContent.tsx @@ -4,7 +4,7 @@ import { User } from './UserCard' export default ({ users }: { users: { data: User[] } }) => { return ( - {({ loadingPrevious, loadingNext }) => ( + {() => ( {users.data.map((user) => ( diff --git a/packages/react/test-app/Pages/Links/AsComponent.tsx b/packages/react/test-app/Pages/Links/AsComponent.tsx index 6b17de88d..6f882c2c1 100644 --- a/packages/react/test-app/Pages/Links/AsComponent.tsx +++ b/packages/react/test-app/Pages/Links/AsComponent.tsx @@ -3,13 +3,13 @@ import { useRef } from 'react' declare global { interface Window { - componentEvents: Array<{ eventName: string; data: any; timestamp: number }> + componentEvents: Array<{ eventName: string; data: unknown; timestamp: number }> } } window.componentEvents = [] -const CustomButton = ({ children, ...props }: { children: React.ReactNode; [key: string]: any }) => ( +const CustomButton = ({ children, ...props }: { children: React.ReactNode; [key: string]: unknown }) => (