"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