mirror of
https://github.com/react/react-native.git
synced 2026-09-29 16:58:04 +08:00
526016d95038b7a70f72c93d4bf16b9a90aa2263
41402
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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
|
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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
|
||
|
|
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 |
||
|
|
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
|
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |