Compare commits

...
Author SHA1 Message Date
React Native Bot 084a723075 Release 0.76.8
#publish-packages-to-npm&0.76-stable
2025-03-26 16:15:05 +00:00
Gabriel Donadel Dall'Agnol bb7f479548 [github] Fix upload-artifact version on maestro android (#50238) 2025-03-25 17:07:45 +01:00
Rubén Norte cea7f4cd0a Avoid errors when dispatching mount operations within mount hooks (#50091)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/50091

Changelog: [internal]

If a library uses mount hooks to perform mount operations, it's possible to get concurrent modifications of the list of pending surface IDs to report.

This fixes that potential error by making a copy of the list before dispatching the mount notifications.

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

Reviewed By: javache

Differential Revision: D71387739

fbshipit-source-id: 96c723ef2d6bcc659c4452434b7a4d5af26117ef
2025-03-24 17:13:10 -03:00
Rubén Norte 14d0dc5ed9 Correctly batch reportMount calls (#50090)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/50090

Changelog: [internal]

I refactored `FabricUIManager` in D54547194 / https://github.com/facebook/react-native/pull/43337 and accidentally removed setting this flag to avoid scheduling redundant tasks in the UI thread to report mount. This fixes it.

Reviewed By: javache

Differential Revision: D71387374

fbshipit-source-id: cad8a3ead2434738325560902cbab817e5d5dde7
2025-03-24 17:13:10 -03:00
Rob Hogan b9c4095e40 community-cli-plugin: resolve cli-server-api via peer dependency on cli (#50095) 2025-03-24 21:12:52 +01:00
Riccardo Cipolleschi 8eec35f134 Convert to JSException only NSException from sync methods (#50193)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/50193

This fix makes sure that we convert to JSException only NSException thrwn by sync methods.
Currently, nothing in the stack will be capable of understanding that js error if it is triggered by an exception raised by an asyc method.

See https://github.com/reactwg/react-native-new-architecture/discussions/276 for further details

We need to cherry pick this in 0.78 and 0.79

## Changelog:
[iOS][Fixed] - Make sure the TM infra does not crash on NSException when triggered by async method

Reviewed By: fabriziocucci

Differential Revision: D71619229

fbshipit-source-id: b87aef5dd2720a2641c8da0904da651866370dc6
2025-03-24 14:15:26 +00:00
Tommy Nguyen ffe7bd1471 fix: fix @react-native-community/cli-platform-* packages not being found in monorepos (#47308)
Summary:
Fix `react-native-community/cli-platform-*` packages not being found in monorepos.

Note that we are making the assumption that `process.cwd()` returns the project root. This is the same assumption that `react-native-community/cli` makes. Specifically, `findProjectRoot()` has an optional argument that defaults to `process.cwd()`:

- [`findProjectRoot()`](https://github.com/react-native-community/cli/blob/14.x/packages/cli-tools/src/findProjectRoot.ts)
- Which gets called without arguments in [`loadConfig()`](https://github.com/react-native-community/cli/blob/14.x/packages/cli-config/src/loadConfig.ts#L89)
- `loadConfig()` gets called from [`setupAndRun()`](https://github.com/react-native-community/cli/blob/14.x/packages/cli/src/index.ts#L196), also without project root set

As far as I can see, the project root argument is only ever used in tests.

## Changelog:

[GENERAL] [FIXED] - Fix `react-native-community/cli-platform-*` packages not being found in monorepos

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

Test Plan:
1. Clone/check out this branch: https://github.com/microsoft/rnx-kit/pull/3409
2. Cherry-pick https://github.com/facebook/react-native/pull/47304
3. Cherry-pick https://github.com/facebook/react-native/pull/47308
4. Run `react-native config` inside `packages/test-app`
5. Verify that `projects` is populated

**Before:**

```js
  "healthChecks": [],
  "platforms": {},
  "assets": [],
  "project": {}
}
```

**After:**

```js
  "healthChecks": [],
  "platforms": {
    "ios": {},
    "android": {}
  },
  "assets": [],
  "project": {
    "ios": {
      "sourceDir": "/~/packages/test-app/ios",
      "xcodeProject": {
        "name": "SampleCrossApp.xcworkspace",
        "path": ".",
        "isWorkspace": true
      },
      "automaticPodsInstallation": false,
      "assets": []
    },
    "android": {
      "sourceDir": "/~/packages/test-app/android",
      "appName": "app",
      "packageName": "com.msft.identity.client.sample.local",
      "applicationId": "com.msft.identity.client.sample.local",
      "mainActivity": "com.microsoft.reacttestapp.MainActivity",
      "assets": []
    }
  }
}
```

Reviewed By: cortinico

Differential Revision: D69465533

Pulled By: robhogan

fbshipit-source-id: 3d6cf32752a7a41d9c7e84f35b0f26ae7d7a971f
2025-03-18 14:19:53 +00:00
Tommy Nguyen 9ba96ad79d fix: fix @react-native-community/cli not being found in pnpm setups (#47304)
Summary:
Fix `react-native-community/cli` not being found in pnpm setups

## Changelog:

[GENERAL] [FIXED] - Fix `react-native-community/cli` not being found in pnpm setups

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

Test Plan:
1. Clone/check out this branch: https://github.com/microsoft/rnx-kit/pull/3409
2. Run `yarn react-native config`

Reviewed By: cortinico

Differential Revision: D65209065

Pulled By: robhogan

fbshipit-source-id: 2ceb73ad140b4afe193e879779c2d8a4b9adf3fc
2025-03-18 14:17:58 +00:00
zhongwuzw e2b081e66f Fixes TextInput crashes when any text is entered while running as iOS app on apple silicon mac (#49320)
Summary:
Fixes https://github.com/facebook/react-native/issues/48544. We can make `RCTWeakEventEmitterWrapper` to subclass `NSDictionary` that Textinput supports encode it.

System allowed classes are:
```
Allowed classes are:
 {(
    "'NSMorphology' (0x20088b6d8) [/System/Library/Frameworks/Foundation.framework]",
    "'NSString' (0x2003f4738) [/System/Library/Frameworks/Foundation.framework]",
    "'NSInflectionRule' (0x20088d348) [/System/Library/Frameworks/Foundation.framework]",
    "'UIColor' (0x2006c0520) [/System/iOSSupport/System/Library/PrivateFrameworks/UIKitCore.framework]",
    "'NSTextAttachment' (0x20042af98) [/System/Library/PrivateFrameworks/UIFoundation.framework]",
    "'NSShadow' (0x20042ae30) [/System/Library/PrivateFrameworks/UIFoundation.framework]",
    "'NSTextEncapsulation' (0x200897e50) [/System/Library/Frameworks/CoreText.framework]",
    "'NSTextAlternatives' (0x20042af70) [/System/Library/PrivateFrameworks/UIFoundation.framework]",
    "'NSFont' (0x20042a9f8) [/System/Library/PrivateFrameworks/UIFoundation.framework]",
    "'NSAttributedString' (0x2003f3838) [/System/Library/Frameworks/Foundation.framework]",
    "'NSData' (0x2003ed528) [/System/Library/Frameworks/CoreFoundation.framework]",
    "'NSURL' (0x2003ed938) [/System/Library/Frameworks/CoreFoundation.framework]",
    "'NSAdaptiveImageGlyph' (0x2008ae538) [/System/Library/PrivateFrameworks/UIFoundation.framework]",
    "'NSNumber' (0x2003f4238) [/System/Library/Frameworks/Foundation.framework]",
    "'NSParagraphStyle' (0x20042ad40) [/System/Library/PrivateFrameworks/UIFoundation.framework]",
    "'NSDictionary' (0x2003ed5a0) [/System/Library/Frameworks/CoreFoundation.framework]",
    "'UIFont' (0x20042c668) [/System/Library/PrivateFrameworks/UIFoundation.framework]",
    "'NSColor' (0x200412350) [/System/Library/Frameworks/AppKit.framework]",
    "'NSGlyphInfo' (0x20042aa98) [/System/Library/PrivateFrameworks/UIFoundation.framework]",
    "'NSArray' (0x2003ed460) [/System/Library/Frameworks/CoreFoundation.framework]",
    "'NSPresentationIntent' (0x20088da28) [/System/Library/Frameworks/Foundation.framework]"
)}
```

## Changelog:

[IOS] [FIXED] -  Fixes TextInput crashes when any text is entered while running as iOS app on apple silicon mac

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

Test Plan:
Run RNTester on apple silicon mac, and entered some text in textinput, no crash occured. Also, verified that the caret was not jumping around, thus preserving the original fix.

https://github.com/user-attachments/assets/6304f6e7-c663-4351-ace8-ab1842ee545f

Reviewed By: javache

Differential Revision: D69981500

Pulled By: cipolleschi

fbshipit-source-id: 2af9b280e42f621446efda9b101af50525e8fef7
2025-03-11 14:24:17 +00:00
Cedric van Putten 68055f2d8a fix(react-native): pass the protocol from bundle URL to HMR client on Android (#48998)
Summary:
This is another attempt at fixing the Android HMR client for HTTPS proxied Metro instances. The previous one unintentionally [caused the following error](https://github.com/facebook/react-native/pull/48970#issuecomment-2617047184):

```
java.lang.AssertionError: Method overloading is unsupported: com.facebook.react.devsupport.HMRClient#setup
```

This PR removes the overloading, and only adds the `scheme` property as a parameter to the existing `.setup` method. Aligning with the exact behavior we have on iOS.

The alternative fix, which should NOT be backward breaking (if this is) - is to move this "infer the protocol from the bundle URL" to the JS side of the HMR client. Where we don't just always default to `http`, but instead default to `https IF port === 443, otherwise http`. It's a bit more hacky, but shouldn't cause any other issues. _**Ideally**_, we have the same working behavior on both Android and iOS without workarounds.

<details><summary>Alternative workaround</summary>

See [this change](https://github.com/facebook/react-native/compare/main...byCedric:react-native:patch-2).

<img width="1179" alt="image" src="https://github.com/user-attachments/assets/47c365bc-6df8-43e6-ad7d-5a667e350cd4" />

</details>

See full explanation on https://github.com/facebook/react-native/issues/48970

> We've noticed that the HMR on Android doesn't seem to be connecting when using a HTTPS-proxied Metro instance, where the proxy is hosted through Cloudflare. This is only an issue on Android - not iOS - and likely caused by the HMR Client not being set up properly on Android.
>
>- On Android, we run `.setup('android', <bundleEntryPath>, <proxiedMetroHost>, <proxiedMetroPort>, <hmrEnabled>)` in the [**react/devsupport/DevSupportManagerBase.java**](https://github.com/facebook/react-native/blob/53d94c3abe3fcd2168b512652bc0169956bffa39/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java#L689-L691) file.
>- On iOS, we run `[self.callableJSModules invokeModule:@"HMRClient" method:@"setup" withArgs:@[ RCTPlatformName, path, host, RCTNullIfNil(port), @(isHotLoadingEnabled), scheme ]];` in the [**React/CoreModules
/RCTDevSettings.mm**](https://github.com/facebook/react-native/blob/53d94c3abe3fcd2168b512652bc0169956bffa39/packages/react-native/React/CoreModules/RCTDevSettings.mm#L488-L491) file.
>
>Notice how Android does not pass in the scheme/protocol of the bundle URL, while iOS actually does? Unfortunately, because the default protocol (`http`) mismatches on Android when using HTTPS proxies, we actually try to connect the HMR client over `http` instead of `https` - while still using port 443 - which is rejected by Cloudflare's infrastructure even before we can redirect or mitigate this issue. And the rejection is valid, as we basically try to connect on `http://<host>:443` (the source URL is `https`, so the port is infered as `443`).
>
>This change adds scheme propagation to Android, exactly like we do on iOS for the HMR Client.

## Changelog:

[ANDROID] [FIXED] Pass the bundle URL protocol when setting up HMR client on Android

<!-- 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

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

Test Plan:
See full explanation on https://github.com/facebook/react-native/issues/48970

> It's a little bit hard to test this out yourself, since you'd need a HTTPS-based proxy and reject HTTP connections for HTTPS/WSS Websocket requests.
>
>You can set this up through:
>- `bun create expo@latest ./test-app`
>- `cd ./test-app`
>- `touch .env`
>- Set `EXPO_PACKAGER_PROXY_URL=https://<proxied-metro-hostname>` in **.env**
>- Set `REACT_NATIVE_PACKAGER_HOSTNAME=<proxied-metro-hostname>` in **.env**
>- `bun run start`
>
>Setting both these envvars, the bundle URL in the manifest is set to `https://...` - which triggers this HMR issue on Android. You can validate the **.env** setup through:
>
>```bash
>curl "http://localhost:8081" -H "expo-platform: android" | jq .launchAsset.url
>```
>
>This should point the entry bundle URL towards the `EXPO_PACKAGER_PROXY_URL`.

Reviewed By: cortinico

Differential Revision: D68768351

Pulled By: javache

fbshipit-source-id: 49bf1dc60f11b2af6e57177141270632d62ab564
2025-03-11 11:48:32 +00:00
Vitali Zaidman 08c04147ba increase ping-pong timeout before killing WS connection to DevTools (#49358)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49358

When the network is under strain, the code responsible for detecting if the inspector proxy's connection to the client has been lost may incorrectly assume the connection is dead. This false positive occurs because the system assumes that if a pong is not received within 5 seconds of a ping, the other side has disconnected. However, I was able to consistently reproduce scenarios where a delay of more than 5 seconds (even more than 20 seconds) was followed by a return to normal ping-pong communication without any issues.

Since I can't think of any issues with increasing this number, I'm increasing it to 60s.

Changelog:
[General][Fixed] - Disconnections of DevTools when the network is under significant strain.

Reviewed By: robhogan, huntie

Differential Revision: D69523906

fbshipit-source-id: 50db1e7bbe690b42421bc226aa30fd6571ba2257
2025-03-11 11:46:44 +00:00
Hugo FOYART 9e846b4d11 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
2025-03-11 11:45:15 +00:00
Nicola Corti ac637ff448 Fix @react-native/popup-menu-android not building for 3rd party developers (#49212)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49212

Currently, developers can't use `popup-menu-android` at all because the Gradle file we publish is referencing
internal machinery.

I'm adding a pre-publish script that manipulates the Gradle. This is the easiest solution without having to do
crazy setup inside RNGP or having duplicated version codes around in the monorepo.

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

Changelog:
[Android] [Fixed] - Fix react-native/popup-menu-android not building for 3rd party developers

Reviewed By: cipolleschi

Differential Revision: D69192874

fbshipit-source-id: 9f9e8a0a6e76308e598a09f4c70dbc659c238b00
2025-03-11 11:44:48 +00:00
chrsmys 23b888ff2d Fix force RTL support on new architecture. (#49455)
Summary:
This fixes an issue in Fabric where changing the layout direction and then reloading the JS bundle did not honor the layout direction until the app was restarted on iOS. This now calls  `_updateLayoutContext` whenever RCTSurfaceView is recreated which happens on bundle reload. This is not an issue on the old architecture because the layout direction is determined within the [SurfaceViews](https://github.com/facebook/react-native/blob/acdddef48eb60b002c954d7d2447cb9c2883c8b3/packages/react-native/React/Views/RCTRootShadowView.m#L18) which were recreated on bundle reload.

## Related Issues:
- https://github.com/react-native-community/discussions-and-proposals/issues/847
- https://github.com/facebook/react-native/issues/49451
- https://github.com/facebook/react-native/issues/48311
- https://github.com/facebook/react-native/issues/45661

## How can we take this further?
If we want to make it so that it doesn't require an entire bundle reload for RTL to take effect I believe these are the steps that would need to be taken:
- Make it so [RCTI18nManager](https://github.com/facebook/react-native/blob/acdddef48eb60b002c954d7d2447cb9c2883c8b3/packages/react-native/React/CoreModules/RCTI18nManager.mm#L52) exports isRTL as a method instead of consts
- Send Notification Center notif when RTL is forced on or off
- Listen for that notification RCTSurfaceView and call _updateLayoutContext similar to UIContentSizeCategoryDidChangeNotification.

## Changelog:

[iOS] [FIXED] - Layout direction changes are now honored on bundle reload.

<!-- 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

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

Test Plan:
On the new architecture change force the layout direction and reload the bundle:
```
import React, { useCallback } from "react";
import { Button, I18nManager, StyleSheet, Text, View } from "react-native";
import RNRestart from "react-native-restart";

export default function Explore() {
    const onApplyRTL = useCallback(() => {
        if (!I18nManager.isRTL) {
            I18nManager.forceRTL(true);
            RNRestart.restart();
        }
    }, []);

    const onApplyLTR = useCallback(() => {
        if (I18nManager.isRTL) {
            I18nManager.forceRTL(false);
            RNRestart.restart();
        }
    }, []);

    return (
        <View style={styles.area}>
            <Text>Test Block</Text>
            <View style={styles.testBlock}>
                <Text>Leading</Text>
                <Text>Trailing</Text>
            </View>
            <Button title={"Apply RTL"} onPress={onApplyRTL} />
            <Button title={"Apply LTR"} onPress={onApplyLTR} />
        </View>
    );
}

const styles = StyleSheet.create({
    area: {
        marginVertical: 50,
        paddingHorizontal: 24,
    },
    testBlock: {
        paddingVertical: 10,
        flexDirection: "row",
        justifyContent: "space-between",
    },
});

```

https://github.com/user-attachments/assets/0eab0d79-de3f-4eeb-abd0-439ba4fe25c0

Reviewed By: cortinico, cipolleschi

Differential Revision: D69797645

Pulled By: NickGerleman

fbshipit-source-id: 97499621f3dd735d466f5119e0f2a0eccf1c3c05
2025-03-10 17:23:55 +00:00
Nicola Corti edea009534 Update Podfile.lock
Changelog: [Internal]
2025-02-06 11:55:44 +00:00
React Native Bot 39502c290b Release 0.76.7
#publish-packages-to-npm&0.76-stable
2025-02-05 17:54:02 +00:00
Riccardo Cipolleschi b0bc141574 [LOCAL] Fix build of AttributedString 2025-02-05 15:39:24 +00:00
Nicola Corti db3239a84a [LOCAL] Fix build failure in TextAttributes.cpp due to merge conflicts 2025-02-05 15:03:47 +00:00
Rickard Zrinski c0e6523d0f Fix maxFontSizeMultiplier prop on Text and TextInput components in new architecture (#47614)
Summary:
The `maxFontSizeMultiplier` prop for `Text` and `TextInput` was not handled in Fabric / New Architecture as documented in https://github.com/facebook/react-native/issues/47499.

bypass-github-export-checks

[GENERAL] [FIXED] - Fix `maxFontSizeMultiplier` prop on `Text` and `TextInput` components in Fabric / New Architecture

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

Test Plan:
I have not added any automated tests for this change but try to do so if requested. I have however added examples to RN Tester for both the Text and TextInput components, as well as compared the behaviour with Paper / Old Architecture. Both on version 0.76.

Noticed now I didn't do exactly the same steps in both videos, oops! Be aware that reapplying changes made in the Settings are currently half-broken on the new architecture, thus I'm restarting the app on Android and iOS. But this issue is unrelated to my changes. I've tested on main branch and it has the same issue.

Here are comparison videos between Paper and Fabric on iOS *after* I've made my fix.

| Paper  | Fabric |
| ------------- | ------------- |
| <video src="https://github.com/user-attachments/assets/f4fd009f-aa6d-41ab-92fa-8dcf1e351ba1" /> | <video src="https://github.com/user-attachments/assets/fda42cc6-34c2-42a7-a6e2-028e7c866075" /> |

| Paper  | Fabric |
| ------------- | ------------- |
| <video src="https://github.com/user-attachments/assets/59b59f7b-25d2-4b5b-a8e2-d2054cc6390b" /> | <video src="https://github.com/user-attachments/assets/72068566-8f2a-4463-874c-45a6f5b63b0d" /> |

Reviewed By: Abbondanzo

Differential Revision: D65953019

Pulled By: cipolleschi

fbshipit-source-id: 90c3c7e236229e9ad9bd346941fafe4af8a9d9fc
2025-02-05 14:32:46 +00:00
Nicola Corti f8857ba3b5 [0.76] Bump Kotlin to 1.9.25 to mitigate #49115 (#49135) 2025-02-05 14:32:37 +00:00
David Vacca 5f1134c8df Fix execution of early InteropEvents (#48823)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48823

This diff is fixing the execution of Events that are sent early in the rendering of surfaces.

This diff fixes a bug in the queueing of events that are built with not surfaceId (-1), the fixes is to call getSurfaceManagerForView() to retrieve the proper surfaceId (as we do in the execution of events)

calling getSurfaceManagerForView() has a perf hit, we believe this won't be a problem because this method will only be called in edge cases (no surfaceId and early execution of events)

changelog: [Android][Fixed] Fix execution of early InteropEvents

Reviewed By: shwanton, lenaic

Differential Revision: D68454811

fbshipit-source-id: a79be0b392004e645c48d1683bba774b6b597ca0
2025-02-05 14:21:54 +00:00
Riccardo Cipolleschi edafe1fe76 Be less strict with method parsing of TurboModule Interop Layer (#49072)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49072

We have instance of apps crashing when enabling the New Architecture because of the TurboModule interop layer.

What's happening is that when the module is loaded, the TM Interop Layer tries to parse the method definition to expose them in JS. However, for some libraries in the Legacy Architecture, it is possible to define a method in Objective-C and to define a different signature in Swift.

For example, the [`RNBluetoothClassic` library](https://github.com/kenjdavidson/react-native-bluetooth-classic) defines a selector in objective-c which [has the signature](https://github.com/kenjdavidson/react-native-bluetooth-classic/blob/main/ios/RNBluetoothClassic.m#L134-L136)

```
RCT_EXTERN_METHOD(available: (NSString *)deviceId
                  resolver: (RCTPromiseResolveBlock)resolve
                  rejecter: (RCTPromiseRejectBlock)reject)
```

And the method is inmplemented in Swift with [the signature](https://github.com/kenjdavidson/react-native-bluetooth-classic/blob/main/ios/RNBluetoothClassic.swift#L502-L505):

```
func availableFromDevice(
        _ deviceId: String,
        resolver resolve: RCTPromiseResolveBlock,
        rejecter reject: RCTPromiseRejectBlock
    )
```

When the TurboModule interop layer tries to parse the method, it receives the `accept:resolver:rejecter:` signature, but that signature is not actually defined in as a method in the module instance, and it crashes.

This crash was not happening in the Old Architecture, which was handling this case gracefully. Notice that the specific method from the example is not working in the Old Architecture either. However, the app is not crashing in the old architecture.

This change adds the same graceful behaviors plus it adds a warning in development to notify the developer about which methods couldn't be found in the interface.

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

## Changelog:
[iOS][Fixed] - Avoid crashing the app when the InteropLayer can't find some methods in the native implementation.

Reviewed By: javache

Differential Revision: D68901734

fbshipit-source-id: 844d1bf29423d5c601b583540e86d57dfffd1428
2025-02-05 14:21:24 +00:00
Tommy Nguyen 030ed97d80 fix(dev-middleware): add missing invariant dependency (#49047)
Summary:
`dev-middleware` uses `invariant` but does not declare it as a dependency. Under certain hoisting scenarios, or when using pnpm, this will cause `dev-middleware` to fail while being loaded.

## Changelog:

[GENERAL] [FIXED] - add missing `invariant` dependency

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

Test Plan: n/a

Reviewed By: cortinico

Differential Revision: D68835789

Pulled By: huntie

fbshipit-source-id: 13718f4970ed55e6e062b7c2bd719be977abdd0c
2025-02-05 14:20:41 +00:00
Nolan O'Brien 3077055e5d Remove implicit "start", then clean up callsites (#47313)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47313

We're going to fix up a bunch of designated initializers.  `RCTSurfaceHostingProxyRootView` is particularly problematic because different initializers will do different things even though reading the code it looks like they should be equivalent.  Remove the encapsulated "start" to the provided "surface", and require it to be explicit at the callsite.

## Changelog:

[iOS][Changed] - `RCTSurfaceHostingProxyRootView` no longer has different behavior (whether it calls `start` on the provided *surface*) depending on which initializer is used.  Call `start` yourself on the *surface* instead.

Reviewed By: cipolleschi

Differential Revision: D65214656

fbshipit-source-id: 179d5220d4f866b4452561e1bb6e2051020c8a11
2025-02-05 14:20:12 +00:00
Riccardo Cipolleschi dedb9a230f Fix images not displayed when extension is implicit (#48888)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48888

We have a report from OSS where Images are not displayed properly in case they are saved on disk with no extension.

We previously had a fix attempt iwith [this pr](https://github.com/facebook/react-native/pull/46971), but this was breaking some internal apps.

This second attempt should work for both cases.

## Changelog:
[iOS][Fixed] - Load images even when the extension is implicit

Reviewed By: cortinico

Differential Revision: D68555813

fbshipit-source-id: bc25970aafe3e6e5284163b663d36e00b3df3d82
2025-02-05 14:17:40 +00:00
Santiago d04b630daa Fix Appearance.setColorScheme(null) not resetting color scheme value (#47739)
Summary:
Fixes https://github.com/facebook/react-native/issues/47725

Calling Appearance.setColorScheme(null) or Appearance.setColorScheme(undefined) no longer resets the color scheme useColorScheme returns like on previous rn versions.

## 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] - Fix `Appearance.setColorScheme(null)` not resetting color scheme value

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

Test Plan: Repo with a patch ready to test: https://github.com/sangonz193/react-native-color-scheme-patch

Reviewed By: yungsters

Differential Revision: D66230236

Pulled By: cipolleschi

fbshipit-source-id: cc668acb1fde6d30f2706fc0ab7dee5cea1c3b14
2025-02-05 14:16:08 +00:00
Riccardo Cipolleschi 784c0e6734 Fix ruby (#48721)
Summary:
Following the suggestions [here](https://stackoverflow.com/questions/79360526/uninitialized-constant-activesupportloggerthreadsafelevellogger-nameerror), it seems that concurrent-ruby has been released tonight and it is bugged. Let's pin it to the right version.

## Changelog:
[iOS][Changed] - Pin 'concurrent-ruby' to a working version

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

Test Plan: GHA

Reviewed By: robhogan

Differential Revision: D68262719

Pulled By: cipolleschi

fbshipit-source-id: fc6410e28cc96f9d3769d3082a77cac0a3efe6db
2025-02-05 14:13:35 +00:00
Alex Toudic ecb9c582e6 Fix applicationDidEnterBackground not being called (#48376)
Summary:
I noticed that `AppDelegate` subscribers listening for `applicationDidEnterBackground` events in Expo projects (as documented [here](https://github.com/expo/expo/blob/238b6f57e459dd2c0b13ee158f0af709fe922460/docs/pages/modules/appdelegate-subscribers.mdx?plain=1#L56) aren't working as expected anymore.
While investigating, I discovered that the events aren't reaching the `ExpoAppDelegate` implementation ([source](https://github.com/expo/expo/blob/71f2c55ff3f11e43ab43761bb5cece2e48eae0bf/packages/expo-modules-core/ios/AppDelegates/ExpoAppDelegate.swift#L61)) because the `RCTAppDelegate` implementation of `applicationDidEnterBackground` interrupts the event chain. This appears to be affecting some legitimate use cases.
I believe we could improve this by removing the current implementation, but I'd love to hear your thoughts on this approach. I might be missing some context about why this implementation was originally needed, so any insights would be greatly appreciated!

## Changelog:

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

Pick one each for the category and type tags:

[IOS] [FIXED] - Fix applicationDidEnterBackground not being called

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->
[IOS] [FIXED] - Fix applicationDidEnterBackground not being called

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

Test Plan: Ran a local test after change

Reviewed By: javache

Differential Revision: D67621557

Pulled By: cipolleschi

fbshipit-source-id: 2d73711372deba867bd616c79ef4d00c79aa86d5
2025-02-05 14:13:07 +00:00
Riccardo Cipolleschi cc03a857d9 [LOCAL] Bump Podfile.lock 2025-01-09 13:36:38 +00:00
React Native Bot a0411eebe2 Release 0.76.6
#publish-packages-to-npm&latest
2025-01-09 10:47:05 +00:00
Nick Gerleman 342e3ec530 Fix TextMeasureCacheKey Throwing Out Some LayoutConstraints (#48525)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48525

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

`TextMeasureCacheKey` hash and equality functions only incorporates the maximum width constraint. I'm guessing this was an attempt at an optimization, but it can lead to incorrect results in pretty trivial cases. E.g. if Yoga knows a definite size of `Text` in one dimension,  and measures via `YGMeasureModeExactly`, we can have a minimum size corresponding specific to the style in which the text was laid out.

Changelog:
[General][Fixed] - Fix TextMeasureCacheKey Throwing Out Some LayoutConstraints

Reviewed By: christophpurrer

Differential Revision: D67922414

fbshipit-source-id: 0ee0220059fc4e4645b1684c42a0587fe728bedd
2025-01-09 10:44:42 +00:00
Riccardo Cipolleschi 5446d8c701 [LOCAL] Fix prettier 2025-01-08 14:25:11 +00:00
Riccardo Cipolleschi cd7cf07d32 [LOCAL][RN] Clean up feature flag 2025-01-08 14:21:30 +00:00
Riccardo Cipolleschi fb7f87ecb2 [LOCAL] Remove feature flag for allowRecursiveCommitsWithSynchronousMountOnAndroid 2025-01-08 14:13:15 +00:00
Mateo Guzmán 73ffe1394f Paper: TextInput maxLength is not working in old arch (#48126)
Summary:
Fixes https://github.com/facebook/react-native/issues/47563

It seems like a regression from https://github.com/facebook/react-native/issues/45401, where it was aimed to fix `onChangeText` being called multiple times when changing the text programmatically in an input with the `multiline` prop set as true.

This PR reverts that partially, as the `maxLength` check is not being evaluated correctly before setting the text. Not reverting it completely as when removing the second part of the fix, the `onChangeText` gets called multiple times again.

## Changelog:

[IOS] [FIXED] - Fixing TextInput `maxLength` not working in old arch

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

Test Plan:
The issue could be reproduced in the rn-tester. See my videos with the before and after the fix.

<details>
<summary>Before:</summary>

https://github.com/user-attachments/assets/86fd67eb-fc14-469a-a5f8-8e83b49f857c

</details>

<details>
<summary>After:</summary>

https://github.com/user-attachments/assets/368383b1-c1bd-4e0b-ac44-c78022462fa0

</details>

Reviewed By: cortinico

Differential Revision: D67025182

Pulled By: cipolleschi

fbshipit-source-id: 720c400eef362618106ae434aef421c7529214fe
2025-01-08 14:09:14 +00:00
timbocole e6374c6e60 fix: Prioritise local cpp (use default as fallback) (#48340)
Summary:
https://github.com/facebook/react-native/pull/47379 removed local cpp sources from the sources being built with the app. This resulted in a local `android/app/src/main/jni/OnLoad.cpp` file being ignored at build time. I have therefore added logic to the cmake file to prioritise local `cpp` files and fallback to `${REACT_ANDROID_DIR}/cmake-utils/default-app-setup/*.cpp` if none exist.

This resolves https://github.com/facebook/react-native/issues/48298

[ANDROID] [FIXED] - Prioritise local OnLoad.cpp, falling back to default-app-setup

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

Test Plan:
- Followed the https://reactnative.dev/docs/the-new-architecture/pure-cxx-modules guide (which was broken > 0.76.1)
- Applied the patch to the reproduction repository linked to https://github.com/facebook/react-native/issues/47352 to ensure no regression

Reviewed By: cipolleschi

Differential Revision: D67736012

Pulled By: cortinico

fbshipit-source-id: 87f6b8edf1613682585a94e1d1b3e6b4b792e4f5
2025-01-08 14:08:22 +00:00
Joe Vilches 07b7953af1 Small perf fix for new iOS view clipping (#46629)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/46629

If we clipped and had no border or corner radius we would end up hitting this path every time. We can optimize this a bit to avoid that.

Changelog: [Internal]

Reviewed By: NickGerleman

Differential Revision: D63299597

fbshipit-source-id: 90031f964b7669049a4a2efe00a553c888d28cd7
2025-01-08 14:07:07 +00:00
Thomas Nardone 30c3912eed 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
2025-01-08 14:06:35 +00:00
Eric Rozell 36ac19442a 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
2025-01-08 14:05:24 +00:00
Nicola Corti 8428a98c9a 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
2025-01-08 14:04:50 +00:00
Ben Handanyan 4ca9d72eab 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
2025-01-08 13:50:57 +00:00
Blake Friedman ca3cddb636 Update Podfile.lock
Changelog: [Internal]
2024-12-10 00:23:44 +00:00
React Native Bot f8654f9540 Release 0.76.5
#publish-packages-to-npm&latest
2024-12-09 16:30:18 +00:00
Riccardo Cipolleschi fcbcf80d1c [RN][Codegen] Better support filtering out non linked platforms (#48183) 2024-12-09 14:39:29 +00:00
Blake Friedman 93e9d5794e [LOCAL] Use legacy ReactFeatureFlags for 0.76
The d1ce8fafb6 pick from 0.77 used the new ReactNativeFeatureFlags,
backport this.
2024-12-09 14:19:38 +00:00
zhongwuzw 08976e46da automaticallyAdjustKeyboardInsets not shifting scrollview content (#46732)
Summary:
Fixes https://github.com/facebook/react-native/issues/46595 . It seems https://github.com/facebook/react-native/issues/37766 broke the `automaticallyAdjustKeyboardInsets` when input accessory view become first responder.

[IOS] [FIXED] - automaticallyAdjustKeyboardInsets not shifting scrollview content

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

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

Reviewed By: cipolleschi

Differential Revision: D65072478

Pulled By: javache

fbshipit-source-id: 7d5d7566438d4bb0e1d50074a953b18866e324d3
2024-12-09 12:17:48 +00:00
Nicola Corti 94e2b5b1b4 Revert "Include autolinkin.h in OnLoad.cpp only if it exists (#47875)"
This reverts commit 5b2bbb84b1.
2024-12-09 11:48:35 +00:00
zhongwuzw e0374f2199 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 11:43:00 +00:00
Nicola Corti d1ce8fafb6 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 11:42:01 +00:00
zhongwuzw d105c2c6fe Fabric: Fixes insets not adjust when keyboard disappear (#47924)
Summary:
Fixes https://github.com/facebook/react-native/issues/47731 .

[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 11:26:29 +00:00
Blake Friedman 7ea8e50c36 Update Podfile.lock
Changelog: [Internal]
2024-12-07 00:22:06 +00:00
React Native Bot 30f208eb2b Release 0.76.4
#publish-packages-to-npm&latest
2024-12-06 16:00:01 +00:00
Riccardo Cipolleschi a0be560dbf 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:25 +00:00
Blake Friedman bb29d379f0 [LOCAL] Fix linter formatting issue 2024-12-04 17:35:37 +00:00
Mohamed Alsadek 14185f2666 Back out "Enable Multiple Sheet Presentation in React Native" (#46433)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/46433

Original commit changeset: 53667cf1a75e

Original Phabricator Diff: D62143463

Changelog: [Internal]  revert previous modal presentation improvement

Reviewed By: sammy-SC

Differential Revision: D62474039

fbshipit-source-id: b510c0460c06ab9d595d414d4ea32acd224871bc
2024-12-04 17:33:06 +00:00
Alex Hunt 43fe69c315 Sync debugger-frontend to latest 0.76-stable (fix Expo node_modules entry points in Sources panel) (#47726) 2024-12-04 17:25:54 +00:00
Riccardo Cipolleschi 3cedb09a65 [Codegen] Exclude unlinked libs from codegen (#47712) 2024-12-04 17:25:17 +00:00
CHOIMINSEOK 33fce4488c Avoid NPE when touch event is triggered before SurfaceManager is initiated (#48007)
Summary:
A NPE can occur when a user touches the screen before the `SurfaceMountingManager` is initialized. Below is an example of the error log from our production service. This issue can also be reproduced using RNTester. To prevent invalid touch events during init time of rn app from causing an NPE, add a null check for SurfaceMountingManager before calling mark/sweepActiveTouchForTag.

```
Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.facebook.react.fabric.mounting.SurfaceMountingManager.markActiveTouchForTag(int)' on a null object reference
       at com.facebook.react.fabric.FabricUIManager.markActiveTouchForTag(FabricUIManager.java)
       at com.facebook.react.uimanager.JSTouchDispatcher.markActiveTouchForTag(JSTouchDispatcher.java)
       at com.facebook.react.uimanager.JSTouchDispatcher.handleTouchEvent(JSTouchDispatcher.java)
       at com.facebook.react.runtime.ReactSurfaceView.dispatchJSTouchEvent(ReactSurfaceView.java)
       at com.facebook.react.ReactRootView.onInterceptTouchEvent(ReactRootView.java)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2870)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
       at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
       at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
       at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:794)
       at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1967)
       at android.app.Activity.dispatchTouchEvent(Activity.java:4571)
       at com.rainist.banksalad2.feature.common.BaseActivity.dispatchTouchEvent(BaseActivity.java)
       at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:70)
       at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:752)
       at android.view.View.dispatchPointerEvent(View.java:16498)
       at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:8676)
       at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:8423)
       at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7752)
       at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7809)
       at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7775)
       at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:7978)
       at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7783)
       at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:8035)
       at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7756)
       at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7809)
       at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7775)
       at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7783)
       at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7756)
       at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:11343)
       at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:11212)
       at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:11168)
       at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:11477)
       at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:337)
       at android.os.MessageQueue.nativePollOnce(MessageQueue.java)
       at android.os.MessageQueue.next(MessageQueue.java:335)
       at android.os.Looper.loopOnce(Looper.java:187)
       at android.os.Looper.loop(Looper.java:319)
       at android.app.ActivityThread.main(ActivityThread.java:9063)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:588)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)
```
https://github.com/user-attachments/assets/e9c6ff84-c94d-4392-9042-8e635197202e

## Changelog:

[Android] [Fixed] - Avoid NPE when touch event is triggered before SurfaceManager is initiated

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

Test Plan:
I checked the crashed being fixed on RNTester.

https://github.com/user-attachments/assets/71f7e359-707a-494c-ae34-fef8d432e612

Reviewed By: cortinico

Differential Revision: D66594576

Pulled By: javache

fbshipit-source-id: b1559d94866bdb021e0374f1953684849603033c
2024-12-04 17:24:04 +00:00
zhongwuzw 5d82c32de7 Fabric: Fixes Modal onRequestClose not called (#48037)
Summary:
Fixes https://github.com/facebook/react-native/issues/48030 .

[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-04 17:19:46 +00:00
Kudo Chien 3b64ed0097 Fix lazy import error from jest and Appearance.js (#47629)
Summary:
currently running jest test, it shows an error:

```
ReferenceError: You are trying to `import` a file after the Jest environment has been torn down. From __tests__/App.test.tsx.

      at getState (node_modules/react-native/Libraries/Utilities/Appearance.js:18:26)
      at addChangeListener (node_modules/react-native/Libraries/Utilities/Appearance.js:71:19)
      at subscribe (node_modules/react-native/Libraries/Utilities/useColorScheme.js:10:66)
      at subscribeToStore (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6232:10)
      at commitHookEffectListMount (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13038:26)
      at commitPassiveMountOnFiber (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14461:11)
      at commitPassiveMountEffects_complete (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14421:9)
      at commitPassiveMountEffects_begin (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14408:7)
      at commitPassiveMountEffects (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14396:3)
      at flushPassiveEffectsImpl (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:16287:3)
      at flushPassiveEffects (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:16236:14)
      at node_modules/react-test-renderer/cjs/react-test-renderer.development.js:16051:9
      at workLoop (node_modules/scheduler/cjs/scheduler.development.js:266:34)
      at flushWork (node_modules/scheduler/cjs/scheduler.development.js:239:14)
      at Immediate.performWorkUntilDeadline [as _onImmediate] (node_modules/scheduler/cjs/scheduler.development.js:533:21)
```

it is a regression from https://github.com/facebook/react-native/issues/46123 that to have a lazy require.

this pr tries to mock `useColorScheme` to return `light`. i think we don't necessarily test the color scheme changes in jest runtime. originally `useColorScheme` also returns `light` because of [this statement](https://github.com/facebook/react-native/blob/9a60038a40e16925ea1adeb3e3c937c22a615485/packages/react-native/Libraries/Utilities/Appearance.js#L77-L83)

## Changelog:

[GENERAL] [FIXED] - Fixed jest error from Appearance.js

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

Test Plan:
```sh
$ npx react-native-community/cli init RN0762 --pm bun --version 0.76.2
$ cd RN0762
$ bun test run
```

Reviewed By: cipolleschi

Differential Revision: D66297456

Pulled By: huntie

fbshipit-source-id: 80d1460532e76bd1815c66964547b50d7f7b3558
2024-12-04 16:34:06 +00:00
Riccardo Cipolleschi 5b2bbb84b1 Include autolinkin.h in OnLoad.cpp only if it exists (#47875)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47875

The old architecture looks broken because in the OnLoad.cpp file we try to include the autolinking.h header which is only generated when the New Architecture is enabled.

This fix guards the include and the usage of the function provided by the autolinking so that the old architecture should work as well.

## Changelog
[Internal] - Include autolinkin.h in OnLoad.cpp only if it exists

Reviewed By: blakef

Differential Revision: D66295318

fbshipit-source-id: 18461e6b70ac92af57b805bef51c0df49db02283
2024-12-04 16:33:46 +00:00
Rob Hogan 304179d297 metro-config: Revert setting hermesParser: true in default Metro config (#47670)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47670

Now that we have `babel-plugin-syntax-hermes-parser` in `react-native/babel-preset` (since D63535216), it's no longer necessary to use `hermes-parser` directly in Metro in order to use newer Flow syntax.

Babel with `babel-plugin-syntax-hermes-parser` is generally preferable, because it intelligently falls back to parsing with Babel for any non-`flow` files.

See https://github.com/facebook/hermes/issues/1549 for context.

Changelog:
[General][Fixed] metro-config: Don't use `hermes-parser` by default, prefer `babel-plugin-syntax-hermes-parser`, which supports other syntax plugins.

Reviewed By: huntie

Differential Revision: D66002056

fbshipit-source-id: cf48acec347e2c0791872f8ca4b53f5f8af1c783
2024-12-04 16:33:31 +00:00
Nick Gerleman 73f6277175 Fix possible NSRangeException when updating typing attributes in response to new text content (#47737)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47737

We may see an NSRangeException when setting new AttributedString content, where setting the AttributedString itself changes selection (before we mutate it later).

It seems like the selection here is not in a good state yet in regards to the AttributedString backing exposed (since we are reading it while modifying it). So let's fold the logic for updating typing attributes into the collection of ignored work from non-user-selection updates, since programatically setting an AttributedString will already trigger updating typing attributes.

I also added a nil check here, which is unrelated to the crash, but it seems like we should have it for safety...

Changelog:
[iOS][Fixed] - Fix possible NSRangeException when updating typing attributes in response to new text content

Reviewed By: cipolleschi

Differential Revision: D66202986

fbshipit-source-id: fded492b5022c5fef5b9563f93a57549d06a7020
2024-12-04 16:33:16 +00:00
zhongwuzw bc04bb4072 Fabric: Adjusts the weight according to the font name (#47742)
Summary:
Fixes another font weight issue mentioned in https://github.com/facebook/react-native/issues/47656#issuecomment-2486282496. We can get the weight from font name if user not specify weight.

esbenvb Is this work for you ?

## Changelog:

[IOS] [FIXED] - Fabric: Adjusts the weight according to the font name

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

Test Plan: Demo in https://github.com/facebook/react-native/issues/47656#issuecomment-2486282496.

Reviewed By: cipolleschi

Differential Revision: D66236128

Pulled By: javache

fbshipit-source-id: d325a7fee28681a1e95fa0341cb7a16fcd9918c0
2024-12-04 16:32:58 +00:00
zhongwuzw 9f1c6bcc13 Fabric: Fixes custom font of weight is not honored (#47691)
Summary:
Fixes https://github.com/facebook/react-native/issues/47656 .

## Changelog:

[IOS] [FIXED] - Fabric: Fixes custom font of weight is not honored

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

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

Reviewed By: cipolleschi

Differential Revision: D66174732

Pulled By: javache

fbshipit-source-id: 5e6a8c870d3a13283548c736aed73193f7976bfc
2024-12-04 16:32:36 +00:00
Riccardo Cipolleschi a1ac30193d Do not discard props when setNativeProps is used (#47669)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47669

When investigating [#47476](https://github.com/facebook/react-native/issues/47476), I found that the `secureTextInput` prop was not changing in the Mounting layer when changing it in JS.

I track down the problem to the `UIManager::cloneNode` method.
When we clone the node, we first merge the patch that arrives from React into the props controlled by setNativeProps, ignoring the patch's props that are controlled by React.

But then, we forgot to merge back the React's controlled property into the final props, effectively losing them.

This change adds an extra merging step, merging the props controlled with setNativeProps back into the patch of props controlled by React, and then using this new set of props as source of truth.

## Changelog:
[General][Fixed] - do not discard props in the patch when they are not null while using `useNativeProps`

Reviewed By: sammy-SC

Differential Revision: D65948574

fbshipit-source-id: db4f2b793f4a6348456933c95a151012252b8ebc
2024-12-04 16:32:15 +00:00
Alex Hunt 0e8769e7cd Revert to @babel/eslint-parser in eslint-config (#47333)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47333

Motivated by https://github.com/facebook/hermes/issues/1549. This was originally changed in https://github.com/facebook/react-native/pull/46696, as our internal Flow support had diverged from `babel/eslint-parser` (https://github.com/facebook/react-native/issues/46601).

We effectively have three flavours of JavaScript in support:
- Flow@latest for the `react-native` package, shipped as source — uses `hermes-parser`.
- TypeScript for product code (community template, Expo) — uses `babel/plugin-syntax-typescript`.
- Plain JavaScript or Flow in product code, *which may be extended with additional user Babel plugins and needs lenient parsing* — uses `babel/plugin-syntax-flow` via `babel/eslint-parser` (**this change**).

I'd love to simplify this 😅.

Switching to `hermes-eslint` for the RN monorepo codebase (D63541483) is unchanged.

Changelog: [Internal]

Reviewed By: robhogan, cipolleschi

Differential Revision: D65272156

fbshipit-source-id: 3a2bbe3fcf8ed6057f6d994a0be4985e6bf46fa9
2024-12-04 16:27:07 +00:00
BIKI DAS abbe117a7b Dispatch onMomentumScrollEnd after programmatic scrolling (#45187)
Summary:
in iOS on a scroll generated programatically, the `onMomentScrollEnd` is fired, though in case of android the same does not happen, this PR tries to implement the same behaviour for android as well, while diving through the code it seems we have two extra `onMomentumScrollEnd` events. Only one event should be fired.

**iOS Behaviour on Programmatic Scroll**

https://github.com/facebook/react-native/assets/72331432/fb8f16b1-4db6-49fe-83a1-a1c40bf49705

https://github.com/facebook/react-native/assets/72331432/9842f522-b616-4fb3-b197-40817f4aa9cb

**Android Behaviour on Programmatic Scroll**

https://github.com/facebook/react-native/assets/72331432/c24d3f06-4e2a-4bef-81af-d9227a3b1a4a

https://github.com/facebook/react-native/assets/72331432/d4917843-730b-4bd7-90d9-33efb0f471a7

If closely observed we can see the `onMomentumScrollEnd` does not gets called in Android unlike to iOS.

[Android][Fixed] - Dispatch onMomentumScrollEnd after programmatic scrolling

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

Test Plan:
i have added updates to the FlatList example and ScrollViewSimple
here is a ScreenRecording of `onMomentumScrollEnd` firing in android after the code changes

https://github.com/facebook/react-native/assets/72331432/f036d1a5-6ebf-47ba-becd-4db98a406b15

https://github.com/facebook/react-native/assets/72331432/8c788c39-3392-4822-99c5-6e320398714b

Reviewed By: javache

Differential Revision: D65539724

Pulled By: Abbondanzo

fbshipit-source-id: f3a5527ac5979f5ec0c6ae18d80fdc20c9c9c14b
2024-12-04 16:16:40 +00:00
Blake Friedman 2d337efc23 Update Podfile.lock
Changelog: [Internal]
2024-11-21 23:09:09 +00:00
Blake Friedman 3287014ee9 [LOCAL] fix GHA publishTemplate verification 2024-11-21 22:37:02 +00:00
React Native Bot 605e2e443b Release 0.76.3
#publish-packages-to-npm&latest
2024-11-21 19:12:36 +00:00
Riccardo Cipolleschi d8b727c6bf Allow downgrading CMake to build hermesc on Windows (#47867)
Summary:
CI is failing to build HermesC on windows due to a version mismatch of the CMake already installed

## Changelog:
[Internal] - Fix Windows CI for HermesC

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

Test Plan: GHA

Reviewed By: robhogan

Differential Revision: D66292617

Pulled By: cipolleschi

fbshipit-source-id: 5e8f4f45e33fbdd9ff163b4e8a09cb98d4366dc7
2024-11-21 16:21:31 +00:00
Blake Friedman e70cad24f0 Support Windows sdkmanager.bat (#47874)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47874

We should be searching for the .bat file on Windows to remain compatible with some user setups.

Changelog: [Android][Fixed] look for sdkmanager.bat

Reviewed By: cipolleschi

Differential Revision: D66295240

fbshipit-source-id: 6b79a9aa40f77ed9c5b3d6ad92b1a62e78159223
2024-11-21 15:41:45 +00:00
Blake Friedman 9946838bed CMake Windows path normalization (#47702)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47702

Use `file(TO_CMAKE_PATH` to normalize paths, and normalizing `input_SRC` as it's already a CMake path.

Changelog: [Internal]

Reviewed By: rshest

Differential Revision: D66101321

fbshipit-source-id: e81af40551d2777901f9c7cf9a4175f2bce76ec8
2024-11-21 15:41:45 +00:00
Fouad Magdy 08b8300548 fix build failure on windows in android (#47641)
Summary:
This pull request addresses a CMake configuration issue where an invalid escape character in file paths caused the build process to fail. Specifically, it resolves the issue in the React Native CMake configuration file where the path separator was incorrectly handled, leading to an error in the build system.

the issue is in [This Issue](https://github.com/expo/expo/issues/32955) and [This](https://github.com/expo/expo/issues/32957)

## 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
-->

[INTERNAL] [FIXED] - Corrected invalid escape character in CMake path handling

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

Test Plan:
To test the changes, I performed the following steps:

1. Cloned the repository and checked out the `fix-cmake-invalid-escape-character` branch.
2. Ran the CMake build on a Windows environment where the issue was previously occurring.
3. Verified that the build process completed successfully without the "invalid character escape" error.
4. Ensured that the path handling now works correctly in CMake on Windows platforms.

Reviewed By: rshest

Differential Revision: D66073896

Pulled By: cipolleschi

fbshipit-source-id: bd2a71bb00ce5c5509ed403842c995c32f58f91d
2024-11-21 15:41:45 +00:00
Riccardo Cipolleschi d01d01464b [LOCAL] Bump Podfile.lock 2024-11-18 11:00:33 +00:00
Blake Friedman ac61c14b58 Fix typo in template action watcher script (#47286)
Summary:
Trivial typo - static analysis would have been a good thing.

Changelog: [Internal]

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

Test Plan: eyes

Reviewed By: NickGerleman

Differential Revision: D65145600

Pulled By: blakef

fbshipit-source-id: 567ef3637441aa84651dce03f45b80068c5f4290
2024-11-15 10:23:57 +00:00
React Native Bot 81737c2b99 Release 0.76.2
#publish-packages-to-npm&latest
2024-11-14 16:50:52 +00:00
Alex Hunt ff1261e7dc [0.76] Skip hermes-parser under Babel for non-Flow JS code (#47569) 2024-11-13 12:12:23 +00:00
Riccardo Cipolleschi 1e659dc44e Fix Typo and skip generation of app-specific component registration (#47547)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47547

In [#47176](https://github.com/facebook/react-native/pull/47176) we disabled the generation of the component registration for app specific components as it was creating a circular dependency between the app and React Native.

However, we made a couple of typos that make it not work as expected and users picked up those typos soon.

This change fixes them for good.

## Changelog
[iOS][Fixed] - Properly stop generating component registration for components defined in app.

Reviewed By: blakef

Differential Revision: D65750433

fbshipit-source-id: 1a879c5be014905558b9fd05e6f16ac36b784ed6
2024-11-12 19:14:32 +00:00
Jakub Piasecki 9f65442f2d Fix timers in headless tasks on bridgeless mode (#47496)
Summary:
Fixes https://github.com/facebook/react-native/issues/47495

`JavaTimerManager` is being registered to receive headless tasks events in the [`TimingModule`](https://github.com/facebook/react-native/blob/0ee963ea65bcc88122044d51027511e611bde584/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/TimingModule.kt#L28-L29). This module is not used on bridgeless: [1](https://github.com/facebook/react-native/blob/0ee963ea65bcc88122044d51027511e611bde584/packages/react-native/Libraries/Core/setUpTimers.js#L44-L61), [2](https://github.com/facebook/react-native/blob/0ee963ea65bcc88122044d51027511e611bde584/packages/react-native/Libraries/Core/setUpTimers.js#L123-L132) and since it's loaded lazily, the event listener is never registered.

This PR moves registration to the constructor of `JavaTimerManager` and deregistration to the `onInstanceDestroy` method. This way the event listener is always registered when an instance of the timer manager exists.

## Changelog:

[ANDROID] [FIXED] - Fix timers in headless tasks on bridgeless mode

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

Test Plan: See the reproducer from the issue

Reviewed By: javache

Differential Revision: D65615601

Pulled By: alanleedev

fbshipit-source-id: 6e1d36f8783e813065f79730a928b99c3e385718
2024-11-12 19:14:18 +00:00
Nicola Corti c4fd80f414 Use absolute path when compiling appmodules.so sources (#47379)
Summary:
Fixes https://github.com/facebook/react-native/issues/47352

This fixes a bug when the user is providing its own CMakeLists.txt file say because they want to compile more C++ code than we actually provide.

Previously the `*.cpp` will evalute file in the current directory, meaning that the app's default `OnLoad.cpp` file would be ignored.

## Changelog:

[ANDROID] [FIXED] - Use absolute path when compiling appmodules.so sources

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

Test Plan:
Tested against the reproducer provided in:
- Use absolute path when compiling appmodules.so sources

Reviewed By: cipolleschi

Differential Revision: D65428676

Pulled By: cortinico

fbshipit-source-id: 7f3e4d470da0fffc5191c1a2c7e8fec517fee496
2024-11-12 19:13:59 +00:00
Nicola Corti e9fc092156 Properly handle paths with spaces in autolinking (#47388)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47388

Fixes https://github.com/facebook/react-native/issues/47364
Fixes https://github.com/facebook/react-native/issues/47377
Fixes https://github.com/facebook/react-native/issues/37124

We're having problems is a path contains a space ' ' because when autolinking,
the `add_subdirectory()` function of CMake consider the path with space as 2 parameters.

This fixes it by properly quoting the path.

Changelog:
[Android] [Fixed] - Properly handle paths with spaces in autolinking

Reviewed By: cipolleschi

Differential Revision: D65434413

fbshipit-source-id: b9147482f98f7e222405cc8d9e6f3c17a5f4ed02
2024-11-12 19:13:31 +00:00
Alan Lee e8c4faaf08 fix Modal content being cut off when Android Activity is edge-to-edge (#47339)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47339

Fixing issue raised in https://github.com/facebook/react-native/issues/47307

This is a follow up from D62286026.
It appears there was a line that went missing while trying to refactor the code.

`fitsSystemWindows = true` is needeod for < API 30 to avoid content rendering under the system bars when Modal is shown with Activity that is edge-to-edge.

Changelog:
[Android][Fixed] Fix Regression - Modal content rendering below system bar on < API 30 when activity is edge-to-edge

Reviewed By: cortinico

Differential Revision: D65280014

fbshipit-source-id: 616ff739be55635f1295ef3bf8b997a27ef769ae
2024-11-12 19:12:32 +00:00
Nick Gerleman fbe38bb2a3 Fix missing emitter attributes on iOS TextInput when controlled component value specified using value instead of children (#47269)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47269

There were [reports](https://github.com/reactwg/react-native-releases/issues/595) that patching in the fixes for iOS controlled input did not work as expected.

I think tracked this down to a difference in how I tested, where the controlled component I used passed value as a child of the `TextInput`, instead of via `value`. Passing via `value` triggers a secondary bug, where we don't correctly pass a reference to correct ShadowView when creating attributedstring, specifically in the iOS TextInputShadowNode impl.

We previously passed nothing for the ShadowView (only the first two struct fields). This was exposed in D52589303 which enabled `-Wextra`, but there, I went with same behavior of passing empty ShadowView, instead of the correct behavior (like Android impl) of passing a ShadowView of the current ShadowNode.

After fixing this, we now correctly create event emitters in the passed attributedstring, which matches expectations for pargraph-level eventemitter now in typing attributes. We don't seem actually use this on iOS for TextInput right now (just Text), but this is likely the right foundation for events regardless.

Changelog:
[iOS][Fixed] - Fix missing emitter attributes on iOS TextInput when controlled component value specified using `value` instead of `children`

Reviewed By: cipolleschi

Differential Revision: D65108163

fbshipit-source-id: 499fe28439fabd2579eca6ded7fd13fd8ea2e43e
2024-11-12 19:11:55 +00:00
Nick Gerleman 40093d96d1 Fix cursor moving while typing quickly and autocorrection triggered in controlled single line TextInput on iOS (New Arch) (#46970)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/46970

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

This one is a bit of a doozy...

During auto-correct in UITextField (used for single line TextInput) iOS will mutate the buffer in two parts, non-atomically. After the first part, after iOS triggers `textFieldDidChange`, selection is in the wrong position. If we set full new AttributedText at this point, we propagate the incorrect cursor position, and it is never restored.

In the common case, where we are not mutating text in the controlled component, we shouldn't need to be setting AttributedString in the first place, and we do have an equality comparison there currently. But it is defeated because attributes are not identical. There are a few sources of that:
1. NSParagraphStyle is present in backing input, but not the AttributedString we are setting.
2. Backing text has an NSShadow with no color (does not render) not in the AttributedText
3. Event emitter attributes change on each update, and new text does not inherit the attributes.

The first two are part of the backing input `typingAttributes`, even if we set a dictionary without them. To solve for them, we make attribute comparison insensitive to the attribute values in a default initialized control. There is code around here fully falling back to attribute insensitive comparison, which we would ideally fix to instead role into this "effective" attribute comparison.

The event emitter attributes being misaligned is a real problem. We fix in a couple ways.
1. We treat the attribute values as equal if the backing event emitter is the same
2. We set paragraph level event emitter as a default attribute so the first typed character receives it

After these fixes, scenario in https://github.com/facebook/react-native-website/pull/4247 no longer repros in new arch. Typing in debug build also subjectively seems faster? (we are not doing second invalidation of the control on every keypress).

Changes which do mutate content may be susceptible to the same style of issue, though on web/`react-dom` in Chrome, this seems to not try to preserve selection at all if the selection is uncontrolled, so this seems like less of an issue.

I haven't yet looked at old arch, but my guess is we have similar issues there, and could be fixed in similar ways (though, we've been trying to avoid changing it as much as possible, and 0.76+ has new arch as default, so not sure if worth fixing in old impl as well if this is very long running issue).

Changelog:
[iOS][Fixed] - Fix cursor moving in iOS controlled single line TextInput on Autocorrection (New Arch)

Reviewed By: javache, philIip

Differential Revision: D64121570

fbshipit-source-id: 2b3bd8a3002c33b68af60ffabeffe01e25c7ccfe
2024-11-12 19:11:55 +00:00
Riccardo Cipolleschi 6f9ddd8f89 Add yoga to app search paths (#47195)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47195

When a user wants to create a Fabric Component i their app (not in a separate library) the app fails to build because:
- The custom component has to inherit from `RCTViewComponentView`
- `RCTViewComponentView` imports `ViewProps.h`
- `ViewProps.h` imports `HostPlatformViewProps.h`
- `HostPlatformViewProps.h` imports `BaseViewProps.h`
- `BaseViewProps.h` imports `YogaStylableProps.h`

which is a Yoga private header and the App has not visibility over it.

It is also not possible to fix this issue with forward declaring the `YogaStylableProps`, because `BaseViewProps` inherit from the yoga's props, so the compiler needs the full declaration of `YogaStylableProps` to work

This needs to be picked in 0.76

## Changelog
[iOS][Fixed] - Give apps access to Yoga headers

Reviewed By: blakef

Differential Revision: D64925222

fbshipit-source-id: e724076bbfb0a678948340dfab2ce609e6509533
2024-11-12 19:00:04 +00:00
Nick Gerleman 3ce4b80e6f Include existing attributes in newly typed text (#47018)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47018

This change makes it so that newly typed text in a TextInput will include the existing attributes present based on cursor position. E.g. if you type after an inner fragment with blue text, the next character will be blue (or, an event emitter specific to an inner fragment will also be expanded). This is a behavior change for the (admittedly rare) case of uncontrolled TextInput with initially present children AttributedText, but more often effect controlled components, before state update (we are after, less likely to need to reset AttributedString because of mismatch).

Originally included this in D64121570, but it's not needed to fix the common case since we include paragraph-level event emitter as part of default attributes, and has some of its own risk, so I decided it is better separate.

Changelog:
[iOS][Changed] - Include existing attributes in newly typed text

Reviewed By: cipolleschi

Differential Revision: D64352310

fbshipit-source-id: 90ef8c49f50186eadf777e81cf6af57e1aada207
2024-11-12 18:58:46 +00:00
Riccardo Cipolleschi a09df751eb Add TS types for Codegen (#46484)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/46484

We recently realized that we don't have TS types for Codegen.
These are needed to let our users use these types when writing Specs in TS

## Changelog
[General][Added] - Add CodegenTypes for TS

Reviewed By: christophpurrer

Differential Revision: D62644516

fbshipit-source-id: 92bb7e8998d31806f6eb63319fb6d406fcd65ad8
2024-11-12 18:58:36 +00:00
Blake Friedman 94d4bfd7c8 [LOCAL] Update Hermes
Adds fix: facebook/hermes@c2c4ee7
2024-11-12 18:57:54 +00:00
Riccardo Cipolleschi a35852f976 [LOCAL] Fix lint process 2024-10-31 11:20:25 +00:00
Szymon Rybczak 51b98c24bd Bump CLI to 15.0.1 (#47328) 2024-10-31 10:25:02 +00:00
Blake FriedmanandRick Hanlon bbe5e72768 fix logbox error stack (#47303)
* Re-enable integration tests (#46639)

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

These tests were skipped when we were switching to component stacks, which also hid a bug later in the stack. Re-enable them.

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D63349616

fbshipit-source-id: ccde7d5bb3fcd9a27adf4af2068a160f02f7432a

* Add integration tests for console errors + ExceptionManager (#46636)

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

Adds more integration tests for LogBox (currently incorrect, but fixed in a later diff).

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D63349614

fbshipit-source-id: 8f5c6545b48a1ed18aea08d4ecbecd7a6b9fa05a

* Refactor LogBox tests to spies (#46638)

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

This is annoying, but in the next diff that fixes a bug I need to test using the default warning filter instead of a mock (really, all this mocking is terrible, idk why I did it this way).

Unfortunately, in Jest you can't just reset mocks from `jest.mock`, `restoreMocks` only resets spies and not mocks (wild right).

So in this diff I converted all the `jest.mock` calls to `jest.spyOn`. I also corrected some of the mocks that require `monitorEvent: 'warning',` like the warning filter sets.

I also added a test that works without the fix.

Changelog: [Internal]

Reviewed By: javache

Differential Revision: D63349615

fbshipit-source-id: 4f2a5a8800c8fe1a10e3613d3c2d0ed02fca773e

* Fix errors with component stacks reported as warnings (#46637)

Summary:
Ok so this is a doozy.

## Overview
There was a report that some console.error calls were being shown as warnings in LogBox but as console.error in the console. The only time we should downlevel an error to a warning is if the custom warning filter says so (which is used for some noisy legacy warning filter warnings internally).

However, in when I switched from using the `Warning: ` prefix, to using the presence of component stacks, I subtly missed the default warning filter case.

In the internal warning filter, the `monitorEvent` is always set to something other than `unknown` and if it's set to `warning_unhandled` then `suppressDialog_LEGACY` is always false.

However, the default values for the warning filter are that `monitorEvent = 'unknown'` and `suppressDialog_LEGACY = true`. In this case, we would downlevel the error to a warning.

## What's the fix?
Change the default settings for the warning filter.

## What's the root cause?

Bad configuration combinations in a fragile system that needs cleaned up, and really really bad testing practices with excessive mocking and snapshot testing (I can say that, I wrote the tests)

## How could it have been caught?
It was, but I turned off the integration tests while landing the component stack changes because of mismatches between flags internally and in OSS, and never turned them back on.

Changelog: [General] [Fixed] - Fix logbox reporting React errors as Warnings

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

Reviewed By: huntie

Differential Revision: D63349613

Pulled By: rickhanlonii

fbshipit-source-id: 32e3fa4e2f2077114a6e9f4feac73673973ab50c

* [LOCAL] using older version of React Dev Tools

- Older version has old URL, updated tests
- Comments on test don't match what's being tested.  Updated.

---------

Co-authored-by: Rick Hanlon <rickhanlonii@meta.com>
2024-10-31 10:24:52 +00:00
Riccardo Cipolleschi dac6d508af [RN][JS] Fix setUpErrorHandling to show early JS errors (#47287) 2024-10-31 10:24:33 +00:00
Tommy Nguyen 0def73d1a6 fix: fix semver not being found in pnpm setups (#47310) 2024-10-31 10:24:21 +00:00
Blake Friedman 4fc2c8fd1f Update Podfile.lock
Changelog: [Internal]
2024-10-29 15:21:39 +00:00
React Native Bot b048659ceb Release 0.76.1
#publish-packages-to-npm&latest
2024-10-29 10:55:33 +00:00
Blake Friedman 201b517de0 [CI]: verify template is published method
This step called an old reference, this function identifier was updated.

Changelog: [Internal]
2024-10-28 14:35:29 +00:00
Robert Pasiński 980f4b42ca fix: AppRegistry not callable from Native in bridgeless (#46480)
Summary:
AppRegistry was not treated as a Callable Module in bridgeless mode. This is breaking headless tasks on Android.

Fixes:

 - https://github.com/facebook/react-native/issues/46050

## Changelog:

[ANDROID] [FIXED] - Made AppRegistry callable from Native code in Bridgeless (fixes headless tasks)

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

Test Plan: Used repro from linked issue

Reviewed By: javache

Differential Revision: D62637486

Pulled By: cortinico

fbshipit-source-id: 756527003ac6d712e76c02c188e280d15c010068
2024-10-28 14:23:31 +00:00
zhongwuzw 621d4ee298 Fixes regression of RCTWindowFrameDidChangeNotification not fired (#47236)
Summary:
Fixes https://github.com/facebook/react-native/issues/47234. regression from https://github.com/facebook/react-native/commit/391680fe844aad887e497912378c699aed13464b#diff-b7fda5d350ac535115fa683faa7317b43aa11f3448f95266ef9ff051c3753a6fL63

bypass-github-export-checks

## Changelog:

[IOS] [FIXED] - Fixes regression of RCTWindowFrameDidChangeNotification not fired

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

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

Reviewed By: blakef

Differential Revision: D65058105

Pulled By: cipolleschi

fbshipit-source-id: 0e286182ed93f289cb853710e2e00801ef2d4f73
2024-10-28 14:20:09 +00:00
Riccardo Cipolleschi e8776240b4 Pin Xcodeproj to < 1.26.0 (#47237)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47237

The Xcodeproj gem has been released yesterday to version 1.26.0 and it broke the CI pipeline of react native.

This should fix the issue

## Changelog
[Internal] - Pin Xcodeproj gem to 1.26.0

Reviewed By: blakef

Differential Revision: D65057797

fbshipit-source-id: f4035a1d3c75dd4140eb1646ab2aa0ccb08fb16b
2024-10-28 14:20:04 +00:00
Joe Vilches defc0c8c21 Fix animating background colors in View (#47101)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47101

See https://github.com/facebook/react-native/issues/47011

borders do not have this problem because they call `removeAllAnimations` on the layer after changing it, which is what I am doing here

Changelog: [iOS] [fixed] - Fixed bug where background colors would sometimes animate when changing on Views

Reviewed By: cipolleschi

Differential Revision: D64493968

fbshipit-source-id: cf81549f21b124b67c6e7647c6ae827bfe80a9cf
2024-10-28 14:19:54 +00:00
Sunny Luo e56bd89eff Add jsBundleFile to DefaultReactNativeHost.kt (#47188)
Summary:
The JsBundleFilePath has been ignored when converting DefaultReactNativeHost to ReactHost

Changelog:
[Internal] [Changed] - Add jsBundleFile to DefaultReactNativeHost.kt

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

Reviewed By: javache

Differential Revision: D64914149

Pulled By: cortinico

fbshipit-source-id: d437ca81df5a170e0c5f01a22ccda83f43a09dd2
2024-10-28 14:19:49 +00:00
Riccardo Cipolleschi 807500a63e Exclude generation of app-defined components from RCTThirdPartyFabricComponentsProvider (#47176)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47176

While writing the guide for the New Architecture, we realized that we need to exclude the generation of the Cls function in the RCTThirdPartyFabricComponentsProvider for components defined in the app.

This is needed because a component that is defined in the app will have those function defined in the app project. However, the RCTThirdPartyFabricComponentsProvider is generated in Fabric, inside the Pods project.

The pod project needs to build in isolation from the app and cocoapods then link the app to the pods project. But the compilation of the pods project fails if one of the symbol needed by the pods lives in the app.

By disabling the generation of that function in th RCTThirdPartyFabricComponentsProvider, we can successfully build the app.

The downside is that the user needs to register the component manually, but this is not an issue because if they are writing a component in the app space, they have all the information tomanually register it in the AppDelegate

## Changelog
[iOS][Fixed] - Do not generate the ComponentCls function in the RCTThirdPartyFabricComponentsProvider for components deined in the app.

Reviewed By: cortinico, blakef

Differential Revision: D64739896

fbshipit-source-id: 0eca818ea0198532a611377d14a3ff4c95cb5fe3
2024-10-28 14:19:41 +00:00
Blake Friedman bea4535246 Let .xcode.env.local NODE_BINARY handle path spaces
Summary:
For users who may have node installed in a path with a space, this requires escaping.  For example:

```
NODE_BINARY=/Users/blakef/Library/Application Support/fnm/node-versions/v20.12.0/installation/bin/node
```

Needs to be:

```
NODE_BINARY=/Users/blakef/Library/Application\ Support/fnm/node-versions/v20.12.0/installation/bin/node
```

# Changelog
[iOS][Fixed] Generated NODE_BINARY in .xcode.env.local now supports paths with a space

Reviewed By: cipolleschi

Differential Revision: D64080118

fbshipit-source-id: 1045473e4fd284fc570fa538984618630be1af6d
2024-10-28 14:19:34 +00:00
Blake Friedman 699a94d938 Update Podfile.lock
Changelog: [Internal]
2024-10-23 13:58:04 -07:00
React Native Bot 2e10ba945f Release 0.76.0
#publish-packages-to-npm&latest
2024-10-23 16:09:59 +00:00
Riccardo Cipolleschi 007a8e12b8 [LOCAL] Fix template publishing (#47116) 2024-10-18 11:13:11 +01:00
Riccardo Cipolleschi 1be8c51173 [LOCAL] Bump Podfile.lock 2024-10-17 19:06:26 +01:00
React Native Bot 9f9e1a41ca Release 0.76.0-rc.6
#publish-packages-to-npm&next
2024-10-17 15:57:42 +00:00
Riccardo Cipolleschi c967deaa2d Revert "Fix Android AlertFragment Title Accessibility (#45395)"
This reverts commit 80a3ed7d0c.
2024-10-17 16:08:03 +01:00
Nicola Corti 55671c00e5 [0.76] Undo breaking change on UIManager eventDispatcher accessor (#47090)
Summary:

Whe migrating this interface to Kotlin we've subtly introduced a breaking change which is causing a lot of breakages in the ecosystem.

This is forcing users to do:
```
// Before
reactContext.getNativeModule(UIManagerModule::class.java)!!.eventDispatcher
// After
reactContext.getNativeModule(UIManagerModule::class.java)!!.getEventDispatcher()
```

This reverts this breaking change.

Plus the method had a generic parameters which was completely unnecessary so I'm removing it.


Changelog:
[Android] [Fixed] - Undo breaking change on UIManager eventDispatcher accessor

Reviewed By: cipolleschi

Differential Revision: D64533594
2024-10-17 14:40:04 +01:00
Nicola Corti ce1620616c Undo breaking change on ViewManagerDelegate.kt String params (#47086)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47086

When we migrated `ViewManagerDelegate` to Kotlin, we convered his string params to be `String` (rather than `String?`).

Existing implementation of this interface in OSS written in Kotlin were using `String?` due to this interface being in Java (and not being Nullsafe annotated).
Therefore now changing this interface from `String?` to `String` is a breaking change for them.

Affected libraries are:
https://github.com/search?q=%22fun+receiveCommand%28%22+%22commandId%3A+String%3F%22+%22args%3A+ReadableArray%22+language%3Akotlin+-org%3Afacebook+-is%3Afork&type=code&p=4

This prevents the breaking change and should be included in 0.76.

Changelog:
[Android] [Fixed] - Undo breaking change on ViewManagerDelegate.kt String params

Reviewed By: cipolleschi

Differential Revision: D64532446

fbshipit-source-id: aac286554ad0e35f557160f900bcbad1acc5930d
2024-10-17 14:38:33 +01:00
Blake Friedman ab0d812cc6 Update Podfile.lock
Changelog: [Internal]
2024-10-15 23:49:56 +01:00
React Native Bot 5e2f3e018c Release 0.76.0-rc.5
#publish-packages-to-npm&next
2024-10-15 17:25:47 +00:00
Blake Friedman 45ae0b44d5 Replace sh scripts with tested JS scripts to release template (#46363)
Summary:
The previous scripts to trigger the react-native-communty/template
release workflow has not been working. This is a rewrite is js, along
with some testing to make this more robust.

I've have a PR to combine the publish and tag steps in the template publication: https://github.com/react-native-community/template/pull/65, this takes advantage of that change.

Changelog: [Internal]

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

Test Plan:
1. Unit tests
2. Once the infrastructure lands in the `react-native-community/template` workflow, we can trigger a dry run.

## TODO:
- ~~Still needs to be used in the GH release workflow.~~
- ~~Template release workflow needs to land the dry_run input change.~~

## Changelog: [Internal]

Reviewed By: cipolleschi

Differential Revision: D62296008

Pulled By: blakef

fbshipit-source-id: 217326c44b1d820e36a1d847cf9ad24d228087c1
2024-10-15 14:47:59 +01:00
Alex Hunt 02b879b1e2 [0.76] Fix server.end() usage following Metro bump (#47023) 2024-10-15 11:39:02 +01:00
Blake Friedman 788dd2e681 [LOCAL] Fix cherry-pick error
Looks like #46787 wasn't picked correctly.
2024-10-15 11:26:52 +01:00
Rob Hogan 3f8d1fa286 [0.76] Update Metro to "^0.81.0" (#47013) 2024-10-15 10:13:05 +01:00
Gabriel Donadel 066128321d Make PackagerConnectionSettings class open again (#47005)
Summary:
When migrating `PackagerConnectionSettings` from Java to Kotlin in https://github.com/facebook/react-native/pull/45800 the new class ended up being declared as final, causing a breaking change in 0.76.

We should add the `open` directive to `PackagerConnectionSettings.kt` to restore the old behavior. That would be crucial for the `expo-dev-client` package, given that Expo needs to be able to extend this class in order to overwrite the `debugServerHost` value.

## Changelog:

[ANDROID] [FIXED] - Make PackagerConnectionSettings class open again

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

Test Plan: Run RNTester on Android

Reviewed By: huntie

Differential Revision: D64323645

Pulled By: cortinico

fbshipit-source-id: 6870a3dee929ba664e4c402f321f84af7704f892
2024-10-15 10:11:48 +01:00
Nicola Corti 60a2706e97 Gradle to 8.10.2 (#46656)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/46656

This contains the fix for:
- https://github.com/gradle/gradle/issues/30472

Changelog:
[Internal] [Changed] - Gradle to 8.10.2

Reviewed By: tdn120

Differential Revision: D63457979

fbshipit-source-id: 1439a9ce198c1df0dafa8f5088c079c3fb3d1543
2024-10-15 10:11:32 +01:00
Thibault Malbranche d91a12bc8b fix: override podspecs dependencies c++ version (#46888)
Summary:
Some dependencies would override C++ version like [this](https://github.com/mrousavy/react-native-vision-camera/blob/83abb0832a22b6b080f8412ed17b0992532b0eb2/VisionCamera.podspec#L36).

We force it back to be the current version set by react-native so that we are sure projects are using the correct version

[IOS] [FIXED] - Enforce we use the correct C++ version for all, even if dependency tries to set it

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

Test Plan:
This can be test via
https://github.com/Titozzz/cpp17bug

Reviewed By: blakef

Differential Revision: D64042099

Pulled By: cipolleschi

fbshipit-source-id: c36dda0a718e52e19d53c3e9d895315141cb040c
2024-10-15 10:10:41 +01:00
Alan Lee 111d013c03 fix crash for Modal not attached to window manager (2) (#46764)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/46764

This is a patched fix to earlier attempt (D63700769) and resolving crash due to`java.lang.IllegalArgumentException: View=DecorView@b9f88af[AdsManagerActivity] not attached to window manager`.
- removing ` !dialogWindow.isActive` check as it is always true resulting in always doing early return causing other bugs
- adding try/ catch instead so the code can still run but can catch the known exception without crashing

Changelog:
[Android][Fixed] - Fix crash for Modal not attached to window manager

Reviewed By: mdvacca

Differential Revision: D63712422

fbshipit-source-id: 85fb6df340eb1139f954c92b5f1daf0dc41671d2
2024-10-15 10:10:04 +01:00
Riccardo Cipolleschi 7e14ec5177 Exclude dSYM from the archive (#46472)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/46472

Currently, we are building the Debug symbols (dSYM) for hermes dSYM but we are not shipping them with the xcframework.
This is correct, because Debug symbols can increase the size of Hermes thus enalrging the iOS IPA and increasing the download time when installing pods.

We distribute the dSYM separatedly, in case users needs to symbolicate Hermes stack traces.

However the path to the dSYM still appears in the Info.plist of the universal XCFramework and this can cause issues when submitting an app to apple.

This change should remove those lines from the universal framework.

It fixes https://github.com/facebook/react-native/issues/35863

[Internal] - Remove dSYM path from Info.plist

Reviewed By: cortinico

Differential Revision: D62603425

fbshipit-source-id: 038ec3d6b056a3d6f5585c8125d0430f56f11bb9
2024-10-15 10:08:57 +01:00
171 changed files with 2607 additions and 1315 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
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 [...]
+1 -1
View File
@@ -56,7 +56,7 @@ runs:
echo "Stop recording. Saving to screen.mp4"
adb pull /sdcard/screen.mp4
- name: Store tests result
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4.3.4
with:
name: e2e_android_${{ inputs.app-id }}_report_${{ inputs.jsengine }}
path: |
@@ -0,0 +1,169 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const {
publishTemplate,
verifyPublishedTemplate,
} = require('../publishTemplate');
const mockRun = jest.fn();
const mockSleep = jest.fn();
const mockGetNpmPackageInfo = jest.fn();
const silence = () => {};
jest.mock('../utils.js', () => ({
log: silence,
run: mockRun,
sleep: mockSleep,
getNpmPackageInfo: mockGetNpmPackageInfo,
}));
const getMockGithub = () => ({
rest: {
actions: {
createWorkflowDispatch: jest.fn(),
},
},
});
describe('#publishTemplate', () => {
beforeEach(jest.clearAllMocks);
it('checks commits for magic #publish-package-to-npm&latest string and sets latest', async () => {
mockRun.mockReturnValueOnce(`
The commit message
#publish-packages-to-npm&latest`);
const github = getMockGithub();
await publishTemplate(github, '0.76.0', true);
expect(github.rest.actions.createWorkflowDispatch).toHaveBeenCalledWith({
owner: 'react-native-community',
repo: 'template',
workflow_id: 'release.yaml',
ref: '0.76-stable',
inputs: {
dry_run: true,
is_latest_on_npm: true,
version: '0.76.0',
},
});
});
it('pubished as is_latest_on_npm = false if missing magic string', async () => {
mockRun.mockReturnValueOnce(`
The commit message without magic
`);
const github = getMockGithub();
await publishTemplate(github, '0.76.0', false);
expect(github.rest.actions.createWorkflowDispatch).toHaveBeenCalledWith({
owner: 'react-native-community',
repo: 'template',
workflow_id: 'release.yaml',
ref: '0.76-stable',
inputs: {
dry_run: false,
is_latest_on_npm: false,
version: '0.76.0',
},
});
});
});
describe('#verifyPublishedTemplate', () => {
beforeEach(jest.clearAllMocks);
it("fixes versions prefixed with 'v'", async () => {
const dirtyVersion = 'v0.76.0';
const cleanVersion = '0.76.0';
await verifyPublishedTemplate(dirtyVersion);
expect(mockGetNpmPackageInfo).toHaveBeenLastCalledWith(
'@react-native-community/template',
cleanVersion,
);
});
it("waits on npm updating for version and not 'latest'", async () => {
const NOT_LATEST = false;
mockGetNpmPackageInfo
// template@<version>
.mockReturnValueOnce(Promise.reject('mock http/404'))
.mockReturnValueOnce(Promise.resolve());
mockSleep.mockReturnValueOnce(Promise.resolve()).mockImplementation(() => {
throw new Error('Should not be called again!');
});
const version = '0.77.0';
await verifyPublishedTemplate(version, NOT_LATEST);
expect(mockGetNpmPackageInfo).toHaveBeenLastCalledWith(
'@react-native-community/template',
version,
);
});
it('waits on npm updating version and latest tag', async () => {
const IS_LATEST = true;
const version = '0.77.0';
mockGetNpmPackageInfo
// template@latest → unknown tag
.mockReturnValueOnce(Promise.reject('mock http/404'))
// template@latest != version → old tag
.mockReturnValueOnce(Promise.resolve({version: '0.76.5'}))
// template@latest == version → correct tag
.mockReturnValueOnce(Promise.resolve({version}));
mockSleep
.mockReturnValueOnce(Promise.resolve())
.mockReturnValueOnce(Promise.resolve())
.mockImplementation(() => {
throw new Error('Should not be called again!');
});
await verifyPublishedTemplate(version, IS_LATEST);
expect(mockGetNpmPackageInfo).toHaveBeenCalledWith(
'@react-native-community/template',
'latest',
);
});
describe('timeouts', () => {
let mockProcess;
beforeEach(() => {
mockProcess = jest.spyOn(process, 'exit').mockImplementation(code => {
throw new Error(`process.exit(${code}) called!`);
});
});
afterEach(() => mockProcess.mockRestore());
it('will timeout if npm does not update package version after a set number of retries', async () => {
const RETRIES = 2;
mockGetNpmPackageInfo.mockReturnValue(Promise.reject('mock http/404'));
mockSleep.mockReturnValue(Promise.resolve());
await expect(() =>
verifyPublishedTemplate('0.77.0', true, RETRIES),
).rejects.toThrowError('process.exit(1) called!');
expect(mockGetNpmPackageInfo).toHaveBeenCalledTimes(RETRIES);
});
it('will timeout if npm does not update latest tag after a set number of retries', async () => {
const RETRIES = 7;
const IS_LATEST = true;
mockGetNpmPackageInfo.mockReturnValue(
Promise.resolve({version: '0.76.5'}),
);
mockSleep.mockReturnValue(Promise.resolve());
await expect(async () => {
await verifyPublishedTemplate('0.77.0', IS_LATEST, RETRIES);
}).rejects.toThrowError('process.exit(1) called!');
expect(mockGetNpmPackageInfo).toHaveBeenCalledTimes(RETRIES);
});
});
});
+104
View File
@@ -0,0 +1,104 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const {run, sleep, getNpmPackageInfo, log} = require('./utils.js');
const TAG_AS_LATEST_REGEX = /#publish-packages-to-npm&latest/;
/**
* Should this commit be `latest` on npm?
*/
function isLatest() {
const commitMessage = run('git log -n1 --pretty=%B');
return TAG_AS_LATEST_REGEX.test(commitMessage);
}
module.exports.isLatest = isLatest;
/**
* Create a Github Action to publish the community template matching the released version
* of React Native.
*/
module.exports.publishTemplate = async (github, version, dryRun = true) => {
log(`📤 Get the ${TEMPLATE_NPM_PKG} repo to publish ${version}`);
const is_latest_on_npm = isLatest();
const majorMinor = /^v?(\d+\.\d+)/.exec(version);
if (!majorMinor) {
log(`🔥 can't capture MAJOR.MINOR from '${version}', giving up.`);
process.exit(1);
}
// MAJOR.MINOR-stable
const ref = `${majorMinor[1]}-stable`;
await github.rest.actions.createWorkflowDispatch({
owner: 'react-native-community',
repo: 'template',
workflow_id: 'release.yaml',
ref,
inputs: {
dry_run: dryRun,
is_latest_on_npm,
// 0.75.0-rc.0, note no 'v' prefix
version: version.replace(/^v/, ''),
},
});
};
const SLEEP_S = 10;
const MAX_RETRIES = 3 * 6; // 3 minutes
const TEMPLATE_NPM_PKG = '@react-native-community/template';
/**
* Will verify that @latest and the @<version> have been published.
*
* NOTE: This will infinitely query each step until successful, make sure the
* calling job has a timeout.
*/
module.exports.verifyPublishedTemplate = async (
version,
latest = false,
retries = MAX_RETRIES,
) => {
version = version.replace(/^v/, '');
log(`🔍 Is ${TEMPLATE_NPM_PKG}@${version} on npm?`);
let count = retries;
while (count-- > 0) {
try {
const json = await getNpmPackageInfo(
TEMPLATE_NPM_PKG,
latest ? 'latest' : version,
);
log(`🎉 Found ${TEMPLATE_NPM_PKG}@${version} on npm`);
if (!latest) {
return;
}
if (json.version === version) {
log(`🎉 ${TEMPLATE_NPM_PKG}@latest → ${json.version} on npm`);
return;
}
log(
`🐌 ${TEMPLATE_NPM_PKG}@latest → ${json.version} on npm and not ${version} as expected, retrying...`,
);
} catch (e) {
log(`Nope, fetch failed: ${e.message}`);
}
await sleep(SLEEP_S);
}
let msg = `🚨 Timed out when trying to verify ${TEMPLATE_NPM_PKG}@${version} on npm`;
if (latest) {
msg += ' and latest tag points to this version.';
}
log(msg);
process.exit(1);
};
+29
View File
@@ -0,0 +1,29 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
const {execSync} = require('child_process');
function run(cmd) {
return execSync(cmd, 'utf8').toString().trim();
}
module.exports.run = run;
async function sleep(seconds) {
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
}
module.exports.sleep = sleep;
async function getNpmPackageInfo(pkg, versionOrTag) {
return fetch(`https://registry.npmjs.org/${pkg}/${versionOrTag}`).then(resp =>
resp.json(),
);
}
module.exports.getNpmPackageInfo = getNpmPackageInfo;
module.exports.log = (...args) => console.log(...args);
+15 -38
View File
@@ -191,46 +191,23 @@ jobs:
gha-npm-token: ${{ env.GHA_NPM_TOKEN }}
- name: Publish @react-native-community/template
id: publish-template-to-npm
shell: bash
run: |
COMMIT_MSG=$(git log -n1 --pretty=%B);
if grep -q '#publish-packages-to-npm&latest' <<< "$COMMIT_MSG"; then
echo "TAG=latest" >> $GITHUB_OUTPUT
IS_LATEST=true
else
IS_LATEST=false
fi
# Go from v0.75.0-rc.4 -> 0.75-stable, which is the template's branching scheme
VERSION=$(grep -oE '\d+\.\d+' <<< "${{ github.ref_name }}" | { read version; echo "$version-stable"; })
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
curl -L https://api.github.com/repos/react-native-community/template/actions/workflows/release.yaml/dispatches
-H "Accept: application/vnd.github.v3+json" \
-H "Authorization: Bearer $REACT_NATIVE_BOT_GITHUB_TOKEN" \
-d "{\"ref\":\"$VERSION\",\"inputs\":{\"version\":\"${{ github.ref_name }}\",\"is_latest_on_npm\":\"$IS_LATEST\"}}"
uses: actions/github-script@v6
with:
github-token: ${{ secrets.REACT_NATIVE_BOT_GITHUB_TOKEN }}
script: |
const {publishTemplate} = require('./.github/workflow-scripts/publishTemplate.js')
const version = "${{ github.ref_name }}"
const isDryRun = false
await publishTemplate(github, version, isDryRun);
- name: Wait for template to be published
timeout-minutes: 3
env:
VERSION: ${{ steps.publish-template-to-npm.outputs.VERSION }}
TAG: ${{ steps.publish-template-to-npm.outputs.TAG }}
shell: bash
run: |
echo "Waiting until @react-native-community/template is published to npm"
while true; do
if curl -o /dev/null -s -f "https://registry.npmjs.org/@react-native-community/template/$VERSION"; then
echo "Confirm that @react-native-community/template@$VERSION is published on npm"
break
fi
sleep 10
done
while [ "$TAG" == "latest" ]; do
CURRENT=$(curl -s "https://registry.npmjs.org/react-native/latest" | jq -r '.version');
if [ "$CURRENT" == "$VERSION" ]; then
echo "Confirm that @react-native-community/template@latest == $VERSION on npm"
break
fi
sleep 10
done
uses: actions/github-script@v6
with:
github-token: ${{ secrets.REACT_NATIVE_BOT_GITHUB_TOKEN }}
script: |
const {verifyPublishedTemplate, isLatest} = require('./.github/workflow-scripts/publishTemplate.js')
const version = "${{ github.ref_name }}"
await verifyPublishedTemplate(version, isLatest());
- name: Update rn-diff-purge to generate upgrade-support diff
run: |
curl -X POST https://api.github.com/repos/react-native-community/rn-diff-purge/dispatches \
+2
View File
@@ -5,3 +5,5 @@ ruby ">= 2.6.10"
gem 'cocoapods', '~> 1.13', '!= 1.15.0', '!= 1.15.1'
gem 'activesupport', '>= 6.1.7.5', '< 7.1.0'
gem 'xcodeproj', '< 1.26.0'
gem 'concurrent-ruby', '< 1.3.4'
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
+4 -4
View File
@@ -49,8 +49,8 @@
"@definitelytyped/dtslint": "^0.0.127",
"@jest/create-cache-key-function": "^29.6.3",
"@pkgjs/parseargs": "^0.11.0",
"@react-native/metro-babel-transformer": "0.76.0-rc.4",
"@react-native/metro-config": "0.76.0-rc.4",
"@react-native/metro-babel-transformer": "0.76.8",
"@react-native/metro-config": "0.76.8",
"@tsconfig/node18": "1.0.1",
"@types/react": "^18.2.6",
"@typescript-eslint/parser": "^7.1.1",
@@ -86,8 +86,8 @@
"jest": "^29.6.3",
"jest-junit": "^10.0.0",
"jscodeshift": "^0.14.0",
"metro-babel-register": "^0.81.0-alpha.2",
"metro-memory-fs": "^0.81.0-alpha.2",
"metro-babel-register": "^0.81.0",
"metro-memory-fs": "^0.81.0",
"micromatch": "^4.0.4",
"mkdirp": "^0.5.1",
"node-fetch": "^2.2.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/assets-registry",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"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.76.0-rc.4",
"version": "0.76.8",
"description": "Babel plugin to generate native module and view manager code for React Native.",
"license": "MIT",
"repository": {
@@ -25,7 +25,7 @@
"index.js"
],
"dependencies": {
"@react-native/codegen": "0.76.0-rc.4"
"@react-native/codegen": "0.76.8"
},
"devDependencies": {
"@babel/core": "^7.25.2"
+11 -10
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/community-cli-plugin",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"description": "Core CLI commands for React Native",
"keywords": [
"react-native",
@@ -22,25 +22,26 @@
"dist"
],
"dependencies": {
"@react-native/dev-middleware": "0.76.0-rc.4",
"@react-native/metro-babel-transformer": "0.76.0-rc.4",
"@react-native/dev-middleware": "0.76.8",
"@react-native/metro-babel-transformer": "0.76.8",
"chalk": "^4.0.0",
"execa": "^5.1.1",
"invariant": "^2.2.4",
"metro": "^0.81.0-alpha.2",
"metro-config": "^0.81.0-alpha.2",
"metro-core": "^0.81.0-alpha.2",
"metro": "^0.81.0",
"metro-config": "^0.81.0",
"metro-core": "^0.81.0",
"node-fetch": "^2.2.0",
"readline": "^1.3.0"
"readline": "^1.3.0",
"semver": "^7.1.3"
},
"devDependencies": {
"metro-resolver": "^0.81.0-alpha.2"
"metro-resolver": "^0.81.0"
},
"peerDependencies": {
"@react-native-community/cli-server-api": "*"
"@react-native-community/cli": "*"
},
"peerDependenciesMeta": {
"@react-native-community/cli-server-api": {
"@react-native-community/cli": {
"optional": true
}
},
@@ -140,7 +140,7 @@ async function buildBundleWithConfig(
args.assetCatalogDest,
);
} finally {
server.end();
await server.end();
}
}
@@ -12,6 +12,8 @@
import type {NextHandleFunction, Server} from 'connect';
import type {TerminalReportableEvent} from 'metro/src/lib/TerminalReporter';
import typeof * as CLIServerAPI from '@react-native-community/cli-server-api';
import {logger} from '../../utils/logger';
type MiddlewareReturn = {
@@ -65,11 +67,26 @@ const communityMiddlewareFallback = {
// Attempt to use the community middleware if it exists, but fallback to
// the stubs if it doesn't.
try {
const community = require('@react-native-community/cli-server-api');
communityMiddlewareFallback.indexPageMiddleware =
community.indexPageMiddleware;
// `@react-native-community/cli` is an optional peer dependency of this
// package, and should be a dev dependency of the host project (via the
// community template's package.json).
const communityCliPath = require.resolve('@react-native-community/cli');
// `@react-native-community/cli-server-api` is a dependency of
// `@react-native-community/cli`, but is not re-exported by it, so we need
// to resolve the former through the latter.
const communityCliServerApiPath = require.resolve(
'@react-native-community/cli-server-api',
{paths: [communityCliPath]},
);
// $FlowIgnore[unsupported-syntax] dynamic import
const communityCliServerApi: CLIServerAPI = require(
communityCliServerApiPath,
);
communityMiddlewareFallback.createDevServerMiddleware =
community.createDevServerMiddleware;
communityCliServerApi.createDevServerMiddleware;
communityMiddlewareFallback.indexPageMiddleware =
communityCliServerApi.indexPageMiddleware;
} catch {
logger.debug(`⚠️ Unable to find @react-native-community/cli-server-api
Starting the server without the community middleware.`);
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/core-cli-utils",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"description": "React Native CLI library for Frameworks to build on",
"license": "MIT",
"main": "./src/index.flow.js",
+2 -2
View File
@@ -1,5 +1,5 @@
@generated SignedSource<<2ae3cc4cc779b7d9b387f7d8ba402248>>
Git revision: ce5d32a14f18a8fc4bdaf787eb46800b7f73e524
@generated SignedSource<<e1b6cf83a0e98051a2f929ad191b1d6c>>
Git revision: f1f917329169ff3d2c12bcfaea7e301b71c3149e
Built with --nohooks: false
Is local checkout: false
Remote URL: https://github.com/facebookexperimental/rn-chrome-devtools-frontend
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/debugger-frontend",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"description": "Debugger frontend for React Native based on Chrome DevTools",
"keywords": [
"react-native",
+3 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/dev-middleware",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"description": "Dev server middleware for React Native",
"keywords": [
"react-native",
@@ -23,11 +23,12 @@
],
"dependencies": {
"@isaacs/ttlcache": "^1.4.1",
"@react-native/debugger-frontend": "0.76.0-rc.4",
"@react-native/debugger-frontend": "0.76.8",
"chrome-launcher": "^0.15.2",
"chromium-edge-launcher": "^0.2.0",
"connect": "^3.6.5",
"debug": "^2.2.0",
"invariant": "^2.2.4",
"nullthrows": "^1.1.1",
"open": "^7.0.3",
"selfsigned": "^2.4.1",
@@ -37,7 +37,7 @@ const WS_DEBUGGER_URL = '/inspector/debug';
const PAGES_LIST_JSON_URL = '/json';
const PAGES_LIST_JSON_URL_2 = '/json/list';
const PAGES_LIST_JSON_VERSION_URL = '/json/version';
const MAX_PONG_LATENCY_MS = 5000;
const MAX_PONG_LATENCY_MS = 60000;
const DEBUGGER_HEARTBEAT_INTERVAL_MS = 10000;
const INTERNAL_ERROR_CODE = 1011;
+2 -2
View File
@@ -39,7 +39,7 @@ module.exports = {
overrides: [
{
files: ['*.js'],
parser: 'hermes-eslint',
parser: '@babel/eslint-parser',
plugins: ['ft-flow'],
rules: {
// Flow Plugin
@@ -51,7 +51,7 @@ module.exports = {
},
{
files: ['*.jsx'],
parser: 'hermes-eslint',
parser: '@babel/eslint-parser',
},
{
files: ['*.ts', '*.tsx'],
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-config",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"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.76.0-rc.4",
"@react-native/eslint-plugin": "0.76.8",
"@typescript-eslint/eslint-plugin": "^7.1.1",
"@typescript-eslint/parser": "^7.1.1",
"eslint-config-prettier": "^8.5.0",
@@ -31,8 +31,7 @@
"eslint-plugin-jest": "^27.9.0",
"eslint-plugin-react": "^7.30.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-native": "^4.0.0",
"hermes-eslint": "^0.23.1"
"eslint-plugin-react-native": "^4.0.0"
},
"peerDependencies": {
"eslint": ">=8",
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-plugin",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"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.76.0-rc.4",
"version": "0.76.8",
"description": "ESLint rules to validate NativeModule and Component Specs",
"license": "MIT",
"repository": {
@@ -27,7 +27,7 @@
"@babel/core": "^7.25.2",
"@babel/plugin-transform-flow-strip-types": "^7.25.2",
"@babel/preset-flow": "^7.24.7",
"@react-native/codegen": "0.76.0-rc.4",
"@react-native/codegen": "0.76.8",
"make-dir": "^2.1.0",
"pirates": "^4.0.1",
"source-map-support": "0.5.0"
@@ -4,7 +4,7 @@ gson = "2.8.9"
guava = "31.0.1-jre"
javapoet = "1.13.0"
junit = "4.13.2"
kotlin = "1.9.24"
kotlin = "1.9.25"
assertj = "3.25.1"
[libraries]
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-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.76.0-rc.4",
"version": "0.76.8",
"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)
}
}
@@ -59,15 +59,15 @@ abstract class GenerateAutolinkingNewArchitecturesFileTask : DefaultTask() {
val cxxModuleCMakeListsPath = dep.cxxModuleCMakeListsPath
if (libraryName != null && cmakeListsPath != null) {
// If user provided a custom cmakeListsPath, let's honor it.
val nativeFolderPath = cmakeListsPath.replace("CMakeLists.txt", "")
val nativeFolderPath = sanitizeCmakeListsPath(cmakeListsPath)
addDirectoryString +=
"add_subdirectory($nativeFolderPath ${libraryName}_autolinked_build)"
"add_subdirectory(\"$nativeFolderPath\" ${libraryName}_autolinked_build)"
}
if (cxxModuleCMakeListsPath != null) {
// If user provided a custom cxxModuleCMakeListsPath, let's honor it.
val nativeFolderPath = cxxModuleCMakeListsPath.replace("CMakeLists.txt", "")
val nativeFolderPath = sanitizeCmakeListsPath(cxxModuleCMakeListsPath)
addDirectoryString +=
"\nadd_subdirectory($nativeFolderPath ${libraryName}_cxxmodule_autolinked_build)"
"\nadd_subdirectory(\"$nativeFolderPath\" ${libraryName}_cxxmodule_autolinked_build)"
}
addDirectoryString
}
@@ -159,6 +159,9 @@ abstract class GenerateAutolinkingNewArchitecturesFileTask : DefaultTask() {
const val COMPONENT_DESCRIPTOR_FILENAME = "ComponentDescriptors.h"
const val COMPONENT_INCLUDE_PATH = "react/renderer/components"
internal fun sanitizeCmakeListsPath(cmakeListsPath: String): String =
cmakeListsPath.replace("CMakeLists.txt", "").replace(" ", "\\ ")
// language=cmake
val CMAKE_TEMPLATE =
"""
@@ -11,6 +11,7 @@ import com.facebook.react.model.ModelAutolinkingConfigJson
import com.facebook.react.model.ModelAutolinkingDependenciesJson
import com.facebook.react.model.ModelAutolinkingDependenciesPlatformAndroidJson
import com.facebook.react.model.ModelAutolinkingDependenciesPlatformJson
import com.facebook.react.tasks.GenerateAutolinkingNewArchitecturesFileTask.Companion.sanitizeCmakeListsPath
import com.facebook.react.tests.createTestTask
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
@@ -145,9 +146,9 @@ class GenerateAutolinkingNewArchitecturesFileTaskTest {
# or link against a old prefab target (this is needed for React Native 0.76 on).
set(REACTNATIVE_MERGED_SO true)
add_subdirectory(./a/directory/ aPackage_autolinked_build)
add_subdirectory(./another/directory/ anotherPackage_autolinked_build)
add_subdirectory(./another/directory/cxx/ anotherPackage_cxxmodule_autolinked_build)
add_subdirectory("./a/directory/" aPackage_autolinked_build)
add_subdirectory("./another/directory/with\ spaces/" anotherPackage_autolinked_build)
add_subdirectory("./another/directory/cxx/" anotherPackage_cxxmodule_autolinked_build)
set(AUTOLINKED_LIBRARIES
react_codegen_aPackage
@@ -258,6 +259,24 @@ class GenerateAutolinkingNewArchitecturesFileTaskTest {
.trimIndent())
}
@Test
fun sanitizeCmakeListsPath_withPathEndingWithFileName_removesFilename() {
val input = "./a/directory/CMakeLists.txt"
assertThat(sanitizeCmakeListsPath(input)).isEqualTo("./a/directory/")
}
@Test
fun sanitizeCmakeListsPath_withSpaces_removesSpaces() {
val input = "./a/dir ectory/with spaces/"
assertThat(sanitizeCmakeListsPath(input)).isEqualTo("./a/dir\\ ectory/with\\ spaces/")
}
@Test
fun sanitizeCmakeListsPath_withPathEndingWithFileNameAndSpaces_sanitizesIt() {
val input = "./a/dir ectory/CMakeLists.txt"
assertThat(sanitizeCmakeListsPath(input)).isEqualTo("./a/dir\\ ectory/")
}
private val testDependencies =
listOf(
ModelAutolinkingDependenciesPlatformAndroidJson(
@@ -276,7 +295,7 @@ class GenerateAutolinkingNewArchitecturesFileTaskTest {
buildTypes = emptyList(),
libraryName = "anotherPackage",
componentDescriptors = listOf("AnotherPackageComponentDescriptor"),
cmakeListsPath = "./another/directory/CMakeLists.txt",
cmakeListsPath = "./another/directory/with spaces/CMakeLists.txt",
cxxModuleCMakeListsPath = "./another/directory/cxx/CMakeLists.txt",
cxxModuleHeaderName = "AnotherCxxModule",
cxxModuleCMakeListsModuleName = "another_cxxModule",
+2
View File
@@ -4,3 +4,5 @@ ruby ">= 2.6.10"
gem 'cocoapods', '~> 1.13', '!= 1.15.0', '!= 1.15.1'
gem 'activesupport', '>= 6.1.7.5', '< 7.1.0'
gem 'xcodeproj', '< 1.26.0'
gem 'concurrent-ruby', '< 1.3.4'
+1 -1
View File
@@ -12,7 +12,7 @@ buildscript {
compileSdkVersion = 35
targetSdkVersion = 34
ndkVersion = "26.1.10909125"
kotlinVersion = "1.9.24"
kotlinVersion = "1.9.25"
}
repositories {
google()
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-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.76.0-rc.4",
"version": "0.76.8",
"private": true,
"scripts": {
"bootstrap": "node ./cli.js bootstrap",
@@ -13,16 +13,16 @@
},
"dependencies": {
"react": "18.3.1",
"react-native": "0.76.0-rc.4"
"react-native": "0.76.8"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.3",
"@babel/runtime": "^7.25.0",
"@react-native/babel-preset": "0.76.0-rc.4",
"@react-native/core-cli-utils": "0.76.0-rc.4",
"@react-native/eslint-config": "0.76.0-rc.4",
"@react-native/metro-config": "0.76.0-rc.4",
"@react-native/babel-preset": "0.76.8",
"@react-native/core-cli-utils": "0.76.8",
"@react-native/eslint-config": "0.76.8",
"@react-native/metro-config": "0.76.8",
"babel-jest": "^29.6.3",
"chalk": "^4.1.2",
"commander": "^12.0.0",
@@ -1,6 +1,6 @@
{
"name": "@react-native/hermes-inspector-msggen",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"private": true,
"description": "Hermes Inspector Message Generator for React Native",
"license": "MIT",
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/metro-config",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"description": "Metro configuration for React Native.",
"license": "MIT",
"repository": {
@@ -26,9 +26,9 @@
"dist"
],
"dependencies": {
"@react-native/js-polyfills": "0.76.0-rc.4",
"@react-native/metro-babel-transformer": "0.76.0-rc.4",
"metro-config": "^0.81.0-alpha.2",
"metro-runtime": "^0.81.0-alpha.2"
"@react-native/js-polyfills": "0.76.8",
"@react-native/metro-babel-transformer": "0.76.8",
"metro-config": "^0.81.0",
"metro-runtime": "^0.81.0"
}
}
-1
View File
@@ -89,7 +89,6 @@ export function getDefaultConfig(projectRoot: string): ConfigT {
babelTransformerPath: require.resolve(
'@react-native/metro-babel-transformer',
),
hermesParser: true,
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/normalize-colors",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"description": "Color normalization for React Native.",
"license": "MIT",
"repository": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/js-polyfills",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"description": "Polyfills for React Native.",
"license": "MIT",
"repository": {
@@ -1,6 +1,6 @@
{
"name": "@react-native/babel-preset",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"description": "Babel preset for React Native applications",
"main": "src/index.js",
"repository": {
@@ -55,8 +55,8 @@
"@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.76.0-rc.4",
"babel-plugin-syntax-hermes-parser": "^0.23.1",
"@react-native/babel-plugin-codegen": "0.76.8",
"babel-plugin-syntax-hermes-parser": "^0.25.1",
"babel-plugin-transform-flow-enums": "^0.0.2",
"react-refresh": "^0.14.0"
},
+1 -1
View File
@@ -25,7 +25,7 @@ function isTSXSource(fileName) {
const loose = true;
const defaultPlugins = [
[require('babel-plugin-syntax-hermes-parser')],
[require('babel-plugin-syntax-hermes-parser'), {parseLangTypes: 'flow'}],
[require('babel-plugin-transform-flow-enums')],
[require('@babel/plugin-transform-block-scoping')],
[require('@babel/plugin-transform-class-properties'), {loose}],
@@ -1,6 +1,6 @@
{
"name": "@react-native/metro-babel-transformer",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"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.76.0-rc.4",
"@react-native/babel-preset": "0.76.8",
"hermes-parser": "0.23.1",
"nullthrows": "^1.1.1"
},
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@react-native/bots",
"description": "React Native Bots",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"private": true,
"license": "MIT",
"repository": {
@@ -1,6 +1,6 @@
{
"name": "@react-native/codegen-typescript-test",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"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.76.0-rc.4"
"@react-native/codegen": "0.76.8"
},
"devDependencies": {
"@babel/core": "^7.25.2",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/codegen",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"description": "Code generation tools for React Native",
"license": "MIT",
"repository": {
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "react-native-info",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"main": "build/index.js",
"license": "MIT",
"private": true,
@@ -17,10 +17,10 @@
"directory": "packages/react-native-info"
},
"dependencies": {
"@react-native-community/cli-config": "15.0.0-alpha.2",
"@react-native-community/cli-platform-apple": "15.0.0-alpha.2",
"@react-native-community/cli-tools": "15.0.0-alpha.2",
"@react-native-community/cli-types": "15.0.0-alpha.2",
"@react-native-community/cli-config": "15.0.1",
"@react-native-community/cli-platform-apple": "15.0.1",
"@react-native-community/cli-tools": "15.0.1",
"@react-native-community/cli-types": "15.0.1",
"commander": "^12.0.0",
"fs-extra": "^11.2.0",
"yaml": "^2.4.1"
@@ -7,8 +7,8 @@
plugins {
id("com.facebook.react")
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
id("com.android.library")
id("org.jetbrains.kotlin.android")
}
android {
@@ -1,11 +1,12 @@
{
"name": "@react-native/popup-menu-android",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"description": "PopupMenu for the Android platform",
"main": "index.js",
"files": [
"js",
"android",
"react-native.config.js",
"!android/build",
"!**/__tests__",
"!**/__fixtures__",
@@ -15,9 +16,12 @@
"react-native",
"android"
],
"scripts": {
"prepublishOnly": "node ./scripts/prepublish-popup-menu-android.js"
},
"license": "MIT",
"devDependencies": {
"@react-native/codegen": "0.76.0-rc.4"
"@react-native/codegen": "0.76.8"
},
"peerDependencies": {
"@types/react": "^18.2.6",
@@ -0,0 +1,46 @@
/**
* 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.
*/
/*
* This script is used to update the android/build.gradle.kts file
* with the versions from the libs.versions.toml file.
*
* This is needed because this package is consumed from source from
* external users and we don't want to have several SDK version around to
* maintain.
*
* It's invoked as a prepublish script for this package.
*/
function extractVersion(tomlContent, regex) {
const match = tomlContent.match(regex);
return match && match[1] ? match[1] : null;
}
const fs = require('fs');
const buildGradleKtsPath = 'android/build.gradle.kts';
const libsVersionsTomlPath = '../react-native/gradle/libs.versions.toml';
console.log(`Updating ${buildGradleKtsPath} with versions from ${libsVersionsTomlPath}...`);
let gradleContent = fs.readFileSync(buildGradleKtsPath, 'utf8');
const tomlContent = fs.readFileSync(libsVersionsTomlPath, 'utf8');
const compileSdk = extractVersion(tomlContent, /compileSdk\s*=\s*"(\d+)"/);
const minSdk = extractVersion(tomlContent, /minSdk\s*=\s*"(\d+)"/);
const buildTools = extractVersion(tomlContent, /buildTools\s*=\s*"([\d.]+)"/);
gradleContent = gradleContent
.replace('libs.versions.compileSdk.get().toInt()', compileSdk)
.replace('libs.versions.minSdk.get().toInt()', minSdk)
.replace('libs.versions.buildTools.get()', `"${buildTools}"`)
.replace('project(":packages:react-native:ReactAndroid")', '"com.facebook.react:react-android"');
fs.writeFileSync(buildGradleKtsPath, gradleContent);
console.log('Done!');
@@ -1,6 +1,6 @@
{
"name": "@react-native/oss-library-example",
"version": "0.76.0-rc.4",
"version": "0.76.8",
"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.76.0-rc.4",
"react-native": "0.76.0-rc.4"
"@react-native/babel-preset": "0.76.8",
"react-native": "0.76.8"
},
"peerDependencies": {
"react": "*",
@@ -1,7 +1,7 @@
{
"name": "@react-native/test-renderer",
"private": true,
"version": "0.76.0-rc.4",
"version": "0.76.8",
"description": "A Test rendering library for React Native",
"license": "MIT",
"devDependencies": {
@@ -76,15 +76,11 @@
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [self createRootViewController];
[self setRootView:rootView toRootViewController:rootViewController];
_window.windowScene.delegate = self;
_window.rootViewController = rootViewController;
[_window makeKeyAndVisible];
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Noop
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
[NSException raise:@"RCTBridgeDelegate::sourceURLForBridge not implemented"
@@ -53,7 +53,9 @@ RCTAppSetupDefaultRootView(RCTBridge *bridge, NSString *moduleName, NSDictionary
id<RCTSurfaceProtocol> surface = [[RCTFabricSurface alloc] initWithBridge:bridge
moduleName:moduleName
initialProperties:initialProperties];
return [[RCTSurfaceHostingProxyRootView alloc] initWithSurface:surface];
UIView *rootView = [[RCTSurfaceHostingProxyRootView alloc] initWithSurface:surface];
[surface start];
return rootView;
}
return [[RCTRootView alloc] initWithBridge:bridge moduleName:moduleName initialProperties:initialProperties];
}
@@ -159,9 +159,8 @@ static NSDictionary *updateInitialProps(NSDictionary *initialProps, BOOL isFabri
RCTFabricSurface *surface = [self.reactHost createSurfaceWithModuleName:moduleName initialProperties:initProps];
RCTSurfaceHostingProxyRootView *surfaceHostingProxyRootView = [[RCTSurfaceHostingProxyRootView alloc]
initWithSurface:surface
sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact];
RCTSurfaceHostingProxyRootView *surfaceHostingProxyRootView =
[[RCTSurfaceHostingProxyRootView alloc] initWithSurface:surface];
surfaceHostingProxyRootView.backgroundColor = [UIColor systemBackgroundColor];
if (self->_configuration.customizeRootView != nil) {
@@ -63,7 +63,7 @@ Pod::Spec.new do |s|
"CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(),
"DEFINES_MODULE" => "YES"
}
s.user_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/Headers/Private/React-Core\""}
s.user_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/Headers/Private/React-Core\" \"$(PODS_ROOT)/Headers/Private/Yoga\""}
s.dependency "React-Core"
s.dependency "RCT-Folly", folly_version
+2 -2
View File
@@ -16,8 +16,8 @@ const version: $ReadOnly<{
}> = {
major: 0,
minor: 76,
patch: 0,
prerelease: 'rc.4',
patch: 8,
prerelease: null,
};
module.exports = {version};
+1 -7
View File
@@ -21,13 +21,7 @@ ExceptionsManager.installConsoleErrorReporter();
if (!global.__fbDisableExceptionsManager) {
const handleError = (e: mixed, isFatal: boolean) => {
try {
// TODO(T196834299): We should really use a c++ turbomodule for this
if (
!global.RN$handleException ||
!global.RN$handleException(e, isFatal)
) {
ExceptionsManager.handleException(e, isFatal);
}
ExceptionsManager.handleException(e, isFatal);
} catch (ee) {
console.log('Failed to print error: ', ee.message);
throw e;
@@ -477,7 +477,15 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image, CGSize size, CGFloat scal
// Add missing png extension
if (request.URL.fileURL && request.URL.pathExtension.length == 0) {
mutableRequest.URL = [request.URL URLByAppendingPathExtension:@"png"];
// Check if there exists a file with that url on disk already
// This should fix issue https://github.com/facebook/react-native/issues/46870
if ([[NSFileManager defaultManager] fileExistsAtPath:request.URL.path]) {
mutableRequest.URL = request.URL;
} else {
// This is the default behavior in case there is no file on disk with no extension.
// We assume that the extension is `png`.
mutableRequest.URL = [request.URL URLByAppendingPathExtension:@"png"];
}
}
if (_redirectDelegate != nil) {
mutableRequest.URL = [_redirectDelegate redirectAssetsURL:mutableRequest.URL];
+2 -2
View File
@@ -82,9 +82,9 @@ let warningFilter: WarningFilter = function (format) {
return {
finalFormat: format,
forceDialogImmediately: false,
suppressDialog_LEGACY: true,
suppressDialog_LEGACY: false,
suppressCompletely: false,
monitorEvent: 'unknown',
monitorEvent: 'warning_unhandled',
monitorListVersion: 0,
monitorSampleRate: 1,
};
@@ -11,45 +11,29 @@
import {
DoesNotUseKey,
FragmentWithProp,
ManualConsoleError,
ManualConsoleErrorWithStack,
} from './__fixtures__/ReactWarningFixtures';
import * as React from 'react';
const LogBoxData = require('../Data/LogBoxData');
const TestRenderer = require('react-test-renderer');
const installLogBox = () => {
const LogBox = require('../LogBox');
const ExceptionsManager = require('../../Core/ExceptionsManager.js');
const installLogBox = () => {
const LogBox = require('../LogBox').default;
LogBox.install();
};
const uninstallLogBox = () => {
const LogBox = require('../LogBox');
const LogBox = require('../LogBox').default;
LogBox.uninstall();
};
const BEFORE_SLASH_RE = /(?:\/[a-zA-Z]+\/)(.+?)(?:\/.+)\//;
const cleanPath = message => {
return message.replace(BEFORE_SLASH_RE, '/path/to/');
};
const cleanLog = logs => {
return logs.map(log => {
return {
...log,
componentStack: log.componentStack.map(stack => ({
...stack,
fileName: cleanPath(stack.fileName),
})),
};
});
};
// TODO(T71117418): Re-enable skipped LogBox integration tests once React component
// stack frames are the same internally and in open source.
// eslint-disable-next-line jest/no-disabled-tests
describe.skip('LogBox', () => {
// TODO: we can remove all the symetric matchers once OSS lands component stack frames.
// For now, the component stack parsing differs in ways we can't easily detect in this test.
describe('LogBox', () => {
const {error, warn} = console;
const mockError = jest.fn();
const mockWarn = jest.fn();
@@ -57,10 +41,14 @@ describe.skip('LogBox', () => {
beforeEach(() => {
jest.resetModules();
jest.restoreAllMocks();
jest.spyOn(console, 'error').mockImplementation(() => {});
mockError.mockClear();
mockWarn.mockClear();
// Reset ExceptionManager patching.
if (console._errorOriginal) {
console._errorOriginal = null;
}
(console: any).error = mockError;
(console: any).warn = mockWarn;
});
@@ -79,7 +67,10 @@ describe.skip('LogBox', () => {
// so we can assert on what React logs.
jest.spyOn(console, 'error');
const output = TestRenderer.create(<DoesNotUseKey />);
let output;
TestRenderer.act(() => {
output = TestRenderer.create(<DoesNotUseKey />);
});
// The key error should always be the highest severity.
// In LogBox, we expect these errors to:
@@ -88,16 +79,37 @@ describe.skip('LogBox', () => {
// - Pass to console.error, with a "Warning" prefix so it does not pop a RedBox.
expect(output).toBeDefined();
expect(mockWarn).not.toBeCalled();
expect(console.error.mock.calls[0].map(cleanPath)).toMatchSnapshot(
'Log sent from React',
);
expect(cleanLog(spy.mock.calls[0])).toMatchSnapshot('Log added to LogBox');
expect(mockError.mock.calls[0].map(cleanPath)).toMatchSnapshot(
'Log passed to console error',
);
expect(console.error).toBeCalledTimes(1);
expect(console.error.mock.calls[0]).toEqual([
'Warning: Each child in a list should have a unique "key" prop.%s%s See https://reactjs.org/link/warning-keys for more information.%s',
'\n\nCheck the render method of `DoesNotUseKey`.',
'',
expect.stringMatching('at DoesNotUseKey'),
]);
expect(spy).toHaveBeenCalledWith({
level: 'error',
category: expect.stringContaining(
'Warning: Each child in a list should have a unique',
),
componentStack: expect.anything(),
componentStackType: 'stack',
message: {
content:
'Warning: Each child in a list should have a unique "key" prop.\n\nCheck the render method of `DoesNotUseKey`. See https://reactjs.org/link/warning-keys for more information.',
substitutions: [
{length: 45, offset: 62},
{length: 0, offset: 107},
],
},
});
// The Warning: prefix is added due to a hack in LogBox to prevent double logging.
expect(mockError.mock.calls[0][0].startsWith('Warning: ')).toBe(true);
// We also interpolate the string before passing to the underlying console method.
expect(mockError.mock.calls[0]).toEqual([
expect.stringMatching(
'Warning: Each child in a list should have a unique "key" prop.\n\nCheck the render method of `DoesNotUseKey`. See https://reactjs.org/link/warning-keys for more information.\n at ',
),
]);
});
it('integrates with React and handles a fragment warning in LogBox', () => {
@@ -108,7 +120,10 @@ describe.skip('LogBox', () => {
// so we can assert on what React logs.
jest.spyOn(console, 'error');
const output = TestRenderer.create(<FragmentWithProp />);
let output;
TestRenderer.act(() => {
output = TestRenderer.create(<FragmentWithProp />);
});
// The fragment warning is not as severe. For this warning we don't want to
// pop open a dialog, so we show a collapsed error UI.
@@ -118,15 +133,125 @@ describe.skip('LogBox', () => {
// - Pass to console.error, with a "Warning" prefix so it does not pop a RedBox.
expect(output).toBeDefined();
expect(mockWarn).not.toBeCalled();
expect(console.error.mock.calls[0].map(cleanPath)).toMatchSnapshot(
'Log sent from React',
);
expect(cleanLog(spy.mock.calls[0])).toMatchSnapshot('Log added to LogBox');
expect(mockError.mock.calls[0].map(cleanPath)).toMatchSnapshot(
'Log passed to console error',
);
expect(console.error).toBeCalledTimes(1);
expect(console.error.mock.calls[0]).toEqual([
'Warning: Invalid prop `%s` supplied to `React.Fragment`. React.Fragment can only have `key` and `children` props.%s',
'invalid',
expect.stringMatching('at FragmentWithProp'),
]);
expect(spy).toHaveBeenCalledWith({
level: 'error',
category: expect.stringContaining('Warning: Invalid prop'),
componentStack: expect.anything(),
componentStackType: expect.stringMatching(/(stack|legacy)/),
message: {
content:
'Warning: Invalid prop `invalid` supplied to `React.Fragment`. React.Fragment can only have `key` and `children` props.',
substitutions: [{length: 7, offset: 23}],
},
});
// The Warning: prefix is added due to a hack in LogBox to prevent double logging.
expect(mockError.mock.calls[0][0].startsWith('Warning: ')).toBe(true);
// We also interpolate the string before passing to the underlying console method.
expect(mockError.mock.calls[0]).toEqual([
expect.stringMatching(
'Warning: Invalid prop `invalid` supplied to `React.Fragment`. React.Fragment can only have `key` and `children` props.\n at FragmentWithProp',
),
]);
});
it('handles a manual console.error without a component stack in LogBox', () => {
const LogBox = require('../LogBox').default;
const spy = jest.spyOn(LogBox, 'addException');
installLogBox();
// console.error handling depends on installing the ExceptionsManager error reporter.
ExceptionsManager.installConsoleErrorReporter();
// Spy console.error after LogBox is installed
// so we can assert on what React logs.
jest.spyOn(console, 'error');
let output;
TestRenderer.act(() => {
output = TestRenderer.create(<ManualConsoleError />);
});
// Manual console errors should show a collapsed error dialog.
// When there is no component stack, we expect these errors to:
// - Go to the LogBox patch and fall through to console.error.
// - Get picked up by the ExceptionsManager console.error override.
// - Get passed back to LogBox via addException (non-fatal).
expect(output).toBeDefined();
expect(mockWarn).not.toBeCalled();
expect(spy).toBeCalledTimes(1);
expect(console.error).toBeCalledTimes(1);
expect(console.error.mock.calls[0]).toEqual(['Manual console error']);
expect(spy).toHaveBeenCalledWith({
id: 1,
isComponentError: false,
isFatal: false,
name: 'console.error',
originalMessage: 'Manual console error',
message: 'console.error: Manual console error',
extraData: expect.anything(),
componentStack: null,
stack: expect.anything(),
});
// No Warning: prefix is added due since this is falling through.
expect(mockError.mock.calls[0]).toEqual(['Manual console error']);
});
it('handles a manual console.error with a component stack in LogBox', () => {
const spy = jest.spyOn(LogBoxData, 'addLog');
installLogBox();
// console.error handling depends on installing the ExceptionsManager error reporter.
ExceptionsManager.installConsoleErrorReporter();
// Spy console.error after LogBox is installed
// so we can assert on what React logs.
jest.spyOn(console, 'error');
let output;
TestRenderer.act(() => {
output = TestRenderer.create(<ManualConsoleErrorWithStack />);
});
// Manual console errors should show a collapsed error dialog.
// When there is a component stack, we expect these errors to:
// - Go to the LogBox patch and be detected as a React error.
// - Check the warning filter to see if there is a fiter setting.
// - Call console.error with the parsed error.
// - Get picked up by ExceptionsManager console.error override.
// - Log to console.error.
expect(output).toBeDefined();
expect(mockWarn).not.toBeCalled();
expect(console.error).toBeCalledTimes(1);
expect(spy).toBeCalledTimes(1);
expect(console.error.mock.calls[0]).toEqual([
expect.stringContaining(
'Manual console error\n at ManualConsoleErrorWithStack',
),
]);
expect(spy).toHaveBeenCalledWith({
level: 'error',
category: expect.stringContaining('Warning: Manual console error'),
componentStack: expect.anything(),
componentStackType: 'stack',
message: {
content: 'Warning: Manual console error',
substitutions: [],
},
});
// The Warning: prefix is added due to a hack in LogBox to prevent double logging.
// We also interpolate the string before passing to the underlying console method.
expect(mockError.mock.calls[0]).toEqual([
expect.stringMatching(
'Warning: Manual console error\n at ManualConsoleErrorWithStack',
),
]);
});
});
@@ -13,6 +13,7 @@
const LogBoxData = require('../Data/LogBoxData');
const LogBox = require('../LogBox').default;
const ExceptionsManager = require('../../Core/ExceptionsManager.js');
declare var console: any;
@@ -34,15 +35,18 @@ describe('LogBox', () => {
beforeEach(() => {
jest.resetModules();
jest.restoreAllMocks();
console.error = jest.fn();
console.log = jest.fn();
console.warn = jest.fn();
});
afterEach(() => {
LogBox.uninstall();
// Reset ExceptionManager patching.
if (console._errorOriginal) {
console._errorOriginal = null;
}
console.error = error;
console.log = log;
console.warn = warn;
});
@@ -95,7 +99,7 @@ describe('LogBox', () => {
});
it('registers warnings', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'addLog');
LogBox.install();
@@ -105,13 +109,14 @@ describe('LogBox', () => {
});
it('reports a LogBox exception if we fail to add warnings', () => {
jest.mock('../Data/LogBoxData');
const mockError = new Error('Simulated error');
jest.spyOn(LogBoxData, 'addLog');
jest.spyOn(LogBoxData, 'reportLogBoxError');
// Picking a random implementation detail to simulate throwing.
(LogBoxData.isMessageIgnored: any).mockImplementation(() => {
jest.spyOn(LogBoxData, 'isMessageIgnored').mockImplementation(() => {
throw mockError;
});
const mockError = new Error('Simulated error');
LogBox.install();
@@ -123,7 +128,8 @@ describe('LogBox', () => {
});
it('only registers errors beginning with "Warning: "', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'addLog');
jest.spyOn(LogBoxData, 'checkWarningFilter');
LogBox.install();
@@ -133,7 +139,8 @@ describe('LogBox', () => {
});
it('registers react errors with the formatting from filter', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'addLog');
jest.spyOn(LogBoxData, 'checkWarningFilter');
mockFilterResult({
finalFormat: 'Custom format',
@@ -157,7 +164,8 @@ describe('LogBox', () => {
});
it('registers errors with component stack as errors by default', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'addLog');
jest.spyOn(LogBoxData, 'checkWarningFilter');
mockFilterResult({});
@@ -174,7 +182,8 @@ describe('LogBox', () => {
});
it('registers errors with component stack as errors by default if not found in warning filter', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'addLog');
jest.spyOn(LogBoxData, 'checkWarningFilter');
mockFilterResult({
monitorEvent: 'warning_unhandled',
@@ -193,10 +202,12 @@ describe('LogBox', () => {
});
it('registers errors with component stack with legacy suppression as warning', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'addLog');
jest.spyOn(LogBoxData, 'checkWarningFilter');
mockFilterResult({
suppressDialog_LEGACY: true,
monitorEvent: 'warning',
});
LogBox.install();
@@ -211,10 +222,12 @@ describe('LogBox', () => {
});
it('registers errors with component stack and a forced dialog as fatals', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'addLog');
jest.spyOn(LogBoxData, 'checkWarningFilter');
mockFilterResult({
forceDialogImmediately: true,
monitorEvent: 'warning',
});
LogBox.install();
@@ -229,7 +242,8 @@ describe('LogBox', () => {
});
it('registers warning module errors with the formatting from filter', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'addLog');
jest.spyOn(LogBoxData, 'checkWarningFilter');
mockFilterResult({
finalFormat: 'Custom format',
@@ -248,7 +262,8 @@ describe('LogBox', () => {
});
it('registers warning module errors as errors by default', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'addLog');
jest.spyOn(LogBoxData, 'checkWarningFilter');
mockFilterResult({});
@@ -262,10 +277,12 @@ describe('LogBox', () => {
});
it('registers warning module errors with only legacy suppression as warning', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'addLog');
jest.spyOn(LogBoxData, 'checkWarningFilter');
mockFilterResult({
suppressDialog_LEGACY: true,
monitorEvent: 'warning',
});
LogBox.install();
@@ -277,10 +294,12 @@ describe('LogBox', () => {
});
it('registers warning module errors with a forced dialog as fatals', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'addLog');
jest.spyOn(LogBoxData, 'checkWarningFilter');
mockFilterResult({
forceDialogImmediately: true,
monitorEvent: 'warning',
});
LogBox.install();
@@ -292,10 +311,12 @@ describe('LogBox', () => {
});
it('ignores warning module errors that are suppressed completely', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'addLog');
jest.spyOn(LogBoxData, 'checkWarningFilter');
mockFilterResult({
suppressCompletely: true,
monitorEvent: 'warning',
});
LogBox.install();
@@ -305,10 +326,11 @@ describe('LogBox', () => {
});
it('ignores warning module errors that are pattern ignored', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'checkWarningFilter');
jest.spyOn(LogBoxData, 'isMessageIgnored').mockReturnValue(true);
jest.spyOn(LogBoxData, 'addLog');
mockFilterResult({});
(LogBoxData.isMessageIgnored: any).mockReturnValue(true);
LogBox.install();
@@ -317,10 +339,11 @@ describe('LogBox', () => {
});
it('ignores warning module errors that are from LogBox itself', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'checkWarningFilter');
jest.spyOn(LogBoxData, 'isLogBoxErrorMessage').mockReturnValue(true);
jest.spyOn(LogBoxData, 'addLog');
mockFilterResult({});
(LogBoxData.isLogBoxErrorMessage: any).mockReturnValue(true);
LogBox.install();
@@ -329,8 +352,9 @@ describe('LogBox', () => {
});
it('ignores logs that are pattern ignored"', () => {
jest.mock('../Data/LogBoxData');
(LogBoxData.isMessageIgnored: any).mockReturnValue(true);
jest.spyOn(LogBoxData, 'checkWarningFilter');
jest.spyOn(LogBoxData, 'isMessageIgnored').mockReturnValue(true);
jest.spyOn(LogBoxData, 'addLog');
LogBox.install();
@@ -339,8 +363,8 @@ describe('LogBox', () => {
});
it('does not add logs that are from LogBox itself"', () => {
jest.mock('../Data/LogBoxData');
(LogBoxData.isLogBoxErrorMessage: any).mockReturnValue(true);
jest.spyOn(LogBoxData, 'isLogBoxErrorMessage').mockReturnValue(true);
jest.spyOn(LogBoxData, 'addLog');
LogBox.install();
@@ -349,7 +373,7 @@ describe('LogBox', () => {
});
it('ignores logs starting with "(ADVICE)"', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'addLog');
LogBox.install();
@@ -358,7 +382,7 @@ describe('LogBox', () => {
});
it('does not ignore logs formatted to start with "(ADVICE)"', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'addLog');
LogBox.install();
@@ -376,7 +400,7 @@ describe('LogBox', () => {
});
it('ignores console methods after uninstalling', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'addLog');
LogBox.install();
LogBox.uninstall();
@@ -389,7 +413,7 @@ describe('LogBox', () => {
});
it('does not add logs after uninstalling', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'addLog');
LogBox.install();
LogBox.uninstall();
@@ -406,7 +430,7 @@ describe('LogBox', () => {
});
it('does not add exceptions after uninstalling', () => {
jest.mock('../Data/LogBoxData');
jest.spyOn(LogBoxData, 'addException');
LogBox.install();
LogBox.uninstall();
@@ -482,4 +506,80 @@ describe('LogBox', () => {
'Custom: after installing for the second time',
);
});
it('registers errors with component stack as errors by default, when ExceptionManager is registered first', () => {
jest.spyOn(LogBoxData, 'checkWarningFilter');
jest.spyOn(LogBoxData, 'addLog');
ExceptionsManager.installConsoleErrorReporter();
LogBox.install();
console.error(
'HIT\n at Text (/path/to/Component:30:175)\n at DoesNotUseKey',
);
expect(LogBoxData.addLog).toBeCalledWith(
expect.objectContaining({level: 'error'}),
);
expect(LogBoxData.checkWarningFilter).toBeCalledWith(
'HIT\n at Text (/path/to/Component:30:175)\n at DoesNotUseKey',
);
});
it('registers errors with component stack as errors by default, when ExceptionManager is registered second', () => {
jest.spyOn(LogBoxData, 'checkWarningFilter');
jest.spyOn(LogBoxData, 'addLog');
LogBox.install();
ExceptionsManager.installConsoleErrorReporter();
console.error(
'HIT\n at Text (/path/to/Component:30:175)\n at DoesNotUseKey',
);
expect(LogBoxData.addLog).toBeCalledWith(
expect.objectContaining({level: 'error'}),
);
expect(LogBoxData.checkWarningFilter).toBeCalledWith(
'HIT\n at Text (/path/to/Component:30:175)\n at DoesNotUseKey',
);
});
it('registers errors without component stack as errors by default, when ExceptionManager is registered first', () => {
jest.spyOn(LogBoxData, 'checkWarningFilter');
jest.spyOn(LogBoxData, 'addException');
ExceptionsManager.installConsoleErrorReporter();
LogBox.install();
console.error('HIT');
// Errors without a component stack skip the warning filter and
// fall through to the ExceptionManager, which are then reported
// back to LogBox as non-fatal exceptions, in a convuluted dance
// in the most legacy cruft way.
expect(LogBoxData.addException).toBeCalledWith(
expect.objectContaining({originalMessage: 'HIT'}),
);
expect(LogBoxData.checkWarningFilter).not.toBeCalled();
});
it('registers errors without component stack as errors by default, when ExceptionManager is registered second', () => {
jest.spyOn(LogBoxData, 'checkWarningFilter');
jest.spyOn(LogBoxData, 'addException');
LogBox.install();
ExceptionsManager.installConsoleErrorReporter();
console.error('HIT');
// Errors without a component stack skip the warning filter and
// fall through to the ExceptionManager, which are then reported
// back to LogBox as non-fatal exceptions, in a convuluted dance
// in the most legacy cruft way.
expect(LogBoxData.addException).toBeCalledWith(
expect.objectContaining({originalMessage: 'HIT'}),
);
expect(LogBoxData.checkWarningFilter).not.toBeCalled();
});
});
@@ -30,3 +30,27 @@ export const FragmentWithProp = () => {
</React.Fragment>
);
};
export const ManualConsoleError = () => {
console.error('Manual console error');
return (
<React.Fragment>
{['foo', 'bar'].map(item => (
<Text key={item}>{item}</Text>
))}
</React.Fragment>
);
};
export const ManualConsoleErrorWithStack = () => {
console.error(
'Manual console error\n at ManualConsoleErrorWithStack (/path/to/ManualConsoleErrorWithStack:30:175)\n at TestApp',
);
return (
<React.Fragment>
{['foo', 'bar'].map(item => (
<Text key={item}>{item}</Text>
))}
</React.Fragment>
);
};
+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',
+3 -3
View File
@@ -13,7 +13,7 @@ import type {RootTag} from '../Types/RootTagTypes';
import type {IPerformanceLogger} from '../Utilities/createPerformanceLogger';
import type {DisplayModeType} from './DisplayMode';
import BatchedBridge from '../BatchedBridge/BatchedBridge';
import registerCallableModule from '../Core/registerCallableModule';
import BugReporting from '../BugReporting/BugReporting';
import createPerformanceLogger from '../Utilities/createPerformanceLogger';
import infoLog from '../Utilities/infoLog';
@@ -363,8 +363,8 @@ global.RN$SurfaceRegistry = {
if (global.RN$Bridgeless === true) {
console.log('Bridgeless mode is enabled');
} else {
BatchedBridge.registerCallableModule('AppRegistry', AppRegistry);
}
registerCallableModule('AppRegistry', AppRegistry);
module.exports = AppRegistry;
@@ -35,6 +35,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, assign, readonly) CGFloat zoomScale;
@property (nonatomic, assign, readonly) CGPoint contentOffset;
@property (nonatomic, assign, readonly) UIEdgeInsets contentInset;
@property (nullable, nonatomic, copy) NSDictionary<NSAttributedStringKey, id> *typingAttributes;
// This protocol disallows direct access to `selectedTextRange` property because
// unwise usage of it can break the `delegate` behavior. So, we always have to
@@ -36,6 +36,7 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
{
if (![self isDescendantOfView:scrollView]) {
// View is outside scroll view
scrollView.firstResponderViewOutsideScrollView = self.backedTextInputView;
return;
}
@@ -456,7 +457,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame)
_maxLength.integerValue - (NSInteger)backedTextInputView.attributedText.string.length + (NSInteger)range.length,
0);
if (text.length > _maxLength.integerValue) {
if (text.length > allowedLength) {
// If we typed/pasted more than one character, limit the text inputted.
if (text.length > 1) {
if (allowedLength > 0) {
+3 -1
View File
@@ -105,7 +105,9 @@ export function setColorScheme(colorScheme: ?ColorSchemeName): void {
const {NativeAppearance} = state;
if (NativeAppearance != null) {
NativeAppearance.setColorScheme(colorScheme ?? 'unspecified');
state.appearance = {colorScheme};
state.appearance = {
colorScheme: toColorScheme(NativeAppearance.getColorScheme()),
};
}
}
@@ -23,8 +23,8 @@ NSDictionary* RCTGetReactNativeVersion(void)
__rnVersion = @{
RCTVersionMajor: @(0),
RCTVersionMinor: @(76),
RCTVersionPatch: @(0),
RCTVersionPrerelease: @"rc.4",
RCTVersionPatch: @(8),
RCTVersionPrerelease: [NSNull null],
};
});
return __rnVersion;
@@ -53,11 +53,8 @@ static RCTRootViewSizeFlexibility convertToRootViewSizeFlexibility(RCTSurfaceSiz
- (instancetype)initWithSurface:(id<RCTSurfaceProtocol>)surface
{
if (self = [super initWithSurface:surface
sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact]) {
[surface start];
}
return self;
return [super initWithSurface:surface
sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact];
}
RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame)
@@ -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.
@@ -134,9 +134,7 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
completion:(void (^)(void))completion
{
UIViewController *controller = [self reactViewController];
[[self _topMostViewControllerFrom:controller] presentViewController:modalViewController
animated:animated
completion:completion];
[controller presentViewController:modalViewController animated:animated completion:completion];
}
- (void)dismissViewController:(UIViewController *)modalViewController
@@ -151,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
@@ -276,24 +276,14 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
[childComponentView removeFromSuperview];
}
#pragma mark - Private
#pragma mark - UIAdaptivePresentationControllerDelegate
- (UIViewController *)_topMostViewControllerFrom:(UIViewController *)rootViewController
- (void)presentationControllerDidAttemptToDismiss:(UIPresentationController *)controller
{
UIViewController *topController = rootViewController;
while (topController.presentedViewController) {
topController = topController.presentedViewController;
auto eventEmitter = [self modalEventEmitter];
if (eventEmitter) {
eventEmitter->onRequestClose({});
}
if ([topController isKindOfClass:[UINavigationController class]]) {
UINavigationController *navigationController = (UINavigationController *)topController;
topController = navigationController.visibleViewController;
return [self _topMostViewControllerFrom:topController];
} else if ([topController isKindOfClass:[UITabBarController class]]) {
UITabBarController *tabBarController = (UITabBarController *)topController;
topController = tabBarController.selectedViewController;
return [self _topMostViewControllerFrom:topController];
}
return topController;
}
@end
@@ -38,6 +38,9 @@ NS_ASSUME_NONNULL_BEGIN
/** Focus area of newly-activated text input relative to the window to compare against UIKeyboardFrameBegin/End */
@property (nonatomic, assign) CGRect firstResponderFocus;
/** newly-activated text input outside of the scroll view */
@property (nonatomic, weak) UIView *firstResponderViewOutsideScrollView;
/*
* Returns the subview of the scroll view that the component uses to mount all subcomponents into. That's useful to
* separate component views from auxiliary views to be able to reliably implement pull-to-refresh- and RTL-related
@@ -182,16 +182,18 @@ RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrollView, NSInt
UIViewAnimationCurve curve =
(UIViewAnimationCurve)[notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] unsignedIntegerValue];
CGRect keyboardEndFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGRect keyboardBeginFrame = [notification.userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
CGPoint absoluteViewOrigin = [self convertPoint:self.bounds.origin toView:nil];
CGFloat scrollViewLowerY = isInverted ? absoluteViewOrigin.y : absoluteViewOrigin.y + self.bounds.size.height;
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;
@@ -203,21 +205,18 @@ RCTSendScrollEventForNativeAnimations_DEPRECATED(UIScrollView *scrollView, NSInt
from:self
forEvent:nil]) {
if (CGRectEqualToRect(_firstResponderFocus, CGRectNull)) {
// Text input view is outside of the scroll view.
return;
}
CGRect viewIntersection = CGRectIntersection(self.firstResponderFocus, keyboardEndFrame);
if (CGRectIsNull(viewIntersection)) {
return;
}
// Inner text field focused
CGFloat focusEnd = CGRectGetMaxY(self.firstResponderFocus);
if (focusEnd > keyboardEndFrame.origin.y) {
// Text field active region is below visible area with keyboard - update diff to bring into view
contentDiff = keyboardEndFrame.origin.y - focusEnd;
UIView *inputAccessoryView = _firstResponderViewOutsideScrollView.inputAccessoryView;
if (inputAccessoryView) {
// Text input view is within the inputAccessoryView.
contentDiff = keyboardEndFrame.origin.y - keyboardBeginFrame.origin.y;
}
} else {
// Inner text field focused
CGFloat focusEnd = CGRectGetMaxY(self.firstResponderFocus);
if (focusEnd > keyboardEndFrame.origin.y) {
// Text field active region is below visible area with keyboard - update diff to bring into view
contentDiff = keyboardEndFrame.origin.y - focusEnd;
}
}
}
@@ -243,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];
}
@@ -61,6 +61,13 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
*/
BOOL _comingFromJS;
BOOL _didMoveToWindow;
/*
* Newly initialized default typing attributes contain a no-op NSParagraphStyle and NSShadow. These cause inequality
* between the AttributedString backing the input and those generated from state. We store these attributes to make
* later comparison insensitive to them.
*/
NSDictionary<NSAttributedStringKey, id> *_originalTypingAttributes;
}
#pragma mark - UIView overrides
@@ -76,6 +83,7 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
_ignoreNextTextInputCall = NO;
_comingFromJS = NO;
_didMoveToWindow = NO;
_originalTypingAttributes = [_backedTextInputView.typingAttributes copy];
[self addSubview:_backedTextInputView];
[self initializeReturnKeyType];
@@ -84,6 +92,18 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
return self;
}
- (void)updateEventEmitter:(const EventEmitter::Shared &)eventEmitter
{
[super updateEventEmitter:eventEmitter];
NSMutableDictionary<NSAttributedStringKey, id> *defaultAttributes =
[_backedTextInputView.defaultTextAttributes mutableCopy];
defaultAttributes[RCTAttributedStringEventEmitterKey] = RCTWrapEventEmitter(_eventEmitter);
_backedTextInputView.defaultTextAttributes = defaultAttributes;
}
- (void)didMoveToWindow
{
[super didMoveToWindow];
@@ -104,6 +124,7 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
{
if (![self isDescendantOfView:scrollView.scrollView] || !_backedTextInputView.isFirstResponder) {
// View is outside scroll view or it's not a first responder.
scrollView.firstResponderViewOutsideScrollView = _backedTextInputView;
return;
}
@@ -236,8 +257,11 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
}
if (newTextInputProps.textAttributes != oldTextInputProps.textAttributes) {
_backedTextInputView.defaultTextAttributes =
NSMutableDictionary<NSAttributedStringKey, id> *defaultAttributes =
RCTNSTextAttributesFromTextAttributes(newTextInputProps.getEffectiveTextAttributes(RCTFontSizeMultiplier()));
defaultAttributes[RCTAttributedStringEventEmitterKey] =
_backedTextInputView.defaultTextAttributes[RCTAttributedStringEventEmitterKey];
_backedTextInputView.defaultTextAttributes = defaultAttributes;
}
if (newTextInputProps.selectionColor != oldTextInputProps.selectionColor) {
@@ -421,6 +445,12 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
if (_comingFromJS) {
return;
}
// T207198334: Setting a new AttributedString (_comingFromJS) will trigger a selection change before the backing
// string is updated, so indicies won't point to what we want yet. Only respond to user selection change, and let
// `_setAttributedString` handle updating typing attributes if content changes.
[self _updateTypingAttributes];
const auto &props = static_cast<const TextInputProps &>(*_props);
if (props.traits.multiline && ![_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) {
[self textInputDidChange];
@@ -674,9 +704,26 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
[_backedTextInputView scrollRangeToVisible:NSMakeRange(offsetStart, 0)];
}
[self _restoreTextSelection];
[self _updateTypingAttributes];
_lastStringStateWasUpdatedWith = attributedString;
}
// Ensure that newly typed text will inherit any custom attributes. We follow the logic of RN Android, where attributes
// to the left of the cursor are copied into new text, unless we are at the start of the field, in which case we will
// copy the attributes from text to the right. This allows consistency between backed input and new AttributedText
// https://github.com/facebook/react-native/blob/3102a58df38d96f3dacef0530e4dbb399037fcd2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/span/SetSpanOperation.kt#L30
- (void)_updateTypingAttributes
{
if (_backedTextInputView.attributedText.length > 0 && _backedTextInputView.selectedTextRange != nil) {
NSUInteger offsetStart = [_backedTextInputView offsetFromPosition:_backedTextInputView.beginningOfDocument
toPosition:_backedTextInputView.selectedTextRange.start];
NSUInteger samplePoint = offsetStart == 0 ? 0 : offsetStart - 1;
_backedTextInputView.typingAttributes = [_backedTextInputView.attributedText attributesAtIndex:samplePoint
effectiveRange:NULL];
}
}
- (void)_setMultiline:(BOOL)multiline
{
[_backedTextInputView removeFromSuperview];
@@ -732,9 +779,10 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
_backedTextInputView.markedTextRange || _backedTextInputView.isSecureTextEntry || fontHasBeenUpdatedBySystem;
if (shouldFallbackToBareTextComparison) {
return ([newText.string isEqualToString:oldText.string]);
return [newText.string isEqualToString:oldText.string];
} else {
return ([newText isEqualToAttributedString:oldText]);
return RCTIsAttributedStringEffectivelySame(
newText, oldText, _originalTypingAttributes, static_cast<const TextInputProps &>(*_props).textAttributes);
}
}
@@ -833,6 +833,8 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
_backgroundColorLayer.mask = maskLayer;
_backgroundColorLayer.cornerRadius = 0;
}
[_backgroundColorLayer removeAllAnimations];
}
// borders
@@ -1001,15 +1003,10 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
}
// clipping
self.currentContainerView.layer.mask = nil;
if (self.currentContainerView.clipsToBounds) {
BOOL clipToPaddingBox = ReactNativeFeatureFlags::enableIOSViewClipToPaddingBox();
if (clipToPaddingBox) {
CALayer *maskLayer = [self createMaskLayer:RCTCGRectFromRect(_layoutMetrics.getPaddingFrame())
cornerInsets:RCTGetCornerInsets(
RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii),
RCTUIEdgeInsetsFromEdgeInsets(borderMetrics.borderWidths))];
self.currentContainerView.layer.mask = maskLayer;
} else {
if (!clipToPaddingBox) {
if (borderMetrics.borderRadii.isUniform()) {
self.currentContainerView.layer.cornerRadius = borderMetrics.borderRadii.topLeft.horizontal;
} else {
@@ -1031,9 +1028,17 @@ static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
subview.layer.mask = [self createMaskLayer:subview.bounds cornerInsets:cornerInsets];
}
}
} else if (
!borderMetrics.borderWidths.isUniform() || borderMetrics.borderWidths.left != 0 ||
!borderMetrics.borderRadii.isUniform()) {
CALayer *maskLayer = [self createMaskLayer:RCTCGRectFromRect(_layoutMetrics.getPaddingFrame())
cornerInsets:RCTGetCornerInsets(
RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii),
RCTUIEdgeInsetsFromEdgeInsets(borderMetrics.borderWidths))];
self.currentContainerView.layer.mask = maskLayer;
} else {
self.currentContainerView.layer.cornerRadius = borderMetrics.borderRadii.topLeft.horizontal;
}
} else {
self.currentContainerView.layer.mask = nil;
}
}
@@ -142,6 +142,7 @@ using namespace facebook::react;
if (!_view) {
_view = [[RCTSurfaceView alloc] initWithSurface:(RCTSurface *)self];
[self _updateLayoutContext];
_touchHandler = [RCTSurfaceTouchHandler new];
[_touchHandler attachToView:_view];
}
@@ -10,6 +10,7 @@
#import <React/RCTConvert.h>
typedef UIFont * (^RCTFontHandler)(CGFloat fontSize, NSString *fontWeightDescription);
typedef CGFloat RCTFontWeight;
/**
* React Native will use the System font for rendering by default. If you want to
@@ -19,6 +20,7 @@ typedef UIFont * (^RCTFontHandler)(CGFloat fontSize, NSString *fontWeightDescrip
*/
RCT_EXTERN void RCTSetDefaultFontHandler(RCTFontHandler handler);
RCT_EXTERN BOOL RCTHasFontHandlerSet(void);
RCT_EXTERN RCTFontWeight RCTGetFontWeight(UIFont *font);
@interface RCTFont : NSObject
+4 -5
View File
@@ -11,8 +11,7 @@
#import <CoreText/CoreText.h>
typedef CGFloat RCTFontWeight;
static RCTFontWeight weightOfFont(UIFont *font)
RCTFontWeight RCTGetFontWeight(UIFont *font)
{
static NSArray<NSString *> *weightSuffixes;
static NSArray<NSNumber *> *fontWeights;
@@ -405,7 +404,7 @@ RCT_ARRAY_CONVERTER(RCTFontVariantDescriptor)
if (font) {
familyName = font.familyName ?: defaultFontFamily;
fontSize = font.pointSize ?: defaultFontSize;
fontWeight = weightOfFont(font);
fontWeight = RCTGetFontWeight(font);
isItalic = isItalicFont(font);
isCondensed = isCondensedFont(font);
}
@@ -453,7 +452,7 @@ RCT_ARRAY_CONVERTER(RCTFontVariantDescriptor)
// It's actually a font name, not a font family name,
// but we'll do what was meant, not what was said.
familyName = font.familyName;
fontWeight = weight ? fontWeight : weightOfFont(font);
fontWeight = weight ? fontWeight : RCTGetFontWeight(font);
isItalic = style ? isItalic : isItalicFont(font);
isCondensed = isCondensedFont(font);
} else {
@@ -476,7 +475,7 @@ RCT_ARRAY_CONVERTER(RCTFontVariantDescriptor)
for (NSString *name in names) {
UIFont *match = [UIFont fontWithName:name size:fontSize];
if (isItalic == isItalicFont(match) && isCondensed == isCondensedFont(match)) {
CGFloat testWeight = weightOfFont(match);
CGFloat testWeight = RCTGetFontWeight(match);
if (ABS(testWeight - fontWeight) < ABS(closestWeight - fontWeight)) {
font = match;
closestWeight = testWeight;
@@ -50,6 +50,8 @@
@property (nonatomic, assign) BOOL inverted;
/** Focus area of newly-activated text input relative to the window to compare against UIKeyboardFrameBegin/End */
@property (nonatomic, assign) CGRect firstResponderFocus;
/** newly-activated text input outside of the scroll view */
@property (nonatomic, weak) UIView *firstResponderViewOutsideScrollView;
// NOTE: currently these event props are only declared so we can export the
// event names to JS - we don't call the blocks directly because scroll events
@@ -338,6 +338,12 @@ static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCu
if (!didFocusExternalTextField && focusEnd > endFrame.origin.y) {
// Text field active region is below visible area with keyboard - update diff to bring into view
contentDiff = endFrame.origin.y - focusEnd;
} else {
UIView *inputAccessoryView = _firstResponderViewOutsideScrollView.inputAccessoryView;
if (inputAccessoryView) {
// Text input view is within the inputAccessoryView.
contentDiff = endFrame.origin.y - beginFrame.origin.y;
}
}
} else if (endFrame.origin.y <= beginFrame.origin.y) {
// Keyboard opened for other reason
@@ -2248,7 +2248,7 @@ public abstract interface class com/facebook/react/devsupport/HMRClient : com/fa
public abstract fun disable ()V
public abstract fun enable ()V
public abstract fun registerBundle (Ljava/lang/String;)V
public abstract fun setup (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IZ)V
public abstract fun setup (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IZLjava/lang/String;)V
}
public final class com/facebook/react/devsupport/InspectorFlags {
@@ -3306,7 +3306,6 @@ public final class com/facebook/react/modules/core/TimingModule : com/facebook/f
public fun createTimer (DDDZ)V
public fun deleteTimer (D)V
public fun emitTimeDriftWarning (Ljava/lang/String;)V
public fun initialize ()V
public fun invalidate ()V
public fun setSendIdleEvents (Z)V
}
@@ -3798,13 +3797,13 @@ public abstract class com/facebook/react/packagerconnection/NotificationOnlyHand
public final fun onRequest (Ljava/lang/Object;Lcom/facebook/react/packagerconnection/Responder;)V
}
public final class com/facebook/react/packagerconnection/PackagerConnectionSettings {
public class com/facebook/react/packagerconnection/PackagerConnectionSettings {
public fun <init> (Landroid/content/Context;)V
public final fun getAdditionalOptionsForPackager ()Ljava/util/Map;
public final fun getDebugServerHost ()Ljava/lang/String;
public fun getDebugServerHost ()Ljava/lang/String;
public final fun getPackageName ()Ljava/lang/String;
public final fun setAdditionalOptionForPackager (Ljava/lang/String;Ljava/lang/String;)V
public final fun setDebugServerHost (Ljava/lang/String;)V
public fun setDebugServerHost (Ljava/lang/String;)V
}
public final class com/facebook/react/packagerconnection/ReconnectingWebSocket : okhttp3/WebSocketListener {
@@ -7281,6 +7280,8 @@ public final class com/facebook/react/views/scroll/ReactScrollViewHelper {
public static final field SNAP_ALIGNMENT_END I
public static final field SNAP_ALIGNMENT_START I
public static final fun addScrollListener (Lcom/facebook/react/views/scroll/ReactScrollViewHelper$ScrollListener;)V
public static final fun dispatchMomentumEndOnAnimationEnd (Landroid/view/ViewGroup;)V
public static final fun emitLayoutChangeEvent (Landroid/view/ViewGroup;)V
public static final fun emitLayoutEvent (Landroid/view/ViewGroup;)V
public static final fun emitScrollBeginDragEvent (Landroid/view/ViewGroup;)V
public static final fun emitScrollEndDragEvent (Landroid/view/ViewGroup;FF)V
@@ -7858,6 +7859,7 @@ public class com/facebook/react/views/text/TextAttributeProps {
public static final field TA_KEY_LETTER_SPACING S
public static final field TA_KEY_LINE_BREAK_STRATEGY S
public static final field TA_KEY_LINE_HEIGHT S
public static final field TA_KEY_MAX_FONT_SIZE_MULTIPLIER S
public static final field TA_KEY_OPACITY S
public static final field TA_KEY_ROLE S
public static final field TA_KEY_TEXT_DECORATION_COLOR S
@@ -7890,6 +7892,7 @@ public class com/facebook/react/views/text/TextAttributeProps {
protected field mLetterSpacingInput F
protected field mLineHeight F
protected field mLineHeightInput F
protected field mMaxFontSizeMultiplier F
protected field mNumberOfLines I
protected field mOpacity F
protected field mRole Lcom/facebook/react/uimanager/ReactAccessibilityDelegate$Role;
@@ -557,7 +557,6 @@ android {
listOf(
"src/main/res/devsupport",
"src/main/res/shell",
"src/main/res/views/alert",
"src/main/res/views/modal",
"src/main/res/views/uimanager"))
java.exclude("com/facebook/react/processing")
@@ -31,13 +31,27 @@ if(CCACHE_FOUND)
endif(CCACHE_FOUND)
set(BUILD_DIR ${PROJECT_BUILD_DIR})
if(CMAKE_HOST_WIN32)
string(REPLACE "\\" "/" BUILD_DIR ${BUILD_DIR})
endif()
file(TO_CMAKE_PATH "${BUILD_DIR}" BUILD_DIR)
file(TO_CMAKE_PATH "${REACT_ANDROID_DIR}" REACT_ANDROID_DIR)
file(GLOB input_SRC CONFIGURE_DEPENDS
*.cpp
${BUILD_DIR}/generated/autolinking/src/main/jni/*.cpp)
if (PROJECT_ROOT_DIR)
# This empty `if` is just to silence a CMake warning and make sure the `PROJECT_ROOT_DIR`
# variable is defined if user need to access it.
endif ()
file(GLOB override_cpp_SRC CONFIGURE_DEPENDS *.cpp)
# We check if the user is providing a custom OnLoad.cpp file. If so, we pick that
# for compilation. Otherwise we fallback to using the `default-app-setup/OnLoad.cpp`
# file instead.
if(override_cpp_SRC)
file(GLOB input_SRC CONFIGURE_DEPENDS
*.cpp
${BUILD_DIR}/generated/autolinking/src/main/jni/*.cpp)
else()
file(GLOB input_SRC CONFIGURE_DEPENDS
${REACT_ANDROID_DIR}/cmake-utils/default-app-setup/*.cpp
${BUILD_DIR}/generated/autolinking/src/main/jni/*.cpp)
endif()
add_library(${CMAKE_PROJECT_NAME} SHARED ${input_SRC})
@@ -1,4 +1,4 @@
VERSION_NAME=0.76.0-rc.4
VERSION_NAME=0.76.8
react.internal.publishingGroup=com.facebook.react
android.useAndroidX=true
@@ -36,9 +36,13 @@ fun getSDKPath(): String {
fun getSDKManagerPath(): String {
val metaSdkManagerPath = File("${getSDKPath()}/cmdline-tools/latest/bin/sdkmanager")
val ossSdkManagerPath = File("${getSDKPath()}/tools/bin/sdkmanager")
val windowsMetaSdkManagerPath = File("${getSDKPath()}/cmdline-tools/latest/bin/sdkmanager.bat")
val windowsOssSdkManagerPath = File("${getSDKPath()}/tools/bin/sdkmanager.bat")
return when {
metaSdkManagerPath.exists() -> metaSdkManagerPath.absolutePath
windowsMetaSdkManagerPath.exists() -> windowsMetaSdkManagerPath.absolutePath
ossSdkManagerPath.exists() -> ossSdkManagerPath.absolutePath
windowsOssSdkManagerPath.exists() -> windowsOssSdkManagerPath.absolutePath
else -> throw GradleException("Could not find sdkmanager executable.")
}
}
@@ -124,15 +124,11 @@ public abstract class HeadlessJsTaskService extends Service implements HeadlessJ
@Override
public void onDestroy() {
super.onDestroy();
ReactContext reactContext = getReactContext();
if (getReactNativeHost().hasInstance()) {
ReactInstanceManager reactInstanceManager = getReactNativeHost().getReactInstanceManager();
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
if (reactContext != null) {
HeadlessJsTaskContext headlessJsTaskContext =
HeadlessJsTaskContext.getInstance(reactContext);
headlessJsTaskContext.removeTaskEventListener(this);
}
if (reactContext != null) {
HeadlessJsTaskContext headlessJsTaskContext = HeadlessJsTaskContext.getInstance(reactContext);
headlessJsTaskContext.removeTaskEventListener(this);
}
if (sWakeLock != null) {
sWakeLock.release();
@@ -183,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 (ReactFeatureFlags.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();
@@ -198,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();
}
}
}
@@ -12,6 +12,7 @@ import androidx.annotation.AnyThread
import androidx.annotation.UiThread
import com.facebook.infer.annotation.ThreadConfined
import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.uimanager.events.EventDispatcher
@OptIn(UnstableReactNativeAPI::class)
public interface UIManager : PerformanceCounter {
@@ -78,7 +79,7 @@ public interface UIManager : PerformanceCounter {
public fun dispatchCommand(reactTag: Int, commandId: String, commandArgs: ReadableArray?)
/** @return the [EventDispatcher] object that is used by this class. */
public fun <T> getEventDispatcher(): T
public val eventDispatcher: EventDispatcher
/**
* Used by native animated module to bypass the process of updating the values through the shadow
@@ -111,7 +111,7 @@ protected constructor(
packages,
jsMainModuleName,
bundleAssetName ?: "index",
null,
jsBundleFile,
isHermesEnabled ?: true,
useDeveloperSupport,
)
@@ -696,10 +696,12 @@ public abstract class DevSupportManagerBase implements DevSupportManager {
URL sourceUrl = new URL(getSourceUrl());
String path = sourceUrl.getPath().substring(1); // strip initial slash in path
String host = sourceUrl.getHost();
String scheme = sourceUrl.getProtocol();
int port = sourceUrl.getPort() != -1 ? sourceUrl.getPort() : sourceUrl.getDefaultPort();
mCurrentContext
.getJSModule(HMRClient.class)
.setup("android", path, host, port, mDevSettings.isHotModuleReplacementEnabled());
.setup(
"android", path, host, port, mDevSettings.isHotModuleReplacementEnabled(), scheme);
} catch (MalformedURLException e) {
showNewJavaError(e.getMessage(), e);
}
@@ -26,8 +26,11 @@ public interface HMRClient extends JavaScriptModule {
* @param host The host that the HMRClient should communicate with.
* @param port The port that the HMRClient should communicate with on the host.
* @param isEnabled Whether HMR is enabled initially.
* @param scheme The protocol that the HMRClient should communicate with on the host (defaults to
* http).
*/
void setup(String platform, String bundleEntry, String host, int port, boolean isEnabled);
void setup(
String platform, String bundleEntry, String host, int port, boolean isEnabled, String scheme);
/** Registers an additional JS bundle with HMRClient. */
void registerBundle(String bundleUrl);
@@ -172,7 +172,7 @@ public class FabricUIManager
private final CopyOnWriteArrayList<UIManagerListener> mListeners = new CopyOnWriteArrayList<>();
private boolean mMountNotificationScheduled = false;
private final List<Integer> mMountedSurfaceIds = new ArrayList<>();
private List<Integer> mSurfaceIdsWithPendingMountNotification = new ArrayList<>();
@ThreadConfined(UI)
@NonNull
@@ -449,12 +449,18 @@ public class FabricUIManager
@Override
public void markActiveTouchForTag(int surfaceId, int reactTag) {
mMountingManager.getSurfaceManager(surfaceId).markActiveTouchForTag(reactTag);
SurfaceMountingManager surfaceMountingManager = mMountingManager.getSurfaceManager(surfaceId);
if (surfaceMountingManager != null) {
surfaceMountingManager.markActiveTouchForTag(reactTag);
}
}
@Override
public void sweepActiveTouchForTag(int surfaceId, int reactTag) {
mMountingManager.getSurfaceManager(surfaceId).sweepActiveTouchForTag(reactTag);
SurfaceMountingManager surfaceMountingManager = mMountingManager.getSurfaceManager(surfaceId);
if (surfaceMountingManager != null) {
surfaceMountingManager.sweepActiveTouchForTag(reactTag);
}
}
/**
@@ -1021,7 +1027,6 @@ public class FabricUIManager
@Override
@NonNull
@SuppressWarnings("unchecked")
public EventDispatcher getEventDispatcher() {
return mEventDispatcher;
}
@@ -1252,12 +1257,15 @@ public class FabricUIManager
// Collect surface IDs for all the mount items
for (MountItem mountItem : mountItems) {
if (mountItem != null && !mMountedSurfaceIds.contains(mountItem.getSurfaceId())) {
mMountedSurfaceIds.add(mountItem.getSurfaceId());
if (mountItem != null
&& !mSurfaceIdsWithPendingMountNotification.contains(mountItem.getSurfaceId())) {
mSurfaceIdsWithPendingMountNotification.add(mountItem.getSurfaceId());
}
}
if (!mMountNotificationScheduled && !mMountedSurfaceIds.isEmpty()) {
if (!mMountNotificationScheduled && !mSurfaceIdsWithPendingMountNotification.isEmpty()) {
mMountNotificationScheduled = true;
// Notify mount when the effects are visible and prevent mount hooks to
// delay paint.
UiThreadUtil.getUiThreadHandler()
@@ -1267,17 +1275,19 @@ public class FabricUIManager
public void run() {
mMountNotificationScheduled = false;
// Create a copy in case mount hooks trigger more mutations
final List<Integer> surfaceIdsToReportMount =
mSurfaceIdsWithPendingMountNotification;
mSurfaceIdsWithPendingMountNotification = new ArrayList<>();
final @Nullable Binding binding = mBinding;
if (binding == null || mDestroyed) {
mMountedSurfaceIds.clear();
return;
}
for (int surfaceId : mMountedSurfaceIds) {
for (int surfaceId : surfaceIdsToReportMount) {
binding.reportMount(surfaceId);
}
mMountedSurfaceIds.clear();
}
});
}
@@ -330,14 +330,11 @@ public class MountingManager {
@AnyThread
@ThreadConfined(ANY)
public @Nullable EventEmitterWrapper getEventEmitter(int surfaceId, int reactTag) {
SurfaceMountingManager surfaceMountingManager =
(surfaceId == ViewUtil.NO_SURFACE_ID
? getSurfaceManagerForView(reactTag)
: getSurfaceManager(surfaceId));
if (surfaceMountingManager == null) {
SurfaceMountingManager smm = getSurfaceMountingManager(surfaceId, reactTag);
if (smm == null) {
return null;
}
return surfaceMountingManager.getEventEmitter(reactTag);
return smm.getEventEmitter(reactTag);
}
/**
@@ -434,11 +431,21 @@ public class MountingManager {
boolean canCoalesceEvent,
@Nullable WritableMap params,
@EventCategoryDef int eventCategory) {
@Nullable SurfaceMountingManager smm = getSurfaceManager(surfaceId);
SurfaceMountingManager smm = getSurfaceMountingManager(surfaceId, reactTag);
if (smm == null) {
// Cannot queue event without valid surface mountng manager. Do nothing here.
FLog.d(
TAG,
"Cannot queue event without valid surface mounting manager for tag: %d, surfaceId: %d",
reactTag,
surfaceId);
return;
}
smm.enqueuePendingEvent(reactTag, eventName, canCoalesceEvent, params, eventCategory);
}
private @Nullable SurfaceMountingManager getSurfaceMountingManager(int surfaceId, int reactTag) {
return (surfaceId == ViewUtil.NO_SURFACE_ID
? getSurfaceManagerForView(reactTag)
: getSurfaceManager(surfaceId));
}
}
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<6cc52570dd571ddc792a0fd842c05dd9>>
* @generated SignedSource<<89491eb63a7ca59b17419ed4432a4f88>>
*/
/**
@@ -34,12 +34,6 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun commonTestFlag(): Boolean = accessor.commonTestFlag()
/**
* Adds support for recursively processing commits that mount synchronously (Android only).
*/
@JvmStatic
public fun allowRecursiveCommitsWithSynchronousMountOnAndroid(): Boolean = accessor.allowRecursiveCommitsWithSynchronousMountOnAndroid()
/**
* When enabled, the RuntimeScheduler processing the event loop will batch all rendering updates and dispatch them together at the end of each iteration of the loop.
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<eca842a1b1c823b72136c625b3bfd16e>>
* @generated SignedSource<<9f741ec3df7cd5ecd8d5c3c099c86aba>>
*/
/**
@@ -21,7 +21,6 @@ package com.facebook.react.internal.featureflags
public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccessor {
private var commonTestFlagCache: Boolean? = null
private var allowRecursiveCommitsWithSynchronousMountOnAndroidCache: Boolean? = null
private var batchRenderingUpdatesInEventLoopCache: Boolean? = null
private var completeReactInstanceCreationOnBgThreadOnAndroidCache: Boolean? = null
private var destroyFabricSurfacesInReactInstanceManagerCache: Boolean? = null
@@ -78,15 +77,6 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
return cached
}
override fun allowRecursiveCommitsWithSynchronousMountOnAndroid(): Boolean {
var cached = allowRecursiveCommitsWithSynchronousMountOnAndroidCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.allowRecursiveCommitsWithSynchronousMountOnAndroid()
allowRecursiveCommitsWithSynchronousMountOnAndroidCache = cached
}
return cached
}
override fun batchRenderingUpdatesInEventLoop(): Boolean {
var cached = batchRenderingUpdatesInEventLoopCache
if (cached == null) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<ad54375c4ae3be2f377260887ae5aaf9>>
* @generated SignedSource<<774337b6aee6f528b0852704271ed96f>>
*/
/**
@@ -30,8 +30,6 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun commonTestFlag(): Boolean
@DoNotStrip @JvmStatic public external fun allowRecursiveCommitsWithSynchronousMountOnAndroid(): Boolean
@DoNotStrip @JvmStatic public external fun batchRenderingUpdatesInEventLoop(): Boolean
@DoNotStrip @JvmStatic public external fun completeReactInstanceCreationOnBgThreadOnAndroid(): Boolean
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<2a0cd5a4875a54bb724e5765ffe7753e>>
* @generated SignedSource<<43c4ba7a6c4f5a12ada181c081f91bfc>>
*/
/**
@@ -25,8 +25,6 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun commonTestFlag(): Boolean = false
override fun allowRecursiveCommitsWithSynchronousMountOnAndroid(): Boolean = false
override fun batchRenderingUpdatesInEventLoop(): Boolean = false
override fun completeReactInstanceCreationOnBgThreadOnAndroid(): Boolean = false
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<d02af2a8ef015c57d45aba8280539606>>
* @generated SignedSource<<0ca6ebf7ef1418d721b6f183f89b96a2>>
*/
/**
@@ -25,7 +25,6 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
private val accessedFeatureFlags = mutableSetOf<String>()
private var commonTestFlagCache: Boolean? = null
private var allowRecursiveCommitsWithSynchronousMountOnAndroidCache: Boolean? = null
private var batchRenderingUpdatesInEventLoopCache: Boolean? = null
private var completeReactInstanceCreationOnBgThreadOnAndroidCache: Boolean? = null
private var destroyFabricSurfacesInReactInstanceManagerCache: Boolean? = null
@@ -83,16 +82,6 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun allowRecursiveCommitsWithSynchronousMountOnAndroid(): Boolean {
var cached = allowRecursiveCommitsWithSynchronousMountOnAndroidCache
if (cached == null) {
cached = currentProvider.allowRecursiveCommitsWithSynchronousMountOnAndroid()
accessedFeatureFlags.add("allowRecursiveCommitsWithSynchronousMountOnAndroid")
allowRecursiveCommitsWithSynchronousMountOnAndroidCache = cached
}
return cached
}
override fun batchRenderingUpdatesInEventLoop(): Boolean {
var cached = batchRenderingUpdatesInEventLoopCache
if (cached == null) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<92b1214e3a526d7c67dcc7b0c2a131de>>
* @generated SignedSource<<94e1e69be22ec978859e3f242610f21b>>
*/
/**
@@ -25,8 +25,6 @@ import com.facebook.proguard.annotations.DoNotStrip
public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun commonTestFlag(): Boolean
@DoNotStrip public fun allowRecursiveCommitsWithSynchronousMountOnAndroid(): Boolean
@DoNotStrip public fun batchRenderingUpdatesInEventLoop(): Boolean
@DoNotStrip public fun completeReactInstanceCreationOnBgThreadOnAndroid(): Boolean

Some files were not shown because too many files have changed in this diff Show More