From 57315c52be96d2a2c013f0cfb0de5429980e301a Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 2 Nov 2025 17:49:34 +0900 Subject: refactor(blog): rename directory, services/{blog => nuldoc}/ --- services/nuldoc/nuldoc-src/dom.ts | 154 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 services/nuldoc/nuldoc-src/dom.ts (limited to 'services/nuldoc/nuldoc-src/dom.ts') diff --git a/services/nuldoc/nuldoc-src/dom.ts b/services/nuldoc/nuldoc-src/dom.ts new file mode 100644 index 0000000..abe7ff8 --- /dev/null +++ b/services/nuldoc/nuldoc-src/dom.ts @@ -0,0 +1,154 @@ +export type Text = { + kind: "text"; + content: string; +}; + +export type RawHTML = { + kind: "raw"; + html: string; +}; + +export type Element = { + kind: "element"; + name: string; + attributes: Record; + children: Node[]; +}; + +export type Node = Element | Text | RawHTML; + +export function text(content: string): Text { + return { + kind: "text", + content, + }; +} + +export function rawHTML(html: string): RawHTML { + return { + kind: "raw", + html, + }; +} + +export function elem( + name: string, + attributes?: Record, + ...children: Node[] +): Element { + return { + kind: "element", + name, + attributes: attributes || {}, + children, + }; +} + +export function addClass(e: Element, klass: string) { + const classes = e.attributes.class; + if (classes === undefined) { + e.attributes.class = klass; + } else { + const classList = classes.split(" "); + classList.push(klass); + classList.sort(); + e.attributes.class = classList.join(" "); + } +} + +export function findFirstChildElement( + e: Element, + name: string, +): Element | null { + for (const c of e.children) { + if (c.kind === "element" && c.name === name) { + return c; + } + } + return null; +} + +export function findChildElements(e: Element, name: string): Element[] { + const cs = []; + for (const c of e.children) { + if (c.kind === "element" && c.name === name) { + cs.push(c); + } + } + return cs; +} + +export function innerText(e: Element): string { + let t = ""; + forEachChild(e, (c) => { + if (c.kind === "text") { + t += c.content; + } + }); + return t; +} + +export function forEachChild(e: Element, f: (n: Node) => void) { + for (const c of e.children) { + f(c); + } +} + +export async function forEachChildAsync( + e: Element, + f: (n: Node) => Promise, +): Promise { + for (const c of e.children) { + await f(c); + } +} + +export function forEachChildRecursively(e: Element, f: (n: Node) => void) { + const g = (c: Node) => { + f(c); + if (c.kind === "element") { + forEachChild(c, g); + } + }; + forEachChild(e, g); +} + +export async function forEachChildRecursivelyAsync( + e: Element, + f: (n: Node) => Promise, +): Promise { + const g = async (c: Node) => { + await f(c); + if (c.kind === "element") { + await forEachChildAsync(c, g); + } + }; + await forEachChildAsync(e, g); +} + +export function forEachElementOfType( + root: Element, + elementName: string, + f: (e: Element) => void, +) { + forEachChildRecursively(root, (n) => { + if (n.kind === "element" && n.name === elementName) { + f(n); + } + }); +} + +export function processTextNodesInElement( + e: Element, + f: (text: string) => Node[], +) { + const newChildren: Node[] = []; + for (const child of e.children) { + if (child.kind === "text") { + newChildren.push(...f(child.content)); + } else { + newChildren.push(child); + } + } + e.children = newChildren; +} -- cgit v1.2.3-70-g09d2