From e0da5cf0b93050ce8a2f95921478d1f51bf3d75a Mon Sep 17 00:00:00 2001 From: johnnyreilly Date: Mon, 25 Dec 2023 06:33:07 +0000 Subject: [PATCH 01/22] feat: JSON-LD structured data implementation for blog --- .../src/blogUtils.ts | 5 + .../src/plugin-content-blog.d.ts | 47 +++++++++ .../src/theme/BlogLayout/index.tsx | 4 +- .../BlogListPage/StructuredData/index.tsx | 95 +++++++++++++++++++ .../src/theme/BlogListPage/index.tsx | 2 + .../theme/BlogPostItem/Container/index.tsx | 27 +----- .../src/theme/BlogPostItem/Content/index.tsx | 3 +- .../BlogPostItem/Header/Author/index.tsx | 23 +---- .../theme/BlogPostItem/Header/Info/index.tsx | 6 +- .../theme/BlogPostItem/Header/Title/index.tsx | 10 +- .../BlogPostPage/StructuredData/index.tsx | 84 ++++++++++++++++ .../src/theme/BlogPostPage/index.tsx | 10 +- website/docusaurus.config.js | 2 + 13 files changed, 255 insertions(+), 63 deletions(-) create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx create mode 100644 packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index 36028495381c..8222431490ee 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -217,6 +217,7 @@ async function processBlogSourceFile( truncateMarker, showReadingTime, editUrl, + blogTitle: baseBlogTitle, } = options; // Lookup in localized folder in priority @@ -314,6 +315,8 @@ async function processBlogSourceFile( return undefined; } + const baseBlogPermalink = normalizeUrl([baseUrl, routeBasePath]); + const tagsBasePath = normalizeUrl([ baseUrl, routeBasePath, @@ -325,6 +328,8 @@ async function processBlogSourceFile( id: slug, metadata: { permalink, + baseBlogPermalink, + baseBlogTitle, editUrl: getBlogEditUrl(), source: aliasedSource, title, diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index b1915f75196f..6d9fa2db921b 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -197,6 +197,10 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the readonly formattedDate: string; /** Full link including base URL. */ readonly permalink: string; + /** the path to the base of the blog */ + readonly baseBlogPermalink: string; + /** title of the overall blog */ + readonly baseBlogTitle: string; /** * Description used in the meta. Could be an empty string (empty content) */ @@ -552,6 +556,27 @@ declare module '@theme/BlogPostPage/Metadata' { export default function BlogPostPageMetadata(): JSX.Element; } +declare module '@theme/BlogPostPage/StructuredData' { + import type { + BlogPostFrontMatter, + PropBlogPostContent, + } from '@docusaurus/plugin-content-blog'; + + export type FrontMatter = BlogPostFrontMatter; + + export type Assets = PropBlogPostContent['assets']; + + export type Metadata = PropBlogPostContent['metadata']; + + export interface Props { + readonly assets: Assets; + readonly frontMatter: FrontMatter; + readonly metadata: Metadata; + } + + export default function BlogPostStructuredData(props: Props): JSX.Element; +} + declare module '@theme/BlogListPage' { import type {Content} from '@theme/BlogPostPage'; import type { @@ -574,6 +599,28 @@ declare module '@theme/BlogListPage' { export default function BlogListPage(props: Props): JSX.Element; } +declare module '@theme/BlogListPage/StructuredData' { + import type {Content} from '@theme/BlogPostPage'; + import type { + BlogSidebar, + BlogPaginatedMetadata, + } from '@docusaurus/plugin-content-blog'; + + export interface Props { + /** Blog sidebar. */ + readonly sidebar: BlogSidebar; + /** Metadata of the current listing page. */ + readonly metadata: BlogPaginatedMetadata; + /** + * Array of blog posts included on this page. Every post's metadata is also + * available. + */ + readonly items: readonly {readonly content: Content}[]; + } + + export default function BlogListPageStructuredData(props: Props): JSX.Element; +} + declare module '@theme/BlogTagsListPage' { import type {BlogSidebar} from '@docusaurus/plugin-content-blog'; import type {TagsListItem} from '@docusaurus/utils'; diff --git a/packages/docusaurus-theme-classic/src/theme/BlogLayout/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogLayout/index.tsx index 60f9b5e2833f..45dbbb2d2546 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogLayout/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogLayout/index.tsx @@ -25,9 +25,7 @@ export default function BlogLayout(props: Props): JSX.Element { className={clsx('col', { 'col--7': hasSidebar, 'col--9 col--offset-1': !hasSidebar, - })} - itemScope - itemType="https://schema.org/Blog"> + })}> {children} {toc &&
{toc}
} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx new file mode 100644 index 000000000000..13cda428422a --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx @@ -0,0 +1,95 @@ +/** + * 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 React from 'react'; +import {useBaseUrlUtils} from '@docusaurus/useBaseUrl'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import type {Props} from '@theme/BlogListPage/StructuredData'; + +export default function BlogListPageStructuredData(props: Props): JSX.Element { + 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 + const blogStructuredData = { + '@context': 'https://schema.org', + '@type': 'Blog', + '@id': url, + mainEntityOfPage: url, + headline: blogTitle, + description: blogDescription, + blogPost: props.items.map((blogItem) => { + const { + content: {assets, frontMatter, metadata}, + } = blogItem; + const {date, title, description} = metadata; + + const image = assets.image ?? frontMatter.image; + const keywords = frontMatter.keywords ?? []; + + // an array of https://schema.org/Person + const authorsStructuredData = metadata.authors.map((author) => ({ + '@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} : {}), + })); + + const blogUrl = `${siteConfig.url}${metadata.permalink}`; + const imageUrl = image ? withBaseUrl(image, {absolute: true}) : undefined; + + return { + '@type': 'BlogPosting', + '@id': blogUrl, + mainEntityOfPage: blogUrl, + url: blogUrl, + headline: title, + name: title, + description, + datePublished: date, + author: + authorsStructuredData.length === 1 + ? authorsStructuredData[0] + : authorsStructuredData, + ...(image + ? { + // details on structured data support: https://schema.org/ImageObject + image: { + '@type': 'ImageObject', + '@id': imageUrl, + url: imageUrl, + contentUrl: imageUrl, + caption: `title image for the blog post: ${title}`, + }, + } + : {}), + ...(keywords ? {keywords} : {}), + }; + }), + }; + + return ( + + Some content... @@ -115,6 +113,7 @@ For JSX pages, you can use the Docusaurus [``](docusaurus-core.mdx#head) c ```jsx title="my-react-page.jsx" import React from 'react'; import Layout from '@theme/Layout'; +import StructuredData from '@theme/StructuredData'; import Head from '@docusaurus/Head'; export default function page() { @@ -124,15 +123,15 @@ export default function page() { - + }} + /> {/* ... */} From c21d57ab881d13d1145fdd9288472ff9ea69f4b1 Mon Sep 17 00:00:00 2001 From: johnnyreilly Date: Fri, 9 Feb 2024 08:07:05 +0000 Subject: [PATCH 07/22] feat: add structuredDataUtils --- .../BlogListPage/StructuredData/index.tsx | 27 +++++--------- .../BlogPostPage/StructuredData/index.tsx | 24 ++++-------- packages/docusaurus-theme-common/src/index.ts | 5 +++ .../src/utils/structuredDataUtils.ts | 37 +++++++++++++++++++ 4 files changed, 60 insertions(+), 33 deletions(-) create mode 100644 packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts diff --git a/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx index 18457be02633..298193500286 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx @@ -8,6 +8,10 @@ import React from 'react'; import {useBaseUrlUtils} from '@docusaurus/useBaseUrl'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import { + makeImageStructuredData, + makePersonStructuredData, +} from '@docusaurus/theme-common'; import type {Props} from '@theme/BlogListPage/StructuredData'; import StructuredData from '@theme/StructuredData'; @@ -38,18 +42,11 @@ export default function BlogListPageStructuredData(props: Props): JSX.Element { const image = assets.image ?? frontMatter.image; const keywords = frontMatter.keywords ?? []; - // an array of https://schema.org/Person - const authorsStructuredData = metadata.authors.map((author) => ({ - '@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} : {}), - })); + const authorsStructuredData = metadata.authors.map( + makePersonStructuredData, + ); const blogUrl = `${siteConfig.url}${metadata.permalink}`; - const imageUrl = image ? withBaseUrl(image, {absolute: true}) : undefined; return { '@type': 'BlogPosting', @@ -66,14 +63,10 @@ export default function BlogListPageStructuredData(props: Props): JSX.Element { : authorsStructuredData, ...(image ? { - // details on structured data support: https://schema.org/ImageObject - image: { - '@type': 'ImageObject', - '@id': imageUrl, - url: imageUrl, - contentUrl: imageUrl, + image: makeImageStructuredData({ + imageUrl: withBaseUrl(image, {absolute: true}), caption: `title image for the blog post: ${title}`, - }, + }), } : {}), ...(keywords ? {keywords} : {}), diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx index 050395f39a68..7cd8cc08e1c2 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx @@ -8,6 +8,10 @@ import React from 'react'; import {useBaseUrlUtils} from '@docusaurus/useBaseUrl'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import { + makeImageStructuredData, + makePersonStructuredData, +} from '@docusaurus/theme-common'; import type {Props} from '@theme/BlogPostPage/StructuredData'; import StructuredData from '@theme/StructuredData'; @@ -21,17 +25,9 @@ export default function BlogPostStructuredData(props: Props): JSX.Element { const keywords = frontMatter.keywords ?? []; // an array of https://schema.org/Person - const authorsStructuredData = metadata.authors.map((author) => ({ - '@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} : {}), - })); + const authorsStructuredData = metadata.authors.map(makePersonStructuredData); const url = `${siteConfig.url}${metadata.permalink}`; - const imageUrl = image ? withBaseUrl(image, {absolute: true}) : undefined; // details on structured data support: https://schema.org/BlogPosting // BlogPosting is one of the structured data types that Google explicitly @@ -52,14 +48,10 @@ export default function BlogPostStructuredData(props: Props): JSX.Element { : authorsStructuredData, ...(image ? { - // details on structured data support: https://schema.org/ImageObject - image: { - '@type': 'ImageObject', - '@id': imageUrl, - url: imageUrl, - contentUrl: imageUrl, + image: makeImageStructuredData({ + imageUrl: withBaseUrl(image, {absolute: true}), caption: `title image for the blog post: ${title}`, - }, + }), } : {}), ...(keywords ? {keywords} : {}), diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index 9dfbf59bc81c..22e7512e2abc 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -39,6 +39,11 @@ export { filterDocCardListItems, } from './utils/docsUtils'; +export { + makeImageStructuredData, + makePersonStructuredData, +} from './utils/structuredDataUtils'; + export {usePluralForm} from './utils/usePluralForm'; export {useCollapsible, Collapsible} from './components/Collapsible'; diff --git a/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts b/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts new file mode 100644 index 000000000000..d00491288378 --- /dev/null +++ b/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts @@ -0,0 +1,37 @@ +/** + * 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 type {Author} from '@docusaurus/plugin-content-blog'; + +/** @returns A {@link https://schema.org/Person} constructed from the {@link Author} */ +export function makePersonStructuredData(author: Author): object { + 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} */ +export function makeImageStructuredData({ + imageUrl, + caption, +}: { + imageUrl: string; + caption: string; +}): object { + return { + '@type': 'ImageObject', + '@id': imageUrl, + url: imageUrl, + contentUrl: imageUrl, + caption, + }; +} From c5961eaa73bf3a57b7b6c52118ba7f7a6ac444c9 Mon Sep 17 00:00:00 2001 From: johnnyreilly Date: Fri, 9 Feb 2024 09:34:27 +0000 Subject: [PATCH 08/22] feat: add schema-dts --- .../src/theme/BlogListPage/StructuredData/index.tsx | 3 ++- .../src/theme/BlogPostPage/StructuredData/index.tsx | 3 ++- packages/docusaurus-theme-common/package.json | 3 ++- .../docusaurus-theme-common/src/utils/structuredDataUtils.ts | 5 +++-- yarn.lock | 5 +++++ 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx index 298193500286..9ad871105d0c 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx @@ -14,6 +14,7 @@ import { } from '@docusaurus/theme-common'; import type {Props} from '@theme/BlogListPage/StructuredData'; import StructuredData from '@theme/StructuredData'; +import type {Blog, WithContext} from 'schema-dts'; export default function BlogListPageStructuredData(props: Props): JSX.Element { const {siteConfig} = useDocusaurusContext(); @@ -26,7 +27,7 @@ export default function BlogListPageStructuredData(props: Props): JSX.Element { const url = `${siteConfig.url}${permalink}`; // details on structured data support: https://schema.org/Blog - const blogStructuredData = { + const blogStructuredData: WithContext = { '@context': 'https://schema.org', '@type': 'Blog', '@id': url, diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx index 7cd8cc08e1c2..f7e5fe9bd3ee 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx @@ -14,6 +14,7 @@ import { } from '@docusaurus/theme-common'; import type {Props} from '@theme/BlogPostPage/StructuredData'; import StructuredData from '@theme/StructuredData'; +import type {BlogPosting, WithContext} from 'schema-dts'; export default function BlogPostStructuredData(props: Props): JSX.Element { const {siteConfig} = useDocusaurusContext(); @@ -32,7 +33,7 @@ export default function BlogPostStructuredData(props: Props): JSX.Element { // 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 - const blogPostStructuredData = { + const blogPostStructuredData: WithContext = { '@context': 'https://schema.org', '@type': 'BlogPosting', '@id': url, diff --git a/packages/docusaurus-theme-common/package.json b/packages/docusaurus-theme-common/package.json index 6c439ebab211..7e6effa0abb8 100644 --- a/packages/docusaurus-theme-common/package.json +++ b/packages/docusaurus-theme-common/package.json @@ -50,7 +50,8 @@ "@docusaurus/core": "3.0.0", "@docusaurus/types": "3.0.0", "fs-extra": "^11.1.1", - "lodash": "^4.17.21" + "lodash": "^4.17.21", + "schema-dts": "^1.1.2" }, "peerDependencies": { "react": "^18.0.0", diff --git a/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts b/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts index d00491288378..54c7174427d2 100644 --- a/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts @@ -6,9 +6,10 @@ */ import type {Author} from '@docusaurus/plugin-content-blog'; +import type {Person, ImageObject} from 'schema-dts'; /** @returns A {@link https://schema.org/Person} constructed from the {@link Author} */ -export function makePersonStructuredData(author: Author): object { +export function makePersonStructuredData(author: Author): Person { return { '@type': 'Person', ...(author.name ? {name: author.name} : {}), @@ -26,7 +27,7 @@ export function makeImageStructuredData({ }: { imageUrl: string; caption: string; -}): object { +}): ImageObject { return { '@type': 'ImageObject', '@id': imageUrl, diff --git a/yarn.lock b/yarn.lock index ab5360ca4a31..7c4d16690734 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14548,6 +14548,11 @@ scheduler@^0.23.0: dependencies: loose-envify "^1.1.0" +schema-dts@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/schema-dts/-/schema-dts-1.1.2.tgz#82ccf71b5dcb80065a1cc5941888507a4ce1e44b" + integrity sha512-MpNwH0dZJHinVxk9bT8XUdjKTxMYrA5bLtrrGmFA6PTLwlOKnhi67XoRd6/ty+Djt6ZC0slR57qFhZDNMI6DhQ== + schema-utils@2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" From 4cc7a50e11614d37073d143677f633ce92ba2033 Mon Sep 17 00:00:00 2001 From: johnnyreilly Date: Fri, 9 Feb 2024 09:49:46 +0000 Subject: [PATCH 09/22] fix: single blogMetadata --- .../src/index.ts | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index 238f11bb0adc..6ae1ea5436aa 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -256,7 +256,17 @@ export default async function pluginContentBlog( ), ); - const baseBlogPermalink = normalizeUrl([baseUrl, routeBasePath]); + const blogMetadataPath = await createData( + `blogMetadata-${pluginId}.json`, + JSON.stringify( + { + baseBlogPermalink: normalizeUrl([baseUrl, routeBasePath]), + blogTitle, + }, + null, + 2, + ), + ); // Create routes for blog entries. await Promise.all( @@ -269,18 +279,6 @@ export default async function pluginContentBlog( JSON.stringify(metadata, null, 2), ); - const blogMetadataPath = await createData( - `${docuHash(`${metadata.source}-blogMetadata`)}.json`, - JSON.stringify( - { - baseBlogPermalink, - blogTitle, - }, - null, - 2, - ), - ); - addRoute({ path: metadata.permalink, component: blogPostComponent, From 885dbaf9ca27667b544856369b4e1b783bf3e59e Mon Sep 17 00:00:00 2001 From: johnnyreilly Date: Sat, 10 Feb 2024 07:03:11 +0000 Subject: [PATCH 10/22] fix: split out getImage --- .../BlogListPage/StructuredData/index.tsx | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx index 9ad871105d0c..01081da1e7d9 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import {useBaseUrlUtils} from '@docusaurus/useBaseUrl'; +import {type BaseUrlOptions, useBaseUrlUtils} from '@docusaurus/useBaseUrl'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import { makeImageStructuredData, @@ -16,6 +16,21 @@ import type {Props} from '@theme/BlogListPage/StructuredData'; import StructuredData from '@theme/StructuredData'; import type {Blog, WithContext} from 'schema-dts'; +function getImage( + image: string | undefined, + withBaseUrl: (url: string, options?: BaseUrlOptions | undefined) => string, + title: string, +) { + return image + ? { + image: makeImageStructuredData({ + imageUrl: withBaseUrl(image, {absolute: true}), + caption: `title image for the blog post: ${title}`, + }), + } + : {}; +} + export default function BlogListPageStructuredData(props: Props): JSX.Element { const {siteConfig} = useDocusaurusContext(); const {withBaseUrl} = useBaseUrlUtils(); @@ -62,14 +77,7 @@ export default function BlogListPageStructuredData(props: Props): JSX.Element { authorsStructuredData.length === 1 ? authorsStructuredData[0] : authorsStructuredData, - ...(image - ? { - image: makeImageStructuredData({ - imageUrl: withBaseUrl(image, {absolute: true}), - caption: `title image for the blog post: ${title}`, - }), - } - : {}), + ...getImage(image, withBaseUrl, title), ...(keywords ? {keywords} : {}), }; }), From 66637260f56fc384a77208d2c213cd92f8db5156 Mon Sep 17 00:00:00 2001 From: johnnyreilly Date: Sat, 10 Feb 2024 07:14:53 +0000 Subject: [PATCH 11/22] fix: getAuthor / getImage move --- .../BlogListPage/StructuredData/index.tsx | 20 +++++---- .../BlogPostPage/StructuredData/index.tsx | 45 ++++++++++++------- 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx index 01081da1e7d9..db533e2ae0bc 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx @@ -14,8 +14,19 @@ import { } from '@docusaurus/theme-common'; import type {Props} from '@theme/BlogListPage/StructuredData'; import StructuredData from '@theme/StructuredData'; +import type {Author} from '@docusaurus/plugin-content-blog'; import type {Blog, WithContext} from 'schema-dts'; +function getAuthor(authors: Author[]) { + const authorsStructuredData = authors.map(makePersonStructuredData); + return { + author: + authorsStructuredData.length === 1 + ? authorsStructuredData[0] + : authorsStructuredData, + }; +} + function getImage( image: string | undefined, withBaseUrl: (url: string, options?: BaseUrlOptions | undefined) => string, @@ -58,10 +69,6 @@ export default function BlogListPageStructuredData(props: Props): JSX.Element { const image = assets.image ?? frontMatter.image; const keywords = frontMatter.keywords ?? []; - const authorsStructuredData = metadata.authors.map( - makePersonStructuredData, - ); - const blogUrl = `${siteConfig.url}${metadata.permalink}`; return { @@ -73,10 +80,7 @@ export default function BlogListPageStructuredData(props: Props): JSX.Element { name: title, description, datePublished: date, - author: - authorsStructuredData.length === 1 - ? authorsStructuredData[0] - : authorsStructuredData, + ...getAuthor(metadata.authors), ...getImage(image, withBaseUrl, title), ...(keywords ? {keywords} : {}), }; diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx index f7e5fe9bd3ee..9a09b1b84481 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import {useBaseUrlUtils} from '@docusaurus/useBaseUrl'; +import {type BaseUrlOptions, useBaseUrlUtils} from '@docusaurus/useBaseUrl'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import { makeImageStructuredData, @@ -15,6 +15,32 @@ import { import type {Props} from '@theme/BlogPostPage/StructuredData'; import StructuredData from '@theme/StructuredData'; import type {BlogPosting, WithContext} from 'schema-dts'; +import type {Author} from '@docusaurus/plugin-content-blog'; + +function getAuthor(authors: Author[]) { + const authorsStructuredData = authors.map(makePersonStructuredData); + return { + author: + authorsStructuredData.length === 1 + ? authorsStructuredData[0] + : authorsStructuredData, + }; +} + +function getImage( + image: string | undefined, + withBaseUrl: (url: string, options?: BaseUrlOptions | undefined) => string, + title: string, +) { + return image + ? { + image: makeImageStructuredData({ + imageUrl: withBaseUrl(image, {absolute: true}), + caption: `title image for the blog post: ${title}`, + }), + } + : {}; +} export default function BlogPostStructuredData(props: Props): JSX.Element { const {siteConfig} = useDocusaurusContext(); @@ -25,9 +51,6 @@ export default function BlogPostStructuredData(props: Props): JSX.Element { const image = assets.image ?? frontMatter.image; const keywords = frontMatter.keywords ?? []; - // an array of https://schema.org/Person - const authorsStructuredData = metadata.authors.map(makePersonStructuredData); - const url = `${siteConfig.url}${metadata.permalink}`; // details on structured data support: https://schema.org/BlogPosting @@ -43,18 +66,8 @@ export default function BlogPostStructuredData(props: Props): JSX.Element { name: title, description, datePublished: date, - author: - authorsStructuredData.length === 1 - ? authorsStructuredData[0] - : authorsStructuredData, - ...(image - ? { - image: makeImageStructuredData({ - imageUrl: withBaseUrl(image, {absolute: true}), - caption: `title image for the blog post: ${title}`, - }), - } - : {}), + ...getAuthor(metadata.authors), + ...getImage(image, withBaseUrl, title), ...(keywords ? {keywords} : {}), isPartOf: { '@type': 'Blog', From 8ffcb5876c53e257c47d6e5517369e7498c770b6 Mon Sep 17 00:00:00 2001 From: johnnyreilly Date: Sat, 10 Feb 2024 07:24:21 +0000 Subject: [PATCH 12/22] fix: getBlogPost --- .../BlogListPage/StructuredData/index.tsx | 66 +++++++++++-------- .../BlogPostPage/StructuredData/index.tsx | 4 +- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx index db533e2ae0bc..b7bb2aa0562a 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import {type BaseUrlOptions, useBaseUrlUtils} from '@docusaurus/useBaseUrl'; +import {useBaseUrlUtils, type BaseUrlUtils} from '@docusaurus/useBaseUrl'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import { makeImageStructuredData, @@ -14,8 +14,40 @@ import { } from '@docusaurus/theme-common'; import type {Props} from '@theme/BlogListPage/StructuredData'; import StructuredData from '@theme/StructuredData'; -import type {Author} from '@docusaurus/plugin-content-blog'; +import type { + Author, + PropBlogPostContent, +} from '@docusaurus/plugin-content-blog'; import type {Blog, WithContext} from 'schema-dts'; +import type {DocusaurusConfig} from '@docusaurus/types'; + +function getBlogPost( + blogPostContent: PropBlogPostContent, + siteConfig: DocusaurusConfig, + withBaseUrl: BaseUrlUtils['withBaseUrl'], +) { + const {assets, frontMatter, metadata} = blogPostContent; + const {date, title, description} = metadata; + + const image = assets.image ?? frontMatter.image; + const keywords = frontMatter.keywords ?? []; + + const blogUrl = `${siteConfig.url}${metadata.permalink}`; + + return { + '@type': 'BlogPosting', + '@id': blogUrl, + mainEntityOfPage: blogUrl, + url: blogUrl, + headline: title, + name: title, + description, + datePublished: date, + ...getAuthor(metadata.authors), + ...getImage(image, withBaseUrl, title), + ...(keywords ? {keywords} : {}), + }; +} function getAuthor(authors: Author[]) { const authorsStructuredData = authors.map(makePersonStructuredData); @@ -29,7 +61,7 @@ function getAuthor(authors: Author[]) { function getImage( image: string | undefined, - withBaseUrl: (url: string, options?: BaseUrlOptions | undefined) => string, + withBaseUrl: BaseUrlUtils['withBaseUrl'], title: string, ) { return image @@ -60,31 +92,9 @@ export default function BlogListPageStructuredData(props: Props): JSX.Element { mainEntityOfPage: url, headline: blogTitle, description: blogDescription, - blogPost: props.items.map((blogItem) => { - const { - content: {assets, frontMatter, metadata}, - } = blogItem; - const {date, title, description} = metadata; - - const image = assets.image ?? frontMatter.image; - const keywords = frontMatter.keywords ?? []; - - const blogUrl = `${siteConfig.url}${metadata.permalink}`; - - return { - '@type': 'BlogPosting', - '@id': blogUrl, - mainEntityOfPage: blogUrl, - url: blogUrl, - headline: title, - name: title, - description, - datePublished: date, - ...getAuthor(metadata.authors), - ...getImage(image, withBaseUrl, title), - ...(keywords ? {keywords} : {}), - }; - }), + blogPost: props.items.map((blogItem) => + getBlogPost(blogItem.content, siteConfig, withBaseUrl), + ), }; return ; diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx index 9a09b1b84481..1665a1510c50 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import {type BaseUrlOptions, useBaseUrlUtils} from '@docusaurus/useBaseUrl'; +import {useBaseUrlUtils, type BaseUrlUtils} from '@docusaurus/useBaseUrl'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import { makeImageStructuredData, @@ -29,7 +29,7 @@ function getAuthor(authors: Author[]) { function getImage( image: string | undefined, - withBaseUrl: (url: string, options?: BaseUrlOptions | undefined) => string, + withBaseUrl: BaseUrlUtils['withBaseUrl'], title: string, ) { return image From ce1b6643f4f0d9da97a7fe760b909a841255d506 Mon Sep 17 00:00:00 2001 From: johnnyreilly Date: Sat, 10 Feb 2024 07:26:47 +0000 Subject: [PATCH 13/22] fix: baseBlogPermalink -> blogBasePath --- packages/docusaurus-plugin-content-blog/src/index.ts | 2 +- .../docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts | 2 +- .../src/theme/BlogPostPage/StructuredData/index.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index 6ae1ea5436aa..bf7f72d71b6e 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -260,7 +260,7 @@ export default async function pluginContentBlog( `blogMetadata-${pluginId}.json`, JSON.stringify( { - baseBlogPermalink: normalizeUrl([baseUrl, routeBasePath]), + blogBasePath: normalizeUrl([baseUrl, routeBasePath]), blogTitle, }, null, diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index af94b9e81e1f..619ec00f2c86 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -463,7 +463,7 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the export type BlogMetadata = { /** the path to the base of the blog */ - baseBlogPermalink: string; + blogBasePath: string; /** title of the overall blog */ blogTitle: string; }; diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx index 1665a1510c50..be1bde8e53e8 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx @@ -71,7 +71,7 @@ export default function BlogPostStructuredData(props: Props): JSX.Element { ...(keywords ? {keywords} : {}), isPartOf: { '@type': 'Blog', - '@id': `${siteConfig.url}${props.blogMetadata.baseBlogPermalink}`, + '@id': `${siteConfig.url}${props.blogMetadata.blogBasePath}`, name: props.blogMetadata.blogTitle, }, }; From 3ce60a1228a2d075a50296aad41be78be2ad1ef7 Mon Sep 17 00:00:00 2001 From: johnnyreilly Date: Sat, 10 Feb 2024 07:51:59 +0000 Subject: [PATCH 14/22] fix: migrate structuredData logic to theme-common --- .../BlogListPage/StructuredData/index.tsx | 91 +----------- .../BlogPostPage/StructuredData/index.tsx | 70 +-------- packages/docusaurus-theme-common/src/index.ts | 2 + .../src/utils/structuredDataUtils.ts | 138 +++++++++++++++++- 4 files changed, 144 insertions(+), 157 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx index b7bb2aa0562a..5289a9855e7c 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx @@ -6,96 +6,11 @@ */ import React from 'react'; -import {useBaseUrlUtils, type BaseUrlUtils} from '@docusaurus/useBaseUrl'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import { - makeImageStructuredData, - makePersonStructuredData, -} from '@docusaurus/theme-common'; +import {useBlogListPageStructuredData} from '@docusaurus/theme-common'; import type {Props} from '@theme/BlogListPage/StructuredData'; import StructuredData from '@theme/StructuredData'; -import type { - Author, - PropBlogPostContent, -} from '@docusaurus/plugin-content-blog'; -import type {Blog, WithContext} from 'schema-dts'; -import type {DocusaurusConfig} from '@docusaurus/types'; - -function getBlogPost( - blogPostContent: PropBlogPostContent, - siteConfig: DocusaurusConfig, - withBaseUrl: BaseUrlUtils['withBaseUrl'], -) { - const {assets, frontMatter, metadata} = blogPostContent; - const {date, title, description} = metadata; - - const image = assets.image ?? frontMatter.image; - const keywords = frontMatter.keywords ?? []; - - const blogUrl = `${siteConfig.url}${metadata.permalink}`; - - return { - '@type': 'BlogPosting', - '@id': blogUrl, - mainEntityOfPage: blogUrl, - url: blogUrl, - headline: title, - name: title, - description, - datePublished: date, - ...getAuthor(metadata.authors), - ...getImage(image, withBaseUrl, title), - ...(keywords ? {keywords} : {}), - }; -} - -function getAuthor(authors: Author[]) { - const authorsStructuredData = authors.map(makePersonStructuredData); - return { - author: - authorsStructuredData.length === 1 - ? authorsStructuredData[0] - : authorsStructuredData, - }; -} - -function getImage( - image: string | undefined, - withBaseUrl: BaseUrlUtils['withBaseUrl'], - title: string, -) { - return image - ? { - image: makeImageStructuredData({ - imageUrl: withBaseUrl(image, {absolute: true}), - caption: `title image for the blog post: ${title}`, - }), - } - : {}; -} export default function BlogListPageStructuredData(props: Props): JSX.Element { - 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 - const blogStructuredData: WithContext = { - '@context': 'https://schema.org', - '@type': 'Blog', - '@id': url, - mainEntityOfPage: url, - headline: blogTitle, - description: blogDescription, - blogPost: props.items.map((blogItem) => - getBlogPost(blogItem.content, siteConfig, withBaseUrl), - ), - }; - - return ; + const structuredData = useBlogListPageStructuredData(props); + return ; } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx index be1bde8e53e8..4a77e41c7a07 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx @@ -6,75 +6,11 @@ */ import React from 'react'; -import {useBaseUrlUtils, type BaseUrlUtils} from '@docusaurus/useBaseUrl'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import { - makeImageStructuredData, - makePersonStructuredData, -} from '@docusaurus/theme-common'; +import {useBlogPostStructuredData} from '@docusaurus/theme-common'; import type {Props} from '@theme/BlogPostPage/StructuredData'; import StructuredData from '@theme/StructuredData'; -import type {BlogPosting, WithContext} from 'schema-dts'; -import type {Author} from '@docusaurus/plugin-content-blog'; - -function getAuthor(authors: Author[]) { - const authorsStructuredData = authors.map(makePersonStructuredData); - return { - author: - authorsStructuredData.length === 1 - ? authorsStructuredData[0] - : authorsStructuredData, - }; -} - -function getImage( - image: string | undefined, - withBaseUrl: BaseUrlUtils['withBaseUrl'], - title: string, -) { - return image - ? { - image: makeImageStructuredData({ - imageUrl: withBaseUrl(image, {absolute: true}), - caption: `title image for the blog post: ${title}`, - }), - } - : {}; -} export default function BlogPostStructuredData(props: Props): JSX.Element { - const {siteConfig} = useDocusaurusContext(); - const {withBaseUrl} = useBaseUrlUtils(); - const {assets, frontMatter, metadata} = props; - const {date, title, description} = metadata; - - const image = assets.image ?? frontMatter.image; - const keywords = frontMatter.keywords ?? []; - - 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 - const blogPostStructuredData: WithContext = { - '@context': 'https://schema.org', - '@type': 'BlogPosting', - '@id': url, - mainEntityOfPage: url, - url, - headline: title, - name: title, - description, - datePublished: date, - ...getAuthor(metadata.authors), - ...getImage(image, withBaseUrl, title), - ...(keywords ? {keywords} : {}), - isPartOf: { - '@type': 'Blog', - '@id': `${siteConfig.url}${props.blogMetadata.blogBasePath}`, - name: props.blogMetadata.blogTitle, - }, - }; - - return ; + const structuredData = useBlogPostStructuredData(props); + return ; } diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index 22e7512e2abc..68041da2f214 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -40,6 +40,8 @@ export { } from './utils/docsUtils'; export { + useBlogListPageStructuredData, + useBlogPostStructuredData, makeImageStructuredData, makePersonStructuredData, } from './utils/structuredDataUtils'; diff --git a/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts b/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts index 54c7174427d2..72b5c22d49a4 100644 --- a/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts @@ -5,8 +5,142 @@ * LICENSE file in the root directory of this source tree. */ -import type {Author} from '@docusaurus/plugin-content-blog'; -import type {Person, ImageObject} from 'schema-dts'; +import {useBaseUrlUtils, type BaseUrlUtils} from '@docusaurus/useBaseUrl'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import type {Props as BlogListPageStructuredDataProps} from '@theme/BlogListPage/StructuredData'; +import type {Props as BlogPostPageStructuredDataProps} from '@theme/BlogPostPage/StructuredData'; +import type { + Blog, + BlogPosting, + WithContext, + Person, + ImageObject, +} from 'schema-dts'; +import type { + Author, + PropBlogPostContent, +} from '@docusaurus/plugin-content-blog'; +import type {DocusaurusConfig} from '@docusaurus/types'; + +function getBlogPost( + blogPostContent: PropBlogPostContent, + siteConfig: DocusaurusConfig, + withBaseUrl: BaseUrlUtils['withBaseUrl'], +) { + const {assets, frontMatter, metadata} = blogPostContent; + const {date, title, description} = metadata; + + const image = assets.image ?? frontMatter.image; + const keywords = frontMatter.keywords ?? []; + + const blogUrl = `${siteConfig.url}${metadata.permalink}`; + + return { + '@type': 'BlogPosting', + '@id': blogUrl, + mainEntityOfPage: blogUrl, + url: blogUrl, + headline: title, + name: title, + description, + datePublished: date, + ...getAuthor(metadata.authors), + ...getImage(image, withBaseUrl, title), + ...(keywords ? {keywords} : {}), + }; +} + +function getAuthor(authors: Author[]) { + const authorsStructuredData = authors.map(makePersonStructuredData); + return { + author: + authorsStructuredData.length === 1 + ? authorsStructuredData[0] + : authorsStructuredData, + }; +} + +function getImage( + image: string | undefined, + withBaseUrl: BaseUrlUtils['withBaseUrl'], + title: string, +) { + return image + ? { + image: makeImageStructuredData({ + imageUrl: withBaseUrl(image, {absolute: true}), + caption: `title image for the blog post: ${title}`, + }), + } + : {}; +} + +export function useBlogListPageStructuredData( + props: BlogListPageStructuredDataProps, +): WithContext { + 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 + const blogStructuredData: WithContext = { + '@context': 'https://schema.org', + '@type': 'Blog', + '@id': url, + mainEntityOfPage: url, + headline: blogTitle, + description: blogDescription, + blogPost: props.items.map((blogItem) => + getBlogPost(blogItem.content, siteConfig, withBaseUrl), + ), + }; + + return blogStructuredData; +} + +export function useBlogPostStructuredData( + props: BlogPostPageStructuredDataProps, +): WithContext { + const {siteConfig} = useDocusaurusContext(); + const {withBaseUrl} = useBaseUrlUtils(); + const {assets, frontMatter, metadata} = props; + const {date, title, description} = metadata; + + const image = assets.image ?? frontMatter.image; + const keywords = frontMatter.keywords ?? []; + + 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 + const blogPostStructuredData: WithContext = { + '@context': 'https://schema.org', + '@type': 'BlogPosting', + '@id': url, + mainEntityOfPage: url, + url, + headline: title, + name: title, + description, + datePublished: date, + ...getAuthor(metadata.authors), + ...getImage(image, withBaseUrl, title), + ...(keywords ? {keywords} : {}), + isPartOf: { + '@type': 'Blog', + '@id': `${siteConfig.url}${props.blogMetadata.blogBasePath}`, + name: props.blogMetadata.blogTitle, + }, + }; + + return blogPostStructuredData; +} /** @returns A {@link https://schema.org/Person} constructed from the {@link Author} */ export function makePersonStructuredData(author: Author): Person { From 356d8f281b2fd63f2930fecbbc10a4ae31b4bbb2 Mon Sep 17 00:00:00 2001 From: johnnyreilly Date: Sat, 10 Feb 2024 19:35:02 +0000 Subject: [PATCH 15/22] fix: move StructuredData to theme-common --- packages/docusaurus-theme-classic/src/theme-classic.d.ts | 8 -------- .../src/theme/BlogListPage/StructuredData/index.tsx | 6 ++++-- .../src/theme/BlogPostPage/StructuredData/index.tsx | 6 ++++-- .../src/components}/StructuredData/index.tsx | 4 +++- packages/docusaurus-theme-common/src/index.ts | 2 ++ website/docs/seo.mdx | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) rename packages/{docusaurus-theme-classic/src/theme => docusaurus-theme-common/src/components}/StructuredData/index.tsx (92%) diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index 236334fb132b..053e036dc5eb 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -813,14 +813,6 @@ declare module '@theme/SearchMetadata' { export default function SearchMetadata(props: Props): JSX.Element; } -declare module '@theme/StructuredData' { - export interface Props { - readonly structuredData: object; - } - - export default function StructuredData(props: Props): JSX.Element; -} - declare module '@theme/LastUpdated' { export interface Props { readonly lastUpdatedAt?: number; diff --git a/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx index 5289a9855e7c..72a144baef13 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx @@ -6,9 +6,11 @@ */ import React from 'react'; -import {useBlogListPageStructuredData} from '@docusaurus/theme-common'; +import { + useBlogListPageStructuredData, + StructuredData, +} from '@docusaurus/theme-common'; import type {Props} from '@theme/BlogListPage/StructuredData'; -import StructuredData from '@theme/StructuredData'; export default function BlogListPageStructuredData(props: Props): JSX.Element { const structuredData = useBlogListPageStructuredData(props); diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx index 4a77e41c7a07..4aa9c33ab2fb 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx @@ -6,9 +6,11 @@ */ import React from 'react'; -import {useBlogPostStructuredData} from '@docusaurus/theme-common'; +import { + useBlogPostStructuredData, + StructuredData, +} from '@docusaurus/theme-common'; import type {Props} from '@theme/BlogPostPage/StructuredData'; -import StructuredData from '@theme/StructuredData'; export default function BlogPostStructuredData(props: Props): JSX.Element { const structuredData = useBlogPostStructuredData(props); diff --git a/packages/docusaurus-theme-classic/src/theme/StructuredData/index.tsx b/packages/docusaurus-theme-common/src/components/StructuredData/index.tsx similarity index 92% rename from packages/docusaurus-theme-classic/src/theme/StructuredData/index.tsx rename to packages/docusaurus-theme-common/src/components/StructuredData/index.tsx index 3ff6d5cf3a05..e3496c291bf6 100644 --- a/packages/docusaurus-theme-classic/src/theme/StructuredData/index.tsx +++ b/packages/docusaurus-theme-common/src/components/StructuredData/index.tsx @@ -7,7 +7,9 @@ import React from 'react'; -import type {Props} from '@theme/StructuredData'; +interface Props { + readonly structuredData: object; +} export default function StructuredData({structuredData}: Props): JSX.Element { return ( diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index 68041da2f214..4719b5a6affc 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -26,6 +26,8 @@ export { export {default as ThemedComponent} from './components/ThemedComponent'; +export {default as StructuredData} from './components/StructuredData'; + export { createStorageSlot, useStorageSlot, diff --git a/website/docs/seo.mdx b/website/docs/seo.mdx index d13cdb5ef201..35fd63d940af 100644 --- a/website/docs/seo.mdx +++ b/website/docs/seo.mdx @@ -113,7 +113,7 @@ For JSX pages, you can use the Docusaurus [``](docusaurus-core.mdx#head) c ```jsx title="my-react-page.jsx" import React from 'react'; import Layout from '@theme/Layout'; -import StructuredData from '@theme/StructuredData'; +import {StructuredData} from '@docusaurus/theme-common'; import Head from '@docusaurus/Head'; export default function page() { From 55433ebf95ce0e2be69b8fba9a3ddd1cd78cf554 Mon Sep 17 00:00:00 2001 From: johnnyreilly Date: Sat, 10 Feb 2024 19:40:59 +0000 Subject: [PATCH 16/22] fix: remove unnecessary prop --- .../src/plugin-content-blog.d.ts | 1 - .../docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx | 1 - .../docusaurus-theme-common/src/utils/structuredDataUtils.ts | 4 ++-- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 619ec00f2c86..a245fbcbd901 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -577,7 +577,6 @@ declare module '@theme/BlogPostPage/StructuredData' { export interface Props { readonly assets: Assets; - readonly frontMatter: FrontMatter; readonly metadata: Metadata; readonly blogMetadata: BlogMetadata; } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx index 320d757886b2..fc18e60ab471 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx @@ -50,7 +50,6 @@ function BlogPostPageContent({ {unlisted && } { const {siteConfig} = useDocusaurusContext(); const {withBaseUrl} = useBaseUrlUtils(); - const {assets, frontMatter, metadata} = props; - const {date, title, description} = metadata; + const {assets, metadata} = props; + const {date, title, description, frontMatter} = metadata; const image = assets.image ?? frontMatter.image; const keywords = frontMatter.keywords ?? []; From e59c882b708d1285ee14c16c7bbd5942d4fdaedd Mon Sep 17 00:00:00 2001 From: johnnyreilly Date: Sat, 10 Feb 2024 19:47:57 +0000 Subject: [PATCH 17/22] fix: less prop drilling --- .../src/theme/BlogPostPage/index.tsx | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx index fc18e60ab471..0d97a6386752 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx @@ -17,18 +17,16 @@ import BlogPostPageStructuredData from '@theme/BlogPostPage/StructuredData'; import TOC from '@theme/TOC'; import type {Props} from '@theme/BlogPostPage'; import Unlisted from '@theme/Unlisted'; -import type {BlogMetadata, BlogSidebar} from '@docusaurus/plugin-content-blog'; +import type {BlogSidebar} from '@docusaurus/plugin-content-blog'; function BlogPostPageContent({ sidebar, - blogMetadata, children, }: { sidebar: BlogSidebar; - blogMetadata: BlogMetadata; children: ReactNode; }): JSX.Element { - const {metadata, toc, assets} = useBlogPost(); + const {metadata, toc} = useBlogPost(); const {nextItem, prevItem, frontMatter, unlisted} = metadata; const { hide_table_of_contents: hideTableOfContents, @@ -49,12 +47,6 @@ function BlogPostPageContent({ }> {unlisted && } - - {children} {(nextItem || prevItem) && ( @@ -74,9 +66,12 @@ export default function BlogPostPage(props: Props): JSX.Element { ThemeClassNames.page.blogPostPage, )}> - + + From 0be47bd71a46a5953fb8f94c622977eebf345307 Mon Sep 17 00:00:00 2001 From: johnnyreilly Date: Wed, 14 Feb 2024 05:41:20 +0000 Subject: [PATCH 18/22] fix: address review feedback --- packages/docusaurus-theme-common/src/index.ts | 2 -- .../src/utils/structuredDataUtils.ts | 8 ++++---- website/docs/seo.mdx | 4 +++- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index 4719b5a6affc..2ef3b9109fb5 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -44,8 +44,6 @@ export { export { useBlogListPageStructuredData, useBlogPostStructuredData, - makeImageStructuredData, - makePersonStructuredData, } from './utils/structuredDataUtils'; export {usePluralForm} from './utils/usePluralForm'; diff --git a/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts b/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts index 0451e8bfcb08..99743766c640 100644 --- a/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts @@ -51,7 +51,7 @@ function getBlogPost( } function getAuthor(authors: Author[]) { - const authorsStructuredData = authors.map(makePersonStructuredData); + const authorsStructuredData = authors.map(createPersonStructuredData); return { author: authorsStructuredData.length === 1 @@ -67,7 +67,7 @@ function getImage( ) { return image ? { - image: makeImageStructuredData({ + image: createImageStructuredData({ imageUrl: withBaseUrl(image, {absolute: true}), caption: `title image for the blog post: ${title}`, }), @@ -143,7 +143,7 @@ export function useBlogPostStructuredData( } /** @returns A {@link https://schema.org/Person} constructed from the {@link Author} */ -export function makePersonStructuredData(author: Author): Person { +function createPersonStructuredData(author: Author): Person { return { '@type': 'Person', ...(author.name ? {name: author.name} : {}), @@ -155,7 +155,7 @@ export function makePersonStructuredData(author: Author): Person { } /** @returns A {@link https://schema.org/ImageObject} */ -export function makeImageStructuredData({ +function createImageStructuredData({ imageUrl, caption, }: { diff --git a/website/docs/seo.mdx b/website/docs/seo.mdx index 35fd63d940af..74825f63837f 100644 --- a/website/docs/seo.mdx +++ b/website/docs/seo.mdx @@ -60,7 +60,9 @@ To read more about types of meta tags, visit [the MDN docs](https://developer.mo Similar to [global metadata](#global-metadata), Docusaurus also allows for the addition of meta-information to individual pages. Follow [this guide](./guides/markdown-features/markdown-features-head-metadata.mdx) for configuring the `` tag. In short: -```md title="my-markdown-page.md" +```md title="my-markdown-page.mdx" +import {StructuredData} from '@docusaurus/theme-common'; + # A cooking guide From 6d26f5cf3e971c79681717110f2e1ce69ebc193a Mon Sep 17 00:00:00 2001 From: sebastien Date: Wed, 14 Feb 2024 15:59:06 +0100 Subject: [PATCH 19/22] Add blog plugin useBlogMetadata() client hook + refactor to use it --- .../package.json | 16 +++++++++++-- .../src/client/index.ts | 20 ++++++++++++++++ .../src/index.ts | 5 +++- .../src/plugin-content-blog.d.ts | 22 +++--------------- .../tsconfig.client.json | 16 +++++++++++++ .../tsconfig.json | 3 ++- .../BlogPostPage/StructuredData/index.tsx | 5 ++-- .../src/theme/BlogPostPage/index.tsx | 6 +---- .../src/utils/structuredDataUtils.ts | 23 ++++++++----------- packages/docusaurus-types/src/routing.d.ts | 2 +- 10 files changed, 73 insertions(+), 45 deletions(-) create mode 100644 packages/docusaurus-plugin-content-blog/src/client/index.ts create mode 100644 packages/docusaurus-plugin-content-blog/tsconfig.client.json diff --git a/packages/docusaurus-plugin-content-blog/package.json b/packages/docusaurus-plugin-content-blog/package.json index 81b398107e59..e01d774d0b91 100644 --- a/packages/docusaurus-plugin-content-blog/package.json +++ b/packages/docusaurus-plugin-content-blog/package.json @@ -4,9 +4,21 @@ "description": "Blog plugin for Docusaurus.", "main": "lib/index.js", "types": "src/plugin-content-blog.d.ts", + "exports": { + "./lib/*": "./lib/*", + "./src/*": "./src/*", + "./client": { + "type": "./lib/client/index.d.ts", + "default": "./lib/client/index.js" + }, + ".": { + "types": "./src/plugin-content-blog.d.ts", + "default": "./lib/index.js" + } + }, "scripts": { - "build": "tsc", - "watch": "tsc --watch", + "build": "tsc --build", + "watch": "tsc --build --watch", "test:generate-build-snap": "yarn docusaurus build src/__tests__/__fixtures__/website --out-dir build-snap && yarn rimraf src/__tests__/__fixtures__/website/.docusaurus && yarn rimraf src/__tests__/__fixtures__/website/build-snap/assets && git add src/__tests__/__fixtures__/website/build-snap" }, "repository": { diff --git a/packages/docusaurus-plugin-content-blog/src/client/index.ts b/packages/docusaurus-plugin-content-blog/src/client/index.ts new file mode 100644 index 000000000000..333ddc5a4d7b --- /dev/null +++ b/packages/docusaurus-plugin-content-blog/src/client/index.ts @@ -0,0 +1,20 @@ +/** + * 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 useRouteContext from '@docusaurus/useRouteContext'; +import type {BlogMetadata} from '@docusaurus/plugin-content-blog'; + +export function useBlogMetadata(): BlogMetadata { + const routeContext = useRouteContext(); + const blogMetadata = routeContext?.data?.blogMetadata; + if (!blogMetadata) { + throw new Error( + "useBlogMetadata() can't be called on the current route because the blog metadata could not be found in route context", + ); + } + return blogMetadata as BlogMetadata; +} diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index bf7f72d71b6e..75f6edf0a3d0 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -42,6 +42,7 @@ import type { BlogTags, BlogContent, BlogPaginated, + BlogMetadata, } from '@docusaurus/plugin-content-blog'; export default async function pluginContentBlog( @@ -262,7 +263,7 @@ export default async function pluginContentBlog( { blogBasePath: normalizeUrl([baseUrl, routeBasePath]), blogTitle, - }, + } satisfies BlogMetadata, null, 2, ), @@ -286,6 +287,8 @@ export default async function pluginContentBlog( modules: { sidebar: aliasedSource(sidebarProp), content: metadata.source, + }, + context: { blogMetadata: aliasedSource(blogMetadataPath), }, }); diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index a245fbcbd901..2a37d67dfe54 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -5,6 +5,8 @@ * LICENSE file in the root directory of this source tree. */ +/// + declare module '@docusaurus/plugin-content-blog' { import type {LoadedMDXContent} from '@docusaurus/mdx-loader'; import type {MDXOptions} from '@docusaurus/mdx-loader'; @@ -563,25 +565,7 @@ declare module '@theme/BlogPostPage/Metadata' { } declare module '@theme/BlogPostPage/StructuredData' { - import type { - BlogPostFrontMatter, - PropBlogPostContent, - BlogMetadata, - } from '@docusaurus/plugin-content-blog'; - - export type FrontMatter = BlogPostFrontMatter; - - export type Assets = PropBlogPostContent['assets']; - - export type Metadata = PropBlogPostContent['metadata']; - - export interface Props { - readonly assets: Assets; - readonly metadata: Metadata; - readonly blogMetadata: BlogMetadata; - } - - export default function BlogPostStructuredData(props: Props): JSX.Element; + export default function BlogPostStructuredData(): JSX.Element; } declare module '@theme/BlogListPage' { diff --git a/packages/docusaurus-plugin-content-blog/tsconfig.client.json b/packages/docusaurus-plugin-content-blog/tsconfig.client.json new file mode 100644 index 000000000000..5d06aa818c96 --- /dev/null +++ b/packages/docusaurus-plugin-content-blog/tsconfig.client.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": false, + "composite": true, + "incremental": true, + "tsBuildInfoFile": "./lib/.tsbuildinfo-client", + "moduleResolution": "bundler", + "module": "esnext", + "target": "esnext", + "rootDir": "src", + "outDir": "lib" + }, + "include": ["src/client", "src/*.d.ts"], + "exclude": ["**/__tests__/**"] +} diff --git a/packages/docusaurus-plugin-content-blog/tsconfig.json b/packages/docusaurus-plugin-content-blog/tsconfig.json index e16d5c2c5d33..3936df64b7e4 100644 --- a/packages/docusaurus-plugin-content-blog/tsconfig.json +++ b/packages/docusaurus-plugin-content-blog/tsconfig.json @@ -1,5 +1,6 @@ { "extends": "../../tsconfig.json", + "references": [{"path": "./tsconfig.client.json"}], "compilerOptions": { "noEmit": false, "incremental": true, @@ -8,5 +9,5 @@ "outDir": "lib" }, "include": ["src"], - "exclude": ["**/__tests__/**"] + "exclude": ["src/client", "**/__tests__/**"] } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx index 4aa9c33ab2fb..0041a7e88f3a 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx @@ -10,9 +10,8 @@ import { useBlogPostStructuredData, StructuredData, } from '@docusaurus/theme-common'; -import type {Props} from '@theme/BlogPostPage/StructuredData'; -export default function BlogPostStructuredData(props: Props): JSX.Element { - const structuredData = useBlogPostStructuredData(props); +export default function BlogPostStructuredData(): JSX.Element { + const structuredData = useBlogPostStructuredData(); return ; } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx index 0d97a6386752..6d7ed51a7fd5 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx @@ -66,11 +66,7 @@ export default function BlogPostPage(props: Props): JSX.Element { ThemeClassNames.page.blogPostPage, )}> - + diff --git a/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts b/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts index 99743766c640..eb8cea1ad1b3 100644 --- a/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts @@ -7,8 +7,9 @@ import {useBaseUrlUtils, type BaseUrlUtils} from '@docusaurus/useBaseUrl'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import {useBlogMetadata} from '@docusaurus/plugin-content-blog/client'; import type {Props as BlogListPageStructuredDataProps} from '@theme/BlogListPage/StructuredData'; -import type {Props as BlogPostPageStructuredDataProps} from '@theme/BlogPostPage/StructuredData'; +import {useBlogPost} from '../contexts/blogPost'; import type { Blog, BlogPosting, @@ -88,7 +89,7 @@ export function useBlogListPageStructuredData( const url = `${siteConfig.url}${permalink}`; // details on structured data support: https://schema.org/Blog - const blogStructuredData: WithContext = { + return { '@context': 'https://schema.org', '@type': 'Blog', '@id': url, @@ -99,16 +100,14 @@ export function useBlogListPageStructuredData( getBlogPost(blogItem.content, siteConfig, withBaseUrl), ), }; - - return blogStructuredData; } -export function useBlogPostStructuredData( - props: BlogPostPageStructuredDataProps, -): WithContext { +export function useBlogPostStructuredData(): WithContext { + const blogMetadata = useBlogMetadata(); + const {assets, metadata} = useBlogPost(); const {siteConfig} = useDocusaurusContext(); const {withBaseUrl} = useBaseUrlUtils(); - const {assets, metadata} = props; + const {date, title, description, frontMatter} = metadata; const image = assets.image ?? frontMatter.image; @@ -119,7 +118,7 @@ export function useBlogPostStructuredData( // 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 - const blogPostStructuredData: WithContext = { + return { '@context': 'https://schema.org', '@type': 'BlogPosting', '@id': url, @@ -134,12 +133,10 @@ export function useBlogPostStructuredData( ...(keywords ? {keywords} : {}), isPartOf: { '@type': 'Blog', - '@id': `${siteConfig.url}${props.blogMetadata.blogBasePath}`, - name: props.blogMetadata.blogTitle, + '@id': `${siteConfig.url}${blogMetadata.blogBasePath}`, + name: blogMetadata.blogTitle, }, }; - - return blogPostStructuredData; } /** @returns A {@link https://schema.org/Person} constructed from the {@link Author} */ diff --git a/packages/docusaurus-types/src/routing.d.ts b/packages/docusaurus-types/src/routing.d.ts index 7d1129c2a0a6..5c5ad90f1b4f 100644 --- a/packages/docusaurus-types/src/routing.d.ts +++ b/packages/docusaurus-types/src/routing.d.ts @@ -70,7 +70,7 @@ export type RouteContext = { /** * Plugin-specific context data. */ - data?: object | undefined; + data?: {[key: string]: unknown}; }; /** From 84a13d16f439a6693864332fc87e9aeacdc7e253 Mon Sep 17 00:00:00 2001 From: sebastien Date: Wed, 14 Feb 2024 16:06:45 +0100 Subject: [PATCH 20/22] fix blog tests??? --- .../docusaurus-plugin-content-blog/src/index.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index 75f6edf0a3d0..d98dc7b63faf 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -257,16 +257,13 @@ export default async function pluginContentBlog( ), ); + const blogMetadata: BlogMetadata = { + blogBasePath: normalizeUrl([baseUrl, routeBasePath]), + blogTitle, + }; const blogMetadataPath = await createData( `blogMetadata-${pluginId}.json`, - JSON.stringify( - { - blogBasePath: normalizeUrl([baseUrl, routeBasePath]), - blogTitle, - } satisfies BlogMetadata, - null, - 2, - ), + JSON.stringify(blogMetadata, null, 2), ); // Create routes for blog entries. From 20256fa95c1d3c2b812dd06bb718da5048220cef Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 15 Feb 2024 15:59:35 +0100 Subject: [PATCH 21/22] revert usage of StructuredData comp in docs --- website/docs/seo.mdx | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/website/docs/seo.mdx b/website/docs/seo.mdx index 74825f63837f..678aa2530d03 100644 --- a/website/docs/seo.mdx +++ b/website/docs/seo.mdx @@ -61,21 +61,21 @@ To read more about types of meta tags, visit [the MDN docs](https://developer.mo Similar to [global metadata](#global-metadata), Docusaurus also allows for the addition of meta-information to individual pages. Follow [this guide](./guides/markdown-features/markdown-features-head-metadata.mdx) for configuring the `` tag. In short: ```md title="my-markdown-page.mdx" -import {StructuredData} from '@docusaurus/theme-common'; - # A cooking guide - + Some content... @@ -115,7 +115,6 @@ For JSX pages, you can use the Docusaurus [``](docusaurus-core.mdx#head) c ```jsx title="my-react-page.jsx" import React from 'react'; import Layout from '@theme/Layout'; -import {StructuredData} from '@docusaurus/theme-common'; import Head from '@docusaurus/Head'; export default function page() { @@ -125,15 +124,15 @@ export default function page() { - + {JSON.stringify({ '@context': 'https://schema.org/', '@type': 'Organization', name: 'Meta Open Source', url: 'https://opensource.fb.com/', logo: 'https://opensource.fb.com/img/logos/Meta-Open-Source.svg', - }} - /> + })} + {/* ... */} From 96073e8d1c9b55ea1a7e8ab6195905bde3627ace Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 15 Feb 2024 15:59:46 +0100 Subject: [PATCH 22/22] remove StructuredData component --- .../BlogListPage/StructuredData/index.tsx | 14 ++++++---- .../BlogPostPage/StructuredData/index.tsx | 14 ++++++---- .../src/components/StructuredData/index.tsx | 27 ------------------- packages/docusaurus-theme-common/src/index.ts | 2 -- 4 files changed, 18 insertions(+), 39 deletions(-) delete mode 100644 packages/docusaurus-theme-common/src/components/StructuredData/index.tsx diff --git a/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx index 72a144baef13..a4f808091d82 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx @@ -6,13 +6,17 @@ */ import React from 'react'; -import { - useBlogListPageStructuredData, - StructuredData, -} from '@docusaurus/theme-common'; +import Head from '@docusaurus/Head'; +import {useBlogListPageStructuredData} from '@docusaurus/theme-common'; import type {Props} from '@theme/BlogListPage/StructuredData'; export default function BlogListPageStructuredData(props: Props): JSX.Element { const structuredData = useBlogListPageStructuredData(props); - return ; + return ( + + + + ); } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx index 0041a7e88f3a..72a15a5d2d22 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx @@ -6,12 +6,16 @@ */ import React from 'react'; -import { - useBlogPostStructuredData, - StructuredData, -} from '@docusaurus/theme-common'; +import Head from '@docusaurus/Head'; +import {useBlogPostStructuredData} from '@docusaurus/theme-common'; export default function BlogPostStructuredData(): JSX.Element { const structuredData = useBlogPostStructuredData(); - return ; + return ( + + + + ); } diff --git a/packages/docusaurus-theme-common/src/components/StructuredData/index.tsx b/packages/docusaurus-theme-common/src/components/StructuredData/index.tsx deleted file mode 100644 index e3496c291bf6..000000000000 --- a/packages/docusaurus-theme-common/src/components/StructuredData/index.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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 React from 'react'; - -interface Props { - readonly structuredData: object; -} - -export default function StructuredData({structuredData}: Props): JSX.Element { - return ( -