app/sitemap.ts
Rama main · réplica de lectura del repositorio original.
import type { MetadataRoute } from "next";
import { PUBLIC_ROUTES, publicRouteVariants } from "@/lib/public-routes";
import { getChangelogIndex } from "@/lib/server/changelog";
import { SITE_URL } from "@/lib/site";
import { getPublishedDocumentation } from "@/lib/server/documentation";
import { documentationPath } from "@/lib/documentation-core.mjs";
import { locales } from "@/i18n/config";
/**
* Sitemap for every explicit public locale URL (MIN-88). `/login` and
* `/signup` are accessible without an account but have no independent public
* content, so listing them would dilute the index without adding anything.
*
* Each entry carries its `alternates.languages`, which Next renders in
* `<xhtml:link rel="alternate" hreflang="…">`. It is the obligatory counterpart of
* `hreflang` of `<head>`: declared on one side only, Google signals a “return
* missing” and ignores the group, leaving variants to compete on the same queries.
*
* The route table (and its hand-held `lastModified`) lives in
* `lib/public-routes.ts`: proxy, metadata and links read it
* Also. `priority` and `changeFrequency` stay because Bing is looking at them
* a little more; Google has been ignoring them for a long time.
*/
export const revalidate = 60;
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const latestRelease = (await getChangelogIndex())[0]?.publishedAt;
const publicPages = PUBLIC_ROUTES.flatMap((route) => {
const variants = publicRouteVariants(route);
const languages = Object.fromEntries(
variants.map(({ locale, path }) => [locale, `${SITE_URL}${path}`]),
);
languages["x-default"] = `${SITE_URL}${route.en}`;
return variants.map(({ path }) => ({
url: `${SITE_URL}${path}`,
lastModified: route.key === "changelog" ? latestRelease ?? route.lastModified : route.lastModified,
changeFrequency: "monthly" as const,
priority: route.priority,
alternates: { languages },
}));
});
// Route handlers do not share React's render cache. Load each locale once.
const documentation = new Map(locales.map(locale => [locale,
new Map(getPublishedDocumentation(locale).map(article => [article.id, article]))]));
const articles = [...documentation.get("en")!.values()].flatMap(article => {
const languages = Object.fromEntries(locales.map(locale => [locale, `${SITE_URL}${documentationPath(article.id, locale)}`]));
languages["x-default"] = `${SITE_URL}${documentationPath(article.id, "en")}`;
return locales.map(locale => ({ url: `${SITE_URL}${documentationPath(article.id, locale)}`,
lastModified: documentation.get(locale)!.get(article.id)!.updatedAt,
changeFrequency: "monthly" as const, priority: 0.7, alternates: { languages } }));
});
return [...publicPages, ...articles];
}