import "./types.ts"; const Fragment = Symbol("fragment"); declare namespace JSX { interface Element {} interface IntrinsicElements { div: any; } } export type Element = { component: Component | string | typeof Fragment; props: any; children: any[]; }; export type ComponentRetElm = Element | Element[]; export type Component = ( props: any, children: any ) => ComponentRetElm | Promise; export function h( component: string | Component, props: any, ...children: Element[] ): Element { return { component, props, children, }; } const createElement = h; export { Fragment, createElement }; export async function renderSSR(element: Element | string): Promise { if (typeof element === "string") return element; else if (typeof element.component === "string") return await renderHTML(element as Element); else if ( typeof element.component === "function" || element.component === Fragment ) return await renderCustom(element as Element); console.warn("renderSSR: invalid element", element); return ""; } const selfClosing = new Set([ "area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr", ]); function flatDeep(arr: any, d = Infinity): any[] { if (Array.isArray(arr) && d >= 0) { let res = []; for (const val of arr) { const v = flatDeep(val, d - 1); res.push(...v); } return res; } return [arr]; } function cleanChildren(children: any) { return flatDeep(children).filter((e) => !!e); } async function renderHTML(element: Element) { if (typeof element.component !== "string") throw new Error("Internal consistency error"); let props = ""; for (const key in element.props) { if (key == "innerHTML") continue; props += `${key}="${element.props[key] || ""}" `; } const tag = element.component; if (selfClosing.has(element.component)) { return `<${tag} ${props}/>`; } else { let inner = ""; if (element.props && element.props["innerHTML"]) { inner = element.props["innerHTML"]; } else { const children = cleanChildren(element.children); inner = ( await Promise.all(children.map((child) => renderSSR(child))) ).join(""); } return `<${tag} ${props}>${inner || ""}`; } } async function renderCustom(element: Element) { if (typeof element.component === "string") throw new Error("Internal consistency error"); if (element.component === Fragment) { const ch = ( await Promise.all( cleanChildren(element.children).map((child) => renderSSR(child)) ) ).join(""); return ch; } else { const res = await Promise.resolve( element.component( { ...element.props, children: element.children, }, element.children ) ); const ch = ( await Promise.all(cleanChildren(res).map((child) => renderSSR(child))) ).join(""); return ch; } }