1import Constants from 'expo-constants'; 2import qs from 'qs'; 3import URL from 'url-parse'; 4import { hasCustomScheme, resolveScheme } from './Schemes'; 5import { validateURL } from './validateURL'; 6function getHostUri() { 7 if (Constants.expoConfig?.hostUri) { 8 return Constants.expoConfig.hostUri; 9 } 10 else if (!hasCustomScheme()) { 11 // we're probably not using up-to-date xdl, so just fake it for now 12 // we have to remove the /--/ on the end since this will be inserted again later 13 return removeScheme(Constants.linkingUri).replace(/\/--($|\/.*$)/, ''); 14 } 15 else { 16 return null; 17 } 18} 19function isExpoHosted() { 20 const hostUri = getHostUri(); 21 return !!(hostUri && 22 (/^(.*\.)?(expo\.io|exp\.host|exp\.direct|expo\.test|expo\.dev)(:.*)?(\/.*)?$/.test(hostUri) || 23 Constants.expoGoConfig?.developer)); 24} 25function removeScheme(url) { 26 return url.replace(/^[a-zA-Z0-9+.-]+:\/\//, ''); 27} 28function removePort(url) { 29 return url.replace(/(?=([a-zA-Z0-9+.-]+:\/\/)?[^/]):\d+/, ''); 30} 31function removeLeadingSlash(url) { 32 return url.replace(/^\//, ''); 33} 34function removeTrailingSlashAndQueryString(url) { 35 return url.replace(/\/?\?.*$/, ''); 36} 37function ensureLeadingSlash(input, shouldAppend) { 38 const hasSlash = input.startsWith('/'); 39 if (hasSlash && !shouldAppend) { 40 return input.substring(1); 41 } 42 else if (!hasSlash && shouldAppend) { 43 return `/${input}`; 44 } 45 return input; 46} 47// @needsAudit 48/** 49 * Helper method for constructing a deep link into your app, given an optional path and set of query 50 * parameters. Creates a URI scheme with two slashes by default. 51 * 52 * The scheme in bare and standalone must be defined in the Expo config (`app.config.js` or `app.json`) 53 * under `expo.scheme`. 54 * 55 * # Examples 56 * - Bare: `<scheme>://path` - uses provided scheme or scheme from Expo config `scheme`. 57 * - Standalone, Custom: `yourscheme://path` 58 * - Web (dev): `https://localhost:19006/path` 59 * - Web (prod): `https://myapp.com/path` 60 * - Expo Client (dev): `exp://128.0.0.1:8081/--/path` 61 * - Expo Client (prod): `exp://exp.host/@yourname/your-app/--/path` 62 * 63 * @param path Addition path components to append to the base URL. 64 * @param namedParameters Additional options object. 65 * @return A URL string which points to your app with the given deep link information. 66 */ 67export function createURL(path, { scheme, queryParams = {}, isTripleSlashed = false } = {}) { 68 const resolvedScheme = resolveScheme({ scheme }); 69 let hostUri = getHostUri() || ''; 70 if (hasCustomScheme() && isExpoHosted()) { 71 hostUri = ''; 72 } 73 if (path) { 74 if (isExpoHosted() && hostUri) { 75 path = `/--/${removeLeadingSlash(path)}`; 76 } 77 if (isTripleSlashed && !path.startsWith('/')) { 78 path = `/${path}`; 79 } 80 } 81 else { 82 path = ''; 83 } 84 // merge user-provided query params with any that were already in the hostUri 85 // e.g. release-channel 86 let queryString = ''; 87 const queryStringMatchResult = hostUri.match(/(.*)\?(.+)/); 88 if (queryStringMatchResult) { 89 hostUri = queryStringMatchResult[1]; 90 queryString = queryStringMatchResult[2]; 91 let paramsFromHostUri = {}; 92 try { 93 const parsedParams = qs.parse(queryString); 94 if (typeof parsedParams === 'object') { 95 paramsFromHostUri = parsedParams; 96 } 97 } 98 catch { } 99 queryParams = { 100 ...queryParams, 101 ...paramsFromHostUri, 102 }; 103 } 104 queryString = qs.stringify(queryParams); 105 if (queryString) { 106 queryString = `?${queryString}`; 107 } 108 hostUri = ensureLeadingSlash(hostUri, !isTripleSlashed); 109 return encodeURI(`${resolvedScheme}:${isTripleSlashed ? '/' : ''}/${hostUri}${path}${queryString}`); 110} 111// @needsAudit 112/** 113 * Helper method for parsing out deep link information from a URL. 114 * @param url A URL that points to the currently running experience (e.g. an output of `Linking.createURL()`). 115 * @return A `ParsedURL` object. 116 */ 117export function parse(url) { 118 validateURL(url); 119 const parsed = URL(url, /* parseQueryString */ true); 120 for (const param in parsed.query) { 121 parsed.query[param] = decodeURIComponent(parsed.query[param]); 122 } 123 const queryParams = parsed.query; 124 const hostUri = getHostUri() || ''; 125 const hostUriStripped = removePort(removeTrailingSlashAndQueryString(hostUri)); 126 let path = parsed.pathname || null; 127 let hostname = parsed.hostname || null; 128 let scheme = parsed.protocol || null; 129 if (scheme) { 130 // Remove colon at end 131 scheme = scheme.substring(0, scheme.length - 1); 132 } 133 if (path) { 134 path = removeLeadingSlash(path); 135 let expoPrefix = null; 136 if (hostUriStripped) { 137 const parts = hostUriStripped.split('/'); 138 expoPrefix = parts.slice(1).concat(['--/']).join('/'); 139 } 140 if (isExpoHosted() && !hasCustomScheme() && expoPrefix && path.startsWith(expoPrefix)) { 141 path = path.substring(expoPrefix.length); 142 hostname = null; 143 } 144 else if (path.indexOf('+') > -1) { 145 path = path.substring(path.indexOf('+') + 1); 146 } 147 } 148 return { 149 hostname, 150 path, 151 queryParams, 152 scheme, 153 }; 154} 155//# sourceMappingURL=createURL.js.map