diff --git a/packages/docusaurus-plugin-content-blog/src/pluginOptionSchema.ts b/packages/docusaurus-plugin-content-blog/src/pluginOptionSchema.ts index 757a50b02657..d3605c1d97ef 100644 --- a/packages/docusaurus-plugin-content-blog/src/pluginOptionSchema.ts +++ b/packages/docusaurus-plugin-content-blog/src/pluginOptionSchema.ts @@ -10,6 +10,7 @@ import { RemarkPluginsSchema, RehypePluginsSchema, AdmonitionsSchema, + URISchema, } from '@docusaurus/utils-validation'; export const DEFAULT_OPTIONS = { @@ -55,7 +56,7 @@ export const PluginOptionSchema = Joi.object({ remarkPlugins: RemarkPluginsSchema.default(DEFAULT_OPTIONS.remarkPlugins), rehypePlugins: RehypePluginsSchema.default(DEFAULT_OPTIONS.rehypePlugins), admonitions: AdmonitionsSchema.default(DEFAULT_OPTIONS.admonitions), - editUrl: Joi.string().uri(), + editUrl: URISchema, truncateMarker: Joi.object().default(DEFAULT_OPTIONS.truncateMarker), beforeDefaultRemarkPlugins: Joi.array() .items( diff --git a/packages/docusaurus-plugin-content-docs/src/pluginOptionSchema.ts b/packages/docusaurus-plugin-content-docs/src/pluginOptionSchema.ts index e72783e73f51..0c4eb0bf2c0e 100644 --- a/packages/docusaurus-plugin-content-docs/src/pluginOptionSchema.ts +++ b/packages/docusaurus-plugin-content-docs/src/pluginOptionSchema.ts @@ -10,6 +10,7 @@ import { RemarkPluginsSchema, RehypePluginsSchema, AdmonitionsSchema, + URISchema, } from '@docusaurus/utils-validation'; const REVERSED_DOCS_HOME_PAGE_ID = '_index'; @@ -33,7 +34,7 @@ export const DEFAULT_OPTIONS: PluginOptions = { export const PluginOptionSchema = Joi.object({ path: Joi.string().default(DEFAULT_OPTIONS.path), - editUrl: Joi.string().uri(), + editUrl: URISchema, routeBasePath: Joi.string().allow('').default(DEFAULT_OPTIONS.routeBasePath), homePageId: Joi.string().default(DEFAULT_OPTIONS.homePageId), include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include), diff --git a/packages/docusaurus-theme-classic/package.json b/packages/docusaurus-theme-classic/package.json index 15fcbe1f43c0..0eb68a49b41d 100644 --- a/packages/docusaurus-theme-classic/package.json +++ b/packages/docusaurus-theme-classic/package.json @@ -26,7 +26,8 @@ "prismjs": "^1.20.0", "prop-types": "^15.7.2", "react-router-dom": "^5.1.2", - "react-toggle": "^4.1.1" + "react-toggle": "^4.1.1", + "@docusaurus/utils-validation": "^2.0.0-alpha.61" }, "devDependencies": { "@docusaurus/module-type-aliases": "^2.0.0-alpha.61", diff --git a/packages/docusaurus-theme-classic/src/validateThemeConfig.js b/packages/docusaurus-theme-classic/src/validateThemeConfig.js index 7f28d1e64c28..1267db7c56dc 100644 --- a/packages/docusaurus-theme-classic/src/validateThemeConfig.js +++ b/packages/docusaurus-theme-classic/src/validateThemeConfig.js @@ -6,6 +6,7 @@ */ const Joi = require('@hapi/joi'); +const {URISchema} = require('@docusaurus/utils-validation'); const DEFAULT_COLOR_MODE_CONFIG = { defaultMode: 'light', @@ -28,7 +29,7 @@ const NavbarItemPosition = Joi.string().equal('left', 'right').default('left'); const DefaultNavbarItemSchema = Joi.object({ items: Joi.array().optional().items(Joi.link('...')), to: Joi.string(), - href: Joi.string().uri(), + href: URISchema, label: Joi.string(), position: NavbarItemPosition, activeBasePath: Joi.string(), @@ -140,7 +141,7 @@ const ColorModeSchema = Joi.object({ const FooterLinkItemSchema = Joi.object({ to: Joi.string(), - href: Joi.string().uri(), + href: URISchema, html: Joi.string(), label: Joi.string(), }) diff --git a/packages/docusaurus-utils-validation/src/__tests__/__snapshots__/validationSchemas.test.ts.snap b/packages/docusaurus-utils-validation/src/__tests__/__snapshots__/validationSchemas.test.ts.snap index ff5d78809419..16ec0a6e8a0e 100644 --- a/packages/docusaurus-utils-validation/src/__tests__/__snapshots__/validationSchemas.test.ts.snap +++ b/packages/docusaurus-utils-validation/src/__tests__/__snapshots__/validationSchemas.test.ts.snap @@ -59,3 +59,5 @@ exports[`validation schemas RemarkPluginsSchema: for value=3 1`] = `"\\"value\\" exports[`validation schemas RemarkPluginsSchema: for value=false 1`] = `"\\"value\\" must be an array"`; exports[`validation schemas RemarkPluginsSchema: for value=null 1`] = `"\\"value\\" must be an array"`; + +exports[`validation schemas URISchema: for value="invalidURL" 1`] = `"\\"value\\" does not match any of the allowed types"`; diff --git a/packages/docusaurus-utils-validation/src/__tests__/validationSchemas.test.ts b/packages/docusaurus-utils-validation/src/__tests__/validationSchemas.test.ts index 05a614bd0d3e..4045a838e070 100644 --- a/packages/docusaurus-utils-validation/src/__tests__/validationSchemas.test.ts +++ b/packages/docusaurus-utils-validation/src/__tests__/validationSchemas.test.ts @@ -12,6 +12,7 @@ import { RehypePluginsSchema, RemarkPluginsSchema, PluginIdSchema, + URISchema, } from '../validationSchemas'; function createTestHelpers({ @@ -106,4 +107,16 @@ describe('validation schemas', () => { test('RehypePluginsSchema', () => { testMarkdownPluginSchemas(RehypePluginsSchema); }); + + test('URISchema', () => { + const validURL = 'https://docusaurus.io'; + const doubleHash = 'https://docusaurus.io#github#/:'; + const invalidURL = 'invalidURL'; + const urlFromIssue = 'https://riot.im/app/#/room/#ligo-public:matrix.org'; + const {testFail, testOK} = createTestHelpers({schema: URISchema}); + testOK(validURL); + testOK(doubleHash); + testFail(invalidURL); + testOK(urlFromIssue); + }); }); diff --git a/packages/docusaurus-utils-validation/src/validationSchemas.ts b/packages/docusaurus-utils-validation/src/validationSchemas.ts index 70f6925e9e78..d6ffd6d6928a 100644 --- a/packages/docusaurus-utils-validation/src/validationSchemas.ts +++ b/packages/docusaurus-utils-validation/src/validationSchemas.ts @@ -25,3 +25,19 @@ export const RemarkPluginsSchema = MarkdownPluginsSchema; export const RehypePluginsSchema = MarkdownPluginsSchema; export const AdmonitionsSchema = Joi.object().default({}); + +export const URISchema = Joi.alternatives( + Joi.string().uri(), + Joi.custom((val, helpers) => { + try { + const url = new URL(val); + if (url) { + return val; + } else { + return helpers.error('any.invalid'); + } + } catch { + return helpers.error('any.invalid'); + } + }), +); diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index 12a3e0561df7..67f803841c8b 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -107,7 +107,8 @@ "webpack-bundle-analyzer": "^3.6.1", "webpack-dev-server": "^3.11.0", "webpack-merge": "^4.2.2", - "webpackbar": "^4.0.0" + "webpackbar": "^4.0.0", + "@docusaurus/utils-validation": "^2.0.0-alpha.61" }, "peerDependencies": { "react": "^16.8.4", diff --git a/packages/docusaurus/src/server/configValidation.ts b/packages/docusaurus/src/server/configValidation.ts index 229443211769..cf5b5ad42a5a 100644 --- a/packages/docusaurus/src/server/configValidation.ts +++ b/packages/docusaurus/src/server/configValidation.ts @@ -11,6 +11,7 @@ import Joi from '@hapi/joi'; import { logValidationBugReportHint, isValidationDisabledEscapeHatch, + URISchema, } from '@docusaurus/utils-validation'; export const DEFAULT_CONFIG: Pick< @@ -55,7 +56,7 @@ const ConfigSchema = Joi.object({ .message('{{#label}} must be a string with a trailing `/`'), favicon: Joi.string().required(), title: Joi.string().required(), - url: Joi.string().uri().required(), + url: URISchema.required(), onBrokenLinks: Joi.string() .equal('ignore', 'log', 'warn', 'error', 'throw') .default(DEFAULT_CONFIG.onBrokenLinks),