diff --git a/packages/react-native/Libraries/Lists/FillRateHelper.js b/packages/react-native/Libraries/Lists/FillRateHelper.js index c4780365e2433d..25f439a8036db3 100644 --- a/packages/react-native/Libraries/Lists/FillRateHelper.js +++ b/packages/react-native/Libraries/Lists/FillRateHelper.js @@ -10,10 +10,9 @@ 'use strict'; -import {typeof FillRateHelper as FillRateHelperType} from '@react-native/virtualized-lists'; +import VirtualizedLists from '@react-native/virtualized-lists'; -const FillRateHelper: FillRateHelperType = - require('@react-native/virtualized-lists').FillRateHelper; +const FillRateHelper = VirtualizedLists.FillRateHelper; export type {FillRateInfo} from '@react-native/virtualized-lists'; export default FillRateHelper; diff --git a/packages/react-native/Libraries/Lists/FlatList.js b/packages/react-native/Libraries/Lists/FlatList.js index d3c5613106e029..e6a475a0788956 100644 --- a/packages/react-native/Libraries/Lists/FlatList.js +++ b/packages/react-native/Libraries/Lists/FlatList.js @@ -11,8 +11,8 @@ import typeof ScrollViewNativeComponent from '../Components/ScrollView/ScrollViewNativeComponent'; import type {ViewStyleProp} from '../StyleSheet/StyleSheet'; import type { - RenderItemProps, - RenderItemType, + ListRenderItem, + ListRenderItemInfo, ViewabilityConfigCallbackPair, ViewToken, } from '@react-native/virtualized-lists'; @@ -20,10 +20,7 @@ import type { import * as ReactNativeFeatureFlags from '../../src/private/featureflags/ReactNativeFeatureFlags'; import {type ScrollResponderType} from '../Components/ScrollView/ScrollView'; import View from '../Components/View/View'; -import { - VirtualizedList, - keyExtractor as defaultKeyExtractor, -} from '@react-native/virtualized-lists'; +import VirtualizedLists from '@react-native/virtualized-lists'; import memoizeOne from 'memoize-one'; import React from 'react'; @@ -32,6 +29,9 @@ const deepDiffer = require('../Utilities/differ/deepDiffer'); const Platform = require('../Utilities/Platform'); const invariant = require('invariant'); +const VirtualizedList = VirtualizedLists.VirtualizedList; +const defaultKeyExtractor = VirtualizedLists.keyExtractor; + type RequiredProps = { /** * An array (or array-like list) of items to render. Other data types can be @@ -66,7 +66,7 @@ type OptionalProps = { * `highlight` and `unhighlight` (which set the `highlighted: boolean` prop) are insufficient for * your use-case. */ - renderItem?: ?RenderItemType, + renderItem?: ?ListRenderItem, /** * Optional custom style for multi-item rows generated when numColumns > 1. @@ -618,7 +618,7 @@ class FlatList extends React.PureComponent, void> { _renderer = ( ListItemComponent: ?(React.ComponentType | React.MixedElement), - renderItem: ?RenderItemType, + renderItem: ?ListRenderItem, columnWrapperStyle: ?ViewStyleProp, numColumns: ?number, extraData: ?any, @@ -626,7 +626,7 @@ class FlatList extends React.PureComponent, void> { ) => { const cols = numColumnsOrDefault(numColumns); - const render = (props: RenderItemProps): React.Node => { + const render = (props: ListRenderItemInfo): React.Node => { if (ListItemComponent) { // $FlowFixMe[not-a-component] Component isn't valid // $FlowFixMe[incompatible-type-arg] Component isn't valid @@ -640,7 +640,7 @@ class FlatList extends React.PureComponent, void> { } }; - const renderProp = (info: RenderItemProps) => { + const renderProp = (info: ListRenderItemInfo) => { if (cols > 1) { const {item, index} = info; invariant( diff --git a/packages/react-native/Libraries/Lists/SectionList.js b/packages/react-native/Libraries/Lists/SectionList.js index 77b9566ec50f9b..98f38edd58b540 100644 --- a/packages/react-native/Libraries/Lists/SectionList.js +++ b/packages/react-native/Libraries/Lists/SectionList.js @@ -18,9 +18,11 @@ import type { } from '@react-native/virtualized-lists'; import Platform from '../Utilities/Platform'; -import {VirtualizedSectionList} from '@react-native/virtualized-lists'; +import VirtualizedLists from '@react-native/virtualized-lists'; import * as React from 'react'; +const VirtualizedSectionList = VirtualizedLists.VirtualizedSectionList; + type Item = any; export type SectionBase = _SectionBase; diff --git a/packages/react-native/Libraries/Lists/SectionListModern.js b/packages/react-native/Libraries/Lists/SectionListModern.js index c6ee31d14b3b13..b1826cb2ce0c67 100644 --- a/packages/react-native/Libraries/Lists/SectionListModern.js +++ b/packages/react-native/Libraries/Lists/SectionListModern.js @@ -19,9 +19,11 @@ import type { import type {ElementRef} from 'react'; import Platform from '../Utilities/Platform'; -import {VirtualizedSectionList} from '@react-native/virtualized-lists'; +import VirtualizedLists from '@react-native/virtualized-lists'; import React, {forwardRef, useImperativeHandle, useRef} from 'react'; +const VirtualizedSectionList = VirtualizedLists.VirtualizedSectionList; + type Item = any; export type SectionBase = _SectionBase; diff --git a/packages/react-native/Libraries/Lists/ViewabilityHelper.js b/packages/react-native/Libraries/Lists/ViewabilityHelper.js index d92112a9e431ee..3c0d99cdcd3349 100644 --- a/packages/react-native/Libraries/Lists/ViewabilityHelper.js +++ b/packages/react-native/Libraries/Lists/ViewabilityHelper.js @@ -16,9 +16,8 @@ export type { ViewabilityConfigCallbackPair, } from '@react-native/virtualized-lists'; -import {typeof ViewabilityHelper as ViewabilityHelperType} from '@react-native/virtualized-lists'; +import VirtualizedLists from '@react-native/virtualized-lists'; -const ViewabilityHelper: ViewabilityHelperType = - require('@react-native/virtualized-lists').ViewabilityHelper; +const ViewabilityHelper = VirtualizedLists.ViewabilityHelper; export default ViewabilityHelper; diff --git a/packages/react-native/Libraries/Lists/VirtualizeUtils.js b/packages/react-native/Libraries/Lists/VirtualizeUtils.js index a8e0de2872d12c..571234323c430c 100644 --- a/packages/react-native/Libraries/Lists/VirtualizeUtils.js +++ b/packages/react-native/Libraries/Lists/VirtualizeUtils.js @@ -10,7 +10,6 @@ 'use strict'; -import {typeof keyExtractor as KeyExtractorType} from '@react-native/virtualized-lists'; +import VirtualizedLists from '@react-native/virtualized-lists'; -export const keyExtractor: KeyExtractorType = - require('@react-native/virtualized-lists').keyExtractor; +export const keyExtractor = VirtualizedLists.keyExtractor; diff --git a/packages/react-native/Libraries/Lists/VirtualizedList.js b/packages/react-native/Libraries/Lists/VirtualizedList.js index da0cf74f9360a4..0901c5a161bd5f 100644 --- a/packages/react-native/Libraries/Lists/VirtualizedList.js +++ b/packages/react-native/Libraries/Lists/VirtualizedList.js @@ -10,14 +10,13 @@ 'use strict'; -import {typeof VirtualizedList as VirtualizedListType} from '@react-native/virtualized-lists'; +import VirtualizedLists from '@react-native/virtualized-lists'; -const VirtualizedList: VirtualizedListType = - require('@react-native/virtualized-lists').VirtualizedList; +const VirtualizedList = VirtualizedLists.VirtualizedList; export type { - RenderItemProps, - RenderItemType, + ListRenderItemInfo, + ListRenderItem, Separators, } from '@react-native/virtualized-lists'; export default VirtualizedList; diff --git a/packages/react-native/Libraries/Lists/VirtualizedListContext.js b/packages/react-native/Libraries/Lists/VirtualizedListContext.js index 918bffbbf2e9b6..70b0533133ce46 100644 --- a/packages/react-native/Libraries/Lists/VirtualizedListContext.js +++ b/packages/react-native/Libraries/Lists/VirtualizedListContext.js @@ -10,7 +10,7 @@ 'use strict'; -import {typeof VirtualizedListContextResetter as VirtualizedListContextResetterType} from '@react-native/virtualized-lists'; +import VirtualizedLists from '@react-native/virtualized-lists'; -export const VirtualizedListContextResetter: VirtualizedListContextResetterType = - require('@react-native/virtualized-lists').VirtualizedListContextResetter; +export const VirtualizedListContextResetter = + VirtualizedLists.VirtualizedListContextResetter; diff --git a/packages/react-native/Libraries/Lists/VirtualizedSectionList.js b/packages/react-native/Libraries/Lists/VirtualizedSectionList.js index c0a6a8fb7e0f70..67d057e0824c5f 100644 --- a/packages/react-native/Libraries/Lists/VirtualizedSectionList.js +++ b/packages/react-native/Libraries/Lists/VirtualizedSectionList.js @@ -10,10 +10,9 @@ 'use strict'; -import {typeof VirtualizedSectionList as VirtualizedSectionListType} from '@react-native/virtualized-lists'; +import VirtualizedLists from '@react-native/virtualized-lists'; -const VirtualizedSectionList: VirtualizedSectionListType = - require('@react-native/virtualized-lists').VirtualizedSectionList; +const VirtualizedSectionList = VirtualizedLists.VirtualizedSectionList; export type { SectionBase, diff --git a/packages/react-native/Libraries/Modal/Modal.js b/packages/react-native/Libraries/Modal/Modal.js index edc24f0ecb6e6f..5e0f9af48fdee2 100644 --- a/packages/react-native/Libraries/Modal/Modal.js +++ b/packages/react-native/Libraries/Modal/Modal.js @@ -17,7 +17,7 @@ import {type EventSubscription} from '../vendor/emitter/EventEmitter'; import ModalInjection from './ModalInjection'; import NativeModalManager from './NativeModalManager'; import RCTModalHostView from './RCTModalHostViewNativeComponent'; -import {VirtualizedListContextResetter} from '@react-native/virtualized-lists'; +import VirtualizedLists from '@react-native/virtualized-lists'; import React from 'react'; const ScrollView = require('../Components/ScrollView/ScrollView').default; @@ -28,6 +28,9 @@ const {RootTagContext} = require('../ReactNative/RootTag'); const StyleSheet = require('../StyleSheet/StyleSheet'); const Platform = require('../Utilities/Platform'); +const VirtualizedListContextResetter = + VirtualizedLists.VirtualizedListContextResetter; + type ModalEventDefinitions = { modalDismissed: [{modalID: number}], }; diff --git a/packages/react-native/Libraries/Utilities/ReactNativeTestTools.js b/packages/react-native/Libraries/Utilities/ReactNativeTestTools.js index f3e04bddcc7853..e59ab346fcf54e 100644 --- a/packages/react-native/Libraries/Utilities/ReactNativeTestTools.js +++ b/packages/react-native/Libraries/Utilities/ReactNativeTestTools.js @@ -20,7 +20,7 @@ const Switch = require('../Components/Switch/Switch').default; const TextInput = require('../Components/TextInput/TextInput').default; const View = require('../Components/View/View').default; const Text = require('../Text/Text').default; -const {VirtualizedList} = require('@react-native/virtualized-lists'); +const {VirtualizedList} = require('@react-native/virtualized-lists').default; export type ReactTestInstance = $PropertyType; export type Predicate = (node: ReactTestInstance) => boolean; diff --git a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap index edf733b61b7057..f04591fc853080 100644 --- a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap +++ b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap @@ -4986,7 +4986,7 @@ exports[`public API should not change unintentionally Libraries/Lists/FlatList.j data: ?$ReadOnly<$ArrayLike>, }; type OptionalProps = { - renderItem?: ?RenderItemType, + renderItem?: ?ListRenderItem, columnWrapperStyle?: ViewStyleProp, extraData?: any, getItemLayout?: ( @@ -5201,8 +5201,8 @@ exports[`public API should not change unintentionally Libraries/Lists/Virtualize exports[`public API should not change unintentionally Libraries/Lists/VirtualizedList.js 1`] = ` "declare const VirtualizedList: VirtualizedListType; export type { - RenderItemProps, - RenderItemType, + ListRenderItemInfo, + ListRenderItem, Separators, } from \\"@react-native/virtualized-lists\\"; declare export default typeof VirtualizedList; diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/devsupport/MultipartStreamReaderTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/devsupport/MultipartStreamReaderTest.kt index 5c65d981a75500..b6e56307d3198c 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/devsupport/MultipartStreamReaderTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/devsupport/MultipartStreamReaderTest.kt @@ -11,10 +11,7 @@ import okio.Buffer import okio.ByteString import org.assertj.core.api.Assertions.assertThat import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -@RunWith(RobolectricTestRunner::class) class MultipartStreamReaderTest { @Test diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/devsupport/StackTraceHelperTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/devsupport/StackTraceHelperTest.kt index c3ab6a2ea8f775..9476927d3eba4d 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/devsupport/StackTraceHelperTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/devsupport/StackTraceHelperTest.kt @@ -13,11 +13,8 @@ import com.facebook.react.common.annotations.UnstableReactNativeAPI import com.facebook.react.interfaces.exceptionmanager.ReactJsExceptionHandler.* import org.assertj.core.api.Assertions.assertThat import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner @OptIn(UnstableReactNativeAPI::class) -@RunWith(RobolectricTestRunner::class) class StackTraceHelperTest { @Test fun testParseAlternateFormatStackFrameWithMethod() { diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/network/ProgressiveStringDecoderTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/network/ProgressiveStringDecoderTest.kt index 4e74f9582c8b50..b8319d49c97fe7 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/network/ProgressiveStringDecoderTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/network/ProgressiveStringDecoderTest.kt @@ -12,10 +12,7 @@ import java.nio.charset.StandardCharsets import kotlin.math.min import org.assertj.core.api.Assertions.assertThat import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -@RunWith(RobolectricTestRunner::class) class ProgressiveStringDecoderTest { private val TEST_DATA_1_BYTE = diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/network/ReactCookieJarContainerTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/network/ReactCookieJarContainerTest.kt index e9dac8494e8d35..e4006635968fcd 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/network/ReactCookieJarContainerTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/modules/network/ReactCookieJarContainerTest.kt @@ -12,14 +12,11 @@ import okhttp3.CookieJar import okhttp3.HttpUrl import org.assertj.core.api.Assertions.assertThat import org.junit.Test -import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.whenever -import org.robolectric.RobolectricTestRunner /** Tests for {@link NetworkingModule}. */ -@RunWith(RobolectricTestRunner::class) class ReactCookieJarContainerTest { private val httpUrl: HttpUrl = HttpUrl.Builder().host("example.com").scheme("http").build() diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/MatrixMathHelperTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/MatrixMathHelperTest.kt index d60978bea2d868..b6a9e6d04ee547 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/MatrixMathHelperTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/MatrixMathHelperTest.kt @@ -12,11 +12,8 @@ import kotlin.math.cos import kotlin.math.sin import org.assertj.core.api.Assertions.assertThat import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner /** Test for [MatrixMathHelper] */ -@RunWith(RobolectricTestRunner::class) class MatrixMathHelperTest { @Test diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropAnnotationSetterSpecTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropAnnotationSetterSpecTest.kt index 130531a81ef3d4..df3d23918a8ef0 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropAnnotationSetterSpecTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropAnnotationSetterSpecTest.kt @@ -12,11 +12,8 @@ import com.facebook.react.uimanager.annotations.ReactProp import com.facebook.react.uimanager.annotations.ReactPropGroup import java.util.Date import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner /** Test that verifies that spec of methods annotated with @ReactProp is correct */ -@RunWith(RobolectricTestRunner::class) @Suppress("UNUSED_PARAMETER") class ReactPropAnnotationSetterSpecTest { private abstract inner class BaseViewManager : ViewManager>() { diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/layoutanimation/InterpolatorTypeTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/layoutanimation/InterpolatorTypeTest.kt index 37bcf0edc25da5..58690e01e0a9e9 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/layoutanimation/InterpolatorTypeTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/layoutanimation/InterpolatorTypeTest.kt @@ -10,10 +10,7 @@ package com.facebook.react.uimanager.layoutanimation import java.util.Locale import org.assertj.core.api.Assertions.assertThat import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -@RunWith(RobolectricTestRunner::class) class InterpolatorTypeTest { @Test fun testCamelCase() { diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/image/ImageResizeModeTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/image/ImageResizeModeTest.kt index a00ea2e5a691b2..2e02fe36700edc 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/image/ImageResizeModeTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/image/ImageResizeModeTest.kt @@ -11,10 +11,7 @@ import android.graphics.Shader.TileMode import com.facebook.drawee.drawable.ScalingUtils import org.assertj.core.api.Assertions import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -@RunWith(RobolectricTestRunner::class) class ImageResizeModeTest { @Test diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextTransformTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextTransformTest.kt index 6bd1c2dbe09db5..3e6919151eea9d 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextTransformTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextTransformTest.kt @@ -9,10 +9,7 @@ package com.facebook.react.views.text import org.assertj.core.api.Assertions.assertThat import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -@RunWith(RobolectricTestRunner::class) class TextTransformTest { @Test fun textTransformCapitalize() { diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/view/ColorUtilTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/view/ColorUtilTest.kt index cee90dce009669..581066060ed8f2 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/view/ColorUtilTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/view/ColorUtilTest.kt @@ -9,11 +9,8 @@ package com.facebook.react.views.view import junit.framework.TestCase.assertEquals import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner /** Based on Fresco's DrawableUtilsTest (https://github.com/facebook/fresco). */ -@RunWith(RobolectricTestRunner::class) class ColorUtilTest { @Test fun testNormalize() { diff --git a/packages/react-native/src/private/inspector/NetworkOverlay.js b/packages/react-native/src/private/inspector/NetworkOverlay.js index b21134c5b9efd4..a08d776cae6986 100644 --- a/packages/react-native/src/private/inspector/NetworkOverlay.js +++ b/packages/react-native/src/private/inspector/NetworkOverlay.js @@ -11,7 +11,7 @@ 'use strict'; import type XMLHttpRequest from '../../../Libraries/Network/XMLHttpRequest'; -import type {RenderItemProps} from '@react-native/virtualized-lists'; +import type {ListRenderItemInfo} from '@react-native/virtualized-lists'; import ScrollView from '../../../Libraries/Components/ScrollView/ScrollView'; import React from 'react'; @@ -355,7 +355,7 @@ class NetworkOverlay extends React.Component { _renderItem = ({ item, index, - }: RenderItemProps): React.MixedElement => { + }: ListRenderItemInfo): React.MixedElement => { const tableRowViewStyle = [ styles.tableRow, index % 2 === 1 ? styles.tableRowOdd : styles.tableRowEven, diff --git a/packages/rn-tester/js/examples/Experimental/PlatformTest/RNTesterPlatformTestResultView.js b/packages/rn-tester/js/examples/Experimental/PlatformTest/RNTesterPlatformTestResultView.js index 009dab35cefc96..1b220c502582bc 100644 --- a/packages/rn-tester/js/examples/Experimental/PlatformTest/RNTesterPlatformTestResultView.js +++ b/packages/rn-tester/js/examples/Experimental/PlatformTest/RNTesterPlatformTestResultView.js @@ -12,7 +12,7 @@ import type { PlatformTestResult, PlatformTestResultStatus, } from './RNTesterPlatformTestTypes'; -import type {RenderItemProps} from 'react-native/Libraries/Lists/VirtualizedList'; +import type {ListRenderItemInfo} from 'react-native/Libraries/Lists/VirtualizedList'; import type { TextStyle, ViewStyleProp, @@ -177,7 +177,7 @@ const TableRow = React.memo( }, ); -function renderTableRow({item}: RenderItemProps) { +function renderTableRow({item}: ListRenderItemInfo) { return ; } diff --git a/packages/rn-tester/js/examples/FlatList/BaseFlatListExample.js b/packages/rn-tester/js/examples/FlatList/BaseFlatListExample.js index 9b7eb8a4dd4130..0e75103d36a1c7 100644 --- a/packages/rn-tester/js/examples/FlatList/BaseFlatListExample.js +++ b/packages/rn-tester/js/examples/FlatList/BaseFlatListExample.js @@ -8,7 +8,7 @@ * @format */ -import type {RenderItemProps} from 'react-native/Libraries/Lists/VirtualizedList'; +import type {ListRenderItemInfo} from 'react-native/Libraries/Lists/VirtualizedList'; import * as React from 'react'; import { @@ -34,7 +34,7 @@ const DATA = [ 'Brownie', ]; -const Item = ({item, separators}: RenderItemProps) => { +const Item = ({item, separators}: ListRenderItemInfo) => { return ( { diff --git a/packages/rn-tester/js/examples/FlatList/FlatList-basic.js b/packages/rn-tester/js/examples/FlatList/FlatList-basic.js index 29dbb52574b1d0..37ec319a7f9622 100644 --- a/packages/rn-tester/js/examples/FlatList/FlatList-basic.js +++ b/packages/rn-tester/js/examples/FlatList/FlatList-basic.js @@ -13,7 +13,7 @@ import type {Item} from '../../components/ListExampleShared'; import type {RNTesterModuleExample} from '../../types/RNTesterTypes'; import type FlatList from 'react-native/Libraries/Lists/FlatList'; -import type {RenderItemProps} from 'react-native/Libraries/Lists/VirtualizedList'; +import type {ListRenderItemInfo} from 'react-native/Libraries/Lists/VirtualizedList'; import { FooterComponent, @@ -354,7 +354,7 @@ class FlatListExample extends React.PureComponent { _onRefresh = () => Alert.alert('onRefresh: nothing to refresh :P'); // $FlowFixMe[missing-local-annot] _renderItemComponent = () => { - const renderProp = ({item, separators}: RenderItemProps) => { + const renderProp = ({item, separators}: ListRenderItemInfo) => { return ( ) => ( + ({item}: ListRenderItemInfo<{id: string}>) => ( ): $FlowFixMe => { + }: ListRenderItemInfo): $FlowFixMe => { return ( index * items.length * 3 + i)} - renderItem={(p: RenderItemProps) => ( + renderItem={(p: ListRenderItemInfo) => ( index * items.length * 3 + i + items.length)} - renderItem={(p: RenderItemProps) => ( + renderItem={(p: ListRenderItemInfo) => ( index * items.length * 3 + i + 2 * items.length, )} - renderItem={(p: RenderItemProps) => ( + renderItem={(p: ListRenderItemInfo) => ( )} style={styles.childList} diff --git a/packages/rn-tester/js/examples/FlatList/FlatList-stickyHeaders.js b/packages/rn-tester/js/examples/FlatList/FlatList-stickyHeaders.js index d4a50689d3f8b6..b6bace9f658f75 100644 --- a/packages/rn-tester/js/examples/FlatList/FlatList-stickyHeaders.js +++ b/packages/rn-tester/js/examples/FlatList/FlatList-stickyHeaders.js @@ -9,7 +9,7 @@ */ import type {RNTesterModuleExample} from '../../types/RNTesterTypes'; -import type {RenderItemProps} from 'react-native/Libraries/Lists/VirtualizedList'; +import type {ListRenderItemInfo} from 'react-native/Libraries/Lists/VirtualizedList'; import * as React from 'react'; import {FlatList, StyleSheet, Text, View} from 'react-native'; @@ -30,7 +30,7 @@ const DATA = [ const STICKY_HEADER_INDICES = [0, 2, 4]; -const Item = ({item, separators}: RenderItemProps) => { +const Item = ({item, separators}: ListRenderItemInfo) => { return ( {item} diff --git a/packages/rn-tester/js/examples/SwipeableCardExample/SwipeableCardExample.js b/packages/rn-tester/js/examples/SwipeableCardExample/SwipeableCardExample.js index 9e07c99c213c5e..01839aeff94087 100644 --- a/packages/rn-tester/js/examples/SwipeableCardExample/SwipeableCardExample.js +++ b/packages/rn-tester/js/examples/SwipeableCardExample/SwipeableCardExample.js @@ -8,7 +8,7 @@ * @format */ -import type {RenderItemProps} from 'react-native/Libraries/Lists/VirtualizedList'; +import type {ListRenderItemInfo} from 'react-native/Libraries/Lists/VirtualizedList'; import * as React from 'react'; import { @@ -142,7 +142,7 @@ function SwipeableCard(props: { const cardData = Array(5); function Card(props: {color: string}) { - const renderItem = ({item, index}: RenderItemProps<$FlowFixMe>) => ( + const renderItem = ({item, index}: ListRenderItemInfo<$FlowFixMe>) => ( ); diff --git a/packages/virtualized-lists/Lists/FillRateHelper.js b/packages/virtualized-lists/Lists/FillRateHelper.js index be37ac83ba7b0f..f09e946e3da30d 100644 --- a/packages/virtualized-lists/Lists/FillRateHelper.js +++ b/packages/virtualized-lists/Lists/FillRateHelper.js @@ -244,4 +244,4 @@ class FillRateHelper { } } -module.exports = FillRateHelper; +export default FillRateHelper; diff --git a/packages/virtualized-lists/Lists/ListMetricsAggregator.js b/packages/virtualized-lists/Lists/ListMetricsAggregator.js index 9516269aee024e..8d31576cb5006e 100644 --- a/packages/virtualized-lists/Lists/ListMetricsAggregator.js +++ b/packages/virtualized-lists/Lists/ListMetricsAggregator.js @@ -8,7 +8,7 @@ * @format */ -import type {Props as VirtualizedListProps} from './VirtualizedListProps'; +import type {VirtualizedListProps} from './VirtualizedListProps'; import type {Layout} from 'react-native/Libraries/Types/CoreEventTypes'; import {keyExtractor as defaultKeyExtractor} from './VirtualizeUtils'; diff --git a/packages/virtualized-lists/Lists/StateSafePureComponent.js b/packages/virtualized-lists/Lists/StateSafePureComponent.js index 503f97eba0543b..8640f14383c527 100644 --- a/packages/virtualized-lists/Lists/StateSafePureComponent.js +++ b/packages/virtualized-lists/Lists/StateSafePureComponent.js @@ -31,8 +31,8 @@ export default class StateSafePureComponent< this._installSetStateHooks(); } - setState( - partialState: ?(Partial | ((State, Props) => ?Partial)), + setState>( + partialState: ?(Pick | ((State, Props) => ?Pick)), callback?: () => mixed, ): void { if (typeof partialState === 'function') { diff --git a/packages/virtualized-lists/Lists/ViewabilityHelper.js b/packages/virtualized-lists/Lists/ViewabilityHelper.js index c250d55ff2b1c4..b49455794d5f57 100644 --- a/packages/virtualized-lists/Lists/ViewabilityHelper.js +++ b/packages/virtualized-lists/Lists/ViewabilityHelper.js @@ -35,6 +35,9 @@ export type ViewabilityConfigCallbackPair = { ... }; +export type ViewabilityConfigCallbackPairs = + Array; + export type ViewabilityConfig = $ReadOnly<{ /** * Minimum amount of time (in milliseconds) that an item must be physically viewable before the @@ -346,4 +349,4 @@ function _isEntirelyVisible( return top >= 0 && bottom <= viewportHeight && bottom > top; } -module.exports = ViewabilityHelper; +export default ViewabilityHelper; diff --git a/packages/virtualized-lists/Lists/VirtualizedList.js b/packages/virtualized-lists/Lists/VirtualizedList.js index 17a9b90c349b15..0f66e56a43d4c2 100644 --- a/packages/virtualized-lists/Lists/VirtualizedList.js +++ b/packages/virtualized-lists/Lists/VirtualizedList.js @@ -12,9 +12,9 @@ import type {CellMetricProps, ListOrientation} from './ListMetricsAggregator'; import type {ViewToken} from './ViewabilityHelper'; import type { Item, - Props, - RenderItemProps, - RenderItemType, + VirtualizedListProps, + ListRenderItemInfo, + ListRenderItem, Separators, } from './VirtualizedListProps'; import type {ScrollResponderType} from 'react-native/Libraries/Components/ScrollView/ScrollView'; @@ -63,7 +63,7 @@ import { findNodeHandle, } from 'react-native'; -export type {RenderItemProps, RenderItemType, Separators}; +export type {ListRenderItemInfo, ListRenderItem, Separators}; const ON_EDGE_REACHED_EPSILON = 0.001; @@ -122,7 +122,10 @@ function getScrollingThreshold(threshold: number, visibleLength: number) { * - As an effort to remove defaultProps, use helper functions when referencing certain props * */ -class VirtualizedList extends StateSafePureComponent { +class VirtualizedList extends StateSafePureComponent< + VirtualizedListProps, + State, +> { static contextType: typeof VirtualizedListContext = VirtualizedListContext; // scrollToEnd may be janky without getItemLayout prop @@ -369,7 +372,7 @@ class VirtualizedList extends StateSafePureComponent { state: State; - constructor(props: Props) { + constructor(props: VirtualizedListProps) { super(props); this._checkProps(props); @@ -415,7 +418,7 @@ class VirtualizedList extends StateSafePureComponent { }; } - _checkProps(props: Props) { + _checkProps(props: VirtualizedListProps) { const {onScroll, windowSize, getItemCount, data, initialScrollIndex} = props; @@ -464,7 +467,7 @@ class VirtualizedList extends StateSafePureComponent { } static _findItemIndexWithKey( - props: Props, + props: VirtualizedListProps, key: string, hint: ?number, ): ?number { @@ -486,9 +489,9 @@ class VirtualizedList extends StateSafePureComponent { static _getItemKey( props: { - data: Props['data'], - getItem: Props['getItem'], - keyExtractor: Props['keyExtractor'], + data: VirtualizedListProps['data'], + getItem: VirtualizedListProps['getItem'], + keyExtractor: VirtualizedListProps['keyExtractor'], ... }, index: number, @@ -498,7 +501,7 @@ class VirtualizedList extends StateSafePureComponent { } static _createRenderMask( - props: Props, + props: VirtualizedListProps, cellsAroundViewport: {first: number, last: number}, additionalRegions?: ?$ReadOnlyArray<{first: number, last: number}>, ): CellRenderMask { @@ -541,7 +544,10 @@ class VirtualizedList extends StateSafePureComponent { return renderMask; } - static _initialRenderRegion(props: Props): {first: number, last: number} { + static _initialRenderRegion(props: VirtualizedListProps): { + first: number, + last: number, + } { const itemCount = props.getItemCount(props.data); const firstCellIndex = Math.max( @@ -562,7 +568,7 @@ class VirtualizedList extends StateSafePureComponent { } static _ensureClosestStickyHeader( - props: Props, + props: VirtualizedListProps, stickyIndicesSet: Set, renderMask: CellRenderMask, cellIdx: number, @@ -578,7 +584,7 @@ class VirtualizedList extends StateSafePureComponent { } _adjustCellsAroundViewport( - props: Props, + props: VirtualizedListProps, cellsAroundViewport: {first: number, last: number}, pendingScrollUpdateCount: number, ): {first: number, last: number} { @@ -689,7 +695,10 @@ class VirtualizedList extends StateSafePureComponent { this._fillRateHelper.deactivateAndFlush(); } - static getDerivedStateFromProps(newProps: Props, prevState: State): State { + static getDerivedStateFromProps( + newProps: VirtualizedListProps, + prevState: State, + ): State { // first and last could be stale (e.g. if a new, shorter items props is passed in), so we make // sure we're rendering a reasonable range here. const itemCount = newProps.getItemCount(newProps.data); @@ -821,7 +830,7 @@ class VirtualizedList extends StateSafePureComponent { static _constrainToItemCount( cells: {first: number, last: number}, - props: Props, + props: VirtualizedListProps, ): {first: number, last: number} { const itemCount = props.getItemCount(props.data); const lastPossibleCellIndex = itemCount - 1; @@ -1108,7 +1117,7 @@ class VirtualizedList extends StateSafePureComponent { ( this.props.renderScrollComponent || this._defaultRenderScrollComponent - )(scrollProps), + )(scrollProps) as ExactReactElement_DEPRECATED, { ref: this._captureScrollRef, }, @@ -1153,7 +1162,7 @@ class VirtualizedList extends StateSafePureComponent { } } - componentDidUpdate(prevProps: Props) { + componentDidUpdate(prevProps: VirtualizedListProps) { const {data, extraData, getItemLayout} = this.props; if (data !== prevProps.data || extraData !== prevProps.extraData) { // clear the viewableIndices cache to also trigger @@ -1711,7 +1720,7 @@ class VirtualizedList extends StateSafePureComponent { zoomScale, }; if (this.state.pendingScrollUpdateCount > 0) { - this.setState(state => ({ + this.setState<'pendingScrollUpdateCount'>(state => ({ pendingScrollUpdateCount: state.pendingScrollUpdateCount - 1, })); } @@ -1871,7 +1880,7 @@ class VirtualizedList extends StateSafePureComponent { _updateCellsToRender = () => { this._updateViewableItems(this.props, this.state.cellsAroundViewport); - this.setState((state, props) => { + this.setState<'cellsAroundViewport' | 'renderMask'>((state, props) => { const cellsAroundViewport = this._adjustCellsAroundViewport( props, state.cellsAroundViewport, @@ -2037,4 +2046,4 @@ const styles = StyleSheet.create({ }, }); -module.exports = VirtualizedList; +export default VirtualizedList; diff --git a/packages/virtualized-lists/Lists/VirtualizedListCellRenderer.js b/packages/virtualized-lists/Lists/VirtualizedListCellRenderer.js index d929e78801337a..ad69cf664b0d9d 100644 --- a/packages/virtualized-lists/Lists/VirtualizedListCellRenderer.js +++ b/packages/virtualized-lists/Lists/VirtualizedListCellRenderer.js @@ -8,7 +8,7 @@ * @format */ -import type {CellRendererProps, RenderItemType} from './VirtualizedListProps'; +import type {CellRendererProps, ListRenderItem} from './VirtualizedListProps'; import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet'; import type { FocusEvent, @@ -39,7 +39,7 @@ export type Props = { props: Partial>, ) => void, prevCellKey: ?string, - renderItem?: ?RenderItemType, + renderItem?: ?ListRenderItem, ... }; @@ -64,10 +64,10 @@ export default class CellRenderer extends React.PureComponent< }, }; - static getDerivedStateFromProps( - props: Props, - prevState: State, - ): ?State { + static getDerivedStateFromProps( + props: Props, + prevState: State, + ): ?State { if (props.item !== prevState.separatorProps.leadingItem) { return { separatorProps: { @@ -130,7 +130,7 @@ export default class CellRenderer extends React.PureComponent< }; _renderElement( - renderItem: ?RenderItemType, + renderItem: ?ListRenderItem, ListItemComponent: any, item: ItemT, index: number, diff --git a/packages/virtualized-lists/Lists/VirtualizedListProps.js b/packages/virtualized-lists/Lists/VirtualizedListProps.js index 1964acc58ee25d..4e5570999b12b9 100644 --- a/packages/virtualized-lists/Lists/VirtualizedListProps.js +++ b/packages/virtualized-lists/Lists/VirtualizedListProps.js @@ -31,7 +31,7 @@ export type Separators = { ... }; -export type RenderItemProps = { +export type ListRenderItemInfo = { item: ItemT, index: number, separators: Separators, @@ -48,8 +48,8 @@ export type CellRendererProps = $ReadOnly<{ style: ViewStyleProp, }>; -export type RenderItemType = ( - info: RenderItemProps, +export type ListRenderItem = ( + info: ListRenderItemInfo, ) => React.Node; type RequiredProps = { @@ -68,7 +68,7 @@ type RequiredProps = { getItemCount: (data: any) => number, }; type OptionalProps = { - renderItem?: ?RenderItemType, + renderItem?: ?ListRenderItem, /** * `debug` will turn on extra logging and visual overlays to aid with debugging both usage and * implementation, but with a significant perf hit. @@ -155,26 +155,17 @@ type OptionalProps = { * `highlight` and `unhighlight` (which set the `highlighted: boolean` prop) are insufficient for * your use-case. */ - ListItemComponent?: ?( - | React.ComponentType - | ExactReactElement_DEPRECATED - ), + ListItemComponent?: ?(React.ComponentType | React.MixedElement), /** * Rendered when the list is empty. Can be a React Component Class, a render function, or * a rendered element. */ - ListEmptyComponent?: ?( - | React.ComponentType - | ExactReactElement_DEPRECATED - ), + ListEmptyComponent?: ?(React.ComponentType | React.MixedElement), /** * Rendered at the bottom of all the items. Can be a React Component Class, a render function, or * a rendered element. */ - ListFooterComponent?: ?( - | React.ComponentType - | ExactReactElement_DEPRECATED - ), + ListFooterComponent?: ?(React.ComponentType | React.MixedElement), /** * Styling for internal View for ListFooterComponent */ @@ -183,10 +174,7 @@ type OptionalProps = { * Rendered at the top of all the items. Can be a React Component Class, a render function, or * a rendered element. */ - ListHeaderComponent?: ?( - | React.ComponentType - | ExactReactElement_DEPRECATED - ), + ListHeaderComponent?: ?(React.ComponentType | React.MixedElement), /** * Styling for internal View for ListHeaderComponent */ @@ -256,7 +244,7 @@ type OptionalProps = { * component built internally. The onRefresh and refreshing * props are also ignored. Only works for vertical VirtualizedList. */ - refreshControl?: ?ExactReactElement_DEPRECATED, + refreshControl?: ?React.MixedElement, /** * Set this true while waiting for new data from a refresh. */ @@ -270,7 +258,7 @@ type OptionalProps = { /** * Render a custom scroll component, e.g. with a differently styled `RefreshControl`. */ - renderScrollComponent?: (props: Object) => ExactReactElement_DEPRECATED, + renderScrollComponent?: (props: Object) => React.MixedElement, /** * Amount of time between low-pri item render batches, e.g. for rendering items quite a ways off * screen. Similar fill rate/responsiveness tradeoff as `maxToRenderPerBatch`. @@ -296,10 +284,10 @@ type OptionalProps = { /** * The legacy implementation is no longer supported. */ - legacyImplementation?: empty, + // legacyImplementation?: empty, }; -export type Props = { +export type VirtualizedListProps = { ...React.ElementConfig, ...RequiredProps, ...OptionalProps, diff --git a/packages/virtualized-lists/Lists/VirtualizedSectionList.js b/packages/virtualized-lists/Lists/VirtualizedSectionList.js index ad546b9bf272b7..c759f1dcd55b49 100644 --- a/packages/virtualized-lists/Lists/VirtualizedSectionList.js +++ b/packages/virtualized-lists/Lists/VirtualizedSectionList.js @@ -91,7 +91,7 @@ type OptionalProps> = { type VirtualizedListProps = React.ElementConfig; -export type Props = { +export type Props> = { ...RequiredProps, ...OptionalProps, ...$Diff< @@ -598,7 +598,7 @@ function ItemWithSeparator(props: ItemWithSeparatorProps): React.Node { ); } -module.exports = VirtualizedSectionList as component( +export default VirtualizedSectionList as component( ref: React.RefSetter< interface { getListRef(): ?VirtualizedList, diff --git a/packages/virtualized-lists/Lists/__tests__/FillRateHelper-test.js b/packages/virtualized-lists/Lists/__tests__/FillRateHelper-test.js index 58d47ba68d8c90..d69cd9b2b5ba4d 100644 --- a/packages/virtualized-lists/Lists/__tests__/FillRateHelper-test.js +++ b/packages/virtualized-lists/Lists/__tests__/FillRateHelper-test.js @@ -10,7 +10,7 @@ 'use strict'; -const FillRateHelper = require('../FillRateHelper'); +const FillRateHelper = require('../FillRateHelper').default; let rowFramesGlobal; const dataGlobal = [ diff --git a/packages/virtualized-lists/Lists/__tests__/ViewabilityHelper-test.js b/packages/virtualized-lists/Lists/__tests__/ViewabilityHelper-test.js index 9e7f591b91b94a..80975b127cae42 100644 --- a/packages/virtualized-lists/Lists/__tests__/ViewabilityHelper-test.js +++ b/packages/virtualized-lists/Lists/__tests__/ViewabilityHelper-test.js @@ -10,7 +10,7 @@ 'use strict'; -const ViewabilityHelper = require('../ViewabilityHelper'); +const ViewabilityHelper = require('../ViewabilityHelper').default; let rowFrames; let data; diff --git a/packages/virtualized-lists/Lists/__tests__/VirtualizedSectionList-test.js b/packages/virtualized-lists/Lists/__tests__/VirtualizedSectionList-test.js index ae4d6638481083..4b427f60b34def 100644 --- a/packages/virtualized-lists/Lists/__tests__/VirtualizedSectionList-test.js +++ b/packages/virtualized-lists/Lists/__tests__/VirtualizedSectionList-test.js @@ -13,7 +13,7 @@ import nullthrows from 'nullthrows'; -const VirtualizedSectionList = require('../VirtualizedSectionList'); +const VirtualizedSectionList = require('../VirtualizedSectionList').default; const React = require('react'); const ReactTestRenderer = require('react-test-renderer'); diff --git a/packages/virtualized-lists/Utilities/__tests__/clamp-test.js b/packages/virtualized-lists/Utilities/__tests__/clamp-test.js index c98980451933fa..73512fa8e50da4 100644 --- a/packages/virtualized-lists/Utilities/__tests__/clamp-test.js +++ b/packages/virtualized-lists/Utilities/__tests__/clamp-test.js @@ -11,7 +11,7 @@ 'use strict'; describe('clamp', () => { - const clamp = require('../clamp'); + const clamp = require('../clamp').default; it('should return the value if the value does not exceed boundaries', () => { expect(clamp(0, 5, 10)).toEqual(5); diff --git a/packages/virtualized-lists/Utilities/clamp.js b/packages/virtualized-lists/Utilities/clamp.js index 7bed6157516ccd..228def51c1acab 100644 --- a/packages/virtualized-lists/Utilities/clamp.js +++ b/packages/virtualized-lists/Utilities/clamp.js @@ -20,4 +20,4 @@ function clamp(min: number, value: number, max: number): number { return value; } -module.exports = clamp; +export default clamp; diff --git a/packages/virtualized-lists/Utilities/infoLog.js b/packages/virtualized-lists/Utilities/infoLog.js index 6cb6df8d414971..7f2a63e0cd8448 100644 --- a/packages/virtualized-lists/Utilities/infoLog.js +++ b/packages/virtualized-lists/Utilities/infoLog.js @@ -17,4 +17,4 @@ function infoLog(...args: Array): void { return console.log(...args); } -module.exports = infoLog; +export default infoLog; diff --git a/packages/virtualized-lists/index.js b/packages/virtualized-lists/index.js index 977dd1db499ea4..5cc377c3bc2ea3 100644 --- a/packages/virtualized-lists/index.js +++ b/packages/virtualized-lists/index.js @@ -22,11 +22,12 @@ export type { ViewToken, ViewabilityConfig, ViewabilityConfigCallbackPair, + ViewabilityConfigCallbackPairs, } from './Lists/ViewabilityHelper'; export type { CellRendererProps, - RenderItemProps, - RenderItemType, + ListRenderItemInfo, + ListRenderItem, Separators, } from './Lists/VirtualizedListProps'; export type { @@ -36,23 +37,23 @@ export type { } from './Lists/VirtualizedSectionList'; export type {FillRateInfo} from './Lists/FillRateHelper'; -module.exports = { +export default { keyExtractor, get VirtualizedList(): VirtualizedList { - return require('./Lists/VirtualizedList'); + return require('./Lists/VirtualizedList').default; }, get VirtualizedSectionList(): VirtualizedSectionList { - return require('./Lists/VirtualizedSectionList'); + return require('./Lists/VirtualizedSectionList').default; }, get VirtualizedListContextResetter(): VirtualizedListContextResetter { const VirtualizedListContext = require('./Lists/VirtualizedListContext'); return VirtualizedListContext.VirtualizedListContextResetter; }, get ViewabilityHelper(): ViewabilityHelper { - return require('./Lists/ViewabilityHelper'); + return require('./Lists/ViewabilityHelper').default; }, get FillRateHelper(): FillRateHelper { - return require('./Lists/FillRateHelper'); + return require('./Lists/FillRateHelper').default; }, }; diff --git a/scripts/build/build-types/transforms/__tests__/replaceRequiresWithImports-test.js b/scripts/build/build-types/transforms/__tests__/replaceRequiresWithImports-test.js new file mode 100644 index 00000000000000..971799225793f6 --- /dev/null +++ b/scripts/build/build-types/transforms/__tests__/replaceRequiresWithImports-test.js @@ -0,0 +1,136 @@ +/** + * 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 + * @oncall react_native + */ + +const replaceRequiresWithImports = require('../replaceRequiresWithImports.js'); +const {parse, print} = require('hermes-transform'); + +const prettierOptions = {parser: 'babel'}; + +async function translate(code: string): Promise { + const parsed = await parse(code); + const result = await replaceRequiresWithImports(parsed); + return print(result.ast, result.mutatedCode, prettierOptions); +} + +describe('replaceRequiresWithImports', () => { + test('should replace require().default with a default import', async () => { + const code = ` + const Foo: mixed = require('./Foo').default; + `; + const result = await translate(code); + expect(result).toMatchInlineSnapshot(` + "import Foo from \\"./Foo\\"; + " + `); + }); + + test('should replace require() with a namespace import', async () => { + const code = ` + const Foo: mixed = require('./Foo'); + `; + const result = await translate(code); + expect(result).toMatchInlineSnapshot(` + "import * as Foo from \\"./Foo\\"; + " + `); + }); + + test('should replace require().member with an import', async () => { + const code = ` + const Foo: mixed = require('./Foo').foo; + `; + const result = await translate(code); + expect(result).toMatchInlineSnapshot(` + "import { foo as Foo } from \\"./Foo\\"; + " + `); + }); + + test('should replace require()["member"] with an import', async () => { + const code = ` + const Foo: mixed = require('./Foo')["foo"]; + `; + const result = await translate(code); + expect(result).toMatchInlineSnapshot(` + "import { foo as Foo } from \\"./Foo\\"; + " + `); + }); + + test('should replace spread require() with an import', async () => { + const code = ` + const { foo, bar } = require('./Foo'); + `; + const result = await translate(code); + expect(result).toMatchInlineSnapshot(` + "import { foo, bar } from \\"./Foo\\"; + " + `); + }); + + test('should replace aliased spread require() with an import', async () => { + const code = ` + const { foo: otherFoo, bar: otherBar } = require('./Foo'); + `; + const result = await translate(code); + expect(result).toMatchInlineSnapshot(` + "import { foo as otherFoo, bar as otherBar } from \\"./Foo\\"; + " + `); + }); + + test('should handle aliased and non-aliased values in spread require() with an import', async () => { + const code = ` + const { default: defaultFoo, foo } = require('./Foo'); + `; + const result = await translate(code); + expect(result).toMatchInlineSnapshot(` + "import { default as defaultFoo, foo } from \\"./Foo\\"; + " + `); + }); + + test('should ignore unbound requires', async () => { + const code = ` + require('./Foo'); + `; + const result = await translate(code); + expect(result).toMatchInlineSnapshot(` + "require(\\"./Foo\\"); + " + `); + }); + + test('should ignore local requires', async () => { + const code = ` + function foo() { + const bar = require('./Bar'); + } + `; + const result = await translate(code); + expect(result).toMatchInlineSnapshot(` + "function foo() { + const bar = require(\\"./Bar\\"); + } + " + `); + }); + + test('should throw when encountering spread operator', async () => { + const tranlsateFn = async () => { + const code = ` + const { foo, ...rest } = require('./Foo'); + `; + await translate(code); + }; + await expect(tranlsateFn()).rejects.toThrow(); + }); +}); diff --git a/scripts/build/build-types/transforms/replaceRequiresWithImports.js b/scripts/build/build-types/transforms/replaceRequiresWithImports.js new file mode 100644 index 00000000000000..d72776aef564c2 --- /dev/null +++ b/scripts/build/build-types/transforms/replaceRequiresWithImports.js @@ -0,0 +1,194 @@ +/** + * 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 + * @oncall react_native + */ + +import type {TransformVisitor} from 'hermes-transform'; +import type {ParseResult} from 'hermes-transform/dist/transform/parse'; +import type {TransformASTResult} from 'hermes-transform/dist/transform/transformAST'; + +const {transformAST} = require('hermes-transform/dist/transform/transformAST'); + +const visitors: TransformVisitor = context => ({ + VariableDeclaration(node): void { + if (node.parent?.type !== 'Program') { + // Ignore declarations that are not in the top-level scope + return; + } + + if (node.declarations.length !== 1) { + // Ignore mutliple declarations for now, those can be implemented if it + // turns out to be necessary. + return; + } + + // Handle simple cases where `require` call is the only expression on the RHS + if ( + node.declarations[0].init?.type === 'CallExpression' && + node.declarations[0].init.callee.type === 'Identifier' && + node.declarations[0].init.callee.name === 'require' + ) { + const requiredModule = node.declarations[0].init.arguments[0]; + + if (node.declarations[0].id.type === 'Identifier') { + // $FlowExpectedError[incompatible-call] - we are replacing an expression with a statement but in the top-level scope + context.replaceNode(node, { + type: 'ImportDeclaration', + source: requiredModule, + specifiers: [ + { + type: 'ImportNamespaceSpecifier', + local: { + type: 'Identifier', + name: node.declarations[0].id.name, + optional: false, + }, + }, + ], + }); + } else if (node.declarations[0].id.type === 'ObjectPattern') { + // $FlowExpectedError[incompatible-call] - we are replacing an expression with a statement but in the top-level scope + context.replaceNode(node, { + type: 'ImportDeclaration', + source: requiredModule, + specifiers: node.declarations[0].id.properties.map(property => { + if (property.type !== 'Property') { + throw new Error('Unexpected property type: ' + property.type); + } + + if (property.key.type !== 'Identifier') { + throw new Error('Unexpected key type: ' + property.key.type); + } + + if (property.value.type !== 'Identifier') { + throw new Error('Unexpected value type: ' + property.value.type); + } + + return { + type: 'ImportSpecifier', + local: { + type: 'Identifier', + name: property.value.name, + optional: false, + }, + imported: { + type: 'Identifier', + name: property.key.name, + optional: false, + }, + }; + }), + }); + } + } + + // Handle cases where `require` call is the first expression in a member expression + if ( + node.declarations[0].init?.type === 'MemberExpression' && + node.declarations[0].init.object.type === 'CallExpression' && + node.declarations[0].init.object.callee.type === 'Identifier' && + node.declarations[0].init.object.callee.name === 'require' && + node.declarations[0].id.type === 'Identifier' + ) { + const requiredModule = node.declarations[0].init.object.arguments[0]; + const variableName = node.declarations[0].id.name; + + // Handle member access via dot operator + if (node.declarations[0].init.property.type === 'Identifier') { + // Special treatment for `require().default` case to transform it to + // a default import + if (node.declarations[0].init.property.name === 'default') { + // $FlowExpectedError[incompatible-call] - we are replacing an expression with a statement but in the top-level scope + context.replaceNode(node, { + type: 'ImportDeclaration', + source: requiredModule, + specifiers: [ + { + type: 'ImportDefaultSpecifier', + local: { + type: 'Identifier', + name: variableName, + optional: false, + }, + }, + ], + }); + } else { + // $FlowExpectedError[incompatible-call] - we are replacing an expression with a statement but in the top-level scope + context.replaceNode(node, { + type: 'ImportDeclaration', + source: requiredModule, + specifiers: [ + { + type: 'ImportSpecifier', + local: { + type: 'Identifier', + name: variableName, + optional: false, + }, + imported: { + type: 'Identifier', + name: node.declarations[0].init.property.name, + optional: false, + }, + }, + ], + }); + } + } else if (node.declarations[0].init.property.type === 'Literal') { + // Handle member access via bracket notation + // $FlowExpectedError[incompatible-call] - we are replacing an expression with a statement but in the top-level + context.replaceNode(node, { + type: 'ImportDeclaration', + source: requiredModule, + specifiers: [ + { + type: 'ImportSpecifier', + local: { + type: 'Identifier', + name: variableName, + optional: false, + }, + imported: { + type: 'Identifier', + name: node.declarations[0].init.property.value, + optional: false, + }, + }, + ], + }); + } + } + }, +}); + +/** + * Replace `require` calls with `import` statements. + * + * In the type-land top-level requires can safely be replaced with import + * statements without impacring the runtime. This allows the modern Flow toolkit + * to be used in existing codebases without having to update each file still + * relying on require syntax. + * + * It's expecially useful in more complex cases where a type comes from an import + * but the implementation comes from a require, like so: + * import typeof FooClassT from './Foo'; + * const FooClass: FooClassT = require('./Foo').default; + * const Foo: FooClass = new FooClass(); + * + * Where the types would diverge in the resulting TS output generated by + * flow-api-translator. + */ +async function replaceRequiresWithImports( + source: ParseResult, +): Promise { + return transformAST(source, visitors); +} + +module.exports = replaceRequiresWithImports; diff --git a/scripts/build/build-types/translateSourceFile.js b/scripts/build/build-types/translateSourceFile.js index ce9fd4e64959cc..b6ba035b832cdf 100644 --- a/scripts/build/build-types/translateSourceFile.js +++ b/scripts/build/build-types/translateSourceFile.js @@ -20,6 +20,7 @@ type TransformFn = ParseResult => Promise; const preTransforms: Array = [ require('./transforms/stripPrivateProperties'), + require('./transforms/replaceRequiresWithImports'), ]; const prettierOptions = {parser: 'babel'}; const unsupportedFeatureRegex =