expo-modules-core 2.0.3 → 2.0.5

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.
Files changed (26) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/ExpoModulesCore.podspec +4 -1
  3. package/android/ExpoModulesCorePlugin.gradle +9 -1
  4. package/android/build.gradle +14 -2
  5. package/android/src/main/cpp/JNIUtils.cpp +9 -11
  6. package/android/src/main/cpp/JNIUtils.h +1 -1
  7. package/android/src/main/java/expo/modules/kotlin/events/KModuleEventEmitterWrapper.kt +6 -7
  8. package/android/src/main/java/expo/modules/kotlin/jni/JNIUtils.kt +1 -3
  9. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +4 -0
  10. package/android/src/main/java/expo/modules/kotlin/sharedobjects/SharedObject.kt +1 -0
  11. package/android/src/main/java/expo/modules/kotlin/types/JSTypeConverterHelper.kt +32 -0
  12. package/android/src/main/java/expo/modules/kotlin/views/ExpoView.kt +10 -0
  13. package/android/src/main/java/expo/modules/kotlin/views/FilteredReadableMap.kt +1 -1
  14. package/android/src/main/java/expo/modules/kotlin/views/ViewDefinitionBuilder.kt +42 -32
  15. package/android/src/main/java/expo/modules/kotlin/views/decorators/CSSProps.kt +174 -0
  16. package/android/src/rn74/expo/modules/rncompatibility/ReactNativeFeatureFlags.kt +14 -0
  17. package/android/src/rn77/expo/modules/rncompatibility/ReactNativeFeatureFlags.kt +12 -0
  18. package/build/NativeViewManagerAdapter.native.d.ts.map +1 -1
  19. package/build/ts-declarations/global.d.ts +4 -0
  20. package/build/ts-declarations/global.d.ts.map +1 -1
  21. package/build/web/index.web.d.ts +1 -0
  22. package/build/web/index.web.d.ts.map +1 -1
  23. package/package.json +2 -2
  24. package/src/NativeViewManagerAdapter.native.tsx +5 -3
  25. package/src/ts-declarations/global.ts +5 -0
  26. package/src/web/index.web.ts +4 -0
