- Add sitemap.ts and robots.ts - pageMetadata() helper: consistent title, canonical URL, Open Graph, Twitter card and feed alternate on every page - Generated OG images: a site card, and per post either the cover image or a branded title card (pre-rendered at build) - BlogPosting JSON-LD on post pages - Fail fast with a clear error when SITE_URL is missing Content loading: - Validate post frontmatter at build time and normalize dates to ISO (fixes invalid <time datetime> values) - Add draft: true support (drafts only render in development) - Cache markdown rendering so metadata and page share one render - Replace the three copy-pasted about/projects/setup pages with a shared MarkdownPage component Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
27 lines
1.1 KiB
TypeScript
27 lines
1.1 KiB
TypeScript
import { pageMetadata } from '@/app/metadata'
|
|
import { TableOfContents } from '@/components/post/TableOfContents'
|
|
import { getPageBySlug } from '@/lib/posts'
|
|
import type { Metadata } from 'next'
|
|
|
|
/** Metadata for a standalone page served at `/<slug>` from content/pages/<slug>.md. */
|
|
export async function markdownPageMetadata(slug: string): Promise<Metadata> {
|
|
const { meta } = await getPageBySlug(slug)
|
|
return pageMetadata({ title: meta.title, description: meta.description, path: `/${slug}` })
|
|
}
|
|
|
|
/** Renders a standalone page from content/pages/<slug>.md. */
|
|
export async function MarkdownPage({ slug }: { slug: string }) {
|
|
const { meta, content, tableOfContents } = await getPageBySlug(slug)
|
|
|
|
return (
|
|
<main className="flex w-full">
|
|
<article className="max-w-3xl 2xl:max-w-4xl mx-auto px-4 py-8 mb-8 lg:py-16 min-w-0">
|
|
<h1 className="text-4xl font-bold mb-4">{meta.title}</h1>
|
|
<p className="text-fg text-lg mb-4">{meta.description}</p>
|
|
<div className="prose">{content}</div>
|
|
</article>
|
|
|
|
<TableOfContents items={tableOfContents} />
|
|
</main>
|
|
)
|
|
}
|