UNPKG

@expo/xdl

Version:
566 lines (446 loc) 16.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.constructBundleUrlAsync = constructBundleUrlAsync; exports.constructDeepLinkAsync = constructDeepLinkAsync; exports.constructManifestUrlAsync = constructManifestUrlAsync; exports.constructDevClientUrlAsync = constructDevClientUrlAsync; exports.constructHostUriAsync = constructHostUriAsync; exports.constructLogUrlAsync = constructLogUrlAsync; exports.constructUrlWithExtensionAsync = constructUrlWithExtensionAsync; exports.constructPublishUrlAsync = constructPublishUrlAsync; exports.constructSourceMapUrlAsync = constructSourceMapUrlAsync; exports.constructAssetsUrlAsync = constructAssetsUrlAsync; exports.constructDebuggerHostAsync = constructDebuggerHostAsync; exports.constructBundleQueryParams = constructBundleQueryParams; exports.constructBundleQueryParamsWithConfig = constructBundleQueryParamsWithConfig; exports.constructWebAppUrlAsync = constructWebAppUrlAsync; exports.constructUrlAsync = constructUrlAsync; exports.stripJSExtension = stripJSExtension; exports.randomIdentifier = randomIdentifier; exports.sevenDigitIdentifier = sevenDigitIdentifier; exports.randomIdentifierForUser = randomIdentifierForUser; exports.someRandomness = someRandomness; exports.domainify = domainify; exports.isHttps = isHttps; exports.isURL = isURL; function _config() { const data = require("@expo/config"); _config = function () { return data; }; return data; } function _joi() { const data = _interopRequireDefault(require("@hapi/joi")); _joi = 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 _Config() { const data = _interopRequireDefault(require("./Config")); _Config = function () { return data; }; return data; } function ProjectSettings() { const data = _interopRequireWildcard(require("./ProjectSettings")); ProjectSettings = function () { return data; }; return data; } function Versions() { const data = _interopRequireWildcard(require("./Versions")); Versions = function () { return data; }; return data; } function _XDLError() { const data = _interopRequireDefault(require("./XDLError")); _XDLError = function () { return data; }; return data; } function _ip() { const data = _interopRequireDefault(require("./ip")); _ip = function () { return data; }; return data; } function ProjectUtils() { const data = _interopRequireWildcard(require("./project/ProjectUtils")); ProjectUtils = function () { return data; }; return data; } function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } 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 ProjectSettings().getPackagerOptsAsync(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) { const { scheme } = await ProjectSettings().getPackagerOptsAsync(projectRoot); if (!scheme || typeof scheme !== 'string') { throw new (_XDLError().default)('NO_DEV_CLIENT_SCHEME', 'No scheme specified for development client'); } const protocol = resolveProtocol(projectRoot, { 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 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) { const { exp } = (0, _config().getConfig)(projectRoot); 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; } // No special requirements after SDK 33 (Jun 5 2019) if (Versions().gteSdkVersion(exp, '33.0.0')) { return _querystring().default.stringify(queryParams); } // TODO: Remove this ... // SDK11 to SDK32 require us to inject hashAssetFiles through the params, but this is not // needed with SDK33+ const supportsAssetPlugins = Versions().gteSdkVersion(exp, '11.0.0'); const usesAssetPluginsQueryParam = supportsAssetPlugins && Versions().lteSdkVersion(exp, '32.0.0'); if (usesAssetPluginsQueryParam) { // 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); } else if (!supportsAssetPlugins) { // 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; } return _querystring().default.stringify(queryParams); } async function constructWebAppUrlAsync(projectRoot, options = {}) { var _options$hostType; const packagerInfo = await ProjectSettings().readPackagerInfoAsync(projectRoot); if (!packagerInfo.webpackServerPort) { return null; } const { https, hostType } = await ProjectSettings().readAsync(projectRoot); const host = ((_options$hostType = options.hostType) !== null && _options$hostType !== void 0 ? _options$hostType : hostType) === 'localhost' ? 'localhost' : _ip().default.address(); let urlType = 'http'; if (https === true) { urlType = 'https'; } return `${urlType}://${host}:${packagerInfo.webpackServerPort}`; } function assertValidOptions(opts) { const schema = _joi().default.object().keys({ devClient: _joi().default.boolean().optional(), scheme: _joi().default.string().optional().allow(null), // Replaced by `scheme` urlType: _joi().default.any().valid('exp', 'http', 'redirect', 'no-protocol').allow(null), lanType: _joi().default.any().valid('ip', 'hostname'), hostType: _joi().default.any().valid('localhost', 'lan', 'tunnel'), dev: _joi().default.boolean(), strict: _joi().default.boolean(), minify: _joi().default.boolean(), https: _joi().default.boolean().optional(), urlRandomness: _joi().default.string().optional().allow(null) }); const { error } = schema.validate(opts); if (error) { throw new (_XDLError().default)('INVALID_OPTIONS', error.toString()); } return opts; } async function ensureOptionsAsync(projectRoot, opts) { if (opts) { assertValidOptions(opts); } const defaultOpts = await ProjectSettings().getPackagerOptsAsync(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); // 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 && Versions().gteSdkVersion({ sdkVersion }, '27.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 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' || _Config().default.offline) { 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 = _ip().default.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... 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 { 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 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 randomIdentifier(length = 6) { const alphabet = '23456789qwertyuipasdfghjkzxcvbnm'; let result = ''; for (let i = 0; i < length; i++) { const j = Math.floor(Math.random() * alphabet.length); const c = alphabet.substr(j, 1); result += c; } return result; } function sevenDigitIdentifier() { return `${randomIdentifier(3)}-${randomIdentifier(4)}`; } function randomIdentifierForUser(username) { return `${username}-${randomIdentifier(3)}-${randomIdentifier(2)}`; } function someRandomness() { return [randomIdentifier(2), randomIdentifier(3)].join('-'); } function domainify(s) { return s.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/^-+/, '').replace(/-+$/, ''); } 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 (err) { return false; } } //# sourceMappingURL=__sourcemaps__/UrlUtils.js.map