"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.determineEntryPointAsync = determineEntryPointAsync;
exports.extractAndInitializeTemplateApp = extractAndInitializeTemplateApp;
exports.extractTemplateAppAsync = extractTemplateAppAsync;
exports.saveRecentExpRootAsync = saveRecentExpRootAsync;
exports.expInfoSafeAsync = expInfoSafeAsync;
exports.getThirdPartyInfoAsync = getThirdPartyInfoAsync;
exports.getPublishInfoAsync = getPublishInfoAsync;
exports.recentValidExpsAsync = recentValidExpsAsync;
exports.sendAsync = sendAsync;
exports.getProjectRandomnessAsync = getProjectRandomnessAsync;
exports.resetProjectRandomnessAsync = resetProjectRandomnessAsync;
exports.clearXDLCacheAsync = clearXDLCacheAsync;
exports.ENTRY_POINT_PLATFORM_TEMPLATE_STRING = void 0;

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

  ConfigUtils = function () {
    return data;
  };

  return data;
}

function _fsExtra() {
  const data = _interopRequireDefault(require("fs-extra"));

  _fsExtra = function () {
    return data;
  };

  return data;
}

function _merge() {
  const data = _interopRequireDefault(require("lodash/merge"));

  _merge = function () {
    return data;
  };

  return data;
}

function _path() {
  const data = _interopRequireDefault(require("path"));

  _path = function () {
    return data;
  };

  return data;
}

function _spawnAsync() {
  const data = _interopRequireDefault(require("@expo/spawn-async"));

  _spawnAsync = function () {
    return data;
  };

  return data;
}

function _jsonFile() {
  const data = _interopRequireDefault(require("@expo/json-file"));

  _jsonFile = function () {
    return data;
  };

  return data;
}

function _minipass() {
  const data = _interopRequireDefault(require("minipass"));

  _minipass = function () {
    return data;
  };

  return data;
}

function _pacote() {
  const data = _interopRequireDefault(require("pacote"));

  _pacote = function () {
    return data;
  };

  return data;
}

function _tar() {
  const data = _interopRequireDefault(require("tar"));

  _tar = function () {
    return data;
  };

  return data;
}

function _Api() {
  const data = _interopRequireDefault(require("./Api"));

  _Api = function () {
    return data;
  };

  return data;
}

function _Logger() {
  const data = _interopRequireDefault(require("./Logger"));

  _Logger = function () {
    return data;
  };

  return data;
}

function _NotificationCode() {
  const data = _interopRequireDefault(require("./NotificationCode"));

  _NotificationCode = function () {
    return data;
  };

  return data;
}

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

  ThirdParty = function () {
    return data;
  };

  return data;
}

function _User() {
  const data = _interopRequireDefault(require("./User"));

  _User = function () {
    return data;
  };

  return data;
}

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

  UrlUtils = function () {
    return data;
  };

  return data;
}

function _UserSettings() {
  const data = _interopRequireDefault(require("./UserSettings"));

  _UserSettings = function () {
    return data;
  };

  return data;
}

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

  ProjectSettings = function () {
    return data;
  };

  return data;
}

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

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 _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

// FIXME(perry) eliminate usage of this template
const ENTRY_POINT_PLATFORM_TEMPLATE_STRING = 'PLATFORM_GOES_HERE'; // TODO(ville): update when this has landed: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/36598

exports.ENTRY_POINT_PLATFORM_TEMPLATE_STRING = ENTRY_POINT_PLATFORM_TEMPLATE_STRING;

async function determineEntryPointAsync(root) {
  let {
    exp,
    pkg
  } = await ConfigUtils().readConfigJsonAsync(root); // entryPoint is relative to the packager root and main is relative
  // to the project root. So if your rn-cli.config.js points to a different
  // root than the project root, these can be different. Most of the time
  // you should use main.

  let entryPoint = pkg.main || 'index.js';

  if (exp && exp.entryPoint) {
    entryPoint = exp.entryPoint;
  }

  return entryPoint;
}

class Transformer extends _minipass().default {
  constructor(config) {
    super();

    _defineProperty(this, "data", void 0);

    _defineProperty(this, "config", void 0);

    this.data = '';
    this.config = config;
  }

  write(data) {
    this.data += data;
    return true;
  }

  end() {
    let replaced = this.data.replace(/Hello App Display Name/g, this.config.displayName || this.config.name).replace(/HelloWorld/g, this.config.name).replace(/helloworld/g, this.config.name.toLowerCase());
    super.write(replaced);
    return super.end();
  }

} // Binary files, don't process these (avoid decoding as utf8)


const binaryExtensions = ['.png', '.jar'];

function createFileTransform(config) {
  return function transformFile(entry) {
    if (!binaryExtensions.includes(_path().default.extname(entry.path)) && config.name) {
      return new Transformer(config);
    }

    return;
  };
}

