feat(v2): add option validation for remaining official plugins (#2970)

* feat(v2): add option validation lifecycle method

* chore(v2): add dependencies

* chore(v2): add yup dependency

* feat(v2): add option validation for plugin-content-docs

* chore(v2): add facebook copyright

* refactor(v2): remove unused variable

* chore(v2): add dependencies

* chore(v2): add copyright

* fix(v2): use strict for option validation

* feat(v2): add option validation for plugin-content-pages

* feat(v2): add schema for plugin-google-analytics and plugin-google-gtag

* feat(v2): add option validation for plugin-sitemap

* chore(v2): add dependency for yup

* fix(v2): remove strict to allow normalization

* refactor(v2): refactor validate method

* feat(v2): modify existing tests

* feat(v2): add tests for plugin normalization

* style(v2): use a more descriptive filename for schema

* feat(v2): add normalization tests

* feat(v2): add more tests for option validation

* refactor(v2): remove unused code

* refactor(v2): remove unused code

* refactor(v2): refactor methods and types

* feat(v2): replace Yup with Joi

* fix(v2): fix plugin-content-docs schema

* feat(v2): modify tests for plugin-content-docs

* fix(v2): fix a typo

* refactor(v2): improve tests and refactor code

* feat(v2): support both commonjs and ES modules

* refactor(v2): refactor validateOption method

* style(v2): fix eslint errors and typo in types

* chore(v2): remove unused yup dependency

* style(v2): standardize naming across official plugins

* chore(v2): update test snapshots

* chore(v2): remove obsolete snapshots

* chore(v2): fix a typo and check test

* feat(v2): add validation for new field

* feat(v2): add test for new field
This commit is contained in:
Teik Jun 2020-06-26 21:14:59 +08:00 committed by GitHub
parent 3213955e72
commit 0f59cd1599
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 444 additions and 169 deletions

View file

@ -9,6 +9,7 @@ import path from 'path';
import pluginContentPages from '../index';
import {LoadContext} from '@docusaurus/types';
import normalizePluginOptions from './pluginOptionSchema.test';
describe('docusaurus-plugin-content-pages', () => {
test('simple pages', async () => {
@ -23,9 +24,12 @@ describe('docusaurus-plugin-content-pages', () => {
siteConfig,
} as LoadContext;
const pluginPath = 'src/pages';
const plugin = pluginContentPages(context, {
path: pluginPath,
});
const plugin = pluginContentPages(
context,
normalizePluginOptions({
path: pluginPath,
}),
);
const pagesMetadatas = await plugin.loadContent();
expect(pagesMetadatas).toEqual([

View file

@ -0,0 +1,49 @@
/**
* 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';
export default function normalizePluginOptions(options) {
const {value, error} = PluginOptionSchema.validate(options, {
convert: false,
});
if (error) {
throw error;
} else {
return value;
}
}
describe('normalizePagesPluginOptions', () => {
test('should return default options for undefined user options', async () => {
const {value} = await PluginOptionSchema.validate({});
expect(value).toEqual(DEFAULT_OPTIONS);
});
test('should fill in default options for partially defined user options', async () => {
const {value} = await PluginOptionSchema.validate({path: 'src/pages'});
expect(value).toEqual(DEFAULT_OPTIONS);
});
test('should accept correctly defined user options', async () => {
const userOptions = {
path: 'src/my-pages',
routeBasePath: 'my-pages',
include: ['**/*.{js,jsx,ts,tsx}'],
};
const {value} = await PluginOptionSchema.validate(userOptions);
expect(value).toEqual(userOptions);
});
test('should reject bad path inputs', () => {
expect(() => {
normalizePluginOptions({
path: 42,
});
}).toThrowErrorMatchingInlineSnapshot(`"\\"path\\" must be a string"`);
});
});

View file

@ -9,21 +9,21 @@ import globby from 'globby';
import fs from 'fs';
import path from 'path';
import {encodePath, fileToPath, aliasedSitePath} from '@docusaurus/utils';
import {LoadContext, Plugin} from '@docusaurus/types';
import {
LoadContext,
Plugin,
OptionValidationContext,
ValidationResult,
} from '@docusaurus/types';
import {PluginOptions, LoadedContent} from './types';
const DEFAULT_OPTIONS: PluginOptions = {
path: 'src/pages', // Path to data on filesystem, relative to site dir.
routeBasePath: '', // URL Route.
include: ['**/*.{js,jsx,ts,tsx}'], // Extensions to include.
};
import {PluginOptionSchema} from './pluginOptionSchema';
import {ValidationError} from '@hapi/joi';
export default function pluginContentPages(
context: LoadContext,
opts: Partial<PluginOptions>,
): Plugin<LoadedContent | null> {
const options = {...DEFAULT_OPTIONS, ...opts};
options: PluginOptions,
): Plugin<LoadedContent | null, typeof PluginOptionSchema> {
const contentPath = path.resolve(context.siteDir, options.path);
return {
@ -81,3 +81,14 @@ export default function pluginContentPages(
},
};
}
export function validateOptions({
validate,
options,
}: OptionValidationContext<PluginOptions, ValidationError>): ValidationResult<
PluginOptions,
ValidationError
> {
const validatedOptions = validate(PluginOptionSchema, options);
return validatedOptions;
}

View file

@ -0,0 +1,20 @@
/**
* 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 * as Joi from '@hapi/joi';
import {PluginOptions} from './types';
export const DEFAULT_OPTIONS: PluginOptions = {
path: 'src/pages', // Path to data on filesystem, relative to site dir.
routeBasePath: '', // URL Route.
include: ['**/*.{js,jsx,ts,tsx}'], // Extensions to include.
};
export 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),
});