mirror of
https://github.com/react/react-native.git
synced 2026-09-28 21:33:11 +08:00
pr58505
1603
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3ca6ea3eca |
Buffer async CallInvoker work with module calls (#58313)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/58313 In bridgeless, native reaches JS by two routes that end in the same `RuntimeScheduler` queue but get there differently. `callFunctionOnModule` goes through the instance's `BufferedRuntimeExecutor`; the `CallInvoker` goes straight to `scheduleTask`. The CallInvoker therefore skips the buffer entirely and can reach the runtime while a module call issued earlier is still parked, unflushed, because the bundle is mid-evaluation. Native code that issues both cannot rely on the order it issued them in, and `Task` is a min-heap on `now() + timeout(priority)` with no insertion tiebreak, so equal priorities do not settle it either. Gives the two channels the same buffering. `BufferedRuntimeExecutor` gains a priority-carrying `execute`, so work routed through it keeps the scheduler priority it was submitted with instead of collapsing to the executor default, and buffered work from both overloads stays in one submission-ordered stream. `BufferedCallInvoker` sits on that executor and becomes the bridgeless `jsCallInvoker` on Android, iOS and macOS. `invokeSync` deliberately keeps going straight to the scheduler: a synchronous call cannot wait for a flush that only happens once the bundle has run. Behind `enableBufferedCallInvoker`, default true. `ReactInstance` picks between the buffered invoker and the existing `RuntimeSchedulerCallInvoker` in one place, so the platform call sites are identical either way and the change is revertible at runtime — it moves when native-issued async work first reaches JS during startup, which is the intended contract but affects every native module. One lifetime hazard this surfaces, worth knowing about beyond this diff: `BufferedRuntimeExecutor` reaches the scheduler through a raw pointer captured at construction, which is safe only while the owning instance is alive. A CallInvoker is routinely held across instance teardown, so `BufferedCallInvoker` guards every async dispatch on a weak reference to the scheduler and drops the work when it has expired — the same contract `RuntimeSchedulerCallInvoker` has. Without that guard this reliably segfaults on a reload. Changelog: [General][Changed] - Async `CallInvoker` work is now buffered alongside callable module calls, so it no longer runs before the JS bundle has finished evaluating Reviewed By: rubennorte Differential Revision: D118456662 fbshipit-source-id: 7c6ccb595d69595721092eeb84b4724809140496 |
||
|
|
1a7c318ffc |
Make deprecated native module specs Flow strict-local (#57724)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57724 Upgrade the legacy native module spec files from `flow` to `flow strict-local` to enforce stricter local type checking. Loose `Object` types were replaced with the codegen-equivalent `UnsafeObject`, and `Array<any>` parameters with `Array<unknown>`, both of which are codegen-identical so the generated native interfaces are unchanged. Changelog: [Internal] Reviewed By: javache Differential Revision: D113763785 fbshipit-source-id: 085a0a570246eeda27b3c22a56a2696b04821fd5 |
||
|
|
63d118af72 |
Fix per-node memory regression caused by Grid styles (#58311)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/58311 # Why Currently, grid style properties are stored in yoga style (`gridTemplateRows_`, `gridAutoColumns_` etc). These properties increase the size of style object from `152` bytes to `280` bytes (84% increase). The cost is added even when a node is not a grid container or a grid item. # How Move grid style properties behind a pointer that is lazily allocated, on the first grid property set. A node that doesn't use grid only adds the cost of this pointer (8 bytes). So style now costs 160 bytes (5% increase). The public API remains unchanged. # Tests A test is added to catch the style size regression and `tests/GridStyleTest.cpp` includes additional cases to assert unset style, copy and move behaviour. Changelog: [Internal] X-link: https://github.com/react/yoga/pull/2018 Reviewed By: rubennorte Differential Revision: D118628661 Pulled By: javache fbshipit-source-id: 185370e93bcf5b277b48c436ba3d06dada5a66fe |
||
|
|
bf0e377c0a |
Reduce JNI allocations when importing native maps (#58274)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/58274 `ReadableNativeMap` materialization copied keys and created temporary JNI references for every imported type. Cache pointers to the stable native values and reuse global `ReadableType` references so importing maps and arrays does less allocation and lookup work. Writable maps can continue mutating after materialization because `folly::dynamic` stores object entries in reference-stable `F14NodeMap` nodes. Changelog: [Internal] Reviewed By: christophpurrer, rubennorte Differential Revision: D118277119 fbshipit-source-id: 955a60ef7f1eb4cb7549d64caa1238416e90b223 |
||
|
|
8cf8e094f3 |
Fold optional TextAttributes fields into a presence mask when hashing (#57984)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57984 `hash_combine` mixes each field into the previous seed, so it forms a dependency chain the CPU cannot overlap and an unset optional still costs a full link. `hash_combine_optionals` folds a run of optionals into one presence-mask link plus the engaged values, so a further optional costs a bit in the mask rather than a link. The mask is what keeps it collision-free: skipping disengaged fields alone would make the same value in two different slots hash identically. Equality moves from `std::tie` to a short-circuit chain ordered cheapest first, with the string and vector fields last, because the dominant caller is a successful cache lookup where the keys are equal and every field has to be examined. Changelog: [Internal] Reviewed By: javache Differential Revision: D115621401 fbshipit-source-id: f164c24f6b085ef8d32cafbeb2b61636eec3d0cf |
||
|
|
49bcae1763 |
Wait for Maestro template app startup (#58289)
Summary: Replace the immediate template-app startup assertion with `extendedWaitUntil` and a 60-second timeout. The Android ARM64 debug app occasionally becomes accessibility-visible just after the existing assertion times out under native translation. This failed all three attempts in [run 33609361836](https://github.com/react/react-native/actions/runs/33609361836/job/100196578829) and [run 33540863593](https://github.com/react/react-native/actions/runs/33540863593/job/99984608541). In the captured failure artifact, both the screenshot and XML hierarchy contain `Welcome to React Native`, indicating a startup/accessibility timing race rather than an app failure. Release runs remain fast because `extendedWaitUntil` returns as soon as the element is visible. ## Changelog: [INTERNAL] [FIXED] - Wait for the template app to become visible in Maestro E2E tests. Pull Request resolved: https://github.com/react/react-native/pull/58289 Test Plan: - `MAESTRO_CLI_NO_ANALYTICS=1 maestro check-syntax scripts/e2e/.maestro/start.yml` — passed (`OK`) - `./node_modules/.bin/prettier --check scripts/e2e/.maestro/start.yml` — passed - `git diff --check` — passed - Inspected the failing Android debug artifact: the expected text is present in both the final screenshot and accessibility hierarchy. Reviewed By: christophpurrer Differential Revision: D118457123 Pulled By: cortinico fbshipit-source-id: d2f5232482933e1decb13a9b2dac7911b205db4f |
||
|
|
cec189bd34 |
Support 'react-native/react-private-interface' in build-types (#58075)
Summary: **Motivation** Minimum infra fix to enable subsequently landing https://github.com/react/react-native/pull/57940. Referencing `ReactNativeFeatureFlags` on the existing `react-native/react-private-interface` boundary is blocked by a gap in the `build-types` pipeline and a type translation error, addressed here. **Changes** - `simpleResolve.js`: Explicitly support `react-native/react-private-interface` as a special case, fixing resolution. - `ReactNativeFeatureFlagsBase.js`: Tweak the `OverridesFor` type here to fix TypeScript translation compatibility, where the unconstrained `T` type param is now narrowed. **Impact** No change to the API snapshot (the `ReactNativeFeatureFlags` import is ultimately tree-shaken!), and no effect on generated types until https://github.com/react/react-native/issues/57940 lands. Changelog: [Internal] Pull Request resolved: https://github.com/react/react-native/pull/58075 Test Plan: - `yarn build-types` - Applied https://github.com/react/react-native/issues/57940 patch on top; both stayed green, no `types_generated/**/featureflags/**` Reviewed By: GijsWeterings Differential Revision: D117513265 Pulled By: cortinico fbshipit-source-id: 348e8de3d622081fe0062ef939ce14c21af587a8 |
||
|
|
25ebffd1f7 |
Run Android E2E with ARM64 APKs (#58092)
Summary: Build only the `arm64-v8a` Android ABI for dry-run CI artifacts and run the ARM64 RNTester and template-app APKs on an API 35 `google_apis` x86_64 emulator using the system images built-in NDK translation support. This also makes the Maestro emulator API level and target configurable, logs the device ABI/native-bridge configuration, and updates local RNTester artifact selection to use the ARM64 split. Release and nightly publication builds continue to build all supported Android ABIs. ## Changelog: [INTERNAL] [CHANGED] - Run Android E2E tests with ARM64 APKs through NDK translation. Pull Request resolved: https://github.com/react/react-native/pull/58092 Test Plan: - `node --check .github/workflow-scripts/maestro-android.js` — passed. - `node --check scripts/release-testing/test-release-local.js` — passed. - Parsed all modified YAML files with the `yaml` Node package — passed. - `./node_modules/.bin/prettier --check <modified files>` — passed. - `git diff --check HEAD~3..HEAD` — passed. - `actionlint` reported only existing repository metadata warnings for the custom `4-core-ubuntu` label and the missing description in the local `yarn-install` action. - `yarn test .github/workflow-scripts/__tests__/maestro-android-test.js --runInBand` could not start because the local dependency tree is missing `flow-parser`; restoring dependencies was blocked by HTTP 503 responses from the npm registry. - The draft CI run should validate ARM64 APK installation, Hermes startup, and the complete Maestro suites through `libndk_translation.so`. Reviewed By: Abbondanzo Differential Revision: D117195078 Pulled By: cortinico fbshipit-source-id: a78f2127f04e80c3b5ca4c95d2024dffb92ff122 |
||
|
|
06eb1fefab |
fix: prevent programmatic scrolls from cancelling active touches on iOS (#57546)
Summary:
On iOS, a programmatic non-animated scroll — `scrollTo` / `scrollToOffset({ animated: false })`, or any library driving the offset frame-by-frame — cancels every active touch in enclosing scroll views. Two mechanisms combine into this:
1. `scrollToOffset:animated:` calls `_forceDispatchNextScrollEvent` and, for non-animated scrolls, `_handleFinishedScrolling` — so every call emits `onScroll` (twice) plus `onMomentumScrollEnd`, bypassing `scrollEventThrottle` entirely. A per-frame driver produces a continuous stream of unthrottled `topScroll` events (~60/s measured with `scrollEventThrottle={2000}`).
2. In the responder system, any `topScroll` event without `responderIgnoreScroll: true` starts a responder negotiation, and `ScrollView`'s `onScrollShouldSetResponder` answers `true` whenever a finger is down inside it. Each event therefore steals the responder from a pressed `Pressable`/`Touchable` and the press is cancelled — `onPressIn` fires, `onPress` never does.
On Android scroll events carry `responderIgnoreScroll: true`.
I added `responderIgnoreScroll` to the C++ `ScrollEvent` payload and set it to `!_isUserTriggeredScrolling` in `_scrollViewMetrics`. Programmatic scrolls no longer transfer the responder, while user-initiated scrolls (drag, deceleration, scroll-to-top) keep today's behavior.
## Changelog:
[IOS] [FIXED] - Programmatic (non-user-initiated) scrolls no longer cancel active touches in enclosing scroll views
Pull Request resolved: https://github.com/react/react-native/pull/57546
Test Plan:
Reproducible code — an endless marquee `FlatList` nested in a `ScrollView`, driven by `requestAnimationFrame` + `scrollToOffset({ animated: false })`, with a sibling `TouchableOpacity` and a counter proving the touches reach JS:
<details><summary>App.tsx</summary>
```tsx
import { useEffect, useMemo, useRef, useState } from 'react';
import {
FlatList,
ScrollView,
Text,
TouchableOpacity,
View,
} from 'react-native';
const dpPerSecond = 20;
const size = 80;
const gap = 8;
const data = Array.from({ length: 6 }).map((_, i) => ({
id: `id-${i}`,
n: i + 1,
}));
const renderItem = ({ item }: { item: (typeof data)[0] }) => (
<View
style={{
width: size,
height: size,
backgroundColor: 'red',
justifyContent: 'center',
alignItems: 'center',
}}>
<Text>#{item.n}</Text>
</View>
);
const Carousel = ({ paused }: { paused: boolean }) => {
const ref = useRef<FlatList>(null);
const [width, setWidth] = useState(0);
const offset = useRef(0);
useEffect(() => {
if (paused) return;
const x = (size + gap) * data.length;
let last = Date.now();
let raf: number;
const loop = () => {
const now = Date.now();
offset.current =
(offset.current + (dpPerSecond * (now - last)) / 1000) % x;
last = now;
ref.current?.scrollToOffset({ offset: offset.current, animated: false });
raf = requestAnimationFrame(loop);
};
raf = requestAnimationFrame(loop);
return () => cancelAnimationFrame(raf);
}, [paused]);
const neededToFill = Math.ceil(width / (size + gap));
const extendedData = useMemo(
() => [
...data,
...data.slice(0, neededToFill).map((d) => ({ ...d, id: d.id + '-dup' })),
],
[neededToFill]
);
return (
<FlatList
scrollEnabled={false}
scrollEventThrottle={2000}
showsHorizontalScrollIndicator={false}
windowSize={3}
onLayout={(e) => setWidth(e.nativeEvent.layout.width)}
contentContainerStyle={{ gap }}
ref={ref}
horizontal
data={extendedData}
keyExtractor={(item) => item.id}
renderItem={renderItem}
/>
);
};
export default () => {
const [paused, setPaused] = useState(true);
const [touches, setTouches] = useState(0);
return (
<View style={{ flex: 1 }} onTouchStart={() => setTouches((t) => t + 1)}>
<ScrollView contentContainerStyle={{ paddingVertical: 64, gap: 32 }}>
<Carousel paused={paused} />
<TouchableOpacity onPress={() => setPaused((p) => !p)}>
<Text style={{ fontSize: 20 }}>
Try tapping me {paused ? '▶️' : '⏸️'}
</Text>
</TouchableOpacity>
<Text style={{ fontSize: 16 }}>touches seen by JS: {touches}</Text>
</ScrollView>
</View>
);
};
```
</details>
Before this change, only the first tap works (it starts the marquee); every following tap increments the touch counter but never toggles the button — the press is cancelled by the responder transfer. After this change, every tap toggles the marquee.
Recordings of the repro above (every touch is marked with a blue ring and counted on screen):
Before:
https://github.com/user-attachments/assets/bc5a8764-efe3-40ba-a9cb-a5023d140369
After:
https://github.com/user-attachments/assets/b2c1eea1-dcab-422c-9a4a-90fd69061e9d
Reviewed By: cipolleschi
Differential Revision: D116453881
Pulled By: j-piasecki
fbshipit-source-id: eb72fb0f1a4ac9fdde15f621201009d855d63890
|
||
|
|
22e0eaf4e7 |
Pin clang-format 21.1.2 for OSS formatting (#58001)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/58001 The existing npm `clang-format` wrapper bundles LLVM 15, causing `yarn clang-format` to disagree with the formatting of checked-in headers. Replace it with checksum-pinned DotSlash artifacts for `clang-format` 21.1.2 across Linux, macOS, and Windows. Add a chunked formatting runner and remove the obsolete npm dependency. The runner skips generated sources and ignores dirsynchronized or vendored subtrees that maintain their own formatting. Changelog: [Internal] Reviewed By: christophpurrer Differential Revision: D116500359 fbshipit-source-id: 2141bee22ed2b712aae1637a1f052db23535bbb0 |
||
|
|
b03652af78 |
ResizeObserver Web API implementation (#57723)
Summary: > Stack 3/3 — parent: `feat/LayoutEventEmitter`. Review the two parents first. Implements the [`ResizeObserver`](https://drafts.csswg.org/resize-observer/) Web API for the New Architecture, behind the `enableResizeObserverByDefault` flag (off by default). The motivation is Web compatibility, and it's more capable than `onLayout`: callers pick which box to observe (`content-box`, `border-box`, `device-pixel-content-box`) and the sizes for those boxes are delivered in the notification. The design is as follows: JS `ResizeObserver`/`ResizeObserverEntry`/`ResizeObserverSize` on top of a manager singleton and the `NativeResizeObserver` TurboModule, using the same notify + `takeRecords` pull model. In C++, `ResizeObserverManager` collects observed targets whose layout changed at commit time (via `shadowTreeDidCommit`) and then computes and delivers observations in the event loop's "update the rendering" step (`RuntimeSchedulerResizeObserverDelegate::runResizeObservations`), as the spec requires. Requires bridgeless + the modern event loop; the legacy scheduler gets a no-op delegate. `runResizeObservations` implements the spec's depth-increasing gather/broadcast loop: each round gathers observations deeper than the shallowest target delivered in the previous round, so a callback that synchronously resizes or observes a shallower node is still delivered within the same tick. Known deviations from the spec (each pinned by a test): - Resizes triggered from a callback via a React state update are delivered on the next tick rather than within the current loop, because RN has no synchronous re-layout. Callbacks that resize or `observe()` synchronously do run further rounds in the same tick, and report `ResizeObserver loop completed with undelivered notifications` when an observation is left undelivered. - The loop carries a hard cap of 100 iterations on top of the spec's depth rule. Reaching the cap reports the loop error and stops, so a pathological callback degrades to a log line instead of a frozen app. - Re-observing a target with the same box is a no-op and does not re-deliver. This matches browsers, not the literal `observe()` algorithm. - Callback order across observers follows first-`observe()` order rather than construction order. ## Changelog: [INTERNAL] [ADDED] - Add the `ResizeObserver` API, behind the `enableResizeObserverByDefault` feature flag Pull Request resolved: https://github.com/react/react-native/pull/57723 Test Plan: - Fantom `ResizeObserver-itest.js` covers many test scenarios. - rn-tester has `ResizeObserver` examples (box sizes, text, visibility); verified initial, resize, and animation-driven delivery there. - `RuntimeSchedulerTest.cpp` new test scenarios for update rendering loop. Reviewed By: christophpurrer Differential Revision: D114045415 Pulled By: javache fbshipit-source-id: a561d5321ba45adb35f46ffb0cf26ad78eb3bae6 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
7bfe32fd33 |
Add SceneDelegate support (#57700)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57700 Add SceneDelegate lifecycle support to React Native iOS while retaining the AppDelegate path for backwards compatibility. Update linking, window resolution, bootstrap APIs, RNTester, HelloWorld, and generated API snapshots for scene-based applications. Changelog: [iOS][Added] - Add SceneDelegate lifecycle support Test Plan: - `buck2 build --flagfile fbsource//fbobjc/mode/buck2/linux --config fbobjc.react_native_debug=development --flagfile fbsource//fbobjc/mode/iphonesimulator-arm64 --flagfile fbsource//fbobjc/mode/jest-coverage --config xplat.available_platforms=CXX,APPLE --config cxx.default_platform=iphonesimulator-arm64 --config user.sandcastle_build_mode=profile --config user.platform_flavor=iphonesimulator-arm64 //xplat/js/react-native-github/packages/rn-tester:RNTesterIOS` - `xcodebuild -quiet -workspace RNTesterPods.xcworkspace -scheme RNTester -configuration Debug -destination "generic/platform=iOS Simulator" CODE_SIGNING_ALLOWED=NO build` - `arc lint` - Evaluate RNTester with the `RNTester` SceneDelegate scheme. - Evaluate RNTester with the `RNTester (AppDelegate)` legacy scheme. - Verify HelloWorld bootstrap and deep linking. Reviewed By: cortinico, javache Differential Revision: D113758228 Pulled By: cipolleschi fbshipit-source-id: 31174c03a0936ca397fc1d061650fc0f582bdda7 |
||
|
|
727e87ed26 |
Fully replace hermes-(estree|parser|eslint|transform) packages with flow-* counterparts in react-native (#57851)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57851 The flow-* packages have support for all latest syntax and is better maintained. Changelog: [Internal] Reviewed By: huntie Differential Revision: D115064040 fbshipit-source-id: 7265eb722910a460c76d0dbde0603cf962fe4e4e |
||
|
|
8415753e21 |
Add variable font settings support (#57815)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57815 Apply the existing `fontVariationSettings` text style prop when Fabric constructs fonts on iOS. Parse CSS-compatible axis settings into CoreText variation dictionaries while preserving absent, explicit-clear, and invalid value semantics for nested text. The parser supports quoted four-character OpenType tags and finite numeric values, rejects malformed settings as a complete unit, and applies normalized variations after the base font and feature settings are resolved. This shared attributed-text path covers Fabric `Text` and `TextInput`. Changelog: [iOS][Added] - Add `fontVariationSettings` support for Fabric text Reviewed By: Abbondanzo, christophpurrer Differential Revision: D114121940 fbshipit-source-id: d2b2fffd4fe723c5205e5279a466a125aa7edd38 |
||
|
|
19f7d144b9 |
Add Android text font variation settings (#57804)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57804 Add a `fontVariationSettings` text style prop and carry it through Fabric text attributes into Android text rendering. Android now deserializes the prop for `<Text>`, applies it to `Paint`, and includes it in text measurement cache identity because variable axes can affect layout. Preserve the distinction between an absent setting and an explicitly empty setting so nested text can inherit or clear the parent variation axes. Apply high-level font properties before low-level variation settings so explicit axes take precedence, matching CSS font realization order. Settings syntax is intentionally forwarded unchanged through common text attributes and validated only by the Android font variation parser. This avoids narrowing the grammar Android accepts. As a result, malformed child settings are outside the supported inheritance contract: they replace an inherited value before Android validation and are not guaranteed to fall back to the parent settings. Android also accepts `normal` and the React Native empty-string convention as explicit resets. Changelog: [Android][Added] - Add `fontVariationSettings` support for `<Text>` Reviewed By: Abbondanzo, huntie Differential Revision: D113580491 fbshipit-source-id: 46cea13ef5f06b33ed9f9d0b28b5c16f84193ea6 |
||
|
|
85a81818c6 |
Avoid copying surface props when starting or updating a surface (#57813)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57813 `SurfaceHandler` already takes a throwaway snapshot of its `Parameters` under `parametersMutex_` before handing them to the `UIManager`, but `UIManager::startSurface` and `UIManager::setSurfaceProps` took `moduleName` and `props` by const reference and then copy-captured them into the lambda posted to the `RuntimeExecutor`. That forced a second deep copy of the props tree — which for a real surface holds the initial route params and deep link data — on every surface start, prop update, and display mode change. Take both by value and move them into the lambda, and move at the `SurfaceHandler` call sites, so the snapshot is handed off instead of duplicated. The snapshot is a local that is dead after the call, so there is nothing left to observe the moved-from state. Changelog: [General][Changed] - `UIManager::startSurface` and `UIManager::setSurfaceProps` now take `moduleName` and `props` by value Reviewed By: zeyap, christophpurrer Differential Revision: D114730310 fbshipit-source-id: 7891f6ac61abda43ff0da5f4336741af6c329ed2 |
||
|
|
e2a4c68449 |
refactor(iOS): persist bundle as local file on par with Android (#57751)
Summary: On Android, in development, the JS bundle is downloaded and saved as a file - this is done to avoid passing it through JNI, but iOS could use similar approach for uniform behavior. This allows libraries to be able to obtain the bundle from the file without re-downloading it from Metro. It would impact iOS boot slightly, but only in development, so it should be negligible. ## Changelog: [IOS] [CHANGED] - make iOS preserve dev bundle as a temp file on par with Android implementation Pull Request resolved: https://github.com/react/react-native/pull/57751 Test Plan: rn-tester iOS dev works with this change Reviewed By: cortinico, christophpurrer Differential Revision: D114346244 Pulled By: coado fbshipit-source-id: ebb4264fe8949cd766e71ca0363aa209a61370f7 |
||
|
|
5fb3ebce1a |
ArrayBuffer support to Java TurboModules
Summary: Changelog: [ANDROID] [ADDED] - Add ArrayBuffer support to Java TurboModules - Codegen: ArrayBufferTypeAnnotation → ByteBuffer / ArrayBufferKind / Ljava/nio/ByteBuffer; in GenerateModuleJavaSpec and GenerateModuleJniCpp. Guard in Utils.js rejects Promise<ArrayBuffer> on Android at codegen time — drops async return to avoid CxxCallbackImpl.kt / JCallback / PromiseImpl.kt changes. - Runtime: New JByteBufferMutableBuffer.h — jsi::MutableBuffer wrapping global_ref<JByteBuffer> direct ByteBuffer, holds Java object alive for JS ArrayBuffer lifetime; dtor uses ThreadScope to attach JNI thread (Hermes GC finalizes off-thread). Zero-copy native→JS return mirrors iOS NSMutableDataBuffer. Args JS→Java always copied into Java-owned direct ByteBuffer via allocateDirect+memcpy (mirrors iOS NSData copy), no sync/async branching — safe to retain/dispatch past GC. - No changes to CxxCallbackImpl.kt, JCallback, PromiseImpl.kt. Nested ArrayBuffers still via folly::dynamic, deferred like ObjC/C++. - Samples: NativeSampleTurboModule.js spec, SampleTurboModule.kt (getArrayBuffer/ createNativeBuffer/ processAsyncBuffer), RCTSampleTurboModule.mm. (NSData param / NSMutableData return), RNTester UI SampleTurboModuleExample.js. X-link: https://github.com/facebook/react-native/pull/57062 Reviewed By: javache Differential Revision: D107411163 Pulled By: christophpurrer fbshipit-source-id: 67cc5da796a87f6918f0382debc5c5594f76661b |
||
|
|
3f3425e246 |
Remove unused NativeModalManager spec and all related code (#57806)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57806 Changelog: [INTERNAL] Remove the dead `NativeModalManager` TurboModule spec and all its dependents — 13 files, 192 deletions across JS spec, Modal.js usage, native registration, CXX API snapshots, blocklist entries, and test mocks. The spec was never implemented and the Modal component no longer needs the event subscription pattern since transitioning to the new renderer. Reviewed By: cortinico Differential Revision: D114652881 fbshipit-source-id: af939c002d4d58c662bacb840c805d2dd4f42eb4 |
||
|
|
f535c97904 |
fix(android): props 2.0 image tintColor=transparent broken (#57668)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57668 When enabling the props 2.0 feature flags I noticed that for an `<Image source={...} style={{ tintColor: 'transparent' }} />` the image is actually still showing, instead of becoming transparent. ### The underlying issue All color props are defined as `SharedColor`, where a `SharedColor` has a default value of `0`: https://github.com/facebook/react-native/blob/f3678f51d9873cb19602d7e36a4d8ed71562b9d0/packages/react-native/ReactCommon/react/renderer/graphics/platform/android/react/renderer/graphics/HostPlatformColor.h#L15-L18 https://github.com/facebook/react-native/blob/f3678f51d9873cb19602d7e36a4d8ed71562b9d0/packages/react-native/ReactCommon/react/renderer/graphics/Color.h#L30-L32 > [!NOTE] > This is a bit confusing to me. `0` is not really an "undefined" color, but its actually "transparent". What we are really saying this way is that all color props have a default value of "transparent". The naming makes me unsure whether this has been intentional. For other use cases, this seems to make sense. Ie. a user expects their text to have a background color of transparent/"nothing". However, we do not expect our image's tint color to have a default color of "transparent". This would hide all our images. With props 2.0 we use the `getDiffProp` function, and when we pass `tintColor: 'transparent'` it will be passed to native as `tintColor: 0`. When we then compare the passed prop's value vs the default value here, it will not include the `tintColor`: https://github.com/facebook/react-native/blob/f3678f51d9873cb19602d7e36a4d8ed71562b9d0/packages/react-native/ReactCommon/react/renderer/components/image/ImageProps.cpp#L249-L251 `tintColor` really is an optional color prop, and should be treated as such. The best fix I found was therefor making it really an `std::optional`. Let me know if you think otherwise! ## 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 --> [ANDROID] [CHANGED] - ImageProps make `tintColor` an `std::optional` to support color `transparent` (`0`) with props 2.0 X-link: https://github.com/facebook/react-native/pull/55535 Test Plan: - In the RNTester app change one of the Tint Color image examples to use tintColor of "transparent" - The image should be invisible now as all visible pixels turned transparent - Enable the props 2.0 feature flags - Run the same example, notice that the tintColor has not been applied Reviewed By: lenaic Differential Revision: D93140534 Pulled By: coado fbshipit-source-id: 0d8abf3583d87ad840b3f72603b4c29c775671ca |
||
|
|
fc365f6e72 |
Flow: Prefer imported types from @babel/types over global BabelNode* types (#57752)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57752 ## Rationale Flow lib defs that declare global types (available anywhere, without an `import`) must be referenced in `.flowconfig` and can't be maintained incrementally. That's not too bad for very stable APIs and it's necessary for environment/runtime globals, but for 3P libraries it makes the lib defs much more difficult to maintain for little benefit (we have to import the runtime APIs anyway). Secondarily, it's a problem for generating TypeScript types, as TS doesn't declare any 3P library globally. Babel is one of few cases where a library declares Flow globals - every one has an importable equivalent. ## This diff Replaces usages of Babel global types across xplat/js with their `babel/types` equivalents Changelog: [Internal] Reviewed By: javache Differential Revision: D113574665 fbshipit-source-id: be668d968345a60e76a3515d19d8eaa002d53274 |
||
|
|
7b5d1bc971 |
Switch over to flow-parser/babel-plugin for compilation (#57770)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57770 Changelog: [Internal] Reviewed By: gkz Differential Revision: D114240152 fbshipit-source-id: 7d292d33f75cf548b89e6427684fc3a5994c4b3f |
||
|
|
55195ca43f |
Layout event emitter (#57722)
Summary: > Stack 2/3 — parent: `feat/shadowTreeDidCommit`. Review that first. `onLayout` events are emitted inline from `ShadowTree::emitLayoutEvents`, which forces `ShadowTree` to depend on `ViewProps` and `BaseViewEventEmitter`. This moves that logic into a standalone `LayoutEventEmitter` that consumes the `shadowTreeDidCommit` hook (added in the parent PR) and is registered as a commit hook by `Scheduler`. Same filter (nodes with an `onLayout` prop), same timing (during commit), same `BaseViewEventEmitter::onLayout` call — behavior is unchanged. After this, `ShadowTree` no longer references view props or event emitters, and the per-commit layout-change signal is shared with `ResizeObserver` instead of being duplicated. ## Changelog: [INTERNAL] [CHANGED] - Emit `onLayout` from a `LayoutEventEmitter` commit hook instead of inline in `ShadowTree` Pull Request resolved: https://github.com/react/react-native/pull/57722 Test Plan: Behavior-preserving refactor. Existing `onLayout` tests pass and `ShadowTree` no longer includes `ViewShadowNode`/`ViewProps`. Verified in rn-tester that `onLayout` still fires on mount and on size changes. Reviewed By: christophpurrer Differential Revision: D114045065 Pulled By: javache fbshipit-source-id: 5b887537ad3e947f3a352fb9dd10b7d124f669df |
||
|
|
e55da68478 |
Add new hook shadowTreeDidCommit (#57721)
Summary: > Stack 1/3 — parent: `main`. Followed by `LayoutEventEmitter` → `ResizeObserver`. `ShadowTree` already computes the set of nodes whose layout changed on every commit (`affectedLayoutableNodes`), but today it's only consumed internally by `ShadowTree::emitLayoutEvents`. This adds a `shadowTreeDidCommit(shadowTree, rootShadowNode, affectedLayoutableNodes)` method to `ShadowTreeDelegate` and `UIManagerCommitHook` so other systems can react to layout changes after a commit. `ShadowTree` calls it once the new revision is installed, and `UIManager` forwards it to the registered commit hooks. Both declarations have no-op defaults, so nothing observes the hook yet — this is inert on its own. It's the shared signal the next two PRs build on (extracting `onLayout` emission, and `ResizeObserver`), so neither has to re-derive which nodes changed layout. ## Changelog: [INTERNAL] [ADDED] - Add `shadowTreeDidCommit` commit hook exposing the nodes with layout changes after each commit Pull Request resolved: https://github.com/react/react-native/pull/57721 Test Plan: No behavior change: the hook has no-op defaults and no consumer in this PR. Existing Fabric commit/mounting tests pass. Reviewed By: mdvacca Differential Revision: D114044722 Pulled By: javache fbshipit-source-id: 2a556006f7b66a967aea5559cf10273c61aabcbe |
||
|
|
9da1a01153 |
Wire the Android pull model in C++ behind the feature flag (#57579)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57579 Makes `enableMountingCoordinatorPullModelAndroid` functional. With the flag on, the commit thread only signals transaction availability and the UI thread pulls and applies at mount time (matching iOS/macOS); with the flag off (default), behavior is byte-for-byte identical. - `schedulerShouldRenderTransactions`: notifies via JNI (`FabricMountingManager::onTransactionAvailable`) instead of pulling and building the batch. - `schedulerDidFinishTransaction`: no-op under the pull model. - `FabricUIManagerBinding::pullAndExecuteTransaction` (new JNI method): pulls the surface's transaction on the UI thread and runs `executeMount`. - `FabricMountingManager::executeMount`: gains a `synchronous` mode that executes the batch directly on the UI thread. - The accumulation sites remain gated on `enableAccumulatedUpdatesInRawPropsAndroid`; the pull model requires that flag to be co-enabled, since a pull may collapse several commits into one diff and therefore needs complete accumulated rawProps. ## Changelog: [Android] [Added] - Wire the pull-model mounting path in C++ behind `enableMountingCoordinatorPullModelAndroid` Reviewed By: christophpurrer Differential Revision: D112309053 fbshipit-source-id: 6e3ca5b8fbc8db647356ac555890c2e465890100 |
||
|
|
a44d68ec6d |
Split Android build from release publishing (#57714)
Summary: This should reduce the publishing time needed for `publish_react_native` by parallelizing Android & iOS build ## Changelog: [INTERNAL] - Pull Request resolved: https://github.com/react/react-native/pull/57714 Test Plan: N/A Reviewed By: cipolleschi Differential Revision: D113889729 Pulled By: cortinico fbshipit-source-id: aaa66a3cb9ce5c22bf15780503cf9a063c4b2725 |
||
|
|
00e27b7322 |
PlatformColor lazy fallback: honor a raw-color fallback on Android (native) (#57655)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57655 An implementation for the RFC in https://github.com/react-native-community/discussions-and-proposals/pull/1008 Wires up the Android native color resolver (Fabric) to honor the lazy `{fallback}` carried by `PlatformColor(...)`. A later diff adds the JS argument that emits it, so on its own this change is a no-op for existing call sites. To tell a genuine miss apart from a token that resolves to transparent black (ARGB 0): - `FabricUIManager.getColor` now returns a boxed `Nullable Integer` — `null` means no resource path resolved. The Fabric C++ `PlatformColorParser.h` reads that boxed result over JNI, caches the explicit-miss signal, and on a miss parses the raw fallback string with the shared CSS color parser (`parseCSSProperty<CSSColor>`), matching the iOS Fabric path. The color object is now read as a `map<string, RawValue>` because it mixes an array (`resource_paths`) with an optional string (`fallback`). - `NativeDrawable` carries an optional `colorFallback` alongside `resource_paths` so ripple drawables degrade to the fallback too. Generated files (`ReactAndroid.api` and the `ReactAndroid*Cxx.api` snapshots) are regenerated to match the new public signatures. Changelog: [Internal] - PlatformColor: Android native support for a lazy raw-color fallback Reviewed By: mdvacca, javache Differential Revision: D113329136 fbshipit-source-id: ee15aea8dfed66364e99d427825e1b9ebcbdc483 |
||
|
|
2b992a0d4b |
Flush created animated nodes before handling events (#57656)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57656 Changelog: [Internal] View event handlers can run synchronously before NativeAnimated nodes created on the async path have been flushed into the active graph. Flush pending async-created nodes on the render thread before evaluating view-driven animation events, so focus-driven animations can resolve their value nodes on the first native focus event instead of needing another runloop/event. Reviewed By: zeyap Differential Revision: D111257551 fbshipit-source-id: d5b7322cfce90b820ccf06422f1c830255bea3b3 |
||
|
|
0fc76bb527 |
Fix TS exactOptionalPropertyTypes compatibility for generated types (#57628)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57628 NOTE: Patches over a `flow-api-translator` bug, which I'll fix upstream later. We need to pick this to `0.87-stable` to resolve user integration issues. **Context** TypeScript's `exactOptionalPropertyTypes` flag (strict mode) creates a distinction between `foo?: T` and `foo?: T | undefined`. ```js // Flow's semantics interface Props { onRefresh?: () => void; } const a: Props = { onRefresh: undefined }; // ✅ ok ``` ```ts // TypeScript with exactOptionalPropertyTypes: true (i.e. strict mode) interface Props { onRefresh?: () => void; } const a: Props = { onRefresh: undefined }; // ❌ error interface PropsFixed { onRefresh?: (() => void) | undefined; } const b: PropsFixed = { onRefresh: undefined }; // ✅ ok ``` With this added strictness in TypeScript, our generated types via `flow-api-translator` could create downstream type incompatibility in apps. **This diff** Patches the above issue in React Native's Flow → TS `types_generated/` pipeline. We transform all instances to the wider `foo?: T | undefined` format, for maximum compatibility. **Notes** `foo?: T [| undefined]` **remains stripped** in the API snapshot (existing transform with the aim of a concise format). There is a net, nonfunctional snapshot diff around function members, which (as a positive result) are re-ordered. Changelog: [General][Fixed] - **Strict TypeScript API**: Optional property types are now widened to explicitly include `| undefined` for `exactOptionalPropertyTypes` compatibility Reviewed By: cipolleschi Differential Revision: D113030161 fbshipit-source-id: 3ab005edab6b80b18fbb9ae7125ba56e3bd94195 |
||
|
|
60fac2e14d |
Fix post-release workflow failures (#57627)
Summary: Fixes the three post-release failures from the [0.87.0-rc.2 publish run](https://github.com/react/react-native/actions/runs/29775529785/): - Keep Flow annotations in `verifyArtifactsAreOnMaven.js` as comments so `actions/github-script` can load the file with plain Node. - Pin the Podfile lock workflow to `macos-15`, which provides the configured Xcode 16.4 version. - Use the canonical `react/react-native` owner for release asset API operations so upload POST requests are not redirected from the former owner. ## Changelog: [INTERNAL] [FIXED] - Fix post-release Maven verification, Podfile lock, and release asset jobs Pull Request resolved: https://github.com/react/react-native/pull/57627 Test Plan: ```sh node --check .github/workflow-scripts/verifyArtifactsAreOnMaven.js node --check scripts/releases/upload-release-assets-for-dotslash.js yarn jest .github/workflow-scripts/__tests__/verifyArtifactsAreOnMaven-test.js scripts/releases/__tests__/upload-release-assets-for-dotslash-test.js --runInBand ``` 13 tests and 15 snapshots pass. Reviewed By: zeyap Differential Revision: D113032768 Pulled By: cipolleschi fbshipit-source-id: df5f493603e6c25157fd7660b54aa409dcb742e7 |
||
|
|
89300acb9c |
Migrate Node.js builtin imports to node: scheme (#57611)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57611 Directly follows D112733139 (Metro). **Motivation** - New Node builtins are *only* available under the prefix (e.g. `node:sqlite`), as this allows Node to introduce them without ecosystem-breaking changes, so this is the direction of travel and the only choice that'll allow consistency. - Encouraging them and grouping them separately makes it easier to reason about a module's 3rd party dependencies. Changelog: [Internal] Reviewed By: robhogan Differential Revision: D112803684 fbshipit-source-id: 40668d746a7151b3aa4800ff8af997902a18d198 |
||
|
|
38611186f5 |
fix(iOS): make jsinspector-modern tracing state types move-only for Swift C++ interop (#57605)
Summary: The nightly-tests job `[ios] react-native-unistyles` fails on Xcode 26.3 with: ``` error: no matching function for call to '__construct_at' note: in instantiation of member function 'std::vector<...RuntimeSamplingProfile>::vector' requested here note: in implicit copy constructor for 'facebook::react::jsinspector_modern::tracing::TraceRecordingState' first required here ``` while compiling the **Swift** files of the Unistyles pod. The same failure hits any library built with Swift C++ interop (`-cxx-interoperability-mode=default`) — in practice, every Nitro-based library — against the prebuilt React Native core. ### The error `TraceRecordingState` and `HostTracingProfile` hold `std::vector`s of move-only types (`RuntimeSamplingProfile` and `FrameTimingSequence` explicitly delete their copy constructors). Here's the C++ subtlety: `std::vector<T>`'s copy constructor is **declared for every `T`** — it only becomes ill-formed when *instantiated*. So the implicit copy constructors of these two structs are not implicitly deleted; they exist as declared-but-broken constructors that hard-error the moment anything asks for a copy. ### Why React Native compiles fine today Nothing in RN ever asks. Every usage passes these types by reference; the single constructions move. A pure C++ (or ObjC++) build never instantiates the implicit copy constructors, so this code has always compiled — and always would, no matter how much C++ CI you throw at it. The defect is unobservable from within C++. ### What fails, and why now The prebuilt-core headers now ship as real clang modules. A Swift target with C++ interop imports them (directly or transitively — e.g. via a module member whose `#ifdef __cplusplus` body opens because interop builds modules with C++ enabled), and Swift's ClangImporter surfaces the C++ value types to Swift as copyable. When the consumer's generated interop code then uses such a type as a Swift value — for a Nitro-based library, the nitrogen-generated `*_cxx.swift` bridging does exactly this — the compiler **synthesizes a copy of the type, instantiating the ill-formed implicit copy constructor**. That is the "ask" that plain C++ never makes; on Xcode 26.3 it hard-errors the entire module import, killing every Swift file in the consumer. (Newer Swift toolchains treat such types as non-copyable instead of failing.) Before the prebuilt-modules work there was no Swift-visible module containing these headers, so no interop consumer ever imported these types — which is why this surfaces now despite the C++ being unchanged. We deliberately did **not** fix this by removing headers from the module maps: the guarded-C++-in-modules pattern is shared by ~30 legitimately modular headers and is benign in all but this one shape, and experiments showed the type is reachable through multiple independent module surfaces (removing one member just moved the error to the next path). ### The fix Declare the truth: make both types explicitly move-only. ```cpp TraceRecordingState(const TraceRecordingState &) = delete; TraceRecordingState &operator=(const TraceRecordingState &) = delete; TraceRecordingState(TraceRecordingState &&) = default; TraceRecordingState &operator=(TraceRecordingState &&) = default; ``` With the copy constructor explicitly deleted, Swift's importer sees a non-copyable type and imports it as such instead of instantiating a broken copy. It is also simply more correct C++: these types were never copyable in practice, and the explicit deletion turns any future accidental copy into a clear compile error at the call site instead of a template backtrace. Declaring special members makes `HostTracingProfile` a non-aggregate, so its one designated-initializer construction site (`HostTargetTraceRecording.cpp`) is converted to member-wise assignment. A sweep of the affected header surface (`std::vector`/`std::map`/`std::deque` of move-only element types) found exactly these two types; a follow-up adds a `headers-verify.js` gate that imports the shipped modules under Swift C++ interop at prebuild time, so the next type with this shape fails RN's own CI instead of community nightlies. ## Changelog: [IOS] [FIXED] - Fix Swift C++-interop build failure (implicit copy constructor of TraceRecordingState/HostTracingProfile) for libraries using cxx interop with prebuilt React Native core Pull Request resolved: https://github.com/react/react-native/pull/57605 Test Plan: On a fresh RN-nightly app with stock `react-native-unistyles@3.3.0` + `react-native-nitro-modules`, prebuilt core (`RCT_USE_RN_DEP=1 RCT_USE_PREBUILT_RNCORE=1`), Xcode 26.3: - **Red**: stock headers reproduce the CI failure exactly (`__construct_at` → `TraceRecordingState`). Fixing only `TraceRecordingState` then surfaces the identical failure on `HostTracingProfile` — confirming the shape, not the type, is the bug. - **Green**: with both headers fixed (stock module maps, nothing else changed): BUILD SUCCEEDED — zero `__construct_at`, zero `shadowNodeFromValue`, zero module errors. - All RN-internal usages audited: references and moves only; no behavior change. Plain C++/ObjC++ compilation unaffected by construction. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed By: fabriziocucci Differential Revision: D112802806 Pulled By: cipolleschi fbshipit-source-id: d2ae201c1fbb4de040f51ebc88263c7347b5979b |
||
|
|
566c7ac2d1 |
fix: stabilize CXX API generator on MacOS, make parser indempotent for nested enums (#57536)
Summary:
Fixes C++ API snapshot generation failures for nested enums and macOS/Linux output drift due to platform-dependent behavior, in the CXX API generator.
### Problems
1. Duplicate enums: Doxygen parses codegen `EventEmitters.h` twice (direct codegen input + again via `.mm` includes). The parser raised `RuntimeError: Identifier OnOrientationChangeOrientation already exists in scope ModalHostViewEventEmitter` on `ReactApple*` views.
2. Inconsistent behaviour on MacOS vs Linux:
- for codegen component aliases (`ConcreteComponentDescriptor`, `ConcreteViewShadowNode`), macOS Doxygen emits hybrid XML definitions (`typedef Type Name = Type`) while Linux CI emits `using Name = Type`. The parser keyed off `definition.startswith("typedef")`, so identical source produced different `.api` output per platform.
- `CASE_SENSE_NAMES = SYSTEM` follows the host OS default (case-insensitive on macOS, case-sensitive on Linux), causing inconsistent symbol resolution.
### Resolution
- `.doxygen.config.template` files: set `CASE_SENSE_NAMES = YES` for deterministic name matching across macOS and Linux.
- `snapshot.py`: `create_enum()` returns an existing enum scope instead of raising when the enum is already registered.
- `builders.py`: `create_enum_scope()` to skip enums that already exist; `get_typedef_member()` to normalize Doxygen’s `typedef ... = ...` form to `using` so the output format is unified.
## Changelog:
[INTERNAL] [FIXED] - Fix C++ API snapshot generation crash on duplicate codegen enums
[INTERNAL] [FIXED] - Fix platform-dependent inconsistent CXX API generator output
Pull Request resolved: https://github.com/react/react-native/pull/57536
Test Plan:
- [x] `yarn cxx-api-build` completes on macOS (tested locally)
- [x] `yarn cxx-api-validate` passes (tested locally on MacOS & on the Linux CI)
- [x] `validate-cxx-api-snapshots` passes on Linux (tested on CI)
Reviewed By: j-piasecki
Differential Revision: D112793182
Pulled By: coado
fbshipit-source-id: ada818451fb1207d965c1bed4fb5ce23ee2fe00f
|
||
|
|
9f7af3e088 |
Clean up network inspection feature flags (#57577)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57577 Network Inspection has been Stage 4 / rolled out since 0.83. Circle back to remove feature flags from the codebase. **Changes** - Remove `enableNetworkEventReporting` and `fuseboxNetworkInspectionEnabled` flag definitions. - Remove all feature guards in code. - Update tests (add mocks). - (fbsource) Remove all overrides. Changelog: [Internal] Reviewed By: cortinico Differential Revision: D111696522 fbshipit-source-id: 4c6ba9a6d5abf2770b9b485556602059d1c24b59 |
||
|
|
6aa147f6c9 |
feat(iOS): ReactNativeDependenciesHeaders sidecar + pure-RN ReactNativeHeaders, published to Maven (#57442)
Summary: Step 2 of the prebuilt-deps roadmap: ship the deps headers as a **SwiftPM-ready, self-contained artifact** and make every header namespace have exactly **one physical home**. 1. **New artifact: `ReactNativeDependenciesHeaders.xcframework`** — the binary `ReactNativeDependencies.xcframework` is framework-type, so its root `Headers/` is invisible to SwiftPM binaryTargets (`HeadersPath` is rejected on framework entries; verified empirically). The deps prebuild now emits a headers-only library-type sidecar (stub archives + per-slice `Headers/` + `HeadersPath` — the exact `ReactNativeHeaders` recipe, factored into a shared `headers-xcframework.js` emitter) carrying all seven deps namespaces incl. SocketRocket, with slice parity derived from the binary artifact's Info.plist. Ships inside the deps tarball *and* standalone. 2. **`ReactNativeHeaders` goes pure-RN** — the R2 relocation of deps namespaces (and the `DEPS_NAMESPACES_NOT_RELOCATED` SocketRocket exclusion list) is deleted. Relocated copies are what enabled the SocketRocket dual-copy regression (duplicate `interface` / poisoned module graph under `use_frameworks!`); that bug class is now structurally impossible. Headers gate flipped: deps namespaces must be **absent** from RNH; the sidecar emitter enforces set-equality with `DEPS_NAMESPACES` fail-closed in both directions. On the CocoaPods side, a new `ReactNativeDependenciesUtils.configure_aggregate_xcconfig` injects the deps pod's `Headers/` globally (aggregate + every pod target), mirroring the rncore injection — this replaces the folly/glog resolution pods previously got via the flattened `React-Core-prebuilt/Headers`. 3. **CI: prebuilt + dynamic-frameworks lane** — the regression's exact config had no coverage (the `test-ios-rntester` action hard-coupled `use-frameworks:true` to source builds). New `use-prebuilds` input; `test_ios_rntester`'s dynamic cells now consume the workflow-built prebuilt artifacts. 4. **Maven publishing** — `ReactNativeHeaders` and `ReactNativeDependenciesHeaders` publish standalone on `react-native-artifacts` (classifiers `reactnative-headers-*`, `reactnative-dependencies-headers-*`); `verifyArtifactsAreOnMaven` now HEAD-checks every classifier tarball instead of only the POM. Stacked on https://github.com/react/react-native/issues/57440. The SwiftPM preview (https://github.com/react/react-native/issues/57332) rebases on top and wires the sidecar as its 5th binaryTarget. ## Changelog: [IOS] [CHANGED] - Prebuilt artifacts: ReactNativeHeaders is pure-RN; third-party deps headers ship in the new ReactNativeDependenciesHeaders.xcframework sidecar (and the ReactNativeDependencies pod), published standalone to Maven Pull Request resolved: https://github.com/react/react-native/pull/57442 Test Plan: - Headers gate: include-health, structural (deps absent from RNH, byte-matched module maps), and compile smokes (React module + 14 namespace modules + Expo-shape ObjC++/Swift fixtures vs the deps include path) — ALL PASSED - jest: 33/33 (`scripts/ios-prebuild/__tests__`, incl. new sidecar set-equality tests) - ESLint (`--max-warnings 0`), Prettier, Flow (`yarn flow-check`): clean - E2E (locally built artifacts): rn-tester prebuilt static ✅, prebuilt `USE_FRAMEWORKS=dynamic` ✅ (the regression config — verified `React-Core-prebuilt/Headers` contains no deps namespaces and the deps pod serves all seven), helloworld static ✅, source-core + prebuilt-deps ✅ (React compiled from source resolves folly via the deps pod), source-mode control with unchanged dependency graph ✅ - Sidecar inspected: per-slice `HeadersPath`, 7 namespaces, slice parity with the binary - Publication validated end-to-end with `publishReleasePublicationToMavenLocal`: all 12 files + POM land with the expected classifier names 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed By: fabriziocucci Differential Revision: D111449462 Pulled By: cipolleschi fbshipit-source-id: e1217d14c0588d00a207622d346c9e6f4705a95d |
||
|
|
a8156acf8b |
feat(iOS): serve third-party deps from the prebuilt ReactNativeDependencies pod via dependency-only facades (#57440)
Summary: Step 1 of making the prebuilt `ReactNativeDependencies` pod the **single header authority** for the third-party C/C++ deps (RCT-Folly, glog, boost, DoubleConversion, fmt, fast_float, SocketRocket) in prebuilt-deps mode. Today the deps **binary** replaces the source pods' code, but the pod still `s.dependency`'s the real source pods and borrows their headers via `$(PODS_ROOT)/<pod>` search paths. That split header authority is the dual-copy bug class behind the 2026-07-03 SocketRocket regression (`duplicate interface` under `use_frameworks!` — SocketRocket's ObjC headers have no include guards). Three commits: 1. **fix(cocoapods): harden prebuilt-deps header search paths and artifact handling** — `rndependencies.rb`'s `||= [] << path` only added the deps header search path when `HEADER_SEARCH_PATHS` was unset (silently dropped otherwise); normalize and always append, and point at the pod-local flattened `Headers/`. `ReactNativeDependencies.podspec` `prepare_command` now fails closed (`exit 1`) instead of silently producing a no-link pod. `reactNativeDependencies.js` no longer deletes + re-downloads a locally staged artifact that lacks a version marker. 2. **feat(cocoapods): dependency-only facades for third-party pods in prebuilt-deps mode** — in prebuilt-deps mode the real source pods are not declared, so a community podspec's hardcoded `s.dependency "RCT-Folly"` would resolve from trunk and compile from source next to the prebuilt binary. `RNDepsFacades` generates dependency-only local facade podspecs (no sources, no headers, a single dependency on `ReactNativeDependencies`); versions/subspecs are derived from the real podspecs in `third-party-podspecs/` (SocketRocket synthesized fail-closed from `socket_rocket_config`). Full contract documented in `scripts/cocoapods/__docs__/prebuilt-deps.md`. 3. **feat(ios-prebuild): SocketRocket privacy manifest + Xcode 26 header layout** — embed an RN-authored, accurate-empty `PrivacyInfo.xcprivacy` for SocketRocket (upstream ships none), and stage flat public headers into `include/` so Xcode 26's SwiftPM accepts the header layout. Stacked on https://github.com/react/react-native/issues/57305 (base: `chrfalch/prebuilt-resources`); the SwiftPM preview (https://github.com/react/react-native/issues/57332) rebases on top of this. Follow-up (separate PR): headers-only `ReactNativeDependenciesHeaders.xcframework` sidecar so SPM auto-serves the deps namespaces and `ReactNativeHeaders` goes pure-RN. ## Changelog: [IOS][CHANGED] - Prebuilt-deps mode: serve third-party headers from the ReactNativeDependencies pod itself and resolve community `s.dependency` on RCT-Folly/glog/boost/etc. via dependency-only facade pods Pull Request resolved: https://github.com/react/react-native/pull/57440 Test Plan: E2E matrix (2026-07-06, locally built deps artifact via `prepare-ios-prebuilds.js`): - rn-tester, prebuilt core + prebuilt deps, static linkage — builds - rn-tester, prebuilt core + prebuilt deps, `USE_FRAMEWORKS=dynamic` — builds (the SocketRocket-regression config) - helloworld (private), prebuilt core + prebuilt deps, static — builds - source-mode control: no facades generated, `Podfile.lock` identical to baseline 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed By: fabriziocucci Differential Revision: D111449257 Pulled By: cipolleschi fbshipit-source-id: ace5716868d126a08721200efd640e903b191658 |
||
|
|
cfd0e359a6 |
Add CDP support for WebSocket events (Android) (#57541)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57541 **Context** This stack implements WebSocket event debugging for the Network panel in React Native DevTools. **This diff** Follows the iOS implementation: reports the same six `Network.webSocket*` CDP events through the shared `NetworkReporter` C++ core, gated behind the same `fuseboxWebSocketEventsEnabled` feature flag. **Changes** - `InspectorNetworkReporter` + `JInspectorNetworkReporter`: New JNI-backed `reportWebSocket*` methods bridging to the shared `NetworkReporter` C++ core. Payload conversion costs (base64) are deferred until a debugger is attached. - `WebSocketModule`: Reports connection lifecycle, real handshake headers (from the OkHttp `Request`/`Response`), and sent/received messages, gated behind the same feature flags as iOS. Changelog: [Internal] Reviewed By: GijsWeterings Differential Revision: D111561994 fbshipit-source-id: 735ad0e47fc4e9e943b1092fbc23846ff9baf963 |
||
|
|
020512bf15 |
Add CDP support for WebSocket events (iOS) (#57543)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57543 **Context** This stack implements WebSocket event debugging for the Network panel in React Native DevTools. Discussed with Expo earlier this year, we want upstream parity before Expo adopts the 1P Network panel. This is a comparatively small surface: 6 CDP events, no CDP methods. **Minimum goal**: Parity with Expo's current WS event coverage. We improve on this with Request Initiator support in D111561995. **This diff** Adds first-party reporting for six of the seven WebSocket CDP events (`webSocketCreated`, `webSocketWillSendHandshakeRequest`, `webSocketHandshakeResponseReceived`, `webSocketFrameSent`, `webSocketFrameReceived`, `webSocketClosed`), mirroring the layering of the existing HTTP pipeline: - **C++ core**: WebSocket CDP types and reporting APIs through `CdpNetwork` → `NetworkHandler` → `NetworkReporter`. Compiled to no-ops in production builds; inert unless the Network domain is enabled. - **iOS**: A new `RCTInspectorWebSocketReporter` bridges `RCTWebSocketModule` to `NetworkReporter`, reporting connection lifecycle, real handshake headers, and sent/received messages. Android follows separately. - **Gating**: A new `fuseboxWebSocketEventsEnabled` feature flag (experimentation, default off), checked alongside `enableNetworkEventReporting`. **Notes** - **Omitted**: [`Network.webSocketFrameError`](https://chromedevtools.github.io/devtools-protocol/tot/Network/#event-webSocketFrameError). Neither SocketRocket nor OkHttp exposes a frame-scoped error; connection failures are terminal and already reported via `webSocketClosed`. Cheap to bolt on once a genuine per-message error source exists (e.g. OkHttp `send()` returning false). - **Omitted**: Optional [`WebSocketResponse`](https://chromedevtools.github.io/devtools-protocol/tot/Network/#type-WebSocketResponse) fields (`headersText`, `requestHeaders`, `requestHeadersText`) — we report structured headers only; raw handshake text isn't available from OkHttp on Android. - No `performance-timeline` integration — WebSocket events report to CDP only; unlike HTTP, there is no `PerformanceResourceTiming` counterpart. **Rollout plan** (New `enableNetworkEventReporting` flag) - 0.88 - Canary channel - 0.89 - Stable channel Changelog: [Internal] Reviewed By: GijsWeterings, javache Differential Revision: D111561998 fbshipit-source-id: ecd46ddb6baa91f1c1d77598ee62b2e410b45bd9 |
||
|
|
2dfdcbf082 |
Fix local release testing: publish with --tag for prerelease versions (#57552)
Summary: `scripts/e2e/init-project-e2e.js` publishes in-repo packages to the local Verdaccio proxy with `npm publish`, but omits `--tag`. **npm ≥ 11** (bundled with Node 24+) refuses to publish a prerelease version (e.g. `0.87.0-rc.0`) without an explicit tag: ``` npm error You must specify a tag using --tag when publishing a prerelease version. ``` This breaks `yarn test-release-local` immediately at the publish step on any machine using npm ≥ 11. ## Fix Pass an explicit `--tag react-native-e2e`. This is a throwaway local registry and the install step pins the **exact** version, so the dist-tag value is not significant for resolution. ## Changelog [Internal] - Fix `test-release-local` publishing on npm ≥ 11 Pull Request resolved: https://github.com/react/react-native/pull/57552 Test Plan: `yarn test-release-local -t RNTestProject -p iOS` now gets past the publish step on npm 11 (previously failed immediately at `npm publish`). Reviewed By: christophpurrer Differential Revision: D111995913 Pulled By: zeyap fbshipit-source-id: 107a96d5c4317129228f783ad52bffe1578d1138 |
||
|
|
042833e699 |
Extend check-packages-test to validate additional required fields (#57510)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57510 Extend `check-packages-test.js` to catch two more classes of manifest drift: published packages missing required fields, and packages under `private/` missing the `private` flag. **Motivation** Inspired by https://github.com/react-native-community/template/pull/241 — avoid a missing field blocking a future RN package publish. **Other changes** - To satisfy the new `files` requirement, add an explicit `files` allowlist to the four config packages that lacked one. As a side effect, this saves some `__tests__` files from being distributed. Changelog: [Internal] Reviewed By: cortinico Differential Revision: D111471314 fbshipit-source-id: e13082d2282128936e22e3a7580b5e434c3b41b2 |
||
|
|
0ed6c560da |
Consolidate package checks into one Jest test (#57509)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57509 Simplify/unify various mechanisms for the monorepo's package invariants. These checks were previously scattered: - `private/monorepo-tests` package (manifest field checks) - `.github/workflow-scripts/lint_files.sh` (`.npmignore` ban) - `react-native/eslint-plugin-monorepo` (manifest field checks — duplicated) This folds everything into `scripts/monorepo-tests/__tests__/check-packages-test.js`. A plain Jest test is the most extensible home for future checks. Changelog: [Internal] Reviewed By: cortinico Differential Revision: D111471313 fbshipit-source-id: ea50abd8148d92e70605dd7f809654348e3c0080 |
||
|
|
c948b61c05 |
Enable Strict TS API by default (#57490)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57490 See [**RFC0894: Removing deep imports from react-native**](https://github.com/react-native-community/discussions-and-proposals/pull/894) This is the **big switch** to enable the Strict TypeScript API (generated types + single index entry point) by default in React Native. **Opt-in → opt-out** After this change, the main `react-native` package resolves its `"types"` entry points only to `types_generated/index.d.ts` — with no other subpaths available. The new `"react-native-legacy-deep-imports"` condition maps to legacy `types/` and `Libraries/*.d.ts` sources. **Impact limitation**: For this stage of rollout, the `"default"` condition continues to resolve to source files. Only TypeScript is affected. **How to opt out** Opposite of today's opt-in, which we will update in [the docs](https://reactnative.dev/docs/strict-typescript-api). Again, the only impact area today is **TypeScript**. ```json5 // tsconfig.json { "extends": "react-native/typescript-config", "compilerOptions": { ... "customConditions": ["react-native-legacy-deep-imports"] } } ``` **Other changes** - Drop `react-native/typescript-config/strict` entry point, update README. - Update `__typetests__`. **Rollout plan** **Target release: 0.87**. This and the contributing stack will be cherry picked for RC1. - We've conducted testing against 100+ real Expo codebases, giving us the confidence that we've reduced breaking changes enough that the vast majority of RN codebases can migrate. - The Strict API includes a number of **intentional breaking changes**, and docs have been kept up to date. - We're shipping a `/migrate-to-strict-api` skill to migrate via agents, see https://github.com/react-native-community/skills/pull/3. **What's improved since 0.80?** Since the initial opt-in launch of the Strict API in 0.80, we've been making continuous improvements over the last year to get our generated types into a widely launchable state. Most notably: - 21+ new/updated root APIs and fixes due to community feedback ([discussion](https://github.com/react-native-community/discussions-and-proposals/discussions/893), [PRs](https://github.com/react/react-native/pulls?q=is%3Apr%20label%3A%22JS%20API%20stabilization%20(1.0)%22%20is%3Aclosed)). - Upstream encapsulation blocker in TypeScript, fixed in 6.0 (https://github.com/react/react-native/issues/53565). - Tailwind/Uniwind compatibility (`interface` types for props). - `*Instance` ref type exports for all built-in components (https://github.com/react-native-community/discussions-and-proposals/pull/1003). - Fixes to previously mistyped, high impact APIs, such as `Appearance`. - New subpath entry points for `asset-registry`, `setup-env`, and others. - Refinements to doc comments/type translation build. **Rollback plan** Revert this diff. IMPORTANT: We'll adopt a policy of **super-eager rollback**, if there are any unsolvable issues during the RC phase. Changelog: [General][Breaking] - React Native's default JavaScript API is now the [Strict TypeScript API](https://reactnative.dev/docs/strict-typescript-api). Use `customConditions: ["react-native-legacy-deep-imports"]` to opt out. Reviewed By: cortinico Differential Revision: D110458670 fbshipit-source-id: 4b0e0b458a5f895f783d6d936e7b11ccff2df076 |
||
|
|
6cfde8f296 |
Move AssetRegistry implementation into main package, expose as public API (#57369)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57369 **Problem** The separate `react-native/assets-registry` package includes a longstanding ecosystem footgun. `registry.js` holds asset state in a module-scoped variable, which makes the package a stateful singleton: exactly one instance must exist per JS runtime, or registration and lookup diverge. We provide no guarantee that this singleton requirement holds: - The install layout — how the package manager dedupes packages in `node_modules` — decides how many copies exist, and `react-native`'s exact-version pin means third-party ranges never dedupe against it. Effects: - **Consumers silently break**: `expo-asset` and `expo-image` can land on a second copy: assets register in one, resolve as `undefined` from the other. Expo neutralizes this with a shim in `expo/cli` that redirects every registry import to a single virtual module — bare React Native + Metro has no such protection. - **This blocks 1.0**: The ecosystem can't move from exact-version lockstep to semver ranges until stateful packages like the asset registry are safe to duplicate. Today, relaxing the pin would turn a latent footgun into a common one. **To solve this**, move towards (but not quite yet) deleting `react-native/assets-registry`, in favour of a replacement `AssetRegistry` API offered directly by `react-native`. **Key changes** NOTE: **Reviewer note**: Browsing file changes on GitHub may be more focused — https://github.com/react/react-native/pull/57369/changes NOTE: Squash of https://github.com/react/react-native/pull/57233 (D108750302) and https://github.com/react/react-native/pull/57232 (D108750303) `'react-native'`: - Add new `AssetRegistry` API, along with the `PackagerAsset` and `AssetDestPathResolver` root type exports in `react-native`. - Add a new `'react-native/asset-registry'` secondary entry point — intended for Metro's `transformer.assetRegistryPath` config contract. `react-native/assets-registry`: - Update to source from this relocated implementation — fixing the duplicate install layout bug (where apps/frameworks enforce a single copy of `react-native`). **Impact** - **✅ Fixed**: Imports from either `react-native` or `react-native/assets-registry` in RN 0.87+ will be durable to duplicate package installs — Expo can remove their virtual module shim. - **✅ Fixed**: Deep import `'react-native/Libraries/Image/AssetRegistry'` dependency removed (migrated in `react-native/metro-config`). Changelog: - [General][Fixed] - **assets-registry**: `react-native/assets-registry` now shares state across duplicate installs, sourcing from a relocated implementation in the `react-native` package - [General][Added] - Add `AssetRegistry` API (replaces `react-native/assets-registry/registry`) - [General][Breaking] - `react-native/Libraries/Image/AssetRegistry` is removed. Please use the `AssetRegistry` API (apps/library code) and/or the `react-native/asset-registry` entrypoint (Metro/build configs). Reviewed By: robhogan Differential Revision: D109019622 fbshipit-source-id: 75599a94a9aba084a1266f2128c448379d1596cd |
||
|
|
07816cba56 |
Move ErrorUtils from cxxreact to jserrorhandler (#57236)
Summary: Pull Request resolved: https://github.com/react/react-native/pull/57236 Move `handleJSError` from `cxxreact/ErrorUtils.h` (header-inline) to `jserrorhandler/ErrorUtils.{h,cpp}` (declared + linked). Inverts the cyclic dep so `jserrorhandler` becomes a standalone leaf and `cxxreact:bridge` can be narrowed out of more consumers in follow-ups. The old `<cxxreact/ErrorUtils.h>` include path continues to work via a deprecated `#warning` forwarder header that includes the new location. `cxxreact:bridge` now depends on `jserrorhandler:jserrorhandler` so existing consumers of the deprecated path still link cleanly. `jserrorhandler/BUCK` drops its `cxxreact:bridge` dep (and the matching `React-cxxreact` podspec entry). Changelog: [Internal] Reviewed By: christophpurrer Differential Revision: D108786498 fbshipit-source-id: 6a34ec47665558ac9c91343253e027ed1df70e93 |