app/root.tsx
145 lines and 10 functions in DrDustinEdwards/dustinedwards-info, at commit 1590ba4 on 2026-10-24. This file on GitHub.
Functions
| Function | Lines | Complexity | Covered | CRAP | Mutation |
|---|---|---|---|---|---|
| <anonymous:28> | 28 to 32 | 2 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| loader | 35 to 39 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| meta | 45 to 49 | 4 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| links | 51 to 82 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| Layout | 84 to 150 | 8 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| <anonymous:94> | 94 to 94 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| <anonymous:97> | 97 to 97 | 2 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| <anonymous:107> | 107 to 107 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| App | 152 to 154 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| ErrorBoundary | 156 to 158 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
Imports
It imports app/components/error-page.tsx, app/lib/context.ts, app/lib/enhance-loader.mjs, app/lib/seo.ts, app/lib/theme.ts, app/lib/timing.ts. Imported by app/routes/admin.tsx, app/routes/workspace.tsx.
Source
import {
isRouteErrorResponse,
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
useMatches,
useRouteLoaderData,
} from "react-router";
import { ErrorPage } from "~/components/error-page";
import { getNonce } from "~/lib/context";
import { ENHANCE_LOADER } from "~/lib/enhance-loader.mjs";
import { SITE, SITE_ORIGIN } from "~/lib/seo";
import { colorSchemeMeta, themeAttribute, themeFromRequest } from "~/lib/theme";
import { timingsContext, wantsTiming, type Timings } from "~/lib/timing";
import type { Route } from "./+types/root";
import interCoreUrl from "./fonts/inter-latin-core-normal.woff2?url";
// `?url`, not a bare import, which would fold the math bytes into the stylesheet every page links.
import katexCssUrl from "./styles/katex.generated.css?url";
// Middleware, not the loader: loaders run alongside each other, so a collector made in one is
// invisible to another.
export const middleware: Route.MiddlewareFunction[] = [
async ({ request, context }, next) => {
const timings: Timings | undefined = wantsTiming(new URL(request.url)) ? [] : undefined;
context.set(timingsContext, { timings });
return next();
},
];
export function loader({ request, context }: Route.LoaderArgs) {
// The nonce is carried through the loader because `Layout` cannot reach the request context.
// Admin only: undefined on every public page, which is edge-cached and trusted by hash instead.
return { theme: themeFromRequest(request), nonce: getNonce(context) };
}
/**
* Only on the error path: an unmatched URL matches no route but this one, so without it the 404 page
* has no title (WCAG 2.4.2). A matched route's own `meta` wins, so its not-found title stands.
*/
export function meta({ error }: Route.MetaArgs) {
if (!error) return [];
const notFound = isRouteErrorResponse(error) && error.status === 404;
return [{ title: `${notFound ? "Page not found" : "Error"} | ${SITE.name}` }];
}
export const links: Route.LinksFunction = () => [
// `links` from every matched route are merged and `meta` is NOT, so these ride on every page.
{ rel: "icon", href: "/favicon.ico", sizes: "48x48" },
{ rel: "icon", href: "/dustin-edwards-favicon.svg", type: "image/svg+xml" },
{ rel: "apple-touch-icon", href: "/dustin-edwards-apple-touch-icon.png" },
{ rel: "manifest", href: "/site.webmanifest" },
// feed+json although served as application/json: readers scan autodiscovery for the feed types.
{
tagName: "link",
rel: "alternate",
type: "application/rss+xml",
title: `${SITE.name} blog`,
href: `${SITE_ORIGIN}/writing/rss.xml`,
},
{
tagName: "link",
rel: "alternate",
type: "application/feed+json",
title: `${SITE.name} blog`,
href: `${SITE_ORIGIN}/writing/feed.json`,
},
// `crossorigin` is mandatory: fonts are fetched in anonymous CORS mode whatever their origin, so a
// preload without it is a different request and the file is fetched twice. The core, which every
// page draws; the rest of the latin range loads only where a page needs it (app.css).
{
rel: "preload",
href: interCoreUrl,
as: "font",
type: "font/woff2",
crossOrigin: "anonymous",
},
];
export function Layout({ children }: { children: React.ReactNode }) {
// Layout also renders the error boundary, where the root loader may not have run.
const data = useRouteLoaderData<typeof loader>("root");
const theme = data ? themeAttribute(data.theme) : undefined;
// No fallback: a made-up nonce would satisfy the markup while matching nothing in the header.
const nonce = data?.nonce;
// Hydration is opt-in by route (`handle = { hydrate: true }`).
const matches = useMatches();
const hydrates = matches.some(
(match) => (match.handle as { hydrate?: boolean } | undefined)?.hydrate === true,
);
// The admin and lab planes are Capsomer's shell, which brings its own skip link and none of the public CSS.
const adminPlane = matches.some((match) => match.id === "routes/admin" || match.id === "routes/workspace");
// Not React's `precedence` hoisting: it lifts the sheet above the color-scheme meta, which must
// arrive before the first stylesheet request.
const postData = useRouteLoaderData("routes/blog.$slug") as { hasMath?: boolean } | undefined;
const previewData = useRouteLoaderData("routes/preview.$token") as
| { hasMath?: boolean }
| undefined;
// Unconditional in the editor: hasMath describes what was SAVED, not what the author is typing.
const editorWantsMath = matches.some(
(match) => (match.handle as { math?: boolean } | undefined)?.math === true,
);
const linksMath =
postData?.hasMath === true || previewData?.hasMath === true || editorWantsMath;
return (
<html lang="en" data-theme={theme} data-cap-site="dustinedwards">
<head>
<meta charSet="utf-8" />
{/* Before any stylesheet: data-theme means nothing to the browser until the CSS is parsed. */}
<meta name="color-scheme" content={colorSchemeMeta(data?.theme ?? "system")} />
<meta name="viewport" content="width=device-width, initial-scale=1" />
{/* Literal hexes: browser chrome does not resolve a custom property. check:contrast
asserts they match the palette. */}
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#f4efe6" />
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#1c1916" />
<Meta />
{/* <Links> takes a nonce from the framework context and has no opt-out. */}
<Links />
{/* After <Links /> so a site rule wins over the upstream katex rule. No `precedence`: React
would lift the element above the color-scheme meta. */}
{linksMath ? <link rel="stylesheet" href={katexCssUrl} /> : null}
</head>
<body>
{adminPlane ? null : (
<a className="skip-link" href="#main">
Skip to content
</a>
)}
{children}
{/* JSON-LD blocks carry no nonce: `script-src` does not gate application/ld+json. */}
{hydrates ? (
<>
<ScrollRestoration nonce={nonce} />
<Scripts nonce={nonce} />
</>
) : null}
{/* Last, so every <Enhance> marker is parsed before it runs. Allowed by its sha256 in the
policy; raw HTML, because the hash is of these exact bytes. The nonce is admin's. */}
<script nonce={nonce} dangerouslySetInnerHTML={{ __html: ENHANCE_LOADER }} />
</body>
</html>
);
}
export default function App() {
return <Outlet />;
}
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
return <ErrorPage error={error} />;
}