From 7f9403b7cf9dec4f786a8a3c378ca142755d055c Mon Sep 17 00:00:00 2001 From: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> Date: Wed, 18 Mar 2026 22:41:29 +0900 Subject: [PATCH] =?UTF-8?q?sitemap,=20robots.txt,=20commits.yml=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/lib/docs.ts | 22 +++++++++++++++++ app/robots.ts | 15 ++++++++++++ app/sitemap.ts | 53 +++++++++++++++++++++++++++++++++++++++++ public/docs/commits.yml | 2 ++ scripts/checkDocs.ts | 23 ++++++++++++++++++ 5 files changed, 115 insertions(+) create mode 100644 app/robots.ts create mode 100644 app/sitemap.ts create mode 100644 public/docs/commits.yml diff --git a/app/lib/docs.ts b/app/lib/docs.ts index 4104a9b0..70006fcb 100644 --- a/app/lib/docs.ts +++ b/app/lib/docs.ts @@ -95,6 +95,9 @@ export interface RevisionYmlEntry { * `langId/pageSlug` */ page: string; + /** + * revisionのリストは末尾が最新のはず。 + */ rev: SectionRevision[]; } export interface SectionRevision { @@ -184,6 +187,25 @@ export async function getRevisions( ]; } +const commitDateCache = new Map>(); +export async function getCommitDate(id: string): Promise { + if (commitDateCache.has(id)) { + return commitDateCache.get(id)!; + } + const p = (async () => { + const commitInfoYml = await readPublicFile(`docs/commits.yml`); + const commitInfo = yaml.load(commitInfoYml) as Record; + const timestamp = commitInfo[id]; + if (timestamp) { + return new Date(timestamp); + } else { + throw new Error(`Commit date for id=${id} not found`); + } + })(); + commitDateCache.set(id, p); + return p; +} + /** * public/docs/{lang}/{pageId}/ 以下のmdファイルを結合して MarkdownSection[] を返す。 */ diff --git a/app/robots.ts b/app/robots.ts new file mode 100644 index 00000000..f520b6bc --- /dev/null +++ b/app/robots.ts @@ -0,0 +1,15 @@ +import type { MetadataRoute } from "next"; + +export const dynamic = "force-static"; + +const origin = "https://my-code.utcode.net"; + +export default function robots(): MetadataRoute.Robots { + return { + rules: { + userAgent: "*", + allow: "/", + }, + sitemap: `${origin}/sitemap.xml`, + }; +} diff --git a/app/sitemap.ts b/app/sitemap.ts new file mode 100644 index 00000000..2a8d6231 --- /dev/null +++ b/app/sitemap.ts @@ -0,0 +1,53 @@ +import type { MetadataRoute } from "next"; +import { + getCommitDate, + getMarkdownSections, + getPagesList, + getRevisions, +} from "./lib/docs"; + +export const dynamic = "force-static"; + +const origin = "https://my-code.utcode.net"; + +export default async function sitemap(): Promise { + const pagesList = await getPagesList(); + + return [ + { + url: origin, + changeFrequency: "monthly", + priority: 1, + }, + ...(await Promise.all( + pagesList + .map((lang) => + lang.pages.map(async (page) => { + const sections = await getMarkdownSections(lang.id, page.slug); + const sectionsDate = await Promise.all( + sections.map((s) => + getRevisions(s.id) + .then((revisions) => revisions?.rev.at(-1)) + .then((lastRev) => + lastRev ? getCommitDate(lastRev.git) : null + ) + ) + ); + return { + url: `${origin}/${lang.id}/${page.slug}`, + priority: 0.8, + changeFrequency: "monthly", + lastModified: sectionsDate.reduce( + (latest: Date, date: Date | null) => { + if (!date) return latest; + return date > latest ? date : latest; + }, + new Date(0) + ), + } satisfies MetadataRoute.Sitemap[number]; + }) + ) + .flat() + )), + ]; +} diff --git a/public/docs/commits.yml b/public/docs/commits.yml new file mode 100644 index 00000000..7f48e225 --- /dev/null +++ b/public/docs/commits.yml @@ -0,0 +1,2 @@ +# This file will be updated by CI. Do not edit manually. +de11512: '2026-03-08T14:27:20+09:00' diff --git a/scripts/checkDocs.ts b/scripts/checkDocs.ts index 638935f3..21716a39 100644 --- a/scripts/checkDocs.ts +++ b/scripts/checkDocs.ts @@ -50,6 +50,8 @@ if (doWrite) { }).trim(); } +let hasNewRevision = false; + const langEntries = await getPagesList(); const revisionsPrevYml = existsSync(join(docsDir, "revisions.yml")) @@ -70,6 +72,7 @@ for (const lang of langEntries) { // ドキュメントが変更された場合 console.warn(`${section.id} has new md5: ${section.md5}`); if (doWrite) { + hasNewRevision = true; revisions[section.id].rev.push({ md5: section.md5, git: commit, @@ -83,6 +86,7 @@ for (const lang of langEntries) { // ドキュメントが新規追加された場合 console.warn(`${section.id} is new section`); if (doWrite) { + hasNewRevision = true; revisions[section.id] = { rev: [ { @@ -142,4 +146,23 @@ if (doWrite) { revisionsYml, "utf-8" ); + + if (hasNewRevision) { + const commitsYml = yaml.load( + await readFile(join(docsDir, "commits.yml"), "utf-8") + ) as Record; + commitsYml[commit] = execFileSync( + "git", + ["show", "-s", "--format=%cI", commit], + { + encoding: "utf8", + } + ).trim(); + await writeFile( + join(docsDir, "commits.yml"), + "# This file will be updated by CI. Do not edit manually.\n" + + yaml.dump(commitsYml, { sortKeys: true }), + "utf-8" + ); + } }