feat(iOS): ReactNativeDependenciesHeaders sidecar + pure-RN ReactNativeHeaders, published to Maven#57442
Closed
chrfalch wants to merge 45 commits into
Closed
feat(iOS): ReactNativeDependenciesHeaders sidecar + pure-RN ReactNativeHeaders, published to Maven#57442chrfalch wants to merge 45 commits into
chrfalch wants to merge 45 commits into
Conversation
chrfalch
force-pushed
the
chrfalch/deps-headers-sidecar
branch
from
July 6, 2026 12:10
73226c2 to
c01f55a
Compare
The minimal machinery to build the packaged header structures: - headers-spec.js: the executable layout contract (rules R1-R8) — which namespaces are hoisted into the React framework, which carry module maps, and how collisions are rejected. - headers-inventory.js: scans the source tree and classifies every shipped header (language surface + modularizability bucket) — the input to the spec. computeInventory() feeds the build in-memory; the CLI writes a JSON manifest. - headers-compose.js: emits the layout — writes the <React/...> headers + umbrella + module map into each React.framework slice (detected by the framework's presence), and assembles the headers-only ReactNativeHeaders.xcframework (every other namespace + deps + Hermes). Called by xcframework.js during compose. This is the alternative header source that lets consumers resolve React Native headers without a clang VFS overlay. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Emit the headers-spec layout unconditionally and delete the VFS overlay across
JS, CI publish, and Ruby. Consumers resolve headers the way the SwiftPM branch
does: <React/...> from the vendored React.framework, every other namespace from
ReactNativeHeaders. No root Headers/ on the xcframework, no VFS.
JS:
- xcframework.js: always emit the React.framework spec layout and build
ReactNativeHeaders.xcframework (was gated behind RN_ZERO_I_LAYOUT=1). Remove
the legacy header path entirely — the podspec->root-Headers enumeration,
createModuleMapFile, and copyHeaderFilesToSlices — so the published
React.xcframework is a standard framework (Info.plist + per-slice
React.framework/{Headers,Modules}), no root Headers/ or Modules/. Ship
ReactNativeHeaders.xcframework inside the reactnative-core tarball (sibling of
React.xcframework) so the prebuilt pod can vend both; keep the standalone
ReactNativeHeaders.xcframework.tar.gz for the SPM path. Drop the
React-VFS-template.yaml emit and the ./vfs import.
- vfs.js: deleted (its only consumer was xcframework.js).
- types.js: drop the now-unused VFSEntry/VFSOverlay/HeaderMapping types.
- replace-rncore-version.js: drop the React-VFS.yaml preservation rationale.
Ruby/CocoaPods:
- React-Core-prebuilt.podspec: vend React.xcframework (its per-slice
React.framework + module map serves <React/...> and @import React via
FRAMEWORK_SEARCH_PATHS); flatten ReactNativeHeaders' headers into a top-level
Headers/ in prepare_command and expose them via the pod header search path.
Drop the VFS-era root header_mappings_dir/module_map.
- rncore.rb: remove the -ivfsoverlay injection and process_vfs_overlay;
add_rncore_dependency and configure_aggregate_xcconfig now add a
HEADER_SEARCH_PATHS to React-Core-prebuilt/Headers for podspec, aggregate, and
third-party targets.
- react_native_pods.rb: drop the process_vfs_overlay post-install call.
Docs: replace the "VFS Overlay System" section with the headers-spec layout;
drop the obsolete "Known Issues" (pre-headers-spec) section.
Verified end-to-end: prebuild compose produces a VFS-free, root-Headers-free
React.xcframework; rn-tester pod install + xcodebuild (prebuilt path) BUILD
SUCCEEDED with zero -ivfsoverlay.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
React.framework is a clang module; when an SPM consumer precompiles it, a
modular React/ header that #imports <react/...> hit
-Wnon-modular-include-in-framework-module because the lowercase react/
namespace (served from ReactNativeHeaders, per R1's Linux/Windows-safe layout)
was deliberately kept out of any module.
Give react/ a module where it already lives instead of relocating it (relocation
would require case-folding react.framework -> React.framework, which only works
on case-insensitive filesystems):
- headers-spec.js: drop the react/ namespace-module exemption so its
objc-modular-candidates get a module; emit that module as
ReactNativeHeaders_react (a module literally named 'react' would alias the
React framework module on a case-insensitive filesystem). Module names are
internal; <react/...> still resolves by header path and is now modular.
- headers-inventory.js: classify C++ default member initializers in aggregates
(e.g. struct { NSString *family = nil; } in RCTFontProperties.h) as ObjC++ so
these are not misclassified objc-modular-candidate and pulled into a plain
ObjC module they cannot compile in.
The unguarded ObjC/C react/ headers (e.g. JSRuntimeFactoryCAPI.h) now resolve
modularly; the C++ react/renderer/* includes are #ifdef __cplusplus-guarded and
skipped during the ObjC module emit, so they need no module.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
In prebuilt mode the React core pods' code + headers live entirely in React.xcframework / React-Core-prebuilt. Re-installing their SOURCE podspecs made them ship duplicate headers that shadow the prebuilt artifact and break the React framework's clang explicit-module precompile (-Wnon-modular-include-in-framework-module) under Xcode 26. Install those core pods as dependency-only FACADES instead: generated podspecs with no sources/headers, installed via :path (so nothing is fetched), each depending on React-Core-prebuilt. Version, subspecs, default_subspec and resources (e.g. the privacy manifest) are DERIVED from the real podspec so the facade stays graph- and resource-equivalent to the source pod. With the shadowing gone the React module precompiles cleanly with SWIFT_ENABLE_EXPLICIT_MODULES on, so the Xcode-26 workaround (#53457) is removed. The prebuilt header search path + ReactNativeHeaders module-map activation are consolidated into a single post-install injection site (configure_aggregate_xcconfig); add_rncore_dependency now only declares the React-Core-prebuilt dependency. rn-tester's NativeComponentExample uses the canonical <React/...> include for RCTFabricComponentsPlugins.h (resolved from the framework) so it builds against the facaded React-RCTFabric in prebuilt mode. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`yarn format-check` (prettier) was failing CI on PR #57285. Run prettier on the ios-prebuild headers scripts (headers-compose.js, headers-inventory.js), replace-rncore-version.js, and __docs__/README.md so format-check passes. No logic changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tarball The prebuilt React core now ships two xcframeworks — React.xcframework and the headers-only ReactNativeHeaders.xcframework. React-Core-prebuilt's prepare_command flattens the latter's Headers (including module.modulemap) into the pod. The compose step only tar'd React.xcframework, so consumers got no React-Core-prebuilt/Headers/module.modulemap and failed the build with "module map file ... not found". Tar both xcframeworks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…le (R9) The public-umbrella model (which replaced the VFS overlay) excludes `+Private` and objc-blocked headers from React.framework's module, so privileged framework consumers (e.g. Expo) that `#import <React/RCTBridge+Private.h>`, `<React/RCTMountingManager.h>`, etc. fail to compile under explicit modules even though the headers still ship in React.framework/Headers. Add R9: a curated allowlist appended to the React module map — `RCTBridge+Private.h` as a real `header` (objc-modular-candidate, reaches no C++) and the six Fabric headers as `textual header` (objc-blocked; a real member would re-trip -Wnon-modular-include, and their <react/...> C++ includes resolve at the consumer's use site). Backwards-compatible: existing `#import <React/...>` (and Swift `import React`) sites are unchanged. Fails closed if an allowlisted header is removed/renamed or drifts bucket. Note: RCTUIKit.h / RCTRootContentView.h are absent from source entirely and need restoration, not exposure — out of scope here.
The flattened ReactNativeHeaders layout ships the individual React_RCTAppDelegate/*.h headers but no per-namespace umbrella. Consumers like Expo probe `<React_RCTAppDelegate/React_RCTAppDelegate-umbrella.h>` via __has_include (RCTAppDelegateUmbrella.h); with the umbrella gone the probe fails and RCTReactNativeFactory / RCTRootViewFactory are never declared, breaking the Expo pod's clang module. Add R10: emit a per-namespace umbrella (content DERIVED from namespaceModules so it can't drift — e.g. RCTArchConfiguratorProtocol.h, gone from this branch, is correctly omitted) and add it to that namespace's module so the import stays modular under explicit modules. Targeted via UMBRELLA_NAMESPACES (currently just React_RCTAppDelegate, the only umbrella Expo imports); fails closed if a listed namespace loses its modular headers.
…ic facade Community Fabric modules quote-import "RCTFabricComponentsPlugins.h" (~47x: slider, maps, pager-view, keyboard-controller, ...). In source, React-RCTFabric vended it at header_dir "React", so it landed in dependents' CocoaPods header maps and the bare quoted name resolved. In prebuilt mode React-RCTFabric is a dependency-only facade that ships no headers — the only copy is baked angle-only into React.framework (it's objc-blocked, excluded from the framework module map), so quoted imports fail to resolve. Re-vend JUST that one header from the facade (FACADE_REEXPOSED_HEADERS), copied as a self-contained snapshot at header_dir "React", restoring dependents' header maps exactly as the source pod did. Header-only (no compiled sources, no duplicate symbols — the impl stays in React.framework). Re-exposing a single header does not put <react/...>/<yoga/...> on -I, so it does not reintroduce the non-modular-include shadowing the modular layout eliminates. Fails closed if the glob matches nothing.
…the headers compose buildReactNativeHeadersXcframework copied each declared deps namespace (folly/glog/boost/fmt/double-conversion/fast_float) from the staged ReactNativeDependencies headers, but only console.warn'd on a missing one and kept going — silently shipping a ReactNativeHeaders.xcframework without third-party header resolution (consumers then fail on <folly/...> etc.). The summary log also printed the INTENDED namespace list, masking the gap. Throw instead: a missing declared deps namespace means deps weren't staged (third-party/ReactNativeDependencies.xcframework/Headers — from a full prebuild or the cache slot), so refuse to emit an incomplete artifact.
…est imports Two CI fixes for the prebuild-ios-core workflow: - prebuild-ios-core.yml: the compose-xcframework job downloaded the build slices and React headers but never staged third-party/ReactNativeDependencies.xcframework. buildReactNativeHeadersXcframework folds the third-party deps namespaces (folly/glog/boost/...) into ReactNativeHeaders.xcframework, so it fail-closed with "deps namespace 'folly' missing ... refusing to ship an incomplete ReactNativeHeaders.xcframework". Add the Download + Extract ReactNativeDependencies steps (mirroring build-slices) so the deps headers are present before composing. - headers-spec-test.js: reorder requires so the `../headers-spec` import sorts before `fs`, fixing the @react-native/monorepo/sort-imports warning that failed `eslint --max-warnings 0` in test_js. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- copy facade resources into the facade pod dir: CocoaPods file accessors cannot glob past the pod root, so the ..-escaping globs shipped the privacy-manifest / i18n resource bundles empty - quote -fmodule-map-file so a PODS_ROOT containing spaces stays a single clang argument (matches the quoted HEADER_SEARCH_PATHS beside it) - fail closed in React-Core-prebuilt's prepare_command and in replace-rncore-version.js when the tarball lacks ReactNativeHeaders.xcframework, instead of silently leaving an empty or deleted Headers/ with the module-map flag dangling - thread rnRoot through planFromInventory/isUmbrellaSafe instead of the hardcoded hosting-package root (the one spot that didn't take the inventory's root) - drop the dead ios-prebuild templates/ files (their only consumers were removed by the headers-spec compose) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…1, verify, ratchet) Hardens the header layout against new/changed headers so consumer-facing regressions fail the PREBUILD instead of a downstream (rn-tester/Expo/ community) build: - headers-verify.js (new; runs in the prebuild compose CI job): include-health ratchet against a committed baseline (notShipped/unresolved/quoted- unresolvable includes in shipped headers — 27 baselined today); structural byte-compare of the composed module maps/umbrellas against the spec render; compile smokes — an ObjC TU precompiling the React module (every umbrella header) + all 14 R5 namespace modules + the R10 umbrella + __has_include asserts, an Expo-shaped ObjC++ TU (the R9 textual Fabric surface), and a Swift TU (import React + RCTBridge.moduleRegistry). - R11: one source, one content location. 116 sources ship under multiple spellings (React/X.h + CoreModules/RCTImage/RCTAnimation/... forms, bare React_RCTAppDelegate aliases). The flattened layout duplicated their declarations, so any -fmodules consumer touching two spellings (even transitively via a legacy spelling) hit redefinition errors — found by the gate's first run. The module-owned spelling keeps the content; every other spelling is emitted as a one-line redirect shim. - Single source of truth for third-party namespaces: the inventory's include classifier now derives from DEPS_NAMESPACES, and compose enforces set-equality with the deps artifact in BOTH directions (missing OR undeclared namespace fails). The undeclared direction immediately surfaced SocketRocket, which the deps artifact ships but was never relocated. - R5 exemption assert: an invalid-module-identifier namespace gaining a modular-candidate header now fails the plan instead of silently shipping a non-modular header. - Inventory records quoted includes that don't resolve to a shipped header (quotedNotShipped) instead of dropping them. Docs: __docs__/headers-rules.md documents rules R1-R11, the emission pipeline, and the resilience model.
…iveHeaders Declaring SocketRocket in DEPS_NAMESPACES (added to satisfy the deps set-equality guard) relocated textual copies of its headers into ReactNativeHeaders and thus into the flattened React-Core-prebuilt/Headers, which sits on every pod's HEADER_SEARCH_PATHS. The real SocketRocket pod still exists in consumer graphs and vends the same headers, so the copies collide: duplicate @interface definitions when the SocketRocket pod compiles (rn-tester, prebuilt + USE_FRAMEWORKS=dynamic), and a poisoned module graph in use_frameworks/explicit-modules apps that don't compile SocketRocket sources (Expo: ReactCodegen/RCTModulesConformingToProtocolsProvider.h "file not found"). Bisect confirmed removing the SocketRocket dir flips red->green and the R11 shims are not implicated. Add DEPS_NAMESPACES_NOT_RELOCATED: such namespaces count as declared for the set-equality guard (a genuinely new dep still fails closed), the include classifier still recognizes them, and the headers gate now asserts they stay ABSENT from the artifact. Note: no CI lane covers prebuilt + use_frameworks (the dynamic-frameworks lane builds from source), which is why CI stayed green.
chrfalch
force-pushed
the
chrfalch/prebuilt-deps-facades
branch
from
July 6, 2026 16:37
069c350 to
017ca51
Compare
chrfalch
force-pushed
the
chrfalch/deps-headers-sidecar
branch
from
July 6, 2026 16:37
90d8f50 to
0c44552
Compare
cipolleschi
reviewed
Jul 8, 2026
cipolleschi
reviewed
Jul 8, 2026
cipolleschi
reviewed
Jul 8, 2026
cipolleschi
reviewed
Jul 8, 2026
cipolleschi
reviewed
Jul 8, 2026
cipolleschi
reviewed
Jul 8, 2026
…ayout
- Embed an RN-authored, accurate-empty PrivacyInfo.xcprivacy for
SocketRocket in the deps prebuild (upstream ships no privacy manifest;
the deps artifact historically carried resource bundles only for
boost/folly/glog). SocketRocket uses no Required Reason APIs.
- Xcode 26's SwiftPM rejects a public-headers directory where the
umbrella header has sibling directories ("target 'SocketRocket' has
invalid header layout"): stage the flat public headers into include/
via prepareScript and point publicHeaderFiles there. Xcode 16 accepts
both layouts; the artifact's Headers/SocketRocket namespace is
unaffected.
- Rename the dependency 'socket-rocket' -> 'SocketRocket' so the bundle
and header-namespace naming line up
(ReactNativeDependencies_SocketRocket.bundle).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pure formatting — no content change. Fixes the format-check lint lane. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review feedback on the prebuilt-deps facades: - rndeps_facades.rb: soften the "graph-equivalent" comment — facades match the source pod's spec/subspec shape but route every derived subspec's dep to ReactNativeDependencies, so intra-pod subspec deps (RCT-Folly/Fabric -> RCT-Folly/Default) are not reproduced (harmless; the deps are declared explicitly in react_native_pods.rb). - rndeps_facades.rb: add `require_relative './helpers'` so Helpers::Constants is self-contained against require ordering (the defined? guard stays as backstop). - reactNativeDependencies.js: document that the no-version-marker branch returns before the version.txt write, so a locally-staged artifact never gains a marker and later runs re-hit the branch (intentional; downstream must not assume it). - rndependencies.rb: uniq the HEADER_SEARCH_PATHS so a second call can't duplicate. - prebuilt-deps.md: drop the stray space before SocketRocket. Note: the Ruby-tests request is not actioned — the repo's cocoapods Ruby test harness is not wired into CI, so the tests would not run. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ODS hint distinct Two review follow-ups on the deps facades: - The prebuilt branch of add_rn_third_party_dependencies appended the deps Headers search path unconditionally; a second call on the same spec would duplicate the entry. Now .uniq, mirroring the from-source branch. - load_real_spec's method-level rescue also caught the tailored not-found raise and re-wrapped it generically, burying the "Update FACADE_PODS" hint. The rescue is now scoped to Pod::Specification.from_file only, so the two failure messages stay distinct. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…idecar The binary ReactNativeDependencies.xcframework is FRAMEWORK-type: it cannot carry a HeadersPath key, so its root Headers/ dir is invisible to SwiftPM binaryTargets (verified empirically 2026-07-04 - xcodebuild rejects -headers without -library, and SwiftPM hard-rejects HeadersPath on framework entries). This adds a headers-only LIBRARY-type sidecar, ReactNativeDependenciesHeaders.xcframework, that SwiftPM auto-serves with zero flags: stub static archives paired with per-slice Headers/ holding the seven third-party namespaces (folly, glog, boost, fmt, double-conversion, fast_float, SocketRocket). - headers-xcframework.js (new): dependency-light shared emitter - composeHeadersOnlyXcframework (the stub-archive recipe factored out of buildReactNativeHeadersXcframework), stubSlicesFromXcframework (slice parity derived from the binary artifact's Info.plist, so the sidecar resolves for every platform the binary does), and buildDepsHeadersXcframework with a set-equality gate that fails closed in both directions (declared-but-missing and undeclared namespaces). - compose-framework.js: the deps compose emits and signs the sidecar right after assembling the root Headers/ (slice-uniform content). - prebuild-ios-dependencies.yml: the sidecar ships INSIDE the deps tarball (self-contained for CocoaPods) and as a standalone tarball; cache key bumped and now also hashes compose-framework.js. - reactNativeDependencies.js: consumers extract the sidecar alongside the binary; pre-sidecar tarballs (pinned RN_DEP_VERSION) warn and continue - CocoaPods is unaffected, SwiftPM recomposes it locally. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every namespace now has exactly ONE physical home: RN namespaces in ReactNativeHeaders, third-party deps namespaces in the ReactNativeDependenciesHeaders sidecar / the ReactNativeDependencies pod. Relocated deps copies are what made the 2026-07-03 SocketRocket regression possible (duplicate @interface + poisoned module graph under use_frameworks when a real pod coexisted with relocated copies) - that bug class is now structurally impossible, and the SocketRocket exclusion list is deleted rather than maintained. - headers-spec.js: R2 rewritten; DEPS_NAMESPACES now includes SocketRocket (= the sidecar contents = the artifact's Headers/ dirs, set-equality enforced); DEPS_NAMESPACES_NOT_RELOCATED deleted. - headers-compose.js: RNH stages RN headers only; ensureHeadersLayout also builds the sidecar from the cache slot's deps headers and returns it, so SwiftPM consumers need no published sidecar. - headers-verify.js: structural gate flipped - deps namespaces must be ABSENT from RNH; compile gates take the deps artifact's Headers on the include path (exactly how consumers resolve them). - CocoaPods: NEW ReactNativeDependenciesUtils.configure_aggregate_xcconfig injects $(PODS_ROOT)/ReactNativeDependencies/Headers into the aggregate and every pod target in prebuilt-deps mode, mirroring the rncore injection. Pods previously resolved folly/glog through the globally-injected React-Core-prebuilt/Headers flatten of RNH; after the flip the deps pod is the single global home, so the mirror injection is what keeps arbitrary pods compiling. Verified: headers gate green (include-health, structural, compile smokes vs the deps include path); rn-tester prebuilt static + dynamic (the regression config), helloworld static, source-core+prebuilt-deps (React compiled from source resolves folly via the deps pod), and a source-mode control with an unchanged dependency graph. BREAKING (prebuilt artifacts only): ReactNativeHeaders.xcframework no longer contains folly/glog/boost/fmt/double-conversion/fast_float; consumers reaching them through React-Core-prebuilt/Headers must use the ReactNativeDependencies pod / ReactNativeDependenciesHeaders sidecar paths instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
prebuilt + use_frameworks is the exact configuration of the 2026-07-03
SocketRocket dual-copy regression, and no CI lane covered it: the
test-ios-rntester action hard-coupled use-frameworks:true to building
from source. Add a use-prebuilds input ('auto' keeps the historical
coupling for existing callers) and pass use-prebuilds:true from
test_ios_rntester, so its dynamic-frameworks matrix cells now consume
the prebuilt ReactCore + ReactNativeDependencies artifacts built in the
same workflow run. Source-built dynamic frameworks stay covered by
test_ios_rntester_dynamic_frameworks.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eaders to Maven
Publish the two headers-only xcframeworks as standalone artifacts on
the react-native-artifacts coordinate so SwiftPM consumers can wire
them as separate binaryTargets (they also keep shipping inside the
ReactCore / ReactNativeDependencies tarballs for CocoaPods):
- external-artifacts/build.gradle.kts: four new PublishArtifacts with
classifiers reactnative-headers-{debug,release} and
reactnative-dependencies-headers-{debug,release}.
- prebuild-ios-core.yml: upload the standalone
ReactNativeHeaders<flavor>.xcframework.tar.gz the compose step
already produces (previously only bundled inside the ReactCore
tarball); include it in the workflow cache.
- build-npm-package action: download ReactNativeHeaders* artifacts
(ReactNativeDependenciesHeaders* already rides the existing
ReactNativeDependencies* pattern).
- verifyArtifactsAreOnMaven.js: HEAD-check every classifier tarball,
not just the POM - a release with a missing classifier artifact now
fails verification instead of passing silently.
Validated locally end-to-end with publishReleasePublicationToMavenLocal:
all 12 files + POM land on com.facebook.react:react-native-artifacts
with the expected classifier names.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The verifier now HEAD-checks the POM plus all eight classifier tarballs instead of GETting only the POM; the mocks/assertions follow, and a new case covers a missing classifier behind an existing POM (the exact gap the classifier checks were added to catch). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review feedback on ReactNativeDependenciesHeaders + pure-RN ReactNativeHeaders: - prebuild-ios-dependencies.yml: the sidecar cache key now hashes the two cross-package files it is actually generated from — ios-prebuild/ headers-xcframework.js (emitter) + headers-spec.js (DEPS_NAMESPACES) — in BOTH occurrences, and bumps v4->v5. A DEPS_NAMESPACES/emitter edit can no longer serve a stale cached sidecar on main. - prebuild-ios-core.yml: bump the core xcframework cache key v2->v3 to reflect the added ReactNativeHeaders tarball in the artifact set (was relying only on the hashFiles component). - headers-xcframework.js: wrap both staging temp dirs (.stub-work-* / .deps-headers-stage-*) in try/finally so an xcrun/xcodebuild failure can't leave them behind in outDir (= third-party/). - verifyArtifactsAreOnMaven.js: clarify the comment — the 8 primary xcframework classifier tarballs are HEAD-checked; the 4 dSYM classifiers are intentionally excluded. - test-ios-rntester/action.yml: harden the use-prebuilds resolution — only true/false enable/disable; an unexpected value warns instead of silently disabling. - reactNativeDependencies.js: comment reconciling the two sidecar sources (the full-slice tarball copy vs the ensureHeadersLayout DEFAULT_STUB_SLICES fallback) — they agree on the iOS slices SPM needs; headers are slice-uniform. - Tests: stubSlicesFromXcframework known-slice mapping + unknown-slice throw (mocked plutil JSON). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mixed is a deprecated utility type (use unknown), and Buffer.from needs the string+encoding overload since JSON.stringify on unknown types as string | void. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sidecar Same macOS-only clonefile flag as headers-compose; keeps the prebuild scripts runnable under Linux CI jest. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ose freshness marker composeToolingHash() covered only three of headers-compose.js's five local sibling modules, so a local edit to headers-xcframework.js (which ensureHeadersLayout delegates the ReactNativeHeaders compose and the deps sidecar build to) or framework-resources.js (privacy manifest + i18n bundle emission) served stale composed output. Extract the list to COMPOSE_TOOLING_FILES, add the two missing files, and add a guard test that parses this file's own requires so the list can't silently drift again — the same bug class as the CI cache-key hashFiles gap, now unrepresentable. Also rewrite the stale headers-rules.md paragraph that still described the removed DEPS_NAMESPACES_NOT_RELOCATED carve-out; SocketRocket lives in DEPS_NAMESPACES with a single physical home in the deps sidecar. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mework.js The pure-RN commit moved the stub-xcframework toolchain out of headers-compose.js (which got execFileSync arg arrays on the vfs branch) into this emitter; apply the same conversion here so the shell-quoting fix survives the move: plutil/xcrun/clang/libtool/lipo/xcodebuild/cp all take argument arrays now, with libtool's stderr suppression via stdio options. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…se guard test new Set() needed an explicit type argument under strict-local, and the exec-in-while-condition loop lost Flow's null refinement — matchAll avoids both. headers-rules.md reflowed by prettier. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
chrfalch
force-pushed
the
chrfalch/prebuilt-deps-facades
branch
from
July 10, 2026 12:47
1b0f40e to
665f06a
Compare
chrfalch
force-pushed
the
chrfalch/deps-headers-sidecar
branch
from
July 10, 2026 12:47
bc159de to
9785803
Compare
|
@cipolleschi has imported this pull request. If you are a Meta employee, you can view this in D111449462. |
cortinico
approved these changes
Jul 15, 2026
cortinico
left a comment
Contributor
There was a problem hiding this comment.
Review automatically exported from Phabricator review in Meta.
meta-codesync Bot
pushed a commit
that referenced
this pull request
Jul 15, 2026
Summary:⚠️ This is a stacked PR — review/merge the base PRs first Builds on, and should land after (bottom-up): 1. #57285 — Remove the Clang VFS overlay / modularize React headers (base of the stack). <React/…> via the framework module map; lowercase namespaces (react/, yoga/, jsi/, …) via the ReactNativeHeaders module map. 2. #57305 — Prebuilt artifact resources + ReactNativeHeaders.xcframework. Ships the prebuilt core artifacts (incl. ReactNativeHeaders in the tarball + embedded React.framework resources) and the CocoaPods React-Core-prebuilt facades. 3. #57440 — Self-serving prebuilt ReactNativeDependencies + dependency-only facades. The deps pod becomes the single header authority for the third-party namespaces (RCT-Folly/glog/boost/…); community `s.dependency "RCT-Folly"` resolves to local facades instead of trunk source pods. 4. #57442 — ReactNativeDependenciesHeaders sidecar + pure-RN ReactNativeHeaders (immediate base). The deps prebuild emits a headers-only LIBRARY-type sidecar (the binary deps xcframework is framework-type — invisible to SwiftPM binaryTargets), ReactNativeHeaders drops the third-party namespaces, and both headers artifacts publish standalone to Maven. The whole stack is rebased onto current main (2026-07-06). This PR consumes the final **five-artifact set**: React, ReactNativeHeaders (pure-RN), ReactNativeDependencies, ReactNativeDependenciesHeaders, hermes-engine. Adds npx react-native spm and the package-generation tooling that turns an app into a SwiftPM-integrated RN app using the base stack's prebuilt XCFrameworks. CocoaPods stays supported — additive, opt-in, no Ruby toolchain. Integration is injected into the existing .xcodeproj in place (nothing generated/renamed/replaced), recorded in .spm-injected.json so it reverses exactly. ### What this PR adds - The spm CLI (add/update/deinit/scaffold + hidden sync/codegen/download), zero-arg auto-resolution, --deintegrate for CocoaPods→SwiftPM. - SwiftPM package generation (scripts/spm/): autolinking→Package.swift, Codegen→React-GeneratedCode, core XCFramework binary targets, artifact download/cache, surgical pbxproj inject/remove. - ReactNativeDependenciesHeaders wired as the 5th binaryTarget: the headers-only companions (ReactNativeHeaders from the core tarball, the deps sidecar from the deps tarball) are staged automatically out of their parent tarballs — no --headers-tarball priming; REQUIRED_ARTIFACTS covers all five. - Community-library scaffolding from podspecs (incl. root-level-source podspecs via generated include/<SwiftName>/ shims, sibling wiring for transitive spm.dependencies, self-ingestion guards). - In-place Xcode integration (XCLocalSwiftPackageReference + Sync build phase + scheme pre-action + auto-sync; ${PODS_ROOT}-anchored REACT_NATIVE_PATH replaced on --deintegrate with exact deinit rollback). - rn-tester + helloworld SwiftPM consumption + test library; npm-packaging hygiene; a small RNCoreFacades.podspec_dir fix. ## Documentation - **Tool docs** (usage, quick start, architecture): [`scripts/spm/__doc__/spm-scripts.md`](https://github.com/react/react-native/blob/chrfalch/swift-package-manager/packages/react-native/scripts/spm/__doc__/spm-scripts.md) - **Header-paths contract** (how the five artifacts serve headers): [`scripts/spm/__doc__/spm-header-paths-contract.md`](https://github.com/react/react-native/blob/chrfalch/swift-package-manager/packages/react-native/scripts/spm/__doc__/spm-header-paths-contract.md) - **RFC** (updated): [react-native-community/discussions-and-proposals#994](react-native-community/discussions-and-proposals#994) ## Changelog: [IOS] [ADDED] - `npx react-native spm` command + SwiftPM package-generation tooling (opt-in; CocoaPods stays supported) Pull Request resolved: #57332 Test Plan: 350 scripts/spm unit tests (incl. byte-identical add→deinit round-trip); manual E2E against the five-artifact set: fresh app via `cli init`→`spm add --deintegrate`→build (artifact resources verified in the app), rn-tester in-place migration (RNTesterPods.xcodeproj, test libraries + spmModules), helloworld in-place migration — all BUILD SUCCEEDED. Verified `npx react-native spm <cmd>` from the command line in each journey. ## Scope & limitations iOS, prebuilt-only (no build-from-source yet); full spm.xcframework/spm.source library metadata not yet (app-local spm.modules + scaffolding are); Expo not yet. ## Follow-ups Remote-mode ReactNative Package.swift must vend the ReactNativeDependenciesHeaders product (spm-distribution repo); library self-containment for repo-portable manifests; build-from-source. Reviewed By: mdvacca Differential Revision: D111449548 Pulled By: cipolleschi fbshipit-source-id: 87a873bf5c8be2d60f7893611e16924bfa730a46
|
@cipolleschi merged this pull request in 6aa147f. |
intergalacticspacehighway
added a commit
to intergalacticspacehighway/react-native
that referenced
this pull request
Jul 15, 2026
Pre-existing formatting lint failure from react#57442 that surfaces on this branch after merging main. Format-only change, no behavior change.
meta-codesync Bot
pushed a commit
that referenced
this pull request
Jul 15, 2026
) Summary: The OSS lint job went red after the SwiftPM stack landed (#57332 and its base PRs). The failing signal is the **Prettier** step of the `lint` job. The offending file (introduced in base PR #57442) is: `packages/react-native/scripts/ios-prebuild/__tests__/headers-xcframework-test.js` ```js let tmp /*: string */; ``` This is a Flow *comment-type annotation* on an **uninitialized** `let`. Prettier reformats an uninitialized declaration by moving the trailing comment past the semicolon: ```js let tmp; /*: string */ // annotation detached — no longer a type annotation ``` Because CI runs `prettier --list-different` (fails on any diff), this made the lint job fail. Prettier only keeps the `/*: string */` annotation inline when the declaration has an initializer, so the fix is to give `tmp` one: ```js let tmp /*: string */ = ''; ``` Semantically safe — `tmp` is assigned in `beforeEach` before any use. ## Changelog: [INTERNAL] [FIXED] - Fix Prettier lint failure in `headers-xcframework` test Pull Request resolved: #57564 Test Plan: - `yarn format-check` — clean across the repo - `yarn lint` (ESLint) — passes - `yarn lint-markdown` — passes - `yarn test-typescript-legacy` — passes - Jest `headers-xcframework-test.js` — all 5 tests pass Reviewed By: cortinico, fabriziocucci Differential Revision: D112114604 Pulled By: cipolleschi fbshipit-source-id: 9f2d116eae0cf5581cf0e1a1e79c0c3b28f7c176
cipolleschi
added a commit
that referenced
this pull request
Jul 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.
New artifact:
ReactNativeDependenciesHeaders.xcframework— the binaryReactNativeDependencies.xcframeworkis framework-type, so its rootHeaders/is invisible to SwiftPM binaryTargets (HeadersPathis rejected on framework entries; verified empirically). The deps prebuild now emits a headers-only library-type sidecar (stub archives + per-sliceHeaders/+HeadersPath— the exactReactNativeHeadersrecipe, factored into a sharedheaders-xcframework.jsemitter) 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.ReactNativeHeadersgoes pure-RN — the R2 relocation of deps namespaces (and theDEPS_NAMESPACES_NOT_RELOCATEDSocketRocket exclusion list) is deleted. Relocated copies are what enabled the SocketRocket dual-copy regression (duplicate@interface/ poisoned module graph underuse_frameworks!); that bug class is now structurally impossible. Headers gate flipped: deps namespaces must be absent from RNH; the sidecar emitter enforces set-equality withDEPS_NAMESPACESfail-closed in both directions. On the CocoaPods side, a newReactNativeDependenciesUtils.configure_aggregate_xcconfiginjects the deps pod'sHeaders/globally (aggregate + every pod target), mirroring the rncore injection — this replaces the folly/glog resolution pods previously got via the flattenedReact-Core-prebuilt/Headers.CI: prebuilt + dynamic-frameworks lane — the regression's exact config had no coverage (the
test-ios-rntesteraction hard-coupleduse-frameworks:trueto source builds). Newuse-prebuildsinput;test_ios_rntester's dynamic cells now consume the workflow-built prebuilt artifacts.Maven publishing —
ReactNativeHeadersandReactNativeDependenciesHeaderspublish standalone onreact-native-artifacts(classifiersreactnative-headers-*,reactnative-dependencies-headers-*);verifyArtifactsAreOnMavennow HEAD-checks every classifier tarball instead of only the POM.Stacked on #57440. The SwiftPM preview (#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
Test Plan
scripts/ios-prebuild/__tests__, incl. new sidecar set-equality tests)--max-warnings 0), Prettier, Flow (yarn flow-check): cleanUSE_FRAMEWORKS=dynamic✅ (the regression config — verifiedReact-Core-prebuilt/Headerscontains 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 ✅HeadersPath, 7 namespaces, slice parity with the binarypublishReleasePublicationToMavenLocal: all 12 files + POM land with the expected classifier names🤖 Generated with Claude Code