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