import type { Metadata, MetadataRoute } from "next" import siteData from "@/data/site.json" type SiteSeoConfig = { siteUrl: string siteName: string defaultTitle: string titleTemplate: string description: string keywords: string[] canonicalPath: string locale?: string robots?: { index?: boolean follow?: boolean } openGraph?: { type?: "website" | "article" image?: string } twitter?: { card?: "summary" | "summary_large_image" | "app" | "player" site?: string creator?: string } verification?: { google?: string yandex?: string other?: Record } } const seo = siteData.seo as SiteSeoConfig function nonEmpty(value?: string) { if (!value) { return undefined } const trimmed = value.trim() return trimmed ? trimmed : undefined } function getMetadataBase() { return new URL(seo.siteUrl) } function getCanonicalPath() { return nonEmpty(seo.canonicalPath) ?? "/" } function getVerificationOther() { const otherEntries = Object.entries(seo.verification?.other ?? {}).filter(([, value]) => Boolean(nonEmpty(value))) return otherEntries.length > 0 ? Object.fromEntries(otherEntries) : undefined } function getOpenGraphImages() { const image = nonEmpty(seo.openGraph?.image) return image ? [{ url: image }] : undefined } export function buildSiteMetadata(): Metadata { const index = seo.robots?.index ?? true const follow = seo.robots?.follow ?? true const verificationOther = getVerificationOther() const canonicalPath = getCanonicalPath() const openGraphImages = getOpenGraphImages() return { metadataBase: getMetadataBase(), applicationName: seo.siteName, title: { default: seo.defaultTitle, template: seo.titleTemplate, }, description: seo.description, keywords: seo.keywords, authors: [{ name: siteData.company.name }], creator: siteData.company.name, publisher: siteData.company.name, alternates: { canonical: canonicalPath, }, robots: { index, follow, googleBot: { index, follow, }, }, verification: { google: nonEmpty(seo.verification?.google), yandex: nonEmpty(seo.verification?.yandex), other: verificationOther, }, openGraph: { title: seo.defaultTitle, description: seo.description, url: canonicalPath, siteName: seo.siteName, locale: nonEmpty(seo.locale), type: seo.openGraph?.type ?? "website", images: openGraphImages, }, twitter: { card: seo.twitter?.card ?? "summary_large_image", title: seo.defaultTitle, description: seo.description, images: openGraphImages?.map((image) => image.url), site: nonEmpty(seo.twitter?.site), creator: nonEmpty(seo.twitter?.creator), }, } } export function buildRobots(): MetadataRoute.Robots { return { rules: [ { userAgent: "*", allow: "/", }, ], sitemap: `${seo.siteUrl.replace(/\/$/, "")}/sitemap.xml`, host: seo.siteUrl, } } export function buildSitemap(): MetadataRoute.Sitemap { const canonicalPath = getCanonicalPath() const normalizedSiteUrl = seo.siteUrl.replace(/\/$/, "") return [ { url: `${normalizedSiteUrl}${canonicalPath}`, lastModified: new Date(), changeFrequency: "weekly", priority: 1, }, ] }