From 3f74a87027307a1b13e519ff0435ab3b975e9d73 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 21 Aug 2025 09:31:30 -0700 Subject: [PATCH 0001/1228] Reland: Remove deprecated DefaultReactHost.getDefaultReactHost() overload - part 1 & 2 Summary: This is a reland of D80622058 + D80623826 + fixes for all the apps. This method was deprecated in React Native 0.79. We should be able to remove it without impact in 0.82. I also verified that there are no users of this API in OSS. There is also a 1:1 replacement for this API which is the other non-deprecated `getDefaultReactHost()` method. bypass-github-export-checks Changelog: [Internal] - Skipping changelog as main already contains a changelog entry Reviewed By: javache Differential Revision: D80704320 fbshipit-source-id: c9aa26b83dbd9f4bf97d0a9e9c6dcaa6eb0afdca --- .../ReactAndroid/api/ReactAndroid.api | 4 - .../react/defaults/DefaultReactHost.kt | 123 ------------------ 2 files changed, 127 deletions(-) diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 935443a962fd..53c415d96532 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -1865,13 +1865,9 @@ public final class com/facebook/react/defaults/DefaultReactHost { public static final fun getDefaultReactHost (Landroid/content/Context;Lcom/facebook/react/ReactNativeHost;Lcom/facebook/react/runtime/JSRuntimeFactory;)Lcom/facebook/react/ReactHost; public static final fun getDefaultReactHost (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/facebook/react/runtime/JSRuntimeFactory;ZLjava/util/List;)Lcom/facebook/react/ReactHost; public static final fun getDefaultReactHost (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/facebook/react/runtime/JSRuntimeFactory;ZLjava/util/List;Lkotlin/jvm/functions/Function1;Lcom/facebook/react/runtime/BindingsInstaller;)Lcom/facebook/react/ReactHost; - public static final fun getDefaultReactHost (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZLjava/util/List;)Lcom/facebook/react/ReactHost; - public static final fun getDefaultReactHost (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZLjava/util/List;Lkotlin/jvm/functions/Function1;Lcom/facebook/react/runtime/BindingsInstaller;)Lcom/facebook/react/ReactHost; public static synthetic fun getDefaultReactHost$default (Landroid/content/Context;Lcom/facebook/react/ReactNativeHost;Lcom/facebook/react/runtime/JSRuntimeFactory;ILjava/lang/Object;)Lcom/facebook/react/ReactHost; public static synthetic fun getDefaultReactHost$default (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/facebook/react/runtime/JSRuntimeFactory;ZLjava/util/List;ILjava/lang/Object;)Lcom/facebook/react/ReactHost; public static synthetic fun getDefaultReactHost$default (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/facebook/react/runtime/JSRuntimeFactory;ZLjava/util/List;Lkotlin/jvm/functions/Function1;Lcom/facebook/react/runtime/BindingsInstaller;ILjava/lang/Object;)Lcom/facebook/react/ReactHost; - public static synthetic fun getDefaultReactHost$default (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZLjava/util/List;ILjava/lang/Object;)Lcom/facebook/react/ReactHost; - public static synthetic fun getDefaultReactHost$default (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZLjava/util/List;Lkotlin/jvm/functions/Function1;Lcom/facebook/react/runtime/BindingsInstaller;ILjava/lang/Object;)Lcom/facebook/react/ReactHost; } public abstract class com/facebook/react/defaults/DefaultReactNativeHost : com/facebook/react/ReactNativeHost { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt index bc11a5791a34..712f29cd03cd 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt @@ -154,129 +154,6 @@ public object DefaultReactHost { return reactHost as ReactHost } - /** - * Util function to create a default [ReactHost] to be used in your application. This method is - * used by the New App template. - * - * @param context the Android [Context] to use for creating the [ReactHost] - * @param packageList the list of [ReactPackage]s to use for creating the [ReactHost] - * @param jsMainModulePath the path to your app's main module on Metro. Usually `index` or - * `index.` - * @param jsBundleAssetPath the path to the JS bundle relative to the assets directory. Will be - * composed in a `asset://...` URL - * @param jsBundleFilePath the path to the JS bundle on the filesystem. Will be composed in a - * `file://...` URL - * @param isHermesEnabled whether to use Hermes as the JS engine, default to true. - * @param useDevSupport whether to enable dev support, default to ReactBuildConfig.DEBUG. - * @param cxxReactPackageProviders a list of cxxreactpackage providers (to register c++ turbo - * modules) - * @param exceptionHandler Callback that can be used by React Native host applications to react to - * exceptions thrown by the internals of React Native. - * @param bindingsInstaller that can be used for installing bindings. - */ - @Deprecated( - message = "Use `getDefaultReactHost` with `jsRuntimeFactory` instead", - replaceWith = - ReplaceWith( - """ - fun getDefaultReactHost( - context: Context, - packageList: List, - jsMainModulePath: String, - jsBundleAssetPath: String, - jsBundleFilePath: String?, - jsRuntimeFactory: JSRuntimeFactory?, - useDevSupport: Boolean, - cxxReactPackageProviders: List<(ReactContext) -> CxxReactPackage>, - exceptionHandler: (Exception) -> Unit, - bindingsInstaller: BindingsInstaller?, - ): ReactHost - """ - ), - ) - @JvmStatic - public fun getDefaultReactHost( - context: Context, - packageList: List, - jsMainModulePath: String = "index", - jsBundleAssetPath: String = "index", - jsBundleFilePath: String? = null, - isHermesEnabled: Boolean = true, - useDevSupport: Boolean = ReactBuildConfig.DEBUG, - cxxReactPackageProviders: List<(ReactContext) -> CxxReactPackage> = emptyList(), - exceptionHandler: (Exception) -> Unit = { throw it }, - bindingsInstaller: BindingsInstaller? = null, - ): ReactHost = - getDefaultReactHost( - context, - packageList, - jsMainModulePath, - jsBundleAssetPath, - jsBundleFilePath, - HermesInstance(), - useDevSupport, - cxxReactPackageProviders, - exceptionHandler, - bindingsInstaller, - ) - - /** - * Util function to create a default [ReactHost] to be used in your application. This method is - * used by the New App template. - * - * @param context the Android [Context] to use for creating the [ReactHost] - * @param packageList the list of [ReactPackage]s to use for creating the [ReactHost] - * @param jsMainModulePath the path to your app's main module on Metro. Usually `index` or - * `index.` - * @param jsBundleAssetPath the path to the JS bundle relative to the assets directory. Will be - * composed in a `asset://...` URL - * @param jsBundleFilePath the path to the JS bundle on the filesystem. Will be composed in a - * `file://...` URL - * @param isHermesEnabled whether to use Hermes as the JS engine, default to true. - * @param useDevSupport whether to enable dev support, default to ReactBuildConfig.DEBUG. - * @param cxxReactPackageProviders a list of cxxreactpackage providers (to register c++ turbo - * modules) - */ - @Deprecated( - message = "Use `getDefaultReactHost` with `jsRuntimeFactory` instead", - replaceWith = - ReplaceWith( - """ - fun getDefaultReactHost( - context: Context, - packageList: List, - jsMainModulePath: String, - jsBundleAssetPath: String, - jsBundleFilePath: String?, - jsRuntimeFactory: JSRuntimeFactory?, - useDevSupport: Boolean, - cxxReactPackageProviders: List<(ReactContext) -> CxxReactPackage>, - ): ReactHost - """ - ), - ) - @JvmStatic - public fun getDefaultReactHost( - context: Context, - packageList: List, - jsMainModulePath: String = "index", - jsBundleAssetPath: String = "index", - jsBundleFilePath: String? = null, - isHermesEnabled: Boolean = true, - useDevSupport: Boolean = ReactBuildConfig.DEBUG, - cxxReactPackageProviders: List<(ReactContext) -> CxxReactPackage> = emptyList(), - ): ReactHost = - getDefaultReactHost( - context, - packageList, - jsMainModulePath, - jsBundleAssetPath, - jsBundleFilePath, - HermesInstance(), - useDevSupport, - cxxReactPackageProviders, - ) - /** * Util function to create a default [ReactHost] to be used in your application. This method is * used by the New App template. From c557311ed836cded8548c5bca32f3eded0abc7ff Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 21 Aug 2025 09:32:21 -0700 Subject: [PATCH 0002/1228] Ship `useNativeEqualsInNativeReadableArrayAndroid` and `useNativeTransformHelperAndroid` to stable (#53372) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53372 We verified that those feature flags are not regressing the experience + we got confirmation from Software Mansion that the fix is effectively mitigating the regression on Android mounting. Hence we can ship this to production. Fixes: https://github.com/facebook/react-native/issues/51869 Changelog: [Android] [Fixed] - Fix mounting is very slow on Android by shipping native transform optimizations Reviewed By: javache Differential Revision: D80624739 fbshipit-source-id: 2e185434f08b5ea0339d59a6ea006017e5abeff2 --- .../featureflags/ReactNativeFeatureFlagsDefaults.kt | 6 +++--- ...FeatureFlagsOverrides_RNOSS_Experimental_Android.kt | 6 +----- .../featureflags/ReactNativeFeatureFlagsDefaults.h | 6 +++--- .../ReactNativeFeatureFlagsOverridesOSSExperimental.h | 10 +--------- .../featureflags/ReactNativeFeatureFlags.config.js | 8 ++++---- .../private/featureflags/ReactNativeFeatureFlags.js | 6 +++--- 6 files changed, 15 insertions(+), 27 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index 68df7d3df200..6ff07b618d9e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<4cc61b5f71805589fa91abbdefb5eade>> + * @generated SignedSource<<69634efaec0f2c63ac2d1ce20dd5c2ac>> */ /** @@ -139,9 +139,9 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun useFabricInterop(): Boolean = true - override fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean = false + override fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean = true - override fun useNativeTransformHelperAndroid(): Boolean = false + override fun useNativeTransformHelperAndroid(): Boolean = true override fun useNativeViewConfigsInBridgelessMode(): Boolean = false diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android.kt index db9f5638f40a..18685fcd6e9c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<1f89971ab4d5b2ee27ace3a3c8da78cf>> + * @generated SignedSource<<4feeb3c1789d8169dd93bee7ae1ff1ac>> */ /** @@ -26,8 +26,4 @@ public open class ReactNativeFeatureFlagsOverrides_RNOSS_Experimental_Android : override fun enableAccessibilityOrder(): Boolean = true override fun preventShadowTreeCommitExhaustion(): Boolean = true - - override fun useNativeEqualsInNativeReadableArrayAndroid(): Boolean = true - - override fun useNativeTransformHelperAndroid(): Boolean = true } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index f8a10ecd3f6b..2ba904e1981c 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<54a695ed4e9c536cd8650498b9dab2fc>> */ /** @@ -260,11 +260,11 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { } bool useNativeEqualsInNativeReadableArrayAndroid() override { - return false; + return true; } bool useNativeTransformHelperAndroid() override { - return false; + return true; } bool useNativeViewConfigsInBridgelessMode() override { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSExperimental.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSExperimental.h index b40aaa717ce6..0d1bd58a85ab 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSExperimental.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsOverridesOSSExperimental.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<77870f0db494c7b6932950a1fa475fdc>> */ /** @@ -34,14 +34,6 @@ class ReactNativeFeatureFlagsOverridesOSSExperimental : public ReactNativeFeatur bool preventShadowTreeCommitExhaustion() override { return true; } - - bool useNativeEqualsInNativeReadableArrayAndroid() override { - return true; - } - - bool useNativeTransformHelperAndroid() override { - return true; - } }; } // namespace facebook::react diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index bcf9dc63dce4..7b1c182a3e72 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -665,7 +665,7 @@ const definitions: FeatureFlagDefinitions = { ossReleaseStage: 'none', }, useNativeEqualsInNativeReadableArrayAndroid: { - defaultValue: false, + defaultValue: true, metadata: { dateAdded: '2025-07-15', description: @@ -673,17 +673,17 @@ const definitions: FeatureFlagDefinitions = { expectedReleaseValue: true, purpose: 'experimentation', }, - ossReleaseStage: 'experimental', + ossReleaseStage: 'stable', }, useNativeTransformHelperAndroid: { - defaultValue: false, + defaultValue: true, metadata: { dateAdded: '2025-07-15', description: 'Use a native implementation of TransformHelper', expectedReleaseValue: true, purpose: 'experimentation', }, - ossReleaseStage: 'experimental', + ossReleaseStage: 'stable', }, useNativeViewConfigsInBridgelessMode: { defaultValue: false, diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index bdd8104971ee..2e11e8838cac 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<66a3905e1c94e7769418becc3bace939>> * @flow strict * @noformat */ @@ -415,11 +415,11 @@ export const useFabricInterop: Getter = createNativeFlagGetter('useFabr /** * Use a native implementation of equals in NativeReadableArray. */ -export const useNativeEqualsInNativeReadableArrayAndroid: Getter = createNativeFlagGetter('useNativeEqualsInNativeReadableArrayAndroid', false); +export const useNativeEqualsInNativeReadableArrayAndroid: Getter = createNativeFlagGetter('useNativeEqualsInNativeReadableArrayAndroid', true); /** * Use a native implementation of TransformHelper */ -export const useNativeTransformHelperAndroid: Getter = createNativeFlagGetter('useNativeTransformHelperAndroid', false); +export const useNativeTransformHelperAndroid: Getter = createNativeFlagGetter('useNativeTransformHelperAndroid', true); /** * When enabled, the native view configs are used in bridgeless mode. */ From c2d96d3a5b731371fa79804b18e567cc5b40ef60 Mon Sep 17 00:00:00 2001 From: Lauren Tan Date: Thu, 21 Aug 2025 09:35:19 -0700 Subject: [PATCH 0003/1228] Add flow suppression for Constant Condition rollout (#34243) Summary: DiffTrain build for [83c7379b9601f25463826449256f0cd3d283702d](https://github.com/facebook/react/commit/83c7379b9601f25463826449256f0cd3d283702d) Reviewed By: marcoww6 Differential Revision: D80661139 fbshipit-source-id: 8ccf2dccba9a07ffd38f50da2c1ded8ebfa78c89 --- .../Libraries/Renderer/shims/ReactNativeViewConfigRegistry.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/Renderer/shims/ReactNativeViewConfigRegistry.js b/packages/react-native/Libraries/Renderer/shims/ReactNativeViewConfigRegistry.js index d1da444187f0..436b74da7674 100644 --- a/packages/react-native/Libraries/Renderer/shims/ReactNativeViewConfigRegistry.js +++ b/packages/react-native/Libraries/Renderer/shims/ReactNativeViewConfigRegistry.js @@ -7,7 +7,7 @@ * @noformat * @nolint * @flow strict-local - * @generated SignedSource<<83073425aa3f71ced2c8c51f25a25938>> + * @generated SignedSource<<1f7876c0dc0b05685a730513dc410236>> */ 'use strict'; @@ -82,6 +82,8 @@ export function register(name: string, callback: () => ViewConfig): string { typeof callback === 'function', 'View config getter callback for component `%s` must be a function (received `%s`)', name, + /* $FlowFixMe[invalid-compare] Error discovered during Constant Condition + * roll out. See https://fburl.com/workplace/5whu3i34. */ callback === null ? 'null' : typeof callback, ); viewConfigCallbacks.set(name, callback); From bcf76e8bb05867f246c7723375173b442590aed7 Mon Sep 17 00:00:00 2001 From: Ruslan Shestopalyuk Date: Thu, 21 Aug 2025 09:47:23 -0700 Subject: [PATCH 0004/1228] Add RN feature flag for ScrollView view recycling (#53373) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53373 # Changelog: [Internal] - Creates a RN feature flag for enabling/disabling view recycling for ScrollView native component on Android. Reviewed By: lenaic Differential Revision: D80625656 fbshipit-source-id: 23eae07512e0ba2ea014404e7afa19db900f7c47 --- .../featureflags/ReactNativeFeatureFlags.kt | 8 +- .../ReactNativeFeatureFlagsCxxAccessor.kt | 12 ++- .../ReactNativeFeatureFlagsCxxInterop.kt | 4 +- .../ReactNativeFeatureFlagsDefaults.kt | 4 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 13 +++- .../ReactNativeFeatureFlagsProvider.kt | 4 +- .../JReactNativeFeatureFlagsCxxInterop.cpp | 16 +++- .../JReactNativeFeatureFlagsCxxInterop.h | 5 +- .../featureflags/ReactNativeFeatureFlags.cpp | 6 +- .../featureflags/ReactNativeFeatureFlags.h | 7 +- .../ReactNativeFeatureFlagsAccessor.cpp | 78 ++++++++++++------- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsDynamicProvider.h | 11 ++- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +- .../NativeReactNativeFeatureFlags.h | 4 +- .../ReactNativeFeatureFlags.config.js | 11 +++ .../featureflags/ReactNativeFeatureFlags.js | 7 +- .../specs/NativeReactNativeFeatureFlags.js | 3 +- 20 files changed, 166 insertions(+), 49 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt index bfd7e672dc38..7995a1db8c20 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<1e8a84a53072fa0c8665aead80d0199f>> */ /** @@ -258,6 +258,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableViewRecycling(): Boolean = accessor.enableViewRecycling() + /** + * Enables View Recycling for via ReactViewGroup/ReactViewManager. + */ + @JvmStatic + public fun enableViewRecyclingForScrollView(): Boolean = accessor.enableViewRecyclingForScrollView() + /** * Enables View Recycling for via ReactTextView/ReactTextViewManager. */ diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt index d5336cbc0a5c..b1c01a45d71a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<51bb91fd70ba266c01bf46b3ca237ad5>> + * @generated SignedSource<> */ /** @@ -58,6 +58,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableResourceTimingAPICache: Boolean? = null private var enableViewCullingCache: Boolean? = null private var enableViewRecyclingCache: Boolean? = null + private var enableViewRecyclingForScrollViewCache: Boolean? = null private var enableViewRecyclingForTextCache: Boolean? = null private var enableViewRecyclingForViewCache: Boolean? = null private var enableVirtualViewDebugFeaturesCache: Boolean? = null @@ -430,6 +431,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableViewRecyclingForScrollView(): Boolean { + var cached = enableViewRecyclingForScrollViewCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableViewRecyclingForScrollView() + enableViewRecyclingForScrollViewCache = cached + } + return cached + } + override fun enableViewRecyclingForText(): Boolean { var cached = enableViewRecyclingForTextCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt index b533e6cc3970..6ce331fd73f3 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<6e384a07f0e7bc237f72b49a1268e16b>> */ /** @@ -104,6 +104,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableViewRecycling(): Boolean + @DoNotStrip @JvmStatic public external fun enableViewRecyclingForScrollView(): Boolean + @DoNotStrip @JvmStatic public external fun enableViewRecyclingForText(): Boolean @DoNotStrip @JvmStatic public external fun enableViewRecyclingForView(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index 6ff07b618d9e..5cea9dbe4725 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<69634efaec0f2c63ac2d1ce20dd5c2ac>> + * @generated SignedSource<<19022bb7e93ed79a1cbe86c71f34bd50>> */ /** @@ -99,6 +99,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableViewRecycling(): Boolean = false + override fun enableViewRecyclingForScrollView(): Boolean = true + override fun enableViewRecyclingForText(): Boolean = true override fun enableViewRecyclingForView(): Boolean = true diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt index f7ebdacae276..b0b54e83625f 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<78c9ee8f53273560de6b75719e317273>> + * @generated SignedSource<<1a19e0569371a038ba8d3849fb26eb5c>> */ /** @@ -62,6 +62,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableResourceTimingAPICache: Boolean? = null private var enableViewCullingCache: Boolean? = null private var enableViewRecyclingCache: Boolean? = null + private var enableViewRecyclingForScrollViewCache: Boolean? = null private var enableViewRecyclingForTextCache: Boolean? = null private var enableViewRecyclingForViewCache: Boolean? = null private var enableVirtualViewDebugFeaturesCache: Boolean? = null @@ -472,6 +473,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun enableViewRecyclingForScrollView(): Boolean { + var cached = enableViewRecyclingForScrollViewCache + if (cached == null) { + cached = currentProvider.enableViewRecyclingForScrollView() + accessedFeatureFlags.add("enableViewRecyclingForScrollView") + enableViewRecyclingForScrollViewCache = cached + } + return cached + } + override fun enableViewRecyclingForText(): Boolean { var cached = enableViewRecyclingForTextCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index afdc5029aece..5c50635fb9e0 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<3db9f160e7221eb9c7660a3e93da0f40>> + * @generated SignedSource<<5f84a9a17209b9ecb95e13e6567e7c3c>> */ /** @@ -99,6 +99,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableViewRecycling(): Boolean + @DoNotStrip public fun enableViewRecyclingForScrollView(): Boolean + @DoNotStrip public fun enableViewRecyclingForText(): Boolean @DoNotStrip public fun enableViewRecyclingForView(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp index 2d568c05d95c..f2d24b7de7e2 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<0dadaee3f6bd74cc13295b8683e8e470>> + * @generated SignedSource<<1eff5bade524e3ad8e827ad4adb37f1a>> */ /** @@ -267,6 +267,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool enableViewRecyclingForScrollView() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableViewRecyclingForScrollView"); + return method(javaProvider_); + } + bool enableViewRecyclingForText() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableViewRecyclingForText"); @@ -635,6 +641,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableViewRecycling( return ReactNativeFeatureFlags::enableViewRecycling(); } +bool JReactNativeFeatureFlagsCxxInterop::enableViewRecyclingForScrollView( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableViewRecyclingForScrollView(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableViewRecyclingForText( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableViewRecyclingForText(); @@ -925,6 +936,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableViewRecycling", JReactNativeFeatureFlagsCxxInterop::enableViewRecycling), + makeNativeMethod( + "enableViewRecyclingForScrollView", + JReactNativeFeatureFlagsCxxInterop::enableViewRecyclingForScrollView), makeNativeMethod( "enableViewRecyclingForText", JReactNativeFeatureFlagsCxxInterop::enableViewRecyclingForText), diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h index 74aeb4701d29..3941c2e6f521 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<74ac256afe63253ddb6576285858444c>> + * @generated SignedSource<<05dba4cd49bd4f490e1ea943dd02cae0>> */ /** @@ -144,6 +144,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableViewRecycling( facebook::jni::alias_ref); + static bool enableViewRecyclingForScrollView( + facebook::jni::alias_ref); + static bool enableViewRecyclingForText( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index 33c9174f7523..87020a8702cc 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<392fdd6e857b7c8ae99329b927be6ae0>> + * @generated SignedSource<> */ /** @@ -178,6 +178,10 @@ bool ReactNativeFeatureFlags::enableViewRecycling() { return getAccessor().enableViewRecycling(); } +bool ReactNativeFeatureFlags::enableViewRecyclingForScrollView() { + return getAccessor().enableViewRecyclingForScrollView(); +} + bool ReactNativeFeatureFlags::enableViewRecyclingForText() { return getAccessor().enableViewRecyclingForText(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index b42fa06492cf..b14e3be19c22 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<4831f7b871223d8347db93f9a0b3a1f9>> */ /** @@ -229,6 +229,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableViewRecycling(); + /** + * Enables View Recycling for via ReactViewGroup/ReactViewManager. + */ + RN_EXPORT static bool enableViewRecyclingForScrollView(); + /** * Enables View Recycling for via ReactTextView/ReactTextViewManager. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index f02ee2c8a231..6f0a805e0932 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<69518331e8b6225d4aa524e46951302d>> + * @generated SignedSource<> */ /** @@ -713,6 +713,24 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecycling() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForScrollView() { + auto flagValue = enableViewRecyclingForScrollView_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(38, "enableViewRecyclingForScrollView"); + + flagValue = currentProvider_->enableViewRecyclingForScrollView(); + enableViewRecyclingForScrollView_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForText() { auto flagValue = enableViewRecyclingForText_.load(); @@ -722,7 +740,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForText() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(38, "enableViewRecyclingForText"); + markFlagAsAccessed(39, "enableViewRecyclingForText"); flagValue = currentProvider_->enableViewRecyclingForText(); enableViewRecyclingForText_ = flagValue; @@ -740,7 +758,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(39, "enableViewRecyclingForView"); + markFlagAsAccessed(40, "enableViewRecyclingForView"); flagValue = currentProvider_->enableViewRecyclingForView(); enableViewRecyclingForView_ = flagValue; @@ -758,7 +776,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewDebugFeatures() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(40, "enableVirtualViewDebugFeatures"); + markFlagAsAccessed(41, "enableVirtualViewDebugFeatures"); flagValue = currentProvider_->enableVirtualViewDebugFeatures(); enableVirtualViewDebugFeatures_ = flagValue; @@ -776,7 +794,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewRenderState() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(41, "enableVirtualViewRenderState"); + markFlagAsAccessed(42, "enableVirtualViewRenderState"); flagValue = currentProvider_->enableVirtualViewRenderState(); enableVirtualViewRenderState_ = flagValue; @@ -794,7 +812,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewWindowFocusDetection() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(42, "enableVirtualViewWindowFocusDetection"); + markFlagAsAccessed(43, "enableVirtualViewWindowFocusDetection"); flagValue = currentProvider_->enableVirtualViewWindowFocusDetection(); enableVirtualViewWindowFocusDetection_ = flagValue; @@ -812,7 +830,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(43, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(44, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -830,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(44, "fuseboxEnabledRelease"); + markFlagAsAccessed(45, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -848,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxNetworkInspectionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(45, "fuseboxNetworkInspectionEnabled"); + markFlagAsAccessed(46, "fuseboxNetworkInspectionEnabled"); flagValue = currentProvider_->fuseboxNetworkInspectionEnabled(); fuseboxNetworkInspectionEnabled_ = flagValue; @@ -866,7 +884,7 @@ bool ReactNativeFeatureFlagsAccessor::hideOffscreenVirtualViewsOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(46, "hideOffscreenVirtualViewsOnIOS"); + markFlagAsAccessed(47, "hideOffscreenVirtualViewsOnIOS"); flagValue = currentProvider_->hideOffscreenVirtualViewsOnIOS(); hideOffscreenVirtualViewsOnIOS_ = flagValue; @@ -884,7 +902,7 @@ bool ReactNativeFeatureFlagsAccessor::perfMonitorV2Enabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(47, "perfMonitorV2Enabled"); + markFlagAsAccessed(48, "perfMonitorV2Enabled"); flagValue = currentProvider_->perfMonitorV2Enabled(); perfMonitorV2Enabled_ = flagValue; @@ -902,7 +920,7 @@ double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(48, "preparedTextCacheSize"); + markFlagAsAccessed(49, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -920,7 +938,7 @@ bool ReactNativeFeatureFlagsAccessor::preventShadowTreeCommitExhaustion() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(49, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(50, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -938,7 +956,7 @@ bool ReactNativeFeatureFlagsAccessor::releaseImageDataWhenConsumed() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(50, "releaseImageDataWhenConsumed"); + markFlagAsAccessed(51, "releaseImageDataWhenConsumed"); flagValue = currentProvider_->releaseImageDataWhenConsumed(); releaseImageDataWhenConsumed_ = flagValue; @@ -956,7 +974,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldPressibilityUseW3CPointerEventsForHo // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(51, "shouldPressibilityUseW3CPointerEventsForHover"); + markFlagAsAccessed(52, "shouldPressibilityUseW3CPointerEventsForHover"); flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover(); shouldPressibilityUseW3CPointerEventsForHover_ = flagValue; @@ -974,7 +992,7 @@ bool ReactNativeFeatureFlagsAccessor::skipActivityIdentityAssertionOnHostPause() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(52, "skipActivityIdentityAssertionOnHostPause"); + markFlagAsAccessed(53, "skipActivityIdentityAssertionOnHostPause"); flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause(); skipActivityIdentityAssertionOnHostPause_ = flagValue; @@ -992,7 +1010,7 @@ bool ReactNativeFeatureFlagsAccessor::sweepActiveTouchOnChildNativeGesturesAndro // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(53, "sweepActiveTouchOnChildNativeGesturesAndroid"); + markFlagAsAccessed(54, "sweepActiveTouchOnChildNativeGesturesAndroid"); flagValue = currentProvider_->sweepActiveTouchOnChildNativeGesturesAndroid(); sweepActiveTouchOnChildNativeGesturesAndroid_ = flagValue; @@ -1010,7 +1028,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(54, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(55, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -1028,7 +1046,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit( // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(55, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(56, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -1046,7 +1064,7 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(56, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(57, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -1064,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(57, "useFabricInterop"); + markFlagAsAccessed(58, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -1082,7 +1100,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeEqualsInNativeReadableArrayAndroi // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(58, "useNativeEqualsInNativeReadableArrayAndroid"); + markFlagAsAccessed(59, "useNativeEqualsInNativeReadableArrayAndroid"); flagValue = currentProvider_->useNativeEqualsInNativeReadableArrayAndroid(); useNativeEqualsInNativeReadableArrayAndroid_ = flagValue; @@ -1100,7 +1118,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeTransformHelperAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(59, "useNativeTransformHelperAndroid"); + markFlagAsAccessed(60, "useNativeTransformHelperAndroid"); flagValue = currentProvider_->useNativeTransformHelperAndroid(); useNativeTransformHelperAndroid_ = flagValue; @@ -1118,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(60, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(61, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1136,7 +1154,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedEventBatchingOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(61, "useOptimizedEventBatchingOnAndroid"); + markFlagAsAccessed(62, "useOptimizedEventBatchingOnAndroid"); flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid(); useOptimizedEventBatchingOnAndroid_ = flagValue; @@ -1154,7 +1172,7 @@ bool ReactNativeFeatureFlagsAccessor::useRawPropsJsiValue() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(62, "useRawPropsJsiValue"); + markFlagAsAccessed(63, "useRawPropsJsiValue"); flagValue = currentProvider_->useRawPropsJsiValue(); useRawPropsJsiValue_ = flagValue; @@ -1172,7 +1190,7 @@ bool ReactNativeFeatureFlagsAccessor::useShadowNodeStateOnClone() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(63, "useShadowNodeStateOnClone"); + markFlagAsAccessed(64, "useShadowNodeStateOnClone"); flagValue = currentProvider_->useShadowNodeStateOnClone(); useShadowNodeStateOnClone_ = flagValue; @@ -1190,7 +1208,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(64, "useTurboModuleInterop"); + markFlagAsAccessed(65, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1208,7 +1226,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(65, "useTurboModules"); + markFlagAsAccessed(66, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; @@ -1226,7 +1244,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(66, "virtualViewPrerenderRatio"); + markFlagAsAccessed(67, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 13bf7584ab7a..8e1c7b5c4396 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -70,6 +70,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableResourceTimingAPI(); bool enableViewCulling(); bool enableViewRecycling(); + bool enableViewRecyclingForScrollView(); bool enableViewRecyclingForText(); bool enableViewRecyclingForView(); bool enableVirtualViewDebugFeatures(); @@ -110,7 +111,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 67> accessedFeatureFlags_; + std::array, 68> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -150,6 +151,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableResourceTimingAPI_; std::atomic> enableViewCulling_; std::atomic> enableViewRecycling_; + std::atomic> enableViewRecyclingForScrollView_; std::atomic> enableViewRecyclingForText_; std::atomic> enableViewRecyclingForView_; std::atomic> enableVirtualViewDebugFeatures_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 2ba904e1981c..73fcebf8c7cf 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<54a695ed4e9c536cd8650498b9dab2fc>> + * @generated SignedSource<<5c12d5107de20283dd7a2a899685cfa5>> */ /** @@ -179,6 +179,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool enableViewRecyclingForScrollView() override { + return true; + } + bool enableViewRecyclingForText() override { return true; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 4f99b50724d6..f55df4907aee 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<60b81844a9af1862b27ed6b4d9be5f59>> + * @generated SignedSource<<6030ed59832efdf42e23c13b689da64f>> */ /** @@ -387,6 +387,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enableViewRecycling(); } + bool enableViewRecyclingForScrollView() override { + auto value = values_["enableViewRecyclingForScrollView"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::enableViewRecyclingForScrollView(); + } + bool enableViewRecyclingForText() override { auto value = values_["enableViewRecyclingForText"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 0bbf52bc1eac..edfeaf7643ad 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<56fe64198dddcba67062ef52a180128e>> + * @generated SignedSource<<95940b74b0055760be69c501a97a6f45>> */ /** @@ -63,6 +63,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableResourceTimingAPI() = 0; virtual bool enableViewCulling() = 0; virtual bool enableViewRecycling() = 0; + virtual bool enableViewRecyclingForScrollView() = 0; virtual bool enableViewRecyclingForText() = 0; virtual bool enableViewRecyclingForView() = 0; virtual bool enableVirtualViewDebugFeatures() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 4ab465aaf93b..a4fa0e554d9e 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<3c321b78f9d710e5f3dc3a73e56be298>> + * @generated SignedSource<<998bdf037d58ca747c22ada099d236f0>> */ /** @@ -234,6 +234,11 @@ bool NativeReactNativeFeatureFlags::enableViewRecycling( return ReactNativeFeatureFlags::enableViewRecycling(); } +bool NativeReactNativeFeatureFlags::enableViewRecyclingForScrollView( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableViewRecyclingForScrollView(); +} + bool NativeReactNativeFeatureFlags::enableViewRecyclingForText( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableViewRecyclingForText(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index d638bbc6ea5a..3fdaeda4acdb 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<15625838a3b9e9ecf3e77940a5f019a6>> + * @generated SignedSource<<89c5d22cd228da57d82843f7e660f262>> */ /** @@ -112,6 +112,8 @@ class NativeReactNativeFeatureFlags bool enableViewRecycling(jsi::Runtime& runtime); + bool enableViewRecyclingForScrollView(jsi::Runtime& runtime); + bool enableViewRecyclingForText(jsi::Runtime& runtime); bool enableViewRecyclingForView(jsi::Runtime& runtime); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 7b1c182a3e72..79447c4c3628 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -451,6 +451,17 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + enableViewRecyclingForScrollView: { + defaultValue: true, + metadata: { + dateAdded: '2025-08-20', + description: + 'Enables View Recycling for via ReactViewGroup/ReactViewManager.', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'none', + }, enableViewRecyclingForText: { defaultValue: true, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 2e11e8838cac..bf73f29256a7 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<66a3905e1c94e7769418becc3bace939>> + * @generated SignedSource<<825b92d137ed3253e0badd0696605882>> * @flow strict * @noformat */ @@ -85,6 +85,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ enableResourceTimingAPI: Getter, enableViewCulling: Getter, enableViewRecycling: Getter, + enableViewRecyclingForScrollView: Getter, enableViewRecyclingForText: Getter, enableViewRecyclingForView: Getter, enableVirtualViewDebugFeatures: Getter, @@ -332,6 +333,10 @@ export const enableViewCulling: Getter = createNativeFlagGetter('enable * Enables View Recycling. When enabled, individual ViewManagers must still opt-in. */ export const enableViewRecycling: Getter = createNativeFlagGetter('enableViewRecycling', false); +/** + * Enables View Recycling for via ReactViewGroup/ReactViewManager. + */ +export const enableViewRecyclingForScrollView: Getter = createNativeFlagGetter('enableViewRecyclingForScrollView', true); /** * Enables View Recycling for via ReactTextView/ReactTextViewManager. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index 38bafbdc20da..30a467f2224b 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<604963e3616f21f84b65b0291fecb182>> + * @generated SignedSource<<2b0679c42d8e1cb021612807d94d9f4c>> * @flow strict * @noformat */ @@ -63,6 +63,7 @@ export interface Spec extends TurboModule { +enableResourceTimingAPI?: () => boolean; +enableViewCulling?: () => boolean; +enableViewRecycling?: () => boolean; + +enableViewRecyclingForScrollView?: () => boolean; +enableViewRecyclingForText?: () => boolean; +enableViewRecyclingForView?: () => boolean; +enableVirtualViewDebugFeatures?: () => boolean; From 28275a0f7b182a215010d47fb841d9c2c36bb24c Mon Sep 17 00:00:00 2001 From: Riccardo Cipolleschi Date: Thu, 21 Aug 2025 10:13:22 -0700 Subject: [PATCH 0005/1228] Fix Crash when rendering Switch component (#53393) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53393 When implementing the changes for the Switch component for iOS 26, I didn't realize that the code in [`RCTIntance.mm`](https://www.internalfb.com/code/fbsource/[a54514baccf4e1f828ef46af8180555754c405d8]/xplat/js/react-native-github/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm?lines=344-375) was running behind a feature flag. This means that all the users not in the experiment where the feature flag is turned on will experience a crash as soon as they navigate to a React Native screen that contains a Switch. This change fixes the problem by adding a jump to the right queue and it also fixes a measurement issue with the Switch. ## Changelog: [iOS][Fixed] - Fixed a crash when rendering the Switch component bypass-github-export-checks Reviewed By: GijsWeterings Differential Revision: D80702245 fbshipit-source-id: 480ea061233d35e31679b4b30758dbc133ff0a77 --- packages/react-native/React/Base/RCTUtils.mm | 4 +++- .../react/renderer/components/switch/IOSSwitchShadowNode.mm | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/react-native/React/Base/RCTUtils.mm b/packages/react-native/React/Base/RCTUtils.mm index 82712cb3f9ec..c0cf35b86a5f 100644 --- a/packages/react-native/React/Base/RCTUtils.mm +++ b/packages/react-native/React/Base/RCTUtils.mm @@ -415,7 +415,9 @@ CGSize RCTSwitchSize(void) static CGSize rctSwitchSize; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - rctSwitchSize = [UISwitch new].intrinsicContentSize; + RCTUnsafeExecuteOnMainQueueSync(^{ + rctSwitchSize = [UISwitch new].intrinsicContentSize; + }); }); return rctSwitchSize; } diff --git a/packages/react-native/ReactCommon/react/renderer/components/switch/iosswitch/react/renderer/components/switch/IOSSwitchShadowNode.mm b/packages/react-native/ReactCommon/react/renderer/components/switch/iosswitch/react/renderer/components/switch/IOSSwitchShadowNode.mm index b38b0b0509c8..d1981f17c062 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/switch/iosswitch/react/renderer/components/switch/IOSSwitchShadowNode.mm +++ b/packages/react-native/ReactCommon/react/renderer/components/switch/iosswitch/react/renderer/components/switch/IOSSwitchShadowNode.mm @@ -20,7 +20,9 @@ const LayoutConstraints & /*layoutConstraints*/) const { CGSize uiSwitchSize = RCTSwitchSize(); - return {.width = uiSwitchSize.width, .height = uiSwitchSize.height}; + // Apple has some error when returning the width of the component and it doesn't + // account for the borders. + return {.width = uiSwitchSize.width + 2, .height = uiSwitchSize.height}; } } // namespace facebook::react From e28784d4b6c8701f9d9aa898786ac63cf1778ea8 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 21 Aug 2025 10:52:56 -0700 Subject: [PATCH 0006/1228] Remove unnecessary NativeArgumentsParseException (#53408) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53408 This class is internal + legacy arch so can safely be removed now. I've replaced the throw/catch site with the superclass of this exception. Changelog: [Internal] [Changed] - Reviewed By: mdvacca Differential Revision: D80710950 fbshipit-source-id: 96615835588ce409de40d31ee83b82c88d3a07a0 --- .../react/bridge/JavaMethodWrapper.kt | 6 ++-- .../bridge/NativeArgumentsParseException.kt | 34 ------------------- .../react/bridge/BaseJavaModuleTest.kt | 2 +- 3 files changed, 4 insertions(+), 38 deletions(-) delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArgumentsParseException.kt diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaMethodWrapper.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaMethodWrapper.kt index a5d763772d42..9ed454902df8 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaMethodWrapper.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaMethodWrapper.kt @@ -189,7 +189,7 @@ internal class JavaMethodWrapper( } if (jsArgumentsNeeded != parameters.size()) { - throw NativeArgumentsParseException( + throw JSApplicationCausedNativeException( "$traceName got ${parameters.size()} arguments, expected $jsArgumentsNeeded" ) } @@ -208,7 +208,7 @@ internal class JavaMethodWrapper( i++ } } catch (e: UnexpectedNativeTypeException) { - throw NativeArgumentsParseException( + throw JSApplicationCausedNativeException( "${e.message} (constructing arguments for $traceName at argument index ${ getAffectedRange( jsArgumentsConsumed, @@ -218,7 +218,7 @@ internal class JavaMethodWrapper( e, ) } catch (e: NullPointerException) { - throw NativeArgumentsParseException( + throw JSApplicationCausedNativeException( "${e.message} (constructing arguments for $traceName at argument index ${ getAffectedRange( jsArgumentsConsumed, diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArgumentsParseException.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArgumentsParseException.kt deleted file mode 100644 index c095010bef2c..000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArgumentsParseException.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.bridge - -import com.facebook.react.common.annotations.internal.LegacyArchitecture -import com.facebook.react.common.annotations.internal.LegacyArchitectureLogLevel -import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger - -/** Exception thrown when a native module method call receives unexpected arguments from JS. */ -@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR) -@Deprecated( - message = "This class is part of Legacy Architecture and will be removed in a future release", - level = DeprecationLevel.WARNING, -) -internal class NativeArgumentsParseException : JSApplicationCausedNativeException { - - constructor(detailMessage: String) : super(detailMessage) - - constructor(detailMessage: String, throwable: Throwable?) : super(detailMessage, throwable) - - private companion object { - init { - LegacyArchitectureLogger.assertLegacyArchitecture( - "NativeArgumentsParseException", - logLevel = LegacyArchitectureLogLevel.ERROR, - ) - } - } -} diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/bridge/BaseJavaModuleTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/bridge/BaseJavaModuleTest.kt index b671f2fc33a6..28b682bda5d0 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/bridge/BaseJavaModuleTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/bridge/BaseJavaModuleTest.kt @@ -44,7 +44,7 @@ class BaseJavaModuleTest { private fun findMethod(mname: String, methods: List): Int = methods.indexOfFirst({ it.name === mname }) - @Test(expected = NativeArgumentsParseException::class) + @Test(expected = JSApplicationCausedNativeException::class) fun testCallMethodWithoutEnoughArgs() { val methodId = findMethod("regularMethod", methods) whenever(arguments.size()).thenReturn(1) From 8e514a8a10ba4d1290ab08dfa7574dec5fad5e87 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 21 Aug 2025 10:52:56 -0700 Subject: [PATCH 0007/1228] Annotate ReactNativeJNISoLoader as InteropLegacyArchitecture (#53406) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53406 The `ReactNativeJNISoLoader` class (previously called BridgeSoLoader) is actually a Legacy Architecture class. However is used by `CxxModuleWrapperBase` which is needed by interop, so we need to keep it around. I'm adding the annotation so we won't forget about it. Changelog: [Internal] [Changed] - Reviewed By: cipolleschi Differential Revision: D80710951 fbshipit-source-id: ed353e2b14c742b25962f0b81beba6dc90157709 --- .../java/com/facebook/react/bridge/ReactNativeJNISoLoader.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactNativeJNISoLoader.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactNativeJNISoLoader.kt index 064e73d38cea..8fe22332b503 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactNativeJNISoLoader.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReactNativeJNISoLoader.kt @@ -7,8 +7,10 @@ package com.facebook.react.bridge +import com.facebook.react.common.annotations.internal.InteropLegacyArchitecture import com.facebook.soloader.SoLoader +@InteropLegacyArchitecture internal object ReactNativeJNISoLoader { @JvmStatic From d35ddb5e59a8cb990dd61a154a8e15e9542f8b15 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 21 Aug 2025 11:16:20 -0700 Subject: [PATCH 0008/1228] Delete unused `DefaultReactHost.getDefaultReactHost()` overload (#53396) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53396 This overload for `getDefaultReactHost` is missing the last 2 parameters and provides the same default as the full method. In OSS is unused as we used the one with `ReactNativeHost`. I'm removing it as it's causing an overload resolution failure internally. This won't be a breaking change for Kotlin users are the signatures are compatibles, but it will be breaking for Java users. I've verified that there are no OSS users. Developers should use Kotlin default parameters + the method `getDefaultReactHost()` with all the params + defaults for ease of use of this API. Changelog: [Android] [Removed] - Delete unused `DefaultReactHost.getDefaultReactHost()` overload Reviewed By: mdvacca Differential Revision: D80704987 fbshipit-source-id: 0b3a61aad3f18cde77bac78e3ba413d7f9166ede --- .../ReactAndroid/api/ReactAndroid.api | 2 - .../react/defaults/DefaultReactHost.kt | 44 ------------------- 2 files changed, 46 deletions(-) diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 53c415d96532..2e5e7b94b3fb 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -1863,10 +1863,8 @@ public class com/facebook/react/defaults/DefaultReactActivityDelegate : com/face public final class com/facebook/react/defaults/DefaultReactHost { public static final field INSTANCE Lcom/facebook/react/defaults/DefaultReactHost; public static final fun getDefaultReactHost (Landroid/content/Context;Lcom/facebook/react/ReactNativeHost;Lcom/facebook/react/runtime/JSRuntimeFactory;)Lcom/facebook/react/ReactHost; - public static final fun getDefaultReactHost (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/facebook/react/runtime/JSRuntimeFactory;ZLjava/util/List;)Lcom/facebook/react/ReactHost; public static final fun getDefaultReactHost (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/facebook/react/runtime/JSRuntimeFactory;ZLjava/util/List;Lkotlin/jvm/functions/Function1;Lcom/facebook/react/runtime/BindingsInstaller;)Lcom/facebook/react/ReactHost; public static synthetic fun getDefaultReactHost$default (Landroid/content/Context;Lcom/facebook/react/ReactNativeHost;Lcom/facebook/react/runtime/JSRuntimeFactory;ILjava/lang/Object;)Lcom/facebook/react/ReactHost; - public static synthetic fun getDefaultReactHost$default (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/facebook/react/runtime/JSRuntimeFactory;ZLjava/util/List;ILjava/lang/Object;)Lcom/facebook/react/ReactHost; public static synthetic fun getDefaultReactHost$default (Landroid/content/Context;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/facebook/react/runtime/JSRuntimeFactory;ZLjava/util/List;Lkotlin/jvm/functions/Function1;Lcom/facebook/react/runtime/BindingsInstaller;ILjava/lang/Object;)Lcom/facebook/react/ReactHost; } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt index 712f29cd03cd..b1638d056c1a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactHost.kt @@ -35,50 +35,6 @@ import java.lang.Exception public object DefaultReactHost { private var reactHost: ReactHost? = null - /** - * Util function to create a default [ReactHost] to be used in your application. This method is - * used by the New App template. - * - * @param context the Android [Context] to use for creating the [ReactHost] - * @param packageList the list of [ReactPackage]s to use for creating the [ReactHost] - * @param jsMainModulePath the path to your app's main module on Metro. Usually `index` or - * `index.` - * @param jsBundleAssetPath the path to the JS bundle relative to the assets directory. Will be - * composed in a `asset://...` URL - * @param jsBundleFilePath the path to the JS bundle on the filesystem. Will be composed in a - * `file://...` URL - * @param jsRuntimeFactory the JS engine to use for executing [ReactHost], default to Hermes. - * @param useDevSupport whether to enable dev support, default to ReactBuildConfig.DEBUG. - * @param cxxReactPackageProviders a list of cxxreactpackage providers (to register c++ turbo - * modules) - * - * TODO(T186951312): Should this be @UnstableReactNativeAPI? - */ - @OptIn(UnstableReactNativeAPI::class) - @JvmStatic - public fun getDefaultReactHost( - context: Context, - packageList: List, - jsMainModulePath: String = "index", - jsBundleAssetPath: String = "index", - jsBundleFilePath: String? = null, - jsRuntimeFactory: JSRuntimeFactory? = null, - useDevSupport: Boolean = ReactBuildConfig.DEBUG, - cxxReactPackageProviders: List<(ReactContext) -> CxxReactPackage> = emptyList(), - ): ReactHost = - getDefaultReactHost( - context, - packageList, - jsMainModulePath, - jsBundleAssetPath, - jsBundleFilePath, - jsRuntimeFactory, - useDevSupport, - cxxReactPackageProviders, - { throw it }, - null, - ) - /** * Util function to create a default [ReactHost] to be used in your application. This method is * used by the New App template. From 2689d4d372e56203337ae8b9d7834b9b516be5a6 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 21 Aug 2025 11:16:20 -0700 Subject: [PATCH 0009/1228] RNTester should not use ReactNativeHost (#53398) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53398 ReactNativeHost is a legacy architecture class. This migrates the app away from it and converts it to use DefaultReactHost instead. Changelog: [Internal] [Changed] - Reviewed By: mdvacca Differential Revision: D80708460 fbshipit-source-id: 7d88c440414c979a2968fc9c910e828f5851195c --- .../react/uiapp/RNTesterApplication.kt | 165 ++++++++---------- 1 file changed, 77 insertions(+), 88 deletions(-) diff --git a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.kt b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.kt index 66ce14c2fc5b..58c6325fbee8 100644 --- a/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.kt +++ b/packages/rn-tester/android/app/src/main/java/com/facebook/react/uiapp/RNTesterApplication.kt @@ -17,14 +17,12 @@ import com.facebook.react.PackageList import com.facebook.react.ReactApplication import com.facebook.react.ReactHost import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative -import com.facebook.react.ReactNativeHost import com.facebook.react.ReactPackage import com.facebook.react.ViewManagerOnDemandReactPackage import com.facebook.react.bridge.NativeModule import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.common.assets.ReactFontManager import com.facebook.react.defaults.DefaultReactHost -import com.facebook.react.defaults.DefaultReactNativeHost import com.facebook.react.module.model.ReactModuleInfo import com.facebook.react.module.model.ReactModuleInfoProvider import com.facebook.react.uiapp.component.MyLegacyViewManager @@ -34,100 +32,91 @@ import com.facebook.react.uimanager.ReactShadowNode import com.facebook.react.uimanager.ViewManager internal class RNTesterApplication : Application(), ReactApplication { - @Deprecated( - "You should not use ReactNativeHost directly in the New Architecture. Use ReactHost instead.", - replaceWith = ReplaceWith("reactHost"), - ) - override val reactNativeHost: ReactNativeHost by + override val reactHost: ReactHost by lazy(LazyThreadSafetyMode.NONE) { - object : DefaultReactNativeHost(this) { - public override fun getJSMainModuleName(): String = BuildConfig.JS_MAIN_MODULE_NAME + val packages: List = + PackageList(this@RNTesterApplication).packages.apply { + add( + object : BaseReactPackage() { + override fun getModule( + name: String, + reactContext: ReactApplicationContext, + ): NativeModule? = + when (name) { + SampleTurboModule.NAME -> SampleTurboModule(reactContext) + SampleLegacyModule.NAME -> SampleLegacyModule(reactContext) + else -> null + } - public override fun getBundleAssetName(): String = BuildConfig.BUNDLE_ASSET_NAME - - override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG - - public override fun getPackages(): List = - PackageList(this).packages.apply { - add( - object : BaseReactPackage() { - override fun getModule( - name: String, - reactContext: ReactApplicationContext, - ): NativeModule? = - when { - SampleTurboModule.NAME == name -> SampleTurboModule(reactContext) - SampleLegacyModule.NAME == name -> SampleLegacyModule(reactContext) - else -> null - } - - // Note: Specialized annotation processor for @ReactModule isn't configured in - // OSS yet. For now, hardcode this information, though it's not necessary for - // most modules. - override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = - ReactModuleInfoProvider { - mapOf( - SampleTurboModule.NAME to - ReactModuleInfo( - SampleTurboModule.NAME, - "SampleTurboModule", - canOverrideExistingModule = false, - needsEagerInit = false, - isCxxModule = false, - isTurboModule = true, - ), - SampleLegacyModule.NAME to - ReactModuleInfo( - SampleLegacyModule.NAME, - "SampleLegacyModule", - canOverrideExistingModule = false, - needsEagerInit = false, - isCxxModule = false, - isTurboModule = false, - ), - ) - } - } - ) - add( - object : ReactPackage, ViewManagerOnDemandReactPackage { - override fun getViewManagerNames(reactContext: ReactApplicationContext) = - listOf( - "RNTMyNativeView", - "RNTMyLegacyNativeView", - "RNTReportFullyDrawnView", + // Note: Specialized annotation processor for @ReactModule isn't configured in + // OSS yet. For now, hardcode this information, though it's not necessary for + // most modules. + override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = + ReactModuleInfoProvider { + mapOf( + SampleTurboModule.NAME to + ReactModuleInfo( + SampleTurboModule.NAME, + "SampleTurboModule", + canOverrideExistingModule = false, + needsEagerInit = false, + isCxxModule = false, + isTurboModule = true, + ), + SampleLegacyModule.NAME to + ReactModuleInfo( + SampleLegacyModule.NAME, + "SampleLegacyModule", + canOverrideExistingModule = false, + needsEagerInit = false, + isCxxModule = false, + isTurboModule = false, + ), ) + } + } + ) + add( + object : ReactPackage, ViewManagerOnDemandReactPackage { + override fun getViewManagerNames(reactContext: ReactApplicationContext) = + listOf( + "RNTMyNativeView", + "RNTMyLegacyNativeView", + "RNTReportFullyDrawnView", + ) - override fun createViewManagers( - reactContext: ReactApplicationContext - ): List> = - listOf( - MyNativeViewManager(), - MyLegacyViewManager(reactContext), - ReportFullyDrawnViewManager(), - ) + override fun createViewManagers( + reactContext: ReactApplicationContext + ): List> = + listOf( + MyNativeViewManager(), + MyLegacyViewManager(reactContext), + ReportFullyDrawnViewManager(), + ) - override fun createViewManager( - reactContext: ReactApplicationContext, - viewManagerName: String, - ): ViewManager<*, out ReactShadowNode<*>>? = - when (viewManagerName) { - "RNTMyNativeView" -> MyNativeViewManager() - "RNTMyLegacyNativeView" -> MyLegacyViewManager(reactContext) - "RNTReportFullyDrawnView" -> ReportFullyDrawnViewManager() - else -> null - } - } - ) - } + override fun createViewManager( + reactContext: ReactApplicationContext, + viewManagerName: String, + ): ViewManager<*, out ReactShadowNode<*>>? = + when (viewManagerName) { + "RNTMyNativeView" -> MyNativeViewManager() + "RNTMyLegacyNativeView" -> MyLegacyViewManager(reactContext) + "RNTReportFullyDrawnView" -> ReportFullyDrawnViewManager() + else -> null + } + } + ) + } - override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED - } + DefaultReactHost.getDefaultReactHost( + applicationContext, + packages, + jsMainModulePath = BuildConfig.JS_MAIN_MODULE_NAME, + jsBundleAssetPath = BuildConfig.BUNDLE_ASSET_NAME, + useDevSupport = BuildConfig.DEBUG, + ) } - override val reactHost: ReactHost - get() = DefaultReactHost.getDefaultReactHost(applicationContext, reactNativeHost) - override fun onCreate() { ReactFontManager.getInstance().addCustomFont(this, "Rubik", R.font.rubik) super.onCreate() From aaa7ba4ab3499a5e8655f7249b5f85e5285e4cf2 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 21 Aug 2025 11:16:20 -0700 Subject: [PATCH 0010/1228] HelloWorld should not use ReactNativeHost (#53399) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53399 We currently create application template that are still using `ReactNativeHost`. As this is a legacy arch class, we should remove it from the template ASAP so that users can organically migrate away from it. I will also update https://github.com/react-native-community/template with the same changes I'm applying here. Changelog: [Internal] [Changed] - Reviewed By: mdvacca Differential Revision: D80708461 fbshipit-source-id: e0d4a1f817e6fdddd93405decf77fd115956ec51 --- .../java/com/helloworld/MainApplication.kt | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/private/helloworld/android/app/src/main/java/com/helloworld/MainApplication.kt b/private/helloworld/android/app/src/main/java/com/helloworld/MainApplication.kt index fc0329abe979..8e98179d1635 100644 --- a/private/helloworld/android/app/src/main/java/com/helloworld/MainApplication.kt +++ b/private/helloworld/android/app/src/main/java/com/helloworld/MainApplication.kt @@ -12,29 +12,22 @@ import com.facebook.react.PackageList import com.facebook.react.ReactApplication import com.facebook.react.ReactHost import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative -import com.facebook.react.ReactNativeHost -import com.facebook.react.ReactPackage import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost -import com.facebook.react.defaults.DefaultReactNativeHost class MainApplication : Application(), ReactApplication { - override val reactNativeHost: ReactNativeHost = - object : DefaultReactNativeHost(this) { - override fun getPackages(): List = - PackageList(this).packages.apply { - // Packages that cannot be autolinked yet can be added manually here, for example: - // add(MyReactNativePackage()) - } - - override fun getJSMainModuleName(): String = "index" - - override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG + override val reactHost: ReactHost by + lazy(LazyThreadSafetyMode.NONE) { + getDefaultReactHost( + context = applicationContext, + packageList = + PackageList(this).packages.apply { + // Packages that cannot be autolinked yet can be added manually here, for example: + // add(MyReactNativePackage()) + }, + ) } - override val reactHost: ReactHost - get() = getDefaultReactHost(applicationContext, reactNativeHost) - override fun onCreate() { super.onCreate() loadReactNative(this) From 191ddc1ec72be6641ebb8b9cb729cf0e142fff55 Mon Sep 17 00:00:00 2001 From: Umar Mohammad Date: Thu, 21 Aug 2025 11:33:52 -0700 Subject: [PATCH 0011/1228] Fix React Native Commands Export Validation in Coverage Mode (#53381) Summary: Changelog: [GENERAL] [FIXED] - Fixed babel plugin validation error when coverage instrumentation is enabled Pull Request resolved: https://github.com/facebook/react-native/pull/53381 ### Problem [Workplace post](https://fb.workplace.com/groups/235694244595999/permalink/1278937163605030/) React Native tests were failing **only when coverage collection was enabled** with the error: `'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands.` ### Root Cause The React Native Babel plugin's `codegenNativeCommands` validation logic only handled direct `CallExpression` AST nodes. When coverage instrumentation was enabled, it transformed: **Normal code:** `export const Commands = codegenNativeCommands({...})` **With coverage:** `export const Commands = (cov_xxx().s[0]++, codegenNativeCommands({...}))` The plugin failed to recognize the valid `codegenNativeCommands` call wrapped in a `SequenceExpression` by coverage instrumentation. ### **Solution** Added `isCodegenNativeCommandsDeclaration` function to handle: 1. **Coverage instrumentation**: `SequenceExpression` nodes containing the function call 2. **Flow type casts**: `TypeCastExpression` and `AsExpression` 3. **TypeScript assertions**: `TSAsExpression` 4. **Direct calls**: Original `CallExpression` (backward compatibility) Reviewed By: andrewdacenko Differential Revision: D80572666 fbshipit-source-id: 465f4312a0229d8a92e495c685f46b607ce326e4 --- .../__test_fixtures__/failures.js | 137 ++++++++++++++++++ .../__test_fixtures__/fixtures.js | 91 ++++++++++++ .../__snapshots__/index-test.js.snap | 127 ++++++++++++++++ packages/babel-plugin-codegen/index.js | 64 +++++++- 4 files changed, 413 insertions(+), 6 deletions(-) diff --git a/packages/babel-plugin-codegen/__test_fixtures__/failures.js b/packages/babel-plugin-codegen/__test_fixtures__/failures.js index 908a83488e07..af4f40a3ec05 100644 --- a/packages/babel-plugin-codegen/__test_fixtures__/failures.js +++ b/packages/babel-plugin-codegen/__test_fixtures__/failures.js @@ -81,10 +81,147 @@ export {Commands}; export default (codegenNativeComponent('Module'): NativeType); `; +const COMMANDS_WITH_COVERAGE_INVALID = ` +// @flow + +const codegenNativeComponent = require('codegenNativeComponent'); +import type {NativeComponentType} from 'codegenNativeComponent'; + +import type {ViewProps} from 'ViewPropTypes'; + +type ModuleProps = $ReadOnly<{| + ...ViewProps, +|}>; + +type NativeType = NativeComponentType; + +// Coverage instrumentation of invalid Commands export - should still fail +export const Commands = (cov_1234567890().s[0]++, { + hotspotUpdate: () => {}, + scrollTo: () => {}, +}); + +export default (codegenNativeComponent('Module'): NativeType); +`; + +const COMMANDS_WITH_COVERAGE_WRONG_FUNCTION = ` +// @flow + +const codegenNativeComponent = require('codegenNativeComponent'); +import type {NativeComponentType} from 'codegenNativeComponent'; + +import type {ViewProps} from 'ViewPropTypes'; + +type ModuleProps = $ReadOnly<{| + ...ViewProps, +|}>; + +type NativeType = NativeComponentType; + +// Coverage instrumentation of wrong function call - should fail +export const Commands = (cov_abcdef123().s[0]++, someOtherFunction({ + supportedCommands: ['pause', 'play'], +})); + +export default (codegenNativeComponent('Module'): NativeType); +`; + +const COMMANDS_WITH_COMPLEX_COVERAGE_INVALID = ` +// @flow + +const codegenNativeComponent = require('codegenNativeComponent'); +import type {NativeComponentType} from 'codegenNativeComponent'; + +import type {ViewProps} from 'ViewPropTypes'; + +type ModuleProps = $ReadOnly<{| + ...ViewProps, +|}>; + +type NativeType = NativeComponentType; + +// Complex coverage instrumentation with invalid nested structure - should fail +export const Commands = ( + cov_xyz789().f[1]++, + cov_xyz789().s[2]++, + { + pause: (ref) => {}, + play: (ref) => {}, + } +); + +export default (codegenNativeComponent('Module'): NativeType); +`; + +const COMMANDS_WITH_COVERAGE_WRONG_NAME = ` +// @flow + +const codegenNativeCommands = require('codegenNativeCommands'); +const codegenNativeComponent = require('codegenNativeComponent'); +import type {NativeComponentType} from 'codegenNativeComponent'; + +import type {ViewProps} from 'ViewPropTypes'; + +type ModuleProps = $ReadOnly<{| + ...ViewProps, +|}>; + +type NativeType = NativeComponentType; + +interface NativeCommands { + +pause: (viewRef: React.ElementRef) => void; + +play: (viewRef: React.ElementRef) => void; +} + +// Coverage instrumentation with correct function but wrong export name - should fail +export const WrongName = (cov_wrong123().s[0]++, codegenNativeCommands({ + supportedCommands: ['pause', 'play'], +})); + +export default (codegenNativeComponent('Module'): NativeType); +`; + +const COMMANDS_WITH_COVERAGE_TYPE_CAST_INVALID = ` +// @flow + +const codegenNativeComponent = require('codegenNativeComponent'); +import type {NativeComponentType} from 'codegenNativeComponent'; + +import type {ViewProps} from 'ViewPropTypes'; + +type ModuleProps = $ReadOnly<{| + ...ViewProps, +|}>; + +type NativeType = NativeComponentType; + +interface NativeCommands { + +pause: (viewRef: React.ElementRef) => void; + +play: (viewRef: React.ElementRef) => void; +} + +// Coverage instrumentation with type cast but wrong function - should fail +export const Commands: NativeCommands = (cov_cast123().s[0]++, invalidFunction({ + supportedCommands: ['pause', 'play'], +})); + +export default (codegenNativeComponent('Module'): NativeType); +`; + module.exports = { 'CommandsExportedWithDifferentNameNativeComponent.js': COMMANDS_EXPORTED_WITH_DIFFERENT_NAME, 'CommandsExportedWithShorthandNativeComponent.js': COMMANDS_EXPORTED_WITH_SHORTHAND, 'OtherCommandsExportNativeComponent.js': OTHER_COMMANDS_EXPORT, + 'CommandsWithCoverageInvalidNativeComponent.js': + COMMANDS_WITH_COVERAGE_INVALID, + 'CommandsWithCoverageWrongFunctionNativeComponent.js': + COMMANDS_WITH_COVERAGE_WRONG_FUNCTION, + 'CommandsWithComplexCoverageInvalidNativeComponent.js': + COMMANDS_WITH_COMPLEX_COVERAGE_INVALID, + 'CommandsWithCoverageWrongNameNativeComponent.js': + COMMANDS_WITH_COVERAGE_WRONG_NAME, + 'CommandsWithCoverageTypeCastInvalidNativeComponent.js': + COMMANDS_WITH_COVERAGE_TYPE_CAST_INVALID, }; diff --git a/packages/babel-plugin-codegen/__test_fixtures__/fixtures.js b/packages/babel-plugin-codegen/__test_fixtures__/fixtures.js index 1aec6340d2f6..6b813a3f07a6 100644 --- a/packages/babel-plugin-codegen/__test_fixtures__/fixtures.js +++ b/packages/babel-plugin-codegen/__test_fixtures__/fixtures.js @@ -59,6 +59,92 @@ export default codegenNativeComponent('Module', { }); `; +// Coverage instrumentation test cases - should be recognized as valid +const COMMANDS_WITH_SIMPLE_COVERAGE = ` +// @flow + +const codegenNativeCommands = require('codegenNativeCommands'); +const codegenNativeComponent = require('codegenNativeComponent'); + +import type {ViewProps} from 'ViewPropTypes'; +import type {NativeComponentType} from 'codegenNativeComponent'; + +type ModuleProps = $ReadOnly<{| + ...ViewProps, +|}>; + +type NativeType = NativeComponentType; + +interface NativeCommands { + +pause: (viewRef: React.ElementRef) => void; + +play: (viewRef: React.ElementRef) => void; +} + +export const Commands = (cov_1234567890.s[0]++, codegenNativeCommands({ + supportedCommands: ['pause', 'play'], +})); + +export default codegenNativeComponent('Module'); +`; + +const COMMANDS_WITH_COMPLEX_COVERAGE = ` +// @flow + +const codegenNativeCommands = require('codegenNativeCommands'); +const codegenNativeComponent = require('codegenNativeComponent'); + +import type {ViewProps} from 'ViewPropTypes'; +import type {NativeComponentType} from 'codegenNativeComponent'; + +type ModuleProps = $ReadOnly<{| + ...ViewProps, +|}>; + +type NativeType = NativeComponentType; + +interface NativeCommands { + +seek: (viewRef: React.ElementRef, position: number) => void; + +stop: (viewRef: React.ElementRef) => void; +} + +export const Commands = ( + cov_abcdef123().f[2]++, + cov_abcdef123().s[5]++, + codegenNativeCommands({ + supportedCommands: ['seek', 'stop'], + }) +); + +export default codegenNativeComponent('Module'); +`; + +const COMMANDS_WITH_TYPE_CAST_COVERAGE = ` +// @flow + +const codegenNativeCommands = require('codegenNativeCommands'); +const codegenNativeComponent = require('codegenNativeComponent'); + +import type {ViewProps} from 'ViewPropTypes'; +import type {NativeComponentType} from 'codegenNativeComponent'; + +type ModuleProps = $ReadOnly<{| + ...ViewProps, +|}>; + +type NativeType = NativeComponentType; + +interface NativeCommands { + +mute: (viewRef: React.ElementRef) => void; + +unmute: (viewRef: React.ElementRef) => void; +} + +export const Commands: NativeCommands = (cov_xyz789().s[1]++, codegenNativeCommands({ + supportedCommands: ['mute', 'unmute'], +})); + +export default codegenNativeComponent('Module'); +`; + const FULL_NATIVE_COMPONENT_WITH_TYPE_EXPORT = ` // @flow @@ -107,4 +193,9 @@ module.exports = { 'NotANativeComponent.js': NOT_A_NATIVE_COMPONENT, 'FullNativeComponent.js': FULL_NATIVE_COMPONENT, 'FullTypedNativeComponent.js': FULL_NATIVE_COMPONENT_WITH_TYPE_EXPORT, + 'CommandsWithSimpleCoverageNativeComponent.js': COMMANDS_WITH_SIMPLE_COVERAGE, + 'CommandsWithComplexCoverageNativeComponent.js': + COMMANDS_WITH_COMPLEX_COVERAGE, + 'CommandsWithTypeCastCoverageNativeComponent.js': + COMMANDS_WITH_TYPE_CAST_COVERAGE, }; diff --git a/packages/babel-plugin-codegen/__tests__/__snapshots__/index-test.js.snap b/packages/babel-plugin-codegen/__tests__/__snapshots__/index-test.js.snap index 94e334ad3dae..c038ee55988e 100644 --- a/packages/babel-plugin-codegen/__tests__/__snapshots__/index-test.js.snap +++ b/packages/babel-plugin-codegen/__tests__/__snapshots__/index-test.js.snap @@ -1,5 +1,77 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Babel plugin inline view configs can inline config for CommandsWithComplexCoverageNativeComponent.js 1`] = ` +"// @flow + +const codegenNativeCommands = require('codegenNativeCommands'); +const codegenNativeComponent = require('codegenNativeComponent'); +import type { ViewProps } from 'ViewPropTypes'; +import type { NativeComponentType } from 'codegenNativeComponent'; +type ModuleProps = $ReadOnly<{| + ...ViewProps +|}>; +type NativeType = NativeComponentType; +interface NativeCommands { + +seek: (viewRef: React.ElementRef, position: number) => void, + +stop: (viewRef: React.ElementRef) => void, +} +const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry'); +let nativeComponentName = 'Module'; +export const __INTERNAL_VIEW_CONFIG = { + uiViewClassName: \\"Module\\", + validAttributes: {} +}; +export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG);" +`; + +exports[`Babel plugin inline view configs can inline config for CommandsWithSimpleCoverageNativeComponent.js 1`] = ` +"// @flow + +const codegenNativeCommands = require('codegenNativeCommands'); +const codegenNativeComponent = require('codegenNativeComponent'); +import type { ViewProps } from 'ViewPropTypes'; +import type { NativeComponentType } from 'codegenNativeComponent'; +type ModuleProps = $ReadOnly<{| + ...ViewProps +|}>; +type NativeType = NativeComponentType; +interface NativeCommands { + +pause: (viewRef: React.ElementRef) => void, + +play: (viewRef: React.ElementRef) => void, +} +const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry'); +let nativeComponentName = 'Module'; +export const __INTERNAL_VIEW_CONFIG = { + uiViewClassName: \\"Module\\", + validAttributes: {} +}; +export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG);" +`; + +exports[`Babel plugin inline view configs can inline config for CommandsWithTypeCastCoverageNativeComponent.js 1`] = ` +"// @flow + +const codegenNativeCommands = require('codegenNativeCommands'); +const codegenNativeComponent = require('codegenNativeComponent'); +import type { ViewProps } from 'ViewPropTypes'; +import type { NativeComponentType } from 'codegenNativeComponent'; +type ModuleProps = $ReadOnly<{| + ...ViewProps +|}>; +type NativeType = NativeComponentType; +interface NativeCommands { + +mute: (viewRef: React.ElementRef) => void, + +unmute: (viewRef: React.ElementRef) => void, +} +const NativeComponentRegistry = require('react-native/Libraries/NativeComponent/NativeComponentRegistry'); +let nativeComponentName = 'Module'; +export const __INTERNAL_VIEW_CONFIG = { + uiViewClassName: \\"Module\\", + validAttributes: {} +}; +export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG);" +`; + exports[`Babel plugin inline view configs can inline config for FullNativeComponent.js 1`] = ` "// @flow @@ -153,6 +225,61 @@ exports[`Babel plugin inline view configs fails on inline config for CommandsExp 24 |" `; +exports[`Babel plugin inline view configs fails on inline config for CommandsWithComplexCoverageInvalidNativeComponent.js 1`] = ` +"/CommandsWithComplexCoverageInvalidNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands. + 14 | + 15 | // Complex coverage instrumentation with invalid nested structure - should fail +> 16 | export const Commands = ( + | ^ + 17 | cov_xyz789().f[1]++, + 18 | cov_xyz789().s[2]++, + 19 | {" +`; + +exports[`Babel plugin inline view configs fails on inline config for CommandsWithCoverageInvalidNativeComponent.js 1`] = ` +"/CommandsWithCoverageInvalidNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands. + 14 | + 15 | // Coverage instrumentation of invalid Commands export - should still fail +> 16 | export const Commands = (cov_1234567890().s[0]++, { + | ^ + 17 | hotspotUpdate: () => {}, + 18 | scrollTo: () => {}, + 19 | });" +`; + +exports[`Babel plugin inline view configs fails on inline config for CommandsWithCoverageTypeCastInvalidNativeComponent.js 1`] = ` +"/CommandsWithCoverageTypeCastInvalidNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands. + 19 | + 20 | // Coverage instrumentation with type cast but wrong function - should fail +> 21 | export const Commands: NativeCommands = (cov_cast123().s[0]++, invalidFunction({ + | ^ + 22 | supportedCommands: ['pause', 'play'], + 23 | })); + 24 |" +`; + +exports[`Babel plugin inline view configs fails on inline config for CommandsWithCoverageWrongFunctionNativeComponent.js 1`] = ` +"/CommandsWithCoverageWrongFunctionNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands. + 14 | + 15 | // Coverage instrumentation of wrong function call - should fail +> 16 | export const Commands = (cov_abcdef123().s[0]++, someOtherFunction({ + | ^ + 17 | supportedCommands: ['pause', 'play'], + 18 | })); + 19 |" +`; + +exports[`Babel plugin inline view configs fails on inline config for CommandsWithCoverageWrongNameNativeComponent.js 1`] = ` +"/CommandsWithCoverageWrongNameNativeComponent.js: Native commands must be exported with the name 'Commands' + 20 | + 21 | // Coverage instrumentation with correct function but wrong export name - should fail +> 22 | export const WrongName = (cov_wrong123().s[0]++, codegenNativeCommands({ + | ^ + 23 | supportedCommands: ['pause', 'play'], + 24 | })); + 25 |" +`; + exports[`Babel plugin inline view configs fails on inline config for OtherCommandsExportNativeComponent.js 1`] = ` "/OtherCommandsExportNativeComponent.js: 'Commands' is a reserved export and may only be used to export the result of codegenNativeCommands. 17 | } diff --git a/packages/babel-plugin-codegen/index.js b/packages/babel-plugin-codegen/index.js index 37302dfd0503..06a4939ad225 100644 --- a/packages/babel-plugin-codegen/index.js +++ b/packages/babel-plugin-codegen/index.js @@ -102,6 +102,58 @@ function isCodegenDeclaration(declaration) { return false; } +function isCodegenNativeCommandsDeclaration(declaration) { + if (!declaration) { + return false; + } + + // Handle direct calls: codegenNativeCommands() + if ( + declaration.type === 'CallExpression' && + declaration.callee && + declaration.callee.type === 'Identifier' && + declaration.callee.name === 'codegenNativeCommands' + ) { + return true; + } + + // Handle coverage instrumentation: (cov_xxx().s[0]++, codegenNativeCommands()) + if (declaration.type === 'SequenceExpression' && declaration.expressions) { + // Get the last expression in the sequence (the actual function call) + const lastExpression = + declaration.expressions[declaration.expressions.length - 1]; + // Recursively check if the last expression is a valid codegenNativeCommands call + return isCodegenNativeCommandsDeclaration(lastExpression); + } + + // Handle Flow type casts: (codegenNativeCommands(): NativeCommands) + if ( + (declaration.type === 'TypeCastExpression' || + declaration.type === 'AsExpression') && + declaration.expression && + declaration.expression.type === 'CallExpression' && + declaration.expression.callee && + declaration.expression.callee.type === 'Identifier' && + declaration.expression.callee.name === 'codegenNativeCommands' + ) { + return true; + } + + // Handle TypeScript assertions: codegenNativeCommands() as NativeCommands + if ( + declaration.type === 'TSAsExpression' && + declaration.expression && + declaration.expression.type === 'CallExpression' && + declaration.expression.callee && + declaration.expression.callee.type === 'Identifier' && + declaration.expression.callee.name === 'codegenNativeCommands' + ) { + return true; + } + + return false; +} + module.exports = function ({parse, types: t}) { return { pre(state) { @@ -125,12 +177,12 @@ module.exports = function ({parse, types: t}) { const firstDeclaration = path.node.declaration.declarations[0]; if (firstDeclaration.type === 'VariableDeclarator') { - if ( - firstDeclaration.init && - firstDeclaration.init.type === 'CallExpression' && - firstDeclaration.init.callee.type === 'Identifier' && - firstDeclaration.init.callee.name === 'codegenNativeCommands' - ) { + // Check if this is a valid codegenNativeCommands call, handling type annotations + const isValidCommandsExport = isCodegenNativeCommandsDeclaration( + firstDeclaration.init, + ); + + if (isValidCommandsExport) { if ( firstDeclaration.id.type === 'Identifier' && firstDeclaration.id.name !== 'Commands' From fb114da8a47968a9bcf189ef93b0ae19e7c612b4 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 21 Aug 2025 12:25:38 -0700 Subject: [PATCH 0012/1228] Remove unused internal `UIManagerModuleListener` (#53404) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53404 This interface is part of Legacy Architecture. No one is using it either internally or externally, so it's safe to remove now. Interface was also `internal` so this is not a breaking change. Changelog: [Internal] [Changed] - Reviewed By: mdvacca Differential Revision: D80714721 fbshipit-source-id: 82cc6152af7d7980119d2eda704ae50d8e81fd2b --- .../ReactAndroid/api/ReactAndroid.api | 2 -- .../react/uimanager/UIManagerModule.java | 15 ------------ .../uimanager/UIManagerModuleListener.kt | 24 ------------------- 3 files changed, 41 deletions(-) delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleListener.kt diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 2e5e7b94b3fb..3398730f3aca 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -4362,7 +4362,6 @@ public class com/facebook/react/uimanager/UIManagerModule : com/facebook/react/b public fun addRootView (Landroid/view/View;Lcom/facebook/react/bridge/WritableMap;)I public fun addUIBlock (Lcom/facebook/react/uimanager/UIBlock;)V public fun addUIManagerEventListener (Lcom/facebook/react/bridge/UIManagerListener;)V - public fun addUIManagerListener (Lcom/facebook/react/uimanager/UIManagerModuleListener;)V public fun clearJSResponder ()V public fun configureNextLayoutAnimation (Lcom/facebook/react/bridge/ReadableMap;Lcom/facebook/react/bridge/Callback;Lcom/facebook/react/bridge/Callback;)V public static fun createConstants (Ljava/util/List;Ljava/util/Map;Ljava/util/Map;)Ljava/util/Map; @@ -4399,7 +4398,6 @@ public class com/facebook/react/uimanager/UIManagerModule : com/facebook/react/b public fun receiveEvent (ILjava/lang/String;Lcom/facebook/react/bridge/WritableMap;)V public fun removeRootView (I)V public fun removeUIManagerEventListener (Lcom/facebook/react/bridge/UIManagerListener;)V - public fun removeUIManagerListener (Lcom/facebook/react/uimanager/UIManagerModuleListener;)V public fun resolveCustomDirectEventName (Ljava/lang/String;)Ljava/lang/String; public fun resolveRootTagFromReactTag (I)I public fun resolveView (I)Landroid/view/View; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java index fab7bf4f808f..50034a68456b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java @@ -52,7 +52,6 @@ import com.facebook.react.uimanager.internal.LegacyArchitectureShadowNodeLogger; import com.facebook.systrace.Systrace; import com.facebook.systrace.SystraceMessage; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; @@ -117,7 +116,6 @@ public interface CustomEventNamesResolver { private final ViewManagerRegistry mViewManagerRegistry; private final UIImplementation mUIImplementation; private final MemoryTrimCallback mMemoryTrimCallback = new MemoryTrimCallback(); - private final List mListeners = new ArrayList<>(); private final CopyOnWriteArrayList mUIManagerListeners = new CopyOnWriteArrayList<>(); @@ -687,9 +685,6 @@ public void onBatchComplete() { SystraceMessage.beginSection(Systrace.TRACE_TAG_REACT, "onBatchCompleteUI") .arg("BatchId", batchId) .flush(); - for (UIManagerModuleListener listener : mListeners) { - listener.willDispatchViewUpdates(this); - } for (UIManagerListener listener : mUIManagerListeners) { listener.willDispatchViewUpdates(this); } @@ -757,16 +752,6 @@ public void prependUIBlock(UIBlock block) { mUIImplementation.prependUIBlock(block); } - @Deprecated - public void addUIManagerListener(UIManagerModuleListener listener) { - mListeners.add(listener); - } - - @Deprecated - public void removeUIManagerListener(UIManagerModuleListener listener) { - mListeners.remove(listener); - } - public void addUIManagerEventListener(UIManagerListener listener) { mUIManagerListeners.add(listener); } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleListener.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleListener.kt deleted file mode 100644 index ddef0ad2499a..000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleListener.kt +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -@file:Suppress("DEPRECATION") - -package com.facebook.react.uimanager - -import com.facebook.react.common.annotations.internal.LegacyArchitecture -import com.facebook.react.common.annotations.internal.LegacyArchitectureLogLevel - -/** Listener used to hook into the UIManager update process. */ -@Deprecated("Use UIManagerListener instead. This will be deleted in some future release.") -@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR) -internal interface UIManagerModuleListener { - /** - * Called right before view updates are dispatched at the end of a batch. This is useful if a - * module needs to add UIBlocks to the queue before it is flushed. - */ - fun willDispatchViewUpdates(uiManager: UIManagerModule) -} From 046ff8e58bed5da0f19adc860b327c7248b19f48 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 21 Aug 2025 14:05:47 -0700 Subject: [PATCH 0013/1228] Make OnBatchCompleteListener interface internal (#53409) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53409 This interface is public and is part of Legacy Architecture. Having this interface as `public` was a mistake, as users can't really do much with it. There is only one old library that is going to be affected by this change: https://github.com/spoke-ph/react-native-threads The library appears unmaintained since RN 0.69 + no NewArch support so I won't consider this a breaking change given this will land in 0.82. I'm making it internal so we can remove it more easily later. Changelog: [Android] [Changed] - Make OnBatchCompleteListener interface internal Differential Revision: D80715625 fbshipit-source-id: 94fe80eeba95222deab7ca89c5fcafca8fcee0b7 --- packages/react-native/ReactAndroid/api/ReactAndroid.api | 4 ---- .../java/com/facebook/react/bridge/OnBatchCompleteListener.kt | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 3398730f3aca..a1a8fa8202b6 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -1018,10 +1018,6 @@ public abstract interface class com/facebook/react/bridge/NotThreadSafeBridgeIdl public abstract fun onTransitionToBridgeIdle ()V } -public abstract interface class com/facebook/react/bridge/OnBatchCompleteListener { - public abstract fun onBatchComplete ()V -} - public abstract interface class com/facebook/react/bridge/PerformanceCounter { public abstract fun getPerformanceCounters ()Ljava/util/Map; public abstract fun profileNextBatch ()V diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/OnBatchCompleteListener.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/OnBatchCompleteListener.kt index b02ba5cace1e..3d21bece5830 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/OnBatchCompleteListener.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/OnBatchCompleteListener.kt @@ -16,6 +16,6 @@ import com.facebook.react.common.annotations.internal.LegacyArchitectureLogLevel message = "This class is part of Legacy Architecture and will be removed in a future release", level = DeprecationLevel.WARNING, ) -public fun interface OnBatchCompleteListener { - public fun onBatchComplete() +internal fun interface OnBatchCompleteListener { + fun onBatchComplete() } From a2a73739d80c6a1d120e16246614ed4894de7b8a Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Thu, 21 Aug 2025 14:18:27 -0700 Subject: [PATCH 0014/1228] fix: use ThreadId when event is captured, not when transformed (#53411) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53411 # Changelog: [Internal] This regressed after D80263154, when we introduced a local struct for PerforamanceTracerEvent. We should use id of the thread where event was captured (registered), not where the transform to TraceEvent happened. This makes sure that events like Event Loop tick or Microtasks phase tick are correctly point to JavaScript thread, not the thread where the transform could've taken place. Reviewed By: sbuggay Differential Revision: D80728931 fbshipit-source-id: d3af16e68adece9ebc37368fec2b8a17c1293b4b --- .../tracing/PerformanceTracer.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp index 4c8e8cf7e5e0..238003fd3931 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.cpp @@ -190,6 +190,7 @@ void PerformanceTracer::reportTimeStamp( .trackName = std::move(trackName), .trackGroup = std::move(trackGroup), .color = std::move(color), + .threadId = getCurrentThreadId(), }); } @@ -399,7 +400,7 @@ void PerformanceTracer::enqueueTraceEventsFromPerformanceTracerEvent( .ph = 'X', .ts = event.start, .pid = processId_, - .tid = getCurrentThreadId(), + .tid = event.threadId, .dur = event.end - event.start, }); }, @@ -410,7 +411,7 @@ void PerformanceTracer::enqueueTraceEventsFromPerformanceTracerEvent( .ph = 'X', .ts = event.start, .pid = processId_, - .tid = getCurrentThreadId(), + .tid = event.threadId, .dur = event.end - event.start, }); }, @@ -429,7 +430,7 @@ void PerformanceTracer::enqueueTraceEventsFromPerformanceTracerEvent( .ph = 'I', .ts = event.start, .pid = processId_, - .tid = getCurrentThreadId(), + .tid = event.threadId, .args = std::move(eventArgs), }); }, @@ -449,7 +450,7 @@ void PerformanceTracer::enqueueTraceEventsFromPerformanceTracerEvent( .ph = 'b', .ts = event.start, .pid = processId_, - .tid = getCurrentThreadId(), + .tid = event.threadId, .args = std::move(beginEventArgs), }); events.emplace_back(TraceEvent{ @@ -459,7 +460,7 @@ void PerformanceTracer::enqueueTraceEventsFromPerformanceTracerEvent( .ph = 'e', .ts = event.start + event.duration, .pid = processId_, - .tid = getCurrentThreadId(), + .tid = event.threadId, }); }, [&](PerformanceTracerEventTimeStamp&& event) { @@ -500,7 +501,7 @@ void PerformanceTracer::enqueueTraceEventsFromPerformanceTracerEvent( .ph = 'I', .ts = event.createdAt, .pid = processId_, - .tid = getCurrentThreadId(), + .tid = event.threadId, .args = folly::dynamic::object("data", std::move(data)), }); }, From ffe52b75a3c3c244c046bf88a128afc1dbe5aac9 Mon Sep 17 00:00:00 2001 From: Nikita Lutsenko Date: Thu, 21 Aug 2025 14:59:24 -0700 Subject: [PATCH 0015/1228] xplat/js/react-native-github/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp (#53412) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53412 Changelog: [General][Internal] - Encapsulated internal class into anonymous namespace to ensure no collisions with public symbols. Differential Revision: D80689084 fbshipit-source-id: 53ebd8a16a3c217efead0bfe91f66bd50bb6dd2f --- .../ReactCommon/react/renderer/mounting/Differentiator.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp index 001238d6960b..611180e6a20a 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp @@ -126,6 +126,8 @@ static void calculateShadowViewMutations( const CullingContext& oldCullingContext = {}, const CullingContext& newCullingContext = {}); +namespace { + struct OrderedMutationInstructionContainer { ShadowViewMutation::List createMutations{}; ShadowViewMutation::List deleteMutations{}; @@ -136,6 +138,8 @@ struct OrderedMutationInstructionContainer { ShadowViewMutation::List destructiveDownwardMutations{}; }; +} // namespace + static void updateMatchedPairSubtrees( ViewNodePairScope& scope, OrderedMutationInstructionContainer& mutationContainer, From da9136f587fa3b2be708193300d4e55537855e19 Mon Sep 17 00:00:00 2001 From: Marco Wang Date: Thu, 21 Aug 2025 16:46:23 -0700 Subject: [PATCH 0016/1228] Turn on null strict comparison check for xplat js (#53379) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53379 X-link: https://github.com/facebook/react/pull/34240 Changelog: [Internal] Reviewed By: SamChou19815 Differential Revision: D80648362 fbshipit-source-id: cc47ae207f29a3ddb68bc0e029b8773f89503c52 --- .../typescript/components/componentsUtils.js | 4 ++++ .../Libraries/ActionSheetIOS/ActionSheetIOS.js | 4 ++++ .../react-native/Libraries/Blob/URLSearchParams.js | 2 ++ packages/react-native/Libraries/Share/Share.js | 4 ++++ .../react-native/Libraries/Utilities/HMRClient.js | 2 ++ .../Utilities/deepFreezeAndThrowOnMutationInDev.js | 2 ++ .../Libraries/WebSocket/WebSocketInterceptor.js | 2 ++ .../js/examples/Text/TextSharedExamples.js | 14 +++++++++----- .../src/getFantomRenderedOutput.js | 2 ++ 9 files changed, 31 insertions(+), 5 deletions(-) diff --git a/packages/react-native-codegen/src/parsers/typescript/components/componentsUtils.js b/packages/react-native-codegen/src/parsers/typescript/components/componentsUtils.js index f178527433c2..71847b102cfd 100644 --- a/packages/react-native-codegen/src/parsers/typescript/components/componentsUtils.js +++ b/packages/react-native-codegen/src/parsers/typescript/components/componentsUtils.js @@ -350,6 +350,8 @@ function setDefaultValue( common.default = ((defaultValue ? defaultValue : 0): number); break; case 'FloatTypeAnnotation': + /* $FlowFixMe[invalid-compare] Error discovered during Constant Condition + * roll out. See https://fburl.com/workplace/5whu3i34. */ common.default = ((defaultValue === null ? null : defaultValue @@ -357,6 +359,8 @@ function setDefaultValue( : 0): number | null); break; case 'BooleanTypeAnnotation': + /* $FlowFixMe[invalid-compare] Error discovered during Constant Condition + * roll out. See https://fburl.com/workplace/5whu3i34. */ common.default = defaultValue === null ? null : !!defaultValue; break; case 'StringTypeAnnotation': diff --git a/packages/react-native/Libraries/ActionSheetIOS/ActionSheetIOS.js b/packages/react-native/Libraries/ActionSheetIOS/ActionSheetIOS.js index 3ed034026461..84e274c18343 100644 --- a/packages/react-native/Libraries/ActionSheetIOS/ActionSheetIOS.js +++ b/packages/react-native/Libraries/ActionSheetIOS/ActionSheetIOS.js @@ -77,6 +77,8 @@ const ActionSheetIOS = { callback: (buttonIndex: number) => void, ) { invariant( + /* $FlowFixMe[invalid-compare] Error discovered during Constant Condition + * roll out. See https://fburl.com/workplace/5whu3i34. */ typeof options === 'object' && options !== null, 'Options must be a valid object', ); @@ -162,6 +164,8 @@ const ActionSheetIOS = { successCallback: Function | ((success: boolean, method: ?string) => void), ) { invariant( + /* $FlowFixMe[invalid-compare] Error discovered during Constant Condition + * roll out. See https://fburl.com/workplace/5whu3i34. */ typeof options === 'object' && options !== null, 'Options must be a valid object', ); diff --git a/packages/react-native/Libraries/Blob/URLSearchParams.js b/packages/react-native/Libraries/Blob/URLSearchParams.js index c17829849aee..1560fc2b3359 100644 --- a/packages/react-native/Libraries/Blob/URLSearchParams.js +++ b/packages/react-native/Libraries/Blob/URLSearchParams.js @@ -18,6 +18,8 @@ export class URLSearchParams { } constructor(params?: Record | string | [string, string][]) { + /* $FlowFixMe[invalid-compare] Error discovered during Constant Condition + * roll out. See https://fburl.com/workplace/5whu3i34. */ if (params === null) { return; } diff --git a/packages/react-native/Libraries/Share/Share.js b/packages/react-native/Libraries/Share/Share.js index f23720e2df64..adfc6efc7508 100644 --- a/packages/react-native/Libraries/Share/Share.js +++ b/packages/react-native/Libraries/Share/Share.js @@ -83,6 +83,8 @@ class Share { options?: ShareOptions = {}, ): Promise<{action: string, activityType: ?string}> { invariant( + /* $FlowFixMe[invalid-compare] Error discovered during Constant Condition + * roll out. See https://fburl.com/workplace/5whu3i34. */ typeof content === 'object' && content !== null, 'Content to share must be a valid object', ); @@ -91,6 +93,8 @@ class Share { 'At least one of URL or message is required', ); invariant( + /* $FlowFixMe[invalid-compare] Error discovered during Constant Condition + * roll out. See https://fburl.com/workplace/5whu3i34. */ typeof options === 'object' && options !== null, 'Options must be a valid object', ); diff --git a/packages/react-native/Libraries/Utilities/HMRClient.js b/packages/react-native/Libraries/Utilities/HMRClient.js index 3cde3832f741..6d16b6b973f3 100644 --- a/packages/react-native/Libraries/Utilities/HMRClient.js +++ b/packages/react-native/Libraries/Utilities/HMRClient.js @@ -165,6 +165,8 @@ const HMRClient: HMRClientNativeInterface = { // Moving to top gives errors due to NativeModules not being initialized const DevLoadingView = require('./DevLoadingView').default; + /* $FlowFixMe[invalid-compare] Error discovered during Constant Condition + * roll out. See https://fburl.com/workplace/5whu3i34. */ const serverHost = port !== null && port !== '' ? `${host}:${port}` : host; const serverScheme = scheme; diff --git a/packages/react-native/Libraries/Utilities/deepFreezeAndThrowOnMutationInDev.js b/packages/react-native/Libraries/Utilities/deepFreezeAndThrowOnMutationInDev.js index 63701e7bca72..83b79a171829 100644 --- a/packages/react-native/Libraries/Utilities/deepFreezeAndThrowOnMutationInDev.js +++ b/packages/react-native/Libraries/Utilities/deepFreezeAndThrowOnMutationInDev.js @@ -33,6 +33,8 @@ function deepFreezeAndThrowOnMutationInDev>( if (__DEV__) { if ( typeof object !== 'object' || + /* $FlowFixMe[invalid-compare] Error discovered during Constant Condition + * roll out. See https://fburl.com/workplace/5whu3i34. */ object === null || Object.isFrozen(object) || Object.isSealed(object) diff --git a/packages/react-native/Libraries/WebSocket/WebSocketInterceptor.js b/packages/react-native/Libraries/WebSocket/WebSocketInterceptor.js index 796c2c89b67e..ea7f371ad1ef 100644 --- a/packages/react-native/Libraries/WebSocket/WebSocketInterceptor.js +++ b/packages/react-native/Libraries/WebSocket/WebSocketInterceptor.js @@ -232,6 +232,8 @@ const WebSocketInterceptor = { _arrayBufferToString(data: string): ArrayBuffer | string { const value = base64.toByteArray(data).buffer; + /* $FlowFixMe[invalid-compare] Error discovered during Constant Condition + * roll out. See https://fburl.com/workplace/5whu3i34. */ if (value === undefined || value === null) { return '(no value)'; } diff --git a/packages/rn-tester/js/examples/Text/TextSharedExamples.js b/packages/rn-tester/js/examples/Text/TextSharedExamples.js index e45fac6545ca..fb483463394c 100644 --- a/packages/rn-tester/js/examples/Text/TextSharedExamples.js +++ b/packages/rn-tester/js/examples/Text/TextSharedExamples.js @@ -28,11 +28,15 @@ function InlineView(props: { Child Child - {props !== null && props.long === true && ( - - aaaa a aaaa aaaaaa aaa a a a aaaaa sdsds dsdSAD asd ASDasd ASDas - - )} + { + /* $FlowFixMe[invalid-compare] Error discovered during Constant + * Condition roll out. See https://fburl.com/workplace/5whu3i34. */ + props !== null && props.long === true && ( + + aaaa a aaaa aaaaaa aaa a a a aaaaa sdsds dsdSAD asd ASDasd ASDas + + ) + } ); diff --git a/private/react-native-fantom/src/getFantomRenderedOutput.js b/private/react-native-fantom/src/getFantomRenderedOutput.js index b51873446f2d..a0599ef1365d 100644 --- a/private/react-native-fantom/src/getFantomRenderedOutput.js +++ b/private/react-native-fantom/src/getFantomRenderedOutput.js @@ -136,6 +136,8 @@ function convertRawJsonToJSX( actualJSON: FantomJsonObject | $ReadOnlyArray, ): React.Node { let actualJSX; + /* $FlowFixMe[invalid-compare] Error discovered during Constant Condition + * roll out. See https://fburl.com/workplace/5whu3i34. */ if (actualJSON === null || typeof actualJSON === 'string') { actualJSX = actualJSON; } else if (Array.isArray(actualJSON)) { From 646945c2f2e1fe226d3b093cc0e75102eadbdfa1 Mon Sep 17 00:00:00 2001 From: Chi Tsai Date: Thu, 21 Aug 2025 17:35:55 -0700 Subject: [PATCH 0017/1228] Add deleteProperty API (#52911) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/52911 Add a new `deleteProperty` API to JSI. As the name implies, allows users to delete properties from Objects through JSI. The default implementation uses `Reflect.deleteProperty.` For the `PropNameID` overload, convert the propNameID to a String and pass into the `deleteProperty` function. Changelog: [Internal] Reviewed By: dannysu Differential Revision: D79120814 fbshipit-source-id: e30f383247d94bb5971e4909f004c75e8165adda --- .../ReactCommon/jsi/jsi/decorator.h | 27 +++++++++ .../ReactCommon/jsi/jsi/jsi-inl.h | 17 ++++++ .../react-native/ReactCommon/jsi/jsi/jsi.cpp | 31 ++++++++++ .../react-native/ReactCommon/jsi/jsi/jsi.h | 20 +++++++ .../ReactCommon/jsi/jsi/test/testlib.cpp | 59 +++++++++++++++++++ 5 files changed, 154 insertions(+) diff --git a/packages/react-native/ReactCommon/jsi/jsi/decorator.h b/packages/react-native/ReactCommon/jsi/jsi/decorator.h index 6410257b250b..d736aeaaf612 100644 --- a/packages/react-native/ReactCommon/jsi/jsi/decorator.h +++ b/packages/react-native/ReactCommon/jsi/jsi/decorator.h @@ -328,6 +328,18 @@ class RuntimeDecorator : public Base, private jsi::Instrumentation { plain_.setPropertyValue(o, name, value); }; + void deleteProperty(const Object& object, const PropNameID& name) override { + plain_.deleteProperty(object, name); + } + + void deleteProperty(const Object& object, const String& name) override { + plain_.deleteProperty(object, name); + } + + void deleteProperty(const Object& object, const Value& name) override { + plain_.deleteProperty(object, name); + } + bool isArray(const Object& o) const override { return plain_.isArray(o); }; @@ -852,6 +864,21 @@ class WithRuntimeDecorator : public RuntimeDecorator { RD::setPropertyValue(o, name, value); }; + void deleteProperty(const Object& object, const PropNameID& name) override { + Around around{with_}; + RD::deleteProperty(object, name); + } + + void deleteProperty(const Object& object, const String& name) override { + Around around{with_}; + RD::deleteProperty(object, name); + } + + void deleteProperty(const Object& object, const Value& name) override { + Around around{with_}; + RD::deleteProperty(object, name); + } + bool isArray(const Object& o) const override { Around around{with_}; return RD::isArray(o); diff --git a/packages/react-native/ReactCommon/jsi/jsi/jsi-inl.h b/packages/react-native/ReactCommon/jsi/jsi/jsi-inl.h index f859db9d7865..ed4b6fcd8df8 100644 --- a/packages/react-native/ReactCommon/jsi/jsi/jsi-inl.h +++ b/packages/react-native/ReactCommon/jsi/jsi/jsi-inl.h @@ -148,6 +148,23 @@ void Object::setProperty(Runtime& runtime, const PropNameID& name, T&& value) runtime, name, detail::toValue(runtime, std::forward(value))); } +inline void Object::deleteProperty(Runtime& runtime, const char* name) const { + deleteProperty(runtime, String::createFromAscii(runtime, name)); +} + +inline void Object::deleteProperty(Runtime& runtime, const String& name) const { + runtime.deleteProperty(*this, name); +} + +inline void Object::deleteProperty(Runtime& runtime, const PropNameID& name) + const { + runtime.deleteProperty(*this, name); +} + +inline void Object::deleteProperty(Runtime& runtime, const Value& name) const { + runtime.deleteProperty(*this, name); +} + inline Array Object::getArray(Runtime& runtime) const& { assert(runtime.isArray(*this)); (void)runtime; // when assert is disabled we need to mark this as used diff --git a/packages/react-native/ReactCommon/jsi/jsi/jsi.cpp b/packages/react-native/ReactCommon/jsi/jsi/jsi.cpp index 65c57dd37370..5e2b24cf67b3 100644 --- a/packages/react-native/ReactCommon/jsi/jsi/jsi.cpp +++ b/packages/react-native/ReactCommon/jsi/jsi/jsi.cpp @@ -416,6 +416,37 @@ Object Runtime::createObjectWithPrototype(const Value& prototype) { return createFn.call(*this, prototype).asObject(*this); } +void Runtime::deleteProperty(const Object& object, const PropNameID& name) { + auto nameStr = String::createFromUtf16(*this, name.utf16(*this)); + auto deleteFn = global() + .getPropertyAsObject(*this, "Reflect") + .getPropertyAsFunction(*this, "deleteProperty"); + auto res = deleteFn.call(*this, object, nameStr).getBool(); + if (!res) { + throw JSError(*this, "Failed to delete property"); + } +} + +void Runtime::deleteProperty(const Object& object, const String& name) { + auto deleteFn = global() + .getPropertyAsObject(*this, "Reflect") + .getPropertyAsFunction(*this, "deleteProperty"); + auto res = deleteFn.call(*this, object, name).getBool(); + if (!res) { + throw JSError(*this, "Failed to delete property"); + } +} + +void Runtime::deleteProperty(const Object& object, const Value& name) { + auto deleteFn = global() + .getPropertyAsObject(*this, "Reflect") + .getPropertyAsFunction(*this, "deleteProperty"); + auto res = deleteFn.call(*this, object, name).getBool(); + if (!res) { + throw JSError(*this, "Failed to delete property"); + } +} + void Runtime::setRuntimeDataImpl( const UUID& uuid, const void* data, diff --git a/packages/react-native/ReactCommon/jsi/jsi/jsi.h b/packages/react-native/ReactCommon/jsi/jsi/jsi.h index ed8d37f265bf..938ee28ab22b 100644 --- a/packages/react-native/ReactCommon/jsi/jsi/jsi.h +++ b/packages/react-native/ReactCommon/jsi/jsi/jsi.h @@ -486,6 +486,10 @@ class JSI_EXPORT Runtime : public ICast { virtual void setPropertyValue(const Object&, const String& name, const Value& value) = 0; + virtual void deleteProperty(const Object&, const PropNameID& name); + virtual void deleteProperty(const Object&, const String& name); + virtual void deleteProperty(const Object&, const Value& name); + virtual bool isArray(const Object&) const = 0; virtual bool isArrayBuffer(const Object&) const = 0; virtual bool isFunction(const Object&) const = 0; @@ -984,6 +988,22 @@ class JSI_EXPORT Object : public Pointer { template void setProperty(Runtime& runtime, const PropNameID& name, T&& value) const; + /// Delete the property with the given ascii name. Throws if the deletion + /// failed. + void deleteProperty(Runtime& runtime, const char* name) const; + + /// Delete the property with the given String name. Throws if the deletion + /// failed. + void deleteProperty(Runtime& runtime, const String& name) const; + + /// Delete the property with the given PropNameID name. Throws if the deletion + /// failed. + void deleteProperty(Runtime& runtime, const PropNameID& name) const; + + /// Delete the property with the given Value name. Throws if the deletion + /// failed. + void deleteProperty(Runtime& runtime, const Value& name) const; + /// \return true iff JS \c Array.isArray() would return \c true. If /// so, then \c getArray() will succeed. bool isArray(Runtime& runtime) const { diff --git a/packages/react-native/ReactCommon/jsi/jsi/test/testlib.cpp b/packages/react-native/ReactCommon/jsi/jsi/test/testlib.cpp index 4b106621f5a0..195ac28ec4fd 100644 --- a/packages/react-native/ReactCommon/jsi/jsi/test/testlib.cpp +++ b/packages/react-native/ReactCommon/jsi/jsi/test/testlib.cpp @@ -1882,6 +1882,65 @@ TEST_P(JSITest, CastInterface) { EXPECT_TRUE(ptr == nullptr); } +TEST_P(JSITest, DeleteProperty) { + // This Runtime Decorator is used to test the default implementation of + // Runtime::deleteProperty + class RD : public RuntimeDecorator { + public: + explicit RD(Runtime& rt) : RuntimeDecorator(rt) {} + + void deleteProperty(const Object& object, const PropNameID& name) override { + Runtime::deleteProperty(object, name); + } + void deleteProperty(const Object& object, const String& name) override { + Runtime::deleteProperty(object, name); + } + void deleteProperty(const Object& object, const Value& name) override { + Runtime::deleteProperty(object, name); + } + }; + RD rd = RD(rt); + auto obj = eval("obj = {1:2, foo: 'bar', 3: 4, salt:'pepper'}").getObject(rd); + + auto prop = PropNameID::forAscii(rd, "1"); + auto hasRes = obj.hasProperty(rd, prop); + EXPECT_TRUE(hasRes); + obj.deleteProperty(rd, prop); + hasRes = obj.hasProperty(rd, prop); + EXPECT_FALSE(hasRes); + + auto str = String::createFromAscii(rd, "foo"); + hasRes = obj.hasProperty(rd, str); + EXPECT_TRUE(hasRes); + obj.deleteProperty(rd, str); + hasRes = obj.hasProperty(rd, str); + EXPECT_FALSE(hasRes); + + auto valProp = Value(3); + hasRes = obj.hasProperty(rd, "3"); + EXPECT_TRUE(hasRes); + obj.deleteProperty(rd, valProp); + auto getRes = obj.getProperty(rd, "3"); + EXPECT_TRUE(getRes.isUndefined()); + + hasRes = obj.hasProperty(rd, "salt"); + EXPECT_TRUE(hasRes); + obj.deleteProperty(rd, "salt"); + hasRes = obj.hasProperty(rd, "salt"); + EXPECT_FALSE(hasRes); + + obj = eval( + "const obj = {};" + "Object.defineProperty(obj, 'prop', {" + " value: 10," + " configurable: false,});" + "obj;") + .getObject(rd); + EXPECT_THROW(obj.deleteProperty(rd, "prop"), JSError); + hasRes = obj.hasProperty(rd, "prop"); + EXPECT_TRUE(hasRes); +} + INSTANTIATE_TEST_CASE_P( Runtimes, JSITest, From d0744f7e59b628b58a63497e8bf8d8f4cd7fe86d Mon Sep 17 00:00:00 2001 From: Ruslan Shestopalyuk Date: Fri, 22 Aug 2025 01:35:18 -0700 Subject: [PATCH 0018/1228] Use Map as recyclable views container (#53394) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53394 # Changelog: [Internal] - This just fixes a linter warning I noticed when working on related code. Reviewed By: cortinico Differential Revision: D80702545 fbshipit-source-id: fecfed9e9946b971864706306b03b57cd3111aee --- .../src/main/java/com/facebook/react/uimanager/ViewManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java index 9bba8d915c8b..5e23079f36c7 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java @@ -54,7 +54,7 @@ public abstract class ViewManager * null signals that View Recycling is disabled. `enableViewRecycling` must be explicitly called * in a concrete constructor to enable View Recycling per ViewManager. */ - @Nullable private HashMap> mRecyclableViews = null; + @Nullable private Map> mRecyclableViews = null; public ViewManager() { super(null); From 3292cb1dc93f95ad0cc3d3e98d8b8805173dac0a Mon Sep 17 00:00:00 2001 From: Alex Hunt Date: Fri, 22 Aug 2025 03:09:50 -0700 Subject: [PATCH 0019/1228] Fix incorrectly hardcoded landingView for open debugger action (#53400) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53400 In D79329081, we accidentally hardcoded the `landingView` parameter in the default Dev Menu handler to open React Native DevTools. Reset this. Changelog: [Internal] Reviewed By: hoxyq Differential Revision: D80710487 fbshipit-source-id: 9066ffafcb17a6ff46650f7081d9a662f186d995 --- .../java/com/facebook/react/devsupport/DevSupportManagerBase.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.kt index 6f718b79757b..c5f83c9ee904 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.kt @@ -927,7 +927,7 @@ public abstract class DevSupportManagerBase( devServerHelper.openDebugger( currentReactContext, applicationContext.getString(R.string.catalyst_open_debugger_error), - ChromeDevToolsViewKeys.Performance.value, + null, ) } From 8c444f773a44e8554745c9cfc1451083c12b00e3 Mon Sep 17 00:00:00 2001 From: Christian Falch Date: Fri, 22 Aug 2025 03:23:24 -0700 Subject: [PATCH 0020/1228] aligned symbol folders with RNdeps (#53354) Summary: After fixing an isssue with ReactnativeDependencies and how it built symbols (https://github.com/facebook/react-native/issues/53353) this commit will align the output of the Symbols folder for the two frameworks. Previously we had an output in the Symbols folder that looked like this (from a local build on my machine) - catalyst - iphone - iphonesimulator After this we now have the more correct arcitecture names on these folders: - ios-arm64 - ios-arm64_x86_64-simulator - ios-arm64_x86_64-maccatalyst This is in line with how the ReactNativeDependencies Symbol folder is set up. ## Changelog: [IOS] [FIXED] - Aligned Symbols folder in React.xcframework symbols with ReactNativeDependencies.xcframework symbols. Pull Request resolved: https://github.com/facebook/react-native/pull/53354 Test Plan: Nightlies Reviewed By: cortinico Differential Revision: D80692098 Pulled By: cipolleschi fbshipit-source-id: e952b087d5dbdeb929b45d9e6d3d7e077c9d05cc --- .../scripts/ios-prebuild/xcframework.js | 93 +++++++++++++------ 1 file changed, 66 insertions(+), 27 deletions(-) diff --git a/packages/react-native/scripts/ios-prebuild/xcframework.js b/packages/react-native/scripts/ios-prebuild/xcframework.js index 11a6c9bee21f..236103b1a361 100644 --- a/packages/react-native/scripts/ios-prebuild/xcframework.js +++ b/packages/react-native/scripts/ios-prebuild/xcframework.js @@ -185,24 +185,68 @@ function buildXCFrameworks( ); // Copy Symbols to symbols folder - const symbolPaths = frameworkFolders.map(framework => - path.join(framework, `..`, `..`, `React.framework.dSYM`), - ); - - frameworkLog('Copying symbols to symbols folder...'); - const symbolOutput = path.join(outputPath, '..', 'Symbols'); - symbolPaths.forEach(symbol => { - const destination = extractDestinationFromPath(symbol); - const outputFolder = path.join(symbolOutput, destination); - fs.mkdirSync(outputFolder, {recursive: true}); - execSync(`cp -r ${symbol} ${outputFolder}`); - }); + copySymbols(outputPath, frameworkFolders); if (identity) { signXCFramework(identity, outputPath); } } +function copySymbols( + outputPath /*:string*/, + frameworkFolders /*:Array*/, +) { + frameworkLog('Copying symbols to symbols folder...'); + const targetArchFolders = fs + .readdirSync(outputPath) + .map(p => path.join(outputPath, p)) + .filter(folder => { + return ( + fs.statSync(folder).isDirectory() && + !folder.endsWith('Headers') && + !folder.endsWith('Modules') + ); + }); + + const symbolOutput = path.join(outputPath, '..', 'Symbols'); + frameworkFolders.forEach(frameworkFolder => { + // Get archs for current symbol slice + const frameworkPlatforms = getArchsFromFramework( + path.join(frameworkFolder, 'React'), + ); + if (frameworkPlatforms) { + const targetFolder = targetArchFolders.find( + targetArchFolder => + getArchsFromFramework( + path.join(targetArchFolder, 'React.framework', 'React'), + ) === frameworkPlatforms, + ); + if (!targetFolder) { + frameworkLog( + `No target folder found for symbol slice: ${frameworkFolder}`, + 'error', + ); + return; + } + const targetSymbolPath = path.join( + symbolOutput, + path.basename(targetFolder), + ); + const sourceSymbolPath = path.join( + frameworkFolder, + '..', + '..', + 'React.framework.dSYM', + ); + console.log( + ` ${path.relative(outputPath, sourceSymbolPath)} → ${path.basename(targetFolder)}`, + ); + fs.mkdirSync(targetSymbolPath, {recursive: true}); + execSync(`cp -r ${sourceSymbolPath} ${targetSymbolPath}`); + } + }); +} + function linkArchFolders( outputPath /*:string*/, moduleMapFile /*:string*/, @@ -319,22 +363,17 @@ function createModuleMapFile(outputPath /*: string */) { } } -function extractDestinationFromPath(symbolPath /*: string */) /*: string */ { - if (symbolPath.includes('iphoneos')) { - return 'iphoneos'; - } - - if (symbolPath.includes('iphonesimulator')) { - return 'iphonesimulator'; - } - - if (symbolPath.includes('maccatalyst')) { - return 'catalyst'; +function getArchsFromFramework(frameworkPath /*:string*/) { + try { + return execSync(`vtool -show-build ${frameworkPath}|grep platform`) + .toString() + .split('\n') + .map(p => p.trim().split(' ')[1]) + .sort((a, b) => a.localeCompare(b)) + .join(' '); + } catch (error) { + return ''; } - - throw new Error( - `Impossible to extract destination from ${symbolPath}. Valid destinations are iphoneos, iphonesimulator and catalyst.`, - ); } function signXCFramework( From a843119ff1f0e2dfb1d3884ccf255784e3cea1a7 Mon Sep 17 00:00:00 2001 From: Christian Falch Date: Fri, 22 Aug 2025 03:26:54 -0700 Subject: [PATCH 0021/1228] Fix copy symbol files in RNDeps precompile (#53353) Summary: Symbol files wasn't copied correctly when building - as with bundles we did overwrite the files and ended up with only the last symbol file. This commit fixes this by mapping the framework build folder architecture type to the xcframework slices creating the correct file structure under the Symbols folder. - Each slice gets a folder with the architecture name under Symbols containing the dSym folder for that slice - Refactored getting correct architecture folder into a separate function. - Refactored target folder lookup in copyBundles - Removed unused async modifier on function ## Changelog: [IOS] [FIXED] - Fixed how we copy and build the Symbols folder when precompiling ReactNativeDependencies Pull Request resolved: https://github.com/facebook/react-native/pull/53353 Test Plan: Run nightlies and verify that ReactNativeDependencies.framework.dSym files contains symbol files for all architectures. Reviewed By: cortinico Differential Revision: D80692019 Pulled By: cipolleschi fbshipit-source-id: 77983bc29d1965edf3bc0fcbd9cb3177071991d3 --- .../workflows/prebuild-ios-dependencies.yml | 5 +- .../ios-prebuild/compose-framework.js | 140 +++++++++++++----- 2 files changed, 106 insertions(+), 39 deletions(-) diff --git a/.github/workflows/prebuild-ios-dependencies.yml b/.github/workflows/prebuild-ios-dependencies.yml index 3ae438892382..70ca3fe2a5c7 100644 --- a/.github/workflows/prebuild-ios-dependencies.yml +++ b/.github/workflows/prebuild-ios-dependencies.yml @@ -179,8 +179,9 @@ jobs: - name: Compress and Rename dSYM if: steps.restore-xcframework.outputs.cache-hit != 'true' run: | - tar -cz -f packages/react-native/third-party/Symbols/ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz \ - packages/react-native/third-party/Symbols/ReactNativeDependencies.framework.dSYM + cd packages/react-native/third-party/Symbols/ + tar -cz -f ../ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz . + mv ../ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz ./ReactNativeDependencies${{ matrix.flavor }}.framework.dSYM.tar.gz - name: Upload XCFramework Artifact uses: actions/upload-artifact@v4 with: diff --git a/scripts/releases/ios-prebuild/compose-framework.js b/scripts/releases/ios-prebuild/compose-framework.js index e3887b38365e..599622af6bc4 100644 --- a/scripts/releases/ios-prebuild/compose-framework.js +++ b/scripts/releases/ios-prebuild/compose-framework.js @@ -63,17 +63,12 @@ async function createFramework( // Copy bundles into the framework copyBundles(scheme, dependencies, output, frameworkPaths); - // Copy headers to the framework - start by building the Header folder - await copyHeaders(scheme, dependencies, rootFolder); + // Copy Symbols to symbols folder - copy before headers since we're using the folders inside the xcframework + // to get the arch slices. + copySymbols(scheme, output, frameworkPaths); - // Copy Symbols to symbols folder - const symbolPaths = frameworkPaths.map(framework => - path.join(framework, `${scheme}.framework.dSYM`), - ); - console.log('Copying symbols to symbols folder...'); - const symbolOutput = path.join(rootFolder, 'Symbols'); - fs.mkdirSync(symbolOutput, {recursive: true}); - symbolPaths.forEach(symbol => execSync(`cp -r ${symbol} ${symbolOutput}`)); + // Copy headers to the framework - start by building the Header folder + copyHeaders(scheme, dependencies, rootFolder); if (identity) { signXCFramework(identity, output); @@ -84,7 +79,7 @@ async function createFramework( * Copies headers needed from the package to a Header folder that we'll pass to * each framework arch type */ -async function copyHeaders( +function copyHeaders( scheme /*: string */, dependencies /*: $ReadOnlyArray */, rootFolder /*: string */, @@ -134,9 +129,15 @@ function copyBundles( // A bundle is the name of the framework + _ + target name + .bundle. We can // check if the target has a bundle by checking if it defines one or more resources. frameworkPaths.forEach(frameworkPath => { - const frameworkPlatforms = execSync( - `vtool -show-build ${path.join(frameworkPath, 'PackageFrameworks', scheme + '.framework', scheme)}|grep platform`, - ).toString(); + const frameworkPlatforms = getArchsFromFramework( + path.join( + frameworkPath, + 'PackageFrameworks', + scheme + '.framework', + scheme, + ), + ); + dependencies.forEach(dep => { const resources = dep.files.resources; if (!resources || resources.length === 0) { @@ -147,29 +148,25 @@ function copyBundles( const sourceBundlePath = path.join(frameworkPath, bundleName); if (fs.existsSync(sourceBundlePath)) { // Target folder - needs to be copied to the resulting framework - let targetArchFolderFound = false; - targetArchFolders.forEach(targetArchFolder => { - const targetPlatforms = execSync( - `vtool -show-build ${path.join(targetArchFolder, scheme + '.framework', scheme)}|grep platform`, - ).toString(); - - if (targetPlatforms === frameworkPlatforms) { - console.log( - ` ${path.relative(outputFolder, sourceBundlePath)} → ${path.basename(targetArchFolder)}`, - ); - const targetBundlePath = path.join( - targetArchFolder, - `${scheme}.framework`, - bundleName, - ); - - // A bundle is a directory, so we need to copy the whole directory - execSync(`cp -r "${sourceBundlePath}/" "${targetBundlePath}"`); - targetArchFolderFound = true; - } - }); - - if (!targetArchFolderFound) { + const targetFolder = targetArchFolders.find( + targetArchFolder => + getArchsFromFramework( + path.join(targetArchFolder, scheme + '.framework', scheme), + ) === frameworkPlatforms, + ); + if (targetFolder) { + console.log( + ` ${path.relative(outputFolder, sourceBundlePath)} → ${path.basename(targetFolder)}`, + ); + const targetBundlePath = path.join( + targetFolder, + `${scheme}.framework`, + bundleName, + ); + + // A bundle is a directory, so we need to copy the whole directory + execSync(`cp -r "${sourceBundlePath}/" "${targetBundlePath}"`); + } else { throw Error( `Could not find target architecture for folder ${path.relative(outputFolder, frameworkPath)}. Expected to find ${frameworkPlatforms}`, ); @@ -181,6 +178,75 @@ function copyBundles( }); } +function copySymbols( + scheme /*: string */, + outputFolder /*:string*/, + frameworkPaths /*:Array*/, +) { + console.log('Copying dSym files...'); + + const targetArchFolders = fs + .readdirSync(outputFolder) + .map(p => path.join(outputFolder, p)) + .filter(p => fs.statSync(p).isDirectory()); + + // For each framework (in frameworkPaths), copy the symbols from the source folder. + frameworkPaths.forEach(frameworkPath => { + const frameworkPlatforms = getArchsFromFramework( + path.join( + frameworkPath, + 'PackageFrameworks', + scheme + '.framework', + scheme, + ), + ); + + // Find the correct target folder based on the current architectures + const targetFolder = targetArchFolders.find( + targetArchFolder => + frameworkPlatforms === + getArchsFromFramework( + path.join(targetArchFolder, scheme + '.framework', scheme), + ), + ); + + if (!targetFolder) { + throw new Error(`Could not find target folder for ${frameworkPath}`); + } + const sourceSymbolPath = path.join( + frameworkPath, + scheme + '.framework.dSYM', + ); + if (!fs.existsSync(sourceSymbolPath)) { + throw new Error(`dSYM folder ${sourceSymbolPath} not found`); + } + + const archName = path.basename(targetFolder); + console.log( + ` ${path.relative(outputFolder, sourceSymbolPath)} → ${archName}`, + ); + + const targetSymbolPath = path.join( + outputFolder, + '..', + 'Symbols', + archName, + scheme + '.framework.dSYM', + ); + fs.mkdirSync(targetSymbolPath, {recursive: true}); + execSync(`cp -r "${sourceSymbolPath}/" "${targetSymbolPath}"`); + }); +} + +function getArchsFromFramework(frameworkPath /*:string*/) { + return execSync(`vtool -show-build ${frameworkPath}|grep platform`) + .toString() + .split('\n') + .map(p => p.trim().split(' ')[1]) + .sort((a, b) => a.localeCompare(b)) + .join(' '); +} + function signXCFramework( identity /*: string */, xcframeworkPath /*: string */, From 33be0606c1912b90e6e2c93fc8c90de0692478c2 Mon Sep 17 00:00:00 2001 From: Moti Zilberman Date: Fri, 22 Aug 2025 04:03:08 -0700 Subject: [PATCH 0022/1228] Always reload the frontend when launching, even in an existing window (#53407) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53407 Changelog: [Internal] ## Context Upon receiving a launch command, the RNDT shell either: 1. Creates a new window and navigates to the requested frontend URL. 2. Brings an existing window to the foreground *with no further navigation*. In the happy path, (2) is a pretty nice experience: it preserves all prior UI state in the frontend and leaves the user with an instantly responsive debugger - this can be quite a bit faster than (1) because of the overhead of loading and parsing source maps for example. However, this breaks down if the frontend is not in a usable state to begin with. This is, sadly, a frequent-enough occurrence that we must account for it: the CDP connection may have been lost, the frontend app itself might have failed to load the last time, etc. Preserving everything that's nice about (2) while also making it fully reliable - incrementally bringing the frontend to the state specified by a new URL - would require delicate engineering across the shell and frontend codebases, which is an amount of complexity I would like to sidestep for now. NOTE: The more complex solution **is 100% worth implementing in the long term,** as it has tangible benefits for the user, and matches Chrome best. ## This diff Here we take a much cheaper approach than the one described above: the shell will *always* initiate navigation to the new frontend URL, regardless of whether it does so in a new window or a previously opened one. This will consistently bring the user to a state where the frontend is open and working (although it will reset any ephemeral UI state in the process, and typically take a noticeable amount of time to load). Even with this simplified approach, the standalone shell still offers a better experience than launching in a browser (if only because it is zero-install and avoids the "dead tab spam" problem). Reviewed By: huntie Differential Revision: D80711185 fbshipit-source-id: 8f376ccf1717c48a1742c798da3171ac6d2f8af0 --- .../src/electron/MainInstanceEntryPoint.js | 45 +++++++++---------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/packages/debugger-shell/src/electron/MainInstanceEntryPoint.js b/packages/debugger-shell/src/electron/MainInstanceEntryPoint.js index 45c7326d0f72..3434787d5f67 100644 --- a/packages/debugger-shell/src/electron/MainInstanceEntryPoint.js +++ b/packages/debugger-shell/src/electron/MainInstanceEntryPoint.js @@ -36,7 +36,7 @@ function handleLaunchArgs(argv: string[]) { }); // Find an existing window for this app and launch configuration. - const existingWindow = BrowserWindow.getAllWindows().find(window => { + let frontendWindow = BrowserWindow.getAllWindows().find(window => { const metadata = windowMetadata.get(window); if (!metadata) { return false; @@ -44,41 +44,37 @@ function handleLaunchArgs(argv: string[]) { return metadata.windowKey === windowKey; }); - if (existingWindow) { + if (frontendWindow) { // If the window is already visible, flash it. - if (existingWindow.isVisible()) { - existingWindow.flashFrame(true); + if (frontendWindow.isVisible()) { + frontendWindow.flashFrame(true); setTimeout(() => { - existingWindow.flashFrame(false); + frontendWindow.flashFrame(false); }, 1000); } - if (process.platform === 'darwin') { - app.focus({ - steal: true, - }); - } - existingWindow.focus(); - return; + } else { + // Create the browser window. + frontendWindow = new BrowserWindow({ + width: 1200, + height: 600, + webPreferences: { + partition: 'persist:react-native-devtools', + preload: require.resolve('./preload.js'), + }, + // Icon for Linux + icon: path.join(__dirname, 'resources', 'icon.png'), + }); } - // Create the browser window. - const frontendWindow = new BrowserWindow({ - width: 1200, - height: 600, - webPreferences: { - partition: 'persist:react-native-devtools', - preload: require.resolve('./preload.js'), - }, - // Icon for Linux - icon: path.join(__dirname, 'resources', 'icon.png'), - }); - // Open links in the default browser instead of in new Electron windows. frontendWindow.webContents.setWindowOpenHandler(({url}) => { shell.openExternal(url); return {action: 'deny'}; }); + // TODO: If the window contains a live, working frontend instance with a valid connection to the backend, + // we should avoid this reload and instead send the frontend a message to handle the launch arguments + // dynamically (e.g. update the launch ID for telemetry purposes, handle deeplinking to a specific CDT panel, etc). frontendWindow.loadURL(frontendUrl); windowMetadata.set(frontendWindow, { @@ -90,6 +86,7 @@ function handleLaunchArgs(argv: string[]) { steal: true, }); } + frontendWindow.focus(); } app.whenReady().then(() => { From 5848252ee23a897502eea6bbc69f02e9e0764f1c Mon Sep 17 00:00:00 2001 From: generatedunixname537391475639613 Date: Fri, 22 Aug 2025 04:12:06 -0700 Subject: [PATCH 0023/1228] xplat/js/react-native-github/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTaskTest.kt (#53423) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53423 Reviewed By: rshest Differential Revision: D80786149 fbshipit-source-id: 81df8592b142df2f9d9178c13a9480a7186c2604 --- .../react/tasks/internal/PreparePrefabHeadersTaskTest.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTaskTest.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTaskTest.kt index 66feaf85ffef..7fcdc3e9c1bc 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTaskTest.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/tasks/internal/PreparePrefabHeadersTaskTest.kt @@ -186,9 +186,9 @@ class PreparePrefabHeadersTaskTest { val project = createProject(projectDir = tempFolder.root) val task = - createTestTask(project = project) { - it.outputDir.set(outputDir) - it.input.set(listOf(PrefabPreprocessingEntry("sample_library", "boost/" to ""))) + createTestTask(project = project) { task -> + task.outputDir.set(outputDir) + task.input.set(listOf(PrefabPreprocessingEntry("sample_library", "boost/" to ""))) } task.taskAction() From e1b76ab2d54023238f407264a1bdbda782468e88 Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Fri, 22 Aug 2025 04:12:22 -0700 Subject: [PATCH 0024/1228] Add a mechanism for emitting stashed trace recording (#53078) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53078 # Changelog: [Internal] When CDP session is created via `HostTarget::connect`, it will ask `HostTargetDelegate` is there is a previously recorded trace that Host wants to display in the Frontend. `TracingAgent` will serialize and send the recording at the initialization time in constructor. Reviewed By: huntie Differential Revision: D79672597 fbshipit-source-id: 241c9d367ab65ef1e95c62d5025b3bc14bf42608 --- .../jsinspector-modern/HostAgent.cpp | 19 +++++--- .../jsinspector-modern/HostAgent.h | 5 ++- .../jsinspector-modern/HostTarget.cpp | 9 ++-- .../jsinspector-modern/HostTarget.h | 13 ++++++ .../jsinspector-modern/TracingAgent.cpp | 43 +++++++++++-------- .../jsinspector-modern/TracingAgent.h | 15 ++++++- 6 files changed, 76 insertions(+), 28 deletions(-) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp b/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp index 21c4fb8dbd77..989193dec95c 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp @@ -41,14 +41,18 @@ class HostAgent::Impl final { HostTargetController& targetController, HostTargetMetadata hostMetadata, SessionState& sessionState, - VoidExecutor executor) + VoidExecutor executor, + std::optional traceRecordingToEmit) : frontendChannel_(frontendChannel), targetController_(targetController), hostMetadata_(std::move(hostMetadata)), sessionState_(sessionState), networkIOAgent_(NetworkIOAgent(frontendChannel, std::move(executor))), - tracingAgent_( - TracingAgent(frontendChannel, sessionState, targetController)) {} + tracingAgent_(TracingAgent( + frontendChannel, + sessionState, + targetController, + std::move(traceRecordingToEmit))) {} ~Impl() { if (isPausedInDebuggerOverlayVisible_) { @@ -428,7 +432,8 @@ class HostAgent::Impl final { HostTargetController& targetController, HostTargetMetadata hostMetadata, SessionState& sessionState, - VoidExecutor executor) {} + VoidExecutor executor, + std::optional traceRecordingToEmit) {} void handleRequest(const cdp::PreparsedRequest& req) {} void setCurrentInstanceAgent(std::shared_ptr agent) {} @@ -441,14 +446,16 @@ HostAgent::HostAgent( HostTargetController& targetController, HostTargetMetadata hostMetadata, SessionState& sessionState, - VoidExecutor executor) + VoidExecutor executor, + std::optional traceRecordingToEmit) : impl_(std::make_unique( *this, frontendChannel, targetController, std::move(hostMetadata), sessionState, - std::move(executor))) {} + std::move(executor), + std::move(traceRecordingToEmit))) {} HostAgent::~HostAgent() = default; diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.h b/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.h index ac4728062903..cb87a600775a 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.h @@ -36,13 +36,16 @@ class HostAgent final { * \param hostMetadata Metadata about the host that created this agent. * \param sessionState The state of the session that created this agent. * \param executor A void executor to be used by async-aware handlers. + * \param traceRecordingToEmit If set, this is the trace that Host has + * requested to display in the Frontend. */ HostAgent( const FrontendChannel& frontendChannel, HostTargetController& targetController, HostTargetMetadata hostMetadata, SessionState& sessionState, - VoidExecutor executor); + VoidExecutor executor, + std::optional traceRecordingToEmit); HostAgent(const HostAgent&) = delete; HostAgent(HostAgent&&) = delete; diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.cpp b/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.cpp index 190d61477da5..80eb2cdd5bda 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.cpp @@ -34,7 +34,8 @@ class HostTargetSession { std::unique_ptr remote, HostTargetController& targetController, HostTargetMetadata hostMetadata, - VoidExecutor executor) + VoidExecutor executor, + std::optional traceRecordingToEmit) : remote_(std::make_shared(std::move(remote))), frontendChannel_( [remoteWeak = std::weak_ptr(remote_)](std::string_view message) { @@ -47,7 +48,8 @@ class HostTargetSession { targetController, std::move(hostMetadata), state_, - std::move(executor)) {} + std::move(executor), + std::move(traceRecordingToEmit)) {} /** * Called by CallbackLocalConnection to send a message to this Session's @@ -206,7 +208,8 @@ std::unique_ptr HostTarget::connect( std::move(connectionToFrontend), controller_, delegate_.getMetadata(), - makeVoidExecutor(executorFromThis())); + makeVoidExecutor(executorFromThis()), + delegate_.unstable_getTraceRecordingThatWillBeEmittedOnInitialization()); session->setCurrentInstance(currentInstance_.get()); sessions_.insert(std::weak_ptr(session)); return std::make_unique( diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h b/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h index 872af485d42e..8f4ad78e9c25 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h @@ -146,6 +146,19 @@ class HostTargetDelegate : public LoadNetworkResourceDelegate { throw NotImplementedException( "LoadNetworkResourceDelegate.loadNetworkResource is not implemented by this host target delegate."); } + + /** + * [Experimental] Will be called at the CDP session initialization to get the + * trace recording that may have been stashed by the Host from the previous + * background session. + * + * \return the trace recording state if there is one that needs to be + * displayed, otherwise std::nullopt. + */ + virtual std::optional + unstable_getTraceRecordingThatWillBeEmittedOnInitialization() { + return std::nullopt; + } }; /** diff --git a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp index 49568d70799a..dd84991722f0 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp @@ -38,10 +38,15 @@ const uint16_t PROFILE_TRACE_EVENT_CHUNK_SIZE = 1; TracingAgent::TracingAgent( FrontendChannel frontendChannel, SessionState& sessionState, - HostTargetController& hostTargetController) + HostTargetController& hostTargetController, + std::optional traceRecordingToEmit) : frontendChannel_(std::move(frontendChannel)), sessionState_(sessionState), - hostTargetController_(hostTargetController) {} + hostTargetController_(hostTargetController) { + if (traceRecordingToEmit.has_value()) { + emitTraceRecording(std::move(traceRecordingToEmit.value())); + } +} TracingAgent::~TracingAgent() { // Agents are owned by the session. If the agent is destroyed, it means that @@ -86,25 +91,29 @@ bool TracingAgent::handleRequest(const cdp::PreparsedRequest& req) { // Send response to Tracing.end request. frontendChannel_(cdp::jsonResult(req.id)); - auto dataCollectedCallback = [this](folly::dynamic&& eventsChunk) { - frontendChannel_(cdp::jsonNotification( - "Tracing.dataCollected", - folly::dynamic::object("value", std::move(eventsChunk)))); - }; - tracing::TraceRecordingStateSerializer::emitAsDataCollectedChunks( - std::move(state), - dataCollectedCallback, - TRACE_EVENT_CHUNK_SIZE, - PROFILE_TRACE_EVENT_CHUNK_SIZE); - - frontendChannel_(cdp::jsonNotification( - "Tracing.tracingComplete", - folly::dynamic::object("dataLossOccurred", false))); - + emitTraceRecording(std::move(state)); return true; } return false; } +void TracingAgent::emitTraceRecording( + tracing::TraceRecordingState state) const { + auto dataCollectedCallback = [this](folly::dynamic&& eventsChunk) { + frontendChannel_(cdp::jsonNotification( + "Tracing.dataCollected", + folly::dynamic::object("value", std::move(eventsChunk)))); + }; + tracing::TraceRecordingStateSerializer::emitAsDataCollectedChunks( + std::move(state), + dataCollectedCallback, + TRACE_EVENT_CHUNK_SIZE, + PROFILE_TRACE_EVENT_CHUNK_SIZE); + + frontendChannel_(cdp::jsonNotification( + "Tracing.tracingComplete", + folly::dynamic::object("dataLossOccurred", false))); +} + } // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.h b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.h index b46d9a78130d..ec686f9c5286 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.h @@ -24,11 +24,18 @@ class TracingAgent { /** * \param frontendChannel A channel used to send responses to the * frontend. + * \param sessionState The state of the session that created this agent. + * \param hostTargetController An interface to the HostTarget that this agent + * is attached to. The caller is responsible for ensuring that the + * HostTargetDelegate and underlying HostTarget both outlive the agent. + * \param traceRecordingToEmit If set, this is the trace that Host has + * requested to display in the Frontend. */ TracingAgent( FrontendChannel frontendChannel, SessionState& sessionState, - HostTargetController& hostTargetController); + HostTargetController& hostTargetController, + std::optional traceRecordingToEmit); ~TracingAgent(); @@ -48,6 +55,12 @@ class TracingAgent { SessionState& sessionState_; HostTargetController& hostTargetController_; + + /** + * Emits the captured Trace Recording state in a series of + * Tracing.dataCollected events, followed by a Tracing.tracingComplete event. + */ + void emitTraceRecording(tracing::TraceRecordingState state) const; }; } // namespace facebook::react::jsinspector_modern From 80f340e81cba3d006704078e89cbccc17631c2b2 Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Fri, 22 Aug 2025 04:12:22 -0700 Subject: [PATCH 0025/1228] Send custom CDP Event to Frontend to prepare for displaying a trace (#53079) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53079 # Changelog: [Internal] We need this to notify Frontend, so it updates the local state before receiving `Tracing.dataCollected` events. Corresponding change in CDT fork - https://github.com/facebook/react-native-devtools-frontend/pull/199. Reviewed By: sbuggay Differential Revision: D79672598 fbshipit-source-id: b2928cb3942e34a1f4723516ecdf5062d4331591 --- .../ReactCommon/jsinspector-modern/TracingAgent.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp index dd84991722f0..c6ef7037227b 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/TracingAgent.cpp @@ -44,6 +44,8 @@ TracingAgent::TracingAgent( sessionState_(sessionState), hostTargetController_(hostTargetController) { if (traceRecordingToEmit.has_value()) { + frontendChannel_( + cdp::jsonNotification("ReactNativeApplication.traceRequested")); emitTraceRecording(std::move(traceRecordingToEmit.value())); } } From 8d8245123e00a8eaebd7b5a0c921397b580aacba Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Fri, 22 Aug 2025 04:12:22 -0700 Subject: [PATCH 0026/1228] Create endpoints for tracing and stashing on Bridgeless Android ReactHost (#53416) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53416 # Changelog: [Internal] Creates methods on Bridgeless Android Host for starting / stopping tracing and implements logic for storing the recording that will be transferred to `jsinspector-modern` stack via HostTargetDelegate when CDP session is created. Reviewed By: sbuggay Differential Revision: D79725161 fbshipit-source-id: f3e3b39f6d94a6548cf227394f7328f6913c33e4 --- .../react/runtime/ReactHostInspectorTarget.kt | 6 ++ .../runtime/jni/JReactHostInspectorTarget.cpp | 68 ++++++++++++++++--- .../runtime/jni/JReactHostInspectorTarget.h | 36 ++++++++++ 3 files changed, 101 insertions(+), 9 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostInspectorTarget.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostInspectorTarget.kt index 5ce60e160fc0..81d6742ef027 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostInspectorTarget.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/runtime/ReactHostInspectorTarget.kt @@ -34,6 +34,12 @@ internal class ReactHostInspectorTarget(reactHostImpl: ReactHostImpl) : external fun sendDebuggerResumeCommand() + external fun startBackgroundTrace(): Boolean + + external fun stopAndStashBackgroundTrace() + + external fun stopAndDiscardBackgroundTrace() + override fun addPerfMonitorListener(listener: PerfMonitorUpdateListener) { perfMonitorListeners.add(listener) } diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.cpp index 8e06dc322729..2feaa30972d9 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.cpp @@ -80,15 +80,6 @@ void JReactHostInspectorTarget::sendDebuggerResumeCommand() { } } -void JReactHostInspectorTarget::registerNatives() { - registerHybrid({ - makeNativeMethod("initHybrid", JReactHostInspectorTarget::initHybrid), - makeNativeMethod( - "sendDebuggerResumeCommand", - JReactHostInspectorTarget::sendDebuggerResumeCommand), - }); -} - jsinspector_modern::HostTargetMetadata JReactHostInspectorTarget::getMetadata() { jsinspector_modern::HostTargetMetadata metadata = { @@ -157,4 +148,63 @@ HostTarget* JReactHostInspectorTarget::getInspectorTarget() { return inspectorTarget_ ? inspectorTarget_.get() : nullptr; } +bool JReactHostInspectorTarget::startBackgroundTrace() { + if (inspectorTarget_) { + return inspectorTarget_->startTracing(tracing::Mode::Background); + } else { + jni::throwNewJavaException( + "java/lang/IllegalStateException", + "Cannot start Tracing session while the Fusebox backend is not enabled."); + } +} + +tracing::TraceRecordingState JReactHostInspectorTarget::stopTracing() { + if (inspectorTarget_) { + return inspectorTarget_->stopTracing(); + } else { + jni::throwNewJavaException( + "java/lang/IllegalStateException", + "Cannot stop Tracing session while the Fusebox backend is not enabled."); + } +} + +void JReactHostInspectorTarget::stopAndStashBackgroundTrace() { + auto capturedTrace = inspectorTarget_->stopTracing(); + stashTraceRecordingState(std::move(capturedTrace)); +} + +void JReactHostInspectorTarget::stopAndDiscardBackgroundTrace() { + inspectorTarget_->stopTracing(); +} + +void JReactHostInspectorTarget::stashTraceRecordingState( + tracing::TraceRecordingState&& state) { + stashedTraceRecordingState_ = std::move(state); +} + +std::optional JReactHostInspectorTarget:: + unstable_getTraceRecordingThatWillBeEmittedOnInitialization() { + auto state = std::move(stashedTraceRecordingState_); + stashedTraceRecordingState_.reset(); + return state; +} + +void JReactHostInspectorTarget::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", JReactHostInspectorTarget::initHybrid), + makeNativeMethod( + "sendDebuggerResumeCommand", + JReactHostInspectorTarget::sendDebuggerResumeCommand), + makeNativeMethod( + "startBackgroundTrace", + JReactHostInspectorTarget::startBackgroundTrace), + makeNativeMethod( + "stopAndStashBackgroundTrace", + JReactHostInspectorTarget::stopAndStashBackgroundTrace), + makeNativeMethod( + "stopAndDiscardBackgroundTrace", + JReactHostInspectorTarget::stopAndDiscardBackgroundTrace), + }); +} + } // namespace facebook::react diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.h b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.h index fc151e33172a..2918a5f984cc 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/runtime/jni/JReactHostInspectorTarget.h @@ -77,6 +77,22 @@ class JReactHostInspectorTarget static void registerNatives(); void sendDebuggerResumeCommand(); + /** + * Starts a background trace recording for this HostTarget. + * + * \return false if already tracing, true otherwise. + */ + bool startBackgroundTrace(); + /** + * Stops previously started trace recording and stashes the captured trace, + * which will be emitted the next time CDP session is created. + */ + void stopAndStashBackgroundTrace(); + /** + * Stops previously started trace recording and discards the captured trace. + */ + void stopAndDiscardBackgroundTrace(); + jsinspector_modern::HostTarget* getInspectorTarget(); // HostTargetDelegate methods @@ -90,6 +106,8 @@ class JReactHostInspectorTarget const jsinspector_modern::LoadNetworkResourceRequest& params, jsinspector_modern::ScopedExecutor< jsinspector_modern::NetworkRequestListener> executor) override; + std::optional + unstable_getTraceRecordingThatWillBeEmittedOnInitialization() override; private: JReactHostInspectorTarget( @@ -106,6 +124,24 @@ class JReactHostInspectorTarget std::shared_ptr inspectorTarget_; std::optional inspectorPageId_; + /** + * Stops previously started trace recording and returns the captured trace. + */ + jsinspector_modern::tracing::TraceRecordingState stopTracing(); + /** + * Stashes previously recorded trace recording state that will be emitted when + * CDP session is created. Once emitted, the value will be cleared from this + * instance. + */ + void stashTraceRecordingState( + jsinspector_modern::tracing::TraceRecordingState&& state); + /** + * Previously recorded trace recording state that will be emitted when + * CDP session is created. + */ + std::optional + stashedTraceRecordingState_; + friend HybridBase; }; } // namespace facebook::react From 4b34be445d4e166ef6524c86f6f9ae9dc3392e03 Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Fri, 22 Aug 2025 05:07:02 -0700 Subject: [PATCH 0027/1228] Release PerfMetricsBinding to avoid memory leak (#53420) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53420 # Changelog: [Internal] HostRuntimeBinding owns a connection, which is stored as a session on HostTarget, so we need to release it first in order to satifsy the assertion in the destructor. Reviewed By: huntie Differential Revision: D80778273 fbshipit-source-id: be7bf085fadd8808fd5e5c621c3990a5e7e0186d --- .../ReactCommon/jsinspector-modern/HostTarget.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.cpp b/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.cpp index 80eb2cdd5bda..12af8080e91a 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.cpp @@ -220,6 +220,11 @@ HostTarget::~HostTarget() { // HostCommandSender owns a session, so we must release it for the assertion // below to be valid. commandSender_.reset(); + + // HostRuntimeBinding owns a connection, so we must release it for the + // assertion + perfMetricsBinding_.reset(); + // Sessions are owned by InspectorPackagerConnection, not by HostTarget, but // they hold a HostTarget& that we must guarantee is valid. assert( From e60ddf0adbe9e516c333e14563accb579571bbf1 Mon Sep 17 00:00:00 2001 From: generatedunixname89002005287564 Date: Fri, 22 Aug 2025 05:52:14 -0700 Subject: [PATCH 0028/1228] Fix CQS signal modernize-use-designated-initializers in xplat/js/react-native-github/packages [B] [B] (#53425) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53425 Reviewed By: javache Differential Revision: D80619000 fbshipit-source-id: d012c170ed366904b39ef335fea5241b604461ec --- .../textlayoutmanager/RCTTextLayoutManager.mm | 71 ++++++++++--------- .../textlayoutmanager/TextLayoutManager.mm | 51 +++++++------ .../ios/RNTMyNativeViewComponentView.mm | 16 ++--- 3 files changed, 73 insertions(+), 65 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm index 7f17fd7876f9..40b88bd0096d 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTTextLayoutManager.mm @@ -187,34 +187,36 @@ - (LinesMeasurements)getLinesForAttributedString:(facebook::react::AttributedStr std::vector paragraphLines{}; auto blockParagraphLines = ¶graphLines; - [layoutManager enumerateLineFragmentsForGlyphRange:glyphRange - usingBlock:^( - CGRect overallRect, - CGRect usedRect, - NSTextContainer *_Nonnull usedTextContainer, - NSRange lineGlyphRange, - BOOL *_Nonnull stop) { - NSRange range = [layoutManager characterRangeForGlyphRange:lineGlyphRange - actualGlyphRange:nil]; - NSString *renderedString = [textStorage.string substringWithRange:range]; - UIFont *font = [[textStorage attributedSubstringFromRange:range] - attribute:NSFontAttributeName - atIndex:0 - effectiveRange:nil]; - auto rect = facebook::react::Rect{ - facebook::react::Point{usedRect.origin.x, usedRect.origin.y}, - facebook::react::Size{usedRect.size.width, usedRect.size.height}}; - - CGFloat baseline = [layoutManager locationForGlyphAtIndex:range.location].y; - auto line = LineMeasurement{ - std::string([renderedString UTF8String]), - rect, - overallRect.size.height - baseline, - font.capHeight, - baseline, - font.xHeight}; - blockParagraphLines->push_back(line); - }]; + [layoutManager + enumerateLineFragmentsForGlyphRange:glyphRange + usingBlock:^( + CGRect overallRect, + CGRect usedRect, + NSTextContainer *_Nonnull usedTextContainer, + NSRange lineGlyphRange, + BOOL *_Nonnull stop) { + NSRange range = [layoutManager characterRangeForGlyphRange:lineGlyphRange + actualGlyphRange:nil]; + NSString *renderedString = [textStorage.string substringWithRange:range]; + UIFont *font = + [[textStorage attributedSubstringFromRange:range] attribute:NSFontAttributeName + atIndex:0 + effectiveRange:nil]; + auto rect = facebook::react::Rect{ + .origin = facebook::react::Point{.x = usedRect.origin.x, .y = usedRect.origin.y}, + .size = facebook::react::Size{ + .width = usedRect.size.width, .height = usedRect.size.height}}; + + CGFloat baseline = [layoutManager locationForGlyphAtIndex:range.location].y; + auto line = LineMeasurement{ + std::string([renderedString UTF8String]), + rect, + overallRect.size.height - baseline, + font.capHeight, + baseline, + font.xHeight}; + blockParagraphLines->push_back(line); + }]; return paragraphLines; } @@ -416,19 +418,20 @@ - (TextMeasurement)_measureTextStorage:(NSTextStorage *)textStorage atIndex:0 effectiveRange:nil]; frame = { - {glyphRect.origin.x, - glyphRect.origin.y + glyphRect.size.height - attachmentSize.height + font.descender}, - attachmentSize}; + .origin = + {glyphRect.origin.x, + glyphRect.origin.y + glyphRect.size.height - attachmentSize.height + font.descender}, + .size = attachmentSize}; auto rect = facebook::react::Rect{ - facebook::react::Point{frame.origin.x, frame.origin.y}, - facebook::react::Size{frame.size.width, frame.size.height}}; + .origin = facebook::react::Point{.x = frame.origin.x, .y = frame.origin.y}, + .size = facebook::react::Size{.width = frame.size.width, .height = frame.size.height}}; attachments.push_back(TextMeasurement::Attachment{.frame = rect, .isClipped = false}); } }]; - return TextMeasurement{{size.width, size.height}, attachments}; + return TextMeasurement{.size = {.width = size.width, .height = size.height}, .attachments = attachments}; } @end diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm index 8ffe7fd80a0f..7f032d78cf64 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/TextLayoutManager.mm @@ -39,23 +39,27 @@ case AttributedStringBox::Mode::Value: { auto attributedString = ensurePlaceholderIfEmpty_DO_NOT_USE(attributedStringBox.getValue()); - measurement = textMeasureCache_.get({attributedString, paragraphAttributes, layoutConstraints}, [&]() { - auto telemetry = TransactionTelemetry::threadLocalTelemetry(); - if (telemetry) { - telemetry->willMeasureText(); - } - - auto measurement = [textLayoutManager measureAttributedString:attributedString - paragraphAttributes:paragraphAttributes - layoutContext:layoutContext - layoutConstraints:layoutConstraints]; - - if (telemetry) { - telemetry->didMeasureText(); - } - - return measurement; - }); + measurement = textMeasureCache_.get( + {.attributedString = attributedString, + .paragraphAttributes = paragraphAttributes, + .layoutConstraints = layoutConstraints}, + [&]() { + auto telemetry = TransactionTelemetry::threadLocalTelemetry(); + if (telemetry) { + telemetry->willMeasureText(); + } + + auto measurement = [textLayoutManager measureAttributedString:attributedString + paragraphAttributes:paragraphAttributes + layoutContext:layoutContext + layoutConstraints:layoutConstraints]; + + if (telemetry) { + telemetry->didMeasureText(); + } + + return measurement; + }); break; } @@ -96,12 +100,13 @@ RCTTextLayoutManager *textLayoutManager = (RCTTextLayoutManager *)unwrapManagedObject(nativeTextLayoutManager_); - auto measurement = lineMeasureCache_.get({attributedString, paragraphAttributes, size}, [&]() { - auto measurement = [textLayoutManager getLinesForAttributedString:attributedString - paragraphAttributes:paragraphAttributes - size:{size.width, size.height}]; - return measurement; - }); + auto measurement = lineMeasureCache_.get( + {.attributedString = attributedString, .paragraphAttributes = paragraphAttributes, .size = size}, [&]() { + auto measurement = [textLayoutManager getLinesForAttributedString:attributedString + paragraphAttributes:paragraphAttributes + size:{size.width, size.height}]; + return measurement; + }); return measurement; } diff --git a/packages/rn-tester/NativeComponentExample/ios/RNTMyNativeViewComponentView.mm b/packages/rn-tester/NativeComponentExample/ios/RNTMyNativeViewComponentView.mm index 571dc28eaa53..2eb7bff28e67 100644 --- a/packages/rn-tester/NativeComponentExample/ios/RNTMyNativeViewComponentView.mm +++ b/packages/rn-tester/NativeComponentExample/ios/RNTMyNativeViewComponentView.mm @@ -79,14 +79,14 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & newIntVectorVector.push_back({val, val, val}); } RNTMyNativeViewEventEmitter::OnIntArrayChanged value = { - newVector, - newBoolVector, - newFloatVector, - newDoubleVector, - newYesNoVector, - newStringVector, - newLatLonVector, - newIntVectorVector}; + .values = newVector, + .boolValues = newBoolVector, + .floats = newFloatVector, + .doubles = newDoubleVector, + .yesNos = newYesNoVector, + .strings = newStringVector, + .latLons = newLatLonVector, + .multiArrays = newIntVectorVector}; std::static_pointer_cast(_eventEmitter)->onIntArrayChanged(value); } } From 56ab2e72b279b3be8ff89660efb67a35ab8e9030 Mon Sep 17 00:00:00 2001 From: generatedunixname89002005287564 Date: Fri, 22 Aug 2025 07:02:13 -0700 Subject: [PATCH 0029/1228] Fix CQS signal modernize-use-designated-initializers in xplat/js/react-native-github/packages [B] [A] [A] (#53401) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53401 Reviewed By: cipolleschi Differential Revision: D80713611 fbshipit-source-id: 73dc034d966eea5b388a51ab84da2416531c7376 --- .../ios/react/renderer/graphics/RCTPlatformColorUtils.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/ReactCommon/react/renderer/graphics/platform/ios/react/renderer/graphics/RCTPlatformColorUtils.mm b/packages/react-native/ReactCommon/react/renderer/graphics/platform/ios/react/renderer/graphics/RCTPlatformColorUtils.mm index 3e037710add5..4e79aa4dab44 100644 --- a/packages/react-native/ReactCommon/react/renderer/graphics/platform/ios/react/renderer/graphics/RCTPlatformColorUtils.mm +++ b/packages/react-native/ReactCommon/react/renderer/graphics/platform/ios/react/renderer/graphics/RCTPlatformColorUtils.mm @@ -184,7 +184,7 @@ { CGFloat rgba[4]; [color getRed:&rgba[0] green:&rgba[1] blue:&rgba[2] alpha:&rgba[3]]; - return {(float)rgba[0], (float)rgba[1], (float)rgba[2], (float)rgba[3]}; + return {.red = (float)rgba[0], .green = (float)rgba[1], .blue = (float)rgba[2], .alpha = (float)rgba[3]}; } facebook::react::ColorComponents RCTPlatformColorComponentsFromSemanticItems(std::vector &semanticItems) From 0530ea334995c3f4cf1fa99583363592f3f04a22 Mon Sep 17 00:00:00 2001 From: Sam Zhou Date: Fri, 22 Aug 2025 10:35:39 -0700 Subject: [PATCH 0030/1228] Migrate to suppression with error code in xplat: 1/n (#53439) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/53439 Changelog: [Internal] Reviewed By: panagosg7 Differential Revision: D80809220 fbshipit-source-id: 6f432d8302934b9fee9780ac1d6ba6c87c0b3899 --- .../src/commands/start/middleware.js | 4 +-- .../__tests__/normalizeColor-test.js | 2 +- .../src/index.js | 2 +- .../src/ErrorFormatting.js | 2 +- .../Animated/components/AnimatedScrollView.js | 3 ++- .../components/AnimatedSectionList.js | 2 +- .../Components/ScrollView/ScrollView.js | 2 +- .../Components/TextInput/TextInputState.js | 2 +- .../TextInput/__tests__/TextInput-test.js | 2 +- .../Libraries/Image/Image.android.js | 2 +- .../Lists/__flowtests__/FlatList-flowtest.js | 26 +++++++++++-------- .../__flowtests__/SectionList-flowtest.js | 11 ++++---- .../ReactFabricHostComponent.js | 4 +-- .../ReactNative/RendererImplementation.js | 6 ++--- .../__flowtests__/StyleSheet-flowtest.js | 6 ++--- .../Libraries/StyleSheet/processFilter.js | 10 +++---- .../emitter/__tests__/EventEmitter-test.js | 10 +++---- .../react-native/jest/mockNativeComponent.js | 2 +- .../Accessibility/AccessibilityExample.js | 2 +- .../DrawerLayoutAndroidExample.js | 4 +-- .../ManyPointersPropertiesExample.js | 2 +- .../PointerEventSupport.js | 7 ++--- .../W3CPointerEventsEventfulView.js | 3 ++- .../js/examples/Filter/FilterExample.js | 2 +- .../MixBlendMode/MixBlendModeExample.js | 2 +- .../PointerEvents/PointerEventsExample.js | 4 +-- .../Pressable/PressableExampleFbInternal.js | 2 +- .../NativeCxxModuleExampleExample.js | 2 +- scripts/run-ci-javascript-tests.js | 4 ++- 29 files changed, 71 insertions(+), 61 deletions(-) diff --git a/packages/community-cli-plugin/src/commands/start/middleware.js b/packages/community-cli-plugin/src/commands/start/middleware.js index 8df3991cb421..919cc834ecc6 100644 --- a/packages/community-cli-plugin/src/commands/start/middleware.js +++ b/packages/community-cli-plugin/src/commands/start/middleware.js @@ -31,9 +31,9 @@ type MiddlewareReturn = { ... }; -// $FlowFixMe +// $FlowFixMe[incompatible-type] const unusedStubWSServer: ws$WebSocketServer = {}; -// $FlowFixMe +// $FlowFixMe[incompatible-type] const unusedMiddlewareStub: Server = {}; const communityMiddlewareFallback = { diff --git a/packages/normalize-color/__tests__/normalizeColor-test.js b/packages/normalize-color/__tests__/normalizeColor-test.js index b9af70519b2f..95a26a415ec9 100644 --- a/packages/normalize-color/__tests__/normalizeColor-test.js +++ b/packages/normalize-color/__tests__/normalizeColor-test.js @@ -47,7 +47,7 @@ it('refuses non-spec compliant colors', () => { expect(normalizeColor('rgb (0, 1, 2)')).toBe(null); expect(normalizeColor('rgba(0 0 0 0.0)')).toBe(null); expect(normalizeColor('hsv(0, 1, 2)')).toBe(null); - // $FlowExpectedError - Intentionally malformed argument. + // $FlowExpectedError[incompatible-type] - Intentionally malformed argument. expect(normalizeColor({r: 10, g: 10, b: 10})).toBe(null); expect(normalizeColor('hsl(1%, 2, 3)')).toBe(null); expect(normalizeColor('rgb(1%, 2%, 3%)')).toBe(null); diff --git a/packages/react-native-babel-transformer/src/index.js b/packages/react-native-babel-transformer/src/index.js index 42cb9ed97b8a..8ba7c3afe3d9 100644 --- a/packages/react-native-babel-transformer/src/index.js +++ b/packages/react-native-babel-transformer/src/index.js @@ -218,7 +218,7 @@ const transform /*: BabelTransformer['transform'] */ = ({ // The result from `transformFromAstSync` can be null (if the file is ignored) if (!result) { - /* $FlowFixMe BabelTransformer specifies that the `ast` can never be null but + /* $FlowFixMe[incompatible-type] BabelTransformer specifies that the `ast` can never be null but * the function returns here. Discovered when typing `BabelNode`. */ return {ast: null}; } diff --git a/packages/react-native-compatibility-check/src/ErrorFormatting.js b/packages/react-native-compatibility-check/src/ErrorFormatting.js index f19c796661ae..91e77078cf08 100644 --- a/packages/react-native-compatibility-check/src/ErrorFormatting.js +++ b/packages/react-native-compatibility-check/src/ErrorFormatting.js @@ -275,7 +275,7 @@ export function formatNativeSpecErrorStore( export function formatDiffSet(summary: DiffSummary): FormattedDiffSummary { const summaryStatus = summary.status; if (summaryStatus === 'ok' || summaryStatus === 'patchable') { - // $FlowFixMe I don't think we can ever get in this branch + // $FlowFixMe[incompatible-type] I don't think we can ever get in this branch return summary; } const hasteModules = Object.keys(summary.incompatibilityReport); diff --git a/packages/react-native/Libraries/Animated/components/AnimatedScrollView.js b/packages/react-native/Libraries/Animated/components/AnimatedScrollView.js index 90d5356f0b9b..7a1a1d7b32ec 100644 --- a/packages/react-native/Libraries/Animated/components/AnimatedScrollView.js +++ b/packages/react-native/Libraries/Animated/components/AnimatedScrollView.js @@ -54,7 +54,8 @@ const AnimatedScrollView: AnimatedComponentType< props.style != null ) { return ( - // $FlowFixMe - It should return an Animated ScrollView but it returns a ScrollView with Animated props applied. + // $FlowFixMe[incompatible-type] - It should return an Animated ScrollView but it returns a ScrollView with Animated props applied. + // $FlowFixMe[incompatible-variance] { keyboardNeverPersistTaps && this._keyboardIsDismissible() && e.target != null && - // $FlowFixMe Error supressed during the migration of HostInstance to ReactNativeElement + // $FlowFixMe[incompatible-type] Error supressed during the migration of HostInstance to ReactNativeElement !TextInputState.isTextInput(e.target) ) { return true; diff --git a/packages/react-native/Libraries/Components/TextInput/TextInputState.js b/packages/react-native/Libraries/Components/TextInput/TextInputState.js index 4707e227002c..9ef598defeea 100644 --- a/packages/react-native/Libraries/Components/TextInput/TextInputState.js +++ b/packages/react-native/Libraries/Components/TextInput/TextInputState.js @@ -88,7 +88,7 @@ function focusTextInput(textField: ?HostInstance) { if (textField != null) { const fieldCanBeFocused = currentlyFocusedInputRef !== textField && - // $FlowFixMe - `currentProps` is missing in `NativeMethods` + // $FlowFixMe[prop-missing] - `currentProps` is missing in `NativeMethods` textField.currentProps?.editable !== false; if (!fieldCanBeFocused) { diff --git a/packages/react-native/Libraries/Components/TextInput/__tests__/TextInput-test.js b/packages/react-native/Libraries/Components/TextInput/__tests__/TextInput-test.js index b47a964e1d07..a33757e1e15a 100644 --- a/packages/react-native/Libraries/Components/TextInput/__tests__/TextInput-test.js +++ b/packages/react-native/Libraries/Components/TextInput/__tests__/TextInput-test.js @@ -123,7 +123,7 @@ jest.unmock('../TextInput'); throw new Error('Expected `textInputElement` to be non-null'); } - // $FlowFixMe + // $FlowFixMe[prop-missing] textInputElement.currentProps = textInputElement.props; expect(textInputElement.isFocused()).toBe(false); diff --git a/packages/react-native/Libraries/Image/Image.android.js b/packages/react-native/Libraries/Image/Image.android.js index b543c9f709d7..0bf9936bed33 100644 --- a/packages/react-native/Libraries/Image/Image.android.js +++ b/packages/react-native/Libraries/Image/Image.android.js @@ -387,7 +387,7 @@ if (ReactNativeFeatureFlags.reduceDefaultPropsInImage()) { // in order to have a better alignment between platforms in the future. src: sources, source: sources, - /* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found + /* $FlowFixMe[prop-missing](>=0.78.0 site=react_native_android_fb) This issue was found * when making Flow check .android.js files. */ headers: (source?.[0]?.headers || source?.headers: ?{[string]: string}), defaultSource: defaultSource ? defaultSource.uri : null, diff --git a/packages/react-native/Libraries/Lists/__flowtests__/FlatList-flowtest.js b/packages/react-native/Libraries/Lists/__flowtests__/FlatList-flowtest.js index 18042946737a..9a7efd0e236a 100644 --- a/packages/react-native/Libraries/Lists/__flowtests__/FlatList-flowtest.js +++ b/packages/react-native/Libraries/Lists/__flowtests__/FlatList-flowtest.js @@ -36,7 +36,7 @@ export function testBadDataWithTypicalItem(): React.Node { key: 1, }, ]; - // $FlowExpectedError - bad title type 6, should be string + // $FlowExpectedError[incompatible-type] - bad title type 6, should be string return ; } @@ -46,7 +46,7 @@ export function testMissingFieldWithTypicalItem(): React.Node { key: 1, }, ]; - // $FlowExpectedError - missing title + // $FlowExpectedError[incompatible-type] - missing title return ; } @@ -62,7 +62,7 @@ export function testGoodDataWithBadCustomRenderItemFunction(): React.Node { renderItem={info => ( { - // $FlowExpectedError - bad widgetCount type 6, should be Object + // $FlowExpectedError[prop-missing] - bad widgetCount type 6, should be Object info.item.widget.missingProp } @@ -80,22 +80,26 @@ export function testBadRenderItemFunction(): $ReadOnlyArray { }, ]; return [ - // $FlowExpectedError - title should be inside `item` + // $FlowExpectedError[incompatible-type] - title should be inside `item` + // $FlowExpectedError[incompatible-exact] } data={data} />, } data={data} />, } - // $FlowExpectedError - bad title type number, should be string + // $FlowExpectedError[incompatible-type] - bad title type number, should be string data={data} />, // EverythingIsFine } data={data} />, @@ -104,11 +108,11 @@ export function testBadRenderItemFunction(): $ReadOnlyArray { export function testOtherBadProps(): $ReadOnlyArray { return [ - // $FlowExpectedError - bad numColumns type "lots" + // $FlowExpectedError[incompatible-type] - bad numColumns type "lots" , - // $FlowExpectedError - bad windowSize type "big" + // $FlowExpectedError[incompatible-type] - bad windowSize type "big" , - // $FlowExpectedError - missing `data` prop + // $FlowExpectedError[incompatible-type] - missing `data` prop , ]; } diff --git a/packages/react-native/Libraries/Lists/__flowtests__/SectionList-flowtest.js b/packages/react-native/Libraries/Lists/__flowtests__/SectionList-flowtest.js index f954e9e8b034..49345b0de800 100644 --- a/packages/react-native/Libraries/Lists/__flowtests__/SectionList-flowtest.js +++ b/packages/react-native/Libraries/Lists/__flowtests__/SectionList-flowtest.js @@ -55,12 +55,13 @@ export function testBadRenderItemFunction(): $ReadOnlyArray { ]; return [ } sections={sections} />, } sections={sections} />, @@ -78,14 +79,14 @@ export function testBadInheritedDefaultProp(): React.MixedElement { ); } export function testMissingData(): React.MixedElement { - // $FlowExpectedError - missing `sections` prop + // $FlowExpectedError[incompatible-type] - missing `sections` prop return ; } @@ -122,7 +123,7 @@ export function testBadSectionsMetadata(): React.MixedElement { diff --git a/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricHostComponent.js b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricHostComponent.js index c240e383e312..df6d96c04f5d 100644 --- a/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricHostComponent.js +++ b/packages/react-native/Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricHostComponent.js @@ -58,12 +58,12 @@ export default class ReactFabricHostComponent implements NativeMethods { } blur() { - // $FlowFixMe - Error supressed during the migration of HostInstance to ReactNativeElement + // $FlowFixMe[incompatible-type] - Error supressed during the migration of HostInstance to ReactNativeElement TextInputState.blurTextInput(this); } focus() { - // $FlowFixMe - Error supressed during the migration of HostInstance to ReactNativeElement + // $FlowFixMe[incompatible-type] - Error supressed during the migration of HostInstance to ReactNativeElement TextInputState.focusTextInput(this); } diff --git a/packages/react-native/Libraries/ReactNative/RendererImplementation.js b/packages/react-native/Libraries/ReactNative/RendererImplementation.js index 2814aa811be8..770ac325093f 100644 --- a/packages/react-native/Libraries/ReactNative/RendererImplementation.js +++ b/packages/react-native/Libraries/ReactNative/RendererImplementation.js @@ -47,14 +47,14 @@ const getMethod: (>( ) => ReactNativeType[MethodName]) = (getRenderer, methodName) => { let cachedImpl; - // $FlowExpectedError + // $FlowExpectedError[incompatible-type] return function (arg1, arg2, arg3, arg4, arg5, arg6) { if (cachedImpl == null) { - // $FlowExpectedError + // $FlowExpectedError[prop-missing] cachedImpl = getRenderer()[methodName]; } - // $FlowExpectedError + // $FlowExpectedError[extra-arg] return cachedImpl(arg1, arg2, arg3, arg4, arg5); }; }; diff --git a/packages/react-native/Libraries/StyleSheet/__flowtests__/StyleSheet-flowtest.js b/packages/react-native/Libraries/StyleSheet/__flowtests__/StyleSheet-flowtest.js index 9ec8c2efa3fd..533fc11a9dee 100644 --- a/packages/react-native/Libraries/StyleSheet/__flowtests__/StyleSheet-flowtest.js +++ b/packages/react-native/Libraries/StyleSheet/__flowtests__/StyleSheet-flowtest.js @@ -37,17 +37,17 @@ export function testGoodCompose() { } export function testBadCompose() { - // $FlowExpectedError - Incompatible type. + // $FlowExpectedError[incompatible-type] - Incompatible type. (StyleSheet.compose(textStyle, textStyle): ImageStyleProp); - // $FlowExpectedError - Incompatible type. + // $FlowExpectedError[incompatible-type] - Incompatible type. (StyleSheet.compose( // $FlowExpectedError - Incompatible type. [textStyle], null, ): ImageStyleProp); - // $FlowExpectedError - Incompatible type. + // $FlowExpectedError[incompatible-type] - Incompatible type. (StyleSheet.compose( Math.random() < 0.5 ? textStyle : null, null, diff --git a/packages/react-native/Libraries/StyleSheet/processFilter.js b/packages/react-native/Libraries/StyleSheet/processFilter.js index 465e90c80bcf..3c15c1c9adaa 100644 --- a/packages/react-native/Libraries/StyleSheet/processFilter.js +++ b/packages/react-native/Libraries/StyleSheet/processFilter.js @@ -69,9 +69,9 @@ export default function processFilter( if (amount != null) { const filterFunction = {}; - // $FlowFixMe The key will be the correct one but flow can't see that. + // $FlowFixMe[prop-missing] The key will be the correct one but flow can't see that. filterFunction[camelizedName] = amount; - // $FlowFixMe The key will be the correct one but flow can't see that. + // $FlowFixMe[incompatible-type] The key will be the correct one but flow can't see that. result.push(filterFunction); } else { // If any primitive is invalid then apply none of the filters. This is how @@ -85,7 +85,7 @@ export default function processFilter( for (const filterFunction of filter) { const [filterName, filterValue] = Object.entries(filterFunction)[0]; if (filterName === 'dropShadow') { - // $FlowFixMe + // $FlowFixMe[incompatible-type] const dropShadow = parseDropShadow(filterValue); if (dropShadow == null) { return []; @@ -96,9 +96,9 @@ export default function processFilter( if (amount != null) { const resultObject = {}; - // $FlowFixMe + // $FlowFixMe[prop-missing] resultObject[filterName] = amount; - // $FlowFixMe + // $FlowFixMe[incompatible-type] result.push(resultObject); } else { // If any primitive is invalid then apply none of the filters. This is how diff --git a/packages/react-native/Libraries/vendor/emitter/__tests__/EventEmitter-test.js b/packages/react-native/Libraries/vendor/emitter/__tests__/EventEmitter-test.js index 393ce4af4d4b..4cb184d840e9 100644 --- a/packages/react-native/Libraries/vendor/emitter/__tests__/EventEmitter-test.js +++ b/packages/react-native/Libraries/vendor/emitter/__tests__/EventEmitter-test.js @@ -106,27 +106,27 @@ describe('listeners', () => { }).not.toThrow(); expect(() => { - // $FlowExpectedError + // $FlowExpectedError[incompatible-type] emitter.addListener('A', null); }).toThrow(); expect(() => { - // $FlowExpectedError + // $FlowExpectedError[incompatible-type] emitter.addListener('A', undefined); }).toThrow(); expect(() => { - // $FlowExpectedError + // $FlowExpectedError[incompatible-type] emitter.addListener('A', 'abc'); }).toThrow(); expect(() => { - // $FlowExpectedError + // $FlowExpectedError[incompatible-type] emitter.addListener('A', 123); }).toThrow(); expect(() => { - // $FlowExpectedError + // $FlowExpectedError[incompatible-type] emitter.addListener('A', 123); }).toThrow(); }); diff --git a/packages/react-native/jest/mockNativeComponent.js b/packages/react-native/jest/mockNativeComponent.js index 824c0870db9a..27eaa1e129d1 100644 --- a/packages/react-native/jest/mockNativeComponent.js +++ b/packages/react-native/jest/mockNativeComponent.js @@ -47,6 +47,6 @@ export default function mockNativeComponent( Component.displayName = viewName; } - // $FlowFixMe - Error supressed during the migration of HostInstance to ReactNativeElement + // $FlowFixMe[incompatible-type] - Error supressed during the migration of HostInstance to ReactNativeElement return Component; } diff --git a/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js b/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js index 71906a96d548..4430b7d66c2d 100644 --- a/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js +++ b/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js @@ -1330,7 +1330,7 @@ function SetAccessibilityFocusExample(props: {}): React.Node { return ( - {/* $FlowFixMe[incompatible-type */} + {/* $FlowFixMe[incompatible-type] */} SetAccessibilityFocus on native element. This should get focus after clicking the button! diff --git a/packages/rn-tester/js/examples/DrawerLayoutAndroid/DrawerLayoutAndroidExample.js b/packages/rn-tester/js/examples/DrawerLayoutAndroid/DrawerLayoutAndroidExample.js index c0360008167c..5e70b342aa4d 100644 --- a/packages/rn-tester/js/examples/DrawerLayoutAndroid/DrawerLayoutAndroidExample.js +++ b/packages/rn-tester/js/examples/DrawerLayoutAndroid/DrawerLayoutAndroidExample.js @@ -40,7 +40,7 @@ const Drawer = () => { I'm in the Drawer!