Commit Graph
41402 Commits
Author SHA1 Message Date
Alex Hunt 526016d950 Support committing debugger-frontend sync under Git checkouts (#57997)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57997

`scripts/debugger-frontend/sync-and-build` with `--create-diff` only worked under fbsource, making commit creation (in particular the generated changes table) inconvenient outside of Meta.

This diff lifts commit creation functionality to both Git and Mercurial (`sl`), and makes writing a local commit the default behaviour.

**Changes**

- Synced `debugger-frontend` artifacts are now always committed, regardless of version control backend. Internal Mercurial behaviour is forked to a `fbsource-backend.fb.js` script.
- `--create-diff` is narrowed to draft Phabricator diff submission (fbsource only).
- The script now aborts if there are any working copy changes.

Changelog: [Internal]

Reviewed By: vzaidman

Differential Revision: D116031207

fbshipit-source-id: 8ef3ed465b91d8f71ffa44fd1cba16f28860e3a9
2026-08-18 07:09:53 -07:00
Alex Hunt 3eab03df4c Type Animated.Value.addListener callback (#57992)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57992

Addresses a type narrowness regression (Strict API vs legacy manual types), raised by user feedback:
https://github.com/react-native-community/discussions-and-proposals/discussions/1015#discussioncomment-18053943

Changelog:
[General][Fixed] - **Animated**: `Animated.Value.addListener()` types its callback payload as `{value: number}` rather than `any`

Reviewed By: vzaidman

Differential Revision: D116440622

fbshipit-source-id: b85c7d7fbd8e81cc2e953c9a6f60701f3991ba6e
2026-08-18 06:45:17 -07:00
Alex Hunt 3eb330a365 Type Animated.event() as an event handler instead of any (#57991)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57991

Addresses a type narrowness regression (Strict API vs legacy manual types), raised by user feedback:
https://github.com/react-native-community/discussions-and-proposals/discussions/1015#discussioncomment-18053943

Changelog:
[General][Fixed] - **Animated**: `Animated.event()` is typed as an event handler rather than `any`

Reviewed By: vzaidman

Differential Revision: D116440620

fbshipit-source-id: 4ba629c511fcf28981700d1bc841a4ce8dcf17f8
2026-08-18 06:45:17 -07:00
Pieter De Baets 2ce2c0794b Remove accidental ref callback return values (#57849)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57849

Ref callbacks commonly used expression bodies that returned assignment or collection method results. Use block bodies so these callbacks return `void`, matching React's ref contract.

Changelog:
[Internal] - Make callback refs return `void` explicitly.

Reviewed By: SamChou19815

Differential Revision: D115064073

fbshipit-source-id: ad8fcdb1a94421a700e958c6e4ae981bf350d1ed
2026-08-18 05:35:06 -07:00
Christian Falch 8ac3e2fa58 fix(swiftpm): fix two ways array build settings were mishandled (#57744)
Summary:
Two bugs in `addArrayStringValues`, which adds members to an array build setting (`HEADER_SEARCH_PATHS`, `OTHER_LDFLAGS`, `FRAMEWORK_SEARCH_PATHS`, `LD_RUNPATH_SEARCH_PATHS`). Both hit real projects; neither was visible from the existing fixture.

**1. A promoted scalar was never restored.** A setting that already exists as a scalar gets promoted to a `( … )` array, but that was recorded as a plain member-append — so `deinit` stripped the members and left the array shell plus its injected `"$(inherited)"` behind. Stock Xcode projects hit this: the app template sets `LD_RUNPATH_SEARCH_PATHS = "$(inherited) executable_path/Frameworks";` as a target-level scalar.

Fixed by pinning the pre-injection value in `.spm-injected.json` and restoring it in place. It is stored raw (a bare scalar's token runs to the `;`, carrying whitespace that must come back), recorded only if the merge actually changed the field, and not restored if the field is gone.

**2. A one-line array was corrupted.** The append anchored on `lastIndexOf('\n', tokenEnd - 1)`, which assumes multi-line. With no newline in the value that lands on the *previous* line, so members were spliced above the field, outside the array:

```
{
		"/new",                             ← bare entry in the dict body: invalid pbxproj
	HEADER_SEARCH_PATHS = ("/vendor", );    ← member never added
}
```

The result is a project Xcode cannot open, and `deinit` could not remove the stray line. Xcode writes multi-line arrays, but hand-edited projects and other generators (XcodeGen, Tuist) emit compact ones. Fixed by splicing inline ahead of the `)`; removal gained matching delimiter-anchored patterns, so the span removed is the span inserted. The dedupe parse was also quote-blind — a member holding a quoted comma parsed as two tokens — and is now quote-aware.

**Tradeoff:** reversing a promotion rewrites the whole field, so members hand-added to a promoted array afterwards are lost.

**Rebase note:** `main` has since grown an overlapping guard (`buildSettingValueTokens`) that skips the append when every value is already present, avoiding the *no-op* promotion. It is kept and complements this change: main still promotes irreversibly when there *is* a fresh value to add to a scalar, which is what the restore here covers. The two records stay mutually exclusive per key, pinned by a test.

## Changelog:

[Internal] [Fixed] - SwiftPM: `spm deinit` restores a promoted scalar build setting, and `spm add` no longer corrupts a one-line array

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

Test Plan:
`yarn jest packages/react-native/scripts` → **963 tests, 32 suites** green; eslint, prettier and flow clean.

Written red first: byte-identical `add` → `deinit` round-trips for each pre-existing shape (absent, multi-line, bare and quoted scalars, and the one-line forms), plus `add` → `update` → `deinit`. The multi-line path is unchanged byte-for-byte, verified by a differential harness over 48 add/remove cases against the previous implementation.

Re-verified after the rebase on the committed `HelloWorld.xcodeproj`, driving the real `injectSpmIntoExistingXcodeproj` / `removeSpmInjection`. On `main` a one-line `HEADER_SEARCH_PATHS` gains a bare `"…/autolinking/headers",` entry above the field and never receives the member; with this change it lands inside the array, and a pre-existing scalar comes back exactly.

Reviewed By: fabriziocucci

Differential Revision: D114317839

Pulled By: cipolleschi

fbshipit-source-id: 08bc81f80855721bf2123143063462109a99ac25
2026-08-18 05:08:17 -07:00
Dawid Małecki 385fa84925 Add stable API guards to text and textinput components (#57363)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57363

This diff rolls umbrella guards for `react/renderer/components/text` and `react/renderer/components/textinput`.

Initially, we consider `text` component target as `public` and `textinput` as `for frameworks`, so the umbrella is added only to the `text` target.

This diff also adds missing `components/text/platform/android/` to the prefab as otherwise the Text umbrella cannot be properly resolved on Android.

Changelog:
[Internal]

Reviewed By: cipolleschi

Differential Revision: D109849283

fbshipit-source-id: 14fcab2a8ee437b94f7d131b8a373afc8b7d4f18
2026-08-18 05:03:03 -07:00
Alex Hunt 72e8874c13 Replace ECOSYSTEM.md with Foundation site link (#57949)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57949

Changelog: [Internal]

Reviewed By: vzaidman

Differential Revision: D115886097

fbshipit-source-id: 6ce920da1e2de8a7dbd26cb824abdbbc7186afe6
2026-08-18 03:52:45 -07:00
Alex Hunt 6660f4aa24 Refresh README (#57946)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57946

Refresh the root `README.md`. This is generally simplified and aligned closer with the React repo's headings/structure.

**Changes**

- **Rewrite intro** and add banner logo.
- Drop Contents, Requirements, and Upgrading sections.
- Extend set of website docs links.
- **Add inline starting guidance for Expo**.
- Drop emojis.
- Inline markdown links.
- Fix badge styling (underline bleed).
- Drop Bluesky badge.
- Add download count badge.
- Facebook → Meta.

Changelog: [Internal]

Reviewed By: vzaidman

Differential Revision: D115879599

fbshipit-source-id: de5a254ac2724751d6527badf0b851bd4d2b73dc
2026-08-18 03:52:45 -07:00
Dawid Malecki 5e11ed122a Add guards for react/renderer/components/view (#57357) (#57357) (#57357)
Summary:
Roll umbrella with umbrella guards for headers under `react/renderer/components/view` subtree. Initially, we assume that the entire target is public, so each header within includes a public umbrella guard.

Changelog:
[Internal]

Pulled By:
coado

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

coado

Test Plan:
Signals,

Test locally in `rn-tester` by building on iOS (cocoapods and swiftPM) and Android with `RN_STRICT_API` turned on. Then directly include guarded headers in `NativeCxxModuleExample.h` to see if build fails and then change to umbrella include to see if it passes.

Reviewed By: cipolleschi

Differential Revision: D109847184

fbshipit-source-id: 852c0213313f45d9a7c6ac3c94ef7cc2166608af
2026-08-18 03:16:02 -07:00
Kamil Paradowski 11f9a7f449 RCTArrayBuffer zero-copy class for ObjC TM (#57879)
Summary:
iOS TurboModules mapped a JS `ArrayBuffer` to `NSData` on arguments and `NSMutableData` on returns, so every crossing copied — and `NSMutableData` cannot alias foreign memory, so there was no way to express "these bytes live somewhere else".

This adds `RCTArrayBuffer` (`packages/react-native/React/Base/`) as the ObjC representation of an `ArrayBuffer`. It carries an `isOwningBytes` flag: an owning buffer can be stored and read from any thread, a non-owning one aliases bytes valid only for the synchronous call that produced it.

Codegen now emits `RCTArrayBuffer *` for `ArrayBufferTypeAnnotation` params (was `NSData *`) and returns (was `NSMutableData *`).

## Changelog:

[IOS] [BREAKING] - Add `RCTArrayBuffer`, the ObjC representation of a JS `ArrayBuffer` for TurboModules, with an explicit byte-ownership contract

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

Test Plan:
- `RCTTurboModuleArrayBufferTests` — 9 tests over the sync in-place path, `isOwningBytes` on a sync argument, returning one's own argument, the void/Promise copy paths, nesting, and a zero-length round trip.
 - `RCTTurboModuleTests.mm` adds `testNativeBackedArrayBufferIsAliasedAndKeepsBackingStoreAlive`.
 -  `RCTSampleTurboModule` doubles its argument in place and returns the same buffer, covering the path end to end.
 - Codegen and C++ API snapshots regenerated.

Reviewed By: cipolleschi

Differential Revision: D115629409

Pulled By: christophpurrer

fbshipit-source-id: a7ee6eadf99fd5f9f82fd2a95b0d3ff1369f4ddf
2026-08-17 17:57:28 -07:00
Christoph Purrer d84c13d511 Enforce the ArrayBuffer borrow contract for Java TurboModules (#57982)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57982

Changelog: [ANDROID][FIXED]  Enforce the ArrayBuffer borrow contract for Java TurboModules

Follow-up hardening for the Android `ArrayBuffer` TurboModule type. Three problems:

**1. Borrowed JS-heap bytes outlived the call that lent them.** For a synchronous
method, `convertJSIArgsToJNIArgs` hands the module a `ByteBuffer` aliasing the JS
`ArrayBuffer`'s bytes without copying. Nothing stopped a module from stashing that
`ArrayBuffer` in a field and reading it later, after the JS heap may have moved,
freed, or reused the memory — a use-after-free that reads as intermittent data
corruption rather than a crash.

The borrow is now explicitly scoped to the call frame. `JNIArgs` records every
borrowed `ArrayBuffer` and revokes it in its destructor — including when the call
throws — via the new `JArrayBuffer::invalidate`, which drops the C++ side's
reference to the bytes. `ArrayBuffer.bytes` and `ArrayBuffer.size` then throw,
with a message pointing at `ArrayBuffer.arrayBufferWithCopiedBytes`, and
`JArrayBuffer::toJSBuffer` throws rather than aliasing revoked memory. Modules
that need the bytes past the call copy them; modules that don't keep the
zero-copy fast path.

Revocation lives entirely on the C++ side: the peer is the single source of
truth, and Kotlin asks it through `isBytesValid`. The destructor runs while the
stack unwinds, possibly with a Java exception pending, so it resolves each peer
pointer at borrow time — the `global_ref` alongside it keeps the Java object, and
therefore the peer, alive — and calls only the `noexcept`
`JArrayBuffer::invalidate`. No JNI calls are made from the destructor, which is
what lets it stay `noexcept` honestly.

**2. Argument conversion aborted under runtimes that refuse `tryGetMutableBuffer`.**
`jsi::Runtime::tryGetMutableBuffer` and `detached` are not universally
implemented: tracing and replay runtimes throw from `tryGetMutableBuffer`, and
`detached` throws a `JSINativeException` if the JS-side property isn't a bool.
`ArrayBuffer` argument conversion is not wrapped in a try/catch, so either throw
propagated out of a JNI frame. Both calls now go through exception-tolerant
helpers in `react/bridging/ArrayBuffer.h`; a runtime that refuses to answer is
treated as "no native buffer available", which selects the copy path. Routing
`AsyncArrayBuffer::acquire` and `::borrow` through the same helper fixes the
identical latent bug on the shared C++/ObjC path.

**3. A wrong return type from a module crashed instead of raising a JS error.**
The `ArrayBufferKind` return path cast the returned `jobject` to `JArrayBuffer`
unconditionally. A module returning any other object type produced undefined
behavior. The cast is now guarded by an `isInstanceOf` check that throws a
`jsi::JSError` naming the offending module and method.

Also in this change:
- `JByteBufferMutableBuffer::data()` reports null for a zero-capacity direct
  buffer instead of calling `getDirectBytes()`, which throws for one. That made
  `createArrayBuffer` throw for an empty `ArrayBuffer`.
- Dropped two dead zero-size branches in `JArrayBuffer`: `JByteBuffer::wrapBytes`
  already routes `size == 0` to an empty buffer.
- `JArrayBuffer.cpp` reuses the shared `detail::OwnedBytesBuffer` from
  `react/bridging/ArrayBuffer.h` instead of a second local copy.
- `ArrayBuffer.kt` KDoc corrected: the returned JS `ArrayBuffer` is a new object
  over the same bytes rather than the identical one, `size` is the capacity and
  not a view's remaining bytes, and `arrayBufferWithOwnedBytes` documents the
  caller's lifetime obligation.
- `ArrayBuffer.kt` moves from the `bridge` target to `native-types`, alongside the
  other JNI-backed bridge types.

Changelog:
[Android][Breaking] - TurboModule methods taking or returning an `ArrayBuffer`
now use `com.facebook.react.bridge.ArrayBuffer` instead of
`java.nio.ByteBuffer`, and an `ArrayBuffer` argument must not be retained past
the method that receives it unless its bytes are copied with
`ArrayBuffer.arrayBufferWithCopiedBytes()`.

Reviewed By: javache

Differential Revision: D115794808

fbshipit-source-id: 26f5d863469cc14a3f1bffc2cbc3302f3e983ecb
2026-08-17 17:57:28 -07:00
Kamil Paradowski 5bb9639594 JArrayBuffer zero-copy class for Java TM (#57897)
Summary:
Android TurboModules mapped a JS `ArrayBuffer` to `java.nio.ByteBuffer`, copying every argument into a direct buffer — and `ByteBuffer` carries no ownership contract, so there was no way to express aliased or borrowed bytes for synchronous in-place access.

This adds `ArrayBuffer` (`packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ArrayBuffer.kt`) as the Java representation of an `ArrayBuffer`. It carries an `isOwningBytes` flag: an owning buffer can be stored and returned to JS, a non-owning one aliases bytes valid only for the synchronous call that produced it.

Codegen now emits `ArrayBuffer` for `ArrayBufferTypeAnnotation` params (was `ByteBuffer`) and returns (was `ByteBuffer`).

## Changelog:

[ANDROID] [ADDED] - Add `ArrayBuffer`, the Java representation of a JS `ArrayBuffer` for TurboModules, with an explicit byte-ownership contract

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

Test Plan:
- Codegen Java spec and JNI C++ snapshot tests updated for `ArrayBuffer` param/return signatures.
- `SampleTurboModule` doubles its sync argument in place and returns the same buffer, covering the zero-copy path end to end; `createNativeBuffer` allocates via `ArrayBuffer`.
- C++ API snapshots regenerated.

Reviewed By: javache

Differential Revision: D115755247

Pulled By: christophpurrer

fbshipit-source-id: de067789ad145b7202da721a02f838358c82f4d8
2026-08-17 15:48:23 -07:00
Christoph Purrer 4bf5575490 Make codegen'd TurboModule event emitters no-op when the emitter callback is absent (#57893)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57893

The codegen'd TurboModule `emitOn<Event>` methods invoked their `EventEmitterCallback` without checking it was set. That callback is installed by the generated `*SpecJSI` constructor, which runs when JS first looks the module up — so a native module that emits before that point invoked an empty `std::function` on iOS (`std::bad_function_call`) or a null field on Android (NPE). Native code commonly holds the module instance and pushes events well before any JS surface mounts, so call sites had to wrap every emit in a try/catch to stay crash-free.

Both generators now read the callback into a local and no-op when it is absent:

- ObjC++ (`serializeEventEmitter.js`): copies `_eventEmitterCallback`, calls it only if non-empty.
- Java (`GenerateModuleJavaSpec.js`): copies the `Nullable CxxCallbackImpl` field and null-checks it.

The local is for readability, not synchronization: the callback is installed once and never cleared, and Java reference reads are already atomic.

The `setEventEmitterCallback` lambdas had a separate lifetime bug: they captured `eventEmitterMap_` by reference and looked events up with `operator[]`. The Java/ObjC module owns the callback and can outlive the C++ `*SpecJSI` that installed it, so a stale callback dereferenced a dangling map; and `operator[]` silently default-inserted a null `shared_ptr` for an unknown event name, which the next line dereferenced. They now capture a copy of the map and use a checked `find` (`serializeModule.js`, `JavaTurboModule.cpp`). Every emitter is registered before the callback is installed, so the copy is complete.

Note the scope of the guarantee: it covers the ObjC++ and Java generators, which route through an `EventEmitterCallback`. A C++-only TurboModule (`<Module>CxxSpec`, from `GenerateModuleH.js`) has no callback to check — it emits through `eventEmitterMap_` entries its own constructor registers — so the "emit unconditionally" guidance is about the callback, not about emitting before construction finishes.

Changelog:
[General][Fixed] - TurboModule event emitters no longer throw when an event is emitted before the emitter callback is installed

Reviewed By: javache

Differential Revision: D115130767

fbshipit-source-id: 7d7b9a706b8983968e3e1207b22c66322f04a223
2026-08-17 10:41:02 -07:00
Gijs Weterings c467843ed0 Fix DOMHighResTimeStamp round-trip truncation in timing primitives (#57975)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57975

`HighResDuration::fromDOMHighResTimeStamp` and `HighResTimeStamp::fromDOMHighResTimeStamp` converted milliseconds back to nanoseconds with `static_cast<int64_t>(units * 1e6)`, which truncates toward zero.

`toDOMHighResTimeStamp()` divides the nanosecond count by 1e6 (one rounding) and multiplying back by 1e6 rounds again, so the product frequently lands a hair below the original integer (e.g. `537648854729249.97`). Truncation then chops off a whole nanosecond, so `fromDOMHighResTimeStamp(toDOMHighResTimeStamp(x)) != x` for roughly 2% of random `now()` values.

That is the source of an intermittent `BridgingTest/highResTimeStampTest` failure, which reported:

```
Expected equality of these values:
  timestamp
    Which is: 8-byte object <22-02 00-21 FD-E8 01-00>
  bridging::fromJs<HighResTimeStamp>( rt, bridging::toJs(rt, timestamp), invoker)
    Which is: 8-byte object <21-02 00-21 FD-E8 01-00>
```

Round to the nearest nanosecond instead of truncating. Nanosecond values below 2^53 are exactly representable in a double, so the round trip is now exact for every value below 2.25e15 ns (26 days of monotonic clock); beyond that the double's ULP exceeds 0.5 ns and residual error is at most 2 ns, which is a floor of the DOM representation itself.

Rounding is also the correct semantic independently of the round trip — truncation gives a systematic downward bias and is asymmetric across zero, which affects the other callers (`RCTHighResTimeStampFromSeconds` for touch timestamps, `RuntimeTargetConsole` `console.timeStamp`, and `PerformanceTracer`).

The rounding is `std::llround`, which costs both overloads their `constexpr`: `<cmath>` rounding functions do not become usable in a constant expression until C++23 (P0533), and this header is compiled as C++20 everywhere (`-std=c++20` in `rn_defs.bzl`, `react-native-flags.cmake`, the podspecs and `Package.swift`). Both carry a `TODO` to restore `constexpr` once the C++23 rollout reaches them.

Dropping it is safe here. All 14 call sites are plain runtime calls — none is in a constant-expression context, none assigns to a `constexpr` variable or feeds a `static_assert` — and both functions are defined inside the class body, so they stay implicitly `inline` and neither linkage nor ABI changes. `fromDOMHighResTimeStamp` converts a `double` arriving from JS, so constant-evaluating it was never meaningful in the first place.

This does narrow the published API surface, so the C++ API snapshots are regenerated: the only change across all nine `.api` files is the `constexpr` keyword dropping off these two declarations, 18 lines in total.

`HighResTimeStamp::fromDOMHighResTimeStamp` now delegates to `HighResDuration`'s so there is a single implementation.

`highResTimeStampTest` previously asserted on `HighResTimeStamp::now()`, whose magnitude is host-uptime-dependent, so it only tripped the bug on about 2% of runs. It now round-trips five fixed nanosecond values (including the exact value from the failing run), which exercises the bug on every run. No test was skipped, disabled, or loosened.

Changelog:
[General][Fixed] - Round instead of truncate when converting a `DOMHighResTimeStamp` back to nanoseconds, so `HighResTimeStamp` and `HighResDuration` round trips are exact

Reviewed By: javache

Differential Revision: D116286868

fbshipit-source-id: 1adafd1b57b1185f51f08fa4dc55d96b88c176cd
2026-08-17 09:21:41 -07:00
Pieter De Baets 88820254e5 Align ref helper flow types (#57850)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57850

React ref callbacks may return only `void` or a cleanup function. Align `TRefCallbackFor` and the related shared callback-ref helpers directly with the core Flow React ref types, then update consumers whose callbacks returned incompatible values. This keeps the contracts canonical and avoids introducing a parallel restricted variant.

Changelog:
[Internal] - Align ref helper types with the React callback contract.

Reviewed By: SamChou19815

Differential Revision: D115064072

fbshipit-source-id: b308ac2b9a453b494c64bdf3d37873b69040ce4c
2026-08-17 08:43:33 -07:00
Gijs Weterings 37dd4044fd Run the ReactAndroid JNI gtests as instrumentation tests (#57976)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57976

The `ReactAndroid` JNI gtests in `react/fabric/test` and `react/jni/test` were configured as plain host C++ tests, but they link against the Android-only JNI libraries. The resulting binary is an Android aarch64 ELF that needs `/system/bin/linker64` and so cannot run on a Linux host:

```
qemu-aarch64: Could not open '/system/bin/linker64': No such file or directory
Test failed to produce the expected output! ... IO error: No result xml files found.
```

They were not failing their assertions — they were never executing. The runner enumerated case names statically out of the binary without running it, so all 6 cases across the two targets were known and reported as failing, with no stack traces at all. The absence of stack traces was itself the tell: the process never started, so no gtest XML was ever produced.

Both targets move to the established pattern for gtests against Android-only native libs: build them as Android instrumentation tests, packaged into an APK and run on a device or emulator.

`FabricMountingManagerTest` can drop its deliberate leak as a result. It previously allocated the manager with a no-op deleter, because `~FabricMountingManager()` calls `jni::ThreadScope::WithClassLoader`, which throws without an attached `JavaVM`. Under instrumentation the gtest runs inside a native method registered via fbjni `makeNativeMethod`, so `cachedOrNull()` is non-null and the closure runs inline — destruction is safe. The fixture now holds a default-constructed (null) `jni::global_ref` member and returns a `std::unique_ptr`; releasing a null `global_ref` is a no-op. All four test bodies are byte-identical. `ModuleRegistryBuilderTest.cpp` is a comment-only correction.

Changelog: [Internal]

Differential Revision: D116286866

fbshipit-source-id: a283deae18e42b0caad330bd3a27307428cfcec4
2026-08-17 05:44:56 -07:00
Rohan Kulkarni 5cb65244dc Fix maintainVisibleContentPosition with rapid data updates (#53542) (#57955)
Summary:
Fixes https://github.com/react/react-native/issues/53542

**Problem:** `maintainVisibleContentPosition` fails when FlatList data is updated rapidly with prepends in quick succession (e.g., chat receiving messages). After 2 prepends before native scroll drains, render window stays frozen, `onViewableItemsChanged` suppressed, `onEndReached` never fires.

**Root cause:** `pendingScrollUpdateCount` assumption is structurally unsound – it increments by 1 per prepend in `getDerivedStateFromProps` (0→1→2) but native scroll events are dispatched as unique events that coalesce. Verified in C++:
- `ScrollViewEventEmitter.cpp:13` – `onScroll` dispatched with `dispatchUniqueEvent("scroll", ...)`
- `EventQueue.cpp:29-50` – unique event whose target+type matches existing replaces in place (line 49) instead of appending
So N scroll events emitted before queue flush deliver exactly 1 to JS. Single coalesced event drains 2→1 leaving blocked. Also leak when JS predicate fires (old key found at new index) but native declines to adjust (tag recycled, view deleted, delta ≤0.5, clamped).

**Fix (1 file, 2 lines core):** Clamp pending to at most 1 and drain to 0 on any scroll:
- `pendingScrollUpdateCount: 1` instead of `prev+1` – prevents accumulation during rapid prepends
- `setState({pendingScrollUpdateCount: 0})` instead of `-1` – single coalesced scroll unblocks

This turns "stuck at N" into unblocked after next scroll, fixing frozen window / viewability / onEndReached for rapid updates. For leak path where native declines (no scroll ever), flag would still be stranded at 1 – addressed by boolean rename + escape hatch in follow-up, but this PR already strictly improves and matches existing tests that drain 0→1→0.

## Changelog:
[GENERAL] [FIXED] - Fix maintainVisibleContentPosition with rapid data updates (https://github.com/react/react-native/issues/53542)

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

Test Plan:
**Jest (VirtualizedList):**
```bash
yarn jest --watchman=false packages/virtualized-lists/Lists/__tests__/VirtualizedList-test.js --no-coverage --ci
# Before: new coalesced test fails with pending 1
# After: 83 passed (82 existing + 1 new rapid prepends with coalesced scroll), 1 skipped, 59 snapshots
```

**New test ( regression for https://github.com/react/react-native/issues/53542 ):**
- Simulates 2 rapid prepends WITHOUT intermediate scroll (5 + 3 items)
- Only 1 coalesced scroll event (delta 8*ITEM_HEIGHT)
- Asserts `pendingScrollUpdateCount === 0` (fails on main with 1) and `firstVisibleItemKey` not null

**Existing MVCP tests still pass:**
- `handles maintainVisibleContentPosition`
- `handles multiple rapid prepends` (separate scroll per prepend)
- `delta stays bounded`
- `minIndexForVisible >0` and `inverted`

**Lint:**
```bash
yarn lint
# Done (max-warnings 0)
```

Closes https://github.com/react/react-native/issues/53542

Reviewed By: javache

Differential Revision: D115907610

Pulled By: fabriziocucci

fbshipit-source-id: 0cd59b35d7c43803a000b93779cd84c9c4202053
2026-08-17 04:55:47 -07:00
Rob Hogan f63b2a1cb5 Babel preset: Make Platform inlining opt-in via inlinePlatform (default on via @react-native/metro-babel-transformer) (#57973)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57973

`react-native/babel-preset` inlines `Platform.OS` and `Platform.select(...)` whenever it is handed a `platform` (since yesterday - https://github.com/react/react-native/pull/57848 ), but this is over-zealous - `platform` is a pre-existing transform option whose intent is to set the transform target. In some cases (eg, to allow `Platform` mocking from tests), we don't want `Platform` inlining, even though `platform` may already be passed for other reasons.

Gate the inlining behind a separate `inlinePlatform` opt-in, resolved as `options.inlinePlatform ?? babel.caller(...) ?? false`, mirroring how `platform` and `unstable_transformProfile` are already resolved. Metro already models this as a distinct transform option, so both Babel transformers pass it straight through. The caller channel covers the case where the preset is named in a `babel.config.js` and so receives no preset options at all.

Bundling is unaffected: Metro sets `inlinePlatform` on every transform, so `Platform` continues to be inlined through `react-native/metro-babel-transformer`.

Changelog:
[General][Changed] - `Platform.OS` and `Platform.select(...)` inlining in `react-native/babel-preset` now requires the `inlinePlatform` option in addition to `platform`

Reviewed By: GijsWeterings

Differential Revision: D116281656

fbshipit-source-id: 0230cd74f38661862396391da62cf81b34773b85
2026-08-17 04:38:28 -07:00
Evan Katz 376b99ff14 Accept object font variation settings (#57929)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57929

Allow `fontVariationSettings` to accept either the existing CSS-compatible
string or an object keyed by four-character OpenType axis tags. Normalize the
object at the shared style boundary so Text and TextInput retain the existing
native string representation on every platform.

Object serialization is deterministic, supports fractional finite values, and
preserves the current unset versus explicit-clear behavior.

Changelog:
[General][Added] - Add object syntax for `fontVariationSettings`

Reviewed By: Abbondanzo

Differential Revision: D115640672

fbshipit-source-id: e2dbb2498ec213686a3547c8f74c2ade417ef88a
2026-08-16 16:48:16 -07:00
Rob Hogan 40c06121a1 Inline Platform within RN’s Babel preset (#57848)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57848

Adds a React Native-owned Babel plugin that inlines `Platform.OS` and
`Platform.select(...)`, from
`react-native/babel-preset`  at the front of the preset's plugin list (importantly, before
the preset lowers ES modules to CommonJS).

Metro already inlines `Platform`, but its matcher (`metro-transform-plugins`'
`inline-plugin` via `createInlinePlatformChecks`) runs *after* ESM has been
lowered to CommonJS. By then a genuine `import {Platform} from 'react-native'`
has become `_reactNative.Platform.OS` and provenance is obscured and fragile to track. Metro compensates by matching on the local
identifier spelling - so `Platform.OS` inlines whether or not `Platform` is
actually React Native's, and a genuine deep or RN-relative import may be missed entirely.

`Platform` inlining is in any case much more of an RN concern than a Metro one - it has to be aware of RN APIs and module names, and even the module’s path.

This plugin recognises:

- `import {Platform} from 'react-native'` (including aliased imports)
- `import * as RN from 'react-native'`, uses of `RN.Platform`
- `import P from 'react-native/Libraries/Utilities/Platform'`
- the equivalent `require` forms, plus destructuring and immutable aliases
- RN's internal relative imports, e.g. `../../Utilities/Platform`

…and deliberately does *not* recognise anything it cannot prove - a bare
`Platform.OS` global, `React.Platform.OS`, or a same-named import from another
package.

Metro's late matcher is left fully enabled and remains responsible for
those historical forms, so this change is purely additive: it inlines genuine
imports Metro was missing, and changes nothing Metro already handled. Metro’s plugin will be deprecated and removed in future.

Notable details:

- Relative RN imports are resolved lexically, with no filesystem access and no
  platform-extension resolution — the identity we need is the extension-less
  module `<rn-root>/Libraries/Utilities/Platform`, and Metro picks
  `Platform.ios.js` / `Platform.android.js` later. The RN package root is
  identified by directory name, which covers `node_modules/react-native`,
  `packages/react-native` and pnpm layouts alike, and rejects
  `react-native-web` and friends (who would be expected to adjust the paths in their forks)
- Imports are left in place. Removing them would change dependency collection,
  so it is handled separately.
- A no-platform build (`null`, or the empty string that RN's Jest preprocessor
  passes) is a no-op — inlining `Platform.OS` to `""` would be wrong.
- The plugin source is added to the preset's `getCacheKey` so edits invalidate Metro's transform cache.

Changelog:
[General][Changed] - Inline `Platform.OS` and `Platform.select(...)` for
React Native `Platform` imports during the Babel preset, covering some cases that were previously left un-inlined.

Reviewed By: huntie

Differential Revision: D114647943

fbshipit-source-id: b44f38bd22970abba6e7d75c00fdbb27159c17a6
2026-08-16 06:36:58 -07:00
Alex Hunt d07bd6f83a Promote fuseboxFrameRecordingEnabled to canary (#57953)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57953

Changelog: [Internal]

Reviewed By: hoxyq

Differential Revision: D111901168

fbshipit-source-id: 90f8b0432fbec5b144fa4e0fa9e7bd4ad12ce445
2026-08-14 07:54:22 -07:00
Alex Hunt b74adf051f Add idle frame support to Performance timeline (#57952)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57952

Implement Idle frame spans in React Native DevTools, by emitting synthetic `NeedsBeginFrameChanged` + `BeginFrame` events.

**Definition**

An idle frame is emitted whenever the gap between two consecutive frames exceeds one vsync interval, derived from the display's refresh rate (`CADisplayLink.duration` on iOS, `Display.refreshRate` on Android, falling back to 60 Hz).

```
   frame N                  gap > 1 vsync                 frame N+1
+------------+  +--------------------------------+  +------------+
| BeginFrame |  | NeedsBeginFrameChanged         |  | BeginFrame |
| DrawFrame  |  | BeginFrame     (no DrawFrame)  |  | DrawFrame  |
+------------+  +--------------------------------+  +------------+
                    rendered as an "Idle frame"
```

**Implementation notes**

- Drop frames that begin before the recording start — Android `FrameMetrics` can deliver frames from app startup, and the first iOS `CADisplayLink` callback reports the previous vsync.
- Sort frames by begin timestamp before serializing, since async screenshot encoding can deliver them out of order and break gap detection.
- iOS: skip the frame event when the screenshot is unchanged, letting the gap render as an idle frame.

Changelog: [Internal]

Reviewed By: hoxyq

Differential Revision: D97502569

fbshipit-source-id: 7cb19e0a462ad878f0b44b66f6b5de3f76426ffe
2026-08-14 07:54:22 -07:00
Nycollas ead93778a1 Fix Hermes bytecode version mismatch in SwiftPM Release builds (#57928)
Summary:
`react-native spm add`'s Release builds crash on launch with:

```
Compiling JS failed: Wrong bytecode version. Expected 99 but got 98
```

This happens because the SwiftPM integration resolves the Hermes **runtime**
(the downloaded `hermes-engine.xcframework`) and the Hermes **compiler** (the
`hermesc` binary that turns the JS bundle into bytecode) from two independent,
unsynced sources:

- `download-spm-artifacts.js`'s `resolveHermesArtifact()` picked the runtime
  by querying the `hermes-compiler` package's `latest-v1` dist-tag on the npm
  registry **live, at build time**.
- `generate-spm-xcodeproj.js`'s `resolveHermesCliPathSetting()` (and
  `react-native-xcode.sh`, for the CocoaPods-free fallback) points
  `HERMES_CLI_PATH` at the `hermes-compiler` package **already installed in
  this project's own `node_modules`** — whatever got pinned the last time
  `npm install` ran.

If the `latest-v1` dist-tag advances on npm between `npm install` and the
Release build (which happens routinely as new Hermes builds are published),
the downloaded VM and the locally pinned `hermesc` fall out of sync and the
app crashes at launch. `react-native-xcode.sh` already documents this exact
invariant ("react native pins the hermes-compiler version, so the compiler's
bytecode version always matches the prebuilt hermes VM artifacts") — SwiftPM's
artifact download just wasn't honoring it.

This PR makes `resolveHermesArtifact()` read the pinned `hermes-compiler`
version from `node_modules` first (the same `require.resolve` lookup already
used for `HERMES_CLI_PATH`), so the runtime download and the compiler always
agree. It falls back to the previous `latest-v1` npm lookup only when
`hermes-compiler` isn't locally resolvable (e.g. `USE_HERMES=false` apps that
never installed it). Explicit `HERMES_VERSION` overrides (`nightly`,
`latest-v1`, a literal version) are unchanged.

Fixes https://github.com/react/react-native/issues/57917.

## Changelog:

[IOS] [FIXED] - Fix Hermes runtime/compiler version mismatch causing "Wrong bytecode version" crashes in SwiftPM Release builds

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

Test Plan:
Added unit tests covering the new local-resolution path, the fallback when
`hermes-compiler` isn't installed, and confirming existing `HERMES_VERSION`
overrides still take precedence over the local pin.

```
$ node_modules/.bin/jest packages/react-native/scripts/spm/__tests__/download-spm-artifacts-test.js
Test Suites: 1 passed, 1 total
Tests:       70 passed, 70 total

$ node_modules/.bin/flow check packages/react-native/scripts/spm/download-spm-artifacts.js
No errors!

$ node_modules/.bin/eslint packages/react-native/scripts/spm/download-spm-artifacts.js packages/react-native/scripts/spm/__tests__/download-spm-artifacts-test.js
(no output — clean)

$ node_modules/.bin/prettier --check packages/react-native/scripts/spm/download-spm-artifacts.js packages/react-native/scripts/spm/__tests__/download-spm-artifacts-test.js
All matched files use Prettier code style!
```

Reproduced the crash and confirmed the fix end-to-end using the public
reproducer linked from the issue
(https://github.com/marandaneto/react-native-087-swiftpm-hermes-bytecode-repro):

- Before the fix: `npm run reproduce` builds successfully but launching the
  app in the iOS Simulator crashes with `Compiling JS failed: Wrong bytecode
  version. Expected 99 but got 98`.
- After applying the equivalent fix to the reproducer's installed
  `react-native` copy: the log shows `Using locally pinned hermes-compiler:
  250829098.0.16`, and both the debug and release Hermes runtime artifacts
  resolve to that exact version — matching the `hermesc` used for
  `HERMES_CLI_PATH`. `xcodebuild ... -configuration Release` succeeds, and
  the app installs and launches cleanly on an iPhone 17 Pro (iOS 26.5)
  simulator with no crash.

Reviewed By: cortinico

Differential Revision: D115859923

Pulled By: cipolleschi

fbshipit-source-id: 1b65a7aa28f502374a3553c1849b8ff29b5afd10
2026-08-14 07:12:17 -07:00
Sam Zhou 88043334e3 Remove unused babel-plugin-syntax-hermes-parser (#57958)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57958

Changelog: [Internal]

Reviewed By: gkz

Differential Revision: D115933669

fbshipit-source-id: beb16a462472effcdcc2f9aea3d1baae5fd075b8
2026-08-13 17:58:27 -07:00
Dongda Li 1291d7ce8e fix(ios): make the unmountChildComponentView assert message bounds-safe (#57865)
Summary:
`-[RCTViewComponentView unmountChildComponentView:index:]` bounds-checks the `RCTAssert` *condition*, but the failure message it formats afterwards calls `-objectAtIndex:` on the same out-of-range `index`:

```objc
RCTAssert(
    (self.currentContainerView.subviews.count > index) &&              // guarded
        [self.currentContainerView.subviews objectAtIndex:index] == childComponentView,
    @"... tag at index: %@)", ...,
    @([[self.currentContainerView.subviews objectAtIndex:index] tag]));  // not guarded
```

So the exact situation the assert exists to report — a shadow-tree/native child-index mismatch — raises `NSRangeException` *while being reported*, instead of being reported.

That is normally invisible, because a source Release build strips `RCTAssert` and the mismatch is harmless: `index` is used only inside the asserts, and the real work, `[childComponentView removeFromSuperview]`, never needed it. But the prebuilt `React.framework` published to Maven Central is built with assertions enabled (https://github.com/react/react-native/issues/57454), so this is live in App Store builds — we hit it on RN 0.81.5 via Expo SDK 54, symbolicated against the published `reactnative-core-dSYM-release` artifact:

```
NSRangeException: *** -[__NSArrayM objectAtIndex:]: index 13 beyond bounds [0 .. 8]
  -[RCTViewComponentView unmountChildComponentView:index:]  (RCTViewComponentView.mm:163)
  RCTPerformMountInstructions(...)                          (RCTMountingManager.mm:97)
  -[RCTMountingManager performTransaction:]                 (RCTMountingManager.mm:258)
  -[RCTMountingManager initiateTransaction:]                (RCTMountingManager.mm:247)
```

This is the second of the two fixes suggested in https://github.com/react/react-native/issues/57454 and stands on its own for any build with assertions enabled.

The change also reads `self.currentContainerView` once instead of four times. That getter is not a plain accessor — it creates or tears down `_containerView` and reparents subviews — so re-invoking it inside an assert's arguments is worth avoiding regardless.

The hoisted locals and the assert sit inside `#ifndef NS_BLOCK_ASSERTIONS`, matching the existing guard at `RCTViewComponentView.mm:335`. Without it the locals are unused once assertions are compiled out, which is a `-Werror,-Wunused-variable` build failure, and the hoist would otherwise make that side-effecting getter run on every unmount in a Release build where it previously did not run at all. With the guard it is read once per unmount in Debug and not at all in Release.

## Changelog:

[IOS] [FIXED] - Report a `RCTViewComponentView` child-index mismatch instead of raising `NSRangeException` while formatting the assert message

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

Test Plan:
I don't have a macOS build environment, so I have not compiled this — flagging that plainly. What backs the change:

- Five App Store crash reports (RN 0.81.5, iOS 26.5.2 / 26.6, three device models) all symbolicate to the message argument on this line, never to the guarded condition.
- `strings` on `react-native-artifacts-0.81.5-reactnative-core-release.tar.gz` shows both `Attempt to unmount…` format strings present, confirming the call sites are compiled into the release artifact — same check https://github.com/react/react-native/issues/57454 reports for 0.85.3 and 0.86.0, so the artifact defect reaches at least as far back as 0.81.
- Behaviour is unchanged whenever the assert passes; when it fails, the message now prints `out of bounds` in place of a tag that cannot be read.

Happy to rework this if you would rather the assert drop the `tag at index` field entirely, or if fixing the artifact build flags is considered sufficient on its own.

## Added on import: build, tests, and answers to the open questions

Compiled and tested, which the author could not do.

```
buck2 test fbsource//xplat/js/react-native-github:MountingTestsApple
→ Pass 19. Fail 0.
```

Adds `React/Tests/Mounting/RCTViewComponentViewUnmountTests.mm`, picked up by the existing `MountingTestsApple` glob. Restoring the pre-fix `RCTViewComponentView.mm` and re-running fails exactly the out-of-bounds case:

```
✗ RCTViewComponentViewUnmountTests/testUnmountWithOutOfBoundsIndexReportsRatherThanRaisingRangeException
Tests finished: Pass 18. Fail 1.
```

The second case, `testUnmountWithInBoundsMismatchStillReportsTagAtIndex`, passes against both old and new code on purpose: it pins that the in-bounds mismatch path still reports the real tag rather than the new `out of bounds` placeholder.

### Why the test is shaped the way it is

Two non-obvious constraints, both of which broke a more natural first attempt:

1. `RCT_NSASSERT` is defined as `RCT_DEBUG`, so in a debug build a failing `RCTAssert` calls the custom handler **and then** raises through `NSAssertionHandler`. Every failing assert throws, fixed or not, so `XCTAssertNoThrow` cannot be the assertion. The discriminator is that pre-fix the message arguments raise `NSRangeException` at the call site *before* `_RCTAssertFormat` runs, so the handler never fires at all. The test asserts the handler ran and that whatever escaped was not `NSRangeException`.
2. `RCTPerformBlockWithAssertFunction` calls `block()` between pushing and popping its handler with no `try`/`finally`, so an exception escaping the block leaks the handler into every later test in the process. The raise is therefore caught inside the block.

### Other checks

- `index` is `NSInteger` (`RCTComponentViewProtocol.h:64`), so the added `index >= 0` is meaningful rather than tautological. The previous `count > index` relied on a negative index promoting to a large `NSUInteger` and failing the unsigned comparison, which was correct by accident.
- The `NSNumber *` / `NSString *` ternary in the message compiles without warning.
- `arc lint -e extra` on the new test reports only a `NULLSAFECLANG` infrastructure failure that self-identifies as "not a code issue". The 55 pre-existing CLANGTIDY warnings in `RCTViewComponentView.mm` are untouched and are not attributed to this diff.

### NOTE: on the author's two questions

**Is fixing the artifact build flags sufficient on its own?** No. https://github.com/react/react-native/issues/57454 is the right root-cause fix and should still happen, but this change is worth having independently: the assert is broken *as an assert*. In any build where assertions are compiled in, including local Debug, the assert that exists to report an index mismatch raises while reporting it, so the diagnostic is unavailable exactly when it is needed. Fixing the artifact flags would hide that from production without repairing it.

**Should the `tag at index` field be dropped instead?** Recommend keeping it. It is the field that tells you which view actually occupies the slot, which is the useful part of the diagnostic, and the second test above locks in that it still appears when the index is in bounds.

### Release-build guard (added after the first sanity-check failure)

The first import version failed the sanity check with:

```
error: unused variable 'isIndexInBounds' [-Werror,-Wunused-variable]
```

`NS_BLOCK_ASSERTIONS` makes `RCTAssert` expand to `do {} while(false)`, so with assertions compiled out the hoisted locals have no remaining use. The hoisted lines and the assert are now wrapped in `#ifndef NS_BLOCK_ASSERTIONS`, which is the idiom this same file already uses at `RCTViewComponentView.mm:335` for a local pulled out of an assert.

That also restores a property the hoist had quietly removed. Before this diff the four `self.currentContainerView` reads sat inside the macro arguments and disappeared entirely in a Release build. Hoisting them out made the getter run on every unmount in Release, and that getter is not a plain accessor: it can allocate `_containerView`, reparent every subview into it and move `clipsToBounds` and `layer.mask`, or in the other branch tear the container down and nil it. With the guard the getter is read once per unmount in Debug and not at all in Release, which is better than both the original and the unguarded version.

Verified locally by defining `NS_BLOCK_ASSERTIONS` at the top of the file to simulate a Release build, against `RCTFabricComponentViewsBaseApple`, which is the target that owns this file:

| State | Result |
|---|---|
| Unguarded, assertions off | `error: unused variable 'isIndexInBounds'`, BUILD FAILED |
| Guarded, assertions off | exit 0 |
| Guarded, assertions on (debug) | Pass 19. Fail 0. |

Reviewed By: christophpurrer

Differential Revision: D115424016

Pulled By: fabriziocucci

fbshipit-source-id: b825f145c1867a230c0c6cd12a41950a83468ed2
2026-08-13 12:51:39 -07:00
bintangakbarRK e0aa7a8b0a Fix keyboard inset state across ScrollView recycling (#57811)
Summary:
Closes https://github.com/react/react-native/issues/57755.

`RCTScrollViewComponentView` remained subscribed to keyboard notifications while pooled with `_automaticallyAdjustKeyboardInsets` still enabled. A late notification could therefore restore keyboard insets after `prepareForRecycle` had cleared them.

Disarm keyboard inset adjustment during recycling, then always synchronize the flag from the incoming props so a recycled view is correctly re-armed when the next ScrollView opts in.

## Changelog:

[IOS] [FIXED] - Prevent recycled ScrollViews from retaining automatic keyboard inset behavior.

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

Test Plan:
- Added `RCTScrollViewComponentViewTests.testAutomaticallyAdjustKeyboardInsetsAcrossRecycling`, covering enabled behavior, ignored keyboard notifications while recycled, and re-enabling after remount.
- `/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang-format --dry-run --Werror packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm packages/react-native/React/Tests/Mounting/RCTScrollViewComponentViewTests.mm`
- `git diff --check`

The full native XCTest suite was not run locally because this sparse checkout does not include a bootstrapped RNTester/CocoaPods workspace.

Reviewed By: christophpurrer

Differential Revision: D114719614

Pulled By: fabriziocucci

fbshipit-source-id: e8e035fe8a74909b2281fc8d52af0e034f2bafe4
2026-08-13 12:48:48 -07:00
Sam Zhou 704c2b1954 Bump flow-{estree,parser,eslint,transform,api-translator} to latest (#57932)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57932

Changelog: [Internal]

Reviewed By: gkz

Differential Revision: D115802149

fbshipit-source-id: aa4c730e47ab9b362f7a6e1a16c8127117b09c08
2026-08-13 12:21:06 -07:00
Peter Abbondanzo 579aedcbd5 Add TextInput placeholder screenshot coverage (#57925)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57925

Add RNTester screenshot coverage for single-line and multiline `TextInput` placeholders on Android and iOS. The test verifies that a long single-line placeholder is ellipsized while a multiline placeholder wraps.

Changelog: [Internal]

Reviewed By: christophpurrer

Differential Revision: D115731487

fbshipit-source-id: bf13562b181f9c104056638c7cfa1d57416a3364
2026-08-13 11:48:22 -07:00
Alex Hunt 8a7dfce604 Update debugger-frontend from 571dc30...dbc1c52 (#57954)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57954

Changelog: [Internal] - Update `react-native/debugger-frontend` from 571dc30...dbc1c52

Resyncs `react-native/debugger-frontend` from GitHub - see `rn-chrome-devtools-frontend` [changelog](https://github.com/react/react-native-devtools-frontend/compare/571dc30a5de05c09d6734fd38f6a6c279d5d7ec2...dbc1c525fbe78bf415ff5441ea05d5b2475b2c24).

### Changelog

| Commit | Author | Date/Time | Subject |
| ------ | ------ | --------- | ------- |
| [dbc1c525f](https://github.com/react/react-native-devtools-frontend/commit/dbc1c525f) | Alex Hunt (hello@alexhunt.dev) | 2026-08-13T17:58:45+01:00 | [Enable "Capture screenshot" command for React Native targets (#252)](https://github.com/react/react-native-devtools-frontend/commit/dbc1c525f) |
| [e06f16d4e](https://github.com/react/react-native-devtools-frontend/commit/e06f16d4e) | Nicola Corti (corti.nico@gmail.com) | 2026-06-10T14:11:47+01:00 | [Bump remaining vulnerable dev dependencies (#260)](https://github.com/react/react-native-devtools-frontend/commit/e06f16d4e) |
| [950f4a8e3](https://github.com/react/react-native-devtools-frontend/commit/950f4a8e3) | Nicola Corti (corti.nico@gmail.com) | 2026-06-04T14:46:30+01:00 | [Bump vulnerable dev dependencies (#259)](https://github.com/react/react-native-devtools-frontend/commit/950f4a8e3) |
| [af53aca17](https://github.com/react/react-native-devtools-frontend/commit/af53aca17) | Alex Hunt (hello@alexhunt.dev) | 2026-04-20T15:00:21+01:00 | [Disable inapplicable commands for React Native targets (#251)](https://github.com/react/react-native-devtools-frontend/commit/af53aca17) |

Reviewed By: hoxyq

Differential Revision: D115891642

fbshipit-source-id: fcf1353aa4a4f45fe246fbc1ac6afa6837213ae1
2026-08-13 10:37:36 -07:00
riteshshukla04 092734772a perf: Cache normalisedColors output (#57896)
Summary:
While looking into something I saw this processColor function , for every color we  are calculating normalise Color even though it would have been done earlier.
This is a simple approach. Maybe we can add things like LRU. With some simple scripts(Rendering around 1000 cells) I found around 13% faster .
## Changelog:

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

Pick one each for the category and type tags:

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

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->
[GENERAL][FIXED] - use cached results for already normalised colors

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

Test Plan: Tested in RN tester.

Reviewed By: christophpurrer

Differential Revision: D115779590

Pulled By: Abbondanzo

fbshipit-source-id: a915bc641fbaff1055c18ac1efa20552a725102f
2026-08-13 08:59:46 -07:00
Riccardo Cipolleschi bc35168d1b Back out "Add Maestro Cloud CI for RNTester" (#57948)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57948

Original commit changeset: 20a5feb21ec1

Original Phabricator Diff: D115732295

## Changelog:
[Internal] -

Reviewed By: cortinico

Differential Revision: D115880038

fbshipit-source-id: c3ccc0f46be029ec13866465df02d73d3b36413e
2026-08-13 08:33:30 -07:00
Riccardo Cipolleschi 789e54c827 Back out "Fix Maestro Cloud project ID reference" (#57947)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57947

Original commit changeset: f7475fa8fcd3

Original Phabricator Diff: D115871650

## Changelog:
[Internal] -

Reviewed By: cortinico

Differential Revision: D115880037

fbshipit-source-id: 0d4e121e37d087e94fa0e47642cd4fd8c3e07309
2026-08-13 08:33:30 -07:00
Dawid Małecki b7f0baf8f9 Emit a real framework for React-cxxstableapi under use_frameworks! (#57936)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57936

`react/cxxstableapi` is a header-only module — the stable API guards are pure
preprocessor headers with no compilable source. Under `use_frameworks!`,
CocoaPods emits a `PBXAggregateTarget` for a pod with no compilable sources, and
an aggregate target produces no `.framework`, so pods depending on
`React-cxxstableapi` cannot resolve the guard headers.

Add an anchor translation unit so the pod has one source and CocoaPods emits a
real framework target instead, and widen the podspec's build-from-source glob to
pick it up. The prebuilt glob stays headers-only, since prebuilt pods do not
generate frameworks. This mirrors the existing `CSSDummy.cpp` anchor for the
header-only `React-renderercss` pod.

Also register `react/cxxstableapi` as a subdirectory of the Fantom tester CMake
build so the guard headers resolve there.

Changelog: [Internal]

Reviewed By: cortinico

Differential Revision: D115859623

fbshipit-source-id: 47e63560e1478ad507d1f28f053b925438232ea4
2026-08-13 08:20:05 -07:00
Dawid Małecki a9df3d5408 Define RN_BUILDING for React Native's own CMake, SwiftPM, and Buck targets (#57861)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57861

React Native's public C++ headers are gaining guards from `react/cxxstableapi`, which
turn a direct include of a fine-grained header into an error for consumers that opt into
the strict API by defining `RN_STRICT_API`. React Native's own sources keep including
those headers directly, so they are exempted via `RN_BUILDING`.

Unlike CocoaPods, these three build systems each have a single chokepoint:

- CMake: one `add_compile_definitions(RN_BUILDING)` in the ReactAndroid JNI project,
  a directory property inherited by every `add_react_common_subdir` below it. It is
  declared *after* the third-party NDK subdirectories so glog/boost/folly/fmt never see
  it, and this project never compiles app or third-party module code.
- SwiftPM: one `.define` in the shared `Target.reactNativeTarget` factory that every
  React Native target is created through. `cxxSettings` are per-target and are not
  inherited by packages that depend on React.
- Buck: a `_set_rn_building_flag` helper called from the four macros React Native's own
  targets use. It is `preprocessor_flags`, deliberately
  not `exported_preprocessor_flags`, so dependents are not exempted either.

This change is inert on its own: nothing behaves differently unless a consumer defines
`RN_STRICT_API`.

Changelog: [Internal]

Reviewed By: cipolleschi

Differential Revision: D115051088

fbshipit-source-id: 70b525a7291227a4400beefdba2b0a3571dc032c
2026-08-13 08:20:05 -07:00
Alex Hunt 2e4e8303ea Update agent guidance, move to AGENTS.md (#57944)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57944

The V1 of our `CLAUDE.md` was mostly contributor boilerplate. Revise this towards a plan overview of the repo — giving the agent immediate context on working in the codebase (and reducing exploratory file reads).

Also moves `CLAUDE.md` to `AGENTS.md`, for interop with non-Claude harnesses — `CLAUDE.md` is now a pointer to this source.

New contents:

- Repo structure
- Common commands
- Gotchas
- Contributing guidelines

This remains intentionally high level rather than exhaustive, to keep the context window lean. We can move towards nested `AGENTS.md` files over time.

**Notes**

- `CONTRIBUTING.md` is unchanged — already a short pointer to reactnative.dev. (Possible candidate for later deletion.)

Changelog: [Internal]

Reviewed By: Abbondanzo

Differential Revision: D115564651

fbshipit-source-id: 8e237cd1ab6189632bf59b2bd865c5f06fef8bcc
2026-08-13 07:20:06 -07:00
Nicola Corti 9c770f5718 Fix Maestro Cloud project ID reference (#57943)
Summary:
- read the Maestro Cloud project ID from the repository secret in both iOS and Android jobs
- match the existing repository configuration and unblock input validation

The failing main run resolved vars.MAESTRO_CLOUD_PROJECT_ID to an empty string because MAESTRO_CLOUD_PROJECT_ID is configured as a GitHub Actions secret.

Changelog:
[Internal] [Changed] -

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

Test Plan:
- node_modules/.bin/prettier --check .github/workflows/test-all.yml
- git diff --check

Reviewed By: huntie

Differential Revision: D115871650

Pulled By: cortinico

fbshipit-source-id: f7475fa8fcd30e334b9bf3d361f6f718bd0ca7e4
2026-08-13 07:11:06 -07:00
generatedunixname910664361436750 b80bc19dc7 Fix CQS signal facebook-hte-BadImplicitCast in xplat/jsi/jsi/test (#57942)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57942

Reviewed By: Daniel9z

Differential Revision: D115846369

fbshipit-source-id: f28285263cd970cfb88c9d3aa5c4428e97bd3206
2026-08-13 07:10:39 -07:00
Riccardo Cipolleschi e334b9318d Wait for images before box-shadow screenshots (#57922)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57922

Changelog: [Internal]

Expose the box-shadow example test identifier only after all three images have loaded. The E2E navigation helper now waits for stable image content before capturing the screenshot, and the Nougat golden reflects the complete example.

Reviewed By: Abbondanzo

Differential Revision: D115740465

fbshipit-source-id: 78bbec4ef20ce14832ec423c5e98320eb3ba34a0
2026-08-13 06:59:30 -07:00
Riccardo Cipolleschi 1c4a46f4e3 Ignore stale minimum-view-time viewability updates (#57923)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57923

Changelog: [General][Fixed] - Ignore stale viewability updates while enforcing minimum view time

Minimum view-time callbacks can outlive a newer viewport update. During initial layout, callbacks for intermediate viewport snapshots can fire after the current snapshot and replace the correct visible-item set. Discard callbacks whose captured visible-index set is no longer current, and cover the transition with a unit test.

Reviewed By: Abbondanzo

Differential Revision: D115740467

fbshipit-source-id: 0a308dbc57c1d1fc5b2b669d998d1d9f8f1495f1
2026-08-13 06:59:30 -07:00
Riccardo Cipolleschi e774f0d623 Stabilize sampling-profiler tracing test (#57924)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57924

Changelog: [Internal]

Exercise JavaScript briefly before ending each trace. This gives the sampling profiler a bounded opportunity to record a stack while preserving the test comparison between disabled and enabled categories.

Reviewed By: Abbondanzo

Differential Revision: D115740464

fbshipit-source-id: 9aa42ccf597aa9198cb31c2dae601f1709506d24
2026-08-13 06:59:30 -07:00
Andrew Ghostuhin 551d12a787 Fix Android inline Text views with small font scale
Summary:
Fixes https://github.com/facebook/react-native/issues/50916.
Closes torin-asakura/workspace#126.

Android inline views inside `<Text>` receive their measured layout size from Fabric as layout units/DIP. TextLayoutManager was converting those attachment dimensions with `PixelUtil.toPixelFromSP(...)`, so a device font scale below 1.0 shrank the inline view placeholder width. In the https://github.com/facebook/react-native/issues/50916 RNTester reproducer, a measured `Row cutoff` attachment around 155px became about 132px at `font_scale=0.85`, clipping the rendered text to `Row`.

This changes inline text attachment dimensions to use DIP conversion and rounds them up to the pixel grid in both spannable construction paths.

## Changelog:

[ANDROID] [FIXED] - Keep inline views inside Text from shrinking with Android system font scale

X-link: https://github.com/facebook/react-native/pull/57132

Reviewed By: christophpurrer

Differential Revision: D108030457

Pulled By: cipolleschi

fbshipit-source-id: af68489aed0ee9c39f3a440f07ca9286ff5ed30c
2026-08-13 04:51:25 -07:00
Rob Hogan 2b995be464 Make BabelNode* types module-local within @babel/types (remove ambient globals) (#57866)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57866

Now that all internal and OSS Babel types use explicitly imported types (see stack), modify our Babel type generator scripts and the generated lib defs so that they no longer declare global types.

This unblocks migrating to a modern `flowtyped` declaration and removing the `.flowconfig` coupling.

Changelog: [Internal]

Reviewed By: huntie

Differential Revision: D114881982

fbshipit-source-id: df29b8e22cf070f2e39c994f3a0856be4939fdc9
2026-08-13 04:49:31 -07:00
Radosław Rolka 4433cdbe62 Set SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG when injecting SPM (#57914)
Summary:
Swift's `#if DEBUG` is gated by SWIFT_ACTIVE_COMPILATION_CONDITIONS, not by GCC_PREPROCESSOR_DEFINITIONS (which only reaches C/ObjC/C++). The app template does not commit that setting; CocoaPods injects it at `pod install` time (react_native_post_install -> set_build_setting
SWIFT_ACTIVE_COMPILATION_CONDITIONS = ["$(inherited)", "DEBUG"] on Debug).

An app set up with the experimental SwiftPM support never runs CocoaPods, so `#if DEBUG` is false even in a Debug build: AppDelegate.swift's `bundleURL()` skips the Metro URL and falls back to a main.jsbundle a Debug build never produced, and the app dies at launch with "No script url provided ... unsanitizedScriptURLString = (null)" while Metro is running.

Info: https://github.com/react-native-community/template/pull/244

## Changelog:

Inject the setting from `spm add`/`update` alongside the other React build settings, into debug-flavored configurations only (the same flavorForBuildConfiguration test that selects the debug xcframeworks), so a config linking the debug binaries also compiles its Swift with DEBUG.

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

Pick one each for the category and type tags:

[IOS] [FIXED] - Set SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG when injecting SPM

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

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

Test Plan:
1. Build spm-only rn app in debug
    - App will fail to connect to the Metro
2. Apply changes and run `react-native spm`
3. Build again
    - App will work as expected in debug and Metro will be connected

Reviewed By: Abbondanzo

Differential Revision: D115736551

Pulled By: cipolleschi

fbshipit-source-id: 1e42ca26ac1992bfa827ce85a63cf4564d313e15
2026-08-13 03:55:48 -07:00
Aravind M Nair a4733b1ca1 Fix crash when accessibilityRole="tabbar" is used on Android (#57915)
Summary:
Fixes https://github.com/react/react-native/issues/57890.

`tabbar` is part of the public `AccessibilityRole` union in both Flow and TypeScript, with nothing marking it iOS-only, and on iOS it maps to `UIAccessibilityTraitTabBar`. On Android the `AccessibilityRole` enum has no `TABBAR` entry, so `fromValue()` throws `IllegalArgumentException` from `BaseViewManager.setAccessibilityRole` during `createViewInstance` and the app crashes on mount. It is the only value in the union Android fails to resolve.

This adds `TABBAR` to the enum and maps it to `android.view.View` in `getValue`, next to `TAB` and `TABLIST`, which likewise have no dedicated Android widget. `getValue` is an exhaustive `when` with no `else`, so the second hunk is required for the enum addition to compile.

`setRole` already has an `else` branch, so no `roleDescription` is set for the new value. That looks right to me, since Android has no tab bar concept to announce, but happy to add a string resource if you'd prefer one.

## Changelog:

[ANDROID] [FIXED] - Fix crash when `accessibilityRole="tabbar"` is used on Android

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

Test Plan:
Added a unit test to `ReactAccessibilityDelegateTest` asserting that `fromValue("tabbar")` resolves to `TABBAR` and maps to `android.view.View` rather than throwing.

Repro from the issue:

```jsx
<View accessibilityRole="tabbar">
  <Text>hello</Text>
</View>
```

Before: crashes on mount on Android with `Invalid accessibility role value: tabbar`.
After: renders as a plain view, matching iOS, where roles without a UIKit trait fall back rather than raising.

I have not run the Android suite locally, relying on CI for that.

Reviewed By: christophpurrer

Differential Revision: D115730694

Pulled By: Abbondanzo

fbshipit-source-id: ab0ac7987aa0fa0a4015a08514b8d83cfaa0323c
2026-08-12 19:43:17 -07:00
Peter Abbondanzo aa96fa64d1 Fix AccessibilityRole list conversion (#57926)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57926

Serialize `AccessibilityRole::List` as `list` and recognize `list` when parsing accessibility roles. Add exhaustive round-trip coverage for every named `AccessibilityRole`.

Changelog:
[General][Fixed] - Fix accessibility list role conversion in C++.

Reviewed By: christophpurrer

Differential Revision: D115746399

fbshipit-source-id: fdf4a74bbef7afe61ac18bf9adbc407cf29ac138
2026-08-12 17:22:53 -07:00
Sam Zhou aeee662b62 Deploy 0.327.0 to xplat (#57927)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57927

[changelog](https://github.com/facebook/flow/blob/main/Changelog.md)
Changelog: [Internal]

Reviewed By: gkz

Differential Revision: D115751796

fbshipit-source-id: 0d60967ea1f0556616a9f918410f4105404488dd
2026-08-12 14:17:00 -07:00
Minh Vu e59a1d252c fix(virtualized-lists): invalidate content length on orientation change (#57871)
Summary:
When a list changes between vertical and horizontal layouts, `ListMetricsAggregator` resets its cell measurements but keeps `_contentLength`. That value belongs to the old scrolling axis. In horizontal RTL mode, it can then be used to calculate a cell offset before the new content layout is observed.

This clears `_contentLength` during horizontal orientation changes so RTL calculations wait for the new content length. The reset was present in the original orientation invalidation logic and was removed when bottom-up layout support was removed.

## Changelog:

[GENERAL] [FIXED] - Invalidate list content length when orientation changes.

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

Test Plan:
- Added regression coverage for vertical to horizontal content length invalidation.
- Verified horizontal RTL metrics throw until new content layout is observed.
- Ran `yarn jest packages/virtualized-lists/Lists/__tests__/ListMetricsAggregator-test.js --runInBand`.
- Ran Prettier and targeted ESLint checks.

Reviewed By: christophpurrer

Differential Revision: D115586644

Pulled By: fabriziocucci

fbshipit-source-id: 6c0f1b435575c080444b72381f7ce56ae3f69a15
2026-08-12 12:37:41 -07:00
Jakub Kosmydel 7f18ad0f84 Ellipsize single-line TextInput placeholders (#57909)
Summary:
On Android, long `TextInput` placeholders are hard-clipped with no trailing ellipsis, while iOS truncates with `…`.

Fixes https://github.com/facebook/react-native/issues/29663 (same class of bug as https://github.com/facebook/react-native/pull/55272; uses a correct multiline bitmask check and clears ellipsize when multiline is enabled).

**Repro:** https://github.com/kosmydel/android-ellipsize-repro

| Before | After |
|--------|--------|
| <img width="400" alt="image" src="https://github.com/user-attachments/assets/583a8323-03ae-4075-a662-a4c92e8fec9e" /> |<img width="400" alt="image" src="https://github.com/user-attachments/assets/ece0f350-2349-4244-b7c8-56acd7d15e91" /> |

## Changelog

[Android] [Fixed] - Ellipsize long single-line TextInput placeholders to match iOS

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

Test Plan:
- Unit: `ReactTextInputPropertyTest` — placeholder sets `TruncateAt.END`; toggling `multiline` clears/restores ellipsize
- Manual: https://github.com/kosmydel/android-ellipsize-repro
  - Single-line constrained `TextInput` with a long placeholder → trailing `…`
  - Short placeholder unchanged
  - `multiline={true}` with a long placeholder → still wraps
  - Toggle `multiline` false → true → false → ellipsize restored

Reviewed By: christophpurrer

Differential Revision: D115724716

Pulled By: Abbondanzo

fbshipit-source-id: a267626a4b8d78d5e853729c46400b88a107d53a
2026-08-12 11:57:07 -07:00
Alex Hunt 7cdac2a15c Relocate manual .d.ts definitions under types_DEPRECATED/ (#57513)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57513

See [**RFC0894: Removing deep imports from react-native**](https://github.com/react-native-community/discussions-and-proposals/pull/894)

**Changes**

Rename the hand-written `types/` directory to `types_DEPRECATED/` and consolidate the co-located `.d.ts` declarations beneath it.

- This is a pure relocation with **no change** to the public type surface. The package `"exports"` map is updated so that every `types` condition resolves to the new location (`.` → `./types_DEPRECATED/index.d.ts`, `./Libraries/*` → `./types_DEPRECATED/Libraries/*.d.ts`) — consumers resolve the exact same types and runtime modules through the same import specifiers.
- Relative imports between the moved declarations, and the references from the moved `types/` files into `Libraries/`, were rewritten so the type graph continues to resolve.

**Downsides**

Risk of structural code drift if/when source modules in `Libraries/` move around.

**Counterpoint**: The aim here is to feature-lock the legacy JS API and remove deep `Libraries/` imports by projects.

Changelog: [Internal]

Reviewed By: christophpurrer

Differential Revision: D110602194

fbshipit-source-id: 14fbd9128821516e629f7e5b07cbfc7588fc335e
2026-08-12 10:16:06 -07:00
Nicola Corti 189bb445f6 Add Maestro Cloud CI for RNTester (#57919)
Summary:
This integrates our CI to work with Maestro Cloud.
The change is additive as the previous existing tests are untouched (also because the Debug Maestro tests require a running bundler which we can't have on Maestro Cloud).

## Changelog:

[INTERNAL] -

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

Test Plan: CI

Reviewed By: christophpurrer

Differential Revision: D115732295

Pulled By: cortinico

fbshipit-source-id: 20a5feb21ec130ba227655f415214f91548f2423
2026-08-12 10:06:37 -07:00