Merge branch 'main' of github.com:facebook/docusaurus into lex111/filter-sidebar

This commit is contained in:
Alexey Pyltsyn 2022-03-20 13:24:48 +03:00
commit aa997da632
47 changed files with 979 additions and 836 deletions

View file

@ -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

View file

@ -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',
],
};

View file

@ -30,7 +30,7 @@ export default {
testPathIgnorePatterns: ignorePatterns,
coveragePathIgnorePatterns: ignorePatterns,
transform: {
'^.+\\.[jt]sx?$': 'babel-jest',
'^.+\\.[jt]sx?$': '@swc/jest',
},
errorOnDeprecated: true,
moduleNameMapper: {

View file

@ -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",

View file

@ -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';
}

View file

@ -368,31 +368,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<LayoutProps, 'children'> {}
export default function LayoutHead(props: Props): JSX.Element;
}
declare module '@theme/LayoutProviders' {
import type {ReactNode} from 'react';
@ -484,7 +470,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;
@ -924,28 +910,6 @@ declare module '@theme/Tag' {
export default function Tag(props: Props): JSX.Element;
}
declare module '@theme/prism-include-languages' {
import type * as PrismNamespace from 'prismjs';
export default function prismIncludeLanguages(
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;
}
declare module '@theme/TextHighlight' {
export interface Props {
readonly text?: string;
@ -954,3 +918,11 @@ declare module '@theme/TextHighlight' {
export default function TextHighlight(props: Props): JSX.Element;
}
declare module '@theme/prism-include-languages' {
import type * as PrismNamespace from 'prismjs';
export default function prismIncludeLanguages(
PrismObject: typeof PrismNamespace,
): void;
}

View file

@ -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 (
<Layout title={title} description={description}>
<header className="hero hero--primary">
<div className="container">
<h1 className="hero__title">{title}</h1>
<p className="hero__subtitle">{description}</p>
</div>
</header>
<main>{years.length > 0 && <YearsSection years={years} />}</main>
</Layout>
<>
<PageMetadata title={title} description={description} />
<Layout>
<header className="hero hero--primary">
<div className="container">
<h1 className="hero__title">{title}</h1>
<p className="hero__subtitle">{description}</p>
</div>
</header>
<main>{years.length > 0 && <YearsSection years={years} />}</main>
</Layout>
</>
);
}

View file

@ -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 (
<BlogLayout
title={title}
description={blogDescription}
wrapperClassName={ThemeClassNames.wrapper.blogPages}
pageClassName={ThemeClassNames.page.blogListPage}
searchMetadata={{
// assign unique search tag to exclude this page from search results!
tag: 'blog_posts_list',
}}
sidebar={sidebar}>
<>
<PageMetadata title={title} description={blogDescription} />
<SearchMetadata tag="blog_posts_list" />
</>
);
}
function BlogListPageContent(props: Props): JSX.Element {
const {metadata, items, sidebar} = props;
return (
<BlogLayout sidebar={sidebar}>
{items.map(({content: BlogPostContent}) => (
<BlogPostItem
key={BlogPostContent.metadata.permalink}
@ -48,3 +54,16 @@ export default function BlogListPage(props: Props): JSX.Element {
</BlogLayout>
);
}
export default function BlogListPage(props: Props): JSX.Element {
return (
<HtmlClassNameProvider
className={clsx(
ThemeClassNames.wrapper.blogPages,
ThemeClassNames.page.blogListPage,
)}>
<BlogListPageMetadata {...props} />
<BlogListPageContent {...props} />
</HtmlClassNameProvider>
);
}

View file

@ -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 (
<PageMetadata
title={title}
description={description}
keywords={keywords}
image={image}>
<meta property="og:type" content="article" />
<meta property="article:published_time" content={date} />
{/* TODO double check those article meta array syntaxes, see https://ogp.me/#array */}
{authors.some((author) => author.url) && (
<meta
property="article:author"
content={authors
.map((author) => author.url)
.filter(Boolean)
.join(',')}
/>
)}
{tags.length > 0 && (
<meta
property="article:tag"
content={tags.map((tag) => tag.label).join(',')}
/>
)}
</PageMetadata>
);
}
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 (
<BlogLayout
wrapperClassName={ThemeClassNames.wrapper.blogPages}
pageClassName={ThemeClassNames.page.blogPostPage}
sidebar={sidebar}
toc={
!hideTableOfContents &&
@ -52,35 +75,6 @@ export default function BlogPostPage(props: Props): JSX.Element {
/>
) : undefined
}>
<Seo
// TODO refactor needed: it's a bit annoying but Seo MUST be inside
// BlogLayout, otherwise default image (set by BlogLayout) would shadow
// the custom blog post image
title={title}
description={description}
keywords={keywords}
image={image}>
<meta property="og:type" content="article" />
<meta property="article:published_time" content={date} />
{/* TODO double check those article meta array syntaxes, see https://ogp.me/#array */}
{authors.some((author) => author.url) && (
<meta
property="article:author"
content={authors
.map((author) => author.url)
.filter(Boolean)
.join(',')}
/>
)}
{tags.length > 0 && (
<meta
property="article:tag"
content={tags.map((tag) => tag.label).join(',')}
/>
)}
</Seo>
<BlogPostItem
frontMatter={frontMatter}
assets={assets}
@ -95,3 +89,16 @@ export default function BlogPostPage(props: Props): JSX.Element {
</BlogLayout>
);
}
export default function BlogPostPage(props: Props): JSX.Element {
return (
<HtmlClassNameProvider
className={clsx(
ThemeClassNames.wrapper.blogPages,
ThemeClassNames.page.blogPostPage,
)}>
<BlogPostPageMetadata {...props} />
<BlogPostPageContent {...props} />
</HtmlClassNameProvider>
);
}

View file

@ -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 (
<BlogLayout
title={title}
wrapperClassName={ThemeClassNames.wrapper.blogPages}
pageClassName={ThemeClassNames.page.blogTagsListPage}
searchMetadata={{
// assign unique search tag to exclude this page from search results!
tag: 'blog_tags_list',
}}
sidebar={sidebar}>
<h1>{title}</h1>
<TagsListByLetter tags={Object.values(tags)} />
</BlogLayout>
<HtmlClassNameProvider
className={clsx(
ThemeClassNames.wrapper.blogPages,
ThemeClassNames.page.blogTagsListPage,
)}>
<PageMetadata title={title} />
<SearchMetadata tag="blog_tags_list" />
<BlogLayout sidebar={sidebar}>
<h1>{title}</h1>
<TagsListByLetter tags={Object.values(tags)} />
</BlogLayout>
</HtmlClassNameProvider>
);
}

View file

@ -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 (
<BlogLayout
title={title}
wrapperClassName={ThemeClassNames.wrapper.blogPages}
pageClassName={ThemeClassNames.page.blogTagPostListPage}
searchMetadata={{
// assign unique search tag to exclude this page from search results!
tag: 'blog_tags_posts',
}}
sidebar={sidebar}>
<header className="margin-bottom--xl">
<h1>{title}</h1>
<HtmlClassNameProvider
className={clsx(
ThemeClassNames.wrapper.blogPages,
ThemeClassNames.page.blogTagPostListPage,
)}>
<PageMetadata title={title} />
<SearchMetadata tag="blog_tags_posts" />
<BlogLayout sidebar={sidebar}>
<header className="margin-bottom--xl">
<h1>{title}</h1>
<Link href={allTagsPath}>
<Translate
id="theme.tags.tagsPageLink"
description="The label of the link targeting the tag list page">
View All Tags
</Translate>
</Link>
</header>
<Link href={allTagsPath}>
<Translate
id="theme.tags.tagsPageLink"
description="The label of the link targeting the tag list page">
View All Tags
</Translate>
</Link>
</header>
{items.map(({content: BlogPostContent}) => (
<BlogPostItem
key={BlogPostContent.metadata.permalink}
frontMatter={BlogPostContent.frontMatter}
assets={BlogPostContent.assets}
metadata={BlogPostContent.metadata}
truncated>
<BlogPostContent />
</BlogPostItem>
))}
<BlogListPaginator metadata={listMetadata} />
</BlogLayout>
{items.map(({content: BlogPostContent}) => (
<BlogPostItem
key={BlogPostContent.metadata.permalink}
frontMatter={BlogPostContent.frontMatter}
assets={BlogPostContent.assets}
metadata={BlogPostContent.metadata}
truncated>
<BlogPostContent />
</BlogPostItem>
))}
<BlogListPaginator metadata={listMetadata} />
</BlogLayout>
</HtmlClassNameProvider>
);
}

View file

@ -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 (
<PageMetadata
title={categoryGeneratedIndex.title}
description={categoryGeneratedIndex.description}
keywords={categoryGeneratedIndex.keywords}
// TODO `require` this?
image={useBaseUrl(categoryGeneratedIndex.image)}
/>
);
}
function DocCategoryGeneratedIndexPageContent({
categoryGeneratedIndex,
}: Props): JSX.Element {
const category = useCurrentSidebarCategory();
return (
<>
<Seo
<PageMetadata
title={categoryGeneratedIndex.title}
description={categoryGeneratedIndex.description}
keywords={categoryGeneratedIndex.keywords}
@ -57,3 +73,14 @@ export default function DocCategoryGeneratedIndexPage({
</>
);
}
export default function DocCategoryGeneratedIndexPage(
props: Props,
): JSX.Element {
return (
<>
<DocCategoryGeneratedIndexPageMetadata {...props} />
<DocCategoryGeneratedIndexPageContent {...props} />
</>
);
}

View file

@ -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 <PageMetadata {...{title, description, keywords, image}} />;
}
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 (
<HtmlClassNameProvider className={`docs-doc-id-${metadata.unversionedId}`}>
<Seo {...{title, description, keywords, image}} />
<div className="row">
<div className={clsx('col', !hideTableOfContents && styles.docItemCol)}>
<DocVersionBanner />
<div className={styles.docItemContainer}>
<article>
<DocBreadcrumbs />
<DocVersionBadge />
<div className="row">
<div className={clsx('col', !hideTableOfContents && styles.docItemCol)}>
<DocVersionBanner />
<div className={styles.docItemContainer}>
<article>
<DocBreadcrumbs />
<DocVersionBadge />
{canRenderTOC && (
<TOCCollapsible
toc={DocContent.toc}
minHeadingLevel={tocMinHeadingLevel}
maxHeadingLevel={tocMaxHeadingLevel}
className={clsx(
ThemeClassNames.docs.docTocMobile,
styles.tocMobile,
)}
/>
)}
{canRenderTOC && (
<TOCCollapsible
toc={DocContent.toc}
minHeadingLevel={tocMinHeadingLevel}
maxHeadingLevel={tocMaxHeadingLevel}
className={clsx(
ThemeClassNames.docs.docTocMobile,
styles.tocMobile,
)}
/>
)}
<div
className={clsx(ThemeClassNames.docs.docMarkdown, 'markdown')}>
{/*
<div className={clsx(ThemeClassNames.docs.docMarkdown, 'markdown')}>
{/*
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 && (
<header>
<Heading as="h1">{title}</Heading>
</header>
)}
<MDXContent>
<DocContent />
</MDXContent>
</div>
{shouldAddTitle && (
<header>
<Heading as="h1">{title}</Heading>
</header>
)}
<MDXContent>
<DocContent />
</MDXContent>
</div>
<DocItemFooter {...props} />
</article>
<DocItemFooter {...props} />
</article>
<DocPaginator previous={metadata.previous} next={metadata.next} />
</div>
<DocPaginator previous={metadata.previous} next={metadata.next} />
</div>
{renderTocDesktop && (
<div className="col col--3">
<TOC
toc={DocContent.toc}
minHeadingLevel={tocMinHeadingLevel}
maxHeadingLevel={tocMaxHeadingLevel}
className={ThemeClassNames.docs.docTocDesktop}
/>
</div>
)}
</div>
{renderTocDesktop && (
<div className="col col--3">
<TOC
toc={DocContent.toc}
minHeadingLevel={tocMinHeadingLevel}
maxHeadingLevel={tocMaxHeadingLevel}
className={ThemeClassNames.docs.docTocDesktop}
/>
</div>
)}
</div>
);
}
export default function DocItem(props: Props): JSX.Element {
const docHtmlClassName = `docs-doc-id-${props.content.metadata.unversionedId}`;
return (
<HtmlClassNameProvider className={docHtmlClassName}>
<DocItemMetadata {...props} />
<DocItemContent {...props} />
</HtmlClassNameProvider>
);
}

View file

@ -8,15 +8,6 @@
import React, {type ReactNode, useState, useCallback} from 'react';
import renderRoutes from '@docusaurus/renderRoutes';
import type {PropVersionMetadata} from '@docusaurus/plugin-content-docs';
import {
DocsFilterProvider,
HtmlClassNameProvider,
ThemeClassNames,
docVersionSearchTag,
DocsSidebarProvider,
useDocsSidebar,
DocsVersionProvider,
} from '@docusaurus/theme-common';
import Layout from '@theme/Layout';
import DocSidebar from '@theme/DocSidebar';
import NotFound from '@theme/NotFound';
@ -30,6 +21,17 @@ import {translate} from '@docusaurus/Translate';
import clsx from 'clsx';
import styles from './styles.module.css';
import {
DocsFilterProvider,
HtmlClassNameProvider,
ThemeClassNames,
docVersionSearchTag,
DocsSidebarProvider,
useDocsSidebar,
DocsVersionProvider,
} from '@docusaurus/theme-common';
import SearchMetadata from '@theme/SearchMetadata';
type DocPageContentProps = {
readonly currentDocRoute: DocumentRoute;
readonly versionMetadata: PropVersionMetadata;
@ -56,87 +58,89 @@ function DocPageContent({
}, [hiddenSidebar]);
return (
<Layout
wrapperClassName={ThemeClassNames.wrapper.docsPages}
pageClassName={ThemeClassNames.page.docsDocPage}
searchMetadata={{
version,
tag: docVersionSearchTag(pluginId, version),
}}>
<div className={styles.docPage}>
<BackToTopButton />
<>
<SearchMetadata
version={version}
tag={docVersionSearchTag(pluginId, version)}
/>
<Layout>
<div className={styles.docPage}>
<BackToTopButton />
{sidebar && (
<aside
{sidebar && (
<aside
className={clsx(
ThemeClassNames.docs.docSidebarContainer,
styles.docSidebarContainer,
hiddenSidebarContainer && styles.docSidebarContainerHidden,
)}
onTransitionEnd={(e) => {
if (
!e.currentTarget.classList.contains(
styles.docSidebarContainer!,
)
) {
return;
}
if (hiddenSidebarContainer) {
setHiddenSidebar(true);
}
}}>
<DocSidebar
key={
// Reset sidebar state on sidebar changes
// See https://github.com/facebook/docusaurus/issues/3414
sidebarName
}
sidebar={sidebar}
path={currentDocRoute.path}
onCollapse={toggleSidebar}
isHidden={hiddenSidebar}
/>
{hiddenSidebar && (
<div
className={styles.collapsedDocSidebar}
title={translate({
id: 'theme.docs.sidebar.expandButtonTitle',
message: 'Expand sidebar',
description:
'The ARIA label and title attribute for expand button of doc sidebar',
})}
aria-label={translate({
id: 'theme.docs.sidebar.expandButtonAriaLabel',
message: 'Expand sidebar',
description:
'The ARIA label and title attribute for expand button of doc sidebar',
})}
tabIndex={0}
role="button"
onKeyDown={toggleSidebar}
onClick={toggleSidebar}>
<IconArrow className={styles.expandSidebarButtonIcon} />
</div>
)}
</aside>
)}
<main
className={clsx(
ThemeClassNames.docs.docSidebarContainer,
styles.docSidebarContainer,
hiddenSidebarContainer && styles.docSidebarContainerHidden,
)}
onTransitionEnd={(e) => {
if (
!e.currentTarget.classList.contains(styles.docSidebarContainer!)
) {
return;
}
if (hiddenSidebarContainer) {
setHiddenSidebar(true);
}
}}>
<DocSidebar
key={
// Reset sidebar state on sidebar changes
// See https://github.com/facebook/docusaurus/issues/3414
sidebarName
}
sidebar={sidebar}
path={currentDocRoute.path}
onCollapse={toggleSidebar}
isHidden={hiddenSidebar}
/>
{hiddenSidebar && (
<div
className={styles.collapsedDocSidebar}
title={translate({
id: 'theme.docs.sidebar.expandButtonTitle',
message: 'Expand sidebar',
description:
'The ARIA label and title attribute for expand button of doc sidebar',
})}
aria-label={translate({
id: 'theme.docs.sidebar.expandButtonAriaLabel',
message: 'Expand sidebar',
description:
'The ARIA label and title attribute for expand button of doc sidebar',
})}
tabIndex={0}
role="button"
onKeyDown={toggleSidebar}
onClick={toggleSidebar}>
<IconArrow className={styles.expandSidebarButtonIcon} />
</div>
)}
</aside>
)}
<main
className={clsx(
styles.docMainContainer,
(hiddenSidebarContainer || !sidebar) &&
styles.docMainContainerEnhanced,
)}>
<div
className={clsx(
'container padding-top--md padding-bottom--lg',
styles.docItemWrapper,
hiddenSidebarContainer && styles.docItemWrapperEnhanced,
styles.docMainContainer,
(hiddenSidebarContainer || !sidebar) &&
styles.docMainContainerEnhanced,
)}>
{children}
</div>
</main>
</div>
</Layout>
<div
className={clsx(
'container padding-top--md padding-bottom--lg',
styles.docItemWrapper,
hiddenSidebarContainer && styles.docItemWrapperEnhanced,
)}>
{children}
</div>
</main>
</div>
</Layout>
</>
);
}
@ -161,7 +165,12 @@ export default function DocPage(props: Props): JSX.Element {
: null;
return (
<HtmlClassNameProvider className={versionMetadata.className}>
<HtmlClassNameProvider
className={clsx(
ThemeClassNames.wrapper.docsPages,
ThemeClassNames.page.docsDocPage,
versionMetadata.className,
)}>
<DocsVersionProvider version={versionMetadata}>
<DocsSidebarProvider sidebar={sidebar ?? null}>
<DocsFilterProvider>

View file

@ -10,10 +10,6 @@
--doc-sidebar-hidden-width: 30px;
}
:global(.docs-wrapper) {
display: flex;
}
.docPage,
.docMainContainer {
display: flex;

View file

@ -9,10 +9,17 @@ import React from 'react';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
import {ThemeClassNames, usePluralForm} from '@docusaurus/theme-common';
import {
PageMetadata,
HtmlClassNameProvider,
ThemeClassNames,
usePluralForm,
} from '@docusaurus/theme-common';
import type {PropTagDocListDoc} from '@docusaurus/plugin-content-docs';
import Translate, {translate} from '@docusaurus/Translate';
import type {Props} from '@theme/DocTagDocListPage';
import SearchMetadata from '@theme/SearchMetadata';
import clsx from 'clsx';
// Very simple pluralization: probably good enough for now
function useNDocsTaggedPlural() {
@ -55,35 +62,36 @@ export default function DocTagDocListPage({tag}: Props): JSX.Element {
);
return (
<Layout
title={title}
wrapperClassName={ThemeClassNames.wrapper.docsPages}
pageClassName={ThemeClassNames.page.docsTagDocListPage}
searchMetadata={{
// assign unique search tag to exclude this page from search results!
tag: 'doc_tag_doc_list',
}}>
<div className="container margin-vert--lg">
<div className="row">
<main className="col col--8 col--offset-2">
<header className="margin-bottom--xl">
<h1>{title}</h1>
<Link href={tag.allTagsPath}>
<Translate
id="theme.tags.tagsPageLink"
description="The label of the link targeting the tag list page">
View All Tags
</Translate>
</Link>
</header>
<section className="margin-vert--lg">
{tag.docs.map((doc) => (
<DocItem key={doc.id} doc={doc} />
))}
</section>
</main>
<HtmlClassNameProvider
className={clsx(
ThemeClassNames.wrapper.docsPages,
ThemeClassNames.page.docsTagDocListPage,
)}>
<PageMetadata title={title} />
<SearchMetadata tag="doc_tag_doc_list" />
<Layout>
<div className="container margin-vert--lg">
<div className="row">
<main className="col col--8 col--offset-2">
<header className="margin-bottom--xl">
<h1>{title}</h1>
<Link href={tag.allTagsPath}>
<Translate
id="theme.tags.tagsPageLink"
description="The label of the link targeting the tag list page">
View All Tags
</Translate>
</Link>
</header>
<section className="margin-vert--lg">
{tag.docs.map((doc) => (
<DocItem key={doc.id} doc={doc} />
))}
</section>
</main>
</div>
</div>
</div>
</Layout>
</Layout>
</HtmlClassNameProvider>
);
}

View file

@ -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 (
<Layout
title={title}
wrapperClassName={ThemeClassNames.wrapper.docsPages}
pageClassName={ThemeClassNames.page.docsTagsListPage}
searchMetadata={{
// assign unique search tag to exclude this page from search results!
tag: 'doc_tags_list',
}}>
<div className="container margin-vert--lg">
<div className="row">
<main className="col col--8 col--offset-2">
<h1>{title}</h1>
<TagsListByLetter tags={tags} />
</main>
<HtmlClassNameProvider
className={clsx(
ThemeClassNames.wrapper.docsPages,
ThemeClassNames.page.docsTagsListPage,
)}>
<PageMetadata title={title} />
<SearchMetadata tag="doc_tags_list" />
<Layout>
<div className="container margin-vert--lg">
<div className="row">
<main className="col col--8 col--offset-2">
<h1>{title}</h1>
<TagsListByLetter tags={tags} />
</main>
</div>
</div>
</div>
</Layout>
</Layout>
</HtmlClassNameProvider>
);
}

View file

@ -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 (
<LayoutProviders>
<LayoutHead {...props} />
<PageMetadata title={title} description={description} />
<SkipToContent />
@ -34,12 +44,7 @@ export default function Layout(props: Props): JSX.Element {
<Navbar />
<div
className={clsx(
ThemeClassNames.wrapper.main,
wrapperClassName,
pageClassName,
)}>
<div className={clsx(ThemeClassNames.wrapper.main, wrapperClassName)}>
<ErrorBoundary fallback={ErrorPageContent}>{children}</ErrorBoundary>
</div>

View file

@ -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 (
<Layout
title={title}
description={description}
permalink={permalink}
wrapperClassName={wrapperClassName ?? ThemeClassNames.wrapper.mdxPages}
pageClassName={ThemeClassNames.page.mdxPage}>
<main className="container container--fluid margin-vert--lg">
<div className={clsx('row', styles.mdxPageWrapper)}>
<div className={clsx('col', !hideTableOfContents && 'col--8')}>
<MDXContent>
<MDXPageContent />
</MDXContent>
</div>
{!hideTableOfContents && MDXPageContent.toc && (
<div className="col col--2">
<TOC
toc={MDXPageContent.toc}
minHeadingLevel={frontMatter.toc_min_heading_level}
maxHeadingLevel={frontMatter.toc_max_heading_level}
/>
<HtmlClassNameProvider
className={clsx(
wrapperClassName ?? ThemeClassNames.wrapper.mdxPages,
ThemeClassNames.page.mdxPage,
)}>
<PageMetadata title={title} description={description} />
<Layout>
<main className="container container--fluid margin-vert--lg">
<div className={clsx('row', styles.mdxPageWrapper)}>
<div className={clsx('col', !hideTableOfContents && 'col--8')}>
<MDXContent>
<MDXPageContent />
</MDXContent>
</div>
)}
</div>
</main>
</Layout>
{!hideTableOfContents && MDXPageContent.toc && (
<div className="col col--2">
<TOC
toc={MDXPageContent.toc}
minHeadingLevel={frontMatter.toc_min_heading_level}
maxHeadingLevel={frontMatter.toc_max_heading_level}
/>
</div>
)}
</div>
</main>
</Layout>
</HtmlClassNameProvider>
);
}

View file

@ -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 (
<Layout
title={translate({
id: 'theme.NotFound.title',
message: 'Page Not Found',
})}>
<main className="container margin-vert--xl">
<div className="row">
<div className="col col--6 col--offset-3">
<h1 className="hero__title">
<Translate
id="theme.NotFound.title"
description="The title of the 404 page">
Page Not Found
</Translate>
</h1>
<p>
<Translate
id="theme.NotFound.p1"
description="The first paragraph of the 404 page">
We could not find what you were looking for.
</Translate>
</p>
<p>
<Translate
id="theme.NotFound.p2"
description="The 2nd paragraph of the 404 page">
Please contact the owner of the site that linked you to the
original URL and let them know their link is broken.
</Translate>
</p>
<>
<PageMetadata
title={translate({
id: 'theme.NotFound.title',
message: 'Page Not Found',
})}
/>
<Layout>
<main className="container margin-vert--xl">
<div className="row">
<div className="col col--6 col--offset-3">
<h1 className="hero__title">
<Translate
id="theme.NotFound.title"
description="The title of the 404 page">
Page Not Found
</Translate>
</h1>
<p>
<Translate
id="theme.NotFound.p1"
description="The first paragraph of the 404 page">
We could not find what you were looking for.
</Translate>
</p>
<p>
<Translate
id="theme.NotFound.p2"
description="The 2nd paragraph of the 404 page">
Please contact the owner of the site that linked you to the
original URL and let them know their link is broken.
</Translate>
</p>
</div>
</div>
</div>
</main>
</Layout>
</main>
</Layout>
</>
);
}

View file

@ -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 (
<Head>
{title && <title>{pageTitle}</title>}
{title && <meta property="og:title" content={pageTitle} />}
{description && <meta name="description" content={description} />}
{description && <meta property="og:description" content={description} />}
{keywords && (
<meta
name="keywords"
content={
(Array.isArray(keywords) ? keywords.join(',') : keywords) as string
}
/>
)}
{pageImage && <meta property="og:image" content={pageImage} />}
{pageImage && <meta name="twitter:image" content={pageImage} />}
{children}
</Head>
);
}

View file

@ -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 (
<>
<Head>
<html lang={htmlLang} dir={htmlDir} />
{favicon && <link rel="icon" href={faviconUrl} />}
<title>{pageTitle}</title>
<meta property="og:title" content={pageTitle} />
<meta name="twitter:card" content="summary_large_image" />
{/* The keyboard focus class name need to be applied when SSR so links
are outlined when JS is disabled */}
<body className={keyboardFocusedClassName} />
</Head>
{/* image can override the default image */}
{defaultImage && <Seo image={defaultImage} />}
{image && <Seo image={image} />}
<Seo description={description} keywords={keywords} />
{defaultImage && <PageMetadata image={defaultImage} />}
<CanonicalUrlHeaders />
<AlternateLangHeaders />
<SearchMetadata
tag={DEFAULT_SEARCH_TAG}
locale={currentLocale}
{...searchMetadata}
/>
<SearchMetadata tag={DEFAULT_SEARCH_TAG} locale={currentLocale} />
<Head
// it's important to have an additional <Head> element here,

View file

@ -15,10 +15,10 @@ function TextHighlight({text, highlight}: Props): JSX.Element {
return <>{text}</>;
}
const highlightedText = text.replace(
const highlightedText = text?.replace(
new RegExp(highlight, 'gi'),
(match) => `<mark>${match}</mark>`,
);
) as string;
return (
<span

View file

@ -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,

View file

@ -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 (
<Head>
{title && <title>{pageTitle}</title>}
{title && <meta property="og:title" content={pageTitle} />}
{description && <meta name="description" content={description} />}
{description && <meta property="og:description" content={description} />}
{keywords && (
<meta
name="keywords"
content={
(Array.isArray(keywords) ? keywords.join(',') : keywords) as string
}
/>
)}
{pageImage && <meta property="og:image" content={pageImage} />}
{pageImage && <meta name="twitter:image" content={pageImage} />}
{children}
</Head>
);
}
const HtmlClassNameContext = React.createContext<string | undefined>(undefined);

View file

@ -47,7 +47,8 @@ export class ReactContextError extends Error {
super();
this.name = 'ReactContextError';
this.message = `Hook ${
this.stack?.split('\n')[1]?.match(/at (?<name>\w+)/)?.groups!.name
this.stack?.split('\n')[1]?.match(/at (?:\w+\.)?(?<name>\w+)/)?.groups!
.name
} is called outside the <${providerName}>. ${additionalInfo || ''}`;
}
}

View file

@ -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 (
<Layout wrapperClassName="search-page-wrapper">
<Layout>
<Head>
<title>{useTitleFormatter(getTitle())}</title>
{/*
@ -516,3 +517,11 @@ export default function SearchPage(): JSX.Element {
</Layout>
);
}
export default function SearchPage(): JSX.Element {
return (
<HtmlClassNameProvider className="search-page-wrapper">
<SearchPageContent />
</HtmlClassNameProvider>
);
}

View file

@ -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 {
<DocusaurusContextProvider>
<BrowserContextProvider>
<Root>
<SiteMetadataDefaults />
<SiteMetadata />
<BaseUrlIssueBanner />
<PendingNavigation routes={routes} delay={1000}>
{renderRoutes(routes)}

View file

@ -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 (
<Head defaultTitle={`${title}${tagline ? ` · ${tagline}` : ''}`}>
<html lang={htmlLang} dir={htmlDir} />
{favicon && <link rel="icon" href={faviconUrl} />}
</Head>
);
}

View file

@ -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]);
}
});
@ -75,21 +76,25 @@ 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.`,
);
}
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) => {
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;
@ -103,7 +108,7 @@ export default function ComponentCreator(
// Is there any way to put this RouteContextProvider upper in the tree?
return (
<RouteContextProvider value={routeContextModule}>
<Component {...loadedModules} {...props} />;
<Component {...loadedModules} {...props} />
</RouteContextProvider>
);
},

View file

@ -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={() => <ErrorDisplay error={error} tryAgain={tryAgain} />}>
<Layout title="Page Error">
<Head>
<title>Page Error</title>
</Head>
<Layout>
<ErrorDisplay error={error} tryAgain={tryAgain} />
</Layout>
</ErrorBoundary>

View file

@ -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 (
<>
<Head defaultTitle={`${defaultTitle}${tagline ? ` · ${tagline}` : ''}`}>
{title && <title>{`${title} · ${tagline}`}</title>}
{favicon && <link rel="icon" href={faviconUrl} />}
{description && <meta name="description" content={description} />}
{description && (
<meta property="og:description" content={description} />
)}
</Head>
{children}
</>
);
export default function Layout({children}: Props): JSX.Element {
return <>{children}</>;
}

View file

@ -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 (
<Layout title="Page Not Found">
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '50vh',
fontSize: '20px',
}}>
<h1>Oops, page not found </h1>
</div>
</Layout>
<>
<Head>
<title>Page Not Found</title>
</Head>
<Layout>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '50vh',
fontSize: '20px',
}}>
<h1>Oops, page not found </h1>
</div>
</Layout>
</>
);
}

View file

@ -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}</>;
}

View file

@ -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 <Head>
export default function SiteMetadata(): JSX.Element | null {
return null;
}

View file

@ -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",

View file

@ -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;
}

View file

@ -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 (
<Layout title="Hello">
<Layout title="Hello" description="Hello React Page">
<div
style={{
display: 'flex',
@ -53,8 +53,6 @@ function Hello() {
</Layout>
);
}
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.

View file

@ -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 (
<Layout title="Page" description="A React page demo">
<Head>
<meta property="og:image" content="image.png" />
</Head>
{/* ... */}
</Layout>
);
}
```
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 [`<Head>`](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 (
<Layout title="Page" description="A React page demo">
<Head>
<meta property="og:image" content="image.png" />
</Head>
{/* ... */}
</Layout>
);
}
```
:::tip
For convenience, the default theme `<Layout>` 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.

View file

@ -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',
}
},
}),
},
@ -320,7 +328,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
? {

View file

@ -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"

View file

@ -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);

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View file

