mirror of
https://github.com/facebook/docusaurus.git
synced 2025-04-29 18:27:56 +02:00
refactor: unify how validateOptions is handled (#6961)
* refactor: unify how validateOptions is handled * fix types * fix again
This commit is contained in:
parent
44107fb879
commit
6e2eb44964
43 changed files with 542 additions and 540 deletions
|
@ -15,11 +15,14 @@ function testValidate(options: Options) {
|
|||
|
||||
describe('normalizePluginOptions', () => {
|
||||
it('returns default options for undefined user options', () => {
|
||||
expect(testValidate(undefined)).toEqual(DEFAULT_OPTIONS);
|
||||
expect(testValidate(undefined)).toEqual({
|
||||
...DEFAULT_OPTIONS,
|
||||
id: 'default',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns default options for empty user options', () => {
|
||||
expect(testValidate(undefined)).toEqual(DEFAULT_OPTIONS);
|
||||
expect(testValidate({})).toEqual({...DEFAULT_OPTIONS, id: 'default'});
|
||||
});
|
||||
|
||||
it('overrides one default options with valid user options', () => {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type {LoadContext, Plugin, Props} from '@docusaurus/types';
|
||||
import type {LoadContext, Plugin} from '@docusaurus/types';
|
||||
import type {PluginContext, RedirectMetadata} from './types';
|
||||
import type {PluginOptions} from '@docusaurus/plugin-client-redirects';
|
||||
|
||||
|
@ -24,7 +24,7 @@ export default function pluginClientRedirectsPages(
|
|||
|
||||
return {
|
||||
name: 'docusaurus-plugin-client-redirects',
|
||||
async postBuild(props: Props) {
|
||||
async postBuild(props) {
|
||||
const pluginContext: PluginContext = {
|
||||
relativeRoutesPaths: props.routesPaths.map(
|
||||
(path) => `${addLeadingSlash(removePrefix(path, props.baseUrl))}`,
|
||||
|
|
|
@ -7,12 +7,10 @@
|
|||
|
||||
import type {
|
||||
PluginOptions,
|
||||
Options,
|
||||
RedirectOption,
|
||||
} from '@docusaurus/plugin-client-redirects';
|
||||
import type {
|
||||
OptionValidationContext,
|
||||
ValidationResult,
|
||||
} from '@docusaurus/types';
|
||||
import type {OptionValidationContext} from '@docusaurus/types';
|
||||
import {Joi, PathnameSchema} from '@docusaurus/utils-validation';
|
||||
|
||||
export const DEFAULT_OPTIONS: Partial<PluginOptions> = {
|
||||
|
@ -47,6 +45,6 @@ const UserOptionsSchema = Joi.object<PluginOptions>({
|
|||
export function validateOptions({
|
||||
validate,
|
||||
options: userOptions,
|
||||
}: OptionValidationContext<PluginOptions>): ValidationResult<PluginOptions> {
|
||||
}: OptionValidationContext<Options, PluginOptions>): PluginOptions {
|
||||
return validate(UserOptionsSchema, userOptions);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`validateOptions throws Error in case of invalid feed type 1`] = `"\\"feedOptions.type\\" does not match any of the allowed types"`;
|
||||
|
||||
exports[`validateOptions throws Error in case of invalid options 1`] = `"\\"postsPerPage\\" must be greater than or equal to 1"`;
|
|
@ -1,5 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`blog plugin options schema throws Error in case of invalid feed type 1`] = `[ValidationError: "feedOptions.type" does not match any of the allowed types]`;
|
||||
|
||||
exports[`blog plugin options schema throws Error in case of invalid options 1`] = `[ValidationError: "postsPerPage" must be greater than or equal to 1]`;
|
|
@ -11,7 +11,7 @@ import fs from 'fs-extra';
|
|||
import {createBlogFeedFiles} from '../feed';
|
||||
import type {LoadContext, I18n} from '@docusaurus/types';
|
||||
import type {BlogContentPaths} from '../types';
|
||||
import {DEFAULT_OPTIONS} from '../pluginOptionSchema';
|
||||
import {DEFAULT_OPTIONS} from '../options';
|
||||
import {generateBlogPosts} from '../blogUtils';
|
||||
import type {PluginOptions} from '@docusaurus/plugin-content-blog';
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {validateBlogPostFrontMatter} from '../blogFrontMatter';
|
||||
import {validateBlogPostFrontMatter} from '../frontMatter';
|
||||
import escapeStringRegexp from 'escape-string-regexp';
|
||||
import type {BlogPostFrontMatter} from '@docusaurus/plugin-content-blog';
|
||||
|
|
@ -9,9 +9,9 @@ import {jest} from '@jest/globals';
|
|||
import path from 'path';
|
||||
import pluginContentBlog from '../index';
|
||||
import type {DocusaurusConfig, LoadContext, I18n} from '@docusaurus/types';
|
||||
import {PluginOptionSchema} from '../pluginOptionSchema';
|
||||
import {validateOptions} from '../options';
|
||||
import type {BlogPost} from '../types';
|
||||
import type {Joi} from '@docusaurus/utils-validation';
|
||||
import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
||||
import {posixPath, getFileCommitDate} from '@docusaurus/utils';
|
||||
import type {
|
||||
PluginOptions,
|
||||
|
@ -47,18 +47,6 @@ function getI18n(locale: string): I18n {
|
|||
|
||||
const DefaultI18N: I18n = getI18n('en');
|
||||
|
||||
function validateAndNormalize(
|
||||
schema: Joi.ObjectSchema,
|
||||
options: Partial<PluginOptions>,
|
||||
) {
|
||||
const {value, error} = schema.validate(options);
|
||||
if (error) {
|
||||
throw error;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
const PluginPath = 'blog';
|
||||
|
||||
const BaseEditUrl = 'https://baseEditUrl.com/edit';
|
||||
|
@ -81,11 +69,14 @@ const getPlugin = async (
|
|||
generatedFilesDir,
|
||||
i18n,
|
||||
} as LoadContext,
|
||||
validateAndNormalize(PluginOptionSchema, {
|
||||
path: PluginPath,
|
||||
editUrl: BaseEditUrl,
|
||||
...pluginOptions,
|
||||
}),
|
||||
validateOptions({
|
||||
validate: normalizePluginOptions,
|
||||
options: {
|
||||
path: PluginPath,
|
||||
editUrl: BaseEditUrl,
|
||||
...pluginOptions,
|
||||
},
|
||||
}) as PluginOptions,
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {validateOptions, DEFAULT_OPTIONS} from '../options';
|
||||
import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
||||
import type {Options} from '@docusaurus/plugin-content-blog';
|
||||
|
||||
function testValidate(options: Options) {
|
||||
return validateOptions({validate: normalizePluginOptions, options});
|
||||
}
|
||||
|
||||
// the type of remark/rehype plugins can be either function, object or array
|
||||
const markdownPluginsFunctionStub = () => {};
|
||||
const markdownPluginsObjectStub = {};
|
||||
|
||||
const defaultOptions = {...DEFAULT_OPTIONS, id: 'default'};
|
||||
|
||||
describe('validateOptions', () => {
|
||||
it('returns default options for undefined user options', () => {
|
||||
expect(testValidate(undefined)).toEqual(defaultOptions);
|
||||
});
|
||||
|
||||
it('returns default options for empty user options', () => {
|
||||
expect(testValidate({})).toEqual(defaultOptions);
|
||||
});
|
||||
|
||||
it('accepts correctly defined user options', () => {
|
||||
const userOptions = {
|
||||
...defaultOptions,
|
||||
feedOptions: {type: 'rss' as const, title: 'myTitle'},
|
||||
path: 'not_blog',
|
||||
routeBasePath: 'myBlog',
|
||||
postsPerPage: 5,
|
||||
include: ['api/*', 'docs/*'],
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...userOptions,
|
||||
feedOptions: {type: ['rss'], title: 'myTitle', copyright: ''},
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts valid user options', () => {
|
||||
const userOptions = {
|
||||
...defaultOptions,
|
||||
routeBasePath: 'myBlog',
|
||||
beforeDefaultRemarkPlugins: [],
|
||||
beforeDefaultRehypePlugins: [markdownPluginsFunctionStub],
|
||||
remarkPlugins: [[markdownPluginsFunctionStub, {option1: '42'}]],
|
||||
rehypePlugins: [
|
||||
markdownPluginsObjectStub,
|
||||
[markdownPluginsFunctionStub, {option1: '42'}],
|
||||
],
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual(userOptions);
|
||||
});
|
||||
|
||||
it('throws Error in case of invalid options', () => {
|
||||
expect(() =>
|
||||
testValidate({
|
||||
path: 'not_blog',
|
||||
postsPerPage: -1,
|
||||
include: ['api/*', 'docs/*'],
|
||||
routeBasePath: 'not_blog',
|
||||
}),
|
||||
).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
it('throws Error in case of invalid feed type', () => {
|
||||
expect(() =>
|
||||
testValidate({
|
||||
feedOptions: {
|
||||
type: 'none',
|
||||
},
|
||||
}),
|
||||
).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
it('converts all feed type to array with other feed type', () => {
|
||||
expect(
|
||||
testValidate({
|
||||
feedOptions: {type: 'all'},
|
||||
}),
|
||||
).toEqual({
|
||||
...defaultOptions,
|
||||
feedOptions: {type: ['rss', 'atom', 'json'], copyright: ''},
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts null type and return same', () => {
|
||||
expect(
|
||||
testValidate({
|
||||
feedOptions: {type: null},
|
||||
}),
|
||||
).toEqual({
|
||||
...defaultOptions,
|
||||
feedOptions: {type: null},
|
||||
});
|
||||
});
|
||||
|
||||
it('contains array with rss + atom for missing feed type', () => {
|
||||
expect(
|
||||
testValidate({
|
||||
feedOptions: {},
|
||||
}),
|
||||
).toEqual(defaultOptions);
|
||||
});
|
||||
|
||||
it('has array with rss + atom, title for missing feed type', () => {
|
||||
expect(
|
||||
testValidate({
|
||||
feedOptions: {title: 'title'},
|
||||
}),
|
||||
).toEqual({
|
||||
...defaultOptions,
|
||||
feedOptions: {type: ['rss', 'atom'], title: 'title', copyright: ''},
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts 0 sidebar count', () => {
|
||||
const userOptions = {blogSidebarCount: 0};
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts "ALL" sidebar count', () => {
|
||||
const userOptions = {blogSidebarCount: 'ALL' as const};
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects "abcdef" sidebar count', () => {
|
||||
const userOptions = {blogSidebarCount: 'abcdef'};
|
||||
expect(() => testValidate(userOptions)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"blogSidebarCount\\" must be one of [ALL, number]"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('accepts "all posts" sidebar title', () => {
|
||||
const userOptions = {blogSidebarTitle: 'all posts'};
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects 42 sidebar title', () => {
|
||||
const userOptions = {blogSidebarTitle: 42};
|
||||
expect(() => testValidate(userOptions)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"blogSidebarTitle\\" must be a string"`,
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1,152 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {PluginOptionSchema, DEFAULT_OPTIONS} from '../pluginOptionSchema';
|
||||
|
||||
// the type of remark/rehype plugins can be either function, object or array
|
||||
const markdownPluginsFunctionStub = () => {};
|
||||
const markdownPluginsObjectStub = {};
|
||||
|
||||
describe('blog plugin options schema', () => {
|
||||
it('normalizes options', () => {
|
||||
const {value, error} = PluginOptionSchema.validate({});
|
||||
expect(value).toEqual(DEFAULT_OPTIONS);
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('accepts correctly defined user options', () => {
|
||||
const userOptions = {
|
||||
...DEFAULT_OPTIONS,
|
||||
feedOptions: {type: 'rss', title: 'myTitle'},
|
||||
path: 'not_blog',
|
||||
routeBasePath: 'myBlog',
|
||||
postsPerPage: 5,
|
||||
include: ['api/*', 'docs/*'],
|
||||
};
|
||||
const {value, error} = PluginOptionSchema.validate(userOptions);
|
||||
expect(value).toEqual({
|
||||
...userOptions,
|
||||
feedOptions: {type: ['rss'], title: 'myTitle', copyright: ''},
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('accepts valid user options', async () => {
|
||||
const userOptions = {
|
||||
...DEFAULT_OPTIONS,
|
||||
routeBasePath: 'myBlog',
|
||||
beforeDefaultRemarkPlugins: [],
|
||||
beforeDefaultRehypePlugins: [markdownPluginsFunctionStub],
|
||||
remarkPlugins: [[markdownPluginsFunctionStub, {option1: '42'}]],
|
||||
rehypePlugins: [
|
||||
markdownPluginsObjectStub,
|
||||
[markdownPluginsFunctionStub, {option1: '42'}],
|
||||
],
|
||||
};
|
||||
const {value, error} = PluginOptionSchema.validate(userOptions);
|
||||
expect(value).toEqual(userOptions);
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('throws Error in case of invalid options', () => {
|
||||
const {error} = PluginOptionSchema.validate({
|
||||
path: 'not_blog',
|
||||
postsPerPage: -1,
|
||||
include: ['api/*', 'docs/*'],
|
||||
routeBasePath: 'not_blog',
|
||||
});
|
||||
|
||||
expect(error).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('throws Error in case of invalid feed type', () => {
|
||||
const {error} = PluginOptionSchema.validate({
|
||||
feedOptions: {
|
||||
type: 'none',
|
||||
},
|
||||
});
|
||||
|
||||
expect(error).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('converts all feed type to array with other feed type', () => {
|
||||
const {value} = PluginOptionSchema.validate({
|
||||
feedOptions: {type: 'all'},
|
||||
});
|
||||
expect(value).toEqual({
|
||||
...DEFAULT_OPTIONS,
|
||||
feedOptions: {type: ['rss', 'atom', 'json'], copyright: ''},
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts null type and return same', () => {
|
||||
const {value, error} = PluginOptionSchema.validate({
|
||||
feedOptions: {type: null},
|
||||
});
|
||||
expect(value).toEqual({
|
||||
...DEFAULT_OPTIONS,
|
||||
feedOptions: {type: null},
|
||||
});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('contains array with rss + atom for missing feed type', () => {
|
||||
const {value} = PluginOptionSchema.validate({
|
||||
feedOptions: {},
|
||||
});
|
||||
expect(value).toEqual(DEFAULT_OPTIONS);
|
||||
});
|
||||
|
||||
it('has array with rss + atom, title for missing feed type', () => {
|
||||
const {value} = PluginOptionSchema.validate({
|
||||
feedOptions: {title: 'title'},
|
||||
});
|
||||
expect(value).toEqual({
|
||||
...DEFAULT_OPTIONS,
|
||||
feedOptions: {type: ['rss', 'atom'], title: 'title', copyright: ''},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('blog sidebar', () => {
|
||||
it('accepts 0 sidebar count', () => {
|
||||
const userOptions = {blogSidebarCount: 0};
|
||||
const {value, error} = PluginOptionSchema.validate(userOptions);
|
||||
expect(value).toEqual({...DEFAULT_OPTIONS, ...userOptions});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('accepts "ALL" sidebar count', () => {
|
||||
const userOptions = {blogSidebarCount: 'ALL'};
|
||||
const {value, error} = PluginOptionSchema.validate(userOptions);
|
||||
expect(value).toEqual({...DEFAULT_OPTIONS, ...userOptions});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('rejects "abcdef" sidebar count', () => {
|
||||
const userOptions = {blogSidebarCount: 'abcdef'};
|
||||
const {error} = PluginOptionSchema.validate(userOptions);
|
||||
expect(error).toMatchInlineSnapshot(
|
||||
`[ValidationError: "blogSidebarCount" must be one of [ALL, number]]`,
|
||||
);
|
||||
});
|
||||
|
||||
it('accepts "all posts" sidebar title', () => {
|
||||
const userOptions = {blogSidebarTitle: 'all posts'};
|
||||
const {value, error} = PluginOptionSchema.validate(userOptions);
|
||||
expect(value).toEqual({...DEFAULT_OPTIONS, ...userOptions});
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('rejects 42 sidebar title', () => {
|
||||
const userOptions = {blogSidebarTitle: 42};
|
||||
const {error} = PluginOptionSchema.validate(userOptions);
|
||||
expect(error).toMatchInlineSnapshot(
|
||||
`[ValidationError: "blogSidebarTitle" must be a string]`,
|
||||
);
|
||||
});
|
||||
});
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import type {BlogPost, BlogContent} from '../types';
|
||||
import {getTranslationFiles, translateContent} from '../translations';
|
||||
import {DEFAULT_OPTIONS} from '../pluginOptionSchema';
|
||||
import {DEFAULT_OPTIONS} from '../options';
|
||||
import {updateTranslationFileMessages} from '@docusaurus/utils';
|
||||
import type {PluginOptions} from '@docusaurus/plugin-content-blog';
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ import {
|
|||
getContentPathList,
|
||||
} from '@docusaurus/utils';
|
||||
import type {LoadContext} from '@docusaurus/types';
|
||||
import {validateBlogPostFrontMatter} from './blogFrontMatter';
|
||||
import {validateBlogPostFrontMatter} from './frontMatter';
|
||||
import {type AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors';
|
||||
import logger from '@docusaurus/logger';
|
||||
import type {
|
||||
|
|
|
@ -30,14 +30,7 @@ import type {
|
|||
BlogContentPaths,
|
||||
BlogMarkdownLoaderOptions,
|
||||
} from './types';
|
||||
import {PluginOptionSchema} from './pluginOptionSchema';
|
||||
import type {
|
||||
LoadContext,
|
||||
Plugin,
|
||||
HtmlTags,
|
||||
OptionValidationContext,
|
||||
ValidationResult,
|
||||
} from '@docusaurus/types';
|
||||
import type {LoadContext, Plugin, HtmlTags} from '@docusaurus/types';
|
||||
import {
|
||||
generateBlogPosts,
|
||||
getSourceToPermalink,
|
||||
|
@ -572,10 +565,4 @@ export default async function pluginContentBlog(
|
|||
};
|
||||
}
|
||||
|
||||
export function validateOptions({
|
||||
validate,
|
||||
options,
|
||||
}: OptionValidationContext<PluginOptions>): ValidationResult<PluginOptions> {
|
||||
const validatedOptions = validate(PluginOptionSchema, options);
|
||||
return validatedOptions;
|
||||
}
|
||||
export {validateOptions} from './options';
|
||||
|
|
|
@ -13,7 +13,8 @@ import {
|
|||
URISchema,
|
||||
} from '@docusaurus/utils-validation';
|
||||
import {GlobExcludeDefault} from '@docusaurus/utils';
|
||||
import type {PluginOptions} from '@docusaurus/plugin-content-blog';
|
||||
import type {PluginOptions, Options} from '@docusaurus/plugin-content-blog';
|
||||
import type {OptionValidationContext} from '@docusaurus/types';
|
||||
|
||||
export const DEFAULT_OPTIONS: PluginOptions = {
|
||||
feedOptions: {type: ['rss', 'atom'], copyright: ''},
|
||||
|
@ -46,7 +47,7 @@ export const DEFAULT_OPTIONS: PluginOptions = {
|
|||
sortPosts: 'descending',
|
||||
};
|
||||
|
||||
export const PluginOptionSchema = Joi.object<PluginOptions>({
|
||||
const PluginOptionSchema = Joi.object<PluginOptions>({
|
||||
path: Joi.string().default(DEFAULT_OPTIONS.path),
|
||||
archiveBasePath: Joi.string()
|
||||
.default(DEFAULT_OPTIONS.archiveBasePath)
|
||||
|
@ -125,4 +126,15 @@ export const PluginOptionSchema = Joi.object<PluginOptions>({
|
|||
sortPosts: Joi.string()
|
||||
.valid('descending', 'ascending')
|
||||
.default(DEFAULT_OPTIONS.sortPosts),
|
||||
});
|
||||
}).default(DEFAULT_OPTIONS);
|
||||
|
||||
export function validateOptions({
|
||||
validate,
|
||||
options,
|
||||
}: OptionValidationContext<Options, PluginOptions>): PluginOptions {
|
||||
const validatedOptions = validate(
|
||||
PluginOptionSchema,
|
||||
options,
|
||||
) as PluginOptions;
|
||||
return validatedOptions;
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {validateDocFrontMatter} from '../docFrontMatter';
|
||||
import {validateDocFrontMatter} from '../frontMatter';
|
||||
import type {DocFrontMatter} from '../types';
|
||||
import escapeStringRegexp from 'escape-string-regexp';
|
||||
|
|
@ -20,7 +20,7 @@ import {posixPath, DEFAULT_PLUGIN_ID} from '@docusaurus/utils';
|
|||
import {sortConfig} from '@docusaurus/core/src/server/plugins';
|
||||
|
||||
import * as cliDocs from '../cli';
|
||||
import {OptionsSchema} from '../options';
|
||||
import {validateOptions} from '../options';
|
||||
import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
||||
import type {LoadedVersion} from '../types';
|
||||
import type {
|
||||
|
@ -119,8 +119,11 @@ describe('sidebar', () => {
|
|||
const sidebarPath = path.join(siteDir, 'wrong-sidebars.json');
|
||||
const plugin = await pluginContentDocs(
|
||||
context,
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
sidebarPath,
|
||||
validateOptions({
|
||||
validate: normalizePluginOptions,
|
||||
options: {
|
||||
sidebarPath,
|
||||
},
|
||||
}),
|
||||
);
|
||||
await expect(plugin.loadContent!()).rejects.toThrowErrorMatchingSnapshot();
|
||||
|
@ -133,8 +136,11 @@ describe('sidebar', () => {
|
|||
await expect(async () => {
|
||||
const plugin = await pluginContentDocs(
|
||||
context,
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
sidebarPath: 'wrong-path-sidebar.json',
|
||||
validateOptions({
|
||||
validate: normalizePluginOptions,
|
||||
options: {
|
||||
sidebarPath: 'wrong-path-sidebar.json',
|
||||
},
|
||||
}),
|
||||
);
|
||||
await plugin.loadContent!();
|
||||
|
@ -152,8 +158,11 @@ describe('sidebar', () => {
|
|||
const context = await loadContext(siteDir);
|
||||
const plugin = await pluginContentDocs(
|
||||
context,
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
sidebarPath: undefined,
|
||||
validateOptions({
|
||||
validate: normalizePluginOptions,
|
||||
options: {
|
||||
sidebarPath: undefined,
|
||||
},
|
||||
}),
|
||||
);
|
||||
const result = await plugin.loadContent!();
|
||||
|
@ -167,8 +176,11 @@ describe('sidebar', () => {
|
|||
const context = await loadContext(siteDir);
|
||||
const plugin = await pluginContentDocs(
|
||||
context,
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
sidebarPath: false,
|
||||
validateOptions({
|
||||
validate: normalizePluginOptions,
|
||||
options: {
|
||||
sidebarPath: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
const result = await plugin.loadContent!();
|
||||
|
@ -186,7 +198,7 @@ describe('empty/no docs website', () => {
|
|||
await fs.ensureDir(path.join(siteDir, 'docs'));
|
||||
const plugin = await pluginContentDocs(
|
||||
context,
|
||||
normalizePluginOptions(OptionsSchema, {}),
|
||||
validateOptions({validate: normalizePluginOptions, options: {}}),
|
||||
);
|
||||
await expect(
|
||||
plugin.loadContent!(),
|
||||
|
@ -200,8 +212,11 @@ describe('empty/no docs website', () => {
|
|||
await expect(
|
||||
pluginContentDocs(
|
||||
context,
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
path: `path/does/not/exist`,
|
||||
validateOptions({
|
||||
validate: normalizePluginOptions,
|
||||
options: {
|
||||
path: 'path/does/not/exist',
|
||||
},
|
||||
}),
|
||||
),
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
|
@ -217,9 +232,12 @@ describe('simple website', () => {
|
|||
const sidebarPath = path.join(siteDir, 'sidebars.json');
|
||||
const plugin = await pluginContentDocs(
|
||||
context,
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
path: 'docs',
|
||||
sidebarPath,
|
||||
validateOptions({
|
||||
validate: normalizePluginOptions,
|
||||
options: {
|
||||
path: 'docs',
|
||||
sidebarPath,
|
||||
},
|
||||
}),
|
||||
);
|
||||
const pluginContentDir = path.join(context.generatedFilesDir, plugin.name);
|
||||
|
@ -328,9 +346,12 @@ describe('versioned website', () => {
|
|||
const routeBasePath = 'docs';
|
||||
const plugin = await pluginContentDocs(
|
||||
context,
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
routeBasePath,
|
||||
sidebarPath,
|
||||
validateOptions({
|
||||
validate: normalizePluginOptions,
|
||||
options: {
|
||||
routeBasePath,
|
||||
sidebarPath,
|
||||
},
|
||||
}),
|
||||
);
|
||||
const pluginContentDir = path.join(context.generatedFilesDir, plugin.name);
|
||||
|
@ -455,11 +476,14 @@ describe('versioned website (community)', () => {
|
|||
const pluginId = 'community';
|
||||
const plugin = await pluginContentDocs(
|
||||
context,
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
id: 'community',
|
||||
path: 'community',
|
||||
routeBasePath,
|
||||
sidebarPath,
|
||||
validateOptions({
|
||||
validate: normalizePluginOptions,
|
||||
options: {
|
||||
id: 'community',
|
||||
path: 'community',
|
||||
routeBasePath,
|
||||
sidebarPath,
|
||||
},
|
||||
}),
|
||||
);
|
||||
const pluginContentDir = path.join(context.generatedFilesDir, plugin.name);
|
||||
|
@ -558,9 +582,12 @@ describe('site with doc label', () => {
|
|||
const sidebarPath = path.join(siteDir, 'sidebars.json');
|
||||
const plugin = await pluginContentDocs(
|
||||
context,
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
path: 'docs',
|
||||
sidebarPath,
|
||||
validateOptions({
|
||||
validate: normalizePluginOptions,
|
||||
options: {
|
||||
path: 'docs',
|
||||
sidebarPath,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
|
@ -596,8 +623,11 @@ describe('site with full autogenerated sidebar', () => {
|
|||
const context = await loadContext(siteDir);
|
||||
const plugin = await pluginContentDocs(
|
||||
context,
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
path: 'docs',
|
||||
validateOptions({
|
||||
validate: normalizePluginOptions,
|
||||
options: {
|
||||
path: 'docs',
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
|
@ -648,14 +678,17 @@ describe('site with partial autogenerated sidebars', () => {
|
|||
const context = await loadContext(siteDir, {});
|
||||
const plugin = await pluginContentDocs(
|
||||
context,
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
path: 'docs',
|
||||
sidebarPath: path.join(
|
||||
__dirname,
|
||||
'__fixtures__',
|
||||
'site-with-autogenerated-sidebar',
|
||||
'partialAutogeneratedSidebars.js',
|
||||
),
|
||||
validateOptions({
|
||||
validate: normalizePluginOptions,
|
||||
options: {
|
||||
path: 'docs',
|
||||
sidebarPath: path.join(
|
||||
__dirname,
|
||||
'__fixtures__',
|
||||
'site-with-autogenerated-sidebar',
|
||||
'partialAutogeneratedSidebars.js',
|
||||
),
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
|
@ -701,14 +734,17 @@ describe('site with partial autogenerated sidebars 2 (fix #4638)', () => {
|
|||
const context = await loadContext(siteDir, {});
|
||||
const plugin = await pluginContentDocs(
|
||||
context,
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
path: 'docs',
|
||||
sidebarPath: path.join(
|
||||
__dirname,
|
||||
'__fixtures__',
|
||||
'site-with-autogenerated-sidebar',
|
||||
'partialAutogeneratedSidebars2.js',
|
||||
),
|
||||
validateOptions({
|
||||
validate: normalizePluginOptions,
|
||||
options: {
|
||||
path: 'docs',
|
||||
sidebarPath: path.join(
|
||||
__dirname,
|
||||
'__fixtures__',
|
||||
'site-with-autogenerated-sidebar',
|
||||
'partialAutogeneratedSidebars2.js',
|
||||
),
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
|
@ -735,9 +771,12 @@ describe('site with custom sidebar items generator', () => {
|
|||
const context = await loadContext(siteDir);
|
||||
const plugin = await pluginContentDocs(
|
||||
context,
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
path: 'docs',
|
||||
sidebarItemsGenerator,
|
||||
validateOptions({
|
||||
validate: normalizePluginOptions,
|
||||
options: {
|
||||
path: 'docs',
|
||||
sidebarItemsGenerator,
|
||||
},
|
||||
}),
|
||||
);
|
||||
const content = (await plugin.loadContent?.())!;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {OptionsSchema, DEFAULT_OPTIONS, validateOptions} from '../options';
|
||||
import {validateOptions, DEFAULT_OPTIONS} from '../options';
|
||||
import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
||||
import {DefaultSidebarItemsGenerator} from '../sidebars/generator';
|
||||
import {
|
||||
|
@ -13,27 +13,26 @@ import {
|
|||
DisabledNumberPrefixParser,
|
||||
} from '../numberPrefix';
|
||||
import {GlobExcludeDefault} from '@docusaurus/utils';
|
||||
import type {PluginOptions} from '@docusaurus/plugin-content-docs';
|
||||
import type {Options} from '@docusaurus/plugin-content-docs';
|
||||
|
||||
// the type of remark/rehype plugins is function
|
||||
const markdownPluginsFunctionStub = () => {};
|
||||
const markdownPluginsObjectStub = {};
|
||||
|
||||
function testValidateOptions(options: Partial<PluginOptions>) {
|
||||
return validateOptions({
|
||||
options: {
|
||||
...DEFAULT_OPTIONS,
|
||||
...options,
|
||||
},
|
||||
validate: normalizePluginOptions,
|
||||
});
|
||||
function testValidate(options: Options) {
|
||||
return validateOptions({validate: normalizePluginOptions, options});
|
||||
}
|
||||
|
||||
const defaultOptions = {
|
||||
...DEFAULT_OPTIONS,
|
||||
id: 'default',
|
||||
// The admonitions plugin is automatically added. Not really worth testing
|
||||
remarkPlugins: expect.any(Array),
|
||||
};
|
||||
|
||||
describe('normalizeDocsPluginOptions', () => {
|
||||
it('returns default options for undefined user options', async () => {
|
||||
const {value, error} = await OptionsSchema.validate({});
|
||||
expect(value).toEqual(DEFAULT_OPTIONS);
|
||||
expect(error).toBeUndefined();
|
||||
expect(testValidate({})).toEqual(defaultOptions);
|
||||
});
|
||||
|
||||
it('accepts correctly defined user options', async () => {
|
||||
|
@ -77,14 +76,15 @@ describe('normalizeDocsPluginOptions', () => {
|
|||
sidebarCollapsible: false,
|
||||
sidebarCollapsed: false,
|
||||
};
|
||||
const {value, error} = await OptionsSchema.validate(userOptions);
|
||||
expect(value).toEqual(userOptions);
|
||||
expect(error).toBeUndefined();
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
remarkPlugins: [...userOptions.remarkPlugins, expect.any(Array)],
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts correctly defined remark and rehype plugin options', async () => {
|
||||
const userOptions = {
|
||||
...DEFAULT_OPTIONS,
|
||||
beforeDefaultRemarkPlugins: [],
|
||||
beforeDefaultRehypePlugins: [markdownPluginsFunctionStub],
|
||||
remarkPlugins: [[markdownPluginsFunctionStub, {option1: '42'}]],
|
||||
|
@ -93,85 +93,71 @@ describe('normalizeDocsPluginOptions', () => {
|
|||
[markdownPluginsFunctionStub, {option1: '42'}],
|
||||
],
|
||||
};
|
||||
const {value, error} = await OptionsSchema.validate(userOptions);
|
||||
expect(value).toEqual(userOptions);
|
||||
expect(error).toBeUndefined();
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
remarkPlugins: [...userOptions.remarkPlugins, expect.any(Array)],
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts admonitions false', async () => {
|
||||
const admonitionsFalse = {
|
||||
...DEFAULT_OPTIONS,
|
||||
admonitions: false,
|
||||
};
|
||||
const {value, error} = OptionsSchema.validate(admonitionsFalse);
|
||||
expect(value).toEqual(admonitionsFalse);
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it('accepts numberPrefixParser function', () => {
|
||||
function customNumberPrefixParser() {}
|
||||
expect(
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
...DEFAULT_OPTIONS,
|
||||
numberPrefixParser: customNumberPrefixParser,
|
||||
}),
|
||||
).toEqual({
|
||||
...DEFAULT_OPTIONS,
|
||||
id: 'default',
|
||||
numberPrefixParser: customNumberPrefixParser,
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts numberPrefixParser false', () => {
|
||||
expect(
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
...DEFAULT_OPTIONS,
|
||||
numberPrefixParser: false,
|
||||
}),
|
||||
).toEqual({
|
||||
...DEFAULT_OPTIONS,
|
||||
id: 'default',
|
||||
numberPrefixParser: DisabledNumberPrefixParser,
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts numberPrefixParser true', () => {
|
||||
expect(
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
...DEFAULT_OPTIONS,
|
||||
numberPrefixParser: true,
|
||||
}),
|
||||
).toEqual({
|
||||
...DEFAULT_OPTIONS,
|
||||
id: 'default',
|
||||
numberPrefixParser: DefaultNumberPrefixParser,
|
||||
expect(testValidate(admonitionsFalse)).toEqual({
|
||||
...defaultOptions,
|
||||
...admonitionsFalse,
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects admonitions true', async () => {
|
||||
const admonitionsTrue = {
|
||||
...DEFAULT_OPTIONS,
|
||||
admonitions: true,
|
||||
};
|
||||
const {error} = OptionsSchema.validate(admonitionsTrue);
|
||||
expect(error).toMatchInlineSnapshot(
|
||||
`[ValidationError: "admonitions" contains an invalid value]`,
|
||||
expect(() =>
|
||||
testValidate(admonitionsTrue),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"admonitions\\" contains an invalid value"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('accepts numberPrefixParser function', () => {
|
||||
function customNumberPrefixParser() {}
|
||||
expect(
|
||||
testValidate({numberPrefixParser: customNumberPrefixParser}),
|
||||
).toEqual({
|
||||
...defaultOptions,
|
||||
numberPrefixParser: customNumberPrefixParser,
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts numberPrefixParser false', () => {
|
||||
expect(testValidate({numberPrefixParser: false})).toEqual({
|
||||
...defaultOptions,
|
||||
numberPrefixParser: DisabledNumberPrefixParser,
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts numberPrefixParser true', () => {
|
||||
expect(testValidate({numberPrefixParser: true})).toEqual({
|
||||
...defaultOptions,
|
||||
numberPrefixParser: DefaultNumberPrefixParser,
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects invalid remark plugin options', () => {
|
||||
expect(() => {
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
expect(() =>
|
||||
testValidate({
|
||||
remarkPlugins: [[{option1: '42'}, markdownPluginsFunctionStub]],
|
||||
});
|
||||
}).toThrowErrorMatchingInlineSnapshot(
|
||||
}),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"remarkPlugins[0]\\" does not match any of the allowed types"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects invalid rehype plugin options', () => {
|
||||
expect(() => {
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
expect(() =>
|
||||
testValidate({
|
||||
rehypePlugins: [
|
||||
[
|
||||
markdownPluginsFunctionStub,
|
||||
|
@ -179,61 +165,51 @@ describe('normalizeDocsPluginOptions', () => {
|
|||
markdownPluginsFunctionStub,
|
||||
],
|
||||
],
|
||||
});
|
||||
}).toThrowErrorMatchingInlineSnapshot(
|
||||
}),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"rehypePlugins[0]\\" does not match any of the allowed types"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects bad path inputs', () => {
|
||||
expect(() => {
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
path: 2,
|
||||
});
|
||||
}).toThrowErrorMatchingInlineSnapshot(`"\\"path\\" must be a string"`);
|
||||
expect(() => testValidate({path: 2})).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"path\\" must be a string"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects bad include inputs', () => {
|
||||
expect(() => {
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
include: '**/*.{md,mdx}',
|
||||
});
|
||||
}).toThrowErrorMatchingInlineSnapshot(`"\\"include\\" must be an array"`);
|
||||
expect(() =>
|
||||
testValidate({include: '**/*.{md,mdx}'}),
|
||||
).toThrowErrorMatchingInlineSnapshot(`"\\"include\\" must be an array"`);
|
||||
});
|
||||
|
||||
it('rejects bad showLastUpdateTime inputs', () => {
|
||||
expect(() => {
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
showLastUpdateTime: 'true',
|
||||
});
|
||||
}).toThrowErrorMatchingInlineSnapshot(
|
||||
expect(() =>
|
||||
testValidate({showLastUpdateTime: 'true'}),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"showLastUpdateTime\\" must be a boolean"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects bad remarkPlugins input', () => {
|
||||
expect(() => {
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
remarkPlugins: 'remark-math',
|
||||
});
|
||||
}).toThrowErrorMatchingInlineSnapshot(
|
||||
expect(() =>
|
||||
testValidate({remarkPlugins: 'remark-math'}),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"remarkPlugins\\" must be an array"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects bad lastVersion', () => {
|
||||
expect(() => {
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
lastVersion: false,
|
||||
});
|
||||
}).toThrowErrorMatchingInlineSnapshot(
|
||||
expect(() =>
|
||||
testValidate({lastVersion: false}),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"lastVersion\\" must be a string"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects bad versions', () => {
|
||||
expect(() => {
|
||||
normalizePluginOptions(OptionsSchema, {
|
||||
expect(() =>
|
||||
testValidate({
|
||||
versions: {
|
||||
current: {
|
||||
hey: 3,
|
||||
|
@ -243,32 +219,29 @@ describe('normalizeDocsPluginOptions', () => {
|
|||
label: 'world',
|
||||
},
|
||||
},
|
||||
});
|
||||
}).toThrowErrorMatchingInlineSnapshot(
|
||||
}),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"versions.current.hey\\" is not allowed"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('handles sidebarCollapsed option inconsistencies', () => {
|
||||
expect(
|
||||
testValidateOptions({
|
||||
...DEFAULT_OPTIONS,
|
||||
testValidate({
|
||||
sidebarCollapsible: true,
|
||||
sidebarCollapsed: undefined,
|
||||
}).sidebarCollapsed,
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
testValidateOptions({
|
||||
...DEFAULT_OPTIONS,
|
||||
testValidate({
|
||||
sidebarCollapsible: false,
|
||||
sidebarCollapsed: undefined,
|
||||
}).sidebarCollapsed,
|
||||
).toBe(false);
|
||||
|
||||
expect(
|
||||
testValidateOptions({
|
||||
...DEFAULT_OPTIONS,
|
||||
testValidate({
|
||||
sidebarCollapsible: false,
|
||||
sidebarCollapsed: true,
|
||||
}).sidebarCollapsed,
|
||||
|
|
|
@ -34,7 +34,7 @@ import getSlug from './slug';
|
|||
import {CURRENT_VERSION_NAME} from './constants';
|
||||
import {getDocsDirPaths} from './versions';
|
||||
import {stripPathNumberPrefixes} from './numberPrefix';
|
||||
import {validateDocFrontMatter} from './docFrontMatter';
|
||||
import {validateDocFrontMatter} from './frontMatter';
|
||||
import type {SidebarsUtils} from './sidebars/utils';
|
||||
import {toDocNavigationLink, toNavigationLink} from './sidebars/utils';
|
||||
import type {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type {PluginOptions} from '@docusaurus/plugin-content-docs';
|
||||
import type {PluginOptions, Options} from '@docusaurus/plugin-content-docs';
|
||||
import {
|
||||
Joi,
|
||||
RemarkPluginsSchema,
|
||||
|
@ -15,10 +15,7 @@ import {
|
|||
} from '@docusaurus/utils-validation';
|
||||
import {GlobExcludeDefault} from '@docusaurus/utils';
|
||||
|
||||
import type {
|
||||
OptionValidationContext,
|
||||
ValidationResult,
|
||||
} from '@docusaurus/types';
|
||||
import type {OptionValidationContext} from '@docusaurus/types';
|
||||
import logger from '@docusaurus/logger';
|
||||
import admonitions from 'remark-admonitions';
|
||||
import {DefaultSidebarItemsGenerator} from './sidebars/generator';
|
||||
|
@ -70,7 +67,7 @@ const VersionsOptionsSchema = Joi.object()
|
|||
.pattern(Joi.string().required(), VersionOptionsSchema)
|
||||
.default(DEFAULT_OPTIONS.versions);
|
||||
|
||||
export const OptionsSchema = Joi.object({
|
||||
const OptionsSchema = Joi.object<PluginOptions>({
|
||||
path: Joi.string().default(DEFAULT_OPTIONS.path),
|
||||
editUrl: Joi.alternatives().try(URISchema, Joi.function()),
|
||||
editCurrentVersion: Joi.boolean().default(DEFAULT_OPTIONS.editCurrentVersion),
|
||||
|
@ -80,6 +77,7 @@ export const OptionsSchema = Joi.object({
|
|||
// .allow('') ""
|
||||
.default(DEFAULT_OPTIONS.routeBasePath),
|
||||
tagsBasePath: Joi.string().default(DEFAULT_OPTIONS.tagsBasePath),
|
||||
// @ts-expect-error: deprecated
|
||||
homePageId: Joi.any().forbidden().messages({
|
||||
'any.unknown':
|
||||
'The docs plugin option homePageId is not supported anymore. To make a doc the "home", please add "slug: /" in its front matter. See: https://docusaurus.io/docs/next/docs-introduction#home-page-docs',
|
||||
|
@ -146,7 +144,7 @@ export const OptionsSchema = Joi.object({
|
|||
export function validateOptions({
|
||||
validate,
|
||||
options: userOptions,
|
||||
}: OptionValidationContext<PluginOptions>): ValidationResult<PluginOptions> {
|
||||
}: OptionValidationContext<Options, PluginOptions>): PluginOptions {
|
||||
let options = userOptions;
|
||||
|
||||
if (options.sidebarCollapsible === false) {
|
||||
|
@ -168,7 +166,7 @@ export function validateOptions({
|
|||
}
|
||||
}
|
||||
|
||||
const normalizedOptions = validate(OptionsSchema, options);
|
||||
const normalizedOptions = validate(OptionsSchema, options) as PluginOptions;
|
||||
|
||||
if (normalizedOptions.admonitions) {
|
||||
normalizedOptions.remarkPlugins = normalizedOptions.remarkPlugins.concat([
|
||||
|
|
|
@ -9,7 +9,8 @@ import path from 'path';
|
|||
import {loadContext} from '@docusaurus/core/lib/server';
|
||||
|
||||
import pluginContentPages from '../index';
|
||||
import {PluginOptionSchema} from '../pluginOptionSchema';
|
||||
import {validateOptions} from '../options';
|
||||
import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
||||
|
||||
describe('docusaurus-plugin-content-pages', () => {
|
||||
it('loads simple pages', async () => {
|
||||
|
@ -17,9 +18,12 @@ describe('docusaurus-plugin-content-pages', () => {
|
|||
const context = await loadContext(siteDir);
|
||||
const plugin = await pluginContentPages(
|
||||
context,
|
||||
PluginOptionSchema.validate({
|
||||
path: 'src/pages',
|
||||
}).value,
|
||||
validateOptions({
|
||||
validate: normalizePluginOptions,
|
||||
options: {
|
||||
path: 'src/pages',
|
||||
},
|
||||
}),
|
||||
);
|
||||
const pagesMetadata = await plugin.loadContent!();
|
||||
|
||||
|
@ -37,9 +41,12 @@ describe('docusaurus-plugin-content-pages', () => {
|
|||
currentLocale: 'fr',
|
||||
},
|
||||
},
|
||||
PluginOptionSchema.validate({
|
||||
path: 'src/pages',
|
||||
}).value,
|
||||
validateOptions({
|
||||
validate: normalizePluginOptions,
|
||||
options: {
|
||||
path: 'src/pages',
|
||||
},
|
||||
}),
|
||||
);
|
||||
const pagesMetadata = await plugin.loadContent!();
|
||||
|
||||
|
|
|
@ -5,31 +5,29 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {PluginOptionSchema, DEFAULT_OPTIONS} from '../pluginOptionSchema';
|
||||
import type {PluginOptions} from '@docusaurus/plugin-content-pages';
|
||||
import {validateOptions, DEFAULT_OPTIONS} from '../options';
|
||||
import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
||||
import type {Options} from '@docusaurus/plugin-content-pages';
|
||||
|
||||
function normalizePluginOptions(
|
||||
options: Partial<PluginOptions>,
|
||||
): PluginOptions {
|
||||
const {value, error} = PluginOptionSchema.validate(options, {
|
||||
convert: false,
|
||||
});
|
||||
if (error) {
|
||||
throw error;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
function testValidate(options: Options) {
|
||||
return validateOptions({validate: normalizePluginOptions, options});
|
||||
}
|
||||
|
||||
const defaultOptions = {
|
||||
...DEFAULT_OPTIONS,
|
||||
id: 'default',
|
||||
};
|
||||
|
||||
describe('normalizePagesPluginOptions', () => {
|
||||
it('returns default options for undefined user options', () => {
|
||||
const value = normalizePluginOptions({});
|
||||
expect(value).toEqual(DEFAULT_OPTIONS);
|
||||
expect(testValidate({})).toEqual(defaultOptions);
|
||||
});
|
||||
|
||||
it('fills in default options for partially defined user options', () => {
|
||||
const value = normalizePluginOptions({path: 'src/pages'});
|
||||
expect(value).toEqual(DEFAULT_OPTIONS);
|
||||
expect(testValidate({path: 'src/foo'})).toEqual({
|
||||
...defaultOptions,
|
||||
path: 'src/foo',
|
||||
});
|
||||
});
|
||||
|
||||
it('accepts correctly defined user options', () => {
|
||||
|
@ -39,13 +37,15 @@ describe('normalizePagesPluginOptions', () => {
|
|||
include: ['**/*.{js,jsx,ts,tsx}'],
|
||||
exclude: ['**/$*/'],
|
||||
};
|
||||
const value = normalizePluginOptions(userOptions);
|
||||
expect(value).toEqual({...DEFAULT_OPTIONS, ...userOptions});
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects bad path inputs', () => {
|
||||
expect(() => {
|
||||
normalizePluginOptions({
|
||||
testValidate({
|
||||
// @ts-expect-error: bad attribute
|
||||
path: 42,
|
||||
});
|
|
@ -21,15 +21,9 @@ import {
|
|||
DEFAULT_PLUGIN_ID,
|
||||
parseMarkdownString,
|
||||
} from '@docusaurus/utils';
|
||||
import type {
|
||||
LoadContext,
|
||||
Plugin,
|
||||
OptionValidationContext,
|
||||
ValidationResult,
|
||||
} from '@docusaurus/types';
|
||||
import type {LoadContext, Plugin} from '@docusaurus/types';
|
||||
import admonitions from 'remark-admonitions';
|
||||
import {PluginOptionSchema} from './pluginOptionSchema';
|
||||
import {validatePageFrontMatter} from './pageFrontMatter';
|
||||
import {validatePageFrontMatter} from './frontMatter';
|
||||
|
||||
import type {LoadedContent, PagesContentPaths} from './types';
|
||||
import type {PluginOptions, Metadata} from '@docusaurus/plugin-content-pages';
|
||||
|
@ -176,7 +170,7 @@ export default async function pluginContentPages(
|
|||
);
|
||||
},
|
||||
|
||||
configureWebpack(_config, isServer, {getJSLoader}) {
|
||||
configureWebpack(config, isServer, {getJSLoader}) {
|
||||
const {
|
||||
rehypePlugins,
|
||||
remarkPlugins,
|
||||
|
@ -241,10 +235,4 @@ export default async function pluginContentPages(
|
|||
};
|
||||
}
|
||||
|
||||
export function validateOptions({
|
||||
validate,
|
||||
options,
|
||||
}: OptionValidationContext<PluginOptions>): ValidationResult<PluginOptions> {
|
||||
const validatedOptions = validate(PluginOptionSchema, options);
|
||||
return validatedOptions;
|
||||
}
|
||||
export {validateOptions} from './options';
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type {PluginOptions} from '@docusaurus/plugin-content-pages';
|
||||
import type {PluginOptions, Options} from '@docusaurus/plugin-content-pages';
|
||||
import {
|
||||
Joi,
|
||||
RemarkPluginsSchema,
|
||||
|
@ -13,6 +13,7 @@ import {
|
|||
AdmonitionsSchema,
|
||||
} from '@docusaurus/utils-validation';
|
||||
import {GlobExcludeDefault} from '@docusaurus/utils';
|
||||
import type {OptionValidationContext} from '@docusaurus/types';
|
||||
|
||||
export const DEFAULT_OPTIONS: PluginOptions = {
|
||||
path: 'src/pages', // Path to data on filesystem, relative to site dir.
|
||||
|
@ -27,7 +28,7 @@ export const DEFAULT_OPTIONS: PluginOptions = {
|
|||
admonitions: {},
|
||||
};
|
||||
|
||||
export const PluginOptionSchema = Joi.object({
|
||||
const PluginOptionSchema = Joi.object({
|
||||
path: Joi.string().default(DEFAULT_OPTIONS.path),
|
||||
routeBasePath: Joi.string().default(DEFAULT_OPTIONS.routeBasePath),
|
||||
include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include),
|
||||
|
@ -43,3 +44,11 @@ export const PluginOptionSchema = Joi.object({
|
|||
),
|
||||
admonitions: AdmonitionsSchema.default(DEFAULT_OPTIONS.admonitions),
|
||||
});
|
||||
|
||||
export function validateOptions({
|
||||
validate,
|
||||
options,
|
||||
}: OptionValidationContext<Options, PluginOptions>): PluginOptions {
|
||||
const validatedOptions = validate(PluginOptionSchema, options);
|
||||
return validatedOptions;
|
||||
}
|
|
@ -10,11 +10,10 @@ import type {
|
|||
LoadContext,
|
||||
Plugin,
|
||||
OptionValidationContext,
|
||||
ValidationResult,
|
||||
ThemeConfig,
|
||||
ThemeConfigValidationContext,
|
||||
} from '@docusaurus/types';
|
||||
import type {PluginOptions} from '@docusaurus/plugin-google-analytics';
|
||||
import type {PluginOptions, Options} from '@docusaurus/plugin-google-analytics';
|
||||
|
||||
export default function pluginGoogleAnalytics(
|
||||
context: LoadContext,
|
||||
|
@ -74,13 +73,13 @@ const pluginOptionsSchema = Joi.object<PluginOptions>({
|
|||
export function validateOptions({
|
||||
validate,
|
||||
options,
|
||||
}: OptionValidationContext<PluginOptions>): ValidationResult<PluginOptions> {
|
||||
}: OptionValidationContext<Options, PluginOptions>): PluginOptions {
|
||||
return validate(pluginOptionsSchema, options);
|
||||
}
|
||||
|
||||
export function validateThemeConfig({
|
||||
themeConfig,
|
||||
}: ThemeConfigValidationContext<ThemeConfig>): ValidationResult<ThemeConfig> {
|
||||
}: ThemeConfigValidationContext<ThemeConfig>): ThemeConfig {
|
||||
if ('googleAnalytics' in themeConfig) {
|
||||
throw new Error(
|
||||
'The "googleAnalytics" field in themeConfig should now be specified as option for plugin-google-analytics. More information at https://github.com/facebook/docusaurus/pull/5832.',
|
||||
|
|
|
@ -10,11 +10,10 @@ import type {
|
|||
LoadContext,
|
||||
Plugin,
|
||||
OptionValidationContext,
|
||||
ValidationResult,
|
||||
ThemeConfig,
|
||||
ThemeConfigValidationContext,
|
||||
} from '@docusaurus/types';
|
||||
import type {PluginOptions} from '@docusaurus/plugin-google-gtag';
|
||||
import type {PluginOptions, Options} from '@docusaurus/plugin-google-gtag';
|
||||
|
||||
export default function pluginGoogleGtag(
|
||||
context: LoadContext,
|
||||
|
@ -88,13 +87,13 @@ const pluginOptionsSchema = Joi.object<PluginOptions>({
|
|||
export function validateOptions({
|
||||
validate,
|
||||
options,
|
||||
}: OptionValidationContext<PluginOptions>): ValidationResult<PluginOptions> {
|
||||
}: OptionValidationContext<Options, PluginOptions>): PluginOptions {
|
||||
return validate(pluginOptionsSchema, options);
|
||||
}
|
||||
|
||||
export function validateThemeConfig({
|
||||
themeConfig,
|
||||
}: ThemeConfigValidationContext<ThemeConfig>): ValidationResult<ThemeConfig> {
|
||||
}: ThemeConfigValidationContext<ThemeConfig>): ThemeConfig {
|
||||
if ('gtag' in themeConfig) {
|
||||
throw new Error(
|
||||
'The "gtag" field in themeConfig should now be specified as option for plugin-google-gtag. More information at https://github.com/facebook/docusaurus/pull/5832.',
|
||||
|
|
|
@ -9,7 +9,6 @@ import type {
|
|||
LoadContext,
|
||||
Plugin,
|
||||
OptionValidationContext,
|
||||
ValidationResult,
|
||||
} from '@docusaurus/types';
|
||||
import type {PluginOptions} from '@docusaurus/plugin-ideal-image';
|
||||
import {Joi} from '@docusaurus/utils-validation';
|
||||
|
@ -79,7 +78,7 @@ export default function pluginIdealImage(
|
|||
export function validateOptions({
|
||||
validate,
|
||||
options,
|
||||
}: OptionValidationContext<PluginOptions>): ValidationResult<PluginOptions> {
|
||||
}: OptionValidationContext<PluginOptions, PluginOptions>): PluginOptions {
|
||||
const pluginOptionsSchema = Joi.object({
|
||||
disableInDev: Joi.boolean().default(true),
|
||||
}).unknown();
|
||||
|
|
|
@ -6,11 +6,7 @@
|
|||
*/
|
||||
|
||||
import {Joi} from '@docusaurus/utils-validation';
|
||||
import type {
|
||||
ThemeConfig,
|
||||
ValidationResult,
|
||||
OptionValidationContext,
|
||||
} from '@docusaurus/types';
|
||||
import type {OptionValidationContext} from '@docusaurus/types';
|
||||
import type {PluginOptions} from '@docusaurus/plugin-pwa';
|
||||
|
||||
const DEFAULT_OPTIONS = {
|
||||
|
@ -27,7 +23,7 @@ const DEFAULT_OPTIONS = {
|
|||
reloadPopup: '@theme/PwaReloadPopup',
|
||||
};
|
||||
|
||||
export const Schema = Joi.object({
|
||||
const Schema = Joi.object({
|
||||
debug: Joi.bool().default(DEFAULT_OPTIONS.debug),
|
||||
offlineModeActivationStrategies: Joi.array()
|
||||
.items(
|
||||
|
@ -61,6 +57,6 @@ export const Schema = Joi.object({
|
|||
export function validateOptions({
|
||||
validate,
|
||||
options,
|
||||
}: OptionValidationContext<PluginOptions>): ValidationResult<ThemeConfig> {
|
||||
}: OptionValidationContext<PluginOptions, PluginOptions>): PluginOptions {
|
||||
return validate(Schema, options);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {validateOptions, DEFAULT_OPTIONS} from '../options';
|
||||
import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
||||
import type {Options} from '@docusaurus/plugin-sitemap';
|
||||
|
||||
function testValidate(options: Options) {
|
||||
return validateOptions({validate: normalizePluginOptions, options});
|
||||
}
|
||||
|
||||
const defaultOptions = {
|
||||
...DEFAULT_OPTIONS,
|
||||
id: 'default',
|
||||
};
|
||||
|
||||
describe('validateOptions', () => {
|
||||
it('returns default values for empty user options', () => {
|
||||
expect(testValidate({})).toEqual(defaultOptions);
|
||||
});
|
||||
|
||||
it('accepts correctly defined user options', () => {
|
||||
const userOptions = {
|
||||
changefreq: 'yearly',
|
||||
priority: 0.9,
|
||||
};
|
||||
expect(testValidate(userOptions)).toEqual({
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects out-of-range priority inputs', () => {
|
||||
expect(() =>
|
||||
testValidate({priority: 2}),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"priority\\" must be less than or equal to 1"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects bad changefreq inputs', () => {
|
||||
expect(() =>
|
||||
testValidate({changefreq: 'annually'}),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"changefreq\\" must be one of [daily, monthly, always, hourly, weekly, yearly, never]"`,
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1,55 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {PluginOptionSchema, DEFAULT_OPTIONS} from '../pluginOptionSchema';
|
||||
|
||||
function normalizePluginOptions(options) {
|
||||
const {value, error} = PluginOptionSchema.validate(options, {
|
||||
convert: false,
|
||||
});
|
||||
if (error) {
|
||||
throw error;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
describe('normalizeSitemapPluginOptions', () => {
|
||||
it('returns default values for empty user options', () => {
|
||||
const {value} = PluginOptionSchema.validate({});
|
||||
expect(value).toEqual(DEFAULT_OPTIONS);
|
||||
});
|
||||
|
||||
it('accepts correctly defined user options', () => {
|
||||
const userOptions = {
|
||||
changefreq: 'yearly',
|
||||
priority: 0.9,
|
||||
};
|
||||
const {value} = PluginOptionSchema.validate(userOptions);
|
||||
expect(value).toEqual(userOptions);
|
||||
});
|
||||
|
||||
it('rejects out-of-range priority inputs', () => {
|
||||
expect(() => {
|
||||
normalizePluginOptions({
|
||||
priority: 2,
|
||||
});
|
||||
}).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"priority\\" must be less than or equal to 1"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects bad changefreq inputs', () => {
|
||||
expect(() => {
|
||||
normalizePluginOptions({
|
||||
changefreq: 'annually',
|
||||
});
|
||||
}).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"changefreq\\" must be one of [daily, monthly, always, hourly, weekly, yearly, never]"`,
|
||||
);
|
||||
});
|
||||
});
|
|
@ -9,14 +9,7 @@ import fs from 'fs-extra';
|
|||
import path from 'path';
|
||||
import type {Options} from '@docusaurus/plugin-sitemap';
|
||||
import createSitemap from './createSitemap';
|
||||
import type {
|
||||
LoadContext,
|
||||
Props,
|
||||
OptionValidationContext,
|
||||
ValidationResult,
|
||||
Plugin,
|
||||
} from '@docusaurus/types';
|
||||
import {PluginOptionSchema} from './pluginOptionSchema';
|
||||
import type {LoadContext, Plugin} from '@docusaurus/types';
|
||||
|
||||
export default function pluginSitemap(
|
||||
context: LoadContext,
|
||||
|
@ -25,7 +18,7 @@ export default function pluginSitemap(
|
|||
return {
|
||||
name: 'docusaurus-plugin-sitemap',
|
||||
|
||||
async postBuild({siteConfig, routesPaths, outDir}: Props) {
|
||||
async postBuild({siteConfig, routesPaths, outDir}) {
|
||||
if (siteConfig.noIndex) {
|
||||
return;
|
||||
}
|
||||
|
@ -47,10 +40,4 @@ export default function pluginSitemap(
|
|||
};
|
||||
}
|
||||
|
||||
export function validateOptions({
|
||||
validate,
|
||||
options,
|
||||
}: OptionValidationContext<Options>): ValidationResult<Options> {
|
||||
const validatedOptions = validate(PluginOptionSchema, options);
|
||||
return validatedOptions;
|
||||
}
|
||||
export {validateOptions} from './options';
|
||||
|
|
|
@ -8,13 +8,14 @@
|
|||
import {Joi} from '@docusaurus/utils-validation';
|
||||
import {EnumChangefreq} from 'sitemap';
|
||||
import type {Options} from '@docusaurus/plugin-sitemap';
|
||||
import type {OptionValidationContext} from '@docusaurus/types';
|
||||
|
||||
export const DEFAULT_OPTIONS: Required<Options> = {
|
||||
export const DEFAULT_OPTIONS: Options = {
|
||||
changefreq: EnumChangefreq.WEEKLY,
|
||||
priority: 0.5,
|
||||
};
|
||||
|
||||
export const PluginOptionSchema = Joi.object({
|
||||
const PluginOptionSchema = Joi.object({
|
||||
cacheTime: Joi.forbidden().messages({
|
||||
'any.unknown':
|
||||
'Option `cacheTime` in sitemap config is deprecated. Please remove it.',
|
||||
|
@ -28,3 +29,11 @@ export const PluginOptionSchema = Joi.object({
|
|||
'Please use the new Docusaurus global trailingSlash config instead, and the sitemaps plugin will use it.',
|
||||
}),
|
||||
});
|
||||
|
||||
export function validateOptions({
|
||||
validate,
|
||||
options,
|
||||
}: OptionValidationContext<Options, Options>): Options {
|
||||
const validatedOptions = validate(PluginOptionSchema, options);
|
||||
return validatedOptions;
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
import type {EnumChangefreq} from 'sitemap';
|
||||
|
||||
export type Options = {
|
||||
id?: string;
|
||||
/** @see https://www.sitemaps.org/protocol.html#xmlTagDefinitions */
|
||||
changefreq?: EnumChangefreq;
|
||||
/** @see https://www.sitemaps.org/protocol.html#xmlTagDefinitions */
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import type {LoadContext, Plugin, PostCssOptions} from '@docusaurus/types';
|
||||
import type {LoadContext, Plugin} from '@docusaurus/types';
|
||||
import type {ThemeConfig} from '@docusaurus/theme-common';
|
||||
import {getTranslationFiles, translateThemeConfig} from './translations';
|
||||
import {createRequire} from 'module';
|
||||
|
@ -169,7 +169,7 @@ export default function docusaurusThemeClassic(
|
|||
};
|
||||
},
|
||||
|
||||
configurePostCss(postCssOptions: PostCssOptions) {
|
||||
configurePostCss(postCssOptions) {
|
||||
if (direction === 'rtl') {
|
||||
const resolvedInfimaFile = require.resolve(getInfimaCSSFile(direction));
|
||||
const plugin: PostCssPlugin = {
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
*/
|
||||
|
||||
import {Joi, URISchema} from '@docusaurus/utils-validation';
|
||||
import type {ThemeConfig, Validate, ValidationResult} from '@docusaurus/types';
|
||||
import type {
|
||||
ThemeConfig,
|
||||
ThemeConfigValidationContext,
|
||||
} from '@docusaurus/types';
|
||||
|
||||
const DEFAULT_DOCS_CONFIG = {
|
||||
versionPersistence: 'localStorage',
|
||||
|
@ -373,9 +376,6 @@ export const ThemeConfigSchema = Joi.object({
|
|||
export function validateThemeConfig({
|
||||
validate,
|
||||
themeConfig,
|
||||
}: {
|
||||
validate: Validate<ThemeConfig>;
|
||||
themeConfig: ThemeConfig;
|
||||
}): ValidationResult<ThemeConfig> {
|
||||
}: ThemeConfigValidationContext<ThemeConfig>): ThemeConfig {
|
||||
return validate(ThemeConfigSchema, themeConfig);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
*/
|
||||
|
||||
import {Joi} from '@docusaurus/utils-validation';
|
||||
import type {ThemeConfig, Validate, ValidationResult} from '@docusaurus/types';
|
||||
import type {
|
||||
ThemeConfig,
|
||||
ThemeConfigValidationContext,
|
||||
} from '@docusaurus/types';
|
||||
|
||||
export const DEFAULT_CONFIG = {
|
||||
playgroundPosition: 'bottom',
|
||||
|
@ -25,9 +28,6 @@ export const Schema = Joi.object({
|
|||
export function validateThemeConfig({
|
||||
validate,
|
||||
themeConfig,
|
||||
}: {
|
||||
validate: Validate<ThemeConfig>;
|
||||
themeConfig: ThemeConfig;
|
||||
}): ValidationResult<ThemeConfig> {
|
||||
}: ThemeConfigValidationContext<ThemeConfig>): ThemeConfig {
|
||||
return validate(Schema, themeConfig);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
*/
|
||||
|
||||
import {Joi} from '@docusaurus/utils-validation';
|
||||
import type {ThemeConfig, Validate, ValidationResult} from '@docusaurus/types';
|
||||
import type {
|
||||
ThemeConfig,
|
||||
ThemeConfigValidationContext,
|
||||
} from '@docusaurus/types';
|
||||
|
||||
export const DEFAULT_CONFIG = {
|
||||
// enabled by default, as it makes sense in most cases
|
||||
|
@ -45,9 +48,6 @@ export const Schema = Joi.object({
|
|||
export function validateThemeConfig({
|
||||
validate,
|
||||
themeConfig,
|
||||
}: {
|
||||
validate: Validate<ThemeConfig>;
|
||||
themeConfig: ThemeConfig;
|
||||
}): ValidationResult<ThemeConfig> {
|
||||
}: ThemeConfigValidationContext<ThemeConfig>): ThemeConfig {
|
||||
return validate(Schema, themeConfig);
|
||||
}
|
||||
|
|
22
packages/docusaurus-types/src/index.d.ts
vendored
22
packages/docusaurus-types/src/index.d.ts
vendored
|
@ -320,7 +320,7 @@ export type PluginModule = {
|
|||
<Options, Content>(context: LoadContext, options: Options):
|
||||
| Plugin<Content>
|
||||
| Promise<Plugin<Content>>;
|
||||
validateOptions?: <T>(data: OptionValidationContext<T>) => T;
|
||||
validateOptions?: <T, U>(data: OptionValidationContext<T, U>) => U;
|
||||
validateThemeConfig?: <T>(data: ThemeConfigValidationContext<T>) => T;
|
||||
|
||||
getSwizzleComponentList?: () => string[] | undefined; // TODO deprecate this one later
|
||||
|
@ -436,22 +436,20 @@ interface HtmlTagObject {
|
|||
innerHTML?: string;
|
||||
}
|
||||
|
||||
export type ValidationResult<T> = T;
|
||||
|
||||
export type ValidationSchema<T> = Joi.ObjectSchema<T>;
|
||||
|
||||
export type Validate<T> = (
|
||||
validationSchema: ValidationSchema<T>,
|
||||
options: Partial<T>,
|
||||
) => ValidationResult<T>;
|
||||
export type Validate<T, U> = (
|
||||
validationSchema: ValidationSchema<U>,
|
||||
options: T,
|
||||
) => U;
|
||||
|
||||
export interface OptionValidationContext<T> {
|
||||
validate: Validate<T>;
|
||||
options: Partial<T>;
|
||||
}
|
||||
export type OptionValidationContext<T, U> = {
|
||||
validate: Validate<T, U>;
|
||||
options: T;
|
||||
};
|
||||
|
||||
export interface ThemeConfigValidationContext<T> {
|
||||
validate: Validate<T>;
|
||||
validate: Validate<T, T>;
|
||||
themeConfig: Partial<T>;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,9 @@ describe('normalizePluginOptions', () => {
|
|||
options,
|
||||
),
|
||||
).toEqual(options);
|
||||
expect(
|
||||
normalizePluginOptions(Joi.object({foo: Joi.string()}), undefined),
|
||||
).toEqual({id: 'default'});
|
||||
});
|
||||
|
||||
it('normalizes plugin options', () => {
|
||||
|
|
|
@ -20,7 +20,8 @@ export function printWarning(warning?: Joi.ValidationError): void {
|
|||
|
||||
export function normalizePluginOptions<T extends {id?: string}>(
|
||||
schema: Joi.ObjectSchema<T>,
|
||||
options: Partial<T>,
|
||||
// This allows us to automatically normalize undefined to {id: 'default'}
|
||||
options: Partial<T> = {},
|
||||
): T {
|
||||
// All plugins can be provided an "id" option (multi-instance support)
|
||||
// we add schema validation automatically
|
||||
|
|
|
@ -179,7 +179,7 @@ export default async function initPlugins({
|
|||
|
||||
function doValidatePluginOptions(
|
||||
normalizedPluginConfig: NormalizedPluginConfig,
|
||||
) {
|
||||
): Required<PluginOptions> {
|
||||
const validateOptions = getOptionValidationFunction(normalizedPluginConfig);
|
||||
if (validateOptions) {
|
||||
return validateOptions({
|
||||
|
|
Loading…
Add table
Reference in a new issue