You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

426 lines
16 KiB

"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