expo-dev-launcher 0.7.0 → 0.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +1 -1
  3. package/android/build.gradle +2 -1
  4. package/android/src/debug/assets/expo_dev_launcher_android.bundle +396 -403
  5. package/android/src/debug/java/expo/modules/devlauncher/DevLauncherController.kt +10 -6
  6. package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherAppLoaderFactory.kt +5 -5
  7. package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherExpoAppLoader.kt +5 -5
  8. package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherLocalAppLoader.kt +3 -3
  9. package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherPublishedAppLoader.kt +2 -6
  10. package/android/src/debug/java/expo/modules/devlauncher/modules/DevLauncherInternalModule.kt +6 -1
  11. package/android/src/debug/java/expo/modules/devlauncher/modules/DevLauncherModule.kt +1 -1
  12. package/android/src/main/java/expo/modules/devlauncher/launcher/DevLauncherControllerInterface.kt +2 -2
  13. package/android/src/main/java/expo/modules/devlauncher/launcher/configurators/DevLauncherExpoActivityConfigurator.kt +12 -11
  14. package/android/src/main/java/expo/modules/devlauncher/launcher/manifest/DevLauncherManifestParser.kt +4 -2
  15. package/android/src/main/java/expo/modules/devlauncher/launcher/manifest/DevLauncherManifestTypes.kt +11 -26
  16. package/android/src/main/java/expo/modules/devlauncher/launcher/menu/DevLauncherMenuDelegate.kt +3 -3
  17. package/android/src/release/java/expo/modules/devlauncher/DevLauncherController.kt +2 -2
  18. package/android/src/testDebug/java/expo/modules/devlauncher/DevLauncherControllerTest.kt +50 -0
  19. package/android/src/testDebug/java/expo/modules/devlauncher/launcher/DevLauncherRecentlyOpenedAppsRegistryTest.kt +43 -0
  20. package/android/src/testDebug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherAppLoaderFactoryTest.kt +3 -3
  21. package/android/src/testDebug/java/expo/modules/devlauncher/modules/DevLauncherModuleTest.kt +38 -0
  22. package/build/DevLauncherErrorManager.js +7 -0
  23. package/build/DevLauncherErrorManager.js.map +1 -1
  24. package/expo-dev-launcher.podspec +2 -0
  25. package/ios/EXDevLauncher.m +8 -2
  26. package/ios/EXDevLauncher.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  27. package/ios/EXDevLauncher.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  28. package/ios/EXDevLauncher.xcodeproj/xcuserdata/brentvatne.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
  29. package/ios/EXDevLauncher.xcworkspace/xcuserdata/brentvatne.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  30. package/ios/EXDevLauncherController.h +2 -2
  31. package/ios/EXDevLauncherController.m +22 -11
  32. package/ios/EXDevLauncherInternal.m +5 -0
  33. package/ios/EXDevLauncherMenuDelegate.swift +3 -3
  34. package/ios/Errors/EXDevLauncherRedBox.m +5 -0
  35. package/ios/Manifest/EXDevLauncherManifestHelper.swift +15 -12
  36. package/ios/Manifest/EXDevLauncherManifestParser.h +3 -3
  37. package/ios/Manifest/EXDevLauncherManifestParser.m +17 -5
  38. package/ios/Tests/EXDevLauncherManifestHelperTests.swift +28 -0
  39. package/ios/Tests/EXDevLauncherManifestParserTests.m +113 -0
  40. package/ios/Tests/EXDevLauncherModuleTests.m +39 -0
  41. package/ios/devvyc 2021-05-14 18-19-10/devvyc.ipa +0 -0
  42. package/ios/main.jsbundle +414 -420
  43. package/package.json +6 -6
  44. package/plugin/build/constants.d.ts +1 -0
  45. package/plugin/build/constants.js +4 -0
  46. package/plugin/build/utils.d.ts +2 -0
  47. package/plugin/build/utils.js +24 -0
  48. package/plugin/build/withDevLauncher.js +27 -39
  49. package/plugin/build/withDevLauncherAppDelegate.d.ts +1 -0
  50. package/plugin/build/withDevLauncherAppDelegate.js +161 -16
  51. package/plugin/src/constants.ts +1 -0
  52. package/plugin/src/utils.ts +25 -0
  53. package/plugin/src/withDevLauncher.ts +32 -45
  54. package/plugin/src/withDevLauncherAppDelegate.ts +211 -23
  55. package/src/DevLauncherErrorManager.ts +7 -0
  56. package/android/src/main/java/expo/modules/devlauncher/launcher/manifest/DevLauncherManifest.kt +0 -111
  57. package/ios/Manifest/EXDevLauncherManifest.swift +0 -158
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "expo-dev-launcher",
3
3
  "title": "Expo Development Launcher",
