"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.addDetachedConfigToExp = addDetachedConfigToExp;
exports.createDetachedAsync = createDetachedAsync;
exports.getPaths = getPaths;
exports.getNewestSdkVersionSupportedAsync = getNewestSdkVersionSupportedAsync;

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

  _fsExtra = function () {
    return data;
  };

  return data;
}

function _invariant() {
  const data = _interopRequireDefault(require("invariant"));

  _invariant = function () {
    return data;
  };

  return data;
}

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

  _path = function () {
    return data;
  };

  return data;
}

function _rimraf() {
  const data = _interopRequireDefault(require("rimraf"));

  _rimraf = function () {
    return data;
  };

  return data;
}

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

  _Api = function () {
    return data;
  };

  return data;
}

function _ExponentTools() {
  const data = require("./ExponentTools");

  _ExponentTools = function () {
    return data;
  };

  return data;
}

function _IosPodsTools() {
  const data = require("./IosPodsTools.js");

  _IosPodsTools = function () {
    return data;
  };

  return data;
}

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

  IosPlist = function () {
    return data;
  };

  return data;
}

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

  _Logger = function () {
    return data;
  };

  return data;
}

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

  Utils = function () {
    return data;
  };

  return data;
}

function _StandaloneContext() {
  const data = _interopRequireDefault(require("./StandaloneContext"));

  _StandaloneContext = function () {
    return data;
  };

  return data;
}

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

  Versions = function () {
    return data;
  };

  return data;
}

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

  Modules = function () {
    return data;
  };

  return data;
}

