Skip to content
Merged
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
33 changes: 26 additions & 7 deletions packages/gitbook/src/components/SitePage/SitePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,21 +160,36 @@ export async function generateSitePageMetadata(props: SitePageProps): Promise<Me

const canonical = (
pageMetaLinks?.canonical
? new URL(pageMetaLinks.canonical).toString()
? new URL(
// If the canonical link is an absolute URL, use it as is.
URL.canParse(pageMetaLinks.canonical)
? pageMetaLinks.canonical
: linker.toAbsoluteURL(pageMetaLinks.canonical)
).toString()
: // If no canonical is set, use the current page URL (default case)
linker.toAbsoluteURL(linker.toPathForPage({ pages: revision.pages, page }))
).replace(/\/+$/, ''); // Trim trailing slashes in canonical URL to match the redirect behavior

const languages = pageMetaLinks?.alternates.reduce(
const alternates = pageMetaLinks?.alternates.reduce<{
languages: Record<string, string>;
generic: Array<{
title?: string;
url: string;
}>;
}>(
(acc, alt) => {
// TODO: We can only add language alternates for now as that's all Next.js API supports
// generic alternates are not supported yet
if (alt.space?.language) {
acc[alt.space.language] = alt.href;
acc.languages[alt.space.language] = URL.canParse(alt.href)
? alt.href
: linker.toAbsoluteURL(alt.href);
} else {
acc.generic.push({
url: URL.canParse(alt.href) ? alt.href : linker.toAbsoluteURL(alt.href),
});
}
return acc;
},
{} as Record<TranslationLanguage, string>
{ languages: {}, generic: [] }
);

return {
Expand All @@ -189,9 +204,13 @@ export async function generateSitePageMetadata(props: SitePageProps): Promise<Me
description: page.description ?? '',
alternates: {
canonical,
languages,
languages: alternates?.languages,
types: {
'text/markdown': `${linker.toAbsoluteURL(linker.toPathInSpace(page.path))}.md`,
// Currently it will output with an empty "type" like <link rel="alternate" href="..." type />
// Team at Vercel is aware of this and will ensure it will be omitted when the value is empty in future versions of Next.js
// https://gitbook.slack.com/archives/C04K6MV5W1K/p1763034072958419?thread_ts=1762937203.511629&cid=C04K6MV5W1K
...(alternates?.generic ? { '': alternates?.generic } : {}),
},
},
openGraph: {
Expand Down
Loading