Created
July 30, 2022 21:19
-
-
Save webstrand/cc19d947320af9a9d1cfbceb7fea2d84 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export {jsx, jsx as jsxs, jsx as Fragment} | |
type DOMElement = Element; | |
declare global { | |
namespace JSX { | |
type Element = DOMElement; | |
type ChildElement = string | number | boolean | symbol | bigint | null | undefined | { toString(): string } | (() => ChildElements) | Node; | |
type ChildElements = ChildElement | Iterable<ChildElement>; | |
interface HTMLAttributes<T> { | |
id?: string; | |
class?: string; | |
} | |
interface HTMLImageAttributes<T extends HTMLImageElement> extends HTMLAttributes<T> { | |
src?: string; | |
} | |
interface HTMLAnchorAttributes<T extends HTMLAnchorElement> extends HTMLAttributes<T> { | |
href?: string; | |
} | |
interface IntrinsicElements { | |
div: HTMLAttributes<HTMLDivElement> | |
img: HTMLImageAttributes<HTMLImageElement> | |
a: HTMLAnchorAttributes<HTMLAnchorElement> | |
} | |
} | |
} | |
function isIterable(o: {}): o is Iterable<any> { | |
return Symbol.iterator in o; | |
} | |
function expandChildren(children: JSX.ChildElements): (string | Node)[] { | |
switch(typeof children) { | |
case "string": | |
return [children]; | |
case "function": | |
return expandChildren(children()); | |
case "object": | |
if(children !== null) { | |
if(children instanceof Node) { | |
return [children]; | |
} | |
else if(isIterable(children)) { | |
return [...children].flatMap(child => expandChildren(child)); | |
} | |
break; | |
} | |
case "undefined": | |
return []; | |
case "number": | |
case "boolean": | |
case "symbol": | |
case "bigint": | |
} | |
return [String(children)] | |
} | |
function jsx<T extends keyof JSX.IntrinsicElements, Attrs extends JSX.IntrinsicElements[T] & { children?: JSX.ChildElements }>(tag: string | ((attrs: Attrs) => Element), attrs: Attrs) { | |
if(typeof tag === "function") { | |
if(tag === jsx as never) { | |
const fragment = document.createDocumentFragment(); | |
fragment.append(...expandChildren(attrs.children)); | |
return fragment; | |
} | |
else return tag(attrs); | |
} | |
const element = document.createElement(tag); | |
for(const attr in attrs) { | |
if(attr === "children") continue; | |
element.setAttribute(attr, attrs[attr] as any); | |
} | |
element.append(...expandChildren(attrs.children)); | |
return element; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment