Skip to content

Commit 3259ca1

Browse files
huntiefacebook-github-bot
authored andcommitted
Add unstable-internals entry point (#57489)
Summary: See [**RFC0894: Removing deep imports from react-native**](react-native-community/discussions-and-proposals#894) Adds a `'react-native/unstable-internals-do-not-use'` entry point that acts as an explicit, opt-in escape hatch for frameworks and libraries that depend on specific React Native internals. Follows (and supersedes) react-native-community/discussions-and-proposals#985. Resolves T270727304. **Motivation** - Maintains a known list of which React Native internals are still in use. - Decouples removal of the Strict API opt-out from the longer-term decisions about these APIs. - Lets us relocate supporting source files freely, since consumers reference this subpath instead of deep internal paths. **Notes** - Reuses the runtime **object-with-getter pattern** (same as `index.js`), to avoid any native module side effects caused by loading adjacent file exports. - e.g. in Expo, [`hmrUtils.js`](https://github.com/expo/expo/blob/2d07e3a17f4be2284bbf579d8825fe32f762ce1a/packages/expo/src/async-require/hmrUtils.native.ts?fbclid=IwY2xjawS7GjNwZG9mA2V4dG4DYWVtAjExAGJyaWQRMTZ1ckEwU05oWUw2bDBVN21zcnRjBmFwcF9pZAEwAAEen3UuX4Ig-RD3n1ULZIHIHq21Mfk_ci9b2SxquY7UkngDs6qhnRt5YQlb8Os_aem_ylJy40egSOl1qhZ9bA9HTA) is designed to be run early in RN startup — preserve the lazy require characteristic and do not break this. - TypeScript defs on this API are minimal, with complex internal input/handle types reduced to `unknown`. As with D110602194, these are intended to be feature-locked. **Exposed internals and known consumers** NOTE: The exports the community needs have reduced slightly since the spreadsheet in RFC0895 and would almost all align with `devsupport`. However, we're explicitly leaving a decision to formalise until later, and we may need to add in extra APIs quickly based on RC feedback. | Export | Source module | Known consumers | |---|---|---| | `AppContainer` | `Libraries/ReactNative/AppContainer` | react-native-screens ([DebugContainer.tsx](https://github.com/software-mansion/react-native-screens/blob/6f38d76129d40cc8145d06b98f885b508fa7bf2d/src/components/DebugContainer.tsx#L5)) | | `AssetSourceResolver` | `Libraries/Image/AssetSourceResolver` | Re.pack ([IncludeModules.ts](https://github.com/callstack/repack/blob/2498b3a426203daa4b81c7e6f7a88e86d88e77b9/packages/repack/src/modules/IncludeModules.ts#L9)) | | `customDirectEventTypes` | `Libraries/Renderer/shims/ReactNativeViewConfigRegistry` | react-native-gesture-handler ([customDirectEventTypes.ts](https://github.com/software-mansion/react-native-gesture-handler/blob/ee147abf364bef48a02d3cf437572b4dddd30bec/packages/react-native-gesture-handler/src/handlers/customDirectEventTypes.ts#L2)) | | `DevLoadingView` | `Libraries/Utilities/DevLoadingView` | Expo ([hmrUtils.native.ts](https://github.com/expo/expo/blob/2d07e3a17f4be2284bbf579d8825fe32f762ce1a/packages/expo/src/async-require/hmrUtils.native.ts)), Re.pack ([WebpackHMRClient.ts](https://github.com/callstack/repack/blob/2498b3a426203daa4b81c7e6f7a88e86d88e77b9/packages/repack/src/modules/WebpackHMRClient.ts)) | | `getDevServer` | `Libraries/Core/Devtools/getDevServer` | Expo ([getDevServer.native.ts](https://github.com/expo/expo/blob/2d07e3a17f4be2284bbf579d8825fe32f762ce1a/packages/expo/src/async-require/getDevServer.native.ts), [hmrUtils.native.ts](https://github.com/expo/expo/blob/2d07e3a17f4be2284bbf579d8825fe32f762ce1a/packages/expo/src/async-require/hmrUtils.native.ts), [base.ts](https://github.com/expo/expo/blob/2d07e3a17f4be2284bbf579d8825fe32f762ce1a/packages/expo/src/dom/base.ts), [getDevServer.native.ts](https://github.com/expo/expo/blob/2d07e3a17f4be2284bbf579d8825fe32f762ce1a/packages/expo/metro-runtime/src/getDevServer.native.ts), [devServerEndpoints.ts](https://github.com/expo/expo/blob/2d07e3a17f4be2284bbf579d8825fe32f762ce1a/packages/expo/log-box/src/utils/devServerEndpoints.ts), [getConnectionInfo.native.ts](https://github.com/expo/expo/blob/2d07e3a17f4be2284bbf579d8825fe32f762ce1a/packages/expo/devtools/src/getConnectionInfo.native.ts), [index.native.ts](https://github.com/expo/expo/blob/2d07e3a17f4be2284bbf579d8825fe32f762ce1a/packages/expo-router/src/getDevServer/index.native.ts)) | | `HMRClient` | `Libraries/Utilities/HMRClient` | Expo ([metroServerLogs.native.ts](https://github.com/expo/expo/blob/2d07e3a17f4be2284bbf579d8825fe32f762ce1a/packages/expo/metro-runtime/src/metroServerLogs.native.ts)) | | `NativeExceptionsManager` | `Libraries/Core/NativeExceptionsManager` | Expo ([hmrUtils.native.ts](https://github.com/expo/expo/blob/2d07e3a17f4be2284bbf579d8825fe32f762ce1a/packages/expo/src/async-require/hmrUtils.native.ts)), Re.pack ([WebpackHMRClient.ts](https://github.com/callstack/repack/blob/2498b3a426203daa4b81c7e6f7a88e86d88e77b9/packages/repack/src/modules/WebpackHMRClient.ts)) | | `NativeRedBox` | `Libraries/NativeModules/specs/NativeRedBox` | Expo ([hmrUtils.native.ts](https://github.com/expo/expo/blob/2d07e3a17f4be2284bbf579d8825fe32f762ce1a/packages/expo/src/async-require/hmrUtils.native.ts)), Re.pack ([WebpackHMRClient.ts](https://github.com/callstack/repack/blob/2498b3a426203daa4b81c7e6f7a88e86d88e77b9/packages/repack/src/modules/WebpackHMRClient.ts)) | | `NativeSourceCode` | `Libraries/NativeModules/specs/NativeSourceCode` | Expo ([getBundleUrl.native.ts](https://github.com/expo/expo/blob/2d07e3a17f4be2284bbf579d8825fe32f762ce1a/packages/expo/src/utils/getBundleUrl.native.ts)) | | `PressabilityDebugView` | `Libraries/Pressability/PressabilityDebug` | react-native-gesture-handler ([PressabilityDebugView.tsx](https://github.com/software-mansion/react-native-gesture-handler/blob/ee147abf364bef48a02d3cf437572b4dddd30bec/packages/react-native-gesture-handler/src/handlers/PressabilityDebugView.tsx#L2)) | **Alternative names** - `unstable-frameworks` - `unstable-devsupport` Changelog: [Internal] Reviewed By: rubennorte Differential Revision: D110911864
1 parent c165265 commit 3259ca1

3 files changed

Lines changed: 295 additions & 0 deletions

File tree

packages/react-native/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@
4949
"default": "./src/asset-registry.js"
5050
},
5151
"./setup-env": "./src/setup-env.js",
52+
"./unstable-internals-do-not-use": {
53+
"react-native-unstable-internals": "./src/unstable-internals-do-not-use.d.ts",
54+
"types": null,
55+
"default": "./src/unstable-internals-do-not-use.js"
56+
},
5257
"./src/fb_internal/*": "./src/fb_internal/*",
5358
"./package.json": "./package.json"
5459
},
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
*/
9+
10+
// ----------------------------------------------------------------------------
11+
// Types entry point for react-native/unstable-internals-do-not-use.
12+
//
13+
// IMPORTANT: Keep this file in sync with unstable-internals-do-not-use.js.
14+
// ----------------------------------------------------------------------------
15+
16+
import type * as React from 'react';
17+
18+
// #region AppContainer
19+
20+
interface AppContainerProps {
21+
children?: React.ReactNode | undefined;
22+
rootTag: number;
23+
initialProps?: object | undefined;
24+
WrapperComponent?: React.ComponentType<any> | null | undefined;
25+
rootViewStyle?: unknown | undefined;
26+
internal_excludeLogBox?: boolean | undefined;
27+
internal_excludeInspector?: boolean | undefined;
28+
}
29+
30+
/** Root component that wraps and mounts a React Native app tree. */
31+
export const AppContainer: React.ComponentType<AppContainerProps>;
32+
33+
// #endregion
34+
// #region AssetSourceResolver
35+
36+
interface ResolvedAssetSource {
37+
readonly __packager_asset: boolean;
38+
readonly width: number | null | undefined;
39+
readonly height: number | null | undefined;
40+
readonly uri: string;
41+
readonly scale: number;
42+
}
43+
44+
/** Resolves a packager asset descriptor to a loadable source for the current platform. */
45+
export class AssetSourceResolver {
46+
serverUrl: string | null | undefined;
47+
jsbundleUrl: string | null | undefined;
48+
asset: unknown;
49+
constructor(
50+
serverUrl: string | null | undefined,
51+
jsbundleUrl: string | null | undefined,
52+
asset: unknown,
53+
);
54+
isLoadedFromServer(): boolean;
55+
isLoadedFromFileSystem(): boolean;
56+
defaultAsset(): ResolvedAssetSource;
57+
getAssetUsingResolver(resolver: 'android' | 'generic'): ResolvedAssetSource;
58+
assetServerURL(): ResolvedAssetSource;
59+
scaledAssetPath(): ResolvedAssetSource;
60+
scaledAssetURLNearBundle(): ResolvedAssetSource;
61+
resourceIdentifierWithoutScale(): ResolvedAssetSource;
62+
drawableFolderInBundle(): ResolvedAssetSource;
63+
fromSource(source: string): ResolvedAssetSource;
64+
static pickScale(scales: number[], deviceScale?: number): number;
65+
}
66+
67+
// #endregion
68+
// #region customDirectEventTypes
69+
70+
/** Registry mapping custom direct (non-bubbling) event names to their registration names. */
71+
export const customDirectEventTypes: {
72+
[eventName: string]: Readonly<{
73+
registrationName: string;
74+
}>;
75+
};
76+
77+
// #endregion
78+
// #region DevLoadingView
79+
80+
/** Dev-only overlay banner showing bundle load, refresh, and error status. */
81+
export const DevLoadingView: {
82+
showMessage(
83+
message: string,
84+
type: 'load' | 'refresh' | 'error',
85+
options?: {dismissButton?: boolean | undefined},
86+
): void;
87+
hide(): void;
88+
};
89+
90+
// #endregion
91+
// #region getDevServer
92+
93+
interface DevServerInfo {
94+
url: string;
95+
fullBundleUrl: string | null;
96+
bundleLoadedFromServer: boolean;
97+
}
98+
99+
/** Returns information about the running dev server. */
100+
export function getDevServer(): DevServerInfo;
101+
102+
// #endregion
103+
// #region HMRClient
104+
105+
/** Client that receives Fast Refresh updates and applies them at runtime. */
106+
export class HMRClient {
107+
enable(): void;
108+
disable(): void;
109+
registerBundle(requestUrl: string): void;
110+
log(
111+
level:
112+
| 'trace'
113+
| 'info'
114+
| 'warn'
115+
| 'error'
116+
| 'log'
117+
| 'group'
118+
| 'groupCollapsed'
119+
| 'groupEnd'
120+
| 'debug',
121+
data: ReadonlyArray<unknown>,
122+
): void;
123+
setup(
124+
platform: string,
125+
bundleEntry: string,
126+
host: string,
127+
port: number | string,
128+
isEnabled: boolean,
129+
scheme?: string,
130+
): void;
131+
}
132+
133+
// #endregion
134+
// #region NativeExceptionsManager
135+
136+
interface StackFrame {
137+
column: number | null;
138+
file: string | null;
139+
lineNumber: number | null;
140+
methodName: string;
141+
collapse?: boolean | undefined;
142+
}
143+
144+
interface ExceptionData {
145+
message: string;
146+
originalMessage: string | null;
147+
name: string | null;
148+
componentStack: string | null;
149+
stack: StackFrame[];
150+
id: number;
151+
isFatal: boolean;
152+
extraData?: object | undefined;
153+
}
154+
155+
/** Reports JS exceptions to native and manages RedBox. */
156+
export const NativeExceptionsManager: {
157+
reportFatalException(
158+
message: string,
159+
stack: StackFrame[],
160+
exceptionId: number,
161+
): void;
162+
reportSoftException(
163+
message: string,
164+
stack: StackFrame[],
165+
exceptionId: number,
166+
): void;
167+
dismissRedbox(): void;
168+
reportException(data: ExceptionData): void;
169+
};
170+
171+
// #endregion
172+
// #region NativeRedBox
173+
174+
interface NativeRedBoxSpec {
175+
setExtraData(extraData: object, forIdentifier: string): void;
176+
dismiss(): void;
177+
}
178+
179+
/** Native module for the RedBox error overlay; null when unavailable. */
180+
export const NativeRedBox: NativeRedBoxSpec | null;
181+
182+
// #endregion
183+
// #region NativeSourceCode
184+
185+
interface SourceCodeConstants {
186+
scriptURL: string;
187+
}
188+
189+
/** Native module exposing source-code constants such as the bundle scriptURL. */
190+
export const NativeSourceCode: {
191+
getConstants(): SourceCodeConstants;
192+
};
193+
194+
// #endregion
195+
// #region PressabilityDebugView
196+
197+
type Rect = Readonly<{
198+
bottom?: number | null | undefined;
199+
left?: number | null | undefined;
200+
right?: number | null | undefined;
201+
top?: number | null | undefined;
202+
}>;
203+
204+
type RectOrSize = Rect | number;
205+
206+
interface PressabilityDebugViewProps {
207+
color: unknown;
208+
hitSlop: RectOrSize | null | undefined;
209+
}
210+
211+
/** Debug overlay that visualizes press targets when enabled via the Inspector. */
212+
export const PressabilityDebugView: React.ComponentType<PressabilityDebugViewProps>;
213+
214+
// #endregion
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @noflow
8+
* @format
9+
*/
10+
11+
'use strict';
12+
'use client';
13+
14+
// ----------------------------------------------------------------------------
15+
// react-native/unstable-internals-do-not-use
16+
//
17+
// UNSTABLE WITH NO SEMVER GUARANTEES.
18+
// SHOULD NOT BE DEPENDED ON BY NEW CODE.
19+
//
20+
// This is a secondary entry point for frameworks and libraries that depend on
21+
// specific React Native internals, serving as a compatibility bridge.
22+
//
23+
// Consuming codebases must opt in via tsconfig.json:
24+
// "customConditions": ["react-native-unstable-internals"]
25+
//
26+
// Having this entry point:
27+
// - Maintains a known list of which React Native internals are in use.
28+
// - Enables us to relocate supporting files more freely.
29+
// - Gives us time to decide on the future of these APIs (independent from
30+
// removal of the Strict API opt out).
31+
//
32+
// The long term future of these exports is to formalize/delete them where
33+
// appropriate, and collapse this entry point.
34+
//
35+
// Replaces RFC0985
36+
// https://github.com/react-native-community/discussions-and-proposals/pull/985
37+
// where we reviewed internal APIs used in the ecosystem.
38+
//
39+
// IMPORTANT: Keep this file in sync with unstable-internals-do-not-use.d.ts.
40+
// ----------------------------------------------------------------------------
41+
42+
// eslint-disable-next-line @react-native/monorepo/no-commonjs-exports
43+
module.exports = {
44+
get AppContainer() {
45+
return require('../Libraries/ReactNative/AppContainer').default;
46+
},
47+
get AssetSourceResolver() {
48+
return require('../Libraries/Image/AssetSourceResolver').default;
49+
},
50+
get customDirectEventTypes() {
51+
return require('../Libraries/Renderer/shims/ReactNativeViewConfigRegistry')
52+
.customDirectEventTypes;
53+
},
54+
get DevLoadingView() {
55+
return require('../Libraries/Utilities/DevLoadingView').default;
56+
},
57+
get getDevServer() {
58+
return require('../Libraries/Core/Devtools/getDevServer').default;
59+
},
60+
get HMRClient() {
61+
return require('../Libraries/Utilities/HMRClient').default;
62+
},
63+
get NativeExceptionsManager() {
64+
return require('../Libraries/Core/NativeExceptionsManager').default;
65+
},
66+
get NativeRedBox() {
67+
return require('../Libraries/NativeModules/specs/NativeRedBox').default;
68+
},
69+
get NativeSourceCode() {
70+
return require('../Libraries/NativeModules/specs/NativeSourceCode').default;
71+
},
72+
get PressabilityDebugView() {
73+
return require('../Libraries/Pressability/PressabilityDebug')
74+
.PressabilityDebugView;
75+
},
76+
};

0 commit comments

Comments
 (0)