diff --git a/lib/node.mjs b/lib/nodes.mjs similarity index 74% rename from lib/node.mjs rename to lib/nodes.mjs index 3db071a..8c695bd 100644 --- a/lib/node.mjs +++ b/lib/nodes.mjs @@ -1,22 +1,54 @@ import { isNil } from "./lang.mjs"; -export class TextNode { - #text; - constructor(text) { - this.#text = text; +export class TextNode extends String { +} +export function F(children) { + return new Frag().withChildren(children); +} +export class Frag { + #children; + constructor() { + this.#children = undefined; } - get text() { - return this.#text; + get children() { + return this.#children; + } + withText(text) { + this.addText(text); + return this; + } + addText(text) { + this.addChild(new TextNode(text)); + } + maybeWithChildren(nodes) { + if (isNil(nodes)) + return this; + return this.withChildren(nodes); + } + withChildren(nodes) { + nodes.forEach((n) => this.addChild(n)); + return this; + } + withChild(node) { + this.addChild(node); + return this; + } + addChild(node) { + if (isNil(this.#children)) + this.#children = []; + this.#children.push(node); } } -export class Node { +export function E(tagName, attrs, children) { + return new Elem(tagName).withAttrs(attrs).maybeWithChildren(children); +} +export class Elem extends Frag { #tagName; #attrs; - #children; #isSelfClosed; constructor(tagName) { + super(); this.#tagName = tagName; this.#attrs = {}; - this.#children = undefined; this.#isSelfClosed = selfClosedTagNames.has(tagName); } get tagName() { @@ -25,9 +57,6 @@ export class Node { get attrs() { return this.#attrs; } - get children() { - return this.#children; - } withAttrs(attrs) { Object.entries(attrs).forEach(([key, value]) => this.addAttr(key, value)); return this; @@ -39,27 +68,10 @@ export class Node { addAttr(name, value) { this.#attrs[name] = value; } - withText(text) { - this.addText(text); - return this; - } - addText(text) { - this.addChild(new TextNode(text)); - } - withChildren(nodes) { - nodes.forEach((n) => this.addChild(n)); - return this; - } - withChild(node) { - this.addChild(node); - return this; - } addChild(node) { if (this.#isSelfClosed) throw new Error("You cannot add child to self closed element"); - if (isNil(this.#children)) - this.#children = []; - this.#children.push(node); + super.addChild(node); } } const selfClosedTagNames = new Set([ diff --git a/lib/str.mjs b/lib/str.mjs index 7d2cb9a..c39c0d7 100644 --- a/lib/str.mjs +++ b/lib/str.mjs @@ -1,20 +1,31 @@ -import { TextNode } from "./node.mjs"; import { isBool, isNil } from "./lang.mjs"; +import { Elem, TextNode } from "./nodes.mjs"; export class StrRenderer { async render(node) { - return encodeNode(node); + return encodeNode(await node); } } -function encodeAnyNode(node) { - return node instanceof TextNode ? encodeTextNode(node) : encodeNode(node); +async function encodeAnyNode(node) { + const syncNode = await node; + return syncNode instanceof TextNode + ? encodeTextNode(syncNode) + : encodeNode(syncNode); } function encodeTextNode(node) { - return node.text; + return String(node); } -function encodeNode(node) { - return encodeHtml(node.tagName, node.attrs, node.children?.map(encodeAnyNode)); +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); } -function encodeHtml(tagName, attrs, children) { +function encodeHtmlFragment(children) { + return children?.join("") ?? ""; +} +function encodeHtmlElement(tagName, attrs, children) { const open = `<${tagName} ${encodeAttrs(attrs)}>`; if (isNil(children)) return open; diff --git a/makefile b/makefile index dc66e75..8436e74 100644 --- a/makefile +++ b/makefile @@ -1,4 +1,7 @@ +ts: + npx tsc + ts-w: npx tsc-watch