From 74e37e86ba66525dace05d09871f4aca1af573fd Mon Sep 17 00:00:00 2001 From: Alexey Pyltsyn Date: Fri, 18 Mar 2022 19:16:32 +0300 Subject: [PATCH 1/5] fix: remove semicolon from HTML output (#6936) --- packages/docusaurus/src/client/exports/ComponentCreator.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docusaurus/src/client/exports/ComponentCreator.tsx b/packages/docusaurus/src/client/exports/ComponentCreator.tsx index da50d1b3cf..80d2f585c6 100644 --- a/packages/docusaurus/src/client/exports/ComponentCreator.tsx +++ b/packages/docusaurus/src/client/exports/ComponentCreator.tsx @@ -103,7 +103,7 @@ export default function ComponentCreator( // Is there any way to put this RouteContextProvider upper in the tree? return ( - ; + ); }, From 74f653dd82d9db9f34dc28ecc0cf4340b4e309b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Lorber?= Date: Fri, 18 Mar 2022 18:53:00 +0100 Subject: [PATCH 2/5] refactor(theme-{classic,common}): change how site/page/search metadata is handled (#6925) --- .../src/index.d.ts | 6 +- .../src/theme-classic.d.ts | 38 +--- .../src/theme/BlogArchivePage/index.tsx | 22 ++- .../src/theme/BlogListPage/index.tsx | 47 +++-- .../src/theme/BlogPostPage/index.tsx | 103 ++++++----- .../src/theme/BlogTagsListPage/index.tsx | 28 +-- .../src/theme/BlogTagsPostsPage/index.tsx | 71 ++++---- .../DocCategoryGeneratedIndexPage/index.tsx | 35 +++- .../src/theme/DocItem/index.tsx | 117 ++++++------ .../src/theme/DocPage/index.tsx | 166 +++++++++--------- .../src/theme/DocPage/styles.module.css | 4 - .../src/theme/DocTagDocListPage/index.tsx | 68 +++---- .../src/theme/DocTagsListPage/index.tsx | 37 ++-- .../src/theme/Layout/index.tsx | 25 +-- .../src/theme/MDXPage/index.tsx | 58 +++--- .../src/theme/NotFound.tsx | 71 ++++---- .../src/theme/Seo/index.tsx | 49 ------ .../{LayoutHead => SiteMetadata}/index.tsx | 36 ++-- packages/docusaurus-theme-common/src/index.ts | 3 +- ...etadataUtilsTemp.tsx => metadataUtils.tsx} | 47 +++++ .../src/theme/SearchPage/index.tsx | 13 +- packages/docusaurus/src/client/App.tsx | 4 + .../src/client/SiteMetadataDefaults.tsx | 27 +++ .../src/client/theme-fallback/Error/index.tsx | 6 +- .../client/theme-fallback/Layout/index.tsx | 27 +-- .../client/theme-fallback/NotFound/index.tsx | 30 ++-- .../src/client/theme-fallback/Root/index.tsx | 5 +- .../theme-fallback/SiteMetadata/index.tsx | 11 ++ .../__tests__/__snapshots__/base.test.ts.snap | 2 + website/_dogfooding/dogfooding.css | 22 +++ website/docs/guides/creating-pages.md | 6 +- website/docs/seo.md | 41 +++-- website/docusaurus.config.js | 5 +- website/src/css/custom.css | 4 - .../changelog/theme/ChangelogList/index.tsx | 47 +++-- .../changelog/theme/ChangelogPage/index.tsx | 152 +++++++++------- 36 files changed, 808 insertions(+), 625 deletions(-) delete mode 100644 packages/docusaurus-theme-classic/src/theme/Seo/index.tsx rename packages/docusaurus-theme-classic/src/theme/{LayoutHead => SiteMetadata}/index.tsx (76%) rename packages/docusaurus-theme-common/src/utils/{metadataUtilsTemp.tsx => metadataUtils.tsx} (54%) create mode 100644 packages/docusaurus/src/client/SiteMetadataDefaults.tsx create mode 100644 packages/docusaurus/src/client/theme-fallback/SiteMetadata/index.tsx create mode 100644 website/_dogfooding/dogfooding.css diff --git a/packages/docusaurus-module-type-aliases/src/index.d.ts b/packages/docusaurus-module-type-aliases/src/index.d.ts index d8fbaa1b4b..d8d8b53f42 100644 --- a/packages/docusaurus-module-type-aliases/src/index.d.ts +++ b/packages/docusaurus-module-type-aliases/src/index.d.ts @@ -92,8 +92,6 @@ declare module '@theme/Layout' { export interface Props { readonly children?: ReactNode; - readonly title?: string; - readonly description?: string; } export default function Layout(props: Props): JSX.Element; } @@ -117,6 +115,10 @@ declare module '@theme/Root' { export default function Root({children}: Props): JSX.Element; } +declare module '@theme/SiteMetadata' { + export default function SiteMetadata(): JSX.Element; +} + declare module '@docusaurus/constants' { export const DEFAULT_PLUGIN_ID: 'default'; } diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index 8fb54747d0..dffa79e8d3 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -364,31 +364,17 @@ declare module '@theme/Layout' { export interface Props { readonly children?: ReactNode; - readonly title?: string; readonly noFooter?: boolean; - readonly description?: string; - readonly image?: string; - readonly keywords?: string | string[]; - readonly permalink?: string; readonly wrapperClassName?: string; - readonly pageClassName?: string; - readonly searchMetadata?: { - readonly version?: string; - readonly tag?: string; - }; + + // Not really layout-related, but kept for convenience/retro-compatibility + readonly title?: string; + readonly description?: string; } export default function Layout(props: Props): JSX.Element; } -declare module '@theme/LayoutHead' { - import type {Props as LayoutProps} from '@theme/Layout'; - - export interface Props extends Omit {} - - export default function LayoutHead(props: Props): JSX.Element; -} - declare module '@theme/LayoutProviders' { import type {ReactNode} from 'react'; @@ -480,7 +466,7 @@ declare module '@theme/Navbar/Content' { declare module '@theme/Navbar/Layout' { export interface Props { - children: React.ReactNode; + readonly children: React.ReactNode; } export default function NavbarLayout(props: Props): JSX.Element; @@ -927,17 +913,3 @@ declare module '@theme/prism-include-languages' { PrismObject: typeof PrismNamespace, ): void; } - -declare module '@theme/Seo' { - import type {ReactNode} from 'react'; - - export interface Props { - readonly title?: string; - readonly description?: string; - readonly keywords?: readonly string[] | string; - readonly image?: string; - readonly children?: ReactNode; - } - - export default function Seo(props: Props): JSX.Element; -} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogArchivePage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogArchivePage/index.tsx index f10c59b191..a05afdf43f 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogArchivePage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogArchivePage/index.tsx @@ -10,6 +10,7 @@ import Layout from '@theme/Layout'; import Link from '@docusaurus/Link'; import type {ArchiveBlogPost, Props} from '@theme/BlogArchivePage'; import {translate} from '@docusaurus/Translate'; +import {PageMetadata} from '@docusaurus/theme-common'; type YearProp = { year: string; @@ -75,14 +76,17 @@ export default function BlogArchive({archive}: Props): JSX.Element { }); const years = listPostsByYears(archive.blogPosts); return ( - -
-
-

{title}

-

{description}

-
-
-
{years.length > 0 && }
-
+ <> + + +
+
+

{title}

+

{description}

+
+
+
{years.length > 0 && }
+
+ ); } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogListPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogListPage/index.tsx index 088d6e67f1..910328b214 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogListPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogListPage/index.tsx @@ -12,28 +12,34 @@ import BlogLayout from '@theme/BlogLayout'; import BlogPostItem from '@theme/BlogPostItem'; import BlogListPaginator from '@theme/BlogListPaginator'; import type {Props} from '@theme/BlogListPage'; -import {ThemeClassNames} from '@docusaurus/theme-common'; +import { + PageMetadata, + HtmlClassNameProvider, + ThemeClassNames, +} from '@docusaurus/theme-common'; +import SearchMetadata from '@theme/SearchMetadata'; +import clsx from 'clsx'; -export default function BlogListPage(props: Props): JSX.Element { - const {metadata, items, sidebar} = props; +function BlogListPageMetadata(props: Props): JSX.Element { + const {metadata} = props; const { siteConfig: {title: siteTitle}, } = useDocusaurusContext(); const {blogDescription, blogTitle, permalink} = metadata; const isBlogOnlyMode = permalink === '/'; const title = isBlogOnlyMode ? siteTitle : blogTitle; - return ( - + <> + + + + ); +} + +function BlogListPageContent(props: Props): JSX.Element { + const {metadata, items, sidebar} = props; + return ( + {items.map(({content: BlogPostContent}) => ( ); } + +export default function BlogListPage(props: Props): JSX.Element { + return ( + + + + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx index 84918a3019..c7b9ec88e5 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx @@ -6,40 +6,63 @@ */ import React from 'react'; -import Seo from '@theme/Seo'; import BlogLayout from '@theme/BlogLayout'; import BlogPostItem from '@theme/BlogPostItem'; import BlogPostPaginator from '@theme/BlogPostPaginator'; import type {Props} from '@theme/BlogPostPage'; -import {ThemeClassNames} from '@docusaurus/theme-common'; +import { + PageMetadata, + HtmlClassNameProvider, + ThemeClassNames, +} from '@docusaurus/theme-common'; import TOC from '@theme/TOC'; +import clsx from 'clsx'; -export default function BlogPostPage(props: Props): JSX.Element { +function BlogPostPageMetadata(props: Props): JSX.Element { + const {content: BlogPostContents} = props; + const {assets, metadata} = BlogPostContents; + const {title, description, date, tags, authors, frontMatter} = metadata; + const {keywords} = frontMatter; + const image = assets.image ?? frontMatter.image; + return ( + + + + {/* TODO double check those article meta array syntaxes, see https://ogp.me/#array */} + {authors.some((author) => author.url) && ( + author.url) + .filter(Boolean) + .join(',')} + /> + )} + {tags.length > 0 && ( + tag.label).join(',')} + /> + )} + + ); +} + +function BlogPostPageContent(props: Props): JSX.Element { const {content: BlogPostContents, sidebar} = props; const {assets, metadata} = BlogPostContents; - const { - title, - description, - nextItem, - prevItem, - date, - tags, - authors, - frontMatter, - } = metadata; + const {nextItem, prevItem, frontMatter} = metadata; const { hide_table_of_contents: hideTableOfContents, - keywords, toc_min_heading_level: tocMinHeadingLevel, toc_max_heading_level: tocMaxHeadingLevel, } = frontMatter; - - const image = assets.image ?? frontMatter.image; - return ( ) : undefined }> - - - - - {/* TODO double check those article meta array syntaxes, see https://ogp.me/#array */} - {authors.some((author) => author.url) && ( - author.url) - .filter(Boolean) - .join(',')} - /> - )} - {tags.length > 0 && ( - tag.label).join(',')} - /> - )} - - ); } + +export default function BlogPostPage(props: Props): JSX.Element { + return ( + + + + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogTagsListPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogTagsListPage/index.tsx index 9b354c1f71..1124e06aaa 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogTagsListPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogTagsListPage/index.tsx @@ -11,25 +11,29 @@ import BlogLayout from '@theme/BlogLayout'; import TagsListByLetter from '@theme/TagsListByLetter'; import type {Props} from '@theme/BlogTagsListPage'; import { + PageMetadata, + HtmlClassNameProvider, ThemeClassNames, translateTagsPageTitle, } from '@docusaurus/theme-common'; +import SearchMetadata from '../SearchMetadata'; +import clsx from 'clsx'; export default function BlogTagsListPage(props: Props): JSX.Element { const {tags, sidebar} = props; const title = translateTagsPageTitle(); return ( - -

{title}

- -
+ + + + +

{title}

+ +
+
); } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx index 6791d74f9d..fa3bf26a24 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx @@ -12,8 +12,15 @@ import BlogLayout from '@theme/BlogLayout'; import BlogPostItem from '@theme/BlogPostItem'; import type {Props} from '@theme/BlogTagsPostsPage'; import Translate, {translate} from '@docusaurus/Translate'; -import {ThemeClassNames, usePluralForm} from '@docusaurus/theme-common'; +import { + PageMetadata, + HtmlClassNameProvider, + ThemeClassNames, + usePluralForm, +} from '@docusaurus/theme-common'; import BlogListPaginator from '@theme/BlogListPaginator'; +import SearchMetadata from '@theme/SearchMetadata'; +import clsx from 'clsx'; // Very simple pluralization: probably good enough for now function useBlogPostsPlural() { @@ -47,38 +54,38 @@ export default function BlogTagsPostsPage(props: Props): JSX.Element { ); return ( - -
-

{title}

+ + + + +
+

{title}

- - - View All Tags - - -
+ + + View All Tags + + +
- {items.map(({content: BlogPostContent}) => ( - - - - ))} - -
+ {items.map(({content: BlogPostContent}) => ( + + + + ))} + +
+ ); } diff --git a/packages/docusaurus-theme-classic/src/theme/DocCategoryGeneratedIndexPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocCategoryGeneratedIndexPage/index.tsx index 0d6f3e5ffe..179839ac09 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocCategoryGeneratedIndexPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocCategoryGeneratedIndexPage/index.tsx @@ -6,11 +6,13 @@ */ import React from 'react'; -import {useCurrentSidebarCategory} from '@docusaurus/theme-common'; +import { + PageMetadata, + useCurrentSidebarCategory, +} from '@docusaurus/theme-common'; import type {Props} from '@theme/DocCategoryGeneratedIndexPage'; import DocCardList from '@theme/DocCardList'; import DocPaginator from '@theme/DocPaginator'; -import Seo from '@theme/Seo'; import DocVersionBanner from '@theme/DocVersionBanner'; import DocVersionBadge from '@theme/DocVersionBadge'; import DocBreadcrumbs from '@theme/DocBreadcrumbs'; @@ -19,13 +21,27 @@ import useBaseUrl from '@docusaurus/useBaseUrl'; import styles from './styles.module.css'; -export default function DocCategoryGeneratedIndexPage({ +function DocCategoryGeneratedIndexPageMetadata({ + categoryGeneratedIndex, +}: Props): JSX.Element { + return ( + + ); +} + +function DocCategoryGeneratedIndexPageContent({ categoryGeneratedIndex, }: Props): JSX.Element { const category = useCurrentSidebarCategory(); return ( <> - ); } + +export default function DocCategoryGeneratedIndexPage( + props: Props, +): JSX.Element { + return ( + <> + + + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/DocItem/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocItem/index.tsx index 45b8dd52a7..aa675efa2c 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocItem/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocItem/index.tsx @@ -10,7 +10,6 @@ import clsx from 'clsx'; import DocPaginator from '@theme/DocPaginator'; import DocVersionBanner from '@theme/DocVersionBanner'; import DocVersionBadge from '@theme/DocVersionBadge'; -import Seo from '@theme/Seo'; import type {Props} from '@theme/DocItem'; import DocItemFooter from '@theme/DocItemFooter'; import TOC from '@theme/TOC'; @@ -18,6 +17,7 @@ import TOCCollapsible from '@theme/TOCCollapsible'; import Heading from '@theme/Heading'; import styles from './styles.module.css'; import { + PageMetadata, HtmlClassNameProvider, ThemeClassNames, useWindowSize, @@ -25,18 +25,26 @@ import { import DocBreadcrumbs from '@theme/DocBreadcrumbs'; import MDXContent from '@theme/MDXContent'; -export default function DocItem(props: Props): JSX.Element { +function DocItemMetadata(props: Props): JSX.Element { const {content: DocContent} = props; const {metadata, frontMatter, assets} = DocContent; + const {keywords} = frontMatter; + const {description, title} = metadata; + const image = assets.image ?? frontMatter.image; + + return ; +} + +function DocItemContent(props: Props): JSX.Element { + const {content: DocContent} = props; + const {metadata, frontMatter} = DocContent; const { - keywords, hide_title: hideTitle, hide_table_of_contents: hideTableOfContents, toc_min_heading_level: tocMinHeadingLevel, toc_max_heading_level: tocMaxHeadingLevel, } = frontMatter; - const {description, title} = metadata; - const image = assets.image ?? frontMatter.image; + const {title} = metadata; // We only add a title if: // - user asks to hide it with front matter @@ -53,64 +61,69 @@ export default function DocItem(props: Props): JSX.Element { canRenderTOC && (windowSize === 'desktop' || windowSize === 'ssr'); return ( - - +
+
+ +
+
+ + -
-
- -
-
- - + {canRenderTOC && ( + + )} - {canRenderTOC && ( - - )} - -
- {/* +
+ {/* Title can be declared inside md content or declared through front matter and added manually. To make both cases consistent, the added title is added under the same div.markdown block See https://github.com/facebook/docusaurus/pull/4882#issuecomment-853021120 */} - {shouldAddTitle && ( -
- {title} -
- )} - - - -
+ {shouldAddTitle && ( +
+ {title} +
+ )} + + + +
- -
+ +
- -
+
- {renderTocDesktop && ( -
- -
- )}
+ {renderTocDesktop && ( +
+ +
+ )} + + ); +} + +export default function DocItem(props: Props): JSX.Element { + const docHtmlClassName = `docs-doc-id-${props.content.metadata.unversionedId}`; + return ( + + + ); } diff --git a/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx index be8ef387c7..d75ffa55a5 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx @@ -29,6 +29,7 @@ import { useDocsSidebar, DocsVersionProvider, } from '@docusaurus/theme-common'; +import SearchMetadata from '@theme/SearchMetadata'; type DocPageContentProps = { readonly currentDocRoute: DocumentRoute; @@ -56,87 +57,89 @@ function DocPageContent({ }, [hiddenSidebar]); return ( - -
- + <> + + +
+ - {sidebar && ( - + )} +
{ - if ( - !e.currentTarget.classList.contains(styles.docSidebarContainer!) - ) { - return; - } - - if (hiddenSidebarContainer) { - setHiddenSidebar(true); - } - }}> - - - {hiddenSidebar && ( -
- -
- )} - - )} -
-
- {children} -
-
-
-
+
+ {children} +
+ +
+
+ ); } @@ -161,7 +164,12 @@ export default function DocPage(props: Props): JSX.Element { : null; return ( - + -
-
-
-
-

{title}

- - - View All Tags - - -
-
- {tag.docs.map((doc) => ( - - ))} -
-
+ + + + +
+
+
+
+

{title}

+ + + View All Tags + + +
+
+ {tag.docs.map((doc) => ( + + ))} +
+
+
-
- + + ); } diff --git a/packages/docusaurus-theme-classic/src/theme/DocTagsListPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocTagsListPage/index.tsx index 9443de0841..5ab4f5a44d 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocTagsListPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocTagsListPage/index.tsx @@ -9,31 +9,36 @@ import React from 'react'; import Layout from '@theme/Layout'; import { + PageMetadata, + HtmlClassNameProvider, ThemeClassNames, translateTagsPageTitle, } from '@docusaurus/theme-common'; import TagsListByLetter from '@theme/TagsListByLetter'; import type {Props} from '@theme/DocTagsListPage'; +import SearchMetadata from '@theme/SearchMetadata'; +import clsx from 'clsx'; export default function DocTagsListPage({tags}: Props): JSX.Element { const title = translateTagsPageTitle(); return ( - -
-
-
-

{title}

- -
+ + + + +
+
+
+

{title}

+ +
+
-
- + + ); } diff --git a/packages/docusaurus-theme-classic/src/theme/Layout/index.tsx b/packages/docusaurus-theme-classic/src/theme/Layout/index.tsx index ce56fe8d01..35592ef869 100644 --- a/packages/docusaurus-theme-classic/src/theme/Layout/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Layout/index.tsx @@ -13,20 +13,30 @@ import AnnouncementBar from '@theme/AnnouncementBar'; import Navbar from '@theme/Navbar'; import Footer from '@theme/Footer'; import LayoutProviders from '@theme/LayoutProviders'; -import LayoutHead from '@theme/LayoutHead'; import type {Props} from '@theme/Layout'; -import {ThemeClassNames, useKeyboardNavigation} from '@docusaurus/theme-common'; +import { + PageMetadata, + ThemeClassNames, + useKeyboardNavigation, +} from '@docusaurus/theme-common'; import ErrorPageContent from '@theme/ErrorPageContent'; import './styles.css'; export default function Layout(props: Props): JSX.Element { - const {children, noFooter, wrapperClassName, pageClassName} = props; + const { + children, + noFooter, + wrapperClassName, + // not really layout-related, but kept for convenience/retro-compatibility + title, + description, + } = props; useKeyboardNavigation(); return ( - + @@ -34,12 +44,7 @@ export default function Layout(props: Props): JSX.Element { -
+
{children}
diff --git a/packages/docusaurus-theme-classic/src/theme/MDXPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/MDXPage/index.tsx index 7a1876a5f6..44ba2a5c0f 100644 --- a/packages/docusaurus-theme-classic/src/theme/MDXPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/MDXPage/index.tsx @@ -11,43 +11,49 @@ import Layout from '@theme/Layout'; import MDXContent from '@theme/MDXContent'; import type {Props} from '@theme/MDXPage'; import TOC from '@theme/TOC'; -import {ThemeClassNames} from '@docusaurus/theme-common'; +import { + PageMetadata, + HtmlClassNameProvider, + ThemeClassNames, +} from '@docusaurus/theme-common'; import styles from './styles.module.css'; export default function MDXPage(props: Props): JSX.Element { const {content: MDXPageContent} = props; const { - metadata: {title, description, permalink, frontMatter}, + metadata: {title, description, frontMatter}, } = MDXPageContent; const {wrapperClassName, hide_table_of_contents: hideTableOfContents} = frontMatter; return ( - -
-
-
- - - -
- {!hideTableOfContents && MDXPageContent.toc && ( -
- + + + +
+
+
+ + +
- )} -
-
-
+ {!hideTableOfContents && MDXPageContent.toc && ( +
+ +
+ )} +
+
+
+ ); } diff --git a/packages/docusaurus-theme-classic/src/theme/NotFound.tsx b/packages/docusaurus-theme-classic/src/theme/NotFound.tsx index 6d4b7a0706..2213158d52 100644 --- a/packages/docusaurus-theme-classic/src/theme/NotFound.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NotFound.tsx @@ -8,42 +8,47 @@ import React from 'react'; import Layout from '@theme/Layout'; import Translate, {translate} from '@docusaurus/Translate'; +import {PageMetadata} from '@docusaurus/theme-common'; export default function NotFound(): JSX.Element { return ( - -
-
-
-

- - Page Not Found - -

-

- - We could not find what you were looking for. - -

-

- - Please contact the owner of the site that linked you to the - original URL and let them know their link is broken. - -

+ <> + + +
+
+
+

+ + Page Not Found + +

+

+ + We could not find what you were looking for. + +

+

+ + Please contact the owner of the site that linked you to the + original URL and let them know their link is broken. + +

+
-
-
-
+ + + ); } diff --git a/packages/docusaurus-theme-classic/src/theme/Seo/index.tsx b/packages/docusaurus-theme-classic/src/theme/Seo/index.tsx deleted file mode 100644 index 6acdbe0864..0000000000 --- a/packages/docusaurus-theme-classic/src/theme/Seo/index.tsx +++ /dev/null @@ -1,49 +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'; -import Head from '@docusaurus/Head'; -import {useTitleFormatter} from '@docusaurus/theme-common'; -import {useBaseUrlUtils} from '@docusaurus/useBaseUrl'; - -import type {Props} from '@theme/Seo'; - -export default function Seo({ - title, - description, - keywords, - image, - children, -}: Props): JSX.Element { - const pageTitle = useTitleFormatter(title); - const {withBaseUrl} = useBaseUrlUtils(); - const pageImage = image ? withBaseUrl(image, {absolute: true}) : undefined; - - return ( - - {title && {pageTitle}} - {title && } - - {description && } - {description && } - - {keywords && ( - - )} - - {pageImage && } - {pageImage && } - - {children} - - ); -} diff --git a/packages/docusaurus-theme-classic/src/theme/LayoutHead/index.tsx b/packages/docusaurus-theme-classic/src/theme/SiteMetadata/index.tsx similarity index 76% rename from packages/docusaurus-theme-classic/src/theme/LayoutHead/index.tsx rename to packages/docusaurus-theme-classic/src/theme/SiteMetadata/index.tsx index a3f5204ce5..e21668aa90 100644 --- a/packages/docusaurus-theme-classic/src/theme/LayoutHead/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/SiteMetadata/index.tsx @@ -9,19 +9,18 @@ import React from 'react'; import Head from '@docusaurus/Head'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import useBaseUrl from '@docusaurus/useBaseUrl'; -import type {Props} from '@theme/Layout'; import SearchMetadata from '@theme/SearchMetadata'; -import Seo from '@theme/Seo'; import { + PageMetadata, DEFAULT_SEARCH_TAG, - useTitleFormatter, useAlternatePageUtils, useThemeConfig, keyboardFocusedClassName, } from '@docusaurus/theme-common'; import {useLocation} from '@docusaurus/router'; -// Useful for SEO +// TODO move to SiteMetadataDefaults or theme-common ? +// Useful for i18n/SEO // See https://developers.google.com/search/docs/advanced/crawling/localized-versions // See https://github.com/facebook/docusaurus/issues/3317 function AlternateLangHeaders(): JSX.Element { @@ -66,6 +65,7 @@ function useDefaultCanonicalUrl() { return siteUrl + useBaseUrl(pathname); } +// TODO move to SiteMetadataDefaults or theme-common ? function CanonicalUrlHeaders({permalink}: {permalink?: string}) { const { siteConfig: {url: siteUrl}, @@ -83,45 +83,31 @@ function CanonicalUrlHeaders({permalink}: {permalink?: string}) { ); } -export default function LayoutHead(props: Props): JSX.Element { +export default function SiteMetadata(): JSX.Element { const { - siteConfig: {favicon}, - i18n: {currentLocale, localeConfigs}, + i18n: {currentLocale}, } = useDocusaurusContext(); + + // TODO maybe move these 2 themeConfig to siteConfig? + // These seems useful for other themes as well const {metadata, image: defaultImage} = useThemeConfig(); - const {title, description, image, keywords, searchMetadata} = props; - const faviconUrl = useBaseUrl(favicon); - const pageTitle = useTitleFormatter(title); - const {htmlLang, direction: htmlDir} = localeConfigs[currentLocale]!; return ( <> - - {favicon && } - {pageTitle} - {/* The keyboard focus class name need to be applied when SSR so links are outlined when JS is disabled */} - {/* image can override the default image */} - {defaultImage && } - {image && } - - + {defaultImage && } - + element here, diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index a670df39a2..b479c3089a 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -129,9 +129,10 @@ export {isRegexpStringMatch} from './utils/regexpUtils'; export {useHomePageRoute} from './utils/routesUtils'; export { + PageMetadata, HtmlClassNameProvider, PluginHtmlClassNameProvider, -} from './utils/metadataUtilsTemp'; +} from './utils/metadataUtils'; export { useColorMode, diff --git a/packages/docusaurus-theme-common/src/utils/metadataUtilsTemp.tsx b/packages/docusaurus-theme-common/src/utils/metadataUtils.tsx similarity index 54% rename from packages/docusaurus-theme-common/src/utils/metadataUtilsTemp.tsx rename to packages/docusaurus-theme-common/src/utils/metadataUtils.tsx index f55899063b..9de3163715 100644 --- a/packages/docusaurus-theme-common/src/utils/metadataUtilsTemp.tsx +++ b/packages/docusaurus-theme-common/src/utils/metadataUtils.tsx @@ -9,6 +9,53 @@ import React, {type ReactNode} from 'react'; import Head from '@docusaurus/Head'; import clsx from 'clsx'; import useRouteContext from '@docusaurus/useRouteContext'; +import {useBaseUrlUtils} from '@docusaurus/useBaseUrl'; +import {useTitleFormatter} from './generalUtils'; + +interface PageMetadataProps { + readonly title?: string; + readonly description?: string; + readonly keywords?: readonly string[] | string; + readonly image?: string; + readonly children?: ReactNode; +} + +// Helper component to manipulate page metadata and override site defaults +export function PageMetadata({ + title, + description, + keywords, + image, + children, +}: PageMetadataProps): JSX.Element { + const pageTitle = useTitleFormatter(title); + const {withBaseUrl} = useBaseUrlUtils(); + const pageImage = image ? withBaseUrl(image, {absolute: true}) : undefined; + + return ( + + {title && {pageTitle}} + {title && } + + {description && } + {description && } + + {keywords && ( + + )} + + {pageImage && } + {pageImage && } + + {children} + + ); +} const HtmlClassNameContext = React.createContext(undefined); diff --git a/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx b/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx index a9227bdff4..8ce17fa12a 100644 --- a/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx +++ b/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx @@ -17,6 +17,7 @@ import Head from '@docusaurus/Head'; import Link from '@docusaurus/Link'; import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment'; import { + HtmlClassNameProvider, useTitleFormatter, usePluralForm, isRegexpStringMatch, @@ -149,7 +150,7 @@ type ResultDispatcher = | {type: 'update'; value: ResultDispatcherState} | {type: 'advance'; value?: undefined}; -export default function SearchPage(): JSX.Element { +function SearchPageContent(): JSX.Element { const { siteConfig: {themeConfig}, i18n: {currentLocale}, @@ -356,7 +357,7 @@ export default function SearchPage(): JSX.Element { }, [makeSearch, searchResultState.lastPage]); return ( - + {useTitleFormatter(getTitle())} {/* @@ -516,3 +517,11 @@ export default function SearchPage(): JSX.Element { ); } + +export default function SearchPage(): JSX.Element { + return ( + + + + ); +} diff --git a/packages/docusaurus/src/client/App.tsx b/packages/docusaurus/src/client/App.tsx index 9378463a20..6d434b2c61 100644 --- a/packages/docusaurus/src/client/App.tsx +++ b/packages/docusaurus/src/client/App.tsx @@ -13,7 +13,9 @@ import {BrowserContextProvider} from './exports/browserContext'; import {DocusaurusContextProvider} from './exports/docusaurusContext'; import PendingNavigation from './PendingNavigation'; import BaseUrlIssueBanner from './baseUrlIssueBanner/BaseUrlIssueBanner'; +import SiteMetadataDefaults from './SiteMetadataDefaults'; import Root from '@theme/Root'; +import SiteMetadata from '@theme/SiteMetadata'; import './client-lifecycles-dispatcher'; @@ -27,6 +29,8 @@ export default function App(): JSX.Element { + + {renderRoutes(routes)} diff --git a/packages/docusaurus/src/client/SiteMetadataDefaults.tsx b/packages/docusaurus/src/client/SiteMetadataDefaults.tsx new file mode 100644 index 0000000000..bd2f953aab --- /dev/null +++ b/packages/docusaurus/src/client/SiteMetadataDefaults.tsx @@ -0,0 +1,27 @@ +/** + * 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 useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import useBaseUrl from '@docusaurus/useBaseUrl'; + +export default function SiteMetadataDefaults(): JSX.Element { + const { + siteConfig: {favicon, tagline, title}, + i18n: {currentLocale, localeConfigs}, + } = useDocusaurusContext(); + const faviconUrl = useBaseUrl(favicon); + const {htmlLang, direction: htmlDir} = localeConfigs[currentLocale]!; + + return ( + + + {favicon && } + + ); +} diff --git a/packages/docusaurus/src/client/theme-fallback/Error/index.tsx b/packages/docusaurus/src/client/theme-fallback/Error/index.tsx index 858e42c4de..de8ded15b7 100644 --- a/packages/docusaurus/src/client/theme-fallback/Error/index.tsx +++ b/packages/docusaurus/src/client/theme-fallback/Error/index.tsx @@ -9,6 +9,7 @@ import React from 'react'; import Layout from '@theme/Layout'; import ErrorBoundary from '@docusaurus/ErrorBoundary'; import type {Props} from '@theme/Error'; +import Head from '@docusaurus/Head'; function ErrorDisplay({error, tryAgain}: Props): JSX.Element { return ( @@ -40,7 +41,10 @@ export default function Error({error, tryAgain}: Props): JSX.Element { // Note: we display the original error here, not the error that we // captured in this extra error boundary fallback={() => }> - + + Page Error + + diff --git a/packages/docusaurus/src/client/theme-fallback/Layout/index.tsx b/packages/docusaurus/src/client/theme-fallback/Layout/index.tsx index 1c3de03a60..e897396623 100644 --- a/packages/docusaurus/src/client/theme-fallback/Layout/index.tsx +++ b/packages/docusaurus/src/client/theme-fallback/Layout/index.tsx @@ -6,31 +6,8 @@ */ import React from 'react'; -import Head from '@docusaurus/Head'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import useBaseUrl from '@docusaurus/useBaseUrl'; import type {Props} from '@theme/Layout'; -export default function Layout({ - children, - title, - description, -}: Props): JSX.Element { - const context = useDocusaurusContext(); - const {siteConfig} = context; - const {favicon, tagline, title: defaultTitle} = siteConfig; - const faviconUrl = useBaseUrl(favicon); - return ( - <> - - {title && {`${title} · ${tagline}`}} - {favicon && } - {description && } - {description && ( - - )} - - {children} - - ); +export default function Layout({children}: Props): JSX.Element { + return <>{children}; } diff --git a/packages/docusaurus/src/client/theme-fallback/NotFound/index.tsx b/packages/docusaurus/src/client/theme-fallback/NotFound/index.tsx index 48e11300f7..15ccfaefe6 100644 --- a/packages/docusaurus/src/client/theme-fallback/NotFound/index.tsx +++ b/packages/docusaurus/src/client/theme-fallback/NotFound/index.tsx @@ -7,20 +7,26 @@ import React from 'react'; import Layout from '@theme/Layout'; +import Head from '@docusaurus/Head'; export default function NotFound(): JSX.Element { return ( - -
-

Oops, page not found

-
-
+ <> + + Page Not Found + + +
+

Oops, page not found

+
+
+ ); } diff --git a/packages/docusaurus/src/client/theme-fallback/Root/index.tsx b/packages/docusaurus/src/client/theme-fallback/Root/index.tsx index 0e09dc1f42..27ef85e88a 100644 --- a/packages/docusaurus/src/client/theme-fallback/Root/index.tsx +++ b/packages/docusaurus/src/client/theme-fallback/Root/index.tsx @@ -5,7 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -import React, {type ReactNode} from 'react'; +import React from 'react'; +import type {Props} from '@theme/Root'; // Wrapper at the very top of the app, that is applied constantly // and does not depend on current route (unlike the layout) @@ -14,6 +15,6 @@ import React, {type ReactNode} from 'react'; // and these providers won't reset state when we navigate // // See https://github.com/facebook/docusaurus/issues/3919 -export default function Root({children}: {children: ReactNode}): JSX.Element { +export default function Root({children}: Props): JSX.Element { return <>{children}; } diff --git a/packages/docusaurus/src/client/theme-fallback/SiteMetadata/index.tsx b/packages/docusaurus/src/client/theme-fallback/SiteMetadata/index.tsx new file mode 100644 index 0000000000..600acae8f2 --- /dev/null +++ b/packages/docusaurus/src/client/theme-fallback/SiteMetadata/index.tsx @@ -0,0 +1,11 @@ +/** + * 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. + */ + +// To be implemented by the theme with +export default function SiteMetadata(): JSX.Element | null { + return null; +} diff --git a/packages/docusaurus/src/webpack/__tests__/__snapshots__/base.test.ts.snap b/packages/docusaurus/src/webpack/__tests__/__snapshots__/base.test.ts.snap index 1e46aa3c5e..a0a080259f 100644 --- a/packages/docusaurus/src/webpack/__tests__/__snapshots__/base.test.ts.snap +++ b/packages/docusaurus/src/webpack/__tests__/__snapshots__/base.test.ts.snap @@ -33,6 +33,7 @@ exports[`base webpack config creates webpack aliases 1`] = ` "@theme-original/PluginThemeComponentEnhanced": "secondPluginThemeFolder/PluginThemeComponentEnhanced.js", "@theme-original/PluginThemeComponentOverriddenByUser": "pluginThemeFolder/PluginThemeComponentOverriddenByUser.js", "@theme-original/Root": "../../../../client/theme-fallback/Root/index.tsx", + "@theme-original/SiteMetadata": "../../../../client/theme-fallback/SiteMetadata/index.tsx", "@theme-original/subfolder/PluginThemeComponent2": "pluginThemeFolder/subfolder/PluginThemeComponent2.js", "@theme/Error": "../../../../client/theme-fallback/Error/index.tsx", "@theme/Layout": "../../../../client/theme-fallback/Layout/index.tsx", @@ -42,6 +43,7 @@ exports[`base webpack config creates webpack aliases 1`] = ` "@theme/PluginThemeComponentEnhanced": "src/theme/PluginThemeComponentEnhanced.js", "@theme/PluginThemeComponentOverriddenByUser": "src/theme/PluginThemeComponentOverriddenByUser.js", "@theme/Root": "../../../../client/theme-fallback/Root/index.tsx", + "@theme/SiteMetadata": "../../../../client/theme-fallback/SiteMetadata/index.tsx", "@theme/UserThemeComponent1": "src/theme/UserThemeComponent1.js", "@theme/subfolder/PluginThemeComponent2": "pluginThemeFolder/subfolder/PluginThemeComponent2.js", "@theme/subfolder/UserThemeComponent2": "src/theme/subfolder/UserThemeComponent2.js", diff --git a/website/_dogfooding/dogfooding.css b/website/_dogfooding/dogfooding.css new file mode 100644 index 0000000000..be6cf970b6 --- /dev/null +++ b/website/_dogfooding/dogfooding.css @@ -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. + */ + +html.plugin-docs.plugin-id-docs-tests .red > a { + color: red; +} + +html.plugin-docs.plugin-id-docs-tests .navbar { + border-bottom: solid thin cyan; +} + +html.plugin-blog.plugin-id-blog-tests .navbar { + border-bottom: solid thin lime; +} + +html.plugin-pages.plugin-id-pages-tests .navbar { + border-bottom: solid thin yellow; +} diff --git a/website/docs/guides/creating-pages.md b/website/docs/guides/creating-pages.md index bf4c277d4a..160b4dbf1d 100644 --- a/website/docs/guides/creating-pages.md +++ b/website/docs/guides/creating-pages.md @@ -35,9 +35,9 @@ Create a file `/src/pages/helloReact.js`: import React from 'react'; import Layout from '@theme/Layout'; -function Hello() { +export default function Hello() { return ( - +
); } - -export default Hello; ``` Once you save the file, the development server will automatically reload the changes. Now open `http://localhost:3000/helloReact` and you will see the new page you just created. diff --git a/website/docs/seo.md b/website/docs/seo.md index 111560af28..3c61f7fae5 100644 --- a/website/docs/seo.md +++ b/website/docs/seo.md @@ -42,22 +42,6 @@ Similar to [global metadata](#global-metadata), Docusaurus also allows for the a Some content... ``` -```jsx title="my-react-page.jsx" -import React from 'react'; -import Head from '@docusaurus/Head'; - -export default function page() { - return ( - - - - - {/* ... */} - - ); -} -``` - Docusaurus automatically adds `description`, `title`, canonical URL links, and other useful metadata to each Markdown page. They are configurable through front matter: ```md @@ -77,6 +61,31 @@ Prefer to use front matter for fields like `description` and `keywords`: Docusau ::: +For JSX pages, you can use the Docusaurus [``](docusaurus-core.md#head) component. + +```jsx title="my-react-page.jsx" +import React from 'react'; +import Layout from '@theme/Layout'; +import Head from '@docusaurus/Head'; + +export default function page() { + return ( + + + + + {/* ... */} + + ); +} +``` + +:::tip + +For convenience, the default theme `` component accept `title` and `description` as props. + +::: + ## Static HTML generation {#static-html-generation} Docusaurus is a static site generator—HTML files are statically generated for every URL route, which helps search engines discover your content more easily. diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 7d029dbe20..495072183f 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -320,7 +320,10 @@ const config = { remarkPlugins: [npm2yarn], }, theme: { - customCss: [require.resolve('./src/css/custom.css')], + customCss: [ + require.resolve('./src/css/custom.css'), + require.resolve('./_dogfooding/dogfooding.css'), + ], }, gtag: !isDeployPreview ? { diff --git a/website/src/css/custom.css b/website/src/css/custom.css index 8c774d3fb1..728c5d628a 100644 --- a/website/src/css/custom.css +++ b/website/src/css/custom.css @@ -166,10 +166,6 @@ div[class^='announcementBar_'] { font-weight: bold; } -.red > a { - color: red; -} - .screen-reader-only { border: 0; clip: rect(0 0 0 0); diff --git a/website/src/plugins/changelog/theme/ChangelogList/index.tsx b/website/src/plugins/changelog/theme/ChangelogList/index.tsx index 1d16dda586..f05400ab57 100644 --- a/website/src/plugins/changelog/theme/ChangelogList/index.tsx +++ b/website/src/plugins/changelog/theme/ChangelogList/index.tsx @@ -9,27 +9,35 @@ import React from 'react'; import BlogLayout from '@theme/BlogLayout'; import BlogListPaginator from '@theme/BlogListPaginator'; import type {Props} from '@theme/BlogListPage'; -import {ThemeClassNames} from '@docusaurus/theme-common'; +import { + PageMetadata, + HtmlClassNameProvider, + ThemeClassNames, +} from '@docusaurus/theme-common'; import Link from '@docusaurus/Link'; import ChangelogItem from '@theme/ChangelogItem'; import styles from './styles.module.css'; +import SearchMetadata from '@theme/SearchMetadata'; +import clsx from 'clsx'; -export default function ChangelogList(props: Props): JSX.Element { +function ChangelogListMetadata(props: Props): JSX.Element { + const {metadata} = props; + const {blogTitle, blogDescription} = metadata; + return ( + <> + + + + ); +} + +function ChangelogListContent(props: Props): JSX.Element { const {metadata, items, sidebar} = props; - const {blogDescription, blogTitle} = metadata; + const {blogTitle} = metadata; return ( - +

{blogTitle}

@@ -88,3 +96,16 @@ export default function ChangelogList(props: Props): JSX.Element { ); } + +export default function ChangelogList(props: Props): JSX.Element { + return ( + + + + + ); +} diff --git a/website/src/plugins/changelog/theme/ChangelogPage/index.tsx b/website/src/plugins/changelog/theme/ChangelogPage/index.tsx index 355bfaa58b..186b4aac7c 100644 --- a/website/src/plugins/changelog/theme/ChangelogPage/index.tsx +++ b/website/src/plugins/changelog/theme/ChangelogPage/index.tsx @@ -6,96 +6,116 @@ */ import React from 'react'; -import Seo from '@theme/Seo'; import BlogLayout from '@theme/BlogLayout'; import ChangelogItem from '@theme/ChangelogItem'; import BlogPostPaginator from '@theme/BlogPostPaginator'; import type {Props} from '@theme/BlogPostPage'; -import {ThemeClassNames} from '@docusaurus/theme-common'; +import { + PageMetadata, + HtmlClassNameProvider, + ThemeClassNames, +} from '@docusaurus/theme-common'; import TOC from '@theme/TOC'; import Link from '@docusaurus/Link'; +import clsx from 'clsx'; -// This page doesn't change anything. It's just swapping BlogPostItem with our -// own ChangelogItem. We don't want to apply the swizzled item to the actual -// blog. -export default function BlogPostPage(props: Props): JSX.Element { +function ChangelogPageMetadata(props: Props): JSX.Element { + const {content: BlogPostContents} = props; + const {assets, metadata} = BlogPostContents; + const {title, description, date, tags, authors, frontMatter} = metadata; + const {keywords} = frontMatter; + + const image = assets.image ?? frontMatter.image; + return ( + + + + + {authors.some((author) => author.url) && ( + author.url) + .filter(Boolean) + .join(',')} + /> + )} + {tags.length > 0 && ( + tag.label).join(',')} + /> + )} + + ); +} + +function ChangelogPageContent(props: Props): JSX.Element { const {content: BlogPostContents, sidebar} = props; const {assets, metadata} = BlogPostContents; const { - title, - description, nextItem, prevItem, - date, - tags, - authors, frontMatter, // @ts-expect-error: we injected this listPageLink, } = metadata; const { hide_table_of_contents: hideTableOfContents, - keywords, toc_min_heading_level: tocMinHeadingLevel, toc_max_heading_level: tocMaxHeadingLevel, } = frontMatter; - const image = assets.image ?? frontMatter.image; - return ( - 0 ? ( - - ) : undefined - }> - - - + <> + + 0 ? ( + + ) : undefined + }> + ← Back to index page - {authors.some((author) => author.url) && ( - author.url) - .filter(Boolean) - .join(',')} - /> + + + + + {(nextItem || prevItem) && ( + )} - {tags.length > 0 && ( - tag.label).join(',')} - /> - )} - - - ← Back to index page - - - - - - {(nextItem || prevItem) && ( - - )} - + + + ); +} + +// This page doesn't change anything. It's just swapping BlogPostItem with our +// own ChangelogItem. We don't want to apply the swizzled item to the actual +// blog. +export default function ChangelogPage(props: Props): JSX.Element { + return ( + + + + ); } From 53f152c42d69760cbee11e6741e9542b533fd937 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Sat, 19 Mar 2022 09:30:31 +0800 Subject: [PATCH 3/5] refactor(core): improve error message when a page has no default-export (#6941) --- .../src/client/exports/ComponentCreator.tsx | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/docusaurus/src/client/exports/ComponentCreator.tsx b/packages/docusaurus/src/client/exports/ComponentCreator.tsx index 80d2f585c6..4c610305a7 100644 --- a/packages/docusaurus/src/client/exports/ComponentCreator.tsx +++ b/packages/docusaurus/src/client/exports/ComponentCreator.tsx @@ -75,21 +75,23 @@ export default function ComponentCreator( // Clone the original object since we don't want to alter the original. const loadedModules = JSON.parse(JSON.stringify(chunkNames)); Object.keys(loaded).forEach((key) => { + const newComp = loaded[key].default; + if (!newComp) { + throw new Error( + `The page component at ${path} doesn't have a default export. This makes it impossible to render anything. Consider default-exporting a React component.`, + ); + } + Object.keys(loaded[key]) + .filter((k) => k !== 'default') + .forEach((nonDefaultKey) => { + newComp[nonDefaultKey] = loaded[key][nonDefaultKey]; + }); let val = loadedModules; const keyPath = key.split('.'); keyPath.slice(0, -1).forEach((k) => { val = val[k]; }); - val[keyPath[keyPath.length - 1]!] = loaded[key].default; - const nonDefaultKeys = Object.keys(loaded[key]).filter( - (k) => k !== 'default', - ); - if (nonDefaultKeys?.length) { - nonDefaultKeys.forEach((nonDefaultKey) => { - val[keyPath[keyPath.length - 1]!][nonDefaultKey] = - loaded[key][nonDefaultKey]; - }); - } + val[keyPath[keyPath.length - 1]!] = newComp; }); const Component = loadedModules.component; From 19942f990ab3ede56a0e61b70e72e221ddce23d2 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Sat, 19 Mar 2022 20:56:30 +0800 Subject: [PATCH 4/5] chore: migrate Jest and website to SWC (#6944) * chore: migrate Jest and website to SWC * ignore template --- .eslintignore | 2 +- babel.config.js | 21 -- jest.config.mjs | 2 +- package.json | 2 + .../src/utils/reactUtils.tsx | 3 +- .../src/client/exports/ComponentCreator.tsx | 15 +- website/docusaurus.config.js | 14 +- website/package.json | 3 +- yarn.lock | 258 +++++++----------- 9 files changed, 134 insertions(+), 186 deletions(-) delete mode 100644 babel.config.js diff --git a/.eslintignore b/.eslintignore index d870ab9730..f8b4a50d08 100644 --- a/.eslintignore +++ b/.eslintignore @@ -16,6 +16,6 @@ packages/stylelint-copyright/lib/ copyUntypedFiles.mjs packages/create-docusaurus/lib/* -packages/create-docusaurus/templates/facebook/.eslintrc.js +packages/create-docusaurus/templates/facebook website/_dogfooding/_swizzle_theme_tests diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index 67cf053df3..0000000000 --- a/babel.config.js +++ /dev/null @@ -1,21 +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. - */ - -module.exports = { - presets: [ - [ - '@babel/env', - { - targets: { - node: 'current', - }, - }, - ], - '@babel/react', - '@babel/preset-typescript', - ], -}; diff --git a/jest.config.mjs b/jest.config.mjs index d973ca87d2..09725a8e36 100644 --- a/jest.config.mjs +++ b/jest.config.mjs @@ -30,7 +30,7 @@ export default { testPathIgnorePatterns: ignorePatterns, coveragePathIgnorePatterns: ignorePatterns, transform: { - '^.+\\.[jt]sx?$': 'babel-jest', + '^.+\\.[jt]sx?$': '@swc/jest', }, errorOnDeprecated: true, moduleNameMapper: { diff --git a/package.json b/package.json index fb186a2b16..894d265b87 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,8 @@ "@babel/core": "^7.17.7", "@babel/preset-typescript": "^7.16.7", "@crowdin/cli": "^3.7.8", + "@swc/core": "^1.2.158", + "@swc/jest": "^0.2.20", "@testing-library/react-hooks": "^7.0.2", "@types/fs-extra": "^9.0.13", "@types/jest": "^27.4.1", diff --git a/packages/docusaurus-theme-common/src/utils/reactUtils.tsx b/packages/docusaurus-theme-common/src/utils/reactUtils.tsx index ae34933d1b..e1555c1750 100644 --- a/packages/docusaurus-theme-common/src/utils/reactUtils.tsx +++ b/packages/docusaurus-theme-common/src/utils/reactUtils.tsx @@ -47,7 +47,8 @@ export class ReactContextError extends Error { super(); this.name = 'ReactContextError'; this.message = `Hook ${ - this.stack?.split('\n')[1]?.match(/at (?\w+)/)?.groups!.name + this.stack?.split('\n')[1]?.match(/at (?:\w+\.)?(?\w+)/)?.groups! + .name } is called outside the <${providerName}>. ${additionalInfo || ''}`; } } diff --git a/packages/docusaurus/src/client/exports/ComponentCreator.tsx b/packages/docusaurus/src/client/exports/ComponentCreator.tsx index 4c610305a7..b33b8cff35 100644 --- a/packages/docusaurus/src/client/exports/ComponentCreator.tsx +++ b/packages/docusaurus/src/client/exports/ComponentCreator.tsx @@ -62,7 +62,8 @@ export default function ComponentCreator( if (chunkRegistry) { // eslint-disable-next-line prefer-destructuring optsLoader[key] = chunkRegistry[0]; - optsModules.push(chunkRegistry[1], chunkRegistry[2]); + optsModules.push(chunkRegistry[1]); + optsWebpack.push(chunkRegistry[2]); } }); @@ -81,11 +82,13 @@ export default function ComponentCreator( `The page component at ${path} doesn't have a default export. This makes it impossible to render anything. Consider default-exporting a React component.`, ); } - Object.keys(loaded[key]) - .filter((k) => k !== 'default') - .forEach((nonDefaultKey) => { - newComp[nonDefaultKey] = loaded[key][nonDefaultKey]; - }); + if (typeof newComp === 'object' || typeof newComp === 'function') { + Object.keys(loaded[key]) + .filter((k) => k !== 'default') + .forEach((nonDefaultKey) => { + newComp[nonDefaultKey] = loaded[key][nonDefaultKey]; + }); + } let val = loadedModules; const keyPath = key.split('.'); keyPath.slice(0, -1).forEach((k) => { diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 495072183f..775ada7646 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -94,10 +94,18 @@ const config = { }, webpack: { jsLoader: (isServer) => ({ - loader: require.resolve('esbuild-loader'), + loader: require.resolve('swc-loader'), options: { - loader: 'tsx', - target: isServer ? 'node12' : 'es2017', + jsc: { + "parser": { + "syntax": "typescript", + "tsx": true + }, + target: 'es2017', + }, + module: { + type: isServer ? 'commonjs' : 'es6', + } }, }), }, diff --git a/website/package.json b/website/package.json index 71a07a2fb6..f5df211f8c 100644 --- a/website/package.json +++ b/website/package.json @@ -49,9 +49,9 @@ "@docusaurus/utils": "2.0.0-beta.17", "@docusaurus/utils-common": "2.0.0-beta.17", "@popperjs/core": "^2.11.4", + "@swc/core": "^1.2.158", "clsx": "^1.1.1", "color": "^4.2.1", - "esbuild-loader": "2.18.0", "fs-extra": "^10.0.1", "netlify-plugin-cache": "^1.0.3", "raw-loader": "^4.0.2", @@ -61,6 +61,7 @@ "react-popper": "^2.2.5", "rehype-katex": "^6.0.2", "remark-math": "^3.0.1", + "swc-loader": "^0.1.15", "unist-util-visit": "^2.0.2", "workbox-routing": "^6.5.1", "workbox-strategies": "^6.5.1" diff --git a/yarn.lock b/yarn.lock index c75c67a3a9..039857635d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1751,6 +1751,13 @@ slash "^3.0.0" strip-ansi "^6.0.0" +"@jest/create-cache-key-function@^27.4.2": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-27.5.1.tgz#7448fae15602ea95c828f5eceed35c202a820b31" + integrity sha512-dmH1yW+makpTSURTy8VzdUwFnfQh1G8R+DxO2Ho2FFmBbKFEVm+3jWdvFhE2VqB/LATCTokkP0dotjyQyw5/AQ== + dependencies: + "@jest/types" "^27.5.1" + "@jest/environment@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" @@ -3579,6 +3586,97 @@ "@svgr/plugin-jsx" "^6.2.1" "@svgr/plugin-svgo" "^6.2.0" +"@swc/core-android-arm-eabi@1.2.158": + version "1.2.158" + resolved "https://registry.yarnpkg.com/@swc/core-android-arm-eabi/-/core-android-arm-eabi-1.2.158.tgz#c850b614854da8e58e27cf51390bee656f093cc7" + integrity sha512-8RHlMo9+N8V5EE/2VOCF9H9DU3s3rj6SIRpTnQbIaJlZNwqCHp+q8xQGfKEFTrY2GShhFa/vN+w279gl2NXA+g== + +"@swc/core-android-arm64@1.2.158": + version "1.2.158" + resolved "https://registry.yarnpkg.com/@swc/core-android-arm64/-/core-android-arm64-1.2.158.tgz#62764c3f509df24c6f504827d2cb7adad8a2f634" + integrity sha512-lfSUGzIjIvyj9sMtvnL6VPuC0XryfVCs3Fsvzbk4H0bi3nSDYFmVbpBvXZFhd60lcw1bcOFepBfi70LFmnhHTQ== + +"@swc/core-darwin-arm64@1.2.158": + version "1.2.158" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.2.158.tgz#64ef42f32b06c64c61d2648a5bbca8766338197c" + integrity sha512-vrdITsJjbx7lVN43Aq//gT+NRSdxS1+KxC6EiOct3qLcQA+P7w1nehZnlR+4qRLCgbBmQZQeeNnInaKpm9G7+g== + +"@swc/core-darwin-x64@1.2.158": + version "1.2.158" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.2.158.tgz#0ca64b3d244771e4d99a1555c4e9052dbe63d00f" + integrity sha512-+SIZgX01YEbTTClVdbc4aNR4dDsIVP+JiXxH1Zq5JYSsGxXzunRBMYcmTxnxRK2RHY1wOsLMD8AT5lZqQK6jsg== + +"@swc/core-freebsd-x64@1.2.158": + version "1.2.158" + resolved "https://registry.yarnpkg.com/@swc/core-freebsd-x64/-/core-freebsd-x64-1.2.158.tgz#f0609f2f4c9d5bde24f6ab4b061ee42fbaec85f5" + integrity sha512-a+dF5T+Wi95E5IrMlHdGVETUgFkeL2roFT7cfjfWokR8UudD40kYkr8dxOBFizeIvgoeQdQ0hnJJl1dASL/ydA== + +"@swc/core-linux-arm-gnueabihf@1.2.158": + version "1.2.158" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.2.158.tgz#c166c42e6c07b1f1c273b4034d40ce486a23ef00" + integrity sha512-+B/WYr8RRe6YcCUAfD8r/p2rGrxEEDud2MXxbAS3OMYuSYrFzfOxqKzCd6rQ7/OTXpTpapg0yctvhzOyArtAZw== + +"@swc/core-linux-arm64-gnu@1.2.158": + version "1.2.158" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.2.158.tgz#597577dda702400f5f5d4cf49b32bb3fa6017835" + integrity sha512-QNTs6g9VYMF4UxRnSCMe7TRAPgCdsaUbHeWhaRtRE2nfKN4fd0YYPOzODEi7P3mvLW5p75FlHtRWokaME/J1HA== + +"@swc/core-linux-arm64-musl@1.2.158": + version "1.2.158" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.2.158.tgz#fb8e9aadace980eaa408596edb78090b20f4d982" + integrity sha512-dylgrtZQJIZ6JfRDL87sPdXlOew/hl5VQaIjjhN6hu+tuRmAHzyN50DJIioErMxqFFaxnqJCxMZUFX0AlPwEKg== + +"@swc/core-linux-x64-gnu@1.2.158": + version "1.2.158" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.2.158.tgz#190113e5afa441e20db60d67ea1926ed1f9e1ae6" + integrity sha512-f+l13OggHhdlk3va4tol7KxHm3kt1QPusLZJpVh00OENqXV6Wuv3Xh1BMgv5XMy6oXfOUdrXcPi3GWWi8079XA== + +"@swc/core-linux-x64-musl@1.2.158": + version "1.2.158" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.2.158.tgz#72894115ea3af335be0ec530c00aea88ce4fa298" + integrity sha512-+TuNuzCBkDfoZKaaeqUrDdEANc3iVS8TYQgutHokSu6FCcNd9OGCm81SXknmYuDMtqYGs1LwVNMwCV7YOWEsiA== + +"@swc/core-win32-arm64-msvc@1.2.158": + version "1.2.158" + resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.2.158.tgz#5569523a0b2758d62e65319433e2228ace4cc64e" + integrity sha512-GXfOgEgqWdrol6dpseLXQL9RkRy6TSBMULtwpxwH5uf1jwAAZaMBsd+JemvhW0OjbIX0P9M19hdvQYtxuYxvrg== + +"@swc/core-win32-ia32-msvc@1.2.158": + version "1.2.158" + resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.2.158.tgz#18f0b6cb1b6343a127b9da24904b3d1b1283db82" + integrity sha512-Z/KIIgJrI2lXm+S/vRmYLcanOTvvxWq929ggjgY93m3zWrHjsWGVFoelbn2xLRUOtI/u0qna6DovLHhC4KcuBw== + +"@swc/core-win32-x64-msvc@1.2.158": + version "1.2.158" + resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.2.158.tgz#1f52b172617f62892afab6bfa8d624e99fa26964" + integrity sha512-h0jGYJmcNFhOinLT9vNE95DZfGtxROv9eDD+b5vMz03rvli5EUEUSkQ2MPDMuezHmL/P+cpKfVc/WGWWWXpfuQ== + +"@swc/core@^1.2.158": + version "1.2.158" + resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.2.158.tgz#aba1421d16fc9cdda8e09872776957dcdae70917" + integrity sha512-EBTuqLC2CRd4HN2CSbe+z0QoYdMCGZV2GqUvco0s2pqcNSssrWAZj6xozcJOQ5VeUsYRVdKro2muMAWdNe7qug== + optionalDependencies: + "@swc/core-android-arm-eabi" "1.2.158" + "@swc/core-android-arm64" "1.2.158" + "@swc/core-darwin-arm64" "1.2.158" + "@swc/core-darwin-x64" "1.2.158" + "@swc/core-freebsd-x64" "1.2.158" + "@swc/core-linux-arm-gnueabihf" "1.2.158" + "@swc/core-linux-arm64-gnu" "1.2.158" + "@swc/core-linux-arm64-musl" "1.2.158" + "@swc/core-linux-x64-gnu" "1.2.158" + "@swc/core-linux-x64-musl" "1.2.158" + "@swc/core-win32-arm64-msvc" "1.2.158" + "@swc/core-win32-ia32-msvc" "1.2.158" + "@swc/core-win32-x64-msvc" "1.2.158" + +"@swc/jest@^0.2.20": + version "0.2.20" + resolved "https://registry.yarnpkg.com/@swc/jest/-/jest-0.2.20.tgz#2bddb4348fb730296b86cdcd96748be131b11395" + integrity sha512-5qSUBYY1wyIMn7p0Vl9qqV4hMI69oJwZCIPUpBsTFWN2wlwn6RDugzdgCn+bLXVYh+Cxi8bJcZ1uumDgsoL+FA== + dependencies: + "@jest/create-cache-key-function" "^27.4.2" + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -8160,144 +8258,6 @@ es6-promisify@^6.0.0: resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-6.1.1.tgz#46837651b7b06bf6fff893d03f29393668d01621" integrity sha512-HBL8I3mIki5C1Cc9QjKUenHtnG0A5/xA8Q/AllRcfiwl2CZFXGK7ddBiCoRwAix4i2KxcQfjtIVcrVbB3vbmwg== -esbuild-android-64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.27.tgz#b868bbd9955a92309c69df628d8dd1945478b45c" - integrity sha512-LuEd4uPuj/16Y8j6kqy3Z2E9vNY9logfq8Tq+oTE2PZVuNs3M1kj5Qd4O95ee66yDGb3isaOCV7sOLDwtMfGaQ== - -esbuild-android-arm64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.27.tgz#e7d6430555e8e9c505fd87266bbc709f25f1825c" - integrity sha512-E8Ktwwa6vX8q7QeJmg8yepBYXaee50OdQS3BFtEHKrzbV45H4foMOeEE7uqdjGQZFBap5VAqo7pvjlyA92wznQ== - -esbuild-darwin-64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.27.tgz#4dc7484127564e89b4445c0a560a3cb50b3d68e1" - integrity sha512-czw/kXl/1ZdenPWfw9jDc5iuIYxqUxgQ/Q+hRd4/3udyGGVI31r29LCViN2bAJgGvQkqyLGVcG03PJPEXQ5i2g== - -esbuild-darwin-arm64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.27.tgz#469e59c665f84a8ed323166624c5e7b9b2d22ac1" - integrity sha512-BEsv2U2U4o672oV8+xpXNxN9bgqRCtddQC6WBh4YhXKDcSZcdNh7+6nS+DM2vu7qWIWNA4JbRG24LUUYXysimQ== - -esbuild-freebsd-64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.27.tgz#895df03bf5f87094a56c9a5815bf92e591903d70" - integrity sha512-7FeiFPGBo+ga+kOkDxtPmdPZdayrSzsV9pmfHxcyLKxu+3oTcajeZlOO1y9HW+t5aFZPiv7czOHM4KNd0tNwCA== - -esbuild-freebsd-arm64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.27.tgz#0b72a41a6b8655e9a8c5608f2ec1afdcf6958441" - integrity sha512-8CK3++foRZJluOWXpllG5zwAVlxtv36NpHfsbWS7TYlD8S+QruXltKlXToc/5ZNzBK++l6rvRKELu/puCLc7jA== - -esbuild-linux-32@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.27.tgz#43b8ba3803b0bbe7f051869c6a8bf6de1e95de28" - integrity sha512-qhNYIcT+EsYSBClZ5QhLzFzV5iVsP1YsITqblSaztr3+ZJUI+GoK8aXHyzKd7/CKKuK93cxEMJPpfi1dfsOfdw== - -esbuild-linux-64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.27.tgz#dc8072097327ecfadba1735562824ce8c05dd0bd" - integrity sha512-ESjck9+EsHoTaKWlFKJpPZRN26uiav5gkI16RuI8WBxUdLrrAlYuYSndxxKgEn1csd968BX/8yQZATYf/9+/qg== - -esbuild-linux-arm64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.27.tgz#c52b58cbe948426b1559910f521b0a3f396f10b8" - integrity sha512-no6Mi17eV2tHlJnqBHRLekpZ2/VYx+NfGxKcBE/2xOMYwctsanCaXxw4zapvNrGE9X38vefVXLz6YCF8b1EHiQ== - -esbuild-linux-arm@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.27.tgz#df869dbd67d4ee3a04b3c7273b6bd2b233e78a18" - integrity sha512-JnnmgUBdqLQO9hoNZQqNHFWlNpSX82vzB3rYuCJMhtkuaWQEmQz6Lec1UIxJdC38ifEghNTBsF9bbe8dFilnCw== - -esbuild-linux-mips64le@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.27.tgz#a2b646d9df368b01aa970a7b8968be6dd6b01d19" - integrity sha512-NolWP2uOvIJpbwpsDbwfeExZOY1bZNlWE/kVfkzLMsSgqeVcl5YMen/cedRe9mKnpfLli+i0uSp7N+fkKNU27A== - -esbuild-linux-ppc64le@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.27.tgz#9a21af766a0292578a3009c7408b8509cac7cefd" - integrity sha512-/7dTjDvXMdRKmsSxKXeWyonuGgblnYDn0MI1xDC7J1VQXny8k1qgNp6VmrlsawwnsymSUUiThhkJsI+rx0taNA== - -esbuild-linux-riscv64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.27.tgz#344a27f91568056a5903ad5841b447e00e78d740" - integrity sha512-D+aFiUzOJG13RhrSmZgrcFaF4UUHpqj7XSKrIiCXIj1dkIkFqdrmqMSOtSs78dOtObWiOrFCDDzB24UyeEiNGg== - -esbuild-linux-s390x@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.27.tgz#73a7309bd648a07ef58f069658f989a5096130db" - integrity sha512-CD/D4tj0U4UQjELkdNlZhQ8nDHU5rBn6NGp47Hiz0Y7/akAY5i0oGadhEIg0WCY/HYVXFb3CsSPPwaKcTOW3bg== - -esbuild-loader@2.18.0: - version "2.18.0" - resolved "https://registry.yarnpkg.com/esbuild-loader/-/esbuild-loader-2.18.0.tgz#7b9548578ab954574fd94655693d22aa5ec74120" - integrity sha512-AKqxM3bI+gvGPV8o6NAhR+cBxVO8+dh+O0OXBHIXXwuSGumckbPWHzZ17subjBGI2YEGyJ1STH7Haj8aCrwL/w== - dependencies: - esbuild "^0.14.6" - joycon "^3.0.1" - json5 "^2.2.0" - loader-utils "^2.0.0" - tapable "^2.2.0" - webpack-sources "^2.2.0" - -esbuild-netbsd-64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.27.tgz#482a587cdbd18a6c264a05136596927deb46c30a" - integrity sha512-h3mAld69SrO1VoaMpYl3a5FNdGRE/Nqc+E8VtHOag4tyBwhCQXxtvDDOAKOUQexBGca0IuR6UayQ4ntSX5ij1Q== - -esbuild-openbsd-64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.27.tgz#e99f8cdc63f1628747b63edd124d53cf7796468d" - integrity sha512-xwSje6qIZaDHXWoPpIgvL+7fC6WeubHHv18tusLYMwL+Z6bEa4Pbfs5IWDtQdHkArtfxEkIZz77944z8MgDxGw== - -esbuild-sunos-64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.27.tgz#8611d825bcb8239c78d57452e83253a71942f45c" - integrity sha512-/nBVpWIDjYiyMhuqIqbXXsxBc58cBVH9uztAOIfWShStxq9BNBik92oPQPJ57nzWXRNKQUEFWr4Q98utDWz7jg== - -esbuild-windows-32@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.27.tgz#c06374206d4d92dd31d4fda299b09f51a35e82f6" - integrity sha512-Q9/zEjhZJ4trtWhFWIZvS/7RUzzi8rvkoaS9oiizkHTTKd8UxFwn/Mm2OywsAfYymgUYm8+y2b+BKTNEFxUekw== - -esbuild-windows-64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.27.tgz#756631c1d301dfc0d1a887deed2459ce4079582f" - integrity sha512-b3y3vTSl5aEhWHK66ngtiS/c6byLf6y/ZBvODH1YkBM+MGtVL6jN38FdHUsZasCz9gFwYs/lJMVY9u7GL6wfYg== - -esbuild-windows-arm64@0.14.27: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.27.tgz#ad7e187193dcd18768b16065a950f4441d7173f4" - integrity sha512-I/reTxr6TFMcR5qbIkwRGvldMIaiBu2+MP0LlD7sOlNXrfqIl9uNjsuxFPGEG4IRomjfQ5q8WT+xlF/ySVkqKg== - -esbuild@^0.14.6: - version "0.14.27" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.27.tgz#41fe0f1b6b68b9f77cac025009bc54bb96e616f1" - integrity sha512-MZQt5SywZS3hA9fXnMhR22dv0oPGh6QtjJRIYbgL1AeqAoQZE+Qn5ppGYQAoHv/vq827flj4tIJ79Mrdiwk46Q== - optionalDependencies: - esbuild-android-64 "0.14.27" - esbuild-android-arm64 "0.14.27" - esbuild-darwin-64 "0.14.27" - esbuild-darwin-arm64 "0.14.27" - esbuild-freebsd-64 "0.14.27" - esbuild-freebsd-arm64 "0.14.27" - esbuild-linux-32 "0.14.27" - esbuild-linux-64 "0.14.27" - esbuild-linux-arm "0.14.27" - esbuild-linux-arm64 "0.14.27" - esbuild-linux-mips64le "0.14.27" - esbuild-linux-ppc64le "0.14.27" - esbuild-linux-riscv64 "0.14.27" - esbuild-linux-s390x "0.14.27" - esbuild-netbsd-64 "0.14.27" - esbuild-openbsd-64 "0.14.27" - esbuild-sunos-64 "0.14.27" - esbuild-windows-32 "0.14.27" - esbuild-windows-64 "0.14.27" - esbuild-windows-arm64 "0.14.27" - escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -11825,11 +11785,6 @@ joi@^17.6.0: "@sideway/formula" "^3.0.0" "@sideway/pinpoint" "^2.0.0" -joycon@^3.0.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" - integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== - jpeg-js@0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.2.tgz#8b345b1ae4abde64c2da2fe67ea216a114ac279d" @@ -17352,7 +17307,7 @@ sort-keys@^4.0.0: dependencies: is-plain-obj "^2.0.0" -source-list-map@^2.0.0, source-list-map@^2.0.1: +source-list-map@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== @@ -18040,6 +17995,13 @@ svgo@^2.5.0, svgo@^2.7.0: picocolors "^1.0.0" stable "^0.1.8" +swc-loader@^0.1.15: + version "0.1.15" + resolved "https://registry.yarnpkg.com/swc-loader/-/swc-loader-0.1.15.tgz#cb9c630ccfbb46dabc5aebc5560cced658e32992" + integrity sha512-cn1WPIeQJvXM4bbo3OwdEIapsQ4uUGOfyFj0h2+2+brT0k76DCGnZXDE2KmcqTd2JSQ+b61z2NPMib7eEwMYYw== + dependencies: + loader-utils "^2.0.0" + symbol-observable@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" @@ -19490,14 +19452,6 @@ webpack-sources@^1.4.3: source-list-map "^2.0.0" source-map "~0.6.1" -webpack-sources@^2.2.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.3.1.tgz#570de0af163949fe272233c2cefe1b56f74511fd" - integrity sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA== - dependencies: - source-list-map "^2.0.1" - source-map "^0.6.1" - webpack-sources@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" From 92fcc0ddc5011252b9333e91b7f6eb10a75a8161 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sat, 19 Mar 2022 21:21:11 +0800 Subject: [PATCH 5/5] docs: add SeaORM docs to showcase (#6943) * Add SeaORM docs to showcase * minor tweak Co-authored-by: Joshua Chen --- website/src/data/showcase/SeaORM.png | Bin 0 -> 37393 bytes website/src/data/users.tsx | 9 +++++++++ 2 files changed, 9 insertions(+) create mode 100644 website/src/data/showcase/SeaORM.png diff --git a/website/src/data/showcase/SeaORM.png b/website/src/data/showcase/SeaORM.png new file mode 100644 index 0000000000000000000000000000000000000000..4afe4e5bd2eecd680de367ee2a4972a59af3c9cd GIT binary patch literal 37393 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