import { AstroJSX, isVNode } from "../../jsx-runtime/index.js"; import { HTMLString, escapeHTML, markHTMLString, renderToString, spreadAttributes, voidElementNames } from "./index.js"; import { renderComponentToString } from "./render/component.js"; const ClientOnlyPlaceholder = "astro-client-only"; class Skip { constructor(vnode) { this.vnode = vnode; this.count = 0; } count; increment() { this.count++; } haveNoTried() { return this.count === 0; } isCompleted() { return this.count > 2; } static symbol = Symbol("astro:jsx:skip"); } let originalConsoleError; let consoleFilterRefs = 0; async function renderJSX(result, vnode) { switch (true) { case vnode instanceof HTMLString: if (vnode.toString().trim() === "") { return ""; } return vnode; case typeof vnode === "string": return markHTMLString(escapeHTML(vnode)); case typeof vnode === "function": return vnode; case (!vnode && vnode !== 0): return ""; case Array.isArray(vnode): return markHTMLString( (await Promise.all(vnode.map((v) => renderJSX(result, v)))).join("") ); } let skip; if (vnode.props) { if (vnode.props[Skip.symbol]) { skip = vnode.props[Skip.symbol]; } else { skip = new Skip(vnode); } } else { skip = new Skip(vnode); } return renderJSXVNode(result, vnode, skip); } async function renderJSXVNode(result, vnode, skip) { if (isVNode(vnode)) { switch (true) { case !vnode.type: { throw new Error(`Unable to render ${result.pathname} because it contains an undefined Component! Did you forget to import the component or is it possible there is a typo?`); } case vnode.type === Symbol.for("astro:fragment"): return renderJSX(result, vnode.props.children); case vnode.type.isAstroComponentFactory: { let props = {}; let slots = {}; for (const [key, value] of Object.entries(vnode.props ?? {})) { if (key === "children" || value && typeof value === "object" && value["$$slot"]) { slots[key === "children" ? "default" : key] = () => renderJSX(result, value); } else { props[key] = value; } } const str = await renderToString(result, vnode.type, props, slots); if (str instanceof Response) { throw str; } const html = markHTMLString(str); return html; } case (!vnode.type && vnode.type !== 0): return ""; case (typeof vnode.type === "string" && vnode.type !== ClientOnlyPlaceholder): return markHTMLString(await renderElement(result, vnode.type, vnode.props ?? {})); } if (vnode.type) { let extractSlots2 = function(child) { if (Array.isArray(child)) { return child.map((c) => extractSlots2(c)); } if (!isVNode(child)) { _slots.default.push(child); return; } if ("slot" in child.props) { _slots[child.props.slot] = [..._slots[child.props.slot] ?? [], child]; delete child.props.slot; return; } _slots.default.push(child); }; var extractSlots = extractSlots2; if (typeof vnode.type === "function" && vnode.type["astro:renderer"]) { skip.increment(); } if (typeof vnode.type === "function" && vnode.props["server:root"]) { const output2 = await vnode.type(vnode.props ?? {}); return await renderJSX(result, output2); } if (typeof vnode.type === "function") { if (skip.haveNoTried() || skip.isCompleted()) { useConsoleFilter(); try { const output2 = await vnode.type(vnode.props ?? {}); let renderResult; if (output2?.[AstroJSX]) { renderResult = await renderJSXVNode(result, output2, skip); return renderResult; } else if (!output2) { renderResult = await renderJSXVNode(result, output2, skip); return renderResult; } } catch (e) { if (skip.isCompleted()) { throw e; } skip.increment(); } finally { finishUsingConsoleFilter(); } } else { skip.increment(); } } const { children = null, ...props } = vnode.props ?? {}; const _slots = { default: [] }; extractSlots2(children); for (const [key, value] of Object.entries(props)) { if (value["$$slot"]) { _slots[key] = value; delete props[key]; } } const slotPromises = []; const slots = {}; for (const [key, value] of Object.entries(_slots)) { slotPromises.push( renderJSX(result, value).then((output2) => { if (output2.toString().trim().length === 0) return; slots[key] = () => output2; }) ); } await Promise.all(slotPromises); props[Skip.symbol] = skip; let output; if (vnode.type === ClientOnlyPlaceholder && vnode.props["client:only"]) { output = await renderComponentToString( result, vnode.props["client:display-name"] ?? "", null, props, slots ); } else { output = await renderComponentToString( result, typeof vnode.type === "function" ? vnode.type.name : vnode.type, vnode.type, props, slots ); } return markHTMLString(output); } } return markHTMLString(`${vnode}`); } async function renderElement(result, tag, { children, ...props }) { return markHTMLString( `<${tag}${spreadAttributes(props)}${markHTMLString( (children == null || children == "") && voidElementNames.test(tag) ? `/>` : `>${children == null ? "" : await renderJSX(result, prerenderElementChildren(tag, children))}` )}` ); } function prerenderElementChildren(tag, children) { if (typeof children === "string" && (tag === "style" || tag === "script")) { return markHTMLString(children); } else { return children; } } function useConsoleFilter() { consoleFilterRefs++; if (!originalConsoleError) { originalConsoleError = console.error; try { console.error = filteredConsoleError; } catch (error) { } } } function finishUsingConsoleFilter() { consoleFilterRefs--; } function filteredConsoleError(msg, ...rest) { if (consoleFilterRefs > 0 && typeof msg === "string") { const isKnownReactHookError = msg.includes("Warning: Invalid hook call.") && msg.includes("https://reactjs.org/link/invalid-hook-call"); if (isKnownReactHookError) return; } originalConsoleError(msg, ...rest); } export { renderJSX };