function ind(_: number | undefined | null, str: string): string { return str; }; var tagsToReplace: Record = { '&': '&', '<': '<', '>': '>' }; export function escapeHtmlShitty(x: string): string { return x.replace(/[&<>]/g, (tag) => tagsToReplace[tag] || tag); } export interface HtmlEnt { toHtmlStr(indent?: number): string; }; class HtmlElem implements HtmlEnt { public name: string; public attrs: { [id: string]: string | boolean | undefined } = {}; public content: HtmlEnt[] = []; constructor(n: string) { this.name = n; } setAttribute(id: string, val: string | boolean) { this.attrs[id] = val; } appendChild(...c: HtmlEnt[]) { this.content.push(...c); } toHtmlStr(indent?: number) { const out: string[] = []; const tag: string[] = [`<${this.name}`]; for (const key of Object.keys(this.attrs)) { if (!this.attrs.hasOwnProperty(key)) continue; const x = this.attrs[key]; if (x === undefined) continue; if (typeof x === "boolean" && !!x) { tag.push(key); } else { tag.push(`${key}="${x}"`); } } if (this.content.length === 0) { return ind(indent, tag.join(" ")) + " />"; } out.push(ind(indent, tag.join(" ")) + '>'); for (const e of this.content) { try { out.push(e.toHtmlStr((indent ?? 0) + 2)); } catch(exc) { out.push(e.toString()); } } out.push(ind(indent, ``)); return out.join(""); } } class HtmlText implements HtmlEnt { public value: string; constructor(v: string) { this.value = v; } toHtmlStr(indent?: number) { return ind(indent, this.value); } } type Content = HtmlEnt | string | Content[] | undefined; const add = (element: HtmlElem, child: Content) => { if (typeof child === 'string') { element.appendChild(new HtmlText(child.toString())) } else if (child instanceof Array) { child.forEach((x) => add(element, x)); } else if (child === undefined) { return; } else { element.appendChild(child); } }; type JSXName = string | ((props: T) => HtmlEnt); type ElemProps = { [id: string]: string | boolean } export class JSX { static createTextNode(t: string): HtmlEnt { return new HtmlText(t); } static createElement(fn: (props: T) => HtmlEnt, props: T, ...content: Content[]): HtmlEnt; static createElement>(name: T, arg: T extends 'string' ? ElemProps : P, ...content: Content[]): HtmlEnt { if (typeof name !== 'string') { return name(arg); } else { const element = new HtmlElem(name); const props = (arg as { [id: string]: string | boolean }) || {}; for (let name in props) { if (name && props.hasOwnProperty(name)) { let value = props[name]; if (value === true) { element.setAttribute(name, name); } else if (value !== false && value != null) { element.setAttribute(name, value.toString()); } } } for (let i = 0; i < content.length; i++) { let child = content[i]; add(element, child); } return element; } } }; export default JSX;