Skip to content

Feat/migrate to react router 7 #897

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e899918
activate v3_singleFetch
hakimLyon Oct 13, 2024
d0a9ff5
mv installGlobals --> vite conf
hakimLyon Oct 14, 2024
56603cc
Fix: playwright failures
hakimLyon Oct 16, 2024
e6eca57
add installGlobals
hakimLyon Oct 16, 2024
c2d2244
Fix: eslint
hakimLyon Oct 16, 2024
b4d40b6
format
hakimLyon Oct 16, 2024
cb2f5c9
import as data
hakimLyon Oct 16, 2024
6b40b78
remove installGlobals
hakimLyon Oct 16, 2024
5044ab5
fix typecheck
hakimLyon Oct 16, 2024
359f494
rename auth.$provider to auth_.$provider
hakimLyon Oct 20, 2024
edb182e
Merge branch 'main' into v3_singleFetch
hakimLyon Oct 21, 2024
bc898bc
enable v3_singleFetch
hakimLyon Oct 21, 2024
75a6fc3
Merge branch 'hakim-single-fetch' into feat/migrate-to-react-router-7
DennisKraaijeveld Dec 31, 2024
4102828
feat: init
DennisKraaijeveld Dec 31, 2024
889be28
feat: initial codemod remix > rr7
DennisKraaijeveld Dec 31, 2024
96a2142
feat: SerializeFrom: https://github.com/remix-run/react-router/discus…
DennisKraaijeveld Dec 31, 2024
44a44b9
feat: await results from submit hooks
DennisKraaijeveld Dec 31, 2024
a9a9553
feat: move to new file uploading package
DennisKraaijeveld Dec 31, 2024
55645fc
feat: add routes.ts
DennisKraaijeveld Dec 31, 2024
f016946
feat: typegen
DennisKraaijeveld Dec 31, 2024
eeb5069
feat: rr config file; enable ssr
DennisKraaijeveld Dec 31, 2024
66815e6
feat: rr config file; enable ssr
DennisKraaijeveld Dec 31, 2024
939c7b6
fix: move ignore .react-router to the correct file
DennisKraaijeveld Dec 31, 2024
ac9ad5e
fix: remove typegen folder
DennisKraaijeveld Dec 31, 2024
1c2f055
feat: small fixes
DennisKraaijeveld Dec 31, 2024
489f675
feat: code format/styling
DennisKraaijeveld Dec 31, 2024
a31cce4
fix: dev server broke due to sentry package
DennisKraaijeveld Dec 31, 2024
de4c39a
fix: uncommented commented comment jeezz
DennisKraaijeveld Dec 31, 2024
31d4690
feat: prod server
DennisKraaijeveld Dec 31, 2024
e5b70ac
feat: move to @sentry/node
DennisKraaijeveld Jan 4, 2025
1895661
fix: typo
DennisKraaijeveld Jan 4, 2025
1c7480d
fix: correct import sentry node
DennisKraaijeveld Jan 4, 2025
2084016
chore: format everything
kentcdodds Jan 15, 2025
0f380b5
use new type helpers
kentcdodds Jan 15, 2025
e06e003
remove use of SerializeFrom
kentcdodds Jan 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ node_modules

# generated files
/app/components/ui/icons
.react-router/
13 changes: 8 additions & 5 deletions app/components/error-boundary.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { captureException } from '@sentry/react'
import { useEffect, type ReactElement } from 'react'
import {
type ErrorResponse,
isRouteErrorResponse,
useParams,
useRouteError,
} from '@remix-run/react'
import { captureRemixErrorBoundaryError } from '@sentry/remix'
import { type ReactElement } from 'react'
import { getErrorMessage } from '#app/utils/misc.tsx'
} from 'react-router'
import { getErrorMessage } from '#app/utils/misc'