function _installPackagesAsync() {
  const data = _interopRequireDefault(require("./installPackagesAsync"));

  _installPackagesAsync = 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 _getVersionedExpoKitConfigAsync(sdkVersion, skipServerValidation) {
  const versions = await Versions().versionsAsync();
  let sdkVersionConfig = versions.sdkVersions[sdkVersion];

  if (!sdkVersionConfig) {
    if (skipServerValidation) {
      sdkVersionConfig = {};
    } else {
      throw new Error(`Unsupported SDK version: ${sdkVersion}`);
    }
  }

  const {
    iosVersion,
    iosExpoViewUrl
  } = sdkVersionConfig;
  const iosClientVersion = iosVersion ? iosVersion : versions.iosVersion;
  return {
    iosClientVersion,
    iosExpoViewUrl
  };
}

async function _getOrCreateTemplateDirectoryAsync(context, iosExpoViewUrl) {
  if (context.type === 'service') {
    return _path().default.join(context.data.expoSourcePath, '..');
  } else if (context.type === 'user') {
    let expoRootTemplateDirectory;

    if (process.env.EXPO_VIEW_DIR) {
      // Only for testing
      expoRootTemplateDirectory = process.env.EXPO_VIEW_DIR;
    } else {
      // HEY: if you need other paths into the extracted archive, be sure and include them
      // when the archive is generated in `ios/pipeline.js`
      expoRootTemplateDirectory = _path().default.join(context.data.projectPath, 'temp-ios-directory');

      if (!(0, _ExponentTools().isDirectory)(expoRootTemplateDirectory)) {
        _fsExtra().default.mkdirpSync(expoRootTemplateDirectory);

        _Logger().default.info('Downloading iOS code...');

        (0, _invariant().default)(iosExpoViewUrl, `The URL for ExpoKit iOS must be set`);
        await _Api().default.downloadAsync(iosExpoViewUrl, expoRootTemplateDirectory, {
          extract: true
        });
      }
    }

    return expoRootTemplateDirectory;
  }
}

async function _renameAndMoveProjectFilesAsync(context, projectDirectory, projectName) {
  // remove .gitignore, as this actually pertains to internal expo template management
  try {
    const gitIgnorePath = _path().default.join(projectDirectory, '.gitignore');

    if (_fsExtra().default.existsSync(gitIgnorePath)) {
      _rimraf().default.sync(gitIgnorePath);
    }
  } catch (e) {}

  const filesToTransform = [_path().default.join('exponent-view-template.xcodeproj', 'project.pbxproj'), _path().default.join('exponent-view-template.xcworkspace', 'contents.xcworkspacedata'), _path().default.join('exponent-view-template.xcodeproj', 'xcshareddata', 'xcschemes', 'exponent-view-template.xcscheme')];
  let bundleIdentifier;

  if (context.type === 'user') {
    const exp = context.data.exp;
    bundleIdentifier = exp.ios && exp.ios.bundleIdentifier ? exp.ios.bundleIdentifier : null;

    if (!bundleIdentifier) {
      throw new Error(`Cannot configure an ExpoKit workspace with no iOS bundle identifier.`);
    }
  } else if (context.type === 'service') {
    bundleIdentifier = 'host.exp.Exponent';
  }

  await Promise.all(filesToTransform.map(fileName => (0, _ExponentTools().transformFileContentsAsync)(_path().default.join(projectDirectory, fileName), fileString => {
    return fileString.replace(/com.getexponent.exponent-view-template/g, bundleIdentifier).replace(/exponent-view-template/g, projectName);
  }))); // order of this array matters

  const filesToMove = ['exponent-view-template', _path().default.join('exponent-view-template.xcodeproj', 'xcshareddata', 'xcschemes', 'exponent-view-template.xcscheme'), 'exponent-view-template.xcodeproj', 'exponent-view-template.xcworkspace'];
  filesToMove.forEach(async fileName => {
    let destFileName = _path().default.join(_path().default.dirname(fileName), `${projectName}${_path().default.extname(fileName)}`);

    await (0, _ExponentTools().spawnAsyncThrowError)('/bin/mv', [_path().default.join(projectDirectory, fileName), _path().default.join(projectDirectory, destFileName)]);
  });
}

async function _configureVersionsPlistAsync(configFilePath, standaloneSdkVersion) {
  await IosPlist().modifyAsync(configFilePath, 'EXSDKVersions', versionConfig => {
    versionConfig.sdkVersions = [standaloneSdkVersion];
    versionConfig.detachedNativeVersions = {
      shell: standaloneSdkVersion,
      kernel: standaloneSdkVersion
    };
    return versionConfig;
  });
}

async function _configureBuildConstantsPlistAsync(configFilePath, context) {
  await IosPlist().modifyAsync(configFilePath, 'EXBuildConstants', constantsConfig => {
    constantsConfig.STANDALONE_CONTEXT_TYPE = context.type;
    return constantsConfig;
  });
}

async function _renderPodfileFromTemplateAsync(context, expoRootTemplateDirectory, sdkVersion, iosClientVersion) {
  const {
    iosProjectDirectory,
    projectName
  } = getPaths(context);
  let podfileTemplateFilename;
  let podfileSubstitutions = {
    TARGET_NAME: projectName
  };
  let reactNativeDependencyPath;
  let modulesPath;
  const detachableUniversalModules = Modules().getDetachableModules('ios', context.data.shellAppSdkVersion || sdkVersion);

  if (context.type === 'user') {
    (0, _invariant().default)(iosClientVersion, `The iOS client version must be specified`);
    reactNativeDependencyPath = _path().default.join(context.data.projectPath, 'node_modules', 'react-native');
    modulesPath = _path().default.join(context.data.projectPath, 'node_modules');
    podfileSubstitutions.EXPOKIT_TAG = `ios/${iosClientVersion}`;
    podfileTemplateFilename = 'ExpoKit-Podfile';
  } else if (context.type === 'service') {
    reactNativeDependencyPath = _path().default.join(expoRootTemplateDirectory, 'react-native-lab', 'react-native');
    modulesPath = _path().default.join(expoRootTemplateDirectory, 'packages');
    podfileSubstitutions.EXPOKIT_PATH = _path().default.relative(iosProjectDirectory, expoRootTemplateDirectory);
    podfileSubstitutions.VERSIONED_REACT_NATIVE_PATH = _path().default.relative(iosProjectDirectory, _path().default.join(expoRootTemplateDirectory, 'ios', 'versioned-react-native'));
    podfileTemplateFilename = 'ExpoKit-Podfile-versioned';
  } else {
    throw new Error(`Unsupported context type: ${context.type}`);
  }

  podfileSubstitutions.REACT_NATIVE_PATH = _path().default.relative(iosProjectDirectory, reactNativeDependencyPath);
  podfileSubstitutions.UNIVERSAL_MODULES_PATH = _path().default.relative(iosProjectDirectory, modulesPath);
  podfileSubstitutions.UNIVERSAL_MODULES = detachableUniversalModules.map(module => ({ ...module,
    path: _path().default.join(podfileSubstitutions.UNIVERSAL_MODULES_PATH, module.libName, module.subdirectory)
  })); // env flags for testing

  if (process.env.EXPOKIT_TAG_IOS) {
    _Logger().default.info(`EXPOKIT_TAG_IOS: Using custom ExpoKit iOS tag...`);

    podfileSubstitutions.EXPOKIT_TAG = process.env.EXPOKIT_TAG_IOS;
  } else if (process.env.EXPO_VIEW_DIR) {
    _Logger().default.info('EXPO_VIEW_DIR: Using local ExpoKit source for iOS...');

    podfileSubstitutions.EXPOKIT_PATH = _path().default.relative(iosProjectDirectory, process.env.EXPO_VIEW_DIR); // If EXPO_VIEW_DIR is defined overwrite UNIVERSAL_MODULES with paths pointing to EXPO_VIEW_DIR

    podfileSubstitutions.UNIVERSAL_MODULES = podfileSubstitutions.UNIVERSAL_MODULES.map(module => ({ ...module,
      path: _path().default.relative(iosProjectDirectory, _path().default.join(process.env.EXPO_VIEW_DIR, 'packages', module.libName, module.subdirectory))
    }));
  }

  const templatePodfilePath = _path().default.join(expoRootTemplateDirectory, 'template-files', 'ios', podfileTemplateFilename);

  await (0, _IosPodsTools().renderPodfileAsync)(templatePodfilePath, _path().default.join(iosProjectDirectory, 'Podfile'), podfileSubstitutions, context.data.shellAppSdkVersion, sdkVersion);
}

async function createDetachedAsync(context) {
  const {
    iosProjectDirectory,
    projectName,
    supportingDirectory,
    projectRootDirectory
  } = getPaths(context);

  _Logger().default.info(`Creating ExpoKit workspace at ${iosProjectDirectory}...`);

  const isServiceContext = context.type === 'service';
  const standaloneSdkVersion = await getNewestSdkVersionSupportedAsync(context);
  let iosClientVersion;
  let iosExpoViewUrl;

  if (context.type === 'user') {
    ({
      iosClientVersion,
      iosExpoViewUrl
    } = await _getVersionedExpoKitConfigAsync(standaloneSdkVersion, process.env.EXPO_VIEW_DIR));
  }

  const expoRootTemplateDirectory = await _getOrCreateTemplateDirectoryAsync(context, iosExpoViewUrl); // copy template workspace

  _Logger().default.info('Moving iOS project files...');

  _Logger().default.info('Attempting to create project directory...');

  _fsExtra().default.mkdirpSync(iosProjectDirectory);

  _Logger().default.info('Created project directory! Copying files:');

  await Utils().ncpAsync(_path().default.join(expoRootTemplateDirectory, 'exponent-view-template', 'ios'), iosProjectDirectory);

  const projectPackageJsonPath = _path().default.join(projectRootDirectory, 'package.json');

  if (!(await _fsExtra().default.exists(projectPackageJsonPath))) {
    _Logger().default.info('Copying blank package.json...');

    await _fsExtra().default.copy(_path().default.join(expoRootTemplateDirectory, 'exponent-view-template', 'package.json'), projectPackageJsonPath);
  }

  _Logger().default.info('Installing required packages...');

  await _installRequiredPackagesAsync(projectRootDirectory, standaloneSdkVersion);

  _Logger().default.info('Naming iOS project...');

  await _renameAndMoveProjectFilesAsync(context, iosProjectDirectory, projectName);

  _Logger().default.info('Configuring iOS dependencies...'); // this configuration must happen prior to build time because it affects which
  // native versions of RN we depend on.


  await _configureVersionsPlistAsync(supportingDirectory, standaloneSdkVersion);
  await _configureBuildConstantsPlistAsync(supportingDirectory, context);
  await _renderPodfileFromTemplateAsync(context, expoRootTemplateDirectory, standaloneSdkVersion, iosClientVersion);

  if (!process.env.EXPO_VIEW_DIR) {
    if (context.type === 'user') {
      (0, _ExponentTools().rimrafDontThrow)(expoRootTemplateDirectory);
    }

    await IosPlist().cleanBackupAsync(supportingDirectory, 'EXSDKVersions', false);
  }
}

async function _getPackagesToInstallWhenEjecting(sdkVersion) {
  const versions = await Versions().versionsAsync();
  return versions.sdkVersions[sdkVersion].packagesToInstallWhenEjecting;
} // @tsapeta: Temporarily copied from Detach._detachAsync. This needs to be invoked also when creating a shell app workspace
// and not only when ejecting. These copies can be moved to one place if we decide to have just one flow for these two processes.


async function _installRequiredPackagesAsync(projectRoot, sdkVersion) {
  const packagesToInstallWhenEjecting = await _getPackagesToInstallWhenEjecting(sdkVersion);
  const packagesToInstall = [];

  if (packagesToInstallWhenEjecting && typeof packagesToInstallWhenEjecting === 'object') {
    Object.keys(packagesToInstallWhenEjecting).forEach(packageName => {
      packagesToInstall.push(`${packageName}@${packagesToInstallWhenEjecting[packageName]}`);
    });
  }

  if (packagesToInstall.length) {
    await (0, _installPackagesAsync().default)(projectRoot, packagesToInstall);
  }
}

function addDetachedConfigToExp(exp, context) {
  if (context.type !== 'user') {
    _Logger().default.warn(`Tried to modify exp for a non-user StandaloneContext, ignoring`);

    return exp;
  }

  const {
    supportingDirectory
  } = getPaths(context);
  exp.ios.publishBundlePath = _path().default.relative(context.data.projectPath, _path().default.join(supportingDirectory, 'shell-app.bundle'));
  exp.ios.publishManifestPath = _path().default.relative(context.data.projectPath, _path().default.join(supportingDirectory, 'shell-app-manifest.json'));
  return exp;
}
/**
 *  paths returned:
 *    iosProjectDirectory - root directory of an (uncompiled) xcworkspace and obj-c source tree
 *    projectName - xcworkspace project name normalized from context.config
 *    supportingDirectory - location of Info.plist, xib files, etc. during configuration.
 *      for an unbuilt app this is underneath iosProjectDirectory. for a compiled app it's just
 *      a path to the flat xcarchive.
 *    intermediatesDirectory - temporary spot to write whatever files are needed during the
 *      detach/build process but can be discarded afterward.
 */


function getPaths(context) {
  let iosProjectDirectory;
  let projectName;
  let supportingDirectory;
  let intermediatesDirectory;
  let projectRootDirectory;

  if (context.build.isExpoClientBuild()) {
    projectName = 'Exponent';
  } else if (context.isAnonymous()) {
    projectName = 'ExpoKitApp';
  } else if (context.config && context.config.name) {
    let projectNameLabel = context.config.name;
    projectName = projectNameLabel.replace(/[^a-z0-9_-]/gi, '-').toLowerCase();
  } else {
    throw new Error('Cannot configure an Expo project with no name.');
  }

  if (context.type === 'user') {
    projectRootDirectory = context.data.projectPath;
    iosProjectDirectory = _path().default.join(context.data.projectPath, 'ios');
    supportingDirectory = _path().default.join(iosProjectDirectory, projectName, 'Supporting');
  } else if (context.type === 'service') {
    projectRootDirectory = _path().default.dirname(context.build.ios.workspaceSourcePath);
    iosProjectDirectory = context.build.ios.workspaceSourcePath;

    if (context.data.archivePath) {
      // compiled archive has a flat NSBundle
      supportingDirectory = context.data.archivePath;
    } else {
      supportingDirectory = _path().default.join(iosProjectDirectory, projectName, 'Supporting');
    }
  } else {
    throw new Error(`Unsupported StandaloneContext type: ${context.type}`);
  } // sandbox intermediates directory by workspace so that concurrently operating
  // contexts do not interfere with one another.


  intermediatesDirectory = _path().default.join(iosProjectDirectory, context.build.isExpoClientBuild() ? 'ExponentIntermediates' : 'ExpoKitIntermediates');
  return {
    projectRootDirectory,
    intermediatesDirectory,
    iosProjectDirectory,
    projectName,
    supportingDirectory
  };
}
/**
 *  Get the newest sdk version supported given the standalone context.
 *  Not all contexts support the newest sdk version.
 */


async function getNewestSdkVersionSupportedAsync(context) {
  if (context.type === 'user') {
    return context.data.exp.sdkVersion;
  } else if (context.type === 'service') {
    // when running in universe or on a turtle machine,
    // we care about what sdk version is actually present in this working copy.
    // this might not be the same thing deployed to our www Versions endpoint.
    let {
      supportingDirectory
    } = getPaths(context);

    if (!_fsExtra().default.existsSync(supportingDirectory)) {
      // if we run this method before creating the workspace, we may need to look at the template.
      supportingDirectory = _path().default.join(context.data.expoSourcePath, '..', 'exponent-view-template', 'ios', 'exponent-view-template', 'Supporting');
    }

    let allVersions, newestVersion;
    await IosPlist().modifyAsync(supportingDirectory, 'EXSDKVersions', versionConfig => {
      allVersions = versionConfig.sdkVersions;
      return versionConfig;
    });
    let highestMajorComponent = 0;
    allVersions.forEach(version => {
      let majorComponent = (0, _ExponentTools().parseSdkMajorVersion)(version);

      if (majorComponent > highestMajorComponent) {
        highestMajorComponent = majorComponent;
        newestVersion = version;
      }
    });
    return newestVersion;
  }
}
//# sourceMappingURL=../__sourcemaps__/detach/IosWorkspace.js.map