Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/docusaurus-module-type-aliases/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,12 @@ declare module '@docusaurus/useDocusaurusContext' {
export default function useDocusaurusContext(): DocusaurusContext;
}

declare module '@docusaurus/useThemeConfig' {
import type {ThemeConfig} from '@docusaurus/types';

export default function useThemeConfig(): ThemeConfig;
}

declare module '@docusaurus/useRouteContext' {
import type {PluginRouteContext} from '@docusaurus/types';

Expand Down
13 changes: 7 additions & 6 deletions packages/docusaurus-preset-classic/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
* LICENSE file in the root directory of this source tree.
*/

// Adds the Algolia fields to the @docusaurus/types UserThemeConfig interface
/// <reference types="@docusaurus/theme-search-algolia" preserve="true" />

import type {Options as DocsPluginOptions} from '@docusaurus/plugin-content-docs';
import type {Options as BlogPluginOptions} from '@docusaurus/plugin-content-blog';
import type {Options as PagesPluginOptions} from '@docusaurus/plugin-content-pages';
Expand All @@ -14,9 +17,8 @@ import type {Options as GtagPluginOptions} from '@docusaurus/plugin-google-gtag'
import type {Options as GTMPluginOptions} from '@docusaurus/plugin-google-tag-manager';
import type {Options as CssCascadeLayersPluginOptions} from '@docusaurus/plugin-css-cascade-layers';
import type {Options as ThemeOptions} from '@docusaurus/theme-classic';
import type {ThemeConfig as BaseThemeConfig} from '@docusaurus/types';
import type {UserThemeConfig as ClassicThemeConfig} from '@docusaurus/theme-common';
import type {UserThemeConfig as AlgoliaThemeConfig} from '@docusaurus/theme-search-algolia';
// Importing from theme-common also adds the classic theme fields
import type {UserThemeConfig} from '@docusaurus/theme-common';

export type Options = {
/**
Expand Down Expand Up @@ -49,6 +51,5 @@ export type Options = {
cssCascadeLayers?: false | CssCascadeLayersPluginOptions;
};

export type ThemeConfig = BaseThemeConfig &
ClassicThemeConfig &
AlgoliaThemeConfig;
// Theme config fields of all the themes of this preset, including Algolia's
export type ThemeConfig = UserThemeConfig;
3 changes: 2 additions & 1 deletion packages/docusaurus-theme-classic/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
} from '@theme/NavbarItem/DocsVersionDropdownNavbarItem';
import type {Options, PluginOptions} from '@docusaurus/theme-classic';
import type {ThemeConfig} from '@docusaurus/theme-common';
import type {ClassicThemeConfig} from '@docusaurus/theme-common/internal';
import type {
ThemeConfigValidationContext,
OptionValidationContext,
Expand Down Expand Up @@ -58,7 +59,7 @@ const DEFAULT_COLOR_MODE_CONFIG: ThemeConfig['colorMode'] = {
respectPrefersColorScheme: false,
};

export const DEFAULT_CONFIG: ThemeConfig = {
export const DEFAULT_CONFIG: ClassicThemeConfig = {
colorMode: DEFAULT_COLOR_MODE_CONFIG,
docs: DEFAULT_DOCS_CONFIG,
blog: DEFAULT_BLOG_CONFIG,
Expand Down
2 changes: 2 additions & 0 deletions packages/docusaurus-theme-common/src/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export {
} from './utils/tabsUtils';
export type {TabValue, TabsProps, TabItemProps} from './utils/tabsUtils';

export type {ClassicThemeConfig} from './utils/useThemeConfig';

export {useNavbarMobileSidebar} from './contexts/navbarMobileSidebar';
export {useNavbarSecondaryMenu} from './contexts/navbarSecondaryMenu/display';

Expand Down
21 changes: 13 additions & 8 deletions packages/docusaurus-theme-common/src/utils/useThemeConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
* LICENSE file in the root directory of this source tree.
*/

import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import useCoreThemeConfig from '@docusaurus/useThemeConfig';
import type {PrismTheme} from 'prism-react-renderer';
import type {DeepPartial} from 'utility-types';
import type {ThemeConfig, UserThemeConfig} from '@docusaurus/types';
import type {MagicCommentConfig} from './codeBlockUtils';
import type {ColorMode} from '../contexts/colorMode';

Expand Down Expand Up @@ -104,9 +105,8 @@ type TableOfContents = {
maxHeadingLevel: number;
};

// TODO Docusaurus v4: use interface + declaration merging to enhance
// Theme config after validation/normalization
export type ThemeConfig = {
// Theme config fields of the classic theme, after validation/normalization
export type ClassicThemeConfig = {
docs: {
versionPersistence: DocsVersionPersistence;
sidebar: {
Expand Down Expand Up @@ -136,12 +136,17 @@ export type ThemeConfig = {
tableOfContents: TableOfContents;
};

// User-provided theme config, unnormalized
export type UserThemeConfig = DeepPartial<ThemeConfig>;
// Adds the classic theme fields to the @docusaurus/types interfaces
declare module '@docusaurus/types' {
interface ThemeConfig extends ClassicThemeConfig {}
interface UserThemeConfig extends DeepPartial<ClassicThemeConfig> {}
}

export type {ThemeConfig, UserThemeConfig};

/**
* A convenient/more semantic way to get theme config from context.
* Same as the `@docusaurus/useThemeConfig` hook of Docusaurus core.
*/
export function useThemeConfig(): ThemeConfig {
return useDocusaurusContext().siteConfig.themeConfig as ThemeConfig;
return useCoreThemeConfig();
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@
import {describe, expect, it} from 'vitest';
import {validateThemeConfig, DEFAULT_CONFIG} from '../validateThemeConfig';
import type {Joi} from '@docusaurus/utils-validation';
import type {ThemeConfig} from '@docusaurus/theme-live-codeblock';

function testValidateThemeConfig(themeConfig: {[key: string]: unknown}) {
function validate(
schema: Joi.ObjectSchema<{[key: string]: unknown}>,
cfg: {[key: string]: unknown},
) {
function validate(schema: Joi.ObjectSchema<ThemeConfig>, cfg: ThemeConfig) {
const {value, error} = schema.validate(cfg, {
convert: false,
});
Expand All @@ -23,7 +21,11 @@ function testValidateThemeConfig(themeConfig: {[key: string]: unknown}) {
return value;
}

return validateThemeConfig({validate, themeConfig});
// Some tests pass invalid theme configs on purpose
return validateThemeConfig({
validate,
themeConfig: themeConfig as ThemeConfig,
});
}

describe('validateThemeConfig', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,26 @@
declare module '@docusaurus/theme-live-codeblock' {
import type {PlaygroundPosition} from '@theme/Playground';

export type ThemeConfigLiveCodeBlock = {
playgroundPosition: PlaygroundPosition;
};

export type ThemeConfig = {
liveCodeBlock: {
playgroundPosition: PlaygroundPosition;
};
liveCodeBlock: ThemeConfigLiveCodeBlock;
};

// Adds the theme config fields to the @docusaurus/types interfaces
// Their names intentionally shadow the ThemeConfig types of this module
/* eslint-disable @typescript-eslint/no-shadow */
module '@docusaurus/types' {
interface ThemeConfig {
liveCodeBlock: ThemeConfigLiveCodeBlock;
}
interface UserThemeConfig {
liveCodeBlock?: Partial<ThemeConfigLiveCodeBlock>;
}
}
/* eslint-enable @typescript-eslint/no-shadow */
}

declare module '@theme/LiveCodeBlock' {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,13 @@
*/

import type {ReactNode} from 'react';
import {useThemeConfig} from '@docusaurus/theme-common';
import useThemeConfig from '@docusaurus/useThemeConfig';
import PlaygroundPreview from '@theme/Playground/Preview';
import PlaygroundEditor from '@theme/Playground/Editor';
import type {Props} from '@theme/Playground/Layout';

import type {ThemeConfig} from '@docusaurus/theme-live-codeblock';

function useLiveCodeBlockThemeConfig() {
const themeConfig = useThemeConfig() as unknown as ThemeConfig;
return themeConfig.liveCodeBlock;
}

export default function PlaygroundLayout(props: Props): ReactNode {
const themeConfig = useLiveCodeBlockThemeConfig();
const themeConfig = useThemeConfig().liveCodeBlock;
const position = props.position ?? themeConfig.playgroundPosition;
return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@
*/

import {Joi} from '@docusaurus/utils-validation';
import type {
ThemeConfig,
ThemeConfigValidationContext,
} from '@docusaurus/types';
import type {ThemeConfig} from '@docusaurus/theme-live-codeblock';
import type {ThemeConfigValidationContext} from '@docusaurus/types';

export const DEFAULT_CONFIG = {
playgroundPosition: 'bottom',
Expand Down
9 changes: 5 additions & 4 deletions packages/docusaurus-theme-mermaid/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@
*/

import {useState, useEffect, useMemo, useId, useRef} from 'react';
import {useColorMode, useThemeConfig} from '@docusaurus/theme-common';
import useThemeConfig from '@docusaurus/useThemeConfig';
import {useColorMode} from '@docusaurus/theme-common';
import {loadMermaid} from './loadMermaid';

import type {RenderResult, MermaidConfig} from 'mermaid';
import type {ThemeConfig} from '@docusaurus/theme-mermaid';
import type {ThemeConfigMermaid} from '@docusaurus/theme-mermaid';

// Stable className to allow users to easily target with CSS
export const MermaidContainerClassName = 'docusaurus-mermaid-container';

export function useMermaidThemeConfig(): ThemeConfig['mermaid'] {
return (useThemeConfig() as unknown as ThemeConfig).mermaid;
export function useMermaidThemeConfig(): ThemeConfigMermaid {
return useThemeConfig().mermaid;
}

export function useMermaidConfig(): MermaidConfig {
Expand Down
29 changes: 22 additions & 7 deletions packages/docusaurus-theme-mermaid/src/theme-mermaid.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,32 @@ declare module '@docusaurus/theme-mermaid' {
import type {MermaidConfig} from 'mermaid';
import type {Plugin} from '@docusaurus/types';

export type ThemeConfig = {
mermaid: {
theme: {
light: NonNullable<MermaidConfig['theme']>;
dark: NonNullable<MermaidConfig['theme']>;
};
options: MermaidConfig;
export type ThemeConfigMermaid = {
theme: {
light: NonNullable<MermaidConfig['theme']>;
dark: NonNullable<MermaidConfig['theme']>;
};
options: MermaidConfig;
};

export type ThemeConfig = {
mermaid: ThemeConfigMermaid;
};
export type UserThemeConfig = DeepPartial<ThemeConfig>;

// Adds the theme config fields to the @docusaurus/types interfaces
// Their names intentionally shadow the ThemeConfig types of this module
/* eslint-disable @typescript-eslint/no-shadow */
module '@docusaurus/types' {
interface ThemeConfig {
mermaid: ThemeConfigMermaid;
}
interface UserThemeConfig {
mermaid?: DeepPartial<ThemeConfigMermaid>;
}
}
/* eslint-enable @typescript-eslint/no-shadow */

export default function themeMermaid(): Plugin<undefined>;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,25 +59,40 @@ declare module '@docusaurus/theme-search-algolia' {
};
};

export type UserThemeConfigAlgolia = Overwrite<
DeepPartial<ThemeConfigAlgolia>,
{
// Required fields:
appId: ThemeConfigAlgolia['appId'];
apiKey: ThemeConfigAlgolia['apiKey'];
indexName: ThemeConfigAlgolia['indexName'];
// askAi also accepts a shorter string form
askAi?: string | Optional<AskAiConfig, 'indexName' | 'appId' | 'apiKey'>;
}
>;

export type ThemeConfig = {
algolia: ThemeConfigAlgolia;
};

export type UserThemeConfig = {
algolia?: Overwrite<
DeepPartial<ThemeConfigAlgolia>,
{
// Required fields:
appId: ThemeConfigAlgolia['appId'];
apiKey: ThemeConfigAlgolia['apiKey'];
indexName: ThemeConfigAlgolia['indexName'];
// askAi also accepts a shorter string form
askAi?:
| string
| Optional<AskAiConfig, 'indexName' | 'appId' | 'apiKey'>;
}
>;
algolia?: UserThemeConfigAlgolia;
};

// Adds the theme config fields to the @docusaurus/types interfaces
// Their names intentionally shadow the ThemeConfig types of this module
/* eslint-disable @typescript-eslint/no-shadow */
module '@docusaurus/types' {
interface ThemeConfig {
// Optional: the classic preset only enables this theme when the
// "algolia" field is provided
algolia?: ThemeConfigAlgolia;
}
interface UserThemeConfig {
algolia?: UserThemeConfigAlgolia;
}
}
/* eslint-enable @typescript-eslint/no-shadow */
}

declare module '@theme/SearchPage' {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {useHistory} from '@docusaurus/router';
import {isRegexpStringMatch} from '@docusaurus/theme-common';
import {
useAlgoliaContextualFacetFilters,
useAlgoliaThemeConfig,
useSearchResultUrlProcessor,
useAlgoliaAskAi,
mergeFacetFilters,
Expand Down Expand Up @@ -310,14 +311,14 @@ function DocSearch({externalUrlRegex, ...props}: DocSearchV4Props) {
}

export default function SearchBar(props: Partial<DocSearchV4Props>): ReactNode {
const {siteConfig} = useDocusaurusContext();
const {algolia} = useAlgoliaThemeConfig();

const docSearchProps: DocSearchV4Props = {
...(siteConfig.themeConfig.algolia as DocSearchV4Props),
const docSearchProps = {
...algolia,
// Let props override theme config
// See https://github.com/facebook/docusaurus/pull/11581
...props,
};
} as DocSearchV4Props;

return <DocSearch {...docSearchProps} />;
}
28 changes: 26 additions & 2 deletions packages/docusaurus-types/src/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,32 @@ import type {MarkdownConfig} from './markdown';

export type RouterType = 'browser' | 'hash';

export type ThemeConfig = {
/**
* Theme config, after validation and normalization by themes and plugins.
*
* Themes and plugins can declare their own fields with declaration merging:
*
* ```ts
* declare module '@docusaurus/types' {
* interface ThemeConfig {
* myTheme: {position: 'top' | 'bottom'};
* }
* }
* ```
*/
export interface ThemeConfig {
[key: string]: unknown;
};
}

/**
* Theme config, as written by users in their site config, before validation.
*
* Themes and plugins can declare their own fields with declaration merging,
* like for {@link ThemeConfig}.
*/
export interface UserThemeConfig {
[key: string]: unknown;
}

export type StorageConfig = {
type: SiteStorage['type'];
Expand Down Expand Up @@ -403,5 +426,6 @@ export type Config = Overwrite<
*/
webpack?: boolean | WebpackConfig;
future?: DeepPartial<FutureConfig>;
themeConfig?: UserThemeConfig;
}
>;
Loading
Loading