1"use strict"; 2var __importDefault = (this && this.__importDefault) || function (mod) { 3 return (mod && mod.__esModule) ? mod : { "default": mod }; 4}; 5Object.defineProperty(exports, "__esModule", { value: true }); 6exports.modifyJavaMainActivity = void 0; 7const config_plugins_1 = require("@expo/config-plugins"); 8const fs_1 = __importDefault(require("fs")); 9const path_1 = __importDefault(require("path")); 10const semver_1 = __importDefault(require("semver")); 11const constants_1 = require("./constants"); 12const resolveExpoUpdatesVersion_1 = require("./resolveExpoUpdatesVersion"); 13const utils_1 = require("./utils"); 14const withDevLauncherAppDelegate_1 = require("./withDevLauncherAppDelegate"); 15const pkg = require('expo-dev-launcher/package.json'); 16const DEV_LAUNCHER_ANDROID_IMPORT = 'expo.modules.devlauncher.DevLauncherController'; 17const DEV_LAUNCHER_UPDATES_ANDROID_IMPORT = 'expo.modules.updates.UpdatesDevLauncherController'; 18const DEV_LAUNCHER_ON_NEW_INTENT = [ 19 '', 20 ' @Override', 21 ' public void onNewIntent(Intent intent) {', 22 ' super.onNewIntent(intent);', 23 ' }', 24 '', 25].join('\n'); 26const DEV_LAUNCHER_HANDLE_INTENT = [ 27 ' if (DevLauncherController.tryToHandleIntent(this, intent)) {', 28 ' return;', 29 ' }', 30].join('\n'); 31const DEV_LAUNCHER_WRAPPED_ACTIVITY_DELEGATE = (activityDelegateDeclaration) => `DevLauncherController.wrapReactActivityDelegate(this, () -> ${activityDelegateDeclaration})`; 32const DEV_LAUNCHER_ANDROID_INIT = 'DevLauncherController.initialize(this, getReactNativeHost());'; 33const DEV_LAUNCHER_UPDATES_ANDROID_INIT = `if (BuildConfig.DEBUG) { 34 DevLauncherController.getInstance().setUpdatesInterface(UpdatesDevLauncherController.initialize(this)); 35 }`; 36const DEV_LAUNCHER_UPDATES_DEVELOPER_SUPPORT = 'return DevLauncherController.getInstance().getUseDeveloperSupport();'; 37const DEV_LAUNCHER_JS_REGISTER_ERROR_HANDLERS = `import 'expo-dev-client'`; 38const DEV_LAUNCHER_JS_REGISTER_ERROR_HANDLERS_VIA_LAUNCHER = `import 'expo-dev-launcher'`; 39async function readFileAsync(path) { 40 return fs_1.default.promises.readFile(path, 'utf8'); 41} 42async function saveFileAsync(path, content) { 43 return fs_1.default.promises.writeFile(path, content, 'utf8'); 44} 45function findClosingBracketMatchIndex(str, pos) { 46 if (str[pos] !== '(') { 47 throw new Error("No '(' at index " + pos); 48 } 49 let depth = 1; 50 for (let i = pos + 1; i < str.length; i++) { 51 switch (str[i]) { 52 case '(': 53 depth++; 54 break; 55 case ')': 56 if (--depth === 0) { 57 return i; 58 } 59 break; 60 } 61 } 62 return -1; // No matching closing parenthesis 63} 64const replaceBetween = (origin, startIndex, endIndex, insertion) => `${origin.substring(0, startIndex)}${insertion}${origin.substring(endIndex)}`; 65function addJavaImports(javaSource, javaImports) { 66 const lines = javaSource.split('\n'); 67 const lineIndexWithPackageDeclaration = lines.findIndex((line) => line.match(/^package .*;$/)); 68 for (const javaImport of javaImports) { 69 if (!javaSource.includes(javaImport)) { 70 const importStatement = `import ${javaImport};`; 71 lines.splice(lineIndexWithPackageDeclaration + 1, 0, importStatement); 72 } 73 } 74 return lines.join('\n'); 75} 76async function editMainApplication(config, action) { 77 const mainApplicationPath = path_1.default.join(config.modRequest.platformProjectRoot, 'app', 'src', 'main', 'java', ...config.android.package.split('.'), 'MainApplication.java'); 78 try { 79 const mainApplication = action(await readFileAsync(mainApplicationPath)); 80 return await saveFileAsync(mainApplicationPath, mainApplication); 81 } 82 catch (e) { 83 config_plugins_1.WarningAggregator.addWarningAndroid('expo-dev-launcher', `Couldn't modify MainApplication.java - ${e}. 84See the expo-dev-client installation instructions to modify your MainApplication.java manually: ${constants_1.InstallationPage}`); 85 } 86} 87async function editPodfile(config, action) { 88 const podfilePath = path_1.default.join(config.modRequest.platformProjectRoot, 'Podfile'); 89 try { 90 const podfile = action(await readFileAsync(podfilePath)); 91 return await saveFileAsync(podfilePath, podfile); 92 } 93 catch (e) { 94 config_plugins_1.WarningAggregator.addWarningIOS('expo-dev-launcher', `Couldn't modify AppDelegate.m - ${e}. 95See the expo-dev-client installation instructions to modify your AppDelegate.m manually: ${constants_1.InstallationPage}`); 96 } 97} 98async function editIndex(config, action) { 99 const indexPath = path_1.default.join(config.modRequest.projectRoot, 'index.js'); 100 try { 101 const index = action(await readFileAsync(indexPath)); 102 return await saveFileAsync(indexPath, index); 103 } 104 catch (e) { 105 config_plugins_1.WarningAggregator.addWarningIOS('expo-dev-launcher', `Couldn't modify index.js - ${e}. 106See the expo-dev-client installation instructions to modify your index.js manually: ${constants_1.InstallationPage}`); 107 } 108} 109const withDevLauncherApplication = (config) => { 110 return (0, config_plugins_1.withDangerousMod)(config, [ 111 'android', 112 async (config) => { 113 await editMainApplication(config, (mainApplication) => { 114 mainApplication = addJavaImports(mainApplication, [DEV_LAUNCHER_ANDROID_IMPORT]); 115 mainApplication = (0, utils_1.addLines)(mainApplication, 'initializeFlipper\\(this', 0, [ 116 ` ${DEV_LAUNCHER_ANDROID_INIT}`, 117 ]); 118 let expoUpdatesVersion; 119 try { 120 expoUpdatesVersion = (0, resolveExpoUpdatesVersion_1.resolveExpoUpdatesVersion)(config.modRequest.projectRoot); 121 } 122 catch (e) { 123 config_plugins_1.WarningAggregator.addWarningAndroid('expo-dev-launcher', `Failed to check compatibility with expo-updates - ${e}`); 124 } 125 if (expoUpdatesVersion && semver_1.default.gt(expoUpdatesVersion, '0.6.0')) { 126 mainApplication = addJavaImports(mainApplication, [DEV_LAUNCHER_UPDATES_ANDROID_IMPORT]); 127 mainApplication = (0, utils_1.addLines)(mainApplication, 'initializeFlipper\\(this', 0, [ 128 ` ${DEV_LAUNCHER_UPDATES_ANDROID_INIT}`, 129 ]); 130 mainApplication = (0, utils_1.replaceLine)(mainApplication, 'return BuildConfig.DEBUG;', ` ${DEV_LAUNCHER_UPDATES_DEVELOPER_SUPPORT}`); 131 } 132 return mainApplication; 133 }); 134 return config; 135 }, 136 ]); 137}; 138function modifyJavaMainActivity(content) { 139 content = addJavaImports(content, [DEV_LAUNCHER_ANDROID_IMPORT, 'android.content.Intent']); 140 if (!content.includes('onNewIntent')) { 141 const lines = content.split('\n'); 142 const onCreateIndex = lines.findIndex((line) => line.includes('public class MainActivity')); 143 lines.splice(onCreateIndex + 1, 0, DEV_LAUNCHER_ON_NEW_INTENT); 144 content = lines.join('\n'); 145 } 146 if (!content.includes(DEV_LAUNCHER_HANDLE_INTENT)) { 147 content = (0, utils_1.addLines)(content, /super\.onNewIntent\(intent\)/, 0, [DEV_LAUNCHER_HANDLE_INTENT]); 148 } 149 if (!content.includes('DevLauncherController.wrapReactActivityDelegate')) { 150 const activityDelegateMatches = Array.from(content.matchAll(/new ReactActivityDelegate(Wrapper)/g)); 151 if (activityDelegateMatches.length !== 1) { 152 config_plugins_1.WarningAggregator.addWarningAndroid('expo-dev-launcher', `Failed to wrap 'ReactActivityDelegate' 153See the expo-dev-client installation instructions to modify your MainActivity.java manually: ${constants_1.InstallationPage}`); 154 return content; 155 } 156 const activityDelegateMatch = activityDelegateMatches[0]; 157 const matchIndex = activityDelegateMatch.index; 158 const openingBracketIndex = matchIndex + activityDelegateMatch[0].length; // next character after `new ReactActivityDelegateWrapper` 159 const closingBracketIndex = findClosingBracketMatchIndex(content, openingBracketIndex); 160 const reactActivityDelegateDeclaration = content.substring(matchIndex, closingBracketIndex + 1); 161 content = replaceBetween(content, matchIndex, closingBracketIndex + 1, DEV_LAUNCHER_WRAPPED_ACTIVITY_DELEGATE(reactActivityDelegateDeclaration)); 162 } 163 return content; 164} 165exports.modifyJavaMainActivity = modifyJavaMainActivity; 166const withDevLauncherActivity = (config) => { 167 return (0, config_plugins_1.withMainActivity)(config, (config) => { 168 if (config.modResults.language === 'java') { 169 config.modResults.contents = modifyJavaMainActivity(config.modResults.contents); 170 } 171 else { 172 config_plugins_1.WarningAggregator.addWarningAndroid('expo-dev-launcher', `Cannot automatically configure MainActivity if it's not java. 173See the expo-dev-client installation instructions to modify your MainActivity manually: ${constants_1.InstallationPage}`); 174 } 175 return config; 176 }); 177}; 178const withDevLauncherPodfile = (config) => { 179 return (0, config_plugins_1.withDangerousMod)(config, [ 180 'ios', 181 async (config) => { 182 await editPodfile(config, (podfile) => { 183 // replace all iOS versions below 12 184 podfile = podfile.replace(/platform :ios, '((\d\.0)|(1[0-1].0))'/, "platform :ios, '12.0'"); 185 // Match both variations of Ruby config: 186 // unknown: pod 'expo-dev-launcher', path: '../node_modules/expo-dev-launcher', :configurations => :debug 187 // Rubocop: pod 'expo-dev-launcher', path: '../node_modules/expo-dev-launcher', configurations: :debug 188 if (!podfile.match(/pod ['"]expo-dev-launcher['"],\s?path: ['"][^'"]*node_modules\/expo-dev-launcher['"],\s?:?configurations:?\s(?:=>\s)?:debug/)) { 189 const packagePath = path_1.default.dirname(require.resolve('expo-dev-launcher/package.json')); 190 const relativePath = path_1.default.relative(config.modRequest.platformProjectRoot, packagePath); 191 podfile = (0, utils_1.addLines)(podfile, 'use_react_native', 0, [ 192 ` pod 'expo-dev-launcher', path: '${relativePath}', :configurations => :debug`, 193 ]); 194 } 195 return podfile; 196 }); 197 return config; 198 }, 199 ]); 200}; 201const withErrorHandling = (config) => { 202 const injectErrorHandlers = async (config) => { 203 await editIndex(config, (index) => { 204 if (!index.includes(DEV_LAUNCHER_JS_REGISTER_ERROR_HANDLERS) && 205 !index.includes(DEV_LAUNCHER_JS_REGISTER_ERROR_HANDLERS_VIA_LAUNCHER)) { 206 index = DEV_LAUNCHER_JS_REGISTER_ERROR_HANDLERS + ';\n\n' + index; 207 } 208 return index; 209 }); 210 return config; 211 }; 212 // We need to run the same task twice to ensure it will work on both platforms, 213 // because if someone runs `expo run:ios`, it will trigger only dangerous mode for that specific platform. 214 // Note: after the first execution, the second one won't change anything. 215 config = (0, config_plugins_1.withDangerousMod)(config, ['android', injectErrorHandlers]); 216 config = (0, config_plugins_1.withDangerousMod)(config, ['ios', injectErrorHandlers]); 217 return config; 218}; 219const withDevLauncher = (config) => { 220 config = withDevLauncherActivity(config); 221 config = withDevLauncherApplication(config); 222 config = withDevLauncherPodfile(config); 223 config = (0, withDevLauncherAppDelegate_1.withDevLauncherAppDelegate)(config); 224 config = withErrorHandling(config); 225 return config; 226}; 227exports.default = (0, config_plugins_1.createRunOncePlugin)(withDevLauncher, pkg.name, pkg.version); 228