expo-modules-core 58.0.10 → 58.0.12

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 (25) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/android/build.gradle +3 -3
  3. package/android/src/compose/expo/modules/kotlin/views/ExpoComposeView.kt +0 -10
  4. package/android/src/main/java/expo/modules/kotlin/views/ExpoView.kt +15 -1
  5. package/android/src/main/java/expo/modules/v2/ExpoModulesV2Host.kt +1 -1
  6. package/build/NativeViewManagerAdapter.native.js +46 -1
  7. package/build/NativeViewManagerAdapter.native.js.map +1 -1
  8. package/common/cpp/SharedObject.cpp +7 -2
  9. package/expo-module-gradle-plugin/build.gradle.kts +1 -1
  10. package/expo-module-gradle-plugin/src/main/kotlin/expo/modules/plugin/android/MavenPublicationExtension.kt +1 -1
  11. package/ios/Core/ArrayBuffers/ArrayBuffer.swift +5 -0
  12. package/ios/Core/Coding/JavaScriptCodable+Enumerable.swift +15 -0
  13. package/ios/Core/Coding/JavaScriptCodable+Record.swift +19 -3
  14. package/ios/Core/Coding/JavaScriptCodable+SharedObject.swift +5 -0
  15. package/ios/Core/Coding/JavaScriptCodable+TypedArray.swift +5 -0
  16. package/ios/Core/ExpoModulesMacros.swift +1 -1
  17. package/ios/Core/SharedObjects/SharedObject.swift +3 -3
  18. package/ios/Core/SharedObjects/SharedObjectNativeState.swift +8 -0
  19. package/ios/Core/SharedObjects/SharedObjectRegistry.swift +14 -3
  20. package/package.json +4 -4
  21. package/prebuilds/output/debug/xcframeworks/ExpoModulesCore.tar.gz +0 -0
  22. package/prebuilds/output/debug/xcframeworks/ExpoModulesWorklets.tar.gz +0 -0
  23. package/prebuilds/output/release/xcframeworks/ExpoModulesCore.tar.gz +0 -0
  24. package/prebuilds/output/release/xcframeworks/ExpoModulesWorklets.tar.gz +0 -0
  25. package/src/NativeViewManagerAdapter.native.tsx +81 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 58.0.12
