diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index ddda9dc016..54d9d1b442 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -27,6 +27,7 @@ import { normalizeFrontMatterTags, groupTaggedItems, getContentPathList, + sanitizeURL, } from '@docusaurus/utils'; import type {LoadContext} from '@docusaurus/types'; import {validateBlogPostFrontMatter} from './blogFrontMatter'; @@ -189,7 +190,7 @@ async function processBlogSourceFile( const title = frontMatter.title ?? contentTitle ?? parsedBlogFileName.text; const description = frontMatter.description ?? excerpt ?? ''; - const slug = frontMatter.slug || parsedBlogFileName.slug; + const slug = sanitizeURL(frontMatter.slug || parsedBlogFileName.slug); const permalink = normalizeUrl([baseUrl, routeBasePath, slug]); diff --git a/packages/docusaurus-plugin-content-docs/src/slug.ts b/packages/docusaurus-plugin-content-docs/src/slug.ts index e5168563c2..31004c136a 100644 --- a/packages/docusaurus-plugin-content-docs/src/slug.ts +++ b/packages/docusaurus-plugin-content-docs/src/slug.ts @@ -10,6 +10,7 @@ import { addTrailingSlash, isValidPathname, resolvePathname, + sanitizeURL, } from '@docusaurus/utils'; import { DefaultNumberPrefixParser, @@ -66,10 +67,10 @@ export default function getSlug({ throw new Error( `We couldn't compute a valid slug for document with id "${baseID}" in "${sourceDirName}" directory. The slug we computed looks invalid: ${slug}. -Maybe your slug front matter is incorrect or you use weird chars in the file path? -By using the slug front matter, you should be able to fix this error, by using the slug of your choice: +Maybe your slug front matter is incorrect or you used weird chars in the file path? +By using the slug front matter, you should be able to fix this error: -Example => +Example: --- slug: /my/customDocPath --- @@ -79,5 +80,5 @@ slug: /my/customDocPath return slug; } - return ensureValidSlug(computeSlug()); + return ensureValidSlug(sanitizeURL(computeSlug())); } diff --git a/packages/docusaurus-plugin-content-pages/src/index.ts b/packages/docusaurus-plugin-content-pages/src/index.ts index c553d08444..9ffae7ed2f 100644 --- a/packages/docusaurus-plugin-content-pages/src/index.ts +++ b/packages/docusaurus-plugin-content-pages/src/index.ts @@ -8,6 +8,7 @@ import fs from 'fs-extra'; import path from 'path'; import { + sanitizeURL, encodePath, fileToPath, aliasedSitePath, @@ -110,7 +111,7 @@ export default async function pluginContentPages( const permalink = normalizeUrl([ baseUrl, options.routeBasePath, - encodePath(fileToPath(relativeSource)), + encodePath(sanitizeURL(fileToPath(relativeSource))), ]); if (isMarkdownSource(relativeSource)) { const content = await fs.readFile(source, 'utf-8'); diff --git a/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts b/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts index 3acd892bbe..5cd5127dea 100644 --- a/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts +++ b/packages/docusaurus-utils/src/__tests__/urlUtils.test.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import {normalizeUrl, getEditUrl} from '../urlUtils'; +import {normalizeUrl, getEditUrl, sanitizeURL} from '../urlUtils'; describe('normalizeUrl', () => { test('should normalize urls correctly', () => { @@ -132,6 +132,18 @@ describe('normalizeUrl', () => { }); }); +describe('sanitizeURL', () => { + test('sanitize paths that are RR dynamic routes', () => { + expect(sanitizeURL('/:user')).toEqual('/user'); + expect(sanitizeURL('/f{u}')).toEqual('/fu'); + expect(sanitizeURL('/a(1)')).toEqual('/a1'); + }); + test('do not sanitize paths that can be encoded', () => { + expect(sanitizeURL('/a:b')).toEqual('/a:b'); + expect(sanitizeURL('/你好')).toEqual('/你好'); + }); +}); + describe('getEditUrl', () => { test('returns right path', () => { expect( diff --git a/packages/docusaurus-utils/src/urlUtils.ts b/packages/docusaurus-utils/src/urlUtils.ts index 1c02abb774..0fa4334bf8 100644 --- a/packages/docusaurus-utils/src/urlUtils.ts +++ b/packages/docusaurus-utils/src/urlUtils.ts @@ -83,6 +83,13 @@ export function normalizeUrl(rawUrls: string[]): string { return str; } +/** + * Some slugs that seem valid may be dynamic routes for React router. We sanitize these paths. + */ +export function sanitizeURL(url: string): string { + return url.replace(/(?:(?<=\/):)|[()*[\]{}]/g, ''); +} + export function getEditUrl( fileRelativePath: string, editUrl?: string, diff --git a/website/_dogfooding/_docs tests/weird-paths/weird-paths/:file.md b/website/_dogfooding/_docs tests/weird-paths/weird-paths/:file.md new file mode 100644 index 0000000000..318ca13d8a --- /dev/null +++ b/website/_dogfooding/_docs tests/weird-paths/weird-paths/:file.md @@ -0,0 +1 @@ +# Filename with leading colon diff --git a/website/_dogfooding/_docs tests/weird-paths/weird-paths/[bracket].md b/website/_dogfooding/_docs tests/weird-paths/weird-paths/[bracket].md new file mode 100644 index 0000000000..9d5745ca88 --- /dev/null +++ b/website/_dogfooding/_docs tests/weird-paths/weird-paths/[bracket].md @@ -0,0 +1,3 @@ +# Filename with brackets + +I don't actually represent any React router quirks, but it's funny that I can end up not being sanitized diff --git a/website/_dogfooding/_docs tests/weird-paths/weird-paths/_category_.yml b/website/_dogfooding/_docs tests/weird-paths/weird-paths/_category_.yml new file mode 100644 index 0000000000..00e4b32a7c --- /dev/null +++ b/website/_dogfooding/_docs tests/weird-paths/weird-paths/_category_.yml @@ -0,0 +1 @@ +label: Weird paths diff --git a/website/_dogfooding/_docs tests/weird-paths/weird-paths/file (paren).md b/website/_dogfooding/_docs tests/weird-paths/weird-paths/file (paren).md new file mode 100644 index 0000000000..3a513436c6 --- /dev/null +++ b/website/_dogfooding/_docs tests/weird-paths/weird-paths/file (paren).md @@ -0,0 +1,3 @@ +# Filename with parentheses + +If not sanitized, I should actually become [page URL without parens](file%20paren) diff --git a/website/_dogfooding/_docs tests/weird-paths/weird-paths/file*aster.md b/website/_dogfooding/_docs tests/weird-paths/weird-paths/file*aster.md new file mode 100644 index 0000000000..0c930c7c66 --- /dev/null +++ b/website/_dogfooding/_docs tests/weird-paths/weird-paths/file*aster.md @@ -0,0 +1,5 @@ +# Filename with asterisk + + + +Can I be accessible at [any page](filecaster)? diff --git a/website/_dogfooding/_docs tests/weird-paths/weird-paths/{brace}.md b/website/_dogfooding/_docs tests/weird-paths/weird-paths/{brace}.md new file mode 100644 index 0000000000..882aaad0a8 --- /dev/null +++ b/website/_dogfooding/_docs tests/weird-paths/weird-paths/{brace}.md @@ -0,0 +1 @@ +# Filename with braces diff --git a/website/_dogfooding/docs-tests-sidebars.js b/website/_dogfooding/docs-tests-sidebars.js index 7e8a83ea49..d0bec41820 100644 --- a/website/_dogfooding/docs-tests-sidebars.js +++ b/website/_dogfooding/docs-tests-sidebars.js @@ -27,6 +27,10 @@ const sidebars = { type: 'autogenerated', dirName: 'tests', }, + { + type: 'autogenerated', + dirName: 'weird-paths', + }, { type: 'link', label: 'External Link test',