import { Outlet, createRootRouteWithContext, createRoute, createRouter, useRouter, useRouterState, } from '@tanstack/react-router' import type { RouterHistory } from '@tanstack/react-router' import type { AppShellContext } from './router-context.ts' import { AppShellReactContext } from './router-context.ts' import { Document } from './ui/document.tsx' import { ErrorBanner } from './ui/error-banner.tsx' import { HomePage } from './pages/home.tsx' import { AboutPage } from './pages/about.tsx' import { NotesPage } from './pages/notes.tsx' import { toErrorMessage } from './ui/to-error-message.ts' import type { Note } from './data/notes.ts' export type RouterContext = AppShellContext type PageStaticData = { page?: string; title?: string } const rootRoute = createRootRouteWithContext()({ component: function Root() { const router = useRouter() const ctx = router.options.context as RouterContext const leaf = useRouterState({ select: (s) => s.matches.at(-1) }) const staticData = (leaf?.staticData || {}) as PageStaticData return ( ) }, errorComponent: function RootError({ error }) { const router = useRouter() const ctx = router.options.context as RouterContext return (
) }, }) const homeRoute = createRoute({ getParentRoute: () => rootRoute, path: '/', staticData: { page: 'home', title: '__APP_TITLE__' } satisfies PageStaticData, loader: async ({ context }): Promise<{ noteCount: number }> => { if (context.listNotes) { const notes = await context.listNotes() return { noteCount: notes.length } } const appBase = context.appBasePath.replace(/\/$/, '') const res = await fetch(`${appBase}/notes`, { headers: { Accept: 'application/json' }, credentials: 'same-origin', }) if (!res.ok) return { noteCount: 0 } const data = (await res.json()) as { notes?: Array } return { noteCount: Array.isArray(data.notes) ? data.notes.length : 0 } }, component: HomePage, }) const aboutRoute = createRoute({ getParentRoute: () => rootRoute, path: '/about', staticData: { page: 'about', title: 'About · __APP_TITLE__' } satisfies PageStaticData, loader: async ({ context }) => { if (context.loadAbout) return await context.loadAbout() const appBase = context.appBasePath.replace(/\/$/, '') const res = await fetch(`${appBase}/api/version`, { cache: 'no-store', credentials: 'same-origin', }) const version = res.ok ? ((await res.json()) as { sha?: string shortSha?: string message?: string publishedAt?: string committedAt?: string }) : {} return { sha: version.sha || '', shortSha: version.shortSha || '', message: version.message || '', publishedAt: version.publishedAt || '', committedAt: version.committedAt || '', codeUrl: null as string | null, runningSha: version.sha || '', } }, component: AboutPage, }) const notesRoute = createRoute({ getParentRoute: () => rootRoute, path: '/notes', staticData: { page: 'notes', title: 'Notes · __APP_TITLE__' } satisfies PageStaticData, loader: async ({ context }): Promise<{ notes: Array }> => { if (context.listNotes) { return { notes: await context.listNotes() } } const appBase = context.appBasePath.replace(/\/$/, '') const res = await fetch(`${appBase}/notes`, { headers: { Accept: 'application/json' }, credentials: 'same-origin', }) if (!res.ok) return { notes: [] } const data = (await res.json()) as { notes?: Array } return { notes: Array.isArray(data.notes) ? data.notes : [] } }, component: NotesPage, }) export const routeTree = rootRoute.addChildren([homeRoute, aboutRoute, notesRoute]) export function createAppRouter(input: { history: RouterHistory context: RouterContext basepath?: string }) { return createRouter({ routeTree, history: input.history, context: input.context, basepath: input.basepath && input.basepath !== '/' ? input.basepath : undefined, defaultPreload: 'intent', scrollRestoration: true, }) } declare module '@tanstack/react-router' { interface Register { router: ReturnType } }