-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhjsx.ts
98 lines (87 loc) · 3.04 KB
/
hjsx.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import * as u from "./util";
export function hjsx(
type: hjsx.Element["type"],
props?: Record<PropertyKey, unknown>,
children?: hjsx.Node,
) {
return {
type,
props,
children,
render() {
return renderToString(this);
},
$$typeof: Symbol.for("hjsx.element"),
key: null,
ref: null,
_owner: null,
};
}
export function fragment({ children }: hjsx.RenderProps) {
return children;
}
export const renderToString = (component?: unknown): string => {
if (u.isPrimitive(component)) return u.escapeHtml(String(component));
if (u.isNullish(component)) return "";
if (u.isIterable(component)) return renderChildren({ children: component });
if (!u.isObject(component)) return "";
if (!("children" in component)) return renderChildren({ children: component });
validateElement(component);
let { type, props, children } = component;
props = props ?? {};
if (typeof type === "function") {
const componentInstance = u.isClassConstructor(type)
? new type({ ...props, children })
: type({ ...props, children });
return renderToString(componentInstance);
}
const innerHTML = props.dangerouslySetInnerHTML
? u.dangerouslySetInnerHTML(props.dangerouslySetInnerHTML)
: null;
const propsString = Object.entries(props)
.filter(
([key, value]) =>
key !== "u.dangerouslySetInnerHTML" && value !== false && value != null,
)
.map(([key, value]) => {
const normalizedKey = u.normalizeAttributeName(key);
const normalizedValue = u.escapeHtml(
String(key === "style" ? u.handleStyle(value) : value),
);
return value === true ? normalizedKey : `${normalizedKey}="${normalizedValue}"`;
})
.join(" ");
const childrenString = innerHTML ?? renderChildren({ children });
if (typeof type !== "string") {
return renderToString({ type, props, children });
}
return u.SELF_CLOSING_TAGS.includes(type)
? `<${type} ${propsString} />`
: `<${type} ${propsString}>${childrenString}</${type}>`;
};
const renderChildren = (args: unknown): string => {
if (u.isNullish(args)) return "";
if (u.isPrimitive(args)) return u.escapeHtml(String(args));
if (!u.isObject(args)) return "";
const { children } = args;
const childrenArray = Array.isArray(children) ? children : [children];
return childrenArray
.filter((child) => !u.isNullish(child))
.map(renderToString)
.join("");
};
function validateElement(
element: unknown,
): asserts element is hjsx.PropsWithChildren<hjsx.Element> {
if (!u.isObject(element)) {
throw new Error("Element must be an object");
}
if (!("type" in element)) {
throw new Error("Element must have a type");
}
if (!("props" in element)) {
throw new Error("Element must have props");
}
}
// set globals
globalThis.hjsx = Object.assign(hjsx, { fragment });