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
@@ -11,7 +11,9 @@ import fs from 'fs';
11
11
  import path from 'path';
12
12
  import semver from 'semver';
13
13
 
14
+ import { InstallationPage } from './constants';
14
15
  import { resolveExpoUpdatesVersion } from './resolveExpoUpdatesVersion';
16
+ import { addLines, replaceLine } from './utils';
15
17
  import { withDevLauncherAppDelegate } from './withDevLauncherAppDelegate';
16
18
 
17
19
  const pkg = require('expo-dev-launcher/package.json');
@@ -46,35 +48,9 @@ async function saveFileAsync(path: string, content: string): Promise<void> {
46
48
  return fs.promises.writeFile(path, content, 'utf8');
47
49
  }
48
50
 
49
- function addLines(content: string, find: string | RegExp, offset: number, toAdd: string[]) {
50
- const lines = content.split('\n');
51
-
52
- let lineIndex = lines.findIndex(line => line.match(find));
53
-
54
- for (const newLine of toAdd) {
55
- if (!content.includes(newLine)) {
56
- lines.splice(lineIndex + offset, 0, newLine);
57
- lineIndex++;
58
- }
59
- }
60
-
61
- return lines.join('\n');
62
- }
63
-
64
- function replaceLine(content: string, find: string | RegExp, replace: string) {
65
- const lines = content.split('\n');
66
-
67
- if (!content.includes(replace)) {
68
- const lineIndex = lines.findIndex(line => line.match(find));
69
- lines.splice(lineIndex, 1, replace);
70
- }
71
-
72
- return lines.join('\n');
73
- }
74
-
75
51
  function addJavaImports(javaSource: string, javaImports: string[]): string {
76
52
  const lines = javaSource.split('\n');
77
- const lineIndexWithPackageDeclaration = lines.findIndex(line => line.match(/^package .*;$/));
53
+ const lineIndexWithPackageDeclaration = lines.findIndex((line) => line.match(/^package .*;$/));
78
54
  for (const javaImport of javaImports) {
79
55
  if (!javaSource.includes(javaImport)) {
80
56
  const importStatement = `import ${javaImport};`;
@@ -102,9 +78,10 @@ async function editMainApplication(
102
78
  const mainApplication = action(await readFileAsync(mainApplicationPath));
103
79
  return await saveFileAsync(mainApplicationPath, mainApplication);
104
80
  } catch (e) {
105
- WarningAggregator.addWarningIOS(
81
+ WarningAggregator.addWarningAndroid(
106
82
  'expo-dev-launcher',
107
- `Couldn't modify MainApplication.java - ${e}.`
83
+ `Couldn't modify MainApplication.java - ${e}.
84
+ See the expo-dev-client installation instructions to modify your MainApplication.java manually: ${InstallationPage}`
108
85
  );
109
86
  }
110
87
  }
@@ -115,7 +92,11 @@ async function editPodfile(config: ExportedConfigWithProps, action: (podfile: st
115
92
  const podfile = action(await readFileAsync(podfilePath));
116
93
  return await saveFileAsync(podfilePath, podfile);
117
94
  } catch (e) {
118
- WarningAggregator.addWarningIOS('expo-dev-launcher', `Couldn't modify AppDelegate.m - ${e}.`);
95
+ WarningAggregator.addWarningIOS(
96
+ 'expo-dev-launcher',
97
+ `Couldn't modify AppDelegate.m - ${e}.
98
+ See the expo-dev-client installation instructions to modify your AppDelegate.m manually: ${InstallationPage}`
99
+ );
119
100
  }
120
101
  }
121
102
 
@@ -125,15 +106,19 @@ async function editIndex(config: ExportedConfigWithProps, action: (index: string
125
106
  const index = action(await readFileAsync(indexPath));
126
107
  return await saveFileAsync(indexPath, index);
127
108
  } catch (e) {
128
- WarningAggregator.addWarningIOS('expo-dev-launcher', `Couldn't modify index.js - ${e}.`);
109
+ WarningAggregator.addWarningIOS(
110
+ 'expo-dev-launcher',
111
+ `Couldn't modify index.js - ${e}.
112
+ See the expo-dev-client installation instructions to modify your index.js manually: ${InstallationPage}`
113
+ );
129
114
  }
130
115
  }
131
116
 
132
- const withDevLauncherApplication: ConfigPlugin = config => {
117
+ const withDevLauncherApplication: ConfigPlugin = (config) => {
133
118
  return withDangerousMod(config, [
134
119
  'android',
135
- async config => {
136
- await editMainApplication(config, mainApplication => {
120
+ async (config) => {
121
+ await editMainApplication(config, (mainApplication) => {
137
122
  mainApplication = addJavaImports(mainApplication, [DEV_LAUNCHER_ANDROID_IMPORT]);
138
123
 
139
124
  mainApplication = addLines(mainApplication, 'initializeFlipper\\(this', 0, [
@@ -168,8 +153,8 @@ const withDevLauncherApplication: ConfigPlugin = config => {
168
153
  ]);
169
154
  };
170
155
 
171
- const withDevLauncherActivity: ConfigPlugin = config => {
172
- return withMainActivity(config, config => {
156
+ const withDevLauncherActivity: ConfigPlugin = (config) => {
157
+ return withMainActivity(config, (config) => {
173
158
  if (config.modResults.language === 'java') {
174
159
  let content = addJavaImports(config.modResults.contents, [
175
160
  DEV_LAUNCHER_ANDROID_IMPORT,
@@ -178,7 +163,7 @@ const withDevLauncherActivity: ConfigPlugin = config => {
178
163
 
179
164
  if (!content.includes(DEV_LAUNCHER_ON_NEW_INTENT)) {
180
165
  const lines = content.split('\n');
181
- const onCreateIndex = lines.findIndex(line => line.includes('public class MainActivity'));
166
+ const onCreateIndex = lines.findIndex((line) => line.includes('public class MainActivity'));
182
167
 
183
168
  lines.splice(onCreateIndex + 1, 0, DEV_LAUNCHER_ON_NEW_INTENT);
184
169
 
@@ -187,7 +172,7 @@ const withDevLauncherActivity: ConfigPlugin = config => {
187
172
 
188
173
  if (!content.includes('DevLauncherController.wrapReactActivityDelegate')) {
189
174
  content = content.replace(
190
- /(new ReactActivityDelegate(.*|\s)*});$/m,
175
+ /(new ReactActivityDelegate(Wrapper)?(.|\s)*\}\)?);$/mu,
191
176
  DEV_LAUNCHER_WRAPPED_ACTIVITY_DELEGATE
192
177
  );
193
178
  }
@@ -196,7 +181,8 @@ const withDevLauncherActivity: ConfigPlugin = config => {
196
181
  } else {
197
182
  WarningAggregator.addWarningAndroid(
198
183
  'expo-dev-launcher',
199
- `Cannot automatically configure MainActivity if it's not java`
184
+ `Cannot automatically configure MainActivity if it's not java.
185
+ See the expo-dev-client installation instructions to modify your MainActivity manually: ${InstallationPage}`
200
186
  );
201
187
  }
202
188
 
@@ -204,12 +190,13 @@ const withDevLauncherActivity: ConfigPlugin = config => {
204
190
  });
205
191
  };
206
192
 
207
- const withDevLauncherPodfile: ConfigPlugin = config => {
193
+ const withDevLauncherPodfile: ConfigPlugin = (config) => {
208
194
  return withDangerousMod(config, [
209
195
  'ios',
210
- async config => {
211
- await editPodfile(config, podfile => {
212
- podfile = podfile.replace("platform :ios, '10.0'", "platform :ios, '11.0'");
196
+ async (config) => {
197
+ await editPodfile(config, (podfile) => {
198
+ // replace all iOS versions below 12
199
+ podfile = podfile.replace(/platform :ios, '((\d\.0)|(1[0-1].0))'/, "platform :ios, '12.0'");
213
200
  // Match both variations of Ruby config:
214
201
  // unknown: pod 'expo-dev-launcher', path: '../node_modules/expo-dev-launcher', :configurations => :debug
215
202
  // Rubocop: pod 'expo-dev-launcher', path: '../node_modules/expo-dev-launcher', configurations: :debug
@@ -231,9 +218,9 @@ const withDevLauncherPodfile: ConfigPlugin = config => {
231
218
  ]);
232
219
  };
233
220
 
234
- const withErrorHandling: ConfigPlugin = config => {
221
+ const withErrorHandling: ConfigPlugin = (config) => {
235
222
  const injectErrorHandlers = async (config: ExportedConfigWithProps) => {
236
- await editIndex(config, index => {
223
+ await editIndex(config, (index) => {
237
224
  if (
238
225
  !index.includes(DEV_LAUNCHER_JS_REGISTER_ERROR_HANDLERS) &&
239
226
  !index.includes(DEV_LAUNCHER_JS_REGISTER_ERROR_HANDLERS_VIA_LAUNCHER)
@@ -1,7 +1,16 @@
1
1
  import { ConfigPlugin, WarningAggregator, withAppDelegate } from '@expo/config-plugins';
2
2
  import semver from 'semver';
3
3
 
4
+ import { InstallationPage } from './constants';
4
5
  import { resolveExpoUpdatesVersion } from './resolveExpoUpdatesVersion';
6
+ import { addLines } from './utils';
7
+
8
+ function escapeRegExpCharacters(string: string) {
9
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
10
+ }
11
+
12
+ const INITIALIZE_REACT_NATIVE_APP_FUNCTION = `- (RCTBridge *)initializeReactNativeApp`;
13
+ const NEW_INITIALIZE_REACT_NATIVE_APP_FUNCTION = `- (RCTBridge *)initializeReactNativeApp:(NSDictionary *)launchOptions`;
5
14
 
6
15
  const DEV_LAUNCHER_APP_DELEGATE_SOURCE_FOR_URL = ` #if defined(EX_DEV_LAUNCHER_ENABLED)
7
16
  return [[EXDevLauncherController sharedInstance] sourceUrl];
@@ -23,7 +32,7 @@ const DEV_LAUNCHER_UPDATES_APP_DELEGATE_IOS_IMPORT = `
23
32
  #include <EXDevLauncher/EXDevLauncherController.h>
24
33
  #import <EXUpdates/EXUpdatesDevLauncherController.h>
25
34
  #endif`;
26
- const DEV_LAUNCHER_APP_DELEGATE_CONTROLLER_DELEGATE = `
35
+ const DEV_LAUNCHER_APP_DELEGATE_CONTROLLER_DELEGATE_LEGACY = `
27
36
  #if defined(EX_DEV_LAUNCHER_ENABLED)
28
37
  @implementation AppDelegate (EXDevLauncherControllerDelegate)
29
38
 
@@ -38,6 +47,21 @@ const DEV_LAUNCHER_APP_DELEGATE_CONTROLLER_DELEGATE = `
38
47
  @end
39
48
  #endif
40
49
  `;
50
+
51
+ const DEV_LAUNCHER_APP_DELEGATE_CONTROLLER_DELEGATE = `
52
+ #if defined(EX_DEV_LAUNCHER_ENABLED)
53
+ @implementation AppDelegate (EXDevLauncherControllerDelegate)
54
+
55
+ - (void)devLauncherController:(EXDevLauncherController *)developmentClientController
56
+ didStartWithSuccess:(BOOL)success
57
+ {
58
+ developmentClientController.appBridge = [self initializeReactNativeApp:[EXDevLauncherController.sharedInstance getLaunchOptions]];
59
+ }
60
+
61
+ @end
62
+ #endif
63
+ `;
64
+
41
65
  const DEV_LAUNCHER_APP_DELEGATE_INIT = `#if defined(EX_DEV_LAUNCHER_ENABLED)
42
66
  EXDevLauncherController *controller = [EXDevLauncherController sharedInstance];
43
67
  [controller startWithWindow:self.window delegate:(id<EXDevLauncherControllerDelegate>)self launchOptions:launchOptions];
@@ -61,10 +85,84 @@ const DEV_MENU_IOS_INIT = `
61
85
  [DevMenuManager configureWithBridge:bridge];
62
86
  #endif`;
63
87
 
64
- export function modifyAppDelegate(appDelegate: string, expoUpdatesVersion: string | null = null) {
65
- const shouldAddUpdatesIntegration =
66
- expoUpdatesVersion != null && semver.gt(expoUpdatesVersion, '0.6.0');
88
+ const DEV_LAUNCHER_INIT_TO_REMOVE = new RegExp(
89
+ escapeRegExpCharacters(`RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
90
+ RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"main" initialProperties:nil];
91
+ id rootViewBackgroundColor = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"RCTRootViewBackgroundColor"];
92
+ if (rootViewBackgroundColor != nil) {
93
+ rootView.backgroundColor = [RCTConvert UIColor:rootViewBackgroundColor];
94
+ } else {
95
+ rootView.backgroundColor = [UIColor whiteColor];
96
+ }
97
+
98
+ self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
99
+ UIViewController *rootViewController = `) +
100
+ `([^;]+)` +
101
+ escapeRegExpCharacters(`;
102
+ rootViewController.view = rootView;
103
+ self.window.rootViewController = rootViewController;
104
+ [self.window makeKeyAndVisible];`),
105
+ 'm'
106
+ );
107
+
108
+ const DEV_LAUNCHER_NEW_INIT = `self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
109
+ #if defined(EX_DEV_LAUNCHER_ENABLED)
110
+ EXDevLauncherController *controller = [EXDevLauncherController sharedInstance];
111
+ [controller startWithWindow:self.window delegate:(id<EXDevLauncherControllerDelegate>)self launchOptions:launchOptions];
112
+ #else
113
+ [self initializeReactNativeApp:launchOptions];
114
+ #endif`;
115
+
116
+ const DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION_REGEX = new RegExp(
117
+ escapeRegExpCharacters(`
118
+ - (RCTBridge *)initializeReactNativeApp:(NSDictionary *)launchOptions
119
+ {
120
+ RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
121
+ RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"main" initialProperties:nil];
122
+ id rootViewBackgroundColor = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"RCTRootViewBackgroundColor"];
123
+ if (rootViewBackgroundColor != nil) {
124
+ rootView.backgroundColor = [RCTConvert UIColor:rootViewBackgroundColor];
125
+ } else {
126
+ rootView.backgroundColor = [UIColor whiteColor];
127
+ }
67
128
 
129
+ UIViewController *rootViewController = `) +
130
+ `[^;]+` +
131
+ escapeRegExpCharacters(`;
132
+ rootViewController.view = rootView;
133
+ self.window.rootViewController = rootViewController;
134
+ [self.window makeKeyAndVisible];
135
+
136
+ return bridge;
137
+ }
138
+ `),
139
+ 'm'
140
+ );
141
+
142
+ const DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION = (
143
+ viewControllerInit: string | undefined
144
+ ) => `
145
+ - (RCTBridge *)initializeReactNativeApp:(NSDictionary *)launchOptions
146
+ {
147
+ RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
148
+ RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"main" initialProperties:nil];
149
+ id rootViewBackgroundColor = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"RCTRootViewBackgroundColor"];
150
+ if (rootViewBackgroundColor != nil) {
151
+ rootView.backgroundColor = [RCTConvert UIColor:rootViewBackgroundColor];
152
+ } else {
153
+ rootView.backgroundColor = [UIColor whiteColor];
154
+ }
155
+
156
+ UIViewController *rootViewController = ${viewControllerInit ?? '[UIViewController new]'};
157
+ rootViewController.view = rootView;
158
+ self.window.rootViewController = rootViewController;
159
+ [self.window makeKeyAndVisible];
160
+
161
+ return bridge;
162
+ }
163
+ `;
164
+
165
+ function addImports(appDelegate: string, shouldAddUpdatesIntegration: boolean): string {
68
166
  if (
69
167
  !appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_IOS_IMPORT) &&
70
168
  !appDelegate.includes(DEV_LAUNCHER_UPDATES_APP_DELEGATE_IOS_IMPORT)
@@ -81,6 +179,47 @@ export function modifyAppDelegate(appDelegate: string, expoUpdatesVersion: strin
81
179
  appDelegate = lines.join('\n');
82
180
  }
83
181
 
182
+ return appDelegate;
183
+ }
184
+
185
+ function removeDevMenuInit(appDelegate: string): string {
186
+ if (!appDelegate.includes(DEV_MENU_IMPORT)) {
187
+ // expo-dev-launcher is responsible for initializing the expo-dev-menu.
188
+ // We need to remove init block from AppDelegate.
189
+ appDelegate = appDelegate.replace(DEV_MENU_IOS_INIT, '');
190
+ }
191
+ return appDelegate;
192
+ }
193
+
194
+ function addDeepLinkHandler(appDelegate: string): string {
195
+ if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_ON_DEEP_LINK)) {
196
+ appDelegate = appDelegate.replace(
197
+ 'return [RCTLinkingManager application:application openURL:url options:options];',
198
+ DEV_LAUNCHER_APP_DELEGATE_ON_DEEP_LINK
199
+ );
200
+ }
201
+ return appDelegate;
202
+ }
203
+
204
+ function changeDebugURL(appDelegate: string): string {
205
+ if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_SOURCE_FOR_URL)) {
206
+ appDelegate = appDelegate.replace(
207
+ 'return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];',
208
+ DEV_LAUNCHER_APP_DELEGATE_SOURCE_FOR_URL
209
+ );
210
+ }
211
+ return appDelegate;
212
+ }
213
+
214
+ export function modifyLegacyAppDelegate(
215
+ appDelegate: string,
216
+ expoUpdatesVersion: string | null = null
217
+ ) {
218
+ const shouldAddUpdatesIntegration =
219
+ expoUpdatesVersion != null && semver.gt(expoUpdatesVersion, '0.6.0');
220
+
221
+ appDelegate = addImports(appDelegate, shouldAddUpdatesIntegration);
222
+
84
223
  if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_INIT)) {
85
224
  appDelegate = appDelegate.replace(
86
225
  /(didFinishLaunchingWithOptions([^}])*)\[self initializeReactNativeApp\];(([^}])*})/,
@@ -105,35 +244,72 @@ export function modifyAppDelegate(appDelegate: string, expoUpdatesVersion: strin
105
244
  );
106
245
  }
107
246
 
108
- if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_SOURCE_FOR_URL)) {
109
- appDelegate = appDelegate.replace(
110
- 'return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];',
111
- DEV_LAUNCHER_APP_DELEGATE_SOURCE_FOR_URL
112
- );
247
+ appDelegate = changeDebugURL(appDelegate);
248
+ appDelegate = addDeepLinkHandler(appDelegate);
249
+
250
+ if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_CONTROLLER_DELEGATE_LEGACY)) {
251
+ appDelegate += DEV_LAUNCHER_APP_DELEGATE_CONTROLLER_DELEGATE_LEGACY;
113
252
  }
114
253
 
115
- if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_ON_DEEP_LINK)) {
254
+ appDelegate = removeDevMenuInit(appDelegate);
255
+ return appDelegate;
256
+ }
257
+
258
+ export function modifyAppDelegate(appDelegate: string, expoUpdatesVersion: string | null = null) {
259
+ const shouldAddUpdatesIntegration =
260
+ expoUpdatesVersion != null && semver.gt(expoUpdatesVersion, '0.6.0');
261
+
262
+ if (!DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION_REGEX.test(appDelegate)) {
263
+ if (DEV_LAUNCHER_INIT_TO_REMOVE.test(appDelegate)) {
264
+ // UIViewController can be initialized differently depending on whether expo-screen-orientation is installed,
265
+ // so we need to preserve whatever is there already.
266
+ let viewControllerInit;
267
+ appDelegate = appDelegate.replace(DEV_LAUNCHER_INIT_TO_REMOVE, (match, p1) => {
268
+ viewControllerInit = p1;
269
+ return DEV_LAUNCHER_NEW_INIT;
270
+ });
271
+ appDelegate = addLines(appDelegate, '@implementation AppDelegate', 1, [
272
+ DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION(viewControllerInit),
273
+ ]);
274
+ } else {
275
+ WarningAggregator.addWarningIOS(
276
+ 'expo-dev-launcher',
277
+ `Failed to modify AppDelegate init function.
278
+ See the expo-dev-client installation instructions to modify your AppDelegate manually: ${InstallationPage}`
279
+ );
280
+ }
281
+ }
282
+
283
+ if (
284
+ shouldAddUpdatesIntegration &&
285
+ !appDelegate.includes(DEV_LAUNCHER_UPDATES_APP_DELEGATE_INIT)
286
+ ) {
116
287
  appDelegate = appDelegate.replace(
117
- 'return [RCTLinkingManager application:application openURL:url options:options];',
118
- DEV_LAUNCHER_APP_DELEGATE_ON_DEEP_LINK
288
+ 'EXDevLauncherController *controller = [EXDevLauncherController sharedInstance];',
289
+ DEV_LAUNCHER_UPDATES_APP_DELEGATE_INIT
119
290
  );
120
291
  }
121
292
 
293
+ appDelegate = addImports(appDelegate, shouldAddUpdatesIntegration);
294
+
122
295
  if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_CONTROLLER_DELEGATE)) {
123
296
  appDelegate += DEV_LAUNCHER_APP_DELEGATE_CONTROLLER_DELEGATE;
124
297
  }
125
298
 
126
- if (!appDelegate.includes(DEV_MENU_IMPORT)) {
127
- // expo-dev-launcher is responsible for initializing the expo-dev-menu.
128
- // We need to remove init block from AppDelegate.
129
- appDelegate = appDelegate.replace(DEV_MENU_IOS_INIT, '');
299
+ if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_ON_DEEP_LINK)) {
300
+ appDelegate = appDelegate.replace(
301
+ 'return [RCTLinkingManager application:application openURL:url options:options];',
302
+ DEV_LAUNCHER_APP_DELEGATE_ON_DEEP_LINK
303
+ );
130
304
  }
131
305
 
306
+ appDelegate = changeDebugURL(appDelegate);
307
+ appDelegate = removeDevMenuInit(appDelegate);
132
308
  return appDelegate;
133
309
  }
134
310
 
135
- export const withDevLauncherAppDelegate: ConfigPlugin = config => {
136
- return withAppDelegate(config, config => {
311
+ export const withDevLauncherAppDelegate: ConfigPlugin = (config) => {
312
+ return withAppDelegate(config, (config) => {
137
313
  if (config.modResults.language === 'objc') {
138
314
  let expoUpdatesVersion;
139
315
  try {
@@ -144,14 +320,26 @@ export const withDevLauncherAppDelegate: ConfigPlugin = config => {
144
320
  `Failed to check compatibility with expo-updates - ${e}`
145
321
  );
146
322
  }
147
- config.modResults.contents = modifyAppDelegate(
148
- config.modResults.contents,
149
- expoUpdatesVersion
150
- );
323
+
324
+ if (
325
+ config.modResults.contents.includes(INITIALIZE_REACT_NATIVE_APP_FUNCTION) &&
326
+ !config.modResults.contents.includes(NEW_INITIALIZE_REACT_NATIVE_APP_FUNCTION)
327
+ ) {
328
+ config.modResults.contents = modifyLegacyAppDelegate(
329
+ config.modResults.contents,
330
+ expoUpdatesVersion
331
+ );
332
+ } else {
333
+ config.modResults.contents = modifyAppDelegate(
334
+ config.modResults.contents,
335
+ expoUpdatesVersion
336
+ );
337
+ }
151
338
  } else {
152
339
  WarningAggregator.addWarningIOS(
153
340
  'expo-dev-launcher',
154
- 'Swift AppDelegate files are not supported yet.'
341
+ `Swift AppDelegate files are not supported yet.
342
+ See the expo-dev-client installation instructions to modify your AppDelegate manually: ${InstallationPage}`
155
343
  );
156
344
  }
157
345
  return config;
@@ -2,6 +2,7 @@
2
2
  type CodedError = Error & { code?: string };
3
3
 
4
4
  let isErrorHandlingEnabled = true;
5
+ let wasHit = false; // whether the original error handler was called
5
6
 
6
7
  const unavailableErrorPossibleSolutions = `Some possible solutions:
7
8
  - Make sure that the method is available on the current platform.
@@ -40,9 +41,15 @@ function customizeError(error: Error | CodedError) {
40
41
 
41
42
  function errorHandler(originalHandler, error, isFatal) {
42
43
  if (error instanceof Error) {
44
+ // Suppresses `"main" has not been registered` error only if it was caused by a different error.
45
+ // Otherwise, we want to show it, cause the user may forget to call `AppRegistry.registerComponent`.
46
+ if (wasHit && error.message?.includes('has not been registered. This can happen if')) {
47
+ return;
48
+ }
43
49
  customizeError(error);
44
50
  }
45
51
 
52
+ wasHit = true;
46
53
  originalHandler(error, isFatal);
47
54
  }
48
55
 
@@ -1,111 +0,0 @@
1
- package expo.modules.devlauncher.launcher.manifest
2
-
3
- import android.util.Log
4
- import com.google.gson.Gson
5
- import com.google.gson.GsonBuilder
6
- import com.google.gson.JsonDeserializationContext
7
- import com.google.gson.JsonDeserializer
8
- import com.google.gson.JsonElement
9
- import java.io.Reader
10
- import java.lang.reflect.Type
11
- import kotlin.reflect.full.declaredMemberProperties
12
- import kotlin.reflect.jvm.isAccessible
13
-
14
- /**
15
- * A representation of the android specific properties such as `primaryColor` or status bar configuration.
16
- * Note that this class doesn't contain fields which are also defined in the main section of `app.json`
17
- * and can be overridden by the [DevLauncherManifest.android] section, like [DevLauncherManifest.backgroundColor].
18
- * Those fields are combined during deserialization and the final value is written to the [DevLauncherManifest] object.
19
- */
20
- class DevLauncherAndroidManifestSection
21
-
22
- data class DevLauncherStatusBarSection(
23
- val barStyle: DevLauncherStatusBarStyle?,
24
- val backgroundColor: String?,
25
- val hidden: Boolean?,
26
- val translucent: Boolean?
27
- )
28
-
29
- data class DevLauncherManifestDeveloperSection(
30
- val tool: String?
31
- )
32
-
33
- data class DevLauncherManifest(
34
- val name: String,
35
- val slug: String,
36
- val bundleUrl: String,
37
- val hostUri: String,
38
- val mainModuleName: String,
39
- val version: String,
40
-
41
- val orientation: DevLauncherOrientation?,
42
-
43
- val android: DevLauncherAndroidManifestSection?,
44
-
45
- val developer: DevLauncherManifestDeveloperSection?,
46
-
47
- val userInterfaceStyle: DevLauncherUserInterface?,
48
- val backgroundColor: String?,
49
- val primaryColor: String?,
50
-
51
- val androidStatusBar: DevLauncherStatusBarSection?
52
- ) {
53
- var rawData: String? = null
54
- private set
55
-
56
- fun isUsingDeveloperTool(): Boolean {
57
- return developer?.tool != null
58
- }
59
-
60
- /**
61
- * Class which contains all fields that the user can override in the android section.
62
- */
63
- private data class DevLauncherOverriddenProperties(
64
- val userInterfaceStyle: DevLauncherUserInterface?,
65
- val backgroundColor: String?
66
- )
67
-
68
- private class DevLauncherManifestDeserializer : JsonDeserializer<DevLauncherManifest> {
69
-
70
- override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): DevLauncherManifest {
71
- with(Gson()) {
72
- val jsonObject = json.asJsonObject
73
- val baseManifest = fromJson(jsonObject, DevLauncherManifest::class.java)
74
- if (jsonObject.has("android")) {
75
- val overriddenProperties = fromJson(jsonObject.getAsJsonObject("android"), DevLauncherOverriddenProperties::class.java)
76
- applyOverriddenProperties(baseManifest, overriddenProperties)
77
- }
78
- return baseManifest
79
- }
80
- }
81
-
82
- private fun applyOverriddenProperties(baseManifest: DevLauncherManifest, overriddenProperties: DevLauncherOverriddenProperties) {
83
- for (field in DevLauncherOverriddenProperties::class.declaredMemberProperties) {
84
- try {
85
- // It shouldn't be needed, but when we try to run this code on JVM (unit test)
86
- // we get `IllegalAccessException`.
87
- field.isAccessible = true
88
- val overriddenValue = field.get(overriddenProperties) ?: continue
89
- val baseField = baseManifest::class.java.getDeclaredField(field.name)
90
- baseField.isAccessible = true
91
- baseField.set(baseManifest, overriddenValue)
92
- } catch (e: Exception) {
93
- Log.w("DevelopmentClient", "Could't apply overridden property ${field.name}", e)
94
- }
95
- }
96
- }
97
- }
98
-
99
- companion object {
100
- fun fromJson(jsonReader: Reader): DevLauncherManifest {
101
- val manifestString = jsonReader.readText()
102
- return GsonBuilder()
103
- .registerTypeAdapter(DevLauncherManifest::class.java, DevLauncherManifestDeserializer())
104
- .create()
105
- .fromJson(manifestString, DevLauncherManifest::class.java)
106
- .apply {
107
- rawData = manifestString
108
- }
109
- }
110
- }
111
- }