Fix prerender

engine-in-web-worker
Jérémy Rialland 2023-09-07 14:53:52 +02:00
parent 25a9b1ad31
commit 9355dca142
6 changed files with 86 additions and 54 deletions

View File

@ -22,32 +22,25 @@ interface Params {
lang: string
}
// const vite = await createViteServer({
// server: {},
// appType: 'mpa',
// })
const script = `
<script>
window.PRERENDER = true;
</script>
`
export default async ({ site, url, lang }: Params) => {
const fileTemplate =
const template =
cache[site] ??
readFileSync(path.join(dirname, `../dist/${site}.html`), 'utf-8')
cache[site] ??= fileTemplate
cache[site] ??= template
// const template = await vite.transformIndexHtml(url, fileTemplate)
const template = fileTemplate
// const { render } = await vite.ssrLoadModule(
// './source/entries/entry-server.tsx'
// )
// TODO: Add CI test to enforce meta tags on SSR pages
// // TODO: Add CI test to enforce meta tags on SSR pages
const { html, styleTags, helmet } = await render(url, lang)
console.log({ html, styleTags, helmet })
const page = template
.replace(regexHTML, html)
.replace('<!--app-script-->', script)
.replace('<!--app-style-->', styleTags)
.replace(regexHelmet, helmet.title.toString() + helmet.meta.toString())

View File

@ -1,6 +1,10 @@
import NodeWorker from '@eshaz/web-worker'
import { createWorkerEngineClient } from '@publicodes/worker'
import { useWorkerEngine, WorkerEngineProvider } from '@publicodes/worker-react'
import {
SuspensePromise,
useWorkerEngine,
WorkerEngineProvider,
} from '@publicodes/worker-react'
import { OverlayProvider } from '@react-aria/overlays'
import { ErrorBoundary } from '@sentry/react'
import i18next from 'i18next'
@ -80,30 +84,32 @@ export default function Provider({
<I18nextProvider i18n={i18next}>
<ReduxProvider store={store}>
<BrowserRouterProvider basename={basename}>
<WorkerEngineProvider workerClient={workerClient}>
<SituationSynchronize>
<ErrorBoundary
fallback={(errorData) => (
// eslint-disable-next-line react/jsx-props-no-spreading
<ErrorFallback {...errorData} />
)}
>
{!import.meta.env.SSR &&
import.meta.env.MODE === 'production' &&
'serviceWorker' in navigator && <ServiceWorker />}
<IframeResizer />
<OverlayProvider>
<ThemeColorsProvider>
<DisableAnimationOnPrintProvider>
<SiteNameContext.Provider value={basename}>
{children}
</SiteNameContext.Provider>
</DisableAnimationOnPrintProvider>
</ThemeColorsProvider>
</OverlayProvider>
</ErrorBoundary>
</SituationSynchronize>
</WorkerEngineProvider>
<SuspensePromise isSSR={import.meta.env.SSR}>
<WorkerEngineProvider workerClient={workerClient}>
<SituationSynchronize>
<ErrorBoundary
fallback={(errorData) => (
// eslint-disable-next-line react/jsx-props-no-spreading
<ErrorFallback {...errorData} />
)}
>
{!import.meta.env.SSR &&
import.meta.env.MODE === 'production' &&
'serviceWorker' in navigator && <ServiceWorker />}
<IframeResizer />
<OverlayProvider>
<ThemeColorsProvider>
<DisableAnimationOnPrintProvider>
<SiteNameContext.Provider value={basename}>
{children}
</SiteNameContext.Provider>
</DisableAnimationOnPrintProvider>
</ThemeColorsProvider>
</OverlayProvider>
</ErrorBoundary>
</SituationSynchronize>
</WorkerEngineProvider>
</SuspensePromise>
</BrowserRouterProvider>
</ReduxProvider>
</I18nextProvider>

View File

@ -1,12 +1,18 @@
import { I18nProvider } from '@react-aria/i18n'
import { withProfiler } from '@sentry/react'
import { createRoot } from 'react-dom/client'
import { createRoot, hydrateRoot } from 'react-dom/client'
import App from '../components/App'
import i18next from '../locales/i18n'
import '../api/sentry'
declare global {
interface Window {
PRERENDER?: boolean
}
}
export const AppFr = () => {
return (
<I18nProvider locale="fr-FR">
@ -23,6 +29,10 @@ if (!import.meta.env.SSR) {
console.error(err)
)
const container = document.querySelector('#js') as Element
const root = createRoot(container)
root.render(<AppFrWithProfiler />)
if (window.PRERENDER) {
const root = hydrateRoot(container, <AppFrWithProfiler />)
} else {
const root = createRoot(container)
root.render(<AppFrWithProfiler />)
}
}

View File

@ -1,12 +1,16 @@
import { SSRProvider } from '@react-aria/ssr'
import { lazy } from 'react'
import ReactDomServer, { type renderToReadableStream } from 'react-dom/server'
import ReactDomServerType from 'react-dom/server'
// @ts-ignore
import ReactDomServer from 'react-dom/server.browser'
import { FilledContext, HelmetProvider } from 'react-helmet-async'
import { StaticRouter } from 'react-router-dom/server'
import { ServerStyleSheet, StyleSheetManager } from 'styled-components'
import i18next from '../locales/i18n'
const { renderToReadableStream } = ReactDomServer as typeof ReactDomServerType
function streamToString(stream: ReadableStream<Uint8Array>) {
return new Response(stream).text()
}
@ -59,9 +63,7 @@ export async function render(url: string, lang: 'fr' | 'en'): Promise<Result> {
console.log('!!! STARTING !!!')
try {
const stream = await (
ReactDomServer.renderToReadableStream as unknown as typeof renderToReadableStream
)(element, {
const stream = await renderToReadableStream(element, {
onError(error, errorInfo) {
console.error({ error, errorInfo })
},

View File

@ -230,6 +230,8 @@
}
</script>
<!--app-script-->
<script
crossorigin="anonymous"
src="https://polyfill.io/v3/polyfill.min.js?features=ResizeObserver%2CIntl.~locale.en%2CIntl.~locale.fr%2CString.prototype.replaceAll%2CObject.fromEntries%2CString.prototype.matchAll%2CrequestIdleCallback"

View File

@ -1,4 +1,8 @@
import { useWorkerEngine, WorkerEngine } from '@publicodes/worker-react'
import {
SuspensePromise,
useWorkerEngine,
WorkerEngine,
} from '@publicodes/worker-react'
import rules, { DottedName } from 'modele-social'
import Engine from 'publicodes'
import { RulePage, useDocumentationSiteMap } from 'publicodes-react'
@ -34,17 +38,32 @@ import { RootState } from '@/store/reducers/rootReducer'
import { TrackPage } from '../components/ATInternetTracking'
import RuleLink from '../components/RuleLink'
export default function Documentation({
documentationPath,
engine,
}: {
interface DocumentationProps {
documentationPath: string
engine: WorkerEngine
}) {
}
export default function DocumentationWrapper(props: DocumentationProps) {
return (
<SuspensePromise
isSSR={import.meta.env.SSR}
fallback={<div>DocumentationWrapper loading...</div>}
activateInBrowser
>
<Documentation
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
/>
</SuspensePromise>
)
}
function Documentation({ documentationPath, engine }: DocumentationProps) {
const { t } = useTranslation()
const location = useLocation()
const pathname = decodeURI(location?.pathname ?? '')
const workerEngine = useWorkerEngine()
const documentationSitePaths = useDocumentationSiteMap(
workerEngine,
documentationPath