4
+
5
+ ### Patch Changes
6
+
7
+ - [Internal][Android] `expoPublish` now formats the updated `expo-module.config.json` with `oxfmt` instead of `prettier`. ([#47438](https://github.com/expo/expo/pull/47438) by [@hassankhan](https://github.com/hassankhan))
8
+ - [iOS] Add `decodableKinds` to `JavaScriptDecodable`: the kinds of JavaScript value (`JavaScriptValueKinds`) that `decode` can accept, so code that picks between several types can skip the ones that can't match. ([#50905](https://github.com/expo/expo/pull/50905) by [@tsapeta](https://github.com/tsapeta))
9
+ - [iOS] Add `JavaScriptValue.withUnownedValue(in:_:)`, and give the owning `JavaScriptDecodable.decode` a default that borrows the value and decodes it through the `JavaScriptUnownedValue` overload, so a conformer can implement only that one. Arrays, dictionaries, dates, records and enums now decode unowned values without copying them first. ([#50960](https://github.com/expo/expo/pull/50960) by [@tsapeta](https://github.com/tsapeta))
10
+ - Fixed native views ignoring `aria-*`, `id` and `tabIndex` props such as `aria-hidden` and `aria-label` ([#50782](https://github.com/expo/expo/issues/50782)). ([#50959](https://github.com/expo/expo/pull/50959) by [@alanjhughes](https://github.com/alanjhughes))
11
+ - Fixed `release()` throwing on a shared object whose JS object is frozen, for example an `ImageRef` passed as a view prop in development ([#50962](https://github.com/expo/expo/issues/50962)). ([#50970](https://github.com/expo/expo/pull/50970) by [@tsapeta](https://github.com/tsapeta))
12
+ - Updated dependencies. ([#50905](https://github.com/expo/expo/pull/50905), [#50698](https://github.com/expo/expo/pull/50698), [#50937](https://github.com/expo/expo/pull/50937), [#50888](https://github.com/expo/expo/pull/50888), [#50960](https://github.com/expo/expo/pull/50960), [#50980](https://github.com/expo/expo/pull/50980), [#50354](https://github.com/expo/expo/pull/50354))
13
+ - expo-modules-jsi@58.0.8
14
+
15
+ ## 58.0.11
16
+
17
+ ### Patch Changes
18
+
19
+ - 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))
20
+ - [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))
21
+ - [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))
22
+ - Updated dependencies. ([#50881](https://github.com/expo/expo/pull/50881), [#50660](https://github.com/expo/expo/pull/50660))
23
+ - expo-modules-jsi@58.0.7
24
+
3
25
  ## 58.0.10
4
26
 
5
27
  ### Patch Changes
@@ -46,7 +46,7 @@ if (shouldIncludeCompose) {
46
46
  }
47
47
 
48
48
  group = 'host.exp.exponent'
49
- version = '58.0.10'
49
+ version = '58.0.12'
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.0"
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.10"
178
+ versionName "58.0.12"
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
  *
@@ -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 [requestLayout].
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.expoV2")
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
  }
@@ -84,6 +84,51 @@ import { requireNativeModule } from './requireNativeModule';
84
84
  }
85
85
  return cachedNativeComponent;
86
86
  }
87
+ /**
88
+ * Maps `aria-*`, `id` and `tabIndex` props to native props.
89
+ * Mirrors `Libraries/Components/View/View.js` in React Native 0.88.
90
+ */ function mapAriaProps(props) {
91
+ const { accessibilityState, accessibilityValue, 'aria-busy': ariaBusy, 'aria-checked': ariaChecked, 'aria-disabled': ariaDisabled, 'aria-expanded': ariaExpanded, 'aria-hidden': ariaHidden, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-live': ariaLive, 'aria-selected': ariaSelected, 'aria-valuemax': ariaValueMax, 'aria-valuemin': ariaValueMin, 'aria-valuenow': ariaValueNow, 'aria-valuetext': ariaValueText, id, tabIndex, ...resolvedProps } = props;
92
+ if (ariaLabelledBy !== undefined) {
93
+ resolvedProps.accessibilityLabelledBy = ariaLabelledBy.split(/\s*,\s*/g);
94
+ }
95
+ if (ariaLabel !== undefined) {
96
+ resolvedProps.accessibilityLabel = ariaLabel;
97
+ }
98
+ if (ariaLive !== undefined) {
99
+ resolvedProps.accessibilityLiveRegion = ariaLive === 'off' ? 'none' : ariaLive;
100
+ }
101
+ if (ariaHidden !== undefined) {
102
+ resolvedProps.accessibilityElementsHidden = ariaHidden;
103
+ if (ariaHidden === true) {
104
+ resolvedProps.importantForAccessibility = 'no-hide-descendants';
105
+ }
106
+ }
107
+ if (id !== undefined) {
108
+ resolvedProps.nativeID = id;
109
+ }
110
+ if (tabIndex !== undefined) {
111
+ resolvedProps.focusable = !tabIndex;
112
+ }
113
+ if (accessibilityState != null || ariaBusy != null || ariaChecked != null || ariaDisabled != null || ariaExpanded != null || ariaSelected != null) {
114
+ resolvedProps.accessibilityState = {
115
+ busy: ariaBusy ?? accessibilityState?.busy,
116
+ checked: ariaChecked ?? accessibilityState?.checked,
117
+ disabled: ariaDisabled ?? accessibilityState?.disabled,
118
+ expanded: ariaExpanded ?? accessibilityState?.expanded,
119
+ selected: ariaSelected ?? accessibilityState?.selected
120
+ };
121
+ }
122
+ if (accessibilityValue != null || ariaValueMax != null || ariaValueMin != null || ariaValueNow != null || ariaValueText != null) {
123
+ resolvedProps.accessibilityValue = {
124
+ max: ariaValueMax ?? accessibilityValue?.max,
125
+ min: ariaValueMin ?? accessibilityValue?.min,
126
+ now: ariaValueNow ?? accessibilityValue?.now,
127
+ text: ariaValueText ?? accessibilityValue?.text
128
+ };
129
+ }
130
+ return resolvedProps;
131
+ }
87
132
  /**
88
133
  * A drop-in replacement for `requireNativeComponent`.
89
134
  */ export function requireNativeViewManager(moduleName, viewName) {
@@ -104,7 +149,7 @@ import { requireNativeModule } from './requireNativeModule';
104
149
  }
105
150
  render() {
106
151
  return /*#__PURE__*/ _jsx(ReactNativeComponent, {
107
- ...this.props,
152
+ ...mapAriaProps(this.props),
108
153
  ref: this.nativeRef
109
154
  });
110
155
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["NativeViewManagerAdapter.native.tsx"],"sourcesContent":[null],"names":["createRef","PureComponent","findNodeHandle","get","componentRegistryGet","SharedObject","requireNativeModule","nativeComponentsCache","Map","requireNativeComponent","moduleName","viewName","appIdentifier","globalThis","expo","viewNameSuffix","nativeViewName","expoViewConfig","getViewConfig","console","warn","uiViewClassName","directEventTypes","validAttributes","addAttributeProcessing","processPropValue","value","sharedObjectId","__expo_shared_object_id__","descriptor","process","attributes","key","Object","keys","requireCachedNativeComponent","cacheKey","cachedNativeComponent","nativeComponent","set","requireNativeViewManager","ReactNativeComponent","NativeComponent","displayName","nativeRef","nativeTag","componentDidMount","current","getNativeRef","render","props","ref","nativeModule","nativeViewPrototype","ViewPrototypes","assign","prototype"],"mappings":"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"}
1
+ {"version":3,"sources":["NativeViewManagerAdapter.native.tsx"],"sourcesContent":[null],"names":["createRef","PureComponent","findNodeHandle","get","componentRegistryGet","SharedObject","requireNativeModule","nativeComponentsCache","Map","requireNativeComponent","moduleName","viewName","appIdentifier","globalThis","expo","viewNameSuffix","nativeViewName","expoViewConfig","getViewConfig","console","warn","uiViewClassName","directEventTypes","validAttributes","addAttributeProcessing","processPropValue","value","sharedObjectId","__expo_shared_object_id__","descriptor","process","attributes","key","Object","keys","requireCachedNativeComponent","cacheKey","cachedNativeComponent","nativeComponent","set","mapAriaProps","props","accessibilityState","accessibilityValue","ariaBusy","ariaChecked","ariaDisabled","ariaExpanded","ariaHidden","ariaLabel","ariaLabelledBy","ariaLive","ariaSelected","ariaValueMax","ariaValueMin","ariaValueNow","ariaValueText","id","tabIndex","resolvedProps","undefined","accessibilityLabelledBy","split","accessibilityLabel","accessibilityLiveRegion","accessibilityElementsHidden","importantForAccessibility","nativeID","focusable","busy","checked","disabled","expanded","selected","max","min","now","text","requireNativeViewManager","ReactNativeComponent","NativeComponent","displayName","nativeRef","nativeTag","componentDidMount","current","getNativeRef","render","ref","nativeModule","nativeViewPrototype","ViewPrototypes","assign","prototype"],"mappings":"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"}
@@ -38,8 +38,13 @@ void installBaseClass(jsi::Runtime &runtime, const ObjectReleaser& releaser) {
38
38
 
39
39
  releaser(nativeState->objectId);
40
40
 
41
- // Should we reset the native state?
42
- thisObject.setNativeState(runtime, nullptr);
41
+ // Resetting the native state throws when the object is frozen, for example a shared object
42
+ // passed as a view prop, which React Native deep-freezes in development. The native object
43
+ // is already released, so the native state can stay.
44
+ try {
45
+ thisObject.setNativeState(runtime, nullptr);
46
+ } catch (const jsi::JSIException &) {
47
+ }
43
48
  }
44
49
  return jsi::Value::undefined();
45
50
  });
@@ -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.0")
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")
@@ -204,7 +204,7 @@ private fun Project.expoPublishBody(publicationInfo: PublicationInfo, expoModule
204
204
  providers.exec { env ->
205
205
  env.workingDir(layout.projectDirectory.file(".."))
206
206
  // TODO(@lukmccall): support other package managers
207
- env.commandLine("pnpm", "prettier", "--write", "expo-module.config.json")
207
+ env.commandLine("pnpm", "oxfmt", "--write", "expo-module.config.json")
208
208
  }.result.get()
209
209
  }
210
210
 
@@ -225,6 +225,11 @@ public final class ArrayBuffer: AnyArrayBuffer, Sendable {
225
225
  }
226
226
 
227
227
  extension ArrayBuffer: JavaScriptDecodable, JavaScriptEncodable {
228
+ @inlinable
229
+ public static var decodableKinds: JavaScriptValueKinds {
230
+ return .object
231
+ }
232
+
228
233
  // MARK: - JavaScriptDecodable
229
234
  @JavaScriptActor
230
235
  @inlinable
@@ -7,6 +7,12 @@ import ExpoModulesJSI
7
7
  // `String` or an integer type), delegating the actual conversion to that raw value's conformance.
8
8
 
9
9
  extension Enumerable where Self: RawRepresentable, RawValue: JavaScriptCodable {
10
+ // An enum decodes through its raw value, so it accepts what the raw value accepts.
11
+ @inlinable
12
+ public static var decodableKinds: JavaScriptValueKinds {
13
+ return RawValue.decodableKinds
14
+ }
15
+
10
16
  @JavaScriptActor
11
17
  @inlinable
12
18
  public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws -> Self
@@ -15,6 +21,15 @@ extension Enumerable where Self: RawRepresentable, RawValue: JavaScriptCodable {
15
21
  return try create(fromRawValue: rawValue)
16
22
  }
17
23
 
24
+ @JavaScriptActor
25
+ @inlinable
26
+ public static func decode(_ value: borrowing JavaScriptUnownedValue, in runtime: borrowing JavaScriptRuntime) throws
27
+ -> Self
28
+ {
29
+ let rawValue = try RawValue.decode(value, in: runtime)
30
+ return try create(fromRawValue: rawValue)
31
+ }
32
+
18
33
  @JavaScriptActor
19
34
  @inlinable
20
35
  public static func encode(_ value: Self, in runtime: borrowing JavaScriptRuntime) throws -> JavaScriptValue {
@@ -14,11 +14,16 @@ import ExpoModulesJSI
14
14
  // context never prepared has no such state, so recovery returns `nil` and the conversion throws
15
15
  // `Exceptions.AppContextNotFound`.
16
16
  //
17
- // Records only implement the owning `decode` overload (the requirement). They walk an object's
18
- // properties regardless, so the zero-copy `JavaScriptUnownedValue` fast path offers nothing here;
19
- // the defaulted overload materializes an owning value and forwards.
17
+ // Both `decode` overloads read the object and hand it to `from(object:appContext:)`. The unowned one
18
+ // reads it straight from the borrowed value, so a record argument isn't copied first.
20
19
 
21
20
  extension Record {
21
+ // A record decodes only from an object.
22
+ @inlinable
23
+ public static var decodableKinds: JavaScriptValueKinds {
24
+ return .object
25
+ }
26
+
22
27
  @JavaScriptActor
23
28
  @inlinable
24
29
  public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws -> Self
@@ -29,6 +34,17 @@ extension Record {
29
34
  return try from(object: value.asObject(), appContext: appContext)
30
35
  }
31
36
 
37
+ @JavaScriptActor
38
+ @inlinable
39
+ public static func decode(_ value: borrowing JavaScriptUnownedValue, in runtime: borrowing JavaScriptRuntime) throws
40
+ -> Self
41
+ {
42
+ guard let appContext = AppContext.from(runtime: runtime) else {
43
+ throw Exceptions.AppContextNotFound()
44
+ }
45
+ return try from(object: value.asObject(in: runtime), appContext: appContext)
46
+ }
47
+
32
48
  @JavaScriptActor
33
49
  @inlinable
34
50
  public static func encode(_ value: Self, in runtime: borrowing JavaScriptRuntime) throws -> JavaScriptValue {
@@ -7,6 +7,11 @@ import ExpoModulesJSI
7
7
  // object's structure like `Record`.
8
8
 
9
9
  extension SharedObject: JavaScriptDecodable, JavaScriptEncodable {
10
+ @inlinable
11
+ public static var decodableKinds: JavaScriptValueKinds {
12
+ return .object
13
+ }
14
+
10
15
  @JavaScriptActor
11
16
  @inlinable
12
17
  public static func decode(_ value: borrowing JavaScriptValue, in runtime: borrowing JavaScriptRuntime) throws -> Self
@@ -8,6 +8,11 @@ import ExpoModulesJSI
8
8
  // `TypedArray.swift` / `ConcreteTypedArrays.swift`.
9
9
 
10
10
  extension TypedArray: JavaScriptDecodable, JavaScriptEncodable {
11
+ @inlinable
12
+ public static var decodableKinds: JavaScriptValueKinds {
13
+ return .object
14
+ }
15
+
11
16
  @JavaScriptActor
12
17
  @inlinable
13
18
  public static func encode(_ value: TypedArray, in runtime: borrowing JavaScriptRuntime) throws -> JavaScriptValue {
@@ -216,7 +216,7 @@ public macro Record() =
216
216
  /// }
217
217
  @attached(
218
218
  member,
219
- names: named(decode), named(encode), named(`as`), named(_payloadTypeName),
219
+ names: named(decode), named(encode), named(decodableKinds), named(`as`), named(_payloadTypeName),
220
220
  named(_assertTypesConformance))
221
221
  @attached(extension, conformances: JavaScriptDecodable, JavaScriptEncodable)
222
222
  public macro Union() =
@@ -119,13 +119,13 @@ extension SharedObject {
119
119
  /// Returns the base `SharedObject`. Callers wanting a concrete subclass use the `as:` overload, which
120
120
  /// performs a checked downcast.
121
121
  ///
122
- /// Throws `NotFoundException` when the object carries no native state.
122
+ /// Throws `NotFoundException` when the object carries no native state or its native object was released.
123
123
  @JavaScriptActor
124
124
  public static func native(from jsObject: borrowing JavaScriptObject) throws -> SharedObject {
125
- guard let native = jsObject.getNativeState(as: SharedObjectNativeState.self)?.native else {
125
+ guard let nativeState = jsObject.getNativeState(as: SharedObjectNativeState.self), !nativeState.isReleased else {
126
126
  throw NotFoundException()
127
127
  }
128
- return native
128
+ return nativeState.native
129
129
  }
130
130
 
131
131
  /// Recovers the native shared object and casts it to the given subclass, e.g.
@@ -13,6 +13,10 @@ import ExpoModulesJSI
13
13
  internal final class SharedObjectNativeState: JavaScriptNativeState {
14
14
  internal let native: SharedObject
15
15
 
16
+ /// Whether the native object was released. A JS object can keep its native state after the release
17
+ /// (for example a frozen one, whose native state can't be reset), so lookups check this flag.
18
+ internal private(set) var isReleased = false
19
+
16
20
  /// The JS object paired with the native object in each runtime, keyed by runtime identity and held
17
21
  /// weakly. One native object can be exposed to several runtimes (e.g. the main and UI runtimes, or
18
22
  /// worklet contexts), each with its own JS counterpart.
@@ -23,6 +27,10 @@ internal final class SharedObjectNativeState: JavaScriptNativeState {
23
27
  super.init(factory: factory)
24
28
  }
25
29
 
30
+ internal func markReleased() {
31
+ isReleased = true
32
+ }
33
+
26
34
  /// Records `jsObject` as the native object's JS counterpart in `runtime`. The caller is responsible
27
35
  /// for having attached this native state to `jsObject` (`jsObject.setNativeState(self)`), which reuses
28
36
  /// the shared C++ pointee across runtimes.
@@ -96,7 +96,8 @@ public final class SharedObjectRegistry: Sendable {
96
96
  // A native object that already carries a native state was paired in an earlier runtime. Reuse its
97
97
  // id rather than minting a new one: the C++ `NativeState.objectId` is immutable and drives the
98
98
  // releaser/`delete`, so a fresh id would disagree with it and leak this object's registry entry.
99
- let id = nativeObject.nativeState != nil ? nativeObject.sharedObjectId : pullNextId()
99
+ let existingNativeState = unreleasedNativeState(of: nativeObject)
100
+ let id = existingNativeState != nil ? nativeObject.sharedObjectId : pullNextId()
100
101
 
101
102
  // Assign the ID and the app context to the object.
102
103
  nativeObject.sharedObjectId = id
@@ -124,7 +125,7 @@ public final class SharedObjectRegistry: Sendable {
124
125
  // Reuse it so all runtimes share one native state (and one underlying C++ pointee, via
125
126
  // `acquireShared`); a fresh state per runtime would leave `nativeObject.nativeState` pointing at
126
127
  // whichever ran last and lose the earlier runtimes' pairings.
127
- let nativeState = nativeObject.nativeState ?? {
128
+ let nativeState = existingNativeState ?? {
128
129
  let releaser: ObjectReleaser = { [weak self] id in
129
130
  self?.delete(id)
130
131
  }
@@ -169,6 +170,7 @@ public final class SharedObjectRegistry: Sendable {
169
170
  state.withLock { state in
170
171
  if let nativeState = state.pairs[id] {
171
172
  let native = nativeState.native
173
+ nativeState.markReleased()
172
174
  native.sharedObjectWillRelease()
173
175
  // Reset an ID on the object.
174
176
  native.sharedObjectId = 0
@@ -230,12 +232,21 @@ public final class SharedObjectRegistry: Sendable {
230
232
  // The id table and the native object both resolve to the same `SharedObjectNativeState`, which
231
233
  // owns the per-runtime JS counterparts, so a single lookup serves both. Prefer the native object's
232
234
  // own back-pointer and fall back to the id table for objects whose back-pointer was cleared.
233
- let nativeState = nativeObject.nativeState ?? state.withLock { state in
235
+ let nativeState = unreleasedNativeState(of: nativeObject) ?? state.withLock { state in
234
236
  return state.pairs[nativeObject.sharedObjectId]
235
237
  }
236
238
  return nativeState?.javaScriptObject(in: runtime)
237
239
  }
238
240
 
241
+ /// Returns the native object's native state, or `nil` if it was released. A released native state can
242
+ /// outlive the release on a JS object that couldn't drop it (for example a frozen one).
243
+ private func unreleasedNativeState(of nativeObject: SharedObject) -> SharedObjectNativeState? {
244
+ guard let nativeState = nativeObject.nativeState, !nativeState.isReleased else {
245
+ return nil
246
+ }
247
+ return nativeState
248
+ }
249
+
239
250
  /**
240
251
  Creates a plain JS object and pairs it with a given native object.
241
252
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-modules-core",
3
- "version": "58.0.10",
3
+ "version": "58.0.12",
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.6",
67
+ "expo-modules-jsi": "~58.0.8",
68
68
  "expo-modules-macros": "0.14.0",
69
69
  "invariant": "^2.2.4"
70
70
  },
71
71
  "devDependencies": {
72
- "@testing-library/react-native": "^13.3.0",
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.4",
76
+ "expo-module-scripts": "56.0.6",
77
77
  "react-dom": "19.3.0"
78
78
  },
79
79
  "peerDependencies": {
@@ -128,6 +128,86 @@ function requireCachedNativeComponent<Props>(
128
128
  return cachedNativeComponent;
129
129
  }
130
130
 
131
+ /**
132
+ * Maps `aria-*`, `id` and `tabIndex` props to native props.
133
+ * Mirrors `Libraries/Components/View/View.js` in React Native 0.88.
134
+ */
135
+ function mapAriaProps(props: Record<string, any>): Record<string, any> {
136
+ const {
137
+ accessibilityState,
138
+ accessibilityValue,
139
+ 'aria-busy': ariaBusy,
140
+ 'aria-checked': ariaChecked,
141
+ 'aria-disabled': ariaDisabled,
142
+ 'aria-expanded': ariaExpanded,
143
+ 'aria-hidden': ariaHidden,
144
+ 'aria-label': ariaLabel,
145
+ 'aria-labelledby': ariaLabelledBy,
146
+ 'aria-live': ariaLive,
147
+ 'aria-selected': ariaSelected,
148
+ 'aria-valuemax': ariaValueMax,
149
+ 'aria-valuemin': ariaValueMin,
150
+ 'aria-valuenow': ariaValueNow,
151
+ 'aria-valuetext': ariaValueText,
152
+ id,
153
+ tabIndex,
154
+ ...resolvedProps
155
+ } = props;
156
+
157
+ if (ariaLabelledBy !== undefined) {
158
+ resolvedProps.accessibilityLabelledBy = ariaLabelledBy.split(/\s*,\s*/g);
159
+ }
160
+ if (ariaLabel !== undefined) {
161
+ resolvedProps.accessibilityLabel = ariaLabel;
162
+ }
163
+ if (ariaLive !== undefined) {
164
+ resolvedProps.accessibilityLiveRegion = ariaLive === 'off' ? 'none' : ariaLive;
165
+ }
166
+ if (ariaHidden !== undefined) {
167
+ resolvedProps.accessibilityElementsHidden = ariaHidden;
168
+ if (ariaHidden === true) {
169
+ resolvedProps.importantForAccessibility = 'no-hide-descendants';
170
+ }
171
+ }
172
+ if (id !== undefined) {
173
+ resolvedProps.nativeID = id;
174
+ }
175
+ if (tabIndex !== undefined) {
176
+ resolvedProps.focusable = !tabIndex;
177
+ }
178
+ if (
179
+ accessibilityState != null ||
180
+ ariaBusy != null ||
181
+ ariaChecked != null ||
182
+ ariaDisabled != null ||
183
+ ariaExpanded != null ||
184
+ ariaSelected != null
185
+ ) {
186
+ resolvedProps.accessibilityState = {
187
+ busy: ariaBusy ?? accessibilityState?.busy,
188
+ checked: ariaChecked ?? accessibilityState?.checked,
189
+ disabled: ariaDisabled ?? accessibilityState?.disabled,
190
+ expanded: ariaExpanded ?? accessibilityState?.expanded,
191
+ selected: ariaSelected ?? accessibilityState?.selected,
192
+ };
193
+ }
194
+ if (
195
+ accessibilityValue != null ||
196
+ ariaValueMax != null ||
197
+ ariaValueMin != null ||
198
+ ariaValueNow != null ||
199
+ ariaValueText != null
200
+ ) {
201
+ resolvedProps.accessibilityValue = {
202
+ max: ariaValueMax ?? accessibilityValue?.max,
203
+ min: ariaValueMin ?? accessibilityValue?.min,
204
+ now: ariaValueNow ?? accessibilityValue?.now,
205
+ text: ariaValueText ?? accessibilityValue?.text,
206
+ };
207
+ }
208
+ return resolvedProps;
209
+ }
210
+
131
211
  /**
132
212
  * A drop-in replacement for `requireNativeComponent`.
133
213
  */
@@ -158,7 +238,7 @@ export function requireNativeViewManager<P>(
158
238
  }
159
239
 
160
240
  render() {
161
- return <ReactNativeComponent {...this.props} ref={this.nativeRef} />;
241
+ return <ReactNativeComponent {...mapAriaProps(this.props)} ref={this.nativeRef} />;
162
242
  }
163
243
  }
164
244