Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ const config = (async (): Promise<Config> => {
rehypePlugins: [rehypeNameToId],
},
sitemap: {
// Emit a per-URL <lastmod> (YYYY-MM-DD). This is the freshness
// signal search engines actually act on for crawl scheduling and
// discovery of new/updated pages (priority/changefreq are weak
// hints). Defaults to null in the plugin, so it must be set.
lastmod: 'date',
changefreq: 'weekly',
priority: 0.5,
filename: 'sitemap.xml',
Expand Down Expand Up @@ -225,8 +230,11 @@ const config = (async (): Promise<Config> => {
if (path === '/blog/' || path === '/blog') {
return { ...item, priority: 0.8, changefreq: 'daily' };
}
// Pagination & tag archives — thin, paginated, near-duplicate
// listing pages with no standalone ranking value. Keep them
// out of the sitemap so crawl budget goes to real content.
if (path.startsWith('/blog/page/') || path.startsWith('/blog/tags/')) {
return { ...item, priority: 0.3, changefreq: 'weekly' };
return [];
}
return { ...item, priority: 0.7, changefreq: 'monthly' };
}
Expand All @@ -246,9 +254,10 @@ const config = (async (): Promise<Config> => {
if (path === '/community/' || path === '/community') {
return { ...item, priority: 0.7, changefreq: 'monthly' };
}
// Pagination/tag archives — low priority
// Pagination & tag archives — thin listing pages, dropped
// from the sitemap (see blog section above).
if (path.startsWith('/community/page/') || path.startsWith('/community/tags/')) {
return { ...item, priority: 0.3, changefreq: 'weekly' };
return [];
}
// Individual meeting notes & transcripts
return { ...item, priority: 0.5, changefreq: 'monthly' };
Expand All @@ -274,9 +283,11 @@ const config = (async (): Promise<Config> => {
return { ...item, priority: 0.3, changefreq: 'monthly' };
}

// 0.82 docs — already disallowed in robots.txt, minimal priority
// 0.82 docs — disallowed in robots.txt, so they must NOT be in
// the sitemap: listing a robots-blocked URL is a contradictory
// signal that Search Console flags. Drop them entirely.
if (path.startsWith('/docs/0.82/')) {
return { ...item, priority: 0.1, changefreq: 'yearly' };
return [];
}

// Everything else (standalone pages like /contact, etc.)
Expand Down
25 changes: 25 additions & 0 deletions plugins/people-pages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,15 +355,37 @@ export default async function peoplePagesPlugin(
const { createData, addRoute, setGlobalData } = actions;
setGlobalData(content.globalData);

// Most recent appearance date for a profile, as a millisecond
// timestamp. These routes are synthesized via addRoute and have no
// source file, so the sitemap plugin can't derive <lastmod> on its
// own — we feed it `metadata.lastUpdatedAt` (which it reads before
// falling back to a git lookup). A profile's content effectively
// changes when the person gains a new blog post or meeting, so the
// newest appearance is the honest freshness signal.
const latestAppearanceMs = (route: PersonPageData): number | undefined => {
const ms = [...route.blog_posts, ...route.community_meetings]
.map((a) => new Date(a.date).getTime())
.filter((t) => Number.isFinite(t));
return ms.length ? Math.max(...ms) : undefined;
};

let indexLatestMs: number | undefined;

for (const route of content.routes) {
const data = await createData(
`person-${route.person.slug}.json`,
JSON.stringify(route, null, 2),
);
const lastUpdatedAt = latestAppearanceMs(route);
if (lastUpdatedAt !== undefined) {
indexLatestMs =
indexLatestMs === undefined ? lastUpdatedAt : Math.max(indexLatestMs, lastUpdatedAt);
}
addRoute({
path: `/people/${route.person.slug}/`,
component: '@site/src/components/PersonPage/index.tsx',
modules: { data },
...(lastUpdatedAt !== undefined && { metadata: { lastUpdatedAt } }),
exact: true,
});
}
Expand All @@ -378,6 +400,9 @@ export default async function peoplePagesPlugin(
path: '/people/',
component: '@site/src/components/PeopleIndexPage/index.tsx',
modules: { data: indexData },
// The directory listing is "current" as of its most recently
// active profile.
...(indexLatestMs !== undefined && { metadata: { lastUpdatedAt: indexLatestMs } }),
exact: true,
});
},
Expand Down
Loading