Skip to content
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

Added middleware to base-stack #35

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,8 @@
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
}
}
12 changes: 7 additions & 5 deletions app/entry.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ 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 { type EntryContext, ServerRouter, type unstable_RouterContextProvider } from "react-router"
import i18n from "./localization/i18n" // your i18n configuration file
import i18nextOpts from "./localization/i18n.server"
import { resources } from "./localization/resource"
import { globalAppContext } from "./server/context"

// Reject all pending promises from handler functions after 10 seconds
export const streamTimeout = 10000
Expand All @@ -17,11 +18,12 @@ export default async function handleRequest(
responseStatusCode: number,
responseHeaders: Headers,
context: EntryContext,
appContext: AppLoadContext
appContext: unstable_RouterContextProvider
) {
const ctx = appContext.get(globalAppContext)
const callbackName = isbot(request.headers.get("user-agent")) ? "onAllReady" : "onShellReady"
const instance = createInstance()
const lng = appContext.lang
const lng = ctx.lang
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
const ns = i18nextOpts.getRouteNamespaces(context as any)

Expand All @@ -48,8 +50,8 @@ export default async function handleRequest(
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(stream, {
// @ts-expect-error
ctx.body(stream, {
headers: responseHeaders,
status: didError ? 500 : responseStatusCode,
})
Expand Down
3 changes: 2 additions & 1 deletion app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import type { LinksFunction } from "react-router"
import { useChangeLanguage } from "remix-i18next/react"
import type { Route } from "./+types/root"
import { LanguageSwitcher } from "./library/language-switcher"
import { globalAppContext } from "./server/context"
import { ClientHintCheck, getHints } from "./services/client-hints"
import tailwindcss from "./tailwind.css?url"

export async function loader({ context, request }: Route.LoaderArgs) {
const { lang, clientEnv } = context
const { lang, clientEnv } = context.get(globalAppContext)
const hints = getHints(request)
return { lang, clientEnv, hints }
}
Expand Down
4 changes: 3 additions & 1 deletion app/routes/resource.locales.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { cacheHeader } from "pretty-cache-header"
import { z } from "zod"
import { resources } from "~/localization/resource"
import { globalAppContext } from "~/server/context"
import type { Route } from "./+types/resource.locales"

export async function loader({ request, context }: Route.LoaderArgs) {
const { env } = context
const { env } = context.get(globalAppContext)

const url = new URL(request.url)

const lng = z
Expand Down
3 changes: 2 additions & 1 deletion app/routes/robots[.]txt.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { generateRobotsTxt } from "@forge42/seo-tools/robots"

import { globalAppContext } from "~/server/context"
import { createDomain } from "~/utils/http"
import type { Route } from "./+types/robots[.]txt"

export async function loader({ request, context }: Route.LoaderArgs) {
const { env } = context
const { env } = context.get(globalAppContext)
const isProductionDeployment = env.APP_DEPLOYMENT_ENV === "production"
const domain = createDomain(request)
const robotsTxt = generateRobotsTxt([
Expand Down
19 changes: 10 additions & 9 deletions app/server/context.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import type { Context } from "hono"
import { unstable_createContext } from "react-router"
import { i18next } from "remix-hono/i18next"
import { getClientEnv, initEnv } from "~/env.server"

// Setup the .env vars
const env = initEnv()
const globalAppContext = unstable_createContext<GlobalAppContext>()

export const getLoadContext = async (c: Context) => {
const generateContext = async (c: Context) => {
// get the locale from the context
const locale = i18next.getLocale(c)
// get t function for the default namespace
const t = await i18next.getFixedT(c)

const clientEnv = getClientEnv()
return {
lang: locale,
Expand All @@ -22,11 +23,11 @@ export const getLoadContext = async (c: Context) => {
}
}

interface LoadContext extends Awaited<ReturnType<typeof getLoadContext>> {}

/**
* Declare our loaders and actions context type
*/
declare module "react-router" {
interface AppLoadContext extends Omit<LoadContext, "body"> {}
export const getLoadContext = async (c: Context) => {
const ctx = await generateContext(c)
return new Map([[globalAppContext, ctx]])
}

interface GlobalAppContext extends Awaited<ReturnType<typeof generateContext>> {}

export { globalAppContext }
1 change: 1 addition & 0 deletions app/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ export default await createHonoServer({
server.use("*", i18next(i18nextOpts))
},
defaultLogger: false,
// @ts-expect-error
getLoadContext,
})
17 changes: 11 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{

Check failure on line 1 in package.json

View workflow job for this annotation

GitHub Actions / ⬣ Biome lint

format

File content differs from formatting output
"name": "@forge42/base-stack",
"version": "0.0.1",
"author": "forge-42",
Expand Down Expand Up @@ -29,7 +29,7 @@
"dependencies": {
"@epic-web/client-hints": "1.3.5",
"@forge42/seo-tools": "1.3.0",
"@react-router/node": "7.2.0",
"@react-router/node": "7.3.0",
"clsx": "2.1.1",
"hono": "4.6.20",
"i18next": "24.2.2",
Expand All @@ -40,7 +40,7 @@
"react": "19.0.0",
"react-dom": "19.0.0",
"react-i18next": "15.4.0",
"react-router": "7.2.0",
"react-router": "7.3.0",
"react-router-hono-server": "2.10.0",
"remix-hono": "0.0.18",
"remix-i18next": "7.0.2",
Expand All @@ -51,8 +51,8 @@
"@babel/preset-typescript": "7.26.0",
"@biomejs/biome": "1.9.4",
"@dotenvx/dotenvx": "1.34.0",
"@react-router/dev": "7.2.0",
"@react-router/fs-routes": "7.2.0",
"@react-router/dev": "7.3.0",
"@react-router/fs-routes": "7.3.0",
"@tailwindcss/vite": "4.0.9",
"@testing-library/react": "16.2.0",
"@types/node": "22.13.1",
Expand Down Expand Up @@ -89,6 +89,11 @@
"pnpm": ">=10.2.0"
},
"pnpm": {
"onlyBuiltDependencies": ["@biomejs/biome", "esbuild", "lefthook", "msw"]
"onlyBuiltDependencies": [
"@biomejs/biome",
"esbuild",
"lefthook",
"msw"
]
}
}
}
Loading
Loading