Compare commits

...
Author SHA1 Message Date
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
82 changed files with 907 additions and 498 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:
+6 -1
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)}')
+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 -2
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.0",
"@react-native/metro-config": "0.78.0",
"@tsconfig/node18": "1.0.1",
"@types/react": "^19.0.0",
"@typescript-eslint/parser": "^7.1.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/assets-registry",
"version": "0.77.0-main",
"version": "0.78.0",
"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.0",
"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.0"
},
"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
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/community-cli-plugin",
"version": "0.77.0-main",
"version": "0.78.0",
"description": "Core CLI commands for React Native",
"keywords": [
"react-native",
@@ -22,8 +22,8 @@
"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.0",
"@react-native/metro-babel-transformer": "0.78.0",
"chalk": "^4.0.0",
"debug": "^2.2.0",
"invariant": "^2.2.4",
@@ -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,
},
],
};
@@ -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);
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/core-cli-utils",
"version": "0.77.0-main",
"version": "0.78.0",
"description": "React Native CLI library for Frameworks to build on",
"license": "MIT",
"main": "./src/index.flow.js",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/debugger-frontend",
"version": "0.77.0-main",
"version": "0.78.0",
"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.0",
"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.0",
"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',
@@ -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.0",
"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.0",
"@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.0",
"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.0",
"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.0",
"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.0",
"description": "Gradle Plugin for React Native",
"license": "MIT",
"repository": {
+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.0",
"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.0"
},
"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.0",
"@react-native/core-cli-utils": "0.78.0",
"@react-native/eslint-config": "0.78.0",
"@react-native/metro-config": "0.78.0",
"chalk": "^4.1.2",
"commander": "^12.0.0",
"eslint": "^8.19.0",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/metro-config",
"version": "0.77.0-main",
"version": "0.78.0",
"description": "Metro configuration for React Native.",
"license": "MIT",
"repository": {
@@ -26,8 +26,8 @@
"dist"
],
"dependencies": {
"@react-native/js-polyfills": "0.77.0-main",
"@react-native/metro-babel-transformer": "0.77.0-main",
"@react-native/js-polyfills": "0.78.0",
"@react-native/metro-babel-transformer": "0.78.0",
"metro-config": "^0.81.0",
"metro-runtime": "^0.81.0"
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/normalize-colors",
"version": "0.77.0-main",
"version": "0.78.0",
"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.0",
"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.0",
"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.0",
"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.0",
"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.0",
"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.0",
"private": true,
"license": "MIT",
"repository": {
@@ -1,6 +1,6 @@
{
"name": "@react-native/codegen-typescript-test",
"version": "0.77.0-main",
"version": "0.78.0",
"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.0"
},
"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.0",
"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.0",
"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.0",
"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.0",
"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.0"
},
"peerDependencies": {
"@types/react": "^19.0.0",
@@ -1,6 +1,6 @@
{
"name": "@react-native/oss-library-example",
"version": "0.77.0-main",
"version": "0.78.0",
"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.0",
"react-native": "0.78.0"
},
"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]);
}
@@ -255,9 +255,12 @@ class RCTAppDelegateBridgelessFeatureFlags : public ReactNativeFeatureFlagsDefau
- (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
+2 -2
View File
@@ -14,8 +14,8 @@ const version: $ReadOnly<{
patch: number,
prerelease: string | null,
}> = {
major: 1000,
minor: 0,
major: 0,
minor: 78,
patch: 0,
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',
@@ -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];
-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;
@@ -9000,7 +9001,6 @@ export type HMRClientNativeInterface = {|
isEnabled: boolean,
scheme?: string
): void,
unstable_notifyFuseboxConsoleEnabled(): void,
|};
declare const HMRClient: HMRClientNativeInterface;
declare module.exports: HMRClient;
@@ -21,8 +21,8 @@ NSDictionary* RCTGetReactNativeVersion(void)
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^(void){
__rnVersion = @{
RCTVersionMajor: @(1000),
RCTVersionMinor: @(0),
RCTVersionMajor: @(0),
RCTVersionMinor: @(78),
RCTVersionPatch: @(0),
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],
@@ -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.0
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);
@@ -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,
"major", 0,
"minor", 78,
"patch", 0,
"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
}
@@ -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;
@@ -15,8 +15,8 @@
namespace facebook::react {
constexpr struct {
int32_t Major = 1000;
int32_t Minor = 0;
int32_t Major = 0;
int32_t Minor = 78;
int32_t Patch = 0;
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);
@@ -491,7 +500,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 +515,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 +545,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 <>
@@ -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);
@@ -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);
}
@@ -73,6 +73,12 @@ static TextAttributes convertRawProp(
"allowFontScaling",
sourceTextAttributes.allowFontScaling,
defaultTextAttributes.allowFontScaling);
textAttributes.maxFontSizeMultiplier = convertRawProp(
context,
rawProps,
"maxFontSizeMultiplier",
sourceTextAttributes.maxFontSizeMultiplier,
defaultTextAttributes.maxFontSizeMultiplier);
textAttributes.dynamicTypeRamp = convertRawProp(
context,
rawProps,
@@ -266,6 +272,12 @@ void BaseTextProps::setProp(
defaults, value, textAttributes, fontVariant, "fontVariant");
REBUILD_FIELD_SWITCH_CASE(
defaults, value, textAttributes, allowFontScaling, "allowFontScaling");
REBUILD_FIELD_SWITCH_CASE(
defaults,
value,
textAttributes,
maxFontSizeMultiplier,
"maxFontSizeMultiplier");
REBUILD_FIELD_SWITCH_CASE(
defaults, value, textAttributes, letterSpacing, "letterSpacing");
REBUILD_FIELD_SWITCH_CASE(
@@ -135,6 +135,7 @@ inline static CGFloat RCTBaseSizeForDynamicTypeRamp(const DynamicTypeRamp &dynam
inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const TextAttributes &textAttributes)
{
if (textAttributes.allowFontScaling.value_or(true)) {
CGFloat fontSizeMultiplier = !isnan(textAttributes.fontSizeMultiplier) ? textAttributes.fontSizeMultiplier : 1.0;
if (textAttributes.dynamicTypeRamp.has_value()) {
DynamicTypeRamp dynamicTypeRamp = textAttributes.dynamicTypeRamp.value();
UIFontMetrics *fontMetrics =
@@ -142,10 +143,11 @@ inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const Tex
// Using a specific font size reduces rounding errors from -scaledValueForValue:
CGFloat requestedSize =
isnan(textAttributes.fontSize) ? RCTBaseSizeForDynamicTypeRamp(dynamicTypeRamp) : textAttributes.fontSize;
return [fontMetrics scaledValueForValue:requestedSize] / requestedSize;
} else {
return textAttributes.fontSizeMultiplier;
fontSizeMultiplier = [fontMetrics scaledValueForValue:requestedSize] / requestedSize;
}
CGFloat maxFontSizeMultiplier =
!isnan(textAttributes.maxFontSizeMultiplier) ? textAttributes.maxFontSizeMultiplier : 0.0;
return maxFontSizeMultiplier >= 1.0 ? fminf(maxFontSizeMultiplier, fontSizeMultiplier) : fontSizeMultiplier;
} else {
return 1.0;
}
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "react-native",
"version": "1000.0.0",
"version": "0.78.0",
"description": "A framework for building native apps using React",
"license": "MIT",
"repository": {
@@ -108,13 +108,13 @@
},
"dependencies": {
"@jest/create-cache-key-function": "^29.6.3",
"@react-native/assets-registry": "0.77.0-main",
"@react-native/codegen": "0.77.0-main",
"@react-native/community-cli-plugin": "0.77.0-main",
"@react-native/gradle-plugin": "0.77.0-main",
"@react-native/js-polyfills": "0.77.0-main",
"@react-native/normalize-colors": "0.77.0-main",
"@react-native/virtualized-lists": "0.77.0-main",
"@react-native/assets-registry": "0.78.0",
"@react-native/codegen": "0.78.0",
"@react-native/community-cli-plugin": "0.78.0",
"@react-native/gradle-plugin": "0.78.0",
"@react-native/js-polyfills": "0.78.0",
"@react-native/normalize-colors": "0.78.0",
"@react-native/virtualized-lists": "0.78.0",
"abort-controller": "^3.0.0",
"anser": "^1.4.9",
"ansi-regex": "^5.0.0",
@@ -84,12 +84,18 @@ const codegenCommand = {
name: '--outputPath <path>',
description: 'Path where generated artifacts will be output to.',
},
{
name: '--source <string>',
description: 'Whether the script is invoked from an `app` or a `library`',
default: 'app',
},
],
func: (argv, config, args) =>
require('./scripts/codegen/generate-artifacts-executor').execute(
args.path,
args.platform,
args.outputPath,
args.source,
),
};
@@ -899,11 +899,12 @@ function generateFBReactNativeSpecIOS(projectRoot /*: string */) /*: void*/ {
* @parameter projectRoot: the directory with the app source code, where the package.json lives.
* @parameter baseOutputPath: the base output path for the CodeGen.
* @parameter targetPlatform: the target platform. Supported values: 'android', 'ios', 'all'.
* @parameter source: the source that is invoking codegen. Supported values: 'app', 'library'.
* @throws If it can't find a config file for react-native.
* @throws If it can't find a CodeGen configuration in the file.
* @throws If it can't find a cli for the CodeGen.
*/
function execute(projectRoot, targetPlatform, baseOutputPath) {
function execute(projectRoot, targetPlatform, baseOutputPath, source) {
try {
codegenLog(`Analyzing ${path.join(projectRoot, 'package.json')}`);
@@ -951,9 +952,12 @@ function execute(projectRoot, targetPlatform, baseOutputPath) {
platform,
);
generateRCTThirdPartyComponents(libraries, outputPath);
generateCustomURLHandlers(libraries, outputPath);
generateAppDependencyProvider(outputPath);
if (source === 'app') {
// These components are only required by apps, not by libraries
generateRCTThirdPartyComponents(libraries, outputPath);
generateCustomURLHandlers(libraries, outputPath);
generateAppDependencyProvider(outputPath);
}
cleanupEmptyFilesAndFolders(outputPath);
}
@@ -25,7 +25,12 @@ const argv = yargs
alias: 'outputPath',
description: 'Path where generated artifacts will be output to.',
})
.option('s', {
alias: 'source',
description: 'Whether the script is invoked from an `app` or a `library`',
default: 'app',
})
.usage('Usage: $0 -p [path to app] -t [target platform] -o [output path]')
.demandOption(['p', 't']).argv;
executor.execute(argv.path, argv.targetPlatform, argv.outputPath);
executor.execute(argv.path, argv.targetPlatform, argv.outputPath, argv.source);
@@ -0,0 +1 @@
hermes-2025-01-13-RNv0.78.0-a942ef374897d85da38e9c8904574f8376555388
+1
View File
@@ -9,3 +9,4 @@ gem 'cocoapods', '~> 1.13', '!= 1.15.0', '!= 1.15.1'
gem 'rexml'
gem 'activesupport', '>= 6.1.7.5', '< 7.1.0'
gem 'xcodeproj', '< 1.26.0'
gem 'concurrent-ruby', '< 1.3.4'
File diff suppressed because it is too large Load Diff
@@ -482,6 +482,37 @@ function AllowFontScalingExample(props: {}): React.Node {
);
}
function MaxFontSizeMultiplierExample(props: {}): React.Node {
return (
<View testID={'max-font-size-multiplier'}>
<Text>
When allowFontScaling is enabled, you can use the maxFontSizeMultiplier
prop to set an upper limit on how much the font size will be scaled.
</Text>
<Text
allowFontScaling={true}
maxFontSizeMultiplier={1}
style={{marginTop: 10}}>
This text will not scale up (max 1x)
</Text>
<Text allowFontScaling={true} maxFontSizeMultiplier={1.5}>
This text will scale up (max 1.5x)
</Text>
<Text allowFontScaling={true} maxFontSizeMultiplier={1}>
<Text>Inherit max (max 1x)</Text>
</Text>
<Text allowFontScaling={true} maxFontSizeMultiplier={1}>
<Text maxFontSizeMultiplier={1.5}>
Override inherited max (max 1.5x)
</Text>
</Text>
<Text allowFontScaling={true} maxFontSizeMultiplier={1}>
<Text maxFontSizeMultiplier={0}>Ignore inherited max (no max)</Text>
</Text>
</View>
);
}
function NumberOfLinesExample(props: {}): React.Node {
return (
<>
@@ -1462,6 +1493,13 @@ const examples = [
return <AllowFontScalingExample />;
},
},
{
title: 'maxFontSizeMultiplier attribute',
name: 'maxFontSizeMultiplier',
render(): React.Node {
return <MaxFontSizeMultiplierExample />;
},
},
{
title: 'selectable attribute',
name: 'selectable',
@@ -1175,6 +1175,62 @@ const examples = [
);
},
},
{
title: 'maxFontSizeMultiplier attribute',
name: 'maxFontSizeMultiplier',
render(): React.Node {
return (
<View testID={'max-font-size-multiplier'}>
<Text>
When allowFontScaling is enabled, you can use the
maxFontSizeMultiplier prop to set an upper limit on how much the
font size will be scaled.
</Text>
<Text
allowFontScaling={true}
maxFontSizeMultiplier={1}
style={{marginTop: 10}}>
This text will not scale up (max 1x)
</Text>
<Text allowFontScaling={true} maxFontSizeMultiplier={1.5}>
This text will scale up (max 1.5x)
</Text>
<Text allowFontScaling={true} maxFontSizeMultiplier={1}>
<Text>Inherit max (max 1x)</Text>
</Text>
<Text allowFontScaling={true} maxFontSizeMultiplier={1}>
<Text maxFontSizeMultiplier={1.5}>
Override inherited max (max 1.5x)
</Text>
</Text>
<Text allowFontScaling={true} maxFontSizeMultiplier={1}>
<Text maxFontSizeMultiplier={0}>Ignore inherited max (no max)</Text>
</Text>
<Text
allowFontScaling={true}
style={{fontSize: 22}}
dynamicTypeRamp="title2">
This text will scale with 'title2' dynamic type ramp (no max)
</Text>
<Text
allowFontScaling={true}
style={{fontSize: 22}}
dynamicTypeRamp="title2"
maxFontSizeMultiplier={1.2}>
This text will scale with 'title2' dynamic type ramp (max 1.2x)
</Text>
<Text
allowFontScaling={true}
style={{fontSize: 22}}
dynamicTypeRamp="title2"
maxFontSizeMultiplier={1}>
This text uses 'title2' dynamic type ramp but will not scale up (max
1x)
</Text>
</View>
);
},
},
{
title: 'Inline views',
render: (): React.Node => <TextInlineView.Basic />,
@@ -1440,9 +1496,27 @@ const examples = [
<Text style={{fontSize: 20}} dynamicTypeRamp="title3">
Title 3
</Text>
<Text style={{fontSize: 17}} dynamicTypeRamp="headline">
Headline
</Text>
<Text style={{fontSize: 17}} dynamicTypeRamp="body">
Body
</Text>
<Text style={{fontSize: 16}} dynamicTypeRamp="callout">
Callout
</Text>
<Text style={{fontSize: 15}} dynamicTypeRamp="subheadline">
Subheadline
</Text>
<Text style={{fontSize: 13}} dynamicTypeRamp="footnote">
Footnote
</Text>
<Text style={{fontSize: 12}} dynamicTypeRamp="caption1">
Caption
</Text>
<Text style={{fontSize: 11}} dynamicTypeRamp="caption2">
Caption 2
</Text>
</View>
<View style={boxStyle}>
<Text style={boldStyle}>Without `dynamicTypeRamp`:</Text>
@@ -1450,7 +1524,13 @@ const examples = [
<Text style={{fontSize: 28}}>Title</Text>
<Text style={{fontSize: 22}}>Title 2</Text>
<Text style={{fontSize: 20}}>Title 3</Text>
<Text style={{fontSize: 17}}>Headline</Text>
<Text style={{fontSize: 17}}>Body</Text>
<Text style={{fontSize: 16}}>Callout</Text>
<Text style={{fontSize: 15}}>Subheadline</Text>
<Text style={{fontSize: 13}}>Footnote</Text>
<Text style={{fontSize: 12}}>Caption</Text>
<Text style={{fontSize: 11}}>Caption 2</Text>
</View>
</View>
);
@@ -305,6 +305,59 @@ const examples: Array<RNTesterModuleExample> = [
);
},
},
{
title: 'allowFontScaling attribute',
render: function (): React.Node {
return (
<View>
<Text>
By default, text will respect Text Size accessibility setting on
Android. It means that all font sizes will be increased or decreased
depending on the value of the Text Size setting in the OS's Settings
app.
</Text>
<ExampleTextInput
placeholder="allowFontScaling = false"
allowFontScaling={false}
/>
<ExampleTextInput
style={{marginTop: 10}}
placeholder="allowFontScaling = false"
allowFontScaling={true}
/>
</View>
);
},
},
{
title: 'maxFontSizeMultiplier attribute',
name: 'maxFontSizeMultiplier',
render(): React.Node {
return (
<View testID={'max-font-size-multiplier'}>
<Text>
When allowFontScaling is enabled, you can use the
maxFontSizeMultiplier prop to set an upper limit on how much the
font size will be scaled.
</Text>
<ExampleTextInput
allowFontScaling={true}
maxFontSizeMultiplier={1}
placeholder="This text will not scale up (max 1x)"
style={{marginTop: 10}}
testID={'non-scalable-text-input'}
/>
<ExampleTextInput
allowFontScaling={true}
maxFontSizeMultiplier={1.5}
placeholder="This text will scale up (max 1.5x)"
style={{marginTop: 10}}
testID={'scalable-text-input'}
/>
</View>
);
},
},
{
title: 'Text Auto Complete',
render: function (): React.Node {
@@ -730,6 +730,61 @@ const textInputExamples: Array<RNTesterModuleExample> = [
);
},
},
{
title: 'allowFontScaling attribute',
render: function (): React.Node {
return (
<View>
<Text>
By default, text will respect Text Size accessibility setting on
iOS. It means that all font sizes will be increased or decreased
depending on the value of Text Size setting in{' '}
<Text style={{fontWeight: 'bold'}}>
Settings.app - Display & Brightness - Text Size
</Text>
</Text>
<ExampleTextInput
placeholder="allowFontScaling = false"
allowFontScaling={false}
/>
<ExampleTextInput
style={{marginTop: 10}}
placeholder="allowFontScaling = false"
allowFontScaling={true}
/>
</View>
);
},
},
{
title: 'maxFontSizeMultiplier attribute',
name: 'maxFontSizeMultiplier',
render(): React.Node {
return (
<View testID={'max-font-size-multiplier'}>
<Text>
When allowFontScaling is enabled, you can use the
maxFontSizeMultiplier prop to set an upper limit on how much the
font size will be scaled.
</Text>
<ExampleTextInput
allowFontScaling={true}
maxFontSizeMultiplier={1}
placeholder="This text will not scale up (max 1x)"
style={{marginTop: 10}}
testID={'non-scalable-text-input'}
/>
<ExampleTextInput
allowFontScaling={true}
maxFontSizeMultiplier={1.5}
placeholder="This text will scale up (max 1.5x)"
style={{marginTop: 10}}
testID={'scalable-text-input'}
/>
</View>
);
},
},
{
title: 'Auto-expanding',
render: function (): React.Node {
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/tester",
"version": "0.77.0-main",
"version": "0.78.0",
"private": true,
"description": "React Native tester app.",
"license": "MIT",
@@ -27,8 +27,8 @@
"e2e-test-ios": "./scripts/maestro-test-ios.sh"
},
"dependencies": {
"@react-native/oss-library-example": "0.77.0-main",
"@react-native/popup-menu-android": "0.77.0-main",
"@react-native/oss-library-example": "0.78.0",
"@react-native/popup-menu-android": "0.78.0",
"flow-enums-runtime": "^0.0.6",
"invariant": "^2.2.4",
"nullthrows": "^1.1.1"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/typescript-config",
"version": "0.77.0-main",
"version": "0.78.0",
"description": "Default TypeScript configuration for React Native apps",
"license": "MIT",
"repository": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@react-native/virtualized-lists",
"version": "0.77.0-main",
"version": "0.78.0",
"description": "Virtualized lists for React Native.",
"license": "MIT",
"repository": {
+52 -26
View File
@@ -85,40 +85,66 @@ async function testRNTesterIOS(
} version of RNTester iOS with the new Architecture enabled`,
);
// if everything succeeded so far, we can launch Metro and the app
// start the Metro server in a separate window
launchPackagerInSeparateWindow(pwd().toString());
// remember that for this to be successful
// you should have run bundle install once
// in your local setup
if (argv.hermes === true && ciArtifacts != null) {
const hermesURL = await ciArtifacts.artifactURLHermesDebug();
const hermesZipPath = path.join(ciArtifacts.baseTmpPath(), 'hermes.zip');
// download hermes source code from manifold
ciArtifacts.downloadArtifact(hermesURL, hermesZipPath);
// GHA zips by default the artifacts.
const outputFolder = path.join(ciArtifacts.baseTmpPath(), 'hermes');
exec(`rm -rf ${outputFolder}`);
exec(`unzip ${hermesZipPath} -d ${outputFolder}`);
const hermesPath = path.join(outputFolder, 'hermes-ios-Debug.tar.gz');
console.info(`Downloaded Hermes in ${hermesPath}`);
exec(
`HERMES_ENGINE_TARBALL_PATH=${hermesPath} RCT_NEW_ARCH_ENABLED=1 bundle exec pod install --ansi`,
if (ciArtifacts != null) {
const appOutputFolder = path.join(
ciArtifacts.baseTmpPath(),
'RNTester.app',
);
exec(`rm -rf ${appOutputFolder}`);
if (argv.hermes === true) {
// download hermes App
const hermesAppUrl = await ciArtifacts.artifactURLForHermesRNTesterApp();
const hermesAppZipPath = path.join(
ciArtifacts.baseTmpPath(),
'RNTesterAppHermes.zip',
);
ciArtifacts.downloadArtifact(hermesAppUrl, hermesAppZipPath);
exec(`unzip ${hermesAppZipPath} -d ${appOutputFolder}`);
} else {
// download JSC app
const hermesAppUrl = await ciArtifacts.artifactURLForJSCRNTesterApp();
const hermesAppZipPath = path.join(
ciArtifacts.baseTmpPath(),
'RNTesterAppJSC.zip',
);
ciArtifacts.downloadArtifact(hermesAppUrl, hermesAppZipPath);
exec(`unzip ${hermesAppZipPath} -d ${appOutputFolder}`);
}
// boot device
const bootedDevice = String(
exec('xcrun simctl list | grep "iPhone 16 Pro" | grep Booted', {
silent: true,
}),
).trim();
if (!bootedDevice || bootedDevice.length === 0) {
exec('xcrun simctl boot "iPhone 16 Pro"');
}
// install app on device
exec(`xcrun simctl install booted ${appOutputFolder}`);
// launch the app on iOS simulator
exec('xcrun simctl launch booted com.meta.RNTester.localDevelopment');
} else {
exec(
`USE_HERMES=${
argv.hermes === true ? 1 : 0
} CI=${onReleaseBranch.toString()} RCT_NEW_ARCH_ENABLED=1 bundle exec pod install --ansi`,
);
// launch the app on iOS simulator
exec(
'npx react-native run-ios --scheme RNTester --simulator "iPhone 15 Pro"',
);
}
// if everything succeeded so far, we can launch Metro and the app
// start the Metro server in a separate window
launchPackagerInSeparateWindow(pwd().toString());
// launch the app on iOS simulator
exec(
'npx react-native run-ios --scheme RNTester --simulator "iPhone 15 Pro"',
);
}
/**
@@ -201,7 +227,7 @@ async function testRNTesterAndroid(
* - @onReleaseBranch whether we are on a release branch or not
*/
async function testRNTester(
circleCIArtifacts /*:Unwrap<ReturnType<typeof setupGHAArtifacts>> */,
ciArtifacts /*:Unwrap<ReturnType<typeof setupGHAArtifacts>> */,
onReleaseBranch /*: boolean */,
) {
// FIXME: make sure that the commands retains colors
@@ -210,9 +236,9 @@ async function testRNTester(
pushd('packages/rn-tester');
if (argv.platform === 'ios') {
await testRNTesterIOS(circleCIArtifacts, onReleaseBranch);
await testRNTesterIOS(ciArtifacts, onReleaseBranch);
} else {
await testRNTesterAndroid(circleCIArtifacts);
await testRNTesterAndroid(ciArtifacts);
}
popd();
}
@@ -214,6 +214,14 @@ async function artifactURLForHermesRNTesterAPK(
return getArtifactURL('rntester-hermes-debug');
}
async function artifactURLForJSCRNTesterApp() /*: Promise<string> */ {
return getArtifactURL('RNTesterApp-NewArch-JSC-Debug');
}
async function artifactURLForHermesRNTesterApp() /*: Promise<string> */ {
return getArtifactURL('RNTesterApp-NewArch-Hermes-Debug');
}
async function artifactURLForMavenLocal() /*: Promise<string> */ {
return getArtifactURL('maven-local');
}
@@ -247,6 +255,8 @@ module.exports = {
downloadArtifact,
artifactURLForJSCRNTesterAPK,
artifactURLForHermesRNTesterAPK,
artifactURLForJSCRNTesterApp,
artifactURLForHermesRNTesterApp,
artifactURLForMavenLocal,
artifactURLHermesDebug,
artifactURLForReactNative,