aboutsummaryrefslogtreecommitdiffhomepage
path: root/nuldoc-src/commands/build.ts
diff options
context:
space:
mode:
Diffstat (limited to 'nuldoc-src/commands/build.ts')
-rw-r--r--nuldoc-src/commands/build.ts170
1 files changed, 170 insertions, 0 deletions
diff --git a/nuldoc-src/commands/build.ts b/nuldoc-src/commands/build.ts
new file mode 100644
index 0000000..734f520
--- /dev/null
+++ b/nuldoc-src/commands/build.ts
@@ -0,0 +1,170 @@
+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 { writeHtmlFile } from "../html.ts";
+import { Document } from "../docbook/document.ts";
+import convertPost from "../templates/post.ts";
+import convertPostList from "../templates/post_list.ts";
+import convertTag from "../templates/tag.ts";
+import generateAbout from "../templates/about.ts";
+
+export async function runBuildCommand(config: Config) {
+ const posts = await generatePosts(config);
+ await generateTags(posts, config);
+ await generatePostList(posts, config);
+ await generateAboutPage(config);
+ await copyStaticFiles(config);
+}
+
+async function generatePosts(config: Config) {
+ const sourceDir = join(Deno.cwd(), config.locations.contentDir, "posts");
+ const postFiles = await collectPostFiles(sourceDir);
+ const posts = await parsePosts(postFiles, config);
+ await outputPosts(posts, config);
+ return posts;
+}
+
+async function collectPostFiles(sourceDir: string): Promise<string[]> {
+ const filePaths = [];
+ const globPattern = joinGlobs([sourceDir, "**", "*.xml"]);
+ for await (const entry of expandGlob(globPattern)) {
+ filePaths.push(entry.path);
+ }
+ return filePaths;
+}
+
+async function parsePosts(
+ postFiles: string[],
+ config: Config,
+): Promise<Document[]> {
+ const posts = [];
+ for (const postFile of postFiles) {
+ posts.push(
+ await convertPost(await parseDocBookFile(postFile, config), config),
+ );
+ }
+ return posts;
+}
+
+async function outputPosts(posts: Document[], config: Config) {
+ const cwd = Deno.cwd();
+ const contentDir = join(cwd, config.locations.contentDir);
+ const destDir = join(cwd, config.locations.destDir);
+ for (const post of posts) {
+ const destFilePath = join(
+ post.sourceFilePath.replace(contentDir, destDir).replace(".xml", ""),
+ "index.html",
+ );
+ await ensureDir(dirname(destFilePath));
+ await writeHtmlFile(post, destFilePath);
+ }
+}
+
+async function generatePostList(posts: Document[], config: Config) {
+ const postList = await buildPostListDoc(posts, config);
+ await outputPostList(postList, config);
+}
+
+async function buildPostListDoc(
+ posts: Document[],
+ config: Config,
+): Promise<Document> {
+ return await convertPostList(posts, config);
+}
+
+async function outputPostList(postList: Document, config: Config) {
+ const cwd = Deno.cwd();
+ const destDir = join(cwd, config.locations.destDir);
+ const destFilePath = join(destDir, "posts", "index.html");
+ await ensureDir(dirname(destFilePath));
+ await writeHtmlFile(postList, destFilePath);
+}
+
+async function generateAboutPage(config: Config) {
+ const aboutDoc = await generateAbout(config);
+ await outputAboutPage(aboutDoc, config);
+}
+
+async function outputAboutPage(about: Document, config: Config) {
+ const cwd = Deno.cwd();
+ const destDir = join(cwd, config.locations.destDir);
+ const destFilePath = join(destDir, "about", "index.html");
+ await ensureDir(dirname(destFilePath));
+ await writeHtmlFile(about, destFilePath);
+}
+
+async function generateTags(posts: Document[], config: Config) {
+ const tagsAndPosts = collectTags(posts);
+ const tagDocs = await buildTagDocs(tagsAndPosts, config);
+ await outputTags(tagDocs, config);
+}
+
+function collectTags(posts: Document[]): [string, Document[]][] {
+ const tagsAndPosts = new Map();
+ for (const post of posts) {
+ for (const tag of post.tags) {
+ if (!tagsAndPosts.has(tag)) {
+ tagsAndPosts.set(tag, []);
+ }
+ tagsAndPosts.get(tag).push(post);
+ }
+ }
+
+ const result: [string, Document[]][] = [];
+ for (const tag of Array.from(tagsAndPosts.keys()).sort()) {
+ result.push([
+ tag,
+ tagsAndPosts.get(tag).sort((a: Document, b: Document) => {
+ const ta = a.revisions[0].date;
+ const tb = b.revisions[0].date;
+ if (ta > tb) return -1;
+ if (ta < tb) return 1;
+ return 0;
+ }),
+ ]);
+ }
+ return result;
+}
+
+async function buildTagDocs(
+ tagsAndPosts: [string, Document[]][],
+ config: Config,
+): Promise<[string, Document][]> {
+ const docs: [string, Document][] = [];
+ for (const [tag, posts] of tagsAndPosts) {
+ docs.push([tag, await buildTagDoc(tag, posts, config)]);
+ }
+ return docs;
+}
+
+async function buildTagDoc(
+ tag: string,
+ posts: Document[],
+ config: Config,
+): Promise<Document> {
+ return await convertTag(tag, posts, config);
+}
+
+async function outputTags(tagDocs: [string, Document][], config: Config) {
+ const cwd = Deno.cwd();
+ const destDir = join(cwd, config.locations.destDir);
+ for (const [tag, tagDoc] of tagDocs) {
+ const destFilePath = join(destDir, "tags", tag, "index.html");
+ await ensureDir(dirname(destFilePath));
+ await writeHtmlFile(tagDoc, destFilePath);
+ }
+}
+
+async function copyStaticFiles(config: Config) {
+ const globPattern = joinGlobs([Deno.cwd(), config.locations.staticDir, "*"]);
+ for await (const entry of expandGlob(globPattern)) {
+ const src = entry.path;
+ const dst = src.replace(
+ config.locations.staticDir,
+ config.locations.destDir,
+ );
+ await Deno.copyFile(src, dst);
+ }
+}