UNPKG

xdl

Version:

The Expo Development Library

511 lines (405 loc) 20.4 kB
// Copyright 2015-present 650 Industries. All rights reserved. 'use strict'; // Set EXPO_VIEW_DIR to universe/exponent to test locally Object.defineProperty(exports, "__esModule", { value: true }); exports.prepareDetachedBuildAsync = exports.detachAsync = undefined; let detachAsync = exports.detachAsync = (() => { var _ref = _asyncToGenerator(function* (projectRoot, options) { let user = yield (_User || _load_User()).default.ensureLoggedInAsync(); if (!user) { throw new Error('Internal error -- somehow detach is being run in offline mode.'); } let username = user.username; let { exp } = yield (_ProjectUtils || _load_ProjectUtils()).readConfigJsonAsync(projectRoot); let experienceName = `@${username}/${exp.slug}`; let experienceUrl = `exp://exp.host/${experienceName}`; // Check to make sure project isn't fully detached already let hasIosDirectory = (0, (_ExponentTools || _load_ExponentTools()).isDirectory)(_path.default.join(projectRoot, 'ios')); let hasAndroidDirectory = (0, (_ExponentTools || _load_ExponentTools()).isDirectory)(_path.default.join(projectRoot, 'android')); if (hasIosDirectory && hasAndroidDirectory) { throw new (_XDLError || _load_XDLError()).default((_ErrorCode || _load_ErrorCode()).default.DIRECTORY_ALREADY_EXISTS, 'Error detaching. `ios` and `android` directories already exist.'); } // Project was already detached on Windows or Linux if (!hasIosDirectory && hasAndroidDirectory && process.platform === 'darwin') { let response = yield yesnoAsync(`This will add an Xcode project and leave your existing Android project alone. Enter 'yes' to continue:`); if (!response) { console.log('Exiting...'); return false; } } if (hasIosDirectory && !hasAndroidDirectory) { throw new Error('`ios` directory already exists. Please remove it and try again.'); } console.log('Validating project manifest...'); const configName = yield (_ProjectUtils || _load_ProjectUtils()).configFilenameAsync(projectRoot); if (!exp.name) { throw new Error(`${configName} is missing \`name\``); } if (!exp.android || !exp.android.package) { throw new Error(`${configName} is missing android.package field. See https://docs.expo.io/versions/latest/guides/configuration.html#package`); } if (!exp.sdkVersion) { throw new Error(`${configName} is missing \`sdkVersion\``); } let majorSdkVersion = (0, (_ExponentTools || _load_ExponentTools()).parseSdkMajorVersion)(exp.sdkVersion); if (majorSdkVersion < 16) { throw new Error(`${configName} must be updated to SDK 16.0.0 or newer to be detached.`); } const versions = yield (_Versions || _load_Versions()).versionsAsync(); let sdkVersionConfig = versions.sdkVersions[exp.sdkVersion]; if (!sdkVersionConfig || !sdkVersionConfig.androidExpoViewUrl || !sdkVersionConfig.iosExpoViewUrl) { if (process.env.EXPO_VIEW_DIR) { console.warn(`Detaching is not supported for SDK ${exp.sdkVersion}; ignoring this because you provided EXPO_VIEW_DIR`); sdkVersionConfig = {}; } else { throw new Error(`Detaching is not supported for SDK version ${exp.sdkVersion}`); } } // Modify exp.json exp.isDetached = true; if (!exp.detach) { exp.detach = {}; } if (!exp.detach.scheme) { let detachedUUID = (_uuid || _load_uuid()).default.v4().replace(/-/g, ''); exp.detach.scheme = `exp${detachedUUID}`; } let expoDirectory = _path.default.join(projectRoot, '.expo-source'); (_mkdirp || _load_mkdirp()).default.sync(expoDirectory); // iOS let isIosSupported = true; if (process.platform !== 'darwin') { if (options && options.force) { console.warn(`You are not running macOS, but have provided the --force option, so we will attempt to generate an iOS project anyway. This might fail.`); } else { console.warn(`Skipping iOS because you are not running macOS.`); isIosSupported = false; } } if (!hasIosDirectory && isIosSupported) { const context = (_StandaloneContext || _load_StandaloneContext()).default.createUserContext(projectRoot, exp, experienceUrl); yield detachIOSAsync(context); exp = (_IosWorkspace || _load_IosWorkspace()).addDetachedConfigToExp(exp, context); exp.detach.iosExpoViewUrl = sdkVersionConfig.iosExpoViewUrl; } // Android if (!hasAndroidDirectory) { let androidDirectory = _path.default.join(expoDirectory, 'android'); (_rimraf || _load_rimraf()).default.sync(androidDirectory); (_mkdirp || _load_mkdirp()).default.sync(androidDirectory); yield detachAndroidAsync(projectRoot, androidDirectory, exp.sdkVersion, experienceUrl, exp, sdkVersionConfig.androidExpoViewUrl); exp.detach.androidExpoViewUrl = sdkVersionConfig.androidExpoViewUrl; } console.log('Writing ExpoKit configuration...'); // Update exp.json/app.json // if we're writing to app.json, we need to place the configuration under the expo key const nameToWrite = yield (_ProjectUtils || _load_ProjectUtils()).configFilenameAsync(projectRoot); if (nameToWrite === 'app.json') { exp = { expo: exp }; } yield _fs.default.promise.writeFile(_path.default.join(projectRoot, nameToWrite), JSON.stringify(exp, null, 2)); console.log('Finished detaching your project! Look in the `android` and `ios` directories for the respective native projects. Follow the ExpoKit guide at https://docs.expo.io/versions/latest/guides/expokit.html to get your project running.\n'); return true; }); return function detachAsync(_x, _x2) { return _ref.apply(this, arguments); }; })(); /** * Create a detached Expo iOS app pointing at the given project. */ let detachIOSAsync = (() => { var _ref2 = _asyncToGenerator(function* (context) { yield (_IosWorkspace || _load_IosWorkspace()).createDetachedAsync(context); console.log('Configuring iOS project...'); yield (_IosNSBundle || _load_IosNSBundle()).configureAsync(context); console.log(`iOS detach is complete!`); return; }); return function detachIOSAsync(_x3) { return _ref2.apply(this, arguments); }; })(); let regexFileAsync = (() => { var _ref3 = _asyncToGenerator(function* (filename, regex, replace) { let file = yield _fs.default.promise.readFile(filename); let fileString = file.toString(); yield _fs.default.promise.writeFile(filename, fileString.replace(regex, replace)); }); return function regexFileAsync(_x4, _x5, _x6) { return _ref3.apply(this, arguments); }; })(); let renamePackageAsync = (() => { var _ref4 = _asyncToGenerator(function* (directory, originalPkg, destPkg) { let originalSplitPackage = originalPkg.split('.'); let originalDeepDirectory = directory; for (let i = 0; i < originalSplitPackage.length; i++) { originalDeepDirectory = _path.default.join(originalDeepDirectory, originalSplitPackage[i]); } // copy files into temp directory let tmpDirectory = _path.default.join(directory, 'tmp-exponent-directory'); (_mkdirp || _load_mkdirp()).default.sync(tmpDirectory); yield (_Utils || _load_Utils()).ncpAsync(originalDeepDirectory, tmpDirectory); // delete old package (_rimraf || _load_rimraf()).default.sync(_path.default.join(directory, originalSplitPackage[0])); // make new package let newSplitPackage = destPkg.split('.'); let newDeepDirectory = directory; for (let i = 0; i < newSplitPackage.length; i++) { newDeepDirectory = _path.default.join(newDeepDirectory, newSplitPackage[i]); (_mkdirp || _load_mkdirp()).default.sync(newDeepDirectory); } // copy from temp to new package yield (_Utils || _load_Utils()).ncpAsync(tmpDirectory, newDeepDirectory); // delete temp (_rimraf || _load_rimraf()).default.sync(tmpDirectory); }); return function renamePackageAsync(_x7, _x8, _x9) { return _ref4.apply(this, arguments); }; })(); let detachAndroidAsync = (() => { var _ref5 = _asyncToGenerator(function* (projectRoot, expoDirectory, sdkVersion, experienceUrl, manifest, expoViewUrl) { let tmpExpoDirectory; if (process.env.EXPO_VIEW_DIR) { // Only for testing tmpExpoDirectory = process.env.EXPO_VIEW_DIR; } else { tmpExpoDirectory = _path.default.join(projectRoot, 'temp-android-directory'); (_mkdirp || _load_mkdirp()).default.sync(tmpExpoDirectory); console.log('Downloading Android code...'); yield (_Api || _load_Api()).default.downloadAsync(expoViewUrl, tmpExpoDirectory, { extract: true }); } let androidProjectDirectory = _path.default.join(projectRoot, 'android'); console.log('Moving Android project files...'); yield (_Utils || _load_Utils()).ncpAsync(_path.default.join(tmpExpoDirectory, 'android', 'maven'), _path.default.join(expoDirectory, 'maven')); yield (_Utils || _load_Utils()).ncpAsync(_path.default.join(tmpExpoDirectory, 'android', 'detach-scripts'), _path.default.join(expoDirectory, 'detach-scripts')); yield (_Utils || _load_Utils()).ncpAsync(_path.default.join(tmpExpoDirectory, 'exponent-view-template', 'android'), androidProjectDirectory); if (process.env.EXPO_VIEW_DIR) { (_rimraf || _load_rimraf()).default.sync(_path.default.join(androidProjectDirectory, 'build')); (_rimraf || _load_rimraf()).default.sync(_path.default.join(androidProjectDirectory, 'app', 'build')); } // Fix up app/build.gradle console.log('Configuring Android project...'); let appBuildGradle = _path.default.join(androidProjectDirectory, 'app', 'build.gradle'); yield regexFileAsync(appBuildGradle, /\/\* UNCOMMENT WHEN DISTRIBUTING/g, ''); yield regexFileAsync(appBuildGradle, /END UNCOMMENT WHEN DISTRIBUTING \*\//g, ''); yield regexFileAsync(appBuildGradle, `compile project(':expoview')`, ''); // Fix AndroidManifest let androidManifest = _path.default.join(androidProjectDirectory, 'app', 'src', 'main', 'AndroidManifest.xml'); yield regexFileAsync(androidManifest, 'PLACEHOLDER_DETACH_SCHEME', manifest.detach.scheme); // Fix MainActivity let mainActivity = _path.default.join(androidProjectDirectory, 'app', 'src', 'main', 'java', 'detach', 'app', 'template', 'pkg', 'name', 'MainActivity.java'); yield regexFileAsync(mainActivity, 'TEMPLATE_INITIAL_URL', experienceUrl); // Fix package name let packageName = manifest.android.package; yield renamePackageAsync(_path.default.join(androidProjectDirectory, 'app', 'src', 'main', 'java'), ANDROID_TEMPLATE_PKG, packageName); yield renamePackageAsync(_path.default.join(androidProjectDirectory, 'app', 'src', 'test', 'java'), ANDROID_TEMPLATE_PKG, packageName); yield renamePackageAsync(_path.default.join(androidProjectDirectory, 'app', 'src', 'androidTest', 'java'), ANDROID_TEMPLATE_PKG, packageName); let packageNameMatches = yield (_glob || _load_glob()).default.promise(androidProjectDirectory + '/**/*.@(java|gradle|xml)'); if (packageNameMatches) { let oldPkgRegex = new RegExp(`${ANDROID_TEMPLATE_PKG.replace(/\./g, '\\.')}`, 'g'); for (let i = 0; i < packageNameMatches.length; i++) { yield regexFileAsync(packageNameMatches[i], oldPkgRegex, packageName); } } // Fix app name console.log('Naming Android project...'); let appName = manifest.name; yield regexFileAsync(_path.default.resolve(androidProjectDirectory, 'app', 'src', 'main', 'res', 'values', 'strings.xml'), ANDROID_TEMPLATE_NAME, appName); // Fix image let icon = manifest.android && manifest.android.icon ? manifest.android.icon : manifest.icon; if (icon) { let iconMatches = yield (_glob || _load_glob()).default.promise(_path.default.join(androidProjectDirectory, 'app', 'src', 'main', 'res') + '/**/ic_launcher.png'); if (iconMatches) { for (let i = 0; i < iconMatches.length; i++) { yield _fs.default.promise.unlink(iconMatches[i]); // TODO: make more efficient yield (0, (_ExponentTools || _load_ExponentTools()).saveImageToPathAsync)(projectRoot, icon, iconMatches[i]); } } } // Clean up console.log('Cleaning up Android...'); if (!process.env.EXPO_VIEW_DIR) { (0, (_ExponentTools || _load_ExponentTools()).rimrafDontThrow)(tmpExpoDirectory); } console.log('Android detach is complete!\n'); }); return function detachAndroidAsync(_x10, _x11, _x12, _x13, _x14, _x15) { return _ref5.apply(this, arguments); }; })(); let ensureBuildConstantsExistsIOSAsync = (() => { var _ref6 = _asyncToGenerator(function* (configFilePath) { // EXBuildConstants is included in newer ExpoKit projects. // create it if it doesn't exist. const doesBuildConstantsExist = _fs.default.existsSync(_path.default.join(configFilePath, 'EXBuildConstants.plist')); if (!doesBuildConstantsExist) { yield (_IosPlist || _load_IosPlist()).createBlankAsync(configFilePath, 'EXBuildConstants'); console.log('Created `EXBuildConstants.plist` because it did not exist yet'); } return; }); return function ensureBuildConstantsExistsIOSAsync(_x16) { return _ref6.apply(this, arguments); }; })(); let prepareDetachedBuildIosAsync = (() => { var _ref7 = _asyncToGenerator(function* (projectDir, exp, args) { const context = (_StandaloneContext || _load_StandaloneContext()).default.createUserContext(projectDir, exp); let { iosProjectDirectory, supportingDirectory } = (_IosWorkspace || _load_IosWorkspace()).getPaths(context); console.log(`Preparing iOS build at ${iosProjectDirectory}...`); // These files cause @providesModule naming collisions // but are not available until after `pod install` has run. let podsDirectory = _path.default.join(iosProjectDirectory, 'Pods'); if (!(0, (_ExponentTools || _load_ExponentTools()).isDirectory)(podsDirectory)) { throw new Error(`Can't find directory ${podsDirectory}, make sure you've run pod install.`); } let rnPodDirectory = _path.default.join(podsDirectory, 'React'); if ((0, (_ExponentTools || _load_ExponentTools()).isDirectory)(rnPodDirectory)) { let rnFilesToDelete = yield (_glob || _load_glob()).default.promise(rnPodDirectory + '/**/*.@(js|json)'); if (rnFilesToDelete) { for (let i = 0; i < rnFilesToDelete.length; i++) { yield _fs.default.promise.unlink(rnFilesToDelete[i]); } } } // insert expo development url into iOS config if (!args.skipXcodeConfig) { // populate EXPO_RUNTIME_VERSION from ExpoKit pod version let expoKitVersion = ''; const podfileLockPath = _path.default.join(iosProjectDirectory, 'Podfile.lock'); try { const podfileLock = yield _fs.default.promise.readFile(podfileLockPath, 'utf8'); const expoKitVersionRegex = /ExpoKit\/Core\W?\(([0-9\.]+)\)/gi; let match = expoKitVersionRegex.exec(podfileLock); expoKitVersion = match[1]; } catch (e) { throw new Error(`Unable to read ExpoKit version from Podfile.lock. Make sure your project depends on ExpoKit. (${e})`); } // populate development url let devUrl = yield (_UrlUtils || _load_UrlUtils()).constructManifestUrlAsync(projectDir); yield ensureBuildConstantsExistsIOSAsync(supportingDirectory); yield (_IosPlist || _load_IosPlist()).modifyAsync(supportingDirectory, 'EXBuildConstants', function (constantsConfig) { constantsConfig.developmentUrl = devUrl; constantsConfig.EXPO_RUNTIME_VERSION = expoKitVersion; return constantsConfig; }); } }); return function prepareDetachedBuildIosAsync(_x17, _x18, _x19) { return _ref7.apply(this, arguments); }; })(); let prepareDetachedBuildAsync = exports.prepareDetachedBuildAsync = (() => { var _ref8 = _asyncToGenerator(function* (projectDir, args) { let { exp } = yield (_ProjectUtils || _load_ProjectUtils()).readConfigJsonAsync(projectDir); if (args.platform === 'ios') { yield prepareDetachedBuildIosAsync(projectDir, exp, args); } else { let androidProjectDirectory = _path.default.join(projectDir, 'android'); let expoBuildConstantsMatches = yield (_glob || _load_glob()).default.promise(androidProjectDirectory + '/**/ExponentBuildConstants.java'); if (expoBuildConstantsMatches && expoBuildConstantsMatches.length) { let expoBuildConstants = expoBuildConstantsMatches[0]; let devUrl = yield (_UrlUtils || _load_UrlUtils()).constructManifestUrlAsync(projectDir); yield regexFileAsync(expoBuildConstants, /DEVELOPMENT_URL \= \"[^\"]*\"\;/, `DEVELOPMENT_URL = "${devUrl}";`); } } }); return function prepareDetachedBuildAsync(_x20, _x21) { return _ref8.apply(this, arguments); }; })(); require('instapromise'); var _mkdirp; function _load_mkdirp() { return _mkdirp = _interopRequireDefault(require('mkdirp')); } var _fs = _interopRequireDefault(require('fs')); var _path = _interopRequireDefault(require('path')); var _rimraf; function _load_rimraf() { return _rimraf = _interopRequireDefault(require('rimraf')); } var _glob; function _load_glob() { return _glob = _interopRequireDefault(require('glob')); } var _uuid; function _load_uuid() { return _uuid = _interopRequireDefault(require('uuid')); } var _yesno; function _load_yesno() { return _yesno = _interopRequireDefault(require('yesno')); } var _ExponentTools; function _load_ExponentTools() { return _ExponentTools = require('./ExponentTools'); } var _IosPlist; function _load_IosPlist() { return _IosPlist = _interopRequireWildcard(require('./IosPlist')); } var _IosNSBundle; function _load_IosNSBundle() { return _IosNSBundle = _interopRequireWildcard(require('./IosNSBundle')); } var _IosWorkspace; function _load_IosWorkspace() { return _IosWorkspace = _interopRequireWildcard(require('./IosWorkspace')); } var _Api; function _load_Api() { return _Api = _interopRequireDefault(require('../Api')); } var _ErrorCode; function _load_ErrorCode() { return _ErrorCode = _interopRequireDefault(require('../ErrorCode')); } var _ProjectUtils; function _load_ProjectUtils() { return _ProjectUtils = _interopRequireWildcard(require('../project/ProjectUtils')); } var _User; function _load_User() { return _User = _interopRequireDefault(require('../User')); } var _XDLError; function _load_XDLError() { return _XDLError = _interopRequireDefault(require('../XDLError')); } var _StandaloneContext; function _load_StandaloneContext() { return _StandaloneContext = _interopRequireDefault(require('./StandaloneContext')); } var _UrlUtils; function _load_UrlUtils() { return _UrlUtils = _interopRequireWildcard(require('../UrlUtils')); } var _Utils; function _load_Utils() { return _Utils = _interopRequireWildcard(require('../Utils')); } var _Versions; function _load_Versions() { return _Versions = _interopRequireWildcard(require('../Versions')); } 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)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } const ANDROID_TEMPLATE_PKG = 'detach.app.template.pkg.name'; const ANDROID_TEMPLATE_COMPANY = 'detach.app.template.company.domain'; const ANDROID_TEMPLATE_NAME = 'DetachAppTemplate'; function yesnoAsync(question) { return new Promise(resolve => { (_yesno || _load_yesno()).default.ask(question, null, ok => { resolve(ok); }); }); } //# sourceMappingURL=../__sourcemaps__/detach/Detach.js.map