4
- "version": "0.7.0",
4
+ "version": "0.8.3",
5
5
  "description": "Pre-release version of the Expo development launcher package for testing.",
6
6
  "main": "build/DevLauncher.js",
7
7
  "types": "build/DevLauncher.d.ts",
@@ -28,15 +28,15 @@
28
28
  "license": "MIT",
29
29
  "homepage": "https://docs.expo.io",
30
30
  "dependencies": {
31
- "@expo/config-plugins": "^3.0.0",
31
+ "@expo/config-plugins": "^3.1.0",
32
32
  "resolve-from": "^5.0.0",
33
33
  "semver": "^7.3.5"
34
34
  },
35
35
  "devDependencies": {
36
- "babel-preset-expo": "~8.4.1",
36
+ "babel-preset-expo": "~8.5.1",
37
37
  "expo-module-scripts": "^2.0.0",
38
- "react": "16.13.1",
39
- "react-native": "0.63.2"
38
+ "react": "17.0.1",
39
+ "react-native": "0.64.2"
40
40
  },
41
- "gitHead": "6231eededd3121ab5448e6e303214ab7dc39de46"
41
+ "gitHead": "7adf260ab3de492530d4bf250a24b132b5c4ba41"
42
42
  }
@@ -0,0 +1 @@
1
+ export declare const InstallationPage = "https://docs.expo.dev/clients/installation/";
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InstallationPage = void 0;
4
+ exports.InstallationPage = 'https://docs.expo.dev/clients/installation/';
@@ -0,0 +1,2 @@
1
+ export declare function addLines(content: string, find: string | RegExp, offset: number, toAdd: string[]): string;
2
+ export declare function replaceLine(content: string, find: string | RegExp, replace: string): string;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.replaceLine = exports.addLines = void 0;
4
+ function addLines(content, find, offset, toAdd) {
5
+ const lines = content.split('\n');
6
+ let lineIndex = lines.findIndex((line) => line.match(find));
7
+ for (const newLine of toAdd) {
8
+ if (!content.includes(newLine)) {
9
+ lines.splice(lineIndex + offset, 0, newLine);
10
+ lineIndex++;
11
+ }
12
+ }
13
+ return lines.join('\n');
14
+ }
15
+ exports.addLines = addLines;
16
+ function replaceLine(content, find, replace) {
17
+ const lines = content.split('\n');
18
+ if (!content.includes(replace)) {
19
+ const lineIndex = lines.findIndex((line) => line.match(find));
20
+ lines.splice(lineIndex, 1, replace);
21
+ }
22
+ return lines.join('\n');
23
+ }
24
+ exports.replaceLine = replaceLine;
@@ -7,7 +7,9 @@ const config_plugins_1 = require("@expo/config-plugins");
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const semver_1 = __importDefault(require("semver"));
10
+ const constants_1 = require("./constants");
10
11
  const resolveExpoUpdatesVersion_1 = require("./resolveExpoUpdatesVersion");
12
+ const utils_1 = require("./utils");
11
13
  const withDevLauncherAppDelegate_1 = require("./withDevLauncherAppDelegate");
12
14
  const pkg = require('expo-dev-launcher/package.json');
13
15
  const DEV_LAUNCHER_ANDROID_IMPORT = 'expo.modules.devlauncher.DevLauncherController';
