/** * Whether search engines may index a deployment. Only the production site belongs * in search results; the testing, staging and branch deployments are copies of it. */ export type Indexing = "indexable" | "blocked"; export type RobotsFile = { readonly rules: { readonly userAgent: string; readonly allow?: string; readonly disallow: string | readonly string[]; }; readonly sitemap?: string; }; export type RobotsMeta = { readonly index: boolean; readonly follow: boolean; readonly googleBot?: { readonly index: boolean; readonly follow: boolean; readonly "max-snippet": number; readonly "max-image-preview": "large"; readonly "max-video-preview": number; }; }; const BLOCKED_TARGETS: readonly unknown[] = ["testing", "staging", "branch"]; /** Paths that proxy analytics and error reports; nothing there is a page. */ const PROXY_PATHS: readonly string[] = ["/stats/", "/errors/"]; /** * Only the deployments known to be copies are blocked. Anything else stays * indexable, so a production build that lost its target does not drop out of * the search index. */ export function indexingFor(target: unknown): Indexing { return BLOCKED_TARGETS.includes(target) ? "blocked" : "indexable"; } export function robotsFile(indexing: Indexing, siteUrl: string): RobotsFile { if (indexing === "blocked") { return { rules: { userAgent: "*", disallow: "/" } }; } return { rules: { userAgent: "*", allow: "/", disallow: PROXY_PATHS }, sitemap: `${siteUrl}/sitemap.xml`, }; } /** The robots meta tag. Google gets no limit on snippet length or image preview size. */ export function robotsMeta(indexing: Indexing): RobotsMeta { if (indexing === "blocked") return { index: false, follow: false }; return { index: true, follow: true, googleBot: { index: true, follow: true, "max-snippet": -1, "max-image-preview": "large", "max-video-preview": -1, }, }; }