168 lines
6.2 KiB
JavaScript
168 lines
6.2 KiB
JavaScript
import Constants from 'expo-constants';
|
|
import { hasCustomScheme, resolveScheme } from './Schemes';
|
|
import { validateURL } from './validateURL';
|
|
function getHostUri() {
|
|
if (Constants.expoConfig?.hostUri) {
|
|
return Constants.expoConfig.hostUri;
|
|
}
|
|
else if (!hasCustomScheme()) {
|
|
// we're probably not using up-to-date xdl, so just fake it for now
|
|
// we have to remove the /--/ on the end since this will be inserted again later
|
|
return removeScheme(Constants.linkingUri).replace(/\/--($|\/.*$)/, '');
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
}
|
|
function isExpoHosted() {
|
|
const hostUri = getHostUri();
|
|
return !!(hostUri &&
|
|
(/^(.*\.)?(expo\.io|exp\.host|exp\.direct|expo\.test|expo\.dev)(:.*)?(\/.*)?$/.test(hostUri) ||
|
|
Constants.expoGoConfig?.developer));
|
|
}
|
|
function removeScheme(url) {
|
|
return url.replace(/^[a-zA-Z0-9+.-]+:\/\//, '');
|
|
}
|
|
function removePort(url) {
|
|
return url.replace(/(?=([a-zA-Z0-9+.-]+:\/\/)?[^/]):\d+/, '');
|
|
}
|
|
function removeLeadingSlash(url) {
|
|
return url.replace(/^\//, '');
|
|
}
|
|
function removeTrailingSlashAndQueryString(url) {
|
|
return url.replace(/\/?\?.*$/, '');
|
|
}
|
|
function ensureLeadingSlash(input, shouldAppend) {
|
|
const hasSlash = input.startsWith('/');
|
|
if (hasSlash && !shouldAppend) {
|
|
return input.substring(1);
|
|
}
|
|
else if (!hasSlash && shouldAppend) {
|
|
return `/${input}`;
|
|
}
|
|
return input;
|
|
}
|
|
// @needsAudit
|
|
/**
|
|
* Helper method for constructing a deep link into your app, given an optional path and set of query
|
|
* parameters. Creates a URI scheme with two slashes by default.
|
|
*
|
|
* The scheme must be defined in the [app config](./../config/app) under `expo.scheme`
|
|
* or `expo.{android,ios}.scheme`. Platform-specific schemes defined under `expo.{android,ios}.scheme`
|
|
* take precedence over universal schemes defined under `expo.scheme`.
|
|
*
|
|
* # Examples
|
|
* - Development and production builds: `<scheme>://path` - uses the optional `scheme` property if provided, and otherwise uses the first scheme defined by your app config
|
|
* - Web (dev): `https://localhost:19006/path`
|
|
* - Web (prod): `https://myapp.com/path`
|
|
* - Expo Go (dev): `exp://128.0.0.1:8081/--/path`
|
|
*
|
|
* The behavior of this method in Expo Go for published updates is undefined and should not be relied upon.
|
|
* The created URL in this case is neither stable nor predictable during the lifetime of the app.
|
|
* If a stable URL is needed, for example in authorization callbacks, a build (or development build)
|
|
* of your application should be used and the scheme provided.
|
|
*
|
|
* @param path Addition path components to append to the base URL.
|
|
* @param namedParameters Additional options object.
|
|
* @return A URL string which points to your app with the given deep link information.
|
|
*/
|
|
export function createURL(path, { scheme, queryParams = {}, isTripleSlashed = false } = {}) {
|
|
const resolvedScheme = resolveScheme({ scheme });
|
|
let hostUri = getHostUri() || '';
|
|
if (hasCustomScheme() && isExpoHosted()) {
|
|
hostUri = '';
|
|
}
|
|
if (path) {
|
|
if (isExpoHosted() && hostUri) {
|
|
path = `/--/${removeLeadingSlash(path)}`;
|
|
}
|
|
if (isTripleSlashed && !path.startsWith('/')) {
|
|
path = `/${path}`;
|
|
}
|
|
}
|
|
else {
|
|
path = '';
|
|
}
|
|
// merge user-provided query params with any that were already in the hostUri
|
|
// e.g. release-channel
|
|
let queryString = '';
|
|
const queryStringMatchResult = hostUri.match(/(.*)\?(.+)/);
|
|
if (queryStringMatchResult) {
|
|
hostUri = queryStringMatchResult[1];
|
|
queryString = queryStringMatchResult[2];
|
|
let paramsFromHostUri = {};
|
|
try {
|
|
paramsFromHostUri = Object.fromEntries(
|
|
// @ts-ignore: [Symbol.iterator] is indeed, available on every platform.
|
|
new URLSearchParams(queryString));
|
|
}
|
|
catch { }
|
|
queryParams = {
|
|
...queryParams,
|
|
...paramsFromHostUri,
|
|
};
|
|
}
|
|
queryString = new URLSearchParams(
|
|
// For legacy purposes, we'll strip out the nullish values before creating the URL.
|
|
Object.fromEntries(Object.entries(queryParams).filter(([, value]) => value != null))).toString();
|
|
if (queryString) {
|
|
queryString = `?${queryString}`;
|
|
}
|
|
hostUri = ensureLeadingSlash(hostUri, !isTripleSlashed);
|
|
// URLSearchParams.stringify already encodes query parameters, so we only need to encode the remaining part of the URL.
|
|
const encodedURI = encodeURI(`${resolvedScheme}:${isTripleSlashed ? '/' : ''}/${hostUri}${path}`);
|
|
return `${encodedURI}${queryString}`;
|
|
}
|
|
// @needsAudit
|
|
/**
|
|
* Helper method for parsing out deep link information from a URL.
|
|
* @param url A URL that points to the currently running experience (for example, an output of `Linking.createURL()`).
|
|
* @return A `ParsedURL` object.
|
|
*/
|
|
export function parse(url) {
|
|
validateURL(url);
|
|
const queryParams = {};
|
|
let path = null;
|
|
let hostname = null;
|
|
let scheme = null;
|
|
try {
|
|
const parsed = new URL(url);
|
|
parsed.searchParams.forEach((value, key) => {
|
|
queryParams[key] = decodeURIComponent(value);
|
|
});
|
|
path = parsed.pathname || null;
|
|
hostname = parsed.hostname || null;
|
|
scheme = parsed.protocol || null;
|
|
}
|
|
catch {
|
|
path = url;
|
|
}
|
|
const hostUri = getHostUri() || '';
|
|
const hostUriStripped = removePort(removeTrailingSlashAndQueryString(hostUri));
|
|
if (scheme) {
|
|
// Remove colon at end
|
|
scheme = scheme.substring(0, scheme.length - 1);
|
|
}
|
|
if (path) {
|
|
path = removeLeadingSlash(path);
|
|
let expoPrefix = null;
|
|
if (hostUriStripped) {
|
|
const parts = hostUriStripped.split('/');
|
|
expoPrefix = parts.slice(1).concat(['--/']).join('/');
|
|
}
|
|
if (isExpoHosted() && !hasCustomScheme() && expoPrefix && path.startsWith(expoPrefix)) {
|
|
path = path.substring(expoPrefix.length);
|
|
hostname = null;
|
|
}
|
|
else if (path.indexOf('+') > -1) {
|
|
path = path.substring(path.indexOf('+') + 1);
|
|
}
|
|
}
|
|
return {
|
|
hostname,
|
|
path,
|
|
queryParams,
|
|
scheme,
|
|
};
|
|
}
|
|
//# sourceMappingURL=createURL.js.map
|