async function extractAndInitializeTemplateApp(templateSpec, projectRoot, packageManager = 'npm', config) {
  _Logger().default.notifications.info({
    code: _NotificationCode().default.PROGRESS
  }, 'Extracting project files...');

  await extractTemplateAppAsync(templateSpec, projectRoot, config); // Update files

  _Logger().default.notifications.info({
    code: _NotificationCode().default.PROGRESS
  }, 'Customizing project...');

  let appFile = new (_jsonFile().default)(_path().default.join(projectRoot, 'app.json'));
  let appJson = (0, _merge().default)((await appFile.readAsync()), config);
  await appFile.writeAsync(appJson);
  let packageFile = new (_jsonFile().default)(_path().default.join(projectRoot, 'package.json'));
  let packageJson = await packageFile.readAsync(); // Adding `private` stops npm from complaining about missing `name` and `version` fields.
  // We don't add a `name` field because it also exists in `app.json`.

  packageJson = { ...packageJson,
    private: true
  }; // These are metadata fields related to the template package, let's remove them from the package.json.

  delete packageJson.name;
  delete packageJson.version;
  delete packageJson.description;
  delete packageJson.tags;
  delete packageJson.repository; // pacote adds these, but we don't want them in the package.json of the project.

  delete packageJson._resolved;
  delete packageJson._integrity;
  delete packageJson._from;
  await packageFile.writeAsync(packageJson);
  await initGitRepoAsync(projectRoot);
  await installDependenciesAsync(projectRoot, packageManager);
  return projectRoot;
}

async function extractTemplateAppAsync(templateSpec, targetPath, config) {
  let tarStream = await _pacote().default.tarball.stream(templateSpec, {
    cache: _path().default.join(_UserSettings().default.dotExpoHomeDirectory(), 'template-cache')
  });
  await _fsExtra().default.mkdirp(targetPath);
  await new Promise((resolve, reject) => {
    const extractStream = _tar().default.x({
      cwd: targetPath,
      strip: 1,
      // TODO(ville): pending https://github.com/DefinitelyTyped/DefinitelyTyped/pull/36598
      // @ts-ignore property missing from the type definition
      transform: createFileTransform(config),

      onentry(entry) {
        if (config.name) {
          // Rewrite paths for bare workflow
          entry.path = entry.path.replace(/HelloWorld/g, config.name).replace(/helloworld/g, config.name.toLowerCase());
        }

        if (/^file$/i.test(entry.type) && _path().default.basename(entry.path) === 'gitignore') {
          // Rename `gitignore` because npm ignores files named `.gitignore` when publishing.
          // See: https://github.com/npm/npm/issues/1862
          entry.path = entry.path.replace(/gitignore$/, '.gitignore');
        }
      }

    });

    tarStream.on('error', reject);
    extractStream.on('error', reject);
    extractStream.on('close', resolve);
    tarStream.pipe(extractStream);
  });
  return targetPath;
}

async function initGitRepoAsync(root) {
  // let's see if we're in a git tree
  let insideGit = true;

  try {
    await (0, _spawnAsync().default)('git', ['rev-parse', '--is-inside-work-tree'], {
      cwd: root
    });

    _Logger().default.global.debug('New project is already inside of a git repo, skipping git init.');
  } catch (e) {
    if (e.errno == 'ENOENT') {
      _Logger().default.global.warn('Unable to initialize git repo. `git` not in PATH.');
    }

    insideGit = false;
  }

  if (!insideGit) {
    try {
      await (0, _spawnAsync().default)('git', ['init'], {
        cwd: root
      });

      _Logger().default.global.info('Initialized a git repository.');
    } catch (e) {// no-op -- this is just a convenience and we don't care if it fails
    }
  }
}

async function installDependenciesAsync(projectRoot, packageManager) {
  _Logger().default.global.info('Installing dependencies...');

  if (packageManager === 'yarn') {
    await (0, _spawnAsync().default)('yarnpkg', ['install'], {
      cwd: projectRoot,
      stdio: 'inherit'
    });
  } else {
    await (0, _spawnAsync().default)('npm', ['install'], {
      cwd: projectRoot,
      stdio: 'inherit'
    });
  }
}

async function saveRecentExpRootAsync(root) {
  root = _path().default.resolve(root); // Write the recent Exps JSON file

  const recentExpsJsonFile = _UserSettings().default.recentExpsJsonFile();

  let recentExps = await recentExpsJsonFile.readAsync(); // Filter out copies of this so we don't get dupes in this list

  recentExps = recentExps.filter(dir => dir !== root);
  recentExps.unshift(root);
  return await recentExpsJsonFile.writeAsync(recentExps.slice(0, 100));
}

function getHomeDir() {
  return process.env[process.platform === 'win32' ? 'USERPROFILE' : 'HOME'] || '';
}

function makePathReadable(pth) {
  let homedir = getHomeDir();

  if (pth.substr(0, homedir.length) === homedir) {
    return `~${pth.substr(homedir.length)}`;
  } else {
    return pth;
  }
}

