mirror of
https://github.com/facebook/docusaurus.git
synced 2025-08-01 16:00:29 +02:00
refactor(client-redirects): migrate validation to validateOptions lifecycle (#6924)
This commit is contained in:
parent
68aaf9201f
commit
da9f38b748
8 changed files with 86 additions and 113 deletions
|
@ -154,7 +154,9 @@ declare module '@docusaurus/Link' {
|
|||
readonly href?: string;
|
||||
readonly autoAddBaseUrl?: boolean;
|
||||
|
||||
// escape hatch in case broken links check is annoying for a specific link
|
||||
/**
|
||||
* escape hatch in case broken links check is annoying for a specific link
|
||||
*/
|
||||
readonly 'data-noBrokenLinkCheck'?: boolean;
|
||||
};
|
||||
export default function Link(props: Props): JSX.Element;
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`normalizePluginOptions rejects bad createRedirects user inputs 1`] = `
|
||||
"Invalid @docusaurus/plugin-client-redirects options: \\"createRedirects\\" must be of type function
|
||||
{
|
||||
\\"createRedirects\\": [
|
||||
\\"bad\\",
|
||||
\\"value\\"
|
||||
]
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`normalizePluginOptions rejects bad fromExtensions user inputs 1`] = `
|
||||
"Invalid @docusaurus/plugin-client-redirects options: \\"fromExtensions[0]\\" contains an invalid value
|
||||
{
|
||||
\\"fromExtensions\\": [
|
||||
null,
|
||||
null,
|
||||
123,
|
||||
true
|
||||
]
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`normalizePluginOptions rejects bad toExtensions user inputs 1`] = `
|
||||
"Invalid @docusaurus/plugin-client-redirects options: \\"toExtensions[0]\\" contains an invalid value
|
||||
{
|
||||
\\"toExtensions\\": [
|
||||
null,
|
||||
null,
|
||||
123,
|
||||
true
|
||||
]
|
||||
}"
|
||||
`;
|
|
@ -0,0 +1,7 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`normalizePluginOptions rejects bad createRedirects user inputs 1`] = `"\\"createRedirects\\" must be of type function"`;
|
||||
|
||||
exports[`normalizePluginOptions rejects bad fromExtensions user inputs 1`] = `"\\"fromExtensions[0]\\" contains an invalid value"`;
|
||||
|
||||
exports[`normalizePluginOptions rejects bad toExtensions user inputs 1`] = `"\\"toExtensions[0]\\" contains an invalid value"`;
|
|
@ -7,8 +7,9 @@
|
|||
|
||||
import type {PluginContext} from '../types';
|
||||
import collectRedirects from '../collectRedirects';
|
||||
import normalizePluginOptions from '../normalizePluginOptions';
|
||||
import {validateOptions} from '../options';
|
||||
import {removeTrailingSlash} from '@docusaurus/utils';
|
||||
import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
||||
import type {Options} from '@docusaurus/plugin-client-redirects';
|
||||
|
||||
function createTestPluginContext(
|
||||
|
@ -19,7 +20,7 @@ function createTestPluginContext(
|
|||
outDir: '/tmp',
|
||||
baseUrl: 'https://docusaurus.io',
|
||||
relativeRoutesPaths,
|
||||
options: normalizePluginOptions(options),
|
||||
options: validateOptions({validate: normalizePluginOptions, options}),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -5,32 +5,38 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import normalizePluginOptions, {
|
||||
DefaultPluginOptions,
|
||||
} from '../normalizePluginOptions';
|
||||
import type {CreateRedirectsFnOption} from '@docusaurus/plugin-client-redirects';
|
||||
import {validateOptions, DEFAULT_OPTIONS} from '../options';
|
||||
import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
||||
import type {
|
||||
CreateRedirectsFnOption,
|
||||
Options,
|
||||
} from '@docusaurus/plugin-client-redirects';
|
||||
|
||||
function testValidate(options: Options) {
|
||||
return validateOptions({validate: normalizePluginOptions, options});
|
||||
}
|
||||
|
||||
describe('normalizePluginOptions', () => {
|
||||
it('returns default options for undefined user options', () => {
|
||||
expect(normalizePluginOptions()).toEqual(DefaultPluginOptions);
|
||||
expect(testValidate(undefined)).toEqual(DEFAULT_OPTIONS);
|
||||
});
|
||||
|
||||
it('returns default options for empty user options', () => {
|
||||
expect(normalizePluginOptions()).toEqual(DefaultPluginOptions);
|
||||
expect(testValidate(undefined)).toEqual(DEFAULT_OPTIONS);
|
||||
});
|
||||
|
||||
it('overrides one default options with valid user options', () => {
|
||||
expect(
|
||||
normalizePluginOptions({
|
||||
testValidate({
|
||||
toExtensions: ['html'],
|
||||
}),
|
||||
).toEqual({...DefaultPluginOptions, toExtensions: ['html']});
|
||||
).toEqual({...DEFAULT_OPTIONS, id: 'default', toExtensions: ['html']});
|
||||
});
|
||||
|
||||
it('overrides all default options with valid user options', () => {
|
||||
const createRedirects: CreateRedirectsFnOption = (_routePath: string) => [];
|
||||
expect(
|
||||
normalizePluginOptions({
|
||||
testValidate({
|
||||
fromExtensions: ['exe', 'zip'],
|
||||
toExtensions: ['html'],
|
||||
createRedirects,
|
||||
|
@ -47,7 +53,7 @@ describe('normalizePluginOptions', () => {
|
|||
|
||||
it('rejects bad fromExtensions user inputs', () => {
|
||||
expect(() =>
|
||||
normalizePluginOptions({
|
||||
testValidate({
|
||||
fromExtensions: [null, undefined, 123, true] as unknown as string[],
|
||||
}),
|
||||
).toThrowErrorMatchingSnapshot();
|
||||
|
@ -55,7 +61,7 @@ describe('normalizePluginOptions', () => {
|
|||
|
||||
it('rejects bad toExtensions user inputs', () => {
|
||||
expect(() =>
|
||||
normalizePluginOptions({
|
||||
testValidate({
|
||||
toExtensions: [null, undefined, 123, true] as unknown as string[],
|
||||
}),
|
||||
).toThrowErrorMatchingSnapshot();
|
||||
|
@ -63,7 +69,7 @@ describe('normalizePluginOptions', () => {
|
|||
|
||||
it('rejects bad createRedirects user inputs', () => {
|
||||
expect(() =>
|
||||
normalizePluginOptions({
|
||||
testValidate({
|
||||
createRedirects: ['bad', 'value'] as unknown as CreateRedirectsFnOption,
|
||||
}),
|
||||
).toThrowErrorMatchingSnapshot();
|
|
@ -9,7 +9,6 @@ import type {LoadContext, Plugin, Props} from '@docusaurus/types';
|
|||
import type {PluginContext, RedirectMetadata} from './types';
|
||||
import type {PluginOptions} from '@docusaurus/plugin-client-redirects';
|
||||
|
||||
import normalizePluginOptions from './normalizePluginOptions';
|
||||
import collectRedirects from './collectRedirects';
|
||||
import writeRedirectFiles, {
|
||||
toRedirectFilesMetadata,
|
||||
|
@ -19,12 +18,10 @@ import {removePrefix, addLeadingSlash} from '@docusaurus/utils';
|
|||
|
||||
export default function pluginClientRedirectsPages(
|
||||
context: LoadContext,
|
||||
opts: PluginOptions,
|
||||
options: PluginOptions,
|
||||
): Plugin<unknown> {
|
||||
const {trailingSlash} = context.siteConfig;
|
||||
|
||||
const options = normalizePluginOptions(opts);
|
||||
|
||||
return {
|
||||
name: 'docusaurus-plugin-client-redirects',
|
||||
async postBuild(props: Props) {
|
||||
|
@ -53,3 +50,5 @@ export default function pluginClientRedirectsPages(
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
export {validateOptions} from './options';
|
||||
|
|
|
@ -1,59 +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 type {
|
||||
PluginOptions,
|
||||
Options as UserPluginOptions,
|
||||
RedirectOption,
|
||||
} from '@docusaurus/plugin-client-redirects';
|
||||
import {Joi, PathnameSchema} from '@docusaurus/utils-validation';
|
||||
import {DEFAULT_PLUGIN_ID} from '@docusaurus/utils';
|
||||
|
||||
export const DefaultPluginOptions: PluginOptions = {
|
||||
id: DEFAULT_PLUGIN_ID, // TODO temporary
|
||||
fromExtensions: [],
|
||||
toExtensions: [],
|
||||
redirects: [],
|
||||
};
|
||||
|
||||
const RedirectPluginOptionValidation = Joi.object<RedirectOption>({
|
||||
to: PathnameSchema.required(),
|
||||
from: Joi.alternatives().try(
|
||||
PathnameSchema.required(),
|
||||
Joi.array().items(PathnameSchema.required()),
|
||||
),
|
||||
});
|
||||
|
||||
const isString = Joi.string().required().not(null);
|
||||
|
||||
const UserOptionsSchema = Joi.object<UserPluginOptions>({
|
||||
id: Joi.string().optional(), // TODO remove once validation migrated to new system
|
||||
fromExtensions: Joi.array().items(isString),
|
||||
toExtensions: Joi.array().items(isString),
|
||||
redirects: Joi.array().items(RedirectPluginOptionValidation),
|
||||
createRedirects: Joi.function().arity(1),
|
||||
});
|
||||
|
||||
function validateUserOptions(userOptions: UserPluginOptions) {
|
||||
const {error} = UserOptionsSchema.validate(userOptions, {
|
||||
abortEarly: true,
|
||||
allowUnknown: false,
|
||||
});
|
||||
if (error) {
|
||||
throw new Error(
|
||||
`Invalid @docusaurus/plugin-client-redirects options: ${error.message}
|
||||
${JSON.stringify(userOptions, null, 2)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default function normalizePluginOptions(
|
||||
userPluginOptions: UserPluginOptions = {},
|
||||
): PluginOptions {
|
||||
validateUserOptions(userPluginOptions);
|
||||
return {...DefaultPluginOptions, ...userPluginOptions};
|
||||
}
|
52
packages/docusaurus-plugin-client-redirects/src/options.ts
Normal file
52
packages/docusaurus-plugin-client-redirects/src/options.ts
Normal file
|
@ -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 type {
|
||||
PluginOptions,
|
||||
RedirectOption,
|
||||
} from '@docusaurus/plugin-client-redirects';
|
||||
import type {
|
||||
OptionValidationContext,
|
||||
ValidationResult,
|
||||
} from '@docusaurus/types';
|
||||
import {Joi, PathnameSchema} from '@docusaurus/utils-validation';
|
||||
|
||||
export const DEFAULT_OPTIONS: Partial<PluginOptions> = {
|
||||
fromExtensions: [],
|
||||
toExtensions: [],
|
||||
redirects: [],
|
||||
};
|
||||
|
||||
const RedirectPluginOptionValidation = Joi.object<RedirectOption>({
|
||||
to: PathnameSchema.required(),
|
||||
from: Joi.alternatives().try(
|
||||
PathnameSchema.required(),
|
||||
Joi.array().items(PathnameSchema.required()),
|
||||
),
|
||||
});
|
||||
|
||||
const isString = Joi.string().required().not(null);
|
||||
|
||||
const UserOptionsSchema = Joi.object<PluginOptions>({
|
||||
fromExtensions: Joi.array()
|
||||
.items(isString)
|
||||
.default(DEFAULT_OPTIONS.fromExtensions),
|
||||
toExtensions: Joi.array()
|
||||
.items(isString)
|
||||
.default(DEFAULT_OPTIONS.toExtensions),
|
||||
redirects: Joi.array()
|
||||
.items(RedirectPluginOptionValidation)
|
||||
.default(DEFAULT_OPTIONS.redirects),
|
||||
createRedirects: Joi.function().arity(1),
|
||||
}).default(DEFAULT_OPTIONS);
|
||||
|
||||
export function validateOptions({
|
||||
validate,
|
||||
options: userOptions,
|
||||
}: OptionValidationContext<PluginOptions>): ValidationResult<PluginOptions> {
|
||||
return validate(UserOptionsSchema, userOptions);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue