diff --git a/.flowconfig b/.flowconfig index 4d01ae0c0e40..832667b526f1 100644 --- a/.flowconfig +++ b/.flowconfig @@ -56,6 +56,8 @@ experimental.multi_platform.extensions=.android munge_underscores=true module.name_mapper='^react-native$' -> '/packages/react-native/index.js' +module.name_mapper='^react-native/react-private-interface$' -> '/packages/react-native/src/react-private-interface.js' +module.name_mapper='^react-native/setup-env$' -> '/packages/react-native/src/setup-env.js' module.name_mapper='^react-native/\(.*\)$' -> '/packages/react-native/\1' module.name_mapper='^@react-native/dev-middleware$' -> '/packages/dev-middleware' module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\|xml\|ktx\|heic\|heif\)$' -> '/packages/react-native/Libraries/Image/RelativeImageStub' diff --git a/.github/workflows/test-all.yml b/.github/workflows/test-all.yml index 422a90c712ae..1f49896cbf36 100644 --- a/.github/workflows/test-all.yml +++ b/.github/workflows/test-all.yml @@ -471,9 +471,9 @@ jobs: - name: Flow shell: bash run: yarn flow-check - - name: TypeScript + - name: TypeScript (legacy deep imports / manual types) shell: bash - run: yarn test-typescript + run: yarn test-typescript-legacy test_js: runs-on: ubuntu-latest diff --git a/jest.config.js b/jest.config.js index 240abcbfeae2..aa6c08fb0ad2 100644 --- a/jest.config.js +++ b/jest.config.js @@ -26,6 +26,11 @@ module.exports = { '.*': './jest/preprocessor.js', }, resolver: './packages/jest-preset/jest/resolver.js', + moduleNameMapper: { + // `resolver.js` strips `exports`, so alias this subpath to its `src/` impl. + '^react-native/setup-env$': + '/packages/react-native/src/setup-env.js', + }, setupFiles: ['./packages/jest-preset/jest/local-setup.js'], fakeTimers: { enableGlobally: true, diff --git a/package.json b/package.json index 0f8e035b505e..df2852f1c5a2 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "test-release-local-clean": "node ./scripts/release-testing/test-release-local-clean.js", "test-release-local": "node ./scripts/release-testing/test-release-local.js", "test-ios": "./scripts/objc-test.sh test", - "test-typescript": "tsc -p packages/react-native/types/tsconfig.json", - "test-generated-typescript": "tsc -p packages/react-native/types_generated/tsconfig.test.json", + "test-typescript-legacy": "tsc -p packages/react-native/__typetests__/tsconfig.legacy.json", + "test-generated-typescript": "tsc -p packages/react-native/__typetests__/tsconfig.json", "test": "jest", "fantom": "./scripts/fantom.sh", "fantom-cli": "./scripts/fantom-cli.sh", diff --git a/packages/community-cli-plugin/src/utils/loadMetroConfig.js b/packages/community-cli-plugin/src/utils/loadMetroConfig.js index 43d9e4126af7..a41b028fa29a 100644 --- a/packages/community-cli-plugin/src/utils/loadMetroConfig.js +++ b/packages/community-cli-plugin/src/utils/loadMetroConfig.js @@ -60,16 +60,17 @@ function getCommunityCliDefaultConfig( return { resolver, serializer: { - // We can include multiple copies of InitializeCore here because metro will + // We can include multiple copies of setup-env here because Metro will // only add ones that are already part of the bundle getModulesRunBeforeMainModule: () => [ - require.resolve( - path.join(ctx.reactNativePath, 'Libraries/Core/InitializeCore'), - {paths: [ctx.root]}, - ), + // NOTE: ctx.reactNativePath is an absolute path, therefore we need to + // reference setup-env.js here by exact path specifier. + require.resolve(path.join(ctx.reactNativePath, 'src/setup-env.js'), { + paths: [ctx.root], + }), ...outOfTreePlatforms.map(platform => require.resolve( - `${ctx.platforms[platform].npmPackageName}/Libraries/Core/InitializeCore`, + `${ctx.platforms[platform].npmPackageName}/setup-env`, {paths: [ctx.root]}, ), ), diff --git a/packages/eslint-plugin-react-native/__tests__/no-deep-imports-test.js b/packages/eslint-plugin-react-native/__tests__/no-deep-imports-test.js index d28b33f45835..bf14e917f7bf 100644 --- a/packages/eslint-plugin-react-native/__tests__/no-deep-imports-test.js +++ b/packages/eslint-plugin-react-native/__tests__/no-deep-imports-test.js @@ -29,10 +29,10 @@ eslintTester.run('../no-deep-imports', rule, { "import Foo from 'react-native-foo';", "import Foo from 'react-native-foo/Foo';", "import Foo from 'react/native/Foo';", - "import 'react-native/Libraries/Core/InitializeCore';", - "require('react-native/Libraries/Core/InitializeCore');", "import Foo from 'react-native/src/fb_internal/Foo'", "require('react-native/src/fb_internal/Foo')", + "import 'react-native/setup-env';", + "require('react-native/setup-env');", ], invalid: [ { @@ -125,5 +125,31 @@ eslintTester.run('../no-deep-imports', rule, { ], output: null, }, + { + code: "import 'react-native/Libraries/Core/InitializeCore';", + errors: [ + { + messageId: 'useReplacementSource', + data: { + importPath: 'react-native/Libraries/Core/InitializeCore', + replacementSource: 'react-native/setup-env', + }, + }, + ], + output: "import 'react-native/setup-env';", + }, + { + code: "require('react-native/Libraries/Core/InitializeCore');", + errors: [ + { + messageId: 'useReplacementSource', + data: { + importPath: 'react-native/Libraries/Core/InitializeCore', + replacementSource: 'react-native/setup-env', + }, + }, + ], + output: "require('react-native/setup-env');", + }, ], }); diff --git a/packages/eslint-plugin-react-native/no-deep-imports.js b/packages/eslint-plugin-react-native/no-deep-imports.js index 6446c70ba6ac..e84a1f24d45b 100644 --- a/packages/eslint-plugin-react-native/no-deep-imports.js +++ b/packages/eslint-plugin-react-native/no-deep-imports.js @@ -21,6 +21,8 @@ module.exports = { messages: { deepImport: "'{{importPath}}' React Native deep imports are deprecated. Please use the top level import instead.", + useReplacementSource: + "'{{importPath}}' is deprecated. Please import '{{replacementSource}}' instead.", }, schema: [], fixable: 'code', @@ -31,12 +33,14 @@ module.exports = { ImportDeclaration(node) { if ( !isDeepReactNativeImport(node.source) || - isInitializeCoreImport(node.source) || isSecondaryEntryPoint(node.source) || isFbInternalImport(node.source) ) { return; } + if (reportReplacementSource(node.source)) { + return; + } if (isDefaultImport(node)) { const reactNativeSource = node.source.value.slice( 'react-native/'.length, @@ -88,13 +92,16 @@ module.exports = { CallExpression(node) { if ( !isDeepRequire(node) || - isInitializeCoreImport(node.arguments[0]) || isSecondaryEntryPoint(node.arguments[0]) || isFbInternalImport(node.arguments[0]) ) { return; } + if (reportReplacementSource(node.arguments[0])) { + return; + } + const parent = node.parent; const importPath = node.arguments[0].value; @@ -123,6 +130,26 @@ module.exports = { }, }; + function reportReplacementSource(source) { + const reactNativeSource = source.value.slice('react-native/'.length); + const mapping = publicAPIMapping[reactNativeSource]; + if (!mapping || !mapping.replacementSource) { + return false; + } + context.report({ + node: source, + messageId: 'useReplacementSource', + data: { + importPath: source.value, + replacementSource: mapping.replacementSource, + }, + fix(fixer) { + return fixer.replaceText(source, `'${mapping.replacementSource}'`); + }, + }); + return true; + } + function getStandardReport(source) { return { node: source, @@ -167,20 +194,15 @@ module.exports = { return parts.length > 1 && parts[0] === 'react-native'; } - function isInitializeCoreImport(source) { - if (source.type !== 'Literal' || typeof source.value !== 'string') { - return false; - } - - return source.value === 'react-native/Libraries/Core/InitializeCore'; - } - function isSecondaryEntryPoint(source) { if (source.type !== 'Literal' || typeof source.value !== 'string') { return false; } - return source.value === 'react-native/asset-registry'; + return ( + source.value === 'react-native/asset-registry' || + source.value === 'react-native/setup-env' + ); } function isFbInternalImport(source) { diff --git a/packages/eslint-plugin-react-native/utils.js b/packages/eslint-plugin-react-native/utils.js index 98d1e90ca0b0..728cc26d6ab3 100644 --- a/packages/eslint-plugin-react-native/utils.js +++ b/packages/eslint-plugin-react-native/utils.js @@ -37,6 +37,13 @@ const publicAPIMapping = { default: 'experimental_LayoutConformance', types: ['LayoutConformanceProps'], }, + 'Libraries/Core/InitializeCore': { + // `InitializeCore` has no public named export; the deep import must be + // swapped for the `react-native/setup-env` entry point entirely. + default: null, + types: null, + replacementSource: 'react-native/setup-env', + }, 'Libraries/Lists/FlatList': { default: 'FlatList', types: ['FlatListProps'], diff --git a/packages/jest-preset/README.md b/packages/jest-preset/README.md index de420553b828..c53da96056d0 100644 --- a/packages/jest-preset/README.md +++ b/packages/jest-preset/README.md @@ -36,9 +36,3 @@ module.exports = { ``` You can further customize your Jest configuration by specifying other options. See [Jest's `jest.config.js` documentation](https://jestjs.io/docs/configuration) to learn more. - -### Migration Note - -This Jest preset used to be part of the core `react-native` package and accessible at `react-native/jest-preset.js`. As long as `@react-native/jest-preset` is installed, `react-native/jest-preset.js` will be aliased to this package and continue to work but is deprecated. - -Follow the installation instructions above to migrate to `@react-native/jest-preset` and change `preset: 'react-native'` to `preset: '@react-native/jest-preset` to migrate. diff --git a/packages/jest-preset/jest-preset.js b/packages/jest-preset/jest-preset.js index 5632cc7fe5c2..bfe131904a21 100644 --- a/packages/jest-preset/jest-preset.js +++ b/packages/jest-preset/jest-preset.js @@ -18,6 +18,11 @@ module.exports = { platforms: ['android', 'ios', 'native'], }, moduleNameMapper: { + // `setup-env` is a secondary entry point exposed via the package's + // `exports`, but `./jest/resolver.js` strips `exports` and the generic + // mapper below resolves subpaths as literal directory paths. Alias it + // explicitly so it resolves to its `src/` implementation. + '^react-native/setup-env$': `${path.dirname(require.resolve('react-native'))}/src/setup-env.js`, '^react-native($|/.*)': `${path.dirname(require.resolve('react-native'))}/$1`, }, resolver: require.resolve('./jest/resolver.js'), diff --git a/packages/jest-preset/jest/setup.js b/packages/jest-preset/jest/setup.js index 29f86698a129..483f14cad8f3 100644 --- a/packages/jest-preset/jest/setup.js +++ b/packages/jest-preset/jest/setup.js @@ -133,6 +133,7 @@ mock( 'm#react-native/Libraries/Core/InitializeCore', 'm#./mocks/InitializeCore', ); +mock('m#react-native/setup-env', 'm#./mocks/InitializeCore'); mock('m#react-native/Libraries/Core/NativeExceptionsManager'); mock('m#react-native/Libraries/Image/Image', 'm#./mocks/Image'); mock( diff --git a/packages/metro-config/src/index.flow.js b/packages/metro-config/src/index.flow.js index 03a9aed5ace4..dcac214b3978 100644 --- a/packages/metro-config/src/index.flow.js +++ b/packages/metro-config/src/index.flow.js @@ -61,7 +61,7 @@ export function getDefaultConfig(projectRoot: string): ConfigT { serializer: { // Note: This option is overridden in cli-plugin-metro (getOverrideConfig) getModulesRunBeforeMainModule: () => [ - require.resolve('react-native/Libraries/Core/InitializeCore'), + require.resolve('react-native/setup-env'), ], getPolyfills: () => require('@react-native/js-polyfills')(), isThirdPartyModule({path: modulePath}: Readonly<{path: string, ...}>) { diff --git a/packages/react-native-babel-preset/src/__tests__/plugin-warn-on-deep-imports-test.js b/packages/react-native-babel-preset/src/__tests__/plugin-warn-on-deep-imports-test.js index 820fc9c59eae..66f247d7112a 100644 --- a/packages/react-native-babel-preset/src/__tests__/plugin-warn-on-deep-imports-test.js +++ b/packages/react-native-babel-preset/src/__tests__/plugin-warn-on-deep-imports-test.js @@ -82,17 +82,3 @@ test('import from other package', () => { `"import { foo } from 'react-native-foo';"`, ); }); - -test('import react-native/Libraries/Core/InitializeCore', () => { - const code = ` - import 'react-native/Libraries/Core/InitializeCore'; - require('react-native/Libraries/Core/InitializeCore'); - export * from 'react-native/Libraries/Core/InitializeCore'; - `; - - expect(transform(code, [rnDeepImportsWarningPlugin])).toMatchInlineSnapshot(` - "import 'react-native/Libraries/Core/InitializeCore'; - require('react-native/Libraries/Core/InitializeCore'); - export * from 'react-native/Libraries/Core/InitializeCore';" - `); -}); diff --git a/packages/react-native-babel-preset/src/plugin-warn-on-deep-imports.js b/packages/react-native-babel-preset/src/plugin-warn-on-deep-imports.js index 98f317586fa4..5e98ed59ad74 100644 --- a/packages/react-native-babel-preset/src/plugin-warn-on-deep-imports.js +++ b/packages/react-native-babel-preset/src/plugin-warn-on-deep-imports.js @@ -38,10 +38,6 @@ function isDeepReactNativeImport(source) { return parts.length > 1 && parts[0] === 'react-native'; } -function isInitializeCoreImport(source) { - return source === 'react-native/Libraries/Core/InitializeCore'; -} - function withLocation(node, loc) { if (!node.loc) { return {...node, loc}; @@ -55,7 +51,7 @@ module.exports = ({types: t}) => ({ ImportDeclaration(path, state) { const source = path.node.source.value; - if (isDeepReactNativeImport(source) && !isInitializeCoreImport(source)) { + if (isDeepReactNativeImport(source)) { const loc = path.node.loc; state.import.push({source, loc}); } @@ -71,10 +67,7 @@ module.exports = ({types: t}) => ({ ) { const source = args[0].node.type === 'StringLiteral' ? args[0].node.value : ''; - if ( - isDeepReactNativeImport(source) && - !isInitializeCoreImport(source) - ) { + if (isDeepReactNativeImport(source)) { const loc = path.node.loc; state.require.push({source, loc}); } @@ -83,11 +76,7 @@ module.exports = ({types: t}) => ({ ExportNamedDeclaration(path, state) { const source = path.node.source; - if ( - source && - isDeepReactNativeImport(source.value) && - !isInitializeCoreImport(source) - ) { + if (source && isDeepReactNativeImport(source.value)) { const loc = path.node.loc; state.export.push({source: source.value, loc}); } diff --git a/packages/react-native/Libraries/Core/InitializeCore.js b/packages/react-native/Libraries/Core/InitializeCore.js index c3ba83318376..8846ce4dea50 100644 --- a/packages/react-native/Libraries/Core/InitializeCore.js +++ b/packages/react-native/Libraries/Core/InitializeCore.js @@ -22,6 +22,7 @@ * 1. Require system. * 2. Bridged modules. * + * @deprecated Since 0.87. Use `'react-native/setup-env'` instead. */ 'use strict'; diff --git a/packages/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js b/packages/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js index 190d559a7117..2cf1eca02e95 100644 --- a/packages/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js +++ b/packages/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js @@ -8,124 +8,13 @@ * @format */ -import typeof dispatchNativeEvent from '../../src/private/renderer/events/dispatchNativeEvent'; -import typeof CustomEvent from '../../src/private/webapis/dom/events/CustomEvent'; -import typeof BatchedBridge from '../BatchedBridge/BatchedBridge'; -import typeof legacySendAccessibilityEvent from '../Components/AccessibilityInfo/legacySendAccessibilityEvent'; -import typeof TextInputState from '../Components/TextInput/TextInputState'; -import typeof ExceptionsManager from '../Core/ExceptionsManager'; -import typeof RawEventEmitter from '../Core/RawEventEmitter'; -import typeof ReactFiberErrorDialog from '../Core/ReactFiberErrorDialog'; -import typeof RCTEventEmitter from '../EventEmitter/RCTEventEmitter'; -import typeof { - createPublicInstance, - createPublicRootInstance, - createPublicTextInstance, - getInternalInstanceHandleFromPublicInstance, - getNativeTagFromPublicInstance, - getNodeFromPublicInstance, -} from '../ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance'; -import typeof { - create as createAttributePayload, - diff as diffAttributePayloads, -} from '../ReactNative/ReactFabricPublicInstance/ReactNativeAttributePayload'; -import typeof UIManager from '../ReactNative/UIManager'; -import typeof * as ReactNativeViewConfigRegistry from '../Renderer/shims/ReactNativeViewConfigRegistry'; -import typeof flattenStyle from '../StyleSheet/flattenStyle'; -import type {DangerouslyImpreciseStyleProp} from '../StyleSheet/StyleSheet'; -import typeof deepFreezeAndThrowOnMutationInDev from '../Utilities/deepFreezeAndThrowOnMutationInDev'; -import typeof deepDiffer from '../Utilities/differ/deepDiffer'; -import typeof Platform from '../Utilities/Platform'; +import typeof {createPublicTextInstance} from '../ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance'; export type {PublicRootInstance} from '../ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance'; export type PublicTextInstance = ReturnType; -// flowlint unsafe-getters-setters:off +/** + * @deprecated Since 0.88. Use 'react-native/react-private-interface' instead. + */ // eslint-disable-next-line @react-native/monorepo/no-commonjs-exports -module.exports = { - get BatchedBridge(): BatchedBridge { - return require('../BatchedBridge/BatchedBridge').default; - }, - get ExceptionsManager(): ExceptionsManager { - return require('../Core/ExceptionsManager').default; - }, - get Platform(): Platform { - return require('../Utilities/Platform').default; - }, - get RCTEventEmitter(): RCTEventEmitter { - return require('../EventEmitter/RCTEventEmitter').default; - }, - get ReactNativeViewConfigRegistry(): ReactNativeViewConfigRegistry { - return require('../Renderer/shims/ReactNativeViewConfigRegistry'); - }, - get TextInputState(): TextInputState { - return require('../Components/TextInput/TextInputState').default; - }, - get UIManager(): UIManager { - return require('../ReactNative/UIManager').default; - }, - // TODO: Remove when React has migrated to `createAttributePayload` and `diffAttributePayloads` - get deepDiffer(): deepDiffer { - return require('../Utilities/differ/deepDiffer').default; - }, - get deepFreezeAndThrowOnMutationInDev(): deepFreezeAndThrowOnMutationInDev< - {...} | Array, - > { - return require('../Utilities/deepFreezeAndThrowOnMutationInDev').default; - }, - // TODO: Remove when React has migrated to `createAttributePayload` and `diffAttributePayloads` - get flattenStyle(): flattenStyle { - // $FlowFixMe[underconstrained-implicit-instantiation] - // $FlowFixMe[incompatible-type] - return require('../StyleSheet/flattenStyle').default; - }, - get ReactFiberErrorDialog(): ReactFiberErrorDialog { - return require('../Core/ReactFiberErrorDialog').default; - }, - get legacySendAccessibilityEvent(): legacySendAccessibilityEvent { - return require('../Components/AccessibilityInfo/legacySendAccessibilityEvent') - .default; - }, - get RawEventEmitter(): RawEventEmitter { - return require('../Core/RawEventEmitter').default; - }, - get CustomEvent(): CustomEvent { - return require('../../src/private/webapis/dom/events/CustomEvent').default; - }, - get createAttributePayload(): createAttributePayload { - return require('../ReactNative/ReactFabricPublicInstance/ReactNativeAttributePayload') - .create; - }, - get diffAttributePayloads(): diffAttributePayloads { - return require('../ReactNative/ReactFabricPublicInstance/ReactNativeAttributePayload') - .diff; - }, - get createPublicRootInstance(): createPublicRootInstance { - return require('../ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance') - .createPublicRootInstance; - }, - get createPublicInstance(): createPublicInstance { - return require('../ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance') - .createPublicInstance; - }, - get createPublicTextInstance(): createPublicTextInstance { - return require('../ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance') - .createPublicTextInstance; - }, - get getNativeTagFromPublicInstance(): getNativeTagFromPublicInstance { - return require('../ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance') - .getNativeTagFromPublicInstance; - }, - get getNodeFromPublicInstance(): getNodeFromPublicInstance { - return require('../ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance') - .getNodeFromPublicInstance; - }, - get getInternalInstanceHandleFromPublicInstance(): getInternalInstanceHandleFromPublicInstance { - return require('../ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance') - .getInternalInstanceHandleFromPublicInstance; - }, - get dispatchNativeEvent(): dispatchNativeEvent { - return require('../../src/private/renderer/events/dispatchNativeEvent') - .default; - }, -}; +module.exports = require('../../src/react-private-interface'); diff --git a/packages/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js.flow b/packages/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js.flow index da6972e835df..c9b1e0e77c8b 100644 --- a/packages/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js.flow +++ b/packages/react-native/Libraries/ReactPrivate/ReactNativePrivateInterface.js.flow @@ -8,34 +8,8 @@ * @format */ -import typeof {createPublicTextInstance as createPublicTextInstanceT} from '../ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance'; - -export type {PublicRootInstance} from '../ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance'; -export type PublicTextInstance = ReturnType; - -export {default as BatchedBridge} from '../BatchedBridge/BatchedBridge'; -export {default as ExceptionsManager} from '../Core/ExceptionsManager'; -export {default as Platform} from '../Utilities/Platform'; -export {default as RCTEventEmitter} from '../EventEmitter/RCTEventEmitter'; -export * as ReactNativeViewConfigRegistry from '../Renderer/shims/ReactNativeViewConfigRegistry'; -export {default as TextInputState} from '../Components/TextInput/TextInputState'; -export {default as UIManager} from '../ReactNative/UIManager'; -export {default as deepDiffer} from '../Utilities/differ/deepDiffer'; -export {default as deepFreezeAndThrowOnMutationInDev} from '../Utilities/deepFreezeAndThrowOnMutationInDev'; -export {default as flattenStyle} from '../StyleSheet/flattenStyle'; -export {default as ReactFiberErrorDialog} from '../Core/ReactFiberErrorDialog'; -export {default as legacySendAccessibilityEvent} from '../Components/AccessibilityInfo/legacySendAccessibilityEvent'; -export {default as RawEventEmitter} from '../Core/RawEventEmitter'; -export {default as CustomEvent} from '../../src/private/webapis/dom/events/CustomEvent'; -export { - create as createAttributePayload, - diff as diffAttributePayloads, -} from '../ReactNative/ReactFabricPublicInstance/ReactNativeAttributePayload'; -export { - createPublicRootInstance, - createPublicInstance, - createPublicTextInstance, - getNativeTagFromPublicInstance, - getNodeFromPublicInstance, - getInternalInstanceHandleFromPublicInstance, -} from '../ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance'; +/** + * @deprecated Since 0.88. Use 'react-native/react-private-interface' instead. + */ +export type * from '../../src/react-private-interface'; +export * from '../../src/react-private-interface'; diff --git a/packages/react-native/ReactNativeApi.d.ts b/packages/react-native/ReactNativeApi.d.ts index b5cdeaf11758..c4a2d4ff0beb 100644 --- a/packages/react-native/ReactNativeApi.d.ts +++ b/packages/react-native/ReactNativeApi.d.ts @@ -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<<637f1d0b012ebca68c9e020a52eb2803>> + * @generated SignedSource<> * * This file was generated by scripts/js-api/build-types/index.js. */ @@ -403,16 +403,6 @@ declare const staggerImpl: ( time: number, animations: Array, ) => CompositeAnimation -declare const States: { - ERROR: "ERROR" - NOT_RESPONDER: "NOT_RESPONDER" - RESPONDER_ACTIVE_LONG_PRESS_IN: "RESPONDER_ACTIVE_LONG_PRESS_IN" - RESPONDER_ACTIVE_LONG_PRESS_OUT: "RESPONDER_ACTIVE_LONG_PRESS_OUT" - RESPONDER_ACTIVE_PRESS_IN: "RESPONDER_ACTIVE_PRESS_IN" - RESPONDER_ACTIVE_PRESS_OUT: "RESPONDER_ACTIVE_PRESS_OUT" - RESPONDER_INACTIVE_PRESS_IN: "RESPONDER_INACTIVE_PRESS_IN" - RESPONDER_INACTIVE_PRESS_OUT: "RESPONDER_INACTIVE_PRESS_OUT" -} declare const subtract: typeof $$AnimatedImplementation.subtract declare const subtractImpl: ( a: AnimatedNode_default | number, @@ -456,7 +446,6 @@ declare const ToastAndroid_default: { yOffset: number, ) => void } -declare const Touchable: typeof TouchableImpl_default declare const Touchable_default: ( props: TouchableOpacityProps & { ref?: React.Ref @@ -468,33 +457,6 @@ declare const TouchableHighlight_default: ( ref?: React.Ref }, ) => React.ReactNode -declare const TouchableImpl_default: { - Mixin: typeof TouchableMixinImpl - renderDebugView: ($$PARAM_0$$: { - color: ColorValue - hitSlop?: EdgeInsetsProp - }) => null | React.ReactNode -} -declare const TouchableMixinImpl: { - withoutDefaultFocusAndBlur: {} - componentDidMount: () => void - componentWillUnmount: () => void - touchableGetInitialState: () => { - touchable: { - responderID: GestureResponderEvent["currentTarget"] | undefined - touchState: TouchableState | undefined - } - } - touchableHandleBlur: (e: BlurEvent) => void - touchableHandleFocus: (e: FocusEvent) => void - touchableHandleResponderGrant: (e: GestureResponderEvent) => void - touchableHandleResponderMove: (e: GestureResponderEvent) => void - touchableHandleResponderRelease: (e: GestureResponderEvent) => void - touchableHandleResponderTerminate: (e: GestureResponderEvent) => void - touchableHandleResponderTerminationRequest: () => any - touchableHandleStartShouldSetResponder: () => any - touchableLongPressCancelsPress: () => boolean -} declare const TouchableOpacity: typeof Touchable_default declare const UIManager: typeof UIManager_default declare const UIManager_default: UIManagerJSInterface @@ -5371,7 +5333,6 @@ declare type TimingAnimationConfig = Readonly< } > declare type ToastAndroid = typeof ToastAndroid -declare type Touchable = typeof Touchable declare type TouchableHighlight = typeof TouchableHighlight declare type TouchableHighlightBaseProps = { readonly activeOpacity?: number @@ -5478,15 +5439,6 @@ declare type TouchableOpacityTVProps = { readonly nextFocusRight?: number readonly nextFocusUp?: number } -declare type TouchableState = - | typeof States.ERROR - | typeof States.NOT_RESPONDER - | typeof States.RESPONDER_ACTIVE_LONG_PRESS_IN - | typeof States.RESPONDER_ACTIVE_LONG_PRESS_OUT - | typeof States.RESPONDER_ACTIVE_PRESS_IN - | typeof States.RESPONDER_ACTIVE_PRESS_OUT - | typeof States.RESPONDER_INACTIVE_PRESS_IN - | typeof States.RESPONDER_INACTIVE_PRESS_OUT declare function TouchableWithoutFeedback( props: TouchableWithoutFeedbackProps, ): React.ReactNode @@ -6174,7 +6126,6 @@ export { TextProps, // 58466ea1 TextStyle, // b62b8399 ToastAndroid, // 88a8969a - Touchable, // c15da0a2 TouchableHighlight, // 20ba0199 TouchableHighlightInstance, // b510c0eb TouchableHighlightProps, // 337a9164 diff --git a/packages/react-native/__typetests__/index.tsx b/packages/react-native/__typetests__/index.tsx index 1df236c3bc71..54ea8b7694ba 100644 --- a/packages/react-native/__typetests__/index.tsx +++ b/packages/react-native/__typetests__/index.tsx @@ -124,7 +124,6 @@ import { // @ts-ignore SectionListData, ToastAndroid, - Touchable, LayoutAnimation, processColor, experimental_LayoutConformance as LayoutConformance, @@ -487,22 +486,6 @@ class Welcome extends React.Component< export default Welcome; -// TouchableTest -function TouchableTest() { - function basicUsage() { - return Touchable.renderDebugView({ - color: 'mediumspringgreen', - hitSlop: {bottom: 5, top: 5}, - }); - } - - function defaultHitSlop() { - return Touchable.renderDebugView({ - color: 'red', - }); - } -} - export class TouchableHighlightTest extends React.Component { buttonRef = React.createRef>(); diff --git a/scripts/js-api/build-types/templates/tsconfig.test.json b/packages/react-native/__typetests__/tsconfig.json similarity index 59% rename from scripts/js-api/build-types/templates/tsconfig.test.json rename to packages/react-native/__typetests__/tsconfig.json index 7af1dd074cb8..241c3b969fb2 100644 --- a/scripts/js-api/build-types/templates/tsconfig.test.json +++ b/packages/react-native/__typetests__/tsconfig.json @@ -1,4 +1,5 @@ { + "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { "module": "esnext", "lib": ["es2020"], @@ -9,9 +10,7 @@ "jsx": "react", "noEmit": true, "forceConsistentCasingInFileNames": true, - "paths": {"react-native": ["."]}, - "moduleResolution": "bundler", - "customConditions": ["react-native-strict-api"] + "moduleResolution": "bundler" }, - "include": ["**/*.d.ts", "../__typetests__/**/*"] + "include": ["**/*"] } diff --git a/packages/react-native/__typetests__/tsconfig.legacy.json b/packages/react-native/__typetests__/tsconfig.legacy.json new file mode 100644 index 000000000000..3aea5153dceb --- /dev/null +++ b/packages/react-native/__typetests__/tsconfig.legacy.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + // Legacy variant: Opts into the `"react-native-legacy-deep-imports"` export + // condition so `react-native` and `react-native/Libraries/*` resolve to the + // hand-written types in `types/`. + "extends": "./tsconfig.json", + "compilerOptions": { + "customConditions": ["react-native-legacy-deep-imports"] + } +} diff --git a/packages/react-native/index.js b/packages/react-native/index.js index 201ee115755d..0fb660e9aaf7 100644 --- a/packages/react-native/index.js +++ b/packages/react-native/index.js @@ -149,9 +149,6 @@ module.exports = { get TextInput() { return require('./Libraries/Components/TextInput/TextInput').default; }, - get Touchable() { - return require('./Libraries/Components/Touchable/Touchable').default; - }, get TouchableHighlight() { return require('./Libraries/Components/Touchable/TouchableHighlight') .default; @@ -412,6 +409,21 @@ module.exports = { // #endregion } as ReactNativePublicAPI; +// `Touchable` has been removed from the public API types, but remains +// re-exported at runtime here because of a hanging `react-native-svg` call +// site (fbsource). +// TODO(huntie): Remove this re-export once `react-native-svg` is updated. +/* $FlowFixMe[prop-missing] This is intentional: `Touchable` is a value-only + * re-export that is absent from the public API types. */ +/* $FlowFixMe[invalid-export] This is intentional: `Touchable` is a value-only + * re-export that is absent from the public API types. */ +Object.defineProperty(module.exports, 'Touchable', { + configurable: true, + get() { + return require('./Libraries/Components/Touchable/Touchable').default; + }, +}); + if (__DEV__) { /* $FlowFixMe[prop-missing] This is intentional: Flow will error when * attempting to access InteractionManager. */ diff --git a/packages/react-native/index.js.flow b/packages/react-native/index.js.flow index 9869ed8c5907..51e4a54e0552 100644 --- a/packages/react-native/index.js.flow +++ b/packages/react-native/index.js.flow @@ -175,8 +175,6 @@ export type { } from './Libraries/Components/TextInput/TextInput'; export {default as TextInput} from './Libraries/Components/TextInput/TextInput'; -export {default as Touchable} from './Libraries/Components/Touchable/Touchable'; - export type { TouchableHighlightInstance, TouchableHighlightProps, diff --git a/packages/react-native/jest-preset.js b/packages/react-native/jest-preset.js deleted file mode 100644 index 5251fcdbd0d2..000000000000 --- a/packages/react-native/jest-preset.js +++ /dev/null @@ -1,26 +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. - * - * @noflow - * @format - */ - -'use strict'; - -try { - module.exports = require('@react-native/jest-preset'); -} catch (error) { - if (error.code === 'MODULE_NOT_FOUND') { - throw new Error( - `The React Native Jest preset has moved to a separate package. -To migrate, please install "@react-native/jest-preset" and update your -jest.config.js to reference: - preset: '@react-native/jest-preset'`, - ); - } else { - throw error; - } -} diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 524857859285..c44e6658dec9 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -27,33 +27,36 @@ "react-native": "cli.js" }, "main": "./index.js", - "types": "types", "exports": { ".": { - "react-native-strict-api": "./types_generated/index.d.ts", - "types": "./types/index.d.ts", + "react-native-legacy-deep-imports": "./types/index.d.ts", + "types": "./types_generated/index.d.ts", "default": "./index.js" }, "./Libraries/*": { - "react-native-strict-api": null, - "types": "./Libraries/*.d.ts", + "react-native-legacy-deep-imports": "./Libraries/*.d.ts", + "types": null, "default": "./Libraries/*.js" }, "./Libraries/*.js": { - "react-native-strict-api": null, + "types": null, "default": "./Libraries/*.js" }, "./scripts/*": "./scripts/*", - "./src/*": { - "types": null, - "default": "./src/*.js" - }, "./asset-registry": { "types": null, "default": "./src/asset-registry.js" }, - "./jest-preset": "./jest-preset.js", - "./rn-get-polyfills": "./rn-get-polyfills.js", + "./react-private-interface": { + "types": null, + "default": "./src/react-private-interface.js" + }, + "./setup-env": "./src/setup-env.js", + "./unstable-internals-do-not-use": { + "react-native-unstable-internals": "./src/unstable-internals-do-not-use.d.ts", + "types": null, + "default": "./src/unstable-internals-do-not-use.js" + }, "./src/fb_internal/*": "./src/fb_internal/*", "./package.json": "./package.json" }, @@ -69,7 +72,6 @@ "gradle/libs.versions.toml", "index.js", "index.js.flow", - "jest-preset.js", "Libraries", "LICENSE", "React-Core.podspec", @@ -90,7 +92,6 @@ "ReactApple", "ReactCommon", "README.md", - "rn-get-polyfills.js", "scripts/replace-rncore-version.js", "scripts/bundle.js", "scripts/cocoapods", @@ -136,16 +137,12 @@ "featureflags": "node ./scripts/featureflags/index.js" }, "peerDependencies": { - "@react-native/jest-preset": "0.87.0-rc.0", "@types/react": "^19.1.1", "react": "^19.2.3" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@react-native/jest-preset": { - "optional": true } }, "dependencies": { @@ -153,7 +150,6 @@ "@react-native/codegen": "0.87.0-rc.0", "@react-native/community-cli-plugin": "0.87.0-rc.0", "@react-native/gradle-plugin": "0.87.0-rc.0", - "@react-native/js-polyfills": "0.87.0-rc.0", "@react-native/normalize-colors": "0.87.0-rc.0", "@react-native/virtualized-lists": "0.87.0-rc.0", "anser": "^1.4.9", diff --git a/packages/react-native/rn-get-polyfills.js b/packages/react-native/rn-get-polyfills.js deleted file mode 100644 index bf0d0428de37..000000000000 --- a/packages/react-native/rn-get-polyfills.js +++ /dev/null @@ -1,13 +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. - * - * @flow strict-local - * @format - */ - -'use strict'; - -module.exports = require('@react-native/js-polyfills'); diff --git a/packages/react-native/src/asset-registry.js b/packages/react-native/src/asset-registry.js index e98f442e7694..6d3a4cdf34b4 100644 --- a/packages/react-native/src/asset-registry.js +++ b/packages/react-native/src/asset-registry.js @@ -11,7 +11,7 @@ 'use strict'; // ---------------------------------------------------------------------------- -// Secondary react-native/asset-registry entry point. +// react-native/asset-registry // // This is an untyped secondary entry point intended to be referenced from // Metro's `transformer.assetRegistryPath` config option. This entry point may diff --git a/packages/react-native/src/react-private-interface.js b/packages/react-native/src/react-private-interface.js new file mode 100644 index 000000000000..6e965490ebc9 --- /dev/null +++ b/packages/react-native/src/react-private-interface.js @@ -0,0 +1,145 @@ +/** + * 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. + * + * @flow strict-local + * @format + */ + +'use strict'; + +// ---------------------------------------------------------------------------- +// react-native/react-private-interface +// +// This is a private entry point allowing React to require React Native +// internals (previously, Libaries/ReactNativePrivateInterface.js). +// +// These APIs should ONLY be used by first party React internals and are not +// part of our public API. +// +// IMPORTANT: Keep this file in sync with react-private-interface.js.flow. +// ---------------------------------------------------------------------------- + +import typeof BatchedBridge from '../Libraries/BatchedBridge/BatchedBridge'; +import typeof legacySendAccessibilityEvent from '../Libraries/Components/AccessibilityInfo/legacySendAccessibilityEvent'; +import typeof TextInputState from '../Libraries/Components/TextInput/TextInputState'; +import typeof ExceptionsManager from '../Libraries/Core/ExceptionsManager'; +import typeof RawEventEmitter from '../Libraries/Core/RawEventEmitter'; +import typeof ReactFiberErrorDialog from '../Libraries/Core/ReactFiberErrorDialog'; +import typeof RCTEventEmitter from '../Libraries/EventEmitter/RCTEventEmitter'; +import typeof { + createPublicInstance, + createPublicRootInstance, + createPublicTextInstance, + getInternalInstanceHandleFromPublicInstance, + getNativeTagFromPublicInstance, + getNodeFromPublicInstance, +} from '../Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance'; +import typeof { + create as createAttributePayload, + diff as diffAttributePayloads, +} from '../Libraries/ReactNative/ReactFabricPublicInstance/ReactNativeAttributePayload'; +import typeof UIManager from '../Libraries/ReactNative/UIManager'; +import typeof * as ReactNativeViewConfigRegistry from '../Libraries/Renderer/shims/ReactNativeViewConfigRegistry'; +import typeof flattenStyle from '../Libraries/StyleSheet/flattenStyle'; +import type {DangerouslyImpreciseStyleProp} from '../Libraries/StyleSheet/StyleSheet'; +import typeof deepFreezeAndThrowOnMutationInDev from '../Libraries/Utilities/deepFreezeAndThrowOnMutationInDev'; +import typeof deepDiffer from '../Libraries/Utilities/differ/deepDiffer'; +import typeof Platform from '../Libraries/Utilities/Platform'; +import typeof dispatchNativeEvent from './private/renderer/events/dispatchNativeEvent'; +import typeof CustomEvent from './private/webapis/dom/events/CustomEvent'; + +export type {PublicRootInstance} from '../Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance'; +export type PublicTextInstance = ReturnType; + +// flowlint unsafe-getters-setters:off +// eslint-disable-next-line @react-native/monorepo/no-commonjs-exports +module.exports = { + get BatchedBridge(): BatchedBridge { + return require('../Libraries/BatchedBridge/BatchedBridge').default; + }, + get ExceptionsManager(): ExceptionsManager { + return require('../Libraries/Core/ExceptionsManager').default; + }, + get Platform(): Platform { + return require('../Libraries/Utilities/Platform').default; + }, + get RCTEventEmitter(): RCTEventEmitter { + return require('../Libraries/EventEmitter/RCTEventEmitter').default; + }, + get ReactNativeViewConfigRegistry(): ReactNativeViewConfigRegistry { + return require('../Libraries/Renderer/shims/ReactNativeViewConfigRegistry'); + }, + get TextInputState(): TextInputState { + return require('../Libraries/Components/TextInput/TextInputState').default; + }, + get UIManager(): UIManager { + return require('../Libraries/ReactNative/UIManager').default; + }, + // TODO: Remove when React has migrated to `createAttributePayload` and `diffAttributePayloads` + get deepDiffer(): deepDiffer { + return require('../Libraries/Utilities/differ/deepDiffer').default; + }, + get deepFreezeAndThrowOnMutationInDev(): deepFreezeAndThrowOnMutationInDev< + {...} | Array, + > { + return require('../Libraries/Utilities/deepFreezeAndThrowOnMutationInDev') + .default; + }, + // TODO: Remove when React has migrated to `createAttributePayload` and `diffAttributePayloads` + get flattenStyle(): flattenStyle { + // $FlowFixMe[underconstrained-implicit-instantiation] + // $FlowFixMe[incompatible-type] + return require('../Libraries/StyleSheet/flattenStyle').default; + }, + get ReactFiberErrorDialog(): ReactFiberErrorDialog { + return require('../Libraries/Core/ReactFiberErrorDialog').default; + }, + get legacySendAccessibilityEvent(): legacySendAccessibilityEvent { + return require('../Libraries/Components/AccessibilityInfo/legacySendAccessibilityEvent') + .default; + }, + get RawEventEmitter(): RawEventEmitter { + return require('../Libraries/Core/RawEventEmitter').default; + }, + get CustomEvent(): CustomEvent { + return require('./private/webapis/dom/events/CustomEvent').default; + }, + get createAttributePayload(): createAttributePayload { + return require('../Libraries/ReactNative/ReactFabricPublicInstance/ReactNativeAttributePayload') + .create; + }, + get diffAttributePayloads(): diffAttributePayloads { + return require('../Libraries/ReactNative/ReactFabricPublicInstance/ReactNativeAttributePayload') + .diff; + }, + get createPublicRootInstance(): createPublicRootInstance { + return require('../Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance') + .createPublicRootInstance; + }, + get createPublicInstance(): createPublicInstance { + return require('../Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance') + .createPublicInstance; + }, + get createPublicTextInstance(): createPublicTextInstance { + return require('../Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance') + .createPublicTextInstance; + }, + get getNativeTagFromPublicInstance(): getNativeTagFromPublicInstance { + return require('../Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance') + .getNativeTagFromPublicInstance; + }, + get getNodeFromPublicInstance(): getNodeFromPublicInstance { + return require('../Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance') + .getNodeFromPublicInstance; + }, + get getInternalInstanceHandleFromPublicInstance(): getInternalInstanceHandleFromPublicInstance { + return require('../Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance') + .getInternalInstanceHandleFromPublicInstance; + }, + get dispatchNativeEvent(): dispatchNativeEvent { + return require('./private/renderer/events/dispatchNativeEvent').default; + }, +}; diff --git a/packages/react-native/src/react-private-interface.js.flow b/packages/react-native/src/react-private-interface.js.flow new file mode 100644 index 000000000000..b30331d119f8 --- /dev/null +++ b/packages/react-native/src/react-private-interface.js.flow @@ -0,0 +1,48 @@ +/** + * 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. + * + * @flow strict-local + * @format + */ + +// ---------------------------------------------------------------------------- +// Types entry point for react-native/react-private-interface +// +// IMPORTANT: Keep this file in sync with react-private-interface.js. +// ---------------------------------------------------------------------------- + +import typeof {createPublicTextInstance as createPublicTextInstanceT} from '../Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance'; + +export type {PublicRootInstance} from '../Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance'; +export type PublicTextInstance = ReturnType; + +export {default as BatchedBridge} from '../Libraries/BatchedBridge/BatchedBridge'; +export {default as ExceptionsManager} from '../Libraries/Core/ExceptionsManager'; +export {default as Platform} from '../Libraries/Utilities/Platform'; +export {default as RCTEventEmitter} from '../Libraries/EventEmitter/RCTEventEmitter'; +export * as ReactNativeViewConfigRegistry from '../Libraries/Renderer/shims/ReactNativeViewConfigRegistry'; +export {default as TextInputState} from '../Libraries/Components/TextInput/TextInputState'; +export {default as UIManager} from '../Libraries/ReactNative/UIManager'; +export {default as deepDiffer} from '../Libraries/Utilities/differ/deepDiffer'; +export {default as deepFreezeAndThrowOnMutationInDev} from '../Libraries/Utilities/deepFreezeAndThrowOnMutationInDev'; +export {default as flattenStyle} from '../Libraries/StyleSheet/flattenStyle'; +export {default as ReactFiberErrorDialog} from '../Libraries/Core/ReactFiberErrorDialog'; +export {default as legacySendAccessibilityEvent} from '../Libraries/Components/AccessibilityInfo/legacySendAccessibilityEvent'; +export {default as RawEventEmitter} from '../Libraries/Core/RawEventEmitter'; +export {default as CustomEvent} from './private/webapis/dom/events/CustomEvent'; +export { + create as createAttributePayload, + diff as diffAttributePayloads, +} from '../Libraries/ReactNative/ReactFabricPublicInstance/ReactNativeAttributePayload'; +export { + createPublicRootInstance, + createPublicInstance, + createPublicTextInstance, + getNativeTagFromPublicInstance, + getNodeFromPublicInstance, + getInternalInstanceHandleFromPublicInstance, +} from '../Libraries/ReactNative/ReactFabricPublicInstance/ReactFabricPublicInstance'; +export {default as dispatchNativeEvent} from './private/renderer/events/dispatchNativeEvent'; diff --git a/packages/react-native/src/setup-env.js b/packages/react-native/src/setup-env.js new file mode 100644 index 000000000000..fbe7f9178b31 --- /dev/null +++ b/packages/react-native/src/setup-env.js @@ -0,0 +1,22 @@ +/** + * 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. + * + * @flow strict-local + * @format + */ + +'use strict'; +'use client'; + +// ---------------------------------------------------------------------------- +// react-native/setup-env +// +// Side-effectful module that sets up the core React Native JavaScript +// environment. This includes global timers (`setTimeout` etc), the global +// `console` object, and hooks for printing stack traces with source maps. +// ---------------------------------------------------------------------------- + +require('./private/setup/setUpDefaultReactNativeEnvironment').default(); 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 new file mode 100644 index 000000000000..6c1f3bf54c40 --- /dev/null +++ b/packages/react-native/src/unstable-internals-do-not-use.d.ts @@ -0,0 +1,214 @@ +/** + * 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. + * + * @format + */ + +// ---------------------------------------------------------------------------- +// Types entry point for react-native/unstable-internals-do-not-use. +// +// IMPORTANT: Keep this file in sync with unstable-internals-do-not-use.js. +// ---------------------------------------------------------------------------- + +import type * as React from 'react'; + +// #region AppContainer + +interface AppContainerProps { + children?: React.ReactNode | undefined; + rootTag: number; + initialProps?: object | undefined; + WrapperComponent?: React.ComponentType | null | undefined; + rootViewStyle?: unknown | undefined; + internal_excludeLogBox?: boolean | undefined; + internal_excludeInspector?: boolean | undefined; +} + +/** Root component that wraps and mounts a React Native app tree. */ +export const AppContainer: React.ComponentType; + +// #endregion +// #region AssetSourceResolver + +interface ResolvedAssetSource { + readonly __packager_asset: boolean; + readonly width: number | null | undefined; + readonly height: number | null | undefined; + readonly uri: string; + readonly scale: number; +} + +/** Resolves a packager asset descriptor to a loadable source for the current platform. */ +export class AssetSourceResolver { + serverUrl: string | null | undefined; + jsbundleUrl: string | null | undefined; + asset: unknown; + constructor( + serverUrl: string | null | undefined, + jsbundleUrl: string | null | undefined, + asset: unknown, + ); + isLoadedFromServer(): boolean; + isLoadedFromFileSystem(): boolean; + defaultAsset(): ResolvedAssetSource; + getAssetUsingResolver(resolver: 'android' | 'generic'): ResolvedAssetSource; + assetServerURL(): ResolvedAssetSource; + scaledAssetPath(): ResolvedAssetSource; + scaledAssetURLNearBundle(): ResolvedAssetSource; + resourceIdentifierWithoutScale(): ResolvedAssetSource; + drawableFolderInBundle(): ResolvedAssetSource; + fromSource(source: string): ResolvedAssetSource; + static pickScale(scales: number[], deviceScale?: number): number; +} + +// #endregion +// #region customDirectEventTypes + +/** Registry mapping custom direct (non-bubbling) event names to their registration names. */ +export const customDirectEventTypes: { + [eventName: string]: Readonly<{ + registrationName: string; + }>; +}; + +// #endregion +// #region DevLoadingView + +/** Dev-only overlay banner showing bundle load, refresh, and error status. */ +export const DevLoadingView: { + showMessage( + message: string, + type: 'load' | 'refresh' | 'error', + options?: {dismissButton?: boolean | undefined}, + ): void; + hide(): void; +}; + +// #endregion +// #region getDevServer + +interface DevServerInfo { + url: string; + fullBundleUrl: string | null; + bundleLoadedFromServer: boolean; +} + +/** Returns information about the running dev server. */ +export function getDevServer(): DevServerInfo; + +// #endregion +// #region HMRClient + +/** Client that receives Fast Refresh updates and applies them at runtime. */ +export class HMRClient { + enable(): void; + disable(): void; + registerBundle(requestUrl: string): void; + log( + level: + | 'trace' + | 'info' + | 'warn' + | 'error' + | 'log' + | 'group' + | 'groupCollapsed' + | 'groupEnd' + | 'debug', + data: ReadonlyArray, + ): void; + setup( + platform: string, + bundleEntry: string, + host: string, + port: number | string, + isEnabled: boolean, + scheme?: string, + ): void; +} + +// #endregion +// #region NativeExceptionsManager + +interface StackFrame { + column: number | null; + file: string | null; + lineNumber: number | null; + methodName: string; + collapse?: boolean | undefined; +} + +interface ExceptionData { + message: string; + originalMessage: string | null; + name: string | null; + componentStack: string | null; + stack: StackFrame[]; + id: number; + isFatal: boolean; + extraData?: object | undefined; +} + +/** Reports JS exceptions to native and manages RedBox. */ +export const NativeExceptionsManager: { + reportFatalException( + message: string, + stack: StackFrame[], + exceptionId: number, + ): void; + reportSoftException( + message: string, + stack: StackFrame[], + exceptionId: number, + ): void; + dismissRedbox(): void; + reportException(data: ExceptionData): void; +}; + +// #endregion +// #region NativeRedBox + +interface NativeRedBoxSpec { + setExtraData(extraData: object, forIdentifier: string): void; + dismiss(): void; +} + +/** Native module for the RedBox error overlay; null when unavailable. */ +export const NativeRedBox: NativeRedBoxSpec | null; + +// #endregion +// #region NativeSourceCode + +interface SourceCodeConstants { + scriptURL: string; +} + +/** Native module exposing source-code constants such as the bundle scriptURL. */ +export const NativeSourceCode: { + getConstants(): SourceCodeConstants; +}; + +// #endregion +// #region PressabilityDebugView + +type Rect = Readonly<{ + bottom?: number | null | undefined; + left?: number | null | undefined; + right?: number | null | undefined; + top?: number | null | undefined; +}>; + +type RectOrSize = Rect | number; + +interface PressabilityDebugViewProps { + color: unknown; + hitSlop: RectOrSize | null | undefined; +} + +/** Debug overlay that visualizes press targets when enabled via the Inspector. */ +export const PressabilityDebugView: React.ComponentType; + +// #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 new file mode 100644 index 000000000000..20cb19edaec0 --- /dev/null +++ b/packages/react-native/src/unstable-internals-do-not-use.js @@ -0,0 +1,76 @@ +/** + * 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. + * + * @noflow + * @format + */ + +'use strict'; +'use client'; + +// ---------------------------------------------------------------------------- +// react-native/unstable-internals-do-not-use +// +// UNSTABLE WITH NO SEMVER GUARANTEES. +// SHOULD NOT BE DEPENDED ON BY NEW CODE. +// +// This is a secondary entry point for frameworks and libraries that depend on +// specific React Native internals, serving as a compatibility bridge. +// +// Consuming codebases must opt in via tsconfig.json: +// "customConditions": ["react-native-unstable-internals"] +// +// Having this entry point: +// - Maintains a known list of which React Native internals are in use. +// - Enables us to relocate supporting files more freely. +// - Gives us time to decide on the future of these APIs (independent from +// removal of the Strict API opt out). +// +// The long term future of these exports is to formalize/delete them where +// appropriate, and collapse this entry point. +// +// Replaces RFC0985 +// https://github.com/react-native-community/discussions-and-proposals/pull/985 +// where we reviewed internal APIs used in the ecosystem. +// +// IMPORTANT: Keep this file in sync with unstable-internals-do-not-use.d.ts. +// ---------------------------------------------------------------------------- + +// eslint-disable-next-line @react-native/monorepo/no-commonjs-exports +module.exports = { + get AppContainer() { + return require('../Libraries/ReactNative/AppContainer').default; + }, + get AssetSourceResolver() { + return require('../Libraries/Image/AssetSourceResolver').default; + }, + get customDirectEventTypes() { + return require('../Libraries/Renderer/shims/ReactNativeViewConfigRegistry') + .customDirectEventTypes; + }, + get DevLoadingView() { + return require('../Libraries/Utilities/DevLoadingView').default; + }, + get getDevServer() { + return require('../Libraries/Core/Devtools/getDevServer').default; + }, + get HMRClient() { + return require('../Libraries/Utilities/HMRClient').default; + }, + get NativeExceptionsManager() { + return require('../Libraries/Core/NativeExceptionsManager').default; + }, + get NativeRedBox() { + return require('../Libraries/NativeModules/specs/NativeRedBox').default; + }, + get NativeSourceCode() { + return require('../Libraries/NativeModules/specs/NativeSourceCode').default; + }, + get PressabilityDebugView() { + return require('../Libraries/Pressability/PressabilityDebug') + .PressabilityDebugView; + }, +}; diff --git a/packages/react-native/types/tsconfig.json b/packages/react-native/types/tsconfig.json deleted file mode 100644 index bb7659500b10..000000000000 --- a/packages/react-native/types/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "lib": ["es6"], - "strict": false, - "noImplicitAny": true, - "noImplicitThis": true, - "strictFunctionTypes": true, - "strictNullChecks": true, - "types": [], - "jsx": "react", - "noEmit": true, - "forceConsistentCasingInFileNames": true, - "paths": {"react-native": ["."]} - }, - "include": ["**/*.d.ts", "../__typetests__/**/*"] -} diff --git a/packages/rn-tester/IntegrationTests/IntegrationTestsApp.js b/packages/rn-tester/IntegrationTests/IntegrationTestsApp.js index c1f9cf2c760a..1cbc4335348f 100644 --- a/packages/rn-tester/IntegrationTests/IntegrationTestsApp.js +++ b/packages/rn-tester/IntegrationTests/IntegrationTestsApp.js @@ -10,7 +10,8 @@ 'use strict'; -require('react-native/Libraries/Core/InitializeCore'); +require('react-native/setup-env'); + const React = require('react'); const ReactNative = require('react-native'); diff --git a/packages/typescript-config/README.md b/packages/typescript-config/README.md index d58ee3b56455..bf53f0c39a6a 100644 --- a/packages/typescript-config/README.md +++ b/packages/typescript-config/README.md @@ -9,25 +9,18 @@ This package provides the default `tsconfig.json` used by newly built React Nati This template is customized for specific versions of React Native, and should be updated in sync with the rest of your app. -## Strict TypeScript API +## Opting out of the Strict TypeScript API (default in 0.87) -To opt into the new [strict TypeScript API](https://reactnative.dev/blog/2025/06/12/moving-towards-a-stable-javascript-api#strict-typescript-api-opt-in) you can extend from `@react-native/typescript-config/strict` +To opt out of the new [Strict TypeScript API](https://reactnative.dev/docs/strict-typescript-api) you can extend from `@react-native/typescript-config/strict` -```jsonc -{ - "extends": "@react-native/typescript-config/strict", - // ... -} -``` - -or alternatively add the `customConditions` yourself: +If your app still needs access to deep `'react-native/Libraries/*'` imports (deprecated in 0.80), you can opt out via `customConditions` in your `tsconfig.json`: ```jsonc { "extends": "@react-native/typescript-config", "compilerOptions": { // ... - "customConditions": ["react-native-strict-api", "react-native"] + "customConditions": ["react-native", "react-native-legacy-deep-imports"] } } ``` diff --git a/packages/typescript-config/package.json b/packages/typescript-config/package.json index a9bdc0e273b4..5e2b217934de 100644 --- a/packages/typescript-config/package.json +++ b/packages/typescript-config/package.json @@ -16,7 +16,6 @@ ], "bugs": "https://github.com/react/react-native/issues", "exports": { - ".": "./tsconfig.json", - "./strict": "./tsconfig.strict.json" + ".": "./tsconfig.json" } } diff --git a/packages/typescript-config/tsconfig.strict.json b/packages/typescript-config/tsconfig.strict.json deleted file mode 100644 index c060fa2a4191..000000000000 --- a/packages/typescript-config/tsconfig.strict.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig", - "extends": "./tsconfig.json", - "display": "React Native (Strict)", - "compilerOptions": { - "customConditions": ["react-native-strict-api", "react-native"] - } -} diff --git a/packages/virtualized-lists/package.json b/packages/virtualized-lists/package.json index aca2c989aaf6..4ce33c821105 100644 --- a/packages/virtualized-lists/package.json +++ b/packages/virtualized-lists/package.json @@ -21,8 +21,8 @@ }, "exports": { ".": { - "react-native-strict-api": "./types_generated/index.d.ts", - "types": "./index.d.ts", + "react-native-legacy-deep-imports": "./index.d.ts", + "types": "./types_generated/index.d.ts", "default": "./index.js" }, "./*": { diff --git a/scripts/js-api/build-types/buildGeneratedTypes.js b/scripts/js-api/build-types/buildGeneratedTypes.js index 1408b1625b50..3ef71abc2d13 100644 --- a/scripts/js-api/build-types/buildGeneratedTypes.js +++ b/scripts/js-api/build-types/buildGeneratedTypes.js @@ -55,26 +55,10 @@ async function buildGeneratedTypes(): Promise> { } } - await Promise.all([ - fs.copyFile( - path.join(__dirname, 'templates', 'tsconfig.json'), - path.join( - PACKAGES_DIR, - 'react-native', - TYPES_OUTPUT_DIR, - 'tsconfig.json', - ), - ), - fs.copyFile( - path.join(__dirname, 'templates', 'tsconfig.test.json'), - path.join( - PACKAGES_DIR, - 'react-native', - TYPES_OUTPUT_DIR, - 'tsconfig.test.json', - ), - ), - ]); + await fs.copyFile( + path.join(__dirname, 'templates', 'tsconfig.json'), + path.join(PACKAGES_DIR, 'react-native', TYPES_OUTPUT_DIR, 'tsconfig.json'), + ); if (allErrors.length > 0) { console.error( diff --git a/scripts/run-ci-javascript-tests.js b/scripts/run-ci-javascript-tests.js index 7992b46fbcce..aed03f7f812a 100644 --- a/scripts/run-ci-javascript-tests.js +++ b/scripts/run-ci-javascript-tests.js @@ -81,7 +81,7 @@ try { ); describe('Test: TypeScript tests'); - execAndLog(`${YARN_BINARY} run test-typescript`); + execAndLog(`${YARN_BINARY} run test-typescript-legacy`); } catch (e) { if (e instanceof ExecError) { console.error(e.message);