async function expInfoSafeAsync(root) {
  try {
    let {
      exp: {
        name,
        description,
        icon,
        iconUrl
      }
    } = await ConfigUtils().readConfigJsonAsync(root);
    let pathOrUrl = icon || iconUrl || 'https://d3lwq5rlu14cro.cloudfront.net/ExponentEmptyManifest_192.png';

    let resolvedPath = _path().default.resolve(root, pathOrUrl);

    if (_fsExtra().default.existsSync(resolvedPath)) {
      icon = `file://${resolvedPath}`;
    } else {
      icon = pathOrUrl; // Assume already a URL
    }

    return {
      readableRoot: makePathReadable(root),
      root,
      name,
      description,
      icon
    };
  } catch (e) {
    return null;
  }
}

async function getThirdPartyInfoAsync(publicUrl) {
  const user = await _User().default.ensureLoggedInAsync();

  if (!user) {
    throw new Error('Attempted to login in offline mode. This is a bug.');
  }

  const {
    username
  } = user;
  const exp = await ThirdParty().getManifest(publicUrl);
  const {
    slug,
    sdkVersion,
    version
  } = exp;

  if (!sdkVersion) {
    throw new Error(`sdkVersion is missing from ${publicUrl}`);
  }

  if (!slug) {
    // slug is made programmatically for app.json
    throw new Error(`slug field is missing from exp.json.`);
  }

  if (!version) {
    throw new Error(`Can't get version of package.`);
  }

  const iosBundleIdentifier = exp.ios ? exp.ios.bundleIdentifier : null;
  const androidPackage = exp.android ? exp.android.package : null;
  return {
    args: {
      username,
      remoteUsername: username,
      remotePackageName: slug,
      remoteFullPackageName: `@${username}/${slug}`,
      sdkVersion,
      iosBundleIdentifier,
      androidPackage
    }
  };
} // TODO: remove / change, no longer publishInfo, this is just used for signing


async function getPublishInfoAsync(root) {
  const user = await _User().default.ensureLoggedInAsync();

  if (!user) {
    throw new Error('Attempted to login in offline mode. This is a bug.');
  }

  let {
    username
  } = user;
  const {
    exp
  } = await ConfigUtils().readConfigJsonAsync(root);
  const name = exp.slug;
  const {
    version,
    sdkVersion
  } = exp;
  const configName = await ConfigUtils().configFilenameAsync(root);

  if (!sdkVersion) {
    throw new Error(`sdkVersion is missing from ${configName}`);
  }

  if (!name) {
    // slug is made programmatically for app.json
    throw new Error(`slug field is missing from exp.json.`);
  }

  if (!version) {
    throw new Error(`Can't get version of package.`);
  }

  const remotePackageName = name;
  const remoteUsername = username;
  const remoteFullPackageName = `@${remoteUsername}/${remotePackageName}`;
  const iosBundleIdentifier = exp.ios ? exp.ios.bundleIdentifier : null;
  const androidPackage = exp.android ? exp.android.package : null;
  return {
    args: {
      username,
      remoteUsername,
      remotePackageName,
      remoteFullPackageName,
      sdkVersion,
      iosBundleIdentifier,
      androidPackage
    }
  };
}

async function recentValidExpsAsync() {
  let recentExpsJsonFile = _UserSettings().default.recentExpsJsonFile();

  let recentExps = await recentExpsJsonFile.readAsync();
  let results = await Promise.all(recentExps.map(expInfoSafeAsync));
  let filteredResults = results.filter(result => result);
  return filteredResults;
}

async function sendAsync(recipient, url_, allowUnauthed = true) {
  let result = await _Api().default.callMethodAsync('send', [recipient, url_, allowUnauthed]);
  return result;
} // TODO: figure out where these functions should live


async function getProjectRandomnessAsync(projectRoot) {
  let ps = await ProjectSettings().readAsync(projectRoot);
  let randomness = ps.urlRandomness;

  if (randomness) {
    return randomness;
  } else {
    return resetProjectRandomnessAsync(projectRoot);
  }
}

async function resetProjectRandomnessAsync(projectRoot) {
  let randomness = UrlUtils().someRandomness();
  ProjectSettings().setAsync(projectRoot, {
    urlRandomness: randomness
  });
  return randomness;
}

async function clearXDLCacheAsync() {
  let dotExpoHomeDirectory = _UserSettings().default.dotExpoHomeDirectory();

  _fsExtra().default.removeSync(_path().default.join(dotExpoHomeDirectory, 'ios-simulator-app-cache'));

  _fsExtra().default.removeSync(_path().default.join(dotExpoHomeDirectory, 'android-apk-cache'));

  _fsExtra().default.removeSync(_path().default.join(dotExpoHomeDirectory, 'starter-app-cache'));

  _Logger().default.notifications.info(`Cleared cache`);
}
//# sourceMappingURL=__sourcemaps__/Exp.js.map