package/CHANGELOG.md CHANGED
@@ -10,11 +10,39 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 2.0.5 — 2024-11-22
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - [iOS] Fixed build error when using jsEngine=jsc. ([#33130](https://github.com/expo/expo/pull/33130) by [@kudo](https://github.com/kudo))
18
+
19
+ ### 💡 Others
20
+
21
+ - [Android] Introduced `applyKspJvmToolchain()` gradle helper to enforce JVM Toolchain version for KSP. ([#33148](https://github.com/expo/expo/pull/33148) by [@kudo](https://github.com/kudo))
22
+
23
+ ## 2.0.4 — 2024-11-19
24
+
25
+ ### 🎉 New features
26
+
27
+ - [Android] Supported css properties (border, background and shadow) by default. ([#33074](https://github.com/expo/expo/pull/33074) by [@lukmccall](https://github.com/lukmccall))
28
+
29
+ ### 🐛 Bug fixes
30
+
31
+ - Fixed type errors when using `ts-jest`. ([#32954](https://github.com/expo/expo/pull/32954) by [@kudo](https://github.com/kudo))
32
+ - [Android] Fixed `no non-static method "SharedObject.getSharedObjectId()" when proguard is enabled. ([#33011](https://github.com/expo/expo/pull/33011) by [@lukmccall](https://github.com/lukmccall))
33
+
34
+ ### 💡 Others
35
+
36
+ - Use `findNodeHandle` with a native ref instead of a class component instance to avoid expensive calls to `findCurrentFiberUsingSlowPath`. ([#33016](https://github.com/expo/expo/pull/33016) by [@tsapeta](https://github.com/tsapeta))
37
+ - Introduced `ReactNativeFeatureFlags` compat to fix React Native 0.77 breaking changes. ([#33077](https://github.com/expo/expo/pull/33077) by [@kudo](https://github.com/kudo))
38
+ - Fixed compatibility for React Native 0.77. ([#33079](https://github.com/expo/expo/pull/33079) by [@kudo](https://github.com/kudo))
39
+
13
40
  ## 2.0.3 — 2024-11-14
14
41
 
15
42
  ### 🐛 Bug fixes
16
43
 
17
44
  - Fixed requests from `expo/fetch` being stuck on iOS. ([#32894](https://github.com/expo/expo/pull/32894) by [@kudo](https://github.com/kudo))
45
+ - [Android] Fixed sending event containing `ByteArray` from the Kotlin module results in string ID instead of `Uint8Array`. ([#32945](https://github.com/expo/expo/pull/32945) by [@lukmccall](https://github.com/lukmccall))
18
46
 
19
47
  ## 2.0.2 — 2024-11-13
20
48
 
@@ -47,13 +47,16 @@ Pod::Spec.new do |s|
47
47
  s.static_framework = true
48
48
  s.header_dir = 'ExpoModulesCore'
49
49
 
50
- header_search_paths = []
50
+ header_search_paths = [
51
+ '"$(PODS_ROOT)/Headers/Private/React-Core"', # as React-RCTAppDelegate.podspec to access JSCExecutorFactory.h
52
+ ]
51
53
  if ENV['USE_FRAMEWORKS']
52
54
  header_search_paths.concat([
53
55
  # [begin] transitive dependencies of React-RCTAppDelegate that are not defined modules
54
56
  '"${PODS_CONFIGURATION_BUILD_DIR}/React-RuntimeApple/React_RuntimeApple.framework/Headers"',
55
57
  '"${PODS_CONFIGURATION_BUILD_DIR}/React-RuntimeCore/React_RuntimeCore.framework/Headers"',
56
58
  '"${PODS_CONFIGURATION_BUILD_DIR}/React-jserrorhandler/React_jserrorhandler.framework/Headers"',
59
+ '"${PODS_CONFIGURATION_BUILD_DIR}/React-jsinspector/jsinspector_modern.framework/Headers"',
57
60
  '"${PODS_CONFIGURATION_BUILD_DIR}/React-runtimescheduler/React_runtimescheduler.framework/Headers"',
58
61
  '"${PODS_CONFIGURATION_BUILD_DIR}/React-performancetimeline/React_performancetimeline.framework/Headers"',
59
62
  '"${PODS_CONFIGURATION_BUILD_DIR}/React-rendererconsistency/React_rendererconsistency.framework/Headers"',
@@ -26,7 +26,8 @@ class KotlinExpoModulesCorePlugin implements Plugin<Project> {
26
26
  "1.8.10": "1.8.10-1.0.9",
27
27
  "1.8.22": "1.8.22-1.0.11",
28
28
  "1.9.23": "1.9.23-1.0.20",
29
- "1.9.24": "1.9.24-1.0.20"
29
+ "1.9.24": "1.9.24-1.0.20",
30
+ "2.0.21": "2.0.21-1.0.27"
30
31
  ]
31
32
 
32
33
  project.rootProject.ext.has("kspVersion")
@@ -50,6 +51,13 @@ ext.applyKotlinExpoModulesCorePlugin = {
50
51
  apply plugin: KotlinExpoModulesCorePlugin
51
52
  }
52
53
 
54
+ // Apply JVM Toolchain version for KSP
55
+ ext.applyKspJvmToolchain = {
56
+ project.ksp {
57
+ kotlin.jvmToolchain(17)
58
+ }
59
+ }
60
+
53
61
  // Setup build options that are common for all modules
54
62
  ext.useDefaultAndroidSdkVersions = {
55
63
  project.android {
@@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
3
3
  apply plugin: 'com.android.library'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '2.0.3'
6
+ version = '2.0.5'
7
7
 
8
8
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
9
9
  apply from: expoModulesCorePlugin
@@ -67,7 +67,7 @@ android {
67
67
  defaultConfig {
68
68
  consumerProguardFiles 'proguard-rules.pro'
69
69
  versionCode 1
70
- versionName "2.0.3"
70
+ versionName "2.0.5"
71
71
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled.toString()
72
72
 
73
73
  testInstrumentationRunner "expo.modules.TestRunner"
@@ -141,6 +141,18 @@ android {
141
141
  }
142
142
  }
143
143
 
144
+ sourceSets {
145
+ main {
146
+ java {
147
+ if (REACT_NATIVE_TARGET_VERSION >= 77) {
148
+ srcDirs += 'src/rn77'
149
+ } else {
150
+ // TODO(kudo,20241112): Remove this when we drop react-native 0.76 support
151
+ srcDirs += 'src/rn74'
152
+ }
153
+ }
154
+ }
155
+ }
144
156
 
145
157
  testOptions {
146
158
  unitTests.includeAndroidResources = true
@@ -144,27 +144,25 @@ void JNIUtils::emitEventOnJavaScriptModule(
144
144
  jni::alias_ref<JavaScriptModuleObject::javaobject> jsiThis,
145
145
  jni::alias_ref<jni::HybridClass<JSIContext>::javaobject> jsiContextRef,
146
146
  jni::alias_ref<jstring> eventName,
147
- jni::alias_ref<react::ReadableNativeMap::javaobject> eventBody
147
+ jni::alias_ref<jni::JMap<jstring, jobject>> eventBody
148
148
  ) {
149
- folly::dynamic arg;
150
- if (eventBody) {
151
- arg = eventBody->cthis()->consume();
152
- }
149
+ auto globalEventBody = jni::make_global(eventBody);
153
150
 
154
151
  JNIUtils::emitEventOnJSIObject(
155
152
  jsiThis->cthis()->getCachedJSIObject(),
156
153
  jsiContextRef,
157
154
  eventName,
158
- [arg = std::move(arg)](jsi::Runtime &rt) -> std::vector<jsi::Value> {
159
- jsi::Value convertedBody = jsi::valueFromDynamic(rt, arg);
160
- std::vector<jsi::Value> args;
161
- args.emplace_back(std::move(convertedBody));
162
- return args;
155
+ [args = std::move(globalEventBody)](jsi::Runtime &rt) -> std::vector<jsi::Value> {
156
+ JNIEnv *env = jni::Environment::current();
157
+
158
+ auto localArgs = jni::static_ref_cast<jni::JMap<jstring, jobject>>(args);
159
+ std::vector<jsi::Value> result;
160
+ result.push_back(convertToJS(env, rt, localArgs));
161
+ return result;
163
162
  }
164
163
  );
165
164
  }
166
165
 
167
-
168
166
  void JNIUtils::emitEventOnJSIObject(
169
167
  std::weak_ptr<jsi::WeakObject> jsiThis,
170
168
  jni::alias_ref<jni::HybridClass<JSIContext>::javaobject> jsiContextRef,
@@ -57,7 +57,7 @@ public:
57
57
  jni::alias_ref<JavaScriptModuleObject::javaobject> jsiThis,
58
58
  jni::alias_ref<jni::HybridClass<JSIContext>::javaobject> jsiContextRef,
59
59
  jni::alias_ref<jstring> eventName,
60
- jni::alias_ref<react::ReadableNativeMap::javaobject> eventBody
60
+ jni::alias_ref<jni::JMap<jstring, jobject>> eventBody
61
61
  );
62
62
 
63
63
  private:
@@ -4,7 +4,6 @@ import android.os.Bundle
4
4
  import android.view.View
5
5
  import com.facebook.react.bridge.Arguments
6
6
  import com.facebook.react.bridge.ReactApplicationContext
7
- import com.facebook.react.bridge.ReadableNativeMap
8
7
  import com.facebook.react.bridge.WritableMap
9
8
  import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
10
9
  import com.facebook.react.uimanager.UIManagerHelper
@@ -12,7 +11,7 @@ import expo.modules.kotlin.ModuleHolder
12
11
  import expo.modules.kotlin.jni.JNIUtils
13
12
  import expo.modules.kotlin.records.Record
14
13
  import expo.modules.kotlin.types.JSTypeConverter
15
- import expo.modules.kotlin.types.toJSValue
14
+ import expo.modules.kotlin.types.toJSValueExperimental
16
15
  import java.lang.ref.WeakReference
17
16
 
18
17
  /**
@@ -27,25 +26,25 @@ class KModuleEventEmitterWrapper(
27
26
  ) : KEventEmitterWrapper(legacyEventEmitter, reactContextHolder) {
28
27
  override fun emit(eventName: String, eventBody: Bundle?) {
29
28
  checkIfEventWasExported(eventName)
30
- emitNative(eventName, eventBody?.toJSValue(JSTypeConverter.DefaultContainerProvider) as? ReadableNativeMap)
29
+ emitNative(eventName, eventBody?.toJSValueExperimental())
31
30
  }
32
31
 
33
32
  override fun emit(eventName: String, eventBody: WritableMap?) {
34
33
  checkIfEventWasExported(eventName)
35
- emitNative(eventName, eventBody as? ReadableNativeMap)
34
+ emitNative(eventName, eventBody?.toHashMap())
36
35
  }
37
36
 
38
37
  override fun emit(eventName: String, eventBody: Record?) {
39
38
  checkIfEventWasExported(eventName)
40
- emitNative(eventName, eventBody?.toJSValue(JSTypeConverter.DefaultContainerProvider) as? ReadableNativeMap)
39
+ emitNative(eventName, eventBody?.toJSValueExperimental())
41
40
  }
42
41
 
43
42
  override fun emit(eventName: String, eventBody: Map<*, *>?) {
44
43
  checkIfEventWasExported(eventName)
45
- emitNative(eventName, eventBody?.toJSValue(JSTypeConverter.DefaultContainerProvider) as? ReadableNativeMap)
44
+ emitNative(eventName, eventBody?.toJSValueExperimental())
46
45
  }
47
46
 
48
- private fun emitNative(eventName: String, eventBody: ReadableNativeMap?) {
47
+ private fun emitNative(eventName: String, eventBody: Map<String, Any?>?) {
49
48
  val runtimeContext = moduleHolder.module.runtimeContext
50
49
  val jsObject = moduleHolder.safeJSObject ?: return
51
50
  try {
@@ -1,7 +1,5 @@
1
1
  package expo.modules.kotlin.jni
2
2
 
3
- import com.facebook.react.bridge.ReadableNativeMap
4
-
5
3
  @Suppress("KotlinJniMissingFunction")
6
4
  class JNIUtils {
7
5
  companion object {
@@ -26,7 +24,7 @@ class JNIUtils {
26
24
  jsiThis: JavaScriptModuleObject,
27
25
  jsiContext: JSIContext,
28
26
  eventName: String,
29
- eventBody: ReadableNativeMap?
27
+ eventBody: Map<String, Any?>?
30
28
  )
31
29
  }
32
30
  }
@@ -20,6 +20,7 @@ import expo.modules.kotlin.types.LazyKType
20
20
  import expo.modules.kotlin.types.toAnyType
21
21
  import expo.modules.kotlin.views.ViewDefinitionBuilder
22
22
  import expo.modules.kotlin.views.ViewManagerDefinition
23
+ import expo.modules.kotlin.views.decorators.UseCSSProps
23
24
  import kotlin.reflect.KClass
24
25
  import kotlin.reflect.typeOf
25
26
 
@@ -65,6 +66,9 @@ class ModuleDefinitionBuilder(@PublishedApi internal val module: Module? = null)
65
66
  inline fun <reified T : View> View(viewClass: KClass<T>, body: ViewDefinitionBuilder<T>.() -> Unit) {
66
67
  require(viewManagerDefinition == null) { "The module definition may have exported only one view manager." }
67
68
  val viewDefinitionBuilder = ViewDefinitionBuilder(viewClass, LazyKType(classifier = T::class, kTypeProvider = { typeOf<T>() }))
69
+
70
+ viewDefinitionBuilder.UseCSSProps()
71
+
68
72
  body.invoke(viewDefinitionBuilder)
69
73
  viewManagerDefinition = viewDefinitionBuilder.build()
70
74
  }
@@ -20,6 +20,7 @@ open class SharedObject(runtimeContext: RuntimeContext? = null) {
20
20
  internal var sharedObjectId: SharedObjectId = SharedObjectId(0)
21
21
 
22
22
  // Used by JNI
23
+ @DoNotStrip
23
24
  private fun getSharedObjectId(): Int {
24
25
  return sharedObjectId.value
25
26
  }
@@ -18,6 +18,25 @@ import kotlin.reflect.full.memberProperties
18
18
  import kotlin.reflect.full.primaryConstructor
19
19
  import kotlin.reflect.jvm.isAccessible
20
20
 
21
+ fun Record.toJSValueExperimental(): Map<String, Any?> {
22
+ val result = mutableMapOf<String, Any?>()
23
+
24
+ javaClass
25
+ .kotlin
26
+ .memberProperties.map { property ->
27
+ val fieldInformation = property.findAnnotation<Field>() ?: return@map
28
+ val jsKey = fieldInformation.key.takeUnless { it == "" } ?: property.name
29
+
30
+ property.isAccessible = true
31
+
32
+ val value = property.get(this)
33
+ val convertedValue = JSTypeConverter.convertToJSValue(value, useExperimentalConverter = true)
34
+ result[jsKey] = convertedValue
35
+ }
36
+
37
+ return result
38
+ }
39
+
21
40
  fun Record.toJSValue(containerProvider: JSTypeConverter.ContainerProvider): WritableMap {
22
41
  val result = containerProvider.createMap()
23
42
 
@@ -37,6 +56,19 @@ fun Record.toJSValue(containerProvider: JSTypeConverter.ContainerProvider): Writ
37
56
  return result
38
57
  }
39
58
 
59
+ fun Bundle.toJSValueExperimental(): Map<String, Any?> {
60
+ val result = mutableMapOf<String, Any?>()
61
+
62
+ for (key in keySet()) {
63
+ @Suppress("DEPRECATION")
64
+ val value = get(key)
65
+ val convertedValue = JSTypeConverter.convertToJSValue(value, useExperimentalConverter = true)
66
+ result[key] = convertedValue
67
+ }
68
+
69
+ return result
70
+ }
71
+
40
72
  fun Bundle.toJSValue(containerProvider: JSTypeConverter.ContainerProvider): WritableMap {
41
73
  val result = containerProvider.createMap()
42
74
 
@@ -1,8 +1,10 @@
1
1
  package expo.modules.kotlin.views
2
2
 
3
3
  import android.content.Context
4
+ import android.graphics.Canvas
4
5
  import android.widget.LinearLayout
5
6
  import androidx.annotation.UiThread
7
+ import com.facebook.react.uimanager.BackgroundStyleApplicator
6
8
  import expo.modules.kotlin.AppContext
7
9
 
8
10
  /**
@@ -46,4 +48,12 @@ abstract class ExpoView(
46
48
  post(Runnable { measureAndLayout() })
47
49
  }
48
50
  }
51
+
52
+ override fun dispatchDraw(canvas: Canvas) {
53
+ // When the border radius is set, we need to clip the content to the padding box.
54
+ // This is because the border radius is applied to the background drawable, not the view itself.
55
+ // It is the same behavior as in React Native.
56
+ BackgroundStyleApplicator.clipToPaddingBox(this, canvas)
57
+ super.dispatchDraw(canvas)
58
+ }
49
59
  }
@@ -41,7 +41,7 @@ class FilteredReadableMap(
41
41
  private val backingMap: ReadableMap,
42
42
  private val filteredKeys: List<String>
43
43
  ) : ReadableMap by backingMap {
44
- override val entryIterator: Iterator<Map.Entry<String, Any>> =
44
+ override val entryIterator =
45
45
  FilteredIterator(backingMap.entryIterator) {
46
46
  !filteredKeys.contains(it.key)
47
47
  }
@@ -8,6 +8,9 @@ import android.view.View
8
8
  import android.view.ViewGroup
9
9
  import expo.modules.kotlin.AppContext
10
10
  import expo.modules.kotlin.Promise
11
+ import expo.modules.kotlin.component6
12
+ import expo.modules.kotlin.component7
13
+ import expo.modules.kotlin.component8
11
14
  import expo.modules.kotlin.exception.CodedException
12
15
  import expo.modules.kotlin.exception.UnexpectedException
13
16
  import expo.modules.kotlin.functions.AsyncFunction
@@ -16,14 +19,11 @@ import expo.modules.kotlin.functions.AsyncFunctionWithPromiseComponent
16
19
  import expo.modules.kotlin.functions.Queues
17
20
  import expo.modules.kotlin.functions.createAsyncFunctionComponent
18
21
  import expo.modules.kotlin.modules.DefinitionMarker
22
+ import expo.modules.kotlin.types.enforceType
19
23
  import expo.modules.kotlin.types.toAnyType
24
+ import expo.modules.kotlin.types.toArgsArray
20
25
  import kotlin.reflect.KClass
21
26
  import kotlin.reflect.KType
22
- import expo.modules.kotlin.component6
23
- import expo.modules.kotlin.component7
24
- import expo.modules.kotlin.component8
25
- import expo.modules.kotlin.types.enforceType
26
- import expo.modules.kotlin.types.toArgsArray
27
27
 
28
28
  @DefinitionMarker
29
29
  class ViewDefinitionBuilder<T : View>(
@@ -142,6 +142,15 @@ class ViewDefinitionBuilder<T : View>(
142
142
  }
143
143
  }
144
144
 
145
+ inline fun <reified ViewType : View, reified PropType> PropGroup(
146
+ vararg props: String,
147
+ noinline body: (view: ViewType, value: Int, prop: PropType) -> Unit
148
+ ) {
149
+ props.forEachIndexed { index, name ->
150
+ Prop<ViewType, PropType>(name) { view, prop -> body(view, index, prop) }
151
+ }
152
+ }
153
+
145
154
  /**
146
155
  * Defines prop names that should be treated as callbacks.
147
156
  */
@@ -384,39 +393,40 @@ class ViewDefinitionBuilder<T : View>(
384
393
  name: String
385
394
  ) = AsyncFunctionBuilder(name).also { functionBuilders[name] = it }
386
395
 
387
- private fun createViewFactory(): (Context, AppContext) -> View = viewFactory@{ context: Context, appContext: AppContext ->
388
- val fullConstructor = try {
389
- // Try to use constructor with two arguments
390
- viewClass.java.getConstructor(Context::class.java, AppContext::class.java)
391
- } catch (e: NoSuchMethodException) {
392
- null
393
- }
396
+ private fun createViewFactory(): (Context, AppContext) -> View =
397
+ viewFactory@{ context: Context, appContext: AppContext ->
398
+ val fullConstructor = try {
399
+ // Try to use constructor with two arguments
400
+ viewClass.java.getConstructor(Context::class.java, AppContext::class.java)
401
+ } catch (e: NoSuchMethodException) {
402
+ null
403
+ }
394
404
 
395
- fullConstructor?.let {
396
- return@viewFactory try {
397
- it.newInstance(context, appContext)
398
- } catch (e: Throwable) {
399
- handleFailureDuringViewCreation(context, appContext, e)
405
+ fullConstructor?.let {
406
+ return@viewFactory try {
407
+ it.newInstance(context, appContext)
408
+ } catch (e: Throwable) {
409
+ handleFailureDuringViewCreation(context, appContext, e)
410
+ }
400
411
  }
401
- }
402
412
 
403
- val contextConstructor = try {
404
- // Try to use constructor that use Android's context
405
- viewClass.java.getConstructor(Context::class.java)
406
- } catch (e: NoSuchMethodException) {
407
- null
408
- }
413
+ val contextConstructor = try {
414
+ // Try to use constructor that use Android's context
415
+ viewClass.java.getConstructor(Context::class.java)
416
+ } catch (e: NoSuchMethodException) {
417
+ null
418
+ }
409
419
 
410
- contextConstructor?.let {
411
- return@viewFactory try {
412
- it.newInstance(context)
413
- } catch (e: Throwable) {
414
- handleFailureDuringViewCreation(context, appContext, e)
420
+ contextConstructor?.let {
421
+ return@viewFactory try {
422
+ it.newInstance(context)
423
+ } catch (e: Throwable) {
424
+ handleFailureDuringViewCreation(context, appContext, e)
425
+ }
415
426
  }
416
- }
417
427
 
418
- throw IllegalStateException("Didn't find a correct constructor for $viewClass")
419
- }
428
+ throw IllegalStateException("Didn't find a correct constructor for $viewClass")
429
+ }
420
430
 
421
431
  private fun handleFailureDuringViewCreation(context: Context, appContext: AppContext, e: Throwable): View {
422
432
  Log.e("ExpoModulesCore", "Couldn't create view of type $viewClass", e)
@@ -0,0 +1,174 @@
1
+ @file:Suppress("FunctionName")
2
+
3
+ package expo.modules.kotlin.views.decorators
4
+
5
+ import android.view.View
6
+ import com.facebook.react.bridge.ReadableArray
7
+ import com.facebook.react.uimanager.BackgroundStyleApplicator
8
+ import com.facebook.react.uimanager.LengthPercentage
9
+ import com.facebook.react.uimanager.LengthPercentageType
10
+ import com.facebook.react.uimanager.Spacing
11
+ import com.facebook.react.uimanager.ViewProps
12
+ import com.facebook.react.uimanager.style.BorderRadiusProp
13
+ import com.facebook.react.uimanager.style.BorderStyle
14
+ import com.facebook.react.uimanager.style.BoxShadow
15
+ import com.facebook.react.uimanager.style.LogicalEdge
16
+ import expo.modules.kotlin.types.enforceType
17
+ import expo.modules.kotlin.views.ViewDefinitionBuilder
18
+
19
+ inline fun <reified T : View> ViewDefinitionBuilder<T>.UseBorderColorProps(crossinline body: (view: T, edge: LogicalEdge, color: Int?) -> Unit) {
20
+ PropGroup(
21
+ ViewProps.BORDER_COLOR to Spacing.ALL,
22
+ ViewProps.BORDER_LEFT_COLOR to Spacing.LEFT,
23
+ ViewProps.BORDER_RIGHT_COLOR to Spacing.RIGHT,
24
+ ViewProps.BORDER_TOP_COLOR to Spacing.TOP,
25
+ ViewProps.BORDER_BOTTOM_COLOR to Spacing.BOTTOM,
26
+ ViewProps.BORDER_START_COLOR to Spacing.START,
27
+ ViewProps.BORDER_END_COLOR to Spacing.END,
28
+ ViewProps.BORDER_BLOCK_COLOR to Spacing.BLOCK,
29
+ ViewProps.BORDER_BLOCK_END_COLOR to Spacing.BLOCK_END,
30
+ ViewProps.BORDER_BLOCK_START_COLOR to Spacing.BLOCK_START
31
+ ) { view: T, spacing: Int, color: Int? ->
32
+ body(view, LogicalEdge.fromSpacingType(spacing), color)
33
+ }
34
+ }
35
+
36
+ private fun <T : View> ViewDefinitionBuilder<T>.UseBorderColorProps() {
37
+ enforceType<ViewDefinitionBuilder<View>>(this)
38
+ UseBorderColorProps { view: View, edge: LogicalEdge, color: Int? ->
39
+ BackgroundStyleApplicator.setBorderColor(
40
+ view,
41
+ edge,
42
+ color
43
+ )
44
+ }
45
+ }
46
+
47
+ inline fun <reified T : View> ViewDefinitionBuilder<T>.UseBorderWidthProps(crossinline body: (view: T, edge: LogicalEdge, width: Float?) -> Unit) {
48
+ PropGroup(
49
+ ViewProps.BORDER_WIDTH,
50
+ ViewProps.BORDER_LEFT_WIDTH,
51
+ ViewProps.BORDER_RIGHT_WIDTH,
52
+ ViewProps.BORDER_TOP_WIDTH,
53
+ ViewProps.BORDER_BOTTOM_WIDTH,
54
+ ViewProps.BORDER_START_WIDTH,
55
+ ViewProps.BORDER_END_WIDTH
56
+ ) { view: T, index: Int, width: Float? ->
57
+ body(view, LogicalEdge.entries[index], width)
58
+ }
59
+ }
60
+
61
+ private fun <T : View> ViewDefinitionBuilder<T>.UseBorderWidthProps() {
62
+ enforceType<ViewDefinitionBuilder<View>>(this)
63
+ UseBorderWidthProps { view, edge, width ->
64
+ BackgroundStyleApplicator.setBorderWidth(
65
+ view,
66
+ edge,
67
+ width ?: Float.NaN
68
+ )
69
+ }
70
+ }
71
+
72
+ inline fun <reified T : View> ViewDefinitionBuilder<T>.UseBorderRadiusProps(crossinline body: (view: T, border: BorderRadiusProp, radius: LengthPercentage?) -> Unit) {
73
+ PropGroup(
74
+ ViewProps.BORDER_RADIUS,
75
+ ViewProps.BORDER_TOP_LEFT_RADIUS,
76
+ ViewProps.BORDER_TOP_RIGHT_RADIUS,
77
+ ViewProps.BORDER_BOTTOM_RIGHT_RADIUS,
78
+ ViewProps.BORDER_BOTTOM_LEFT_RADIUS,
79
+ ViewProps.BORDER_TOP_START_RADIUS,
80
+ ViewProps.BORDER_TOP_END_RADIUS,
81
+ ViewProps.BORDER_BOTTOM_START_RADIUS,
82
+ ViewProps.BORDER_BOTTOM_END_RADIUS,
83
+ ViewProps.BORDER_END_END_RADIUS,
84
+ ViewProps.BORDER_END_START_RADIUS,
85
+ ViewProps.BORDER_START_END_RADIUS,
86
+ ViewProps.BORDER_START_START_RADIUS
87
+ ) { view: T, index: Int, radius: Float? ->
88
+ body(
89
+ view,
90
+ BorderRadiusProp.entries[index],
91
+ radius?.let { LengthPercentage(it, LengthPercentageType.POINT) }
92
+ )
93
+ }
94
+ }
95
+
96
+ private fun <T : View> ViewDefinitionBuilder<T>.UseBorderRadiusProps() {
97
+ enforceType<ViewDefinitionBuilder<View>>(this)
98
+ UseBorderRadiusProps { view, border, radius ->
99
+ BackgroundStyleApplicator.setBorderRadius(
100
+ view,
101
+ border,
102
+ radius
103
+ )
104
+ }
105
+ }
106
+
107
+ inline fun <reified T : View> ViewDefinitionBuilder<T>.UseBorderStyleProp(crossinline body: (view: T, style: BorderStyle?) -> Unit) {
108
+ Prop("borderStyle") { view: T, style: String? ->
109
+ val parsedBorderStyle = style?.let { BorderStyle.fromString(style) }
110
+ body(view, parsedBorderStyle)
111
+ }
112
+ }
113
+
114
+ private fun <T : View> ViewDefinitionBuilder<T>.UseBorderStyleProp() {
115
+ enforceType<ViewDefinitionBuilder<View>>(this)
116
+ UseBorderStyleProp { view, style ->
117
+ BackgroundStyleApplicator.setBorderStyle(
118
+ view,
119
+ style
120
+ )
121
+ }
122
+ }
123
+
124
+ inline fun <reified T : View> ViewDefinitionBuilder<T>.UseBackgroundProp(crossinline body: (view: T, color: Int?) -> Unit) {
125
+ Prop(ViewProps.BACKGROUND_COLOR) { view: T, color: Int? ->
126
+ body(view, color)
127
+ }
128
+ }
129
+
130
+ private fun <T : View> ViewDefinitionBuilder<T>.UseBackgroundProp() {
131
+ enforceType<ViewDefinitionBuilder<View>>(this)
132
+ UseBackgroundProp { view, color ->
133
+ BackgroundStyleApplicator.setBackgroundColor(view, color)
134
+ }
135
+ }
136
+
137
+ inline fun <reified T : View> ViewDefinitionBuilder<T>.UseBoxShadowProp(crossinline body: (view: T, shadow: List<BoxShadow>) -> Unit) {
138
+ Prop(ViewProps.BOX_SHADOW) { view: T, shadows: ReadableArray? ->
139
+ if (shadows == null) {
140
+ body(view, emptyList())
141
+ return@Prop
142
+ }
143
+
144
+ val shadowStyle = mutableListOf<BoxShadow>()
145
+ for (i in 0..<shadows.size()) {
146
+ val shadow = BoxShadow.parse(shadows.getMap(i)) ?: continue
147
+ shadowStyle.add((shadow))
148
+ }
149
+ body(view, shadowStyle)
150
+ }
151
+ }
152
+
153
+ private fun <T : View> ViewDefinitionBuilder<T>.UseBoxShadowProp() {
154
+ enforceType<ViewDefinitionBuilder<View>>(this)
155
+ UseBoxShadowProp { view, shadows ->
156
+ BackgroundStyleApplicator.setBoxShadow(view, shadows)
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Decorates the view definition builder with CSS props.
162
+ * This includes border, background, and box shadow properties.
163
+ */
164
+ @PublishedApi
165
+ internal fun <T : View> ViewDefinitionBuilder<T>.UseCSSProps() {
166
+ UseBorderColorProps()
167
+ UseBorderWidthProps()
168
+ UseBorderRadiusProps()
169
+ UseBorderStyleProp()
170
+
171
+ UseBackgroundProp()
172
+
173
+ UseBoxShadowProp()
174
+ }
@@ -0,0 +1,14 @@
1
+ @file:Suppress("DEPRECATION")
2
+
3
+ package expo.modules.rncompatibility
4
+
5
+ import com.facebook.react.config.ReactFeatureFlags
6
+
7
+ /**
8
+ * A compatibility helper of
9
+ * `com.facebook.react.config.ReactFeatureFlags` and
10
+ * `com.facebook.react.internal.featureflags.ReactNativeFeatureFlags`
11
+ */
12
+ object ReactNativeFeatureFlags {
13
+ val enableBridgelessArchitecture = ReactFeatureFlags.enableBridgelessArchitecture
14
+ }
@@ -0,0 +1,12 @@
1
+ package expo.modules.rncompatibility
2
+
3
+ import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
4
+
5
+ /**
6
+ * A compatibility helper of
7
+ * `com.facebook.react.config.ReactFeatureFlags` and
8
+ * `com.facebook.react.internal.featureflags.ReactNativeFeatureFlags`
9
+ */
10
+ object ReactNativeFeatureFlags {
11
+ val enableBridgelessArchitecture = ReactNativeFeatureFlags.enableBridgelessArchitecture()
12
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"NativeViewManagerAdapter.native.d.ts","sourceRoot":"","sources":["../src/NativeViewManagerAdapter.native.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAC;AAuD1B;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAmDpF"}
1
+ {"version":3,"file":"NativeViewManagerAdapter.native.d.ts","sourceRoot":"","sources":["../src/NativeViewManagerAdapter.native.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAC;AAuD1B;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAqDpF"}
@@ -70,6 +70,10 @@ declare global {
70
70
  */
71
71
  var expo: ExpoGlobal;
72
72
  var process: ExpoProcess;
73
+ /**
74
+ * ExpoDomWebView is defined in `@expo/dom-webview` runtime.
75
+ */
76
+ var ExpoDomWebView: Record<string, any> | undefined;
73
77
  }
74
78
  export {};
75
79
  //# sourceMappingURL=global.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"global.d.ts","sourceRoot":"","sources":["../../src/ts-declarations/global.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAI7B;;OAEG;IACH,YAAY,EAAE,OAAO,YAAY,CAAC;IAElC;;OAEG;IACH,YAAY,EAAE,OAAO,YAAY,CAAC;IAElC;;OAEG;IACH,SAAS,EAAE,OAAO,SAAS,CAAC;IAE5B;;OAEG;IACH,YAAY,EAAE,OAAO,YAAY,CAAC;IAIlC;;OAEG;IACH,MAAM,IAAI,MAAM,CAAC;IAEjB;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAEhD;;;OAGG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;IAEnD;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAED,KAAK,UAAU,GAAG;IAChB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACrC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChE,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE;QACH,QAAQ,EAAE,MAAM,CAAC;QACjB,qCAAqC;QACrC,sBAAsB,CAAC,EAAE,MAAM,CAAC;QAEhC,uBAAuB,CAAC,EAAE,MAAM,CAAC;QACjC,wBAAwB,CAAC,EAAE,MAAM,CAAC;QAClC,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAE9B,gKAAgK;QAChK,aAAa,CAAC,EAAE,MAAM,CAAC;QAEvB,sKAAsK;QACtK,OAAO,CAAC,EAAE,MAAM,CAAC;QAEjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAID,OAAO,CAAC,MAAM,CAAC;IACb;;;OAGG;IACH,IAAI,IAAI,EAAE,UAAU,CAAC;IAErB,IAAI,OAAO,EAAE,WAAW,CAAC;CAC1B"}
1
+ {"version":3,"file":"global.d.ts","sourceRoot":"","sources":["../../src/ts-declarations/global.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAI7B;;OAEG;IACH,YAAY,EAAE,OAAO,YAAY,CAAC;IAElC;;OAEG;IACH,YAAY,EAAE,OAAO,YAAY,CAAC;IAElC;;OAEG;IACH,SAAS,EAAE,OAAO,SAAS,CAAC;IAE5B;;OAEG;IACH,YAAY,EAAE,OAAO,YAAY,CAAC;IAIlC;;OAEG;IACH,MAAM,IAAI,MAAM,CAAC;IAEjB;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAEhD;;;OAGG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;IAEnD;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAED,KAAK,UAAU,GAAG;IAChB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACrC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChE,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE;QACH,QAAQ,EAAE,MAAM,CAAC;QACjB,qCAAqC;QACrC,sBAAsB,CAAC,EAAE,MAAM,CAAC;QAEhC,uBAAuB,CAAC,EAAE,MAAM,CAAC;QACjC,wBAAwB,CAAC,EAAE,MAAM,CAAC;QAClC,oBAAoB,CAAC,EAAE,MAAM,CAAC;QAE9B,gKAAgK;QAChK,aAAa,CAAC,EAAE,MAAM,CAAC;QAEvB,sKAAsK;QACtK,OAAO,CAAC,EAAE,MAAM,CAAC;QAEjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAID,OAAO,CAAC,MAAM,CAAC;IACb;;;OAGG;IACH,IAAI,IAAI,EAAE,UAAU,CAAC;IAErB,IAAI,OAAO,EAAE,WAAW,CAAC;IAEzB;;OAEG;IACH,IAAI,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;CACrD"}
@@ -1,2 +1,3 @@
1
+ export * from '../ts-declarations/global';
1
2
  export declare function registerWebGlobals(): void;
2
3
  //# sourceMappingURL=index.web.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../src/web/index.web.ts"],"names":[],"mappings":"AAGA,wBAAgB,kBAAkB,SAiBjC"}
1
+ {"version":3,"file":"index.web.d.ts","sourceRoot":"","sources":["../../src/web/index.web.ts"],"names":[],"mappings":"AAKA,cAAc,2BAA2B,CAAC;AAE1C,wBAAgB,kBAAkB,SAiBjC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-core",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "description": "The core of Expo Modules architecture",
5
5
  "main": "src/index.ts",
6
6
  "types": "build/index.d.ts",
@@ -44,5 +44,5 @@
44
44
  "@testing-library/react-native": "^12.5.2",
45
45
  "expo-module-scripts": "^4.0.0"
46
46
  },
47
- "gitHead": "b7b95a93855cb4863b626c4524fc6e8b3a2305eb"
47
+ "gitHead": "6cbc7c671f769d36e4294f25a9445281912e45fd"
48
48
  }
@@ -3,7 +3,7 @@
3
3
  'use client';
4
4
 
5
5
  import React from 'react';
6
- import { findNodeHandle, NativeModules, HostComponent } from 'react-native';
6
+ import { NativeModules, NativeMethods, HostComponent, findNodeHandle } from 'react-native';
7
7
  import * as NativeComponentRegistry from 'react-native/Libraries/NativeComponent/NativeComponentRegistry';
8
8
 
9
9
  import { requireNativeModule } from './requireNativeModule';
@@ -81,16 +81,18 @@ export function requireNativeViewManager<P>(viewName: string): React.ComponentTy
81
81
  class NativeComponent extends React.PureComponent<P> {
82
82
  static displayName = viewName;
83
83
 
84
+ nativeRef = React.createRef<React.Component & NativeMethods>();
85
+
84
86
  // This will be accessed from native when the prototype functions are called,
85
87
  // in order to find the associated native view.
86
88
  nativeTag: number | null = null;
87
89
 
88
90
  componentDidMount(): void {
89
- this.nativeTag = findNodeHandle(this);
91
+ this.nativeTag = findNodeHandle(this.nativeRef.current);
90
92
  }
91
93
 
92
94
  render(): React.ReactNode {
93
- return <ReactNativeComponent {...this.props} />;
95
+ return <ReactNativeComponent {...this.props} ref={this.nativeRef} />;
94
96
  }
95
97
  }
96
98
 
@@ -91,4 +91,9 @@ declare global {
91
91
  var expo: ExpoGlobal;
92
92
 
93
93
  var process: ExpoProcess;
94
+
95
+ /**
96
+ * ExpoDomWebView is defined in `@expo/dom-webview` runtime.
97
+ */
98
+ var ExpoDomWebView: Record<string, any> | undefined;
94
99
  }
@@ -1,6 +1,10 @@
1
1
  import { EventEmitter, NativeModule, SharedObject, SharedRef } from './CoreModule';
2
2
  import uuid from '../uuid';
3
3
 
4
+ // jest-expo imports to this file directly without going through the global types
5
+ // Exporting the types to let jest-expo to know the globalThis types
6
+ export * from '../ts-declarations/global';
7
+
4
8
  export function registerWebGlobals() {
5
9
  if (globalThis.expo) return;
6
10
  globalThis.expo = {