"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.ejectAsync = ejectAsync;

function _chalk() {
  const data = _interopRequireDefault(require("chalk"));

  _chalk = function () {
    return data;
  };

  return data;
}

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

  _fsExtra = function () {
    return data;
  };

  return data;
}

function _npmPackageArg() {
  const data = _interopRequireDefault(require("npm-package-arg"));

  _npmPackageArg = function () {
    return data;
  };

  return data;
}

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

  _path = function () {
    return data;
  };

  return data;
}

function _semver() {
  const data = _interopRequireDefault(require("semver"));

  _semver = function () {
    return data;
  };

  return data;
}

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

  _pacote = function () {
    return data;
  };

  return data;
}

function _tempy() {
  const data = _interopRequireDefault(require("tempy"));

  _tempy = function () {
    return data;
  };

  return data;
}

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

  _jsonFile = function () {
    return data;
  };

  return data;
}

function _xdl() {
  const data = require("@expo/xdl");

  _xdl = function () {
    return data;
  };

  return data;
}

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

  ConfigUtils = function () {
    return data;
  };

  return data;
}

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

  PackageManager = function () {
    return data;
  };

  return data;
}

function _ProjectUtils() {
  const data = require("../utils/ProjectUtils");

  _ProjectUtils = function () {
    return data;
  };

  return data;
}

function _log() {
  const data = _interopRequireDefault(require("../../log"));

  _log = function () {
    return data;
  };

  return data;
}

function _prompt() {
  const data = _interopRequireDefault(require("../../prompt"));

  _prompt = function () {
    return data;
  };

  return data;
}

function _accounts() {
  const data = require("../../accounts");

  _accounts = 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 }; }

const EXPO_APP_ENTRY = 'node_modules/expo/AppEntry.js';

async function warnIfDependenciesRequireAdditionalSetupAsync(projectRoot) {
  const {
    exp,
    pkg: pkgJson
  } = await _xdl().ProjectUtils.readConfigJsonAsync(projectRoot);
  const pkgsWithExtraSetup = await _jsonFile().default.readAsync(ConfigUtils().resolveModule('expo/requiresExtraSetup.json', projectRoot, exp));
  const packagesToWarn = Object.keys(pkgJson.dependencies).filter(pkgName => pkgsWithExtraSetup.hasOwnProperty(pkgName));

  if (packagesToWarn.length === 0) {
    return;
  }

  let plural = packagesToWarn.length > 1;

  _log().default.nested('');

  _log().default.nested(_chalk().default.yellow(`Warning: your app includes ${_chalk().default.bold(packagesToWarn.length)} package${plural ? 's' : ''} that require${plural ? '' : 's'} additional setup. See the following URL${plural ? 's' : ''} for instructions.`));

  _log().default.nested(_chalk().default.yellow(`Your app may not build/run until the additional setup for ${plural ? 'these packages' : 'this package'} has been completed.`));

  _log().default.nested('');

  packagesToWarn.forEach(pkgName => {
    _log().default.nested(_chalk().default.yellow(`- ${_chalk().default.bold(pkgName)}: ${pkgsWithExtraSetup[pkgName]}`));
  });

  _log().default.nested('');
}

