diff options
| author | nsfisis <nsfisis@gmail.com> | 2022-12-23 23:27:09 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2023-03-06 01:46:04 +0900 |
| commit | 88ba6cfe220216f371f8756921059fac51a21262 (patch) | |
| tree | f272db2a0a3340f103df6618f19a101e65941b37 /nuldoc-src/docbook/to_html.ts | |
| parent | 8f988a6e899aed678406ddfac1be4ef105439274 (diff) | |
| download | blog.nsfisis.dev-88ba6cfe220216f371f8756921059fac51a21262.tar.gz blog.nsfisis.dev-88ba6cfe220216f371f8756921059fac51a21262.tar.zst blog.nsfisis.dev-88ba6cfe220216f371f8756921059fac51a21262.zip | |
AsciiDoc to DocBook
Diffstat (limited to 'nuldoc-src/docbook/to_html.ts')
| -rw-r--r-- | nuldoc-src/docbook/to_html.ts | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/nuldoc-src/docbook/to_html.ts b/nuldoc-src/docbook/to_html.ts new file mode 100644 index 0000000..31c419d --- /dev/null +++ b/nuldoc-src/docbook/to_html.ts @@ -0,0 +1,275 @@ +import { Document } from "./document.ts"; +import { DocBookError } from "../errors.ts"; +import { + Element, + findFirstChildElement, + forEachChild, + forEachChildRecursively, + Node, + removeChildElements, +} from "../dom.ts"; + +export default function toHtml(doc: Document): Document { + removeArticleInfo(doc); + removeArticleAttributes(doc); + removeUnnecessaryTextNode(doc); + transformElementNames(doc, "emphasis", "em"); + transformElementNames(doc, "informaltable", "table"); + transformElementNames(doc, "itemizedlist", "ul"); + transformElementNames(doc, "link", "a"); + transformElementNames(doc, "listitem", "li"); + transformElementNames(doc, "literal", "code"); + transformElementNames(doc, "orderedlist", "ol"); + transformElementNames(doc, "simpara", "p"); + transformAttributeNames(doc, "xml:id", "id"); + transformSectionIdAttribute(doc); + setSectionTitleAnchor(doc); + transformSectionTitleElement(doc); + transformProgramListingElement(doc); + transformLiteralLayoutElement(doc); + transformNoteElement(doc); + setDefaultLangAttribute(doc); + traverseFootnotes(doc); + return doc; +} + +function removeArticleInfo(doc: Document) { + const article = findFirstChildElement(doc.root, "article"); + if (!article) { + throw new DocBookError( + `[docbook.tohtml] <article> element not found`, + ); + } + removeChildElements(article, "info"); +} + +function removeArticleAttributes(doc: Document) { + const article = findFirstChildElement(doc.root, "article"); + if (!article) { + throw new DocBookError( + `[docbook.tohtml] <article> element not found`, + ); + } + article.attributes.delete("xmlns"); + article.attributes.delete("xmlns:xl"); + article.attributes.delete("version"); +} + +function removeUnnecessaryTextNode(doc: Document) { + const g = (n: Node) => { + if (n.kind !== "element") { + return; + } + + let changed = true; + while (changed) { + changed = false; + if (n.children.length === 0) { + break; + } + const firstChild = n.children[0]; + if (firstChild.kind === "text" && firstChild.content.trim() === "") { + n.children.shift(); + changed = true; + } + if (n.children.length === 0) { + break; + } + const lastChild = n.children[n.children.length - 1]; + if (lastChild.kind === "text" && lastChild.content.trim() === "") { + n.children.pop(); + changed = true; + } + } + + forEachChild(n, g); + }; + forEachChild(doc.root, g); +} + +function transformElementNames( + doc: Document, + from: string, + to: string, +) { + forEachChildRecursively(doc.root, (n) => { + if (n.kind === "element" && n.name === from) { + n.name = to; + } + }); +} + +function transformAttributeNames( + doc: Document, + from: string, + to: string, +) { + forEachChildRecursively(doc.root, (n) => { + if (n.kind !== "element") { + return; + } + const value = n.attributes.get(from) as string; + if (value !== undefined) { + n.attributes.delete(from); + n.attributes.set(to, value); + } + }); +} + +function transformSectionIdAttribute(doc: Document) { + forEachChildRecursively(doc.root, (n) => { + if (n.kind !== "element" || n.name !== "section") { + return; + } + + const idAttr = n.attributes.get("id"); + n.attributes.set("id", `section--${idAttr}`); + }); +} + +function setSectionTitleAnchor(doc: Document) { + const sectionStack: Element[] = []; + const g = (c: Node) => { + if (c.kind !== "element") { + return; + } + + if (c.name === "section") { + sectionStack.push(c); + } + forEachChild(c, g); + if (c.name === "section") { + sectionStack.pop(); + } + if (c.name === "title") { + const currentSection = sectionStack[sectionStack.length - 1]; + if (!currentSection) { + throw new DocBookError( + "[docbook.tohtml] <title> element must be inside <section>", + ); + } + const sectionId = currentSection.attributes.get("id"); + const aElement: Element = { + kind: "element", + name: "a", + attributes: new Map(), + children: c.children, + }; + aElement.attributes.set("href", `#${sectionId}`); + c.children = [aElement]; + } + }; + forEachChild(doc.root, g); +} + +function transformSectionTitleElement(doc: Document) { + let sectionLevel = 1; + const g = (c: Node) => { + if (c.kind !== "element") { + return; + } + + if (c.name === "section") { + sectionLevel += 1; + c.attributes.set("--section-level", sectionLevel.toString()); + } + forEachChild(c, g); + if (c.name === "section") { + sectionLevel -= 1; + } + if (c.name === "title") { + c.name = `h${sectionLevel}`; + } + }; + forEachChild(doc.root, g); +} + +function transformProgramListingElement(doc: Document) { + forEachChildRecursively(doc.root, (n) => { + if (n.kind !== "element" || n.name !== "programlisting") { + return; + } + + n.name = "pre"; + const codeElement: Element = { + kind: "element", + name: "code", + attributes: new Map(), + children: n.children, + }; + n.children = [codeElement]; + }); +} + +function transformLiteralLayoutElement(doc: Document) { + forEachChildRecursively(doc.root, (n) => { + if (n.kind !== "element" || n.name !== "literallayout") { + return; + } + + n.name = "pre"; + const children = n.children; + const codeElement: Element = { + kind: "element", + name: "code", + attributes: new Map(), + children: children, + }; + n.children = [codeElement]; + }); +} + +function transformNoteElement(doc: Document) { + forEachChildRecursively(doc.root, (n) => { + if (n.kind !== "element" || n.name !== "note") { + return; + } + + const labelElement: Element = { + kind: "element", + name: "div", + attributes: new Map(), + children: [{ + kind: "text", + content: "Note", + }], + }; + const contentElement: Element = { + kind: "element", + name: "div", + attributes: new Map(), + children: n.children, + }; + n.name = "div"; + n.children = [ + labelElement, + contentElement, + ]; + }); +} + +function setDefaultLangAttribute(_doc: Document) { + // TODO + // if (!e.attributes.has("lang")) { + // e.attributes.set("lang", "ja-JP"); + // } +} + +function traverseFootnotes(doc: Document) { + forEachChildRecursively(doc.root, (n) => { + if (n.kind !== "element" || n.name !== "footnote") { + return; + } + + // TODO + // <footnote>x</footnote> + // + // <sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1">1</a>]</sup> + // + // <div class="footnote" id="_footnotedef_1"> + // <a href="#_footnoteref_1">1</a>. RAS syndrome + // </div> + n.name = "span"; + n.children = []; + }); +} |
