expo-dev-menu 55.0.29 → 55.0.31
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 +11 -0
- package/android/build.gradle +2 -2
- package/android/src/debug/java/expo/modules/devmenu/compose/ui/MenuIcons.kt +16 -0
- package/android/src/debug/java/expo/modules/devmenu/compose/ui/ToolsSection.kt +44 -25
- package/android/src/debug/java/expo/modules/devmenu/devtools/DevMenuDevToolsDelegate.kt +30 -2
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,17 @@
|
|
|
10
10
|
|
|
11
11
|
### 💡 Others
|
|
12
12
|
|
|
13
|
+
## 55.0.31 — 2026-06-25
|
|
14
|
+
|
|
15
|
+
### 🐛 Bug fixes
|
|
16
|
+
|
|
17
|
+
- [Android] Restore the "Open React Native dev menu" entry in the dev menu's Tools section (regressed in [#38759](https://github.com/expo/expo/pull/38759)). ([#47047](https://github.com/expo/expo/pull/47047) by [@lindboe](https://github.com/lindboe))
|
|
18
|
+
- [Android] Re-enable the Fast Refresh toggle in the dev menu's Tools section. ([#47136](https://github.com/expo/expo/pull/47136) by [@lukmccall](https://github.com/lukmccall))
|
|
19
|
+
|
|
20
|
+
## 55.0.30 — 2026-05-19
|
|
21
|
+
|
|
22
|
+
_This version does not introduce any user-facing changes._
|
|
23
|
+
|
|
13
24
|
## 55.0.29 — 2026-05-15
|
|
14
25
|
|
|
15
26
|
_This version does not introduce any user-facing changes._
|
package/android/build.gradle
CHANGED
|
@@ -12,7 +12,7 @@ apply plugin: 'expo-module-gradle-plugin'
|
|
|
12
12
|
apply plugin: 'org.jetbrains.kotlin.plugin.compose'
|
|
13
13
|
|
|
14
14
|
group = 'host.exp.exponent'
|
|
15
|
-
version = '55.0.
|
|
15
|
+
version = '55.0.31'
|
|
16
16
|
|
|
17
17
|
def hasDevLauncher = findProject(":expo-dev-launcher") != null
|
|
18
18
|
def configureInRelease = findProperty("expo.devmenu.configureInRelease") == "true"
|
|
@@ -29,7 +29,7 @@ android {
|
|
|
29
29
|
|
|
30
30
|
defaultConfig {
|
|
31
31
|
versionCode 10
|
|
32
|
-
versionName '55.0.
|
|
32
|
+
versionName '55.0.31'
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
buildTypes {
|
|
@@ -122,6 +122,22 @@ object MenuIcons {
|
|
|
122
122
|
)
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
@Composable
|
|
126
|
+
fun Gear(
|
|
127
|
+
size: Dp,
|
|
128
|
+
tint: Color,
|
|
129
|
+
modifier: Modifier = Modifier
|
|
130
|
+
) {
|
|
131
|
+
Icon(
|
|
132
|
+
painter = painterResource(R.drawable.gear_fill),
|
|
133
|
+
contentDescription = "React Native dev menu",
|
|
134
|
+
tint = tint,
|
|
135
|
+
modifier = Modifier
|
|
136
|
+
.size(size)
|
|
137
|
+
.then(modifier)
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
125
141
|
@Composable
|
|
126
142
|
fun Refresh(
|
|
127
143
|
size: Dp,
|
|
@@ -86,31 +86,50 @@ fun ToolsSection(
|
|
|
86
86
|
}
|
|
87
87
|
)
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
89
|
+
Divider(thickness = 0.5.dp)
|
|
90
|
+
|
|
91
|
+
NewMenuButton(
|
|
92
|
+
withSurface = false,
|
|
93
|
+
icon = {
|
|
94
|
+
MenuIcons.Gear(
|
|
95
|
+
size = 20.dp,
|
|
96
|
+
tint = NewAppTheme.colors.icon.tertiary
|
|
97
|
+
)
|
|
98
|
+
},
|
|
99
|
+
content = {
|
|
100
|
+
NewText(
|
|
101
|
+
text = "Open React Native dev menu"
|
|
102
|
+
)
|
|
103
|
+
},
|
|
104
|
+
onClick = {
|
|
105
|
+
onAction(DevMenuAction.OpenReactNativeDevMenu)
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
Divider(thickness = 0.5.dp)
|
|
110
|
+
|
|
111
|
+
NewMenuButton(
|
|
112
|
+
withSurface = false,
|
|
113
|
+
icon = {
|
|
114
|
+
MenuIcons.Refresh(
|
|
115
|
+
size = 20.dp,
|
|
116
|
+
tint = NewAppTheme.colors.icon.tertiary
|
|
117
|
+
)
|
|
118
|
+
},
|
|
119
|
+
content = {
|
|
120
|
+
NewText(
|
|
121
|
+
text = "Fast Refresh"
|
|
122
|
+
)
|
|
123
|
+
},
|
|
124
|
+
rightComponent = {
|
|
125
|
+
ToggleSwitch(
|
|
126
|
+
isToggled = devToolsSettings.isHotLoadingEnabled
|
|
127
|
+
)
|
|
128
|
+
},
|
|
129
|
+
onClick = {
|
|
130
|
+
onAction(DevMenuAction.ToggleFastRefresh(!devToolsSettings.isHotLoadingEnabled))
|
|
131
|
+
}
|
|
132
|
+
)
|
|
114
133
|
|
|
115
134
|
// Hide FAB toggle on Quest devices since FAB is always on there
|
|
116
135
|
if (!VRUtilities.isQuest()) {
|
|
@@ -19,6 +19,7 @@ import kotlinx.coroutines.Dispatchers
|
|
|
19
19
|
import kotlinx.coroutines.GlobalScope
|
|
20
20
|
import kotlinx.coroutines.launch
|
|
21
21
|
import java.lang.ref.WeakReference
|
|
22
|
+
import java.lang.reflect.Proxy
|
|
22
23
|
|
|
23
24
|
class DevMenuDevToolsDelegate(
|
|
24
25
|
private val weakDevSupportManager: WeakReference<out DevSupportManager>
|
|
@@ -77,9 +78,9 @@ class DevMenuDevToolsDelegate(
|
|
|
77
78
|
internalSettings.isHotModuleReplacementEnabled = nextEnabled
|
|
78
79
|
|
|
79
80
|
if (nextEnabled) {
|
|
80
|
-
reactContext?.
|
|
81
|
+
reactContext?.callHMRClientMethod("enable")
|
|
81
82
|
} else {
|
|
82
|
-
reactContext?.
|
|
83
|
+
reactContext?.callHMRClientMethod("disable")
|
|
83
84
|
}
|
|
84
85
|
|
|
85
86
|
if (nextEnabled && !internalSettings.isJSDevModeEnabled) {
|
|
@@ -88,6 +89,33 @@ class DevMenuDevToolsDelegate(
|
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Invokes a no-argument [HMRClient] method (`enable`/`disable`) without tripping a bug in React
|
|
94
|
+
* Native's bridgeless JS module proxy.
|
|
95
|
+
*
|
|
96
|
+
* When a zero-argument method is invoked through the proxy returned by [ReactContext.getJSModule],
|
|
97
|
+
* the JDK passes a `null` args array to the invocation handler. In bridgeless mode that handler
|
|
98
|
+
* (`BridgelessReactContext.BridgelessJSModuleInvocationHandler`) declares the parameter non-null,
|
|
99
|
+
* so Kotlin's null check throws a [NullPointerException] before the call ever reaches JS. We
|
|
100
|
+
* invoke the handler directly with an explicit empty args array to sidestep the null. The legacy
|
|
101
|
+
* (bridge) handler tolerates the empty array too, so this is safe on both architectures.
|
|
102
|
+
*/
|
|
103
|
+
private fun ReactContext.callHMRClientMethod(methodName: String) {
|
|
104
|
+
val hmrClient = getJSModule(HMRClient::class.java) ?: return
|
|
105
|
+
val method = HMRClient::class.java.getMethod(methodName)
|
|
106
|
+
if (Proxy.isProxyClass(hmrClient.javaClass)) {
|
|
107
|
+
Proxy
|
|
108
|
+
.getInvocationHandler(hmrClient)
|
|
109
|
+
.invoke(
|
|
110
|
+
hmrClient,
|
|
111
|
+
method,
|
|
112
|
+
emptyArray<Any?>()
|
|
113
|
+
)
|
|
114
|
+
} else {
|
|
115
|
+
method.invoke(hmrClient)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
91
119
|
@OptIn(DelicateCoroutinesApi::class)
|
|
92
120
|
fun openJSInspector() {
|
|
93
121
|
val devSettings = devSettings ?: return
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-dev-menu",
|
|
3
|
-
"version": "55.0.
|
|
3
|
+
"version": "55.0.31",
|
|
4
4
|
"description": "Expo/React Native module with the developer menu.",
|
|
5
5
|
"main": "build/DevMenu.js",
|
|
6
6
|
"types": "build/DevMenu.d.ts",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"@babel/preset-typescript": "^7.7.4",
|
|
40
40
|
"@testing-library/react-native": "^13.3.0",
|
|
41
41
|
"babel-plugin-module-resolver": "^5.0.0",
|
|
42
|
-
"babel-preset-expo": "~55.0.
|
|
42
|
+
"babel-preset-expo": "~55.0.23",
|
|
43
43
|
"expo-module-scripts": "^55.0.2",
|
|
44
44
|
"react": "19.2.0",
|
|
45
45
|
"react-native": "0.83.6"
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"expo": "*"
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "0c1476ccb1494a2019171f5df1d7a1b7803455e9"
|
|
51
51
|
}
|