expo-modules-core 58.0.9 → 58.0.11
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 +19 -1
- package/android/build.gradle +3 -3
- package/android/src/compose/expo/modules/kotlin/views/ExpoComposeView.kt +0 -10
- package/android/src/main/java/expo/modules/kotlin/types/worklets/SerializableTypeConverter.kt +4 -0
- package/android/src/main/java/expo/modules/kotlin/views/ExpoView.kt +15 -1
- package/android/src/main/java/expo/modules/v2/ExpoModulesV2Host.kt +1 -1
- package/common/cpp/fabric/ExpoViewComponentDescriptor.h +35 -43
- package/common/cpp/fabric/ExpoViewShadowNode.h +22 -0
- package/expo-module-gradle-plugin/build.gradle.kts +1 -1
- package/package.json +4 -4
- package/prebuilds/output/debug/xcframeworks/ExpoModulesCore.tar.gz +0 -0
- package/prebuilds/output/debug/xcframeworks/ExpoModulesWorklets.tar.gz +0 -0
- package/prebuilds/output/release/xcframeworks/ExpoModulesCore.tar.gz +0 -0
- package/prebuilds/output/release/xcframeworks/ExpoModulesWorklets.tar.gz +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 58.0.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Bump precompiled artifacts due to ABI breakage in expo-modules-jsi@58.0.6. ([#50881](https://github.com/expo/expo/pull/50881) by [@kitten](https://github.com/kitten))
|
|
8
|
+
- [iOS] `@Record` now accepts properties that hold free-form `Any` values, such as `[String: Any]?` or `[Any]`. ([#50660](https://github.com/expo/expo/pull/50660) by [@tsapeta](https://github.com/tsapeta))
|
|
9
|
+
- [Android] Fixed Expo views using Android layout not completing system-forced layout passes. ([#48679](https://github.com/expo/expo/pull/48679) by [@lujjjh](https://github.com/lujjjh))
|
|
10
|
+
- Updated dependencies. ([#50881](https://github.com/expo/expo/pull/50881), [#50660](https://github.com/expo/expo/pull/50660))
|
|
11
|
+
- expo-modules-jsi@58.0.7
|
|
12
|
+
|
|
13
|
+
## 58.0.10
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Lay out `RNHostView` hosted content at the size SwiftUI or Compose gives it on an axis that `matchContents` does not match. ([#50674](https://github.com/expo/expo/pull/50674) by [@intergalacticspacehighway](https://github.com/intergalacticspacehighway))
|
|
18
|
+
- Updated dependencies. ([#50805](https://github.com/expo/expo/pull/50805), [#50844](https://github.com/expo/expo/pull/50844), [#50806](https://github.com/expo/expo/pull/50806))
|
|
19
|
+
- expo-modules-jsi@58.0.6
|
|
20
|
+
|
|
3
21
|
## 58.0.9
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
|
@@ -20,7 +38,7 @@
|
|
|
20
38
|
|
|
21
39
|
### 🐛 Bug fixes
|
|
22
40
|
|
|
23
|
-
- Fixed listeners removed with `subscription.remove()` never being garbage-collected, together with their emitter and everything their closure references, when the closure can reach the subscription (as in `useEvent`, `useEventListener` or a `useEffect` that returns `() => subscription.remove()`).
|
|
41
|
+
- Fixed listeners removed with `subscription.remove()` never being garbage-collected, together with their emitter and everything their closure references, when the closure can reach the subscription (as in `useEvent`, `useEventListener` or a `useEffect` that returns `() => subscription.remove()`). ([#50603](https://github.com/expo/expo/pull/50603) by [@gkueny](https://github.com/gkueny))
|
|
24
42
|
- [iOS] Fixed `Exception` reporting `undefined reason` to JavaScript instead of the description it was created with, which also affected every `promise.reject(code, description)` call. ([#50215](https://github.com/expo/expo/pull/50215) by [@Maher-Reven](https://github.com/Maher-Reven))
|
|
25
43
|
|
|
26
44
|
## 58.0.6 — 2026-09-23
|
package/android/build.gradle
CHANGED
|
@@ -46,7 +46,7 @@ if (shouldIncludeCompose) {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
group = 'host.exp.exponent'
|
|
49
|
-
version = '58.0.
|
|
49
|
+
version = '58.0.11'
|
|
50
50
|
|
|
51
51
|
def isExpoModulesCoreTests = {
|
|
52
52
|
Gradle gradle = getGradle()
|
|
@@ -97,7 +97,7 @@ if (workletsProject != null) {
|
|
|
97
97
|
|
|
98
98
|
def reactNativeWorkletsRootDir = workletsProject?.projectDir?.parentFile
|
|
99
99
|
|
|
100
|
-
def expoModulesV2Version = "0.2.
|
|
100
|
+
def expoModulesV2Version = "0.2.2"
|
|
101
101
|
|
|
102
102
|
configurations {
|
|
103
103
|
// Artifact-only: the C++ sources, not something to put on any classpath.
|
|
@@ -175,7 +175,7 @@ android {
|
|
|
175
175
|
defaultConfig {
|
|
176
176
|
consumerProguardFiles 'proguard-rules.pro'
|
|
177
177
|
versionCode 1
|
|
178
|
-
versionName "58.0.
|
|
178
|
+
versionName "58.0.11"
|
|
179
179
|
buildConfigField "String", "EXPO_MODULES_CORE_VERSION", "\"${versionName}\""
|
|
180
180
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", "true"
|
|
181
181
|
|
|
@@ -2,7 +2,6 @@ package expo.modules.kotlin.views
|
|
|
2
2
|
|
|
3
3
|
import android.annotation.SuppressLint
|
|
4
4
|
import android.content.Context
|
|
5
|
-
import android.content.res.Configuration
|
|
6
5
|
import android.util.Log
|
|
7
6
|
import android.view.View
|
|
8
7
|
import android.view.ViewGroup
|
|
@@ -118,15 +117,6 @@ abstract class ExpoComposeView<T : ComposeProps>(
|
|
|
118
117
|
}
|
|
119
118
|
}
|
|
120
119
|
|
|
121
|
-
override fun dispatchConfigurationChanged(newConfig: Configuration) {
|
|
122
|
-
super.dispatchConfigurationChanged(newConfig)
|
|
123
|
-
// React Native owns this view's bounds and may not schedule another Android layout pass
|
|
124
|
-
// when a configuration change leaves its Yoga layout unchanged.
|
|
125
|
-
if (withHostingView && isAttachedToWindow && isLaidOut) {
|
|
126
|
-
requestLayout()
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
120
|
/**
|
|
131
121
|
* Validates that this non-hosting Compose view has a valid Compose parent.
|
|
132
122
|
*
|
package/android/src/main/java/expo/modules/kotlin/types/worklets/SerializableTypeConverter.kt
CHANGED
|
@@ -2,6 +2,7 @@ package expo.modules.kotlin.types.worklets
|
|
|
2
2
|
|
|
3
3
|
import expo.modules.kotlin.jni.CppType
|
|
4
4
|
import expo.modules.kotlin.jni.ExpectedType
|
|
5
|
+
import expo.modules.kotlin.jni.WorkletsSoLoader
|
|
5
6
|
import expo.modules.kotlin.jni.worklets.Serializable
|
|
6
7
|
import expo.modules.kotlin.types.ConverterContext
|
|
7
8
|
import expo.modules.kotlin.types.NonNullableTypeConverter
|
|
@@ -12,6 +13,9 @@ class SerializableTypeConverter : NonNullableTypeConverter<Serializable>() {
|
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
override fun getCppRequiredTypes(): ExpectedType {
|
|
16
|
+
// Register the C++ converter before a function caches its argument converters.
|
|
17
|
+
// Module functions can be exported before the UI worklet runtime is installed.
|
|
18
|
+
WorkletsSoLoader.loadIfPresent()
|
|
15
19
|
return ExpectedType(CppType.SERIALIZABLE)
|
|
16
20
|
}
|
|
17
21
|
|
|
@@ -35,7 +35,8 @@ abstract class ExpoView(
|
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Manually trigger measure and layout.
|
|
38
|
-
* If [shouldUseAndroidLayout] is set to `true`, this method will be called automatically after
|
|
38
|
+
* If [shouldUseAndroidLayout] is set to `true`, this method will be called automatically after
|
|
39
|
+
* [requestLayout] or [forceLayout].
|
|
39
40
|
*/
|
|
40
41
|
@UiThread
|
|
41
42
|
fun measureAndLayout() {
|
|
@@ -54,6 +55,19 @@ abstract class ExpoView(
|
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
override fun forceLayout() {
|
|
59
|
+
super.forceLayout()
|
|
60
|
+
if (shouldUseAndroidLayout && isAttachedToWindow && isLaidOut) {
|
|
61
|
+
// ViewRootImpl can force-layout the hierarchy without React Native laying out its
|
|
62
|
+
// managed children. Preserve the Android-layout contract at that boundary.
|
|
63
|
+
post {
|
|
64
|
+
if (isAttachedToWindow && isLayoutRequested) {
|
|
65
|
+
measureAndLayout()
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
57
71
|
open fun clipToPaddingBox(canvas: Canvas) {
|
|
58
72
|
// When the border radius is set, we need to clip the content to the padding box.
|
|
59
73
|
// This is because the border radius is applied to the background drawable, not the view itself.
|
|
@@ -34,7 +34,7 @@ object ExpoModulesV2Host {
|
|
|
34
34
|
val registry = ModuleRegistry()
|
|
35
35
|
modules.forEach(registry::register)
|
|
36
36
|
runtime = ReactRuntime.attach(reactContext, registry)
|
|
37
|
-
logger.info("✅ Expo Modules v2 installed ${modules.size} module(s) on globalThis.
|
|
37
|
+
logger.info("✅ Expo Modules v2 installed ${modules.size} module(s) on globalThis.expo.modules")
|
|
38
38
|
} catch (e: Throwable) {
|
|
39
39
|
logger.error("❌ Cannot install Expo Modules v2: $e", e)
|
|
40
40
|
}
|
|
@@ -61,17 +61,47 @@ public:
|
|
|
61
61
|
|
|
62
62
|
const auto snode = dynamic_cast<ShadowNodeType *>(&shadowNode);
|
|
63
63
|
const auto state = snode->getStateData();
|
|
64
|
+
auto const &sharedProps = snode->getProps();
|
|
65
|
+
auto &style = const_cast<facebook::yoga::Style &>(
|
|
66
|
+
static_cast<const facebook::react::ViewProps &>(*sharedProps).yogaStyle);
|
|
67
|
+
bool propsStyleChanged = false;
|
|
64
68
|
|
|
69
|
+
if (isRNHostView(sharedProps)) {
|
|
70
|
+
// If RNHostView has align self set to auto or stretch, we should override it to flex-start so that the node can size itself to its content
|
|
71
|
+
auto const alignSelf = style.alignSelf();
|
|
72
|
+
|
|
73
|
+
if (alignSelf == facebook::yoga::Align::Auto || alignSelf == facebook::yoga::Align::Stretch) {
|
|
74
|
+
style.setAlignSelf(facebook::yoga::Align::FlexStart);
|
|
75
|
+
propsStyleChanged = true;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// handle layout style prop update
|
|
80
|
+
if (!isnan(state._styleWidth)) {
|
|
81
|
+
style.setDimension(facebook::yoga::Dimension::Width,
|
|
82
|
+
facebook::yoga::StyleSizeLength::points(state._styleWidth));
|
|
83
|
+
propsStyleChanged = true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!isnan(state._styleHeight)) {
|
|
87
|
+
style.setDimension(facebook::yoga::Dimension::Height,
|
|
88
|
+
facebook::yoga::StyleSizeLength::points(state._styleHeight));
|
|
89
|
+
propsStyleChanged = true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (propsStyleChanged) {
|
|
93
|
+
// Updates yoga style from props and sets the node dirty
|
|
94
|
+
snode->updateYogaProps();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Writes the node style directly, so it runs last.
|
|
65
98
|
auto width = state._width;
|
|
66
99
|
auto height = state._height;
|
|
67
100
|
|
|
68
101
|
if (!isnan(width) || !isnan(height)) {
|
|
69
|
-
auto const &props = *std::static_pointer_cast<const facebook::react::ViewProps>(
|
|
70
|
-
snode->getProps());
|
|
71
|
-
|
|
72
102
|
// The node has width and/or height set as style props, so we should not override it
|
|
73
|
-
auto widthProp =
|
|
74
|
-
auto heightProp =
|
|
103
|
+
auto widthProp = style.dimension(facebook::yoga::Dimension::Width);
|
|
104
|
+
auto heightProp = style.dimension(facebook::yoga::Dimension::Height);
|
|
75
105
|
|
|
76
106
|
if (widthProp.value().isDefined()) {
|
|
77
107
|
// view has fixed dimension size set in props, so we should not autosize it in that axis
|
|
@@ -84,44 +114,6 @@ public:
|
|
|
84
114
|
snode->setSize({width, height});
|
|
85
115
|
}
|
|
86
116
|
|
|
87
|
-
// handle layout style prop update
|
|
88
|
-
auto styleWidth = state._styleWidth;
|
|
89
|
-
auto styleHeight = state._styleHeight;
|
|
90
|
-
|
|
91
|
-
if (!isnan(styleWidth) || !isnan(styleHeight)) {
|
|
92
|
-
auto const &props = *std::static_pointer_cast<const facebook::react::ViewProps>(
|
|
93
|
-
snode->getProps());
|
|
94
|
-
|
|
95
|
-
auto &style = const_cast<facebook::yoga::Style &>(props.yogaStyle);
|
|
96
|
-
|
|
97
|
-
if (!isnan(styleWidth)) {
|
|
98
|
-
style.setDimension(facebook::yoga::Dimension::Width,
|
|
99
|
-
facebook::yoga::StyleSizeLength::points(styleWidth));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (!isnan(styleHeight)) {
|
|
103
|
-
style.setDimension(facebook::yoga::Dimension::Height,
|
|
104
|
-
facebook::yoga::StyleSizeLength::points(styleHeight));
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Updates yoga style from props and sets the node dirty
|
|
108
|
-
snode->updateYogaProps();
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (isRNHostView(snode->getProps())) {
|
|
112
|
-
auto const &props = *std::static_pointer_cast<const facebook::react::ViewProps>(
|
|
113
|
-
snode->getProps());
|
|
114
|
-
auto &style = const_cast<facebook::yoga::Style &>(props.yogaStyle);
|
|
115
|
-
|
|
116
|
-
// If RNHostView has align self set to auto or stretch, we should override it to flex-start so that the node can size itself to its content
|
|
117
|
-
auto const alignSelf = style.alignSelf();
|
|
118
|
-
|
|
119
|
-
if (alignSelf == facebook::yoga::Align::Auto || alignSelf == facebook::yoga::Align::Stretch) {
|
|
120
|
-
style.setAlignSelf(facebook::yoga::Align::FlexStart);
|
|
121
|
-
snode->updateYogaProps();
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
117
|
facebook::react::ConcreteComponentDescriptor<ShadowNodeType>::adopt(shadowNode);
|
|
126
118
|
}
|
|
127
119
|
};
|
|
@@ -163,6 +163,20 @@ private:
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
react::LayoutConstraints hostedContentConstraints(const react::ShadowNode &content) const {
|
|
166
|
+
auto constraints = contentStyleConstraints(content);
|
|
167
|
+
|
|
168
|
+
// An unmatched axis has the size SwiftUI or Compose gave the host (from `setViewSize`), so lay
|
|
169
|
+
// the content out at exactly that size. This overrides the content's own min/max on that axis.
|
|
170
|
+
auto const &ownStyle = this->yogaNode_.style();
|
|
171
|
+
constrainExactlyToPoints(ownStyle.dimension(facebook::yoga::Dimension::Width),
|
|
172
|
+
constraints.minimumSize.width, constraints.maximumSize.width);
|
|
173
|
+
constrainExactlyToPoints(ownStyle.dimension(facebook::yoga::Dimension::Height),
|
|
174
|
+
constraints.minimumSize.height, constraints.maximumSize.height);
|
|
175
|
+
|
|
176
|
+
return constraints;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
react::LayoutConstraints contentStyleConstraints(const react::ShadowNode &content) const {
|
|
166
180
|
react::LayoutConstraints constraints{};
|
|
167
181
|
constraints.layoutDirection = resolvedLayoutDirection();
|
|
168
182
|
|
|
@@ -186,6 +200,14 @@ private:
|
|
|
186
200
|
return constraints;
|
|
187
201
|
}
|
|
188
202
|
|
|
203
|
+
static void constrainExactlyToPoints(facebook::yoga::StyleSizeLength length,
|
|
204
|
+
react::Float &minimum,
|
|
205
|
+
react::Float &maximum) {
|
|
206
|
+
if (length.isPoints() && length.value().isDefined()) {
|
|
207
|
+
minimum = maximum = std::max<react::Float>(0, length.value().unwrap());
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
189
211
|
static void constrainToPoints(facebook::yoga::StyleSizeLength length, react::Float &constraint) {
|
|
190
212
|
if (length.isPoints() && length.value().isDefined()) {
|
|
191
213
|
constraint = std::max<react::Float>(0, length.value().unwrap());
|
|
@@ -33,7 +33,7 @@ dependencies {
|
|
|
33
33
|
implementation("com.facebook.react:react-native-gradle-plugin")
|
|
34
34
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
|
|
35
35
|
implementation("io.github.expo.pika:pika-gradle:1.1.0")
|
|
36
|
-
implementation("io.github.expo:expo-modules-v2-gradle-plugin:0.2.
|
|
36
|
+
implementation("io.github.expo:expo-modules-v2-gradle-plugin:0.2.2")
|
|
37
37
|
|
|
38
38
|
if (isExpoAutolinkingSettingsPluginAvailable) {
|
|
39
39
|
implementation("expo.modules:expo-autolinking-plugin-shared")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-modules-core",
|
|
3
|
-
"version": "58.0.
|
|
3
|
+
"version": "58.0.11",
|
|
4
4
|
"description": "The core of Expo Modules architecture",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"expo",
|
|
@@ -64,16 +64,16 @@
|
|
|
64
64
|
"./package.json": "./package.json"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
|
-
"expo-modules-jsi": "~58.0.
|
|
67
|
+
"expo-modules-jsi": "~58.0.7",
|
|
68
68
|
"expo-modules-macros": "0.14.0",
|
|
69
69
|
"invariant": "^2.2.4"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
|
-
"@testing-library/react-native": "^
|
|
72
|
+
"@testing-library/react-native": "^14.0.1",
|
|
73
73
|
"@types/invariant": "^2.2.33",
|
|
74
74
|
"@types/react": "~19.3.0",
|
|
75
75
|
"@types/react-dom": "^19.1.7",
|
|
76
|
-
"expo-module-scripts": "56.0.
|
|
76
|
+
"expo-module-scripts": "56.0.5",
|
|
77
77
|
"react-dom": "19.3.0"
|
|
78
78
|
},
|
|
79
79
|
"peerDependencies": {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|