refactor: better typing + remove unnecessary eslint-disable (#5335)

* Initial work

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Fix a few errors

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Restore default value

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Update docs

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Use custom route config

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Address a few suggestions

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Revert logo change

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Restore key

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Oops

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Remove use of any

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Remove eslint-disable

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Put type reference back

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>

* Remove

Signed-off-by: Josh-Cena <sidachen2003@gmail.com>
This commit is contained in:
Joshua Chen 2021-08-11 22:07:17 +08:00 committed by GitHub
parent 0c0c14120e
commit ee6ebc4877
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 127 additions and 122 deletions

View file

@ -618,7 +618,6 @@ function migrateVersionedSidebar(
const newSidebar = Object.entries(sidebar.entries).reduce(
(acc: SidebarEntries, val) => {
const key = `version-${sidebar.version}/${val[0]}`;
// eslint-disable-next-line prefer-destructuring
acc[key] = Object.entries(val[1]).map((value) => {
return {
type: 'category',

View file

@ -11,12 +11,16 @@ declare module '@generated/client-modules' {
}
declare module '@generated/docusaurus.config' {
const config: any;
import type {DocusaurusConfig} from '@docusaurus/types';
const config: DocusaurusConfig;
export default config;
}
declare module '@generated/site-metadata' {
const siteMetadata: any;
import type {DocusaurusSiteMetadata} from '@docusaurus/types';
const siteMetadata: DocusaurusSiteMetadata;
export default siteMetadata;
}
@ -28,9 +32,11 @@ declare module '@generated/registry' {
}
declare module '@generated/routes' {
import type {RouteConfig} from 'react-router-config';
type Route = {
readonly path: string;
readonly component: any;
readonly component: RouteConfig['component'];
readonly exact?: boolean;
};
const routes: Route[];
@ -79,16 +85,11 @@ declare module '@docusaurus/Head' {
}
declare module '@docusaurus/Link' {
import type {ReactNode} from 'react';
type RRLinkProps = Partial<import('react-router-dom').LinkProps>;
export type LinkProps = RRLinkProps & {
type NavLinkProps = Partial<import('react-router-dom').NavLinkProps>;
export type LinkProps = NavLinkProps & {
readonly isNavLink?: boolean;
readonly to?: string;
readonly href?: string;
readonly activeClassName?: string;
readonly children?: ReactNode;
readonly isActive?: (match: any, location: any) => boolean;
readonly autoAddBaseUrl?: boolean;
// escape hatch in case broken links check is annoying for a specific link
@ -173,7 +174,9 @@ declare module '@docusaurus/history' {
}
declare module '@docusaurus/useDocusaurusContext' {
export default function (): any;
import type {DocusaurusContext} from '@docusaurus/types';
export default function useDocusaurusContext(): DocusaurusContext;
}
declare module '@docusaurus/useBaseUrl' {

View file

@ -5,9 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
/* eslint-disable import/no-duplicates */
/* eslint-disable camelcase */
declare module '@theme/BlogSidebar' {
export type BlogSidebarItem = {title: string; permalink: string};
export type BlogSidebar = {
@ -28,6 +25,7 @@ declare module '@theme/BlogPostPage' {
import type {BlogSidebar} from '@theme/BlogSidebar';
export type FrontMatter = {
/* eslint-disable camelcase */
readonly title: string;
readonly author?: string;
readonly image?: string;
@ -40,12 +38,15 @@ declare module '@theme/BlogPostPage' {
readonly author_image_url?: string;
readonly authorImageURL?: string;
readonly hide_table_of_contents?: boolean;
/* eslint-enable camelcase */
};
export type FrontMatterAssets = {
/* eslint-disable camelcase */
readonly image?: string;
readonly author_image_url?: string;
readonly authorImageURL?: string;
/* eslint-enable camelcase */
};
export type Metadata = {

View file

@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
/* eslint-disable camelcase */
import {
JoiFrontMatter as Joi, // Custom instance for frontmatter
URISchema,
@ -15,6 +13,7 @@ import {
import {Tag} from './types';
export type BlogPostFrontMatter = {
/* eslint-disable camelcase */
id?: string;
title?: string;
description?: string;
@ -36,6 +35,7 @@ export type BlogPostFrontMatter = {
authorTitle?: string;
authorURL?: string;
authorImageURL?: string;
/* eslint-enable camelcase */
};
// NOTE: we don't add any default value on purpose here

View file

@ -85,7 +85,6 @@ function createVersionedSidebarFile({
}
// Tests depend on non-default export for mocking.
// eslint-disable-next-line import/prefer-default-export
export function cliDocsVersionCommand(
version: string | null | undefined,
siteDir: string,

View file

@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
/* eslint-disable camelcase */
import {
JoiFrontMatter as Joi, // Custom instance for frontmatter
URISchema,

View file

@ -130,9 +130,8 @@ function doProcessDocMetadata({
custom_edit_url: customEditURL,
// Strip number prefixes by default (01-MyFolder/01-MyDoc.md => MyFolder/MyDoc) by default,
// but allow to disable this behavior with frontmatterr
// eslint-disable-next-line camelcase
parse_number_prefixes = true,
// but allow to disable this behavior with frontmatter
parse_number_prefixes: parseNumberPrefixes = true,
} = frontMatter;
// ex: api/plugins/myDoc -> myDoc
@ -146,8 +145,7 @@ function doProcessDocMetadata({
// ex: myDoc -> .
const sourceDirName = path.dirname(source);
// eslint-disable-next-line camelcase
const {filename: unprefixedFileName, numberPrefix} = parse_number_prefixes
const {filename: unprefixedFileName, numberPrefix} = parseNumberPrefixes
? options.numberPrefixParser(sourceFileNameWithoutExtension)
: {filename: sourceFileNameWithoutExtension, numberPrefix: undefined};
@ -175,8 +173,7 @@ function doProcessDocMetadata({
return undefined;
}
// Eventually remove the number prefixes from intermediate directories
// eslint-disable-next-line camelcase
return parse_number_prefixes
return parseNumberPrefixes
? stripPathNumberPrefixes(sourceDirName, options.numberPrefixParser)
: sourceDirName;
}
@ -203,7 +200,7 @@ function doProcessDocMetadata({
baseID,
dirName: sourceDirName,
frontmatterSlug: frontMatter.slug,
stripDirNumberPrefixes: parse_number_prefixes,
stripDirNumberPrefixes: parseNumberPrefixes,
numberPrefixParser: options.numberPrefixParser,
});

View file

@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
/* eslint-disable camelcase */
declare module '@docusaurus/plugin-content-docs-types' {
type VersionBanner = import('./types').VersionBanner;
type GlobalDataVersion = import('./types').GlobalVersion;
@ -64,8 +62,10 @@ declare module '@theme/DocItem' {
readonly title: string;
readonly image?: string;
readonly keywords?: readonly string[];
/* eslint-disable camelcase */
readonly hide_title?: boolean;
readonly hide_table_of_contents?: boolean;
/* eslint-enable camelcase */
};
export type Metadata = {

View file

@ -9,7 +9,7 @@ import React from 'react';
import Head from '@docusaurus/Head';
import isInternalUrl from '@docusaurus/isInternalUrl';
import {useTitleFormatter} from '@docusaurus/theme-common';
import {useTitleFormatter, useThemeConfig} from '@docusaurus/theme-common';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import useBaseUrl from '@docusaurus/useBaseUrl';
@ -18,12 +18,9 @@ import Footer from '@theme/Footer';
import type {Props} from '@theme/Layout';
function Layout(props: Props): JSX.Element {
const {siteConfig = {}} = useDocusaurusContext();
const {
favicon,
themeConfig: {image: defaultImage, metadatas},
url: siteUrl,
} = siteConfig;
const {siteConfig} = useDocusaurusContext();
const {favicon, url: siteUrl} = siteConfig;
const {image: defaultImage, metadatas} = useThemeConfig();
const {
children,
title,

View file

@ -15,7 +15,7 @@ import useBaseUrl from '@docusaurus/useBaseUrl';
import {Props} from '@theme/DocItem';
function DocItem(props: Props): JSX.Element {
const {siteConfig = {}} = useDocusaurusContext();
const {siteConfig = {url: ''}} = useDocusaurusContext();
const {url: siteUrl} = siteConfig;
const {content: DocContent} = props;
const {metadata} = DocContent;

View file

@ -6,11 +6,20 @@
*/
import React from 'react';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import Link from '@docusaurus/Link';
import useBaseUrl from '@docusaurus/useBaseUrl';
import {useThemeConfig} from '@docusaurus/theme-common';
function FooterLink({to, href, label, ...props}) {
function FooterLink({
to,
href,
label,
...props
}: {
to?: string;
href?: string;
label?: string;
}) {
const toUrl = useBaseUrl(to);
return (
@ -31,12 +40,9 @@ function FooterLink({to, href, label, ...props}) {
}
function Footer(): JSX.Element {
const context = useDocusaurusContext();
const {siteConfig = {}} = context;
const {themeConfig = {}} = siteConfig;
const {footer} = themeConfig;
const {footer} = useThemeConfig();
const {links} = footer || {};
const {links = []} = footer || {};
return (
<footer className="container-fluid p-0 align-self-end">

View file

@ -10,6 +10,7 @@ import React from 'react';
import Head from '@docusaurus/Head';
import isInternalUrl from '@docusaurus/isInternalUrl';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import {useThemeConfig} from '@docusaurus/theme-common';
import useBaseUrl from '@docusaurus/useBaseUrl';
import Navbar from '@theme/Navbar';
@ -18,12 +19,7 @@ import type {Props} from '@theme/Layout';
function Layout(props: Props): JSX.Element {
const {siteConfig} = useDocusaurusContext();
const {
favicon,
title: siteTitle,
themeConfig: {image: defaultImage, metadatas},
url: siteUrl,
} = siteConfig;
const {favicon, title: siteTitle, url: siteUrl} = siteConfig;
const {
children,
title,
@ -33,6 +29,7 @@ function Layout(props: Props): JSX.Element {
keywords,
permalink,
} = props;
const {image: defaultImage, metadatas} = useThemeConfig();
const metaTitle = title ? `${title} | ${siteTitle}` : siteTitle;
const metaImage = image || defaultImage;

View file

@ -7,7 +7,7 @@
import React, {useState, useCallback} from 'react';
import Link from '@docusaurus/Link';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import {useThemeConfig} from '@docusaurus/theme-common';
import useBaseUrl from '@docusaurus/useBaseUrl';
import useLogo from '@theme/hooks/useLogo';
import {
@ -18,6 +18,16 @@ import {
NavItem as NavItemBase,
} from 'reactstrap';
interface NavItemProps {
activeBasePath?: string;
activeBaseRegex?: RegExp;
href?: string;
label?: string;
to?: string;
activeClassName?: string;
prependBaseUrlToHref?: boolean;
}
function NavItem({
activeBasePath,
activeBaseRegex,
@ -27,7 +37,7 @@ function NavItem({
activeClassName = 'nav-link text-info',
prependBaseUrlToHref,
...props
}) {
}: NavItemProps) {
const toUrl = useBaseUrl(to);
const activeBaseUrl = useBaseUrl(activeBasePath);
const normalizedHref = useBaseUrl(href, {forcePrependBaseUrl: true});
@ -63,12 +73,7 @@ function NavItem({
}
function Navbar(): JSX.Element {
const {
siteConfig: {
themeConfig: {navbar: {title = '', items: links = []} = {}},
},
isClient,
} = useDocusaurusContext();
const {navbar: {title = '', items: links = []} = {}} = useThemeConfig();
const [navbarShown, setNavbarShown] = useState(false);
const handleNavbarToggle = useCallback(() => {
@ -86,7 +91,6 @@ function Navbar(): JSX.Element {
<Link to={logoLink} {...logoLinkProps}>
{logoImageUrl != null && (
<img
key={isClient}
width="50"
height="50"
style={{

View file

@ -5,19 +5,19 @@
* LICENSE file in the root directory of this source tree.
*/
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import {useThemeConfig} from '@docusaurus/theme-common';
import useBaseUrl from '@docusaurus/useBaseUrl';
import isInternalUrl from '@docusaurus/isInternalUrl';
import type {LogoLinkProps, useLogoReturns} from '@theme/hooks/useLogo';
const useLogo = (): useLogoReturns => {
const {
siteConfig: {themeConfig: {navbar: {logo = {}} = {}} = {}} = {},
} = useDocusaurusContext();
const logoLink = useBaseUrl(logo.href || '/');
navbar: {logo},
} = useThemeConfig();
const logoLink = useBaseUrl(logo?.href || '/');
let logoLinkProps: LogoLinkProps = {};
if (logo.target) {
if (logo?.target) {
logoLinkProps = {target: logo.target};
} else if (!isInternalUrl(logoLink)) {
logoLinkProps = {
@ -25,13 +25,13 @@ const useLogo = (): useLogoReturns => {
target: '_blank',
};
}
const logoImageUrl = useBaseUrl(logo.src);
const logoImageUrl = useBaseUrl(logo?.src);
return {
logoLink,
logoLinkProps,
logoImageUrl,
logoAlt: logo.alt,
logoAlt: logo?.alt ?? '',
};
};

View file

@ -129,14 +129,10 @@ export default function CodeBlock({
highlightLines = rangeParser(highlightLinesRange).filter((n) => n > 0);
}
let language =
languageClassName &&
// Force Prism's language union type to `any` because it does not contain all available languages
// eslint-disable-next-line @typescript-eslint/no-explicit-any
((languageClassName.replace(/language-/, '') as Language) as any);
let language = languageClassName?.replace(/language-/, '') as Language;
if (!language && prism.defaultLanguage) {
language = prism.defaultLanguage;
language = prism.defaultLanguage as Language;
}
// only declaration OR directive highlight can be used for a block

View file

@ -57,7 +57,7 @@ function DocPageContent({
return (
<Layout
key={isClient}
key={`${isClient}`} // TODO seems suspicious
wrapperClassName={ThemeClassNames.wrapper.docPages}
pageClassName={ThemeClassNames.page.docPage}
searchMetadatas={{

View file

@ -16,6 +16,7 @@ import {
DEFAULT_SEARCH_TAG,
useTitleFormatter,
useAlternatePageUtils,
useThemeConfig,
} from '@docusaurus/theme-common';
import {useLocation} from '@docusaurus/router';
@ -83,12 +84,10 @@ function CanonicalUrlHeaders({permalink}: {permalink?: string}) {
export default function LayoutHead(props: Props): JSX.Element {
const {
siteConfig: {
favicon,
themeConfig: {metadatas, image: defaultImage},
},
siteConfig: {favicon},
i18n: {currentLocale, localeConfigs},
} = useDocusaurusContext();
const {metadatas, image: defaultImage} = useThemeConfig();
const {title, description, image, keywords, searchMetadatas} = props;
const faviconUrl = useBaseUrl(favicon);
const pageTitle = useTitleFormatter(title);

View file

@ -12,17 +12,16 @@ import Link from '@docusaurus/Link';
import ThemedImage from '@theme/ThemedImage';
import useBaseUrl from '@docusaurus/useBaseUrl';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import {useThemeConfig} from '@docusaurus/theme-common';
const Logo = (props: Props): JSX.Element => {
const {
siteConfig: {
title,
themeConfig: {
navbar: {title: navbarTitle, logo = {src: ''}},
},
},
siteConfig: {title},
isClient,
} = useDocusaurusContext();
const {
navbar: {title: navbarTitle, logo = {src: ''}},
} = useThemeConfig();
const {imageClassName, titleClassName, ...propsRest} = props;
const logoLink = useBaseUrl(logo.href || '/');
@ -38,7 +37,7 @@ const Logo = (props: Props): JSX.Element => {
{...(logo.target && {target: logo.target})}>
{logo.src && (
<ThemedImage
key={isClient}
key={`${isClient}`} // TODO seems suspicious
className={imageClassName}
sources={sources}
alt={logo.alt || navbarTitle || title}

View file

@ -11,6 +11,7 @@ import IconLanguage from '@theme/IconLanguage';
import type {Props} from '@theme/NavbarItem/LocaleDropdownNavbarItem';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import {useAlternatePageUtils} from '@docusaurus/theme-common';
import type {LinkLikeNavbarItemProps} from '@theme/NavbarItem';
export default function LocaleDropdownNavbarItem({
mobile,
@ -23,11 +24,12 @@ export default function LocaleDropdownNavbarItem({
} = useDocusaurusContext();
const alternatePageUtils = useAlternatePageUtils();
function getLocaleLabel(locale) {
function getLocaleLabel(locale: string) {
return localeConfigs[locale].label;
}
const localeItems = locales.map((locale) => {
const localeItems = locales.map(
(locale): LinkLikeNavbarItemProps => {
const to = `pathname://${alternatePageUtils.createUrl({
locale,
fullyQualified: false,
@ -41,7 +43,8 @@ export default function LocaleDropdownNavbarItem({
className: locale === currentLocale ? 'dropdown__link--active' : '',
style: {textTransform: 'capitalize'},
};
});
},
);
const items = [...dropdownItemsBefore, ...localeItems, ...dropdownItemsAfter];

View file

@ -12,12 +12,13 @@ import type * as PrismNamespace from 'prismjs';
const prismIncludeLanguages = (PrismObject: typeof PrismNamespace): void => {
if (ExecutionEnvironment.canUseDOM) {
const {
themeConfig: {prism: {additionalLanguages = []} = {}},
themeConfig: {prism = {}},
} = siteConfig;
const {additionalLanguages = []} = prism as {additionalLanguages: string[]};
window.Prism = PrismObject;
additionalLanguages.forEach((lang: string) => {
additionalLanguages.forEach((lang) => {
require(`prismjs/components/prism-${lang}`); // eslint-disable-line
});

View file

@ -120,7 +120,7 @@ function useCollapseAnimation({
el.style.willChange = 'height';
function startAnimation(): () => void {
function startAnimation() {
const animationFrame = requestAnimationFrame(() => {
// When collapsing
if (collapsed) {
@ -182,6 +182,7 @@ function CollapsibleBase({
disableSSRStyle,
}: CollapsibleBaseProps) {
// any because TS is a pain for HTML element refs, see https://twitter.com/sebastienlorber/status/1412784677795110914
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const collapsibleRef = useRef<any>(null);
useCollapseAnimation({collapsibleRef, collapsed, animation});
@ -191,7 +192,7 @@ function CollapsibleBase({
// @ts-expect-error: see https://twitter.com/sebastienlorber/status/1412784677795110914
ref={collapsibleRef}
style={disableSSRStyle ? undefined : getSSRStyle(collapsed)}
onTransitionEnd={(e) => {
onTransitionEnd={(e: React.TransitionEvent) => {
if (e.propertyName !== 'height') {
return;
}

View file

@ -7,8 +7,8 @@
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
export const useTitleFormatter = (title?: string | undefined): string => {
const {siteConfig = {}} = useDocusaurusContext();
const {title: siteTitle, titleDelimiter = '|'} = siteConfig;
const {siteConfig} = useDocusaurusContext();
const {title: siteTitle, titleDelimiter} = siteConfig;
return title && title.trim().length
? `${title.trim()} ${titleDelimiter} ${siteTitle}`
: siteTitle;

View file

@ -5,8 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
// ESLint doesn't understand types dependencies in d.ts
// eslint-disable-next-line import/no-extraneous-dependencies
import type {RuleSetRule, Configuration} from 'webpack';
import type {Command} from 'commander';
import type {ParsedUrlQueryInput} from 'querystring';
@ -346,7 +344,7 @@ export interface RouteConfig {
routes?: RouteConfig[];
exact?: boolean;
priority?: number;
[propName: string]: any;
[propName: string]: unknown;
}
// Aliases used for Webpack resolution (when using docusaurus swizzle)

View file

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
function support(feature) {
function support(feature: string) {
if (typeof document === 'undefined') {
return false;
}
@ -65,7 +65,7 @@ const supportedPrefetchStrategy = support('prefetch')
? linkPrefetchStrategy
: xhrPrefetchStrategy;
const preFetched = {};
const preFetched: Record<string, boolean> = {};
function prefetch(url: string): Promise<void> {
return new Promise((resolve) => {
@ -79,7 +79,6 @@ function prefetch(url: string): Promise<void> {
resolve();
preFetched[url] = true;
})
// eslint-disable-next-line @typescript-eslint/no-empty-function
.catch(() => {}); // 404s are logged to the console anyway.
});
}

View file

@ -18,9 +18,7 @@ import path from 'path';
import fs from 'fs-extra';
import routes from '@generated/routes';
import packageJson from '../../package.json';
// eslint-disable-next-line import/no-unresolved
import preload from './preload';
// eslint-disable-next-line import/no-unresolved
import App from './App';
import {
createStatefulLinksCollector,

View file

@ -128,7 +128,6 @@ export default async function loadRoutes(
modules = {},
routes: subroutes,
exact,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
priority,
...props
} = routeConfig;

View file

@ -85,7 +85,7 @@ describe('base webpack config', () => {
siteConfig: {},
baseUrl: '',
generatedFilesDir: '',
routesPaths: '',
routesPaths: [''],
i18n: {
currentLocale: 'en',
},

View file

@ -285,7 +285,6 @@ describe('extending PostCSS', () => {
expect(postCssLoader1.loader).toEqual('postcss-loader-1');
const pluginNames1 = postCssLoader1.options.postcssOptions.plugins.map(
// @ts-expect-error: relax type
(p: unknown) => p[0],
);
expect(pluginNames1).toHaveLength(4);
@ -301,7 +300,6 @@ describe('extending PostCSS', () => {
expect(postCssLoader2.loader).toEqual('postcss-loader-2');
const pluginNames2 = postCssLoader2.options.postcssOptions.plugins.map(
// @ts-expect-error: relax type
(p: unknown) => p[0],
);
expect(pluginNames2).toHaveLength(4);

View file

@ -117,7 +117,6 @@ class CleanWebpackPlugin {
apply(compiler: Compiler): void {
if (!compiler.options.output || !compiler.options.output.path) {
// eslint-disable-next-line no-console
console.warn(
'clean-webpack-plugin: options.output.path not defined. Plugin disabled...',
);
@ -163,7 +162,6 @@ class CleanWebpackPlugin {
*/
if (stats.hasErrors()) {
if (this.verbose) {
// eslint-disable-next-line no-console
console.warn('clean-webpack-plugin: pausing due to webpack errors');
}
@ -233,7 +231,6 @@ class CleanWebpackPlugin {
* https://github.com/webpack/webpack/issues/1904
* https://github.com/johnagan/clean-webpack-plugin/issues/11
*/
// eslint-disable-next-line no-console
console.warn(`clean-webpack-plugin: removed ${filename}`);
});
}

View file

@ -235,9 +235,25 @@ interface DocusaurusSiteMetadata {
readonly pluginVersions: Record<string, DocusaurusPluginVersionInformation>;
}
interface I18nLocaleConfig {
label: string;
direction: string;
}
interface I18n {
defaultLocale: string;
locales: [string, ...string[]];
currentLocale: string;
localeConfigs: Record<string, I18nLocaleConfig>;
}
interface DocusaurusContext {
siteConfig: DocusaurusConfig;
siteMetadata: DocusaurusSiteMetadata;
globalData: Record<string, unknown>;
i18n: I18n;
codeTranslations: Record<string, string>;
isClient: boolean;
}
```