UNPKG

one

Version:

One is a new React Framework that makes Vite serve both native and web.

176 lines (145 loc) 5.28 kB
export function parsePathAndParamsFromExpoGoLink(url: string): { pathname: string queryString: string } { // If the URL is defined (default in Expo Go dev apps) and the URL has no path: // `exp://192.168.87.39:19000/` then use the default `exp://192.168.87.39:19000/--/` const href = parsePathFromExpoGoLink(url) const results = href.match(/([^?]*)(\?.*)?/) return { pathname: results?.[1] ?? '', queryString: results?.[2] ?? '', } } export function parsePathFromExpoGoLink(url: string): string { // If the URL is defined (default in Expo Go dev apps) and the URL has no path: // `exp://192.168.87.39:19000/` then use the default `exp://192.168.87.39:19000/--/` return url.match(/exps?:\/\/.*?\/--\/(.*)/)?.[1] ?? '' } // This is only run on native. function extractExactPathFromURL(url: string): string { if ( // If a universal link / app link / web URL is used, we should use the path // from the URL, while stripping the origin. url.match(/^https?:\/\//) ) { const { origin, href, hostname } = new URL(url) if (hostname === 'exp.host' || hostname === 'u.expo.dev') { // These are QR code generate deep-link that always like to the '/' path // TODO: In the future, QR code may link to a specific path and this logic will need to be udpated return '' } return href.replace(origin, '') } const isExpoGo = typeof globalThis.expo !== 'undefined' && globalThis.expo?.modules?.ExpoGo // Handle special URLs used in Expo Go: `/--/pathname` -> `pathname` if ( isExpoGo && // while not exhaustive, `exp` and `exps` are the only two schemes which // are passed through to other apps in Expo Go. url.match(/^exp(s)?:\/\//) ) { const pathname = parsePathFromExpoGoLink(url) if (pathname) { return fromDeepLink('a://' + pathname) } // Match the `?.*` segment of the URL. const queryParams = url.match(/exps?:\/\/.*\?(.*)/)?.[1] if (queryParams) { return fromDeepLink('a://?' + queryParams) } return '' } // TODO: Support dev client URLs return fromDeepLink(url) } /** Major hack to support the makeshift expo-development-client system. */ function isExpoDevelopmentClient(url: URL): boolean { return url.hostname === 'expo-development-client' } function fromDeepLink(url: string): string { let res: URL | null try { // This is for all standard deep links, e.g. `foobar://` where everything // after the `://` is the path. res = new URL(url) } catch { /** * We failed to parse the URL. This can occur for a variety of reasons, including: * - Its a partial URL (e.g. `/route?query=param`). * - It has a valid App scheme, but the scheme isn't a valid URL scheme (e.g. `my_app://`) */ // If `url` is already a path (starts with `/`), return it as-is // This prevents incorrect rewrites when URL is in query params (e.g. `/?url=https://example.com`) if (url.startsWith('/')) { return url } /** * App schemes are not valid URL schemes, so they will fail to parse. * We need to strip the scheme from these URLs */ return url.replace(/^[^:]+:\/\//, '') } if (isExpoDevelopmentClient(res)) { if (!res.searchParams.get('url')) { return '' } const incomingUrl = res.searchParams.get('url')! return extractExactPathFromURL(decodeURI(incomingUrl)) } let results = '' if (res.host) { results += res.host } if (res.pathname) { results += res.pathname } const qs = !res.search ? '' : // @ts-ignore: `entries` is not on `URLSearchParams` in some typechecks. [...res.searchParams.entries()] .map(([k, v]) => `${k}=${decodeURIComponent(v)}`) .join('&') if (qs) { results += '?' + qs } return results } export function extractExpoPathFromURL(_prefixes: string[], url = '') { const pathFromPrefix = extractPathFromPrefix(_prefixes, url) if (pathFromPrefix !== undefined) { return pathFromPrefix.replace(/^\//, '') } return ( extractExactPathFromURL(url) // TODO: We should get rid of this, dropping specificities is not good .replace(/^\//, '') ) } function extractPathFromPrefix(prefixes: string[] = [], url: string) { const prefix = getMatchingPrefix(prefixes, url) if (!prefix) return undefined return url.slice(prefix.length) } const PREFIX_BOUNDARY_CHARS = ['/', '?', '#'] function getMatchingPrefix(prefixes: string[], url: string) { return prefixes .filter((prefix) => matchesPrefix(prefix, url)) .sort((a, b) => b.length - a.length)[0] } function matchesPrefix(prefix: string, url: string) { if (!url.startsWith(prefix)) return false if (url.length === prefix.length) return true // a prefix already ending in '/' has consumed its boundary if (prefix.endsWith('/')) return true return PREFIX_BOUNDARY_CHARS.includes(url[prefix.length]) } export function adjustPathname(url: { hostname?: string | null; pathname: string }) { if (url.hostname === 'exp.host' || url.hostname === 'u.expo.dev') { // drop the first two segments from pathname: return url.pathname.split('/').slice(2).join('/') } return url.pathname } export const extractPathFromURL = extractExpoPathFromURL