type StatusHandler = (info: {
error: ErrorResponse
Expand All @@ -27,13 +27,16 @@ export function GeneralErrorBoundary({
unexpectedErrorHandler?: (error: unknown) => ReactElement | null
}) {
const error = useRouteError()
captureRemixErrorBoundaryError(error)
const params = useParams()

if (typeof document !== 'undefined') {
console.error(error)
}

useEffect(() => {
captureException(error)
}, [error])

return (
<div className="container flex items-center justify-center p-20 text-h2">
{isRouteErrorResponse(error)
Expand Down
2 changes: 1 addition & 1 deletion app/components/progress-bar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useNavigation } from '@remix-run/react'
import { useNavigation } from 'react-router'
import { useEffect, useRef, useState } from 'react'

Check warning on line 2 in app/components/progress-bar.tsx

View workflow job for this annotation

GitHub Actions / ⬣ ESLint

`react` import should occur before import of `react-router`
import { useSpinDelay } from 'spin-delay'
import { cn } from '#app/utils/misc.tsx'
import { Icon } from './ui/icon.tsx'
Expand Down
6 changes: 3 additions & 3 deletions app/components/search-bar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Form, useSearchParams, useSubmit } from '@remix-run/react'
import { useId } from 'react'
import { Form, useSearchParams, useSubmit } from 'react-router'
import { useDebounce, useIsPending } from '#app/utils/misc.tsx'
import { Icon } from './ui/icon.tsx'
import { Input } from './ui/input.tsx'
Expand All @@ -23,8 +23,8 @@ export function SearchBar({
formAction: '/users',
})

const handleFormChange = useDebounce((form: HTMLFormElement) => {
submit(form)
const handleFormChange = useDebounce(async (form: HTMLFormElement) => {
await submit(form)
}, 400)

return (
Expand Down
4 changes: 2 additions & 2 deletions app/entry.client.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { RemixBrowser } from '@remix-run/react'
import { startTransition } from 'react'
import { hydrateRoot } from 'react-dom/client'
import { HydratedRouter } from 'react-router/dom'

if (ENV.MODE === 'production' && ENV.SENTRY_DSN) {
void import('./utils/monitoring.client.tsx').then(({ init }) => init())
}

startTransition(() => {
hydrateRoot(document, <RemixBrowser />)
hydrateRoot(document, <HydratedRouter />)
})
34 changes: 17 additions & 17 deletions app/entry.server.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import { PassThrough } from 'node:stream'
import { createReadableStreamFromReadable } from '@react-router/node'

import * as Sentry from '@sentry/node'
import chalk from 'chalk'
import { isbot } from 'isbot'
import { renderToPipeableStream } from 'react-dom/server'
import {
createReadableStreamFromReadable,
ServerRouter,
type LoaderFunctionArgs,
type ActionFunctionArgs,
type HandleDocumentRequestFunction,
} from '@remix-run/node'
import { RemixServer } from '@remix-run/react'
import * as Sentry from '@sentry/remix'
import chalk from 'chalk'
import { isbot } from 'isbot'
import { renderToPipeableStream } from 'react-dom/server'
} from 'react-router'
import { getEnv, init } from './utils/env.server.ts'
import { getInstanceInfo } from './utils/litefs.server.ts'
import { NonceProvider } from './utils/nonce-provider.ts'
import { makeTimings } from './utils/timing.server.ts'

const ABORT_DELAY = 5000
export const streamTimeout = 5000

init()
global.ENV = getEnv()
Expand All @@ -27,7 +28,7 @@ export default async function handleRequest(...args: DocRequestArgs) {
request,
responseStatusCode,
responseHeaders,
remixContext,
reactRouterContext,
loadContext,
] = args
const { currentInstance, primaryInstance } = await getInstanceInfo()
Expand All @@ -53,7 +54,11 @@ export default async function handleRequest(...args: DocRequestArgs) {

const { pipe, abort } = renderToPipeableStream(
<NonceProvider value={nonce}>
<RemixServer context={remixContext} url={request.url} />
<ServerRouter
nonce={nonce}
context={reactRouterContext}
url={request.url}
/>
</NonceProvider>,
{
[callbackName]: () => {
Expand All @@ -78,7 +83,7 @@ export default async function handleRequest(...args: DocRequestArgs) {
},
)

setTimeout(abort, ABORT_DELAY)
setTimeout(abort, streamTimeout + 5000)
})
}

Expand All @@ -103,12 +108,7 @@ export function handleError(
}
if (error instanceof Error) {
console.error(chalk.red(error.stack))
void Sentry.captureRemixServerException(
error,
'remix.server',
request,
true,
)
void Sentry.captureException(error)
} else {
console.error(error)
Sentry.captureException(error)
Expand Down
21 changes: 10 additions & 11 deletions app/root.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { wrapUseRoutesV7 } from '@sentry/react'
import { useRef } from 'react'
import {
json,
data,
type LoaderFunctionArgs,
type HeadersFunction,
type LinksFunction,
type MetaFunction,
} from '@remix-run/node'
import {
Form,
Link,
Links,
Expand All @@ -16,9 +16,7 @@ import {
useLoaderData,
useMatches,
useSubmit,
} from '@remix-run/react'
import { withSentry } from '@sentry/remix'
import { useRef } from 'react'
} from 'react-router'
import { HoneypotProvider } from 'remix-utils/honeypot/react'
import appleTouchIconAssetUrl from './assets/favicons/apple-touch-icon.png'
import faviconAssetUrl from './assets/favicons/favicon.svg'
Expand Down Expand Up @@ -121,7 +119,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
const { toast, headers: toastHeaders } = await getToast(request)
const honeyProps = honeypot.getInputProps()

return json(
return data(
{
user,
requestInfo: {
Expand Down Expand Up @@ -161,7 +159,8 @@ function Document({
children: React.ReactNode
nonce: string
theme?: Theme
env?: Record<string, string>
env?: Record<string, string | undefined>
allowIndexing?: boolean
}) {
const allowIndexing = ENV.ALLOW_INDEXING !== 'false'
return (
Expand Down Expand Up @@ -271,7 +270,7 @@ function AppWithProviders() {
)
}

export default withSentry(AppWithProviders)
export default wrapUseRoutesV7(AppWithProviders)

function UserDropdown() {
const user = useUser()
Expand Down Expand Up @@ -317,9 +316,9 @@ function UserDropdown() {
<DropdownMenuItem
asChild
// this prevents the menu from closing before the form submission is completed
onSelect={(event) => {
onSelect={async (event) => {
event.preventDefault()
submit(formRef.current)
await submit(formRef.current)
}}
>
<Form action="/logout" method="POST" ref={formRef}>
Expand Down
21 changes: 21 additions & 0 deletions app/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { type RouteConfig } from '@react-router/dev/routes'
import { remixRoutesOptionAdapter } from '@react-router/remix-routes-option-adapter'
import { flatRoutes } from 'remix-flat-routes'

export default remixRoutesOptionAdapter((defineRoutes) => {
return flatRoutes('routes', defineRoutes, {
ignoredRouteFiles: [
'.*',
'**/*.css',
'**/*.test.{js,jsx,ts,tsx}',
'**/__*.*',
// This is for server-side utilities you want to colocate
// next to your routes without making an additional
// directory. If you need a route that includes "server" or
// "client" in the filename, use the escape brackets like:
// my-route.[server].tsx
'**/*.server.*',
'**/*.client.*',
],
})
}) satisfies RouteConfig
2 changes: 1 addition & 1 deletion app/routes/$.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// ensure the user gets the right status code and we can display a nicer error
// message for them than the Remix and/or browser default.

import { Link, useLocation } from '@remix-run/react'
import { Link, useLocation } from 'react-router'
import { GeneralErrorBoundary } from '#app/components/error-boundary.tsx'
import { Icon } from '#app/components/ui/icon.tsx'

Expand Down
14 changes: 11 additions & 3 deletions app/routes/_auth+/auth.$provider.callback.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { redirect, type LoaderFunctionArgs } from '@remix-run/node'
import { redirect, type LoaderFunctionArgs } from 'react-router'
import {
authenticator,
getSessionExpirationDate,
Expand Down Expand Up @@ -39,8 +39,16 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
const authResult = await authenticator
.authenticate(providerName, request, { throwOnError: true })
.then(
(data) => ({ success: true, data }) as const,
(error) => ({ success: false, error }) as const,
(data) =>
({
success: true,
data,
}) as const,
(error) =>
({
success: false,
error,
}) as const,
)

if (!authResult.success) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { redirect, type ActionFunctionArgs } from '@remix-run/node'
import { redirect, type ActionFunctionArgs } from 'react-router'
import { authenticator } from '#app/utils/auth.server.ts'
import { handleMockAction } from '#app/utils/connections.server.ts'
import { ProviderNameSchema } from '#app/utils/connections.tsx'
Expand Down
11 changes: 6 additions & 5 deletions app/routes/_auth+/forgot-password.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { getZodConstraint, parseWithZod } from '@conform-to/zod'
import { type SEOHandle } from '@nasa-gcn/remix-seo'
import * as E from '@react-email/components'
import {
json,
data,
redirect,
type ActionFunctionArgs,
type MetaFunction,
} from '@remix-run/node'
import { Link, useFetcher } from '@remix-run/react'
Link,
useFetcher,
} from 'react-router'
import { HoneypotInputs } from 'remix-utils/honeypot/react'
import { z } from 'zod'
import { GeneralErrorBoundary } from '#app/components/error-boundary.tsx'
Expand Down Expand Up @@ -54,7 +55,7 @@ export async function action({ request }: ActionFunctionArgs) {
async: true,
})
if (submission.status !== 'success') {
return json(
return data(
{ result: submission.reply() },
{ status: submission.status === 'error' ? 400 : 200 },
)
Expand Down Expand Up @@ -84,7 +85,7 @@ export async function action({ request }: ActionFunctionArgs) {
if (response.status === 'success') {
return redirect(redirectTo.toString())
} else {
return json(
return data(
{ result: submission.reply({ formErrors: [response.error.message] }) },
{ status: 500 },
)
Expand Down
2 changes: 1 addition & 1 deletion app/routes/_auth+/login.server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { invariant } from '@epic-web/invariant'
import { redirect } from '@remix-run/node'
import { redirect } from 'react-router'
import { safeRedirect } from 'remix-utils/safe-redirect'
import { twoFAVerificationType } from '#app/routes/settings+/profile.two-factor.tsx'
import { getUserId, sessionKey } from '#app/utils/auth.server.ts'
Expand Down
13 changes: 8 additions & 5 deletions app/routes/_auth+/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import { getFormProps, getInputProps, useForm } from '@conform-to/react'
import { getZodConstraint, parseWithZod } from '@conform-to/zod'
import { type SEOHandle } from '@nasa-gcn/remix-seo'
import {
json,
data,
type ActionFunctionArgs,
type LoaderFunctionArgs,
type MetaFunction,
} from '@remix-run/node'
import { Form, Link, useActionData, useSearchParams } from '@remix-run/react'
Form,
Link,
useActionData,
useSearchParams,
} from 'react-router'
import { HoneypotInputs } from 'remix-utils/honeypot/react'
import { z } from 'zod'
import { GeneralErrorBoundary } from '#app/components/error-boundary.tsx'
Expand Down Expand Up @@ -37,7 +40,7 @@ const LoginFormSchema = z.object({

export async function loader({ request }: LoaderFunctionArgs) {
await requireAnonymous(request)
return json({})
return {}
}

export async function action({ request }: ActionFunctionArgs) {
Expand All @@ -64,7 +67,7 @@ export async function action({ request }: ActionFunctionArgs) {
})

if (submission.status !== 'success' || !submission.value.session) {
return json(
return data(
{ result: submission.reply({ hideFields: ['password'] }) },
{ status: submission.status === 'error' ? 400 : 200 },
)
Expand Down
2 changes: 1 addition & 1 deletion app/routes/_auth+/logout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { redirect, type ActionFunctionArgs } from '@remix-run/node'
import { redirect, type ActionFunctionArgs } from 'react-router'
import { logout } from '#app/utils/auth.server.ts'

export async function loader() {
Expand Down
2 changes: 1 addition & 1 deletion app/routes/_auth+/onboarding.server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { invariant } from '@epic-web/invariant'
import { redirect } from '@remix-run/node'
import { redirect } from 'react-router'
import { verifySessionStorage } from '#app/utils/verification.server.ts'
import { onboardingEmailSessionKey } from './onboarding.tsx'
import { type VerifyFunctionArgs } from './verify.server.ts'
Expand Down
Loading