UNPKG

easy-jsx-html-engine

Version:

Dead simple HTML engine using JSX syntax.

97 lines 3.36 kB
import clsx from "clsx"; import { escapeHTML, isPromise, normalizeChildren } from "./util"; import { isVoidElem } from "./intrinsics"; class Element { name; attrs; constructor(name, attrs) { this.name = name; this.attrs = attrs; } toHTML() { const { children, ...attrs } = this.attrs; const attrEntries = Object.entries(attrs); return `<${this.name}${attrEntries.length ? " " + attrEntries .filter(([_, value]) => value || value === 0) .map(([key, value]) => value === true ? key : `${key}="${typeof value === "string" ? value.replace(/"/g, '\\"') : value}"`) .join(" ") : ""}${isVoidElem(this.name) ? "/>" : ">" + children + `</${this.name}>`}`; } } export function childrenToString(children) { return children .filter(Boolean) .map((child) => child?.toHTML ? child.toHTML() : typeof child === "string" ? escapeHTML(child) : child.toString()) .join(""); } export function createElement(name, attrs = {}, ...children) { if (typeof name === "function") { return name({ ...attrs, children: !children?.length ? undefined : children.length === 1 ? children[0] : children, }); } // remove children from attrs to avoid extra work if it was erroneously passed in as an attribute const { children: _, ...attrsWithoutChildren } = attrs; const normalizedChildren = normalizeChildren(children); const normalizedAttrs = normalizeAttributes(attrsWithoutChildren); if (isPromise(normalizedChildren) || isPromise(normalizedAttrs)) { return Promise.all([normalizedAttrs, normalizedChildren]).then(([attrs, children]) => createElement(name, attrs, children)); } const { class: __, ...other } = attrsWithoutChildren; const escapedAttrs = Object.fromEntries(Object.entries(other).map(([key, value]) => [ key, typeof value === "string" ? escapeHTML(value) : value, ])); if ("class" in attrs) { escapedAttrs.class = clsx(attrs.class); } return new Element(name, { ...escapedAttrs, children: childrenToString(normalizedChildren), }); } function normalizeAttributes(attrs) { const entries = Object.entries(attrs); if (entries.some(([, value]) => isPromise(value))) { return Promise.all(entries.map(([key, value]) => { if (isPromise(value)) { return value.then((value) => [key, value]); } return [key, value]; })).then((entries) => Object.fromEntries(entries)); } return attrs; } class NoEscape { str; constructor(str) { this.str = str; } toHTML() { return this.str; } } export function dangerouslyPreventEscaping(str) { return new NoEscape(str); } export function Fragment(props) { const children = normalizeChildren(props.children); if (isPromise(children)) { return children.then((children) => createElement(Fragment, {}, children)); } return dangerouslyPreventEscaping(childrenToString(children)); } //# sourceMappingURL=create-element.js.map