aboutsummaryrefslogtreecommitdiffhomepage
path: root/services
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-11-27 04:02:06 +0900
committernsfisis <nsfisis@gmail.com>2025-11-27 04:02:06 +0900
commitd1014de68415df8f0a5dc3389332e086119c6198 (patch)
tree6564411eb6381eb48f86ed2b658c5440295596fa /services
parentb587ae4cfdfb991dcd9d7a1109b7530f774691dd (diff)
downloadnsfisis.dev-d1014de68415df8f0a5dc3389332e086119c6198.tar.gz
nsfisis.dev-d1014de68415df8f0a5dc3389332e086119c6198.tar.zst
nsfisis.dev-d1014de68415df8f0a5dc3389332e086119c6198.zip
refactor(nuldoc): eliminate JSX
Diffstat (limited to 'services')
-rw-r--r--services/nuldoc/deno.jsonc9
-rw-r--r--services/nuldoc/nuldoc-src/commands/build.ts6
-rw-r--r--services/nuldoc/nuldoc-src/components/AboutGlobalHeader.ts18
-rw-r--r--services/nuldoc/nuldoc-src/components/AboutGlobalHeader.tsx13
-rw-r--r--services/nuldoc/nuldoc-src/components/BlogGlobalHeader.ts34
-rw-r--r--services/nuldoc/nuldoc-src/components/BlogGlobalHeader.tsx29
-rw-r--r--services/nuldoc/nuldoc-src/components/DefaultGlobalHeader.ts18
-rw-r--r--services/nuldoc/nuldoc-src/components/DefaultGlobalHeader.tsx13
-rw-r--r--services/nuldoc/nuldoc-src/components/GlobalFooter.ts10
-rw-r--r--services/nuldoc/nuldoc-src/components/GlobalFooter.tsx9
-rw-r--r--services/nuldoc/nuldoc-src/components/PageLayout.ts76
-rw-r--r--services/nuldoc/nuldoc-src/components/PageLayout.tsx65
-rw-r--r--services/nuldoc/nuldoc-src/components/Pagination.ts (renamed from services/nuldoc/nuldoc-src/components/Pagination.tsx)82
-rw-r--r--services/nuldoc/nuldoc-src/components/PostPageEntry.ts49
-rw-r--r--services/nuldoc/nuldoc-src/components/PostPageEntry.tsx46
-rw-r--r--services/nuldoc/nuldoc-src/components/SlidePageEntry.ts49
-rw-r--r--services/nuldoc/nuldoc-src/components/SlidePageEntry.tsx46
-rw-r--r--services/nuldoc/nuldoc-src/components/SlidesGlobalHeader.ts33
-rw-r--r--services/nuldoc/nuldoc-src/components/SlidesGlobalHeader.tsx26
-rw-r--r--services/nuldoc/nuldoc-src/components/StaticScript.ts (renamed from services/nuldoc/nuldoc-src/components/StaticScript.tsx)10
-rw-r--r--services/nuldoc/nuldoc-src/components/StaticStylesheet.ts (renamed from services/nuldoc/nuldoc-src/components/StaticStylesheet.tsx)5
-rw-r--r--services/nuldoc/nuldoc-src/components/TableOfContents.ts34
-rw-r--r--services/nuldoc/nuldoc-src/components/TableOfContents.tsx33
-rw-r--r--services/nuldoc/nuldoc-src/components/TagList.ts17
-rw-r--r--services/nuldoc/nuldoc-src/components/TagList.tsx18
-rw-r--r--services/nuldoc/nuldoc-src/dom.ts23
-rw-r--r--services/nuldoc/nuldoc-src/generators/about.ts7
-rw-r--r--services/nuldoc/nuldoc-src/generators/atom.ts9
-rw-r--r--services/nuldoc/nuldoc-src/generators/home.ts7
-rw-r--r--services/nuldoc/nuldoc-src/generators/not_found.ts7
-rw-r--r--services/nuldoc/nuldoc-src/generators/post.ts7
-rw-r--r--services/nuldoc/nuldoc-src/generators/post_list.ts12
-rw-r--r--services/nuldoc/nuldoc-src/generators/slide.ts7
-rw-r--r--services/nuldoc/nuldoc-src/generators/slide_list.ts7
-rw-r--r--services/nuldoc/nuldoc-src/generators/tag.ts7
-rw-r--r--services/nuldoc/nuldoc-src/generators/tag_list.ts7
-rw-r--r--services/nuldoc/nuldoc-src/jsx/jsx-runtime.ts27
-rw-r--r--services/nuldoc/nuldoc-src/jsx/render.ts41
-rw-r--r--services/nuldoc/nuldoc-src/jsx/types.d.ts84
-rw-r--r--services/nuldoc/nuldoc-src/pages/AboutPage.ts160
-rw-r--r--services/nuldoc/nuldoc-src/pages/AboutPage.tsx113
-rw-r--r--services/nuldoc/nuldoc-src/pages/AtomPage.ts27
-rw-r--r--services/nuldoc/nuldoc-src/pages/AtomPage.tsx26
-rw-r--r--services/nuldoc/nuldoc-src/pages/HomePage.ts78
-rw-r--r--services/nuldoc/nuldoc-src/pages/HomePage.tsx54
-rw-r--r--services/nuldoc/nuldoc-src/pages/NotFoundPage.ts40
-rw-r--r--services/nuldoc/nuldoc-src/pages/NotFoundPage.tsx38
-rw-r--r--services/nuldoc/nuldoc-src/pages/PostListPage.ts49
-rw-r--r--services/nuldoc/nuldoc-src/pages/PostListPage.tsx58
-rw-r--r--services/nuldoc/nuldoc-src/pages/PostPage.ts90
-rw-r--r--services/nuldoc/nuldoc-src/pages/PostPage.tsx71
-rw-r--r--services/nuldoc/nuldoc-src/pages/SlideListPage.ts45
-rw-r--r--services/nuldoc/nuldoc-src/pages/SlideListPage.tsx45
-rw-r--r--services/nuldoc/nuldoc-src/pages/SlidePage.ts98
-rw-r--r--services/nuldoc/nuldoc-src/pages/SlidePage.tsx73
-rw-r--r--services/nuldoc/nuldoc-src/pages/TagListPage.ts70
-rw-r--r--services/nuldoc/nuldoc-src/pages/TagListPage.tsx62
-rw-r--r--services/nuldoc/nuldoc-src/pages/TagPage.ts50
-rw-r--r--services/nuldoc/nuldoc-src/pages/TagPage.tsx50
59 files changed, 1139 insertions, 1158 deletions
diff --git a/services/nuldoc/deno.jsonc b/services/nuldoc/deno.jsonc
index 5ae2eef..aff8a05 100644
--- a/services/nuldoc/deno.jsonc
+++ b/services/nuldoc/deno.jsonc
@@ -9,14 +9,7 @@
"@std/toml": "jsr:@std/toml@^1.0.8",
"checksum/": "https://deno.land/x/checksum@1.4.0/",
"shiki": "npm:shiki@^3.7.0",
- "zod/": "https://deno.land/x/zod@v3.24.2/",
- "myjsx/jsx-runtime": "./nuldoc-src/jsx/jsx-runtime.ts",
- "myjsx-types/jsx-runtime": "./nuldoc-src/jsx/types.d.ts",
- },
- "compilerOptions": {
- "jsx": "react-jsx",
- "jsxImportSource": "myjsx",
- "jsxImportSourceTypes": "myjsx-types",
+ "zod/": "https://deno.land/x/zod@v3.24.2/"
},
"permissions": {
"default": {
diff --git a/services/nuldoc/nuldoc-src/commands/build.ts b/services/nuldoc/nuldoc-src/commands/build.ts
index 3022ee5..5efff55 100644
--- a/services/nuldoc/nuldoc-src/commands/build.ts
+++ b/services/nuldoc/nuldoc-src/commands/build.ts
@@ -90,7 +90,7 @@ async function buildPostListPage(posts: PostPage[], config: Config) {
await writePage(page, config);
}
- const postFeedPage = await generateFeedPageFromEntries(
+ const postFeedPage = generateFeedPageFromEntries(
"/posts/",
"posts",
`投稿一覧|${config.sites.blog.siteName}`,
@@ -136,7 +136,7 @@ async function parseSlides(
async function buildSlideListPage(slides: SlidePage[], config: Config) {
const slideListPage = await generateSlideListPage(slides, config);
await writePage(slideListPage, config);
- const slideFeedPage = await generateFeedPageFromEntries(
+ const slideFeedPage = generateFeedPageFromEntries(
slideListPage.href,
"slides",
`スライド一覧|${config.sites.slides.siteName}`,
@@ -175,7 +175,7 @@ async function buildTagPages(
for (const [tag, pages] of tagsAndPages) {
const tagPage = await generateTagPage(tag, pages, site, config);
await writePage(tagPage, config);
- const tagFeedPage = await generateFeedPageFromEntries(
+ const tagFeedPage = generateFeedPageFromEntries(
tagPage.href,
`tag-${tag}`,
`タグ「${getTagLabel(config, tag)}」一覧|${config.sites[site].siteName}`,
diff --git a/services/nuldoc/nuldoc-src/components/AboutGlobalHeader.ts b/services/nuldoc/nuldoc-src/components/AboutGlobalHeader.ts
new file mode 100644
index 0000000..b6ef84c
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/components/AboutGlobalHeader.ts
@@ -0,0 +1,18 @@
+import { Config } from "../config.ts";
+import { elem, Element } from "../dom.ts";
+
+export default function GlobalHeader({ config }: { config: Config }): Element {
+ return elem(
+ "header",
+ { class: "header" },
+ elem(
+ "div",
+ { class: "site-logo" },
+ elem(
+ "a",
+ { href: `https://${config.sites.default.fqdn}/` },
+ "nsfisis.dev",
+ ),
+ ),
+ );
+}
diff --git a/services/nuldoc/nuldoc-src/components/AboutGlobalHeader.tsx b/services/nuldoc/nuldoc-src/components/AboutGlobalHeader.tsx
deleted file mode 100644
index 2df7296..0000000
--- a/services/nuldoc/nuldoc-src/components/AboutGlobalHeader.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Config } from "../config.ts";
-
-export default function GlobalHeader({ config }: { config: Config }) {
- return (
- <header className="header">
- <div className="site-logo">
- <a href={`https://${config.sites.default.fqdn}/`}>
- nsfisis.dev
- </a>
- </div>
- </header>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/components/BlogGlobalHeader.ts b/services/nuldoc/nuldoc-src/components/BlogGlobalHeader.ts
new file mode 100644
index 0000000..034c2ab
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/components/BlogGlobalHeader.ts
@@ -0,0 +1,34 @@
+import { Config } from "../config.ts";
+import { elem, Element } from "../dom.ts";
+
+export default function GlobalHeader({ config }: { config: Config }): Element {
+ return elem(
+ "header",
+ { class: "header" },
+ elem(
+ "div",
+ { class: "site-logo" },
+ elem(
+ "a",
+ { href: `https://${config.sites.default.fqdn}/` },
+ "nsfisis.dev",
+ ),
+ ),
+ elem("div", { class: "site-name" }, config.sites.blog.siteName),
+ elem(
+ "nav",
+ { class: "nav" },
+ elem(
+ "ul",
+ {},
+ elem(
+ "li",
+ {},
+ elem("a", { href: `https://${config.sites.about.fqdn}/` }, "About"),
+ ),
+ elem("li", {}, elem("a", { href: "/posts/" }, "Posts")),
+ elem("li", {}, elem("a", { href: "/tags/" }, "Tags")),
+ ),
+ ),
+ );
+}
diff --git a/services/nuldoc/nuldoc-src/components/BlogGlobalHeader.tsx b/services/nuldoc/nuldoc-src/components/BlogGlobalHeader.tsx
deleted file mode 100644
index 1f7fe6e..0000000
--- a/services/nuldoc/nuldoc-src/components/BlogGlobalHeader.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { Config } from "../config.ts";
-
-export default function GlobalHeader({ config }: { config: Config }) {
- return (
- <header className="header">
- <div className="site-logo">
- <a href={`https://${config.sites.default.fqdn}/`}>
- nsfisis.dev
- </a>
- </div>
- <div className="site-name">
- {config.sites.blog.siteName}
- </div>
- <nav className="nav">
- <ul>
- <li>
- <a href={`https://${config.sites.about.fqdn}/`}>About</a>
- </li>
- <li>
- <a href="/posts/">Posts</a>
- </li>
- <li>
- <a href="/tags/">Tags</a>
- </li>
- </ul>
- </nav>
- </header>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/components/DefaultGlobalHeader.ts b/services/nuldoc/nuldoc-src/components/DefaultGlobalHeader.ts
new file mode 100644
index 0000000..b6ef84c
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/components/DefaultGlobalHeader.ts
@@ -0,0 +1,18 @@
+import { Config } from "../config.ts";
+import { elem, Element } from "../dom.ts";
+
+export default function GlobalHeader({ config }: { config: Config }): Element {
+ return elem(
+ "header",
+ { class: "header" },
+ elem(
+ "div",
+ { class: "site-logo" },
+ elem(
+ "a",
+ { href: `https://${config.sites.default.fqdn}/` },
+ "nsfisis.dev",
+ ),
+ ),
+ );
+}
diff --git a/services/nuldoc/nuldoc-src/components/DefaultGlobalHeader.tsx b/services/nuldoc/nuldoc-src/components/DefaultGlobalHeader.tsx
deleted file mode 100644
index 2df7296..0000000
--- a/services/nuldoc/nuldoc-src/components/DefaultGlobalHeader.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { Config } from "../config.ts";
-
-export default function GlobalHeader({ config }: { config: Config }) {
- return (
- <header className="header">
- <div className="site-logo">
- <a href={`https://${config.sites.default.fqdn}/`}>
- nsfisis.dev
- </a>
- </div>
- </header>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/components/GlobalFooter.ts b/services/nuldoc/nuldoc-src/components/GlobalFooter.ts
new file mode 100644
index 0000000..835d73f
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/components/GlobalFooter.ts
@@ -0,0 +1,10 @@
+import { Config } from "../config.ts";
+import { elem, Element } from "../dom.ts";
+
+export default function GlobalFooter({ config }: { config: Config }): Element {
+ return elem(
+ "footer",
+ { class: "footer" },
+ `&copy; ${config.site.copyrightYear} ${config.site.author}`,
+ );
+}
diff --git a/services/nuldoc/nuldoc-src/components/GlobalFooter.tsx b/services/nuldoc/nuldoc-src/components/GlobalFooter.tsx
deleted file mode 100644
index 9374aa7..0000000
--- a/services/nuldoc/nuldoc-src/components/GlobalFooter.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Config } from "../config.ts";
-
-export default function GlobalFooter({ config }: { config: Config }) {
- return (
- <footer className="footer">
- {`&copy; ${config.site.copyrightYear} ${config.site.author}`}
- </footer>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/components/PageLayout.ts b/services/nuldoc/nuldoc-src/components/PageLayout.ts
new file mode 100644
index 0000000..19a8e86
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/components/PageLayout.ts
@@ -0,0 +1,76 @@
+import { Config } from "../config.ts";
+import { elem, Element, Node } from "../dom.ts";
+import StaticStylesheet from "./StaticStylesheet.ts";
+
+type Props = {
+ metaCopyrightYear: number;
+ metaDescription: string;
+ metaKeywords?: string[];
+ metaTitle: string;
+ metaAtomFeedHref?: string;
+ requiresSyntaxHighlight?: boolean;
+ site: "default" | "about" | "blog" | "slides";
+ config: Config;
+ children: Node;
+};
+
+export default async function PageLayout(
+ {
+ metaCopyrightYear,
+ metaDescription,
+ metaKeywords,
+ metaTitle,
+ metaAtomFeedHref,
+ requiresSyntaxHighlight: _,
+ site,
+ config,
+ children,
+ }: Props,
+): Promise<Element> {
+ return elem(
+ "html",
+ { lang: "ja-JP" },
+ elem(
+ "head",
+ {},
+ elem("meta", { charset: "UTF-8" }),
+ elem("meta", {
+ name: "viewport",
+ content: "width=device-width, initial-scale=1.0",
+ }),
+ elem("meta", { name: "author", content: config.site.author }),
+ elem("meta", {
+ name: "copyright",
+ content: `&copy; ${metaCopyrightYear} ${config.site.author}`,
+ }),
+ elem("meta", { name: "description", content: metaDescription }),
+ metaKeywords && metaKeywords.length !== 0
+ ? elem("meta", { name: "keywords", content: metaKeywords.join(",") })
+ : null,
+ elem("meta", { property: "og:type", content: "article" }),
+ elem("meta", { property: "og:title", content: metaTitle }),
+ elem("meta", { property: "og:description", content: metaDescription }),
+ elem("meta", {
+ property: "og:site_name",
+ content: config.sites[site].siteName,
+ }),
+ elem("meta", { property: "og:locale", content: "ja_JP" }),
+ elem("meta", { name: "Hatena::Bookmark", content: "nocomment" }),
+ metaAtomFeedHref
+ ? elem("link", {
+ rel: "alternate",
+ href: metaAtomFeedHref,
+ type: "application/atom+xml",
+ })
+ : null,
+ elem("link", {
+ rel: "icon",
+ href: "/favicon.svg",
+ type: "image/svg+xml",
+ }),
+ elem("title", {}, metaTitle),
+ await StaticStylesheet({ fileName: "/style.css", config }),
+ ),
+ children,
+ );
+}
diff --git a/services/nuldoc/nuldoc-src/components/PageLayout.tsx b/services/nuldoc/nuldoc-src/components/PageLayout.tsx
deleted file mode 100644
index b32f229..0000000
--- a/services/nuldoc/nuldoc-src/components/PageLayout.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import { Config } from "../config.ts";
-import { JSXNode } from "myjsx/jsx-runtime";
-import StaticStylesheet from "./StaticStylesheet.tsx";
-
-type Props = {
- metaCopyrightYear: number;
- metaDescription: string;
- metaKeywords?: string[];
- metaTitle: string;
- metaAtomFeedHref?: string;
- requiresSyntaxHighlight?: boolean;
- site: "default" | "about" | "blog" | "slides";
- config: Config;
- children: JSXNode;
-};
-
-export default function PageLayout(
- {
- metaCopyrightYear,
- metaDescription,
- metaKeywords,
- metaTitle,
- metaAtomFeedHref,
- requiresSyntaxHighlight: _,
- site,
- config,
- children,
- }: Props,
-) {
- return (
- <html lang="ja-JP">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <meta name="author" content={config.site.author} />
- <meta
- name="copyright"
- content={`&copy; ${metaCopyrightYear} ${config.site.author}`}
- />
- <meta name="description" content={metaDescription} />
- {metaKeywords && metaKeywords.length !== 0 &&
- <meta name="keywords" content={metaKeywords.join(",")} />}
- <meta property="og:type" content="article" />
- <meta property="og:title" content={metaTitle} />
- <meta property="og:description" content={metaDescription} />
- <meta property="og:site_name" content={config.sites[site].siteName} />
- <meta property="og:locale" content="ja_JP" />
- {/* https://b.hatena.ne.jp/help/entry/nocomment */}
- <meta name="Hatena::Bookmark" content="nocomment" />
- {metaAtomFeedHref &&
- (
- <link
- rel="alternate"
- href={metaAtomFeedHref}
- type="application/atom+xml"
- />
- )}
- <link rel="icon" href="/favicon.svg" type="image/svg+xml" />
- <title>{metaTitle}</title>
- <StaticStylesheet fileName="/style.css" config={config} />
- </head>
- {children}
- </html>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/components/Pagination.tsx b/services/nuldoc/nuldoc-src/components/Pagination.ts
index 84752c5..62e796b 100644
--- a/services/nuldoc/nuldoc-src/components/Pagination.tsx
+++ b/services/nuldoc/nuldoc-src/components/Pagination.ts
@@ -1,3 +1,5 @@
+import { elem, Element } from "../dom.ts";
+
type Props = {
currentPage: number;
totalPages: number;
@@ -6,55 +8,47 @@ type Props = {
export default function Pagination(
{ currentPage, totalPages, basePath }: Props,
-) {
+): Element {
if (totalPages <= 1) {
- return <div></div>;
+ return elem("div", {});
}
const pages = generatePageNumbers(currentPage, totalPages);
- return (
- <nav className="pagination">
- <div className="pagination-prev">
- {currentPage > 1
- ? (
- <a href={pageUrlAt(basePath, currentPage - 1)}>
- 前へ
- </a>
- )
- : null}
- </div>
- {pages.map((page) => {
- if (page === "...") {
- return (
- <div className="pagination-elipsis">
- …
- </div>
- );
- } else if (page === currentPage) {
- return (
- <div className="pagination-page pagination-page-current">
- <span>{String(page)}</span>
- </div>
- );
- } else {
- return (
- <div className="pagination-page">
- <a href={pageUrlAt(basePath, page)}>{String(page)}</a>
- </div>
- );
- }
- })}
- <div className="pagination-next">
- {currentPage < totalPages
- ? (
- <a href={pageUrlAt(basePath, currentPage + 1)}>
- 次へ
- </a>
- )
- : null}
- </div>
- </nav>
+ return elem(
+ "nav",
+ { class: "pagination" },
+ elem(
+ "div",
+ { class: "pagination-prev" },
+ currentPage > 1
+ ? elem("a", { href: pageUrlAt(basePath, currentPage - 1) }, "前へ")
+ : null,
+ ),
+ ...pages.map((page) => {
+ if (page === "...") {
+ return elem("div", { class: "pagination-elipsis" }, "…");
+ } else if (page === currentPage) {
+ return elem(
+ "div",
+ { class: "pagination-page pagination-page-current" },
+ elem("span", {}, String(page)),
+ );
+ } else {
+ return elem(
+ "div",
+ { class: "pagination-page" },
+ elem("a", { href: pageUrlAt(basePath, page) }, String(page)),
+ );
+ }
+ }),
+ elem(
+ "div",
+ { class: "pagination-next" },
+ currentPage < totalPages
+ ? elem("a", { href: pageUrlAt(basePath, currentPage + 1) }, "次へ")
+ : null,
+ ),
);
}
diff --git a/services/nuldoc/nuldoc-src/components/PostPageEntry.ts b/services/nuldoc/nuldoc-src/components/PostPageEntry.ts
new file mode 100644
index 0000000..75ad11c
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/components/PostPageEntry.ts
@@ -0,0 +1,49 @@
+import {
+ getPostPublishedDate,
+ getPostUpdatedDate,
+ postHasAnyUpdates,
+ PostPage,
+} from "../generators/post.ts";
+import { dateToString } from "../revision.ts";
+import { Config } from "../config.ts";
+import { elem, Element } from "../dom.ts";
+import TagList from "./TagList.ts";
+
+type Props = { post: PostPage; config: Config };
+
+export default function PostPageEntry({ post, config }: Props): Element {
+ return elem(
+ "article",
+ { class: "post-entry" },
+ elem(
+ "a",
+ { href: post.href },
+ elem("header", { class: "entry-header" }, elem("h2", {}, post.title)),
+ elem(
+ "section",
+ { class: "entry-content" },
+ elem("p", {}, post.description),
+ ),
+ elem(
+ "footer",
+ { class: "entry-footer" },
+ elem(
+ "time",
+ { datetime: dateToString(getPostPublishedDate(post)) },
+ dateToString(getPostPublishedDate(post)),
+ ),
+ " 投稿",
+ postHasAnyUpdates(post) ? "、" : null,
+ postHasAnyUpdates(post)
+ ? elem(
+ "time",
+ { datetime: dateToString(getPostUpdatedDate(post)) },
+ dateToString(getPostUpdatedDate(post)),
+ )
+ : null,
+ postHasAnyUpdates(post) ? " 更新" : null,
+ post.tags.length !== 0 ? TagList({ tags: post.tags, config }) : null,
+ ),
+ ),
+ );
+}
diff --git a/services/nuldoc/nuldoc-src/components/PostPageEntry.tsx b/services/nuldoc/nuldoc-src/components/PostPageEntry.tsx
deleted file mode 100644
index 23ca88a..0000000
--- a/services/nuldoc/nuldoc-src/components/PostPageEntry.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import {
- getPostPublishedDate,
- getPostUpdatedDate,
- postHasAnyUpdates,
- PostPage,
-} from "../generators/post.ts";
-import { dateToString } from "../revision.ts";
-import { Config } from "../config.ts";
-import TagList from "./TagList.tsx";
-
-type Props = { post: PostPage; config: Config };
-
-export default function PostPageEntry({ post, config }: Props) {
- return (
- <article className="post-entry">
- <a href={post.href}>
- <header className="entry-header">
- <h2>{post.title}</h2>
- </header>
- <section className="entry-content">
- <p>{post.description}</p>
- </section>
- <footer className="entry-footer">
- <time datetime={dateToString(getPostPublishedDate(post))}>
- {dateToString(getPostPublishedDate(post))}
- </time>
- {" 投稿"}
- {
- // TODO(jsx): support Fragment and merge them.
- postHasAnyUpdates(post) && "、"
- }
- {postHasAnyUpdates(post) &&
- (
- <time datetime={dateToString(getPostUpdatedDate(post))}>
- {dateToString(getPostUpdatedDate(post))}
- </time>
- )}
- {postHasAnyUpdates(post) && " 更新"}
- {post.tags.length !== 0 && (
- <TagList tags={post.tags} config={config} />
- )}
- </footer>
- </a>
- </article>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/components/SlidePageEntry.ts b/services/nuldoc/nuldoc-src/components/SlidePageEntry.ts
new file mode 100644
index 0000000..1dc5f1a
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/components/SlidePageEntry.ts
@@ -0,0 +1,49 @@
+import {
+ getPostPublishedDate,
+ getPostUpdatedDate,
+ postHasAnyUpdates,
+} from "../generators/post.ts";
+import { SlidePage } from "../generators/slide.ts";
+import { dateToString } from "../revision.ts";
+import { Config } from "../config.ts";
+import { elem, Element } from "../dom.ts";
+import TagList from "./TagList.ts";
+
+type Props = { slide: SlidePage; config: Config };
+
+export default function SlidePageEntry({ slide, config }: Props): Element {
+ return elem(
+ "article",
+ { class: "post-entry" },
+ elem(
+ "a",
+ { href: slide.href },
+ elem(
+ "header",
+ { class: "entry-header" },
+ elem("h2", {}, slide.description),
+ ),
+ elem("section", { class: "entry-content" }, elem("p", {}, slide.title)),
+ elem(
+ "footer",
+ { class: "entry-footer" },
+ elem(
+ "time",
+ { datetime: dateToString(getPostPublishedDate(slide)) },
+ dateToString(getPostPublishedDate(slide)),
+ ),
+ " 登壇",
+ postHasAnyUpdates(slide) ? "、" : null,
+ postHasAnyUpdates(slide)
+ ? elem(
+ "time",
+ { datetime: dateToString(getPostUpdatedDate(slide)) },
+ dateToString(getPostUpdatedDate(slide)),
+ )
+ : null,
+ postHasAnyUpdates(slide) ? " 更新" : null,
+ slide.tags.length !== 0 ? TagList({ tags: slide.tags, config }) : null,
+ ),
+ ),
+ );
+}
diff --git a/services/nuldoc/nuldoc-src/components/SlidePageEntry.tsx b/services/nuldoc/nuldoc-src/components/SlidePageEntry.tsx
deleted file mode 100644
index 2401765..0000000
--- a/services/nuldoc/nuldoc-src/components/SlidePageEntry.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import {
- getPostPublishedDate,
- getPostUpdatedDate,
- postHasAnyUpdates,
-} from "../generators/post.ts";
-import { SlidePage } from "../generators/slide.ts";
-import { dateToString } from "../revision.ts";
-import { Config } from "../config.ts";
-import TagList from "./TagList.tsx";
-
-type Props = { slide: SlidePage; config: Config };
-
-export default function SlidePageEntry({ slide, config }: Props) {
- return (
- <article className="post-entry">
- <a href={slide.href}>
- <header className="entry-header">
- <h2>{slide.description}</h2>
- </header>
- <section className="entry-content">
- <p>{slide.title}</p>
- </section>
- <footer className="entry-footer">
- <time datetime={dateToString(getPostPublishedDate(slide))}>
- {dateToString(getPostPublishedDate(slide))}
- </time>
- {" 登壇"}
- {
- // TODO(jsx): support Fragment and merge them.
- postHasAnyUpdates(slide) && "、"
- }
- {postHasAnyUpdates(slide) &&
- (
- <time datetime={dateToString(getPostUpdatedDate(slide))}>
- {dateToString(getPostUpdatedDate(slide))}
- </time>
- )}
- {postHasAnyUpdates(slide) && " 更新"}
- {slide.tags.length !== 0 && (
- <TagList tags={slide.tags} config={config} />
- )}
- </footer>
- </a>
- </article>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/components/SlidesGlobalHeader.ts b/services/nuldoc/nuldoc-src/components/SlidesGlobalHeader.ts
new file mode 100644
index 0000000..902e12f
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/components/SlidesGlobalHeader.ts
@@ -0,0 +1,33 @@
+import { Config } from "../config.ts";
+import { elem, Element } from "../dom.ts";
+
+export default function GlobalHeader({ config }: { config: Config }): Element {
+ return elem(
+ "header",
+ { class: "header" },
+ elem(
+ "div",
+ { class: "site-logo" },
+ elem(
+ "a",
+ { href: `https://${config.sites.default.fqdn}/` },
+ "nsfisis.dev",
+ ),
+ ),
+ elem(
+ "nav",
+ { class: "nav" },
+ elem(
+ "ul",
+ {},
+ elem(
+ "li",
+ {},
+ elem("a", { href: `https://${config.sites.about.fqdn}/` }, "About"),
+ ),
+ elem("li", {}, elem("a", { href: "/slides/" }, "Slides")),
+ elem("li", {}, elem("a", { href: "/tags/" }, "Tags")),
+ ),
+ ),
+ );
+}
diff --git a/services/nuldoc/nuldoc-src/components/SlidesGlobalHeader.tsx b/services/nuldoc/nuldoc-src/components/SlidesGlobalHeader.tsx
deleted file mode 100644
index 4d93240..0000000
--- a/services/nuldoc/nuldoc-src/components/SlidesGlobalHeader.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Config } from "../config.ts";
-
-export default function GlobalHeader({ config }: { config: Config }) {
- return (
- <header className="header">
- <div className="site-logo">
- <a href={`https://${config.sites.default.fqdn}/`}>
- nsfisis.dev
- </a>
- </div>
- <nav className="nav">
- <ul>
- <li>
- <a href={`https://${config.sites.about.fqdn}/`}>About</a>
- </li>
- <li>
- <a href="/slides/">Slides</a>
- </li>
- <li>
- <a href="/tags/">Tags</a>
- </li>
- </ul>
- </nav>
- </header>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/components/StaticScript.tsx b/services/nuldoc/nuldoc-src/components/StaticScript.ts
index 0e3ab19..7df40fd 100644
--- a/services/nuldoc/nuldoc-src/components/StaticScript.tsx
+++ b/services/nuldoc/nuldoc-src/components/StaticScript.ts
@@ -1,5 +1,6 @@
import { join } from "@std/path";
import { Config } from "../config.ts";
+import { elem, Element } from "../dom.ts";
import { calculateFileHash } from "./utils.ts";
export default async function StaticScript(
@@ -9,10 +10,11 @@ export default async function StaticScript(
defer?: "true";
config: Config;
},
-) {
+): Promise<Element> {
const filePath = join(Deno.cwd(), config.locations.staticDir, fileName);
const hash = await calculateFileHash(filePath);
- return (
- <script src={`${fileName}?h=${hash}`} type={type} defer={defer}></script>
- );
+ const attrs: Record<string, string> = { src: `${fileName}?h=${hash}` };
+ if (type) attrs.type = type;
+ if (defer) attrs.defer = defer;
+ return elem("script", attrs);
}
diff --git a/services/nuldoc/nuldoc-src/components/StaticStylesheet.tsx b/services/nuldoc/nuldoc-src/components/StaticStylesheet.ts
index 52b695e..43802d2 100644
--- a/services/nuldoc/nuldoc-src/components/StaticStylesheet.tsx
+++ b/services/nuldoc/nuldoc-src/components/StaticStylesheet.ts
@@ -1,11 +1,12 @@
import { join } from "@std/path";
import { Config } from "../config.ts";
+import { elem, Element } from "../dom.ts";
import { calculateFileHash } from "./utils.ts";
export default async function StaticStylesheet(
{ fileName, config }: { fileName: string; config: Config },
-) {
+): Promise<Element> {
const filePath = join(Deno.cwd(), config.locations.staticDir, fileName);
const hash = await calculateFileHash(filePath);
- return <link rel="stylesheet" href={`${fileName}?h=${hash}`} />;
+ return elem("link", { rel: "stylesheet", href: `${fileName}?h=${hash}` });
}
diff --git a/services/nuldoc/nuldoc-src/components/TableOfContents.ts b/services/nuldoc/nuldoc-src/components/TableOfContents.ts
new file mode 100644
index 0000000..ac4205a
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/components/TableOfContents.ts
@@ -0,0 +1,34 @@
+import { TocEntry, TocRoot } from "../djot/document.ts";
+import { elem, Element } from "../dom.ts";
+
+type Props = {
+ toc: TocRoot;
+};
+
+export default function TableOfContents({ toc }: Props): Element {
+ return elem(
+ "nav",
+ { class: "toc" },
+ elem("h2", {}, "目次"),
+ elem(
+ "ul",
+ {},
+ ...toc.entries.map((entry) => TocEntryComponent({ entry })),
+ ),
+ );
+}
+
+function TocEntryComponent({ entry }: { entry: TocEntry }): Element {
+ return elem(
+ "li",
+ {},
+ elem("a", { href: `#${entry.id}` }, entry.text),
+ entry.children.length > 0
+ ? elem(
+ "ul",
+ {},
+ ...entry.children.map((child) => TocEntryComponent({ entry: child })),
+ )
+ : null,
+ );
+}
diff --git a/services/nuldoc/nuldoc-src/components/TableOfContents.tsx b/services/nuldoc/nuldoc-src/components/TableOfContents.tsx
deleted file mode 100644
index 29907d0..0000000
--- a/services/nuldoc/nuldoc-src/components/TableOfContents.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { TocEntry, TocRoot } from "../djot/document.ts";
-
-type Props = {
- toc: TocRoot;
-};
-
-export default function TableOfContents({ toc }: Props) {
- return (
- <nav className="toc">
- <h2>目次</h2>
- <ul>
- {toc.entries.map((entry, index) => (
- <TocEntryComponent key={String(index)} entry={entry} />
- ))}
- </ul>
- </nav>
- );
-}
-
-function TocEntryComponent({ entry }: { entry: TocEntry }) {
- return (
- <li>
- <a href={`#${entry.id}`}>{entry.text}</a>
- {entry.children.length > 0 && (
- <ul>
- {entry.children.map((child, index) => (
- <TocEntryComponent key={String(index)} entry={child} />
- ))}
- </ul>
- )}
- </li>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/components/TagList.ts b/services/nuldoc/nuldoc-src/components/TagList.ts
new file mode 100644
index 0000000..540abe6
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/components/TagList.ts
@@ -0,0 +1,17 @@
+import { Config, getTagLabel } from "../config.ts";
+import { elem, Element, text } from "../dom.ts";
+
+type Props = {
+ tags: string[];
+ config: Config;
+};
+
+export default function TagList({ tags, config }: Props): Element {
+ return elem(
+ "ul",
+ { class: "entry-tags" },
+ ...tags.map((slug) =>
+ elem("li", { class: "tag" }, text(getTagLabel(config, slug)))
+ ),
+ );
+}
diff --git a/services/nuldoc/nuldoc-src/components/TagList.tsx b/services/nuldoc/nuldoc-src/components/TagList.tsx
deleted file mode 100644
index 86ee70b..0000000
--- a/services/nuldoc/nuldoc-src/components/TagList.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Config, getTagLabel } from "../config.ts";
-
-type Props = {
- tags: string[];
- config: Config;
-};
-
-export default function TagList({ tags, config }: Props) {
- return (
- <ul className="entry-tags">
- {tags.map((slug) => (
- <li className="tag" key={slug}>
- {getTagLabel(config, slug)}
- </li>
- ))}
- </ul>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/dom.ts b/services/nuldoc/nuldoc-src/dom.ts
index abe7ff8..be503a3 100644
--- a/services/nuldoc/nuldoc-src/dom.ts
+++ b/services/nuldoc/nuldoc-src/dom.ts
@@ -17,6 +17,25 @@ export type Element = {
export type Node = Element | Text | RawHTML;
+export type NodeLike = Node | string | null | undefined | false | NodeLike[];
+
+function flattenChildren(children: NodeLike[]): Node[] {
+ const result: Node[] = [];
+ for (const child of children) {
+ if (child === null || child === undefined || child === false) {
+ continue;
+ }
+ if (typeof child === "string") {
+ result.push(text(child));
+ } else if (Array.isArray(child)) {
+ result.push(...flattenChildren(child));
+ } else {
+ result.push(child);
+ }
+ }
+ return result;
+}
+
export function text(content: string): Text {
return {
kind: "text",
@@ -34,13 +53,13 @@ export function rawHTML(html: string): RawHTML {
export function elem(
name: string,
attributes?: Record<string, string>,
- ...children: Node[]
+ ...children: NodeLike[]
): Element {
return {
kind: "element",
name,
attributes: attributes || {},
- children,
+ children: flattenChildren(children),
};
}
diff --git a/services/nuldoc/nuldoc-src/generators/about.ts b/services/nuldoc/nuldoc-src/generators/about.ts
index 711c167..628c370 100644
--- a/services/nuldoc/nuldoc-src/generators/about.ts
+++ b/services/nuldoc/nuldoc-src/generators/about.ts
@@ -1,6 +1,5 @@
-import AboutPage from "../pages/AboutPage.tsx";
+import AboutPage from "../pages/AboutPage.ts";
import { Config } from "../config.ts";
-import { renderToDOM } from "../jsx/render.ts";
import { Page } from "../page.ts";
import { SlidePage } from "./slide.ts";
@@ -10,9 +9,7 @@ export async function generateAboutPage(
slides: SlidePage[],
config: Config,
): Promise<AboutPage> {
- const html = await renderToDOM(
- AboutPage(slides, config),
- );
+ const html = await AboutPage(slides, config);
return {
root: html,
diff --git a/services/nuldoc/nuldoc-src/generators/atom.ts b/services/nuldoc/nuldoc-src/generators/atom.ts
index dc62da9..f501d83 100644
--- a/services/nuldoc/nuldoc-src/generators/atom.ts
+++ b/services/nuldoc/nuldoc-src/generators/atom.ts
@@ -3,8 +3,7 @@ import { Page } from "../page.ts";
import { PostPage } from "../generators/post.ts";
import { SlidePage } from "../generators/slide.ts";
import { dateToRfc3339String } from "../revision.ts";
-import AtomPage from "../pages/AtomPage.tsx";
-import { renderToDOM } from "../jsx/render.ts";
+import AtomPage from "../pages/AtomPage.ts";
export type Feed = {
author: string;
@@ -28,14 +27,14 @@ export type Entry = {
const BASE_NAME = "atom.xml";
-export async function generateFeedPageFromEntries(
+export function generateFeedPageFromEntries(
alternateLink: string,
feedSlug: string,
feedTitle: string,
entries: Array<PostPage | SlidePage>,
site: "default" | "blog" | "slides",
config: Config,
-): Promise<Page> {
+): Page {
const entries_: Entry[] = [];
for (const entry of entries) {
entries_.push({
@@ -76,7 +75,7 @@ export async function generateFeedPageFromEntries(
};
return {
- root: await renderToDOM(AtomPage({ feed: feed })),
+ root: AtomPage({ feed: feed }),
renderer: "xml",
site,
destFilePath: feedPath,
diff --git a/services/nuldoc/nuldoc-src/generators/home.ts b/services/nuldoc/nuldoc-src/generators/home.ts
index ac91637..1839f5d 100644
--- a/services/nuldoc/nuldoc-src/generators/home.ts
+++ b/services/nuldoc/nuldoc-src/generators/home.ts
@@ -1,14 +1,11 @@
-import HomePage from "../pages/HomePage.tsx";
-import { renderToDOM } from "../jsx/render.ts";
+import HomePage from "../pages/HomePage.ts";
import { Config } from "../config.ts";
import { Page } from "../page.ts";
export type HomePage = Page;
export async function generateHomePage(config: Config): Promise<HomePage> {
- const html = await renderToDOM(
- HomePage(config),
- );
+ const html = await HomePage(config);
return {
root: html,
diff --git a/services/nuldoc/nuldoc-src/generators/not_found.ts b/services/nuldoc/nuldoc-src/generators/not_found.ts
index 56adc8e..8a5593c 100644
--- a/services/nuldoc/nuldoc-src/generators/not_found.ts
+++ b/services/nuldoc/nuldoc-src/generators/not_found.ts
@@ -1,5 +1,4 @@
-import NotFoundPage from "../pages/NotFoundPage.tsx";
-import { renderToDOM } from "../jsx/render.ts";
+import NotFoundPage from "../pages/NotFoundPage.ts";
import { Config } from "../config.ts";
import { Page } from "../page.ts";
@@ -9,9 +8,7 @@ export async function generateNotFoundPage(
site: "default" | "about" | "blog" | "slides",
config: Config,
): Promise<NotFoundPage> {
- const html = await renderToDOM(
- NotFoundPage(site, config),
- );
+ const html = await NotFoundPage(site, config);
return {
root: html,
diff --git a/services/nuldoc/nuldoc-src/generators/post.ts b/services/nuldoc/nuldoc-src/generators/post.ts
index 4e08f88..11a3ce8 100644
--- a/services/nuldoc/nuldoc-src/generators/post.ts
+++ b/services/nuldoc/nuldoc-src/generators/post.ts
@@ -1,6 +1,5 @@
import { join } from "@std/path";
-import { renderToDOM } from "../jsx/render.ts";
-import PostPage from "../pages/PostPage.tsx";
+import PostPage from "../pages/PostPage.ts";
import { Config } from "../config.ts";
import { Document } from "../djot/document.ts";
import { Page } from "../page.ts";
@@ -37,9 +36,7 @@ export async function generatePostPage(
doc: Document,
config: Config,
): Promise<PostPage> {
- const html = await renderToDOM(
- PostPage(doc, config),
- );
+ const html = await PostPage(doc, config);
const cwd = Deno.cwd();
const contentDir = join(cwd, config.locations.contentDir);
diff --git a/services/nuldoc/nuldoc-src/generators/post_list.ts b/services/nuldoc/nuldoc-src/generators/post_list.ts
index 6a21dd8..cb3d5c8 100644
--- a/services/nuldoc/nuldoc-src/generators/post_list.ts
+++ b/services/nuldoc/nuldoc-src/generators/post_list.ts
@@ -1,5 +1,4 @@
-import { renderToDOM } from "../jsx/render.ts";
-import PostListPage from "../pages/PostListPage.tsx";
+import PostListPage from "../pages/PostListPage.ts";
import { Config } from "../config.ts";
import { Page } from "../page.ts";
import { PostPage } from "./post.ts";
@@ -39,14 +38,7 @@ async function generatePostListPage(
currentPage: number,
totalPages: number,
): Promise<PostListPage> {
- const html = await renderToDOM(
- PostListPage(
- posts,
- config,
- currentPage,
- totalPages,
- ),
- );
+ const html = await PostListPage(posts, config, currentPage, totalPages);
const destFilePath = currentPage === 1
? "/posts/index.html"
diff --git a/services/nuldoc/nuldoc-src/generators/slide.ts b/services/nuldoc/nuldoc-src/generators/slide.ts
index feab583..2c04b40 100644
--- a/services/nuldoc/nuldoc-src/generators/slide.ts
+++ b/services/nuldoc/nuldoc-src/generators/slide.ts
@@ -1,6 +1,5 @@
import { join } from "@std/path";
-import { renderToDOM } from "../jsx/render.ts";
-import SlidePage from "../pages/SlidePage.tsx";
+import SlidePage from "../pages/SlidePage.ts";
import { Config } from "../config.ts";
import { Page } from "../page.ts";
import { Date, Revision } from "../revision.ts";
@@ -24,9 +23,7 @@ export async function generateSlidePage(
slide: Slide,
config: Config,
): Promise<SlidePage> {
- const html = await renderToDOM(
- SlidePage(slide, config),
- );
+ const html = await SlidePage(slide, config);
const cwd = Deno.cwd();
const contentDir = join(cwd, config.locations.contentDir);
diff --git a/services/nuldoc/nuldoc-src/generators/slide_list.ts b/services/nuldoc/nuldoc-src/generators/slide_list.ts
index 9f766ed..b65c9db 100644
--- a/services/nuldoc/nuldoc-src/generators/slide_list.ts
+++ b/services/nuldoc/nuldoc-src/generators/slide_list.ts
@@ -1,5 +1,4 @@
-import { renderToDOM } from "../jsx/render.ts";
-import SlideListPage from "../pages/SlideListPage.tsx";
+import SlideListPage from "../pages/SlideListPage.ts";
import { Config } from "../config.ts";
import { Page } from "../page.ts";
import { SlidePage } from "./slide.ts";
@@ -10,9 +9,7 @@ export async function generateSlideListPage(
slides: SlidePage[],
config: Config,
): Promise<SlideListPage> {
- const html = await renderToDOM(
- SlideListPage(slides, config),
- );
+ const html = await SlideListPage(slides, config);
return {
root: html,
diff --git a/services/nuldoc/nuldoc-src/generators/tag.ts b/services/nuldoc/nuldoc-src/generators/tag.ts
index 11335c6..efe2da5 100644
--- a/services/nuldoc/nuldoc-src/generators/tag.ts
+++ b/services/nuldoc/nuldoc-src/generators/tag.ts
@@ -1,5 +1,4 @@
-import { renderToDOM } from "../jsx/render.ts";
-import TagPage from "../pages/TagPage.tsx";
+import TagPage from "../pages/TagPage.ts";
import { Config, getTagLabel } from "../config.ts";
import { Page } from "../page.ts";
import { TaggedPage } from "./tagged_page.ts";
@@ -17,9 +16,7 @@ export async function generateTagPage(
site: "blog" | "slides",
config: Config,
): Promise<TagPage> {
- const html = await renderToDOM(
- TagPage(tagSlug, pages, site, config),
- );
+ const html = await TagPage(tagSlug, pages, site, config);
return {
root: html,
diff --git a/services/nuldoc/nuldoc-src/generators/tag_list.ts b/services/nuldoc/nuldoc-src/generators/tag_list.ts
index cbc161e..96faa66 100644
--- a/services/nuldoc/nuldoc-src/generators/tag_list.ts
+++ b/services/nuldoc/nuldoc-src/generators/tag_list.ts
@@ -1,5 +1,4 @@
-import { renderToDOM } from "../jsx/render.ts";
-import TagListPage from "../pages/TagListPage.tsx";
+import TagListPage from "../pages/TagListPage.ts";
import { Config } from "../config.ts";
import { Page } from "../page.ts";
import { TagPage } from "./tag.ts";
@@ -11,9 +10,7 @@ export async function generateTagListPage(
site: "blog" | "slides",
config: Config,
): Promise<TagListPage> {
- const html = await renderToDOM(
- TagListPage(tags, site, config),
- );
+ const html = await TagListPage(tags, site, config);
return {
root: html,
diff --git a/services/nuldoc/nuldoc-src/jsx/jsx-runtime.ts b/services/nuldoc/nuldoc-src/jsx/jsx-runtime.ts
deleted file mode 100644
index 9571e87..0000000
--- a/services/nuldoc/nuldoc-src/jsx/jsx-runtime.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import type { Node } from "../dom.ts";
-
-export type JSXElement = {
- tag: string | FunctionComponent;
- props: Props;
-};
-
-export type JSXNullNode = false | null | undefined;
-export type JSXSimpleNode = JSXElement | Node | string;
-export type JSXNullableSimpleNode = JSXSimpleNode | JSXNullNode;
-export type JSXNode = JSXNullableSimpleNode | JSXNode[];
-export type RenderableJSXNode = JSXElement;
-
-type Props = { children?: JSXNode } & Record<string, unknown>;
-export type FunctionComponentResult = JSXElement | Promise<JSXElement>;
-type FunctionComponent = (props: Props) => FunctionComponentResult;
-
-export function jsx(
- tag: string | FunctionComponent,
- props: Props,
-): JSXElement {
- return { tag, props };
-}
-
-export { jsx as jsxs };
-
-// TODO: support Fragment
diff --git a/services/nuldoc/nuldoc-src/jsx/render.ts b/services/nuldoc/nuldoc-src/jsx/render.ts
deleted file mode 100644
index a72d9ad..0000000
--- a/services/nuldoc/nuldoc-src/jsx/render.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import type { Element, Node } from "../dom.ts";
-import { elem, text } from "../dom.ts";
-import type {
- JSXNode,
- JSXNullableSimpleNode,
- JSXSimpleNode,
- RenderableJSXNode,
-} from "myjsx/jsx-runtime";
-
-function transformNode(node: JSXNode): Promise<Node[]> {
- const flattenNodes: JSXNullableSimpleNode[] = Array.isArray(node)
- // @ts-ignore prevents infinite recursion
- ? (node.flat(Infinity) as JSXNullableSimpleNode[])
- : [node];
- return Promise.all(
- flattenNodes
- .filter((c): c is JSXSimpleNode => c != null && c !== false)
- .map((c) => {
- if (typeof c === "string") {
- return text(c);
- } else if ("kind" in c) {
- return c;
- } else {
- return renderToDOM(c);
- }
- }),
- );
-}
-
-export async function renderToDOM(
- element: RenderableJSXNode,
-): Promise<Element> {
- const { tag, props } = element;
- if (typeof tag === "string") {
- const { children, ...attrs } = props;
- const attrsMap = attrs as Record<string, string>;
- return elem(tag, attrsMap, ...(await transformNode(children)));
- } else {
- return renderToDOM(await tag(props));
- }
-}
diff --git a/services/nuldoc/nuldoc-src/jsx/types.d.ts b/services/nuldoc/nuldoc-src/jsx/types.d.ts
deleted file mode 100644
index 973b852..0000000
--- a/services/nuldoc/nuldoc-src/jsx/types.d.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-import type {
- FunctionComponentResult,
- JSXElement,
- JSXNode,
-} from "myjsx/jsx-runtime";
-
-export { JSXNode };
-
-interface IntrinsicElementType {
- children?: JSXNode;
- className?: string;
- id?: string;
- // My JSX runtime does not use key. It is only for linter that complains about missing key.
- key?: string;
-}
-
-declare global {
- namespace JSX {
- type Element = JSXElement;
- type ElementType =
- | string
- // deno-lint-ignore no-explicit-any
- | ((props: any) => FunctionComponentResult);
-
- // TODO: HTML 用の element と XML 用の element を分ける
- interface IntrinsicElements {
- // XML (Atom)
- author: IntrinsicElementType;
- entry: IntrinsicElementType;
- feed: IntrinsicElementType & { xmlns: string };
- id: IntrinsicElementType;
- name: IntrinsicElementType;
- published: IntrinsicElementType;
- summary: IntrinsicElementType;
- updated: IntrinsicElementType;
- // HTML
- a: IntrinsicElementType & {
- href?: string;
- rel?: "noreferrer";
- target?: "_blank";
- };
- article: IntrinsicElementType;
- body: IntrinsicElementType;
- button: IntrinsicElementType & { type: string };
- canvas: { id?: string; "data-slide-link"?: string };
- div: IntrinsicElementType;
- footer: IntrinsicElementType;
- h1: IntrinsicElementType;
- h2: IntrinsicElementType;
- head: unknown;
- header: IntrinsicElementType;
- html: IntrinsicElementType & { lang?: string };
- img: { src: string };
- li: IntrinsicElementType;
- link: { rel: string; href: string; type?: string };
- main: IntrinsicElementType;
- meta: {
- charset?: string;
- name?: string;
- content?: string;
- property?: string;
- };
- nav: IntrinsicElementType;
- noscript: IntrinsicElementType;
- ol: IntrinsicElementType;
- p: IntrinsicElementType;
- script: { src: string; type?: string; defer?: "true" };
- section: IntrinsicElementType;
- span: IntrinsicElementType;
- time: IntrinsicElementType & { datetime?: string };
- title: IntrinsicElementType;
- ul: IntrinsicElementType;
- }
-
- interface ElementChildrenAttribute {
- children: unknown;
- }
-
- type LibraryManagedAttributes<_F, P> = P & {
- // My JSX runtime does not use key. It is only for linter that complains about missing key.
- key?: string;
- };
- }
-}
diff --git a/services/nuldoc/nuldoc-src/pages/AboutPage.ts b/services/nuldoc/nuldoc-src/pages/AboutPage.ts
new file mode 100644
index 0000000..43e239e
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/pages/AboutPage.ts
@@ -0,0 +1,160 @@
+import GlobalFooter from "../components/GlobalFooter.ts";
+import GlobalHeader from "../components/AboutGlobalHeader.ts";
+import PageLayout from "../components/PageLayout.ts";
+import StaticScript from "../components/StaticScript.ts";
+import { Config } from "../config.ts";
+import { dateToString } from "../revision.ts";
+import { getPostPublishedDate } from "../generators/post.ts";
+import { SlidePage } from "../generators/slide.ts";
+import { elem, Element } from "../dom.ts";
+
+export default async function AboutPage(
+ slides: SlidePage[],
+ config: Config,
+): Promise<Element> {
+ return await PageLayout({
+ metaCopyrightYear: config.site.copyrightYear,
+ metaDescription: "このサイトの著者について",
+ metaTitle: `About|${config.sites.about.siteName}`,
+ site: "about",
+ config,
+ children: elem(
+ "body",
+ { class: "single" },
+ GlobalHeader({ config }),
+ elem(
+ "main",
+ { class: "main" },
+ elem(
+ "article",
+ { class: "post-single" },
+ elem(
+ "header",
+ { class: "post-header" },
+ elem("h1", { class: "post-title" }, "nsfisis"),
+ elem(
+ "div",
+ { class: "my-icon" },
+ elem(
+ "div",
+ { id: "myIcon" },
+ elem("img", { src: "/favicon.svg" }),
+ ),
+ await StaticScript({
+ fileName: "/my-icon.js",
+ defer: "true",
+ config,
+ }),
+ ),
+ ),
+ elem(
+ "div",
+ { class: "post-content" },
+ elem(
+ "section",
+ {},
+ elem("h2", {}, "読み方"),
+ elem(
+ "p",
+ {},
+ "読み方は決めていません。音にする必要があるときは本名である「いまむら」をお使いください。",
+ ),
+ ),
+ elem(
+ "section",
+ {},
+ elem("h2", {}, "アカウント"),
+ elem(
+ "ul",
+ {},
+ elem(
+ "li",
+ {},
+ elem(
+ "a",
+ {
+ href: "https://twitter.com/nsfisis",
+ target: "_blank",
+ rel: "noreferrer",
+ },
+ "Twitter (現 𝕏): @nsfisis",
+ ),
+ ),
+ elem(
+ "li",
+ {},
+ elem(
+ "a",
+ {
+ href: "https://github.com/nsfisis",
+ target: "_blank",
+ rel: "noreferrer",
+ },
+ "GitHub: @nsfisis",
+ ),
+ ),
+ ),
+ ),
+ elem(
+ "section",
+ {},
+ elem("h2", {}, "仕事"),
+ elem(
+ "ul",
+ {},
+ elem(
+ "li",
+ {},
+ "2021-01~現在: ",
+ elem(
+ "a",
+ {
+ href: "https://www.dgcircus.com/",
+ target: "_blank",
+ rel: "noreferrer",
+ },
+ "デジタルサーカス株式会社",
+ ),
+ ),
+ ),
+ ),
+ elem(
+ "section",
+ {},
+ elem("h2", {}, "登壇"),
+ elem(
+ "ul",
+ {},
+ ...Array.from(slides)
+ .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;
+ })
+ .map((slide) =>
+ elem(
+ "li",
+ {},
+ elem(
+ "a",
+ {
+ href:
+ `https://${config.sites.slides.fqdn}${slide.href}`,
+ },
+ `${
+ dateToString(getPostPublishedDate(slide))
+ }: ${slide.event} (${slide.talkType})`,
+ ),
+ )
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ GlobalFooter({ config }),
+ ),
+ });
+}
diff --git a/services/nuldoc/nuldoc-src/pages/AboutPage.tsx b/services/nuldoc/nuldoc-src/pages/AboutPage.tsx
deleted file mode 100644
index d0397c1..0000000
--- a/services/nuldoc/nuldoc-src/pages/AboutPage.tsx
+++ /dev/null
@@ -1,113 +0,0 @@
-import GlobalFooter from "../components/GlobalFooter.tsx";
-import GlobalHeader from "../components/AboutGlobalHeader.tsx";
-import PageLayout from "../components/PageLayout.tsx";
-import StaticScript from "../components/StaticScript.tsx";
-import { Config } from "../config.ts";
-import { dateToString } from "../revision.ts";
-import { getPostPublishedDate } from "../generators/post.ts";
-import { SlidePage } from "../generators/slide.ts";
-
-export default function AboutPage(
- slides: SlidePage[],
- config: Config,
-) {
- return (
- <PageLayout
- metaCopyrightYear={config.site.copyrightYear}
- metaDescription="このサイトの著者について"
- metaTitle={`About|${config.sites.about.siteName}`}
- site="about"
- config={config}
- >
- <body className="single">
- <GlobalHeader config={config} />
- <main className="main">
- <article className="post-single">
- <header className="post-header">
- <h1 className="post-title">nsfisis</h1>
- <div className="my-icon">
- <div id="myIcon">
- <img src="/favicon.svg" />
- </div>
- <StaticScript
- fileName="/my-icon.js"
- defer="true"
- config={config}
- />
- </div>
- </header>
- <div className="post-content">
- <section>
- <h2>読み方</h2>
- <p>
- 読み方は決めていません。音にする必要があるときは本名である「いまむら」をお使いください。
- </p>
- </section>
- <section>
- <h2>アカウント</h2>
- <ul>
- <li>
- <a
- href="https://twitter.com/nsfisis"
- target="_blank"
- rel="noreferrer"
- >
- Twitter (現 𝕏): @nsfisis
- </a>
- </li>
- <li>
- <a
- href="https://github.com/nsfisis"
- target="_blank"
- rel="noreferrer"
- >
- GitHub: @nsfisis
- </a>
- </li>
- </ul>
- </section>
- <section>
- <h2>仕事</h2>
- <ul>
- <li>
- {"2021-01~現在: "}
- <a
- href="https://www.dgcircus.com/"
- target="_blank"
- rel="noreferrer"
- >
- デジタルサーカス株式会社
- </a>
- </li>
- </ul>
- </section>
- <section>
- <h2>登壇</h2>
- <ul>
- {Array.from(slides).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;
- }).map((slide) => (
- <li>
- <a
- href={`https://${config.sites.slides.fqdn}${slide.href}`}
- >
- {`${
- dateToString(getPostPublishedDate(slide))
- }: ${slide.event} (${slide.talkType})`}
- </a>
- </li>
- ))}
- </ul>
- </section>
- </div>
- </article>
- </main>
- <GlobalFooter config={config} />
- </body>
- </PageLayout>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/pages/AtomPage.ts b/services/nuldoc/nuldoc-src/pages/AtomPage.ts
new file mode 100644
index 0000000..f270972
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/pages/AtomPage.ts
@@ -0,0 +1,27 @@
+import { Feed } from "../generators/atom.ts";
+import { elem, Element } from "../dom.ts";
+
+export default function AtomPage({ feed }: { feed: Feed }): Element {
+ return elem(
+ "feed",
+ { xmlns: "http://www.w3.org/2005/Atom" },
+ elem("id", {}, feed.id),
+ elem("title", {}, feed.title),
+ elem("link", { rel: "alternate", href: feed.linkToAlternate }),
+ elem("link", { rel: "self", href: feed.linkToSelf }),
+ elem("author", {}, elem("name", {}, feed.author)),
+ elem("updated", {}, feed.updated),
+ ...feed.entries.map((entry) =>
+ elem(
+ "entry",
+ {},
+ elem("id", {}, entry.id),
+ elem("link", { rel: "alternate", href: entry.linkToAlternate }),
+ elem("title", {}, entry.title),
+ elem("summary", {}, entry.summary),
+ elem("published", {}, entry.published),
+ elem("updated", {}, entry.updated),
+ )
+ ),
+ );
+}
diff --git a/services/nuldoc/nuldoc-src/pages/AtomPage.tsx b/services/nuldoc/nuldoc-src/pages/AtomPage.tsx
deleted file mode 100644
index 21c3bfa..0000000
--- a/services/nuldoc/nuldoc-src/pages/AtomPage.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Feed } from "../generators/atom.ts";
-
-export default function AtomPage({ feed }: { feed: Feed }) {
- return (
- <feed xmlns="http://www.w3.org/2005/Atom">
- <id>{feed.id}</id>
- <title>{feed.title}</title>
- <link rel="alternate" href={feed.linkToAlternate} />
- <link rel="self" href={feed.linkToSelf} />
- <author>
- <name>{feed.author}</name>
- </author>
- <updated>{feed.updated}</updated>
- {feed.entries.map((entry) => (
- <entry>
- <id>{entry.id}</id>
- <link rel="alternate" href={entry.linkToAlternate} />
- <title>{entry.title}</title>
- <summary>{entry.summary}</summary>
- <published>{entry.published}</published>
- <updated>{entry.updated}</updated>
- </entry>
- ))}
- </feed>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/pages/HomePage.ts b/services/nuldoc/nuldoc-src/pages/HomePage.ts
new file mode 100644
index 0000000..503a382
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/pages/HomePage.ts
@@ -0,0 +1,78 @@
+import GlobalFooter from "../components/GlobalFooter.ts";
+import GlobalHeader from "../components/DefaultGlobalHeader.ts";
+import PageLayout from "../components/PageLayout.ts";
+import { Config } from "../config.ts";
+import { elem, Element } from "../dom.ts";
+
+export default async function HomePage(config: Config): Promise<Element> {
+ return await PageLayout({
+ metaCopyrightYear: config.site.copyrightYear,
+ metaDescription: "nsfisis のサイト",
+ metaTitle: config.sites.default.siteName,
+ metaAtomFeedHref: `https://${config.sites.default.fqdn}/atom.xml`,
+ site: "default",
+ config,
+ children: elem(
+ "body",
+ { class: "single" },
+ GlobalHeader({ config }),
+ elem(
+ "main",
+ { class: "main" },
+ elem(
+ "article",
+ { class: "post-single" },
+ elem(
+ "article",
+ { class: "post-entry" },
+ elem(
+ "a",
+ { href: `https://${config.sites.about.fqdn}/` },
+ elem(
+ "header",
+ { class: "entry-header" },
+ elem("h2", {}, "About"),
+ ),
+ ),
+ ),
+ elem(
+ "article",
+ { class: "post-entry" },
+ elem(
+ "a",
+ { href: `https://${config.sites.blog.fqdn}/posts/` },
+ elem("header", { class: "entry-header" }, elem("h2", {}, "Blog")),
+ ),
+ ),
+ elem(
+ "article",
+ { class: "post-entry" },
+ elem(
+ "a",
+ { href: `https://${config.sites.slides.fqdn}/slides/` },
+ elem(
+ "header",
+ { class: "entry-header" },
+ elem("h2", {}, "Slides"),
+ ),
+ ),
+ ),
+ elem(
+ "article",
+ { class: "post-entry" },
+ elem(
+ "a",
+ { href: `https://repos.${config.sites.default.fqdn}/` },
+ elem(
+ "header",
+ { class: "entry-header" },
+ elem("h2", {}, "Repositories"),
+ ),
+ ),
+ ),
+ ),
+ ),
+ GlobalFooter({ config }),
+ ),
+ });
+}
diff --git a/services/nuldoc/nuldoc-src/pages/HomePage.tsx b/services/nuldoc/nuldoc-src/pages/HomePage.tsx
deleted file mode 100644
index a2cbdd1..0000000
--- a/services/nuldoc/nuldoc-src/pages/HomePage.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import GlobalFooter from "../components/GlobalFooter.tsx";
-import GlobalHeader from "../components/DefaultGlobalHeader.tsx";
-import PageLayout from "../components/PageLayout.tsx";
-import { Config } from "../config.ts";
-
-export default function HomePage(config: Config) {
- return (
- <PageLayout
- metaCopyrightYear={config.site.copyrightYear}
- metaDescription="nsfisis のサイト"
- metaTitle={config.sites.default.siteName}
- metaAtomFeedHref={`https://${config.sites.default.fqdn}/atom.xml`}
- site="default"
- config={config}
- >
- <body className="single">
- <GlobalHeader config={config} />
- <main className="main">
- <article className="post-single">
- <article className="post-entry">
- <a href={`https://${config.sites.about.fqdn}/`}>
- <header className="entry-header">
- <h2>About</h2>
- </header>
- </a>
- </article>
- <article className="post-entry">
- <a href={`https://${config.sites.blog.fqdn}/posts/`}>
- <header className="entry-header">
- <h2>Blog</h2>
- </header>
- </a>
- </article>
- <article className="post-entry">
- <a href={`https://${config.sites.slides.fqdn}/slides/`}>
- <header className="entry-header">
- <h2>Slides</h2>
- </header>
- </a>
- </article>
- <article className="post-entry">
- <a href={`https://repos.${config.sites.default.fqdn}/`}>
- <header className="entry-header">
- <h2>Repositories</h2>
- </header>
- </a>
- </article>
- </article>
- </main>
- <GlobalFooter config={config} />
- </body>
- </PageLayout>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/pages/NotFoundPage.ts b/services/nuldoc/nuldoc-src/pages/NotFoundPage.ts
new file mode 100644
index 0000000..34854c4
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/pages/NotFoundPage.ts
@@ -0,0 +1,40 @@
+import GlobalFooter from "../components/GlobalFooter.ts";
+import AboutGlobalHeader from "../components/AboutGlobalHeader.ts";
+import BlogGlobalHeader from "../components/BlogGlobalHeader.ts";
+import SlidesGlobalHeader from "../components/SlidesGlobalHeader.ts";
+import DefaultGlobalHeader from "../components/DefaultGlobalHeader.ts";
+import PageLayout from "../components/PageLayout.ts";
+import { Config } from "../config.ts";
+import { elem, Element } from "../dom.ts";
+
+export default async function NotFoundPage(
+ site: "default" | "about" | "blog" | "slides",
+ config: Config,
+): Promise<Element> {
+ const GlobalHeader = site === "about"
+ ? AboutGlobalHeader
+ : site === "blog"
+ ? BlogGlobalHeader
+ : site === "slides"
+ ? SlidesGlobalHeader
+ : DefaultGlobalHeader;
+
+ return await PageLayout({
+ metaCopyrightYear: config.site.copyrightYear,
+ metaDescription: "リクエストされたページが見つかりません",
+ metaTitle: `Page Not Found|${config.sites[site].siteName}`,
+ site,
+ config,
+ children: elem(
+ "body",
+ { class: "single" },
+ GlobalHeader({ config }),
+ elem(
+ "main",
+ { class: "main" },
+ elem("article", {}, elem("div", { class: "not-found" }, "404")),
+ ),
+ GlobalFooter({ config }),
+ ),
+ });
+}
diff --git a/services/nuldoc/nuldoc-src/pages/NotFoundPage.tsx b/services/nuldoc/nuldoc-src/pages/NotFoundPage.tsx
deleted file mode 100644
index 11ce518..0000000
--- a/services/nuldoc/nuldoc-src/pages/NotFoundPage.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import GlobalFooter from "../components/GlobalFooter.tsx";
-import AboutGlobalHeader from "../components/AboutGlobalHeader.tsx";
-import BlogGlobalHeader from "../components/BlogGlobalHeader.tsx";
-import SlidesGlobalHeader from "../components/SlidesGlobalHeader.tsx";
-import DefaultGlobalHeader from "../components/DefaultGlobalHeader.tsx";
-import PageLayout from "../components/PageLayout.tsx";
-import { Config } from "../config.ts";
-
-export default function NotFoundPage(
- site: "default" | "about" | "blog" | "slides",
- config: Config,
-) {
- return (
- <PageLayout
- metaCopyrightYear={config.site.copyrightYear}
- metaDescription="リクエストされたページが見つかりません"
- metaTitle={`Page Not Found|${config.sites[site].siteName}`}
- site={site}
- config={config}
- >
- <body className="single">
- {site === "about"
- ? <AboutGlobalHeader config={config} />
- : site === "blog"
- ? <BlogGlobalHeader config={config} />
- : site === "slides"
- ? <SlidesGlobalHeader config={config} />
- : <DefaultGlobalHeader config={config} />}
- <main className="main">
- <article>
- <div className="not-found">404</div>
- </article>
- </main>
- <GlobalFooter config={config} />
- </body>
- </PageLayout>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/pages/PostListPage.ts b/services/nuldoc/nuldoc-src/pages/PostListPage.ts
new file mode 100644
index 0000000..53b8fa4
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/pages/PostListPage.ts
@@ -0,0 +1,49 @@
+import GlobalFooter from "../components/GlobalFooter.ts";
+import GlobalHeader from "../components/BlogGlobalHeader.ts";
+import PageLayout from "../components/PageLayout.ts";
+import Pagination from "../components/Pagination.ts";
+import PostPageEntry from "../components/PostPageEntry.ts";
+import { Config } from "../config.ts";
+import { PostPage } from "../generators/post.ts";
+import { elem, Element } from "../dom.ts";
+
+export default async function PostListPage(
+ posts: PostPage[],
+ config: Config,
+ currentPage: number,
+ totalPages: number,
+): Promise<Element> {
+ const pageTitle = "投稿一覧";
+
+ const pageInfoSuffix = ` (${currentPage}ページ目)`;
+ const metaTitle =
+ `${pageTitle}${pageInfoSuffix}|${config.sites.blog.siteName}`;
+ const metaDescription = `投稿した記事の一覧${pageInfoSuffix}`;
+
+ return await PageLayout({
+ metaCopyrightYear: config.site.copyrightYear,
+ metaDescription,
+ metaTitle,
+ metaAtomFeedHref: `https://${config.sites.blog.fqdn}/posts/atom.xml`,
+ site: "blog",
+ config,
+ children: elem(
+ "body",
+ { class: "list" },
+ GlobalHeader({ config }),
+ elem(
+ "main",
+ { class: "main" },
+ elem(
+ "header",
+ { class: "page-header" },
+ elem("h1", {}, pageTitle + pageInfoSuffix),
+ ),
+ Pagination({ currentPage, totalPages, basePath: "/posts/" }),
+ ...posts.map((post) => PostPageEntry({ post, config })),
+ Pagination({ currentPage, totalPages, basePath: "/posts/" }),
+ ),
+ GlobalFooter({ config }),
+ ),
+ });
+}
diff --git a/services/nuldoc/nuldoc-src/pages/PostListPage.tsx b/services/nuldoc/nuldoc-src/pages/PostListPage.tsx
deleted file mode 100644
index 5ed9696..0000000
--- a/services/nuldoc/nuldoc-src/pages/PostListPage.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import GlobalFooter from "../components/GlobalFooter.tsx";
-import GlobalHeader from "../components/BlogGlobalHeader.tsx";
-import PageLayout from "../components/PageLayout.tsx";
-import Pagination from "../components/Pagination.tsx";
-import PostPageEntry from "../components/PostPageEntry.tsx";
-import { Config } from "../config.ts";
-import { PostPage } from "../generators/post.ts";
-
-export default function PostListPage(
- posts: PostPage[],
- config: Config,
- currentPage: number,
- totalPages: number,
-) {
- const pageTitle = "投稿一覧";
-
- const pageInfoSuffix = ` (${currentPage}ページ目)`;
- const metaTitle =
- `${pageTitle}${pageInfoSuffix}|${config.sites.blog.siteName}`;
- const metaDescription = `投稿した記事の一覧${pageInfoSuffix}`;
-
- return (
- <PageLayout
- metaCopyrightYear={config.site.copyrightYear}
- metaDescription={metaDescription}
- metaTitle={metaTitle}
- metaAtomFeedHref={`https://${config.sites.blog.fqdn}/posts/atom.xml`}
- site="blog"
- config={config}
- >
- <body className="list">
- <GlobalHeader config={config} />
- <main className="main">
- <header className="page-header">
- <h1>{pageTitle}{pageInfoSuffix}</h1>
- </header>
-
- <Pagination
- currentPage={currentPage}
- totalPages={totalPages}
- basePath="/posts/"
- />
-
- {posts.map((post) => (
- <PostPageEntry post={post} config={config} key={post.uuid} />
- ))}
-
- <Pagination
- currentPage={currentPage}
- totalPages={totalPages}
- basePath="/posts/"
- />
- </main>
- <GlobalFooter config={config} />
- </body>
- </PageLayout>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/pages/PostPage.ts b/services/nuldoc/nuldoc-src/pages/PostPage.ts
new file mode 100644
index 0000000..84f58c3
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/pages/PostPage.ts
@@ -0,0 +1,90 @@
+import GlobalFooter from "../components/GlobalFooter.ts";
+import GlobalHeader from "../components/BlogGlobalHeader.ts";
+import PageLayout from "../components/PageLayout.ts";
+import TableOfContents from "../components/TableOfContents.ts";
+import { Config, getTagLabel } from "../config.ts";
+import { elem, Element } from "../dom.ts";
+import { Document } from "../djot/document.ts";
+import { dateToString } from "../revision.ts";
+import { getPostPublishedDate } from "../generators/post.ts";
+
+export default async function PostPage(
+ doc: Document,
+ config: Config,
+): Promise<Element> {
+ return await PageLayout({
+ metaCopyrightYear: getPostPublishedDate(doc).year,
+ metaDescription: doc.description,
+ metaKeywords: doc.tags.map((slug) => getTagLabel(config, slug)),
+ metaTitle: `${doc.title}|${config.sites.blog.siteName}`,
+ requiresSyntaxHighlight: true,
+ site: "blog",
+ config,
+ children: elem(
+ "body",
+ { class: "single" },
+ GlobalHeader({ config }),
+ elem(
+ "main",
+ { class: "main" },
+ elem(
+ "article",
+ { class: "post-single" },
+ elem(
+ "header",
+ { class: "post-header" },
+ elem("h1", { class: "post-title" }, doc.title),
+ doc.tags.length !== 0
+ ? elem(
+ "ul",
+ { class: "post-tags" },
+ ...doc.tags.map((slug) =>
+ elem(
+ "li",
+ { class: "tag" },
+ elem(
+ "a",
+ { href: `/tags/${slug}/` },
+ getTagLabel(config, slug),
+ ),
+ )
+ ),
+ )
+ : null,
+ ),
+ doc.toc && doc.toc.entries.length > 0
+ ? TableOfContents({ toc: doc.toc })
+ : null,
+ elem(
+ "div",
+ { class: "post-content" },
+ elem(
+ "section",
+ { id: "changelog" },
+ elem("h2", {}, elem("a", { href: "#changelog" }, "更新履歴")),
+ elem(
+ "ol",
+ {},
+ ...doc.revisions.map((rev) =>
+ elem(
+ "li",
+ { class: "revision" },
+ elem(
+ "time",
+ { datetime: dateToString(rev.date) },
+ dateToString(rev.date),
+ ),
+ `: ${rev.remark}`,
+ )
+ ),
+ ),
+ ),
+ // TODO: refactor
+ ...(doc.root.children[0] as Element).children,
+ ),
+ ),
+ ),
+ GlobalFooter({ config }),
+ ),
+ });
+}
diff --git a/services/nuldoc/nuldoc-src/pages/PostPage.tsx b/services/nuldoc/nuldoc-src/pages/PostPage.tsx
deleted file mode 100644
index beebdb3..0000000
--- a/services/nuldoc/nuldoc-src/pages/PostPage.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import GlobalFooter from "../components/GlobalFooter.tsx";
-import GlobalHeader from "../components/BlogGlobalHeader.tsx";
-import PageLayout from "../components/PageLayout.tsx";
-import TableOfContents from "../components/TableOfContents.tsx";
-import { Config, getTagLabel } from "../config.ts";
-import { Element } from "../dom.ts";
-import { Document } from "../djot/document.ts";
-import { dateToString } from "../revision.ts";
-import { getPostPublishedDate } from "../generators/post.ts";
-
-export default function PostPage(
- doc: Document,
- config: Config,
-) {
- return (
- <PageLayout
- metaCopyrightYear={getPostPublishedDate(doc).year}
- metaDescription={doc.description}
- metaKeywords={doc.tags.map((slug) => getTagLabel(config, slug))}
- metaTitle={`${doc.title}|${config.sites.blog.siteName}`}
- requiresSyntaxHighlight
- site="blog"
- config={config}
- >
- <body className="single">
- <GlobalHeader config={config} />
- <main className="main">
- <article className="post-single">
- <header className="post-header">
- <h1 className="post-title">{doc.title}</h1>
- {doc.tags.length !== 0 && (
- <ul className="post-tags">
- {doc.tags.map((slug) => (
- <li className="tag">
- <a href={`/tags/${slug}/`}>{getTagLabel(config, slug)}</a>
- </li>
- ))}
- </ul>
- )}
- </header>
- {doc.toc && doc.toc.entries.length > 0 && (
- <TableOfContents toc={doc.toc} />
- )}
- <div className="post-content">
- <section id="changelog">
- <h2>
- <a href="#changelog">更新履歴</a>
- </h2>
- <ol>
- {doc.revisions.map((rev) => (
- <li className="revision">
- <time datetime={dateToString(rev.date)}>
- {dateToString(rev.date)}
- </time>
- {`: ${rev.remark}`}
- </li>
- ))}
- </ol>
- </section>
- {
- // TODO: refactor
- (doc.root.children[0] as Element).children
- }
- </div>
- </article>
- </main>
- <GlobalFooter config={config} />
- </body>
- </PageLayout>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/pages/SlideListPage.ts b/services/nuldoc/nuldoc-src/pages/SlideListPage.ts
new file mode 100644
index 0000000..9a1c2b2
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/pages/SlideListPage.ts
@@ -0,0 +1,45 @@
+import GlobalFooter from "../components/GlobalFooter.ts";
+import GlobalHeader from "../components/SlidesGlobalHeader.ts";
+import PageLayout from "../components/PageLayout.ts";
+import SlidePageEntry from "../components/SlidePageEntry.ts";
+import { Config } from "../config.ts";
+import { dateToString } from "../revision.ts";
+import { getPostPublishedDate } from "../generators/post.ts";
+import { SlidePage } from "../generators/slide.ts";
+import { elem, Element } from "../dom.ts";
+
+export default async function SlideListPage(
+ slides: SlidePage[],
+ config: Config,
+): Promise<Element> {
+ const pageTitle = "スライド一覧";
+
+ return await PageLayout({
+ metaCopyrightYear: config.site.copyrightYear,
+ metaDescription: "登壇したイベントで使用したスライドの一覧",
+ metaTitle: `${pageTitle}|${config.sites.slides.siteName}`,
+ metaAtomFeedHref: `https://${config.sites.slides.fqdn}/slides/atom.xml`,
+ site: "slides",
+ config,
+ children: elem(
+ "body",
+ { class: "list" },
+ GlobalHeader({ config }),
+ elem(
+ "main",
+ { class: "main" },
+ elem("header", { class: "page-header" }, elem("h1", {}, pageTitle)),
+ ...Array.from(slides)
+ .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;
+ })
+ .map((slide) => SlidePageEntry({ slide, config })),
+ ),
+ GlobalFooter({ config }),
+ ),
+ });
+}
diff --git a/services/nuldoc/nuldoc-src/pages/SlideListPage.tsx b/services/nuldoc/nuldoc-src/pages/SlideListPage.tsx
deleted file mode 100644
index dc216eb..0000000
--- a/services/nuldoc/nuldoc-src/pages/SlideListPage.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import GlobalFooter from "../components/GlobalFooter.tsx";
-import GlobalHeader from "../components/SlidesGlobalHeader.tsx";
-import PageLayout from "../components/PageLayout.tsx";
-import SlidePageEntry from "../components/SlidePageEntry.tsx";
-import { Config } from "../config.ts";
-import { dateToString } from "../revision.ts";
-import { getPostPublishedDate } from "../generators/post.ts";
-import { SlidePage } from "../generators/slide.ts";
-
-export default function SlideListPage(
- slides: SlidePage[],
- config: Config,
-) {
- const pageTitle = "スライド一覧";
-
- return (
- <PageLayout
- metaCopyrightYear={config.site.copyrightYear}
- metaDescription="登壇したイベントで使用したスライドの一覧"
- metaTitle={`${pageTitle}|${config.sites.slides.siteName}`}
- metaAtomFeedHref={`https://${config.sites.slides.fqdn}/slides/atom.xml`}
- site="slides"
- config={config}
- >
- <body className="list">
- <GlobalHeader config={config} />
- <main className="main">
- <header className="page-header">
- <h1>{pageTitle}</h1>
- </header>
- {Array.from(slides).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;
- }).map((slide) => (
- <SlidePageEntry slide={slide} config={config} key={slide.uuid} />
- ))}
- </main>
- <GlobalFooter config={config} />
- </body>
- </PageLayout>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/pages/SlidePage.ts b/services/nuldoc/nuldoc-src/pages/SlidePage.ts
new file mode 100644
index 0000000..8699134
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/pages/SlidePage.ts
@@ -0,0 +1,98 @@
+import GlobalFooter from "../components/GlobalFooter.ts";
+import GlobalHeader from "../components/SlidesGlobalHeader.ts";
+import PageLayout from "../components/PageLayout.ts";
+import StaticScript from "../components/StaticScript.ts";
+import { Config, getTagLabel } from "../config.ts";
+import { dateToString } from "../revision.ts";
+import { Slide } from "../slide/slide.ts";
+import { getPostPublishedDate } from "../generators/post.ts";
+import { elem, Element } from "../dom.ts";
+
+export default async function SlidePage(
+ slide: Slide,
+ config: Config,
+): Promise<Element> {
+ return await PageLayout({
+ metaCopyrightYear: getPostPublishedDate(slide).year,
+ metaDescription: slide.title,
+ metaKeywords: slide.tags.map((slug) => getTagLabel(config, slug)),
+ metaTitle:
+ `${slide.event} (${slide.talkType})|${config.sites.slides.siteName}`,
+ requiresSyntaxHighlight: true,
+ site: "slides",
+ config,
+ children: elem(
+ "body",
+ { class: "single" },
+ GlobalHeader({ config }),
+ elem(
+ "main",
+ { class: "main" },
+ elem(
+ "article",
+ { class: "post-single" },
+ elem(
+ "header",
+ { class: "post-header" },
+ elem("h1", { class: "post-title" }, slide.title),
+ slide.tags.length !== 0
+ ? elem(
+ "ul",
+ { class: "post-tags" },
+ ...slide.tags.map((slug) =>
+ elem(
+ "li",
+ { class: "tag" },
+ elem(
+ "a",
+ { href: `/tags/${slug}/` },
+ getTagLabel(config, slug),
+ ),
+ )
+ ),
+ )
+ : null,
+ ),
+ elem(
+ "div",
+ { class: "post-content" },
+ elem(
+ "section",
+ { id: "changelog" },
+ elem("h2", {}, elem("a", { href: "#changelog" }, "更新履歴")),
+ elem(
+ "ol",
+ {},
+ ...slide.revisions.map((rev) =>
+ elem(
+ "li",
+ { class: "revision" },
+ elem(
+ "time",
+ { datetime: dateToString(rev.date) },
+ dateToString(rev.date),
+ ),
+ `: ${rev.remark}`,
+ )
+ ),
+ ),
+ ),
+ elem("canvas", { id: "slide", "data-slide-link": slide.slideLink }),
+ elem(
+ "div",
+ {},
+ elem("button", { id: "prev", type: "button" }, "Prev"),
+ elem("button", { id: "next", type: "button" }, "Next"),
+ ),
+ await StaticScript({
+ fileName: "/slide.js",
+ type: "module",
+ config,
+ }),
+ ),
+ ),
+ ),
+ GlobalFooter({ config }),
+ ),
+ });
+}
diff --git a/services/nuldoc/nuldoc-src/pages/SlidePage.tsx b/services/nuldoc/nuldoc-src/pages/SlidePage.tsx
deleted file mode 100644
index 53944df..0000000
--- a/services/nuldoc/nuldoc-src/pages/SlidePage.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import GlobalFooter from "../components/GlobalFooter.tsx";
-import GlobalHeader from "../components/SlidesGlobalHeader.tsx";
-import PageLayout from "../components/PageLayout.tsx";
-import StaticScript from "../components/StaticScript.tsx";
-import { Config, getTagLabel } from "../config.ts";
-import { dateToString } from "../revision.ts";
-import { Slide } from "../slide/slide.ts";
-import { getPostPublishedDate } from "../generators/post.ts";
-
-export default function SlidePage(
- slide: Slide,
- config: Config,
-) {
- return (
- <PageLayout
- metaCopyrightYear={getPostPublishedDate(slide).year}
- metaDescription={slide.title}
- metaKeywords={slide.tags.map((slug) => getTagLabel(config, slug))}
- metaTitle={`${slide.event} (${slide.talkType})|${config.sites.slides.siteName}`}
- requiresSyntaxHighlight
- site="slides"
- config={config}
- >
- <body className="single">
- <GlobalHeader config={config} />
- <main className="main">
- <article className="post-single">
- <header className="post-header">
- <h1 className="post-title">{slide.title}</h1>
- {slide.tags.length !== 0 && (
- <ul className="post-tags">
- {slide.tags.map((slug) => (
- <li className="tag">
- <a href={`/tags/${slug}/`}>{getTagLabel(config, slug)}</a>
- </li>
- ))}
- </ul>
- )}
- </header>
- <div className="post-content">
- <section id="changelog">
- <h2>
- <a href="#changelog">更新履歴</a>
- </h2>
- <ol>
- {slide.revisions.map((rev) => (
- <li className="revision">
- <time datetime={dateToString(rev.date)}>
- {dateToString(rev.date)}
- </time>
- {`: ${rev.remark}`}
- </li>
- ))}
- </ol>
- </section>
- <canvas id="slide" data-slide-link={slide.slideLink} />
- <div>
- <button id="prev" type="button">Prev</button>
- <button id="next" type="button">Next</button>
- </div>
- <StaticScript
- fileName="/slide.js"
- type="module"
- config={config}
- />
- </div>
- </article>
- </main>
- <GlobalFooter config={config} />
- </body>
- </PageLayout>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/pages/TagListPage.ts b/services/nuldoc/nuldoc-src/pages/TagListPage.ts
new file mode 100644
index 0000000..6872a2f
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/pages/TagListPage.ts
@@ -0,0 +1,70 @@
+import GlobalFooter from "../components/GlobalFooter.ts";
+import BlogGlobalHeader from "../components/BlogGlobalHeader.ts";
+import SlidesGlobalHeader from "../components/SlidesGlobalHeader.ts";
+import PageLayout from "../components/PageLayout.ts";
+import { Config } from "../config.ts";
+import { TagPage } from "../generators/tag.ts";
+import { elem, Element } from "../dom.ts";
+
+export default async function TagListPage(
+ tags: TagPage[],
+ site: "blog" | "slides",
+ config: Config,
+): Promise<Element> {
+ const pageTitle = "タグ一覧";
+
+ const GlobalHeader = site === "blog" ? BlogGlobalHeader : SlidesGlobalHeader;
+
+ return await PageLayout({
+ metaCopyrightYear: config.site.copyrightYear,
+ metaDescription: "タグの一覧",
+ metaTitle: `${pageTitle}|${config.sites[site].siteName}`,
+ site,
+ config,
+ children: elem(
+ "body",
+ { class: "list" },
+ GlobalHeader({ config }),
+ elem(
+ "main",
+ { class: "main" },
+ elem("header", { class: "page-header" }, elem("h1", {}, pageTitle)),
+ ...Array.from(tags)
+ .sort((a, b) => {
+ const ta = a.tagSlug;
+ const tb = b.tagSlug;
+ if (ta < tb) return -1;
+ if (ta > tb) return 1;
+ return 0;
+ })
+ .map((tag) => {
+ const posts = tag.numOfPosts === 0
+ ? ""
+ : `${tag.numOfPosts}件の記事`;
+ const slides = tag.numOfSlides === 0
+ ? ""
+ : `${tag.numOfSlides}件のスライド`;
+ const footerText = `${posts}${
+ posts && slides ? "、" : ""
+ }${slides}`;
+
+ return elem(
+ "article",
+ { class: "post-entry" },
+ elem(
+ "a",
+ { href: tag.href },
+ elem(
+ "header",
+ { class: "entry-header" },
+ elem("h2", {}, tag.tagLabel),
+ ),
+ elem("footer", { class: "entry-footer" }, footerText),
+ ),
+ );
+ }),
+ ),
+ GlobalFooter({ config }),
+ ),
+ });
+}
diff --git a/services/nuldoc/nuldoc-src/pages/TagListPage.tsx b/services/nuldoc/nuldoc-src/pages/TagListPage.tsx
deleted file mode 100644
index 3ee58f3..0000000
--- a/services/nuldoc/nuldoc-src/pages/TagListPage.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import GlobalFooter from "../components/GlobalFooter.tsx";
-import BlogGlobalHeader from "../components/BlogGlobalHeader.tsx";
-import SlidesGlobalHeader from "../components/SlidesGlobalHeader.tsx";
-import PageLayout from "../components/PageLayout.tsx";
-import { Config } from "../config.ts";
-import { TagPage } from "../generators/tag.ts";
-
-export default function TagListPage(
- tags: TagPage[],
- site: "blog" | "slides",
- config: Config,
-) {
- const pageTitle = "タグ一覧";
-
- return (
- <PageLayout
- metaCopyrightYear={config.site.copyrightYear}
- metaDescription="タグの一覧"
- metaTitle={`${pageTitle}|${config.sites[site].siteName}`}
- site={site}
- config={config}
- >
- <body className="list">
- {site === "blog"
- ? <BlogGlobalHeader config={config} />
- : <SlidesGlobalHeader config={config} />}
- <main className="main">
- <header className="page-header">
- <h1>{pageTitle}</h1>
- </header>
- {Array.from(tags).sort((a, b) => {
- const ta = a.tagSlug;
- const tb = b.tagSlug;
- if (ta < tb) return -1;
- if (ta > tb) return 1;
- return 0;
- }).map((tag) => (
- <article className="post-entry">
- <a href={tag.href}>
- <header className="entry-header">
- <h2>{tag.tagLabel}</h2>
- </header>
- <footer className="entry-footer">
- {(() => {
- const posts = tag.numOfPosts === 0
- ? ""
- : `${tag.numOfPosts}件の記事`;
- const slides = tag.numOfSlides === 0
- ? ""
- : `${tag.numOfSlides}件のスライド`;
- return `${posts}${posts && slides ? "、" : ""}${slides}`;
- })()}
- </footer>
- </a>
- </article>
- ))}
- </main>
- <GlobalFooter config={config} />
- </body>
- </PageLayout>
- );
-}
diff --git a/services/nuldoc/nuldoc-src/pages/TagPage.ts b/services/nuldoc/nuldoc-src/pages/TagPage.ts
new file mode 100644
index 0000000..408f0b4
--- /dev/null
+++ b/services/nuldoc/nuldoc-src/pages/TagPage.ts
@@ -0,0 +1,50 @@
+import GlobalFooter from "../components/GlobalFooter.ts";
+import BlogGlobalHeader from "../components/BlogGlobalHeader.ts";
+import SlidesGlobalHeader from "../components/SlidesGlobalHeader.ts";
+import PageLayout from "../components/PageLayout.ts";
+import PostPageEntry from "../components/PostPageEntry.ts";
+import SlidePageEntry from "../components/SlidePageEntry.ts";
+import { Config, getTagLabel } from "../config.ts";
+import { getPostPublishedDate } from "../generators/post.ts";
+import { TaggedPage } from "../generators/tagged_page.ts";
+import { elem, Element } from "../dom.ts";
+
+export default async function TagPage(
+ tagSlug: string,
+ pages: TaggedPage[],
+ site: "blog" | "slides",
+ config: Config,
+): Promise<Element> {
+ const tagLabel = getTagLabel(config, tagSlug);
+ const pageTitle = `タグ「${tagLabel}」一覧`;
+
+ const GlobalHeader = site === "blog" ? BlogGlobalHeader : SlidesGlobalHeader;
+
+ return await PageLayout({
+ metaCopyrightYear: getPostPublishedDate(pages[pages.length - 1]).year,
+ metaDescription: `タグ「${tagLabel}」のついた記事またはスライドの一覧`,
+ metaKeywords: [tagLabel],
+ metaTitle: `${pageTitle}|${config.sites[site].siteName}`,
+ metaAtomFeedHref: `https://${
+ config.sites[site].fqdn
+ }/tags/${tagSlug}/atom.xml`,
+ site,
+ config,
+ children: elem(
+ "body",
+ { class: "list" },
+ GlobalHeader({ config }),
+ elem(
+ "main",
+ { class: "main" },
+ elem("header", { class: "page-header" }, elem("h1", {}, pageTitle)),
+ ...pages.map((page) =>
+ "event" in page
+ ? SlidePageEntry({ slide: page, config })
+ : PostPageEntry({ post: page, config })
+ ),
+ ),
+ GlobalFooter({ config }),
+ ),
+ });
+}
diff --git a/services/nuldoc/nuldoc-src/pages/TagPage.tsx b/services/nuldoc/nuldoc-src/pages/TagPage.tsx
deleted file mode 100644
index 6c64172..0000000
--- a/services/nuldoc/nuldoc-src/pages/TagPage.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import GlobalFooter from "../components/GlobalFooter.tsx";
-import BlogGlobalHeader from "../components/BlogGlobalHeader.tsx";
-import SlidesGlobalHeader from "../components/SlidesGlobalHeader.tsx";
-import PageLayout from "../components/PageLayout.tsx";
-import PostPageEntry from "../components/PostPageEntry.tsx";
-import SlidePageEntry from "../components/SlidePageEntry.tsx";
-import { Config, getTagLabel } from "../config.ts";
-import { getPostPublishedDate } from "../generators/post.ts";
-import { TaggedPage } from "../generators/tagged_page.ts";
-
-export default function TagPage(
- tagSlug: string,
- pages: TaggedPage[],
- site: "blog" | "slides",
- config: Config,
-) {
- const tagLabel = getTagLabel(config, tagSlug);
- const pageTitle = `タグ「${tagLabel}」一覧`;
-
- return (
- <PageLayout
- metaCopyrightYear={getPostPublishedDate(pages[pages.length - 1]).year}
- metaDescription={`タグ「${tagLabel}」のついた記事またはスライドの一覧`}
- metaKeywords={[tagLabel]}
- metaTitle={`${pageTitle}|${config.sites[site].siteName}`}
- metaAtomFeedHref={`https://${
- config.sites[site].fqdn
- }/tags/${tagSlug}/atom.xml`}
- site={site}
- config={config}
- >
- <body className="list">
- {site === "blog"
- ? <BlogGlobalHeader config={config} />
- : <SlidesGlobalHeader config={config} />}
- <main className="main">
- <header className="page-header">
- <h1>{pageTitle}</h1>
- </header>
- {pages.map((page) =>
- "event" in page
- ? <SlidePageEntry slide={page} config={config} key={page.uuid} />
- : <PostPageEntry post={page} config={config} key={page.uuid} />
- )}
- </main>
- <GlobalFooter config={config} />
- </body>
- </PageLayout>
- );
-}