1import {
2  ConfigPlugin,
3  ExportedConfigWithProps,
4  WarningAggregator,
5  withDangerousMod,
6  withMainActivity,
7} from '@expo/config-plugins';
8import { ExpoConfig } from '@expo/config-types';
9import fs from 'fs';
10import path from 'path';
11
12import { withDevMenuAppDelegate } from './withDevMenuAppDelegate';
13
14const DEV_MENU_ANDROID_IMPORT = 'expo.modules.devmenu.react.DevMenuAwareReactActivity';
15const DEV_MENU_ACTIVITY_CLASS = 'public class MainActivity extends DevMenuAwareReactActivity {';
16
17const DEV_MENU_POD_IMPORT =
18  "pod 'expo-dev-menu', path: '../node_modules/expo-dev-menu', :configurations => :debug";
19
20async function readFileAsync(path: string): Promise<string> {
21  return fs.promises.readFile(path, 'utf8');
22}
23
24async function saveFileAsync(path: string, content: string): Promise<void> {
25  return fs.promises.writeFile(path, content, 'utf8');
26}
27
28function addJavaImports(javaSource: string, javaImports: string[]): string {
29  const lines = javaSource.split('\n');
30  const lineIndexWithPackageDeclaration = lines.findIndex(line => line.match(/^package .*;$/));
31  for (const javaImport of javaImports) {
32    if (!javaSource.includes(javaImport)) {
33      const importStatement = `import ${javaImport};`;
34      lines.splice(lineIndexWithPackageDeclaration + 1, 0, importStatement);
35    }
36  }
37  return lines.join('\n');
38}
39
40function addLines(content: string, find: string | RegExp, offset: number, toAdd: string[]) {
41  const lines = content.split('\n');
42
43  let lineIndex = lines.findIndex(line => line.match(find));
44
45  for (const newLine of toAdd) {
46    if (!content.includes(newLine)) {
47      lines.splice(lineIndex + offset, 0, newLine);
48      lineIndex++;
49    }
50  }
51
52  return lines.join('\n');
53}
54
55async function editPodfile(config: ExportedConfigWithProps, action: (podfile: string) => string) {
56  const podfilePath = path.join(config.modRequest.platformProjectRoot, 'Podfile');
57  try {
58    const podfile = action(await readFileAsync(podfilePath));
59
60    return await saveFileAsync(podfilePath, podfile);
61  } catch (e) {
62    WarningAggregator.addWarningIOS('expo-dev-menu', `Couldn't modified AppDelegate.m - ${e}.`);
63  }
64}
65
66const withDevMenuActivity: ConfigPlugin = config => {
67  return withMainActivity(config, config => {
68    if (config.modResults.language === 'java') {
69      let content = config.modResults.contents;
70      content = addJavaImports(content, [DEV_MENU_ANDROID_IMPORT]);
71      content = content.replace(
72        'public class MainActivity extends ReactActivity {',
73        DEV_MENU_ACTIVITY_CLASS
74      );
75      config.modResults.contents = content;
76    } else {
77      WarningAggregator.addWarningAndroid(
78        'expo-dev-menu',
79        `Cannot automatically configure MainActivity if it's not java`
80      );
81    }
82
83    return config;
84  });
85};
86
87const withDevMenuPodfile: ConfigPlugin = config => {
88  return withDangerousMod(config, [
89    'ios',
90    async config => {
91      await editPodfile(config, podfile => {
92        podfile = podfile.replace("platform :ios, '10.0'", "platform :ios, '11.0'");
93        // Match both variations of Ruby config:
94        // unknown: pod 'expo-dev-menu', path: '../node_modules/expo-dev-menu', :configurations => :debug
95        // Rubocop: pod 'expo-dev-menu', path: '../node_modules/expo-dev-menu', configurations: :debug
96        if (
97          !podfile.match(
98            /pod ['"]expo-dev-menu['"],\s?path: ['"]\.\.\/node_modules\/expo-dev-menu['"],\s?:?configurations:?\s(?:=>\s)?:debug/
99          )
100        ) {
101          podfile = addLines(podfile, 'use_react_native', 0, [`  ${DEV_MENU_POD_IMPORT}`]);
102        }
103        return podfile;
104      });
105      return config;
106    },
107  ]);
108};
109
110const withDevMenu = (config: ExpoConfig) => {
111  config = withDevMenuActivity(config);
112  config = withDevMenuPodfile(config);
113  config = withDevMenuAppDelegate(config);
114  return config;
115};
116
117export default withDevMenu;
118