Skip to content

Commit ac2ea83

Browse files
committed
(feat) simple renderDOM implementation
Signed-off-by: Muthu Kumar <[email protected]>
1 parent 6101935 commit ac2ea83

File tree

5 files changed

+84
-2
lines changed

5 files changed

+84
-2
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
browser-test/test.browser.js

browser-test/index.html

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<title>Hyperactive!</title>
6+
<meta name="viewport" content="width=device-width, initial-scale=1" />
7+
</head>
8+
9+
<body>
10+
<noscript>You need to enable JavaScript to run this app.</noscript>
11+
<div id="root"></div>
12+
<script type="application/javascript" src="/test.browser.js"></script>
13+
</body>
14+
</html>

src/render.ts

+49-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
import { Nodeish, HTMLNode } from "./Node.ts";
2-
import { isFalsy, escapeHTML } from "./util.ts";
1+
///<reference path="https://raw.githubusercontent.com/denoland/deno/main/cli/dts/lib.deno.ns.d.ts" />
2+
///<reference path="https://raw.githubusercontent.com/microsoft/TypeScript/main/lib/lib.dom.d.ts" />
3+
4+
import { Node, Nodeish, HTMLNode } from "./Node.ts";
5+
import { Falsy, isFalsy, escapeHTML, guessEnv } from "./util.ts";
36

47
export function renderHTML(node: Nodeish) {
58
if (isFalsy(node)) return "";
@@ -21,3 +24,47 @@ export function renderHTML(node: Nodeish) {
2124

2225
return stringified;
2326
}
27+
28+
type NodeishtoDOM<N extends Nodeish> = N extends Falsy
29+
? ""
30+
: N extends string
31+
? string
32+
: N extends HTMLNode
33+
? { innerHTML: string }
34+
: HTMLElement;
35+
36+
const toDOM = function toDOM<N extends Nodeish>(node: N) {
37+
if (isFalsy(node)) return "";
38+
if (typeof node === "string") return escapeHTML(node);
39+
if (node instanceof HTMLNode) return { innerHTML: node.htmlString };
40+
41+
const el = document.createElement(node.tag);
42+
43+
for (const attr in node.attrs) {
44+
el.setAttribute(attr, node.attrs[attr]);
45+
}
46+
47+
for (const child of node.children) {
48+
const childNode = toDOM(child);
49+
if (typeof childNode !== "string" && "innerHTML" in childNode)
50+
el.insertAdjacentHTML("beforeend", childNode.innerHTML);
51+
else el.append(childNode);
52+
}
53+
54+
return el;
55+
} as <N extends Nodeish>(node: N) => NodeishtoDOM<N>;
56+
57+
export function renderDOM<
58+
HyNode extends Node | string,
59+
RootNode extends HTMLElement,
60+
>(rootNode: RootNode, hyNode: HyNode) {
61+
const env = guessEnv();
62+
if (env !== "browser")
63+
throw new Error(
64+
`renderDOM is meant to be used in the browser.` +
65+
` Found: '${env || "unknown"}'`,
66+
);
67+
68+
const domNode = toDOM(hyNode);
69+
return rootNode.append(domNode);
70+
}

src/util.ts

+9
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,12 @@ export type Falsy = false | "" | 0 | 0n | undefined | null;
1414
export const Falsy = new Set([false, "", 0, 0n, undefined, null]);
1515
// deno-lint-ignore no-explicit-any
1616
export const isFalsy = (n: any): n is Falsy => Falsy.has(n);
17+
18+
export const guessEnv = () => {
19+
if (typeof window === "undefined") {
20+
// @ts-ignore process is a node global API
21+
if (typeof process === "undefined") return "node";
22+
else return undefined;
23+
} else if (typeof window.Deno !== "undefined") return "deno";
24+
else return "browser";
25+
};

test.browser.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
///<reference path="https://raw.githubusercontent.com/denoland/deno/main/cli/dts/lib.deno.ns.d.ts" />
2+
///<reference path="https://raw.githubusercontent.com/microsoft/TypeScript/main/lib/lib.dom.d.ts" />
3+
4+
import { elements, renderDOM } from "./mod.ts";
5+
6+
const { div, p } = elements;
7+
8+
renderDOM(
9+
document.getElementById("root")!,
10+
div({ class: "container" }, p("Hello world")),
11+
);

0 commit comments

Comments
 (0)