import { OG_HEIGHT, OG_WIDTH, SITE_OG_ALT, siteOgPath } from "~/lib/og"; type SeoInput = { title: string; /** Tab title when it differs from the shared title (e.g. private repositories share only a generic one). */ documentTitle?: string; description?: string; keywords?: string; /** Forge origin (PUBLIC_URL); relative `path` and `image` are resolved against it. */ origin?: string; /** Canonical page path, e.g. `/owner/repo`. */ path?: string; /** Card path from ~/lib/og; defaults to the site card. */ image?: string; imageAlt?: string; }; function absolute(origin: string | undefined, path: string): string { if (!origin) return path; return new URL(path, origin).toString(); } export const seo = ({ title, documentTitle, description, keywords, origin, path, image, imageAlt }: SeoInput) => { const imageUrl = absolute(origin, image ?? siteOgPath()); const alt = imageAlt ?? (image ? title : SITE_OG_ALT); const tags = [ { title: documentTitle ?? title }, { name: "description", content: description }, { name: "keywords", content: keywords }, { name: "twitter:card", content: "summary_large_image" }, { name: "twitter:title", content: title }, { name: "twitter:description", content: description }, { name: "twitter:image", content: imageUrl }, { name: "twitter:image:alt", content: alt }, { property: "og:type", content: "website" }, { property: "og:site_name", content: "gitfor" }, { property: "og:title", content: title }, { property: "og:description", content: description }, ...(path ? [{ property: "og:url", content: absolute(origin, path) }] : []), { property: "og:image", content: imageUrl }, { property: "og:image:width", content: String(OG_WIDTH) }, { property: "og:image:height", content: String(OG_HEIGHT) }, { property: "og:image:alt", content: alt }, ]; return tags.filter((tag) => !("content" in tag) || tag.content !== undefined); };