refactor: replace non-prop interface with type; allow plugin lifecycles to have sync type (#7080)

* refactor: replace non-prop interface with type; allow plugin lifecycles to have sync type

* fix
This commit is contained in:
Joshua Chen 2022-03-31 19:16:07 +08:00 committed by GitHub
parent ce2b631455
commit 24c205a835
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 145 additions and 138 deletions

View file

@ -10,13 +10,13 @@ declare module '@mdx-js/mdx' {
import type {Processor} from 'unified'; import type {Processor} from 'unified';
import type {RemarkOrRehypePlugin} from '@docusaurus/mdx-loader'; import type {RemarkOrRehypePlugin} from '@docusaurus/mdx-loader';
export interface Options { export type Options = {
filepath?: string; filepath?: string;
skipExport?: boolean; skipExport?: boolean;
wrapExport?: string; wrapExport?: string;
remarkPlugins?: RemarkOrRehypePlugin[]; remarkPlugins?: RemarkOrRehypePlugin[];
rehypePlugins?: RemarkOrRehypePlugin[]; rehypePlugins?: RemarkOrRehypePlugin[];
} };
export function sync(content: string, options?: Options): string; export function sync(content: string, options?: Options): string;
export function createMdxAstCompiler(options?: Options): Processor; export function createMdxAstCompiler(options?: Options): Processor;

View file

@ -182,9 +182,9 @@ export default async function mdxLoader(
// Partial are not expected to have associated metadata files or front matter // Partial are not expected to have associated metadata files or front matter
const isMDXPartial = options.isMDXPartial?.(filePath); const isMDXPartial = options.isMDXPartial?.(filePath);
if (isMDXPartial && hasFrontMatter) { if (isMDXPartial && hasFrontMatter) {
const errorMessage = `Docusaurus MDX partial files should not contain FrontMatter. const errorMessage = `Docusaurus MDX partial files should not contain front matter.
Those partial files use the _ prefix as a convention by default, but this is configurable. Those partial files use the _ prefix as a convention by default, but this is configurable.
File at ${filePath} contains FrontMatter that will be ignored: File at ${filePath} contains front matter that will be ignored:
${JSON.stringify(frontMatter, null, 2)}`; ${JSON.stringify(frontMatter, null, 2)}`;
if (!options.isMDXPartialFrontMatterWarningDisabled) { if (!options.isMDXPartialFrontMatterWarningDisabled) {

View file

@ -9,9 +9,8 @@
import {parseMarkdownHeadingId, createSlugger} from '@docusaurus/utils'; import {parseMarkdownHeadingId, createSlugger} from '@docusaurus/utils';
import visit from 'unist-util-visit'; import visit from 'unist-util-visit';
import toString from 'mdast-util-to-string'; import mdastToString from 'mdast-util-to-string';
import type {Transformer} from 'unified'; import type {Transformer} from 'unified';
import type {Parent} from 'unist';
import type {Heading, Text} from 'mdast'; import type {Heading, Text} from 'mdast';
export default function plugin(): Transformer { export default function plugin(): Transformer {
@ -30,10 +29,8 @@ export default function plugin(): Transformer {
const headingTextNodes = headingNode.children.filter( const headingTextNodes = headingNode.children.filter(
({type}) => !['html', 'jsx'].includes(type), ({type}) => !['html', 'jsx'].includes(type),
); );
const heading = toString( const heading = mdastToString(
headingTextNodes.length > 0 headingTextNodes.length > 0 ? headingTextNodes : headingNode,
? ({children: headingTextNodes} as Parent)
: headingNode,
); );
// Support explicit heading IDs // Support explicit heading IDs

View file

@ -27,9 +27,9 @@ const isImport = (child: Node): child is Literal => child.type === 'import';
const hasImports = (index: number) => index > -1; const hasImports = (index: number) => index > -1;
const isExport = (child: Node): child is Literal => child.type === 'export'; const isExport = (child: Node): child is Literal => child.type === 'export';
interface PluginOptions { type PluginOptions = {
name?: string; name?: string;
} };
const isTarget = (child: Literal, name: string) => { const isTarget = (child: Literal, name: string) => {
let found = false; let found = false;

View file

@ -37,7 +37,7 @@ export type SidebarEntries = {
| Array<{[key: string]: unknown} | string>; | Array<{[key: string]: unknown} | string>;
}; };
export interface VersionTwoConfig { export type VersionTwoConfig = {
baseUrl: string; baseUrl: string;
favicon: string; favicon: string;
tagline?: string; tagline?: string;
@ -93,7 +93,7 @@ export interface VersionTwoConfig {
[key: string]: unknown; [key: string]: unknown;
} }
)[]; )[];
} };
export type VersionOneConfig = { export type VersionOneConfig = {
title?: string; title?: string;

View file

@ -140,8 +140,9 @@ declare module '@docusaurus/Head' {
declare module '@docusaurus/Link' { declare module '@docusaurus/Link' {
import type {CSSProperties, ComponentProps} from 'react'; import type {CSSProperties, ComponentProps} from 'react';
import type {NavLinkProps as RRNavLinkProps} from 'react-router-dom';
type NavLinkProps = Partial<import('react-router-dom').NavLinkProps>; type NavLinkProps = Partial<RRNavLinkProps>;
export type Props = NavLinkProps & export type Props = NavLinkProps &
ComponentProps<'a'> & { ComponentProps<'a'> & {
readonly className?: string; readonly className?: string;

View file

@ -314,7 +314,10 @@ describe('validateBlogPostFrontMatter tags', () => {
{tags: ['hello', {label: 'tagLabel', permalink: '/tagPermalink'}]}, {tags: ['hello', {label: 'tagLabel', permalink: '/tagPermalink'}]},
], ],
invalidFrontMatters: [ invalidFrontMatters: [
[{tags: ''}, '"tags" does not look like a valid FrontMatter Yaml array.'], [
{tags: ''},
'"tags" does not look like a valid front matter Yaml array.',
],
[{tags: ['']}, 'not allowed to be empty'], [{tags: ['']}, 'not allowed to be empty'],
], ],
// See https://github.com/facebook/docusaurus/issues/4642 // See https://github.com/facebook/docusaurus/issues/4642

View file

@ -102,7 +102,7 @@ export default async function pluginContentBlog(
) as string[]; ) as string[];
}, },
async getTranslationFiles() { getTranslationFiles() {
return getTranslationFiles(options); return getTranslationFiles(options);
}, },

View file

@ -10,7 +10,7 @@ declare module '@docusaurus/plugin-content-blog' {
import type {FrontMatterTag, Tag} from '@docusaurus/utils'; import type {FrontMatterTag, Tag} from '@docusaurus/utils';
import type {Overwrite} from 'utility-types'; import type {Overwrite} from 'utility-types';
export interface Assets { export type Assets = {
/** /**
* If `metadata.image` is a collocated image path, this entry will be the * If `metadata.image` is a collocated image path, this entry will be the
* bundler-generated image path. Otherwise, it's empty, and the image URL * bundler-generated image path. Otherwise, it's empty, and the image URL
@ -25,13 +25,9 @@ declare module '@docusaurus/plugin-content-blog' {
* should be accessed through `authors.imageURL`. * should be accessed through `authors.imageURL`.
*/ */
authorsImageUrls: (string | undefined)[]; authorsImageUrls: (string | undefined)[];
} };
/** export type Author = {
* Unknown keys are allowed, so that we can pass custom fields to authors,
* e.g., `twitter`.
*/
export interface Author extends Record<string, unknown> {
/** /**
* If `name` doesn't exist, an `imageURL` is expected. * If `name` doesn't exist, an `imageURL` is expected.
*/ */
@ -55,7 +51,12 @@ declare module '@docusaurus/plugin-content-blog' {
* to generate a fallback `mailto:` URL. * to generate a fallback `mailto:` URL.
*/ */
email?: string; email?: string;
} /**
* Unknown keys are allowed, so that we can pass custom fields to authors,
* e.g., `twitter`.
*/
[key: string]: unknown;
};
/** /**
* Everything is partial/unnormalized, because front matter is always * Everything is partial/unnormalized, because front matter is always
@ -443,8 +444,6 @@ declare module '@theme/BlogPostPage' {
>; >;
export type Content = { export type Content = {
// TODO remove this. `metadata.frontMatter` is preferred because it can be
// accessed in enhanced plugins
/** Same as `metadata.frontMatter` */ /** Same as `metadata.frontMatter` */
readonly frontMatter: FrontMatter; readonly frontMatter: FrontMatter;
/** /**

View file

@ -11,39 +11,41 @@ import type {Metadata as BlogPaginatedMetadata} from '@theme/BlogListPage';
export type BlogContentPaths = ContentPaths; export type BlogContentPaths = ContentPaths;
export interface BlogContent { export type BlogContent = {
blogSidebarTitle: string; blogSidebarTitle: string;
blogPosts: BlogPost[]; blogPosts: BlogPost[];
blogListPaginated: BlogPaginated[]; blogListPaginated: BlogPaginated[];
blogTags: BlogTags; blogTags: BlogTags;
blogTagsListPath: string | null; blogTagsListPath: string | null;
} };
export interface BlogTags { export type BlogTags = {
// TODO, the key is the tag slug/permalink // TODO, the key is the tag slug/permalink
// This is due to legacy frontmatter: tags: // This is due to legacy frontmatter: tags:
// [{label: "xyz", permalink: "/1"}, {label: "xyz", permalink: "/2"}] // [{label: "xyz", permalink: "/1"}, {label: "xyz", permalink: "/2"}]
// Soon we should forbid declaring permalink through frontmatter // Soon we should forbid declaring permalink through frontmatter
[tagKey: string]: BlogTag; [tagKey: string]: BlogTag;
} };
export interface BlogTag { export type BlogTag = {
name: string; name: string;
items: string[]; // blog post permalinks /** Blog post permalinks. */
items: string[];
permalink: string; permalink: string;
pages: BlogPaginated[]; pages: BlogPaginated[];
} };
export interface BlogPost { export type BlogPost = {
id: string; id: string;
metadata: BlogPostMetadata; metadata: BlogPostMetadata;
content: string; content: string;
} };
export interface BlogPaginated { export type BlogPaginated = {
metadata: BlogPaginatedMetadata; metadata: BlogPaginatedMetadata;
items: string[]; // blog post permalinks /** Blog post permalinks. */
} items: string[];
};
export type BlogBrokenMarkdownLink = BrokenMarkdownLink<BlogContentPaths>; export type BlogBrokenMarkdownLink = BrokenMarkdownLink<BlogContentPaths>;
export type BlogMarkdownLoaderOptions = { export type BlogMarkdownLoaderOptions = {

View file

@ -265,10 +265,13 @@ describe('validateDocFrontMatter tags', () => {
validFrontMatters: [{}, {tags: undefined}, {tags: ['tag1', 'tag2']}], validFrontMatters: [{}, {tags: undefined}, {tags: ['tag1', 'tag2']}],
convertibleFrontMatter: [[{tags: ['tag1', 42]}, {tags: ['tag1', '42']}]], convertibleFrontMatter: [[{tags: ['tag1', 42]}, {tags: ['tag1', '42']}]],
invalidFrontMatters: [ invalidFrontMatters: [
[{tags: 42}, '"tags" does not look like a valid FrontMatter Yaml array.'], [
{tags: 42},
'"tags" does not look like a valid front matter Yaml array.',
],
[ [
{tags: 'tag1, tag2'}, {tags: 'tag1, tag2'},
'"tags" does not look like a valid FrontMatter Yaml array.', '"tags" does not look like a valid front matter Yaml array.',
], ],
[{tags: [{}]}, '"tags[0]" does not look like a valid tag'], [{tags: [{}]}, '"tags[0]" does not look like a valid tag'],
[{tags: [true]}, '"tags[0]" does not look like a valid tag'], [{tags: [true]}, '"tags[0]" does not look like a valid tag'],

View file

@ -106,7 +106,7 @@ export default async function pluginContentDocs(
}); });
}, },
async getTranslationFiles({content}) { getTranslationFiles({content}) {
return getLoadedContentTranslationFiles(content); return getLoadedContentTranslationFiles(content);
}, },

View file

@ -10,9 +10,9 @@ declare module '@docusaurus/plugin-content-docs' {
import type {ContentPaths, Tag, FrontMatterTag} from '@docusaurus/utils'; import type {ContentPaths, Tag, FrontMatterTag} from '@docusaurus/utils';
import type {Required} from 'utility-types'; import type {Required} from 'utility-types';
export interface Assets { export type Assets = {
image?: string; image?: string;
} };
/** /**
* Custom callback for parsing number prefixes from file/folder names. * Custom callback for parsing number prefixes from file/folder names.

View file

@ -254,18 +254,15 @@ export type SidebarItemsGenerator = (
generatorArgs: SidebarItemsGeneratorArgs, generatorArgs: SidebarItemsGeneratorArgs,
) => Promise<NormalizedSidebar>; ) => Promise<NormalizedSidebar>;
// Also inject the default generator to conveniently wrap/enhance/sort the export type SidebarItemsGeneratorOption = (
// default sidebar gen logic generatorArgs: {
// see https://github.com/facebook/docusaurus/issues/4640#issuecomment-822292320
export type SidebarItemsGeneratorOptionArgs = {
/** /**
* Useful to re-use/enhance the default sidebar generation logic from * Useful to re-use/enhance the default sidebar generation logic from
* Docusaurus. * Docusaurus.
* @see https://github.com/facebook/docusaurus/issues/4640#issuecomment-822292320
*/ */
defaultSidebarItemsGenerator: SidebarItemsGenerator; defaultSidebarItemsGenerator: SidebarItemsGenerator;
} & SidebarItemsGeneratorArgs; } & SidebarItemsGeneratorArgs,
export type SidebarItemsGeneratorOption = (
generatorArgs: SidebarItemsGeneratorOptionArgs,
) => Promise<NormalizedSidebarItem[]>; ) => Promise<NormalizedSidebarItem[]>;
export type SidebarProcessorParams = { export type SidebarProcessorParams = {

View file

@ -33,12 +33,12 @@ declare module '@endiliey/react-ideal-image' {
| 'noicon' | 'noicon'
| 'offline'; | 'offline';
export interface SrcType { export type SrcType = {
width: number; width: number;
src?: string; src?: string;
size?: number; size?: number;
format?: 'webp' | 'jpeg' | 'png' | 'gif'; format?: 'webp' | 'jpeg' | 'png' | 'gif';
} };
type ThemeKey = 'placeholder' | 'img' | 'icon' | 'noscript'; type ThemeKey = 'placeholder' | 'img' | 'icon' | 'noscript';

View file

@ -11,9 +11,9 @@ import type {Node, Parent} from 'unist';
import visit from 'unist-util-visit'; import visit from 'unist-util-visit';
import npmToYarn from 'npm-to-yarn'; import npmToYarn from 'npm-to-yarn';
interface PluginOptions { type PluginOptions = {
sync?: boolean; sync?: boolean;
} };
// E.g. global install: 'npm i' -> 'yarn' // E.g. global install: 'npm i' -> 'yarn'
const convertNpmToYarn = (npmCode: string) => npmToYarn(npmCode, 'yarn'); const convertNpmToYarn = (npmCode: string) => npmToYarn(npmCode, 'yarn');

View file

@ -118,7 +118,7 @@ export default function docusaurusThemeClassic(
return '../src/theme'; return '../src/theme';
}, },
getTranslationFiles: async () => getTranslationFiles({themeConfig}), getTranslationFiles: () => getTranslationFiles({themeConfig}),
translateThemeConfig: (params) => translateThemeConfig: (params) =>
translateThemeConfig({ translateThemeConfig({

View file

@ -13,11 +13,11 @@ import {ReactContextError} from '../utils/reactUtils';
// Inspired by https://github.com/jamiebuilds/unstated-next/blob/master/src/unstated-next.tsx // Inspired by https://github.com/jamiebuilds/unstated-next/blob/master/src/unstated-next.tsx
const EmptyContext: unique symbol = Symbol('EmptyContext'); const EmptyContext: unique symbol = Symbol('EmptyContext');
type SidebarContextValue = {name: string; items: PropSidebar}; type ContextValue = {name: string; items: PropSidebar};
const Context = React.createContext< const Context = React.createContext<ContextValue | null | typeof EmptyContext>(
SidebarContextValue | null | typeof EmptyContext EmptyContext,
>(EmptyContext); );
/** /**
* Provide the current sidebar to your children. * Provide the current sidebar to your children.
@ -31,7 +31,7 @@ export function DocsSidebarProvider({
name: string | undefined; name: string | undefined;
items: PropSidebar | undefined; items: PropSidebar | undefined;
}): JSX.Element { }): JSX.Element {
const stableValue: SidebarContextValue | null = useMemo( const stableValue: ContextValue | null = useMemo(
() => () =>
name && items name && items
? { ? {
@ -45,9 +45,9 @@ export function DocsSidebarProvider({
} }
/** /**
* Gets the sidebar data that's currently displayed, or `null` if there isn't one * Gets the sidebar that's currently displayed, or `null` if there isn't one
*/ */
export function useDocsSidebar(): SidebarContextValue | null { export function useDocsSidebar(): ContextValue | null {
const value = useContext(Context); const value = useContext(Context);
if (value === EmptyContext) { if (value === EmptyContext) {
throw new ReactContextError('DocsSidebarProvider'); throw new ReactContextError('DocsSidebarProvider');

View file

@ -220,8 +220,9 @@ export function useDocsVersionCandidates(
/** /**
* The layout components, like navbar items, must be able to work on all pages, * The layout components, like navbar items, must be able to work on all pages,
* even on non-doc ones. This hook would always return a sidebar to be linked * even on non-doc ones where there's no version context, so a sidebar ID could
* to. See also {@link useDocsVersionCandidates} for how this selection is done. * be ambiguous. This hook would always return a sidebar to be linked to. See
* also {@link useDocsVersionCandidates} for how this selection is done.
* *
* @throws This hook throws if a sidebar with said ID is not found. * @throws This hook throws if a sidebar with said ID is not found.
*/ */
@ -252,8 +253,9 @@ export function useLayoutDocsSidebar(
/** /**
* The layout components, like navbar items, must be able to work on all pages, * The layout components, like navbar items, must be able to work on all pages,
* even on non-doc ones. This hook would always return a doc to be linked * even on non-doc ones where there's no version context, so a doc ID could be
* to. See also {@link useDocsVersionCandidates} for how this selection is done. * ambiguous. This hook would always return a doc to be linked to. See also
* {@link useDocsVersionCandidates} for how this selection is done.
* *
* @throws This hook throws if a doc with said ID is not found. * @throws This hook throws if a doc with said ID is not found.
*/ */

View file

@ -12,13 +12,13 @@ import useRouteContext from '@docusaurus/useRouteContext';
import {useBaseUrlUtils} from '@docusaurus/useBaseUrl'; import {useBaseUrlUtils} from '@docusaurus/useBaseUrl';
import {useTitleFormatter} from './generalUtils'; import {useTitleFormatter} from './generalUtils';
interface PageMetadataProps { type PageMetadataProps = {
readonly title?: string; readonly title?: string;
readonly description?: string; readonly description?: string;
readonly keywords?: readonly string[] | string; readonly keywords?: readonly string[] | string;
readonly image?: string; readonly image?: string;
readonly children?: ReactNode; readonly children?: ReactNode;
} };
/** /**
* Helper component to manipulate page metadata and override site defaults. * Helper component to manipulate page metadata and override site defaults.

View file

@ -54,11 +54,11 @@ Possible reasons: running Docusaurus in an iframe, in an incognito browser sessi
} }
// Convenient storage interface for a single storage key // Convenient storage interface for a single storage key
export interface StorageSlot { export type StorageSlot = {
get: () => string | null; get: () => string | null;
set: (value: string) => void; set: (value: string) => void;
del: () => void; del: () => void;
} };
const NoopStorageSlot: StorageSlot = { const NoopStorageSlot: StorageSlot = {
get: () => null, get: () => null,

View file

@ -101,7 +101,7 @@ function filterTOC({
* to ensure that weird TOC structures preserve their semantics. For example, an * to ensure that weird TOC structures preserve their semantics. For example, an
* h3-h2-h4 sequence should not be treeified as an "h3 > h4" hierarchy with * h3-h2-h4 sequence should not be treeified as an "h3 > h4" hierarchy with
* min=3, max=4, but should rather be "[h3, h4]" (since the h2 heading has split * min=3, max=4, but should rather be "[h3, h4]" (since the h2 heading has split
* the two headings and they are not parents) * the two headings and they are not parent-children)
*/ */
export function useFilteredAndTreeifiedTOC({ export function useFilteredAndTreeifiedTOC({
toc, toc,

View file

@ -10,10 +10,10 @@ declare module '@philpl/buble' {
// eslint-disable-next-line import/no-extraneous-dependencies, no-restricted-syntax // eslint-disable-next-line import/no-extraneous-dependencies, no-restricted-syntax
export * from 'buble'; export * from 'buble';
export const features: string[]; export const features: string[];
export interface TransformOptions extends OriginalTransformOptions { export type TransformOptions = OriginalTransformOptions & {
transforms?: OriginalTransformOptions['transforms'] & { transforms?: OriginalTransformOptions['transforms'] & {
asyncAwait?: boolean; asyncAwait?: boolean;
getterSetter?: boolean; getterSetter?: boolean;
}; };
} };
} }

View file

@ -23,9 +23,9 @@ declare module '@theme/Playground' {
} }
declare module '@theme/ReactLiveScope' { declare module '@theme/ReactLiveScope' {
interface Scope { type Scope = {
[key: string]: unknown; [key: string]: unknown;
} };
const ReactLiveScope: Scope; const ReactLiveScope: Scope;
export default ReactLiveScope; export default ReactLiveScope;

View file

@ -86,7 +86,7 @@ export type DocusaurusConfig = {
| string | string
| { | {
src: string; src: string;
[key: string]: unknown; [key: string]: string | boolean | undefined;
} }
)[]; )[];
clientModules: string[]; clientModules: string[];
@ -96,7 +96,7 @@ export type DocusaurusConfig = {
| string | string
| { | {
href: string; href: string;
[key: string]: unknown; [key: string]: string | boolean | undefined;
} }
)[]; )[];
titleDelimiter?: string; titleDelimiter?: string;
@ -304,16 +304,16 @@ export type ThemeConfigValidationContext<T> = {
export type Plugin<Content = unknown> = { export type Plugin<Content = unknown> = {
name: string; name: string;
loadContent?: () => Promise<Content>; loadContent?: () => Promise<Content> | Content;
contentLoaded?: (args: { contentLoaded?: (args: {
/** the content loaded by this plugin instance */ /** the content loaded by this plugin instance */
content: Content; // content: Content; //
/** content loaded by ALL the plugins */ /** content loaded by ALL the plugins */
allContent: AllContent; allContent: AllContent;
actions: PluginContentLoadedActions; actions: PluginContentLoadedActions;
}) => Promise<void>; }) => Promise<void> | void;
routesLoaded?: (routes: RouteConfig[]) => void; // TODO remove soon, deprecated (alpha-60) routesLoaded?: (routes: RouteConfig[]) => void; // TODO remove soon, deprecated (alpha-60)
postBuild?: (props: Props & {content: Content}) => Promise<void>; postBuild?: (props: Props & {content: Content}) => Promise<void> | void;
// TODO refactor the configureWebpack API surface: use an object instead of // TODO refactor the configureWebpack API surface: use an object instead of
// multiple params (requires breaking change) // multiple params (requires breaking change)
configureWebpack?: ( configureWebpack?: (
@ -342,8 +342,10 @@ export type Plugin<Content = unknown> = {
// translations // translations
getTranslationFiles?: (args: { getTranslationFiles?: (args: {
content: Content; content: Content;
}) => Promise<TranslationFile[]>; }) => Promise<TranslationFile[]> | TranslationFile[];
getDefaultCodeTranslationMessages?: () => Promise<{[id: string]: string}>; getDefaultCodeTranslationMessages?: () =>
| Promise<{[id: string]: string}>
| {[id: string]: string};
translateContent?: (args: { translateContent?: (args: {
/** The content loaded by this plugin instance. */ /** The content loaded by this plugin instance. */
content: Content; content: Content;

View file

@ -83,7 +83,7 @@ export const FrontMatterTagsSchema = JoiFrontMatter.array()
.items(FrontMatterTagSchema) .items(FrontMatterTagSchema)
.messages({ .messages({
'array.base': 'array.base':
'{{#label}} does not look like a valid FrontMatter Yaml array.', '{{#label}} does not look like a valid front matter Yaml array.',
}); });
export const FrontMatterTOCHeadingLevels = { export const FrontMatterTOCHeadingLevels = {

View file

@ -19,18 +19,18 @@ import './nprogress.css';
nprogress.configure({showSpinner: false}); nprogress.configure({showSpinner: false});
interface Props extends RouteComponentProps { type Props = RouteComponentProps & {
readonly routes: RouteConfig[]; readonly routes: RouteConfig[];
readonly delay: number; readonly delay: number;
readonly location: Location; readonly location: Location;
} };
interface State { type State = {
nextRouteHasLoaded: boolean; nextRouteHasLoaded: boolean;
} };
class PendingNavigation extends React.Component<Props, State> { class PendingNavigation extends React.Component<Props, State> {
previousLocation: Location | null; private previousLocation: Location | null;
progressBarTimeout: NodeJS.Timeout | null; private progressBarTimeout: NodeJS.Timeout | null;
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);

View file

@ -11,9 +11,9 @@ import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
import type {Props} from '@docusaurus/ErrorBoundary'; import type {Props} from '@docusaurus/ErrorBoundary';
import DefaultFallback from '@theme/Error'; import DefaultFallback from '@theme/Error';
interface State { type State = {
error: Error | null; error: Error | null;
} };
export default class ErrorBoundary extends React.Component<Props, State> { export default class ErrorBoundary extends React.Component<Props, State> {
constructor(props: Props) { constructor(props: Props) {

View file

@ -26,9 +26,9 @@ declare module 'react-loadable-ssr-addon-v5-slorber' {
modulesToBeLoaded: string[], modulesToBeLoaded: string[],
): {js: Asset[]; css: Asset[]}; ): {js: Asset[]; css: Asset[]};
interface ReactLoadableSSRAddon { type ReactLoadableSSRAddon = {
new (props: {filename: string}); new (props: {filename: string});
} };
const plugin: ReactLoadableSSRAddon; const plugin: ReactLoadableSSRAddon;
export default plugin; export default plugin;
@ -47,7 +47,7 @@ declare module '@slorber/static-site-generator-webpack-plugin' {
noIndex: boolean; noIndex: boolean;
}; };
interface StaticSiteGeneratorPlugin { type StaticSiteGeneratorPlugin = {
new (props: { new (props: {
entry: string; entry: string;
locals: Locals; locals: Locals;
@ -55,7 +55,7 @@ declare module '@slorber/static-site-generator-webpack-plugin' {
preferFoldersOutput?: boolean; preferFoldersOutput?: boolean;
globals: {[key: string]: unknown}; globals: {[key: string]: unknown};
}); });
} };
const plugin: StaticSiteGeneratorPlugin; const plugin: StaticSiteGeneratorPlugin;
export default plugin; export default plugin;

View file

@ -36,26 +36,27 @@ export function createBootstrapPlugin({
return siteConfigClientModules; return siteConfigClientModules;
}, },
injectHtmlTags: () => { injectHtmlTags: () => {
const stylesheetsTags = stylesheets.map((source) => const stylesheetsTags = stylesheets.map(
(source): string | HtmlTagObject =>
typeof source === 'string' typeof source === 'string'
? `<link rel="stylesheet" href="${source}">` ? `<link rel="stylesheet" href="${source}">`
: ({ : {
tagName: 'link', tagName: 'link',
attributes: { attributes: {
rel: 'stylesheet', rel: 'stylesheet',
...source, ...source,
}, },
} as HtmlTagObject), },
); );
const scriptsTags = scripts.map((source) => const scriptsTags = scripts.map((source): string | HtmlTagObject =>
typeof source === 'string' typeof source === 'string'
? `<script src="${source}"></script>` ? `<script src="${source}"></script>`
: ({ : {
tagName: 'script', tagName: 'script',
attributes: { attributes: {
...source, ...source,
}, },
} as HtmlTagObject), },
); );
return { return {
headTags: [...stylesheetsTags, ...scriptsTags], headTags: [...stylesheetsTags, ...scriptsTags],

View file

@ -33,7 +33,7 @@ import type {Compiler, Stats} from 'webpack';
import path from 'path'; import path from 'path';
import {sync as delSync} from 'del'; import {sync as delSync} from 'del';
export interface Options { export type Options = {
/** /**
* Write Logs to Console * Write Logs to Console
* (Always enabled when dry is true) * (Always enabled when dry is true)
@ -65,7 +65,7 @@ export interface Options {
* default: ['**\/*'] * default: ['**\/*']
*/ */
cleanOnceBeforeBuildPatterns?: string[]; cleanOnceBeforeBuildPatterns?: string[];
} };
export default class CleanWebpackPlugin { export default class CleanWebpackPlugin {
private readonly verbose: boolean; private readonly verbose: boolean;

View file

@ -10,9 +10,9 @@ import fs from 'fs-extra';
import waitOn from 'wait-on'; import waitOn from 'wait-on';
import type {Compiler} from 'webpack'; import type {Compiler} from 'webpack';
interface WaitPluginOptions { type WaitPluginOptions = {
filepath: string; filepath: string;
} };
export default class WaitPlugin { export default class WaitPlugin {
filepath: string; filepath: string;

View file

@ -24,13 +24,13 @@ The plugin module's default export is a constructor function with the signature
`context` is plugin-agnostic, and the same object will be passed into all plugins used for a Docusaurus website. The `context` object contains the following fields: `context` is plugin-agnostic, and the same object will be passed into all plugins used for a Docusaurus website. The `context` object contains the following fields:
```ts ```ts
interface LoadContext { type LoadContext = {
siteDir: string; siteDir: string;
generatedFilesDir: string; generatedFilesDir: string;
siteConfig: DocusaurusConfig; siteConfig: DocusaurusConfig;
outDir: string; outDir: string;
baseUrl: string; baseUrl: string;
} };
``` ```
### `options` {#options} ### `options` {#options}

View file

@ -43,17 +43,17 @@ The data that was loaded in `loadContent` will be consumed in `contentLoaded`. I
Create a route to add to the website. Create a route to add to the website.
```ts ```ts
interface RouteConfig { type RouteConfig = {
path: string; path: string;
component: string; component: string;
modules?: RouteModules; modules?: RouteModules;
routes?: RouteConfig[]; routes?: RouteConfig[];
exact?: boolean; exact?: boolean;
priority?: number; priority?: number;
} };
interface RouteModules { type RouteModules = {
[module: string]: Module | RouteModules | RouteModules[]; [module: string]: Module | RouteModules | RouteModules[];
} };
type Module = type Module =
| { | {
path: string; path: string;
@ -339,7 +339,7 @@ function injectHtmlTags(): {
type HtmlTags = string | HtmlTagObject | (string | HtmlTagObject)[]; type HtmlTags = string | HtmlTagObject | (string | HtmlTagObject)[];
interface HtmlTagObject { type HtmlTagObject = {
/** /**
* Attributes of the HTML tag * Attributes of the HTML tag
* E.g. `{'disabled': true, 'value': 'demo', 'rel': 'preconnect'}` * E.g. `{'disabled': true, 'value': 'demo', 'rel': 'preconnect'}`
@ -355,7 +355,7 @@ interface HtmlTagObject {
* The inner HTML * The inner HTML
*/ */
innerHTML?: string; innerHTML?: string;
} };
``` ```
Example: Example:

View file

@ -341,31 +341,31 @@ type PluginVersionInformation =
| {readonly type: 'local'} | {readonly type: 'local'}
| {readonly type: 'synthetic'}; | {readonly type: 'synthetic'};
interface SiteMetadata { type SiteMetadata = {
readonly docusaurusVersion: string; readonly docusaurusVersion: string;
readonly siteVersion?: string; readonly siteVersion?: string;
readonly pluginVersions: Record<string, PluginVersionInformation>; readonly pluginVersions: Record<string, PluginVersionInformation>;
} };
interface I18nLocaleConfig { type I18nLocaleConfig = {
label: string; label: string;
direction: string; direction: string;
} };
interface I18n { type I18n = {
defaultLocale: string; defaultLocale: string;
locales: [string, ...string[]]; locales: [string, ...string[]];
currentLocale: string; currentLocale: string;
localeConfigs: Record<string, I18nLocaleConfig>; localeConfigs: Record<string, I18nLocaleConfig>;
} };
interface DocusaurusContext { type DocusaurusContext = {
siteConfig: DocusaurusConfig; siteConfig: DocusaurusConfig;
siteMetadata: SiteMetadata; siteMetadata: SiteMetadata;
globalData: Record<string, unknown>; globalData: Record<string, unknown>;
i18n: I18n; i18n: I18n;
codeTranslations: Record<string, string>; codeTranslations: Record<string, string>;
} };
``` ```
Usage example: Usage example:

View file

@ -9,14 +9,14 @@ import React, {type ReactNode, type ComponentProps} from 'react';
import clsx from 'clsx'; import clsx from 'clsx';
import styles from './styles.module.css'; import styles from './styles.module.css';
export interface SvgIconProps extends ComponentProps<'svg'> { export type SvgIconProps = ComponentProps<'svg'> & {
viewBox?: string; viewBox?: string;
size?: 'inherit' | 'small' | 'medium' | 'large'; size?: 'inherit' | 'small' | 'medium' | 'large';
color?: 'inherit' | 'primary' | 'secondary' | 'success' | 'error' | 'warning'; color?: 'inherit' | 'primary' | 'secondary' | 'success' | 'error' | 'warning';
svgClass?: string; // Class attribute on the child svgClass?: string; // Class attribute on the child
colorAttr?: string; // Applies a color attribute to the SVG element. colorAttr?: string; // Applies a color attribute to the SVG element.
children: ReactNode; // Node passed into the SVG element. children: ReactNode; // Node passed into the SVG element.
} };
export default function Svg(props: SvgIconProps): JSX.Element { export default function Svg(props: SvgIconProps): JSX.Element {
const { const {

View file

@ -19,13 +19,13 @@ function WebsiteLink({to, children}: {to: string; children?: ReactNode}) {
); );
} }
interface ProfileProps { type ProfileProps = {
className?: string; className?: string;
name: string; name: string;
children: ReactNode; children: ReactNode;
githubUrl?: string; githubUrl?: string;
twitterUrl?: string; twitterUrl?: string;
} };
function TeamProfileCard({ function TeamProfileCard({
className, className,

View file

@ -9,9 +9,9 @@ import React from 'react';
import type {Props as Tweet} from '../components/Tweet'; import type {Props as Tweet} from '../components/Tweet';
export interface TweetItem extends Tweet { export type TweetItem = Tweet & {
showOnHomepage: boolean; showOnHomepage: boolean;
} };
const TWEETS: TweetItem[] = [ const TWEETS: TweetItem[] = [
{ {