/** * Melina.js JSX-to-DOM Runtime * * JSX in client.tsx files creates REAL DOM elements, not virtual DOM. * * Usage: * const el =
Hello
; * document.body.appendChild(el); // Works directly! */ import { assignElementStyle, createDomElement, domAttributeName, setElementClass, } from "./dom"; type Child = Node | string | number | boolean | null | undefined | Child[]; export function jsx( tag: string | ((props: any) => Node), props: Record | null, ...children: Child[] ): Node { // Function component if (typeof tag === "function") { const finalProps = { ...props }; // Use varargs children only if props.children is missing (Classic Runtime fallback) if ((!props || props.children === undefined) && children.length > 0) { finalProps.children = children.length === 1 ? children[0] : children; } return tag(finalProps); } const el = createDomElement(tag); // Set attributes/properties if (props) { for (const [key, value] of Object.entries(props)) { if (key === "children") continue; if (value === null || value === undefined || value === false) continue; if (key === "style" && typeof value === "object") { assignElementStyle(el, value); } else if (key === "className" || key === "class") { setElementClass(el, value); } else if (key === "htmlFor") { el.setAttribute(domAttributeName(el, key), String(value)); } else if (key === "dangerouslySetInnerHTML") { el.innerHTML = value.__html || ""; } else if (key.startsWith("on") && typeof value === "function") { // Event handlers: onClick -> click const event = key.slice(2).toLowerCase(); el.addEventListener(event, value); } else if (key === "ref" && typeof value === "function") { value(el); } else if (value === true) { el.setAttribute(domAttributeName(el, key), ""); } else { el.setAttribute(domAttributeName(el, key), String(value)); } } // Handle children passed as prop (Automatic Runtime) if (props.children !== undefined) { appendChildren( el, Array.isArray(props.children) ? props.children : [props.children], ); } } // Append direct children (Classic Runtime fallback) if ((!props || props.children === undefined) && children.length > 0) { appendChildren(el, children); } return el; } function appendChildren(parent: Element, children: Child[]) { for (const child of children) { if ( child === null || child === undefined || child === false || child === true ) continue; if (Array.isArray(child)) { appendChildren(parent, child); } else if (child instanceof Node) { parent.appendChild(child); } else { parent.appendChild(document.createTextNode(String(child))); } } } // JSX runtime entry points (used by Bun/esbuild JSX transform) export const jsxs = jsx; export const jsxDEV = jsx; export const Fragment = ({ children }: { children: Child | Child[] }) => { const frag = document.createDocumentFragment(); if (children !== undefined) { appendChildren( frag as any, Array.isArray(children) ? children : [children], ); } return frag; }; export default { jsx, jsxs, jsxDEV, Fragment };