Skip to content

Commit

Permalink
React router v7
Browse files Browse the repository at this point in the history
  • Loading branch information
AlemTuzlak committed Oct 9, 2024
1 parent 05907e9 commit 5132885
Show file tree
Hide file tree
Showing 19 changed files with 319 additions and 2,103 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ node_modules
/build
.env
coverage
.history
.history
.react-router
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"[javascript][typescript][typescriptreact][javascriptreact][json][jsonc][vue][astro][svelte][css][graphql]": {
"editor.defaultFormatter": "biomejs.biome"
},
"typescript.tsdk": "node_modules/typescript/lib",
"explorer.fileNesting.patterns": {
"*.ts": "${basename}.*.${extname}",
".env": ".env.*",
Expand Down
4 changes: 2 additions & 2 deletions app/entry.client.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { RemixBrowser } from "@remix-run/react"
import i18next from "i18next"
import LanguageDetector from "i18next-browser-languagedetector"
import Fetch from "i18next-fetch-backend"
import { StrictMode, startTransition } from "react"
import { hydrateRoot } from "react-dom/client"
import { I18nextProvider, initReactI18next } from "react-i18next"
import { HydratedRouter } from "react-router/dom"
import { getInitialNamespaces } from "remix-i18next/client"
import i18n from "~/localization/i18n"

Expand Down Expand Up @@ -37,7 +37,7 @@ async function hydrate() {
document,
<I18nextProvider i18n={i18next}>
<StrictMode>
<RemixBrowser />
<HydratedRouter />
</StrictMode>
</I18nextProvider>
)
Expand Down
18 changes: 9 additions & 9 deletions app/entry.server.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
import { PassThrough } from "node:stream"
import type { AppLoadContext, EntryContext } from "@remix-run/node"
import { RemixServer } from "@remix-run/react"
import { createReadableStreamFromReadable } from "@react-router/node"
import type { Context } from "hono"
import { createInstance } from "i18next"
import { isbot } from "isbot"
import { renderToPipeableStream } from "react-dom/server"
import { I18nextProvider, initReactI18next } from "react-i18next"
import { type AppLoadContext, type EntryContext, ServerRouter } from "react-router"
import { createHonoServer } from "react-router-hono-server/node"
import { i18next } from "remix-hono/i18next"
import { getClientEnv, initEnv } from "./env.server"
import i18n from "./localization/i18n" // your i18n configuration file
import i18nextOpts from "./localization/i18n.server"
import { resources } from "./localization/resource"

const ABORT_DELAY = 5000

export default async function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext,
context: EntryContext,
appContext: AppLoadContext
) {
const callbackName = isbot(request.headers.get("user-agent")) ? "onAllReady" : "onShellReady"
const instance = createInstance()
const lng = appContext.lang
const ns = i18nextOpts.getRouteNamespaces(remixContext)
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
const ns = i18nextOpts.getRouteNamespaces(context as any)

await instance
.use(initReactI18next) // Tell our instance to use react-i18next
Expand All @@ -41,17 +41,17 @@ export default async function handleRequest(

const { pipe, abort } = renderToPipeableStream(
<I18nextProvider i18n={instance}>
<RemixServer context={remixContext} url={request.url} />
<ServerRouter abortDelay={ABORT_DELAY} context={context} url={request.url} />
</I18nextProvider>,
{
[callbackName]: () => {
const body = new PassThrough()

const stream = createReadableStreamFromReadable(body)
responseHeaders.set("Content-Type", "text/html")

resolve(
// @ts-expect-error - We purposely do not define the body as existent so it's not used inside loaders as it's injected there as well
appContext.body(body, {
appContext.body(stream, {
headers: responseHeaders,
status: didError ? 500 : responseStatusCode,
})
Expand Down Expand Up @@ -100,7 +100,7 @@ interface LoadContext extends Awaited<ReturnType<typeof getLoadContext>> {}
/**
* Declare our loaders and actions context type
*/
declare module "@remix-run/node" {
declare module "react-router" {
interface AppLoadContext extends Omit<LoadContext, "body"> {}
}

Expand Down
2 changes: 1 addition & 1 deletion app/library/language-switcher/LanguageSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Link, useLocation } from "@remix-run/react"
import { useTranslation } from "react-i18next"
import { Link, useLocation } from "react-router"
import { supportedLanguages } from "~/localization/resource"

const LanguageSwitcher = () => {
Expand Down
16 changes: 9 additions & 7 deletions app/root.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import type { LinksFunction } from "@remix-run/node"
import { type LoaderFunctionArgs, json } from "@remix-run/node"
import { Links, Meta, Outlet, Scripts, ScrollRestoration, useLoaderData } from "@remix-run/react"
import { useTranslation } from "react-i18next"
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router"
import type { LinksFunction } from "react-router"
import { useChangeLanguage } from "remix-i18next/react"
import type * as Route from "./+types.root"
import { LanguageSwitcher } from "./library/language-switcher"
import tailwindcss from "./tailwind.css?url"

export async function loader({ context: { lang, clientEnv } }: LoaderFunctionArgs) {
return json({ lang, clientEnv })
export async function loader({ context }: Route.LoaderArgs) {
if (!context) throw new Error("No context")
const { lang, clientEnv } = context
return { lang, clientEnv }
}

export const links: LinksFunction = () => [{ rel: "stylesheet", href: tailwindcss }]
Expand All @@ -16,8 +18,8 @@ export const handle = {
i18n: "common",
}

export default function App() {
const { lang, clientEnv } = useLoaderData<typeof loader>()
export default function App({ loaderData }: Route.ComponentProps) {
const { lang, clientEnv } = loaderData
const { i18n } = useTranslation()
useChangeLanguage(lang)

Expand Down
3 changes: 3 additions & 0 deletions app/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { flatRoutes } from "@react-router/fs-routes"

export const routes = flatRoutes()
2 changes: 1 addition & 1 deletion app/routes/_index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { MetaFunction } from "@remix-run/node"
import { useTranslation } from "react-i18next"
import type { MetaFunction } from "react-router"

export const meta: MetaFunction = () => {
return [{ title: "New Remix App" }, { name: "description", content: "Welcome to Remix!" }]
Expand Down
7 changes: 4 additions & 3 deletions app/routes/resource.locales.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { type LoaderFunctionArgs, json } from "@remix-run/node"
import { cacheHeader } from "pretty-cache-header"
import { data } from "react-router"
import { z } from "zod"
import { resources } from "~/localization/resource"
import type * as Route from "./+types.resource.locales"

export async function loader({ request }: LoaderFunctionArgs) {
export async function loader({ request }: Route.LoaderArgs) {
const url = new URL(request.url)

const lng = z
Expand Down Expand Up @@ -35,5 +36,5 @@ export async function loader({ request }: LoaderFunctionArgs) {
)
}

return json(namespaces[ns], { headers })
return data(namespaces[ns], { headers })
}
5 changes: 3 additions & 2 deletions app/routes/robots[.]txt.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { generateRobotsTxt } from "@forge42/seo-tools/robots"
import type { LoaderFunctionArgs } from "@remix-run/node"

import { createDomain } from "~/utils/http"
import type * as Route from "./+types.robots[.]txt"

export async function loader({ request }: LoaderFunctionArgs) {
export async function loader({ request }: Route.LoaderArgs) {
const isProductionDeployment = process.env.DEPLOYMENT_ENV === "production"
const domain = createDomain(request)
const robotsTxt = generateRobotsTxt([
Expand Down
5 changes: 3 additions & 2 deletions app/routes/sitemap-index[.]xml.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { generateSitemapIndex } from "@forge42/seo-tools/sitemap"
import type { LoaderFunctionArgs } from "@remix-run/node"

import { createDomain } from "~/utils/http"
import type * as Route from "./+types.sitemap-index[.]xml"

export const loader = async ({ request }: LoaderFunctionArgs) => {
export const loader = async ({ request }: Route.LoaderArgs) => {
const domain = createDomain(request)
const sitemaps = generateSitemapIndex([
{
Expand Down
6 changes: 4 additions & 2 deletions app/routes/sitemap.$lang[.]xml.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { generateRemixSitemap } from "@forge42/seo-tools/remix/sitemap"
import type { LoaderFunctionArgs } from "@remix-run/node"
import type { LoaderFunctionArgs } from "react-router"
import { createDomain } from "~/utils/http"
import type * as Route from "./+types.sitemap.$lang[.]xml"

export const loader = async ({ request, params }: LoaderFunctionArgs) => {
const domain = createDomain(request)

// @ts-expect-error - This import exists but is not picked up by the typescript compiler because it's a remix internal
const { routes } = await import("virtual:remix/server-build")
const { routes } = await import("virtual:react-router/server-build")

const sitemap = await generateRemixSitemap({
domain,
Expand Down
2 changes: 1 addition & 1 deletion env.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/// <reference types="@remix-run/node" />
/// <reference types="react-router" />
/// <reference types="vite/client" />
/// <reference types="vitest" />
5 changes: 3 additions & 2 deletions knip.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"entry": ["scripts/*.{ts,js}"],
"entry": ["scripts/*.{ts,js}", "app/routes.ts"],
"remix": true,
"project": ["**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx}", "vite.config.ts"],
"ignore": ["app/library/icon/icons/types.ts"],
"ignoreDependencies": ["virtual:remix"]
"ignoreDependencies": ["virtual:react-router", "lefthook"]
}
23 changes: 13 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
"execute": "tsx",
"script": "npm run execute scripts/setup.ts",
"cleanup": "npm run script scripts/cleanup.ts",
"build": "remix vite:build",
"dev": "remix vite:dev",
"build": "react-router build",
"dev": "react-router dev",
"lint": "biome check .",
"lint:ci": "biome ci .",
"lint:fix": "biome check --write .",
"start": "remix-serve ./build/server/index.js",
"start": "react-router-serve ./build/server/index.js",
"test": "vitest run",
"test:ui": "vitest --ui --api 9527",
"test:cov": "vitest run --coverage",
Expand All @@ -23,13 +23,14 @@
"check": "biome check .",
"check:fix": "biome check --fix .",
"check:unused": "knip --max-issues 1",
"check:unused:fix": "knip --fix"
"check:unused:fix": "knip --fix",
"typegen": "react-router typegen && tsc",
"postinstall": "react-router typegen"
},
"dependencies": {
"@forge42/seo-tools": "^1.3.0",
"@remix-run/node": "^2.12.1",
"@remix-run/react": "^2.12.1",
"@remix-run/serve": "^2.12.1",
"@react-router/node": "7.0.0-pre.0",
"@react-router/serve": "7.0.0-pre.0",
"clsx": "^2.1.1",
"hono": "^4.6.3",
"i18next": "^23.15.2",
Expand All @@ -40,7 +41,8 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^15.0.2",
"react-router-hono-server": "^0.2.0",
"react-router": "7.0.0-pre.0",
"react-router-hono-server": "https://pkg.pr.new/rphlmr/[email protected]",
"remix-hono": "^0.0.16",
"remix-i18next": "^6.4.1",
"tailwind-merge": "^2.5.3",
Expand All @@ -49,7 +51,8 @@
"devDependencies": {
"@biomejs/biome": "^1.9.3",
"@dotenvx/dotenvx": "^1.16.0",
"@remix-run/dev": "^2.12.1",
"@react-router/dev": "7.0.0-pre.0",
"@react-router/fs-routes": "7.0.0-pre.0",
"@types/node": "^22.7.5",
"@types/prompt": "^1.1.9",
"@types/react": "^18.3.11",
Expand All @@ -63,7 +66,7 @@
"lefthook": "^1.7.18",
"postcss": "^8.4.47",
"prompt": "^1.3.0",
"remix-development-tools": "^4.7.2",
"remix-development-tools": "https://pkg.pr.new/forge42dev/Remix-Dev-Tools/[email protected]",
"tailwindcss": "^3.4.13",
"tsx": "^4.19.1",
"typescript": "^5.6.2",
Expand Down
Loading

0 comments on commit 5132885

Please sign in to comment.