diff --git a/adex/package.json b/adex/package.json index d7d076d..b7a19b4 100644 --- a/adex/package.json +++ b/adex/package.json @@ -17,6 +17,14 @@ "type": "module", "exports": { "./package.json": "./package.json", + "./router": { + "types": "./src/router.d.ts", + "import": "./src/router.js" + }, + "./utils/isomorphic": { + "types": "./src/utils/isomorphic.d.ts", + "import": "./src/utils/isomorphic.js" + }, "./ssr": { "types": "./src/ssr.d.ts", "import": "./src/ssr.js" @@ -61,6 +69,7 @@ "hoofd": "^1.7.1", "mri": "^1.2.0", "node-stream-zip": "^1.15.0", + "preact-iso": "^2.9.0", "preact-render-to-string": "^6.5.5", "regexparam": "^3.0.0", "sirv": "^2.0.4", diff --git a/adex/runtime/client.js b/adex/runtime/client.js index 96c5ee1..d2bb424 100644 --- a/adex/runtime/client.js +++ b/adex/runtime/client.js @@ -1,20 +1,51 @@ -import { hydrate as preactHydrate, h } from 'preact' +import { h } from 'preact' +import { + LocationProvider, + Router, + Route, + lazy, + hydrate as preactHydrate, + ErrorBoundary, +} from 'adex/router' + import 'virtual:adex:global.css' -const pageRoutes = import.meta.glob('/src/pages/**/*.{tsx,jsx,js}') +// @ts-expect-error injected by vite +import { routes } from '~routes' -async function hydrate() { - const entryPage = document.getElementById('app').dataset.entryPage - const routeParams = document.getElementById('app').dataset.routeParams - const componentModule = await pageRoutes[entryPage]() - const Component = - 'default' in componentModule ? componentModule.default : componentModule - preactHydrate( - h(Component, { - routeParams: routeParams ? JSON.parse(atob(routeParams)) : {}, - }), - document.getElementById('app') +const withComponents = routes.map(d => { + return { + ...d, + component: lazy(d.module), + } +}) + +function ComponentWrapper({ url = '' }) { + return h( + LocationProvider, + //@ts-expect-error no types for non-jsx function + { url: url }, + h( + ErrorBoundary, + {}, + h( + Router, + {}, + withComponents.map(d => + h(Route, { path: d.routePath, component: d.component }) + ) + ) + ) ) } -hydrate() +export const App = ({ url = '' }) => { + return h(ComponentWrapper, { url }) +} + +async function hydrate() { + preactHydrate(h(ComponentWrapper, {}), document.getElementById('app')) +} +if (typeof window !== 'undefined') { + hydrate() +} diff --git a/adex/runtime/handler.js b/adex/runtime/handler.js index 5cc6023..02db02c 100644 --- a/adex/runtime/handler.js +++ b/adex/runtime/handler.js @@ -1,8 +1,12 @@ import { CONSTANTS, emitToHooked } from 'adex/hook' import { prepareRequest, prepareResponse } from 'adex/http' -import { renderToString, toStatic } from 'adex/ssr' +import { toStatic } from 'adex/ssr' +import { renderToString } from 'adex/utils/isomorphic' import { h } from 'preact' +// @ts-expect-error injected by vite +import { App } from 'virtual:adex:client' + // @ts-expect-error injected by vite import { routes as apiRoutes } from '~apiRoutes' // @ts-expect-error injected by vite @@ -16,7 +20,8 @@ export async function handler(req, res) { prepareRequest(req) prepareResponse(res) - const [baseURL] = req.url.split('?') + const [url, search] = req.url.split('?') + const baseURL = normalizeRequestUrl(url) const { metas, links, title, lang } = toStatic() @@ -50,10 +55,15 @@ export async function handler(req, res) { }) if (matchedInPages) { - const module = await matchedInPages.module() - const render = 'default' in module ? module.default : module const routeParams = getRouteParams(baseURL, matchedInPages) + // @ts-expect-error + global.location = new URL(req.url, 'http://localhost') + + const rendered = await renderToString( + h(App, { url: [baseURL, search].filter(Boolean).join('?') }) + ) + const htmlString = HTMLTemplate({ metas, links, @@ -63,7 +73,7 @@ export async function handler(req, res) { routeParams: Buffer.from(JSON.stringify(routeParams), 'utf8').toString( 'base64' ), - body: renderToString(h(render, { routeParams })), + body: rendered.html, }) const modifiableContext = { req: req, @@ -105,13 +115,7 @@ function HTMLTemplate({ ${headString}
-