Only the production deployment may be indexed; testing and preview builds answer with noindex and a closed robots file. The homepage now describes the person, the services and the FAQ as structured data, and title, description and the 404 page are written for what people search for. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
67 lines
1.9 KiB
TypeScript
67 lines
1.9 KiB
TypeScript
/**
|
|
* 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,
|
|
},
|
|
};
|
|
}
|