refactor(theme-classic): apply import/no-named-export eslint rule (#6283)

This commit is contained in:
Sébastien Lorber 2022-01-07 14:44:55 +01:00 committed by GitHub
parent 1c7b0d1353
commit 3bc63b2b09
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 257 additions and 176 deletions

View file

@ -173,6 +173,16 @@ module.exports = {
],
},
overrides: [
{
files: [
'packages/docusaurus-theme-*/src/theme/**/*.js',
'packages/docusaurus-theme-*/src/theme/**/*.ts',
'packages/docusaurus-theme-*/src/theme/**/*.tsx',
],
rules: {
'import/no-named-export': ERROR,
},
},
{
files: [
'packages/create-docusaurus/templates/**/*.js',

View file

@ -149,23 +149,33 @@ declare module '@theme/DocSidebar' {
declare module '@theme/DocSidebarItem' {
import type {PropSidebarItem} from '@docusaurus/plugin-content-docs';
type DocSidebarPropsBase = {
export type DocSidebarPropsBase = {
readonly activePath: string;
readonly onItemClick?: (item: PropSidebarItem) => void;
readonly level: number;
readonly tabIndex?: number;
};
export interface Props extends DocSidebarPropsBase {
export interface Props {
readonly activePath: string;
readonly onItemClick?: (item: PropSidebarItem) => void;
readonly level: number;
readonly tabIndex?: number;
readonly item: PropSidebarItem;
}
const DocSidebarItem: (props: Props) => JSX.Element;
export default DocSidebarItem;
export type DocSidebarItemsProps = DocSidebarPropsBase & {
export default function DocSidebarItem(props: Props): JSX.Element;
}
declare module '@theme/DocSidebarItems' {
import type {Props as DocSidebarItemProps} from '@theme/DocSidebarItem';
import type {PropSidebarItem} from '@docusaurus/plugin-content-docs';
export type Props = Omit<DocSidebarItemProps, 'item'> & {
readonly items: readonly PropSidebarItem[];
};
export const DocSidebarItems: (props: DocSidebarItemsProps) => JSX.Element;
export default function DocSidebarItems(props: Props): JSX.Element;
}
declare module '@theme/DocVersionSuggestions' {
@ -196,12 +206,13 @@ declare module '@theme/Footer' {
declare module '@theme/Heading' {
import type {ComponentProps} from 'react';
export type HeadingType = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
export interface Props extends ComponentProps<HeadingType> {}
type HeadingType = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
const Heading: (Tag: HeadingType) => (props: Props) => JSX.Element;
export default Heading;
export const MainHeading: (props: Props) => JSX.Element;
export interface Props extends ComponentProps<HeadingType> {
as: HeadingType;
}
export default function Heading(props: Props): JSX.Element;
}
declare module '@theme/hooks/useHideableNavbar' {
@ -372,18 +383,9 @@ declare module '@theme/Navbar' {
}
declare module '@theme/NavbarItem/DefaultNavbarItem' {
import type {ReactNode} from 'react';
import type {LinkProps} from '@docusaurus/Link';
import type {Props as NavbarNavLinkProps} from '@theme/NavbarItem/NavbarNavLink';
export type NavLinkProps = LinkProps & {
readonly activeBasePath?: string;
readonly activeBaseRegex?: string;
readonly exact?: boolean;
readonly label?: ReactNode;
readonly prependBaseUrlToHref?: string;
};
export type DesktopOrMobileNavBarItemProps = NavLinkProps & {
export type DesktopOrMobileNavBarItemProps = NavbarNavLinkProps & {
readonly isDropdownItem?: boolean;
readonly className?: string;
readonly position?: 'left' | 'right';
@ -393,17 +395,30 @@ declare module '@theme/NavbarItem/DefaultNavbarItem' {
readonly mobile?: boolean;
}
export const NavLink: (props: NavLinkProps) => JSX.Element;
export default function DefaultNavbarItem(props: Props): JSX.Element;
}
const DefaultNavbarItem: (props: Props) => JSX.Element;
export default DefaultNavbarItem;
declare module '@theme/NavbarItem/NavbarNavLink' {
import type {ReactNode} from 'react';
import type {LinkProps} from '@docusaurus/Link';
export type Props = LinkProps & {
readonly activeBasePath?: string;
readonly activeBaseRegex?: string;
readonly exact?: boolean;
readonly label?: ReactNode;
readonly prependBaseUrlToHref?: string;
};
export default function NavbarNavLink(props: Props): JSX.Element;
}
declare module '@theme/NavbarItem/DropdownNavbarItem' {
import type {NavLinkProps} from '@theme/NavbarItem/DefaultNavbarItem';
import type {Props as NavbarNavLinkProps} from '@theme/NavbarItem/NavbarNavLink';
import type {LinkLikeNavbarItemProps} from '@theme/NavbarItem';
export type DesktopOrMobileNavBarItemProps = NavLinkProps & {
export type DesktopOrMobileNavBarItemProps = NavbarNavLinkProps & {
readonly position?: 'left' | 'right';
readonly items: readonly LinkLikeNavbarItemProps[];
readonly className?: string;
@ -526,6 +541,10 @@ declare module '@theme/NavbarItem' {
export default NavbarItem;
}
declare module '@theme/NavbarItem/utils' {
export function getInfimaActiveClassName(mobile?: boolean): string;
}
declare module '@theme/PaginatorNavLink' {
import type {ReactNode} from 'react';
import type {PropNavigationLink} from '@docusaurus/plugin-content-docs';

View file

@ -13,7 +13,7 @@ import DocPaginator from '@theme/DocPaginator';
import Seo from '@theme/Seo';
import DocVersionBanner from '@theme/DocVersionBanner';
import DocVersionBadge from '@theme/DocVersionBadge';
import {MainHeading} from '@theme/Heading';
import Heading from '@theme/Heading';
import useBaseUrl from '@docusaurus/useBaseUrl';
import styles from './styles.module.css';
@ -35,9 +35,9 @@ export default function DocCategoryGeneratedIndexPage({
<DocVersionBanner />
<DocVersionBadge />
<header>
<MainHeading className={styles.title}>
<Heading as="h1" className={styles.title}>
{categoryGeneratedIndex.title}
</MainHeading>
</Heading>
{categoryGeneratedIndex.description && (
<p>{categoryGeneratedIndex.description}</p>
)}

View file

@ -16,7 +16,7 @@ import type {Props} from '@theme/DocItem';
import DocItemFooter from '@theme/DocItemFooter';
import TOC from '@theme/TOC';
import TOCCollapsible from '@theme/TOCCollapsible';
import {MainHeading} from '@theme/Heading';
import Heading from '@theme/Heading';
import styles from './styles.module.css';
import {ThemeClassNames} from '@docusaurus/theme-common';
@ -80,7 +80,11 @@ export default function DocItem(props: Props): JSX.Element {
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 && <MainHeading>{title}</MainHeading>}
{shouldAddTitle && (
<header>
<Heading as="h1">{title}</Heading>
</header>
)}
<DocContent />
</div>

View file

@ -19,7 +19,7 @@ import useWindowSize from '@theme/hooks/useWindowSize';
import Logo from '@theme/Logo';
import IconArrow from '@theme/IconArrow';
import {translate} from '@docusaurus/Translate';
import {DocSidebarItems} from '@theme/DocSidebarItem';
import DocSidebarItems from '@theme/DocSidebarItems';
import type {Props} from '@theme/DocSidebar';
import styles from './styles.module.css';

View file

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import React, {useEffect, memo, useMemo} from 'react';
import React, {useEffect, useMemo} from 'react';
import clsx from 'clsx';
import {
isActiveSidebarItem,
@ -20,7 +20,8 @@ import isInternalUrl from '@docusaurus/isInternalUrl';
import {translate} from '@docusaurus/Translate';
import IconExternalLink from '@theme/IconExternalLink';
import type {Props, DocSidebarItemsProps} from '@theme/DocSidebarItem';
import DocSidebarItems from '@theme/DocSidebarItems';
import type {Props} from '@theme/DocSidebarItem';
import type {
PropSidebarItemCategory,
PropSidebarItemLink,
@ -29,23 +30,6 @@ import type {
import styles from './styles.module.css';
import useIsBrowser from '@docusaurus/useIsBrowser';
// Optimize sidebar at each "level"
// TODO this item should probably not receive the "activePath" props
// TODO this triggers whole sidebar re-renders on navigation
export const DocSidebarItems = memo(
({items, ...props}: DocSidebarItemsProps): JSX.Element => (
<>
{items.map((item, index) => (
<DocSidebarItem
key={index} // sidebar is static, the index does not change
item={item}
{...props}
/>
))}
</>
),
);
export default function DocSidebarItem({
item,
...props

View file

@ -0,0 +1,31 @@
/**
* 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, {memo} from 'react';
import DocSidebarItem from '@theme/DocSidebarItem';
import type {Props} from '@theme/DocSidebarItems';
// TODO this item should probably not receive the "activePath" props
// TODO this triggers whole sidebar re-renders on navigation
function DocSidebarItems({items, ...props}: Props): JSX.Element {
return (
<>
{items.map((item, index) => (
<DocSidebarItem
key={index} // sidebar is static, the index does not change
item={item}
{...props}
/>
))}
</>
);
}
// Optimize sidebar at each "level"
export default memo(DocSidebarItems);

View file

@ -0,0 +1,19 @@
/**
* 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.
*/
@media (min-width: 997px) {
.menuLinkText {
cursor: initial;
}
.menuLinkText:hover {
background: none;
}
.menuLinkText.hasHref {
cursor: pointer;
}
}

View file

@ -7,40 +7,24 @@
import React from 'react';
import clsx from 'clsx';
import type {HeadingType, Props} from '@theme/Heading';
import type {Props} from '@theme/Heading';
import {translate} from '@docusaurus/Translate';
import {useThemeConfig} from '@docusaurus/theme-common';
import './styles.css';
import styles from './styles.module.css';
type HeadingComponent = (props: Props) => JSX.Element;
// eslint-disable-next-line react/function-component-definition
export const MainHeading: HeadingComponent = ({...props}) => (
<header>
<h1
{...props}
id={undefined} // h1 headings do not need an id because they don't appear in the TOC
>
{props.children}
</h1>
</header>
);
const createAnchorHeading =
(Tag: HeadingType) =>
({id, ...props}: Props) => {
function AnchorHeading({as: As, id, ...props}: Props) {
const {
navbar: {hideOnScroll},
} = useThemeConfig();
if (!id) {
return <Tag {...props} />;
return <As {...props} />;
}
return (
<Tag
<As
{...props}
className={clsx('anchor', {
[styles.anchorWithHideOnScrollNavbar]: hideOnScroll,
@ -58,11 +42,20 @@ const createAnchorHeading =
})}>
&#8203;
</a>
</Tag>
</As>
);
};
}
const Heading = (headingType: HeadingType): ((props: Props) => JSX.Element) =>
headingType === 'h1' ? MainHeading : createAnchorHeading(headingType);
export default Heading;
export default function Heading({as, ...props}: Props) {
if (as === 'h1') {
return (
<h1
{...props}
id={undefined} // h1 headings do not need an id because they don't appear in the TOC
>
{props.children}
</h1>
);
}
return <AnchorHeading as={as} {...props} />;
}

View file

@ -67,12 +67,12 @@ const MDXComponents: MDXComponentsObject = {
</Details>
);
},
h1: Heading('h1'),
h2: Heading('h2'),
h3: Heading('h3'),
h4: Heading('h4'),
h5: Heading('h5'),
h6: Heading('h6'),
h1: (props) => <Heading as="h1" {...props} />,
h2: (props) => <Heading as="h2" {...props} />,
h3: (props) => <Heading as="h3" {...props} />,
h4: (props) => <Heading as="h4" {...props} />,
h5: (props) => <Heading as="h5" {...props} />,
h6: (props) => <Heading as="h6" {...props} />,
};
export default MDXComponents;

View file

@ -7,71 +7,15 @@
import React from 'react';
import clsx from 'clsx';
import Link from '@docusaurus/Link';
import useBaseUrl from '@docusaurus/useBaseUrl';
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
import type {
NavLinkProps,
DesktopOrMobileNavBarItemProps,
Props,
} from '@theme/NavbarItem/DefaultNavbarItem';
import IconExternalLink from '@theme/IconExternalLink';
import isInternalUrl from '@docusaurus/isInternalUrl';
import {isRegexpStringMatch} from '@docusaurus/theme-common';
import {getInfimaActiveClassName} from './index';
const dropdownLinkActiveClass = 'dropdown__link--active';
export function NavLink({
activeBasePath,
activeBaseRegex,
to,
href,
label,
activeClassName = '',
prependBaseUrlToHref,
...props
}: NavLinkProps): JSX.Element {
// TODO all this seems hacky
// {to: 'version'} should probably be forbidden, in favor of {to: '/version'}
const toUrl = useBaseUrl(to);
const activeBaseUrl = useBaseUrl(activeBasePath);
const normalizedHref = useBaseUrl(href, {forcePrependBaseUrl: true});
const isExternalLink = label && href && !isInternalUrl(href);
const isDropdownLink = activeClassName === dropdownLinkActiveClass;
return (
<Link
{...(href
? {
href: prependBaseUrlToHref ? normalizedHref : href,
}
: {
isNavLink: true,
activeClassName: !props.className?.includes(activeClassName)
? activeClassName
: '',
to: toUrl,
...(activeBasePath || activeBaseRegex
? {
isActive: (_match, location) =>
activeBaseRegex
? isRegexpStringMatch(activeBaseRegex, location.pathname)
: location.pathname.startsWith(activeBaseUrl),
}
: null),
})}
{...props}>
{isExternalLink ? (
<span>
{label}
<IconExternalLink {...(isDropdownLink && {width: 12, height: 12})} />
</span>
) : (
label
)}
</Link>
);
}
import {getInfimaActiveClassName} from '@theme/NavbarItem/utils';
function DefaultNavbarItemDesktop({
className,
@ -79,7 +23,7 @@ function DefaultNavbarItemDesktop({
...props
}: DesktopOrMobileNavBarItemProps) {
const element = (
<NavLink
<NavbarNavLink
className={clsx(
isDropdownItem ? 'dropdown__link' : 'navbar__item navbar__link',
className,
@ -102,7 +46,7 @@ function DefaultNavbarItemMobile({
}: DesktopOrMobileNavBarItemProps) {
return (
<li className="menu__list-item">
<NavLink className={clsx('menu__link', className)} {...props} />
<NavbarNavLink className={clsx('menu__link', className)} {...props} />
</li>
);
}

View file

@ -9,7 +9,7 @@ import React from 'react';
import DefaultNavbarItem from '@theme/NavbarItem/DefaultNavbarItem';
import {useLatestVersion, useActiveDocContext} from '@theme/hooks/useDocs';
import clsx from 'clsx';
import {getInfimaActiveClassName} from './index';
import {getInfimaActiveClassName} from '@theme/NavbarItem/utils';
import type {Props} from '@theme/NavbarItem/DocNavbarItem';
import {useDocsPreferredVersion, uniq} from '@docusaurus/theme-common';
import type {GlobalDataVersion} from '@docusaurus/plugin-content-docs';

View file

@ -9,9 +9,10 @@ import React from 'react';
import DefaultNavbarItem from '@theme/NavbarItem/DefaultNavbarItem';
import {useLatestVersion, useActiveDocContext} from '@theme/hooks/useDocs';
import clsx from 'clsx';
import {getInfimaActiveClassName} from './index';
import type {Props} from '@theme/NavbarItem/DocSidebarNavbarItem';
import {getInfimaActiveClassName} from '@theme/NavbarItem/utils';
import {useDocsPreferredVersion, uniq} from '@docusaurus/theme-common';
import type {Props} from '@theme/NavbarItem/DocSidebarNavbarItem';
import type {
GlobalDataVersion,
GlobalDataSidebar,

View file

@ -20,7 +20,7 @@ import type {
} from '@theme/NavbarItem/DropdownNavbarItem';
import type {LinkLikeNavbarItemProps} from '@theme/NavbarItem';
import {NavLink} from '@theme/NavbarItem/DefaultNavbarItem';
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink';
import NavbarItem from '@theme/NavbarItem';
const dropdownLinkActiveClass = 'dropdown__link--active';
@ -84,7 +84,7 @@ function DropdownNavbarItemDesktop({
'dropdown--right': position === 'right',
'dropdown--show': showDropdown,
})}>
<NavLink
<NavbarNavLink
href={props.to ? undefined : '#'}
className={clsx('navbar__link', className)}
{...props}
@ -96,7 +96,7 @@ function DropdownNavbarItemDesktop({
}
}}>
{props.children ?? props.label}
</NavLink>
</NavbarNavLink>
<ul className="dropdown__menu">
{items.map((childItemProps, i) => (
<NavbarItem
@ -146,7 +146,7 @@ function DropdownNavbarItemMobile({
className={clsx('menu__list-item', {
'menu__list-item--collapsed': collapsed,
})}>
<NavLink
<NavbarNavLink
role="button"
className={clsx('menu__link menu__link--sublist', className)}
{...props}
@ -155,7 +155,7 @@ function DropdownNavbarItemMobile({
toggleCollapsed();
}}>
{props.children ?? props.label}
</NavLink>
</NavbarNavLink>
<Collapsible lazy as="ul" className="menu__list" collapsed={collapsed}>
{items.map((childItemProps, i) => (
<NavbarItem

View file

@ -0,0 +1,68 @@
/**
* 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 Link from '@docusaurus/Link';
import useBaseUrl from '@docusaurus/useBaseUrl';
import type {Props} from '@theme/NavbarItem/NavbarNavLink';
import IconExternalLink from '@theme/IconExternalLink';
import isInternalUrl from '@docusaurus/isInternalUrl';
import {isRegexpStringMatch} from '@docusaurus/theme-common';
const dropdownLinkActiveClass = 'dropdown__link--active';
export default function NavbarNavLink({
activeBasePath,
activeBaseRegex,
to,
href,
label,
activeClassName = '',
prependBaseUrlToHref,
...props
}: Props): JSX.Element {
// TODO all this seems hacky
// {to: 'version'} should probably be forbidden, in favor of {to: '/version'}
const toUrl = useBaseUrl(to);
const activeBaseUrl = useBaseUrl(activeBasePath);
const normalizedHref = useBaseUrl(href, {forcePrependBaseUrl: true});
const isExternalLink = label && href && !isInternalUrl(href);
const isDropdownLink = activeClassName === dropdownLinkActiveClass;
return (
<Link
{...(href
? {
href: prependBaseUrlToHref ? normalizedHref : href,
}
: {
isNavLink: true,
activeClassName: !props.className?.includes(activeClassName)
? activeClassName
: '',
to: toUrl,
...(activeBasePath || activeBaseRegex
? {
isActive: (_match, location) =>
activeBaseRegex
? isRegexpStringMatch(activeBaseRegex, location.pathname)
: location.pathname.startsWith(activeBaseUrl),
}
: null),
})}
{...props}>
{isExternalLink ? (
<span>
{label}
<IconExternalLink {...(isDropdownLink && {width: 12, height: 12})} />
</span>
) : (
label
)}
</Link>
);
}

View file

@ -58,9 +58,6 @@ function getComponentType(
return type as NavbarItemComponentType;
}
export const getInfimaActiveClassName = (mobile?: boolean): string =>
mobile ? 'menu__link--active' : 'navbar__link--active';
export default function NavbarItem({type, ...props}: Props): JSX.Element {
const componentType = getComponentType(
type,

View file

@ -0,0 +1,10 @@
/**
* 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.
*/
// eslint-disable-next-line import/no-named-export
export const getInfimaActiveClassName = (mobile?: boolean): string =>
mobile ? 'menu__link--active' : 'navbar__link--active';

View file

@ -8,4 +8,5 @@
// Re-expose useDocs
// Ensure it's always statically available even if user is not using the docs plugin
// Problem reported for the blog-only mode: https://github.com/facebook/docusaurus/issues/3360
// eslint-disable-next-line import/no-named-export
export * from '@docusaurus/plugin-content-docs/lib/theme/hooks/useDocs';