DenReg/jsx/mod.ts

146 lines
3.0 KiB
TypeScript

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<ComponentRetElm>;
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<string> {
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 || ""}</${tag}>`;
}
}
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;
}
}