feat(preset-classic, content-docs/client): JSDoc (#7148)

* refactor: add JSDoc for preset-classic, content-docs/client

* fix
This commit is contained in:
Joshua Chen 2022-04-11 09:36:30 +08:00 committed by GitHub
parent 25ba91fd4d
commit f4ab7c65ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 139 additions and 111 deletions

View file

@ -59,12 +59,10 @@ export function getActivePlugin(
export const getLatestVersion = (data: GlobalPluginData): GlobalVersion => export const getLatestVersion = (data: GlobalPluginData): GlobalVersion =>
data.versions.find((version) => version.isLast)!; data.versions.find((version) => version.isLast)!;
// Note: return undefined on doc-unrelated pages, export function getActiveVersion(
// because there's no version currently considered as active
export const getActiveVersion = (
data: GlobalPluginData, data: GlobalPluginData,
pathname: string, pathname: string,
): GlobalVersion | undefined => { ): GlobalVersion | undefined {
const lastVersion = getLatestVersion(data); const lastVersion = getLatestVersion(data);
// Last version is a route like /docs/*, // Last version is a route like /docs/*,
// we need to match it last or it would match /docs/version-1.0/* as well // we need to match it last or it would match /docs/version-1.0/* as well
@ -80,12 +78,12 @@ export const getActiveVersion = (
strict: false, strict: false,
}), }),
); );
}; }
export const getActiveDocContext = ( export function getActiveDocContext(
data: GlobalPluginData, data: GlobalPluginData,
pathname: string, pathname: string,
): ActiveDocContext => { ): ActiveDocContext {
const activeVersion = getActiveVersion(data, pathname); const activeVersion = getActiveVersion(data, pathname);
const activeDoc = activeVersion?.docs.find( const activeDoc = activeVersion?.docs.find(
(doc) => (doc) =>
@ -119,15 +117,15 @@ export const getActiveDocContext = (
activeDoc, activeDoc,
alternateDocVersions: alternateVersionDocs, alternateDocVersions: alternateVersionDocs,
}; };
}; }
export const getDocVersionSuggestions = ( export function getDocVersionSuggestions(
data: GlobalPluginData, data: GlobalPluginData,
pathname: string, pathname: string,
): DocVersionSuggestions => { ): DocVersionSuggestions {
const latestVersion = getLatestVersion(data); const latestVersion = getLatestVersion(data);
const activeDocContext = getActiveDocContext(data, pathname); const activeDocContext = getActiveDocContext(data, pathname);
const latestDocSuggestion: GlobalDoc | undefined = const latestDocSuggestion: GlobalDoc | undefined =
activeDocContext?.alternateDocVersions[latestVersion.name]; activeDocContext?.alternateDocVersions[latestVersion.name];
return {latestDocSuggestion, latestVersionSuggestion: latestVersion}; return {latestDocSuggestion, latestVersionSuggestion: latestVersion};
}; }

View file

@ -43,67 +43,61 @@ export const useDocsData = (pluginId: string | undefined): GlobalPluginData =>
}) as GlobalPluginData; }) as GlobalPluginData;
// TODO this feature should be provided by docusaurus core // TODO this feature should be provided by docusaurus core
export const useActivePlugin = ( export function useActivePlugin(
options: UseDataOptions = {}, options: UseDataOptions = {},
): ActivePlugin | undefined => { ): ActivePlugin | undefined {
const data = useAllDocsData(); const data = useAllDocsData();
const {pathname} = useLocation(); const {pathname} = useLocation();
return getActivePlugin(data, pathname, options); return getActivePlugin(data, pathname, options);
}; }
export const useActivePluginAndVersion = ( export function useActivePluginAndVersion(
options: UseDataOptions = {}, options: UseDataOptions = {},
): ):
| undefined | {activePlugin: ActivePlugin; activeVersion: GlobalVersion | undefined}
| {activePlugin: ActivePlugin; activeVersion: GlobalVersion | undefined} => { | undefined {
const activePlugin = useActivePlugin(options); const activePlugin = useActivePlugin(options);
const {pathname} = useLocation(); const {pathname} = useLocation();
if (activePlugin) { if (!activePlugin) {
return undefined;
}
const activeVersion = getActiveVersion(activePlugin.pluginData, pathname); const activeVersion = getActiveVersion(activePlugin.pluginData, pathname);
return { return {
activePlugin, activePlugin,
activeVersion, activeVersion,
}; };
} }
return undefined;
};
// versions are returned ordered (most recent first) export function useVersions(pluginId: string | undefined): GlobalVersion[] {
export const useVersions = (pluginId: string | undefined): GlobalVersion[] => {
const data = useDocsData(pluginId); const data = useDocsData(pluginId);
return data.versions; return data.versions;
}; }
export const useLatestVersion = ( export function useLatestVersion(pluginId: string | undefined): GlobalVersion {
pluginId: string | undefined,
): GlobalVersion => {
const data = useDocsData(pluginId); const data = useDocsData(pluginId);
return getLatestVersion(data); return getLatestVersion(data);
}; }
// Note: return undefined on doc-unrelated pages, export function useActiveVersion(
// because there's no version currently considered as active
export const useActiveVersion = (
pluginId: string | undefined, pluginId: string | undefined,
): GlobalVersion | undefined => { ): GlobalVersion | undefined {
const data = useDocsData(pluginId); const data = useDocsData(pluginId);
const {pathname} = useLocation(); const {pathname} = useLocation();
return getActiveVersion(data, pathname); return getActiveVersion(data, pathname);
}; }
export const useActiveDocContext = ( export function useActiveDocContext(
pluginId: string | undefined, pluginId: string | undefined,
): ActiveDocContext => { ): ActiveDocContext {
const data = useDocsData(pluginId); const data = useDocsData(pluginId);
const {pathname} = useLocation(); const {pathname} = useLocation();
return getActiveDocContext(data, pathname); return getActiveDocContext(data, pathname);
}; }
// Useful to say "hey, you are not on the latest docs version, please switch" export function useDocVersionSuggestions(
export const useDocVersionSuggestions = (
pluginId: string | undefined, pluginId: string | undefined,
): DocVersionSuggestions => { ): DocVersionSuggestions {
const data = useDocsData(pluginId); const data = useDocsData(pluginId);
const {pathname} = useLocation(); const {pathname} = useLocation();
return getDocVersionSuggestions(data, pathname); return getDocVersionSuggestions(data, pathname);
}; }

