"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