"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.constructAssetsUrlAsync = constructAssetsUrlAsync; exports.constructBundleQueryParams = constructBundleQueryParams; exports.constructBundleQueryParamsWithConfig = constructBundleQueryParamsWithConfig; exports.constructBundleUrlAsync = constructBundleUrlAsync; exports.constructDebuggerHostAsync = constructDebuggerHostAsync; exports.constructDeepLinkAsync = constructDeepLinkAsync; exports.constructDevClientUrlAsync = constructDevClientUrlAsync; exports.constructHostUriAsync = constructHostUriAsync; exports.constructLoadingUrlAsync = constructLoadingUrlAsync; exports.constructLogUrlAsync = constructLogUrlAsync; exports.constructManifestUrlAsync = constructManifestUrlAsync; exports.constructPublishUrlAsync = constructPublishUrlAsync; exports.constructSourceMapUrlAsync = constructSourceMapUrlAsync; exports.constructUrlAsync = constructUrlAsync; exports.constructUrlWithExtensionAsync = constructUrlWithExtensionAsync; exports.constructWebAppUrlAsync = constructWebAppUrlAsync; exports.isHttps = isHttps; exports.isURL = isURL; exports.stripJSExtension = stripJSExtension; function _config() { const data = require("@expo/config"); _config = function () { return data; }; return data; } function _assert() { const data = _interopRequireDefault(require("assert")); _assert = function () { return data; }; return data; } function _os() { const data = _interopRequireDefault(require("os")); _os = function () { return data; }; return data; } function _querystring() { const data = _interopRequireDefault(require("querystring")); _querystring = function () { return data; }; return data; } function _resolveFrom() { const data = _interopRequireDefault(require("resolve-from")); _resolveFrom = function () { return data; }; return data; } function _url() { const data = _interopRequireDefault(require("url")); _url = function () { return data; }; return data; } function _internal() { const data = require("./internal"); _internal = function () { return data; }; return data; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } async function constructBundleUrlAsync(projectRoot, opts, requestHostname) { return await constructUrlAsync(projectRoot, opts, true, requestHostname); } async function constructDeepLinkAsync(projectRoot, opts, requestHostname) { const { devClient } = await _internal().ProjectSettings.readAsync(projectRoot); if (devClient) { return constructDevClientUrlAsync(projectRoot, opts, requestHostname); } else { return constructManifestUrlAsync(projectRoot, opts, requestHostname); } } async function constructManifestUrlAsync(projectRoot, opts, requestHostname) { return await constructUrlAsync(projectRoot, opts !== null && opts !== void 0 ? opts : null, false, requestHostname); } async function constructDevClientUrlAsync(projectRoot, opts, requestHostname) { let _scheme; if (opts !== null && opts !== void 0 && opts.scheme) { _scheme = opts === null || opts === void 0 ? void 0 : opts.scheme; } else { const { scheme } = await _internal().ProjectSettings.readAsync(projectRoot); if (!scheme || typeof scheme !== 'string') { throw new (_internal().XDLError)('NO_DEV_CLIENT_SCHEME', 'No scheme specified for development client'); } _scheme = scheme; } const protocol = resolveProtocol(projectRoot, { scheme: _scheme, urlType: 'custom' }); const manifestUrl = await constructManifestUrlAsync(projectRoot, { ...opts, urlType: 'http' }, requestHostname); return `${protocol}://expo-development-client/?url=${encodeURIComponent(manifestUrl)}`; } // gets the base manifest URL and removes the scheme async function constructHostUriAsync(projectRoot, requestHostname) { const urlString = await constructUrlAsync(projectRoot, null, false, requestHostname); // we need to use node's legacy urlObject api since the newer one doesn't like empty protocols const urlObj = _url().default.parse(urlString); urlObj.protocol = ''; urlObj.slashes = false; return _url().default.format(urlObj); } async function constructLogUrlAsync(projectRoot, requestHostname) { const baseUrl = await constructUrlAsync(projectRoot, { urlType: 'http' }, false, requestHostname); return `${baseUrl}/logs`; } async function constructLoadingUrlAsync(projectRoot, platform, requestHostname) { const baseUrl = await constructUrlAsync(projectRoot, { urlType: 'http' }, false, requestHostname); const query = platform ? `?platform=${platform}` : ''; return `${baseUrl}/_expo/loading${query}`; } async function constructUrlWithExtensionAsync(projectRoot, entryPoint, ext, requestHostname, metroQueryOptions) { const defaultOpts = { dev: false, minify: true }; metroQueryOptions = metroQueryOptions || defaultOpts; let bundleUrl = await constructBundleUrlAsync(projectRoot, { hostType: 'localhost', urlType: 'http' }, requestHostname); const mainModulePath = stripJSExtension(entryPoint); bundleUrl += `/${mainModulePath}.${ext}`; const queryParams = constructBundleQueryParams(projectRoot, metroQueryOptions); return `${bundleUrl}?${queryParams}`; } async function constructPublishUrlAsync(projectRoot, entryPoint, requestHostname, metroQueryOptions) { return await constructUrlWithExtensionAsync(projectRoot, entryPoint, 'bundle', requestHostname, metroQueryOptions); } async function constructSourceMapUrlAsync(projectRoot, entryPoint, requestHostname) { return await constructUrlWithExtensionAsync(projectRoot, entryPoint, 'map', requestHostname); } async function constructAssetsUrlAsync(projectRoot, entryPoint, requestHostname) { return await constructUrlWithExtensionAsync(projectRoot, entryPoint, 'assets', requestHostname); } async function constructDebuggerHostAsync(projectRoot, requestHostname) { return await constructUrlAsync(projectRoot, { urlType: 'no-protocol' }, true, requestHostname); } function constructBundleQueryParams(projectRoot, opts) { // No SDK Version will assume the latest requirements const { exp } = (0, _config().getConfig)(projectRoot, { skipSDKVersionRequirement: true }); return constructBundleQueryParamsWithConfig(projectRoot, opts, exp); } function constructBundleQueryParamsWithConfig(projectRoot, opts, exp) { const queryParams = { dev: !!opts.dev, hot: false }; if ('strict' in opts) { queryParams.strict = !!opts.strict; } if ('minify' in opts) { // TODO: Maybe default this to true if dev is false queryParams.minify = !!opts.minify; } // TODO: Remove this ... // SDK11 to SDK32 require us to inject hashAssetFiles through the params, but this is not // needed with SDK33+ if (_internal().Versions.lteSdkVersion(exp, '10.0.0')) { // SDK <=10 // Only sdk-10.1.0+ supports the assetPlugin parameter. We use only the // major version in the sdkVersion field, so check for 11.0.0 to be sure. queryParams.includeAssetFileHashes = true; } else if (_internal().Versions.lteSdkVersion(exp, '32.0.0')) { // SDK 11-32 // Use an absolute path here so that we can not worry about symlinks/relative requires const pluginModule = (0, _resolveFrom().default)(projectRoot, 'expo/tools/hashAssetFiles'); queryParams.assetPlugin = encodeURIComponent(pluginModule); } // Special requirements aren't needed after SDK 33 (Jun 5 2019) return _querystring().default.stringify(queryParams); } async function constructWebAppUrlAsync(projectRoot, options = {}) { var _options$hostType; const packagerInfo = await _internal().ProjectSettings.readPackagerInfoAsync(projectRoot); if (!packagerInfo.webpackServerPort) { return null; } const { https, hostType } = await _internal().ProjectSettings.readAsync(projectRoot); const host = ((_options$hostType = options.hostType) !== null && _options$hostType !== void 0 ? _options$hostType : hostType) === 'localhost' ? 'localhost' : _internal().ip.address(); let urlType = 'http'; if (https === true) { urlType = 'https'; } return `${urlType}://${host}:${packagerInfo.webpackServerPort}`; } function assertValidOptions(opts) { if (opts.devClient && typeof opts.devClient !== 'boolean') { throw new (_internal().XDLError)('INVALID_OPTIONS', `"devClient" must be a boolean if specified`); } if (opts.scheme && typeof opts.scheme !== 'string') { throw new (_internal().XDLError)('INVALID_OPTIONS', `"scheme" must be a string if specified`); } if (![undefined, null, 'exp', 'http', 'redirect', 'no-protocol'].includes(opts.urlType)) { throw new (_internal().XDLError)('INVALID_OPTIONS', `"urlType" must be one of: "exp", "http", "redirect", "no-protocol" if specified`); } if (![undefined, 'ip', 'hostname'].includes(opts.lanType)) { throw new (_internal().XDLError)('INVALID_OPTIONS', `"lanType" must be one of: "ip", "hostname" if specified`); } if (![undefined, 'localhost', 'lan', 'tunnel'].includes(opts.hostType)) { throw new (_internal().XDLError)('INVALID_OPTIONS', `"hostType" must be one of: "localhost", "lan", "tunnel" if specified`); } if (opts.dev && typeof opts.dev !== 'boolean') { throw new (_internal().XDLError)('INVALID_OPTIONS', `"dev" must be a boolean if specified`); } if (opts.strict && typeof opts.strict !== 'boolean') { throw new (_internal().XDLError)('INVALID_OPTIONS', `"strict" must be a boolean if specified`); } if (opts.minify && typeof opts.minify !== 'boolean') { throw new (_internal().XDLError)('INVALID_OPTIONS', `"minify" must be a boolean if specified`); } if (opts.https && typeof opts.https !== 'boolean') { throw new (_internal().XDLError)('INVALID_OPTIONS', `"https" must be a boolean if specified`); } if (opts.urlRandomness && typeof opts.urlRandomness !== 'string') { throw new (_internal().XDLError)('INVALID_OPTIONS', `"urlRandomness" must be a string if specified`); } Object.keys(opts).forEach(key => { if (!['devClient', 'scheme', 'urlType', 'lanType', 'hostType', 'dev', 'strict', 'minify', 'https', 'urlRandomness'].includes(key)) { throw new (_internal().XDLError)('INVALID_OPTIONS', `"${key}" is not a valid option`); } }); return opts; } async function ensureOptionsAsync(projectRoot, opts) { if (opts) { assertValidOptions(opts); } const defaultOpts = await _internal().ProjectSettings.readAsync(projectRoot); if (!opts) { return { urlType: null, ...defaultOpts }; } const optionsWithDefaults = { ...defaultOpts, ...opts }; return assertValidOptions(optionsWithDefaults); } function resolveProtocol(projectRoot, { urlType, ...options }) { if (urlType === 'http') { return 'http'; } else if (urlType === 'no-protocol') { return null; } else if (urlType === 'custom') { return options.scheme; } let protocol = 'exp'; const { exp } = (0, _config().getConfig)(projectRoot, { skipSDKVersionRequirement: true }); // We only use these values from the config const { scheme, detach, sdkVersion } = exp; if (detach) { // Normalize schemes and filter invalid schemes. const schemes = (Array.isArray(scheme) ? scheme : [scheme]).filter(scheme => typeof scheme === 'string' && !!scheme); // Get the first valid scheme. const firstScheme = schemes[0]; if (firstScheme && !_internal().Versions.lteSdkVersion({ sdkVersion }, '26.0.0')) { protocol = firstScheme; } else if (detach.scheme) { // must keep this fallback in place for older projects // and those detached with an older version of xdl protocol = detach.scheme; } } return protocol; } async function constructUrlAsync(projectRoot, incomingOpts, isPackager, requestHostname) { const opts = await ensureOptionsAsync(projectRoot, incomingOpts); const packagerInfo = await _internal().ProjectSettings.readPackagerInfoAsync(projectRoot); let protocol = resolveProtocol(projectRoot, opts); let hostname; let port; const proxyURL = isPackager ? process.env.EXPO_PACKAGER_PROXY_URL : process.env.EXPO_MANIFEST_PROXY_URL; if (proxyURL) { const parsedProxyURL = _url().default.parse(proxyURL); hostname = parsedProxyURL.hostname; port = parsedProxyURL.port; if (parsedProxyURL.protocol === 'https:') { if (protocol === 'http') { protocol = 'https'; } if (!port) { port = '443'; } } } else if (opts.hostType === 'localhost' || requestHostname === 'localhost') { hostname = '127.0.0.1'; port = isPackager ? packagerInfo.packagerPort : packagerInfo.expoServerPort; } else if (opts.hostType === 'lan' || _internal().ConnectionStatus.isOffline()) { if (process.env.EXPO_PACKAGER_HOSTNAME) { hostname = process.env.EXPO_PACKAGER_HOSTNAME.trim(); } else if (process.env.REACT_NATIVE_PACKAGER_HOSTNAME) { hostname = process.env.REACT_NATIVE_PACKAGER_HOSTNAME.trim(); } else if (opts.lanType === 'ip') { if (requestHostname) { hostname = requestHostname; } else { hostname = _internal().ip.address(); } } else { // Some old versions of OSX work with hostname but not local ip address. hostname = _os().default.hostname(); } port = isPackager ? packagerInfo.packagerPort : packagerInfo.expoServerPort; } else { const ngrokUrl = isPackager ? packagerInfo.packagerNgrokUrl : packagerInfo.expoServerNgrokUrl; if (!ngrokUrl || typeof ngrokUrl !== 'string') { // TODO: if you start with --tunnel flag then this warning will always // show up right before the tunnel starts... _internal().ProjectUtils.logWarning(projectRoot, 'expo', 'Tunnel URL not found (it might not be ready yet), falling back to LAN URL.', 'tunnel-url-not-found'); return constructUrlAsync(projectRoot, { ...opts, hostType: 'lan' }, isPackager, requestHostname); } else { _internal().ProjectUtils.clearNotification(projectRoot, 'tunnel-url-not-found'); const pnu = _url().default.parse(ngrokUrl); hostname = pnu.hostname; port = pnu.port; } } const url_ = joinURLComponents({ protocol, hostname, port }); if (opts.urlType === 'redirect') { return createRedirectURL(url_); } return url_; } function createRedirectURL(url) { return `https://exp.host/--/to-exp/${encodeURIComponent(url)}`; } function joinURLComponents({ protocol, hostname, port }) { (0, _assert().default)(hostname, 'hostname cannot be inferred.'); // Android HMR breaks without this port 80. // This is because Android React Native WebSocket implementation is not spec compliant and fails without a port: // `E unknown:ReactNative: java.lang.IllegalArgumentException: Invalid URL port: "-1"` // Invoked first in `metro-runtime/src/modules/HMRClient.js` const validPort = port !== null && port !== void 0 ? port : '80'; const validProtocol = protocol ? `${protocol}://` : ''; return `${validProtocol}${hostname}:${validPort}`; } function stripJSExtension(entryPoint) { return entryPoint.replace(/\.js$/, ''); } function isHttps(urlString) { return isURL(urlString, { protocols: ['https'] }); } function isURL(urlString, { protocols, requireProtocol }) { try { // eslint-disable-next-line new (_url().default.URL)(urlString); const parsed = _url().default.parse(urlString); if (!parsed.protocol && !requireProtocol) { return true; } return protocols ? parsed.protocol ? protocols.map(x => `${x.toLowerCase()}:`).includes(parsed.protocol) : false : true; } catch { return false; } } //# sourceMappingURL=UrlUtils.js.map