= (props: P) => Renderable;
export type ElementType = string | Component | typeof Fragment;
export class Html {
constructor(public readonly html: string) {}
toString(): string {
return this.html;
}
}
export const Fragment = Symbol.for("wrnexus.fragment");
const VOID_ELEMENTS = new Set([
"area",
"base",
"br",
"col",
"embed",
"hr",
"img",
"input",
"link",
"meta",
"param",
"source",
"track",
"wbr",
]);
const SAFE_TAG_NAME = /^[A-Za-z][A-Za-z0-9._:-]*$/;
const SAFE_ATTR_NAME = /^[A-Za-z_:][A-Za-z0-9:._-]*$/;
function isHtml(value: unknown): value is Html {
return value instanceof Html;
}
function raw(value: string): Html {
return new Html(value);
}
export function mustache(expr: string): Html;
export function mustache(strings: TemplateStringsArray, ...values: unknown[]): Html;
export function mustache(input: string | TemplateStringsArray, ...values: unknown[]): Html {
const expr =
typeof input === "string"
? input
: input.reduce((out, part, index) => {
const value = index < values.length ? String(values[index]) : "";
return out + part + value;
}, "");
return raw(`{{${expr.trim()}}}`);
}
function renderChild(value: Renderable): string {
if (value === null || value === undefined || typeof value === "boolean") return "";
if (Array.isArray(value)) return value.map(renderChild).join("");
if (isHtml(value)) return value.html;
return escapeHtml(String(value));
}
function renderComponentResult(value: Renderable): string {
if (value === null || value === undefined || typeof value === "boolean") return "";
if (Array.isArray(value)) return value.map(renderComponentResult).join("");
if (isHtml(value)) return value.html;
// WrNexus page/component strings are HTML by convention.
if (typeof value === "string") return value;
return escapeHtml(String(value));
}
function attrName(name: string): string {
if (name === "className") return "class";
if (name === "htmlFor") return "for";
return name;
}
function styleToString(value: Record