async function ejectAsync(projectRoot, options) {
  await (0, _ProjectUtils().validateGitStatusAsync)();

  _log().default.nested('');

  let reactNativeOptionMessage = "Bare: I'd like a bare React Native project.";
  const questions = [{
    type: 'list',
    name: 'ejectMethod',
    message: 'How would you like to eject your app?\n  Read more: https://docs.expo.io/versions/latest/expokit/eject/',
    default: 'bare',
    choices: [{
      name: reactNativeOptionMessage,
      value: 'bare',
      short: 'Bare'
    }, {
      name: "ExpoKit: I'll create or log in with an Expo account to use React Native and the Expo SDK.",
      value: 'expokit',
      short: 'ExpoKit'
    }, {
      name: "Cancel: I'll continue with my current project structure.",
      value: 'cancel',
      short: 'cancel'
    }]
  }];
  const ejectMethod = options.ejectMethod || (await (0, _prompt().default)(questions, {
    nonInteractiveHelp: 'Please specify eject method (bare, expokit) with the --eject-method option.'
  })).ejectMethod;

  if (ejectMethod === 'bare') {
    await ejectToBareAsync(projectRoot, options);

    _log().default.nested(_chalk().default.green('Ejected successfully!'));

    _log().default.newLine();

    _log().default.nested(`Before running your app on iOS, make sure you have CocoaPods installed and initialize the project:`);

    _log().default.nested('');

    _log().default.nested(`  cd ios`);

    _log().default.nested(`  pod install`);

    _log().default.nested('');

    _log().default.nested('Then you can run the project:');

    _log().default.nested('');

    let packageManager = ConfigUtils().isUsingYarn(projectRoot) ? 'yarn' : 'npm';

    _log().default.nested(`  ${packageManager === 'npm' ? 'npm run android' : 'yarn android'}`);

    _log().default.nested(`  ${packageManager === 'npm' ? 'npm run ios' : 'yarn ios'}`);

    await warnIfDependenciesRequireAdditionalSetupAsync(projectRoot);
  } else if (ejectMethod === 'expokit') {
    await (0, _accounts().loginOrRegisterIfLoggedOut)();
    await _xdl().Detach.detachAsync(projectRoot, options);
    (0, _log().default)(_chalk().default.green('Ejected successfully!'));
  } else if (ejectMethod === 'cancel') {
    // we don't want to print the survey for cancellations
    (0, _log().default)('OK! If you change your mind you can run this command again.');
  } else {
    throw new Error(`Unrecognized eject method "${ejectMethod}". Valid options are: bare, expokit.`);
  }
}

async function ejectToBareAsync(projectRoot, options) {
  const useYarn = ConfigUtils().isUsingYarn(projectRoot);
  const npmOrYarn = useYarn ? 'yarn' : 'npm';
  const {
    configPath,
    configName
  } = await ConfigUtils().findConfigFileAsync(projectRoot);
  const {
    exp,
    pkg: pkgJson
  } = await _xdl().ProjectUtils.readConfigJsonAsync(projectRoot);
  const appJson = configName === 'app.json' ? JSON.parse((await _fsExtra().default.readFile(configPath))) : {};
  /**
   * Perform validations
   */

  if (!exp) throw new Error(`Couldn't read ${configName}`);
  if (!pkgJson) throw new Error(`Couldn't read package.json`);

  if (!_xdl().Versions.gteSdkVersion(exp, '34.0.0')) {
    throw new Error(`Ejecting to a bare project is only available for SDK 34 and higher`);
  } // Validate that the template exists


  let sdkMajorVersionNumber = _semver().default.major(exp.sdkVersion);

  let templateSpec = (0, _npmPackageArg().default)(`expo-template-bare-minimum@sdk-${sdkMajorVersionNumber}`);

  try {
    await _pacote().default.manifest(templateSpec);
  } catch (e) {
    if (e.code === 'E404') {
      throw new Error(`Unable to eject because an eject template for SDK ${sdkMajorVersionNumber} was not found`);
    } else {
      throw e;
    }
  }
  /**
   * Customize app.json
   */


  let {
    displayName,
    name
  } = await getAppNamesAsync(projectRoot);
  appJson.displayName = displayName;
  appJson.name = name;

  if (appJson.expo.entryPoint && appJson.expo.entryPoint !== EXPO_APP_ENTRY) {
    (0, _log().default)(_chalk().default.yellow(`expo.entryPoint is already configured, we recommend using "${EXPO_APP_ENTRY}`));
  } else {
    appJson.expo.entryPoint = EXPO_APP_ENTRY;
  }

  (0, _log().default)('Writing app.json...');
  await _fsExtra().default.writeFile(_path().default.resolve('app.json'), JSON.stringify(appJson, null, 2));
  (0, _log().default)(_chalk().default.green('Wrote to app.json, please update it manually in the future.'));
  let defaultDependencies = {};
  /**
   * Extract the template and copy it over
   */

  try {
    let tempDir = _tempy().default.directory();

    await _xdl().Exp.extractTemplateAppAsync(templateSpec, tempDir, appJson);

    _fsExtra().default.copySync(_path().default.join(tempDir, 'ios'), _path().default.join(projectRoot, 'ios'));

    _fsExtra().default.copySync(_path().default.join(tempDir, 'android'), _path().default.join(projectRoot, 'android'));

    let packageJson = _fsExtra().default.readJsonSync(_path().default.join(tempDir, 'package.json'));

    defaultDependencies = packageJson.dependencies;
    (0, _log().default)('Successfully copied template native code.');
  } catch (e) {
    (0, _log().default)(_chalk().default.red(e.message));
    (0, _log().default)(_chalk().default.red(`Eject failed, see above output for any issues.`));
    (0, _log().default)(_chalk().default.yellow('You may want to delete the `ios` and/or `android` directories.'));
    process.exit(1);
  }

  (0, _log().default)(`Updating your package.json...`);

  if (!pkgJson.scripts) {
    pkgJson.scripts = {};
  }

  delete pkgJson.scripts.eject;
  pkgJson.scripts.start = 'react-native start';
  pkgJson.scripts.ios = 'react-native run-ios';
  pkgJson.scripts.android = 'react-native run-android'; // The template may have some dependencies beyond react/react-native/react-native-unimodules,
  // for example RNGH and Reanimated. We should prefer the version that is already being used
  // in the project for those, but swap the react/react-native/react-native-unimodules versions
  // with the ones in the template.

  let combinedDependencies = { ...defaultDependencies,
    ...pkgJson.dependencies
  };
  combinedDependencies['react-native'] = defaultDependencies['react-native'];
  combinedDependencies['react'] = defaultDependencies['react'];
  combinedDependencies['react-native-unimodules'] = defaultDependencies['react-native-unimodules'];
  pkgJson.dependencies = combinedDependencies;
  await _fsExtra().default.writeFile(_path().default.resolve('package.json'), JSON.stringify(pkgJson, null, 2));
  (0, _log().default)(_chalk().default.green('Your package.json is up to date!'));
  (0, _log().default)(`Adding entry point...`);

  if (pkgJson.main !== EXPO_APP_ENTRY) {
    (0, _log().default)(_chalk().default.yellow(`Removing "main": ${pkgJson.main} from package.json. We recommend using index.js instead.`));
  }

  delete pkgJson.main;
  await _fsExtra().default.writeFile(_path().default.resolve('package.json'), JSON.stringify(pkgJson, null, 2));
  const indexjs = `import { AppRegistry } from 'react-native';
import App from './App';

AppRegistry.registerComponent('${appJson.name}', () => App);
`;
  await _fsExtra().default.writeFile(_path().default.resolve('index.js'), indexjs);
  (0, _log().default)(_chalk().default.green('Added new entry points!'));
  (0, _log().default)(_chalk().default.grey(`Note that using \`${npmOrYarn} start\` will now require you to run Xcode and/or Android Studio to build the native code for your project.`));
  (0, _log().default)('Removing node_modules...');
  await _fsExtra().default.remove('node_modules');
  (0, _log().default)('Installing new packages...');
  const packageManager = PackageManager().createForProject(projectRoot);
  await packageManager.installAsync();

  _log().default.newLine();
}

