expo-dev-launcher 4.0.11 → 4.0.13

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 CHANGED
@@ -10,6 +10,19 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 4.0.13 — 2024-05-09
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - Fixed loading error when both `expo-dev-client` and `expo-updates` installed but no `runtimeVersion` configured. ([#28662](https://github.com/expo/expo/pull/28662) by [@kudo](https://github.com/kudo))
18
+ - Fixed loading error from a https dev-server on Android. ([#28691](https://github.com/expo/expo/pull/28691) by [@kudo](https://github.com/kudo))
19
+
20
+ ## 4.0.12 — 2024-05-08
21
+
22
+ ### 🐛 Bug fixes
23
+
24
+ - [iOS] Fixed DevMenu getting unresponsive after reloading bundle. ([#28664](https://github.com/expo/expo/pull/28664) by [@gabrieldonadel](https://github.com/gabrieldonadel))
25
+
13
26
  ## 4.0.11 — 2024-05-04
14
27
 
15
28
  _This version does not introduce any user-facing changes._
@@ -17,7 +17,7 @@ android {
17
17
  namespace "expo.modules.devlauncher"
18
18
  defaultConfig {
19
19
  versionCode 9
20
- versionName "4.0.11"
20
+ versionName "4.0.13"
21
21
  }
22
22
 
23
23
  buildTypes {
@@ -38,15 +38,16 @@ class DevLauncherAppLoaderFactory : DevLauncherKoinComponent, DevLauncherAppLoad
38
38
  // It's (maybe) a raw React Native bundle
39
39
  DevLauncherReactNativeAppLoader(url, appHost, context, controller)
40
40
  } else {
41
- if (updatesInterface == null) {
41
+ val runtimeVersion = getRuntimeVersion(context)
42
+ val configuration = createUpdatesConfigurationWithUrl(url, projectUrl, runtimeVersion, installationIDHelper.getOrCreateInstallationID(context))
43
+
44
+ if (updatesInterface?.isValidUpdatesConfiguration(configuration, context) != true) {
42
45
  manifest = manifestParser.parseManifest()
43
46
  if (!manifest!!.isUsingDeveloperTool()) {
44
47
  throw Exception("expo-updates is not properly installed or integrated. In order to load published projects with this development client, follow all installation and setup instructions for both the expo-dev-client and expo-updates packages.")
45
48
  }
46
49
  DevLauncherLocalAppLoader(manifest!!, appHost, context, controller)
47
50
  } else {
48
- val runtimeVersion = getRuntimeVersion(context)
49
- val configuration = createUpdatesConfigurationWithUrl(url, projectUrl, runtimeVersion, installationIDHelper.getOrCreateInstallationID(context))
50
51
  val update = updatesInterface!!.loadUpdate(configuration, context) {
51
52
  manifest = Manifest.fromManifestJson(it) // TODO: might be able to pass actual manifest object in here
52
53
  return@loadUpdate !manifest!!.isUsingDeveloperTool()
@@ -2,17 +2,36 @@ package com.facebook.react.devsupport
2
2
 
3
3
  import android.content.Context
4
4
  import android.net.Uri
5
+ import com.facebook.react.devsupport.interfaces.PackagerStatusCallback
5
6
  import com.facebook.react.modules.debug.interfaces.DeveloperSettings
6
7
  import com.facebook.react.packagerconnection.PackagerConnectionSettings
7
8
  import expo.modules.devlauncher.launcher.DevLauncherControllerInterface
9
+ import okhttp3.Call
10
+ import okhttp3.Callback
11
+ import okhttp3.OkHttpClient
12
+ import okhttp3.Request
13
+ import okhttp3.Response
14
+ import java.io.IOException
15
+ import java.util.concurrent.TimeUnit
16
+
17
+ private const val PACKAGER_OK_STATUS = "packager-status:running"
18
+ private const val HTTP_CONNECT_TIMEOUT_MS = 5000L
19
+ private const val PACKAGER_STATUS_ENDPOINT = "status"
8
20
 
9
21
  class DevLauncherDevServerHelper(
10
22
  context: Context,
11
23
  private val controller: DevLauncherControllerInterface?,
12
24
  devSettings: DeveloperSettings,
13
25
  packagerConnection: PackagerConnectionSettings
14
- ) :
15
- DevServerHelper(devSettings, context.packageName, packagerConnection) {
26
+ ) : DevServerHelper(devSettings, context.packageName, packagerConnection) {
27
+
28
+ private val httpClient: OkHttpClient by lazy {
29
+ OkHttpClient.Builder()
30
+ .connectTimeout(HTTP_CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS)
31
+ .readTimeout(0, TimeUnit.MILLISECONDS)
32
+ .writeTimeout(0, TimeUnit.MILLISECONDS)
33
+ .build()
34
+ }
16
35
 
17
36
  override fun getDevServerBundleURL(jsModulePath: String?): String {
18
37
  return controller?.manifest?.getBundleURL() ?: super.getDevServerBundleURL(jsModulePath)
@@ -47,4 +66,35 @@ class DevLauncherDevServerHelper(
47
66
  }
48
67
  return "$defaultValue&$customOptionsString"
49
68
  }
69
+
70
+ /**
71
+ * Re-implement [PackagerStatusCheck](https://github.com/facebook/react-native/blob/958f8e2bb55ba3a2ac/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/PackagerStatusCheck.java)
72
+ * to support https.
73
+ */
74
+ override fun isPackagerRunning(callback: PackagerStatusCallback) {
75
+ val bundleURL = controller?.manifest?.getBundleURL() ?: return super.isPackagerRunning(callback)
76
+ val bundleUri = Uri.parse(bundleURL)
77
+ val statusUrl = bundleUri.buildUpon()
78
+ .path(PACKAGER_STATUS_ENDPOINT)
79
+ .clearQuery()
80
+ .build()
81
+ .toString()
82
+ val request = Request.Builder().url(statusUrl).build()
83
+ httpClient
84
+ .newCall(request)
85
+ .enqueue(object : Callback {
86
+ override fun onFailure(call: Call, e: IOException) {
87
+ callback.onPackagerStatusFetched(false)
88
+ }
89
+
90
+ override fun onResponse(call: Call, response: Response) {
91
+ if (!response.isSuccessful) {
92
+ callback.onPackagerStatusFetched(false)
93
+ return
94
+ }
95
+ val body = response.body?.string() ?: ""
96
+ callback.onPackagerStatusFetched(body == PACKAGER_OK_STATUS)
97
+ }
98
+ })
99
+ }
50
100
  }
@@ -13,6 +13,7 @@ import expo.modules.devlauncher.launcher.manifest.DevLauncherManifestParser
13
13
  import expo.modules.manifests.core.Manifest
14
14
  import expo.modules.updatesinterface.UpdatesInterface
15
15
  import io.mockk.coEvery
16
+ import io.mockk.every
16
17
  import io.mockk.mockk
17
18
  import io.mockk.mockkStatic
18
19
  import io.mockk.slot
@@ -94,6 +95,8 @@ internal class DevLauncherAppLoaderFactoryTest {
94
95
  fun `loads app locally if manifest indicates developer tool and updatesInterface exists`() = runBlocking<Unit> {
95
96
  mockUpdatesInterface(developmentManifestJSONString) {
96
97
  Truth.assertThat(it).isFalse()
98
+ }.let { updatesInterface ->
99
+ every { updatesInterface.isValidUpdatesConfiguration(any(), any()) } returns true
97
100
  }
98
101
  val appLoaderFactory = DevLauncherAppLoaderFactory()
99
102
 
@@ -109,6 +112,8 @@ internal class DevLauncherAppLoaderFactoryTest {
109
112
  fun `loads published app if manifest is published and updatesInterface exists`() = runBlocking<Unit> {
110
113
  mockUpdatesInterface(publishedManifestJSONString) {
111
114
  Truth.assertThat(it).isTrue()
115
+ }.let { updatesInterface ->
116
+ every { updatesInterface.isValidUpdatesConfiguration(any(), any()) } returns true
112
117
  }
113
118
  val appLoaderFactory = DevLauncherAppLoaderFactory()
114
119
 
@@ -120,7 +125,26 @@ internal class DevLauncherAppLoaderFactoryTest {
120
125
  Truth.assertThat(appLoaderFactory.shouldUseDeveloperSupport()).isFalse()
121
126
  }
122
127
 
123
- private fun mockUpdatesInterface(manifestJSONString: String, verifyShouldContinue: (Boolean) -> Unit) {
128
+ @Test
129
+ fun `loads app locally if manifest indicates developer tool but updates is mis-configured`() = runBlocking<Unit> {
130
+ mockUpdatesInterface(developmentManifestJSONString) {
131
+ Truth.assertThat(it).isTrue()
132
+ }.let { updatesInterface ->
133
+ every { updatesInterface.isValidUpdatesConfiguration(any(), any()) } returns false
134
+ }
135
+ val appLoaderFactory = DevLauncherAppLoaderFactory()
136
+
137
+ val manifestParser = mockk<DevLauncherManifestParser>()
138
+ val manifest = Manifest.fromManifestJson(JSONObject(developmentManifestJSONString))
139
+ coEvery { manifestParser.isManifestUrl() } returns true
140
+ coEvery { manifestParser.parseManifest() } returns manifest
141
+
142
+ val appLoader = appLoaderFactory.createAppLoader(developmentManifestURL, developmentManifestURL, manifestParser)
143
+ Truth.assertThat(appLoader).isInstanceOf(DevLauncherLocalAppLoader::class.java)
144
+ Truth.assertThat(appLoaderFactory.shouldUseDeveloperSupport()).isTrue()
145
+ }
146
+
147
+ private fun mockUpdatesInterface(manifestJSONString: String, verifyShouldContinue: (Boolean) -> Unit): UpdatesInterface {
124
148
  val updatesInterface = mockk<UpdatesInterface>()
125
149
  val manifest = JSONObject(manifestJSONString)
126
150
  val slot = slot<(JSONObject) -> Boolean>()
@@ -151,5 +175,7 @@ internal class DevLauncherAppLoaderFactoryTest {
151
175
  }
152
176
  )
153
177
  )
178
+
179
+ return updatesInterface
154
180
  }
155
181
  }
@@ -503,7 +503,7 @@
503
503
  return;
504
504
  }
505
505
 
506
- if (!self->_updatesInterface) {
506
+ if ([self->_updatesInterface isValidUpdatesConfiguration:updatesConfiguration] != YES) {
507
507
  [manifestParser tryToParseManifest:^(EXManifestsManifest *manifest) {
508
508
  if (!manifest.isUsingDeveloperTool) {
509
509
  onError([NSError errorWithDomain:@"DevelopmentClient" code:1 userInfo:@{NSLocalizedDescriptionKey: @"expo-updates is not properly installed or integrated. In order to load published projects with this development client, follow all installation and setup instructions for both the expo-dev-client and expo-updates packages."}]);
@@ -672,6 +672,8 @@
672
672
  // expo-dev-menu registers its commands here: https://github.com/expo/expo/blob/6da15324ff0b4a9cb24055e9815b8aa11f0ac3af/packages/expo-dev-menu/ios/Interceptors/DevMenuKeyCommandsInterceptor.swift#L27-L29
673
673
  [[RCTKeyCommands sharedInstance] unregisterKeyCommandWithInput:@"d"
674
674
  modifierFlags:UIKeyModifierCommand];
675
+ [[RCTKeyCommands sharedInstance] unregisterKeyCommandWithInput:@"r"
676
+ modifierFlags:UIKeyModifierCommand];
675
677
  }
676
678
  }
677
679
 
@@ -722,7 +724,7 @@
722
724
  - (void)setDevMenuAppBridge
723
725
  {
724
726
  DevMenuManager *manager = [DevMenuManager shared];
725
- manager.currentBridge = self.appBridge;
727
+ manager.currentBridge = self.appBridge.parentBridge;
726
728
 
727
729
  if (self.manifest != nil) {
728
730
  manager.currentManifest = self.manifest;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "expo-dev-launcher",
3
3
  "title": "Expo Development Launcher",
4
- "version": "4.0.11",
4
+ "version": "4.0.13",
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",
@@ -31,7 +31,7 @@
31
31
  "homepage": "https://docs.expo.dev",
32
32
  "dependencies": {
33
33
  "ajv": "8.11.0",
34
- "expo-dev-menu": "5.0.12",
34
+ "expo-dev-menu": "5.0.13",
35
35
  "expo-manifests": "~0.14.0",
36
36
  "resolve-from": "^5.0.0",
37
37
  "semver": "^7.6.0"
@@ -66,5 +66,5 @@
66
66
  "./setupTests.ts"
67
67
  ]
68
68
  },
69
- "gitHead": "2d80ee3cc6b1703101e8fa2639474efa7dc75097"
69
+ "gitHead": "9c05bec11c7f5b031d1b9b0ebee78c3b71a7e11d"
70
70
  }