2022-03-14 10:32:45 +03:00
|
|
|
import { isBool, isNil } from "./lang.mjs";
|
2022-03-16 22:25:36 +03:00
|
|
|
import { Elem, TextNode } from "./nodes.mjs";
|
2022-03-14 10:32:45 +03:00
|
|
|
export class StrRenderer {
|
|
|
|
async render(node) {
|
2022-03-16 22:25:36 +03:00
|
|
|
return encodeNode(await node);
|
2022-03-14 10:32:45 +03:00
|
|
|
}
|
|
|
|
}
|
2022-03-16 22:25:36 +03:00
|
|
|
async function encodeAnyNode(node) {
|
|
|
|
const syncNode = await node;
|
|
|
|
return syncNode instanceof TextNode
|
|
|
|
? encodeTextNode(syncNode)
|
|
|
|
: encodeNode(syncNode);
|
2022-03-14 10:32:45 +03:00
|
|
|
}
|
|
|
|
function encodeTextNode(node) {
|
2022-03-16 22:25:36 +03:00
|
|
|
return String(node);
|
2022-03-14 10:32:45 +03:00
|
|
|
}
|
2022-03-16 22:25:36 +03:00
|
|
|
async function encodeNode(node) {
|
|
|
|
const encodedChildren = isNil(node.children)
|
|
|
|
? undefined
|
|
|
|
: await Promise.all(node.children.map(encodeAnyNode));
|
|
|
|
return node instanceof Elem
|
|
|
|
? encodeHtmlElement(node.tagName, node.attrs, encodedChildren)
|
|
|
|
: encodeHtmlFragment(encodedChildren);
|
2022-03-14 10:32:45 +03:00
|
|
|
}
|
2022-03-16 22:25:36 +03:00
|
|
|
function encodeHtmlFragment(children) {
|
|
|
|
return children?.join("") ?? "";
|
|
|
|
}
|
|
|
|
function encodeHtmlElement(tagName, attrs, children) {
|
2022-03-14 10:32:45 +03:00
|
|
|
const open = `<${tagName} ${encodeAttrs(attrs)}>`;
|
|
|
|
if (isNil(children))
|
|
|
|
return open;
|
|
|
|
return `${open}${children.join("")}</${tagName}>`;
|
|
|
|
}
|
|
|
|
function encodeAttrs(attrs) {
|
|
|
|
if (!attrs)
|
|
|
|
return "";
|
|
|
|
return Object.entries(attrs)
|
|
|
|
.map(([key, value]) => encodeAttr(key, value))
|
|
|
|
.filter(Boolean)
|
|
|
|
.join(" ");
|
|
|
|
}
|
|
|
|
function encodeAttr(key, value) {
|
|
|
|
if (isNil(value))
|
|
|
|
return null;
|
|
|
|
if (isBool(value))
|
|
|
|
return value ? key : null;
|
|
|
|
return `${key}="${value}"`;
|
|
|
|
}
|