diff --git a/.eslintrc.js b/.eslintrc.js index 993d1d1e19be..1b5801fe7d23 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -132,6 +132,12 @@ module.exports = { 'header/header': OFF, }, }, + { + files: ['*.d.ts'], + rules: { + 'import/no-duplicates': OFF, + }, + }, { files: ['*.js'], rules: { diff --git a/packages/docusaurus-module-type-aliases/package.json b/packages/docusaurus-module-type-aliases/package.json index f2747f6322b1..61d00806694b 100644 --- a/packages/docusaurus-module-type-aliases/package.json +++ b/packages/docusaurus-module-type-aliases/package.json @@ -14,7 +14,9 @@ "devDependencies": { "@types/react": "*", "@types/react-helmet": "*", - "@types/react-router-dom": "*" + "@types/react-loadable": "*", + "@types/react-router-dom": "*", + "@types/react-router-config": "*" }, "license": "MIT" } diff --git a/packages/docusaurus-module-type-aliases/src/index.d.ts b/packages/docusaurus-module-type-aliases/src/index.d.ts index 112b45660f26..e44eefb76eb4 100644 --- a/packages/docusaurus-module-type-aliases/src/index.d.ts +++ b/packages/docusaurus-module-type-aliases/src/index.d.ts @@ -69,16 +69,30 @@ declare module '@theme-original/*'; declare module '@docusaurus/*'; declare module '@docusaurus/Head' { - const helmet: typeof import('react-helmet').Helmet; - export default helmet; + import type {HelmetProps} from 'react-helmet'; + import type {ReactNode} from 'react'; + + export type HeadProps = HelmetProps & {children: ReactNode}; + + const Head: (props: HeadProps) => JSX.Element; + export default Head; } declare module '@docusaurus/Link' { + import type {ReactNode} from 'react'; + type RRLinkProps = Partial; - type LinkProps = RRLinkProps & { - to?: string; - activeClassName?: string; - isNavLink?: boolean; + export type LinkProps = RRLinkProps & { + 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 + readonly 'data-noBrokenLinkCheck'?: boolean; }; const Link: (props: LinkProps) => JSX.Element; export default Link; @@ -125,31 +139,33 @@ declare module '@docusaurus/Translate' { InterpolateValues, } from '@docusaurus/Interpolate'; - type TranslateProps = InterpolateProps & { + export type TranslateParam = Partial< + InterpolateProps + > & { + message: Str; id?: string; description?: string; + values?: InterpolateValues; }; - export default function Translate( - props: TranslateProps, - ): JSX.Element; export function translate( - param: { - message: Str; - id?: string; - description?: string; - }, + param: TranslateParam, values?: InterpolateValues, ): string; + + export type TranslateProps = InterpolateProps & { + id?: string; + description?: string; + }; + + export default function Translate( + props: TranslateProps, + ): JSX.Element; } declare module '@docusaurus/router' { - export const Redirect: (props: {to: string}) => import('react').Component; - export function matchPath( - pathname: string, - opts: {path?: string; exact?: boolean; strict?: boolean}, - ): boolean; - export function useLocation(): Location; + // eslint-disable-next-line import/no-extraneous-dependencies + export * from 'react-router-dom'; } declare module '@docusaurus/useDocusaurusContext' { @@ -157,9 +173,20 @@ declare module '@docusaurus/useDocusaurusContext' { } declare module '@docusaurus/useBaseUrl' { + export type BaseUrlOptions = { + forcePrependBaseUrl?: boolean; + absolute?: boolean; + }; + + export type BaseUrlUtils = { + withBaseUrl: (url: string, options?: BaseUrlOptions) => string; + }; + + export function useBaseUrlUtils(): BaseUrlUtils; + export default function useBaseUrl( relativePath: string | undefined, - opts?: {absolute?: true; forcePrependBaseUrl?: true}, + opts?: BaseUrlOptions, ): string; } @@ -173,6 +200,16 @@ declare module '@docusaurus/ExecutionEnvironment' { export default ExecutionEnvironment; } +declare module '@docusaurus/ComponentCreator' { + import type Loadable from 'react-loadable'; + + function ComponentCreator( + path: string, + hash: string, + ): ReturnType; + export default ComponentCreator; +} + declare module '@docusaurus/BrowserOnly' { export type Props = { children?: () => JSX.Element; @@ -187,6 +224,31 @@ declare module '@docusaurus/isInternalUrl' { export default function isInternalUrl(url?: string): boolean; } +declare module '@docusaurus/Noop' { + export default function (): null; +} + +declare module '@docusaurus/renderRoutes' { + // eslint-disable-next-line import/no-extraneous-dependencies + import {renderRoutes} from 'react-router-config'; + + export default renderRoutes; +} + +declare module '@docusaurus/useGlobalData' { + export function useAllPluginInstancesData( + pluginName: string, + ): Record; + + export function usePluginData( + pluginName: string, + pluginId?: string, + ): T; + + function useGlobalData(): Record; + export default useGlobalData; +} + declare module '*.module.css' { const classes: {readonly [key: string]: string}; export default classes; diff --git a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts index 9d398b5dcaad..87b6c07d07e5 100644 --- a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts +++ b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts @@ -100,7 +100,7 @@ declare module '@theme/DocPage' { readonly route: { readonly path: string; readonly component: () => JSX.Element; - readonly routes: readonly DocumentRoute[]; + readonly routes: DocumentRoute[]; }; }; diff --git a/packages/docusaurus-theme-classic/src/types.d.ts b/packages/docusaurus-theme-classic/src/types.d.ts index db097f87d76a..ebe36f5d51b6 100644 --- a/packages/docusaurus-theme-classic/src/types.d.ts +++ b/packages/docusaurus-theme-classic/src/types.d.ts @@ -259,7 +259,9 @@ declare module '@theme/Layout' { } declare module '@theme/LayoutHead' { - import type {Props} from '@theme/Layout'; + import type {Props as LayoutProps} from '@theme/Layout'; + + export type Props = Omit; const LayoutHead: (props: Props) => JSX.Element; export default LayoutHead; diff --git a/packages/docusaurus/src/client/exports/ComponentCreator.tsx b/packages/docusaurus/src/client/exports/ComponentCreator.tsx index 2cc048abca30..ad5f73c414b1 100644 --- a/packages/docusaurus/src/client/exports/ComponentCreator.tsx +++ b/packages/docusaurus/src/client/exports/ComponentCreator.tsx @@ -12,6 +12,8 @@ import routesChunkNames from '@generated/routesChunkNames'; import registry from '@generated/registry'; import flat from '../flat'; +type OptsLoader = Record; + function ComponentCreator( path: string, hash: string, @@ -28,7 +30,7 @@ function ComponentCreator( const chunkNames = routesChunkNames[chunkNamesKey]; const optsModules: string[] = []; const optsWebpack: string[] = []; - const optsLoader = {}; + const optsLoader: OptsLoader = {}; /* Prepare opts data that react-loadable needs https://github.com/jamiebuilds/react-loadable#declaring-which-modules-are-being-loaded diff --git a/packages/docusaurus/src/client/exports/Head.tsx b/packages/docusaurus/src/client/exports/Head.tsx index 42d39197c14c..ad4d49ef98b9 100644 --- a/packages/docusaurus/src/client/exports/Head.tsx +++ b/packages/docusaurus/src/client/exports/Head.tsx @@ -5,10 +5,9 @@ * LICENSE file in the root directory of this source tree. */ -import React, {ReactNode} from 'react'; -import {Helmet, HelmetProps} from 'react-helmet'; - -type HeadProps = HelmetProps & {children: ReactNode}; +import React from 'react'; +import {Helmet} from 'react-helmet'; +import type {HeadProps} from '@docusaurus/Head'; function Head(props: HeadProps): JSX.Element { return ; diff --git a/packages/docusaurus/src/client/exports/Interpolate.tsx b/packages/docusaurus/src/client/exports/Interpolate.tsx index bbb252c240db..50afd7743558 100644 --- a/packages/docusaurus/src/client/exports/Interpolate.tsx +++ b/packages/docusaurus/src/client/exports/Interpolate.tsx @@ -6,6 +6,11 @@ */ import React, {ReactNode} from 'react'; +import type { + InterpolateProps, + InterpolateValues, + ExtractInterpolatePlaceholders, +} from '@docusaurus/Interpolate'; /* Minimal implementation of a React interpolate component. @@ -16,16 +21,6 @@ More details here: https://github.com/facebook/docusaurus/pull/4295 const ValueRegexp = /{\w+}/g; const ValueFoundMarker = '{}'; // does not care much -// TODO use TS template literal feature to make values typesafe! -// (requires upgrading TS first) -// eslint-disable-next-line @typescript-eslint/no-unused-vars -type ExtractInterpolatePlaceholders = string; - -type InterpolateValues = Record< - ExtractInterpolatePlaceholders, - Value ->; - // TS function overload: if all the values are plain strings, then interpolate returns a simple string export function interpolate( text: Str, @@ -93,11 +88,6 @@ export function interpolate( } } -export type InterpolateProps = { - children: Str; - values?: InterpolateValues; -}; - export default function Interpolate({ children, values, diff --git a/packages/docusaurus/src/client/exports/Link.tsx b/packages/docusaurus/src/client/exports/Link.tsx index 9c1504507a68..feb49924d603 100644 --- a/packages/docusaurus/src/client/exports/Link.tsx +++ b/packages/docusaurus/src/client/exports/Link.tsx @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import React, {ReactNode, useEffect, useRef} from 'react'; +import React, {useEffect, useRef} from 'react'; import {NavLink, Link as RRLink} from 'react-router-dom'; import isInternalUrl from './isInternalUrl'; @@ -13,28 +13,15 @@ import ExecutionEnvironment from './ExecutionEnvironment'; import {useLinksCollector} from '../LinksCollector'; import {useBaseUrlUtils} from './useBaseUrl'; +import type {LinkProps} from '@docusaurus/Link'; +import type docusaurus from '../docusaurus'; + declare global { interface Window { - docusaurus: { - prefetch: (routePath: string) => boolean; - preload: (routePath: string) => boolean; - }; + docusaurus: typeof docusaurus; } } -interface Props { - readonly isNavLink?: boolean; - readonly to?: string; - readonly href?: string; - readonly activeClassName?: string; - readonly children?: ReactNode; - readonly isActive?: () => boolean; - readonly autoAddBaseUrl?: boolean; - - // escape hatch in case broken links check is annoying for a specific link - readonly 'data-noBrokenLinkCheck'?: boolean; -} - // TODO all this wouldn't be necessary if we used ReactRouter basename feature // We don't automatically add base urls to all links, // only the "safe" ones, starting with / (like /docs/introduction) @@ -51,7 +38,7 @@ function Link({ 'data-noBrokenLinkCheck': noBrokenLinkCheck, autoAddBaseUrl = true, ...props -}: Props): JSX.Element { +}: LinkProps): JSX.Element { const {withBaseUrl} = useBaseUrlUtils(); const linksCollector = useLinksCollector(); @@ -92,8 +79,8 @@ function Link({ const IOSupported = ExecutionEnvironment.canUseIntersectionObserver; - let io; - const handleIntersection = (el, cb) => { + let io: IntersectionObserver; + const handleIntersection = (el: HTMLAnchorElement, cb: () => void) => { io = new window.IntersectionObserver((entries) => { entries.forEach((entry) => { if (el === entry.target) { @@ -112,7 +99,7 @@ function Link({ io.observe(el); }; - const handleRef = (ref) => { + const handleRef = (ref: HTMLAnchorElement | null) => { if (IOSupported && ref && isInternal) { // If IO supported and element reference found, setup Observer functionality. handleIntersection(ref, () => { diff --git a/packages/docusaurus/src/client/exports/Translate.tsx b/packages/docusaurus/src/client/exports/Translate.tsx index a4253ed2fe6d..ec0a39a74831 100644 --- a/packages/docusaurus/src/client/exports/Translate.tsx +++ b/packages/docusaurus/src/client/exports/Translate.tsx @@ -8,9 +8,9 @@ import React from 'react'; import Interpolate, { interpolate, - InterpolateProps, InterpolateValues, } from '@docusaurus/Interpolate'; +import type {TranslateParam, TranslateProps} from '@docusaurus/Translate'; // Can't read it from context, due to exposing imperative API import codeTranslations from '@generated/codeTranslations'; @@ -25,12 +25,6 @@ function getLocalizedMessage({ return codeTranslations[id ?? message] ?? message; } -export type TranslateParam = { - message: Str; - id?: string; - description?: string; - values?: InterpolateValues; -}; // Imperative translation API is useful for some edge-cases: // - translating page titles (meta) // - translating string props (input placeholders, image alt, aria labels...) @@ -42,11 +36,6 @@ export function translate( return interpolate(localizedMessage, values); } -export type TranslateProps = InterpolateProps & { - id?: string; - description?: string; -}; - // Maybe we'll want to improve this component with additional features // Like toggling a translation mode that adds a little translation button near the text? export default function Translate({ diff --git a/packages/docusaurus/src/client/exports/useBaseUrl.ts b/packages/docusaurus/src/client/exports/useBaseUrl.ts index 627d140141a5..926e8979cbdc 100644 --- a/packages/docusaurus/src/client/exports/useBaseUrl.ts +++ b/packages/docusaurus/src/client/exports/useBaseUrl.ts @@ -7,11 +7,7 @@ import useDocusaurusContext from './useDocusaurusContext'; import {hasProtocol} from './isInternalUrl'; - -type BaseUrlOptions = Partial<{ - forcePrependBaseUrl: boolean; - absolute: boolean; -}>; +import type {BaseUrlOptions, BaseUrlUtils} from '@docusaurus/useBaseUrl'; function addBaseUrl( siteUrl: string | undefined, @@ -45,10 +41,6 @@ function addBaseUrl( return absolute ? siteUrl + basePath : basePath; } -export type BaseUrlUtils = { - withBaseUrl: (url: string, options?: BaseUrlOptions) => string; -}; - export function useBaseUrlUtils(): BaseUrlUtils { const { siteConfig: {baseUrl = '/', url: siteUrl} = {}, diff --git a/yarn.lock b/yarn.lock index 96d103e2df30..276f9b0f46f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3629,18 +3629,18 @@ dependencies: "@types/react" "*" -"@types/react-loadable@^5.5.3": - version "5.5.3" - resolved "https://registry.yarnpkg.com/@types/react-loadable/-/react-loadable-5.5.3.tgz#65d50a6f9f7ff62513010bd6a460ed60ba58ca7d" - integrity sha512-BRzQhbMo5CjfxFU2tmmBNh16QqKUwNiaX0vflCwIVPVG8g/pCOyJ3rOdSPo4m+TPS7C9q/TupaqYXXTMtFoyng== +"@types/react-loadable@*", "@types/react-loadable@^5.5.3": + version "5.5.4" + resolved "https://registry.yarnpkg.com/@types/react-loadable/-/react-loadable-5.5.4.tgz#9bfb31e1299e9a3b23ea6cae437fa8813afde544" + integrity sha512-otKcjNCfVUzdBMdwOqFITTmBruIXw6GeoZitTBvJ6BMrif8Utu2JLy42GWukNnYI7ewJdncUCooz5Y/1dBz4+w== dependencies: "@types/react" "*" "@types/webpack" "*" -"@types/react-router-config@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@types/react-router-config/-/react-router-config-5.0.1.tgz#54da8418190ee47484dee279975e2b8038fb8b5d" - integrity sha512-D4srFL4XP21GjWWnM7mL8j+Nrrw13pc2TYr57WTHJxU9YTxnrXL7p1iqGtwecgwhyeXJSm4WrGwq0SOgSALVbA== +"@types/react-router-config@*", "@types/react-router-config@^5.0.1": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@types/react-router-config/-/react-router-config-5.0.2.tgz#4d3b52e71ed363a1976a12321e67b09a99ad6d10" + integrity sha512-WOSetDV3YPxbkVJAdv/bqExJjmcdCi/vpCJh3NfQOy1X15vHMSiMioXIcGekXDJJYhqGUMDo9e337mh508foAA== dependencies: "@types/history" "*" "@types/react" "*"