Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(docusaurus-utils-validation): baseUrl + routeBasePath: allow empty string, normalized as "/" #8258

Merged
merged 6 commits into from
Dec 8, 2022
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ describe('validateOptions', () => {
...defaultOptions,
feedOptions: {type: 'rss' as const, title: 'myTitle'},
path: 'not_blog',
routeBasePath: 'myBlog',
routeBasePath: '/myBlog',
postsPerPage: 5,
include: ['api/*', 'docs/*'],
};
Expand All @@ -53,7 +53,7 @@ describe('validateOptions', () => {
it('accepts valid user options', () => {
const userOptions: Options = {
...defaultOptions,
routeBasePath: 'myBlog',
routeBasePath: '/myBlog',
beforeDefaultRemarkPlugins: [],
beforeDefaultRehypePlugins: [markdownPluginsFunctionStub],
remarkPlugins: [[markdownPluginsFunctionStub, {option1: '42'}]],
Expand Down
6 changes: 2 additions & 4 deletions packages/docusaurus-plugin-content-blog/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
RemarkPluginsSchema,
RehypePluginsSchema,
AdmonitionsSchema,
RouteBasePathSchema,
URISchema,
} from '@docusaurus/utils-validation';
import {GlobExcludeDefault} from '@docusaurus/utils';
Expand Down Expand Up @@ -56,10 +57,7 @@ const PluginOptionSchema = Joi.object<PluginOptions>({
archiveBasePath: Joi.string()
.default(DEFAULT_OPTIONS.archiveBasePath)
.allow(null),
routeBasePath: Joi.string()
// '' not allowed, see https://github.com/facebook/docusaurus/issues/3374
// .allow('')
.default(DEFAULT_OPTIONS.routeBasePath),
routeBasePath: RouteBasePathSchema.default(DEFAULT_OPTIONS.routeBasePath),
tagsBasePath: Joi.string().default(DEFAULT_OPTIONS.tagsBasePath),
include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include),
exclude: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.exclude),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe('normalizeDocsPluginOptions', () => {
it('accepts correctly defined user options', () => {
const userOptions: Options = {
path: 'my-docs', // Path to data on filesystem, relative to site dir.
routeBasePath: 'my-docs', // URL Route.
routeBasePath: '/my-docs', // URL Route.
tagsBasePath: 'tags', // URL Tags Route.
include: ['**/*.{md,mdx}'], // Extensions to include.
exclude: GlobExcludeDefault,
Expand Down
6 changes: 2 additions & 4 deletions packages/docusaurus-plugin-content-docs/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
RemarkPluginsSchema,
RehypePluginsSchema,
AdmonitionsSchema,
RouteBasePathSchema,
URISchema,
} from '@docusaurus/utils-validation';
import {GlobExcludeDefault} from '@docusaurus/utils';
Expand Down Expand Up @@ -73,10 +74,7 @@ const OptionsSchema = Joi.object<PluginOptions>({
editUrl: Joi.alternatives().try(URISchema, Joi.function()),
editCurrentVersion: Joi.boolean().default(DEFAULT_OPTIONS.editCurrentVersion),
editLocalizedFiles: Joi.boolean().default(DEFAULT_OPTIONS.editLocalizedFiles),
routeBasePath: Joi.string()
// '' not allowed, see https://github.com/facebook/docusaurus/issues/3374
// .allow('') ""
.default(DEFAULT_OPTIONS.routeBasePath),
routeBasePath: RouteBasePathSchema.default(DEFAULT_OPTIONS.routeBasePath),
tagsBasePath: Joi.string().default(DEFAULT_OPTIONS.tagsBasePath),
// @ts-expect-error: deprecated
homePageId: Joi.any().forbidden().messages({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('normalizePagesPluginOptions', () => {
it('accepts correctly defined user options', () => {
const userOptions = {
path: 'src/my-pages',
routeBasePath: 'my-pages',
routeBasePath: '/my-pages',
include: ['**/*.{js,jsx,ts,tsx}'],
exclude: ['**/$*/'],
};
Expand All @@ -51,4 +51,15 @@ describe('normalizePagesPluginOptions', () => {
});
}).toThrowErrorMatchingInlineSnapshot(`""path" must be a string"`);
});

it('empty routeBasePath replace default path("/")', () => {
expect(
testValidate({
routeBasePath: '',
}),
).toEqual({
...defaultOptions,
routeBasePath: '/',
});
});
});
3 changes: 2 additions & 1 deletion packages/docusaurus-plugin-content-pages/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
RemarkPluginsSchema,
RehypePluginsSchema,
AdmonitionsSchema,
RouteBasePathSchema,
} from '@docusaurus/utils-validation';
import {GlobExcludeDefault} from '@docusaurus/utils';
import type {OptionValidationContext} from '@docusaurus/types';
Expand All @@ -30,7 +31,7 @@ export const DEFAULT_OPTIONS: PluginOptions = {

const PluginOptionSchema = Joi.object<PluginOptions>({
path: Joi.string().default(DEFAULT_OPTIONS.path),
routeBasePath: Joi.string().default(DEFAULT_OPTIONS.routeBasePath),
routeBasePath: RouteBasePathSchema.default(DEFAULT_OPTIONS.routeBasePath),
include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include),
exclude: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.exclude),
mdxPageComponent: Joi.string().default(DEFAULT_OPTIONS.mdxPageComponent),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,12 @@ exports[`validation schemas remarkPluginsSchema: for value=false 1`] = `""value"

exports[`validation schemas remarkPluginsSchema: for value=null 1`] = `""value" must be an array"`;

exports[`validation schemas routeBasePathSchema: for value=[] 1`] = `""value" must be a string"`;

exports[`validation schemas routeBasePathSchema: for value={} 1`] = `""value" must be a string"`;

exports[`validation schemas routeBasePathSchema: for value=3 1`] = `""value" must be a string"`;

exports[`validation schemas routeBasePathSchema: for value=null 1`] = `""value" must be a string"`;

exports[`validation schemas uRISchema: for value="spaces are invalid in a URL" 1`] = `""value" does not look like a valid url (value='')"`;
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
PluginIdSchema,
URISchema,
PathnameSchema,
RouteBasePathSchema,
ContentVisibilitySchema,
} from '../validationSchemas';

Expand All @@ -24,8 +25,9 @@ function createTestHelpers({
schema: Joi.Schema;
defaultValue?: unknown;
}) {
function testOK(value: unknown) {
expect(Joi.attempt(value, schema)).toEqual(value ?? defaultValue);
function testOK(value: unknown, options?: {normalizedValue?: unknown}) {
const expectedValue = options?.normalizedValue ?? value ?? defaultValue;
expect(Joi.attempt(value, schema)).toEqual(expectedValue);
}

function testFail(value: unknown) {
Expand Down Expand Up @@ -168,6 +170,29 @@ describe('validation schemas', () => {
testFail('https://github.com/foo');
});

it('routeBasePathSchema', () => {
const {testFail, testOK} = createTestHelpers({
schema: RouteBasePathSchema,
defaultValue: undefined,
});

testOK('', {normalizedValue: '/'});
testOK('/');
testOK('/foo', {normalizedValue: '/foo'});
testOK('foo', {normalizedValue: '/foo'});
testOK('blog', {normalizedValue: '/blog'});
testOK('blog/', {normalizedValue: '/blog/'});
testOK('prefix/blog', {normalizedValue: '/prefix/blog'});
testOK('prefix/blog/', {normalizedValue: '/prefix/blog/'});
testOK('/prefix/blog', {normalizedValue: '/prefix/blog'});
testOK(undefined);

testFail(3);
testFail([]);
testFail(null);
testFail({});
});

it('contentVisibilitySchema', () => {
const {testFail, testOK} = createTestHelpers({
schema: ContentVisibilitySchema,
Expand Down
1 change: 1 addition & 0 deletions packages/docusaurus-utils-validation/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export {
RemarkPluginsSchema,
RehypePluginsSchema,
AdmonitionsSchema,
RouteBasePathSchema,
URISchema,
PathnameSchema,
FrontMatterTagsSchema,
Expand Down
27 changes: 26 additions & 1 deletion packages/docusaurus-utils-validation/src/validationSchemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
* LICENSE file in the root directory of this source tree.
*/

import {isValidPathname, DEFAULT_PLUGIN_ID, type Tag} from '@docusaurus/utils';
import {
isValidPathname,
DEFAULT_PLUGIN_ID,
type Tag,
addLeadingSlash,
} from '@docusaurus/utils';
import Joi from './Joi';
import {JoiFrontMatter} from './JoiFrontMatter';

Expand Down Expand Up @@ -96,6 +101,26 @@ export const PathnameSchema = Joi.string()
'{{#label}} is not a valid pathname. Pathname should start with slash and not contain any domain or query string.',
);

// Normalized schema for url path segments: baseUrl + routeBasePath...
// Note we only add a leading slash
// we don't always want to enforce a trailing slash on urls such as /docs
//
// Examples:
// '' => '/'
// 'docs' => '/docs'
// '/docs' => '/docs'
// 'docs/' => '/docs'
// 'prefix/docs' => '/prefix/docs'
// TODO tighter validation: not all strings are valid path segments
export const RouteBasePathSchema = Joi
// Weird Joi trick needed, otherwise value '' is not normalized...
.alternatives()
.try(Joi.string().required().allow(''))
.custom((value: string) =>
// /!\ do not add trailing slash here
addLeadingSlash(value),
);

const FrontMatterTagSchema = JoiFrontMatter.alternatives()
.try(
JoiFrontMatter.string().required(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ describe('normalizeConfig', () => {
});

it('normalizes various base URLs', () => {
expect(
normalizeConfig({
baseUrl: '',
}).baseUrl,
).toBe('/');
expect(
normalizeConfig({
baseUrl: 'noSlash',
Expand Down
5 changes: 4 additions & 1 deletion packages/docusaurus/src/server/configValidation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,10 @@ const SiteUrlSchema = Joi.string()

// TODO move to @docusaurus/utils-validation
export const ConfigSchema = Joi.object<DocusaurusConfig>({
baseUrl: Joi.string()
baseUrl: Joi
// Weird Joi trick needed, otherwise value '' is not normalized...
.alternatives()
.try(Joi.string().required().allow(''))
.required()
.custom((value: string) => addLeadingSlash(addTrailingSlash(value))),
baseUrlIssueBanner: Joi.boolean().default(DEFAULT_CONFIG.baseUrlIssueBanner),
Expand Down