Compare commits

...
Author SHA1 Message Date
React Native Bot b0d9ef80da Release 0.77.0-rc.4
#publish-packages-to-npm&next
2024-12-23 16:50:28 +00:00
Thomas NardoneandRob Hogan e3970a4bb3 Restore subclipping view removal (#48329)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48329

With the call to `removeView()` removed from `ReactViewClippingManager` in https://github.com/facebook/react-native/pull/47634, we're seeing views erroneously sticking around in the layout.

While `removeViewWithSubviewClippingEnabled()` calls `removeViewsInLayout()`, it does not trigger the corresponding side effects of [removeView()](https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-15.0.0_r9/core/java/android/view/ViewGroup.java#5501):
```
public void removeView(View view) {
  if (removeViewInternal(view)) {
--> requestLayout();
--> invalidate(true);
  }
}
```
To compensate, flip `removeViewsInLayout()` to [`removeViews()`](https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-15.0.0_r9/core/java/android/view/ViewGroup.java#5562), which will ensure layout.

Changelog: [Android][Fixed] Restore layout/invalidate during ReactViewClippingManager.removeViewAt()

Reviewed By: javache

Differential Revision: D67398971

fbshipit-source-id: b100db468cc3be6ddc6edd6c6d078a8a0b59a2c1
2024-12-23 12:14:51 +00:00
Riccardo Cipolleschi 4370860c8c [LOCAL] Bump podfile.lock 2024-12-17 11:36:23 +00:00
React Native Bot 0840eab8ed Release 0.77.0-rc.3
#publish-packages-to-npm&next
2024-12-17 10:01:53 +00:00
Nicola CortiandRiccardo Cipolleschi e7c44903e1 Gradle to 8.11.1 (#48026)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48026

This should mitigate this particular issue we're seeing on Windows:
- https://github.com/facebook/react-native/issues/46210

Changelog:
[Android] [Changed] - Gradle to 8.11.1

Reviewed By: javache

Differential Revision: D66600321

fbshipit-source-id: d58437485222e189d90bcf4d6b41ca956449ed22
2024-12-16 15:35:32 +00:00
Riccardo Cipolleschi 6b22412069 [LOCAL] Add missing imports in ReactViewGroup.java and remove unused symbols 2024-12-16 15:32:04 +00:00
Riccardo Cipolleschi c4c52fb2bd [LOCAL] Fix badly resolved conflict on ReactViewGroup 2024-12-16 14:56:11 +00:00
Riccardo CipolleschiandGitHub d4941c7c6d [LOCAL][RN][CI] Add Maestro tests for the Old Arch in Template App (#48045) 2024-12-16 14:24:37 +00:00
Nicola CortiandRiccardo Cipolleschi 125b0f47d1 Revert "Include autolinkin.h in OnLoad.cpp only if it exists (#47875)"
This reverts commit 5b2bbb84b1.
2024-12-16 14:23:56 +00:00
Kacper KafaraandRiccardo Cipolleschi 81aaf46c67 Fix handling removal of transitioning views (#47634)
Summary:
Related PR in `react-native-screens`:

* https://github.com/software-mansion/react-native-screens/pull/2495

Additional context:
   * [my detailed explanation of **one of the issues**](https://github.com/software-mansion/react-native-screens/pull/2495#issuecomment-2478915818)
   * [Android Developer: ViewGroup.startViewTransition docs](https://developer.android.com/reference/android/view/ViewGroup#startViewTransition(android.view.View))

On Android view groups can be marked as "transitioning" with a `ViewGroup.startViewTransition` call. This effectively ensures, that in case a view group is marked with this call and its children are removed, they will be still drawn until `endViewTransition` is not called.

This mechanism is implemented in Android by [keeping track of "transitioning" children in auxiliary `mTransitioningViews` array](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewGroup.java#7178). Then when such "transitioning" child is removed, [it is removed from children array](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewGroup.java#5595) but it's [parent-child relationship is not cleared](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewGroup.java#5397) and it is still retained in the auxiliary array.

Having that established we can proceed with problem description.

https://github.com/user-attachments/assets/d0356bf5-2f17-4b06-ba53-bfca659a1071

<details>
<summary>Full code</summary>

```javascript
import { NavigationContainer } from 'react-navigation/native';
import React from 'react';
import { createNativeStackNavigator } from 'react-navigation/native-stack';
import { enableScreens } from 'react-native-screens';
import {
  StyleSheet,
  Text,
  View,
  FlatList,
  Button,
  ViewProps,
  Image,
  FlatListProps,
  findNodeHandle,
} from 'react-native';

enableScreens(true);

function Item({ children, ...props }: ViewProps) {
  return (
    <View style={styles.item} {...props}>
      <Image source={require('../assets/trees.jpg')} style={styles.image} />
      <Text style={styles.text}>{children}</Text>
    </View>
  );
}

function Home({ navigation }: any) {
  return (
    <View style={styles.container}>
      <Button title="Go to List" onPress={() => navigation.navigate('List')} />
    </View>
  );
}

function ListScreenSimplified({secondVisible}: {secondVisible?: (visible: boolean) => void}) {
  const containerRef = React.useRef<View>(null);
  const innerViewRef = React.useRef<View>(null);
  const childViewRef = React.useRef<View>(null);

  React.useEffect(() => {
    if (containerRef.current != null) {
      const tag = findNodeHandle(containerRef.current);
      console.log(`Container has tag [${tag}]`);
    }
    if (innerViewRef.current != null) {
      const tag = findNodeHandle(innerViewRef.current);
      console.log(`InnerView has tag [${tag}]`);
    }
    if (childViewRef.current != null) {
      const tag = findNodeHandle(childViewRef.current);
      console.log(`ChildView has tag [${tag}]`);
    }
  }, [containerRef.current, innerViewRef.current, childViewRef.current]);

  return (
    <View
      ref={containerRef}
      style={{ flex: 1, backgroundColor: 'slateblue', overflow: 'hidden' }}
      removeClippedSubviews={false}>
      <View ref={innerViewRef} removeClippedSubviews style={{ height: '100%' }}>
        <View ref={childViewRef} style={{ backgroundColor: 'pink', width: '100%', height: 50 }} removeClippedSubviews={false}>
          {secondVisible && (<Button title='Hide second' onPress={() => secondVisible(false)} />)}
        </View>
      </View>
    </View>
  );
}

function ParentFlatlist(props: Partial<FlatListProps<number>>) {
  return (
    <FlatList
      data={Array.from({ length: 30 }).fill(0) as number[]}
      renderItem={({ index }) => {
        if (index === 10) {
          return <NestedFlatlist key={index} />;
        } else if (index === 15) {
          return <ExtraNestedFlatlist key={index} />;
        } else if (index === 20) {
          return <NestedFlatlist key={index} horizontal />;
        } else if (index === 25) {
          return <ExtraNestedFlatlist key={index} horizontal />;
        } else {
          return <Item key={index}>List item {index + 1}</Item>;
        }
      }}
      {...props}
    />
  );
}

function NestedFlatlist(props: Partial<FlatListProps<number>>) {
  return (
    <FlatList
      style={[styles.nestedList, props.style]}
      data={Array.from({ length: 10 }).fill(0) as number[]}
      renderItem={({ index }) => (
        <Item key={'nested' + index}>Nested list item {index + 1}</Item>
      )}
      {...props}
    />
  );
}

function ExtraNestedFlatlist(props: Partial<FlatListProps<number>>) {
  return (
    <FlatList
      style={styles.nestedList}
      data={Array.from({ length: 10 }).fill(0) as number[]}
      renderItem={({ index }) =>
        index === 4 ? (
          <NestedFlatlist key={index} style={{ backgroundColor: '#d24729' }} />
        ) : (
          <Item key={'nested' + index}>Nested list item {index + 1}</Item>
        )
      }
      {...props}
    />
  );
}

const Stack = createNativeStackNavigator();

export default function App(): React.JSX.Element {
  return (
    <NavigationContainer>
      <Stack.Navigator screenOptions={{ animation: 'slide_from_right' }}>
        <Stack.Screen name="Home" component={Home} />
        <Stack.Screen name="List" component={ListScreenSimplified}/>
      </Stack.Navigator>
    </NavigationContainer>
  );
}

export function AppSimple(): React.JSX.Element {
  const [secondVisible, setSecondVisible] = React.useState(false);

  return (
    <View style={{ flex: 1, backgroundColor: 'lightsalmon' }}>
      {!secondVisible && (
        <View style={{ flex: 1, backgroundColor: 'lightblue' }} >
          <Button title='Show second' onPress={() => setSecondVisible(true)} />
        </View>
      )}
      {secondVisible && (
        <ListScreenSimplified secondVisible={setSecondVisible} />
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  nestedList: {
    backgroundColor: '#FFA07A',
  },
  item: {
    flexDirection: 'row',
    alignItems: 'center',
    padding: 10,
    gap: 10,
  },
  text: {
    fontSize: 24,
    fontWeight: 'bold',
    color: 'black',
  },
  image: {
    width: 50,
    height: 50,
  },
});

```

</details>

Explanation (copied from [here](https://github.com/software-mansion/react-native-screens/pull/2495#issuecomment-2478915818)):

I've debugged this for a while now & I have good understanding of what's going on. This bug is caused by our usage of `startViewTransition` and its implications. We use it well, however React does not account for case that some view might be in transition. Error mechanism is as follows:

1. Let's have initially simple stack with two screens: "A, B". This is component rendered under "B":

```javascript
    <View //<-- ContainerView (CV)
      removeClippedSubviews={false}
      style={{ flex: 1, backgroundColor: 'slateblue', overflow: 'hidden' }}>
      <View removeClippedSubviews style={{ height: '100%' }}> // <--- IntermediateView (IV)
        <View removeClippedSubviews={false} style={{ backgroundColor: 'pink', width: '100%', height: 50 }} /> // <--- ChildView (ChV)
      </View>
    </View>
```

2. We press the back button.
3. We're on Fabric, therefore subtree of B gets destroyed before B itself is unmounted -> in our commit hook we detect that the screen B will be unmounted & we mark every node under B as transitioning by calling `startViewTransition`.
4. React Mounting stage starts, view hierarchy is disassembled in bottom-up fashion (leafs first).
5. ReactViewGroupManager receives MountItem to detach ChV from IV.
6. A call to [`IV.removeView(ChV)` is made](https://github.com/facebook/react-native/blob/9c11d7ca68c5c62ab7bab9919161d8417e96b28b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactClippingViewManager.kt#L58-L73), which effectively removes ChV from `IV.children`, ***HOWEVER*** it does not clear `ChV.parent`, meaning that after the call, `ChV.parent == IV`. This happens, due to view being marked as in-transition by our call to `startViewTransition`. If the view is not marked as in-transition this parent-child relationship is removed.
7. IV has `removeClippedSubviews` enabled, therefore a [call to `IV.removeViewWithSubviewsClippingEnabled(ChV)` is made](https://github.com/facebook/react-native/blob/9c11d7ca68c5c62ab7bab9919161d8417e96b28b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactClippingViewManager.kt#L68). [This function](https://github.com/facebook/react-native/blob/9c11d7ca68c5c62ab7bab9919161d8417e96b28b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java#L726-L744) does effectively two things:
    1. if the ChV has parent (interpretation: it has not yet been detached from parent), we compute it's index in `IV.children` (Android.ViewGroup's state) and remove it from the array,
    2. remove the ChV from `mAllChildren` array (this is state maintained by ReactViewGroup for purposes of implementing the "subview clipping" mechanism".

The crash happens in 7.1, because ChV has been removed from `IV.children` in step 6, but the parent-child relationship has not been broken up there. Under usual circumstances (this is my hypothesis now, yet unconfirmed) 7.1 does not execute, because `ChV.parent` is nulled in step no. 6.

Transitions. On Fabric, when some subtree is unmounted, views in the subtree are unmounted in bottom-up order. This leads to uncomfortable situation, where our components (react-native-screens), who want to drive & manage transitions are notified that their children will be removed after the subtrees mounted in screen subviews are already disassembled. **If we start animation in this very moment we will have staggering effect of white flash** [(issue)](https://github.com/software-mansion/react-native-screens/issues/1685) (we animate just the screen with white background without it's children). This was not a problem on Paper, because the order of subtree disassembling was opposite - top-down. While we've managed to workaround this issue on Fabric using `MountingTransactionObserving` protocol on iOS and a commit hook on Android (we can inspect mutations in incoming transaction before it starts being applied) we still need to prevent view hierarchy from being disassembled in the middle of transition (on Paper this has also been less of an issue) - and this is where `startViewTransition` comes in. It allows us to draw views throughout transition after React Native removes them from HostTree model. On iOS we exchange subtree for its snapshot for transition time, however this approach isn't feasible on Android, because [snapshots do not capture shadows](https://stackoverflow.com/questions/42212600/android-screenshot-of-view-with-shadow).

[Android does not expose a method to verify whether a view is in transition](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewGroup.java#7162) (it has `package` visibility), therefore we need to retrieve this information with some workaround. I see two posibilities:

* first approach would be to override `startViewTransition` & `endViewTransition` in ReactViewGroup and keep the state on whether the view is transitioning there,
* second possible approach would be as follows: we can check for "transitioning" view by checking whether a view has parent but is not it's parent child (this **should** be reliable),

Having information on whether the view is in transition or not, we can prevent multiple removals of the same view in every call site (currently only in `removeViewAt` if `parent.removeClippingSubviews == true`).

Another option would be to do just as this PR does: having in mind this "transitioning" state we can pass a flag to `removeViewWithSubviewClippingEnabled` and prevent duplicated removal from parent if we already know that this has been requested.

I can also add override of this method:

```java
  /*package*/ void removeViewWithSubviewClippingEnabled(View view) {
    this.removeViewWithSubviewClippingEnabled(view, false);
  }
```

to make this parameter optional.

[ANDROID] [FIXED] - Handle removal of in-transition views.

Pull Request resolved: https://github.com/facebook/react-native/pull/47634

Test Plan: WIP WIP

Reviewed By: javache

Differential Revision: D66539065

Pulled By: tdn120

fbshipit-source-id: cf1add67000ebd1b5dfdb2048461a55deac10b16
2024-12-16 14:23:31 +00:00
Riccardo Cipolleschi 621f13f9a2 Skip hidden folders when looking for third party components (#48182)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48182

Maintainers from SVG reached out because of an edge case they inencountered when generating the ComponentProvider. In their setup, they had a `.git` folder in the repo and the algorithm was spending a lot of time crawling the git folder.

In general, we should avoid crawling hidden folders.

This change fix that.

## Changelog:
[General][Fixed] - Skip hidden folders when looking for third party components.

Reviewed By: javache

Differential Revision: D66959345

fbshipit-source-id: 992a79f3cff22cd6a459e0272c8140bc329888da
2024-12-16 14:19:51 +00:00
Ben HandanyanandRiccardo Cipolleschi b153ec8af7 Enable hermes debugger by configuration type instead of configuration name (#48174)
Summary:
Fixes an [issue](https://github.com/facebook/react-native/issues/48168) where only iOS configurations with "Debug" in the name are configured to use the hermes debugger.

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[IOS] [FIXED] - Enable hermes debugger by configuration type instead of configuration name

Pull Request resolved: https://github.com/facebook/react-native/pull/48174

Test Plan:
Added new test scenarios that all pass:
```
ruby -Itest packages/react-native/scripts/cocoapods/__tests__/utils-test.rb
Loaded suite packages/react-native/scripts/cocoapods/__tests__/utils-test
Started
Finished in 0.336047 seconds.
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
56 tests, 149 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
166.64 tests/s, 443.39 assertions/s
```

In a personal project with the following configurations:
```
project 'ReactNativeProject', {
    'Local' => :debug,
    'Development' => :release,
    'Staging' => :release,
    'Production' => :release,
  }
```
I added the following to my Podfile:
```
installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
        puts "#{config.name} is debug? #{config.type == :debug}"
    end
end
```
To confirm that my logic is correct:
```
Local is debug? true
Development is debug? false
Staging is debug? false
Production is debug? false
```

Reviewed By: robhogan

Differential Revision: D66962860

Pulled By: cipolleschi

fbshipit-source-id: 7bd920e123c9064c8a1b5d45df546ff5d2a7d8be
2024-12-16 14:18:58 +00:00
Eric RozellandRiccardo Cipolleschi 840382d22f Disable weak event emitter in AttributedString for Mac Catalyst (#48225)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48225

Fixes https://github.com/facebook/react-native/issues/47762

The weak event emitter in AttributedString attributes is causing a serialization error when typing into a TextInput in a Mac Catalyst build. We can resolve this by not putting the event emitters in the attributed string, but this is likely to cause other issues with event handling for nested <Text> components.

## Changelog

[iOS][Fixed] - Workaround for Mac Catalyst TextInput crash due to serialization attempt of WeakEventEmitter

Reviewed By: NickGerleman

Differential Revision: D66664583

fbshipit-source-id: efdfbcb0db4d5e6b9bf7c14f9bbb221faae2d724
2024-12-16 14:16:20 +00:00
Blake Friedman 77b9c55b22 Update Podfile.lock
Changelog: [Internal]
2024-12-10 01:41:19 +00:00
React Native Bot c5b53fdfab Release 0.77.0-rc.2
#publish-packages-to-npm&next
2024-12-09 23:47:06 +00:00
zhongwuzwandBlake Friedman db798c18d6 Fabric: Post RCTInstanceDidLoadBundle notification after bundle loaded (#48082)
Summary:
Fixes https://github.com/facebook/react-native/issues/47949

## Changelog:

[IOS] [FIXED] - Fabric: Post RCTInstanceDidLoadBundle notification after bundle loaded

Pull Request resolved: https://github.com/facebook/react-native/pull/48082

Test Plan: Post RCTInstanceDidLoadBundle notification after bundle loaded

Reviewed By: philIip

Differential Revision: D66754060

Pulled By: cipolleschi

fbshipit-source-id: d30f0ed73e127936082e6f91e137b9b4013c6651
2024-12-09 15:50:24 +00:00
Riccardo CipolleschiandBlake Friedman d4d1788cc5 Exclude mapping generation of core component (#48145)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48145

While writing the docs for 0.77, I found an edge case in the generation of the RCTThirdPartyComponentProvider:
* If the app has the `codegenConfig` field set in the `package.json`
* And it does not have the `ios.componentProvider` field is not provided

Codegen was generating the mapping for the react-native core components. That's not expected as, in that case, it should only generate components that are declared in the app or in libraries.

This change fixes this edge case.

## Changelog:
[Internal] - Exclude mapping generation of core component

Reviewed By: blakef

Differential Revision: D66875080

fbshipit-source-id: 65fe10381729ec7808efec70feacf2a55f0056e9
2024-12-09 15:49:31 +00:00
Tim YungandBlake Friedman a78d74ca20 RN: Backout "Scheduling Animated End Callbacks in Microtask" (#48132)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48132

Backs out D63573322 and D65645981, reverting the change that makes callbacks passed to `animation.start(<callback>)` scheduled for execution in a microtask.

This is being reverted becuase the latency introduced by the current macro and pending micro tasks can introduce visible latency artifacts that diminish the fidelity of animations.

Changelog:
[General][Changed] - Reverts #47503. (~~Callbacks passed to `animation.start(<callback>)` will be scheduled for execution in a microtask. Previously, there were certain scenarios in which the callback could be synchronously executed by `start`.~~)

Reviewed By: javache

Differential Revision: D66852804

fbshipit-source-id: 08434b9876813fe9e8b189b6b467198933843bf0
2024-12-09 15:48:53 +00:00
Nicola CortiandBlake Friedman d81be20084 Fix crash on HeadlessJsTaskService on old architecture (#48124)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48124

Fixes https://github.com/facebook/react-native/issues/47592

The logic in HeadlessJsTaskService is broken. We should not check whether `getReactContext` is null or not.
Instead we should use the `enableBridgelessArchitecture` feature flag to understand if New Architecture was enabled or not.

The problem we were having is that `HeadlessJsTaskService` was attempting to load the New Architecture even if the user would have it turned off. The Service would then die attempting to load `libappmodules.so` which was correctly missing.

Changelog:
[Android] [Fixed] - Fix crash on HeadlessJsTaskService on old architecture

Reviewed By: javache

Differential Revision: D66826271

fbshipit-source-id: 2b8418e0b01b65014cdbfd0ec2f843420a15f9db
2024-12-09 15:48:20 +00:00
Nicola CortiandBlake Friedman 6cde3df040 Re-introduce the deprecated constructor on ReactModuleInfo (#48090)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48090

This alleviates a breaking change on `ReactModuleInfo` constructor.
While the ctor was deprecated, we realized that there are more than 250 usages in OSS.
We'll need to properly communicate this removal before we do it.

Changelog:
[Android] [Fixed] - Re-introduce the deprecated constructor on ReactModuleInfo

Reviewed By: cipolleschi

Differential Revision: D66755541

fbshipit-source-id: 3673d8f2af278d55491cea89f1594d368513e3d8
2024-12-09 15:46:44 +00:00
zhongwuzwandBlake Friedman 19bff65415 Fabric: Fixes insets not adjust when keyboard disappear (#47924)
Summary:
Fixes https://github.com/facebook/react-native/issues/47731 .

## Changelog:

[IOS] [FIXED] - Fabric: Fixes insets not adjust when keyboard disappear

Pull Request resolved: https://github.com/facebook/react-native/pull/47924

Test Plan: Demo in https://github.com/facebook/react-native/issues/47731

Reviewed By: blakef

Differential Revision: D66651865

Pulled By: cipolleschi

fbshipit-source-id: a75afbd1a7651f0c77022d913f910821c482fcf7
2024-12-09 15:46:39 +00:00
Riccardo CipolleschiandBlake Friedman 88c5b4f8ed Do not install CMake on Windows machine (#48122)
Summary:
GHA to build HermesC for windows are failing because the machines comes with a different CMake version already.
Let's try not to install Cmake and use the one provided by the machine.

## Changelog:
[Internal] -

Pull Request resolved: https://github.com/facebook/react-native/pull/48122

Test Plan: GHA {F1973187648}

Reviewed By: alanleedev

Differential Revision: D66825216

Pulled By: cipolleschi

fbshipit-source-id: 9a9376a5409e192195a6b6cc25b4d58cb47f15da
2024-12-06 10:45:46 +00:00
Rob Hogan 1c3160c7d0 Update Podfile.lock 2024-12-04 10:59:58 +00:00
React Native Bot b19898e259 Release 0.77.0-rc.1
#publish-packages-to-npm&next
2024-12-04 09:51:19 +00:00
Alex HuntandRob Hogan 22072bffaa Update debugger-frontend from b61aae3...6b80704 (#48042)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48042

Changelog: [Internal] - Update `react-native/debugger-frontend` from b61aae3...6b80704

Resyncs `react-native/debugger-frontend` from GitHub - see `rn-chrome-devtools-frontend` [changelog](https://github.com/facebookexperimental/rn-chrome-devtools-frontend/compare/b61aae3ccc6e2684dfbf1e2a06b0f985b459f11f...6b80704fd50ea0bf10f5f5da5a4343de29aff8b2).

Reviewed By: blakef

Differential Revision: D66651149

fbshipit-source-id: 6848eebb4b7c04c7c04ae1f784fc39785945bf7b
2024-12-02 15:45:30 +00:00
Pieter De BaetsandRob Hogan d663fc4397 Restore deprecated TurboReactPackage (#48039)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48039

This class was removed in D66127067 but was marked as DeprecatedInNewArchitecture and not Deprecated, which limited the signal we gave to developers to move away from this.

Restore for now to e

Changelog: [Android][Fixed] Reverted removal of TurboReactPackage

Reviewed By: rshest

Differential Revision: D66648209

fbshipit-source-id: 165f9390b4874e69353612b929d87b0c495588af
2024-12-02 15:45:12 +00:00
Vojtech NovakandRob Hogan 70f6cec5d7 fix IOException in BuildCodegenCLITask (#48008)
Summary:
building RN tester with 0.77 rc-0 doesn't work now because of `java.io.IOException:  No such file or directory` on line 48.

`buildDirectory` is a Gradle property representing a file

https://github.com/facebook/react-native/pull/47552 removes this file altogether so feel free to close if that one is the "right one"

## Changelog:

[ANDROID] [FIXED] - fix IOException in `BuildCodegenCLITask`

Pull Request resolved: https://github.com/facebook/react-native/pull/48008

Test Plan: After this change, building RN tester works.

Reviewed By: cortinico

Differential Revision: D66650038

Pulled By: robhogan

fbshipit-source-id: 11cd83493fa118c6b79d11c9113228dd3971a803
2024-12-02 14:31:10 +00:00
zhongwuzwandRob Hogan b79ec10b78 Fabric: Fixes Modal onRequestClose not called (#48037)
Summary:
Fixes https://github.com/facebook/react-native/issues/48030 .

## Changelog:

[IOS] [FIXED] - Fabric: Fixes Modal onRequestClose not called

Pull Request resolved: https://github.com/facebook/react-native/pull/48037

Test Plan: Repro please see  https://github.com/facebook/react-native/issues/48030.

Reviewed By: cortinico

Differential Revision: D66647232

Pulled By: cipolleschi

fbshipit-source-id: 773517dfe45f6f2e6348cda225e972fbac05edc2
2024-12-02 14:30:45 +00:00
Rob Hogan ee4333812c Fix Animated on JSC: Object.hasOwn -> obj.hasOwnProperty (#48035)
Summary:
https://github.com/facebook/react-native/pull/46385 introduced use of `Object.hasOwn` as an incidental detail of some `Animated` performance improvements.

Unfortunately, `Object.hasOwn` is not present in the version of JSC shipped with Android, nor the built in iOS JSC until iOS 15.4, which is greater than React Native's minimum version (13.4).

Instead:
 - Use `obj.hasOwnProperty(prop)` for known objects that have the `Object` prototype.
 - Otherwise, use `Object.hasOwn` where it is defined.
 - Lastly, fall back to `Object.prototype.hasOwnProperty.call(obj, prop)`, which is compatible with passed `null`-prototype objects.

Fixes https://github.com/facebook/react-native/issues/47963

Intend to pick for RN 0.77.

## Changelog:

[GENERAL][FIXED] Replace Object.hasOwn usages to fix Animated on JSC

Pull Request resolved: https://github.com/facebook/react-native/pull/48035

Test Plan:
- Run `rn-tester` on Android with Hermes disabled.
 - Verify the FlatList->Basic example redboxes before this change, and works after it.

Reviewed By: yungsters

Differential Revision: D66638379

Pulled By: robhogan

fbshipit-source-id: 51ac525851b41adea3bf3cc41349225138e1f2fe
2024-12-02 14:28:14 +00:00
Hugo FOYARTandRob Hogan 9388a7a699 fix: FormData filename in content-disposition (#46543)
Summary:
This Pull Request fixes a regression introduced in https://github.com/facebook/react-native/commit/7c7e9e6571c1f702213e9ffbb40921cd5a1a786b, which adds a `filename*` attribute to the `content-disposition` of a FormData part. However, as the [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#directives) states, there is no `filename*` attribute for the `content-disposition` header in case of a form data.

The `filename*` attribute would break the parsing of form data in the request, such as in frameworks like `Next.js` which uses the web implementation of [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request).

Fixes https://github.com/facebook/react-native/issues/44737

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[General] [Fixed] - Remove non compliant `filename*` attribute in a FormData `content-disposition` header

Pull Request resolved: https://github.com/facebook/react-native/pull/46543

Test Plan:
- Clone the `react-native` repo
- Create a simple JS file that will act as a node server and execute it

```javascript
const http = require('http');

const server = http.createServer(async function (r, res) {
    const req = new Request(new URL(r.url, 'http://localhost:3000'), {
      headers: r.headers,
      method: r.method,
      body: r,
      duplex: 'half',
    });

    const fileData = await req.formData();

    console.log(fileData);
    res.writeHead(200);
    res.end();
});
server.listen(3000);
```

- Go to `packages/rn-tester`
- Add a `useEffect` in `js/RNTesterAppShared.js`

```javascript
React.useEffect(() => {
    const formData = new FormData();
    formData.append('file', {
      uri: 'https://www.gravatar.com/avatar',
      name: '测试photo/1.jpg',
      type: 'image/jpeg',
    });

    fetch('http://localhost:3000', {
      method: 'POST',
      body: formData,
    }).then(res => console.log(res.ok));
  });
```

- Run the app on iOS or Android
- The node server should output the file added to the FormData with an encoded name

Reviewed By: robhogan

Differential Revision: D66643317

Pulled By: yungsters

fbshipit-source-id: 0d531528005025bff303505363671e854c0a2b63
2024-12-02 14:27:17 +00:00
Pieter De BaetsandRob Hogan 8ab87c617e Fix stale reference to ReactViewGroup#mAllChildren (#47950)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47950

`addInArray` may reallocate `mAllChildren` so it's not correct to store this reference.

Changelog: [Internal]

Reviewed By: tdn120

Differential Revision: D66474532

fbshipit-source-id: 90ce2fcbf8ff236501ed47b2acc413e54ef8b82a
2024-12-02 14:26:41 +00:00
Jakub RomanczykandRob Hogan 184da8907e refactor(community-cli-plugin): use node builtin fetch (#47397)
Summary:
Removed `node-fetch` in favour of node builtin fetch to get rid of the deprecated `punycode` warning when using Node 22.

`react-native/community-cli-plugin` already requires Node >= 18 where it was made available by default (without `--experimental-fetch` flag).

This change is similar to the one made in https://github.com/facebook/react-native/pull/45227

## Changelog:

[GENERAL] [CHANGED] - Drop node-fetch in favor of Node's built-in fetch from undici in `react-native/community-cli-plugin`

Pull Request resolved: https://github.com/facebook/react-native/pull/47397

Test Plan: tests pass

Reviewed By: blakef

Differential Revision: D66512595

Pulled By: NickGerleman

fbshipit-source-id: c4e01baf388f9fae8cea7b4bfe25034bff28b461
2024-12-02 14:25:53 +00:00
Rob Hogan abfa3a23dc Update Podfile.lock
Changelog: [Internal]
2024-11-26 16:57:20 +00:00
React Native Bot f6cce65bec Release 0.77.0-rc.0
#publish-packages-to-npm&next
2024-11-26 11:38:45 +00:00
Riccardo CipolleschiandBlake Friedman 385318bf6a [LOCAL] Fix testing script to use debug versions of the Android APK 2024-11-25 19:09:40 +00:00
Rob Hogan 4cffff35e0 [LOCAL] Update Hermes 2024-11-25 14:02:19 +00:00
80 changed files with 661 additions and 519 deletions
@@ -43,9 +43,6 @@ runs:
shell: powershell
run: |
if (-not(Test-Path -Path $Env:HERMES_WS_DIR\win64-bin\hermesc.exe)) {
choco install --no-progress cmake --version 3.14.7 --allow-downgrade
if (-not $?) { throw "Failed to install CMake" }
cd $Env:HERMES_WS_DIR\icu
# If Invoke-WebRequest shows a progress bar, it will fail with
# Win32 internal error "Access is denied" 0x5 occurred [...]
+9 -4
View File
@@ -25,6 +25,10 @@ inputs:
required: false
default: "."
description: The directory from which metro should be started
architecture:
required: false
default: "NewArch"
description: The react native architecture to test
runs:
using: composite
@@ -52,6 +56,7 @@ runs:
if: ${{ inputs.flavor == 'debug' }}
run: ./packages/react-native-codegen/scripts/oss/build.sh
- name: Run e2e tests
id: run-tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 24
@@ -69,16 +74,16 @@ runs:
NORM_APP_ID=$(echo "${{ inputs.app-id }}" | tr '.' '-')
echo "app-id=$NORM_APP_ID" >> $GITHUB_OUTPUT
- name: Store tests result
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4.3.4
if: always()
with:
name: e2e_android_${{ steps.normalize-app-id.outputs.app-id }}_report_${{ inputs.jsengine }}_${{ inputs.flavor }}
name: e2e_android_${{ steps.normalize-app-id.outputs.app-id }}_report_${{ inputs.jsengine }}_${{ inputs.flavor }}_${{ inputs.architecture }}
path: |
report.xml
screen.mp4
- name: Store Logs
if: failure() && steps.run-tests.outcome == 'failure'
if: steps.run-tests.outcome == 'failure'
uses: actions/upload-artifact@v4.3.4
with:
name: maestro-logs-android-${{ steps.normalize-app-id.outputs.app-id }}-${{ inputs.jsengine }}-${{ inputs.flavor }}
name: maestro-logs-android-${{ steps.normalize-app-id.outputs.app-id }}-${{ inputs.jsengine }}-${{ inputs.flavor }}-${{ inputs.architecture }}
path: /tmp/MaestroLogs
+6 -4
View File
@@ -21,6 +21,10 @@ inputs:
required: false
default: "."
description: The directory from which metro should be started
architecture:
required: false
default: "NewArch"
description: The react native architecture to test
runs:
using: composite
@@ -86,8 +90,6 @@ runs:
CURR_ATTEMPT=$((CURR_ATTEMPT+1))
echo "Attempt number $CURR_ATTEMPT"
echo "Start video record using pid: video_record_${{ inputs.jsengine }}_$CURR_ATTEMPT.pid"
xcrun simctl io booted recordVideo video_record_$CURR_ATTEMPT.mov & echo $! > video_record_${{ inputs.jsengine }}_$CURR_ATTEMPT.pid
@@ -105,7 +107,7 @@ runs:
if: always()
uses: actions/upload-artifact@v4.3.4
with:
name: e2e_ios_${{ inputs.app-id }}_report_${{ inputs.jsengine }}_${{ inputs.flavor }}
name: e2e_ios_${{ inputs.app-id }}_report_${{ inputs.jsengine }}_${{ inputs.flavor }}_${{ inputs.architecture }}
path: |
video_record_1.mov
video_record_2.mov
@@ -117,5 +119,5 @@ runs:
if: failure() && steps.run-tests.outcome == 'failure'
uses: actions/upload-artifact@v4.3.4
with:
name: maestro-logs-${{ inputs.app-id }}-${{ inputs.jsengine }}-${{ inputs.flavor }}
name: maestro-logs-${{ inputs.app-id }}-${{ inputs.jsengine }}-${{ inputs.flavor }}-${{ inputs.architecture }}
path: /tmp/MaestroLogs
+3 -2
View File
@@ -55,6 +55,7 @@ async function main() {
stdio: 'ignore',
detached: true,
});
metroProcess.unref();
console.info(`- Metro PID: ${metroProcess.pid}`);
}
@@ -88,15 +89,15 @@ async function main() {
if (IS_DEBUG && metroProcess != null) {
const pid = metroProcess.pid;
console.info(`Kill Metro. PID: ${pid}`);
process.kill(-pid);
process.kill(pid);
console.info(`Metro Killed`);
process.exit();
}
}
if (error) {
throw error;
}
process.exit();
}
function sleep(ms) {
+15 -1
View File
@@ -233,6 +233,7 @@ jobs:
matrix:
jsengine: [Hermes, JSC]
flavor: [Debug, Release]
architecture: [OldArch, NewArch]
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -277,7 +278,12 @@ jobs:
cd /tmp/RNTestProject/ios
bundle install
HERMES_ENGINE_TARBALL_PATH=$HERMES_PATH bundle exec pod install
NEW_ARCH_ENABLED=1
if [[ ${{ matrix.architecture }} == "OldArch" ]]; then
echo "Disable the New Architecture"
NEW_ARCH_ENABLED=0
fi
HERMES_ENGINE_TARBALL_PATH=$HERMES_PATH RCT_NEW_ARCH_ENABLED=$NEW_ARCH_ENABLED bundle exec pod install
xcodebuild \
-scheme "RNTestProject" \
@@ -295,6 +301,7 @@ jobs:
maestro-flow: ./scripts/e2e/.maestro/
flavor: ${{ matrix.flavor }}
working-directory: /tmp/RNTestProject
architecture: ${{ matrix.architecture }}
test_e2e_android_templateapp:
if: ${{ github.ref == 'refs/heads/main' || contains(github.ref, 'stable') || inputs.run-e2e-tests }}
@@ -306,6 +313,7 @@ jobs:
matrix:
jsengine: [Hermes, JSC]
flavor: [debug, release]
architecture: [OldArch, NewArch]
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -351,6 +359,11 @@ jobs:
cd /tmp/RNTestProject
echo "react.internal.mavenLocalRepo=$MAVEN_LOCAL" >> android/gradle.properties
if [[ ${{matrix.architecture}} == "OldArch" ]]; then
echo "Disabling the New Architecture"
sed -i 's/newArchEnabled=true/newArchEnabled=false/' android/gradle.properties
fi
# Build
cd android
CAPITALIZED_FLAVOR=$(echo "${{ matrix.flavor }}" | awk '{print toupper(substr($0, 1, 1)) substr($0, 2)}')
@@ -366,6 +379,7 @@ jobs:
install-java: 'false'
flavor: ${{ matrix.flavor }}
working-directory: /tmp/RNTestProject
architecture: ${{ matrix.architecture }}
build_hermesc_linux:
runs-on: ubuntu-latest
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
+2 -2
View File
@@ -48,8 +48,8 @@
"@babel/preset-flow": "^7.24.7",
"@definitelytyped/dtslint": "^0.0.127",
"@jest/create-cache-key-function": "^29.6.3",
"@react-native/metro-babel-transformer": "0.77.0-main",
"@react-native/metro-config": "0.77.0-main",
"@react-native/metro-babel-transformer": "0.77.0-rc.4",
"@react-native/metro-config": "0.77.0-rc.4",
"@tsconfig/node18": "1.0.1",
"@types/react": "^18.2.6",
"@typescript-eslint/parser": "^7.1.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/assets-registry",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "Asset support code for React Native.",
"license": "MIT",
"repository": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/babel-plugin-codegen",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "Babel plugin to generate native module and view manager code for React Native.",
"license": "MIT",
"repository": {
@@ -26,7 +26,7 @@
],
"dependencies": {
"@babel/traverse": "^7.25.3",
"@react-native/codegen": "0.77.0-main"
"@react-native/codegen": "0.77.0-rc.4"
},
"devDependencies": {
"@babel/core": "^7.25.2"
+3 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/community-cli-plugin",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "Core CLI commands for React Native",
"keywords": [
"react-native",
@@ -22,15 +22,14 @@
"dist"
],
"dependencies": {
"@react-native/dev-middleware": "0.77.0-main",
"@react-native/metro-babel-transformer": "0.77.0-main",
"@react-native/dev-middleware": "0.77.0-rc.4",
"@react-native/metro-babel-transformer": "0.77.0-rc.4",
"chalk": "^4.0.0",
"debug": "^2.2.0",
"invariant": "^2.2.4",
"metro": "^0.81.0",
"metro-config": "^0.81.0",
"metro-core": "^0.81.0",
"node-fetch": "^2.2.0",
"readline": "^1.3.0",
"semver": "^7.1.3"
},
@@ -12,7 +12,6 @@
import type TerminalReporter from 'metro/src/lib/TerminalReporter';
import chalk from 'chalk';
import fetch from 'node-fetch';
type PageDescription = $ReadOnly<{
id: string,
@@ -10,7 +10,6 @@
*/
import net from 'net';
import fetch from 'node-fetch';
/**
* Determine whether we can run the dev server.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/core-cli-utils",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "React Native CLI library for Frameworks to build on",
"license": "MIT",
"main": "./src/index.flow.js",
+3 -3
View File
@@ -1,9 +1,9 @@
@generated SignedSource<<6b92b66e59525cef52902139f863f175>>
Git revision: b61aae3ccc6e2684dfbf1e2a06b0f985b459f11f
@generated SignedSource<<d09e665db66f49ff47c245adf0a4b543>>
Git revision: 6b80704fd50ea0bf10f5f5da5a4343de29aff8b2
Built with --nohooks: false
Is local checkout: false
Remote URL: https://github.com/facebookexperimental/rn-chrome-devtools-frontend
Remote branch: main
Remote branch: 0.77-stable
GN build args (overrides only):
is_official_build = true
Git status in checkout:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -42,7 +42,7 @@ import*as e from"../../ui/legacy/legacy.js";import*as t from"../../core/host/hos
<p>${d(a.docsDebuggingBasicsDetail)}</p>
</div>
</button>
<button class="rn-welcome-docsfeed-item" type="button" role="link" @click=${this.#o.bind(this,"https://reactnative.dev/docs/react-devtools")} title=${d(a.docsReactNativeDevTools)}>
<button class="rn-welcome-docsfeed-item" type="button" role="link" @click=${this.#o.bind(this,"https://reactnative.dev/docs/react-native-devtools")} title=${d(a.docsReactNativeDevTools)}>
<div class="rn-welcome-image" style="background-image: url('${c}')"></div>
<div>
<p class="devtools-link">${d(a.docsReactNativeDevTools)}</p>
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/debugger-frontend",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "Debugger frontend for React Native based on Chrome DevTools",
"keywords": [
"react-native",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/dev-middleware",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "Dev server middleware for React Native",
"keywords": [
"react-native",
@@ -23,7 +23,7 @@
],
"dependencies": {
"@isaacs/ttlcache": "^1.4.1",
"@react-native/debugger-frontend": "0.77.0-main",
"@react-native/debugger-frontend": "0.77.0-rc.4",
"chrome-launcher": "^0.15.2",
"chromium-edge-launcher": "^0.2.0",
"connect": "^3.6.5",
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-config",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "ESLint config for React Native",
"license": "MIT",
"repository": {
@@ -22,7 +22,7 @@
"dependencies": {
"@babel/core": "^7.25.2",
"@babel/eslint-parser": "^7.25.1",
"@react-native/eslint-plugin": "0.77.0-main",
"@react-native/eslint-plugin": "0.77.0-rc.4",
"@typescript-eslint/eslint-plugin": "^7.1.1",
"@typescript-eslint/parser": "^7.1.1",
"eslint-config-prettier": "^8.5.0",
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-plugin",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "ESLint rules for @react-native/eslint-config",
"license": "MIT",
"repository": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-plugin-specs",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "ESLint rules to validate NativeModule and Component Specs",
"license": "MIT",
"repository": {
@@ -26,7 +26,7 @@
"dependencies": {
"@babel/core": "^7.25.2",
"@babel/plugin-transform-flow-strip-types": "^7.25.2",
"@react-native/codegen": "0.77.0-main",
"@react-native/codegen": "0.77.0-rc.4",
"make-dir": "^2.1.0",
"pirates": "^4.0.1",
"source-map-support": "0.5.0"
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/gradle-plugin",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "Gradle Plugin for React Native",
"license": "MIT",
"repository": {
@@ -12,6 +12,7 @@ import com.facebook.react.utils.detectOSAwareHermesCommand
import com.facebook.react.utils.moveTo
import com.facebook.react.utils.windowsAwareCommandLine
import java.io.File
import javax.inject.Inject
import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileTree
import org.gradle.api.file.DirectoryProperty
@@ -19,6 +20,7 @@ import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import org.gradle.process.ExecOperations
abstract class BundleHermesCTask : DefaultTask() {
@@ -26,6 +28,8 @@ abstract class BundleHermesCTask : DefaultTask() {
group = "react"
}
@get:Inject abstract val execOperations: ExecOperations
@get:Internal abstract val root: DirectoryProperty
@get:InputFiles
@@ -127,9 +131,9 @@ abstract class BundleHermesCTask : DefaultTask() {
File(jsIntermediateSourceMapsDir.get().asFile, "$bundleAssetName.compiler.map")
private fun runCommand(command: List<Any>) {
project.exec {
it.workingDir(root.get().asFile)
it.commandLine(command)
execOperations.exec { exec ->
exec.workingDir(root.get().asFile)
exec.commandLine(command)
}
}
@@ -39,7 +39,7 @@ abstract class BuildCodegenCLITask : Exec() {
}
override fun exec() {
val logfile = "${project.layout.buildDirectory}/build-cli.log"
val logfile = "${project.layout.buildDirectory.getAsFile().get()}/build-cli.log"
File(logfile).apply {
parentFile.mkdirs()
if (exists()) {
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "helloworld",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"private": true,
"scripts": {
"bootstrap": "node ./cli.js bootstrap",
@@ -13,16 +13,16 @@
},
"dependencies": {
"react": "18.3.1",
"react-native": "1000.0.0"
"react-native": "0.77.0-rc.4"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.3",
"@babel/runtime": "^7.25.0",
"@react-native/babel-preset": "0.77.0-main",
"@react-native/core-cli-utils": "0.77.0-main",
"@react-native/eslint-config": "0.77.0-main",
"@react-native/metro-config": "0.77.0-main",
"@react-native/babel-preset": "0.77.0-rc.4",
"@react-native/core-cli-utils": "0.77.0-rc.4",
"@react-native/eslint-config": "0.77.0-rc.4",
"@react-native/metro-config": "0.77.0-rc.4",
"chalk": "^4.1.2",
"commander": "^12.0.0",
"eslint": "^8.19.0",
@@ -1,6 +1,6 @@
{
"name": "@react-native/hermes-inspector-msggen",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"private": true,
"description": "Hermes Inspector Message Generator for React Native",
"license": "MIT",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/metro-config",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "Metro configuration for React Native.",
"license": "MIT",
"repository": {
@@ -26,8 +26,8 @@
"dist"
],
"dependencies": {
"@react-native/js-polyfills": "0.77.0-main",
"@react-native/metro-babel-transformer": "0.77.0-main",
"@react-native/js-polyfills": "0.77.0-rc.4",
"@react-native/metro-babel-transformer": "0.77.0-rc.4",
"metro-config": "^0.81.0",
"metro-runtime": "^0.81.0"
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/normalize-colors",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "Color normalization for React Native.",
"license": "MIT",
"repository": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/js-polyfills",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "Polyfills for React Native.",
"license": "MIT",
"repository": {
@@ -1,6 +1,6 @@
{
"name": "@react-native/babel-preset",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "Babel preset for React Native applications",
"main": "src/index.js",
"repository": {
@@ -55,7 +55,7 @@
"@babel/plugin-transform-typescript": "^7.25.2",
"@babel/plugin-transform-unicode-regex": "^7.24.7",
"@babel/template": "^7.25.0",
"@react-native/babel-plugin-codegen": "0.77.0-main",
"@react-native/babel-plugin-codegen": "0.77.0-rc.4",
"babel-plugin-syntax-hermes-parser": "0.25.1",
"babel-plugin-transform-flow-enums": "^0.0.2",
"react-refresh": "^0.14.0"
@@ -1,6 +1,6 @@
{
"name": "@react-native/metro-babel-transformer",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "Babel transformer for React Native applications.",
"main": "src/index.js",
"repository": {
@@ -16,7 +16,7 @@
"license": "MIT",
"dependencies": {
"@babel/core": "^7.25.2",
"@react-native/babel-preset": "0.77.0-main",
"@react-native/babel-preset": "0.77.0-rc.4",
"hermes-parser": "0.25.1",
"nullthrows": "^1.1.1"
},
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@react-native/bots",
"description": "React Native Bots",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"private": true,
"license": "MIT",
"repository": {
@@ -1,6 +1,6 @@
{
"name": "@react-native/codegen-typescript-test",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"private": true,
"description": "TypeScript related unit test for @react-native/codegen",
"license": "MIT",
@@ -19,7 +19,7 @@
"prepare": "yarn run build"
},
"dependencies": {
"@react-native/codegen": "0.77.0-main"
"@react-native/codegen": "0.77.0-rc.4"
},
"devDependencies": {
"@babel/core": "^7.25.2",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/codegen",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "Code generation tools for React Native",
"license": "MIT",
"repository": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "react-native-info",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"main": "build/index.js",
"license": "MIT",
"private": true,
@@ -1,6 +1,6 @@
{
"name": "@react-native/popup-menu-android",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "PopupMenu for the Android platform",
"main": "index.js",
"files": [
@@ -17,7 +17,7 @@
],
"license": "MIT",
"devDependencies": {
"@react-native/codegen": "0.77.0-main"
"@react-native/codegen": "0.77.0-rc.4"
},
"peerDependencies": {
"@types/react": "^18.2.6",
@@ -1,6 +1,6 @@
{
"name": "@react-native/oss-library-example",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"private": true,
"description": "Package that includes native module exapmle, native component example, targets both the old and the new architecture. It should serve as an example of a real-world OSS library.",
"license": "MIT",
@@ -26,8 +26,8 @@
],
"devDependencies": {
"@babel/core": "^7.25.2",
"@react-native/babel-preset": "0.77.0-main",
"react-native": "1000.0.0"
"@react-native/babel-preset": "0.77.0-rc.4",
"react-native": "0.77.0-rc.4"
},
"peerDependencies": {
"react": "*",
@@ -1,7 +1,7 @@
{
"name": "@react-native/test-renderer",
"private": true,
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "A Test rendering library for React Native",
"license": "MIT",
"devDependencies": {
@@ -106,17 +106,17 @@ export function allowTransformProp(prop: string): void {
}
export function isSupportedColorStyleProp(prop: string): boolean {
return Object.hasOwn(SUPPORTED_COLOR_STYLES, prop);
return SUPPORTED_COLOR_STYLES.hasOwnProperty(prop);
}
export function isSupportedInterpolationParam(param: string): boolean {
return Object.hasOwn(SUPPORTED_INTERPOLATION_PARAMS, param);
return SUPPORTED_INTERPOLATION_PARAMS.hasOwnProperty(param);
}
export function isSupportedStyleProp(prop: string): boolean {
return Object.hasOwn(SUPPORTED_STYLES, prop);
return SUPPORTED_STYLES.hasOwnProperty(prop);
}
export function isSupportedTransformProp(prop: string): boolean {
return Object.hasOwn(SUPPORTED_TRANSFORMS, prop);
return SUPPORTED_TRANSFORMS.hasOwnProperty(prop);
}
@@ -121,12 +121,10 @@ describe('Animated', () => {
await unmount(root);
expect(callback).not.toBeCalled();
await jest.runOnlyPendingTimersAsync();
expect(callback).toBeCalledWith({finished: false});
});
it('triggers callback when spring is at rest', async () => {
it('triggers callback when spring is at rest', () => {
const anim = new Animated.Value(0);
const callback = jest.fn();
Animated.spring(anim, {
@@ -134,10 +132,7 @@ describe('Animated', () => {
velocity: 0,
useNativeDriver: false,
}).start(callback);
expect(callback).not.toBeCalled();
await jest.runOnlyPendingTimersAsync();
expect(callback).toBeCalledWith({finished: true});
expect(callback).toBeCalled();
});
it('send toValue when a critically damped spring stops', () => {
@@ -165,7 +165,7 @@ export default class Animation {
const callback = this.#onEnd;
if (callback != null) {
this.#onEnd = null;
queueMicrotask(() => callback(result));
callback(result);
}
}
}
@@ -37,7 +37,7 @@ function createAnimatedProps(
const key = keys[ii];
const value = inputProps[key];
if (allowlist == null || Object.hasOwn(allowlist, key)) {
if (allowlist == null || hasOwn(allowlist, key)) {
let node;
if (key === 'style') {
node = AnimatedStyle.from(value, allowlist?.style);
@@ -271,3 +271,11 @@ export default class AnimatedProps extends AnimatedNode {
};
}
}
// Supported versions of JSC do not implement the newer Object.hasOwn. Remove
// this shim when they do.
// $FlowIgnore[method-unbinding]
const _hasOwnProp = Object.prototype.hasOwnProperty;
const hasOwn: (obj: $ReadOnly<{...}>, prop: string) => boolean =
// $FlowIgnore[method-unbinding]
Object.hasOwn ?? ((obj, prop) => _hasOwnProp.call(obj, prop));
@@ -35,7 +35,7 @@ function createAnimatedStyle(
const key = keys[ii];
const value = inputStyle[key];
if (allowlist == null || Object.hasOwn(allowlist, key)) {
if (allowlist == null || hasOwn(allowlist, key)) {
let node;
if (value != null && key === 'transform') {
node = ReactNativeFeatureFlags.shouldUseAnimatedObjectForTransform()
@@ -241,3 +241,11 @@ export default class AnimatedStyle extends AnimatedWithChildren {
};
}
}
// Supported versions of JSC do not implement the newer Object.hasOwn. Remove
// this shim when they do.
// $FlowIgnore[method-unbinding]
const _hasOwnProp = Object.prototype.hasOwnProperty;
const hasOwn: (obj: $ReadOnly<{...}>, prop: string) => boolean =
// $FlowIgnore[method-unbinding]
Object.hasOwn ?? ((obj, prop) => _hasOwnProp.call(obj, prop));
+3 -3
View File
@@ -14,10 +14,10 @@ const version: $ReadOnly<{
patch: number,
prerelease: string | null,
}> = {
major: 1000,
minor: 0,
major: 0,
minor: 77,
patch: 0,
prerelease: null,
prerelease: 'rc.4',
};
module.exports = {version};
+11 -3
View File
@@ -28,6 +28,15 @@ type FormDataPart =
...
};
/**
* Encode a FormData filename compliant with RFC 2183
*
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#directives
*/
function encodeFilename(filename: string): string {
return encodeURIComponent(filename.replace(/\//g, '_'));
}
/**
* Polyfill for XMLHttpRequest2 FormData API, allowing multipart POST requests
* with mixed data (string, native files) to be submitted via XMLHttpRequest.
@@ -82,9 +91,8 @@ class FormData {
// content type (cf. web Blob interface.)
if (typeof value === 'object' && !Array.isArray(value) && value) {
if (typeof value.name === 'string') {
headers['content-disposition'] += `; filename="${
value.name
}"; filename*=utf-8''${encodeURI(value.name)}`;
headers['content-disposition'] +=
`; filename="${encodeFilename(value.name)}"`;
}
if (typeof value.type === 'string') {
headers['content-type'] = value.type;
@@ -48,8 +48,7 @@ describe('FormData', function () {
type: 'image/jpeg',
name: 'photo.jpg',
headers: {
'content-disposition':
'form-data; name="photo"; filename="photo.jpg"; filename*=utf-8\'\'photo.jpg',
'content-disposition': 'form-data; name="photo"; filename="photo.jpg"',
'content-type': 'image/jpeg',
},
fieldName: 'photo',
@@ -70,7 +69,7 @@ describe('FormData', function () {
name: '测试photo.jpg',
headers: {
'content-disposition':
'form-data; name="photo"; filename="测试photo.jpg"; filename*=utf-8\'\'%E6%B5%8B%E8%AF%95photo.jpg',
'form-data; name="photo"; filename="%E6%B5%8B%E8%AF%95photo.jpg"',
'content-type': 'image/jpeg',
},
fieldName: 'photo',
@@ -21,10 +21,10 @@ NSDictionary* RCTGetReactNativeVersion(void)
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^(void){
__rnVersion = @{
RCTVersionMajor: @(1000),
RCTVersionMinor: @(0),
RCTVersionMajor: @(0),
RCTVersionMinor: @(77),
RCTVersionPatch: @(0),
RCTVersionPrerelease: [NSNull null],
RCTVersionPrerelease: @"rc.4",
};
});
return __rnVersion;
@@ -10,7 +10,7 @@
/**
* UIView class for root <ModalHostView> component.
*/
@interface RCTModalHostViewComponentView : RCTViewComponentView
@interface RCTModalHostViewComponentView : RCTViewComponentView <UIAdaptivePresentationControllerDelegate>
/**
* Subclasses may override this method and present the modal on different view controller.
@@ -149,6 +149,8 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
{
BOOL shouldBePresented = !_isPresented && _shouldPresent && self.window;
if (shouldBePresented) {
self.viewController.presentationController.delegate = self;
_isPresented = YES;
[self presentViewController:self.viewController
animated:_shouldAnimatePresentation
@@ -274,6 +276,16 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
[childComponentView removeFromSuperview];
}
#pragma mark - UIAdaptivePresentationControllerDelegate
- (void)presentationControllerDidAttemptToDismiss:(UIPresentationController *)controller
{
auto eventEmitter = [self modalEventEmitter];
if (eventEmitter) {
eventEmitter->onRequestClose({});
}
}
@end
#ifdef __cplusplus
@@ -189,10 +189,11 @@ RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrollView, NSInt
UIEdgeInsets newEdgeInsets = _scrollView.contentInset;
CGFloat inset = MAX(scrollViewLowerY - keyboardEndFrame.origin.y, 0);
const auto &props = static_cast<const ScrollViewProps &>(*_props);
if (isInverted) {
newEdgeInsets.top = MAX(inset, _scrollView.contentInset.top);
newEdgeInsets.top = MAX(inset, props.contentInset.top);
} else {
newEdgeInsets.bottom = MAX(inset, _scrollView.contentInset.bottom);
newEdgeInsets.bottom = MAX(inset, props.contentInset.bottom);
}
CGPoint newContentOffset = _scrollView.contentOffset;
@@ -210,12 +211,6 @@ RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrollView, NSInt
contentDiff = keyboardEndFrame.origin.y - keyboardBeginFrame.origin.y;
}
} else {
CGRect viewIntersection = CGRectIntersection(self.firstResponderFocus, keyboardEndFrame);
if (CGRectIsNull(viewIntersection)) {
return;
}
// Inner text field focused
CGFloat focusEnd = CGRectGetMaxY(self.firstResponderFocus);
if (focusEnd > keyboardEndFrame.origin.y) {
@@ -247,7 +242,7 @@ RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrollView, NSInt
animations:^{
self->_scrollView.contentInset = newEdgeInsets;
self->_scrollView.verticalScrollIndicatorInsets = newEdgeInsets;
[self scrollToOffset:newContentOffset animated:NO];
[self scrollTo:newContentOffset.x y:newContentOffset.y animated:NO];
}
completion:nil];
}
@@ -99,9 +99,11 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
NSMutableDictionary<NSAttributedStringKey, id> *defaultAttributes =
[_backedTextInputView.defaultTextAttributes mutableCopy];
#if !TARGET_OS_MACCATALYST
RCTWeakEventEmitterWrapper *eventEmitterWrapper = [RCTWeakEventEmitterWrapper new];
eventEmitterWrapper.eventEmitter = _eventEmitter;
defaultAttributes[RCTAttributedStringEventEmitterKey] = eventEmitterWrapper;
#endif
_backedTextInputView.defaultTextAttributes = defaultAttributes;
}
@@ -262,8 +264,10 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
if (newTextInputProps.textAttributes != oldTextInputProps.textAttributes) {
NSMutableDictionary<NSAttributedStringKey, id> *defaultAttributes =
RCTNSTextAttributesFromTextAttributes(newTextInputProps.getEffectiveTextAttributes(RCTFontSizeMultiplier()));
#if !TARGET_OS_MACCATALYST
defaultAttributes[RCTAttributedStringEventEmitterKey] =
_backedTextInputView.defaultTextAttributes[RCTAttributedStringEventEmitterKey];
#endif
_backedTextInputView.defaultTextAttributes = defaultAttributes;
}
@@ -426,6 +426,10 @@ public abstract interface class com/facebook/react/ReactRootView$ReactRootViewEv
public abstract fun onAttachedToReactInstance (Lcom/facebook/react/ReactRootView;)V
}
public abstract class com/facebook/react/TurboReactPackage : com/facebook/react/BaseReactPackage {
public fun <init> ()V
}
public abstract interface class com/facebook/react/ViewManagerOnDemandReactPackage {
public abstract fun createViewManager (Lcom/facebook/react/bridge/ReactApplicationContext;Ljava/lang/String;)Lcom/facebook/react/uimanager/ViewManager;
public abstract fun getViewManagerNames (Lcom/facebook/react/bridge/ReactApplicationContext;)Ljava/util/Collection;
@@ -2959,6 +2963,7 @@ public abstract interface annotation class com/facebook/react/module/annotations
public final class com/facebook/react/module/model/ReactModuleInfo {
public static final field Companion Lcom/facebook/react/module/model/ReactModuleInfo$Companion;
public fun <init> (Ljava/lang/String;Ljava/lang/String;ZZZZ)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;ZZZZZ)V
public final fun canOverrideExistingModule ()Z
public static final fun classIsTurboModule (Ljava/lang/Class;)Z
public final fun className ()Ljava/lang/String;
@@ -7831,6 +7836,7 @@ public class com/facebook/react/views/view/ReactViewGroup : android/view/ViewGro
protected fun dispatchSetPressed (Z)V
public fun draw (Landroid/graphics/Canvas;)V
protected fun drawChild (Landroid/graphics/Canvas;Landroid/view/View;J)Z
public fun endViewTransition (Landroid/view/View;)V
protected fun getChildDrawingOrder (II)I
public fun getClippingRect (Landroid/graphics/Rect;)V
public fun getHitSlopRect ()Landroid/graphics/Rect;
@@ -29,12 +29,7 @@
#include <DefaultComponentsRegistry.h>
#include <DefaultTurboModuleManagerDelegate.h>
#if __has_include("<autolinking.h>")
#define AUTOLINKING_AVAILABLE 1
#include <autolinking.h>
#else
#define AUTOLINKING_AVAILABLE 0
#endif
#include <fbjni/fbjni.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <rncore.h>
@@ -61,10 +56,8 @@ void registerComponents(
REACT_NATIVE_APP_COMPONENT_REGISTRATION(registry);
#endif
#if AUTOLINKING_AVAILABLE
// And we fallback to the components autolinked
autolinking_registerProviders(registry);
#endif
}
std::shared_ptr<TurboModule> cxxModuleProvider(
@@ -78,12 +71,8 @@ std::shared_ptr<TurboModule> cxxModuleProvider(
// return std::make_shared<NativeCxxModuleExample>(jsInvoker);
// }
#if AUTOLINKING_AVAILABLE
// And we fallback to the CXX module providers autolinked
return autolinking_cxxModuleProvider(name, jsInvoker);
#endif
return nullptr;
}
std::shared_ptr<TurboModule> javaModuleProvider(
@@ -112,12 +101,10 @@ std::shared_ptr<TurboModule> javaModuleProvider(
return module;
}
#if AUTOLINKING_AVAILABLE
// And we fallback to the module providers autolinked
if (auto module = autolinking_ModuleProvider(name, params)) {
return module;
}
#endif
return nullptr;
}
@@ -1,4 +1,4 @@
VERSION_NAME=1000.0.0
VERSION_NAME=0.77.0-rc.4
react.internal.publishingGroup=com.facebook.react
android.useAndroidX=true
@@ -179,9 +179,18 @@ public abstract class HeadlessJsTaskService extends Service implements HeadlessJ
}
private void createReactContextAndScheduleTask(final HeadlessJsTaskConfig taskConfig) {
final ReactHost reactHost = getReactHost();
if (reactHost == null) { // old arch
if (ReactNativeFeatureFlags.enableBridgelessArchitecture()) {
final ReactHost reactHost = getReactHost();
reactHost.addReactInstanceEventListener(
new ReactInstanceEventListener() {
@Override
public void onReactContextInitialized(@NonNull ReactContext reactContext) {
invokeStartTask(reactContext, taskConfig);
reactHost.removeReactInstanceEventListener(this);
}
});
reactHost.start();
} else {
final ReactInstanceManager reactInstanceManager =
getReactNativeHost().getReactInstanceManager();
@@ -194,16 +203,6 @@ public abstract class HeadlessJsTaskService extends Service implements HeadlessJ
}
});
reactInstanceManager.createReactContextInBackground();
} else { // new arch
reactHost.addReactInstanceEventListener(
new ReactInstanceEventListener() {
@Override
public void onReactContextInitialized(@NonNull ReactContext reactContext) {
invokeStartTask(reactContext, taskConfig);
reactHost.removeReactInstanceEventListener(this);
}
});
reactHost.start();
}
}
}
@@ -0,0 +1,13 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.react
@Deprecated(
message = "Use BaseReactPackage instead",
replaceWith = ReplaceWith(expression = "BaseReactPackage"))
public abstract class TurboReactPackage : BaseReactPackage() {}
@@ -21,6 +21,24 @@ public class ReactModuleInfo(
public val isCxxModule: Boolean,
public val isTurboModule: Boolean
) {
@Deprecated(
"This constructor is deprecated and will be removed in the future. Use ReactModuleInfo(String, String, boolean, boolean, boolean, boolean)]",
replaceWith =
ReplaceWith(
expression =
"ReactModuleInfo(name, className, canOverrideExistingModule, needsEagerInit, isCxxModule, isTurboModule)"),
level = DeprecationLevel.WARNING)
public constructor(
name: String,
className: String,
canOverrideExistingModule: Boolean,
needsEagerInit: Boolean,
@Suppress("UNUSED_PARAMETER") hasConstants: Boolean,
isCxxModule: Boolean,
isTurboModule: Boolean
) : this(name, className, canOverrideExistingModule, needsEagerInit, isCxxModule, isTurboModule)
public companion object {
/**
* Checks if the passed class is a TurboModule. Useful to populate the parameter [isTurboModule]
@@ -15,8 +15,8 @@ import java.util.Map;
public class ReactNativeVersion {
public static final Map<String, Object> VERSION = MapBuilder.<String, Object>of(
"major", 1000,
"minor", 0,
"major", 0,
"minor", 77,
"patch", 0,
"prerelease", null);
"prerelease", "rc.4");
}
@@ -62,9 +62,6 @@ public abstract class ReactClippingViewManager<T : ReactViewGroup> : ViewGroupMa
if (removeClippedSubviews) {
val child = getChildAt(parent, index)
if (child != null) {
if (child.parent != null) {
parent.removeView(child)
}
parent.removeViewWithSubviewClippingEnabled(child)
}
} else {
@@ -56,6 +56,8 @@ import com.facebook.react.uimanager.style.BorderRadiusProp;
import com.facebook.react.uimanager.style.BorderStyle;
import com.facebook.react.uimanager.style.LogicalEdge;
import com.facebook.react.uimanager.style.Overflow;
import java.util.HashSet;
import java.util.Set;
/**
* Backing for a React View. Has support for borders, but since borders aren't common, lazy
@@ -134,6 +136,7 @@ public class ReactViewGroup extends ViewGroup
private @Nullable ViewGroupDrawingOrderHelper mDrawingOrderHelper;
private float mBackfaceOpacity;
private String mBackfaceVisibility;
private @Nullable Set<Integer> mChildrenRemovedWhileTransitioning;
/**
* Creates a new `ReactViewGroup` instance.
@@ -167,6 +170,7 @@ public class ReactViewGroup extends ViewGroup
mDrawingOrderHelper = null;
mBackfaceOpacity = 1.f;
mBackfaceVisibility = "visible";
mChildrenRemovedWhileTransitioning = null;
}
/* package */ void recycleView() {
@@ -349,6 +353,7 @@ public class ReactViewGroup extends ViewGroup
return;
}
mRemoveClippedSubviews = removeClippedSubviews;
mChildrenRemovedWhileTransitioning = null;
if (removeClippedSubviews) {
mClippingRect = new Rect();
ReactClippingViewGroupHelper.calculateClippingRect(this, mClippingRect);
@@ -402,6 +407,26 @@ public class ReactViewGroup extends ViewGroup
updateClippingToRect(mClippingRect);
}
@Override
public void endViewTransition(View view) {
super.endViewTransition(view);
if (mChildrenRemovedWhileTransitioning != null) {
mChildrenRemovedWhileTransitioning.remove(view.getId());
}
}
private void trackChildViewTransition(int childId) {
if (mChildrenRemovedWhileTransitioning == null) {
mChildrenRemovedWhileTransitioning = new HashSet<>();
}
mChildrenRemovedWhileTransitioning.add(childId);
}
private boolean isChildRemovedWhileTransitioning(View child) {
return mChildrenRemovedWhileTransitioning != null
&& mChildrenRemovedWhileTransitioning.contains(child.getId());
}
private void updateClippingToRect(Rect clippingRect) {
Assertions.assertNotNull(mAllChildren);
int clippedSoFar = 0;
@@ -548,6 +573,12 @@ public class ReactViewGroup extends ViewGroup
} else {
setChildrenDrawingOrderEnabled(false);
}
// The parent might not be null in case the child is transitioning.
if (child.getParent() != null) {
trackChildViewTransition(child.getId());
}
super.onViewRemoved(child);
}
@@ -622,11 +653,12 @@ public class ReactViewGroup extends ViewGroup
/*package*/ void addViewWithSubviewClippingEnabled(
final View child, int index, ViewGroup.LayoutParams params) {
Assertions.assertCondition(mRemoveClippedSubviews);
Rect clippingRect = Assertions.assertNotNull(mClippingRect);
View[] childArray = Assertions.assertNotNull(mAllChildren);
addInArray(child, index);
// we add view as "clipped" and then run {@link #updateSubviewClipStatus} to conditionally
// attach it
Rect clippingRect = Assertions.assertNotNull(mClippingRect);
View[] childArray = Assertions.assertNotNull(mAllChildren);
int clippedSoFar = 0;
for (int i = 0; i < index; i++) {
if (isViewClipped(childArray[i])) {
@@ -676,6 +708,7 @@ public class ReactViewGroup extends ViewGroup
}
}
removeViewsInLayout(index - clippedSoFar, 1);
invalidate();
}
removeFromArray(index);
}
@@ -15,10 +15,10 @@
namespace facebook::react {
constexpr struct {
int32_t Major = 1000;
int32_t Minor = 0;
int32_t Major = 0;
int32_t Minor = 77;
int32_t Patch = 0;
std::string_view Prerelease = "";
std::string_view Prerelease = "rc.4";
} ReactNativeVersion;
} // namespace facebook::react
@@ -403,6 +403,7 @@ static NSMutableAttributedString *RCTNSAttributedStringFragmentWithAttributesFro
{
auto nsAttributedStringFragment = RCTNSAttributedStringFragmentFromFragment(fragment, placeholderImage);
#if !TARGET_OS_MACCATALYST
if (fragment.parentShadowView.componentHandle) {
RCTWeakEventEmitterWrapper *eventEmitterWrapper = [RCTWeakEventEmitterWrapper new];
eventEmitterWrapper.eventEmitter = fragment.parentShadowView.eventEmitter;
@@ -413,6 +414,7 @@ static NSMutableAttributedString *RCTNSAttributedStringFragmentWithAttributesFro
[nsAttributedStringFragment addAttributes:additionalTextAttributes
range:NSMakeRange(0, nsAttributedStringFragment.length)];
}
#endif
return nsAttributedStringFragment;
}
@@ -236,47 +236,51 @@ std::string simpleBasename(const std::string& path) {
*/
void ReactInstance::loadScript(
std::unique_ptr<const JSBigString> script,
const std::string& sourceURL) {
const std::string& sourceURL,
std::function<void(jsi::Runtime& runtime)>&& completion) {
auto buffer = std::make_shared<BigStringBuffer>(std::move(script));
std::string scriptName = simpleBasename(sourceURL);
runtimeScheduler_->scheduleWork(
[this,
scriptName,
sourceURL,
buffer = std::move(buffer),
weakBufferedRuntimeExecuter = std::weak_ptr<BufferedRuntimeExecutor>(
bufferedRuntimeExecutor_)](jsi::Runtime& runtime) {
SystraceSection s("ReactInstance::loadScript");
bool hasLogger(ReactMarker::logTaggedMarkerBridgelessImpl);
if (hasLogger) {
ReactMarker::logTaggedMarkerBridgeless(
ReactMarker::RUN_JS_BUNDLE_START, scriptName.c_str());
}
runtimeScheduler_->scheduleWork([this,
scriptName,
sourceURL,
buffer = std::move(buffer),
weakBufferedRuntimeExecuter =
std::weak_ptr<BufferedRuntimeExecutor>(
bufferedRuntimeExecutor_),
completion](jsi::Runtime& runtime) {
SystraceSection s("ReactInstance::loadScript");
bool hasLogger(ReactMarker::logTaggedMarkerBridgelessImpl);
if (hasLogger) {
ReactMarker::logTaggedMarkerBridgeless(
ReactMarker::RUN_JS_BUNDLE_START, scriptName.c_str());
}
runtime.evaluateJavaScript(buffer, sourceURL);
runtime.evaluateJavaScript(buffer, sourceURL);
/**
* TODO(T183610671): We need a safe/reliable way to enable the js
* pipeline from javascript. Remove this after we figure that out, or
* after we just remove the js pipeline.
*/
if (!jsErrorHandler_->hasHandledFatalError()) {
jsErrorHandler_->setRuntimeReady();
}
/**
* TODO(T183610671): We need a safe/reliable way to enable the js
* pipeline from javascript. Remove this after we figure that out, or
* after we just remove the js pipeline.
*/
if (!jsErrorHandler_->hasHandledFatalError()) {
jsErrorHandler_->setRuntimeReady();
}
if (hasLogger) {
ReactMarker::logTaggedMarkerBridgeless(
ReactMarker::RUN_JS_BUNDLE_STOP, scriptName.c_str());
ReactMarker::logMarkerBridgeless(
ReactMarker::INIT_REACT_RUNTIME_STOP);
ReactMarker::logMarkerBridgeless(ReactMarker::APP_STARTUP_STOP);
}
if (auto strongBufferedRuntimeExecuter =
weakBufferedRuntimeExecuter.lock()) {
strongBufferedRuntimeExecuter->flush();
}
});
if (hasLogger) {
ReactMarker::logTaggedMarkerBridgeless(
ReactMarker::RUN_JS_BUNDLE_STOP, scriptName.c_str());
ReactMarker::logMarkerBridgeless(ReactMarker::INIT_REACT_RUNTIME_STOP);
ReactMarker::logMarkerBridgeless(ReactMarker::APP_STARTUP_STOP);
}
if (auto strongBufferedRuntimeExecuter =
weakBufferedRuntimeExecuter.lock()) {
strongBufferedRuntimeExecuter->flush();
}
if (completion) {
completion(runtime);
}
});
}
/*
@@ -49,7 +49,8 @@ class ReactInstance final : private jsinspector_modern::InstanceTargetDelegate {
void loadScript(
std::unique_ptr<const JSBigString> script,
const std::string& sourceURL);
const std::string& sourceURL,
std::function<void(jsi::Runtime& runtime)>&& completion = nullptr);
void registerSegment(uint32_t segmentId, const std::string& segmentPath);
@@ -472,8 +472,9 @@ void RCTInstanceSetRuntimeDiagnosticFlags(NSString *flags)
auto script = std::make_unique<NSDataBigString>(source.data);
const auto *url = deriveSourceURL(source.url).UTF8String;
_reactInstance->loadScript(std::move(script), url);
[[NSNotificationCenter defaultCenter] postNotificationName:@"RCTInstanceDidLoadBundle" object:nil];
_reactInstance->loadScript(std::move(script), url, [](jsi::Runtime &_) {
[[NSNotificationCenter defaultCenter] postNotificationName:@"RCTInstanceDidLoadBundle" object:nil];
});
}
- (void)_handleJSError:(const JsErrorHandler::ParsedError &)error withRuntime:(jsi::Runtime &)runtime
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "react-native",
"version": "1000.0.0",
"version": "0.77.0-rc.4",
"description": "A framework for building native apps using React",
"license": "MIT",
"repository": {
@@ -108,13 +108,13 @@
},
"dependencies": {
"@jest/create-cache-key-function": "^29.6.3",
"@react-native/assets-registry": "0.77.0-main",
"@react-native/codegen": "0.77.0-main",
"@react-native/community-cli-plugin": "0.77.0-main",
"@react-native/gradle-plugin": "0.77.0-main",
"@react-native/js-polyfills": "0.77.0-main",
"@react-native/normalize-colors": "0.77.0-main",
"@react-native/virtualized-lists": "0.77.0-main",
"@react-native/assets-registry": "0.77.0-rc.4",
"@react-native/codegen": "0.77.0-rc.4",
"@react-native/community-cli-plugin": "0.77.0-rc.4",
"@react-native/gradle-plugin": "0.77.0-rc.4",
"@react-native/js-polyfills": "0.77.0-rc.4",
"@react-native/normalize-colors": "0.77.0-rc.4",
"@react-native/virtualized-lists": "0.77.0-rc.4",
"abort-controller": "^3.0.0",
"anser": "^1.4.9",
"ansi-regex": "^5.0.0",
@@ -197,6 +197,10 @@ class BuildConfigurationMock
def debug?
return @is_debug
end
def type
@is_debug ? :debug : :release
end
end
class TargetInstallationResultMock
@@ -185,15 +185,24 @@ class UtilsTests < Test::Unit::TestCase
react_hermes_name = "React-hermes"
react_core_name = "React-Core"
hermes_engine_name = "hermes-engine"
react_hermes_debug_config = BuildConfigurationMock.new("Debug")
react_hermes_release_config = BuildConfigurationMock.new("Release")
react_core_debug_config = BuildConfigurationMock.new("Debug")
react_core_release_config = BuildConfigurationMock.new("Release")
hermes_engine_debug_config = BuildConfigurationMock.new("Debug")
hermes_engine_release_config = BuildConfigurationMock.new("Release")
react_hermes_target = TargetMock.new(react_hermes_name, [react_hermes_debug_config, react_hermes_release_config])
react_core_target = TargetMock.new(react_core_name, [react_core_debug_config, react_core_release_config])
hermes_engine_target = TargetMock.new(hermes_engine_name, [hermes_engine_debug_config, hermes_engine_release_config])
react_hermes_debug_config = BuildConfigurationMock.new("Debug", {}, is_debug: true)
react_hermes_release_config = BuildConfigurationMock.new("Release", {}, is_debug: false)
react_hermes_debug_config_rename = BuildConfigurationMock.new("Development", {}, is_debug: true)
react_hermes_release_config_rename = BuildConfigurationMock.new("Production", {}, is_debug: false)
react_hermes_target = TargetMock.new(react_hermes_name, [react_hermes_debug_config, react_hermes_release_config, react_hermes_debug_config_rename, react_hermes_release_config_rename])
react_core_debug_config = BuildConfigurationMock.new("Debug", {}, is_debug: true)
react_core_release_config = BuildConfigurationMock.new("Release", {}, is_debug: false)
react_core_debug_config_rename = BuildConfigurationMock.new("Development", {}, is_debug: true)
react_core_release_config_rename = BuildConfigurationMock.new("Production", {}, is_debug: false)
react_core_target = TargetMock.new(react_core_name, [react_core_debug_config, react_core_release_config, react_core_debug_config_rename, react_core_release_config_rename])
hermes_engine_debug_config = BuildConfigurationMock.new("Debug", {}, is_debug: true)
hermes_engine_release_config = BuildConfigurationMock.new("Release", {}, is_debug: false)
hermes_engine_debug_config_rename = BuildConfigurationMock.new("Development", {}, is_debug: true)
hermes_engine_release_config_rename = BuildConfigurationMock.new("Production", {}, is_debug: false)
hermes_engine_target = TargetMock.new(hermes_engine_name, [hermes_engine_debug_config, hermes_engine_release_config, hermes_engine_debug_config_rename, hermes_engine_release_config_rename])
installer = InstallerMock.new(
:pod_target_installation_results => {
@@ -211,10 +220,18 @@ class UtilsTests < Test::Unit::TestCase
expected_value = "$(inherited) HERMES_ENABLE_DEBUGGER=1"
assert_equal(expected_value, react_hermes_debug_config.build_settings[build_setting])
assert_nil(react_hermes_release_config.build_settings[build_setting])
assert_equal(expected_value, react_hermes_debug_config_rename.build_settings[build_setting])
assert_nil(react_hermes_release_config_rename.build_settings[build_setting])
assert_nil(react_core_debug_config.build_settings[build_setting])
assert_nil(react_core_release_config.build_settings[build_setting])
assert_nil(react_core_debug_config_rename.build_settings[build_setting])
assert_nil(react_core_release_config_rename.build_settings[build_setting])
assert_equal(expected_value, hermes_engine_debug_config.build_settings[build_setting])
assert_nil(hermes_engine_release_config.build_settings[build_setting])
assert_equal(expected_value, hermes_engine_debug_config_rename.build_settings[build_setting])
assert_nil(hermes_engine_release_config_rename.build_settings[build_setting])
end
# ================= #
@@ -44,10 +44,10 @@ class ReactNativePodsUtils
end
def self.set_gcc_preprocessor_definition_for_React_hermes(installer)
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "React-hermes", "Debug")
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "React-jsinspector", "Debug")
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "hermes-engine", "Debug")
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "React-RuntimeHermes", "Debug")
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "React-hermes", :debug)
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "React-jsinspector", :debug)
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "hermes-engine", :debug)
self.add_build_settings_to_pod(installer, "GCC_PREPROCESSOR_DEFINITIONS", "HERMES_ENABLE_DEBUGGER=1", "React-RuntimeHermes", :debug)
end
def self.turn_off_resource_bundle_react_core(installer)
@@ -193,11 +193,11 @@ class ReactNativePodsUtils
private
def self.add_build_settings_to_pod(installer, settings_name, settings_value, target_pod_name, configuration)
def self.add_build_settings_to_pod(installer, settings_name, settings_value, target_pod_name, configuration_type)
installer.target_installation_results.pod_target_installation_results.each do |pod_name, target_installation_result|
if pod_name.to_s == target_pod_name
target_installation_result.native_target.build_configurations.each do |config|
if configuration == nil || (configuration != nil && config.name.include?(configuration))
if configuration_type == nil || (configuration_type != nil && config.type == configuration_type)
config.build_settings[settings_name] ||= '$(inherited) '
config.build_settings[settings_name] << settings_value
end
@@ -763,6 +763,16 @@ function findFilesWithExtension(filePath, extension) {
const dir = fs.readdirSync(filePath);
dir.forEach(file => {
const absolutePath = path.join(filePath, file);
// Exclude files provided by react-native
if (absolutePath.includes(`${path.sep}react-native${path.sep}`)) {
return null;
}
// Skip hidden folders, that starts with `.`
if (absolutePath.includes(`${path.sep}.`)) {
return null;
}
if (
fs.existsSync(absolutePath) &&
fs.statSync(absolutePath).isDirectory()
@@ -0,0 +1 @@
hermes-2024-11-25-RNv0.77.0-d4f25d534ab744866448b36ca3bf3d97c08e638c
@@ -113,7 +113,7 @@ export function createCompositeKeyForProps(
const key = keys[ii];
const value = props[key];
if (allowlist == null || Object.hasOwn(allowlist, key)) {
if (allowlist == null || hasOwn(allowlist, key)) {
let compositeKeyComponent;
if (key === 'style') {
// $FlowFixMe[incompatible-call] - `style` is a valid argument.
@@ -205,7 +205,7 @@ function createCompositeKeyForObject(
for (let ii = 0, length = keys.length; ii < length; ii++) {
const key = keys[ii];
if (allowlist == null || Object.hasOwn(allowlist, key)) {
if (allowlist == null || hasOwn(allowlist, key)) {
const value = object[key];
let compositeKeyComponent;
@@ -250,7 +250,7 @@ export function areCompositeKeysEqual(
}
for (let ii = 0; ii < length; ii++) {
const key = keys[ii];
if (!Object.hasOwn(next, key)) {
if (!hasOwn(next, key)) {
return false;
}
const prevComponent = prev[key];
@@ -336,7 +336,7 @@ function areCompositeKeyComponentsEqual(
for (let ii = 0; ii < length; ii++) {
const key = keys[ii];
if (
!Object.hasOwn(nullthrows(next), key) ||
!hasOwn(nullthrows(next), key) ||
!areCompositeKeyComponentsEqual(prev[key], next[key])
) {
return false;
@@ -346,3 +346,11 @@ function areCompositeKeyComponentsEqual(
}
return false;
}
// Supported versions of JSC do not implement the newer Object.hasOwn. Remove
// this shim when they do.
// $FlowIgnore[method-unbinding]
const _hasOwnProp = Object.prototype.hasOwnProperty;
const hasOwn: (obj: $ReadOnly<{...}>, prop: string) => boolean =
// $FlowIgnore[method-unbinding]
Object.hasOwn ?? ((obj, prop) => _hasOwnProp.call(obj, prop));
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/tester",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"private": true,
"description": "React Native tester app.",
"license": "MIT",
@@ -27,8 +27,8 @@
"e2e-test-ios": "./scripts/maestro-test-ios.sh"
},
"dependencies": {
"@react-native/oss-library-example": "0.77.0-main",
"@react-native/popup-menu-android": "0.77.0-main",
"@react-native/oss-library-example": "0.77.0-rc.4",
"@react-native/popup-menu-android": "0.77.0-rc.4",
"flow-enums-runtime": "^0.0.6",
"invariant": "^2.2.4",
"nullthrows": "^1.1.1"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/typescript-config",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "Default TypeScript configuration for React Native apps",
"license": "MIT",
"repository": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/virtualized-lists",
"version": "0.77.0-main",
"version": "0.77.0-rc.4",
"description": "Virtualized lists for React Native.",
"license": "MIT",
"repository": {
+1 -1
View File
@@ -164,7 +164,7 @@ async function testRNTesterAndroid(
exec(`unzip ${downloadPath} -d ${unzipFolder}`);
let apkPath = path.join(
unzipFolder,
`app-${argv.hermes === true ? 'hermes' : 'jsc'}-${emulatorArch}-release.apk`,
`app-${argv.hermes === true ? 'hermes' : 'jsc'}-${emulatorArch}-debug.apk`,
);
exec(`adb install ${apkPath}`);