-
Notifications
You must be signed in to change notification settings - Fork 16.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
120 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import React from 'react'; | ||
import Head from '@docusaurus/Head'; | ||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; | ||
import useBaseUrl from '@docusaurus/useBaseUrl'; | ||
import {PageMetadata, useThemeConfig} from '@docusaurus/theme-common'; | ||
import { | ||
DEFAULT_SEARCH_TAG, | ||
useAlternatePageUtils, | ||
keyboardFocusedClassName, | ||
} from '@docusaurus/theme-common/internal'; | ||
import {useLocation} from '@docusaurus/router'; | ||
import {applyTrailingSlash} from '@docusaurus/utils-common'; | ||
import SearchMetadata from '@theme/SearchMetadata'; | ||
// TODO move to SiteMetadataDefaults or theme-common ? | ||
// Useful for i18n/SEO | ||
// See https://developers.google.com/search/docs/advanced/crawling/localized-versions | ||
// See https://github.com/facebook/docusaurus/issues/3317 | ||
function AlternateLangHeaders() { | ||
const { | ||
i18n: {defaultLocale, localeConfigs}, | ||
} = useDocusaurusContext(); | ||
const alternatePageUtils = useAlternatePageUtils(); | ||
// Note: it is fine to use both "x-default" and "en" to target the same url | ||
// See https://www.searchviu.com/en/multiple-hreflang-tags-one-url/ | ||
return ( | ||
<Head> | ||
{Object.entries(localeConfigs).map(([locale, {htmlLang}]) => ( | ||
<link | ||
key={locale} | ||
rel="alternate" | ||
href={alternatePageUtils.createUrl({ | ||
locale, | ||
fullyQualified: true, | ||
})} | ||
hrefLang={htmlLang} | ||
/> | ||
))} | ||
<link | ||
rel="alternate" | ||
href={alternatePageUtils.createUrl({ | ||
locale: defaultLocale, | ||
fullyQualified: true, | ||
})} | ||
hrefLang="x-default" | ||
/> | ||
</Head> | ||
); | ||
} | ||
// Default canonical url inferred from current page location pathname | ||
function useDefaultCanonicalUrl() { | ||
const { | ||
siteConfig: {url: siteUrl, baseUrl, trailingSlash}, | ||
} = useDocusaurusContext(); | ||
// TODO using useLocation().pathname is not a super idea | ||
// See https://github.com/facebook/docusaurus/issues/9170 | ||
const {pathname} = useLocation(); | ||
const baseUrlPathname = useBaseUrl(pathname); | ||
const canonicalPathname = applyTrailingSlash(baseUrlPathname, { | ||
trailingSlash, | ||
baseUrl, | ||
}); | ||
const canonicalPathnameNoVersion = canonicalPathname.startsWith('/v0.') ? "/"+canonicalPathname.split('/').slice(2).join('/') : canonicalPathname; | ||
return siteUrl + canonicalPathnameNoVersion; | ||
} | ||
|
||
// TODO move to SiteMetadataDefaults or theme-common ? | ||
function CanonicalUrlHeaders({permalink}) { | ||
const { | ||
siteConfig: {url: siteUrl}, | ||
} = useDocusaurusContext(); | ||
const defaultCanonicalUrl = useDefaultCanonicalUrl(); | ||
const canonicalUrl = permalink | ||
? `${siteUrl}${permalink}` | ||
: defaultCanonicalUrl; | ||
return ( | ||
<Head> | ||
<meta property="og:url" content={canonicalUrl} /> | ||
<link rel="canonical" href={canonicalUrl} /> | ||
</Head> | ||
); | ||
} | ||
export default function SiteMetadata() { | ||
const { | ||
i18n: {currentLocale}, | ||
} = useDocusaurusContext(); | ||
// TODO maybe move these 2 themeConfig to siteConfig? | ||
// These seems useful for other themes as well | ||
const {metadata, image: defaultImage} = useThemeConfig(); | ||
return ( | ||
<> | ||
<Head> | ||
<meta name="twitter:card" content="summary_large_image" /> | ||
{/* The keyboard focus class name need to be applied when SSR so links | ||
are outlined when JS is disabled */} | ||
<body className={keyboardFocusedClassName} /> | ||
</Head> | ||
|
||
{defaultImage && <PageMetadata image={defaultImage} />} | ||
|
||
<CanonicalUrlHeaders /> | ||
|
||
<AlternateLangHeaders /> | ||
|
||
<SearchMetadata tag={DEFAULT_SEARCH_TAG} locale={currentLocale} /> | ||
|
||
{/* | ||
It's important to have an additional <Head> element here, as it allows | ||
react-helmet to override default metadata values set in previous <Head> | ||
like "twitter:card". In same Head, the same meta would appear twice | ||
instead of overriding. | ||
*/} | ||
<Head> | ||
{/* Yes, "metadatum" is the grammatically correct term */} | ||
{metadata.map((metadatum, i) => ( | ||
<meta key={i} {...metadatum} /> | ||
))} | ||
</Head> | ||
</> | ||
); | ||
} |