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.
- package/CHANGELOG.md +52 -0
- package/README.md +1 -1
- package/android/build.gradle +2 -1
- package/android/src/debug/assets/expo_dev_launcher_android.bundle +396 -403
- package/android/src/debug/java/expo/modules/devlauncher/DevLauncherController.kt +10 -6
- package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherAppLoaderFactory.kt +5 -5
- package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherExpoAppLoader.kt +5 -5
- package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherLocalAppLoader.kt +3 -3
- package/android/src/debug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherPublishedAppLoader.kt +2 -6
- package/android/src/debug/java/expo/modules/devlauncher/modules/DevLauncherInternalModule.kt +6 -1
- package/android/src/debug/java/expo/modules/devlauncher/modules/DevLauncherModule.kt +1 -1
- package/android/src/main/java/expo/modules/devlauncher/launcher/DevLauncherControllerInterface.kt +2 -2
- package/android/src/main/java/expo/modules/devlauncher/launcher/configurators/DevLauncherExpoActivityConfigurator.kt +12 -11
- package/android/src/main/java/expo/modules/devlauncher/launcher/manifest/DevLauncherManifestParser.kt +4 -2
- package/android/src/main/java/expo/modules/devlauncher/launcher/manifest/DevLauncherManifestTypes.kt +11 -26
- package/android/src/main/java/expo/modules/devlauncher/launcher/menu/DevLauncherMenuDelegate.kt +3 -3
- package/android/src/release/java/expo/modules/devlauncher/DevLauncherController.kt +2 -2
- package/android/src/testDebug/java/expo/modules/devlauncher/DevLauncherControllerTest.kt +50 -0
- package/android/src/testDebug/java/expo/modules/devlauncher/launcher/DevLauncherRecentlyOpenedAppsRegistryTest.kt +43 -0
- package/android/src/testDebug/java/expo/modules/devlauncher/launcher/loaders/DevLauncherAppLoaderFactoryTest.kt +3 -3
- package/android/src/testDebug/java/expo/modules/devlauncher/modules/DevLauncherModuleTest.kt +38 -0
- package/build/DevLauncherErrorManager.js +7 -0
- package/build/DevLauncherErrorManager.js.map +1 -1
- package/expo-dev-launcher.podspec +2 -0
- package/ios/EXDevLauncher.m +8 -2
- package/ios/EXDevLauncher.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/ios/EXDevLauncher.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/ios/EXDevLauncher.xcodeproj/xcuserdata/brentvatne.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- package/ios/EXDevLauncher.xcworkspace/xcuserdata/brentvatne.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/EXDevLauncherController.h +2 -2
- package/ios/EXDevLauncherController.m +22 -11
- package/ios/EXDevLauncherInternal.m +5 -0
- package/ios/EXDevLauncherMenuDelegate.swift +3 -3
- package/ios/Errors/EXDevLauncherRedBox.m +5 -0
- package/ios/Manifest/EXDevLauncherManifestHelper.swift +15 -12
- package/ios/Manifest/EXDevLauncherManifestParser.h +3 -3
- package/ios/Manifest/EXDevLauncherManifestParser.m +17 -5
- package/ios/Tests/EXDevLauncherManifestHelperTests.swift +28 -0
- package/ios/Tests/EXDevLauncherManifestParserTests.m +113 -0
- package/ios/Tests/EXDevLauncherModuleTests.m +39 -0
- package/ios/devvyc 2021-05-14 18-19-10/devvyc.ipa +0 -0
- package/ios/main.jsbundle +414 -420
- package/package.json +6 -6
- package/plugin/build/constants.d.ts +1 -0
- package/plugin/build/constants.js +4 -0
- package/plugin/build/utils.d.ts +2 -0
- package/plugin/build/utils.js +24 -0
- package/plugin/build/withDevLauncher.js +27 -39
- package/plugin/build/withDevLauncherAppDelegate.d.ts +1 -0
- package/plugin/build/withDevLauncherAppDelegate.js +161 -16
- package/plugin/src/constants.ts +1 -0
- package/plugin/src/utils.ts +25 -0
- package/plugin/src/withDevLauncher.ts +32 -45
- package/plugin/src/withDevLauncherAppDelegate.ts +211 -23
- package/src/DevLauncherErrorManager.ts +7 -0
- package/android/src/main/java/expo/modules/devlauncher/launcher/manifest/DevLauncherManifest.kt +0 -111
- 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.
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
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
|
-
'
|
|
118
|
-
|
|
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(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
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
|
|
package/android/src/main/java/expo/modules/devlauncher/launcher/manifest/DevLauncherManifest.kt
DELETED
|
@@ -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
|
-
}
|