feat(seo): add sitemap generator
This commit is contained in:
+121
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* schema.org JSON-LD builders for content pages.
|
||||
*
|
||||
* Emitting structured data gives Google rich-result eligibility and gives
|
||||
* LLMs clean, linked facts (who authored it, which work it belongs to, when).
|
||||
* Every page renders one of these via the <JsonLd> component
|
||||
* (components/seo/json-ld.tsx).
|
||||
*
|
||||
* The author entity carries `sameAs` links to Wikipedia/Wikidata so search
|
||||
* engines and LLMs can resolve it to the canonical Śrīla Prabhupāda entity.
|
||||
*/
|
||||
|
||||
const SITE_URL =
|
||||
process.env.NEXT_PUBLIC_SITE_URL || "https://prabhupadasays.org";
|
||||
|
||||
const AUTHOR = {
|
||||
"@type": "Person",
|
||||
name: "His Divine Grace A.C. Bhaktivedanta Swami Prabhupāda",
|
||||
alternateName: "A.C. Bhaktivedanta Swami Prabhupāda",
|
||||
sameAs: [
|
||||
"https://en.wikipedia.org/wiki/A._C._Bhaktivedanta_Swami_Prabhupada",
|
||||
"https://www.wikidata.org/wiki/Q318439",
|
||||
],
|
||||
} as const;
|
||||
|
||||
const PUBLISHER = {
|
||||
"@type": "Organization",
|
||||
name: "Prabhupāda Says",
|
||||
url: SITE_URL,
|
||||
} as const;
|
||||
|
||||
function abs(path: string): string {
|
||||
return path.startsWith("http") ? path : `${SITE_URL}${path}`;
|
||||
}
|
||||
|
||||
/** Trim a (possibly HTML) string to a clean plain-text description. */
|
||||
function clean(text: string | undefined, max = 300): string | undefined {
|
||||
if (!text) return undefined;
|
||||
const plain = text
|
||||
.replace(/<[^>]+>/g, "")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
if (!plain) return undefined;
|
||||
return plain.length > max ? plain.slice(0, max - 1).trimEnd() + "…" : plain;
|
||||
}
|
||||
|
||||
/**
|
||||
* An Article — used for every leaf content page (verses, letters, transcripts,
|
||||
* prose chapters, essays, magazine/journal pages). `isPartOf` names the parent
|
||||
* work (book/magazine); `datePublished` must be ISO 8601 (omit otherwise).
|
||||
*/
|
||||
export function articleJsonLd(params: {
|
||||
path: string;
|
||||
headline: string;
|
||||
description?: string;
|
||||
datePublished?: string;
|
||||
isPartOf?: string;
|
||||
}) {
|
||||
const url = abs(params.path);
|
||||
const obj: Record<string, unknown> = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Article",
|
||||
headline: params.headline,
|
||||
author: AUTHOR,
|
||||
publisher: PUBLISHER,
|
||||
inLanguage: "en",
|
||||
isAccessibleForFree: true,
|
||||
url,
|
||||
mainEntityOfPage: url,
|
||||
};
|
||||
const desc = clean(params.description);
|
||||
if (desc) obj.description = desc;
|
||||
if (params.datePublished) obj.datePublished = params.datePublished;
|
||||
if (params.isPartOf) {
|
||||
obj.isPartOf = { "@type": "Book", name: params.isPartOf, author: AUTHOR };
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/** A public profile page → ProfilePage wrapping the Person. */
|
||||
export function profileJsonLd(params: {
|
||||
path: string;
|
||||
name: string;
|
||||
handle: string;
|
||||
image?: string | null;
|
||||
}) {
|
||||
const url = abs(params.path);
|
||||
const person: Record<string, unknown> = {
|
||||
"@type": "Person",
|
||||
name: params.name,
|
||||
alternateName: `@${params.handle}`,
|
||||
url,
|
||||
};
|
||||
if (params.image) person.image = params.image;
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "ProfilePage",
|
||||
url,
|
||||
mainEntity: person,
|
||||
};
|
||||
}
|
||||
|
||||
/** A Book — used for book landing pages. */
|
||||
export function bookJsonLd(params: {
|
||||
path: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
}) {
|
||||
const obj: Record<string, unknown> = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Book",
|
||||
name: params.name,
|
||||
author: AUTHOR,
|
||||
publisher: PUBLISHER,
|
||||
inLanguage: "en",
|
||||
url: abs(params.path),
|
||||
};
|
||||
const desc = clean(params.description);
|
||||
if (desc) obj.description = desc;
|
||||
return obj;
|
||||
}
|
||||
Reference in New Issue
Block a user