@expo/xdl
Version:
The Expo Development Library
566 lines (446 loc) • 16.6 kB
JavaScript
;
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