From 502ea07b9555fe4ffc21bddbf8c8595b6bd77a0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Mon, 21 Sep 2026 08:35:11 -0700 Subject: [PATCH 1/2] Expose renderer through unstable internals (#58618) Summary: Expose the renderer proxy as `Renderer` from `react-native/unstable-internals-do-not-use`. Generated Codegen modules can be emitted into consumer packages and need a package entry point for dispatching renderer commands without deep imports. Changelog: [Internal] Reviewed By: cipolleschi Differential Revision: D120991150 --- .../src/unstable-internals-do-not-use.d.ts | 51 +++++++++++++++++++ .../src/unstable-internals-do-not-use.js | 30 +++++++++++ .../src/unstable-internals-do-not-use.js.flow | 6 +++ 3 files changed, 87 insertions(+) diff --git a/packages/react-native/src/unstable-internals-do-not-use.d.ts b/packages/react-native/src/unstable-internals-do-not-use.d.ts index 6c1f3bf54c40..6ca8ac9f1a01 100644 --- a/packages/react-native/src/unstable-internals-do-not-use.d.ts +++ b/packages/react-native/src/unstable-internals-do-not-use.d.ts @@ -14,6 +14,8 @@ // ---------------------------------------------------------------------------- import type * as React from 'react'; +import type {ColorValue} from '../types_generated/Libraries/StyleSheet/StyleSheet'; +import type {ProcessedColorValue} from '../types_generated/Libraries/StyleSheet/processColor'; // #region AppContainer @@ -64,6 +66,17 @@ export class AssetSourceResolver { static pickScale(scales: number[], deviceScale?: number): number; } +// #endregion +// #region ConditionallyIgnoredEventHandlers + +/** + * Marks generated event-handler view-config attributes as conditionally + * ignored on platforms that do not synthesize them from native view managers. + */ +export function ConditionallyIgnoredEventHandlers< + T extends Readonly>, +>(value: T): T | undefined; + // #endregion // #region customDirectEventTypes @@ -212,3 +225,41 @@ interface PressabilityDebugViewProps { export const PressabilityDebugView: React.ComponentType; // #endregion +// #region Renderer + +/** Renderer APIs used by generated Codegen modules. */ +export * as Renderer from '../types_generated/Libraries/ReactNative/RendererImplementation'; + +// #endregion +// #region Codegen view config helpers + +export const colorAttribute: + | true + | Readonly<{ + process: typeof import('../types_generated/Libraries/StyleSheet/processColor').default; + }>; + +export function processColorArray( + colors: ReadonlyArray | null | undefined, +): ReadonlyArray | null; + +type Point = { + x: number | null | undefined; + y: number | null | undefined; +}; + +export function pointsDiffer( + one: Point | null | undefined, + two: Point | null | undefined, +): boolean; + +type EdgeInsets = { + top: number | null | undefined; + left: number | null | undefined; + right: number | null | undefined; + bottom: number | null | undefined; +}; + +export function insetsDiffer(one: EdgeInsets, two: EdgeInsets): boolean; + +// #endregion diff --git a/packages/react-native/src/unstable-internals-do-not-use.js b/packages/react-native/src/unstable-internals-do-not-use.js index 96c7b48e1ab0..a83bbb9b9d02 100644 --- a/packages/react-native/src/unstable-internals-do-not-use.js +++ b/packages/react-native/src/unstable-internals-do-not-use.js @@ -40,15 +40,21 @@ // and unstable-internals-do-not-use.js.flow. // ---------------------------------------------------------------------------- +import typeof {colorAttribute} from '../Libraries/Components/View/ReactNativeStyleAttributes'; import typeof getDevServer from '../Libraries/Core/Devtools/getDevServer'; import typeof NativeExceptionsManager from '../Libraries/Core/NativeExceptionsManager'; import typeof AssetSourceResolver from '../Libraries/Image/AssetSourceResolver'; +import typeof {ConditionallyIgnoredEventHandlers} from '../Libraries/NativeComponent/ViewConfigIgnore'; import typeof NativeRedBox from '../Libraries/NativeModules/specs/NativeRedBox'; import typeof NativeSourceCode from '../Libraries/NativeModules/specs/NativeSourceCode'; import typeof {PressabilityDebugView} from '../Libraries/Pressability/PressabilityDebug'; import typeof AppContainer from '../Libraries/ReactNative/AppContainer'; +import typeof * as Renderer from '../Libraries/ReactNative/RendererProxy'; import typeof {customDirectEventTypes} from '../Libraries/Renderer/shims/ReactNativeViewConfigRegistry'; +import typeof processColorArray from '../Libraries/StyleSheet/processColorArray'; import typeof DevLoadingView from '../Libraries/Utilities/DevLoadingView'; +import typeof insetsDiffer from '../Libraries/Utilities/differ/insetsDiffer'; +import typeof pointsDiffer from '../Libraries/Utilities/differ/pointsDiffer'; import typeof HMRClient from '../Libraries/Utilities/HMRClient'; // flowlint unsafe-getters-setters:off @@ -60,6 +66,12 @@ module.exports = { get AssetSourceResolver(): AssetSourceResolver { return require('../Libraries/Image/AssetSourceResolver').default; }, + get ConditionallyIgnoredEventHandlers(): ConditionallyIgnoredEventHandlers<{ + readonly [name: string]: true, + }> { + return require('../Libraries/NativeComponent/ViewConfigIgnore') + .ConditionallyIgnoredEventHandlers; + }, get customDirectEventTypes(): customDirectEventTypes { return require('../Libraries/Renderer/shims/ReactNativeViewConfigRegistry') .customDirectEventTypes; @@ -86,4 +98,22 @@ module.exports = { return require('../Libraries/Pressability/PressabilityDebug') .PressabilityDebugView; }, + // Generated Codegen modules can be emitted outside this package, so they + // cannot use package-relative imports to access these implementations. + get colorAttribute(): colorAttribute { + return require('../Libraries/Components/View/ReactNativeStyleAttributes') + .colorAttribute; + }, + get insetsDiffer(): insetsDiffer { + return require('../Libraries/Utilities/differ/insetsDiffer').default; + }, + get pointsDiffer(): pointsDiffer { + return require('../Libraries/Utilities/differ/pointsDiffer').default; + }, + get processColorArray(): processColorArray { + return require('../Libraries/StyleSheet/processColorArray').default; + }, + get Renderer(): Renderer { + return require('../Libraries/ReactNative/RendererProxy'); + }, }; diff --git a/packages/react-native/src/unstable-internals-do-not-use.js.flow b/packages/react-native/src/unstable-internals-do-not-use.js.flow index b5f6ddec6a15..5e18d997a876 100644 --- a/packages/react-native/src/unstable-internals-do-not-use.js.flow +++ b/packages/react-native/src/unstable-internals-do-not-use.js.flow @@ -16,6 +16,8 @@ export {default as AppContainer} from '../Libraries/ReactNative/AppContainer'; export {default as AssetSourceResolver} from '../Libraries/Image/AssetSourceResolver'; +export {colorAttribute} from '../Libraries/Components/View/ReactNativeStyleAttributes'; +export {ConditionallyIgnoredEventHandlers} from '../Libraries/NativeComponent/ViewConfigIgnore'; export {customDirectEventTypes} from '../Libraries/Renderer/shims/ReactNativeViewConfigRegistry'; export {default as DevLoadingView} from '../Libraries/Utilities/DevLoadingView'; export {default as getDevServer} from '../Libraries/Core/Devtools/getDevServer'; @@ -24,3 +26,7 @@ export {default as NativeExceptionsManager} from '../Libraries/Core/NativeExcept export {default as NativeRedBox} from '../Libraries/NativeModules/specs/NativeRedBox'; export {default as NativeSourceCode} from '../Libraries/NativeModules/specs/NativeSourceCode'; export {PressabilityDebugView} from '../Libraries/Pressability/PressabilityDebug'; +export {default as insetsDiffer} from '../Libraries/Utilities/differ/insetsDiffer'; +export {default as pointsDiffer} from '../Libraries/Utilities/differ/pointsDiffer'; +export {default as processColorArray} from '../Libraries/StyleSheet/processColorArray'; +export * as Renderer from '../Libraries/ReactNative/RendererProxy'; From d472c0d1c1de963273e369b2173b56db1d12111d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Mon, 21 Sep 2026 08:35:11 -0700 Subject: [PATCH 2/2] Remove deep imports from the Jest preset (#58602) Summary: Use the React Native root and supported secondary entry points for public APIs used by the Jest preset. Resolve implementation modules through package-relative paths so the preset does not depend on unsupported package subpaths. Changelog: [Internal] Reviewed By: cipolleschi Differential Revision: D120717568 --- jest.config.js | 2 ++ packages/jest-preset/jest/mocks/ActivityIndicator.js | 2 +- packages/jest-preset/jest/mocks/Image.js | 2 +- packages/jest-preset/jest/mocks/Modal.js | 2 +- packages/jest-preset/jest/mocks/RefreshControl.js | 2 +- packages/jest-preset/jest/mocks/ScrollView.js | 2 +- packages/jest-preset/jest/mocks/Text.js | 2 +- packages/jest-preset/jest/mocks/TextInput.js | 2 +- packages/jest-preset/jest/mocks/View.js | 2 +- packages/jest-preset/jest/mocks/ViewNativeComponent.js | 2 +- 10 files changed, 11 insertions(+), 9 deletions(-) diff --git a/jest.config.js b/jest.config.js index 6e98ae747ea4..a91ef75838af 100644 --- a/jest.config.js +++ b/jest.config.js @@ -32,6 +32,8 @@ module.exports = { '/packages/react-native/src/react-private-interface.js', '^react-native/setup-env$': '/packages/react-native/src/setup-env.js', + '^react-native/unstable-internals-do-not-use$': + '/packages/react-native/src/unstable-internals-do-not-use.js', }, setupFiles: ['./packages/jest-preset/jest/local-setup.js'], fakeTimers: { diff --git a/packages/jest-preset/jest/mocks/ActivityIndicator.js b/packages/jest-preset/jest/mocks/ActivityIndicator.js index 42b6075a5a51..71499a60a321 100644 --- a/packages/jest-preset/jest/mocks/ActivityIndicator.js +++ b/packages/jest-preset/jest/mocks/ActivityIndicator.js @@ -9,7 +9,7 @@ */ import typeof * as TmockComponent from '../mockComponent'; -import typeof TActivityIndicator from 'react-native/Libraries/Components/ActivityIndicator/ActivityIndicator'; +import typeof {ActivityIndicator as TActivityIndicator} from 'react-native'; const mockComponent = jest.requireActual('../mockComponent').default; diff --git a/packages/jest-preset/jest/mocks/Image.js b/packages/jest-preset/jest/mocks/Image.js index 8d65ba8a488a..39e335889979 100644 --- a/packages/jest-preset/jest/mocks/Image.js +++ b/packages/jest-preset/jest/mocks/Image.js @@ -9,7 +9,7 @@ */ import typeof * as TmockComponent from '../mockComponent'; -import typeof TImage from 'react-native/Libraries/Image/Image'; +import typeof {Image as TImage} from 'react-native'; const mockComponent = jest.requireActual('../mockComponent').default; diff --git a/packages/jest-preset/jest/mocks/Modal.js b/packages/jest-preset/jest/mocks/Modal.js index d8514d27a44c..e099887ded47 100644 --- a/packages/jest-preset/jest/mocks/Modal.js +++ b/packages/jest-preset/jest/mocks/Modal.js @@ -9,7 +9,7 @@ */ import typeof * as TmockComponent from '../mockComponent'; -import type {ModalProps} from 'react-native/Libraries/Modal/Modal'; +import type {ModalProps} from 'react-native'; const mockComponent = jest.requireActual('../mockComponent').default; diff --git a/packages/jest-preset/jest/mocks/RefreshControl.js b/packages/jest-preset/jest/mocks/RefreshControl.js index cb9800dbbb75..d7a1c072660d 100644 --- a/packages/jest-preset/jest/mocks/RefreshControl.js +++ b/packages/jest-preset/jest/mocks/RefreshControl.js @@ -8,7 +8,7 @@ * @format */ -import type {RefreshControlProps} from 'react-native/Libraries/Components/RefreshControl/RefreshControl'; +import type {RefreshControlProps} from 'react-native'; import type {HostComponent} from 'react-native/src/private/types/HostComponent'; import * as React from 'react'; diff --git a/packages/jest-preset/jest/mocks/ScrollView.js b/packages/jest-preset/jest/mocks/ScrollView.js index 39d8f69af9d1..06a1bf85e4dc 100644 --- a/packages/jest-preset/jest/mocks/ScrollView.js +++ b/packages/jest-preset/jest/mocks/ScrollView.js @@ -10,7 +10,7 @@ import typeof * as TmockComponent from '../mockComponent'; import typeof * as TMockNativeMethods from '../MockNativeMethods'; -import typeof TScrollView from 'react-native/Libraries/Components/ScrollView/ScrollView'; +import typeof {ScrollView as TScrollView} from 'react-native'; import type {ScrollViewNativeProps} from 'react-native/Libraries/Components/ScrollView/ScrollViewNativeComponentType'; import * as React from 'react'; diff --git a/packages/jest-preset/jest/mocks/Text.js b/packages/jest-preset/jest/mocks/Text.js index da56850e8a30..89f33c0b35e0 100644 --- a/packages/jest-preset/jest/mocks/Text.js +++ b/packages/jest-preset/jest/mocks/Text.js @@ -10,7 +10,7 @@ import typeof * as TmockComponent from '../mockComponent'; import typeof * as TMockNativeMethods from '../MockNativeMethods'; -import typeof TText from 'react-native/Libraries/Text/Text'; +import typeof {Text as TText} from 'react-native'; const mockComponent = jest.requireActual('../mockComponent').default; diff --git a/packages/jest-preset/jest/mocks/TextInput.js b/packages/jest-preset/jest/mocks/TextInput.js index fc34a8b65dcf..b74e7ab8a926 100644 --- a/packages/jest-preset/jest/mocks/TextInput.js +++ b/packages/jest-preset/jest/mocks/TextInput.js @@ -10,7 +10,7 @@ import typeof * as TmockComponent from '../mockComponent'; import typeof * as TMockNativeMethods from '../MockNativeMethods'; -import typeof TTextInput from 'react-native/Libraries/Components/TextInput/TextInput'; +import typeof {TextInput as TTextInput} from 'react-native'; const mockComponent = jest.requireActual('../mockComponent').default; diff --git a/packages/jest-preset/jest/mocks/View.js b/packages/jest-preset/jest/mocks/View.js index dca384233d21..fce3ca9fca0c 100644 --- a/packages/jest-preset/jest/mocks/View.js +++ b/packages/jest-preset/jest/mocks/View.js @@ -10,7 +10,7 @@ import typeof * as TmockComponent from '../mockComponent'; import typeof * as TMockNativeMethods from '../MockNativeMethods'; -import typeof TView from 'react-native/Libraries/Components/View/View'; +import typeof {View as TView} from 'react-native'; const mockComponent = jest.requireActual('../mockComponent').default; diff --git a/packages/jest-preset/jest/mocks/ViewNativeComponent.js b/packages/jest-preset/jest/mocks/ViewNativeComponent.js index 716bd4c89a3c..4f6ca6f36eae 100644 --- a/packages/jest-preset/jest/mocks/ViewNativeComponent.js +++ b/packages/jest-preset/jest/mocks/ViewNativeComponent.js @@ -8,7 +8,7 @@ * @format */ -import type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes'; +import type {ViewProps} from 'react-native'; import * as React from 'react'; import {createElement} from 'react';