const voidElements = new Set([ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'source', 'track', 'wbr', ]) export const raw = (html) => ({ type: 'raw', html }) export const h = (tag, attributes = {}, children = []) => ({ type: 'element', tag, attributes, children: Array.isArray(children) ? children : [children], }) export const escapeHtml = (value) => String(value) .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"') .replaceAll("'", ''') export const render = (node) => { if (node == null || node === false) return '' if (Array.isArray(node)) return node.map(render).join('') if (typeof node === 'string' || typeof node === 'number') return escapeHtml(node) if (node.type === 'raw') return node.html const attributes = Object.entries(node.attributes) .filter(([, value]) => value !== false && value != null) .map(([name, value]) => (value === true ? name : `${name}="${escapeHtml(value)}"`)) .join(' ') const opening = attributes ? `<${node.tag} ${attributes}>` : `<${node.tag}>` if (voidElements.has(node.tag)) return opening return `${opening}${node.children.map(render).join('')}` }