diff options
Diffstat (limited to 'src/frontend/h.ts')
-rw-r--r-- | src/frontend/h.ts | 46 |
1 files changed, 46 insertions, 0 deletions
diff --git a/src/frontend/h.ts b/src/frontend/h.ts new file mode 100644 index 000000000..3d9e62f43 --- /dev/null +++ b/src/frontend/h.ts @@ -0,0 +1,46 @@ +export type HProps = Record<string, string> | null | undefined; +export type HChild = string | undefined | (() => string); +export type HMXComponent = (props: HProps, ...children: Array<HChild>) => string; +export type HTag = string | HMXComponent; + +function* _h(tag: string, attrs: HProps, children: Array<HChild>) { + yield `<${tag}`; + if (attrs) { + yield ' '; + for (let [key, value] of Object.entries(attrs)) { + yield `${key}="${value}"`; + } + } + yield '>'; + + for (let child of children) { + // Special: If a child is a function, call it automatically. + // This lets you do {() => ...} without the extra boilerplate + // of wrapping it in a function and calling it. + if (typeof child === 'function') { + yield child(); + } else if (typeof child === 'string') { + yield child; + } else if (!child) { + // do nothing, safe to ignore falsey values. + } else { + yield child; + } + } + + yield `</${tag}>`; +} + +export async function h(tag: HTag, attrs: HProps, ...pChildren: Array<Promise<HChild>>) { + const children = await Promise.all(pChildren.flat(Infinity)); + if (typeof tag === 'function') { + // We assume it's an hmx component + return tag(attrs, ...children); + } + + return Array.from(_h(tag, attrs, children)).join(''); +} + +export function Fragment(_: HProps, ...children: Array<string>) { + return children.join(''); +} |