From a84908b7e8a0e2423afd6b836eccf27a420270b4 Mon Sep 17 00:00:00 2001
From: nsfisis
Date: Wed, 20 Sep 2023 19:56:52 +0900
Subject: feat(blog/nuldoc): change content format from DocBook to NulDoc
---
vhosts/blog/nuldoc-src/commands/build.ts | 8 +-
vhosts/blog/nuldoc-src/commands/new.ts | 72 ++---
.../blog/nuldoc-src/components/post_page_entry.ts | 2 +-
vhosts/blog/nuldoc-src/docbook/document.ts | 108 -------
vhosts/blog/nuldoc-src/docbook/parse.ts | 21 --
vhosts/blog/nuldoc-src/docbook/to_html.ts | 319 ---------------------
vhosts/blog/nuldoc-src/dom.ts | 6 -
vhosts/blog/nuldoc-src/ndoc/document.ts | 56 ++++
vhosts/blog/nuldoc-src/ndoc/parse.ts | 47 +++
vhosts/blog/nuldoc-src/ndoc/to_html.ts | 235 +++++++++++++++
vhosts/blog/nuldoc-src/pages/post.ts | 10 +-
vhosts/blog/nuldoc-src/pages/slide.ts | 2 +-
vhosts/blog/nuldoc-src/renderers/html.ts | 2 +
vhosts/blog/nuldoc-src/slide/parse.ts | 26 +-
vhosts/blog/nuldoc-src/slide/slide.ts | 111 ++++---
vhosts/blog/nuldoc-src/xml.ts | 4 -
16 files changed, 449 insertions(+), 580 deletions(-)
delete mode 100644 vhosts/blog/nuldoc-src/docbook/document.ts
delete mode 100644 vhosts/blog/nuldoc-src/docbook/parse.ts
delete mode 100644 vhosts/blog/nuldoc-src/docbook/to_html.ts
create mode 100644 vhosts/blog/nuldoc-src/ndoc/document.ts
create mode 100644 vhosts/blog/nuldoc-src/ndoc/parse.ts
create mode 100644 vhosts/blog/nuldoc-src/ndoc/to_html.ts
(limited to 'vhosts/blog/nuldoc-src')
diff --git a/vhosts/blog/nuldoc-src/commands/build.ts b/vhosts/blog/nuldoc-src/commands/build.ts
index da7e5cec..92230d7d 100644
--- a/vhosts/blog/nuldoc-src/commands/build.ts
+++ b/vhosts/blog/nuldoc-src/commands/build.ts
@@ -2,7 +2,7 @@ import { dirname, join, joinGlobs } from "std/path/mod.ts";
import { ensureDir } from "std/fs/mod.ts";
import { expandGlob } from "std/fs/expand_glob.ts";
import { Config } from "../config.ts";
-import { parseDocBookFile } from "../docbook/parse.ts";
+import { parseNulDocFile } from "../ndoc/parse.ts";
import { Page } from "../page.ts";
import { render } from "../render.ts";
import { dateToString } from "../revision.ts";
@@ -48,7 +48,7 @@ async function buildPostPages(config: Config): Promise {
async function collectPostFiles(sourceDir: string): Promise {
const filePaths = [];
- const globPattern = joinGlobs([sourceDir, "**", "*.xml"]);
+ const globPattern = joinGlobs([sourceDir, "**", "*.ndoc"]);
for await (const entry of expandGlob(globPattern)) {
filePaths.push(entry.path);
}
@@ -62,7 +62,7 @@ async function parsePosts(
const posts = [];
for (const postFile of postFiles) {
posts.push(
- await generatePostPage(await parseDocBookFile(postFile, config), config),
+ await generatePostPage(await parseNulDocFile(postFile, config), config),
);
}
return posts;
@@ -85,7 +85,7 @@ async function buildSlidePages(config: Config): Promise {
async function collectSlideFiles(sourceDir: string): Promise {
const filePaths = [];
- const globPattern = joinGlobs([sourceDir, "**", "*.xml"]);
+ const globPattern = joinGlobs([sourceDir, "**", "*.toml"]);
for await (const entry of expandGlob(globPattern)) {
filePaths.push(entry.path);
}
diff --git a/vhosts/blog/nuldoc-src/commands/new.ts b/vhosts/blog/nuldoc-src/commands/new.ts
index 22329972..36125755 100644
--- a/vhosts/blog/nuldoc-src/commands/new.ts
+++ b/vhosts/blog/nuldoc-src/commands/new.ts
@@ -21,7 +21,7 @@ export async function runNewCommand(config: Config) {
config.locations.contentDir,
getDirPath(type),
ymd,
- "TODO.xml",
+ "TODO.ndoc",
);
await ensureDir(dirname(destFilePath));
@@ -39,54 +39,40 @@ function getDirPath(type: "post" | "slide"): string {
function getTemplate(type: "post" | "slide", date: string): string {
if (type === "post") {
- return `
-
-
- TODO
-
- TODO
-
-
- TODO
-
-
-
- ${date}
- 公開
-
-
-
-
+ return `---
+[article]
+title = "TODO"
+description = "TODO"
+tags = [
+ "TODO",
+]
+
+[[article.revisions]]
+date = "${date}"
+remark = "公開"
+---
+
+
`;
} else {
- return `
-
-
- TODO
-
- TODO
-
-
- TODO
-
- TODO
-
- TODO
-
-
-
- ${date}
- 登壇
-
-
-
-
+ return `[slide]
+title = "TODO"
+event = "TODO"
+talkType = "TODO"
+link = "TODO"
+tags = [
+ "TODO",
+]
+
+[[slide.revisions]]
+date = "${date}"
+remark = "登壇"
`;
}
}
diff --git a/vhosts/blog/nuldoc-src/components/post_page_entry.ts b/vhosts/blog/nuldoc-src/components/post_page_entry.ts
index 685c03a8..bed7698a 100644
--- a/vhosts/blog/nuldoc-src/components/post_page_entry.ts
+++ b/vhosts/blog/nuldoc-src/components/post_page_entry.ts
@@ -21,7 +21,7 @@ export function postPageEntry(post: PostPage): Element {
el(
"section",
[["class", "entry-content"]],
- el("p", [], text(post.summary)),
+ el("p", [], text(post.description)),
),
el(
"footer",
diff --git a/vhosts/blog/nuldoc-src/docbook/document.ts b/vhosts/blog/nuldoc-src/docbook/document.ts
deleted file mode 100644
index 677c8275..00000000
--- a/vhosts/blog/nuldoc-src/docbook/document.ts
+++ /dev/null
@@ -1,108 +0,0 @@
-import { join } from "std/path/mod.ts";
-import { Config } from "../config.ts";
-import { DocBookError } from "../errors.ts";
-import { Revision, stringToDate } from "../revision.ts";
-import {
- Element,
- findChildElements,
- findFirstChildElement,
- innerText,
-} from "../dom.ts";
-
-export type Document = {
- root: Element;
- sourceFilePath: string;
- link: string;
- title: string;
- summary: string; // TODO: should it be markup text?
- tags: string[];
- revisions: Revision[];
-};
-
-export function createNewDocumentFromRootElement(
- root: Element,
- sourceFilePath: string,
- config: Config,
-): Document {
- const article = findFirstChildElement(root, "article");
- if (!article) {
- throw new DocBookError(
- `[docbook.new] element not found`,
- );
- }
- const info = findFirstChildElement(article, "info");
- if (!info) {
- throw new DocBookError(
- `[docbook.new] element not found`,
- );
- }
-
- const titleElement = findFirstChildElement(info, "title");
- if (!titleElement) {
- throw new DocBookError(
- `[docbook.new] element not found`,
- );
- }
- const title = innerText(titleElement).trim();
- const abstractElement = findFirstChildElement(info, "abstract");
- if (!abstractElement) {
- throw new DocBookError(
- `[docbook.new] element not found`,
- );
- }
- const summary = innerText(abstractElement).trim();
- const keywordsetElement = findFirstChildElement(info, "keywordset");
- let tags: string[];
- if (!keywordsetElement) {
- tags = [];
- } else {
- tags = findChildElements(keywordsetElement, "keyword").map((x) =>
- innerText(x).trim()
- );
- }
- const revhistoryElement = findFirstChildElement(info, "revhistory");
- if (!revhistoryElement) {
- throw new DocBookError(
- `[docbook.new] element not found`,
- );
- }
- const revisions = findChildElements(revhistoryElement, "revision").map(
- (x, i) => {
- const dateElement = findFirstChildElement(x, "date");
- if (!dateElement) {
- throw new DocBookError(
- `[docbook.new] element not found`,
- );
- }
- const revremarkElement = findFirstChildElement(x, "revremark");
- if (!revremarkElement) {
- throw new DocBookError(
- `[docbook.new] element not found`,
- );
- }
- return {
- number: i + 1,
- date: stringToDate(innerText(dateElement).trim()),
- remark: innerText(revremarkElement).trim(),
- };
- },
- );
- if (revisions.length === 0) {
- throw new DocBookError(
- `[docbook.new] element not found`,
- );
- }
-
- const cwd = Deno.cwd();
- const contentDir = join(cwd, config.locations.contentDir);
- const link = sourceFilePath.replace(contentDir, "").replace(".xml", "/");
- return {
- root: root,
- sourceFilePath: sourceFilePath,
- link: link,
- title: title,
- summary: summary,
- tags: tags,
- revisions: revisions,
- };
-}
diff --git a/vhosts/blog/nuldoc-src/docbook/parse.ts b/vhosts/blog/nuldoc-src/docbook/parse.ts
deleted file mode 100644
index bce317e6..00000000
--- a/vhosts/blog/nuldoc-src/docbook/parse.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { Config } from "../config.ts";
-import { parseXmlFile } from "../xml.ts";
-import { DocBookError, XmlParseError } from "../errors.ts";
-import { createNewDocumentFromRootElement, Document } from "./document.ts";
-import toHtml from "./to_html.ts";
-
-export async function parseDocBookFile(
- filePath: string,
- config: Config,
-): Promise {
- try {
- const root = await parseXmlFile(filePath);
- const doc = createNewDocumentFromRootElement(root, filePath, config);
- return toHtml(doc);
- } catch (e) {
- if (e instanceof DocBookError || e instanceof XmlParseError) {
- e.message = `${e.message} in ${filePath}`;
- }
- throw e;
- }
-}
diff --git a/vhosts/blog/nuldoc-src/docbook/to_html.ts b/vhosts/blog/nuldoc-src/docbook/to_html.ts
deleted file mode 100644
index 4add912c..00000000
--- a/vhosts/blog/nuldoc-src/docbook/to_html.ts
+++ /dev/null
@@ -1,319 +0,0 @@
-// @deno-types="../types/highlight-js.d.ts"
-import hljs from "npm:highlight.js";
-import { Document } from "./document.ts";
-import { DocBookError } from "../errors.ts";
-import {
- addClass,
- Element,
- findFirstChildElement,
- forEachChild,
- forEachChildRecursively,
- Node,
- RawHTML,
- removeChildElements,
- Text,
-} 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, "para", "p");
- transformElementNames(doc, "superscript", "sup");
- transformAttributeNames(doc, "xml:id", "id");
- transformAttributeNames(doc, "xl:href", "href");
- transformSectionIdAttribute(doc);
- setSectionTitleAnchor(doc);
- transformSectionTitleElement(doc);
- transformProgramListingElement(doc);
- transformLiteralLayoutElement(doc);
- transformNoteElement(doc);
- setDefaultLangAttribute(doc);
- traverseFootnotes(doc);
- highlightPrograms(doc);
- return doc;
-}
-
-function removeArticleInfo(doc: Document) {
- const article = findFirstChildElement(doc.root, "article");
- if (!article) {
- throw new DocBookError(
- `[docbook.tohtml] element not found`,
- );
- }
- removeChildElements(article, "info");
-}
-
-function removeArticleAttributes(doc: Document) {
- const article = findFirstChildElement(doc.root, "article");
- if (!article) {
- throw new DocBookError(
- `[docbook.tohtml] 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] element must be inside ",
- );
- }
- 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";
- addClass(n, "highlight");
- 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";
- addClass(n, "highlight");
- const codeElement: Element = {
- kind: "element",
- name: "code",
- attributes: new Map(),
- children: n.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([["class", "admonition-label"]]),
- children: [{
- kind: "text",
- content: "NOTE",
- raw: false,
- }],
- };
- const contentElement: Element = {
- kind: "element",
- name: "div",
- attributes: new Map([["class", "admonition-content"]]),
- children: n.children,
- };
- n.name = "div";
- addClass(n, "admonition");
- 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
- // x
- //
- //
- //
- //
- n.name = "span";
- n.children = [];
- });
-}
-
-function highlightPrograms(doc: Document) {
- forEachChildRecursively(doc.root, (n) => {
- if (n.kind !== "element" || n.name !== "pre") {
- return;
- }
- const preClass = n.attributes.get("class") || "";
- if (!preClass.includes("highlight")) {
- return;
- }
- const codeElement = findFirstChildElement(n, "code");
- if (!codeElement) {
- return;
- }
- const language = n.attributes.get("language");
- if (!language) {
- return;
- }
- const sourceCodeNode = codeElement.children[0] as Text | RawHTML;
- const sourceCode = sourceCodeNode.content;
-
- if (!hljs.getLanguage(language)) {
- return;
- }
-
- const highlighted =
- hljs.highlight(sourceCode, { language: language }).value;
-
- sourceCodeNode.content = highlighted;
- sourceCodeNode.raw = true;
- codeElement.attributes.set("class", "highlight");
- });
-}
diff --git a/vhosts/blog/nuldoc-src/dom.ts b/vhosts/blog/nuldoc-src/dom.ts
index d8f53d76..1147f01b 100644
--- a/vhosts/blog/nuldoc-src/dom.ts
+++ b/vhosts/blog/nuldoc-src/dom.ts
@@ -53,12 +53,6 @@ export function findChildElements(e: Element, name: string): Element[] {
return cs;
}
-export function removeChildElements(e: Element, name: string) {
- e.children = e.children.filter((c) =>
- c.kind !== "element" || c.name !== name
- );
-}
-
export function innerText(e: Element): string {
let t = "";
forEachChild(e, (c) => {
diff --git a/vhosts/blog/nuldoc-src/ndoc/document.ts b/vhosts/blog/nuldoc-src/ndoc/document.ts
new file mode 100644
index 00000000..31bae616
--- /dev/null
+++ b/vhosts/blog/nuldoc-src/ndoc/document.ts
@@ -0,0 +1,56 @@
+import { join } from "std/path/mod.ts";
+import { Config } from "../config.ts";
+import { DocBookError } from "../errors.ts";
+import { Revision, stringToDate } from "../revision.ts";
+import { Element, findFirstChildElement } from "../dom.ts";
+
+export type Document = {
+ root: Element;
+ sourceFilePath: string;
+ link: string;
+ title: string;
+ description: string; // TODO: should it be markup text?
+ tags: string[];
+ revisions: Revision[];
+};
+
+export function createNewDocumentFromRootElement(
+ root: Element,
+ meta: {
+ article: {
+ title: string;
+ description: string;
+ tags: string[];
+ revisions: {
+ date: string;
+ remark: string;
+ }[];
+ };
+ },
+ sourceFilePath: string,
+ config: Config,
+): Document {
+ const article = findFirstChildElement(root, "article");
+ if (!article) {
+ throw new DocBookError(
+ `[docbook.new] element not found`,
+ );
+ }
+
+ const cwd = Deno.cwd();
+ const contentDir = join(cwd, config.locations.contentDir);
+ const link = sourceFilePath.replace(contentDir, "").replace(".xml", "/");
+ return {
+ root: root,
+ sourceFilePath: sourceFilePath,
+ link: link,
+ title: meta.article.title,
+ description: meta.article.description,
+ tags: meta.article.tags,
+ revisions: meta.article.revisions.map((r, i) => ({
+ number: i,
+ date: stringToDate(r.date),
+ remark: r.remark,
+ })),
+ };
+}
diff --git a/vhosts/blog/nuldoc-src/ndoc/parse.ts b/vhosts/blog/nuldoc-src/ndoc/parse.ts
new file mode 100644
index 00000000..419d2630
--- /dev/null
+++ b/vhosts/blog/nuldoc-src/ndoc/parse.ts
@@ -0,0 +1,47 @@
+import { parse as parseToml } from "std/encoding/toml.ts";
+import { Config } from "../config.ts";
+import { parseXmlString } from "../xml.ts";
+import { createNewDocumentFromRootElement, Document } from "./document.ts";
+import toHtml from "./to_html.ts";
+
+export async function parseNulDocFile(
+ filePath: string,
+ config: Config,
+): Promise {
+ try {
+ const fileContent = await Deno.readTextFile(filePath);
+ const parts = fileContent.split(/^---$/m);
+ const meta = parseMetaInfo(parts[1]);
+ const root = parseXmlString("" + parts[2]);
+ const doc = createNewDocumentFromRootElement(root, meta, filePath, config);
+ return toHtml(doc);
+ } catch (e) {
+ e.message = `${e.message} in ${filePath}`;
+ throw e;
+ }
+}
+
+function parseMetaInfo(s: string): {
+ article: {
+ title: string;
+ description: string;
+ tags: string[];
+ revisions: {
+ date: string;
+ remark: string;
+ }[];
+ };
+} {
+ const root = parseToml(s) as {
+ article: {
+ title: string;
+ description: string;
+ tags: string[];
+ revisions: {
+ date: string;
+ remark: string;
+ }[];
+ };
+ };
+ return root;
+}
diff --git a/vhosts/blog/nuldoc-src/ndoc/to_html.ts b/vhosts/blog/nuldoc-src/ndoc/to_html.ts
new file mode 100644
index 00000000..dc39919b
--- /dev/null
+++ b/vhosts/blog/nuldoc-src/ndoc/to_html.ts
@@ -0,0 +1,235 @@
+// @deno-types="../types/highlight-js.d.ts"
+import hljs from "npm:highlight.js";
+import { Document } from "./document.ts";
+import { DocBookError } from "../errors.ts";
+import {
+ addClass,
+ Element,
+ findFirstChildElement,
+ forEachChild,
+ forEachChildRecursively,
+ Node,
+ RawHTML,
+ Text,
+} from "../dom.ts";
+
+export default function toHtml(doc: Document): Document {
+ removeUnnecessaryTextNode(doc);
+ transformSectionIdAttribute(doc);
+ setSectionTitleAnchor(doc);
+ transformSectionTitleElement(doc);
+ transformCodeBlockElement(doc);
+ transformNoteElement(doc);
+ setDefaultLangAttribute(doc);
+ traverseFootnotes(doc);
+ highlightPrograms(doc);
+ return doc;
+}
+
+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 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 === "h") {
+ const currentSection = sectionStack[sectionStack.length - 1];
+ if (!currentSection) {
+ throw new DocBookError(
+ "[docbook.tohtml] element must be inside ",
+ );
+ }
+ 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 === "h") {
+ c.name = `h${sectionLevel}`;
+ }
+ };
+ forEachChild(doc.root, g);
+}
+
+function transformCodeBlockElement(doc: Document) {
+ forEachChildRecursively(doc.root, (n) => {
+ if (n.kind !== "element" || n.name !== "codeblock") {
+ return;
+ }
+
+ n.name = "pre";
+ addClass(n, "highlight");
+ const codeElement: Element = {
+ kind: "element",
+ name: "code",
+ attributes: new Map(),
+ children: n.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([["class", "admonition-label"]]),
+ children: [{
+ kind: "text",
+ content: "NOTE",
+ raw: false,
+ }],
+ };
+ const contentElement: Element = {
+ kind: "element",
+ name: "div",
+ attributes: new Map([["class", "admonition-content"]]),
+ children: n.children,
+ };
+ n.name = "div";
+ addClass(n, "admonition");
+ 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
+ // x
+ //
+ //
+ //
+ //
+ n.name = "span";
+ n.children = [];
+ });
+}
+
+function highlightPrograms(doc: Document) {
+ forEachChildRecursively(doc.root, (n) => {
+ if (n.kind !== "element" || n.name !== "pre") {
+ return;
+ }
+ const preClass = n.attributes.get("class") || "";
+ if (!preClass.includes("highlight")) {
+ return;
+ }
+ const codeElement = findFirstChildElement(n, "code");
+ if (!codeElement) {
+ return;
+ }
+ const language = n.attributes.get("language");
+ if (!language) {
+ return;
+ }
+ const sourceCodeNode = codeElement.children[0] as Text | RawHTML;
+ const sourceCode = sourceCodeNode.content;
+
+ if (!hljs.getLanguage(language)) {
+ return;
+ }
+
+ const highlighted =
+ hljs.highlight(sourceCode, { language: language }).value;
+
+ sourceCodeNode.content = highlighted;
+ sourceCodeNode.raw = true;
+ codeElement.attributes.set("class", "highlight");
+ });
+}
diff --git a/vhosts/blog/nuldoc-src/pages/post.ts b/vhosts/blog/nuldoc-src/pages/post.ts
index 24a6d5f5..ade0916a 100644
--- a/vhosts/blog/nuldoc-src/pages/post.ts
+++ b/vhosts/blog/nuldoc-src/pages/post.ts
@@ -4,13 +4,13 @@ import { globalHeader } from "../components/global_header.ts";
import { pageLayout } from "../components/page_layout.ts";
import { Config, getTagLabel } from "../config.ts";
import { el, Element, text } from "../dom.ts";
-import { Document } from "../docbook/document.ts";
+import { Document } from "../ndoc/document.ts";
import { Page } from "../page.ts";
import { Date, dateToString, Revision } from "../revision.ts";
export interface PostPage extends Page {
title: string;
- summary: string;
+ description: string;
tags: string[];
revisions: Revision[];
}
@@ -112,7 +112,7 @@ export async function generatePostPage(
const html = await pageLayout(
{
metaCopyrightYear: getPostCreatedDate(doc).year,
- metaDescription: doc.summary,
+ metaDescription: doc.description,
metaKeywords: doc.tags.map((slug) => getTagLabel(config, slug)),
metaTitle: `${doc.title} | ${config.blog.siteName}`,
requiresSyntaxHighlight: true,
@@ -124,7 +124,7 @@ export async function generatePostPage(
const cwd = Deno.cwd();
const contentDir = join(cwd, config.locations.contentDir);
const destFilePath = join(
- doc.sourceFilePath.replace(contentDir, "").replace(".xml", ""),
+ doc.sourceFilePath.replace(contentDir, "").replace(".ndoc", ""),
"index.html",
);
return {
@@ -133,7 +133,7 @@ export async function generatePostPage(
destFilePath: destFilePath,
href: destFilePath.replace("index.html", ""),
title: doc.title,
- summary: doc.summary,
+ description: doc.description,
tags: doc.tags,
revisions: doc.revisions,
};
diff --git a/vhosts/blog/nuldoc-src/pages/slide.ts b/vhosts/blog/nuldoc-src/pages/slide.ts
index a75aeb68..ad56fee3 100644
--- a/vhosts/blog/nuldoc-src/pages/slide.ts
+++ b/vhosts/blog/nuldoc-src/pages/slide.ts
@@ -122,7 +122,7 @@ export async function generateSlidePage(
const cwd = Deno.cwd();
const contentDir = join(cwd, config.locations.contentDir);
const destFilePath = join(
- slide.sourceFilePath.replace(contentDir, "").replace(".xml", ""),
+ slide.sourceFilePath.replace(contentDir, "").replace(".toml", ""),
"index.html",
);
return {
diff --git a/vhosts/blog/nuldoc-src/renderers/html.ts b/vhosts/blog/nuldoc-src/renderers/html.ts
index 3b6c6ebc..d3cd9893 100644
--- a/vhosts/blog/nuldoc-src/renderers/html.ts
+++ b/vhosts/blog/nuldoc-src/renderers/html.ts
@@ -87,6 +87,8 @@ function getDtd(name: string): Dtd {
return { type: "block" };
case "span":
return { type: "inline" };
+ case "strong":
+ return { type: "inline" };
case "sup":
return { type: "inline" };
case "table":
diff --git a/vhosts/blog/nuldoc-src/slide/parse.ts b/vhosts/blog/nuldoc-src/slide/parse.ts
index 00ff645f..45ac6388 100644
--- a/vhosts/blog/nuldoc-src/slide/parse.ts
+++ b/vhosts/blog/nuldoc-src/slide/parse.ts
@@ -1,19 +1,29 @@
+import { parse as parseToml } from "std/encoding/toml.ts";
import { Config } from "../config.ts";
-import { parseXmlFile } from "../xml.ts";
-import { SlideError, XmlParseError } from "../errors.ts";
-import { createNewSlideFromRootElement, Slide } from "./slide.ts";
+import { createNewSlideFromTomlRootObject, Slide } from "./slide.ts";
export async function parseSlideFile(
filePath: string,
config: Config,
): Promise {
try {
- const root = await parseXmlFile(filePath);
- return createNewSlideFromRootElement(root, filePath, config);
+ // TODO runtime assertion
+ const root = parseToml(await Deno.readTextFile(filePath)) as {
+ slide: {
+ title: string;
+ event: string;
+ talkType: string;
+ link: string;
+ tags: string[];
+ revisions: {
+ date: string;
+ remark: string;
+ }[];
+ };
+ };
+ return createNewSlideFromTomlRootObject(root, filePath, config);
} catch (e) {
- if (e instanceof SlideError || e instanceof XmlParseError) {
- e.message = `${e.message} in ${filePath}`;
- }
+ e.message = `${e.message} in ${filePath}`;
throw e;
}
}
diff --git a/vhosts/blog/nuldoc-src/slide/slide.ts b/vhosts/blog/nuldoc-src/slide/slide.ts
index a982d4f2..5d5f30eb 100644
--- a/vhosts/blog/nuldoc-src/slide/slide.ts
+++ b/vhosts/blog/nuldoc-src/slide/slide.ts
@@ -1,12 +1,6 @@
import { Config } from "../config.ts";
import { SlideError } from "../errors.ts";
import { Revision, stringToDate } from "../revision.ts";
-import {
- Element,
- findChildElements,
- findFirstChildElement,
- innerText,
-} from "../dom.ts";
export type Slide = {
sourceFilePath: string;
@@ -18,105 +12,102 @@ export type Slide = {
revisions: Revision[];
};
-export function createNewSlideFromRootElement(
- root: Element,
+type Toml = {
+ slide: {
+ title: string;
+ event: string;
+ talkType: string;
+ link: string;
+ tags: string[];
+ revisions: {
+ date: string;
+ remark: string;
+ }[];
+ };
+};
+
+export function createNewSlideFromTomlRootObject(
+ root: Toml,
sourceFilePath: string,
_config: Config,
): Slide {
- const slide = findFirstChildElement(root, "slide");
- if (!slide) {
+ const slide = root.slide ?? null;
+ if (root.slide === undefined) {
throw new SlideError(
- `[slide.new] element not found`,
- );
- }
- const info = findFirstChildElement(slide, "info");
- if (!info) {
- throw new SlideError(
- `[slide.new] element not found`,
+ `[slide.new] 'slide' field not found`,
);
}
- const titleElement = findFirstChildElement(info, "title");
- if (!titleElement) {
+ const title = slide.title ?? null;
+ if (!title) {
throw new SlideError(
- `[slide.new] element not found`,
+ `[slide.new] 'slide.title' field not found`,
);
}
- const title = innerText(titleElement).trim();
- const eventElement = findFirstChildElement(info, "event");
- if (!eventElement) {
+ const event = slide.event ?? null;
+ if (!event) {
throw new SlideError(
- `[slide.new] element not found`,
+ `[slide.new] 'slide.event' field not found`,
);
}
- const event = innerText(eventElement).trim();
- const talkTypeElement = findFirstChildElement(info, "talktype");
- if (!talkTypeElement) {
+ const talkType = slide.talkType ?? null;
+ if (!talkType) {
throw new SlideError(
- `[slide.new] element not found`,
+ `[slide.new] 'slide.talkType' field not found`,
);
}
- const talkType = innerText(talkTypeElement).trim();
- const slideLinkElement = findFirstChildElement(info, "link");
- if (!slideLinkElement) {
+ const link = slide.link ?? null;
+ if (!link) {
throw new SlideError(
- `[slide.new] element not found`,
+ `[slide.new] 'slide.link' field not found`,
);
}
- const slideLink = innerText(slideLinkElement).trim();
- const keywordsetElement = findFirstChildElement(info, "keywordset");
- let tags: string[];
- if (!keywordsetElement) {
- tags = [];
- } else {
- tags = findChildElements(keywordsetElement, "keyword").map((x) =>
- innerText(x).trim()
+ const tags = slide.tags ?? [];
+
+ const revisions = slide.revisions ?? null;
+ if (!revisions) {
+ throw new SlideError(
+ `[slide.new] 'slide.revisions' field not found`,
);
}
- const revhistoryElement = findFirstChildElement(info, "revhistory");
- if (!revhistoryElement) {
+ if (revisions.length === 0) {
throw new SlideError(
- `[slide.new] element not found`,
+ `[slide.new] 'slide.revisions' field not found`,
);
}
- const revisions = findChildElements(revhistoryElement, "revision").map(
- (x, i) => {
- const dateElement = findFirstChildElement(x, "date");
- if (!dateElement) {
+ const revisions_ = revisions.map(
+ (x: { date: string; remark: string }, i: number) => {
+ const date = x.date ?? null;
+ if (!date) {
throw new SlideError(
- `[slide.new] element not found`,
+ `[slide.new] 'date' field not found`,
);
}
- const revremarkElement = findFirstChildElement(x, "revremark");
- if (!revremarkElement) {
+ const remark = x.remark ?? null;
+ if (!remark) {
throw new SlideError(
- `[slide.new] element not found`,
+ `[slide.new] 'remark' field not found`,
);
}
return {
number: i + 1,
- date: stringToDate(innerText(dateElement).trim()),
- remark: innerText(revremarkElement).trim(),
+ date: stringToDate(date),
+ remark: remark,
};
},
);
- if (revisions.length === 0) {
- throw new SlideError(
- `[slide.new] element not found`,
- );
- }
return {
sourceFilePath: sourceFilePath,
title: title,
event: event,
talkType: talkType,
- slideLink: slideLink,
+ slideLink: link,
tags: tags,
- revisions: revisions,
+ revisions: revisions_,
};
}
diff --git a/vhosts/blog/nuldoc-src/xml.ts b/vhosts/blog/nuldoc-src/xml.ts
index 847b5e12..9f53ef8c 100644
--- a/vhosts/blog/nuldoc-src/xml.ts
+++ b/vhosts/blog/nuldoc-src/xml.ts
@@ -1,10 +1,6 @@
import { Element, Node, Text } from "./dom.ts";
import { XmlParseError } from "./errors.ts";
-export async function parseXmlFile(filePath: string): Promise {
- return parseXmlString(await Deno.readTextFile(filePath));
-}
-
export function parseXmlString(source: string): Element {
return parse({ source: source, index: 0 });
}
--
cgit v1.2.3-70-g09d2