diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-06-27 23:39:31 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-06-27 23:39:31 +0900 |
| commit | 674fe965550444db87edc7937ff6932e1a918d9d (patch) | |
| tree | e8a80dd958d3e082485286bf5785a7992b6e6b0e /services/blog/nuldoc-src/commands/build.ts | |
| parent | fe4d1d625b53796c5f20399790e5ff8c7a7e1608 (diff) | |
| download | nsfisis.dev-674fe965550444db87edc7937ff6932e1a918d9d.tar.gz nsfisis.dev-674fe965550444db87edc7937ff6932e1a918d9d.tar.zst nsfisis.dev-674fe965550444db87edc7937ff6932e1a918d9d.zip | |
feat(meta): rename vhosts/ directory to services/
Diffstat (limited to 'services/blog/nuldoc-src/commands/build.ts')
| -rw-r--r-- | services/blog/nuldoc-src/commands/build.ts | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/services/blog/nuldoc-src/commands/build.ts b/services/blog/nuldoc-src/commands/build.ts new file mode 100644 index 00000000..3f765441 --- /dev/null +++ b/services/blog/nuldoc-src/commands/build.ts @@ -0,0 +1,260 @@ +import { dirname, join, joinGlobs } from "@std/path"; +import { ensureDir, expandGlob } from "@std/fs"; +import { generateFeedPageFromEntries } from "../generators/atom.ts"; +import { Config, getTagLabel } from "../config.ts"; +import { parseDjotFile } from "../djot/parse.ts"; +import { Page } from "../page.ts"; +import { render } from "../render.ts"; +import { dateToString } from "../revision.ts"; +import { generateAboutPage } from "../generators/about.ts"; +import { generateHomePage } from "../generators/home.ts"; +import { generateNotFoundPage } from "../generators/not_found.ts"; +import { + generatePostPage, + getPostPublishedDate, + PostPage, +} from "../generators/post.ts"; +import { generatePostListPages } from "../generators/post_list.ts"; +import { generateSlidePage, SlidePage } from "../generators/slide.ts"; +import { generateSlideListPage } from "../generators/slide_list.ts"; +import { generateTagPage, TagPage } from "../generators/tag.ts"; +import { TaggedPage } from "../generators/tagged_page.ts"; +import { generateTagListPage } from "../generators/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 buildFeedOfAllContents(posts, slides, 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, "**", "*.dj"]); + 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 parseDjotFile(postFile, config), config), + ); + } + return posts; +} + +async function buildPostListPage(posts: PostPage[], config: Config) { + // Sort posts by published date (newest first) + const sortedPosts = [...posts].sort((a, b) => { + const ta = dateToString(getPostPublishedDate(a)); + const tb = dateToString(getPostPublishedDate(b)); + if (ta > tb) return -1; + if (ta < tb) return 1; + return 0; + }); + + const postListPages = await generatePostListPages(sortedPosts, config); + for (const page of postListPages) { + await writePage(page, config); + } + + const postFeedPage = await generateFeedPageFromEntries( + "/posts/", + "posts", + `投稿一覧|${config.blog.siteName}`, + posts, + config, + ); + await writePage(postFeedPage, 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, "**", "*.toml"]); + 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), + ); + } + return slides; +} + +async function buildSlideListPage(slides: SlidePage[], config: Config) { + const slideListPage = await generateSlideListPage(slides, config); + await writePage(slideListPage, config); + const slideFeedPage = await generateFeedPageFromEntries( + slideListPage.href, + "slides", + `スライド一覧|${config.blog.siteName}`, + slides, + config, + ); + await writePage(slideFeedPage, 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 buildFeedOfAllContents( + posts: PostPage[], + slides: SlidePage[], + config: Config, +) { + const feed = await generateFeedPageFromEntries( + "/", + "all", + config.blog.siteName, + [...posts, ...slides], + config, + ); + await writePage(feed, 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); + const tagFeedPage = await generateFeedPageFromEntries( + tagPage.href, + `tag-${tag}`, + `タグ「${getTagLabel(config, tag)}」一覧|${config.blog.siteName}`, + pages, + config, + ); + await writePage(tagFeedPage, 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(getPostPublishedDate(a)); + const tb = dateToString(getPostPublishedDate(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)); +} |