View file

@ -554,56 +554,18 @@ declare module '@theme/DocBreadcrumbs' {
declare module '@theme/DocPage' { declare module '@theme/DocPage' {
import type {PropVersionMetadata} from '@docusaurus/plugin-content-docs'; import type {PropVersionMetadata} from '@docusaurus/plugin-content-docs';
import type {DocumentRoute} from '@theme/DocItem'; import type {RouteConfigComponentProps} from 'react-router-config';
import type {Required} from 'utility-types';
export interface Props { export interface Props extends Required<RouteConfigComponentProps, 'route'> {
readonly location: {readonly pathname: string};
readonly versionMetadata: PropVersionMetadata; readonly versionMetadata: PropVersionMetadata;
readonly route: {
readonly path: string;
readonly component: () => JSX.Element;
readonly routes: DocumentRoute[];
};
} }
export default function DocPage(props: Props): JSX.Element; export default function DocPage(props: Props): JSX.Element;
} }
declare module '@theme/DocPage/Layout' { // TODO TS only supports reading `exports` in 4.7. We will need to merge the
import type {ReactNode} from 'react'; // type defs (and JSDoc) here with the implementation after that
export interface Props {
children: ReactNode;
}
export default function DocPageLayout(props: Props): JSX.Element;
}
declare module '@theme/DocPage/Layout/Aside' {
import type {Dispatch, SetStateAction} from 'react';
import type {PropSidebar} from '@docusaurus/plugin-content-docs';
export interface Props {
sidebar: PropSidebar;
hiddenSidebarContainer: boolean;
setHiddenSidebarContainer: Dispatch<SetStateAction<boolean>>;
}
export default function DocPageLayoutAside(props: Props): JSX.Element;
}
declare module '@theme/DocPage/Layout/Main' {
import type {ReactNode} from 'react';
export interface Props {
hiddenSidebarContainer: boolean;
children: ReactNode;
}
export default function DocPageLayoutMain(props: Props): JSX.Element;
}
// TODO until TS supports exports field... hope it's in 4.6
declare module '@docusaurus/plugin-content-docs/client' { declare module '@docusaurus/plugin-content-docs/client' {
import type {UseDataOptions} from '@docusaurus/types'; import type {UseDataOptions} from '@docusaurus/types';
@ -617,6 +579,11 @@ declare module '@docusaurus/plugin-content-docs/client' {
alternateDocVersions: {[versionName: string]: GlobalDoc}; alternateDocVersions: {[versionName: string]: GlobalDoc};
}; };
export type GlobalDoc = { export type GlobalDoc = {
/**
* For generated index pages, this is the `slug`, **not** `permalink`
* (without base URL). Because slugs have leading slashes but IDs don't,
* there won't be clashes.
*/
id: string; id: string;
path: string; path: string;
sidebar: string | undefined; sidebar: string | undefined;
@ -627,7 +594,8 @@ declare module '@docusaurus/plugin-content-docs/client' {
label: string; label: string;
isLast: boolean; isLast: boolean;
path: string; path: string;
mainDocId: string; // home doc (if docs homepage configured), or first doc /** The doc with `slug: /`, or first doc in first sidebar */
mainDocId: string;
docs: GlobalDoc[]; docs: GlobalDoc[];
sidebars?: {[sidebarId: string]: GlobalSidebar}; sidebars?: {[sidebarId: string]: GlobalSidebar};
}; };
@ -645,9 +613,9 @@ declare module '@docusaurus/plugin-content-docs/client' {
breadcrumbs: boolean; breadcrumbs: boolean;
}; };
export type DocVersionSuggestions = { export type DocVersionSuggestions = {
// suggest the latest version /** suggest the latest version */
latestVersionSuggestion: GlobalVersion; latestVersionSuggestion: GlobalVersion;
// suggest the same doc, in latest version (if exist) /** suggest the same doc, in latest version (if exist) */
latestDocSuggestion?: GlobalDoc; latestDocSuggestion?: GlobalDoc;
}; };
@ -661,12 +629,20 @@ declare module '@docusaurus/plugin-content-docs/client' {
) => ) =>
| {activePlugin: ActivePlugin; activeVersion: GlobalVersion | undefined} | {activePlugin: ActivePlugin; activeVersion: GlobalVersion | undefined}
| undefined; | undefined;
/** Versions are returned ordered (most recent first). */
export const useVersions: (pluginId?: string) => GlobalVersion[]; export const useVersions: (pluginId?: string) => GlobalVersion[];
export const useLatestVersion: (pluginId?: string) => GlobalVersion; export const useLatestVersion: (pluginId?: string) => GlobalVersion;
/**
* Returns `undefined` on doc-unrelated pages, because there's no version
* currently considered as active.
*/
export const useActiveVersion: ( export const useActiveVersion: (
pluginId?: string, pluginId?: string,
) => GlobalVersion | undefined; ) => GlobalVersion | undefined;
export const useActiveDocContext: (pluginId?: string) => ActiveDocContext; export const useActiveDocContext: (pluginId?: string) => ActiveDocContext;
/**
* Useful to say "hey, you are not on the latest docs version, please switch"
*/
export const useDocVersionSuggestions: ( export const useDocVersionSuggestions: (
pluginId?: string, pluginId?: string,
) => DocVersionSuggestions; ) => DocVersionSuggestions;

View file

@ -17,13 +17,30 @@ import type {UserThemeConfig as ClassicThemeConfig} from '@docusaurus/theme-comm
import type {UserThemeConfig as AlgoliaThemeConfig} from '@docusaurus/theme-search-algolia'; import type {UserThemeConfig as AlgoliaThemeConfig} from '@docusaurus/theme-search-algolia';
export type Options = { export type Options = {
/**
* Options for `@docusaurus/plugin-debug`. Use `false` to disable, or `true`
* to enable even in production.
*/
debug?: boolean; debug?: boolean;
/** Options for `@docusaurus/plugin-content-docs`. Use `false` to disable. */
docs?: false | DocsPluginOptions; docs?: false | DocsPluginOptions;
/** Options for `@docusaurus/plugin-content-blog`. Use `false` to disable. */
blog?: false | BlogPluginOptions; blog?: false | BlogPluginOptions;
/** Options for `@docusaurus/plugin-content-pages`. Use `false` to disable. */
pages?: false | PagesPluginOptions; pages?: false | PagesPluginOptions;
/** Options for `@docusaurus/plugin-sitemap`. Use `false` to disable. */
sitemap?: false | SitemapPluginOptions; sitemap?: false | SitemapPluginOptions;
/** Options for `@docusaurus/theme-classic`. */
theme?: ThemeOptions; theme?: ThemeOptions;
/**
* Options for `@docusaurus/plugin-google-analytics`. Only enabled when the
* key is present.
*/
googleAnalytics?: GAPluginOptions; googleAnalytics?: GAPluginOptions;
/**
* Options for `@docusaurus/plugin-google-gtag`. Only enabled when the key
* is present.
*/
gtag?: GtagPluginOptions; gtag?: GtagPluginOptions;
}; };

View file

@ -12,6 +12,14 @@
/// <reference types="@docusaurus/plugin-content-blog" /> /// <reference types="@docusaurus/plugin-content-blog" />
/// <reference types="@docusaurus/plugin-content-pages" /> /// <reference types="@docusaurus/plugin-content-pages" />
// This file, like all the other ambient declaration files for plugins, is
// needed for TS to understand our `@theme` alias. The export signatures are
// duplicated from the implementation, which is fine, since every module only
// default-exports a React component.
// TODO we'll eventually migrate to TS `paths` option. This is not easy due to
// our theme shadowing—we probably need the user to specify multiple theme paths
// in their tsconfig.
declare module '@docusaurus/theme-classic' { declare module '@docusaurus/theme-classic' {
export type Options = { export type Options = {
customCss?: string | string[]; customCss?: string | string[];
@ -188,6 +196,40 @@ declare module '@theme/DocItemFooter' {
export default function DocItemFooter(props: Props): JSX.Element; export default function DocItemFooter(props: Props): JSX.Element;
} }
declare module '@theme/DocPage/Layout' {
import type {ReactNode} from 'react';
export interface Props {
readonly children: ReactNode;
}
export default function DocPageLayout(props: Props): JSX.Element;
}
declare module '@theme/DocPage/Layout/Aside' {
import type {Dispatch, SetStateAction} from 'react';
import type {PropSidebar} from '@docusaurus/plugin-content-docs';
export interface Props {
readonly sidebar: PropSidebar;
readonly hiddenSidebarContainer: boolean;
readonly setHiddenSidebarContainer: Dispatch<SetStateAction<boolean>>;
}
export default function DocPageLayoutAside(props: Props): JSX.Element;
}
declare module '@theme/DocPage/Layout/Main' {
import type {ReactNode} from 'react';
export interface Props {
readonly hiddenSidebarContainer: boolean;
readonly children: ReactNode;
}
export default function DocPageLayoutMain(props: Props): JSX.Element;
}
declare module '@theme/DocPaginator' { declare module '@theme/DocPaginator' {
import type {PropNavigation} from '@docusaurus/plugin-content-docs'; import type {PropNavigation} from '@docusaurus/plugin-content-docs';
@ -242,7 +284,7 @@ declare module '@theme/DocSidebar/Desktop/Content' {
declare module '@theme/DocSidebar/Desktop/CollapseButton' { declare module '@theme/DocSidebar/Desktop/CollapseButton' {
export interface Props { export interface Props {
onClick: React.MouseEventHandler; readonly onClick: React.MouseEventHandler;
} }
export default function CollapseButton(props: Props): JSX.Element; export default function CollapseButton(props: Props): JSX.Element;
@ -269,7 +311,7 @@ declare module '@theme/DocSidebarItem/Link' {
import type {PropSidebarItemLink} from '@docusaurus/plugin-content-docs'; import type {PropSidebarItemLink} from '@docusaurus/plugin-content-docs';
export interface Props extends DocSidebarItemProps { export interface Props extends DocSidebarItemProps {
item: PropSidebarItemLink; readonly item: PropSidebarItemLink;
} }
export default function DocSidebarItemLink(props: Props): JSX.Element; export default function DocSidebarItemLink(props: Props): JSX.Element;
@ -280,7 +322,7 @@ declare module '@theme/DocSidebarItem/Html' {
import type {PropSidebarItemHtml} from '@docusaurus/plugin-content-docs'; import type {PropSidebarItemHtml} from '@docusaurus/plugin-content-docs';
export interface Props extends DocSidebarItemProps { export interface Props extends DocSidebarItemProps {
item: PropSidebarItemHtml; readonly item: PropSidebarItemHtml;
} }
export default function DocSidebarItemHtml(props: Props): JSX.Element; export default function DocSidebarItemHtml(props: Props): JSX.Element;
@ -291,7 +333,7 @@ declare module '@theme/DocSidebarItem/Category' {
import type {PropSidebarItemCategory} from '@docusaurus/plugin-content-docs'; import type {PropSidebarItemCategory} from '@docusaurus/plugin-content-docs';
export interface Props extends DocSidebarItemProps { export interface Props extends DocSidebarItemProps {
item: PropSidebarItemCategory; readonly item: PropSidebarItemCategory;
} }
export default function DocSidebarItemCategory(props: Props): JSX.Element; export default function DocSidebarItemCategory(props: Props): JSX.Element;
@ -350,7 +392,7 @@ declare module '@theme/Footer/Logo' {
import type {FooterLogo} from '@docusaurus/theme-common'; import type {FooterLogo} from '@docusaurus/theme-common';
export interface Props { export interface Props {
logo: FooterLogo; readonly logo: FooterLogo;
} }
export default function FooterLogo(props: Props): JSX.Element; export default function FooterLogo(props: Props): JSX.Element;
@ -358,7 +400,7 @@ declare module '@theme/Footer/Logo' {
declare module '@theme/Footer/Copyright' { declare module '@theme/Footer/Copyright' {
export interface Props { export interface Props {
copyright: string; readonly copyright: string;
} }
export default function FooterCopyright(props: Props): JSX.Element; export default function FooterCopyright(props: Props): JSX.Element;
@ -368,7 +410,7 @@ declare module '@theme/Footer/LinkItem' {
import type {FooterLinkItem} from '@docusaurus/theme-common'; import type {FooterLinkItem} from '@docusaurus/theme-common';
export interface Props { export interface Props {
item: FooterLinkItem; readonly item: FooterLinkItem;
} }
export default function FooterLinkItem(props: Props): JSX.Element; export default function FooterLinkItem(props: Props): JSX.Element;
@ -378,10 +420,10 @@ declare module '@theme/Footer/Layout' {
import type {ReactNode} from 'react'; import type {ReactNode} from 'react';
export interface Props { export interface Props {
style: 'light' | 'dark'; readonly style: 'light' | 'dark';
links: ReactNode; readonly links: ReactNode;
logo: ReactNode; readonly logo: ReactNode;
copyright: ReactNode; readonly copyright: ReactNode;
} }
export default function FooterLayout(props: Props): JSX.Element; export default function FooterLayout(props: Props): JSX.Element;
@ -391,7 +433,7 @@ declare module '@theme/Footer/Links' {
import type {Footer} from '@docusaurus/theme-common'; import type {Footer} from '@docusaurus/theme-common';
export interface Props { export interface Props {
links: Footer['links']; readonly links: Footer['links'];
} }
export default function FooterLinks(props: Props): JSX.Element; export default function FooterLinks(props: Props): JSX.Element;
@ -401,7 +443,7 @@ declare module '@theme/Footer/Links/MultiColumn' {
import type {MultiColumnFooter} from '@docusaurus/theme-common'; import type {MultiColumnFooter} from '@docusaurus/theme-common';
export interface Props { export interface Props {
columns: MultiColumnFooter['links']; readonly columns: MultiColumnFooter['links'];
} }
export default function FooterLinksMultiColumn(props: Props): JSX.Element; export default function FooterLinksMultiColumn(props: Props): JSX.Element;
@ -411,7 +453,7 @@ declare module '@theme/Footer/Links/Simple' {
import type {SimpleFooter} from '@docusaurus/theme-common'; import type {SimpleFooter} from '@docusaurus/theme-common';
export interface Props { export interface Props {
links: SimpleFooter['links']; readonly links: SimpleFooter['links'];
} }
export default function FooterLinksSimple(props: Props): JSX.Element; export default function FooterLinksSimple(props: Props): JSX.Element;
@ -423,7 +465,7 @@ declare module '@theme/Heading' {
type HeadingType = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; type HeadingType = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
export interface Props extends ComponentProps<HeadingType> { export interface Props extends ComponentProps<HeadingType> {
as: HeadingType; readonly as: HeadingType;
} }
export default function Heading(props: Props): JSX.Element; export default function Heading(props: Props): JSX.Element;
@ -625,9 +667,9 @@ declare module '@theme/Navbar/MobileSidebar/Layout' {
import type {ReactNode} from 'react'; import type {ReactNode} from 'react';
interface Props { interface Props {
header: ReactNode; readonly header: ReactNode;
primaryMenu: ReactNode; readonly primaryMenu: ReactNode;
secondaryMenu: ReactNode; readonly secondaryMenu: ReactNode;
} }
export default function NavbarMobileSidebarLayout(props: Props): JSX.Element; export default function NavbarMobileSidebarLayout(props: Props): JSX.Element;

View file

@ -34,7 +34,7 @@ function extractDocRouteMetadata(props: Props): null | {
versionMetadata, versionMetadata,
location, location,
} = props; } = props;
const currentDocRoute = docRoutes.find((docRoute) => const currentDocRoute = docRoutes!.find((docRoute) =>
matchPath(location.pathname, docRoute), matchPath(location.pathname, docRoute),
); );
if (!currentDocRoute) { if (!currentDocRoute) {

View file

@ -7,8 +7,9 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"strict": true, "strict": true,
// This is important. We run `yarn tsc` in website so we can catch issues // This is important. We run `yarn tsc` in website so we can catch issues
// with our declaration files (mostly name that are forgotten to be // with our declaration files (mostly names that are forgotten to be
// imported). Removing this would make things harder to catch. // imported, invalid semantics...). Because we don't have end-to-end type
// tests, removing this would make things much harder to catch.
"skipLibCheck": false, "skipLibCheck": false,
"types": ["@types/jest"] "types": ["@types/jest"]
}, },