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(core): normalize slashes for url/baseUrl instead of throwing #8066

Merged
merged 1 commit into from Sep 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -69,6 +69,9 @@ export const URISchema = Joi.alternatives(
// This custom validation logic is required notably because Joi does not
// accept paths like /a/b/c ...
Joi.custom((val: unknown, helpers) => {
if (typeof val !== 'string') {
return helpers.error('any.invalid');
}
try {
// eslint-disable-next-line no-new
new URL(String(val));
Expand Down
Expand Up @@ -138,11 +138,6 @@ exports[`normalizeConfig should throw error if themes is not array for the input
"
`;

exports[`normalizeConfig throws error for baseUrl without trailing \`/\` 1`] = `
""baseUrl" must be a string with a trailing slash.
"
`;

exports[`normalizeConfig throws error for required fields 1`] = `
""baseUrl" is required
"title" is required
Expand Down
Expand Up @@ -5,6 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/

import {jest} from '@jest/globals';
import {
ConfigSchema,
DEFAULT_CONFIG,
Expand Down Expand Up @@ -86,12 +87,68 @@ describe('normalizeConfig', () => {
}).toThrowErrorMatchingSnapshot();
});

it('throws error for baseUrl without trailing `/`', () => {
expect(() => {
it('throws for non-string URLs', () => {
expect(() =>
normalizeConfig({
// @ts-expect-error: test
url: 1,
}),
).toThrowErrorMatchingInlineSnapshot(`
""url" contains an invalid value
"
`);
});

it('normalizes various URLs', () => {
const consoleMock = jest
.spyOn(console, 'warn')
.mockImplementation(() => {});

expect(
normalizeConfig({
url: 'https://mysite.com/',
}).url,
).toBe('https://mysite.com');
expect(
normalizeConfig({
// This shouldn't happen
url: 'https://mysite.com/foo/',
}).url,
).toBe('https://mysite.com/foo');

expect(consoleMock.mock.calls[0][0]).toMatchInlineSnapshot(
`"[WARNING] Docusaurus config validation warning. Field "url": The url is not supposed to contain a sub-path like '/foo/'. Please use the baseUrl field for sub-paths."`,
);
});

it('throws for non-string base URLs', () => {
expect(() =>
normalizeConfig({
// @ts-expect-error: test
baseUrl: 1,
}),
).toThrowErrorMatchingInlineSnapshot(`
""baseUrl" must be a string
"
`);
});

it('normalizes various base URLs', () => {
expect(
normalizeConfig({
baseUrl: 'noSlash',
});
}).toThrowErrorMatchingSnapshot();
}).baseUrl,
).toBe('/noSlash/');
expect(
normalizeConfig({
baseUrl: '/noSlash',
}).baseUrl,
).toBe('/noSlash/');
expect(
normalizeConfig({
baseUrl: 'noSlash/foo',
}).baseUrl,
).toBe('/noSlash/foo/');
});

it.each([
Expand Down Expand Up @@ -342,7 +399,7 @@ describe('config warnings', () => {
expect(warning).toBeDefined();
expect(warning.details).toHaveLength(1);
expect(warning.details[0]!.message).toMatchInlineSnapshot(
`"Docusaurus config validation warning. Field "url": the url is not supposed to contain a sub-path like '/someSubpath', please use the baseUrl field for sub-paths"`,
`"Docusaurus config validation warning. Field "url": The url is not supposed to contain a sub-path like '/someSubpath'. Please use the baseUrl field for sub-paths."`,
);
});
});
14 changes: 8 additions & 6 deletions packages/docusaurus/src/server/configValidation.ts
Expand Up @@ -8,6 +8,9 @@
import {
DEFAULT_STATIC_DIR_NAME,
DEFAULT_I18N_DIR_NAME,
addLeadingSlash,
addTrailingSlash,
removeTrailingSlash,
} from '@docusaurus/utils';
import {Joi, URISchema, printWarning} from '@docusaurus/utils-validation';
import type {DocusaurusConfig, I18nConfig} from '@docusaurus/types';
Expand Down Expand Up @@ -149,24 +152,23 @@ const I18N_CONFIG_SCHEMA = Joi.object<I18nConfig>({
.optional()
.default(DEFAULT_I18N_CONFIG);

const SiteUrlSchema = URISchema.required().custom((value: unknown, helpers) => {
const SiteUrlSchema = URISchema.required().custom((value: string, helpers) => {
try {
const {pathname} = new URL(String(value));
if (pathname !== '/') {
helpers.warn('docusaurus.configValidationWarning', {
warningMessage: `the url is not supposed to contain a sub-path like '${pathname}', please use the baseUrl field for sub-paths`,
warningMessage: `The url is not supposed to contain a sub-path like '${pathname}'. Please use the baseUrl field for sub-paths.`,
});
}
} catch {}
return value;
}, 'siteUrlCustomValidation');
return removeTrailingSlash(value);
});

// TODO move to @docusaurus/utils-validation
export const ConfigSchema = Joi.object<DocusaurusConfig>({
baseUrl: Joi.string()
.required()
.regex(/\/$/m)
.message('{{#label}} must be a string with a trailing slash.'),
.custom((value: string) => addLeadingSlash(addTrailingSlash(value))),
baseUrlIssueBanner: Joi.boolean().default(DEFAULT_CONFIG.baseUrlIssueBanner),
favicon: Joi.string().optional(),
title: Joi.string().required(),
Expand Down