mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-13 00:57:53 +02:00
fix(core): normalize slashes for url/baseUrl instead of throwing (#8066)
This commit is contained in:
parent
73d0ede21a
commit
bcae7503ff
4 changed files with 73 additions and 16 deletions
|
@ -69,6 +69,9 @@ export const URISchema = Joi.alternatives(
|
||||||
// This custom validation logic is required notably because Joi does not
|
// This custom validation logic is required notably because Joi does not
|
||||||
// accept paths like /a/b/c ...
|
// accept paths like /a/b/c ...
|
||||||
Joi.custom((val: unknown, helpers) => {
|
Joi.custom((val: unknown, helpers) => {
|
||||||
|
if (typeof val !== 'string') {
|
||||||
|
return helpers.error('any.invalid');
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line no-new
|
// eslint-disable-next-line no-new
|
||||||
new URL(String(val));
|
new URL(String(val));
|
||||||
|
|
|
@ -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`] = `
|
exports[`normalizeConfig throws error for required fields 1`] = `
|
||||||
""baseUrl" is required
|
""baseUrl" is required
|
||||||
"title" is required
|
"title" is required
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {jest} from '@jest/globals';
|
||||||
import {
|
import {
|
||||||
ConfigSchema,
|
ConfigSchema,
|
||||||
DEFAULT_CONFIG,
|
DEFAULT_CONFIG,
|
||||||
|
@ -86,12 +87,68 @@ describe('normalizeConfig', () => {
|
||||||
}).toThrowErrorMatchingSnapshot();
|
}).toThrowErrorMatchingSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws error for baseUrl without trailing `/`', () => {
|
it('throws for non-string URLs', () => {
|
||||||
expect(() => {
|
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({
|
normalizeConfig({
|
||||||
baseUrl: 'noSlash',
|
baseUrl: 'noSlash',
|
||||||
});
|
}).baseUrl,
|
||||||
}).toThrowErrorMatchingSnapshot();
|
).toBe('/noSlash/');
|
||||||
|
expect(
|
||||||
|
normalizeConfig({
|
||||||
|
baseUrl: '/noSlash',
|
||||||
|
}).baseUrl,
|
||||||
|
).toBe('/noSlash/');
|
||||||
|
expect(
|
||||||
|
normalizeConfig({
|
||||||
|
baseUrl: 'noSlash/foo',
|
||||||
|
}).baseUrl,
|
||||||
|
).toBe('/noSlash/foo/');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
|
@ -342,7 +399,7 @@ describe('config warnings', () => {
|
||||||
expect(warning).toBeDefined();
|
expect(warning).toBeDefined();
|
||||||
expect(warning.details).toHaveLength(1);
|
expect(warning.details).toHaveLength(1);
|
||||||
expect(warning.details[0]!.message).toMatchInlineSnapshot(
|
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."`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
import {
|
import {
|
||||||
DEFAULT_STATIC_DIR_NAME,
|
DEFAULT_STATIC_DIR_NAME,
|
||||||
DEFAULT_I18N_DIR_NAME,
|
DEFAULT_I18N_DIR_NAME,
|
||||||
|
addLeadingSlash,
|
||||||
|
addTrailingSlash,
|
||||||
|
removeTrailingSlash,
|
||||||
} from '@docusaurus/utils';
|
} from '@docusaurus/utils';
|
||||||
import {Joi, URISchema, printWarning} from '@docusaurus/utils-validation';
|
import {Joi, URISchema, printWarning} from '@docusaurus/utils-validation';
|
||||||
import type {DocusaurusConfig, I18nConfig} from '@docusaurus/types';
|
import type {DocusaurusConfig, I18nConfig} from '@docusaurus/types';
|
||||||
|
@ -149,24 +152,23 @@ const I18N_CONFIG_SCHEMA = Joi.object<I18nConfig>({
|
||||||
.optional()
|
.optional()
|
||||||
.default(DEFAULT_I18N_CONFIG);
|
.default(DEFAULT_I18N_CONFIG);
|
||||||
|
|
||||||
const SiteUrlSchema = URISchema.required().custom((value: unknown, helpers) => {
|
const SiteUrlSchema = URISchema.required().custom((value: string, helpers) => {
|
||||||
try {
|
try {
|
||||||
const {pathname} = new URL(String(value));
|
const {pathname} = new URL(String(value));
|
||||||
if (pathname !== '/') {
|
if (pathname !== '/') {
|
||||||
helpers.warn('docusaurus.configValidationWarning', {
|
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 {}
|
} catch {}
|
||||||
return value;
|
return removeTrailingSlash(value);
|
||||||
}, 'siteUrlCustomValidation');
|
});
|
||||||
|
|
||||||
// TODO move to @docusaurus/utils-validation
|
// TODO move to @docusaurus/utils-validation
|
||||||
export const ConfigSchema = Joi.object<DocusaurusConfig>({
|
export const ConfigSchema = Joi.object<DocusaurusConfig>({
|
||||||
baseUrl: Joi.string()
|
baseUrl: Joi.string()
|
||||||
.required()
|
.required()
|
||||||
.regex(/\/$/m)
|
.custom((value: string) => addLeadingSlash(addTrailingSlash(value))),
|
||||||
.message('{{#label}} must be a string with a trailing slash.'),
|
|
||||||
baseUrlIssueBanner: Joi.boolean().default(DEFAULT_CONFIG.baseUrlIssueBanner),
|
baseUrlIssueBanner: Joi.boolean().default(DEFAULT_CONFIG.baseUrlIssueBanner),
|
||||||
favicon: Joi.string().optional(),
|
favicon: Joi.string().optional(),
|
||||||
title: Joi.string().required(),
|
title: Joi.string().required(),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue