"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.constructBundleUrlAsync = constructBundleUrlAsync;
exports.constructManifestUrlAsync = constructManifestUrlAsync;
exports.constructHostUriAsync = constructHostUriAsync;
exports.constructLogUrlAsync = constructLogUrlAsync;
exports.constructUrlWithExtensionAsync = constructUrlWithExtensionAsync;
exports.constructPublishUrlAsync = constructPublishUrlAsync;
exports.constructSourceMapUrlAsync = constructSourceMapUrlAsync;
exports.constructAssetsUrlAsync = constructAssetsUrlAsync;
exports.constructDebuggerHostAsync = constructDebuggerHostAsync;
exports.constructBundleQueryParamsAsync = constructBundleQueryParamsAsync;
exports.constructWebAppUrlAsync = constructWebAppUrlAsync;
exports.constructUrlAsync = constructUrlAsync;
exports.guessMainModulePath = guessMainModulePath;
exports.randomIdentifier = randomIdentifier;
exports.sevenDigitIdentifier = sevenDigitIdentifier;
exports.randomIdentifierForUser = randomIdentifierForUser;
exports.someRandomness = someRandomness;
exports.domainify = domainify;
exports.getPlatformSpecificBundleUrl = getPlatformSpecificBundleUrl;
exports.isHttps = isHttps;

function _joi() {
  const data = _interopRequireDefault(require("joi"));

  _joi = function () {
    return data;
  };

  return data;
}

function _os() {
  const data = _interopRequireDefault(require("os"));

  _os = function () {
    return data;
  };

  return data;
}

function _url() {
  const data = _interopRequireDefault(require("url"));

  _url = function () {
    return data;
  };

  return data;
}

function _validator() {
  const data = _interopRequireDefault(require("validator"));

  _validator = function () {
    return data;
  };

  return data;
}

function ConfigUtils() {
  const data = _interopRequireWildcard(require("@expo/config"));

  ConfigUtils = function () {
    return data;
  };

  return data;
}

function _ip() {
  const data = _interopRequireDefault(require("./ip"));

  _ip = function () {
    return data;
  };

  return data;
}

function _Config() {
  const data = _interopRequireDefault(require("./Config"));

  _Config = function () {
    return data;
  };

  return data;
}

function Exp() {
  const data = _interopRequireWildcard(require("./Exp"));

  Exp = function () {
    return data;
  };

  return data;
}

function ProjectSettings() {
  const data = _interopRequireWildcard(require("./ProjectSettings"));

  ProjectSettings = function () {
    return data;
  };

  return data;
}

function ProjectUtils() {
  const data = _interopRequireWildcard(require("./project/ProjectUtils"));

  ProjectUtils = 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 _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

async function constructBundleUrlAsync(projectRoot, opts, requestHostname) {
  return constructUrlAsync(projectRoot, opts, true, requestHostname);
}

async function constructManifestUrlAsync(projectRoot, opts, requestHostname) {
  return constructUrlAsync(projectRoot, opts, false, requestHostname);
} // gets the base manifest URL and removes the scheme


async function constructHostUriAsync(projectRoot, requestHostname) {
  let 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

  let urlObj = _url().default.parse(urlString);

  urlObj.protocol = '';
  urlObj.slashes = false;
  return _url().default.format(urlObj);
}

async function constructLogUrlAsync(projectRoot, requestHostname) {
  let baseUrl = await constructUrlAsync(projectRoot, {
    urlType: 'http'
  }, false, requestHostname);
  return `${baseUrl}/logs`;
}

async function constructUrlWithExtensionAsync(projectRoot, entryPoint, ext, requestHostname, opts) {
  const defaultOpts = {
    dev: false,
    minify: true
  };
  opts = opts || defaultOpts;
  let bundleUrl = await constructBundleUrlAsync(projectRoot, {
    hostType: 'localhost',
    urlType: 'http'
  }, requestHostname);
  let mainModulePath = guessMainModulePath(entryPoint);
  bundleUrl += `/${mainModulePath}.${ext}`;
  let queryParams = await constructBundleQueryParamsAsync(projectRoot, opts);
  return `${bundleUrl}?${queryParams}`;
}

async function constructPublishUrlAsync(projectRoot, entryPoint, requestHostname, opts) {
  return await constructUrlWithExtensionAsync(projectRoot, entryPoint, 'bundle', requestHostname, opts);
}

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 constructUrlAsync(projectRoot, {
    urlType: 'no-protocol'
  }, true, requestHostname);
}

async function constructBundleQueryParamsAsync(projectRoot, opts) {
  let queryParams = `dev=${encodeURIComponent(!!opts.dev)}`;

  if (opts.hasOwnProperty('strict')) {
    queryParams += `&strict=${encodeURIComponent(!!opts.strict)}`;
  }

  if (opts.hasOwnProperty('minify')) {
    queryParams += `&minify=${encodeURIComponent(!!opts.minify)}`;
  }

  queryParams += '&hot=false';
  let {
    exp
  } = await ConfigUtils().readConfigJsonAsync(projectRoot); // SDK11 to SDK32 require us to inject hashAssetFiles through the params, but this is not
  // needed with SDK33+

  let supportsAssetPlugins = Versions().gteSdkVersion(exp, '11.0.0');
  let 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
    let pluginModule = ConfigUtils().resolveModule('expo/tools/hashAssetFiles', projectRoot, exp);
    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.
    if (!supportsAssetPlugins) {
      queryParams += '&includeAssetFileHashes=true';
    }
  }

  return queryParams;
}

async function constructWebAppUrlAsync(projectRoot) {
  let packagerInfo = await ProjectSettings().readPackagerInfoAsync(projectRoot);

  if (!packagerInfo.webpackServerPort) {
    return null;
  }

  const host = _ip().default.address();

  const {
    https
  } = await ProjectSettings().readAsync(projectRoot);
  let urlType = 'http';

  if (https === true) {
    urlType = 'https';
  }

  return `${urlType}://${host}:${packagerInfo.webpackServerPort}`;
}

async function constructUrlAsync(projectRoot, opts, isPackager, requestHostname) {
  if (opts) {
    let schema = _joi().default.object().keys({
      urlType: _joi().default.any().valid('exp', 'http', 'redirect', 'no-protocol'),
      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
    } = _joi().default.validate(opts, schema);

    if (error) {
      throw new (_XDLError().default)('INVALID_OPTIONS', error.toString());
    }
  }

  let defaultOpts = await ProjectSettings().getPackagerOptsAsync(projectRoot);

  if (!opts) {
    opts = defaultOpts;
  } else {
    opts = Object.assign({}, defaultOpts, opts);
  }

  let packagerInfo = await ProjectSettings().readPackagerInfoAsync(projectRoot);
  let protocol;

  if (opts.urlType === 'http') {
    protocol = 'http';
  } else if (opts.urlType === 'no-protocol') {
    protocol = null;
  } else {
    protocol = 'exp';
    let {
      exp
    } = await ConfigUtils().readConfigJsonAsync(projectRoot);

    if (exp.detach) {
      if (exp.scheme && Versions().gteSdkVersion(exp, '27.0.0')) {
        protocol = exp.scheme;
      } else if (exp.detach.scheme) {
        // must keep this fallback in place for older projects
        // and those detached with an older version of xdl
        protocol = exp.detach.scheme;
      }
    }
  }

  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 {
    let ngrokUrl = isPackager ? packagerInfo.packagerNgrokUrl : packagerInfo.expoServerNgrokUrl;

    if (!ngrokUrl || typeof ngrokUrl !== 'string') {
      ProjectUtils().logWarning(projectRoot, 'expo', 'Tunnel URL not found, falled back to LAN URL.', 'tunnel-url-not-found');
      return constructUrlAsync(projectRoot, { ...opts,
        hostType: 'lan'
      }, isPackager, requestHostname);
    } else {
      ProjectUtils().clearNotification(projectRoot, 'tunnel-url-not-found');

      let pnu = _url().default.parse(ngrokUrl);

      hostname = pnu.hostname;
      port = pnu.port;
    }
  }

  let url_ = '';

  if (protocol) {
    url_ += `${protocol}://`;
  }

  if (!hostname) {
    throw new Error('Hostname cannot be inferred.');
  }

  url_ += hostname;

  if (port) {
    url_ += `:${port}`;
  } else {
    // Android HMR breaks without this :|
    url_ += ':80';
  }

  if (opts.urlType === 'redirect') {
    return `https://exp.host/--/to-exp/${encodeURIComponent(url_)}`;
  }

  return url_;
}

function guessMainModulePath(entryPoint) {
  return entryPoint.replace(/\.js$/, '');
}

function randomIdentifier(length = 6) {
  let alphabet = '23456789qwertyuipasdfghjkzxcvbnm';
  let result = '';

  for (let i = 0; i < length; i++) {
    let j = Math.floor(Math.random() * alphabet.length);
    let 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 getPlatformSpecificBundleUrl(url, platform) {
  if (url.includes(Exp().ENTRY_POINT_PLATFORM_TEMPLATE_STRING)) {
    return url.replace(Exp().ENTRY_POINT_PLATFORM_TEMPLATE_STRING, platform);
  } else {
    return url;
  }
}

function isHttps(url) {
  return _validator().default.isURL(url, {
    protocols: ['https']
  });
}
//# sourceMappingURL=__sourcemaps__/UrlUtils.js.map