diff --git a/packages/docusaurus-plugin-content-blog/package.json b/packages/docusaurus-plugin-content-blog/package.json index 4bfb1d44e8e9..8d7e3c1179d7 100644 --- a/packages/docusaurus-plugin-content-blog/package.json +++ b/packages/docusaurus-plugin-content-blog/package.json @@ -44,6 +44,7 @@ "fs-extra": "^11.1.1", "lodash": "^4.17.21", "reading-time": "^1.5.0", + "schema-dts": "^1.1.2", "srcset": "^4.0.0", "tslib": "^2.6.0", "unist-util-visit": "^5.0.0", diff --git a/packages/docusaurus-plugin-content-docs/package.json b/packages/docusaurus-plugin-content-docs/package.json index 3877dd4ef37e..bbfdd49432cd 100644 --- a/packages/docusaurus-plugin-content-docs/package.json +++ b/packages/docusaurus-plugin-content-docs/package.json @@ -49,6 +49,7 @@ "fs-extra": "^11.1.1", "js-yaml": "^4.1.0", "lodash": "^4.17.21", + "schema-dts": "^1.1.2", "tslib": "^2.6.0", "utility-types": "^3.10.0", "webpack": "^5.88.1" diff --git a/packages/docusaurus-plugin-content-docs/src/client/index.ts b/packages/docusaurus-plugin-content-docs/src/client/index.ts index 6734431adca7..3751b4424b3b 100644 --- a/packages/docusaurus-plugin-content-docs/src/client/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/client/index.ts @@ -60,6 +60,8 @@ export { getDocsVersionSearchTag, } from './docsSearch'; +export {useBreadcrumbsStructuredData} from './structuredDataUtils'; + export type ActivePlugin = { pluginId: string; pluginData: GlobalPluginData; diff --git a/packages/docusaurus-plugin-content-docs/src/client/structuredDataUtils.ts b/packages/docusaurus-plugin-content-docs/src/client/structuredDataUtils.ts new file mode 100644 index 000000000000..01367a43881e --- /dev/null +++ b/packages/docusaurus-plugin-content-docs/src/client/structuredDataUtils.ts @@ -0,0 +1,32 @@ +/** + * 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 useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import type {PropSidebarBreadcrumbsItem} from '@docusaurus/plugin-content-docs'; +import type {WithContext, BreadcrumbList} from 'schema-dts'; + +export function useBreadcrumbsStructuredData({ + breadcrumbs, +}: { + breadcrumbs: PropSidebarBreadcrumbsItem[]; +}): WithContext { + const {siteConfig} = useDocusaurusContext(); + return { + '@context': 'https://schema.org', + '@type': 'BreadcrumbList', + itemListElement: breadcrumbs + // We filter breadcrumb items without links, they are not allowed + // See also https://github.com/facebook/docusaurus/issues/9319#issuecomment-2643560845 + .filter((breadcrumb) => breadcrumb.href) + .map((breadcrumb, index) => ({ + '@type': 'ListItem', + position: index + 1, + name: breadcrumb.label, + item: `${siteConfig.url}${breadcrumb.href}`, + })), + }; +} diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index 0e73434b483c..9ab3d8f90324 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -1846,3 +1846,14 @@ declare module '@theme/DocBreadcrumbs/Items/Home' { export default function HomeBreadcrumbItem(): ReactNode; } + +declare module '@theme/DocBreadcrumbs/StructuredData' { + import type {ReactNode} from 'react'; + import type {PropSidebarBreadcrumbsItem} from '@docusaurus/plugin-content-docs'; + + export interface Props { + readonly breadcrumbs: PropSidebarBreadcrumbsItem[]; + } + + export default function DocBreadcrumbsStructuredData(props: Props): ReactNode; +} diff --git a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/StructuredData/index.tsx new file mode 100644 index 000000000000..8268619df078 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/StructuredData/index.tsx @@ -0,0 +1,24 @@ +/** + * 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, {type ReactNode} from 'react'; +import Head from '@docusaurus/Head'; +import {useBreadcrumbsStructuredData} from '@docusaurus/plugin-content-docs/client'; +import type {Props} from '@theme/DocBreadcrumbs/StructuredData'; + +export default function DocBreadcrumbsStructuredData(props: Props): ReactNode { + const structuredData = useBreadcrumbsStructuredData({ + breadcrumbs: props.breadcrumbs, + }); + return ( + + + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx index 61854a0773e5..f1d566eff4c8 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocBreadcrumbs/index.tsx @@ -13,6 +13,7 @@ import {useHomePageRoute} from '@docusaurus/theme-common/internal'; import Link from '@docusaurus/Link'; import {translate} from '@docusaurus/Translate'; import HomeBreadcrumbItem from '@theme/DocBreadcrumbs/Items/Home'; +import DocBreadcrumbsStructuredData from '@theme/DocBreadcrumbs/StructuredData'; import styles from './styles.module.css'; @@ -28,22 +29,13 @@ function BreadcrumbsItemLink({ }): ReactNode { const className = 'breadcrumbs__link'; if (isLast) { - return ( - - {children} - - ); + return {children}; } return href ? ( - - {children} + + {children} ) : ( - // TODO Google search console doesn't like breadcrumb items without href. - // The schema doesn't seem to require `id` for each `item`, although Google - // insist to infer one, even if it's invalid. Removing `itemProp="item - // name"` for now, since I don't know how to properly fix it. - // See https://github.com/facebook/docusaurus/issues/7241 {children} ); } @@ -52,26 +44,16 @@ function BreadcrumbsItemLink({ function BreadcrumbsItem({ children, active, - index, - addMicrodata, }: { children: ReactNode; active?: boolean; - index: number; - addMicrodata: boolean; }): ReactNode { return (
  • {children} -
  • ); } @@ -85,40 +67,36 @@ export default function DocBreadcrumbs(): ReactNode { } return ( - + <> + + + ); } diff --git a/packages/docusaurus-theme-common/package.json b/packages/docusaurus-theme-common/package.json index 6a18379c2415..31a20b655713 100644 --- a/packages/docusaurus-theme-common/package.json +++ b/packages/docusaurus-theme-common/package.json @@ -47,8 +47,7 @@ "@docusaurus/core": "3.7.0", "@docusaurus/types": "3.7.0", "fs-extra": "^11.1.1", - "lodash": "^4.17.21", - "schema-dts": "^1.1.2" + "lodash": "^4.17.21" }, "peerDependencies": { "@docusaurus/plugin-content-docs": "*",