@ -778,6 +778,15 @@ const Users: User[] = [
source: null,
tags: ['product'],
},
{
title: 'SeaORM - 🐚 An async & dynamic ORM for Rust',
description:
'SeaORM is a relational ORM to help you build web services in Rust with the familiarity of dynamic languages.',
preview: require('./showcase/SeaORM.png'),
website: 'https://www.sea-ql.org/SeaORM/',
source: 'https://github.com/SeaQL/seaql.github.io',
tags: ['opensource', 'versioning'],
},
{
title: 'Ionic',
description:

View file

@ -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 (
<>
<PageMetadata title={blogTitle} description={blogDescription} />
<SearchMetadata tag="blog_posts_list" />
</>
);
}
function ChangelogListContent(props: Props): JSX.Element {
const {metadata, items, sidebar} = props;
const {blogDescription, blogTitle} = metadata;
const {blogTitle} = metadata;
return (
<BlogLayout
title={blogTitle}
description={blogDescription}
wrapperClassName={ThemeClassNames.wrapper.blogPages}
pageClassName={ThemeClassNames.page.blogListPage}
searchMetadata={{
// assign unique search tag to exclude this page from search results!
tag: 'blog_posts_list',
}}
sidebar={sidebar}>
<BlogLayout sidebar={sidebar}>
<header className="margin-bottom--lg">
<h1 style={{fontSize: '3rem'}}>{blogTitle}</h1>
<p>
@ -88,3 +96,16 @@ export default function ChangelogList(props: Props): JSX.Element {
</BlogLayout>
);
}
export default function ChangelogList(props: Props): JSX.Element {
return (
<HtmlClassNameProvider
className={clsx(
ThemeClassNames.wrapper.blogPages,
ThemeClassNames.page.blogListPage,
)}>
<ChangelogListMetadata {...props} />
<ChangelogListContent {...props} />
</HtmlClassNameProvider>
);
}

View file

@ -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 (
<PageMetadata
title={title}
description={description}
keywords={keywords}
image={image}>
<meta property="og:type" content="article" />
<meta property="article:published_time" content={date} />
{authors.some((author) => author.url) && (
<meta
property="article:author"
content={authors
.map((author) => author.url)
.filter(Boolean)
.join(',')}
/>
)}
{tags.length > 0 && (
<meta
property="article:tag"
content={tags.map((tag) => tag.label).join(',')}
/>
)}
</PageMetadata>
);
}
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 (
<BlogLayout
wrapperClassName={ThemeClassNames.wrapper.blogPages}
pageClassName={ThemeClassNames.page.blogPostPage}
sidebar={sidebar}
toc={
!hideTableOfContents &&
BlogPostContents.toc &&
BlogPostContents.toc.length > 0 ? (
<TOC
toc={BlogPostContents.toc}
minHeadingLevel={tocMinHeadingLevel}
maxHeadingLevel={tocMaxHeadingLevel}
/>
) : undefined
}>
<Seo
title={title}
description={description}
keywords={keywords}
image={image}>
<meta property="og:type" content="article" />
<meta property="article:published_time" content={date} />
<>
<PageMetadata />
<BlogLayout
sidebar={sidebar}
toc={
!hideTableOfContents &&
BlogPostContents.toc &&
BlogPostContents.toc.length > 0 ? (
<TOC
toc={BlogPostContents.toc}
minHeadingLevel={tocMinHeadingLevel}
maxHeadingLevel={tocMaxHeadingLevel}
/>
) : undefined
}>
<Link to={listPageLink}> Back to index page</Link>
{authors.some((author) => author.url) && (
<meta
property="article:author"
content={authors
.map((author) => author.url)
.filter(Boolean)
.join(',')}
/>
<ChangelogItem
frontMatter={frontMatter}
assets={assets}
metadata={metadata}
isBlogPostPage>
<BlogPostContents />
</ChangelogItem>
{(nextItem || prevItem) && (
<BlogPostPaginator nextItem={nextItem} prevItem={prevItem} />
)}
{tags.length > 0 && (
<meta
property="article:tag"
content={tags.map((tag) => tag.label).join(',')}
/>
)}
</Seo>
<Link to={listPageLink}> Back to index page</Link>
<ChangelogItem
frontMatter={frontMatter}
assets={assets}
metadata={metadata}
isBlogPostPage>
<BlogPostContents />
</ChangelogItem>
{(nextItem || prevItem) && (
<BlogPostPaginator nextItem={nextItem} prevItem={prevItem} />
)}
</BlogLayout>
</BlogLayout>
</>
);
}
// 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 (
<HtmlClassNameProvider
className={clsx(
ThemeClassNames.wrapper.blogPages,
ThemeClassNames.page.blogPostPage,
)}>
<ChangelogPageMetadata {...props} />
<ChangelogPageContent {...props} />
</HtmlClassNameProvider>
);
}

258
yarn.lock
View file

@ -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"