diff --git a/src/app/index.client.tsx b/src/app/index.client.tsx index 92feebdf..52bc900f 100644 --- a/src/app/index.client.tsx +++ b/src/app/index.client.tsx @@ -1,12 +1,12 @@ import * as ReactDOM from 'react-dom/client' import { RouterProvider, createBrowserRouter } from 'react-router-dom' -import { routes } from './routes.js' +import { routesObjects } from './routes.js' import { hydrateLazyRoutes } from './utils.js' hydrate() async function hydrate() { - await hydrateLazyRoutes(routes) - const router = createBrowserRouter(routes) + await hydrateLazyRoutes(routesObjects) + const router = createBrowserRouter(routesObjects) ReactDOM.hydrateRoot(document.getElementById('app')!, ) } diff --git a/src/app/index.server.tsx b/src/app/index.server.tsx index a2a8c05e..ca085757 100644 --- a/src/app/index.server.tsx +++ b/src/app/index.server.tsx @@ -1,7 +1,7 @@ import type { Request } from '@tinyhttp/app' import * as ReactDOMServer from 'react-dom/server' import { Helmet } from 'react-helmet' -import { Route, Routes } from 'react-router-dom' +import { Routes } from 'react-router-dom' import { type StaticHandlerContext, StaticRouter, @@ -10,27 +10,13 @@ import { createStaticRouter, } from 'react-router-dom/server.js' -import { routes } from './routes.js' +import { routesElements, routesObjects } from './routes.js' import { createFetchRequest } from './utils.js' export async function prerender(location: string) { - const unwrappedRoutes = await Promise.all( - routes.map(async (route) => { - const lazyRoute = await route.lazy() - return { - path: route.path, - element: lazyRoute.element, - } - }), - ) - const body = ReactDOMServer.renderToString( - - {unwrappedRoutes.map((route) => ( - - ))} - + {routesElements} , ) @@ -38,7 +24,7 @@ export async function prerender(location: string) { } export async function render(req: Request) { - const { query, dataRoutes } = createStaticHandler(routes) + const { query, dataRoutes } = createStaticHandler(routesObjects) const fetchRequest = createFetchRequest(req) const context = (await query(fetchRequest)) as StaticHandlerContext @@ -55,16 +41,11 @@ export async function render(req: Request) { function head() { const helmet = Helmet.renderStatic() - - const themeKey = 'vocs.theme' - const themeScript = `` - return ` ${helmet.title.toString()} ${helmet.meta.toString()} ${helmet.link.toString()} ${helmet.style.toString()} ${helmet.script.toString()} - ${themeScript} ` } diff --git a/src/app/main.tsx b/src/app/main.tsx deleted file mode 100644 index 019fd10f..00000000 --- a/src/app/main.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { type ReactNode } from 'react' -import { useApplyCssTransition } from './hooks/useApplyCssTransition.js' - -export function Main({ children }: { children: ReactNode }) { - useApplyCssTransition() - return ( -
-
{children}
-
- ) -} diff --git a/src/app/root.tsx b/src/app/root.tsx new file mode 100644 index 00000000..8edc2f29 --- /dev/null +++ b/src/app/root.tsx @@ -0,0 +1,7 @@ +import { type ReactNode } from 'react' +import { useApplyCssTransition } from './hooks/useApplyCssTransition.js' + +export function Root({ children }: { children: ReactNode }) { + useApplyCssTransition() + return
{children}
+} diff --git a/src/app/routes.tsx b/src/app/routes.tsx index c027e9c0..a9e494d0 100644 --- a/src/app/routes.tsx +++ b/src/app/routes.tsx @@ -1,12 +1,12 @@ import type { MDXComponents } from 'mdx/types.js' import { Helmet } from 'react-helmet' -import { type RouteObject } from 'react-router-dom' +import { Outlet, Route, type RouteObject, createRoutesFromElements } from 'react-router-dom' import { routes as routes_virtual } from 'virtual:routes' import { A } from './components/A.js' import { CodeGroup } from './components/CodeGroup.js' import { FrontmatterHead } from './components/FrontmatterHead.js' -import { Main } from './main.js' +import { Root } from './root.js' const components: MDXComponents = { a: A, @@ -16,21 +16,37 @@ const components: MDXComponents = { }, } -export const routes = routes_virtual.map((route_virtual) => ({ - path: route_virtual.path, - lazy: async () => { - const { frontmatter, head, ...route } = await route_virtual.lazy() - return { - ...route, - element: ( - <> - {head && {head}} - {frontmatter && } -
- -
- - ), - } satisfies RouteObject - }, -})) +export const routesElements = ( + + + + } + > + {routes_virtual.map((route_virtual) => ( + { + const { frontmatter, head, ...route } = await route_virtual.lazy() + return { + ...route, + element: ( + <> + {head && {head}} + {frontmatter && } +
+ +
+ + ), + } satisfies RouteObject + }} + /> + ))} +
+) + +export const routesObjects = createRoutesFromElements(routesElements) diff --git a/src/app/utils/initialize-theme.ts b/src/app/utils/initialize-theme.ts new file mode 100644 index 00000000..f0ee2fbe --- /dev/null +++ b/src/app/utils/initialize-theme.ts @@ -0,0 +1,13 @@ +const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)') + +const storedTheme = localStorage.getItem('vocs.theme') +const theme = storedTheme || 'dark' + +if (theme === 'dark') document.documentElement.classList.add('dark') + +if (!storedTheme) + // Update the theme if the user changes their OS preference + darkModeMediaQuery.addEventListener('change', ({ matches: isDark }) => { + if (isDark) document.documentElement.classList.add('dark') + else document.documentElement.classList.remove('dark') + }) diff --git a/src/build.ts b/src/build.ts index a86802de..647240b5 100644 --- a/src/build.ts +++ b/src/build.ts @@ -30,4 +30,19 @@ export async function build({ outDir = 'dist', ssr = false }: BuildParameters = }, root: __dirname, }) + + // initialize theme script + await vite.build({ + build: { + lib: { + formats: ['iife'], + name: 'theme', + entry: [resolve(__dirname, './app/utils/initialize-theme.ts')], + }, + minify: true, + outDir: resolve(outDir, ssr ? 'client' : ''), + emptyOutDir: false, + }, + configFile: undefined, + }) } diff --git a/src/index.html b/src/index.html index 5fbd50d8..4e0ed0b8 100644 --- a/src/index.html +++ b/src/index.html @@ -3,6 +3,7 @@ + diff --git a/src/prerender.ts b/src/prerender.ts index 279882b9..7917b4c1 100644 --- a/src/prerender.ts +++ b/src/prerender.ts @@ -21,7 +21,10 @@ export async function prerender(args: PrerenderParameters = {}) { // Prerender each route. for (const route of routes) { const { head, body } = await mod.prerender(route) - const html = template.replace('', body).replace('', head) + const html = template + .replace('', body) + .replace('', head) + .replace('/app/utils/initialize-theme.ts', '/initialize-theme.iife.js') const filePath = `${route.endsWith('/') ? `${route}index` : route}.html`.replace(/^\//, '') const path = resolve(outDir_resolved, filePath) const pathDir = dirname(path)