@@ -35,28 +37,9 @@ async function readFileAsync(path) {
35
37
  async function saveFileAsync(path, content) {
36
38
  return fs_1.default.promises.writeFile(path, content, 'utf8');
37
39
  }
38
- function addLines(content, find, offset, toAdd) {
39
- const lines = content.split('\n');
40
- let lineIndex = lines.findIndex(line => line.match(find));
41
- for (const newLine of toAdd) {
42
- if (!content.includes(newLine)) {
43
- lines.splice(lineIndex + offset, 0, newLine);
44
- lineIndex++;
45
- }
46
- }
47
- return lines.join('\n');
48
- }
49
- function replaceLine(content, find, replace) {
50
- const lines = content.split('\n');
51
- if (!content.includes(replace)) {
52
- const lineIndex = lines.findIndex(line => line.match(find));
53
- lines.splice(lineIndex, 1, replace);
54
- }
55
- return lines.join('\n');
56
- }
57
40
  function addJavaImports(javaSource, javaImports) {
58
41
  const lines = javaSource.split('\n');
59
- const lineIndexWithPackageDeclaration = lines.findIndex(line => line.match(/^package .*;$/));
42
+ const lineIndexWithPackageDeclaration = lines.findIndex((line) => line.match(/^package .*;$/));
60
43
  for (const javaImport of javaImports) {
61
44
  if (!javaSource.includes(javaImport)) {
62
45
  const importStatement = `import ${javaImport};`;
@@ -72,7 +55,8 @@ async function editMainApplication(config, action) {
72
55
  return await saveFileAsync(mainApplicationPath, mainApplication);
73
56
  }
74
57
  catch (e) {
75
- config_plugins_1.WarningAggregator.addWarningIOS('expo-dev-launcher', `Couldn't modify MainApplication.java - ${e}.`);
58
+ config_plugins_1.WarningAggregator.addWarningAndroid('expo-dev-launcher', `Couldn't modify MainApplication.java - ${e}.
59
+ See the expo-dev-client installation instructions to modify your MainApplication.java manually: ${constants_1.InstallationPage}`);
76
60
  }
77
61
  }
78
62
  async function editPodfile(config, action) {
@@ -82,7 +66,8 @@ async function editPodfile(config, action) {
82
66
  return await saveFileAsync(podfilePath, podfile);
83
67
  }
84
68
  catch (e) {
85
- config_plugins_1.WarningAggregator.addWarningIOS('expo-dev-launcher', `Couldn't modify AppDelegate.m - ${e}.`);
69
+ config_plugins_1.WarningAggregator.addWarningIOS('expo-dev-launcher', `Couldn't modify AppDelegate.m - ${e}.
70
+ See the expo-dev-client installation instructions to modify your AppDelegate.m manually: ${constants_1.InstallationPage}`);
86
71
  }
87
72
  }
88
73
  async function editIndex(config, action) {
@@ -92,16 +77,17 @@ async function editIndex(config, action) {
92
77
  return await saveFileAsync(indexPath, index);
93
78
  }
94
79
  catch (e) {
95
- config_plugins_1.WarningAggregator.addWarningIOS('expo-dev-launcher', `Couldn't modify index.js - ${e}.`);
80
+ config_plugins_1.WarningAggregator.addWarningIOS('expo-dev-launcher', `Couldn't modify index.js - ${e}.
81
+ See the expo-dev-client installation instructions to modify your index.js manually: ${constants_1.InstallationPage}`);
96
82
  }
97
83
  }
98
- const withDevLauncherApplication = config => {
84
+ const withDevLauncherApplication = (config) => {
99
85
  return config_plugins_1.withDangerousMod(config, [
100
86
  'android',
101
87
  async (config) => {
102
- await editMainApplication(config, mainApplication => {
88
+ await editMainApplication(config, (mainApplication) => {
103
89
  mainApplication = addJavaImports(mainApplication, [DEV_LAUNCHER_ANDROID_IMPORT]);
104
- mainApplication = addLines(mainApplication, 'initializeFlipper\\(this', 0, [
90
+ mainApplication = utils_1.addLines(mainApplication, 'initializeFlipper\\(this', 0, [
105
91
  ` ${DEV_LAUNCHER_ANDROID_INIT}`,
106
92
  ]);
107
93
  let expoUpdatesVersion;
@@ -113,10 +99,10 @@ const withDevLauncherApplication = config => {
113
99
  }
114
100
  if (expoUpdatesVersion && semver_1.default.gt(expoUpdatesVersion, '0.6.0')) {
115
101
  mainApplication = addJavaImports(mainApplication, [DEV_LAUNCHER_UPDATES_ANDROID_IMPORT]);
116
- mainApplication = addLines(mainApplication, 'initializeFlipper\\(this', 0, [
102
+ mainApplication = utils_1.addLines(mainApplication, 'initializeFlipper\\(this', 0, [
117
103
  ` ${DEV_LAUNCHER_UPDATES_ANDROID_INIT}`,
118
104
  ]);
119
- mainApplication = replaceLine(mainApplication, 'return BuildConfig.DEBUG;', ` ${DEV_LAUNCHER_UPDATES_DEVELOPER_SUPPORT}`);
105
+ mainApplication = utils_1.replaceLine(mainApplication, 'return BuildConfig.DEBUG;', ` ${DEV_LAUNCHER_UPDATES_DEVELOPER_SUPPORT}`);
120
106
  }
121
107
  return mainApplication;
122
108
  });
@@ -124,8 +110,8 @@ const withDevLauncherApplication = config => {
124
110
  },
125
111
  ]);
126
112
  };
127
- const withDevLauncherActivity = config => {
128
- return config_plugins_1.withMainActivity(config, config => {
113
+ const withDevLauncherActivity = (config) => {
114
+ return config_plugins_1.withMainActivity(config, (config) => {
129
115
  if (config.modResults.language === 'java') {
130
116
  let content = addJavaImports(config.modResults.contents, [
131
117
  DEV_LAUNCHER_ANDROID_IMPORT,
@@ -133,34 +119,36 @@ const withDevLauncherActivity = config => {
133
119
  ]);
134
120
  if (!content.includes(DEV_LAUNCHER_ON_NEW_INTENT)) {
135
121
  const lines = content.split('\n');
136
- const onCreateIndex = lines.findIndex(line => line.includes('public class MainActivity'));
122
+ const onCreateIndex = lines.findIndex((line) => line.includes('public class MainActivity'));
137
123
  lines.splice(onCreateIndex + 1, 0, DEV_LAUNCHER_ON_NEW_INTENT);
138
124
  content = lines.join('\n');
139
125
  }
140
126
  if (!content.includes('DevLauncherController.wrapReactActivityDelegate')) {
141
- content = content.replace(/(new ReactActivityDelegate(.*|\s)*});$/m, DEV_LAUNCHER_WRAPPED_ACTIVITY_DELEGATE);
127
+ content = content.replace(/(new ReactActivityDelegate(Wrapper)?(.|\s)*\}\)?);$/mu, DEV_LAUNCHER_WRAPPED_ACTIVITY_DELEGATE);
142
128
  }
143
129
  config.modResults.contents = content;
144
130
  }
145
131
  else {
146
- config_plugins_1.WarningAggregator.addWarningAndroid('expo-dev-launcher', `Cannot automatically configure MainActivity if it's not java`);
132
+ config_plugins_1.WarningAggregator.addWarningAndroid('expo-dev-launcher', `Cannot automatically configure MainActivity if it's not java.
133
+ See the expo-dev-client installation instructions to modify your MainActivity manually: ${constants_1.InstallationPage}`);
147
134
  }
148
135
  return config;
149
136
  });
150
137
  };
151
- const withDevLauncherPodfile = config => {
138
+ const withDevLauncherPodfile = (config) => {
152
139
  return config_plugins_1.withDangerousMod(config, [
153
140
  'ios',
154
141
  async (config) => {
155
- await editPodfile(config, podfile => {
156
- podfile = podfile.replace("platform :ios, '10.0'", "platform :ios, '11.0'");
142
+ await editPodfile(config, (podfile) => {
143
+ // replace all iOS versions below 12
144
+ podfile = podfile.replace(/platform :ios, '((\d\.0)|(1[0-1].0))'/, "platform :ios, '12.0'");
157
145
  // Match both variations of Ruby config:
158
146
  // unknown: pod 'expo-dev-launcher', path: '../node_modules/expo-dev-launcher', :configurations => :debug
159
147
  // Rubocop: pod 'expo-dev-launcher', path: '../node_modules/expo-dev-launcher', configurations: :debug
160
148
  if (!podfile.match(/pod ['"]expo-dev-launcher['"],\s?path: ['"][^'"]*node_modules\/expo-dev-launcher['"],\s?:?configurations:?\s(?:=>\s)?:debug/)) {
161
149
  const packagePath = path_1.default.dirname(require.resolve('expo-dev-launcher/package.json'));
162
150
  const relativePath = path_1.default.relative(config.modRequest.platformProjectRoot, packagePath);
163
- podfile = addLines(podfile, 'use_react_native', 0, [
151
+ podfile = utils_1.addLines(podfile, 'use_react_native', 0, [
164
152
  ` pod 'expo-dev-launcher', path: '${relativePath}', :configurations => :debug`,
165
153
  ]);
166
154
  }
@@ -170,9 +158,9 @@ const withDevLauncherPodfile = config => {
170
158
  },
171
159
  ]);
172
160
  };
173
- const withErrorHandling = config => {
161
+ const withErrorHandling = (config) => {
174
162
  const injectErrorHandlers = async (config) => {
175
- await editIndex(config, index => {
163
+ await editIndex(config, (index) => {
176
164
  if (!index.includes(DEV_LAUNCHER_JS_REGISTER_ERROR_HANDLERS) &&
177
165
  !index.includes(DEV_LAUNCHER_JS_REGISTER_ERROR_HANDLERS_VIA_LAUNCHER)) {
178
166
  index = DEV_LAUNCHER_JS_REGISTER_ERROR_HANDLERS + ';\n\n' + index;
@@ -1,3 +1,4 @@
1
1
  import { ConfigPlugin } from '@expo/config-plugins';
2
+ export declare function modifyLegacyAppDelegate(appDelegate: string, expoUpdatesVersion?: string | null): string;
2
3
  export declare function modifyAppDelegate(appDelegate: string, expoUpdatesVersion?: string | null): string;
3
4
  export declare const withDevLauncherAppDelegate: ConfigPlugin;
@@ -3,10 +3,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.withDevLauncherAppDelegate = exports.modifyAppDelegate = void 0;
6
+ exports.withDevLauncherAppDelegate = exports.modifyAppDelegate = exports.modifyLegacyAppDelegate = void 0;
7
7
  const config_plugins_1 = require("@expo/config-plugins");
8
8
  const semver_1 = __importDefault(require("semver"));
9
+ const constants_1 = require("./constants");
9
10
  const resolveExpoUpdatesVersion_1 = require("./resolveExpoUpdatesVersion");
11
+ const utils_1 = require("./utils");
12
+ function escapeRegExpCharacters(string) {
13
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
14
+ }
15
+ const INITIALIZE_REACT_NATIVE_APP_FUNCTION = `- (RCTBridge *)initializeReactNativeApp`;
16
+ const NEW_INITIALIZE_REACT_NATIVE_APP_FUNCTION = `- (RCTBridge *)initializeReactNativeApp:(NSDictionary *)launchOptions`;
10
17
  const DEV_LAUNCHER_APP_DELEGATE_SOURCE_FOR_URL = ` #if defined(EX_DEV_LAUNCHER_ENABLED)
11
18
  return [[EXDevLauncherController sharedInstance] sourceUrl];
12
19
  #else
@@ -27,7 +34,7 @@ const DEV_LAUNCHER_UPDATES_APP_DELEGATE_IOS_IMPORT = `
27
34
  #include <EXDevLauncher/EXDevLauncherController.h>
28
35
  #import <EXUpdates/EXUpdatesDevLauncherController.h>
29
36
  #endif`;
30
- const DEV_LAUNCHER_APP_DELEGATE_CONTROLLER_DELEGATE = `
37
+ const DEV_LAUNCHER_APP_DELEGATE_CONTROLLER_DELEGATE_LEGACY = `
31
38
  #if defined(EX_DEV_LAUNCHER_ENABLED)
32
39
  @implementation AppDelegate (EXDevLauncherControllerDelegate)
33
40
 
@@ -39,6 +46,19 @@ const DEV_LAUNCHER_APP_DELEGATE_CONTROLLER_DELEGATE = `
39
46
  [splashScreenService showSplashScreenFor:self.window.rootViewController];
40
47
  }
41
48
 
49
+ @end
50
+ #endif
51
+ `;
52
+ const DEV_LAUNCHER_APP_DELEGATE_CONTROLLER_DELEGATE = `
53
+ #if defined(EX_DEV_LAUNCHER_ENABLED)
54
+ @implementation AppDelegate (EXDevLauncherControllerDelegate)
55
+
56
+ - (void)devLauncherController:(EXDevLauncherController *)developmentClientController
57
+ didStartWithSuccess:(BOOL)success
58
+ {
59
+ developmentClientController.appBridge = [self initializeReactNativeApp:[EXDevLauncherController.sharedInstance getLaunchOptions]];
60
+ }
61
+
42
62
  @end
43
63
  #endif
44
64
  `;
@@ -62,8 +82,72 @@ const DEV_MENU_IOS_INIT = `
62
82
  #if defined(EX_DEV_MENU_ENABLED)
63
83
  [DevMenuManager configureWithBridge:bridge];
64
84
  #endif`;
65
- function modifyAppDelegate(appDelegate, expoUpdatesVersion = null) {
66
- const shouldAddUpdatesIntegration = expoUpdatesVersion != null && semver_1.default.gt(expoUpdatesVersion, '0.6.0');
85
+ const DEV_LAUNCHER_INIT_TO_REMOVE = new RegExp(escapeRegExpCharacters(`RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
86
+ RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"main" initialProperties:nil];
87
+ id rootViewBackgroundColor = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"RCTRootViewBackgroundColor"];
88
+ if (rootViewBackgroundColor != nil) {
89
+ rootView.backgroundColor = [RCTConvert UIColor:rootViewBackgroundColor];
90
+ } else {
91
+ rootView.backgroundColor = [UIColor whiteColor];
92
+ }
93
+
94
+ self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
95
+ UIViewController *rootViewController = `) +
96
+ `([^;]+)` +
97
+ escapeRegExpCharacters(`;
98
+ rootViewController.view = rootView;
99
+ self.window.rootViewController = rootViewController;
100
+ [self.window makeKeyAndVisible];`), 'm');
101
+ const DEV_LAUNCHER_NEW_INIT = `self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
102
+ #if defined(EX_DEV_LAUNCHER_ENABLED)
103
+ EXDevLauncherController *controller = [EXDevLauncherController sharedInstance];
104
+ [controller startWithWindow:self.window delegate:(id<EXDevLauncherControllerDelegate>)self launchOptions:launchOptions];
105
+ #else
106
+ [self initializeReactNativeApp:launchOptions];
107
+ #endif`;
108
+ const DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION_REGEX = new RegExp(escapeRegExpCharacters(`
109
+ - (RCTBridge *)initializeReactNativeApp:(NSDictionary *)launchOptions
110
+ {
111
+ RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
112
+ RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"main" initialProperties:nil];
113
+ id rootViewBackgroundColor = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"RCTRootViewBackgroundColor"];
114
+ if (rootViewBackgroundColor != nil) {
115
+ rootView.backgroundColor = [RCTConvert UIColor:rootViewBackgroundColor];
116
+ } else {
117
+ rootView.backgroundColor = [UIColor whiteColor];
118
+ }
119
+
120
+ UIViewController *rootViewController = `) +
121
+ `[^;]+` +
122
+ escapeRegExpCharacters(`;
123
+ rootViewController.view = rootView;
124
+ self.window.rootViewController = rootViewController;
125
+ [self.window makeKeyAndVisible];
126
+
127
+ return bridge;
128
+ }
129
+ `), 'm');
130
+ const DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION = (viewControllerInit) => `
131
+ - (RCTBridge *)initializeReactNativeApp:(NSDictionary *)launchOptions
132
+ {
133
+ RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
134
+ RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"main" initialProperties:nil];
135
+ id rootViewBackgroundColor = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"RCTRootViewBackgroundColor"];
136
+ if (rootViewBackgroundColor != nil) {
137
+ rootView.backgroundColor = [RCTConvert UIColor:rootViewBackgroundColor];
138
+ } else {
139
+ rootView.backgroundColor = [UIColor whiteColor];
140
+ }
141
+
142
+ UIViewController *rootViewController = ${viewControllerInit !== null && viewControllerInit !== void 0 ? viewControllerInit : '[UIViewController new]'};
143
+ rootViewController.view = rootView;
144
+ self.window.rootViewController = rootViewController;
145
+ [self.window makeKeyAndVisible];
146
+
147
+ return bridge;
148
+ }
149
+ `;
150
+ function addImports(appDelegate, shouldAddUpdatesIntegration) {
67
151
  if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_IOS_IMPORT) &&
68
152
  !appDelegate.includes(DEV_LAUNCHER_UPDATES_APP_DELEGATE_IOS_IMPORT)) {
69
153
  const lines = appDelegate.split('\n');
@@ -72,6 +156,31 @@ function modifyAppDelegate(appDelegate, expoUpdatesVersion = null) {
72
156
  : DEV_LAUNCHER_APP_DELEGATE_IOS_IMPORT);
73
157
  appDelegate = lines.join('\n');
74
158
  }
159
+ return appDelegate;
160
+ }
161
+ function removeDevMenuInit(appDelegate) {
162
+ if (!appDelegate.includes(DEV_MENU_IMPORT)) {
163
+ // expo-dev-launcher is responsible for initializing the expo-dev-menu.
164
+ // We need to remove init block from AppDelegate.
165
+ appDelegate = appDelegate.replace(DEV_MENU_IOS_INIT, '');
166
+ }
167
+ return appDelegate;
168
+ }
169
+ function addDeepLinkHandler(appDelegate) {
170
+ if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_ON_DEEP_LINK)) {
171
+ appDelegate = appDelegate.replace('return [RCTLinkingManager application:application openURL:url options:options];', DEV_LAUNCHER_APP_DELEGATE_ON_DEEP_LINK);
172
+ }
173
+ return appDelegate;
174
+ }
175
+ function changeDebugURL(appDelegate) {
176
+ if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_SOURCE_FOR_URL)) {
177
+ appDelegate = appDelegate.replace('return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];', DEV_LAUNCHER_APP_DELEGATE_SOURCE_FOR_URL);
178
+ }
179
+ return appDelegate;
180
+ }
181
+ function modifyLegacyAppDelegate(appDelegate, expoUpdatesVersion = null) {
182
+ const shouldAddUpdatesIntegration = expoUpdatesVersion != null && semver_1.default.gt(expoUpdatesVersion, '0.6.0');
183
+ appDelegate = addImports(appDelegate, shouldAddUpdatesIntegration);
75
184
  if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_INIT)) {
76
185
  appDelegate = appDelegate.replace(/(didFinishLaunchingWithOptions([^}])*)\[self initializeReactNativeApp\];(([^}])*})/, `$1${DEV_LAUNCHER_APP_DELEGATE_INIT}$3`);
77
186
  }
@@ -82,25 +191,53 @@ function modifyAppDelegate(appDelegate, expoUpdatesVersion = null) {
82
191
  if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_BRIDGE)) {
83
192
  appDelegate = appDelegate.replace('RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:self.launchOptions];', DEV_LAUNCHER_APP_DELEGATE_BRIDGE);
84
193
  }
85
- if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_SOURCE_FOR_URL)) {
86
- appDelegate = appDelegate.replace('return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];', DEV_LAUNCHER_APP_DELEGATE_SOURCE_FOR_URL);
194
+ appDelegate = changeDebugURL(appDelegate);
195
+ appDelegate = addDeepLinkHandler(appDelegate);
196
+ if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_CONTROLLER_DELEGATE_LEGACY)) {
197
+ appDelegate += DEV_LAUNCHER_APP_DELEGATE_CONTROLLER_DELEGATE_LEGACY;
87
198
  }
88
- if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_ON_DEEP_LINK)) {
89
- appDelegate = appDelegate.replace('return [RCTLinkingManager application:application openURL:url options:options];', DEV_LAUNCHER_APP_DELEGATE_ON_DEEP_LINK);
199
+ appDelegate = removeDevMenuInit(appDelegate);
200
+ return appDelegate;
201
+ }
202
+ exports.modifyLegacyAppDelegate = modifyLegacyAppDelegate;
203
+ function modifyAppDelegate(appDelegate, expoUpdatesVersion = null) {
204
+ const shouldAddUpdatesIntegration = expoUpdatesVersion != null && semver_1.default.gt(expoUpdatesVersion, '0.6.0');
205
+ if (!DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION_REGEX.test(appDelegate)) {
206
+ if (DEV_LAUNCHER_INIT_TO_REMOVE.test(appDelegate)) {
207
+ // UIViewController can be initialized differently depending on whether expo-screen-orientation is installed,
208
+ // so we need to preserve whatever is there already.
209
+ let viewControllerInit;
210
+ appDelegate = appDelegate.replace(DEV_LAUNCHER_INIT_TO_REMOVE, (match, p1) => {
211
+ viewControllerInit = p1;
212
+ return DEV_LAUNCHER_NEW_INIT;
213
+ });
214
+ appDelegate = utils_1.addLines(appDelegate, '@implementation AppDelegate', 1, [
215
+ DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION(viewControllerInit),
216
+ ]);
217
+ }
218
+ else {
219
+ config_plugins_1.WarningAggregator.addWarningIOS('expo-dev-launcher', `Failed to modify AppDelegate init function.
220
+ See the expo-dev-client installation instructions to modify your AppDelegate manually: ${constants_1.InstallationPage}`);
221
+ }
222
+ }
223
+ if (shouldAddUpdatesIntegration &&
224
+ !appDelegate.includes(DEV_LAUNCHER_UPDATES_APP_DELEGATE_INIT)) {
225
+ appDelegate = appDelegate.replace('EXDevLauncherController *controller = [EXDevLauncherController sharedInstance];', DEV_LAUNCHER_UPDATES_APP_DELEGATE_INIT);
90
226
  }
227
+ appDelegate = addImports(appDelegate, shouldAddUpdatesIntegration);
91
228
  if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_CONTROLLER_DELEGATE)) {
92
229
  appDelegate += DEV_LAUNCHER_APP_DELEGATE_CONTROLLER_DELEGATE;
93
230
  }
94
- if (!appDelegate.includes(DEV_MENU_IMPORT)) {
95
- // expo-dev-launcher is responsible for initializing the expo-dev-menu.
96
- // We need to remove init block from AppDelegate.
97
- appDelegate = appDelegate.replace(DEV_MENU_IOS_INIT, '');
231
+ if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_ON_DEEP_LINK)) {
232
+ appDelegate = appDelegate.replace('return [RCTLinkingManager application:application openURL:url options:options];', DEV_LAUNCHER_APP_DELEGATE_ON_DEEP_LINK);
98
233
  }
234
+ appDelegate = changeDebugURL(appDelegate);
235
+ appDelegate = removeDevMenuInit(appDelegate);
99
236
  return appDelegate;
100
237
  }
101
238
  exports.modifyAppDelegate = modifyAppDelegate;
102
- exports.withDevLauncherAppDelegate = config => {
103
- return config_plugins_1.withAppDelegate(config, config => {
239
+ const withDevLauncherAppDelegate = (config) => {
240
+ return config_plugins_1.withAppDelegate(config, (config) => {
104
241
  if (config.modResults.language === 'objc') {
105
242
  let expoUpdatesVersion;
106
243
  try {
@@ -109,11 +246,19 @@ exports.withDevLauncherAppDelegate = config => {
109
246
  catch (e) {
110
247
  config_plugins_1.WarningAggregator.addWarningIOS('expo-dev-launcher', `Failed to check compatibility with expo-updates - ${e}`);
111
248
  }
112
- config.modResults.contents = modifyAppDelegate(config.modResults.contents, expoUpdatesVersion);
249
+ if (config.modResults.contents.includes(INITIALIZE_REACT_NATIVE_APP_FUNCTION) &&
250
+ !config.modResults.contents.includes(NEW_INITIALIZE_REACT_NATIVE_APP_FUNCTION)) {
251
+ config.modResults.contents = modifyLegacyAppDelegate(config.modResults.contents, expoUpdatesVersion);
252
+ }
253
+ else {
254
+ config.modResults.contents = modifyAppDelegate(config.modResults.contents, expoUpdatesVersion);
255
+ }
113
256
  }
114
257
  else {
115
- config_plugins_1.WarningAggregator.addWarningIOS('expo-dev-launcher', 'Swift AppDelegate files are not supported yet.');
258
+ config_plugins_1.WarningAggregator.addWarningIOS('expo-dev-launcher', `Swift AppDelegate files are not supported yet.
259
+ See the expo-dev-client installation instructions to modify your AppDelegate manually: ${constants_1.InstallationPage}`);
116
260
  }
117
261
  return config;
118
262
  });
119
263
  };
264
+ exports.withDevLauncherAppDelegate = withDevLauncherAppDelegate;
@@ -0,0 +1 @@
1
+ export const InstallationPage = 'https://docs.expo.dev/clients/installation/';
@@ -0,0 +1,25 @@
1
+ export function addLines(content: string, find: string | RegExp, offset: number, toAdd: string[]) {
2
+ const lines = content.split('\n');
3
+
4
+ let lineIndex = lines.findIndex((line) => line.match(find));
5
+
6
+ for (const newLine of toAdd) {
7
+ if (!content.includes(newLine)) {
8
+ lines.splice(lineIndex + offset, 0, newLine);
9
+ lineIndex++;
10
+ }
11
+ }
12
+
13
+ return lines.join('\n');
14
+ }
15
+
16
+ export function replaceLine(content: string, find: string | RegExp, replace: string) {
17
+ const lines = content.split('\n');
18
+
19
+ if (!content.includes(replace)) {
20
+ const lineIndex = lines.findIndex((line) => line.match(find));
21
+ lines.splice(lineIndex, 1, replace);
22
+ }
23
+
24
+ return lines.join('\n');
25
+ }