expo-dev-launcher 0.9.0 → 0.9.1
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 +9 -1
- package/android/build.gradle +1 -1
- package/android/src/main/java/expo/modules/devlauncher/helpers/DevLauncherOkHttpExtension.kt +3 -2
- package/android/src/main/java/expo/modules/devlauncher/launcher/manifest/DevLauncherManifestParser.kt +7 -2
- package/ios/Manifest/EXDevLauncherManifestParser.m +1 -0
- package/ios/Tests/EXDevLauncherManifestParserTests.m +23 -0
- package/ios/assets/__node_modules/css-select/node_modules/dom-serializer/foreignNames.json +102 -0
- package/ios/assets/node_modules/@react-navigation/elements/src/assets/back-icon-mask.png +0 -0
- package/ios/assets/node_modules/@react-navigation/elements/src/assets/back-icon.png +0 -0
- package/ios/assets/node_modules/@react-navigation/elements/src/assets/back-icon@2x.png +0 -0
- package/ios/assets/node_modules/@react-navigation/elements/src/assets/back-icon@3x.png +0 -0
- package/ios/assets/node_modules/@react-navigation/stack/src/views/assets/back-icon-mask.png +0 -0
- package/ios/assets/node_modules/@react-navigation/stack/src/views/assets/back-icon.png +0 -0
- package/ios/assets/node_modules/@react-navigation/stack/src/views/assets/back-icon@2x.png +0 -0
- package/ios/assets/node_modules/@react-navigation/stack/src/views/assets/back-icon@3x.png +0 -0
- package/package.json +3 -3
- package/plugin/build/resolveExpoUpdatesVersion.js +8 -1
- package/plugin/build/withDevLauncher.d.ts +1 -0
- package/plugin/build/withDevLauncher.js +64 -24
- package/plugin/build/withDevLauncherAppDelegate.js +41 -6
- package/plugin/src/resolveExpoUpdatesVersion.ts +7 -1
- package/plugin/src/withDevLauncher.ts +83 -30
- package/plugin/src/withDevLauncherAppDelegate.ts +48 -6
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,15 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 0.9.1 — 2021-12-15
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- Fix plugin when `MainActivity.onNewIntent` exists. ([#15459](https://github.com/expo/expo/pull/15459) by [@janicduplessis](https://github.com/janicduplessis))
|
|
18
|
+
- Fix plugin when `expo-updates` is not present. ([#15541](https://github.com/expo/expo/pull/15541) by [@esamelson](https://github.com/esamelson))
|
|
19
|
+
- Include expo-platform header in manifest requests. ([#15563](https://github.com/expo/expo/pull/15563) by [@esamelson](https://github.com/esamelson))
|
|
20
|
+
- Fix plugin compatibility with SDK 44. ([#15562](https://github.com/expo/expo/pull/15562) & [#15570](https://github.com/expo/expo/pull/15570) by [@lukmccall](https://github.com/lukmccall) & [@esamelson](https://github.com/esamelson))
|
|
21
|
+
|
|
13
22
|
## 0.9.0 — 2021-12-03
|
|
14
23
|
|
|
15
24
|
### 🎉 New features
|
|
@@ -20,7 +29,6 @@
|
|
|
20
29
|
|
|
21
30
|
- Fix `No native splash screen registered for given view controller` error happening when project is using both `expo-dev-client` and `expo-splash-screen` packages. ([#14745](https://github.com/expo/expo/pull/14745) by [@kudo](https://github.com/kudo))
|
|
22
31
|
- Fix cannot load url that starts with exp. (by [@lukmccall](https://github.com/lukmccall))
|
|
23
|
-
- [plugin] Fix config plugin compatibility with expo-screen-orientation. ([#14752](https://github.com/expo/expo/pull/14752) by [@esamelson](https://github.com/esamelson))
|
|
24
32
|
|
|
25
33
|
## 0.8.4 — 2021-10-21
|
|
26
34
|
|
package/android/build.gradle
CHANGED
package/android/src/main/java/expo/modules/devlauncher/helpers/DevLauncherOkHttpExtension.kt
CHANGED
|
@@ -4,6 +4,7 @@ import android.net.Uri
|
|
|
4
4
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
|
5
5
|
import okhttp3.Call
|
|
6
6
|
import okhttp3.Callback
|
|
7
|
+
import okhttp3.Headers
|
|
7
8
|
import okhttp3.OkHttpClient
|
|
8
9
|
import okhttp3.Request
|
|
9
10
|
import okhttp3.Response
|
|
@@ -31,5 +32,5 @@ suspend inline fun Request.await(okHttpClient: OkHttpClient): Response {
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
fun fetch(url: Uri, method: String) =
|
|
35
|
-
Request.Builder().method(method, null).url(url.toString()).build()
|
|
35
|
+
fun fetch(url: Uri, method: String, headers: Headers) =
|
|
36
|
+
Request.Builder().method(method, null).url(url.toString()).headers(headers).build()
|
|
@@ -4,6 +4,7 @@ import android.net.Uri
|
|
|
4
4
|
import expo.modules.devlauncher.helpers.await
|
|
5
5
|
import expo.modules.devlauncher.helpers.fetch
|
|
6
6
|
import expo.modules.manifests.core.Manifest
|
|
7
|
+
import okhttp3.Headers
|
|
7
8
|
import okhttp3.OkHttpClient
|
|
8
9
|
import org.json.JSONObject
|
|
9
10
|
import java.io.Reader
|
|
@@ -13,7 +14,7 @@ class DevLauncherManifestParser(
|
|
|
13
14
|
private val url: Uri
|
|
14
15
|
) {
|
|
15
16
|
suspend fun isManifestUrl(): Boolean {
|
|
16
|
-
val response = fetch(url, "HEAD").await(httpClient)
|
|
17
|
+
val response = fetch(url, "HEAD", getHeaders()).await(httpClient)
|
|
17
18
|
val contentType = response.header("Content-Type")
|
|
18
19
|
// published projects may respond unsuccessfully to HEAD requests sent with no headers
|
|
19
20
|
return !response.isSuccessful
|
|
@@ -22,7 +23,7 @@ class DevLauncherManifestParser(
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
private suspend fun downloadManifest(): Reader {
|
|
25
|
-
val response = fetch(url, "GET").await(httpClient)
|
|
26
|
+
val response = fetch(url, "GET", getHeaders()).await(httpClient)
|
|
26
27
|
if (!response.isSuccessful) {
|
|
27
28
|
throw Exception("Failed to open app.\n\nIf you are trying to load the app from a development server, check your network connectivity and make sure you can access the server from your device.\n\nIf you are trying to open a published project, install a compatible version of expo-updates and follow all setup and integration steps.")
|
|
28
29
|
}
|
|
@@ -35,4 +36,8 @@ class DevLauncherManifestParser(
|
|
|
35
36
|
return Manifest.fromManifestJson(JSONObject(it.readText()))
|
|
36
37
|
}
|
|
37
38
|
}
|
|
39
|
+
|
|
40
|
+
private fun getHeaders(): Headers {
|
|
41
|
+
return Headers.of(mapOf("expo-platform" to "android"))
|
|
42
|
+
}
|
|
38
43
|
}
|
|
@@ -91,6 +91,7 @@ typedef void (^CompletionHandler)(NSData *data, NSURLResponse *response);
|
|
|
91
91
|
{
|
|
92
92
|
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.url];
|
|
93
93
|
[request setHTTPMethod:method];
|
|
94
|
+
[request setValue:@"ios" forHTTPHeaderField:@"expo-platform"];
|
|
94
95
|
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
|
|
95
96
|
if (error) {
|
|
96
97
|
onError(error);
|
|
@@ -117,6 +117,29 @@
|
|
|
117
117
|
[self waitForExpectationsWithTimeout:5 handler:nil];
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
- (void)testIsManifestURL_RequestIncludesPlatformHeader
|
|
121
|
+
{
|
|
122
|
+
[HTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest * _Nonnull request) {
|
|
123
|
+
return [request.URL.host isEqualToString:@"ohhttpstubs"] && [request.URL.path isEqualToString:@"/platform"];
|
|
124
|
+
} withStubResponse:^HTTPStubsResponse * _Nonnull(NSURLRequest * _Nonnull request) {
|
|
125
|
+
XCTAssertEqualObjects(@"ios", request.allHTTPHeaderFields[@"expo-platform"]);
|
|
126
|
+
return [HTTPStubsResponse responseWithData:[NSData new] statusCode:200 headers:nil];
|
|
127
|
+
}];
|
|
128
|
+
|
|
129
|
+
EXDevLauncherManifestParser *parser = [[EXDevLauncherManifestParser alloc] initWithURL:[NSURL URLWithString:@"http://ohhttpstubs/platform"] session:NSURLSession.sharedSession];
|
|
130
|
+
|
|
131
|
+
XCTestExpectation *expectation = [self expectationWithDescription:@"request should include expo-platform header"];
|
|
132
|
+
|
|
133
|
+
[parser isManifestURLWithCompletion:^(BOOL isManifestURL) {
|
|
134
|
+
[expectation fulfill];
|
|
135
|
+
} onError:^(NSError * _Nonnull error) {
|
|
136
|
+
XCTFail(@"Response should have been successful");
|
|
137
|
+
[expectation fulfill];
|
|
138
|
+
}];
|
|
139
|
+
|
|
140
|
+
[self waitForExpectationsWithTimeout:5 handler:nil];
|
|
141
|
+
}
|
|
142
|
+
|
|
120
143
|
- (void)testParseManifest
|
|
121
144
|
{
|
|
122
145
|
[HTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest * _Nonnull request) {
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
{
|
|
2
|
+
"elementNames" : {
|
|
3
|
+
"altglyph" : "altGlyph",
|
|
4
|
+
"altglyphdef" : "altGlyphDef",
|
|
5
|
+
"altglyphitem" : "altGlyphItem",
|
|
6
|
+
"animatecolor" : "animateColor",
|
|
7
|
+
"animatemotion" : "animateMotion",
|
|
8
|
+
"animatetransform" : "animateTransform",
|
|
9
|
+
"clippath" : "clipPath",
|
|
10
|
+
"feblend" : "feBlend",
|
|
11
|
+
"fecolormatrix" : "feColorMatrix",
|
|
12
|
+
"fecomponenttransfer" : "feComponentTransfer",
|
|
13
|
+
"fecomposite" : "feComposite",
|
|
14
|
+
"feconvolvematrix" : "feConvolveMatrix",
|
|
15
|
+
"fediffuselighting" : "feDiffuseLighting",
|
|
16
|
+
"fedisplacementmap" : "feDisplacementMap",
|
|
17
|
+
"fedistantlight" : "feDistantLight",
|
|
18
|
+
"fedropshadow" : "feDropShadow",
|
|
19
|
+
"feflood" : "feFlood",
|
|
20
|
+
"fefunca" : "feFuncA",
|
|
21
|
+
"fefuncb" : "feFuncB",
|
|
22
|
+
"fefuncg" : "feFuncG",
|
|
23
|
+
"fefuncr" : "feFuncR",
|
|
24
|
+
"fegaussianblur" : "feGaussianBlur",
|
|
25
|
+
"feimage" : "feImage",
|
|
26
|
+
"femerge" : "feMerge",
|
|
27
|
+
"femergenode" : "feMergeNode",
|
|
28
|
+
"femorphology" : "feMorphology",
|
|
29
|
+
"feoffset" : "feOffset",
|
|
30
|
+
"fepointlight" : "fePointLight",
|
|
31
|
+
"fespecularlighting" : "feSpecularLighting",
|
|
32
|
+
"fespotlight" : "feSpotLight",
|
|
33
|
+
"fetile" : "feTile",
|
|
34
|
+
"feturbulence" : "feTurbulence",
|
|
35
|
+
"foreignobject" : "foreignObject",
|
|
36
|
+
"glyphref" : "glyphRef",
|
|
37
|
+
"lineargradient" : "linearGradient",
|
|
38
|
+
"radialgradient" : "radialGradient",
|
|
39
|
+
"textpath" : "textPath"
|
|
40
|
+
},
|
|
41
|
+
"attributeNames" : {
|
|
42
|
+
"definitionurl" : "definitionURL",
|
|
43
|
+
"attributename" : "attributeName",
|
|
44
|
+
"attributetype" : "attributeType",
|
|
45
|
+
"basefrequency" : "baseFrequency",
|
|
46
|
+
"baseprofile" : "baseProfile",
|
|
47
|
+
"calcmode" : "calcMode",
|
|
48
|
+
"clippathunits" : "clipPathUnits",
|
|
49
|
+
"diffuseconstant" : "diffuseConstant",
|
|
50
|
+
"edgemode" : "edgeMode",
|
|
51
|
+
"filterunits" : "filterUnits",
|
|
52
|
+
"glyphref" : "glyphRef",
|
|
53
|
+
"gradienttransform" : "gradientTransform",
|
|
54
|
+
"gradientunits" : "gradientUnits",
|
|
55
|
+
"kernelmatrix" : "kernelMatrix",
|
|
56
|
+
"kernelunitlength" : "kernelUnitLength",
|
|
57
|
+
"keypoints" : "keyPoints",
|
|
58
|
+
"keysplines" : "keySplines",
|
|
59
|
+
"keytimes" : "keyTimes",
|
|
60
|
+
"lengthadjust" : "lengthAdjust",
|
|
61
|
+
"limitingconeangle" : "limitingConeAngle",
|
|
62
|
+
"markerheight" : "markerHeight",
|
|
63
|
+
"markerunits" : "markerUnits",
|
|
64
|
+
"markerwidth" : "markerWidth",
|
|
65
|
+
"maskcontentunits" : "maskContentUnits",
|
|
66
|
+
"maskunits" : "maskUnits",
|
|
67
|
+
"numoctaves" : "numOctaves",
|
|
68
|
+
"pathlength" : "pathLength",
|
|
69
|
+
"patterncontentunits" : "patternContentUnits",
|
|
70
|
+
"patterntransform" : "patternTransform",
|
|
71
|
+
"patternunits" : "patternUnits",
|
|
72
|
+
"pointsatx" : "pointsAtX",
|
|
73
|
+
"pointsaty" : "pointsAtY",
|
|
74
|
+
"pointsatz" : "pointsAtZ",
|
|
75
|
+
"preservealpha" : "preserveAlpha",
|
|
76
|
+
"preserveaspectratio" : "preserveAspectRatio",
|
|
77
|
+
"primitiveunits" : "primitiveUnits",
|
|
78
|
+
"refx" : "refX",
|
|
79
|
+
"refy" : "refY",
|
|
80
|
+
"repeatcount" : "repeatCount",
|
|
81
|
+
"repeatdur" : "repeatDur",
|
|
82
|
+
"requiredextensions" : "requiredExtensions",
|
|
83
|
+
"requiredfeatures" : "requiredFeatures",
|
|
84
|
+
"specularconstant" : "specularConstant",
|
|
85
|
+
"specularexponent" : "specularExponent",
|
|
86
|
+
"spreadmethod" : "spreadMethod",
|
|
87
|
+
"startoffset" : "startOffset",
|
|
88
|
+
"stddeviation" : "stdDeviation",
|
|
89
|
+
"stitchtiles" : "stitchTiles",
|
|
90
|
+
"surfacescale" : "surfaceScale",
|
|
91
|
+
"systemlanguage" : "systemLanguage",
|
|
92
|
+
"tablevalues" : "tableValues",
|
|
93
|
+
"targetx" : "targetX",
|
|
94
|
+
"targety" : "targetY",
|
|
95
|
+
"textlength" : "textLength",
|
|
96
|
+
"viewbox" : "viewBox",
|
|
97
|
+
"viewtarget" : "viewTarget",
|
|
98
|
+
"xchannelselector" : "xChannelSelector",
|
|
99
|
+
"ychannelselector" : "yChannelSelector",
|
|
100
|
+
"zoomandpan" : "zoomAndPan"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
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.9.
|
|
4
|
+
"version": "0.9.1",
|
|
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",
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
"semver": "^7.3.5"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"babel-preset-expo": "~
|
|
36
|
+
"babel-preset-expo": "~9.0.0",
|
|
37
37
|
"expo-module-scripts": "^2.0.0",
|
|
38
38
|
"react": "17.0.1",
|
|
39
39
|
"react-native": "0.64.3"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "58a32c58b6532ec0e14df10b772fa33f7657fbcc"
|
|
42
42
|
}
|
|
@@ -8,7 +8,14 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const resolve_from_1 = __importDefault(require("resolve-from"));
|
|
10
10
|
function resolveExpoUpdatesVersion(projectRoot) {
|
|
11
|
-
|
|
11
|
+
let expoUpdatesBuildPath;
|
|
12
|
+
try {
|
|
13
|
+
expoUpdatesBuildPath = (0, resolve_from_1.default)(projectRoot, 'expo-updates');
|
|
14
|
+
}
|
|
15
|
+
catch (e) {
|
|
16
|
+
// this is expected in projects that don't have expo-updates installed
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
12
19
|
if (!expoUpdatesBuildPath) {
|
|
13
20
|
return null;
|
|
14
21
|
}
|
|
@@ -3,6 +3,7 @@ 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.modifyJavaMainActivity = void 0;
|
|
6
7
|
const config_plugins_1 = require("@expo/config-plugins");
|
|
7
8
|
const fs_1 = __importDefault(require("fs"));
|
|
8
9
|
const path_1 = __importDefault(require("path"));
|
|
@@ -14,16 +15,20 @@ const withDevLauncherAppDelegate_1 = require("./withDevLauncherAppDelegate");
|
|
|
14
15
|
const pkg = require('expo-dev-launcher/package.json');
|
|
15
16
|
const DEV_LAUNCHER_ANDROID_IMPORT = 'expo.modules.devlauncher.DevLauncherController';
|
|
16
17
|
const DEV_LAUNCHER_UPDATES_ANDROID_IMPORT = 'expo.modules.updates.UpdatesDevLauncherController';
|
|
17
|
-
const DEV_LAUNCHER_ON_NEW_INTENT =
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
const DEV_LAUNCHER_ON_NEW_INTENT = [
|
|
19
|
+
'',
|
|
20
|
+
' @Override',
|
|
21
|
+
' public void onNewIntent(Intent intent) {',
|
|
22
|
+
' super.onNewIntent(intent);',
|
|
23
|
+
' }',
|
|
24
|
+
'',
|
|
25
|
+
].join('\n');
|
|
26
|
+
const DEV_LAUNCHER_HANDLE_INTENT = [
|
|
27
|
+
' if (DevLauncherController.tryToHandleIntent(this, intent)) {',
|
|
28
|
+
' return;',
|
|
29
|
+
' }',
|
|
30
|
+
].join('\n');
|
|
31
|
+
const DEV_LAUNCHER_WRAPPED_ACTIVITY_DELEGATE = (activityDelegateDeclaration) => `DevLauncherController.wrapReactActivityDelegate(this, () -> ${activityDelegateDeclaration})`;
|
|
27
32
|
const DEV_LAUNCHER_ANDROID_INIT = 'DevLauncherController.initialize(this, getReactNativeHost());';
|
|
28
33
|
const DEV_LAUNCHER_UPDATES_ANDROID_INIT = `if (BuildConfig.DEBUG) {
|
|
29
34
|
DevLauncherController.getInstance().setUpdatesInterface(UpdatesDevLauncherController.initialize(this));
|
|
@@ -37,6 +42,26 @@ async function readFileAsync(path) {
|
|
|
37
42
|
async function saveFileAsync(path, content) {
|
|
38
43
|
return fs_1.default.promises.writeFile(path, content, 'utf8');
|
|
39
44
|
}
|
|
45
|
+
function findClosingBracketMatchIndex(str, pos) {
|
|
46
|
+
if (str[pos] !== '(') {
|
|
47
|
+
throw new Error("No '(' at index " + pos);
|
|
48
|
+
}
|
|
49
|
+
let depth = 1;
|
|
50
|
+
for (let i = pos + 1; i < str.length; i++) {
|
|
51
|
+
switch (str[i]) {
|
|
52
|
+
case '(':
|
|
53
|
+
depth++;
|
|
54
|
+
break;
|
|
55
|
+
case ')':
|
|
56
|
+
if (--depth === 0) {
|
|
57
|
+
return i;
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return -1; // No matching closing parenthesis
|
|
63
|
+
}
|
|
64
|
+
const replaceBetween = (origin, startIndex, endIndex, insertion) => `${origin.substring(0, startIndex)}${insertion}${origin.substring(endIndex)}`;
|
|
40
65
|
function addJavaImports(javaSource, javaImports) {
|
|
41
66
|
const lines = javaSource.split('\n');
|
|
42
67
|
const lineIndexWithPackageDeclaration = lines.findIndex((line) => line.match(/^package .*;$/));
|
|
@@ -110,23 +135,38 @@ const withDevLauncherApplication = (config) => {
|
|
|
110
135
|
},
|
|
111
136
|
]);
|
|
112
137
|
};
|
|
138
|
+
function modifyJavaMainActivity(content) {
|
|
139
|
+
content = addJavaImports(content, [DEV_LAUNCHER_ANDROID_IMPORT, 'android.content.Intent']);
|
|
140
|
+
if (!content.includes('onNewIntent')) {
|
|
141
|
+
const lines = content.split('\n');
|
|
142
|
+
const onCreateIndex = lines.findIndex((line) => line.includes('public class MainActivity'));
|
|
143
|
+
lines.splice(onCreateIndex + 1, 0, DEV_LAUNCHER_ON_NEW_INTENT);
|
|
144
|
+
content = lines.join('\n');
|
|
145
|
+
}
|
|
146
|
+
if (!content.includes(DEV_LAUNCHER_HANDLE_INTENT)) {
|
|
147
|
+
content = (0, utils_1.addLines)(content, /super\.onNewIntent\(intent\)/, 0, [DEV_LAUNCHER_HANDLE_INTENT]);
|
|
148
|
+
}
|
|
149
|
+
if (!content.includes('DevLauncherController.wrapReactActivityDelegate')) {
|
|
150
|
+
const activityDelegateMatches = Array.from(content.matchAll(/new ReactActivityDelegate(Wrapper)/g));
|
|
151
|
+
if (activityDelegateMatches.length !== 1) {
|
|
152
|
+
config_plugins_1.WarningAggregator.addWarningAndroid('expo-dev-launcher', `Failed to wrap 'ReactActivityDelegate'
|
|
153
|
+
See the expo-dev-client installation instructions to modify your MainActivity.java manually: ${constants_1.InstallationPage}`);
|
|
154
|
+
return content;
|
|
155
|
+
}
|
|
156
|
+
const activityDelegateMatch = activityDelegateMatches[0];
|
|
157
|
+
const matchIndex = activityDelegateMatch.index;
|
|
158
|
+
const openingBracketIndex = matchIndex + activityDelegateMatch[0].length; // next character after `new ReactActivityDelegateWrapper`
|
|
159
|
+
const closingBracketIndex = findClosingBracketMatchIndex(content, openingBracketIndex);
|
|
160
|
+
const reactActivityDelegateDeclaration = content.substring(matchIndex, closingBracketIndex + 1);
|
|
161
|
+
content = replaceBetween(content, matchIndex, closingBracketIndex + 1, DEV_LAUNCHER_WRAPPED_ACTIVITY_DELEGATE(reactActivityDelegateDeclaration));
|
|
162
|
+
}
|
|
163
|
+
return content;
|
|
164
|
+
}
|
|
165
|
+
exports.modifyJavaMainActivity = modifyJavaMainActivity;
|
|
113
166
|
const withDevLauncherActivity = (config) => {
|
|
114
167
|
return (0, config_plugins_1.withMainActivity)(config, (config) => {
|
|
115
168
|
if (config.modResults.language === 'java') {
|
|
116
|
-
|
|
117
|
-
DEV_LAUNCHER_ANDROID_IMPORT,
|
|
118
|
-
'android.content.Intent',
|
|
119
|
-
]);
|
|
120
|
-
if (!content.includes(DEV_LAUNCHER_ON_NEW_INTENT)) {
|
|
121
|
-
const lines = content.split('\n');
|
|
122
|
-
const onCreateIndex = lines.findIndex((line) => line.includes('public class MainActivity'));
|
|
123
|
-
lines.splice(onCreateIndex + 1, 0, DEV_LAUNCHER_ON_NEW_INTENT);
|
|
124
|
-
content = lines.join('\n');
|
|
125
|
-
}
|
|
126
|
-
if (!content.includes('DevLauncherController.wrapReactActivityDelegate')) {
|
|
127
|
-
content = content.replace(/(new ReactActivityDelegate(Wrapper)?(.|\s)*\}\)?);$/mu, DEV_LAUNCHER_WRAPPED_ACTIVITY_DELEGATE);
|
|
128
|
-
}
|
|
129
|
-
config.modResults.contents = content;
|
|
169
|
+
config.modResults.contents = modifyJavaMainActivity(config.modResults.contents);
|
|
130
170
|
}
|
|
131
171
|
else {
|
|
132
172
|
config_plugins_1.WarningAggregator.addWarningAndroid('expo-dev-launcher', `Cannot automatically configure MainActivity if it's not java.
|
|
@@ -91,6 +91,16 @@ const DEV_LAUNCHER_INIT_TO_REMOVE = new RegExp(escapeRegExpCharacters(`RCTBridge
|
|
|
91
91
|
rootView.backgroundColor = [UIColor whiteColor];
|
|
92
92
|
}
|
|
93
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_INIT_TO_REMOVE_SDK_44 = new RegExp(escapeRegExpCharacters(`RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];
|
|
102
|
+
RCTRootView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@"main" initialProperties:nil];
|
|
103
|
+
rootView.backgroundColor = [UIColor whiteColor];
|
|
94
104
|
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
|
95
105
|
UIViewController *rootViewController = `) +
|
|
96
106
|
`([^;]+)` +
|
|
@@ -147,6 +157,20 @@ const DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION = (viewContro
|
|
|
147
157
|
return bridge;
|
|
148
158
|
}
|
|
149
159
|
`;
|
|
160
|
+
const DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION_SDK_44 = `
|
|
161
|
+
- (RCTBridge *)initializeReactNativeApp:(NSDictionary *)launchOptions
|
|
162
|
+
{
|
|
163
|
+
RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];
|
|
164
|
+
RCTRootView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@"main" initialProperties:nil];
|
|
165
|
+
rootView.backgroundColor = [UIColor whiteColor];
|
|
166
|
+
UIViewController *rootViewController = [self.reactDelegate createRootViewController];
|
|
167
|
+
rootViewController.view = rootView;
|
|
168
|
+
self.window.rootViewController = rootViewController;
|
|
169
|
+
[self.window makeKeyAndVisible];
|
|
170
|
+
|
|
171
|
+
return bridge;
|
|
172
|
+
}
|
|
173
|
+
`;
|
|
150
174
|
function addImports(appDelegate, shouldAddUpdatesIntegration) {
|
|
151
175
|
if (!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_IOS_IMPORT) &&
|
|
152
176
|
!appDelegate.includes(DEV_LAUNCHER_UPDATES_APP_DELEGATE_IOS_IMPORT)) {
|
|
@@ -202,18 +226,29 @@ function modifyLegacyAppDelegate(appDelegate, expoUpdatesVersion = null) {
|
|
|
202
226
|
exports.modifyLegacyAppDelegate = modifyLegacyAppDelegate;
|
|
203
227
|
function modifyAppDelegate(appDelegate, expoUpdatesVersion = null) {
|
|
204
228
|
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
|
-
|
|
229
|
+
if (!DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION_REGEX.test(appDelegate) &&
|
|
230
|
+
!appDelegate.includes(DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION_SDK_44)) {
|
|
231
|
+
let initToRemove;
|
|
232
|
+
let shouldAddSDK44Init = false;
|
|
233
|
+
if (DEV_LAUNCHER_INIT_TO_REMOVE_SDK_44.test(appDelegate)) {
|
|
234
|
+
initToRemove = DEV_LAUNCHER_INIT_TO_REMOVE_SDK_44;
|
|
235
|
+
shouldAddSDK44Init = true;
|
|
236
|
+
}
|
|
237
|
+
else if (DEV_LAUNCHER_INIT_TO_REMOVE.test(appDelegate)) {
|
|
238
|
+
initToRemove = DEV_LAUNCHER_INIT_TO_REMOVE;
|
|
239
|
+
}
|
|
240
|
+
if (initToRemove) {
|
|
207
241
|
// UIViewController can be initialized differently depending on whether expo-screen-orientation is installed,
|
|
208
242
|
// so we need to preserve whatever is there already.
|
|
209
243
|
let viewControllerInit;
|
|
210
|
-
appDelegate = appDelegate.replace(
|
|
244
|
+
appDelegate = appDelegate.replace(initToRemove, (match, p1) => {
|
|
211
245
|
viewControllerInit = p1;
|
|
212
246
|
return DEV_LAUNCHER_NEW_INIT;
|
|
213
247
|
});
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
248
|
+
const initToAdd = shouldAddSDK44Init
|
|
249
|
+
? DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION_SDK_44
|
|
250
|
+
: DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION(viewControllerInit);
|
|
251
|
+
appDelegate = (0, utils_1.addLines)(appDelegate, '@implementation AppDelegate', 1, [initToAdd]);
|
|
217
252
|
}
|
|
218
253
|
else {
|
|
219
254
|
config_plugins_1.WarningAggregator.addWarningIOS('expo-dev-launcher', `Failed to modify AppDelegate init function.
|
|
@@ -3,7 +3,13 @@ import path from 'path';
|
|
|
3
3
|
import resolveFrom from 'resolve-from';
|
|
4
4
|
|
|
5
5
|
export function resolveExpoUpdatesVersion(projectRoot: string): string | null {
|
|
6
|
-
|
|
6
|
+
let expoUpdatesBuildPath;
|
|
7
|
+
try {
|
|
8
|
+
expoUpdatesBuildPath = resolveFrom(projectRoot, 'expo-updates');
|
|
9
|
+
} catch (e) {
|
|
10
|
+
// this is expected in projects that don't have expo-updates installed
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
7
13
|
if (!expoUpdatesBuildPath) {
|
|
8
14
|
return null;
|
|
9
15
|
}
|
|
@@ -20,16 +20,21 @@ const pkg = require('expo-dev-launcher/package.json');
|
|
|
20
20
|
|
|
21
21
|
const DEV_LAUNCHER_ANDROID_IMPORT = 'expo.modules.devlauncher.DevLauncherController';
|
|
22
22
|
const DEV_LAUNCHER_UPDATES_ANDROID_IMPORT = 'expo.modules.updates.UpdatesDevLauncherController';
|
|
23
|
-
const DEV_LAUNCHER_ON_NEW_INTENT =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
const DEV_LAUNCHER_ON_NEW_INTENT = [
|
|
24
|
+
'',
|
|
25
|
+
' @Override',
|
|
26
|
+
' public void onNewIntent(Intent intent) {',
|
|
27
|
+
' super.onNewIntent(intent);',
|
|
28
|
+
' }',
|
|
29
|
+
'',
|
|
30
|
+
].join('\n');
|
|
31
|
+
const DEV_LAUNCHER_HANDLE_INTENT = [
|
|
32
|
+
' if (DevLauncherController.tryToHandleIntent(this, intent)) {',
|
|
33
|
+
' return;',
|
|
34
|
+
' }',
|
|
35
|
+
].join('\n');
|
|
36
|
+
const DEV_LAUNCHER_WRAPPED_ACTIVITY_DELEGATE = (activityDelegateDeclaration: string) =>
|
|
37
|
+
`DevLauncherController.wrapReactActivityDelegate(this, () -> ${activityDelegateDeclaration})`;
|
|
33
38
|
const DEV_LAUNCHER_ANDROID_INIT = 'DevLauncherController.initialize(this, getReactNativeHost());';
|
|
34
39
|
const DEV_LAUNCHER_UPDATES_ANDROID_INIT = `if (BuildConfig.DEBUG) {
|
|
35
40
|
DevLauncherController.getInstance().setUpdatesInterface(UpdatesDevLauncherController.initialize(this));
|
|
@@ -48,6 +53,29 @@ async function saveFileAsync(path: string, content: string): Promise<void> {
|
|
|
48
53
|
return fs.promises.writeFile(path, content, 'utf8');
|
|
49
54
|
}
|
|
50
55
|
|
|
56
|
+
function findClosingBracketMatchIndex(str: string, pos: number) {
|
|
57
|
+
if (str[pos] !== '(') {
|
|
58
|
+
throw new Error("No '(' at index " + pos);
|
|
59
|
+
}
|
|
60
|
+
let depth = 1;
|
|
61
|
+
for (let i = pos + 1; i < str.length; i++) {
|
|
62
|
+
switch (str[i]) {
|
|
63
|
+
case '(':
|
|
64
|
+
depth++;
|
|
65
|
+
break;
|
|
66
|
+
case ')':
|
|
67
|
+
if (--depth === 0) {
|
|
68
|
+
return i;
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return -1; // No matching closing parenthesis
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const replaceBetween = (origin: string, startIndex: number, endIndex: number, insertion: string) =>
|
|
77
|
+
`${origin.substring(0, startIndex)}${insertion}${origin.substring(endIndex)}`;
|
|
78
|
+
|
|
51
79
|
function addJavaImports(javaSource: string, javaImports: string[]): string {
|
|
52
80
|
const lines = javaSource.split('\n');
|
|
53
81
|
const lineIndexWithPackageDeclaration = lines.findIndex((line) => line.match(/^package .*;$/));
|
|
@@ -153,31 +181,56 @@ const withDevLauncherApplication: ConfigPlugin = (config) => {
|
|
|
153
181
|
]);
|
|
154
182
|
};
|
|
155
183
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (config.modResults.language === 'java') {
|
|
159
|
-
let content = addJavaImports(config.modResults.contents, [
|
|
160
|
-
DEV_LAUNCHER_ANDROID_IMPORT,
|
|
161
|
-
'android.content.Intent',
|
|
162
|
-
]);
|
|
184
|
+
export function modifyJavaMainActivity(content: string): string {
|
|
185
|
+
content = addJavaImports(content, [DEV_LAUNCHER_ANDROID_IMPORT, 'android.content.Intent']);
|
|
163
186
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
187
|
+
if (!content.includes('onNewIntent')) {
|
|
188
|
+
const lines = content.split('\n');
|
|
189
|
+
const onCreateIndex = lines.findIndex((line) => line.includes('public class MainActivity'));
|
|
167
190
|
|
|
168
|
-
|
|
191
|
+
lines.splice(onCreateIndex + 1, 0, DEV_LAUNCHER_ON_NEW_INTENT);
|
|
169
192
|
|
|
170
|
-
|
|
171
|
-
|
|
193
|
+
content = lines.join('\n');
|
|
194
|
+
}
|
|
195
|
+
if (!content.includes(DEV_LAUNCHER_HANDLE_INTENT)) {
|
|
196
|
+
content = addLines(content, /super\.onNewIntent\(intent\)/, 0, [DEV_LAUNCHER_HANDLE_INTENT]);
|
|
197
|
+
}
|
|
172
198
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
199
|
+
if (!content.includes('DevLauncherController.wrapReactActivityDelegate')) {
|
|
200
|
+
const activityDelegateMatches = Array.from(
|
|
201
|
+
content.matchAll(/new ReactActivityDelegate(Wrapper)/g)
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
if (activityDelegateMatches.length !== 1) {
|
|
205
|
+
WarningAggregator.addWarningAndroid(
|
|
206
|
+
'expo-dev-launcher',
|
|
207
|
+
`Failed to wrap 'ReactActivityDelegate'
|
|
208
|
+
See the expo-dev-client installation instructions to modify your MainActivity.java manually: ${InstallationPage}`
|
|
209
|
+
);
|
|
210
|
+
return content;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const activityDelegateMatch = activityDelegateMatches[0];
|
|
214
|
+
const matchIndex = activityDelegateMatch.index!;
|
|
215
|
+
const openingBracketIndex = matchIndex + activityDelegateMatch[0].length; // next character after `new ReactActivityDelegateWrapper`
|
|
216
|
+
|
|
217
|
+
const closingBracketIndex = findClosingBracketMatchIndex(content, openingBracketIndex);
|
|
218
|
+
const reactActivityDelegateDeclaration = content.substring(matchIndex, closingBracketIndex + 1);
|
|
179
219
|
|
|
180
|
-
|
|
220
|
+
content = replaceBetween(
|
|
221
|
+
content,
|
|
222
|
+
matchIndex,
|
|
223
|
+
closingBracketIndex + 1,
|
|
224
|
+
DEV_LAUNCHER_WRAPPED_ACTIVITY_DELEGATE(reactActivityDelegateDeclaration)
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
return content;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const withDevLauncherActivity: ConfigPlugin = (config) => {
|
|
231
|
+
return withMainActivity(config, (config) => {
|
|
232
|
+
if (config.modResults.language === 'java') {
|
|
233
|
+
config.modResults.contents = modifyJavaMainActivity(config.modResults.contents);
|
|
181
234
|
} else {
|
|
182
235
|
WarningAggregator.addWarningAndroid(
|
|
183
236
|
'expo-dev-launcher',
|
|
@@ -105,6 +105,20 @@ const DEV_LAUNCHER_INIT_TO_REMOVE = new RegExp(
|
|
|
105
105
|
'm'
|
|
106
106
|
);
|
|
107
107
|
|
|
108
|
+
const DEV_LAUNCHER_INIT_TO_REMOVE_SDK_44 = new RegExp(
|
|
109
|
+
escapeRegExpCharacters(`RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];
|
|
110
|
+
RCTRootView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@"main" initialProperties:nil];
|
|
111
|
+
rootView.backgroundColor = [UIColor whiteColor];
|
|
112
|
+
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
|
113
|
+
UIViewController *rootViewController = `) +
|
|
114
|
+
`([^;]+)` +
|
|
115
|
+
escapeRegExpCharacters(`;
|
|
116
|
+
rootViewController.view = rootView;
|
|
117
|
+
self.window.rootViewController = rootViewController;
|
|
118
|
+
[self.window makeKeyAndVisible];`),
|
|
119
|
+
'm'
|
|
120
|
+
);
|
|
121
|
+
|
|
108
122
|
const DEV_LAUNCHER_NEW_INIT = `self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
|
109
123
|
#if defined(EX_DEV_LAUNCHER_ENABLED)
|
|
110
124
|
EXDevLauncherController *controller = [EXDevLauncherController sharedInstance];
|
|
@@ -162,6 +176,21 @@ const DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION = (
|
|
|
162
176
|
}
|
|
163
177
|
`;
|
|
164
178
|
|
|
179
|
+
const DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION_SDK_44 = `
|
|
180
|
+
- (RCTBridge *)initializeReactNativeApp:(NSDictionary *)launchOptions
|
|
181
|
+
{
|
|
182
|
+
RCTBridge *bridge = [self.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions];
|
|
183
|
+
RCTRootView *rootView = [self.reactDelegate createRootViewWithBridge:bridge moduleName:@"main" initialProperties:nil];
|
|
184
|
+
rootView.backgroundColor = [UIColor whiteColor];
|
|
185
|
+
UIViewController *rootViewController = [self.reactDelegate createRootViewController];
|
|
186
|
+
rootViewController.view = rootView;
|
|
187
|
+
self.window.rootViewController = rootViewController;
|
|
188
|
+
[self.window makeKeyAndVisible];
|
|
189
|
+
|
|
190
|
+
return bridge;
|
|
191
|
+
}
|
|
192
|
+
`;
|
|
193
|
+
|
|
165
194
|
function addImports(appDelegate: string, shouldAddUpdatesIntegration: boolean): string {
|
|
166
195
|
if (
|
|
167
196
|
!appDelegate.includes(DEV_LAUNCHER_APP_DELEGATE_IOS_IMPORT) &&
|
|
@@ -259,18 +288,31 @@ export function modifyAppDelegate(appDelegate: string, expoUpdatesVersion: strin
|
|
|
259
288
|
const shouldAddUpdatesIntegration =
|
|
260
289
|
expoUpdatesVersion != null && semver.gt(expoUpdatesVersion, '0.6.0');
|
|
261
290
|
|
|
262
|
-
if (
|
|
263
|
-
|
|
291
|
+
if (
|
|
292
|
+
!DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION_REGEX.test(appDelegate) &&
|
|
293
|
+
!appDelegate.includes(DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION_SDK_44)
|
|
294
|
+
) {
|
|
295
|
+
let initToRemove;
|
|
296
|
+
let shouldAddSDK44Init = false;
|
|
297
|
+
if (DEV_LAUNCHER_INIT_TO_REMOVE_SDK_44.test(appDelegate)) {
|
|
298
|
+
initToRemove = DEV_LAUNCHER_INIT_TO_REMOVE_SDK_44;
|
|
299
|
+
shouldAddSDK44Init = true;
|
|
300
|
+
} else if (DEV_LAUNCHER_INIT_TO_REMOVE.test(appDelegate)) {
|
|
301
|
+
initToRemove = DEV_LAUNCHER_INIT_TO_REMOVE;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (initToRemove) {
|
|
264
305
|
// UIViewController can be initialized differently depending on whether expo-screen-orientation is installed,
|
|
265
306
|
// so we need to preserve whatever is there already.
|
|
266
307
|
let viewControllerInit;
|
|
267
|
-
appDelegate = appDelegate.replace(
|
|
308
|
+
appDelegate = appDelegate.replace(initToRemove, (match, p1) => {
|
|
268
309
|
viewControllerInit = p1;
|
|
269
310
|
return DEV_LAUNCHER_NEW_INIT;
|
|
270
311
|
});
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
312
|
+
const initToAdd = shouldAddSDK44Init
|
|
313
|
+
? DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION_SDK_44
|
|
314
|
+
: DEV_LAUNCHER_INITIALIZE_REACT_NATIVE_APP_FUNCTION_DEFINITION(viewControllerInit);
|
|
315
|
+
appDelegate = addLines(appDelegate, '@implementation AppDelegate', 1, [initToAdd]);
|
|
274
316
|
} else {
|
|
275
317
|
WarningAggregator.addWarningIOS(
|
|
276
318
|
'expo-dev-launcher',
|