Files
2025-07-19 19:59:04 +08:00

123 lines
4.6 KiB
JavaScript

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { useBaseUrlUtils } from '@docusaurus/useBaseUrl';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import { useBlogMetadata } from '@docusaurus/plugin-content-blog/client';
import { useBlogPost } from './contexts';
const convertDate = (dateMs) => new Date(dateMs).toISOString();
function getBlogPost(blogPostContent, siteConfig, withBaseUrl) {
const { assets, frontMatter, metadata } = blogPostContent;
const { date, title, description, lastUpdatedAt } = metadata;
const image = assets.image ?? frontMatter.image;
const keywords = frontMatter.keywords ?? [];
const blogUrl = `${siteConfig.url}${metadata.permalink}`;
const dateModified = lastUpdatedAt ? convertDate(lastUpdatedAt) : undefined;
return {
'@type': 'BlogPosting',
'@id': blogUrl,
mainEntityOfPage: blogUrl,
url: blogUrl,
headline: title,
name: title,
description,
datePublished: date,
...(dateModified ? { dateModified } : {}),
...getAuthor(metadata.authors),
...getImage(image, withBaseUrl, title),
...(keywords ? { keywords } : {}),
};
}
function getAuthor(authors) {
const authorsStructuredData = authors.map(createPersonStructuredData);
return {
author: authorsStructuredData.length === 1
? authorsStructuredData[0]
: authorsStructuredData,
};
}
function getImage(image, withBaseUrl, title) {
return image
? {
image: createImageStructuredData({
imageUrl: withBaseUrl(image, { absolute: true }),
caption: `title image for the blog post: ${title}`,
}),
}
: {};
}
export function useBlogListPageStructuredData(props) {
const { siteConfig } = useDocusaurusContext();
const { withBaseUrl } = useBaseUrlUtils();
const { metadata: { blogDescription, blogTitle, permalink }, } = props;
const url = `${siteConfig.url}${permalink}`;
// details on structured data support: https://schema.org/Blog
return {
'@context': 'https://schema.org',
'@type': 'Blog',
'@id': url,
mainEntityOfPage: url,
headline: blogTitle,
description: blogDescription,
blogPost: props.items.map((blogItem) => getBlogPost(blogItem.content, siteConfig, withBaseUrl)),
};
}
export function useBlogPostStructuredData() {
const blogMetadata = useBlogMetadata();
const { assets, metadata } = useBlogPost();
const { siteConfig } = useDocusaurusContext();
const { withBaseUrl } = useBaseUrlUtils();
const { date, title, description, frontMatter, lastUpdatedAt } = metadata;
const image = assets.image ?? frontMatter.image;
const keywords = frontMatter.keywords ?? [];
const dateModified = lastUpdatedAt ? convertDate(lastUpdatedAt) : undefined;
const url = `${siteConfig.url}${metadata.permalink}`;
// details on structured data support: https://schema.org/BlogPosting
// BlogPosting is one of the structured data types that Google explicitly
// supports: https://developers.google.com/search/docs/appearance/structured-data/article#structured-data-type-definitions
return {
'@context': 'https://schema.org',
'@type': 'BlogPosting',
'@id': url,
mainEntityOfPage: url,
url,
headline: title,
name: title,
description,
datePublished: date,
...(dateModified ? { dateModified } : {}),
...getAuthor(metadata.authors),
...getImage(image, withBaseUrl, title),
...(keywords ? { keywords } : {}),
isPartOf: {
'@type': 'Blog',
'@id': `${siteConfig.url}${blogMetadata.blogBasePath}`,
name: blogMetadata.blogTitle,
},
};
}
/** @returns A {@link https://schema.org/Person} constructed from the {@link Author} */
function createPersonStructuredData(author) {
return {
'@type': 'Person',
...(author.name ? { name: author.name } : {}),
...(author.title ? { description: author.title } : {}),
...(author.url ? { url: author.url } : {}),
...(author.email ? { email: author.email } : {}),
...(author.imageURL ? { image: author.imageURL } : {}),
};
}
/** @returns A {@link https://schema.org/ImageObject} */
function createImageStructuredData({ imageUrl, caption, }) {
return {
'@type': 'ImageObject',
'@id': imageUrl,
url: imageUrl,
contentUrl: imageUrl,
caption,
};
}