From adc0325e7178cf1b8c2c080f955471393a168611 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 27 Nov 2025 17:16:42 +0100 Subject: [PATCH 1/3] hasProtocol should implement the spec more strictly --- .../exports/__tests__/isInternalUrl.test.ts | 48 ++++++++++++++++++- .../src/client/exports/isInternalUrl.ts | 5 +- .../_pages tests/markdown-tests-mdx.mdx | 8 ++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/packages/docusaurus/src/client/exports/__tests__/isInternalUrl.test.ts b/packages/docusaurus/src/client/exports/__tests__/isInternalUrl.test.ts index a91476457447..544bc5889332 100644 --- a/packages/docusaurus/src/client/exports/__tests__/isInternalUrl.test.ts +++ b/packages/docusaurus/src/client/exports/__tests__/isInternalUrl.test.ts @@ -28,7 +28,7 @@ describe('isInternalUrl', () => { expect(isInternalUrl('https://foo.com')).toBeFalsy(); }); - it('returns false for whatever protocol links', () => { + it('returns false for relative protocol links', () => { expect(isInternalUrl('//foo.com')).toBeFalsy(); }); @@ -43,4 +43,50 @@ describe('isInternalUrl', () => { it('returns false for undefined links', () => { expect(isInternalUrl(undefined)).toBeFalsy(); }); + + describe('custom scheme links', () => { + it('returns true for invalid protocol schemes', () => { + expect(isInternalUrl('+customScheme://')).toBeTruthy(); + expect(isInternalUrl('+customScheme://whatever')).toBeTruthy(); + expect(isInternalUrl('+customScheme:whatever')).toBeTruthy(); + + expect(isInternalUrl('.customScheme://')).toBeTruthy(); + expect(isInternalUrl('.customScheme://whatever')).toBeTruthy(); + expect(isInternalUrl('.customScheme:whatever')).toBeTruthy(); + + expect(isInternalUrl('-customScheme://')).toBeTruthy(); + expect(isInternalUrl('-customScheme://whatever')).toBeTruthy(); + expect(isInternalUrl('-customScheme:whatever')).toBeTruthy(); + + expect(isInternalUrl('custom_scheme://')).toBeTruthy(); + expect(isInternalUrl('custom_scheme://whatever')).toBeTruthy(); + expect(isInternalUrl('custom_scheme:whatever')).toBeTruthy(); + + expect(isInternalUrl('custom scheme://')).toBeTruthy(); + expect(isInternalUrl('custom scheme://whatever')).toBeTruthy(); + expect(isInternalUrl('custom scheme:whatever')).toBeTruthy(); + + expect(isInternalUrl('custom$scheme://')).toBeTruthy(); + expect(isInternalUrl('custom$scheme://whatever')).toBeTruthy(); + expect(isInternalUrl('custom$scheme:whatever')).toBeTruthy(); + }); + + it('returns false valid protocol schemes', () => { + expect(isInternalUrl('customScheme://')).toBeFalsy(); + expect(isInternalUrl('customScheme://whatever')).toBeFalsy(); + expect(isInternalUrl('customScheme:whatever')).toBeFalsy(); + + expect(isInternalUrl('custom-scheme://')).toBeFalsy(); + expect(isInternalUrl('custom-scheme://whatever')).toBeFalsy(); + expect(isInternalUrl('custom-scheme:whatever')).toBeFalsy(); + + expect(isInternalUrl('custom.scheme://')).toBeFalsy(); + expect(isInternalUrl('custom.scheme://whatever')).toBeFalsy(); + expect(isInternalUrl('custom.scheme:whatever')).toBeFalsy(); + + expect(isInternalUrl('custom-sch.eme+-.://')).toBeFalsy(); + expect(isInternalUrl('custom-sch.eme+-.://whatever')).toBeFalsy(); + expect(isInternalUrl('custom-sch.eme+-.:whatever')).toBeFalsy(); + }); + }); }); diff --git a/packages/docusaurus/src/client/exports/isInternalUrl.ts b/packages/docusaurus/src/client/exports/isInternalUrl.ts index 489aff74bc0d..6335ae003e61 100644 --- a/packages/docusaurus/src/client/exports/isInternalUrl.ts +++ b/packages/docusaurus/src/client/exports/isInternalUrl.ts @@ -5,8 +5,11 @@ * LICENSE file in the root directory of this source tree. */ +// Poor man's protocol detection +// Spec: https://datatracker.ietf.org/doc/html/rfc3986#section-3.1 +// In particular: scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) export function hasProtocol(url: string): boolean { - return /^(?:\w*:|\/\/)/.test(url); + return /^(?:[A-Za-z][A-Za-z\d+.-]*:|\/\/)/.test(url); } export default function isInternalUrl(url?: string): boolean { diff --git a/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx b/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx index ec3a7534af01..d6de3bbd1378 100644 --- a/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx +++ b/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx @@ -383,6 +383,14 @@ See [#3309](https://github.com/facebook/docusaurus/issues/3309) - [pathname://../dogfooding/javadoc/index.html](pathname://../dogfooding/javadoc/index.html) +### Linking to non-SPA paths with custom schemes + +- [customScheme://whatever](customScheme://customSchemePath) + +- [custom-scheme://whatever](custom-scheme://customSchemePath) + +- [custom-sch.eme+://whatever](custom-scheme://customSchemePath3) + ### Linking to non-SPA page with Link component See [#9758](https://github.com/facebook/docusaurus/issues/9758), these external urls should not be reported by the broken links checker: From ecad6c61b170ed0e4a11868f3026f0812744ee71 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 27 Nov 2025 17:23:08 +0100 Subject: [PATCH 2/3] dogfood fix --- website/_dogfooding/_pages tests/markdown-tests-mdx.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx b/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx index d6de3bbd1378..dc909d68abf1 100644 --- a/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx +++ b/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx @@ -385,11 +385,11 @@ See [#3309](https://github.com/facebook/docusaurus/issues/3309) ### Linking to non-SPA paths with custom schemes -- [customScheme://whatever](customScheme://customSchemePath) +- [customScheme://whatever](customScheme://whatever) -- [custom-scheme://whatever](custom-scheme://customSchemePath) +- [custom-scheme://whatever](custom-scheme://whatever) -- [custom-sch.eme+://whatever](custom-scheme://customSchemePath3) +- [custom-sch.eme+://whatever](custom-sch.eme+://whatever) ### Linking to non-SPA page with Link component From 4de2061dea88d6a9cd43e67d9c354ab3ed790f39 Mon Sep 17 00:00:00 2001 From: sebastien Date: Thu, 27 Nov 2025 17:24:21 +0100 Subject: [PATCH 3/3] remove useless dogfood line breaks --- .../_dogfooding/_pages tests/markdown-tests-mdx.mdx | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx b/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx index dc909d68abf1..55153cd3ca1a 100644 --- a/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx +++ b/website/_dogfooding/_pages tests/markdown-tests-mdx.mdx @@ -360,15 +360,10 @@ This is a test page to see if Docusaurus Markdown features are working properly See [#3337](https://github.com/facebook/docusaurus/issues/3337) - [/someFile.pdf](/someFile.pdf) - - [/someFile.xyz](/someFile.xyz) - - [/image with space.png]() - - [@site/\_dogfooding/\_asset-tests/someFile.pdf](@site/_dogfooding/_asset-tests/someFile.pdf) - - [@site/\_dogfooding/\_asset-tests/someFile.xyz](@site/_dogfooding/_asset-tests/someFile.xyz) - - [@site/\_dogfooding/\_asset-tests/image with space.png](<@site/_dogfooding/_asset-tests/image with spaces.png>) ### Linking to non-SPA page hosted within website @@ -376,20 +371,15 @@ See [#3337](https://github.com/facebook/docusaurus/issues/3337) See [#3309](https://github.com/facebook/docusaurus/issues/3309) - [pathname:///dogfooding/javadoc](pathname:///dogfooding/javadoc) - - [pathname:///dogfooding/javadoc/index.html](pathname:///dogfooding/javadoc/index.html) - - [pathname://../dogfooding/javadoc](pathname://../dogfooding/javadoc) - - [pathname://../dogfooding/javadoc/index.html](pathname://../dogfooding/javadoc/index.html) ### Linking to non-SPA paths with custom schemes - [customScheme://whatever](customScheme://whatever) - - [custom-scheme://whatever](custom-scheme://whatever) - -- [custom-sch.eme+://whatever](custom-sch.eme+://whatever) +- [custom-sch.eme+-.://whatever](custom-sch.eme+-.://whatever) ### Linking to non-SPA page with Link component