import React, { ComponentType } from "react"; import { renderToStaticMarkup } from "react-dom/server"; import { HelmetProvider, FilledContext } from "react-helmet-async"; import { Asset, Bundler } from "../../bundler"; import { Observable } from "../../observable"; import { ServerStyleSheet } from "styled-components"; import { resolve } from "path"; type PageOptions = { path: string; template: Observable>; props: Observable; bundler: Bundler; }; const createPage = (options: PageOptions) => { const data = Observable.combine({ template: options.template, props: options.props, }); const page = data.pipe(async ({ template, props }) => { const sheet = new ServerStyleSheet(); const helmetContext: FilledContext = {} as any; const body = sheet.collectStyles( React.createElement( HelmetProvider, { context: helmetContext }, React.createElement(template, props) ) ); const bodyHtml = renderToStaticMarkup(body); const { helmet } = helmetContext; const css = sheet.getStyleTags(); const headHtml = [ css, helmet.title?.toString(), helmet.priority?.toString(), helmet.meta?.toString(), helmet.link?.toString(), helmet.script?.toString(), ] .filter(Boolean) .join(""); const html = ` ${headHtml} ${bodyHtml} `; const asset: Asset = { content: html }; return asset; }); const path = resolve("/", options.path, "index.html"); return options.bundler.register(path, page); }; export { createPage };