async function getAppNamesAsync(projectRoot) {
  const {
    configPath,
    configName
  } = await ConfigUtils().findConfigFileAsync(projectRoot);
  const {
    exp,
    pkg: pkgJson
  } = await ConfigUtils().readConfigJsonAsync(projectRoot);
  const appJson = configName === 'app.json' ? JSON.parse((await _fsExtra().default.readFile(configPath))) : {};
  let {
    displayName,
    name
  } = appJson;

  if (!displayName || !name) {
    (0, _log().default)("We have a couple of questions to ask you about how you'd like to name your app:");
    ({
      displayName,
      name
    } = await (0, _prompt().default)([{
      name: 'displayName',
      message: "What should your app appear as on a user's home screen?",
      default: name || exp.name,
      validate: s => {
        return s.length > 0 ? true : 'App display name cannot be empty.';
      }
    }, {
      name: 'name',
      message: 'What should your Android Studio and Xcode projects be called?',
      default: pkgJson.name ? stripDashes(pkgJson.name) : undefined,
      validate: s => {
        if (s.length === 0) {
          return 'Project name cannot be empty.';
        } else if (s.indexOf('-') !== -1 || s.indexOf(' ') !== -1) {
          return 'Project name cannot contain hyphens or spaces.';
        }

        return true;
      }
    }], {
      nonInteractiveHelp: 'Please specify "displayName" and "name" in app.json.'
    }));
  }

  return {
    displayName,
    name
  };
}

function stripDashes(s) {
  let ret = '';

  for (let c of s) {
    if (c !== ' ' && c !== '-') {
      ret += c;
    }
  }

  return ret;
}
//# sourceMappingURL=../../__sourcemaps__/commands/eject/Eject.js.map