diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-11-27 04:02:06 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-11-27 04:02:06 +0900 |
| commit | d1014de68415df8f0a5dc3389332e086119c6198 (patch) | |
| tree | 6564411eb6381eb48f86ed2b658c5440295596fa | |
| parent | b587ae4cfdfb991dcd9d7a1109b7530f774691dd (diff) | |
| download | nsfisis.dev-d1014de68415df8f0a5dc3389332e086119c6198.tar.gz nsfisis.dev-d1014de68415df8f0a5dc3389332e086119c6198.tar.zst nsfisis.dev-d1014de68415df8f0a5dc3389332e086119c6198.zip | |
refactor(nuldoc): eliminate JSX
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" }, + `© ${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"> - {`© ${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: `© ${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={`© ${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> - ); -} |
