diff --git a/packages/expo-router/src/global-state/routing.ts b/packages/expo-router/src/global-state/routing.ts index e005d1a5..ef6c88f7 100644 --- a/packages/expo-router/src/global-state/routing.ts +++ b/packages/expo-router/src/global-state/routing.ts @@ -7,7 +7,7 @@ import * as Linking from "expo-linking"; import { ResultState } from "../fork/getStateFromPath"; import { Href, resolveHref } from "../link/href"; import { resolve } from "../link/path"; -import { hasUrlProtocolPrefix } from "../utils/url"; +import { shouldLinkExternally } from "../utils/url"; import type { RouterStore } from "./router-store"; function assertIsReady(store: RouterStore) { @@ -52,7 +52,7 @@ export function setParams( } export function linkTo(this: RouterStore, href: string, event?: string) { - if (hasUrlProtocolPrefix(href)) { + if (shouldLinkExternally(href)) { Linking.openURL(href); return; } diff --git a/packages/expo-router/src/utils/url.ts b/packages/expo-router/src/utils/url.ts index 603087bf..e9198e1b 100644 --- a/packages/expo-router/src/utils/url.ts +++ b/packages/expo-router/src/utils/url.ts @@ -5,3 +5,19 @@ export function hasUrlProtocolPrefix(href: string): boolean { return /^[\w\d_+.-]+:\/\//.test(href); } + +export function isWellKnownUri(href: string): boolean { + // This is a hack and we should change this to work like the web in the future where we have full confidence in the + // ability to match URLs and send anything unmatched to the OS. The main difference between this and `hasUrlProtocolPrefix` is + // that we don't require `//`, e.g. `mailto:` is valid and common, and `mailto://bacon` is invalid. + return /^(https?|mailto|tel|sms|geo|maps|market|itmss?|itms-apps|content|file):/.test( + href + ); +} + +export function shouldLinkExternally(href: string): boolean { + // Cheap check first to avoid regex if the href is not a path fragment. + return ( + !/^[./]/.test(href) && (hasUrlProtocolPrefix(href) || isWellKnownUri(href)) + ); +}