Compare commits

...
Author SHA1 Message Date
Nick GerlemanandNicola Corti 5dd406cb36 Fix build_android GHA Job (#52694)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52694

build_android job is always failing, with this assertion. Seems like find/replace in D78484060 gone wrong?

Changelog: [Internal]

Reviewed By: sbuggay

Differential Revision: D78534334

fbshipit-source-id: 291bdd01b41fa6efea00ed63a0dee8bdb14cbc3a
2025-08-11 15:48:20 +01:00
Riccardo CipolleschiandNicola Corti 550898a325 Fix Windows CI (#52666)
Summary:
As per [this issue](https://github.com/actions/runner-images/issues/12416), Windows machine doesn't have access to D: drive anymore

## Changelog:
[Internal] -

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

Test Plan: GHA

Reviewed By: huntie

Differential Revision: D78484060

Pulled By: cipolleschi

fbshipit-source-id: 36d844f9d7d69f1d74a154b019307cc1e269ad66
2025-08-11 15:48:13 +01:00
Fabrizio Cucci d4f7b991ab Update Podfile.lock
Changelog: [Internal]
2025-07-09 19:12:52 +01:00
React Native Bot 026ea353de Release 0.78.3
#publish-packages-to-npm&0.78-stable
2025-07-09 15:32:45 +00:00
Fabrizio Cucci aa29779a07 Update Podfile.lock
Changelog: [Internal]
2025-07-09 15:07:05 +01:00
Fabrizio Cucci 63c8f7ebd5 Exclude selectively disabled libraries from codegen generation (#51078)
Summary:
Selectively disabling autolinking of a native dependency with components registered in the codegen configuration in react-native.config.js causes builds to crash upon launch on iOS. This is because the generated `RCTThirdPartyComponentsProvider.mm` file contains references to the excluded library using `NSClassFromString` causing the returned NSDictionary from `+[RCTThirdPartyComponentsProvider thirdPartyFabricComponents]` to be populated with nil values and therefore crashing the app. This has been confirmed in 0.78.2 and 0.79.2 but probably exists in 0.77.x as well.

The issue has been further described in #51077.

[IOS][FIXED] - Skip codegen for selectively disabled libraries in react-native.config.js

Pull Request resolved: #51078

Test Plan:
1. Install a library that has the componentProvider field set in the codegen config (see reproducer) and install the iOS pods.
2. Build the app.
3. App should run successfully without any crashes.

Reviewed By: cortinico

Differential Revision: D74248371

Pulled By: cipolleschi

fbshipit-source-id: 1ff7b477ed3d94ca45616ae243d3d2d30bd897db
2025-07-09 14:07:41 +01:00
Nicola CortiandFabrizio Cucci 58a4c2fe36 RNGP - Add support for exclusiveEnterpriseRepository (#52378)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52378

This adds a Gradle property called `exclusiveEnterpriseRepository`
that users can set in their `android/gradle.properties` as such:

```diff
hermesEnabled=true

+exclusiveEnterpriseRepository=https://my.internal.proxy.net/
```

This will remove all the existing Maven repositories and only use the internal mirror they have.

Changelog:
[Android] [Added] - RNGP - Add support for `exclusiveEnterpriseRepository` to specify an internal Maven mirror.

Reviewed By: mdvacca

Differential Revision: D77667573

fbshipit-source-id: 835004d2ae7aa4e250b6f7a88a41918b573f5bd5
2025-07-09 12:47:31 +01:00
Oskar KwaśniewskiandFabrizio Cucci ef42f5f996 fix(iOS): bring back enableFixForViewCommandRace feature flag (#49126)
Summary:
As pointed out by RyanCommits the ReactNativeFactory PR removed `enableFixForViewCommandRace` feature flag by mistake. Reference: https://github.com/facebook/react-native/pull/46298/files

This PR re-adds the feature flag.

## Changelog:

[IOS] [FIXED] - Re-enable enableFixForViewCommandRace feature flag

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

Test Plan: Not needed, the feature flag was there before refactor.

Reviewed By: huntie

Differential Revision: D69049668

Pulled By: cipolleschi

fbshipit-source-id: b7bf382c76878e72619145283fa8cc2c1046b486
2025-07-09 11:37:55 +01:00
zhongwuzwandFabrizio Cucci 78d6092385 iOS: Fixes Switch component incorrectly renders as toggled on even though value prop is hardcoded to false (#50049)
Summary:
Fixes https://github.com/facebook/react-native/issues/50026

## Changelog:

[IOS] [FIXED] - [Fabric] iOS: Fixes Switch component incorrectly renders as toggled on even though value prop is hardcoded to false

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

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

Reviewed By: javache

Differential Revision: D71307864

Pulled By: cipolleschi

fbshipit-source-id: 57238296842229cf2f23a3f44134f43e27b54609
2025-07-09 11:35:08 +01:00
Ritesh ShuklaandFabrizio Cucci 6b2826e67c Fixed borderBottomEndRadius on RTL (#51229)
Summary:
Fixes https://github.com/facebook/react-native/issues/51193
[ANDROID][FIXED] Wrong borderBottomEndRadius on RTL

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

Test Plan:
Tested with Arabic (RTL) and English(LTR)

English
<img src="https://github.com/user-attachments/assets/5bd1eca9-194c-4d83-b75f-689bc13d827b" width=200>

Arabic
<img src="https://github.com/user-attachments/assets/459320a0-6bb2-4585-97bb-bd4ded6ec399" width=200>

Reviewed By: cortinico

Differential Revision: D74709219

Pulled By: NickGerleman

fbshipit-source-id: 885bb84d97d73324983d044fd2874a457b40f646
2025-07-09 11:34:30 +01:00
chrsmysandFabrizio Cucci 1a1814a466 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-07-09 11:33:48 +01:00
Phil PluckthunandFabrizio Cucci 157cf118ce fix: Align TimerManager sequential ids and function error handling with web standard (#51500)
Summary:
Calls to create timers should return sequential ids (integers greater than zero in the spec's words). This regressed in the `TimerManager` implementation, which instead starts at zero inclusively.

This has two side-effects for code assuming a spec-compliant implementation of `setTimeout` and `setInterval`:
- Calls to `clearTimeout(0)` or `clearInterval(0)` will potentially cancel scheduled timers, although it's supposed to be a noop
- Predicates like `if (timeoutId)` will fail since they assume non-negative ids

The change in this PR is to align with WHATWG HTML 8.6.2 (Timers): https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers

> otherwise, let id be an [implementation-defined](https://infra.spec.whatwg.org/#implementation-defined) integer that is **greater than zero** and does not already [exist](https://infra.spec.whatwg.org/#map-exists) in global's [map of setTimeout and setInterval IDs](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#map-of-settimeout-and-setinterval-ids).

Specifically,
- we should return `0` to indicate that no timer was scheduled
- we should start generating timer IDs at `1` instead of `0`

This was previously raised in review comments here: https://github.com/facebook/react-native/pull/45092/files#r1650790008

The spec-incompliant behaviour was raised in an issue here: https://github.com/apollographql/apollo-client/issues/12632#issue-3075269978

This PR does not,
- add bounds checking on `timerIndex_` and add a search of an available id that isn't in the unordered map
- exclude `0` from being an accepted `TimerHandle` in `TimerManager::createTimer` or `TimerManager::deleteTimer` since the above bounds checking hasn't been added either

## Changelog:

[GENERAL] [FIXED] - Align timer IDs and timer function argument error handling with web standards.

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

Test Plan:
- Run `setTimeout` / `setInterval`; before applied changes the timeout for the first timer will be `0`
- Run `setTimeout(null)`; before applied changes the timer ID will be non-zero
- Run `setInterval(null)`; before applied changes an error will be thrown rather than `0` being returned

Reviewed By: cipolleschi

Differential Revision: D75145909

Pulled By: rshest

fbshipit-source-id: 6646439abd29cf3cfa9e5cf0a57448e3b7cd1b48
2025-07-09 11:33:12 +01:00
Christian FalchandFabrizio Cucci d59159df17 fix adding children (#51213)
Summary:
See https://github.com/facebook/react-native/issues/51212 - children aren't updated correctly in an old arch native view using the interop layer under Fabric.

This is caused by the mountChildComponentView method not updating the view, only adding the new view to a list that will be used on the next update to mount the child.

This commit fixes this by adding the same pattern as in unmountChildComponentView where children are inserted directly if the underlying paperview is available in the adapter - otherwise it uses the mounting list as before.

#Closes 51212

bypass-github-export-checks

## Changelog:

[IOS] [FIXED] - fixed adding child views to a native view using the interop layer

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

Test Plan:
**Previous output**:

<image src="https://github.com/user-attachments/assets/472b95e7-0921-46c9-be6a-f31759c0cd26" width="200px" />

**After fix**:

<image src="https://github.com/user-attachments/assets/554387cd-c264-483e-9c52-d9cd40b42601" width="200px" />

Reviewed By: sammy-SC

Differential Revision: D74471278

Pulled By: cipolleschi

fbshipit-source-id: 798f9e7be389359bd6e3aa1b6a6e9fb799fcb369
2025-07-09 11:32:36 +01:00
Riccardo CipolleschiandFabrizio Cucci 8d0191151e Bump windows to latests as GH will remove windows-2019 EOM (#51798)
Summary:
As per title, GH is [removing windows-2019](https://github.com/facebook/react-native/actions/runs/15421451006/job/43403215354) at the end of the month. We need to migrate away from them.

## Changelog:
[Internal] - Bump windows runners

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

Test Plan: GHA

Reviewed By: cortinico

Differential Revision: D75946994

Pulled By: cipolleschi

fbshipit-source-id: 972b60300c918c0eae0403ed4149347a4ffa8bd0
2025-07-09 11:28:04 +01:00
Riccardo CipolleschiandReact Native Bot 8367b4250a Pin cmake version to 3.31.6 (#50464)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/50464

Runners in GHA has been updated by github and they now ship with CMake 4.0. (actions/runner-images#11926)

This version is not compatible with React Native, so we are pinning cmake to 3.36.1

## Changelog:
[Internal] - Pin cmake to 3.36.1

Reviewed By: cortinico

Differential Revision: D72379834

fbshipit-source-id: ab09009102118e6590f02cf57fa6f9149482f62b
2025-07-08 17:50:15 +00:00
Alex HuntandGitHub 07717b5275 Update debugger-frontend from 7727db85...911a4d98 (S523384 mitigation) (#51571) 2025-07-08 11:27:26 +01:00
Nicola Corti 7f664d9811 Publish, Close and Release the Maven repository during build_npm_package
Summary:
Due to us moving to central.sonatype.com for publishing, we cannot publish and release the Maven repository in 2 distinct invocations.
This consolidates all the publishing job to happen during build_npm_package

Changelog:
[Internal] [Changed] -

Reviewed By: fabriziocucci

Differential Revision: D76888543

fbshipit-source-id: 3cb0db6176ed2221a12b4f3f1f575232aa006a6c
2025-06-18 14:00:23 +01:00
Nicola Corti 36c47a6572 Update Nightly URL for newly published versions on central.sonatype.com (#52004)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/52004

This is necessary because the snapshots are now going to be published on a different repository:
central.sonatype.com.

Changelog:
[Internal] [Changed] -

Reviewed By: cipolleschi

Differential Revision: D76596802

fbshipit-source-id: 424fb1134e41502d53b76209fba325c895c79ba8
2025-06-16 10:44:04 +01:00
Nicola Corti 5dd0369033 Move React Native publishing URLs to Central Portal (#51693)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/51693

This moves React Native to use the Central Portal URLs rather than the legacy OSSRH ones.
See https://github.com/gradle-nexus/publish-plugin for more context.

Changelog:
[Internal] [Changed] -

Reviewed By: cipolleschi

Differential Revision: D75673984

fbshipit-source-id: 1de6746809eed72f232eac0c3fb4d809c2046620
2025-06-16 10:44:04 +01:00
Riccardo Cipolleschi 541c8d577f [LOCAL] Bump Podfile.lock 2025-04-01 17:26:15 +02:00
React Native Bot f3d1ad1204 Release 0.78.2
#publish-packages-to-npm&latest
2025-04-01 12:57:44 +00:00
Riccardo Cipolleschi ae1841ac96 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-04-01 11:28:22 +02:00
Rubén NorteandReact Native Bot aa75e94831 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-04-01 09:23:43 +00:00
Rubén NorteandReact Native Bot eb8c1c3e95 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-04-01 09:22:57 +00:00
Zhi ZhouandReact Native Bot 5861f7eea7 fix: iOS app crash caused by the request operation canceling (#48350)
Summary:
Currently we observed many iOS app crashes caused by the `[RCTFileRequestHanlder invalidate]` method, just as the below screenshot.
<img width="1008" alt="image" src="https://github.com/user-attachments/assets/d2d6714f-63d9-40ae-8de5-742cfe718a36" />

## Changelog:

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

Pick one each for the category and type tags:

[IOS] [FIXED] - app crash caused by the `[RCTFileRequestHanlder invalidate]` method

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->
[IOS] [FIXED] - app crash caused by the `[RCTFileRequestHanlder invalidate]` method

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

Test Plan: I am not able to reproduce this issue locally either, so the changes in this PR are totally from my inference, I am not sure if it really makes sense, so please help take a deeper look, thanks.

Reviewed By: javache

Differential Revision: D69751695

Pulled By: cipolleschi

fbshipit-source-id: aa4654a30f5dfac99b72ed1bda0dae1e0dc881c9
2025-04-01 09:22:06 +00:00
Riccardo CipolleschiandReact Native Bot 994ea3b44a Add missing loadFromSource method in the DefaultRNFactoryDelegate (#49931)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49931

This change fixes the app startup in the Old Architecture by implementing the loadSourceForBridge:onProgress:onComplete method in the RCTDefaultReactNativeFactoryDelegate object.

The method was missing here, so the Bridge was never trying to load the JS bundle from Metro, resulting in an empty app.

## Changelog:
[iOS][Fixed] - Implement the loadSourceForBridge:onProgress:onComplete in the RCTDefaultReactNativeFactoryDelegate.

Reviewed By: cortinico

Differential Revision: D70898811

fbshipit-source-id: 3e5d519a1965e92ace91ca6d5b316a9069279448
2025-04-01 09:21:44 +00:00
Eric RozellandReact Native Bot 31e09bcc4d Fix issue with zIndex child removal (#49900)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49900

This appears to fix an issue where removing a sibling with zIndex breaks drawing of the next sibling. The theory is that eager return in `onViewRemoved` prevents the view from reverting into a state where it no longer uses custom draw order. However, tracing back history, this eager return was [added](https://github.com/facebook/react-native/pull/43389) to fix a bug in Reanimated. cc bartlomiejbloniarz to confirm if [this Reanimated issue](https://github.com/software-mansion/react-native-reanimated/issues/5715) resurfaces from this change.

Fixes #49838

## Changelog

[Android][Fixed] Fixes issue with z-indexed sibling removal

Reviewed By: NickGerleman, cipolleschi

Differential Revision: D70795631

fbshipit-source-id: 500af92226be29af73f36f911ffff27a0c083ae9
2025-04-01 09:20:55 +00:00
Olivier BouilletandReact Native Bot 301532b51f fix: set text position should not reset component text (#49450)
Summary:
fix: https://github.com/facebook/react-native/issues/49368
description is provided inside the ticket.
When we use TextInput on ios and manage selection with the selection prop, TextInput is reset when we change selection.

## Changelog:

[IOS] [FIXED] - Fix selection makes TextInput clear its content when using children

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

Test Plan:
Tested with sample provided in ticket.
I also test it with my app on both android and ios, but I cannot share video

Reviewed By: sammy-SC

Differential Revision: D69984616

Pulled By: cipolleschi

fbshipit-source-id: a17169608f9df0ea1cb579e6038345f8e48bbc27
2025-04-01 09:20:18 +00:00
Rob HoganandGitHub 23c9dbc563 Metro minimum to 0.81.3, fix "_interopRequireDefault is not a function" (#50129) 2025-04-01 11:18:23 +02:00
Riccardo CipolleschiandGitHub 1e54c5d305 Remove the v prefix from the version passed by GHA to the script (#50130) 2025-03-20 13:34:28 +00:00
Riccardo Cipolleschi 166ea3166c [LOCAL][RN][iOS] Bump Podfile.lock 2025-03-19 13:57:20 +00:00
React Native Bot 1299ef7be0 Release 0.78.1
#publish-packages-to-npm&latest
2025-03-19 10:29:06 +00:00
Riccardo CipolleschiandReact Native Bot 29e39d1ed6 Handle null params in the Interop TM layer (#49873)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49873

In the old architecture, when we were passing a `null` value as a parameter in a function that accepted nullable parameter, the null value was mapped to `nil` on iOS.

After my changes in [d4236791e2](https://github.com/facebook/react-native/commit/d4236791e238a614d2fadf5c5659874d983ab029), in the New Architecture, through the interop layer, legacy modules were receiving an `NSNull` object instead of nil.

This was breaking those modules which started crashing or observing undesired behavior.

This change fixes the issue by making sure that, in those cases, a `nil` value is passed.

Note that nested objects in the old architecture were correctly receiving NSNull, so nested objects were behaving correctly already.

## Changelog:
[iOS][Fixed] - Properly pass `nil` for nullable parameters instead of `NSNull` for legacy modules

Reviewed By: javache

Differential Revision: D70723460

fbshipit-source-id: 384f48b6dbb3f54c369b31b6d2ee06069fa3591c
2025-03-18 15:44:16 +00:00
Rob HoganandGitHub 9ffbeadf8a community-cli-plugin: resolve cli-server-api via peer dependency on cli (#49518) (#50098)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49518

`react-native/community-cli-plugin` depends on `createDevServerMiddleware` from `react-native-community/cli-server-api`.

`react-native/community-cli-plugin` currently [declares an optional peer dependency](https://github.com/facebook/react-native/blob/bae895500052bda2f55e1832b0c8a63a1b449de3/packages/community-cli-plugin/package.json#L39-L45) on `react-native-community/cli-server-api`, however because the latter isn't a dependency of `react-native` or the community template, the peer dependency is not available to package managers that enforce isolated node_modules - see https://github.com/facebook/react-native/issues/47309.

Rather than add an unnecessary dependency to the template (like [this](https://github.com/react-native-community/template/pull/105)), my proposal is to switch to a peer dependency on only `react-native-community/cli`, because that *is* a dependency of the community template and therefore will be resolvable.

Because `react-native-community/cli` doesn't re-export `createDevServerMiddleware` from its dependency on `cli-server-api`, we need to resolve the latter through the former. This can be cleaned up once a re-export lands - https://github.com/react-native-community/cli/pull/2605.

Changelog:
[GENERAL][FIXED] Fix registering of `start` and `bundle` commands with community CLI and isolated node_modules.

Reviewed By: huntie

Differential Revision: D69848688

fbshipit-source-id: 009b8ffd43b2ab2d84fcc71e9e48382eb8950bb1
2025-03-18 15:15:48 +00:00
Mateo GuzmánandRiccardo Cipolleschi 665212e22c Fix Android Image defaultSource runtime error (#49097)
Summary:
Fixes https://github.com/facebook/react-native/issues/49075

The Image `defaultSource` prop is causing a runtime error from 0.77 just by using it in the Image component (see error in the linked issue). This might be a regression from some changes in the prop processing logic from either https://github.com/facebook/react-native/issues/47710, https://github.com/facebook/react-native/issues/47713 or https://github.com/facebook/react-native/issues/47754.

[ANDROID] [FIXED] - Fix Image defaultSource runtime error

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

Test Plan:
- Verify it doesn't throw any error on runtime anymore for Android.
- iOS behaviour should not be impacted as changes are Android specific

In the RNTester, you can use the following component:

```tsx
import * as React from 'react';
import {Image, View} from 'react-native';

function Playground() {
  return (
    <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
      <Image
        defaultSource={require('../../assets/bandaged.png')}
        source={{
          uri: 'https://i.natgeofe.com/n/548467d8-c5f1-4551-9f58-6817a8d2c45e/NationalGeographic_2572187_4x3.jpg',
        }}
        style={{width: 200, height: 200}}
      />
    </View>
  );
}
```

Also, this `defaultSource` prop is ignored in debug builds for Android ([as per the docs](https://reactnative.dev/docs/image#defaultsource)) – but I've verified we get the defaultSource as a string, which is what we expect on the native side:

<details>
<summary>Screenshot of the Android logs</summary>

![image](https://github.com/user-attachments/assets/e62ae2c3-6a93-4e44-a2d7-c913f5db2173)

</details>

Reviewed By: javache

Differential Revision: D69052723

Pulled By: cortinico

fbshipit-source-id: 2860dd4c18cefcfcbc4e39f94dfa6305f45773a3
2025-03-18 11:42:15 +00:00
zhongwuzwandReact Native Bot 282cdc9fb4 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:00 +00:00
Vitali ZaidmanandReact Native Bot 9e47ed9a20 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:39:06 +00:00
Riccardo CipolleschiandGitHub 12b70ae285 [RN][Releases] Fix Publish template (#49789)
The publishTemplate script references a variable that is not defined.

## Changelog:
[Internal] -
2025-03-11 11:37:41 +00:00
Bruno AybarandReact Native Bot 97adbd897f Fix issue with extraModulesForBridge callback (#49849)
Summary:
Fixes https://github.com/facebook/react-native/issues/49819 . Details about how the issue was introduced in the issue description.
bypass-github-export-checks
## Changelog:

[IOS] [FIXED] - Fixed: extraModulesForBridge callback not called when New Architecture enabled

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

Test Plan:
Without the change:
1. Open `packages/rn-tester` project
2. In `AppDelegate.mm`, implement `extraModulesForBridge` and add a breakpoint / output something
3. Run the app in iOS <-- Verify that the method is not executed

With the change:

1-3. Same as above <-- verify that the method is called correctly

> [!NOTE]
> As far as I could tell, there is no test suite for this specific codepath, so I didn't write a test for this change. Happy to write one if someone can guide me a little bit.

Reviewed By: rshest

Differential Revision: D70724196

Pulled By: cipolleschi

fbshipit-source-id: cc08798d08cdbd6883347810c7d2697c358770fb
2025-03-10 17:24:18 +00:00
68dc582305 Fix elevation with border-radius set (#48982) (#49877)
* Fix elevation with border-radius set (#48982)

* Update packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/drawable/CompositeBackgroundDrawable.kt

---------

Co-authored-by: Nicola Corti <corti.nico@gmail.com>
2025-03-07 12:06:04 +00:00
Alex HuntandReact Native Bot 123bfb89be Update cli-server-api middleware imports (#48997)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48997

Follows https://github.com/react-native-community/cli/pull/2584.

- Also add FIXME comment flagging potential core APIs gap without CLI.

Changelog: [Internal]

Reviewed By: cortinico

Differential Revision: D68766565

fbshipit-source-id: 60747715f76c4323e306c39ab0613fb4818b4914
2025-03-06 16:35:19 +00:00
zhongwuzwandReact Native Bot bdc83cb129 Added custom load js block in bridge mode (#48845)
Summary:
`loadSourceForBridge` is broken after we refactor the appdelegate. So let's add it back.

## Changelog:

[IOS] [FIXED] - Added custom load js block in bridge mode

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

Test Plan: Custom Appdelegate's `loadSourceForBridge` can be called in bridge mode.

Reviewed By: robhogan

Differential Revision: D68832046

Pulled By: cipolleschi

fbshipit-source-id: dcea791e6d8243fdb2f45a33af175aee1a4e1223
2025-03-03 15:14:05 +00:00
Fabrizio Cucci 721f5d33ea Update Podfile.lock
Changelog: [Internal]
2025-02-20 10:31:29 +00:00
React Native Bot c64256bf2b Release 0.78.0
#publish-packages-to-npm&latest
2025-02-19 10:23:10 +00:00
Fabrizio Cucci 1e5ae5f7ba Update Podfile.lock
Changelog: [Internal]
2025-02-13 12:46:55 +00:00
React Native Bot a2f17c5361 Release 0.78.0-rc.5
#publish-packages-to-npm&next
2025-02-13 10:59:19 +00:00
Alex HuntandGitHub 86db4fa90b Restore Metro log streaming via CLI flag (#49354)
Summary:
This change adds an opt-in to restore JavaScript log streaming via the Metro dev server, [removed from React Native core in 0.77](https://reactnative.dev/blog/2025/01/21/version-0.77#removal-of-consolelog-streaming-in-metro).

Users can opt into this legacy behaviour by adding the `--client-logs` flag to `npx react-native-community/cli start`.

- The default experience remains without streamed JS logs.
- The existing "JavaScript logs have moved! ..." notice is printed in all cases, and we do not advertise the new flag for new users.
- Under non-Community CLI dev servers (i.e. Expo), log streaming is restored implicitly.

We will clean up this functionality again when we eventually remove JS log streaming over `HMRClient`, tasked in T214991636.

**Implementation notes**

- Logs are always sent over `HMRClient` (previous status quo), even with log streaming off in the dev server. This is a necessary evil to be able to flag this functionality in a user-accessible place, and to move fast for 0.78.
- Necessarily, emitting `fusebox_console_notice` moves to the dev server itself, on first device (Fusebox) connection.

Changelog:
[General][Added] - Add opt in for legacy Metro log streaming via `--client-logs` flag
2025-02-12 14:36:52 +00:00
Riccardo CipolleschiandFabrizio Cucci 7b088fa0b1 Allow multiple RN instances to run at the same time (#49300)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49300

With the refactor of the AppDelegate in favor ReactNativeFactory, the users can now instantiate multiple instances of react native.
However, currently, if you try to run multiple instances, the app will crash with the message:

```
libc++abi: terminating due to uncaught exception of type std::runtime_error: Feature flags cannot be overridden more than once
```

This happens also when the feature flags we would like to set are the same that we already applied. This should be an allowed scenario because reapplying the excatly same features flags should have no effect on React native and that's not the use case we want to forbid.

With this change, we are creating a static variable that checks whether we already apply that set of feature flags and it allows you to create multiple instances by keeping the same flags

## Changelog:
[iOS][Fixed] - Allow multiple RN instances to run at the same time

Reviewed By: rubennorte

Differential Revision: D69398441

fbshipit-source-id: a377c6a1402d38d66d348fa8c6a65e645973aadc
2025-02-12 14:34:47 +00:00
David VaccaandFabrizio Cucci 9e664dc144 Make UIBlock.execute params non nullable
Summary:
Make UIBlock.execute params non nullable to avoid breaking changes for kotlin usages in OSS

changelog: [internal] internal

Reviewed By: NickGerleman

Differential Revision: D69407325

fbshipit-source-id: 5fc01fba9baba97e21a388d02cf98d5aebb5ac20
2025-02-12 14:34:05 +00:00
Fabrizio Cucci 98e8e8462e Update Podfile.lock
Changelog: [Internal]
2025-02-11 16:43:49 +00:00
React Native Bot 461ec6555f Release 0.78.0-rc.4
#publish-packages-to-npm&next
2025-02-11 10:54:41 +00:00
Riccardo CipolleschiandGitHub ebfd7057af [RN][iOS] Properly handle null values in TM interop layer (#49291) 2025-02-10 15:35:29 +00:00
Pieter De BaetsandFabrizio Cucci b96e94873d Revert visibility change of ReactCookieJarContainer (#49247)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49247

This was incorrectly made internal in https://www.internalfb.com/diff/D66724567

Changelog: [Android][Removed] Made ReactCookieJarContainer internal.

Reviewed By: cortinico, andrewdacenko

Differential Revision: D69254203

fbshipit-source-id: 5c4ba9b4f9a8e53002df25b55f0c8762874e6736
2025-02-10 15:19:17 +00:00
Nicola CortiandFabrizio Cucci ef145305b1 Undo a breaking change on ReactOverflowView (#49229)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49229

This interface was converted to Kotlin, but the single method should have been converted to a `val`.
People kotlin consumers could call ReactOverflowView.overflow; now they need to call getOverflow().

Changelog:
[Internal] [Changed] - Undo a breaking change on ReactOverflowView

Reviewed By: NickGerleman

Differential Revision: D69250226

fbshipit-source-id: 5c7cca8c83f5c76a9cc1d254f8aa51409150c356
2025-02-10 14:59:04 +00:00
Nicola CortiandFabrizio Cucci 80726279d8 Undo breaking change in ReactPointerEventsView due to Kotlin conversion (#49233)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49233

I'm converting the function inside ReactPointerEventsView from `fun` to `val`.
This Kotlin conversion resulted in a breakign change for Kotlin consumer which I believe can be prevented
if we do this change instead.

Changelog:
[Internal] [Changed] -

Reviewed By: alanleedev

Differential Revision: D69252562

fbshipit-source-id: b277c6720f3156ed532bf5f2253d54cd72e38050
2025-02-10 14:58:55 +00:00
David VaccaandFabrizio Cucci 9468ac632d 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-10 14:58:38 +00:00
Rob HoganandFabrizio Cucci a567736f1a Fix "paused on debugger" overlay icon (#48736)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48736

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

This icon was broken by D65457771, identified in [OSS testing for the 0.77 release](https://github.com/reactwg/react-native-releases/issues/724).

By explicitly setting the image for the `Disabled` state to the same as `Normal`, we get the same behaviour as the deprecated [`adjustsImageWhenDisabled = NO`](https://developer.apple.com/documentation/uikit/uibutton/adjustsimagewhendisabled?language=objc) without the need for `configurationUpdateHandler`.

Changelog: [iOS][Fixed] Restore "Paused in debugger" overlay icon

Reviewed By: cipolleschi

Differential Revision: D68274336

fbshipit-source-id: 3f4b84eb7cfb518ca953c721da9885df8f98b437
2025-02-10 14:58:04 +00:00
Riccardo CipolleschiandFabrizio Cucci d5b9e797cb 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-10 14:57:50 +00:00
Riccardo Cipolleschi 5f1aeffe7b [LOCAL] Bump Podfile.lock 2025-02-03 20:31:22 +00:00
React Native Bot 82b78f3fec Release 0.78.0-rc.3
#publish-packages-to-npm&next
2025-02-03 17:46:38 +00:00
Riccardo Cipolleschi 5c55580a06 Add extra parameter to define whether codegen is invoked by lib or app (#48995)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48995

This change adds an extra parameter to the codegen script that allow our users to trigger codegen for Apps or for Libraries.

When running codegen for Apps, we have to generate some extra files that are not needed by the Libraries. This is causing issues to our library maintainers and this change will provide more flexibility in the DevX of libraries.

The default value is App, so if the new parameter is not passed, nothing will change in the current behavior.

[iOS][Added] - Add the `source` parameter to generate-codegen-artifacts to avoid generating files not needed by libraries.

Reviewed By: cortinico

Differential Revision: D68765478

fbshipit-source-id: 8030b4472ad4f5058e58b1c91089de5122a4f60a
2025-02-03 15:29:11 +00:00
Rickard ZrinskiandRiccardo Cipolleschi ea49d4d1b0 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

## Changelog:

[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.

### Text
| 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" /> |

### TextInput
| 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-03 15:27:52 +00:00
Tommy NguyenandRiccardo Cipolleschi cbf4f26dea 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-03 15:27:44 +00:00
Cedric van PuttenandRiccardo Cipolleschi 1bd9d36b70 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-02-03 15:27:39 +00:00
Fabrizio Cucci 76da20aab4 Update Podfile.lock
Changelog: [Internal]
2025-01-29 11:42:42 +00:00
React Native Bot 465f49ea2a Release 0.78.0-rc.2
#publish-packages-to-npm&next
2025-01-28 13:44:52 +00:00
Riccardo CipolleschiandFabrizio Cucci 8207a09107 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-01-27 14:59:35 +00:00
Fabrizio Cucci 7e883be800 Update Podfile.lock
Changelog: [Internal]
2025-01-22 10:32:32 +00:00
React Native Bot 1f412d0826 Release 0.78.0-rc.1
#publish-packages-to-npm&next
2025-01-20 20:09:09 +00:00
Tim YungandFabrizio Cucci 3abb4cab7d Animated: Defer onAnimatedValueUpdate on Attach + Native (#48715)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48715

{D68154908} fixed a problem with the `onAnimatedValueUpdate` listener not being correctly attached if `__attach` were called before `__makeNative` (which sets `__isNative` to true).

We're potentially seeing production symptoms of stuttering interactions and user responsiveness, after queuing up many operations. Our hypothesis is that in scenarios where `ensureUpdateSubscriptionExists` is being called during `__makeNative` (instead of during `__attach`), a backup of operations occurs leading to these symptoms.

This diff attempts to validate and mitigate this hypothesis by deferring `ensureUpdateSubscriptionExists` to when an `AnimatedValue` instance has had both `__attach` and `__makeNative` invoked.

Changelog:
[Internal]

Differential Revision: D68236594

fbshipit-source-id: 2089100a773ebfc161fb5b567123eb58a893939f
2025-01-20 15:08:03 +00:00
Tim YungandFabrizio Cucci bb5f971389 Animated: Fix onUserDrivenAnimationEnded w/ Insertion Effects (#48678)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48678

While diagnosing a recent issue in which `AnimatedValue` instances were not being correctly updated as expected, the insertion effects feature flag was identified as a root cause.

Upon further investigation, it appears that this is because the `onUserDrivenAnimationEnded` listener was not implemented the same way in the two feature flag states:

- When `useInsertionEffectsForAnimations` is disabled, `useAnimatedProps` listens to `onUserDrivenAnimationEnded` in a passive effect, after all nodes have been attached.
- When `useInsertionEffectsForAnimations` is enabled, `useAnimatedProps` listens to `onUserDrivenAnimationEnded` in an insertion effect when attaching nodes.

The bugs occurs because `useAnimatedProps` checks whether native driver is employed to decide whether to listen to `onUserDrivenAnimationEnded`. However, we do not know whether native driver will be employed during the insertion effect. (Actually, we do not necessarily know that in a passive effect, either... but that is a separate matter.)

This fixes the bug when that occurs when `useInsertionEffectsForAnimations` is enabled, by moving the listening logic of `onUserDrivenAnimationEnded` into a passive effect. This is the same way that it is implemented when `useInsertionEffectsForAnimations` is disabled.

Changelog:
[Internal]

Reviewed By: javache, sammy-SC

Differential Revision: D68171721

fbshipit-source-id: 50b23348fd4641580581cacebc920959651f96a7
2025-01-20 15:07:52 +00:00
Samuel SuslaandFabrizio Cucci 36e2ae6a9c fix Pressable when transform style is animated (#48672)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48672

## Changelog:

[General] [Fixed] - Buttons becoming unresponsive when transform is animated

# The problem

D67872307 changes when `ensureUpdateSubscriptionExists`  is called to in `__attach`. This breaks the functionality because `__attach` is called before flag `__isNative` is set and subscriptions are never setup.

# Fix

The diff sets up subscriptions in `__makeNative` method.

Reviewed By: yungsters

Differential Revision: D68154908

fbshipit-source-id: e2ac108b064a66dda08902653d6bd20286f92458
2025-01-20 15:07:31 +00:00
Riccardo CipolleschiandFabrizio Cucci 5bff540ad3 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-01-20 15:07:09 +00:00
Riccardo CipolleschiandFabrizio Cucci ddfa8a780b Use RNTester App downloaded from CI instead of building (#48637)
Summary:
This change improves the E2E testing by downloading the iOS RNTesterApp that is built in CI instead of building it locally. This should let us save 10 to 20 minutes when we test a new release.

## Changelog:
[Internal] - Use the RNTester app built in CI for release testing on iOS

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

Test Plan:
- build the app in ci
- run `yarn test-e2e-local -c <my-token>` and `yarn test-e2e-local -h false -c <my-token>` and verify that the iOS app is not built, but run in the simulator

Reviewed By: cortinico

Differential Revision: D68161477

Pulled By: cipolleschi

fbshipit-source-id: 577d110f9ff0197a2d3348a08a60e60a4d0a752b
2025-01-20 15:06:52 +00:00
Riccardo CipolleschiandFabrizio Cucci 5c7823f32c Properly test JSC for template_app e2e tests (#48656)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48656

While working on 0.78, I realize we were not testing the template app with JSC.

This change should fix this.

## Changelog:
[Internal] - Disable Hermes for the JSC E2E tests with Maestro

Reviewed By: cortinico, fabriziocucci

Differential Revision: D68147849

fbshipit-source-id: 4fbe005b5d04d6163a37041d1bd57fd48a9dfda8
2025-01-20 15:06:37 +00:00
React Native Bot b713f273b6 Release 0.78.0-rc.0
#publish-packages-to-npm&next
2025-01-15 08:40:26 +00:00
Nicola Corti 94b7082a46 Revert "Do not reset rn-artifacts-version on release branch (#48572)"
This reverts commit a28d3961bd.
2025-01-13 19:19:03 +00:00
Riccardo Cipolleschi 0c8e15e8bb [LOCAL] Bump Hermes 2025-01-13 15:57:54 +01:00
126 changed files with 1683 additions and 951 deletions
-2
View File
@@ -20,8 +20,6 @@ runs:
- name: Set React Native Version
shell: bash
run: node ./scripts/releases/set-rn-artifacts-version.js --build-type ${{ inputs.release-type }}
# We don't want to re-set the artifacts version if we're on the release branch.
if: ${{ !contains(github.ref, '-stable') }}
- name: Setup gradle
uses: ./.github/actions/setup-gradle
with:
@@ -14,8 +14,12 @@ runs:
shell: bash
run: |
sudo apt update
sudo apt install -y git openssh-client cmake build-essential \
sudo apt install -y git openssh-client build-essential \
libreadline-dev libicu-dev jq zip python3
# Install cmake 3.28.3-1build7
sudo apt-get install cmake=3.28.3-1build7
sudo ln -sf /usr/bin/cmake /usr/local/bin/cmake
- name: Restore Hermes workspace
uses: ./.github/actions/restore-hermes-workspace
- name: Linux cache
@@ -14,23 +14,23 @@ runs:
uses: actions/download-artifact@v4
with:
name: hermes-workspace
path: 'D:\tmp\hermes'
path: 'C:\tmp\hermes'
- name: Set up workspace
shell: powershell
run: |
mkdir -p D:\tmp\hermes\osx-bin
mkdir -p C:\tmp\hermes\osx-bin
mkdir -p .\packages\react-native\sdks\hermes
cp -r -Force D:\tmp\hermes\hermes\* .\packages\react-native\sdks\hermes\.
cp -r -Force C:\tmp\hermes\hermes\* .\packages\react-native\sdks\hermes\.
cp -r -Force .\packages\react-native\sdks\hermes-engine\utils\* .\packages\react-native\sdks\hermes\.
- name: Windows cache
uses: actions/cache@v4
with:
key: v2-hermes-${{ github.job }}-windows-${{ inputs.hermes-version }}-${{ inputs.react-native-version }}
key: v3-hermes-${{ github.job }}-windows-${{ inputs.hermes-version }}-${{ inputs.react-native-version }}
path: |
D:\tmp\hermes\win64-bin\
D:\tmp\hermes\hermes\icu\
D:\tmp\hermes\hermes\deps\
D:\tmp\hermes\hermes\build_release\
C:\tmp\hermes\win64-bin\
C:\tmp\hermes\hermes\icu\
C:\tmp\hermes\hermes\deps\
C:\tmp\hermes\hermes\build_release\
- name: setup-msbuild
uses: microsoft/setup-msbuild@v1.3.2
- name: Set up workspace
@@ -39,6 +39,9 @@ runs:
New-Item -ItemType Directory -ErrorAction SilentlyContinue $Env:HERMES_WS_DIR\icu
New-Item -ItemType Directory -ErrorAction SilentlyContinue $Env:HERMES_WS_DIR\deps
New-Item -ItemType Directory -ErrorAction SilentlyContinue $Env:HERMES_WS_DIR\win64-bin
- name: Downgrade CMake
shell: powershell
run: choco install cmake --version 3.31.6 --force
- name: Build HermesC for Windows
shell: powershell
run: |
@@ -60,7 +63,7 @@ runs:
$Env:PATH += ";$Env:CMAKE_DIR;$Env:MSBUILD_DIR"
$Env:ICU_ROOT = "$Env:HERMES_WS_DIR\icu"
cmake -S hermes -B build_release -G 'Visual Studio 16 2019' -Ax64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=True -DHERMES_ENABLE_WIN10_ICU_FALLBACK=OFF
cmake -S hermes -B build_release -G 'Visual Studio 17 2022' -Ax64 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=True -DHERMES_ENABLE_WIN10_ICU_FALLBACK=OFF
if (-not $?) { throw "Failed to configure Hermes" }
echo "Running windows build..."
cd build_release
@@ -80,4 +83,4 @@ runs:
uses: actions/upload-artifact@v4.3.4
with:
name: hermes-win64-bin
path: D:\tmp\hermes\win64-bin\
path: C:\tmp\hermes\win64-bin\
+5 -1
View File
@@ -68,6 +68,10 @@ module.exports.verifyPublishedTemplate = async (
latest = false,
retries = MAX_RETRIES,
) => {
if (version.startsWith('v')) {
version = version.slice(1);
}
log(`🔍 Is ${TEMPLATE_NPM_PKG}@${version} on npm?`);
let count = retries;
@@ -86,7 +90,7 @@ module.exports.verifyPublishedTemplate = async (
return;
}
log(
`🐌 ${TEMPLATE_NPM_PKG}@latest → ${pkg.version} on npm and not ${version} as expected, retrying...`,
`🐌 ${TEMPLATE_NPM_PKG}@latest → ${json.version} on npm and not ${version} as expected, retrying...`,
);
} catch (e) {
log(`Nope, fetch failed: ${e.message}`);
+4 -4
View File
@@ -116,12 +116,12 @@ jobs:
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
build_hermesc_windows:
runs-on: windows-2019
runs-on: windows-2025
needs: prepare_hermes_workspace
env:
HERMES_WS_DIR: 'D:\tmp\hermes'
HERMES_TARBALL_ARTIFACTS_DIR: 'D:\tmp\hermes\hermes-runtime-darwin'
HERMES_OSXBIN_ARTIFACTS_DIR: 'D:\tmp\hermes\osx-bin'
HERMES_WS_DIR: 'C:\tmp\hermes'
HERMES_TARBALL_ARTIFACTS_DIR: 'C:\tmp\hermes\hermes-runtime-darwin'
HERMES_OSXBIN_ARTIFACTS_DIR: 'C:\tmp\hermes\osx-bin'
ICU_URL: "https://github.com/unicode-org/icu/releases/download/release-64-2/icu4c-64_2-Win64-MSVC2017.zip"
MSBUILD_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin'
CMAKE_DIR: 'C:\Program Files\CMake\bin'
+4 -25
View File
@@ -113,12 +113,12 @@ jobs:
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
build_hermesc_windows:
runs-on: windows-2019
runs-on: windows-2025
needs: prepare_hermes_workspace
env:
HERMES_WS_DIR: 'D:\tmp\hermes'
HERMES_TARBALL_ARTIFACTS_DIR: 'D:\tmp\hermes\hermes-runtime-darwin'
HERMES_OSXBIN_ARTIFACTS_DIR: 'D:\tmp\hermes\osx-bin'
HERMES_WS_DIR: 'C:\tmp\hermes'
HERMES_TARBALL_ARTIFACTS_DIR: 'C:\tmp\hermes\hermes-runtime-darwin'
HERMES_OSXBIN_ARTIFACTS_DIR: 'C:\tmp\hermes\osx-bin'
ICU_URL: "https://github.com/unicode-org/icu/releases/download/release-64-2/icu4c-64_2-Win64-MSVC2017.zip"
MSBUILD_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin'
CMAKE_DIR: 'C:\Program Files\CMake\bin'
@@ -131,26 +131,6 @@ jobs:
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
build_android:
runs-on: 8-core-ubuntu
needs: [set_release_type]
container:
image: reactnativecommunity/react-native-android:latest
env:
TERM: "dumb"
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }}
ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }}
ORG_GRADLE_PROJECT_SONATYPE_USERNAME: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_USERNAME }}
ORG_GRADLE_PROJECT_SONATYPE_PASSWORD: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_PASSWORD }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build Android
uses: ./.github/actions/build-android
with:
release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }}
build_npm_package:
runs-on: 8-core-ubuntu
needs:
@@ -160,7 +140,6 @@ jobs:
build_hermes_macos,
build_hermesc_linux,
build_hermesc_windows,
build_android,
]
container:
image: reactnativecommunity/react-native-android:latest
+10 -5
View File
@@ -157,7 +157,7 @@ jobs:
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
test_ios_rntester:
runs-on: macos-13
runs-on: macos-13-large
needs:
[build_apple_slices_hermes, prepare_hermes_workspace, build_hermes_macos]
env:
@@ -369,6 +369,11 @@ jobs:
sed -i 's/newArchEnabled=true/newArchEnabled=false/' android/gradle.properties
fi
if [[ ${{matrix.jsengine}} == "JSC" ]]; then
echo "Using JSC instead of Hermes"
sed -i 's/hermesEnabled=true/hermesEnabled=false/' android/gradle.properties
fi
# Build
cd android
CAPITALIZED_FLAVOR=$(echo "${{ matrix.flavor }}" | awk '{print toupper(substr($0, 1, 1)) substr($0, 2)}')
@@ -403,12 +408,12 @@ jobs:
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
build_hermesc_windows:
runs-on: windows-2019
runs-on: windows-2025
needs: prepare_hermes_workspace
env:
HERMES_WS_DIR: 'D:\tmp\hermes'
HERMES_TARBALL_ARTIFACTS_DIR: 'D:\tmp\hermes\hermes-runtime-darwin'
HERMES_OSXBIN_ARTIFACTS_DIR: 'D:\tmp\hermes\osx-bin'
HERMES_WS_DIR: 'C:\tmp\hermes'
HERMES_TARBALL_ARTIFACTS_DIR: 'C:\tmp\hermes\hermes-runtime-darwin'
HERMES_OSXBIN_ARTIFACTS_DIR: 'C:\tmp\hermes\osx-bin'
ICU_URL: "https://github.com/unicode-org/icu/releases/download/release-64-2/icu4c-64_2-Win64-MSVC2017.zip"
MSBUILD_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin'
CMAKE_DIR: 'C:\Program Files\CMake\bin'
+1
View File
@@ -6,3 +6,4 @@ 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'
+2
View File
@@ -54,6 +54,8 @@ nexusPublishing {
sonatype {
username.set(sonatypeUsername)
password.set(sonatypePassword)
nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/"))
snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/"))
}
}
}
+5 -5
View File
@@ -49,8 +49,8 @@
"@babel/preset-flow": "^7.24.7",
"@definitelytyped/dtslint": "^0.0.127",
"@jest/create-cache-key-function": "^29.6.3",
"@react-native/metro-babel-transformer": "0.77.0-main",
"@react-native/metro-config": "0.77.0-main",
"@react-native/metro-babel-transformer": "0.78.3",
"@react-native/metro-config": "0.78.3",
"@tsconfig/node18": "1.0.1",
"@types/react": "^19.0.0",
"@typescript-eslint/parser": "^7.1.1",
@@ -87,9 +87,9 @@
"jest-junit": "^10.0.0",
"jest-snapshot": "^29.7.0",
"jscodeshift": "^0.14.0",
"metro-babel-register": "^0.81.0",
"metro-memory-fs": "^0.81.0",
"metro-transform-plugins": "^0.81.0",
"metro-babel-register": "^0.81.3",
"metro-memory-fs": "^0.81.3",
"metro-transform-plugins": "^0.81.3",
"micromatch": "^4.0.4",
"node-fetch": "^2.2.0",
"nullthrows": "^1.1.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/assets-registry",
"version": "0.77.0-main",
"version": "0.78.3",
"description": "Asset support code for React Native.",
"license": "MIT",
"repository": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/babel-plugin-codegen",
"version": "0.77.0-main",
"version": "0.78.3",
"description": "Babel plugin to generate native module and view manager code for React Native.",
"license": "MIT",
"repository": {
@@ -26,7 +26,7 @@
],
"dependencies": {
"@babel/traverse": "^7.25.3",
"@react-native/codegen": "0.77.0-main"
"@react-native/codegen": "0.78.3"
},
"devDependencies": {
"@babel/core": "^7.25.2"
+3 -2
View File
@@ -15,7 +15,7 @@ Start the React Native development server.
#### Usage
```sh
npx react-native start [options]
npx @react-native-community/cli start [options]
```
#### Options
@@ -37,6 +37,7 @@ npx react-native start [options]
| `--cert <path>` | Specify path to a custom SSL cert. |
| `--config <string>` | Path to the CLI configuration file. |
| `--no-interactive` | Disable interactive mode. |
| `--client-logs` | **[Deprecated]** Enable plain text JavaScript log streaming for all connected apps. |
### `bundle`
@@ -45,7 +46,7 @@ Build the bundle for the provided JavaScript entry file.
#### Usage
```sh
npx react-native bundle --entry-file <path> [options]
npx @react-native-community/cli bundle --entry-file <path> [options]
```
#### Options
+9 -9
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/community-cli-plugin",
"version": "0.77.0-main",
"version": "0.78.3",
"description": "Core CLI commands for React Native",
"keywords": [
"react-native",
@@ -22,25 +22,25 @@
"dist"
],
"dependencies": {
"@react-native/dev-middleware": "0.77.0-main",
"@react-native/metro-babel-transformer": "0.77.0-main",
"@react-native/dev-middleware": "0.78.3",
"@react-native/metro-babel-transformer": "0.78.3",
"chalk": "^4.0.0",
"debug": "^2.2.0",
"invariant": "^2.2.4",
"metro": "^0.81.0",
"metro-config": "^0.81.0",
"metro-core": "^0.81.0",
"metro": "^0.81.3",
"metro-config": "^0.81.3",
"metro-core": "^0.81.3",
"readline": "^1.3.0",
"semver": "^7.1.3"
},
"devDependencies": {
"metro-resolver": "^0.81.0"
"metro-resolver": "^0.81.3"
},
"peerDependencies": {
"@react-native-community/cli-server-api": "*"
"@react-native-community/cli": "*"
},
"peerDependenciesMeta": {
"@react-native-community/cli-server-api": {
"@react-native-community/cli": {
"optional": true
}
},
@@ -95,6 +95,14 @@ const startCommand: Command = {
name: '--no-interactive',
description: 'Disables interactive mode',
},
{
name: '--client-logs',
description:
'[Deprecated] Enable plain text JavaScript log streaming for all ' +
'connected apps. This feature is deprecated and will be removed in ' +
'future.',
default: false,
},
],
};
@@ -9,7 +9,8 @@
* @oncall react_native
*/
import type {NextHandleFunction, Server} from 'connect';
import typeof * as CLIServerAPI from '@react-native-community/cli-server-api';
import type {Server} from 'connect';
import type {TerminalReportableEvent} from 'metro/src/lib/TerminalReporter';
const debug = require('debug')('ReactNative:CommunityCliPlugin');
@@ -30,10 +31,6 @@ type MiddlewareReturn = {
...
};
const noopNextHandle: NextHandleFunction = (req, res, next) => {
next();
};
// $FlowFixMe
const unusedStubWSServer: ws$WebSocketServer = {};
// $FlowFixMe
@@ -45,6 +42,12 @@ const communityMiddlewareFallback = {
port: number,
watchFolders: $ReadOnlyArray<string>,
}): MiddlewareReturn => ({
// FIXME: Several features will break without community middleware and
// should be migrated into core.
// e.g. used by Libraries/Core/Devtools:
// - /open-stack-frame
// - /open-url
// - /symbolicate
middleware: unusedMiddlewareStub,
websocketEndpoints: {},
messageSocketEndpoint: {
@@ -59,17 +62,30 @@ const communityMiddlewareFallback = {
reportEvent: (event: TerminalReportableEvent) => {},
},
}),
indexPageMiddleware: noopNextHandle,
};
// 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,
);
// $FlowIgnore[unsupported-syntax] dynamic import
communityMiddlewareFallback.createDevServerMiddleware =
community.createDevServerMiddleware;
communityCliServerApi.createDevServerMiddleware;
} catch {
debug(`⚠️ Unable to find @react-native-community/cli-server-api
Starting the server without the community middleware.`);
@@ -77,5 +93,3 @@ Starting the server without the community middleware.`);
export const createDevServerMiddleware =
communityMiddlewareFallback.createDevServerMiddleware;
export const indexPageMiddleware =
communityMiddlewareFallback.indexPageMiddleware;
@@ -19,7 +19,7 @@ import isDevServerRunning from '../../utils/isDevServerRunning';
import loadMetroConfig from '../../utils/loadMetroConfig';
import * as version from '../../utils/version';
import attachKeyHandlers from './attachKeyHandlers';
import {createDevServerMiddleware, indexPageMiddleware} from './middleware';
import {createDevServerMiddleware} from './middleware';
import {createDevMiddleware} from '@react-native/dev-middleware';
import chalk from 'chalk';
import Metro from 'metro';
@@ -44,6 +44,7 @@ export type StartCommandArgs = {
config?: string,
projectRoot?: string,
interactive: boolean,
clientLogs: boolean,
};
async function runServer(
@@ -96,6 +97,11 @@ async function runServer(
require.resolve(plugin),
);
}
// TODO(T214991636): Remove legacy Metro log forwarding
if (!args.clientLogs) {
// $FlowIgnore[cannot-write] Assigning to readonly property
metroConfig.server.forwardClientLogs = false;
}
let reportEvent: (event: TerminalReportableEvent) => void;
const terminal = new Terminal(process.stdout);
@@ -146,11 +152,7 @@ async function runServer(
secure: args.https,
secureCert: args.cert,
secureKey: args.key,
unstable_extraMiddleware: [
communityMiddleware,
indexPageMiddleware,
middleware,
],
unstable_extraMiddleware: [communityMiddleware, middleware],
websocketEndpoints: {
...communityWebsocketEndpoints,
...websocketEndpoints,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/core-cli-utils",
"version": "0.77.0-main",
"version": "0.78.3",
"description": "React Native CLI library for Frameworks to build on",
"license": "MIT",
"main": "./src/index.flow.js",
+3 -3
View File
@@ -1,9 +1,9 @@
@generated SignedSource<<890c168c017f387915457465e90400e9>>
Git revision: 7727db85ac767cf41cce3e9ee54d27e97b2637f9
@generated SignedSource<<7379c02d269a700f429c1c1e33cb6419>>
Git revision: 911a4d983fc2609abc59e73230b7a8684e3a7ac9
Built with --nohooks: false
Is local checkout: false
Remote URL: https://github.com/facebookexperimental/rn-chrome-devtools-frontend
Remote branch: main
Remote branch: 0.78-stable
GN build args (overrides only):
is_official_build = true
Git status in checkout:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
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.77.0-main",
"version": "0.78.3",
"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.77.0-main",
"version": "0.78.3",
"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.77.0-main",
"@react-native/debugger-frontend": "0.78.3",
"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",
@@ -150,6 +150,17 @@ function createWrappedEventReporter(
`Profiling build target "${event.appId}" registered for debugging`,
);
break;
case 'fusebox_console_notice':
logger?.info(
'\n' +
'\u001B[7m' +
' \u001B[1m💡 JavaScript logs have moved!\u001B[22m They can now be ' +
'viewed in React Native DevTools. Tip: Type \u001B[1mj\u001B[22m in ' +
'the terminal to open (requires Google Chrome or Microsoft Edge).' +
'\u001B[27m' +
'\n',
);
break;
}
reporter?.logEvent(event);
@@ -41,6 +41,8 @@ const PAGES_POLLING_INTERVAL = 1000;
// more details.
const FILE_PREFIX = 'file://';
let fuseboxConsoleNoticeLogged = false;
type DebuggerConnection = {
// Debugger web socket connection
socket: WS,
@@ -519,6 +521,7 @@ export default class Device {
// created instead of manually checking this on every getPages result.
for (const page of this.#pages.values()) {
if (this.#pageHasCapability(page, 'nativePageReloads')) {
this.#logFuseboxConsoleNotice();
continue;
}
@@ -1073,4 +1076,14 @@ export default class Device {
dangerouslyGetSocket(): WS {
return this.#deviceSocket;
}
// TODO(T214991636): Remove notice
#logFuseboxConsoleNotice() {
if (fuseboxConsoleNoticeLogged) {
return;
}
this.#deviceEventReporter?.logFuseboxConsoleNotice();
fuseboxConsoleNoticeLogged = true;
}
}
@@ -223,6 +223,12 @@ class DeviceEventReporter {
});
}
logFuseboxConsoleNotice(): void {
this.#eventReporter.logEvent({
type: 'fusebox_console_notice',
});
}
#logExpiredCommand(pendingCommand: PendingCommand): void {
this.#eventReporter.logEvent({
type: 'debugger_command',
@@ -39,7 +39,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;
@@ -82,6 +82,9 @@ export type ReportableEvent =
status: 'success',
...DebuggerSessionIDs,
}
| {
type: 'fusebox_console_notice',
}
| {
type: 'proxy_error',
status: 'error',
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-config",
"version": "0.77.0-main",
"version": "0.78.3",
"description": "ESLint config for React Native",
"license": "MIT",
"repository": {
@@ -22,7 +22,7 @@
"dependencies": {
"@babel/core": "^7.25.2",
"@babel/eslint-parser": "^7.25.1",
"@react-native/eslint-plugin": "0.77.0-main",
"@react-native/eslint-plugin": "0.78.3",
"@typescript-eslint/eslint-plugin": "^7.1.1",
"@typescript-eslint/parser": "^7.1.1",
"eslint-config-prettier": "^8.5.0",
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-plugin",
"version": "0.77.0-main",
"version": "0.78.3",
"description": "ESLint rules for @react-native/eslint-config",
"license": "MIT",
"repository": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/eslint-plugin-specs",
"version": "0.77.0-main",
"version": "0.78.3",
"description": "ESLint rules to validate NativeModule and Component Specs",
"license": "MIT",
"repository": {
@@ -26,7 +26,7 @@
"dependencies": {
"@babel/core": "^7.25.2",
"@babel/plugin-transform-flow-strip-types": "^7.25.2",
"@react-native/codegen": "0.77.0-main",
"@react-native/codegen": "0.78.3",
"make-dir": "^2.1.0",
"pirates": "^4.0.1",
"source-map-support": "0.5.0"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/gradle-plugin",
"version": "0.77.0-main",
"version": "0.78.3",
"description": "Gradle Plugin for React Native",
"license": "MIT",
"repository": {
@@ -8,12 +8,14 @@
package com.facebook.react.utils
import com.facebook.react.utils.PropertyUtils.DEFAULT_INTERNAL_PUBLISHING_GROUP
import com.facebook.react.utils.PropertyUtils.EXCLUSIVE_ENTEPRISE_REPOSITORY
import com.facebook.react.utils.PropertyUtils.INCLUDE_JITPACK_REPOSITORY
import com.facebook.react.utils.PropertyUtils.INCLUDE_JITPACK_REPOSITORY_DEFAULT
import com.facebook.react.utils.PropertyUtils.INTERNAL_PUBLISHING_GROUP
import com.facebook.react.utils.PropertyUtils.INTERNAL_REACT_NATIVE_MAVEN_LOCAL_REPO
import com.facebook.react.utils.PropertyUtils.INTERNAL_USE_HERMES_NIGHTLY
import com.facebook.react.utils.PropertyUtils.INTERNAL_VERSION_NAME
import com.facebook.react.utils.PropertyUtils.SCOPED_EXCLUSIVE_ENTEPRISE_REPOSITORY
import com.facebook.react.utils.PropertyUtils.SCOPED_INCLUDE_JITPACK_REPOSITORY
import java.io.File
import java.net.URI
@@ -28,6 +30,12 @@ internal object DependencyUtils {
* party libraries which are auto-linked.
*/
fun configureRepositories(project: Project) {
val exclusiveEnterpriseRepository = project.rootProject.exclusiveEnterpriseRepository()
if (exclusiveEnterpriseRepository != null) {
project.logger.lifecycle(
"Replacing ALL Maven Repositories with: $exclusiveEnterpriseRepository")
}
project.rootProject.allprojects { eachProject ->
with(eachProject) {
if (hasProperty(INTERNAL_REACT_NATIVE_MAVEN_LOCAL_REPO)) {
@@ -36,8 +44,18 @@ internal object DependencyUtils {
repo.content { it.excludeGroup("org.webkit") }
}
}
if (exclusiveEnterpriseRepository != null) {
// We remove all previously set repositories and only configure the proxy provided by the
// user.
rootProject.repositories.clear()
mavenRepoFromUrl(exclusiveEnterpriseRepository)
// We return here as we don't want to configure other repositories as well.
return@allprojects
}
// We add the snapshot for users on nightlies.
mavenRepoFromUrl("https://oss.sonatype.org/content/repositories/snapshots/") { repo ->
mavenRepoFromUrl("https://central.sonatype.com/repository/maven-snapshots/") { repo ->
repo.content { it.excludeGroup("org.webkit") }
}
repositories.mavenCentral { repo ->
@@ -181,4 +199,13 @@ internal object DependencyUtils {
property(INCLUDE_JITPACK_REPOSITORY).toString().toBoolean()
else -> INCLUDE_JITPACK_REPOSITORY_DEFAULT
}
internal fun Project.exclusiveEnterpriseRepository() =
when {
hasProperty(SCOPED_EXCLUSIVE_ENTEPRISE_REPOSITORY) ->
property(SCOPED_EXCLUSIVE_ENTEPRISE_REPOSITORY).toString()
hasProperty(EXCLUSIVE_ENTEPRISE_REPOSITORY) ->
property(EXCLUSIVE_ENTEPRISE_REPOSITORY).toString()
else -> null
}
}
@@ -26,7 +26,13 @@ object PropertyUtils {
const val INCLUDE_JITPACK_REPOSITORY = "includeJitpackRepository"
const val SCOPED_INCLUDE_JITPACK_REPOSITORY = "react.includeJitpackRepository"
/** By default we include JitPack till React Native 0.80 where this is going to become false */
/**
* Public property that allows to configure an enterprise repository proxy as exclusive repository
*/
const val EXCLUSIVE_ENTEPRISE_REPOSITORY = "exclusiveEnterpriseRepository"
const val SCOPED_EXCLUSIVE_ENTEPRISE_REPOSITORY = "react.exclusiveEnterpriseRepository"
/** By default we include JitPack to avoid breaking user builds */
internal const val INCLUDE_JITPACK_REPOSITORY_DEFAULT = true
/**
@@ -10,6 +10,7 @@ package com.facebook.react.utils
import com.facebook.react.tests.createProject
import com.facebook.react.utils.DependencyUtils.configureDependencies
import com.facebook.react.utils.DependencyUtils.configureRepositories
import com.facebook.react.utils.DependencyUtils.exclusiveEnterpriseRepository
import com.facebook.react.utils.DependencyUtils.getDependencySubstitutions
import com.facebook.react.utils.DependencyUtils.mavenRepoFromURI
import com.facebook.react.utils.DependencyUtils.mavenRepoFromUrl
@@ -45,7 +46,7 @@ class DependencyUtilsTest {
@Test
fun configureRepositories_containsSnapshotRepo() {
val repositoryURI = URI.create("https://oss.sonatype.org/content/repositories/snapshots/")
val repositoryURI = URI.create("https://central.sonatype.com/repository/maven-snapshots/")
val project = createProject()
configureRepositories(project)
@@ -99,6 +100,24 @@ class DependencyUtilsTest {
.isNotNull()
}
@Test
fun configureRepositories_withExclusiveEnterpriseRepository_replacesAllRepositories() {
val repositoryURI = URI.create("https://maven.myfabolousorganization.it")
val project = createProject()
project.rootProject.extensions.extraProperties.set(
"exclusiveEnterpriseRepository", repositoryURI.toString())
configureRepositories(project)
assertThat(project.repositories).hasSize(1)
assertThat(
project.repositories.firstOrNull {
it is MavenArtifactRepository && it.url == repositoryURI
})
.isNotNull()
}
@Test
fun configureRepositories_withIncludeJitpackRepositoryFalse_doesNotContainJitPack() {
val repositoryURI = URI.create("https://www.jitpack.io")
@@ -176,7 +195,7 @@ class DependencyUtilsTest {
@Test
fun configureRepositories_snapshotRepoHasHigherPriorityThanMavenCentral() {
val repositoryURI = URI.create("https://oss.sonatype.org/content/repositories/snapshots/")
val repositoryURI = URI.create("https://central.sonatype.com/repository/maven-snapshots/")
val mavenCentralURI = URI.create("https://repo.maven.apache.org/maven2/")
val project = createProject()
@@ -470,7 +489,7 @@ class DependencyUtilsTest {
@Test
fun shouldAddJitPack_withUnscopedProperty() {
val project = createProject(tempFolder.root)
project.extensions.extraProperties.set("react.includeJitpackRepository", "false")
project.extensions.extraProperties.set("includeJitpackRepository", "false")
assertThat(project.shouldAddJitPack()).isFalse()
}
@@ -479,4 +498,28 @@ class DependencyUtilsTest {
val project = createProject(tempFolder.root)
assertThat(project.shouldAddJitPack()).isTrue()
}
@Test
fun exclusiveEnterpriseRepository_withScopedProperty() {
val project = createProject(tempFolder.root)
project.extensions.extraProperties.set(
"react.exclusiveEnterpriseRepository", "https://maven.myfabolousorganization.it")
assertThat(project.exclusiveEnterpriseRepository())
.isEqualTo("https://maven.myfabolousorganization.it")
}
@Test
fun exclusiveEnterpriseRepository_withUnscopedProperty() {
val project = createProject(tempFolder.root)
project.extensions.extraProperties.set(
"exclusiveEnterpriseRepository", "https://maven.myfabolousorganization.it")
assertThat(project.exclusiveEnterpriseRepository())
.isEqualTo("https://maven.myfabolousorganization.it")
}
@Test
fun exclusiveEnterpriseRepository_defaultIsTrue() {
val project = createProject(tempFolder.root)
assertThat(project.exclusiveEnterpriseRepository()).isNull()
}
}
+1
View File
@@ -5,3 +5,4 @@ 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'
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "helloworld",
"version": "0.77.0-main",
"version": "0.78.3",
"private": true,
"scripts": {
"bootstrap": "node ./cli.js bootstrap",
@@ -13,16 +13,16 @@
},
"dependencies": {
"react": "19.0.0",
"react-native": "1000.0.0"
"react-native": "0.78.3"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.3",
"@babel/runtime": "^7.25.0",
"@react-native/babel-preset": "0.77.0-main",
"@react-native/core-cli-utils": "0.77.0-main",
"@react-native/eslint-config": "0.77.0-main",
"@react-native/metro-config": "0.77.0-main",
"@react-native/babel-preset": "0.78.3",
"@react-native/core-cli-utils": "0.78.3",
"@react-native/eslint-config": "0.78.3",
"@react-native/metro-config": "0.78.3",
"chalk": "^4.1.2",
"commander": "^12.0.0",
"eslint": "^8.19.0",
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/metro-config",
"version": "0.77.0-main",
"version": "0.78.3",
"description": "Metro configuration for React Native.",
"license": "MIT",
"repository": {
@@ -26,9 +26,9 @@
"dist"
],
"dependencies": {
"@react-native/js-polyfills": "0.77.0-main",
"@react-native/metro-babel-transformer": "0.77.0-main",
"metro-config": "^0.81.0",
"metro-runtime": "^0.81.0"
"@react-native/js-polyfills": "0.78.3",
"@react-native/metro-babel-transformer": "0.78.3",
"metro-config": "^0.81.3",
"metro-runtime": "^0.81.3"
}
}
+1 -1
View File
@@ -54,7 +54,7 @@ export function getDefaultConfig(projectRoot: string): ConfigT {
resolver: {
resolverMainFields: ['react-native', 'browser', 'main'],
platforms: ['android', 'ios'],
unstable_conditionNames: ['require', 'import', 'react-native'],
unstable_conditionNames: ['react-native'],
},
serializer: {
// Note: This option is overridden in cli-plugin-metro (getOverrideConfig)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/normalize-colors",
"version": "0.77.0-main",
"version": "0.78.3",
"description": "Color normalization for React Native.",
"license": "MIT",
"repository": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/js-polyfills",
"version": "0.77.0-main",
"version": "0.78.3",
"description": "Polyfills for React Native.",
"license": "MIT",
"repository": {
@@ -1,6 +1,6 @@
{
"name": "@react-native/babel-preset",
"version": "0.77.0-main",
"version": "0.78.3",
"description": "Babel preset for React Native applications",
"main": "src/index.js",
"repository": {
@@ -55,7 +55,7 @@
"@babel/plugin-transform-typescript": "^7.25.2",
"@babel/plugin-transform-unicode-regex": "^7.24.7",
"@babel/template": "^7.25.0",
"@react-native/babel-plugin-codegen": "0.77.0-main",
"@react-native/babel-plugin-codegen": "0.78.3",
"babel-plugin-syntax-hermes-parser": "0.25.1",
"babel-plugin-transform-flow-enums": "^0.0.2",
"react-refresh": "^0.14.0"
@@ -1,6 +1,6 @@
{
"name": "@react-native/metro-babel-transformer",
"version": "0.77.0-main",
"version": "0.78.3",
"description": "Babel transformer for React Native applications.",
"main": "src/index.js",
"repository": {
@@ -16,7 +16,7 @@
"license": "MIT",
"dependencies": {
"@babel/core": "^7.25.2",
"@react-native/babel-preset": "0.77.0-main",
"@react-native/babel-preset": "0.78.3",
"hermes-parser": "0.25.1",
"nullthrows": "^1.1.1"
},
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@react-native/bots",
"description": "React Native Bots",
"version": "0.77.0-main",
"version": "0.78.3",
"private": true,
"license": "MIT",
"repository": {
@@ -1,6 +1,6 @@
{
"name": "@react-native/codegen-typescript-test",
"version": "0.77.0-main",
"version": "0.78.3",
"private": true,
"description": "TypeScript related unit test for @react-native/codegen",
"license": "MIT",
@@ -19,7 +19,7 @@
"prepare": "yarn run build"
},
"dependencies": {
"@react-native/codegen": "0.77.0-main"
"@react-native/codegen": "0.78.3"
},
"devDependencies": {
"@babel/core": "^7.25.2",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/codegen",
"version": "0.77.0-main",
"version": "0.78.3",
"description": "Code generation tools for React Native",
"license": "MIT",
"repository": {
+9 -9
View File
@@ -1,11 +1,11 @@
{
"name": "@react-native/fantom",
"private": true,
"version": "0.77.0-main",
"main": "src/index.js",
"description": "Internal integration testing and benchmarking tool for React Native",
"peerDependencies":{
"jest":"^29.7.0",
"jest-snapshot": "^29.7.0"
}
"name": "@react-native/fantom",
"private": true,
"version": "0.78.3",
"main": "src/index.js",
"description": "Internal integration testing and benchmarking tool for React Native",
"peerDependencies": {
"jest": "^29.7.0",
"jest-snapshot": "^29.7.0"
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "react-native-info",
"version": "0.77.0-main",
"version": "0.78.3",
"main": "build/index.js",
"license": "MIT",
"private": true,
@@ -1,6 +1,6 @@
{
"name": "@react-native/popup-menu-android",
"version": "0.77.0-main",
"version": "0.78.3",
"description": "PopupMenu for the Android platform",
"main": "index.js",
"files": [
@@ -17,7 +17,7 @@
],
"license": "MIT",
"devDependencies": {
"@react-native/codegen": "0.77.0-main"
"@react-native/codegen": "0.78.3"
},
"peerDependencies": {
"@types/react": "^19.0.0",
@@ -1,6 +1,6 @@
{
"name": "@react-native/oss-library-example",
"version": "0.77.0-main",
"version": "0.78.3",
"private": true,
"description": "Package that includes native module exapmle, native component example, targets both the old and the new architecture. It should serve as an example of a real-world OSS library.",
"license": "MIT",
@@ -26,8 +26,8 @@
],
"devDependencies": {
"@babel/core": "^7.25.2",
"@react-native/babel-preset": "0.77.0-main",
"react-native": "1000.0.0"
"@react-native/babel-preset": "0.78.3",
"react-native": "0.78.3"
},
"peerDependencies": {
"react": "*",
@@ -9,6 +9,7 @@
*/
import type {EventSubscription} from '../../vendor/emitter/EventEmitter';
import type {PlatformConfig} from '../AnimatedPlatformConfig';
import type Animation, {EndCallback} from '../animations/Animation';
import type {InterpolationConfigType} from './AnimatedInterpolation';
import type AnimatedNode from './AnimatedNode';
@@ -84,6 +85,7 @@ function _executeAsAnimatedBatch(id: string, operation: () => void) {
* See https://reactnative.dev/docs/animatedvalue
*/
export default class AnimatedValue extends AnimatedWithChildren {
#attached: boolean = false;
#updateSubscription: ?EventSubscription = null;
_value: number;
@@ -106,14 +108,8 @@ export default class AnimatedValue extends AnimatedWithChildren {
}
__attach(): void {
if (this.__isNative) {
// NOTE: In theory, we should only need to call this when any listeners
// are added. However, there is a global `onUserDrivenAnimationEnded`
// listener that relies on `onAnimatedValueUpdate` having fired to update
// the values in JavaScript. If that listener is removed, this could be
// re-optimized.
this.#ensureUpdateSubscriptionExists();
}
this.#attached = true;
this.#ensureUpdateSubscriptionExists();
}
__detach(): void {
@@ -125,16 +121,34 @@ export default class AnimatedValue extends AnimatedWithChildren {
}
this.stopAnimation();
super.__detach();
this.#attached = false;
}
__getValue(): number {
return this._value + this._offset;
}
__makeNative(platformConfig: ?PlatformConfig): void {
super.__makeNative(platformConfig);
this.#ensureUpdateSubscriptionExists();
}
/**
* NOTE: In theory, we should only need to call this when any listeners
* are added. However, there is a global `onUserDrivenAnimationEnded`
* listener that relies on `onAnimatedValueUpdate` having fired to update
* the values in JavaScript. If that listener is removed, this could be
* re-optimized.
*/
#ensureUpdateSubscriptionExists(): void {
if (this.#updateSubscription != null) {
return;
}
// The order in which `__attach` and `__makeNative` are called is not
// deterministic, and we only want to do this when both have occurred.
if (!this.#attached || !this.__isNative) {
return;
}
const nativeTag = this.__getNativeTag();
NativeAnimatedAPI.startListeningToAnimatedNodeValue(nativeTag);
const subscription: EventSubscription =
+14 -13
View File
@@ -276,6 +276,20 @@ function useAnimatedPropsLifecycle_insertionEffects(node: AnimatedProps): void {
// if the queue is empty. When multiple animated components are mounted at
// the same time. Only first component flushes the queue and the others will noop.
NativeAnimatedHelper.API.flushQueue();
let drivenAnimationEndedListener: ?EventSubscription = null;
if (node.__isNative) {
drivenAnimationEndedListener =
NativeAnimatedHelper.nativeEventEmitter.addListener(
'onUserDrivenAnimationEnded',
data => {
node.update();
},
);
}
return () => {
drivenAnimationEndedListener?.remove();
};
});
useInsertionEffect(() => {
@@ -287,17 +301,6 @@ function useAnimatedPropsLifecycle_insertionEffects(node: AnimatedProps): void {
useInsertionEffect(() => {
node.__attach();
let drivenAnimationEndedListener: ?EventSubscription = null;
if (node.__isNative) {
drivenAnimationEndedListener =
NativeAnimatedHelper.nativeEventEmitter.addListener(
'onUserDrivenAnimationEnded',
data => {
node.update();
},
);
}
if (prevNodeRef.current != null) {
const prevNode = prevNodeRef.current;
// TODO: Stop restoring default values (unless `reset` is called).
@@ -312,8 +315,6 @@ function useAnimatedPropsLifecycle_insertionEffects(node: AnimatedProps): void {
} else {
prevNodeRef.current = node;
}
drivenAnimationEndedListener?.remove();
};
}, [node]);
}
@@ -118,4 +118,11 @@
return nullptr;
}
- (void)loadSourceForBridge:(RCTBridge *)bridge
onProgress:(RCTSourceLoadProgressBlock)onProgress
onComplete:(RCTSourceLoadBlock)loadCallback
{
[RCTJavaScriptLoader loadBundleAtURL:[self sourceURLForBridge:bridge] onProgress:onProgress onComplete:loadCallback];
}
@end
@@ -156,6 +156,15 @@ using namespace facebook::react;
return RCTAppSetupDefaultModuleFromClass(moduleClass, self.delegate.dependencyProvider);
}
- (NSArray<id<RCTBridgeModule>> *)extraModulesForBridge:(RCTBridge *)bridge
{
if ([_delegate respondsToSelector:@selector(extraModulesForBridge:)]) {
return [_delegate extraModulesForBridge:bridge];
}
return @[];
}
#pragma mark - RCTComponentViewFactoryComponentProvider
- (NSDictionary<NSString *, Class<RCTComponentViewProtocol>> *)thirdPartyFabricComponents
@@ -228,6 +237,21 @@ using namespace facebook::react;
};
}
if ([self.delegate respondsToSelector:@selector(loadSourceForBridge:onProgress:onComplete:)]) {
configuration.loadSourceForBridgeWithProgress =
^(RCTBridge *_Nonnull bridge,
RCTSourceLoadProgressBlock _Nonnull onProgress,
RCTSourceLoadBlock _Nonnull loadCallback) {
[weakSelf.delegate loadSourceForBridge:bridge onProgress:onProgress onComplete:loadCallback];
};
}
if ([self.delegate respondsToSelector:@selector(loadSourceForBridge:withBlock:)]) {
configuration.loadSourceForBridge = ^(RCTBridge *_Nonnull bridge, RCTSourceLoadBlock _Nonnull loadCallback) {
[weakSelf.delegate loadSourceForBridge:bridge withBlock:loadCallback];
};
}
return [[RCTRootViewFactory alloc] initWithTurboModuleDelegate:self hostDelegate:self configuration:configuration];
}
@@ -251,13 +275,20 @@ class RCTAppDelegateBridgelessFeatureFlags : public ReactNativeFeatureFlagsDefau
{
return true;
}
bool enableFixForViewCommandRace() override
{
return true;
}
};
- (void)_setUpFeatureFlags
{
if ([self bridgelessEnabled]) {
ReactNativeFeatureFlags::override(std::make_unique<RCTAppDelegateBridgelessFeatureFlags>());
}
static dispatch_once_t setupFeatureFlagsToken;
dispatch_once(&setupFeatureFlagsToken, ^{
if ([self bridgelessEnabled]) {
ReactNativeFeatureFlags::override(std::make_unique<RCTAppDelegateBridgelessFeatureFlags>());
}
});
}
@end
@@ -31,6 +31,11 @@ typedef NSURL *_Nullable (^RCTBundleURLBlock)(void);
typedef NSArray<id<RCTBridgeModule>> *_Nonnull (^RCTExtraModulesForBridgeBlock)(RCTBridge *bridge);
typedef NSDictionary<NSString *, Class> *_Nonnull (^RCTExtraLazyModuleClassesForBridge)(RCTBridge *bridge);
typedef BOOL (^RCTBridgeDidNotFindModuleBlock)(RCTBridge *bridge, NSString *moduleName);
typedef void (^RCTLoadSourceForBridgeWithProgressBlock)(
RCTBridge *bridge,
RCTSourceLoadProgressBlock onProgress,
RCTSourceLoadBlock loadCallback);
typedef void (^RCTLoadSourceForBridgeBlock)(RCTBridge *bridge, RCTSourceLoadBlock loadCallback);
#pragma mark - RCTRootViewFactory Configuration
@interface RCTRootViewFactoryConfiguration : NSObject
@@ -145,6 +150,19 @@ typedef BOOL (^RCTBridgeDidNotFindModuleBlock)(RCTBridge *bridge, NSString *modu
*/
@property (nonatomic, nullable) RCTBridgeDidNotFindModuleBlock bridgeDidNotFindModule;
/**
* The bridge will automatically attempt to load the JS source code from the
* location specified by the `sourceURLForBridge:` method, however, if you want
* to handle loading the JS yourself, you can do so by setting this property.
*/
@property (nonatomic, nullable) RCTLoadSourceForBridgeWithProgressBlock loadSourceForBridgeWithProgress;
/**
* Similar to loadSourceForBridgeWithProgress but without progress
* reporting.
*/
@property (nonatomic, nullable) RCTLoadSourceForBridgeBlock loadSourceForBridge;
@end
#pragma mark - RCTRootViewFactory
@@ -302,6 +302,22 @@
return NO;
}
- (void)loadSourceForBridge:(RCTBridge *)bridge withBlock:(RCTSourceLoadBlock)loadCallback
{
if (_configuration.loadSourceForBridge != nil) {
_configuration.loadSourceForBridge(bridge, loadCallback);
}
}
- (void)loadSourceForBridge:(RCTBridge *)bridge
onProgress:(RCTSourceLoadProgressBlock)onProgress
onComplete:(RCTSourceLoadBlock)loadCallback
{
if (_configuration.loadSourceForBridgeWithProgress != nil) {
_configuration.loadSourceForBridgeWithProgress(bridge, onProgress, loadCallback);
}
}
- (NSURL *)bundleURL
{
return self->_configuration.bundleURLBlock();
@@ -1019,7 +1019,7 @@ function useTextInputStateSynchronization_STATE({
mostRecentEventCount: number,
selection: ?Selection,
inputRef: React.RefObject<null | HostInstance>,
text: string,
text?: string,
viewCommands: ViewCommands,
}): {
setLastNativeText: string => void,
@@ -1100,7 +1100,7 @@ function useTextInputStateSynchronization_REFS({
mostRecentEventCount: number,
selection: ?Selection,
inputRef: React.RefObject<null | HostInstance>,
text: string,
text?: string,
viewCommands: ViewCommands,
}): {
setLastNativeText: string => void,
@@ -1314,7 +1314,7 @@ function InternalTextInput(props: Props): React.Node {
? props.value
: typeof props.defaultValue === 'string'
? props.defaultValue
: '';
: undefined;
const viewCommands =
AndroidTextInputCommands ||
@@ -7,7 +7,7 @@
* @format
*/
const {create} = require('../../../../jest/renderer');
const {create, update} = require('../../../../jest/renderer');
const ReactNativeFeatureFlags = require('../../../../src/private/featureflags/ReactNativeFeatureFlags');
const ReactNative = require('../../../ReactNative/RendererProxy');
const {
@@ -20,8 +20,14 @@ const ReactTestRenderer = require('react-test-renderer');
jest.unmock('../TextInput');
[true, false].forEach(useRefsForTextInputState => {
describe(`TextInput tests (useRefsForTextInputState = ${useRefsForTextInputState}`, () => {
[
{useRefsForTextInputState: true, useTextChildren: true},
{useRefsForTextInputState: false, useTextChildren: true},
{useRefsForTextInputState: true, useTextChildren: false},
{useRefsForTextInputState: false, useTextChildren: false},
].forEach(testCase => {
const {useRefsForTextInputState, useTextChildren} = testCase;
describe(`TextInput tests (useRefsForTextInputState = ${useRefsForTextInputState}) useTextChildren = ${useTextChildren}`, () => {
let input;
let inputRef;
let onChangeListener;
@@ -43,15 +49,16 @@ jest.unmock('../TextInput');
return (
<TextInput
ref={inputRef}
value={state.text}
value={useTextChildren ? undefined : state.text}
onChangeText={text => {
onChangeTextListener(text);
setState({text});
}}
onChange={event => {
onChangeListener(event);
}}
/>
}}>
{useTextChildren ? state.text : undefined}
</TextInput>
);
}
const renderTree = await create(<TextInputWrapper />);
@@ -75,12 +82,20 @@ jest.unmock('../TextInput');
);
});
it('calls onChange callbacks', () => {
expect(input.props.value).toBe(initialValue);
if (!useTextChildren) {
expect(input.props.value).toBe(initialValue);
} else {
expect(input.props.children).toBe(initialValue);
}
const message = 'This is a test message';
ReactTestRenderer.act(() => {
enter(input, message);
});
expect(input.props.value).toBe(message);
if (!useTextChildren) {
expect(input.props.value).toBe(message);
} else {
expect(input.props.children).toBe(message);
}
expect(onChangeTextListener).toHaveBeenCalledWith(message);
expect(onChangeListener).toHaveBeenCalledWith({
nativeEvent: {text: message},
@@ -90,7 +105,12 @@ jest.unmock('../TextInput');
async function createTextInput(extraProps) {
const textInputRef = React.createRef(null);
await create(
<TextInput ref={textInputRef} value="value1" {...extraProps} />,
<TextInput
ref={textInputRef}
value={useTextChildren ? undefined : 'value1'}
{...extraProps}>
{useTextChildren ? 'value1' : undefined}
</TextInput>,
);
return textInputRef;
}
@@ -134,14 +154,55 @@ jest.unmock('../TextInput');
expect(TextInput.State.currentlyFocusedInput()).toBe(null);
});
it('change selection keeps content', async () => {
const defaultValue = 'value1';
// create content
let renderTree = await create(
<TextInput
value={useTextChildren ? undefined : defaultValue}
position={{start: 1, end: 1}}>
{useTextChildren ? defaultValue : undefined}
</TextInput>,
);
input = renderTree.root.findByType(TextInput);
expect(
useTextChildren ? input.children[0].props.children : input.props.value,
).toBe(defaultValue);
expect(input.props.position.start).toBe(1);
expect(input.props.position.end).toBe(1);
// update position
renderTree = await update(
renderTree,
<TextInput
value={useTextChildren ? undefined : defaultValue}
position={{start: 2, end: 2}}>
{useTextChildren ? defaultValue : undefined}
</TextInput>,
);
expect(
useTextChildren ? input.children[0].props.children : input.props.value,
).toBe(defaultValue);
expect(input.props.position.start).toBe(2);
expect(input.props.position.end).toBe(2);
});
it('should unfocus when other TextInput is focused', async () => {
const textInputRe1 = React.createRef(null);
const textInputRe2 = React.createRef(null);
await create(
<>
<TextInput ref={textInputRe1} value="value1" />
<TextInput ref={textInputRe2} value="value2" />
<TextInput
ref={textInputRe1}
value={useTextChildren ? undefined : 'value1'}>
{useTextChildren ? 'value1' : undefined}
</TextInput>
<TextInput
ref={textInputRe2}
value={useTextChildren ? undefined : 'value2'}>
{useTextChildren ? 'value2' : undefined}
</TextInput>
</>,
);
ReactNative.findNodeHandle = jest.fn().mockImplementation(ref => {
@@ -210,7 +271,6 @@ jest.unmock('../TextInput');
rejectResponderTermination={true}
selection={null}
submitBehavior="blurAndSubmit"
text=""
textContentType="emailAddress"
underlineColorAndroid="transparent"
/>
@@ -255,7 +315,6 @@ jest.unmock('../TextInput');
rejectResponderTermination={true}
selection={null}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
/>
`);
@@ -301,7 +360,6 @@ jest.unmock('../TextInput');
selection={null}
submitBehavior="blurAndSubmit"
testID="testID"
text=""
underlineColorAndroid="transparent"
/>
`);
@@ -432,7 +490,6 @@ jest.unmock('../TextInput');
role="main"
selection={null}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
/>
`);
@@ -489,7 +546,6 @@ jest.unmock('../TextInput');
]
}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
/>
`);
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`TextInput tests (useRefsForTextInputState = false should render as expected: should deep render when mocked (please verify output manually) 1`] = `
exports[`TextInput tests (useRefsForTextInputState = false) useTextChildren = false should render as expected: should deep render when mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
@@ -23,12 +23,11 @@ exports[`TextInput tests (useRefsForTextInputState = false should render as expe
rejectResponderTermination={true}
selection={null}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
/>
`;
exports[`TextInput tests (useRefsForTextInputState = false should render as expected: should deep render when not mocked (please verify output manually) 1`] = `
exports[`TextInput tests (useRefsForTextInputState = false) useTextChildren = false should render as expected: should deep render when not mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
@@ -51,12 +50,11 @@ exports[`TextInput tests (useRefsForTextInputState = false should render as expe
rejectResponderTermination={true}
selection={null}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
/>
`;
exports[`TextInput tests (useRefsForTextInputState = true should render as expected: should deep render when mocked (please verify output manually) 1`] = `
exports[`TextInput tests (useRefsForTextInputState = false) useTextChildren = true should render as expected: should deep render when mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
@@ -79,12 +77,119 @@ exports[`TextInput tests (useRefsForTextInputState = true should render as expec
rejectResponderTermination={true}
selection={null}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
/>
`;
exports[`TextInput tests (useRefsForTextInputState = true should render as expected: should deep render when not mocked (please verify output manually) 1`] = `
exports[`TextInput tests (useRefsForTextInputState = false) useTextChildren = true should render as expected: should deep render when not mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
focusable={true}
forwardedRef={null}
mostRecentEventCount={0}
onBlur={[Function]}
onChange={[Function]}
onClick={[Function]}
onFocus={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onScroll={[Function]}
onSelectionChange={[Function]}
onSelectionChangeShouldSetResponder={[Function]}
onStartShouldSetResponder={[Function]}
rejectResponderTermination={true}
selection={null}
submitBehavior="blurAndSubmit"
underlineColorAndroid="transparent"
/>
`;
exports[`TextInput tests (useRefsForTextInputState = true) useTextChildren = false should render as expected: should deep render when mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
focusable={true}
forwardedRef={null}
mostRecentEventCount={0}
onBlur={[Function]}
onChange={[Function]}
onClick={[Function]}
onFocus={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onScroll={[Function]}
onSelectionChange={[Function]}
onSelectionChangeShouldSetResponder={[Function]}
onStartShouldSetResponder={[Function]}
rejectResponderTermination={true}
selection={null}
submitBehavior="blurAndSubmit"
underlineColorAndroid="transparent"
/>
`;
exports[`TextInput tests (useRefsForTextInputState = true) useTextChildren = false should render as expected: should deep render when not mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
focusable={true}
forwardedRef={null}
mostRecentEventCount={0}
onBlur={[Function]}
onChange={[Function]}
onClick={[Function]}
onFocus={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onScroll={[Function]}
onSelectionChange={[Function]}
onSelectionChangeShouldSetResponder={[Function]}
onStartShouldSetResponder={[Function]}
rejectResponderTermination={true}
selection={null}
submitBehavior="blurAndSubmit"
underlineColorAndroid="transparent"
/>
`;
exports[`TextInput tests (useRefsForTextInputState = true) useTextChildren = true should render as expected: should deep render when mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
focusable={true}
forwardedRef={null}
mostRecentEventCount={0}
onBlur={[Function]}
onChange={[Function]}
onClick={[Function]}
onFocus={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onScroll={[Function]}
onSelectionChange={[Function]}
onSelectionChangeShouldSetResponder={[Function]}
onStartShouldSetResponder={[Function]}
rejectResponderTermination={true}
selection={null}
submitBehavior="blurAndSubmit"
underlineColorAndroid="transparent"
/>
`;
exports[`TextInput tests (useRefsForTextInputState = true) useTextChildren = true should render as expected: should deep render when not mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
@@ -107,7 +212,6 @@ exports[`TextInput tests (useRefsForTextInputState = true should render as expec
rejectResponderTermination={true}
selection={null}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
/>
`;
+3 -3
View File
@@ -14,9 +14,9 @@ const version: $ReadOnly<{
patch: number,
prerelease: string | null,
}> = {
major: 1000,
minor: 0,
patch: 0,
major: 0,
minor: 78,
patch: 3,
prerelease: null,
};
@@ -42,9 +42,8 @@ if (__DEV__) {
if (!Platform.isTesting) {
const HMRClient = require('../Utilities/HMRClient');
if (global.__FUSEBOX_HAS_FULL_CONSOLE_SUPPORT__) {
HMRClient.unstable_notifyFuseboxConsoleEnabled();
} else if (console._isPolyfilled) {
// TODO(T214991636): Remove legacy Metro log forwarding
if (console._isPolyfilled) {
// We assume full control over the console and send JavaScript logs to Metro.
[
'trace',
@@ -133,6 +133,7 @@ let BaseImage: AbstractImageAndroid = React.forwardRef(
width: undefined,
height: undefined,
};
const defaultSource = resolveAssetSource(props.defaultSource);
const loadingIndicatorSource = resolveAssetSource(
props.loadingIndicatorSource,
);
@@ -178,6 +179,7 @@ let BaseImage: AbstractImageAndroid = React.forwardRef(
/* $FlowFixMe(>=0.78.0 site=react_native_android_fb) This issue was found
* when making Flow check .android.js files. */
headers: (source?.[0]?.headers || source?.headers: ?{[string]: string}),
defaultSource: defaultSource ? defaultSource.uri : null,
loadingIndicatorSrc: loadingIndicatorSource
? loadingIndicatorSource.uri
: null,
@@ -21,6 +21,7 @@ import type {
} from '../StyleSheet/StyleSheet';
import type {ResolvedAssetSource} from './AssetSourceResolver';
import type {ImageProps} from './ImageProps';
import type {ImageSource} from './ImageSource';
import * as NativeComponentRegistry from '../NativeComponent/NativeComponentRegistry';
import {ConditionallyIgnoredEventHandlers} from '../NativeComponent/ViewConfigIgnore';
@@ -42,7 +43,7 @@ type Props = $ReadOnly<{
| ?ResolvedAssetSource
| ?$ReadOnlyArray<?$ReadOnly<{uri?: ?string, ...}>>,
headers?: ?{[string]: string},
defaultSrc?: ?string,
defaultSource?: ?ImageSource | ?string,
loadingIndicatorSrc?: ?string,
}>;
@@ -82,9 +83,7 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig =
},
validAttributes: {
blurRadius: true,
defaultSource: {
process: require('./resolveAssetSource'),
},
defaultSource: true,
internal_analyticTag: true,
resizeMethod: true,
resizeMode: true,
@@ -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];
@@ -8,6 +8,8 @@
#import <React/RCTDataRequestHandler.h>
#import <ReactCommon/RCTTurboModule.h>
#import <mutex>
#import "RCTNetworkPlugins.h"
@interface RCTDataRequestHandler () <RCTTurboModule>
@@ -15,14 +17,22 @@
@implementation RCTDataRequestHandler {
NSOperationQueue *_queue;
std::mutex _operationHandlerMutexLock;
}
RCT_EXPORT_MODULE()
- (void)invalidate
{
[_queue cancelAllOperations];
_queue = nil;
std::lock_guard<std::mutex> lock(_operationHandlerMutexLock);
if (_queue) {
for (NSOperation *operation in _queue.operations) {
if (!operation.isCancelled && !operation.isFinished) {
[operation cancel];
}
}
_queue = nil;
}
}
- (BOOL)canHandleRequest:(NSURLRequest *)request
@@ -32,6 +42,7 @@ RCT_EXPORT_MODULE()
- (NSOperation *)sendRequest:(NSURLRequest *)request withDelegate:(id<RCTURLRequestDelegate>)delegate
{
std::lock_guard<std::mutex> lock(_operationHandlerMutexLock);
// Lazy setup
if (!_queue) {
_queue = [NSOperationQueue new];
@@ -69,7 +80,10 @@ RCT_EXPORT_MODULE()
- (void)cancelRequest:(NSOperation *)op
{
[op cancel];
std::lock_guard<std::mutex> lock(_operationHandlerMutexLock);
if (!op.isCancelled && !op.isFinished) {
[op cancel];
}
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
@@ -7,6 +7,8 @@
#import <React/RCTFileRequestHandler.h>
#import <mutex>
#import <MobileCoreServices/MobileCoreServices.h>
#import <React/RCTUtils.h>
@@ -19,14 +21,22 @@
@implementation RCTFileRequestHandler {
NSOperationQueue *_fileQueue;
std::mutex _operationHandlerMutexLock;
}
RCT_EXPORT_MODULE()
- (void)invalidate
{
[_fileQueue cancelAllOperations];
_fileQueue = nil;
std::lock_guard<std::mutex> lock(_operationHandlerMutexLock);
if (_fileQueue) {
for (NSOperation *operation in _fileQueue.operations) {
if (!operation.isCancelled && !operation.isFinished) {
[operation cancel];
}
}
_fileQueue = nil;
}
}
- (BOOL)canHandleRequest:(NSURLRequest *)request
@@ -36,6 +46,7 @@ RCT_EXPORT_MODULE()
- (NSOperation *)sendRequest:(NSURLRequest *)request withDelegate:(id<RCTURLRequestDelegate>)delegate
{
std::lock_guard<std::mutex> lock(_operationHandlerMutexLock);
// Lazy setup
if (!_fileQueue) {
_fileQueue = [NSOperationQueue new];
@@ -83,7 +94,10 @@ RCT_EXPORT_MODULE()
- (void)cancelRequest:(NSOperation *)op
{
[op cancel];
std::lock_guard<std::mutex> lock(_operationHandlerMutexLock);
if (!op.isCancelled && !op.isFinished) {
[op cancel];
}
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
-28
View File
@@ -26,7 +26,6 @@ let hmrUnavailableReason: string | null = null;
let currentCompileErrorMessage: string | null = null;
let didConnect: boolean = false;
let pendingLogs: Array<[LogLevel, $ReadOnlyArray<mixed>]> = [];
let pendingFuseboxConsoleNotification = false;
type LogLevel =
| 'trace'
@@ -52,7 +51,6 @@ export type HMRClientNativeInterface = {|
isEnabled: boolean,
scheme?: string,
): void,
unstable_notifyFuseboxConsoleEnabled(): void,
|};
/**
@@ -142,29 +140,6 @@ const HMRClient: HMRClientNativeInterface = {
}
},
unstable_notifyFuseboxConsoleEnabled() {
if (!hmrClient) {
pendingFuseboxConsoleNotification = true;
return;
}
hmrClient.send(
JSON.stringify({
type: 'log',
level: 'info',
data: [
'\n' +
'\u001B[7m' +
' \u001B[1m💡 JavaScript logs have moved!\u001B[22m They can now be ' +
'viewed in React Native DevTools. Tip: Type \u001B[1mj\u001B[22m in ' +
'the terminal to open (requires Google Chrome or Microsoft Edge).' +
'\u001B[27m' +
'\n',
],
}),
);
pendingFuseboxConsoleNotification = false;
},
// Called once by the bridge on startup, even if Fast Refresh is off.
// It creates the HMR client but doesn't actually set up the socket yet.
setup(
@@ -341,9 +316,6 @@ function flushEarlyLogs(client: MetroHMRClient) {
pendingLogs.forEach(([level, data]) => {
HMRClient.log(level, data);
});
if (pendingFuseboxConsoleNotification) {
HMRClient.unstable_notifyFuseboxConsoleEnabled();
}
} finally {
pendingLogs.length = 0;
}
@@ -25,7 +25,6 @@ const HMRClientProdShim: HMRClientNativeInterface = {
disable() {},
registerBundle() {},
log() {},
unstable_notifyFuseboxConsoleEnabled() {},
};
module.exports = HMRClientProdShim;
@@ -1169,6 +1169,7 @@ declare export default class AnimatedValue extends AnimatedWithChildren {
__attach(): void;
__detach(): void;
__getValue(): number;
__makeNative(platformConfig: ?PlatformConfig): void;
setValue(value: number): void;
setOffset(offset: number): void;
flattenOffset(): void;
@@ -5064,7 +5065,7 @@ exports[`public API should not change unintentionally Libraries/Image/ImageViewN
| ?ResolvedAssetSource
| ?$ReadOnlyArray<?$ReadOnly<{ uri?: ?string, ... }>>,
headers?: ?{ [string]: string },
defaultSrc?: ?string,
defaultSource?: ?ImageSource | ?string,
loadingIndicatorSrc?: ?string,
}>;
interface NativeCommands {
@@ -9000,7 +9001,6 @@ export type HMRClientNativeInterface = {|
isEnabled: boolean,
scheme?: string
): void,
unstable_notifyFuseboxConsoleEnabled(): void,
|};
declare const HMRClient: HMRClientNativeInterface;
declare module.exports: HMRClient;
@@ -21,9 +21,9 @@ NSDictionary* RCTGetReactNativeVersion(void)
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^(void){
__rnVersion = @{
RCTVersionMajor: @(1000),
RCTVersionMinor: @(0),
RCTVersionPatch: @(0),
RCTVersionMajor: @(0),
RCTVersionMinor: @(78),
RCTVersionPatch: @(3),
RCTVersionPrerelease: [NSNull null],
};
});
@@ -54,13 +54,11 @@
]];
UIButton *resumeButton = [UIButton buttonWithType:UIButtonTypeCustom];
[resumeButton setImage:[UIImage systemImageNamed:@"forward.frame.fill"] forState:UIControlStateNormal];
UIImage *image = [UIImage systemImageNamed:@"forward.frame.fill"];
[resumeButton setImage:image forState:UIControlStateNormal];
[resumeButton setImage:image forState:UIControlStateDisabled];
resumeButton.tintColor = [UIColor colorWithRed:0.37 green:0.37 blue:0.37 alpha:1];
resumeButton.configurationUpdateHandler = ^(UIButton *button) {
button.imageView.tintAdjustmentMode = UIViewTintAdjustmentModeNormal;
};
resumeButton.enabled = NO;
[NSLayoutConstraint activateConstraints:@[
[resumeButton.widthAnchor constraintEqualToConstant:48],
@@ -170,10 +170,24 @@ static NSString *const kRCTLegacyInteropChildIndexKey = @"index";
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
{
[_viewsToBeMounted addObject:@{
kRCTLegacyInteropChildIndexKey : [NSNumber numberWithInteger:index],
kRCTLegacyInteropChildComponentKey : childComponentView
}];
if (_adapter && index == _adapter.paperView.reactSubviews.count) {
// This is a new child view that is being added to the end of the children array.
// After the children is added, we need to call didUpdateReactSubviews to make sure that it is rendered.
// Without this change, the new child will not be rendered right away because the didUpdateReactSubviews is not
// called and the `finalizeUpdate` is not invoked.
if ([childComponentView isKindOfClass:[RCTLegacyViewManagerInteropComponentView class]]) {
UIView *target = ((RCTLegacyViewManagerInteropComponentView *)childComponentView).contentView;
[_adapter.paperView insertReactSubview:target atIndex:index];
} else {
[_adapter.paperView insertReactSubview:childComponentView atIndex:index];
}
[_adapter.paperView didUpdateReactSubviews];
} else {
[_viewsToBeMounted addObject:@{
kRCTLegacyInteropChildIndexKey : [NSNumber numberWithInteger:index],
kRCTLegacyInteropChildComponentKey : childComponentView
}];
}
}
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
@@ -60,7 +60,7 @@ using namespace facebook::react;
const auto &newSwitchProps = static_cast<const SwitchProps &>(*props);
// `value`
if (oldSwitchProps.value != newSwitchProps.value) {
if (!_isInitialValueSet || oldSwitchProps.value != newSwitchProps.value) {
BOOL shouldAnimate = _isInitialValueSet == YES;
[_switchView setOn:newSwitchProps.value animated:shouldAnimate];
}
@@ -101,11 +101,7 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
NSMutableDictionary<NSAttributedStringKey, id> *defaultAttributes =
[_backedTextInputView.defaultTextAttributes mutableCopy];
#if !TARGET_OS_MACCATALYST
RCTWeakEventEmitterWrapper *eventEmitterWrapper = [RCTWeakEventEmitterWrapper new];
eventEmitterWrapper.eventEmitter = _eventEmitter;
defaultAttributes[RCTAttributedStringEventEmitterKey] = eventEmitterWrapper;
#endif
defaultAttributes[RCTAttributedStringEventEmitterKey] = RCTWrapEventEmitter(_eventEmitter);
_backedTextInputView.defaultTextAttributes = defaultAttributes;
}
@@ -266,10 +262,8 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
if (newTextInputProps.textAttributes != oldTextInputProps.textAttributes) {
NSMutableDictionary<NSAttributedStringKey, id> *defaultAttributes =
RCTNSTextAttributesFromTextAttributes(newTextInputProps.getEffectiveTextAttributes(RCTFontSizeMultiplier()));
#if !TARGET_OS_MACCATALYST
defaultAttributes[RCTAttributedStringEventEmitterKey] =
_backedTextInputView.defaultTextAttributes[RCTAttributedStringEventEmitterKey];
#endif
_backedTextInputView.defaultTextAttributes = defaultAttributes;
}
@@ -143,6 +143,7 @@ using namespace facebook::react;
if (!_view) {
_view = [[RCTSurfaceView alloc] initWithSurface:(RCTSurface *)self];
[self _updateLayoutContext];
_touchHandler = [RCTSurfaceTouchHandler new];
[_touchHandler attachToView:_view];
}
@@ -2215,7 +2215,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 {
@@ -3482,6 +3482,14 @@ public class com/facebook/react/modules/network/ResponseUtil {
public static fun onResponseReceived (Lcom/facebook/react/bridge/ReactApplicationContext;IILcom/facebook/react/bridge/WritableMap;Ljava/lang/String;)V
}
public final class com/facebook/react/modules/network/ReactCookieJarContainer : com/facebook/react/modules/network/CookieJarContainer {
public fun <init> ()V
public fun loadForRequest (Lokhttp3/HttpUrl;)Ljava/util/List;
public fun removeCookieJar ()V
public fun saveFromResponse (Lokhttp3/HttpUrl;Ljava/util/List;)V
public fun setCookieJar (Lokhttp3/CookieJar;)V
}
public class com/facebook/react/modules/network/TLSSocketFactory : javax/net/ssl/SSLSocketFactory {
public fun <init> ()V
public fun createSocket (Ljava/lang/String;I)Ljava/net/Socket;
@@ -7342,6 +7350,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
@@ -7374,6 +7383,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;
@@ -7823,4 +7833,3 @@ public final class com/facebook/react/views/view/WindowUtilKt {
public static final fun setStatusBarVisibility (Landroid/view/Window;Z)V
public static final fun setSystemBarsTranslucency (Landroid/view/Window;Z)V
}
@@ -1,4 +1,4 @@
VERSION_NAME=1000.0.0
VERSION_NAME=0.78.3
react.internal.publishingGroup=com.facebook.react
android.useAndroidX=true
@@ -685,10 +685,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();
mCurrentReactContext
.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);
@@ -173,7 +173,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
@@ -1250,12 +1250,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()
@@ -1265,17 +1268,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 FabricUIManagerBinding binding = mBinding;
if (binding == null || mDestroyed) {
mMountedSurfaceIds.clear();
return;
}
for (int surfaceId : mMountedSurfaceIds) {
for (int surfaceId : surfaceIdsToReportMount) {
binding.reportMount(surfaceId);
}
mMountedSurfaceIds.clear();
}
});
}
@@ -331,14 +331,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);
}
/**
@@ -458,11 +455,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));
}
}
@@ -16,7 +16,7 @@ import okhttp3.Headers
import okhttp3.HttpUrl
/** Basic okhttp3 CookieJar container */
internal class ReactCookieJarContainer : CookieJarContainer {
public class ReactCookieJarContainer : CookieJarContainer {
private var cookieJar: CookieJar? = null
@@ -15,8 +15,8 @@ import java.util.Map;
public class ReactNativeVersion {
public static final Map<String, Object> VERSION = MapBuilder.<String, Object>of(
"major", 1000,
"minor", 0,
"patch", 0,
"major", 0,
"minor", 78,
"patch", 3,
"prerelease", null);
}
@@ -17,5 +17,5 @@ public interface ReactOverflowView {
* Gets the overflow state of a view. If set, this should be one of [ViewProps#HIDDEN],
* [ViewProps#VISIBLE] or [ViewProps#SCROLL].
*/
public fun getOverflow(): String?
public val overflow: String?
}
@@ -13,6 +13,6 @@ package com.facebook.react.uimanager
*/
public interface ReactPointerEventsView {
/** Return the PointerEvents of the View. */
public fun getPointerEvents(): PointerEvents
/** The PointerEvents of the View. */
public val pointerEvents: PointerEvents
}
@@ -9,5 +9,5 @@ package com.facebook.react.uimanager
/** A task to execute on the UI View for third party libraries. */
public fun interface UIBlock {
public fun execute(nativeViewHierarchyManager: NativeViewHierarchyManager?): Unit
public fun execute(nativeViewHierarchyManager: NativeViewHierarchyManager): Unit
}
@@ -16,6 +16,7 @@ import android.graphics.drawable.LayerDrawable
import android.os.Build
import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
import com.facebook.react.uimanager.PixelUtil.dpToPx
import com.facebook.react.uimanager.style.BorderInsets
import com.facebook.react.uimanager.style.BorderRadiusStyle
@@ -198,17 +199,17 @@ internal class CompositeBackgroundDrawable(
computedBorderRadius?.let {
pathForOutline.addRoundRect(
RectF(bounds),
floatArrayOf(
it.topLeft.horizontal + (computedBorderInsets?.left ?: 0f),
it.topLeft.vertical + (computedBorderInsets?.top ?: 0f),
it.topRight.horizontal + (computedBorderInsets?.right ?: 0f),
it.topRight.vertical + (computedBorderInsets?.top ?: 0f),
it.bottomRight.horizontal + (computedBorderInsets?.right ?: 0f),
it.bottomRight.vertical + (computedBorderInsets?.bottom ?: 0f),
it.bottomLeft.horizontal + (computedBorderInsets?.left ?: 0f),
it.bottomLeft.vertical) + (computedBorderInsets?.bottom ?: 0f),
Path.Direction.CW)
RectF(bounds),
floatArrayOf(
(it.topLeft.horizontal + (computedBorderInsets?.left ?: 0f)).dpToPx(),
(it.topLeft.vertical + (computedBorderInsets?.top ?: 0f)).dpToPx(),
(it.topRight.horizontal + (computedBorderInsets?.right ?: 0f)).dpToPx(),
(it.topRight.vertical + (computedBorderInsets?.top ?: 0f)).dpToPx(),
(it.bottomRight.horizontal + (computedBorderInsets?.right ?: 0f)).dpToPx(),
(it.bottomRight.vertical + (computedBorderInsets?.bottom ?: 0f)).dpToPx(),
(it.bottomLeft.horizontal + (computedBorderInsets?.left ?: 0f)).dpToPx(),
(it.bottomLeft.vertical + (computedBorderInsets?.bottom ?: 0f)).dpToPx()),
Path.Direction.CW)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
@@ -136,10 +136,10 @@ public data class BorderRadiusStyle(
(startStart ?: topStart ?: topLeft ?: uniform)?.resolve(width, height)
?: zeroRadii,
bottomLeft =
(endEnd ?: bottomStart ?: bottomRight ?: uniform)?.resolve(width, height)
(endEnd ?: bottomEnd ?: bottomRight ?: uniform)?.resolve(width, height)
?: zeroRadii,
bottomRight =
(startEnd ?: bottomEnd ?: bottomLeft ?: uniform)?.resolve(width, height)
(startEnd ?: bottomStart ?: bottomLeft ?: uniform)?.resolve(width, height)
?: zeroRadii,
width = width,
height = height,
@@ -124,7 +124,7 @@ public constructor(
}
}
@ReactProp(name = "defaultSource", customType = "ImageSource")
@ReactProp(name = "defaultSource")
public fun setDefaultSource(view: ReactImageView, source: String?) {
view.setDefaultSource(source)
}
@@ -61,6 +61,7 @@ public class TextAttributeProps {
public static final short TA_KEY_LINE_BREAK_STRATEGY = 25;
public static final short TA_KEY_ROLE = 26;
public static final short TA_KEY_TEXT_TRANSFORM = 27;
public static final short TA_KEY_MAX_FONT_SIZE_MULTIPLIER = 29;
public static final int UNSET = -1;
@@ -81,6 +82,7 @@ public class TextAttributeProps {
protected float mLineHeight = Float.NaN;
protected boolean mIsColorSet = false;
protected boolean mAllowFontScaling = true;
protected float mMaxFontSizeMultiplier = Float.NaN;
protected int mColor;
protected boolean mIsBackgroundColorSet = false;
protected int mBackgroundColor;
@@ -227,6 +229,9 @@ public class TextAttributeProps {
case TA_KEY_TEXT_TRANSFORM:
result.setTextTransform(entry.getStringValue());
break;
case TA_KEY_MAX_FONT_SIZE_MULTIPLIER:
result.setMaxFontSizeMultiplier((float) entry.getDoubleValue());
break;
}
}
@@ -243,6 +248,8 @@ public class TextAttributeProps {
result.setLineHeight(getFloatProp(props, ViewProps.LINE_HEIGHT, ReactConstants.UNSET));
result.setLetterSpacing(getFloatProp(props, ViewProps.LETTER_SPACING, Float.NaN));
result.setAllowFontScaling(getBooleanProp(props, ViewProps.ALLOW_FONT_SCALING, true));
result.setMaxFontSizeMultiplier(
getFloatProp(props, ViewProps.MAX_FONT_SIZE_MULTIPLIER, Float.NaN));
result.setFontSize(getFloatProp(props, ViewProps.FONT_SIZE, ReactConstants.UNSET));
result.setColor(props.hasKey(ViewProps.COLOR) ? props.getInt(ViewProps.COLOR, 0) : null);
result.setColor(
@@ -411,7 +418,14 @@ public class TextAttributeProps {
mAllowFontScaling = allowFontScaling;
setFontSize(mFontSizeInput);
setLineHeight(mLineHeightInput);
setLetterSpacing(mLetterSpacingInput);
}
}
private void setMaxFontSizeMultiplier(float maxFontSizeMultiplier) {
if (maxFontSizeMultiplier != mMaxFontSizeMultiplier) {
mMaxFontSizeMultiplier = maxFontSizeMultiplier;
setFontSize(mFontSizeInput);
setLineHeight(mLineHeightInput);
}
}
@@ -420,7 +434,7 @@ public class TextAttributeProps {
if (fontSize != ReactConstants.UNSET) {
fontSize =
mAllowFontScaling
? (float) Math.ceil(PixelUtil.toPixelFromSP(fontSize))
? (float) Math.ceil(PixelUtil.toPixelFromSP(fontSize, mMaxFontSizeMultiplier))
: (float) Math.ceil(PixelUtil.toPixelFromDIP(fontSize));
}
mFontSize = (int) fontSize;
@@ -588,9 +588,6 @@ public class ReactViewGroup extends ViewGroup
UiThreadUtil.assertOnUiThread();
checkViewClippingTag(child, Boolean.TRUE);
if (!customDrawOrderDisabled()) {
if (indexOfChild(child) == -1) {
return;
}
getDrawingOrderHelper().handleRemoveView(child);
setChildrenDrawingOrderEnabled(getDrawingOrderHelper().shouldEnableCustomDrawingOrder());
} else {
@@ -88,13 +88,13 @@ class BorderRadiusStyleTest {
arrayOf(
BorderRadiusProp.BORDER_RADIUS,
BorderRadiusProp.BORDER_BOTTOM_RIGHT_RADIUS,
BorderRadiusProp.BORDER_BOTTOM_START_RADIUS,
BorderRadiusProp.BORDER_BOTTOM_END_RADIUS,
BorderRadiusProp.BORDER_END_END_RADIUS),
ComputedBorderRadiusProp.COMPUTED_BORDER_BOTTOM_RIGHT_RADIUS to
arrayOf(
BorderRadiusProp.BORDER_RADIUS,
BorderRadiusProp.BORDER_BOTTOM_LEFT_RADIUS,
BorderRadiusProp.BORDER_BOTTOM_END_RADIUS,
BorderRadiusProp.BORDER_BOTTOM_START_RADIUS,
BorderRadiusProp.BORDER_START_END_RADIUS),
)
@@ -15,9 +15,9 @@
namespace facebook::react {
constexpr struct {
int32_t Major = 1000;
int32_t Minor = 0;
int32_t Patch = 0;
int32_t Major = 0;
int32_t Minor = 78;
int32_t Patch = 3;
std::string_view Prerelease = "";
} ReactNativeVersion;
@@ -123,6 +123,15 @@ std::vector<ExportedMethod> parseExportedMethods(std::string moduleName, Class m
NSArray<RCTMethodArgument *> *arguments;
SEL objCMethodSelector = NSSelectorFromString(RCTParseMethodSignature(methodInfo->objcName, &arguments));
NSMethodSignature *objCMethodSignature = [moduleClass instanceMethodSignatureForSelector:objCMethodSelector];
if (objCMethodSignature == nullptr) {
RCTLogWarn(
@"The objective-c `%s` method signature for the JS method `%@` can not be found in the ObjecitveC definition of the %s module.\nThe `%@` JS method will not be available.",
methodInfo->objcName,
jsMethodName,
moduleName.c_str(),
jsMethodName);
continue;
}
std::string objCMethodReturnType = [objCMethodSignature methodReturnType];
if (objCMethodSignature.numberOfArguments - 2 != [arguments count]) {
@@ -337,7 +346,7 @@ void ObjCInteropTurboModule::setInvocationArg(
SEL selector = selectorForType(argumentType);
if ([RCTConvert respondsToSelector:selector]) {
id objCArg = TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_);
id objCArg = TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_, YES);
if (objCArgType == @encode(char)) {
char arg = RCTConvertTo<char>(selector, objCArg);
@@ -437,6 +446,15 @@ void ObjCInteropTurboModule::setInvocationArg(
if (objCArgType == @encode(id)) {
id arg = RCTConvertTo<id>(selector, objCArg);
// Handle the special case where there is an argument and it must be nil
// Without this check, the JS side will receive an object.
// See: discussion at
// https://github.com/facebook/react-native/pull/49250#issuecomment-2668465893
if (arg == [NSNull null]) {
return;
}
if (arg) {
[retainedObjectsForInvocation addObject:arg];
}
@@ -491,7 +509,7 @@ void ObjCInteropTurboModule::setInvocationArg(
}
RCTResponseSenderBlock arg =
(RCTResponseSenderBlock)TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_);
(RCTResponseSenderBlock)TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_, YES);
if (arg) {
[retainedObjectsForInvocation addObject:arg];
}
@@ -506,7 +524,7 @@ void ObjCInteropTurboModule::setInvocationArg(
}
RCTResponseSenderBlock senderBlock =
(RCTResponseSenderBlock)TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_);
(RCTResponseSenderBlock)TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_, YES);
RCTResponseErrorBlock arg = ^(NSError *error) {
senderBlock(@[ RCTJSErrorFromNSError(error) ]);
};
@@ -536,7 +554,7 @@ void ObjCInteropTurboModule::setInvocationArg(
runtime, errorPrefix + "JavaScript argument must be a plain object. Got " + getType(runtime, jsiArg));
}
id arg = TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_);
id arg = TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsiArg, jsInvoker_, YES);
RCTManagedPointer *(*convert)(id, SEL, id) = (__typeof__(convert))objc_msgSend;
RCTManagedPointer *box = convert([RCTCxxConvert class], selector, arg);
@@ -32,6 +32,7 @@ using EventEmitterCallback = std::function<void(const std::string &, id)>;
namespace TurboModuleConvertUtils {
jsi::Value convertObjCObjectToJSIValue(jsi::Runtime &runtime, id value);
id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr<CallInvoker> jsInvoker);
id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr<CallInvoker> jsInvoker, BOOL useNSNull);
} // namespace TurboModuleConvertUtils
template <>
@@ -57,7 +57,7 @@ static jsi::Value convertNSNumberToJSINumber(jsi::Runtime &runtime, NSNumber *va
static jsi::String convertNSStringToJSIString(jsi::Runtime &runtime, NSString *value)
{
return jsi::String::createFromUtf8(runtime, [value UTF8String] ?: "");
return jsi::String::createFromUtf8(runtime, [value UTF8String] ? [value UTF8String] : "");
}
static jsi::Object convertNSDictionaryToJSIObject(jsi::Runtime &runtime, NSDictionary *value)
@@ -112,20 +112,20 @@ static NSString *convertJSIStringToNSString(jsi::Runtime &runtime, const jsi::St
}
static NSArray *
convertJSIArrayToNSArray(jsi::Runtime &runtime, const jsi::Array &value, std::shared_ptr<CallInvoker> jsInvoker)
convertJSIArrayToNSArray(jsi::Runtime &runtime, const jsi::Array &value, std::shared_ptr<CallInvoker> jsInvoker, BOOL useNSNull)
{
size_t size = value.size(runtime);
NSMutableArray *result = [NSMutableArray new];
for (size_t i = 0; i < size; i++) {
// Insert kCFNull when it's `undefined` value to preserve the indices.
id convertedObject = convertJSIValueToObjCObject(runtime, value.getValueAtIndex(runtime, i), jsInvoker);
id convertedObject = convertJSIValueToObjCObject(runtime, value.getValueAtIndex(runtime, i), jsInvoker, useNSNull);
[result addObject:convertedObject ? convertedObject : (id)kCFNull];
}
return [result copy];
}
static NSDictionary *
convertJSIObjectToNSDictionary(jsi::Runtime &runtime, const jsi::Object &value, std::shared_ptr<CallInvoker> jsInvoker)
convertJSIObjectToNSDictionary(jsi::Runtime &runtime, const jsi::Object &value, std::shared_ptr<CallInvoker> jsInvoker, BOOL useNSNull)
{
jsi::Array propertyNames = value.getPropertyNames(runtime);
size_t size = propertyNames.size(runtime);
@@ -133,7 +133,7 @@ convertJSIObjectToNSDictionary(jsi::Runtime &runtime, const jsi::Object &value,
for (size_t i = 0; i < size; i++) {
jsi::String name = propertyNames.getValueAtIndex(runtime, i).getString(runtime);
NSString *k = convertJSIStringToNSString(runtime, name);
id v = convertJSIValueToObjCObject(runtime, value.getProperty(runtime, name), jsInvoker);
id v = convertJSIValueToObjCObject(runtime, value.getProperty(runtime, name), jsInvoker, useNSNull);
if (v) {
result[k] = v;
}
@@ -159,11 +159,14 @@ convertJSIFunctionToCallback(jsi::Runtime &rt, jsi::Function &&function, std::sh
};
}
id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr<CallInvoker> jsInvoker)
id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr<CallInvoker> jsInvoker, BOOL useNSNull)
{
if (value.isUndefined() || value.isNull()) {
if (value.isUndefined() || (value.isNull() && !useNSNull)) {
return nil;
}
if (value.isNull() && useNSNull) {
return [NSNull null];
}
if (value.isBool()) {
return @(value.getBool());
}
@@ -176,17 +179,22 @@ id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, s
if (value.isObject()) {
jsi::Object o = value.getObject(runtime);
if (o.isArray(runtime)) {
return convertJSIArrayToNSArray(runtime, o.getArray(runtime), jsInvoker);
return convertJSIArrayToNSArray(runtime, o.getArray(runtime), jsInvoker, useNSNull);
}
if (o.isFunction(runtime)) {
return convertJSIFunctionToCallback(runtime, o.getFunction(runtime), jsInvoker);
}
return convertJSIObjectToNSDictionary(runtime, o, jsInvoker);
return convertJSIObjectToNSDictionary(runtime, o, jsInvoker, useNSNull);
}
throw std::runtime_error("Unsupported jsi::Value kind");
}
id convertJSIValueToObjCObject(jsi::Runtime &runtime, const jsi::Value &value, std::shared_ptr<CallInvoker> jsInvoker)
{
return convertJSIValueToObjCObject(runtime, value, jsInvoker, NO);
}
static jsi::Value createJSRuntimeError(jsi::Runtime &runtime, const std::string &message)
{
return runtime.global().getPropertyAsFunction(runtime, "Error").call(runtime, message);
@@ -195,7 +203,11 @@ static jsi::Value createJSRuntimeError(jsi::Runtime &runtime, const std::string
/**
* Creates JSError with current JS runtime and NSException stack trace.
*/
static jsi::JSError convertNSExceptionToJSError(jsi::Runtime &runtime, NSException *exception)
static jsi::JSError convertNSExceptionToJSError(
jsi::Runtime &runtime,
NSException *exception,
const std::string &moduleName,
const std::string &methodName)
{
std::string reason = [exception.reason UTF8String];
@@ -206,7 +218,8 @@ static jsi::JSError convertNSExceptionToJSError(jsi::Runtime &runtime, NSExcepti
cause.setProperty(
runtime, "stackReturnAddresses", convertNSArrayToJSIArray(runtime, exception.callStackReturnAddresses));
jsi::Value error = createJSRuntimeError(runtime, "Exception in HostFunction: " + reason);
std::string message = moduleName + "." + methodName + " raised an exception: " + reason;
jsi::Value error = createJSRuntimeError(runtime, message);
error.asObject(runtime).setProperty(runtime, "cause", std::move(cause));
return {runtime, std::move(error)};
}
@@ -338,28 +351,34 @@ id ObjCTurboModule::performMethodInvocation(
}
if (isSync) {
TurboModulePerfLogger::syncMethodCallExecutionStart(moduleName, methodNameStr.c_str());
TurboModulePerfLogger::syncMethodCallExecutionStart(moduleName, methodName);
} else {
TurboModulePerfLogger::asyncMethodCallExecutionStart(moduleName, methodNameStr.c_str(), asyncCallCounter);
TurboModulePerfLogger::asyncMethodCallExecutionStart(moduleName, methodName, asyncCallCounter);
}
@try {
[inv invokeWithTarget:strongModule];
} @catch (NSException *exception) {
throw convertNSExceptionToJSError(runtime, exception);
if (isSync) {
// We can only convert NSException to JSError in sync method calls.
// See https://github.com/reactwg/react-native-new-architecture/discussions/276#discussioncomment-12567155
throw convertNSExceptionToJSError(runtime, exception, std::string{moduleName}, methodNameStr);
} else {
@throw exception;
}
} @finally {
[retainedObjectsForInvocation removeAllObjects];
}
if (!isSync) {
TurboModulePerfLogger::asyncMethodCallExecutionEnd(moduleName, methodNameStr.c_str(), asyncCallCounter);
TurboModulePerfLogger::asyncMethodCallExecutionEnd(moduleName, methodName, asyncCallCounter);
return;
}
void *rawResult;
[inv getReturnValue:&rawResult];
result = (__bridge id)rawResult;
TurboModulePerfLogger::syncMethodCallExecutionEnd(moduleName, methodNameStr.c_str());
TurboModulePerfLogger::syncMethodCallExecutionEnd(moduleName, methodName);
};
if (isSync) {
@@ -401,23 +420,23 @@ void ObjCTurboModule::performVoidMethodInvocation(
}
if (shouldVoidMethodsExecuteSync_) {
TurboModulePerfLogger::syncMethodCallExecutionStart(moduleName, methodNameStr.c_str());
TurboModulePerfLogger::syncMethodCallExecutionStart(moduleName, methodName);
} else {
TurboModulePerfLogger::asyncMethodCallExecutionStart(moduleName, methodNameStr.c_str(), asyncCallCounter);
TurboModulePerfLogger::asyncMethodCallExecutionStart(moduleName, methodName, asyncCallCounter);
}
@try {
[inv invokeWithTarget:strongModule];
} @catch (NSException *exception) {
throw convertNSExceptionToJSError(runtime, exception);
throw convertNSExceptionToJSError(runtime, exception, std::string{moduleName}, methodNameStr);
} @finally {
[retainedObjectsForInvocation removeAllObjects];
}
if (shouldVoidMethodsExecuteSync_) {
TurboModulePerfLogger::syncMethodCallExecutionEnd(moduleName, methodNameStr.c_str());
TurboModulePerfLogger::syncMethodCallExecutionEnd(moduleName, methodName);
} else {
TurboModulePerfLogger::asyncMethodCallExecutionEnd(moduleName, methodNameStr.c_str(), asyncCallCounter);
TurboModulePerfLogger::asyncMethodCallExecutionEnd(moduleName, methodName, asyncCallCounter);
}
return;
@@ -46,6 +46,9 @@ void TextAttributes::apply(TextAttributes textAttributes) {
allowFontScaling = textAttributes.allowFontScaling.has_value()
? textAttributes.allowFontScaling
: allowFontScaling;
maxFontSizeMultiplier = !std::isnan(textAttributes.maxFontSizeMultiplier)
? textAttributes.maxFontSizeMultiplier
: maxFontSizeMultiplier;
dynamicTypeRamp = textAttributes.dynamicTypeRamp.has_value()
? textAttributes.dynamicTypeRamp
: dynamicTypeRamp;
@@ -168,6 +171,7 @@ bool TextAttributes::operator==(const TextAttributes& rhs) const {
rhs.accessibilityRole,
rhs.role,
rhs.textTransform) &&
floatEquality(maxFontSizeMultiplier, rhs.maxFontSizeMultiplier) &&
floatEquality(opacity, rhs.opacity) &&
floatEquality(fontSize, rhs.fontSize) &&
floatEquality(fontSizeMultiplier, rhs.fontSizeMultiplier) &&
@@ -224,6 +228,10 @@ SharedDebugStringConvertibleList TextAttributes::getDebugProps() const {
"allowFontScaling",
allowFontScaling,
textAttributes.allowFontScaling),
debugStringConvertibleItem(
"maxFontSizeMultiplier",
maxFontSizeMultiplier,
textAttributes.maxFontSizeMultiplier),
debugStringConvertibleItem(
"dynamicTypeRamp", dynamicTypeRamp, textAttributes.dynamicTypeRamp),
debugStringConvertibleItem(
@@ -51,6 +51,7 @@ class TextAttributes : public DebugStringConvertible {
std::optional<FontStyle> fontStyle{};
std::optional<FontVariant> fontVariant{};
std::optional<bool> allowFontScaling{};
Float maxFontSizeMultiplier{std::numeric_limits<Float>::quiet_NaN()};
std::optional<DynamicTypeRamp> dynamicTypeRamp{};
Float letterSpacing{std::numeric_limits<Float>::quiet_NaN()};
std::optional<TextTransform> textTransform{};
@@ -117,6 +118,7 @@ struct hash<facebook::react::TextAttributes> {
textAttributes.opacity,
textAttributes.fontFamily,
textAttributes.fontSize,
textAttributes.maxFontSizeMultiplier,
textAttributes.fontSizeMultiplier,
textAttributes.fontWeight,
textAttributes.fontStyle,
@@ -910,6 +910,7 @@ constexpr static MapBuffer::Key TA_KEY_LINE_BREAK_STRATEGY = 25;
constexpr static MapBuffer::Key TA_KEY_ROLE = 26;
constexpr static MapBuffer::Key TA_KEY_TEXT_TRANSFORM = 27;
constexpr static MapBuffer::Key TA_KEY_ALIGNMENT_VERTICAL = 28;
constexpr static MapBuffer::Key TA_KEY_MAX_FONT_SIZE_MULTIPLIER = 29;
// constants for ParagraphAttributes serialization
constexpr static MapBuffer::Key PA_KEY_MAX_NUMBER_OF_LINES = 0;
@@ -1004,6 +1005,10 @@ inline MapBuffer toMapBuffer(const TextAttributes& textAttributes) {
builder.putBool(
TA_KEY_ALLOW_FONT_SCALING, *textAttributes.allowFontScaling);
}
if (!std::isnan(textAttributes.maxFontSizeMultiplier)) {
builder.putDouble(
TA_KEY_MAX_FONT_SIZE_MULTIPLIER, textAttributes.maxFontSizeMultiplier);
}
if (!std::isnan(textAttributes.letterSpacing)) {
builder.putDouble(TA_KEY_LETTER_SPACING, textAttributes.letterSpacing);
}

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