Skip to content

fix(theme/Link): support custom URI schemes in external URL detection#3304

Open
Copilot wants to merge 3 commits intomainfrom
copilot/support-custom-uri-schemes
Open

fix(theme/Link): support custom URI schemes in external URL detection#3304
Copilot wants to merge 3 commits intomainfrom
copilot/support-custom-uri-schemes

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 13, 2026

editLink and the theme Link component regressed for custom URI schemes such as vscode:// and jetbrains://, causing them to be treated as relative paths and resolved against the current page URL. This updates external URL detection so valid custom schemes continue to work while preserving existing behavior for normal site links.

  • What changed

    • Replaced the hardcoded http/https/mailto/tel checks in isExternalUrl() with scheme-based detection.
    • Kept unsafe or unintended schemes out of the external-link path:
      • javascript:
      • data:
      • plain file:
  • Where the fix applies

    • editLink now preserves custom IDE links such as vscode://file/...
    • Theme Link now treats custom URI schemes as external instead of resolving them via window.location
    • Any other call sites using isExternalUrl() now get the same scheme-aware behavior
  • Regression coverage

    • Added focused tests for:
      • vscode://file/...
      • jetbrains://...
      • existing https:, mailto:, tel:
      • relative and protocol-relative paths
      • blocked javascript:, data:, and file:
  • Example

    export default defineConfig({
      themeConfig: {
        editLink: {
          docRepoBaseUrl: 'vscode://file/Users/test/docs',
          text: 'Edit this page',
        },
      },
    });

    close [Feature]: support custom URI schemes (vscode://, jetbrains://) in editLink and Link component #3301

  • Screenshot

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Apr 13, 2026

Deploying rspress-v2 with  Cloudflare Pages  Cloudflare Pages

Latest commit: d7fda2c
Status: ✅  Deploy successful!
Preview URL: https://ed4bc1b3.rspress-v2.pages.dev
Branch Preview URL: https://copilot-support-custom-uri-s.rspress-v2.pages.dev

View logs

Copilot AI changed the title [WIP] Add support for custom URI schemes in editLink and Link component fix(shared): support custom URI schemes in external URL detection Apr 13, 2026
Copilot AI requested a review from SoonIter April 13, 2026 03:12
@github-actions
Copy link
Copy Markdown
Contributor

Rsdoctor Bundle Diff Analysis

Found 3 projects in monorepo, 1 project with changes.

📊 Quick Summary
Project Total Size Change
node 12.7 MB 0
node_md 1.6 MB 0
web 16.3 MB -9.0 B (-0.0%)
📋 Detailed Reports (Click to expand)

📁 web

Path: website/doc_build/diff-rsdoctor/web/rsdoctor-data.json

📌 Baseline Commit: 9ea8b703ec | PR: #3299

Metric Current Baseline Change
📊 Total Size 16.3 MB 16.3 MB -9.0 B (-0.0%)
📄 JavaScript 16.0 MB 16.0 MB -9.0 B (-0.0%)
🎨 CSS 122.4 KB 122.4 KB 0
🌐 HTML 0 B 0 B 0
📁 Other Assets 167.9 KB 167.9 KB 0

📦 Download Diff Report: web Bundle Diff

Generated by Rsdoctor GitHub Action

@SoonIter SoonIter marked this pull request as ready for review April 13, 2026 12:59
Copilot AI review requested due to automatic review settings April 13, 2026 12:59
@SoonIter SoonIter changed the title fix(shared): support custom URI schemes in external URL detection fix(theme/Link): support custom URI schemes in external URL detection Apr 13, 2026
@SoonIter SoonIter requested a review from Timeless0911 April 13, 2026 13:00
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates shared runtime URL utilities to correctly classify custom URI schemes (e.g. vscode://, jetbrains://) as external, preventing them from being rewritten/resolved as relative links in theme Link navigation and editLink handling.

Changes:

  • Replace hardcoded external URL prefixes with scheme-based detection in isExternalUrl(), while blocking javascript:, data:, and file:.
  • Add unit tests covering custom schemes, common schemes, relative/protocol-relative paths, and blocked schemes.
  • Extend normalizeHref() test coverage to ensure custom schemes are preserved.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
packages/shared/src/runtime-utils/utils.ts Switch isExternalUrl() to scheme-based detection with an unsafe-scheme denylist.
packages/shared/src/runtime-utils/utils.test.ts Add tests for the new scheme-aware behavior (custom schemes + unsafe schemes).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +84 to 87
const UNSAFE_EXTERNAL_URL_SCHEME_REGEXP = /^(?:javascript|data|file):/i;

export function isExternalUrl(url = '') {
return (
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EXTERNAL_URL_SCHEME_REGEXP will treat Windows drive-letter paths like C:\foo / C:/foo as having a URI scheme (c:), so isExternalUrl() returns true and downstream code (e.g. link classification / addLeadingSlash) will treat local paths as external. Consider explicitly excluding drive-letter patterns (e.g. ^[a-zA-Z]:[\\/]) or otherwise narrowing the scheme detection to avoid this false positive.

Suggested change
const UNSAFE_EXTERNAL_URL_SCHEME_REGEXP = /^(?:javascript|data|file):/i;
export function isExternalUrl(url = '') {
return (
const UNSAFE_EXTERNAL_URL_SCHEME_REGEXP = /^(?:javascript|data|file):/i;
const WINDOWS_DRIVE_LETTER_PATH_REGEXP = /^[a-zA-Z]:[\\/]/;
export function isExternalUrl(url = '') {
return (
!WINDOWS_DRIVE_LETTER_PATH_REGEXP.test(url) &&

Copilot uses AI. Check for mistakes.
Comment on lines +83 to 90
const EXTERNAL_URL_SCHEME_REGEXP = /^[a-zA-Z][a-zA-Z\d+\-.]*:/;
const UNSAFE_EXTERNAL_URL_SCHEME_REGEXP = /^(?:javascript|data|file):/i;

export function isExternalUrl(url = '') {
return (
url.startsWith('http://') ||
url.startsWith('https://') ||
url.startsWith('mailto:') ||
url.startsWith('tel:')
EXTERNAL_URL_SCHEME_REGEXP.test(url) &&
!UNSAFE_EXTERNAL_URL_SCHEME_REGEXP.test(url)
);
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isExternalUrl() doesn’t normalize leading whitespace before applying the scheme/unsafe-scheme regexes. Inputs like ' javascript:alert(1)' or '\n data:text/plain,...' won’t match UNSAFE_EXTERNAL_URL_SCHEME_REGEXP, and also won’t match the scheme regexp, leading to inconsistent handling vs isDataUrl() (which allows leading whitespace). Consider trimStart() (or using ^\s* in both regexes) so unsafe schemes are consistently recognized and blocked even when padded.

Copilot uses AI. Check for mistakes.
Comment on lines +103 to +106
expect(isExternalUrl('//example.com/foo')).toBe(false);
expect(isExternalUrl('file:///Users/test/docs/index.md')).toBe(false);
expect(isExternalUrl('javascript:alert(1)')).toBe(false);
expect(isExternalUrl('data:text/plain,hello')).toBe(false);
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The isExternalUrl test set covers custom schemes and blocked schemes, but it doesn’t cover the new edge cases introduced by scheme-based detection (e.g. Windows drive-letter paths like C:\\Users\\... / C:/Users/..., or unsafe schemes with leading whitespace). Adding assertions for these would help prevent regressions from the new regex behavior.

Suggested change
expect(isExternalUrl('//example.com/foo')).toBe(false);
expect(isExternalUrl('file:///Users/test/docs/index.md')).toBe(false);
expect(isExternalUrl('javascript:alert(1)')).toBe(false);
expect(isExternalUrl('data:text/plain,hello')).toBe(false);
expect(isExternalUrl('//example.com/foo')).toBe(false);
expect(isExternalUrl('C:\\Users\\test\\docs\\index.md')).toBe(false);
expect(isExternalUrl('C:/Users/test/docs/index.md')).toBe(false);
expect(isExternalUrl('file:///Users/test/docs/index.md')).toBe(false);
expect(isExternalUrl('javascript:alert(1)')).toBe(false);
expect(isExternalUrl(' javascript:alert(1)')).toBe(false);
expect(isExternalUrl('data:text/plain,hello')).toBe(false);
expect(isExternalUrl(' data:text/plain,hello')).toBe(false);

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: support custom URI schemes (vscode://, jetbrains://) in editLink and Link component

3 participants