summaryrefslogtreecommitdiffhomepage
path: root/vhosts/blog/nuldoc-src/commands
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2023-09-07 22:27:48 +0900
committernsfisis <nsfisis@gmail.com>2023-09-07 22:35:53 +0900
commit994e0114d76ae19768d5c303874a968cf6369fd0 (patch)
tree5fd3f8b169eea00084b24fbae820f75273864d2a /vhosts/blog/nuldoc-src/commands
parent57f015992f678bfd7281f171fb9d71349c96a1a0 (diff)
downloadnsfisis.dev-994e0114d76ae19768d5c303874a968cf6369fd0.tar.gz
nsfisis.dev-994e0114d76ae19768d5c303874a968cf6369fd0.tar.zst
nsfisis.dev-994e0114d76ae19768d5c303874a968cf6369fd0.zip
meta: migrate to monorepo
Diffstat (limited to 'vhosts/blog/nuldoc-src/commands')
-rw-r--r--vhosts/blog/nuldoc-src/commands/build.ts208
-rw-r--r--vhosts/blog/nuldoc-src/commands/new.ts92
-rw-r--r--vhosts/blog/nuldoc-src/commands/serve.ts38
3 files changed, 338 insertions, 0 deletions
diff --git a/vhosts/blog/nuldoc-src/commands/build.ts b/vhosts/blog/nuldoc-src/commands/build.ts
new file mode 100644
index 00000000..da7e5cec
--- /dev/null
+++ b/vhosts/blog/nuldoc-src/commands/build.ts
@@ -0,0 +1,208 @@
+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 { Page } from "../page.ts";
+import { render } from "../render.ts";
+import { dateToString } from "../revision.ts";
+import { generateAboutPage } from "../pages/about.ts";
+import { generateHomePage } from "../pages/home.ts";
+import { generateNotFoundPage } from "../pages/not_found.ts";
+import {
+ generatePostPage,
+ getPostCreatedDate,
+ PostPage,
+} from "../pages/post.ts";
+import { generatePostListPage } from "../pages/post_list.ts";
+import { generateSlidePage, SlidePage } from "../pages/slide.ts";
+import { generateSlideListPage } from "../pages/slide_list.ts";
+import { generateTagPage, TagPage } from "../pages/tag.ts";
+import { TaggedPage } from "../pages/tagged_page.ts";
+import { generateTagListPage } from "../pages/tag_list.ts";
+import { parseSlideFile } from "../slide/parse.ts";
+
+export async function runBuildCommand(config: Config) {
+ const posts = await buildPostPages(config);
+ await buildPostListPage(posts, config);
+ const slides = await buildSlidePages(config);
+ await buildSlideListPage(slides, config);
+ const tags = await buildTagPages(posts, slides, config);
+ await buildTagListPage(tags, config);
+ await buildHomePage(config);
+ await buildAboutPage(slides, config);
+ await buildNotFoundPage(config);
+ await copyStaticFiles(config);
+ await copyAssetFiles(slides, config);
+}
+
+async function buildPostPages(config: Config): Promise<PostPage[]> {
+ const sourceDir = join(Deno.cwd(), config.locations.contentDir, "posts");
+ const postFiles = await collectPostFiles(sourceDir);
+ const posts = await parsePosts(postFiles, config);
+ for (const post of posts) {
+ await writePage(post, 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<PostPage[]> {
+ const posts = [];
+ for (const postFile of postFiles) {
+ posts.push(
+ await generatePostPage(await parseDocBookFile(postFile, config), config),
+ );
+ }
+ return posts;
+}
+
+async function buildPostListPage(posts: PostPage[], config: Config) {
+ const postListPage = await generatePostListPage(posts, config);
+ await writePage(postListPage, config);
+}
+
+async function buildSlidePages(config: Config): Promise<SlidePage[]> {
+ const sourceDir = join(Deno.cwd(), config.locations.contentDir, "slides");
+ const slideFiles = await collectSlideFiles(sourceDir);
+ const slides = await parseSlides(slideFiles, config);
+ for (const slide of slides) {
+ await writePage(slide, config);
+ }
+ return slides;
+}
+
+async function collectSlideFiles(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 parseSlides(
+ slideFiles: string[],
+ config: Config,
+): Promise<SlidePage[]> {
+ const slides = [];
+ for (const slideFile of slideFiles) {
+ slides.push(
+ await generateSlidePage(await parseSlideFile(slideFile, config), config),
+ );
+ }
+ return slides;
+}
+
+async function buildSlideListPage(slides: SlidePage[], config: Config) {
+ const slideListPage = await generateSlideListPage(slides, config);
+ await writePage(slideListPage, config);
+}
+
+async function buildHomePage(config: Config) {
+ const homePage = await generateHomePage(config);
+ await writePage(homePage, config);
+}
+
+async function buildAboutPage(slides: SlidePage[], config: Config) {
+ const aboutPage = await generateAboutPage(slides, config);
+ await writePage(aboutPage, config);
+}
+
+async function buildNotFoundPage(config: Config) {
+ const notFoundPage = await generateNotFoundPage(config);
+ await writePage(notFoundPage, config);
+}
+
+async function buildTagPages(
+ posts: PostPage[],
+ slides: SlidePage[],
+ config: Config,
+): Promise<TagPage[]> {
+ const tagsAndPages = collectTags([...posts, ...slides]);
+ const tags = [];
+ for (const [tag, pages] of tagsAndPages) {
+ const tagPage = await generateTagPage(tag, pages, config);
+ await writePage(tagPage, config);
+ tags.push(tagPage);
+ }
+ return tags;
+}
+
+async function buildTagListPage(tags: TagPage[], config: Config) {
+ const tagListPage = await generateTagListPage(tags, config);
+ await writePage(tagListPage, config);
+}
+
+function collectTags(taggedPages: TaggedPage[]): [string, TaggedPage[]][] {
+ const tagsAndPages = new Map();
+ for (const page of taggedPages) {
+ for (const tag of page.tags) {
+ if (!tagsAndPages.has(tag)) {
+ tagsAndPages.set(tag, []);
+ }
+ tagsAndPages.get(tag).push(page);
+ }
+ }
+
+ const result: [string, TaggedPage[]][] = [];
+ for (const tag of Array.from(tagsAndPages.keys()).sort()) {
+ result.push([
+ tag,
+ tagsAndPages.get(tag).sort((a: TaggedPage, b: TaggedPage) => {
+ const ta = dateToString(getPostCreatedDate(a));
+ const tb = dateToString(getPostCreatedDate(b));
+ if (ta > tb) return -1;
+ if (ta < tb) return 1;
+ return 0;
+ }),
+ ]);
+ }
+ return result;
+}
+
+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);
+ }
+}
+
+async function copyAssetFiles(slides: SlidePage[], config: Config) {
+ const cwd = Deno.cwd();
+ const contentDir = join(cwd, config.locations.contentDir);
+ const destDir = join(cwd, config.locations.destDir);
+
+ for (const slide of slides) {
+ const src = join(contentDir, slide.slideLink);
+ const dst = join(destDir, slide.slideLink);
+ await ensureDir(dirname(dst));
+ await Deno.copyFile(src, dst);
+ }
+}
+
+async function writePage(page: Page, config: Config) {
+ const destFilePath = join(
+ Deno.cwd(),
+ config.locations.destDir,
+ page.destFilePath,
+ );
+ await ensureDir(dirname(destFilePath));
+ await Deno.writeTextFile(destFilePath, render(page.root, page.renderer));
+}
diff --git a/vhosts/blog/nuldoc-src/commands/new.ts b/vhosts/blog/nuldoc-src/commands/new.ts
new file mode 100644
index 00000000..22329972
--- /dev/null
+++ b/vhosts/blog/nuldoc-src/commands/new.ts
@@ -0,0 +1,92 @@
+import { dirname, join } from "std/path/mod.ts";
+import { ensureDir } from "std/fs/mod.ts";
+import { Config } from "../config.ts";
+
+export async function runNewCommand(config: Config) {
+ const type = Deno.args[1];
+ if (type !== "post" && type !== "slide") {
+ console.log(`Usage: nuldoc new <type>
+
+<type> must be either post or slide.`);
+ Deno.exit(1);
+ }
+
+ const now = new Date();
+ const ymd = `${now.getFullYear()}-${
+ (now.getMonth() + 1).toString().padStart(2, "0")
+ }-${now.getDate().toString().padStart(2, "0")}`;
+
+ const destFilePath = join(
+ Deno.cwd(),
+ config.locations.contentDir,
+ getDirPath(type),
+ ymd,
+ "TODO.xml",
+ );
+
+ await ensureDir(dirname(destFilePath));
+ await Deno.writeTextFile(destFilePath, getTemplate(type, ymd));
+ console.log(
+ `New file ${
+ destFilePath.replace(Deno.cwd(), "")
+ } was successfully created.`,
+ );
+}
+
+function getDirPath(type: "post" | "slide"): string {
+ return type === "post" ? "posts" : "slides";
+}
+
+function getTemplate(type: "post" | "slide", date: string): string {
+ if (type === "post") {
+ return `<?xml version="1.0" encoding="UTF-8"?>
+<article xmlns="http://docbook.org/ns/docbook" xmlns:xl="http://www.w3.org/1999/xlink" version="5.0">
+ <info>
+ <title>TODO</title>
+ <abstract>
+ TODO
+ </abstract>
+ <keywordset>
+ <keyword>TODO</keyword>
+ </keywordset>
+ <revhistory>
+ <revision>
+ <date>${date}</date>
+ <revremark>公開</revremark>
+ </revision>
+ </revhistory>
+ </info>
+ <section xml:id="TODO">
+ <title>TODO</title>
+ <para>
+ TODO
+ </para>
+ </section>
+</article>
+`;
+ } else {
+ return `<?xml version="1.0" encoding="UTF-8"?>
+<slide>
+ <info>
+ <title>TODO</title>
+ <event>
+ TODO
+ </event>
+ <talktype>
+ TODO
+ </talktype>
+ <link>TODO</link>
+ <keywordset>
+ <keyword>TODO</keyword>
+ </keywordset>
+ <revhistory>
+ <revision>
+ <date>${date}</date>
+ <revremark>登壇</revremark>
+ </revision>
+ </revhistory>
+ </info>
+</slide>
+`;
+ }
+}
diff --git a/vhosts/blog/nuldoc-src/commands/serve.ts b/vhosts/blog/nuldoc-src/commands/serve.ts
new file mode 100644
index 00000000..aa5221df
--- /dev/null
+++ b/vhosts/blog/nuldoc-src/commands/serve.ts
@@ -0,0 +1,38 @@
+import { serveDir } from "std/http/file_server.ts";
+import { Status, STATUS_TEXT } from "std/http/http_status.ts";
+import { serve } from "std/http/server.ts";
+import { join } from "std/path/mod.ts";
+import { Config } from "../config.ts";
+
+export function runServeCommand(config: Config) {
+ const rootDir = join(Deno.cwd(), config.locations.destDir);
+ serve(async (req) => {
+ const pathname = new URL(req.url).pathname;
+ if (!pathname.endsWith("css") && !pathname.endsWith("svg")) {
+ const command = new Deno.Command(
+ join(Deno.cwd(), "nuldoc"),
+ {
+ args: ["build"],
+ },
+ );
+ await command.output();
+ console.log("rebuild");
+ }
+ const res = await serveDir(req, {
+ fsRoot: rootDir,
+ showIndex: true,
+ });
+ if (res.status !== Status.NotFound) {
+ return res;
+ }
+
+ const notFoundHtml = await Deno.readTextFile(join(rootDir, "404.html"));
+ return new Response(notFoundHtml, {
+ status: Status.NotFound,
+ statusText: STATUS_TEXT[Status.NotFound],
+ headers: {
+ "content-type": "text/html",
+ },
+ });
+ });
+}