mirror of
https://github.com/facebook/docusaurus.git
synced 2025-04-29 18:27:56 +02:00
fix(theme-classic): validate options properly (#7755)
* fix(theme-classic): validate options properly * improve normalization * fix doc
This commit is contained in:
parent
636d47060e
commit
cba8be01a3
5 changed files with 141 additions and 56 deletions
|
@ -7,11 +7,16 @@
|
|||
|
||||
import _ from 'lodash';
|
||||
|
||||
import {normalizeThemeConfig} from '@docusaurus/utils-validation';
|
||||
import {
|
||||
normalizeThemeConfig,
|
||||
normalizePluginOptions,
|
||||
} from '@docusaurus/utils-validation';
|
||||
import theme from 'prism-react-renderer/themes/github';
|
||||
import darkTheme from 'prism-react-renderer/themes/dracula';
|
||||
import {ThemeConfigSchema, DEFAULT_CONFIG} from '../validateThemeConfig';
|
||||
import {ThemeConfigSchema, DEFAULT_CONFIG, validateOptions} from '../options';
|
||||
import type {Options, PluginOptions} from '@docusaurus/theme-classic';
|
||||
import type {ThemeConfig} from '@docusaurus/theme-common';
|
||||
import type {Validate} from '@docusaurus/types';
|
||||
|
||||
function testValidateThemeConfig(partialThemeConfig: {[key: string]: unknown}) {
|
||||
return normalizeThemeConfig(ThemeConfigSchema, {
|
||||
|
@ -20,12 +25,10 @@ function testValidateThemeConfig(partialThemeConfig: {[key: string]: unknown}) {
|
|||
});
|
||||
}
|
||||
|
||||
function testOk(partialThemeConfig: {[key: string]: unknown}) {
|
||||
expect(
|
||||
testValidateThemeConfig({...DEFAULT_CONFIG, ...partialThemeConfig}),
|
||||
).toEqual({
|
||||
...DEFAULT_CONFIG,
|
||||
...partialThemeConfig,
|
||||
function testValidateOptions(options: Options) {
|
||||
return validateOptions({
|
||||
validate: normalizePluginOptions as Validate<Options, PluginOptions>,
|
||||
options,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -642,36 +645,6 @@ describe('themeConfig', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('customCss config', () => {
|
||||
it('accepts customCss undefined', () => {
|
||||
testOk({
|
||||
customCss: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts customCss string', () => {
|
||||
testOk({
|
||||
customCss: './path/to/cssFile.css',
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts customCss string array', () => {
|
||||
testOk({
|
||||
customCss: ['./path/to/cssFile.css', './path/to/cssFile2.css'],
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects customCss number', () => {
|
||||
expect(() =>
|
||||
testValidateThemeConfig({
|
||||
customCss: 42,
|
||||
}),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`""customCss" must be one of [array, string]"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('color mode config', () => {
|
||||
const withDefaultValues = (colorMode?: ThemeConfig['colorMode']) =>
|
||||
_.merge({}, DEFAULT_CONFIG.colorMode, colorMode);
|
||||
|
@ -849,3 +822,51 @@ describe('themeConfig', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateOptions', () => {
|
||||
describe('customCss config', () => {
|
||||
it('accepts customCss undefined', () => {
|
||||
expect(
|
||||
testValidateOptions({
|
||||
customCss: undefined,
|
||||
}),
|
||||
).toEqual({
|
||||
id: 'default',
|
||||
customCss: [],
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts customCss string', () => {
|
||||
expect(
|
||||
testValidateOptions({
|
||||
customCss: './path/to/cssFile.css',
|
||||
}),
|
||||
).toEqual({
|
||||
id: 'default',
|
||||
customCss: ['./path/to/cssFile.css'],
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts customCss string array', () => {
|
||||
expect(
|
||||
testValidateOptions({
|
||||
customCss: ['./path/to/cssFile.css', './path/to/cssFile2.css'],
|
||||
}),
|
||||
).toEqual({
|
||||
id: 'default',
|
||||
customCss: ['./path/to/cssFile.css', './path/to/cssFile2.css'],
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects customCss number', () => {
|
||||
expect(() =>
|
||||
testValidateOptions({
|
||||
// @ts-expect-error: test
|
||||
customCss: 42,
|
||||
}),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`""customCss" must be a string or an array of strings"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -13,7 +13,7 @@ import {getTranslationFiles, translateThemeConfig} from './translations';
|
|||
import type {LoadContext, Plugin} from '@docusaurus/types';
|
||||
import type {ThemeConfig} from '@docusaurus/theme-common';
|
||||
import type {Plugin as PostCssPlugin} from 'postcss';
|
||||
import type {Options} from '@docusaurus/theme-classic';
|
||||
import type {PluginOptions} from '@docusaurus/theme-classic';
|
||||
import type webpack from 'webpack';
|
||||
|
||||
const requireFromDocusaurusCore = createRequire(
|
||||
|
@ -98,7 +98,7 @@ function getInfimaCSSFile(direction: string) {
|
|||
|
||||
export default function themeClassic(
|
||||
context: LoadContext,
|
||||
options: Options,
|
||||
options: PluginOptions,
|
||||
): Plugin<undefined> {
|
||||
const {
|
||||
i18n: {currentLocale, localeConfigs},
|
||||
|
@ -109,7 +109,7 @@ export default function themeClassic(
|
|||
colorMode,
|
||||
prism: {additionalLanguages},
|
||||
} = themeConfig;
|
||||
const {customCss} = options ?? {};
|
||||
const {customCss} = options;
|
||||
const {direction} = localeConfigs[currentLocale]!;
|
||||
|
||||
return {
|
||||
|
@ -145,13 +145,7 @@ export default function themeClassic(
|
|||
'./nprogress',
|
||||
];
|
||||
|
||||
if (customCss) {
|
||||
modules.push(
|
||||
...(Array.isArray(customCss) ? customCss : [customCss]).map((p) =>
|
||||
path.resolve(context.siteDir, p),
|
||||
),
|
||||
);
|
||||
}
|
||||
modules.push(...customCss.map((p) => path.resolve(context.siteDir, p)));
|
||||
|
||||
return modules;
|
||||
},
|
||||
|
@ -211,4 +205,4 @@ ${announcementBar ? AnnouncementBarInlineJavaScript : ''}
|
|||
}
|
||||
|
||||
export {default as getSwizzleConfig} from './getSwizzleConfig';
|
||||
export {validateThemeConfig} from './validateThemeConfig';
|
||||
export {validateThemeConfig, validateOptions} from './options';
|
||||
|
|
|
@ -7,8 +7,12 @@
|
|||
|
||||
import defaultPrismTheme from 'prism-react-renderer/themes/palenight';
|
||||
import {Joi, URISchema} from '@docusaurus/utils-validation';
|
||||
import type {Options, PluginOptions} from '@docusaurus/theme-classic';
|
||||
import type {ThemeConfig} from '@docusaurus/theme-common';
|
||||
import type {ThemeConfigValidationContext} from '@docusaurus/types';
|
||||
import type {
|
||||
ThemeConfigValidationContext,
|
||||
OptionValidationContext,
|
||||
} from '@docusaurus/types';
|
||||
|
||||
const DEFAULT_DOCS_CONFIG: ThemeConfig['docs'] = {
|
||||
versionPersistence: 'localStorage',
|
||||
|
@ -296,10 +300,6 @@ const FooterLinkItemSchema = Joi.object({
|
|||
// attributes like target, aria-role, data-customAttribute...)
|
||||
.unknown();
|
||||
|
||||
const CustomCssSchema = Joi.alternatives()
|
||||
.try(Joi.array().items(Joi.string().required()), Joi.string().required())
|
||||
.optional();
|
||||
|
||||
const LogoSchema = Joi.object({
|
||||
alt: Joi.string().allow(''),
|
||||
src: Joi.string().required(),
|
||||
|
@ -324,7 +324,6 @@ export const ThemeConfigSchema = Joi.object<ThemeConfig>({
|
|||
'any.unknown':
|
||||
'defaultDarkMode theme config is deprecated. Please use the new colorMode attribute. You likely want: config.themeConfig.colorMode.defaultMode = "dark"',
|
||||
}),
|
||||
customCss: CustomCssSchema,
|
||||
colorMode: ColorModeSchema,
|
||||
image: Joi.string(),
|
||||
docs: DocsSchema,
|
||||
|
@ -442,3 +441,29 @@ export function validateThemeConfig({
|
|||
}: ThemeConfigValidationContext<ThemeConfig>): ThemeConfig {
|
||||
return validate(ThemeConfigSchema, themeConfig);
|
||||
}
|
||||
|
||||
const DEFAULT_OPTIONS = {
|
||||
customCss: [],
|
||||
};
|
||||
|
||||
const PluginOptionSchema = Joi.object<PluginOptions>({
|
||||
customCss: Joi.alternatives()
|
||||
.try(
|
||||
Joi.array().items(Joi.string().required()),
|
||||
Joi.alternatives().conditional(Joi.string().required(), {
|
||||
then: Joi.custom((val: string) => [val]),
|
||||
otherwise: Joi.forbidden().messages({
|
||||
'any.unknown': '"customCss" must be a string or an array of strings',
|
||||
}),
|
||||
}),
|
||||
)
|
||||
.default(DEFAULT_OPTIONS.customCss),
|
||||
});
|
||||
|
||||
export function validateOptions({
|
||||
validate,
|
||||
options,
|
||||
}: OptionValidationContext<Options, PluginOptions>): PluginOptions {
|
||||
const validatedOptions = validate(PluginOptionSchema, options);
|
||||
return validatedOptions;
|
||||
}
|
|
@ -23,8 +23,12 @@
|
|||
declare module '@docusaurus/theme-classic' {
|
||||
import type {LoadContext, Plugin, PluginModule} from '@docusaurus/types';
|
||||
|
||||
export type PluginOptions = {
|
||||
customCss: string[];
|
||||
};
|
||||
|
||||
export type Options = {
|
||||
customCss?: string | string[];
|
||||
customCss?: string[] | string;
|
||||
};
|
||||
|
||||
export const getSwizzleConfig: PluginModule['getSwizzleConfig'];
|
||||
|
|
|
@ -18,3 +18,44 @@ npm install --save @docusaurus/theme-classic
|
|||
If you have installed `@docusaurus/preset-classic`, you don't need to install it as a dependency.
|
||||
|
||||
:::
|
||||
|
||||
## Configuration {#configuration}
|
||||
|
||||
Accepted fields:
|
||||
|
||||
```mdx-code-block
|
||||
<APITable>
|
||||
```
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `customCss` | <code>string[] \| string</code> | `[]` | Stylesheets to be imported globally as [client modules](../../advanced/client.md#client-modules). Relative paths are resolved against the site directory. |
|
||||
|
||||
```mdx-code-block
|
||||
</APITable>
|
||||
```
|
||||
|
||||
:::note
|
||||
|
||||
Most configuration for the theme is done in `themeConfig`, which can be found in [theme configuration](./theme-configuration.md).
|
||||
|
||||
:::
|
||||
|
||||
### Example configuration {#ex-config}
|
||||
|
||||
You can configure this theme through preset options or plugin options.
|
||||
|
||||
:::tip
|
||||
|
||||
Most Docusaurus users configure this plugin through the preset options.
|
||||
|
||||
:::
|
||||
|
||||
```js config-tabs
|
||||
// Preset Options: theme
|
||||
// Plugin Options: @docusaurus/theme-classic
|
||||
|
||||
const config = {
|
||||
customCss: require.resolve('./src/css/custom.css'),
|
||||
};
|
||||
```
|
||||
|
|
Loading…
Add table
Reference in a new issue