Picks up two fixes released in hermes-v260318099.0.4:
- Avoid reserve() in ConsecutiveStringStorage. Lazy compilation appends to
the string storage on every newly compiled function, and reserve() on
libc++ sizes to exactly the requested capacity rather than growing
geometrically, so repeated appends were quadratic.
- Re-land of the VariableScope rescan optimization.
See https://github.com/reactwg/react-native-releases/issues/1430
The revert of the NSData argument path moved codegen back to NSMutableData
but did not refresh the recorded snapshots, leaving Validate C++ API
Snapshots red on the release branch.
Changelog: [Internal]
Summary:
The iOS prebuild (`node scripts/ios-prebuild`) stages React Native's headers into `packages/react-native/.build/headers` as hard links, and skipped any target that already existed.
When changing the original source files, these hard links can become stale and errors like this can occur:
```
Libraries/LinkingIOS/RCTLinkingManager.mm:91:7: error: use of undeclared identifier 'RCTIsSceneDelegateApp'
```
This is a problem that contributors will see - not regular users, but the fix helps with strange error messages.
## Changelog:
[INTERNAL] [FIXED] - Repair stale staged headers in the iOS prebuild instead of compiling against a previous checkout's copies
Pull Request resolved: https://github.com/react/react-native/pull/58596
Test Plan:
✅ New unit tests, 16 cases in `packages/react-native/scripts/ios-prebuild/__tests__/setup-test.js`, using real temporary directories rather than an `fs` mock, since the defect is about inodes.
End to end on an Xcode 27 checkout:
- Replaced a staged header with a copy, so it kept the same contents but a different inode. `node scripts/ios-prebuild -s -f Debug` restored it to the source inode and logged `Linked React/Base → .build/headers/React`. The previous code skipped it.
- Ran setup a second time with nothing changed: no file was relinked, and nothing was logged.
- Confirmed the three colliding targets still resolve to the same source as before the change, by inode.
- `node scripts/ios-prebuild -b -f Debug -p ios-simulator` → `** BUILD SUCCEEDED **`.
Reviewed By: cipolleschi
Differential Revision: D120740409
Pulled By: shwanton
fbshipit-source-id: 41c1eb112c3395d6c7158d7151be7fdd92a21cef
(cherry picked from commit b935e74ad5)
Summary:
Building an app in Xcode fails when it autolinks a library that ships its own `Package.swift`, with one error per such library:
```
Missing package product 'reactnativeskia_ReactNativeSkia.ReactNativeSkia'
Missing package product 'reactnativesafeareacontext_ReactNativeSafeAreaContext.ReactNativeSafeAreaContext'
```
The same project builds fine from the command line. Two problems combine.
**The sync destroys package roots Xcode has already loaded.** Autolinking writes one symlink per self-managed library into `build/generated/autolinking/libs/<Name>`, and Xcode treats each as a local Swift package root. The generator deleted that whole directory and recreated it on every run, even when the generated output was byte-for-byte identical. Measured on a real app, across one no-op sync: the `libs/` inode changed from 1913513750 to 1913930645, `libs/ReactNativeSkia` from 1913513937 to 1913930649, while the generated `Package.swift` kept the same MD5. Recreating a package root that Xcode has already resolved is what produces the error above.
**Xcode's own bookkeeping made the sync run every time.** The "Sync SPM Autolinking" build phase re-syncs when a watched input looks newer than its stamp, and it checked each library's whole directory with `find <dir> -newer <stamp>`. Xcode writes its per-user scheme state inside that directory, at `<lib>/.swiftpm/xcode/xcuserdata/<user>.xcuserdatad/xcschemes/xcschememanagement.plist`. So Xcode's own write marked the next build stale, which triggered the destructive re-sync, which broke that build. `xcodebuild` does not write that file, which is why command-line builds were never affected.
This change makes the `libs/` tree idempotent — unchanged entries keep their inode, and entries that are no longer autolinked are pruned instead of wiped — and makes the staleness check skip `.swiftpm`.
### How to verify
In an app that autolinks a library shipping its own `Package.swift`, build in Xcode twice in a row. Both builds should succeed. Before this change the second build fails with `Missing package product`.
## Changelog:
[IOS][FIXED] - Stop SwiftPM autolinking from recreating library package roots on every sync, which broke Xcode builds of apps using libraries that ship their own Package.swift
Pull Request resolved: https://github.com/react/react-native/pull/58597
Test Plan:
**Unit tests.** `yarn test packages/react-native/scripts/spm` — 21 suites, 1009 tests pass.
New tests, written and seen failing before the fix:
- Two consecutive generation runs over an unchanged self-managed dependency keep both the `libs/` inode and each entry's inode. Failed before the fix with the same inode churn measured on the real app.
- A dependency removed between two runs leaves no symlink under `libs/`.
- The emitted staleness snippet is extracted from the generated script and executed under `/bin/bash -c` with `set -euo pipefail` against a temporary tree. A write under `.swiftpm/` is ignored; a real source change is still detected.
**Real app.** A React Native 0.87.1 app on Xcode 27 autolinking `shopify/react-native-skia` and `react-native-safe-area-context`, both self-managed. Before: `xcodebuild` succeeded, Xcode failed in about 5 seconds with the two errors above, on nearly every build.
**Not run:** the full CI matrix, and no Android-side check — this touches iOS SwiftPM tooling only.
## Scope
Deliberately minimal.
One case still re-syncs: the first time Xcode creates `.swiftpm` inside a library, that bumps the library directory's own mtime, so the build after a fresh checkout reports stale once per library. That is harmless now that the sync is idempotent.
One related item is left alone: the aggregate `Package.swift` is still rewritten on every sync even when its content is unchanged, which bumps its mtime and can make Xcode re-resolve. That is wasteful but not destructive, and it is no longer reached on an ordinary IDE build now that `.swiftpm` writes do not mark the sync stale.
Reviewed By: cipolleschi
Differential Revision: D120740029
Pulled By: shwanton
fbshipit-source-id: 3783d4cee4791ab13517d8f0dcac71f752cb7c1b
(cherry picked from commit 97cc934dc7)
Summary:
Fixes https://github.com/react/react-native/issues/57933.
`react-native/virtualized-lists` is published separately and imported `ReactNativeFeatureFlags` through `react-native/src/private/featureflags/ReactNativeFeatureFlags`, which is not listed in `react-native`'s `"exports"`. Metro therefore warned and fell back to file-based resolution whenever an app rendered a virtualized list.
Thanks huntie for the patch and the direction — this PR now applies it instead of the original approach:
- `ReactNativeFeatureFlags` is exposed on the existing private package boundary, `react-native/react-private-interface` (both the runtime getter and the `.js.flow` re-export);
- `VirtualizedList.js` and `VirtualizeUtils.js` import it from there.
No new `src/private/*` subpath is exported, and the feature-flag singleton is unchanged.
Per your review, the `scripts/monorepo-tests/__tests__/check-packages-test.js` and `scripts/shared/monorepoUtils.js` changes have been dropped — the PR is now just the patch above. Happy to look at enabling `react-native/no-deep-imports` on `virtualized-lists` as a follow-up if that's wanted.
`VirtualizeUtils.js` is included alongside `VirtualizedList.js` because it carried the same runtime deep import. The remaining occurrences are out of scope: the four in `react-native/jest-preset` are all `import type` and are erased before resolution, and the one in `VirtualizeUtils-test.js` is not shipped (`virtualized-lists` excludes `**/__tests__/**` from `files`).
## Changelog:
[GENERAL] [FIXED] - Fix the Metro package-exports warning caused by `react-native/virtualized-lists` importing an unexported React Native subpath.
Pull Request resolved: https://github.com/react/react-native/pull/57940
Test Plan:
No new test is added. The existing `virtualized-lists` suites already cover this route, because `VirtualizeUtils`/`VirtualizedList` read the flags at runtime through the new boundary.
Counterfactual — dropping only the `ReactNativeFeatureFlags` getter and its `import typeof` from `react-private-interface.js`, keeping the two `virtualized-lists` imports:
```text
TypeError: Cannot read properties of undefined (reading 'fixVirtualizeListCollapseWindowSize')
182 | let lastWillAddMore;
183 |
> 184 | if (ReactNativeFeatureFlags.fixVirtualizeListCollapseWindowSize()) {
| ^
at computeWindowedRenderLimits (packages/virtualized-lists/Lists/VirtualizeUtils.js:184:32)
at Object.<anonymous> (packages/virtualized-lists/Lists/__tests__/VirtualizeUtils-test.js:261:47)
Test Suites: 2 failed, 6 passed, 8 total
Tests: 20 failed, 1 skipped, 151 passed, 172 total
```
Restoring the getter makes it green again.
```text
$ yarn jest packages/virtualized-lists scripts/monorepo-tests packages/react-native/Libraries/ReactPrivate --runInBand
Test Suites: 9 passed, 9 total
Tests: 1 skipped, 176 passed, 177 total
Snapshots: 69 passed, 69 total
$ yarn flow-check
Found 0 errors
$ yarn lint
$ eslint --max-warnings 0 .
Done in 10.23s.
```
Reviewed By: javache
Differential Revision: D119164284
Pulled By: cortinico
fbshipit-source-id: 1e8ae3b85ae7fb28c55f207f887a97aad85b563c
(cherry picked from commit a506ed66cc)
Summary:
since https://github.com/react/react-native/issues/56646 the view registry is a `MutableIntObjectMap`; `androidx.collection` 1.4.0 to 1.4.2 corrupt `ScatterMap` and its primitive variants after remove-heavy sequences, fixed in 1.4.3 (https://issuetracker.google.com/issues/352560465). Apps resolve 1.4.0 or 1.4.2 and lose registry entries; RN then drops mount instructions and can leave stale views.
Fixes https://github.com/react/react-native/issues/58388.
## 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] [FIXED] - Bump androidx.collection to 1.4.4 to fix view registry entries getting lost in SurfaceMountingManager`
Pull Request resolved: https://github.com/react/react-native/pull/58389
Test Plan:
Reproducer: https://github.com/pawicao/rn-android-view-registry-corruption
- RN 0.87.1 app resolving androidx.collection 1.4.2: 91 `Unable to find viewState` soft exceptions on 45 tags over two 40-round runs on a Pixel 9a, and a stale view left on screen.
- Same screen with androidx.collection pinned to 1.4.4: none over 120 rounds.
Reviewed By: cortinico
Differential Revision: D119149754
Pulled By: javache
fbshipit-source-id: afb13330bac575218a07a030bb1bcd5bf7e00824
(cherry picked from commit d6a5f159c6)
## Summary:
Bump minimum Metro version to 0.87.1, within the same semver range, to ensure upgraders get the same version as fresh projects would.
Mostly fixes, including a couple of `Platform` inlining corrections. It also replaces the `image-size` dependency to clear CVE alerts.
Metro release notes: https://github.com/react/metro/releases/tag/v0.87.1
## Changelog:
[General] [Changed] - Bump Metro minimum to 0.87.1
## Test Plan:
```
yarn install --frozen-lockfile --ignore-scripts
yarn flow-check
yarn test packages/metro-config packages/community-cli-plugin packages/dev-middleware
```
(cherry picked from commit 18a22b1d59)
Relocating the hand-written declarations under types_DEPRECATED/ (#57513)
changed where the react-native-legacy-deep-imports export condition points
for ./Libraries/*. It used to resolve to ./Libraries/*.d.ts, beside the real
.js, so TypeScript's extension substitution still found modules with no
hand-written declaration. It now resolves into types_DEPRECATED/, which
contains no .js files, so those specifiers stop resolving.
Reported against 0.88.0-rc.0 with react-native/Libraries/Core/InitializeCore.
Add the old location as a second entry so the condition falls back to it.
Modules that do have a declaration keep resolving to types_DEPRECATED/
because that entry is still first.
This is applied to 0.88-stable only. 0.88 is a non-breaking release, so the
regression must not ship in it. main intentionally keeps the current
behaviour pending a deliberate decision on retiring deep imports.
getCacheKey only hashes package contents when the version ends in -main;
published releases short-circuit to the version string. The test asserted
only the main-build property, so it failed on every release branch.
Assert the correct property for each case.
Summary:
The SwiftPM manifests React Native generates — the `Autolinked` aggregate, the per-dependency synth packages, and the scaffolded community packages — hardcoded `platforms: [.iOS(.v15)]`. SwiftPM refuses to link a product whose minimum platform is above the depending target's, so any self-managed package with a higher floor could not be autolinked. Every Expo package declares iOS 16.4, so a stock Expo app on 0.87.1 fails at build time with:
```
error: The package product 'Expo' requires minimum platform version 16.4 for the iOS platform,
but this target supports 15.0 (in target 'AutolinkedAggregate' from project 'Autolinked')
```
The app itself is correct (`IPHONEOS_DEPLOYMENT_TARGET = 16.4`); the only disagreement was the generated manifests.
The floor is now the app's own `IPHONEOS_DEPLOYMENT_TARGET`, read from the injected `.xcodeproj`:
- Target selection: the injection marker's `targetUuid`, else the application target named by `--product-name`, else every application target.
- Per configuration: the target's own value, else the project-level configuration of the same name. The lowest value wins, since one manifest floor has to hold for every configuration.
- Clamped to React Native's minimum (15.1, `min_ios_version_supported`) and emitted in string form (`.iOS("16.4")`), because the `.vNN` enum form cannot express a minor version.
`setup-apple-spm.js` resolves the value once per `add`/`update`/`sync`/`scaffold` run, logs it (`iOS deployment target: 16.4 (from MyApp.xcodeproj)`), and passes it to the generator, the sync script, and the scaffolder via a new `--ios-deployment-target` flag. The flag value is sanitized at the generator boundary as well. `SCAFFOLDER_VERSION` is bumped to 20 so existing scaffolds regenerate with the new floor.
A floor set in an `.xcconfig` the configuration is based on is honored, including `#include` chains (target literal → target xcconfig → project literal → project xcconfig). A floor supplied through a build-setting variable (`$(MY_FLOOR)`) is not resolved and falls back to the minimum; the log line says so. `spm add` and `spm update` also refresh the `platforms:` element of existing scaffolded manifests (those carrying the scaffolder marker, including the pre-v20 `.iOS(.v15)` form, over the same expanded dependency set the scaffolder uses, transitive SwiftPM dependencies included) without creating new scaffolds or touching anything else in them.
Known limitation, documented in `scripts/spm/__docs__/spm-scripts.md`: the project file is not one of the auto-sync build phase's staleness inputs, so a deployment target changed after `spm add` takes effect on the next `spm update`.
The default floor moves from 15.0 to 15.1 for apps without a readable project setting; React Native already requires 15.1 for the app target, so this is not observable.
## Changelog:
[IOS] [FIXED] - SwiftPM: generated manifests derive their iOS platform floor from the app's `IPHONEOS_DEPLOYMENT_TARGET` instead of hardcoding iOS 15, so dependencies with a higher minimum (e.g. Expo, 16.4) can be autolinked
Pull Request resolved: https://github.com/react/react-native/pull/58379
Test Plan:
Unit tests were written first and seen failing, then made green:
```
yarn jest --no-cache -i packages/react-native/scripts/spm
Test Suites: 37 passed, 37 total
Tests: 1733 passed, 1733 total
```
New/updated coverage: `ios-deployment-target-test.js` (pbxproj reading: target-level, project-level fallback, xcconfig chain with `#include` and `<group>` anchoring, minimum across configurations, marker/`--product-name`/all-targets selection, invalid values, clamping, unreadable project), `scaffold-package-swift-test.js` (emitted floor, refresh of existing scaffolded manifests), `generate-spm-autolinking-test.js` (aggregate and synth templates, `--ios-deployment-target` through `main()`), `scaffold-package-swift-test.js`, `sync-spm-autolinking-test.js` (flag forwarding), `setup-apple-spm-test.js` (resolution and log line).
ESLint (`--max-warnings 0`), Prettier, and Flow are clean on the touched files.
Not run here: an end-to-end Xcode build of an Expo app on this branch. The failing scenario and the fix were verified by the Expo team against 0.87.1 with the same manifest change patched in locally (build proceeds past SwiftPM resolution into compiling `Expo`).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Reviewed By: cortinico
Differential Revision: D119653699
Pulled By: cipolleschi
fbshipit-source-id: 7c69dd4d97bed719f7bbd28fb0832cdf2f151f55
(cherry picked from commit 92564f33fd)
Summary:
SPM setup scripts start with `set -euo pipefail`, but every one of them runs under `/bin/sh`. On a machine where `/bin/sh` isn't bash (e.g. dash), that fails immediately with `set: Illegal option -o pipefail`, so no SwiftPM build gets past the first build phase.
Two spots in `generate-spm-xcodeproj.js`:
- `shellScriptPhase` hardcodes `shellPath: '/bin/sh'` for every `PBXShellScriptBuildPhase` it builds (Sync SPM Autolinking, Embed React Native Flavored Frameworks, plugin-contributed phases), while their bodies are bash-only. Now sets `shellPath: '/bin/bash'`, and refreshes it on an already-injected project too.
- The "Sync SPM Autolinking" scheme pre-action runs the same kind of script, but a scheme pre-action ignores a build phase's `shellPath` entirely — Xcode reads the shell from the `ActionContent`'s own `shellToInvoke` attribute instead (default `/bin/sh`). Now sets `shellToInvoke = "/bin/bash"` on both scheme creation and when adding/refreshing the pre-action on an existing scheme.
Fixes https://github.com/react/react-native/issues/58359
## Changelog:
[IOS] [FIXED] - SwiftPM: run generated build scripts under bash instead of /bin/sh, so they don't break on a host where /bin/sh isn't bash
Pull Request resolved: https://github.com/react/react-native/pull/58362
Test Plan: `yarn test packages/react-native/scripts/spm` — 838 passed, including new cases for the refreshed `shellPath`/`shellToInvoke` on a project injected before this fix, and a scheme injected before the attribute existed.
Reviewed By: cortinico
Differential Revision: D119328611
Pulled By: cipolleschi
fbshipit-source-id: 2039a5c3c0224ca17d096f4837380b0977f4b539
(cherry picked from commit 25f48bd72f)
Summary:
An autolinked library's SwiftPM target name is also its header import prefix, so deriving it from the npm package name was wrong for most of the ecosystem (`react-native-svg` publishes `RNSVG`, not `ReactNativeSvg`) and wrong silently — no error, just headers nobody can import under the expected name.
## How:
This PR reads the podspec file on scaffolding, and will use the name from the podspec if available. In addition it deprecates the react-native.config.js `spm` section in favor of the library's `package.json` file.
### Configuration
A package's SwiftPM settings now live in `swiftpmConfig` in its `package.json`, following `codegenConfig`'s conventions: `name`, `dependencies`, `autolinkingPlugin` and `scaffold` for a library, `modules` and `denyPlugins` for an app. The `spm` block in react-native.config.js is deprecated — still read, so nothing breaks, but it warns once per file and package.json wins field by field.
### Resolving
A name resolves from `swiftpmConfig.name`, then the deprecated `spm.name`, then the podspec's `header_dir` or name, then the npm name. The podspec is thereby transitional rather than permanent: `spm scaffold` records the name it derived as `swiftpmConfig.name` in the library's package.json, so the next run needs no podspec to name it. It never overwrites a name the library already declares, never records a name guessed from the npm name, and reports what it did.
### Failsafety
A prefix Swift cannot spell is normalized to the identifier SwiftPM would compile it as, with a warning. A reserved name or two deps landing on one name is a hard error naming swiftpmConfig.name as the fix; the scope-borrowing that auto-corrected collisions is removed, since a name the build invents is a name no #import can predict. Collision checks key on SwiftPM's c99 name, so react-native-svg and react_native_svg no longer pass and then compile as one module.
### Implementation
Name resolution reads the two podspec fields it needs with the regex parser, so it adds no `pod ipc spec` spawn, and the full read is memoized on the resolved path so one run reads a podspec once across name resolution, header search paths and scaffolding.
`rn-tester` and the Apple test library move to the new location.
## Changelog:
[IOS] [FIXED] - Read SwiftPM name from podspec and store in package.json when scaffolding
Pull Request resolved: https://github.com/react/react-native/pull/58290
Test Plan: ✅ Unit tests
Reviewed By: cortinico
Differential Revision: D119159904
Pulled By: cipolleschi
fbshipit-source-id: 59a7c62ecb601498060a5721f362dae8c5cc92de
(cherry picked from commit 1cfc5f29a0)
Summary:
`findFilesWithExtension` walks a library's directory looking for `.mm` files that declare a Fabric component. The walk descends into every subdirectory, `node_modules` included, resolves symlinks through `statSync`, and keeps no visited set.
This got broken after https://github.com/react/react-native/issues/57790 because `parseiOSAnnotations` now enters every library that declares a `codegenConfig` without an `ios` key into its map with an empty `components` object, so nothing removes it from `librariesToCrawl`. Previously such libraries were filtered before crawling.
An app that declares `codegenConfig` with `"type": "all"` and no `ios` key now gets treated as a component library, so the walk covers the entire project.
What we notice in the [expo repo](https://github.com/expo/expo/pull/50134) is that under pnpm, the traversal never terminates. Workspace packages link into each other's `node_modules` and form cycles. Because symlinks are followed, the walk only stops once paths hit the 1023 byte limit. CocoaPods progress stops right after the "Using React Native Core and React Native Dependencies prebuilt versions" line while a `generate-codegen-artifacts.js` child sits at 100% CPU.
Under npm and yarn, the walk completes but with the wrong result. In those cases, `node_modules` holds real directories, but the walk covers the whole dependency tree. Crawling one app of roughly 1100 packages turned up 382 `.mm` files, 43 of which declare a component, among them React core views such as `RCTImageComponentView` and `RCTScrollViewComponentView` from `react-native-macos`. Each one is written into the app's entry in `RCTThirdPartyComponentsProvider.mm`. The existing `/react-native/` path filter does not exclude them, since it requires a trailing separator and `react-native-macos` has none.
## Changelog:
[IOS] [FIXED] - Codegen no longer crawls `node_modules` or follows symlinks when discovering components
Pull Request resolved: https://github.com/react/react-native/pull/58518
Test Plan:
Two cases added to `packages/react-native/scripts/codegen/__tests__/generate-artifacts-executor-test.js`. The three existing `findFilesWithExtension` mocks move from `statSync` to `lstatSync`.
The symlink case builds a link pointing back at its own parent, so the pre-fix walk never terminates.
**Negative control:** with the `generateRCTThirdPartyComponents.js` change reverted and the tests left in place, both new cases fail. All 24 existing snapshots pass unchanged, so the change is additive for projects that already declare an `ios` config.
Measured by crawling the app directory of two real projects:
| Project | Before | After |
| --- | --- | --- |
| `apps/bare-expo`, pnpm | 116,800 reads in 30s, still running | 34 files, 1.1s |
| yarn app, ~1100 packages | 382 files, 5.8s | 8 files, 0.3s |
Reviewed By: fabriziocucci
Differential Revision: D120122552
Pulled By: vzaidman
fbshipit-source-id: 233b8dc338b92372a34a0855f12a549e52513200
(cherry picked from commit 39751d864d)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58479
`enableImperativeEvents` was defined as a JS-only feature flag, but OSS release stages are only applied to common flags: `ossReleaseStage` is consumed exclusively by the generators that produce the native override classes, so setting it on a JS-only flag is silently a no-op. The feature could therefore never be shipped through the canary channel.
This defines `enableImperativeEvents` as a common flag at release stage `canary`, and renames the JS-only flag to `enableImperativeEvents_DEPRECATED`. The deprecated flag is kept because the common flag is read through the native module: a JS bundle delivered to a native build that predates this change finds no such method and falls back to the default, which would silently turn the feature off. The gate in `ReactNativeElement` and `ReadOnlyText` now keeps the public EventTarget methods when either flag is enabled.
Changelog:
[General][Added] - Enable the imperative EventTarget API (`addEventListener`, `removeEventListener`, `dispatchEvent`) on native view refs in canary
Reviewed By: javache
Differential Revision: D119645412
fbshipit-source-id: 4fd33dfaff1640fd5d40b5bc9c3f657ea7b3bb86
(cherry picked from commit 8480b86820)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58450
The renderer can dispatch events either through the legacy plugin-based system or through the W3C EventTarget API (addEventListener/dispatchEvent), gated behind the enableNativeEventTargetEventDispatching feature flag. Enabling it by default was previously blocked on the renderer, which did not carry the necessary changes until React 19.3.0. That sync has now landed, so flip the flag's default value to true and drop the TODO that tracked the blocker.
Fully removing the flag and the legacy dispatch path is left for a follow-up.
Changelog:
[General][Changed] - Enabled Web-based event dispatching refactor
Reviewed By: javache
Differential Revision: D119495069
fbshipit-source-id: 53ad5b88c8b5cdd11c3fd1ee27d0f147b83c41a9
(cherry picked from commit 31d33ce91b)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58398
Expose the native Fabric UI manager through `react-native/react-private-interface` so the React renderer can consume it without reading the global directly.
Changelog: [Internal]
Reviewed By: cortinico
Differential Revision: D119165837
fbshipit-source-id: a41860ef4962c9a71b8052db29f7ff86529db18e
(cherry picked from commit 73a76ddced)
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58353
Fixes https://github.com/react/react-native/issues/57787.
Supersedes https://github.com/react/react-native/pull/57788.
## Context
On Android, `Image.getSize()` and `Image.getSizeWithHeaders()` reject for every `data:` URI.
Both resolve dimensions through Fresco's encoded-image pipeline. That pipeline has producer
sequences only for network, local-file and local-content URIs; every other scheme falls through to
a throw:
Unsupported uri scheme for encoded image fetch! Uri is: data:image/jpg;base64,...
Fresco's decoded-image pipeline does handle the scheme (`SOURCE_TYPE_DATA -> dataFetchSequence`), so
the capability exists — the encoded entry point simply does not expose it. A previous change added a
fast path for `res://` URIs for exactly this reason; `data:` was never given one.
Any app that gates rendering on `getSize` therefore cannot display an inline base64 image on
Android at all, while iOS is unaffected.
## This Diff
- Adds a `data:` fast path to `getSize` and `getSizeWithHeaders`, mirroring the existing
resource-drawable fast path, routed through `fetchDecodedImage` rather than `fetchEncodedImage`.
- Pins the request to auto-rotate so it produces the same Fresco bitmap cache key that
`ReactImageView` builds for the same URI. Rotation options are part of that key, so a mismatch
here would decode into a key nothing reads and force a second decode at render time.
- Sets `DownsampleMode.NEVER` so the reported dimensions stay intrinsic rather than
post-downsample, preserving the behaviour the encoded path was originally adopted for. That
option is absent from the bitmap cache key, so it does not disturb the parity above.
- Reads the visible dimensions straight off the decoded image, which has already had its EXIF
rotation applied, rather than repeating the axis swap the encoded subscriber performs by hand.
`BaseCloseableStaticBitmap` applies that same predicate internally, so repeating it here would
double-apply it.
- Adds unit coverage for the `data:` scheme, which had none: decoded-pipeline routing, cache-key
parity with `ReactImageView`, intrinsic dimensions, the headers variant, and both failure paths.
- Adds two `data:` rows to the RNTester `Image.getSize` platform test — one plain, one tagged EXIF
orientation 6 — so a real decode, rather than a mocked pipeline, proves the reported dimensions
are the visible ones. Both are inline base64, so they need no network.
Because the decode now warms the bitmap memory cache under the key the `<Image>` subsequently
reads, a caller that sets its image source from the `getSize` success callback paints from cache
instead of decoding at render time.
## Alternatives Considered
**Parse the dimensions out of the base64 header.** Satisfies the `getSize` contract and is cheaper,
but warms nothing. Callers that relied on the decode side effect for smooth playback would keep
re-decoding every frame at render time — it fixes the rejection without fixing the regression that
accompanied it.
**Add a `data:` arm to Fresco's encoded producer sequence.** Pushes a behavioural change into shared
image infrastructure used by every app, to serve a caller that wants a decoded result anyway. The
narrower fix belongs on this side.
**Have callers stop gating rendering on `getSize`.** Makes the image appear, but permanently
discards the decode-then-render ordering, leaving the surface visibly flickering.
Changelog:
[Android][Fixed] - Fix `Image.getSize()` and `Image.getSizeWithHeaders()` rejecting `data:` URIs
Reviewed By: Abbondanzo, javache, cortinico
Differential Revision: D118657320
fbshipit-source-id: 576ce8458c921b199d717161c2113a7d4f99221b
Summary:
When `crossOrigin="use-credentials"` or `referrerPolicy` is used on an `Image`, `getImageSourcesFromImageProps` currently fully replaces `source.headers`, removing custom headers such as `Authorization`.
This fix merges the generated headers with the user-provided source headers instead (user-provided headers take precedence).
## Changelog:
[GENERAL] [FIXED] - Preserve Image source headers when using crossOrigin or referrerPolicy
Pull Request resolved: https://github.com/react/react-native/pull/58319
Test Plan: Confirmed existing tests are unchanged & added a regression test
Reviewed By: christophpurrer
Differential Revision: D118797893
Pulled By: javache
fbshipit-source-id: 8018846647232f97cb9e12d6095774826999c89b
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
Summary:
When `accessibilityState.disabled` conflicts with an explicit `disabled` prop, the `Text` component currently updates `accessibilityState` in place. This mutates an object owned by the caller, which can cause issues when other code uses that same object.
This fix creates a new object instead, while retaining existing behavior (giving priority to the explicit prop).
## Changelog:
[GENERAL] [FIXED] - Prevent Text from mutating the accessibilityState prop
Pull Request resolved: https://github.com/react/react-native/pull/58318
Test Plan: Unchanged tests pass + added a regression test
Reviewed By: Abbondanzo
Differential Revision: D118788803
Pulled By: javache
fbshipit-source-id: 77f69e851d06250aa27e20912a2c2fc91394b81d
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58307
Classifies `react/renderer/imagemanager:imagemanager` as a "for frameworks" target under the three-tier C++ stable API visibility model. Consumers that opt into `RN_STRICT_API` now get a warning if they include its headers directly, which they can acknowledge with `RN_ALLOW_FRAMEWORKS`; without that flag the guards are inert, so no existing build changes behaviour.
`React-Fabric` already depended on `React-cxxstableapi` from an earlier migration in the same pod, so the module's subspec needs no change.
Changelog: [Internal]
Reviewed By: rubennorte
Differential Revision: D118621752
fbshipit-source-id: ecf701cc3377904088a7bd56bf39b2b924850ca2
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58323
Restore `paperComponentName: 'RCTSwitch'` in `SwitchNativeComponent.js` and regenerate affected snapshots to fix test breakages introduced by D117877024.
`testing-library/react-native` 13.3.3 hardcodes `HOST_SWITCH_NAMES = ['RCTSwitch']`, so renaming the component to `Switch` broke `getByRole('switch')` queries and `toBeChecked()` assertions. This was the only xplat/js-wide breakage from that diff—only bare `<Switch>` renders without explicit `accessible` props failed, and only in `TWInternBoolSettingComponent-test.js`.
Add an inline comment documenting the `testing-library/react-native` constraint to prevent future regressions. The four other component renames in D117877024 have no RNTL special-casing and remain as landed.
Changelog: [iOS][Fixed] Restore `paperComponentName: 'RCTSwitch'` to fix `testing-library/react-native` queries
Reviewed By: javache
Differential Revision: D118743290
fbshipit-source-id: 0fbd5e99fb1dfd7f0b1a307b0ed058f0d2082b77
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57733
Upgrade several remaining Libraries modules (WebSocket, PanResponder, RCTEventEmitter, and Flow type tests) from `flow` to `flow strict-local` with accurate types. WebSocket exposes spec-mandated getters/setters and opts out of the `unsafe-getters-setters` lint with a scoped directive. Behavior is unchanged.
Changelog: [Internal]
Reviewed By: javache
Differential Revision: D113763779
fbshipit-source-id: 86827a975e996a4c85dd92344d36746e61887dde
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57741
Upgrade the element inspector dev support modules from `flow` to `flow strict-local`, replacing loose `Object`/`Function` types with the accurate types already used by their callers and dependencies.
Changelog: [Internal]
Reviewed By: javache
Differential Revision: D113763787
fbshipit-source-id: 114d12cb02e244431d59b18e3b3c6cfa363bd25f
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57735
Upgrade `AppRegistry` (and its Flow type companion) and several Core timer modules from `flow` to `flow strict-local` with accurate types. Behavior is unchanged.
Changelog: [Internal]
Reviewed By: javache
Differential Revision: D113763781
fbshipit-source-id: 48936dbc898c9d409818fb23794711e7bb6c66ed
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57729
Upgrade assorted Utilities modules from `flow` to `flow strict-local` with accurate types. `insetsDiffer` uses local variables instead of reassigning its function parameters. Behavior is unchanged.
Changelog: [Internal]
Reviewed By: javache
Differential Revision: D113763792
fbshipit-source-id: 4c7672439ae5af27f4298c04dc5606ced5509888
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/57728
Upgrade the Blob-related modules (`File`, `FileReader`, `URL`, `URLSearchParams`) from `flow` to `flow strict-local`. These expose spec-mandated getters/setters, so they opt out of the `unsafe-getters-setters` lint with a scoped `// flowlint unsafe-getters-setters:off` directive. `URL` also received small behavior-preserving refactors (local variables instead of parameter reassignment, explicit null/empty checks).
Changelog: [Internal]
Reviewed By: javache
Differential Revision: D113763778
fbshipit-source-id: 1e0ceb02270d458fae6b89886ae41d9893ae7329
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
Summary:
The source-optimized preset enables native component codegen only when `codegenNativeComponent` is immediately followed by `<`. Valid Flow syntax permits comments or whitespace before the type arguments, so `codegenNativeComponent /* comment */ <NativeProps>(...)` bypasses codegen and emits an ordinary runtime call instead of the generated `__INTERNAL_VIEW_CONFIG` produced by the full preset path.
Use the stable `codegenNativeComponent` identifier token as the cheap source prefilter while retaining the existing null/undefined full-scan path. Formatting no longer changes build output; false-positive tokens only enable the codegen visitor, which remains a no-op without a matching call.
## Changelog:
[GENERAL] [FIXED] - Run native component codegen when Flow type arguments are separated by trivia.
Pull Request resolved: https://github.com/react/react-native/pull/58257
Test Plan:
- Added a valid Flow native-component spec containing a comment before `<NativeProps>`.
- Exact optimized baseline emits the ordinary call without `__INTERNAL_VIEW_CONFIG`; the fixed transform generates the static view config.
- Full preset Jest passes: 4/4 suites, 111/111 tests, 16 snapshots.
- Fresh Flow check reports 0 errors.
- Targeted no-ignore ESLint, Prettier, and `git diff --check` pass.
No public API or breaking behavior change; optimized and full preset paths now agree for valid formatting.
Reviewed By: GijsWeterings
Differential Revision: D118266389
Pulled By: javache
fbshipit-source-id: 0b4279eed9f831e1e8f4669bd522645023dfdeac
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
Summary:
Pull Request resolved: https://github.com/react/react-native/pull/58304
Classifies `react/renderer/animationbackend:animationbackend` as a "for frameworks" target under the three-tier C++ stable API visibility model. Consumers that opt into `RN_STRICT_API` now get a warning if they include its headers directly, which they can acknowledge with `RN_ALLOW_FRAMEWORKS`; without that flag the guards are inert, so no existing build changes behaviour.
`React-Fabric` already depended on `React-cxxstableapi` from an earlier migration in the same pod, so the module's subspec needs no change.
Changelog: [Internal]
Reviewed By: rubennorte
Differential Revision: D118616103
fbshipit-source-id: dbe574a524c8a29e204792ca2193c4c8238d1c20
Summary:
`npx react-native spm add` writes `HERMES_CLI_PATH` into the app's committed `project.pbxproj` — the absolute path of `hermesc` in the `hermes-compiler` npm package, as resolved on whichever machine ran the command. So every SwiftPM-converted app commits one developer's disk layout.
Nothing needs it: `react-native-xcode.sh` already resolves `hermesc` at build time through react-native's own dependency graph when the current value is not a file. A relative setting can't replace it either — `$(REACT_NATIVE_PATH)/../hermes-compiler` breaks on a symlinked `react-native`, and `$(SRCROOT)/../node_modules/…` breaks on hoisted monorepos.
So this deletes `resolveHermesCliPathSetting()`, along with the `hermesCliPath` parameter it fed on `injectSpmIntoPbxproj` and `mergeReactBuildSettings`. Already-injected projects self-clean on the next `spm add`/`update`, which strips every scalar recorded in `.spm-injected.json` before re-injecting.
### Scope: SwiftPM only
Both pieces involved arrived with SwiftPM in https://github.com/react/react-native/issues/57332 and first shipped in 0.87.0 — `scripts/spm/generate-spm-xcodeproj.js` (the whole file, including the write) and the `NODE_HERMESC` build-time fallback in `react-native-xcode.sh`.
Everything CocoaPods relies on is older and untouched, all present in 0.86.0: the pod-derived `HERMES_CLI_PATH` default, the "hermesc could not be found" error, and the `hermesc -emit-binary` call. **A CocoaPods build cannot be affected by this change.**
That parameter shipped in 0.87.0 and 0.87.1, but SwiftPM is Preview-labelled, so removing it is in scope. Nothing in the repo passed it except the deleted resolver.
## Known limitation
The fallback is gated on `PODS_ROOT` being absent, so a SwiftPM app that keeps side-by-side non-RN pods skips it and needs an explicit `HERMES_CLI_PATH`. Evaluating that gate verbatim out of the unchanged script:
| app | gate | hermesc |
| --- | --- | --- |
| no `Pods/` (normal SwiftPM app) | fires | from `node_modules` |
| `Pods/`, non-RN pods only | skipped | pod path — absent |
| `Pods/` with `hermes-engine` | skipped | the pod's, unchanged |
Set it in an xcconfig, not the pbxproj: `createdScalars` cleanup removes by key, so it would drop a hand-edited value too. Keying the gate on the `hermes-engine` directory would close this — left as a follow-up so this PR doesn't touch the bundling script.
## Changelog:
[IOS] [FIXED] - SwiftPM: stop baking an absolute, machine-specific HERMES_CLI_PATH into the app's pbxproj; resolve hermesc at build time instead
Pull Request resolved: https://github.com/react/react-native/pull/58292
Test Plan:
Red first — with the source reverted, the new entry-point test fails on a seeded `hermes-compiler` fixture: `✕ writes no HERMES_CLI_PATH, in any configuration or the marker` (1 failed, 76 passed).
```
yarn jest packages/react-native/scripts/spm --no-cache -i
Test Suites: 18 passed, 18 total
Tests: 835 passed, 835 total
```
`yarn flow-check` (0 errors), plus `yarn eslint --max-warnings 0` and `yarn prettier --check` on the four changed files.
End to end: a 0.87.1 SwiftPM app (`shopify/react-native-skia` example, no Pods) with the `HERMES_CLI_PATH` lines removed from its pbxproj builds in Release, the fallback resolving `hermesc` from `node_modules/hermes-compiler`. That build ran against an earlier revision of this branch which also widened the shell gate; with no `Pods/` it is row 1, where both conditions behave identically. There is no shell test harness, and the script is unchanged here.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Reviewed By: cortinico
Differential Revision: D118624244
Pulled By: cipolleschi
fbshipit-source-id: 5784a2de69af0c50c82129d96423801b0f5b0eba
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