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 770a468f297a..35d6ac6b34db 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( @@ -182,6 +183,7 @@ export default async function pluginContentBlog( blogArchiveComponent, routeBasePath, archiveBasePath, + blogTitle, } = options; const {addRoute, createData} = actions; @@ -257,6 +259,15 @@ export default async function pluginContentBlog( ), ); + const blogMetadata: BlogMetadata = { + blogBasePath: normalizeUrl([baseUrl, routeBasePath]), + blogTitle, + }; + const blogMetadataPath = await createData( + `blogMetadata-${pluginId}.json`, + JSON.stringify(blogMetadata, null, 2), + ); + // Create routes for blog entries. await Promise.all( blogPosts.map(async (blogPost) => { @@ -276,6 +287,9 @@ export default async function pluginContentBlog( sidebar: aliasedSource(sidebarProp), content: metadata.source, }, + context: { + blogMetadata: aliasedSource(blogMetadataPath), + }, }); blogItemsToMetadata[id] = metadata; 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 7443e14e1621..f8b0bd3123ae 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'; @@ -466,6 +468,13 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the blogTagsListPath: string; }; + export type BlogMetadata = { + /** the path to the base of the blog */ + blogBasePath: string; + /** title of the overall blog */ + blogTitle: string; + }; + export type BlogTags = { [permalink: string]: BlogTag; }; @@ -537,6 +546,7 @@ declare module '@theme/BlogPostPage' { BlogPostFrontMatter, BlogSidebar, PropBlogPostContent, + BlogMetadata, } from '@docusaurus/plugin-content-blog'; export type FrontMatter = BlogPostFrontMatter; @@ -548,6 +558,8 @@ declare module '@theme/BlogPostPage' { readonly sidebar: BlogSidebar; /** Content of this post as an MDX component, with useful metadata. */ readonly content: Content; + /** Metadata about the blog. */ + readonly blogMetadata: BlogMetadata; } export default function BlogPostPage(props: Props): JSX.Element; @@ -557,6 +569,10 @@ declare module '@theme/BlogPostPage/Metadata' { export default function BlogPostPageMetadata(): JSX.Element; } +declare module '@theme/BlogPostPage/StructuredData' { + export default function BlogPostStructuredData(): JSX.Element; +} + declare module '@theme/BlogListPage' { import type {Content} from '@theme/BlogPostPage'; import type { @@ -579,6 +595,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-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/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..a4f808091d82 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogListPage/StructuredData/index.tsx @@ -0,0 +1,22 @@ +/** + * 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 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 ( + + + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogListPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogListPage/index.tsx index 4bb30e8cbf40..563327a8a2a2 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogListPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogListPage/index.tsx @@ -19,6 +19,7 @@ import BlogListPaginator from '@theme/BlogListPaginator'; import SearchMetadata from '@theme/SearchMetadata'; import type {Props} from '@theme/BlogListPage'; import BlogPostItems from '@theme/BlogPostItems'; +import BlogListPageStructuredData from '@theme/BlogListPage/StructuredData'; function BlogListPageMetadata(props: Props): JSX.Element { const {metadata} = props; @@ -54,6 +55,7 @@ export default function BlogListPage(props: Props): JSX.Element { ThemeClassNames.page.blogListPage, )}> + ); diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Container/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Container/index.tsx index 05c12d3345ca..5a4fb04e273c 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Container/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Container/index.tsx @@ -6,36 +6,11 @@ */ import React from 'react'; -import {useBaseUrlUtils} from '@docusaurus/useBaseUrl'; -import {useBlogPost} from '@docusaurus/theme-common/internal'; import type {Props} from '@theme/BlogPostItem/Container'; export default function BlogPostItemContainer({ children, className, }: Props): JSX.Element { - const { - frontMatter, - assets, - metadata: {description}, - } = useBlogPost(); - const {withBaseUrl} = useBaseUrlUtils(); - const image = assets.image ?? frontMatter.image; - const keywords = frontMatter.keywords ?? []; - return ( -
- {description && } - {image && ( - - )} - {keywords.length > 0 && ( - - )} - {children} -
- ); + return
{children}
; } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Content/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Content/index.tsx index 592df27d24f6..ebbae6884e67 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Content/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Content/index.tsx @@ -21,8 +21,7 @@ export default function BlogPostItemContent({
+ className={clsx('markdown', className)}> {children}
); diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx index 5f2eb1d7be38..5d9935febb04 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Author/index.tsx @@ -28,31 +28,18 @@ export default function BlogPostItemHeaderAuthor({
{imageURL && ( - {name} + {name} )} {name && ( - diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Info/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Info/index.tsx index 0775884f60de..22811b499ecc 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Info/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Info/index.tsx @@ -40,11 +40,7 @@ function ReadingTime({readingTime}: {readingTime: number}) { } function Date({date, formattedDate}: {date: string; formattedDate: string}) { - return ( - - ); + return ; } function Spacer() { diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Title/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Title/index.tsx index ea40c2ec752f..1a6ed4a17253 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Title/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostItem/Header/Title/index.tsx @@ -20,14 +20,8 @@ export default function BlogPostItemHeaderTitle({ const {permalink, title} = metadata; const TitleHeading = isBlogPostPage ? 'h1' : 'h2'; return ( - - {isBlogPostPage ? ( - title - ) : ( - - {title} - - )} + + {isBlogPostPage ? title : {title}} ); } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx new file mode 100644 index 000000000000..72a15a5d2d22 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/StructuredData/index.tsx @@ -0,0 +1,21 @@ +/** + * 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 Head from '@docusaurus/Head'; +import {useBlogPostStructuredData} from '@docusaurus/theme-common'; + +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 f640f603347b..6d7ed51a7fd5 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx @@ -13,6 +13,7 @@ import BlogLayout from '@theme/BlogLayout'; import BlogPostItem from '@theme/BlogPostItem'; import BlogPostPaginator from '@theme/BlogPostPaginator'; import BlogPostPageMetadata from '@theme/BlogPostPage/Metadata'; +import BlogPostPageStructuredData from '@theme/BlogPostPage/StructuredData'; import TOC from '@theme/TOC'; import type {Props} from '@theme/BlogPostPage'; import Unlisted from '@theme/Unlisted'; @@ -45,6 +46,7 @@ function BlogPostPageContent({ ) : undefined }> {unlisted && } + {children} {(nextItem || prevItem) && ( @@ -64,6 +66,7 @@ export default function BlogPostPage(props: Props): JSX.Element { ThemeClassNames.page.blogPostPage, )}> + 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/index.ts b/packages/docusaurus-theme-common/src/index.ts index 9dfbf59bc81c..b39a078c1bc0 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 { + useBlogListPageStructuredData, + useBlogPostStructuredData, +} 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..eb8cea1ad1b3 --- /dev/null +++ b/packages/docusaurus-theme-common/src/utils/structuredDataUtils.ts @@ -0,0 +1,169 @@ +/** + * 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, 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 {useBlogPost} from '../contexts/blogPost'; +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(createPersonStructuredData); + return { + author: + authorsStructuredData.length === 1 + ? authorsStructuredData[0] + : authorsStructuredData, + }; +} + +function getImage( + image: string | undefined, + withBaseUrl: BaseUrlUtils['withBaseUrl'], + title: string, +) { + return image + ? { + image: createImageStructuredData({ + 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 + 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(): WithContext { + const blogMetadata = useBlogMetadata(); + const {assets, metadata} = useBlogPost(); + const {siteConfig} = useDocusaurusContext(); + const {withBaseUrl} = useBaseUrlUtils(); + + const {date, title, description, frontMatter} = 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 + return { + '@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}${blogMetadata.blogBasePath}`, + name: blogMetadata.blogTitle, + }, + }; +} + +/** @returns A {@link https://schema.org/Person} constructed from the {@link Author} */ +function createPersonStructuredData(author: Author): Person { + 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, +}: { + imageUrl: string; + caption: string; +}): ImageObject { + return { + '@type': 'ImageObject', + '@id': imageUrl, + url: imageUrl, + contentUrl: imageUrl, + caption, + }; +} diff --git a/packages/docusaurus-types/src/routing.d.ts b/packages/docusaurus-types/src/routing.d.ts index 3e2ae2ab0bbd..aff344d52472 100644 --- a/packages/docusaurus-types/src/routing.d.ts +++ b/packages/docusaurus-types/src/routing.d.ts @@ -75,7 +75,7 @@ export type RouteContext = { /** * Plugin-specific context data. */ - data?: object | undefined; + data?: {[key: string]: unknown}; }; /** diff --git a/website/docs/seo.mdx b/website/docs/seo.mdx index 031ab1ddf340..5d8f6d56ed40 100644 --- a/website/docs/seo.mdx +++ b/website/docs/seo.mdx @@ -60,7 +60,7 @@ 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" # A cooking guide diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts index 6599b6fb0744..0d5127874378 100644 --- a/website/docusaurus.config.ts +++ b/website/docusaurus.config.ts @@ -453,6 +453,8 @@ export default async function createConfigAsync() { type: 'all', copyright: `Copyright © ${new Date().getFullYear()} Facebook, Inc.`, }, + blogTitle: 'Docusaurus blog', + blogDescription: 'Read blog posts about Docusaurus from the team', blogSidebarCount: 'ALL', blogSidebarTitle: 'All our posts', } satisfies BlogOptions, diff --git a/yarn.lock b/yarn.lock index e44765c458bd..54c8fb7ce9bc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14539,6 +14539,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"