diff --git a/packages/react-native/Package.swift b/packages/react-native/Package.swift index 386ca6958b7f..b55e5ae3ae0a 100644 --- a/packages/react-native/Package.swift +++ b/packages/react-native/Package.swift @@ -136,7 +136,8 @@ let reactFeatureFlags = RNTarget( let reactPerfLogger = RNTarget( name: .reactPerfLogger, path: "ReactCommon/reactperflogger", - excludedPaths: ["fusebox"] + excludedPaths: ["fusebox"], + dependencies: [.reactDebug, .reactNativeDependencies] ) /// React-logger.podspec diff --git a/packages/react-native/ReactCommon/jserrorhandler/CMakeLists.txt b/packages/react-native/ReactCommon/jserrorhandler/CMakeLists.txt index 8cdc80401168..88032ada27c2 100644 --- a/packages/react-native/ReactCommon/jserrorhandler/CMakeLists.txt +++ b/packages/react-native/ReactCommon/jserrorhandler/CMakeLists.txt @@ -22,6 +22,7 @@ target_link_libraries(jserrorhandler folly_runtime ${mapbufferjni} react_cxxstableapi + react_debug react_featureflags ) target_compile_reactnative_options(jserrorhandler PRIVATE) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/ConsoleTimeStamp.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/ConsoleTimeStamp.h index 036b1bb1ea35..07ed70233d59 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/ConsoleTimeStamp.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/ConsoleTimeStamp.h @@ -9,9 +9,9 @@ #include +#include #include #include -#include #include #include diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/EventLoopReporter.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/EventLoopReporter.h index 768fbc174bf8..4531200f4f00 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/EventLoopReporter.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/EventLoopReporter.h @@ -10,7 +10,7 @@ #include #if defined(REACT_NATIVE_DEBUGGER_ENABLED) -#include +#include #endif namespace facebook::react::jsinspector_modern::tracing { diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/FrameTimingSequence.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/FrameTimingSequence.h index a65269eaa11e..aeeaba017d84 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/FrameTimingSequence.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/FrameTimingSequence.h @@ -11,7 +11,7 @@ #include "TraceEvent.h" -#include +#include #include #include diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/HostTracingProfile.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/HostTracingProfile.h index efe22ff3e900..b55c9225ccc6 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/HostTracingProfile.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/HostTracingProfile.h @@ -13,7 +13,7 @@ #include "InstanceTracingProfile.h" #include "RuntimeSamplingProfile.h" -#include +#include #include diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h index 7f556cc5b9c0..f85c16c96bd7 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/PerformanceTracer.h @@ -13,7 +13,7 @@ #include "TraceEvent.h" #include "TraceEventProfile.h" -#include +#include #include #include diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/RuntimeSamplingProfileTraceEventSerializer.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/RuntimeSamplingProfileTraceEventSerializer.h index b06cf1de0e05..a1b0b6198e5a 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/RuntimeSamplingProfileTraceEventSerializer.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/RuntimeSamplingProfileTraceEventSerializer.h @@ -11,7 +11,7 @@ #include "RuntimeSamplingProfile.h" -#include +#include namespace facebook::react::jsinspector_modern::tracing { diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TimeWindowedBuffer.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TimeWindowedBuffer.h index f21901190e5d..182b3c78975c 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TimeWindowedBuffer.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TimeWindowedBuffer.h @@ -14,7 +14,7 @@ #include #include -#include +#include namespace facebook::react::jsinspector_modern::tracing { diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/Timing.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/Timing.h index 8c9c66b6b3ec..5406602bdfcd 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/Timing.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/Timing.h @@ -11,7 +11,7 @@ #include -#include +#include namespace facebook::react::jsinspector_modern::tracing { diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceEvent.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceEvent.h index f885f9e6e6f1..520b5290277a 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceEvent.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceEvent.h @@ -9,8 +9,8 @@ #include +#include #include -#include #include diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceEventGenerator.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceEventGenerator.h index d7ae8b42fec7..a46e7c2ba7f0 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceEventGenerator.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceEventGenerator.h @@ -11,8 +11,8 @@ #include "TraceEvent.h" +#include #include -#include #include #include diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceEventProfile.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceEventProfile.h index 0b49bc05ba4c..f7725cd8844c 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceEventProfile.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceEventProfile.h @@ -9,7 +9,7 @@ #include -#include +#include #include #include diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h index b736b9bf46cd..4439a1a48593 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tracing/TraceRecordingState.h @@ -13,7 +13,7 @@ #include "RuntimeSamplingProfile.h" #include "TracingMode.h" -#include +#include #include diff --git a/packages/react-native/ReactCommon/jsitooling/CMakeLists.txt b/packages/react-native/ReactCommon/jsitooling/CMakeLists.txt index b8024cf8b78e..7b834d53742c 100644 --- a/packages/react-native/ReactCommon/jsitooling/CMakeLists.txt +++ b/packages/react-native/ReactCommon/jsitooling/CMakeLists.txt @@ -21,7 +21,8 @@ target_link_libraries(jsitooling folly_runtime glog jsi - react_cxxstableapi) + react_cxxstableapi + react_timing) target_compile_reactnative_options(jsitooling PRIVATE) target_compile_options(jsitooling PRIVATE -Wpedantic) diff --git a/packages/react-native/ReactCommon/react/renderer/bridging/bridging.h b/packages/react-native/ReactCommon/react/renderer/bridging/bridging.h index 1a4523d6b30d..a04c0f4947d2 100644 --- a/packages/react-native/ReactCommon/react/renderer/bridging/bridging.h +++ b/packages/react-native/ReactCommon/react/renderer/bridging/bridging.h @@ -9,9 +9,9 @@ #include +#include +#include #include -#include -#include namespace facebook::react { diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h index 59c8f6ceac82..6847f482af61 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.h @@ -15,12 +15,10 @@ #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include @@ -29,7 +27,6 @@ #include #include #include -#include namespace facebook::react { diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h b/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h index 10f45018d94a..e6ca676fdbae 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerDelegate.h @@ -11,7 +11,7 @@ #include -#include +#include #include #include diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerToolbox.h b/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerToolbox.h index 50ddbdca8015..2e6e34399b36 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerToolbox.h +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/SchedulerToolbox.h @@ -11,14 +11,13 @@ #include -#include +#include +#include +#include +#include #include -#include -#include #include #include -#include -#include namespace facebook::react { diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceHandler.h b/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceHandler.h index 40a1ee28f79c..5e03e796e67a 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceHandler.h +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceHandler.h @@ -12,11 +12,9 @@ #include #include +#include +#include #include -#include -#include -#include -#include namespace facebook::react { diff --git a/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceManager.h b/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceManager.h index ed76f11d4c11..dcc3968659de 100644 --- a/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceManager.h +++ b/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceManager.h @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include diff --git a/packages/react-native/ReactCommon/react/renderer/telemetry/SurfaceTelemetry.h b/packages/react-native/ReactCommon/react/renderer/telemetry/SurfaceTelemetry.h index 26848773c92a..685e9dbf3406 100644 --- a/packages/react-native/ReactCommon/react/renderer/telemetry/SurfaceTelemetry.h +++ b/packages/react-native/ReactCommon/react/renderer/telemetry/SurfaceTelemetry.h @@ -11,8 +11,8 @@ #include +#include #include -#include namespace facebook::react { diff --git a/packages/react-native/ReactCommon/react/renderer/telemetry/TransactionTelemetry.h b/packages/react-native/ReactCommon/react/renderer/telemetry/TransactionTelemetry.h index 2f53debedf58..92289403b252 100644 --- a/packages/react-native/ReactCommon/react/renderer/telemetry/TransactionTelemetry.h +++ b/packages/react-native/ReactCommon/react/renderer/telemetry/TransactionTelemetry.h @@ -13,7 +13,7 @@ #include #include -#include +#include namespace facebook::react { diff --git a/packages/react-native/ReactCommon/react/runtime/BridgelessNativeMethodCallInvoker.h b/packages/react-native/ReactCommon/react/runtime/BridgelessNativeMethodCallInvoker.h index 19afec86b7b3..21c9ea91a57a 100644 --- a/packages/react-native/ReactCommon/react/runtime/BridgelessNativeMethodCallInvoker.h +++ b/packages/react-native/ReactCommon/react/runtime/BridgelessNativeMethodCallInvoker.h @@ -9,7 +9,7 @@ #include -#include +#include #include #include diff --git a/packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.h b/packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.h index a29b3fc702c9..4f54ea30bc8b 100644 --- a/packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.h +++ b/packages/react-native/ReactCommon/react/runtime/BufferedRuntimeExecutor.h @@ -9,8 +9,8 @@ #include -#include -#include +#include +#include #include #include #include diff --git a/packages/react-native/ReactCommon/react/runtime/CallInvokerImpl.h b/packages/react-native/ReactCommon/react/runtime/CallInvokerImpl.h index f4b44505ac1b..ba94771c527d 100644 --- a/packages/react-native/ReactCommon/react/runtime/CallInvokerImpl.h +++ b/packages/react-native/ReactCommon/react/runtime/CallInvokerImpl.h @@ -9,8 +9,7 @@ #include -#include -#include +#include #include #include diff --git a/packages/react-native/ReactCommon/react/runtime/ReactInstance.h b/packages/react-native/ReactCommon/react/runtime/ReactInstance.h index 5d61e681fbb1..1ec9392feeb0 100644 --- a/packages/react-native/ReactCommon/react/runtime/ReactInstance.h +++ b/packages/react-native/ReactCommon/react/runtime/ReactInstance.h @@ -9,9 +9,9 @@ #include -#include -#include -#include +#include +#include +#include #include #include #include diff --git a/packages/react-native/ReactCommon/react/runtime/TimerManager.h b/packages/react-native/ReactCommon/react/runtime/TimerManager.h index 7ace46ee81fd..ecd04cf4205d 100644 --- a/packages/react-native/ReactCommon/react/runtime/TimerManager.h +++ b/packages/react-native/ReactCommon/react/runtime/TimerManager.h @@ -9,7 +9,7 @@ #include -#include +#include #include #include diff --git a/packages/react-native/ReactCommon/react/timing/primitives.h b/packages/react-native/ReactCommon/react/timing/primitives.h index d7e2ebe8816b..d691df706674 100644 --- a/packages/react-native/ReactCommon/react/timing/primitives.h +++ b/packages/react-native/ReactCommon/react/timing/primitives.h @@ -9,7 +9,7 @@ #include -#include +#include #include #include #include diff --git a/packages/react-native/scripts/cocoapods/utils.rb b/packages/react-native/scripts/cocoapods/utils.rb index 2140b3013b0c..9a2326e38cad 100644 --- a/packages/react-native/scripts/cocoapods/utils.rb +++ b/packages/react-native/scripts/cocoapods/utils.rb @@ -424,6 +424,7 @@ def self.update_search_paths(installer) .concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-featureflags", "React_featureflags", [])) .concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-renderercss", "React_renderercss", [])) .concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-cxxstableapi", "React_cxxstableapi", [])) + .concat(ReactNativePodsUtils.create_header_search_path_for_frameworks("PODS_CONFIGURATION_BUILD_DIR", "React-debug", "React_debug", [])) .each{ |search_path| header_search_paths = self.add_search_path_if_not_included(header_search_paths, search_path) } diff --git a/packages/react-native/scripts/ios-prebuild/__docs__/headers-rules.md b/packages/react-native/scripts/ios-prebuild/__docs__/headers-rules.md index 3c4de65e246a..dbd9ceac6128 100644 --- a/packages/react-native/scripts/ios-prebuild/__docs__/headers-rules.md +++ b/packages/react-native/scripts/ios-prebuild/__docs__/headers-rules.md @@ -8,7 +8,7 @@ scripts under `scripts/ios-prebuild/`: | Script | Role | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `headers-inventory.js` | **Discover + classify** every shipped header (the facts) | -| `headers-spec.js` | **The rules** (R1–R11) — turns the inventory into a layout plan + module maps | +| `headers-spec.js` | **The rules** (R1–R12) — turns the inventory into a layout plan + module maps | | `headers-compose.js` | **Emit** — projects the plan into `React.xcframework` and `ReactNativeHeaders.xcframework` | | `headers-verify.js` | **Gate** — generator-time verification: include-health ratchet, structural byte-compare, consumer-shaped compile smokes (runs in the prebuild CI compose job) | @@ -35,7 +35,7 @@ everything else. No overlay, no include rewriting, no consumer flags. podspecs ──► headers-inventory.js ──► inventory (facts per header) │ ▼ - headers-spec.js (rules R1–R10) + headers-spec.js (rules R1–R12) │ plan: what goes where + module maps ▼ headers-compose.js (emission) @@ -233,6 +233,28 @@ emitted as a one-line redirect shim (`#import `). Shims that are namespace-module members are fine: they import the owning module, so declarations stay single-owned. +**R12 — a namespace module's own umbrella stays in ReactNativeHeaders.** The C++ +stable API ships one umbrella per module, physically nested inside it +(`ReactCommon/react/debug/React/Debug.h`), so its natural path is +`React/Debug.h` and R1 would hoist it into the framework. It must not be: +**ReactNativeHeaders is the lower layer** — `React.framework` imports it +(`RCTCallInvoker.h` → ``). A framework-owned umbrella +makes every `#include ` inside a lowercase-namespace header an import +of module `React`, closing a cycle: + +```text +React -> ReactNativeHeaders_react -> React +``` + +Found empirically: `react/timing/primitives.h` -> ``. This is +the same two-module-ownership failure as `UMBRELLA_CXX_GUARDED_EXCLUSIONS` +(`RCTFrameTimingsObserver.h`, which reaches the same `primitives.h`), in the +opposite direction. They are `objc-blocked` by construction (they re-export +their module's C++ surface), so they were never R4 umbrella or R5 module +members; `planFromInventory` fails closed if one ever becomes a modular +candidate, since its R5 module would be named `React` and alias the framework +module. + ## Stage 3 — Emission (headers-compose.js) `computeSpecPlan(rnRoot)` = inventory → plan, throwing on R8 collisions. Then: diff --git a/packages/react-native/scripts/ios-prebuild/__tests__/headers-spec-test.js b/packages/react-native/scripts/ios-prebuild/__tests__/headers-spec-test.js index be9df53c06dc..057362ccc3dc 100644 --- a/packages/react-native/scripts/ios-prebuild/__tests__/headers-spec-test.js +++ b/packages/react-native/scripts/ios-prebuild/__tests__/headers-spec-test.js @@ -266,6 +266,55 @@ describe('R11 redirect shims for dual-identity headers', () => { }); }); +describe('R12 namespace module umbrellas stay in ReactNativeHeaders', () => { + test('a module-nested React/ umbrella is not hoisted into the framework', () => { + const m = validManifest(); + m.headers.push( + entry( + 'React/Debug.h', + 'objc-blocked', + 'ReactCommon/react/debug/React/Debug.h', + ), + ); + const plan = planFromInventoryForTest(m); + // Lower layer: including it from a react/-namespace header cannot create a + // React module edge (React -> ReactNativeHeaders_react -> React). + expect( + plan.reactNativeHeaders.find(e => e.naturalPath === 'React/Debug.h') + ?.relPath, + ).toBe('React/Debug.h'); + expect( + plan.react.find(e => e.naturalPath === 'React/Debug.h'), + ).toBeUndefined(); + expect(plan.umbrella).not.toContain('React/Debug.h'); + expect(Object.keys(plan.namespaceModules)).not.toContain('React'); + }); + + test('genuine React.framework headers are still hoisted (R1)', () => { + const m = validManifest(); + m.headers.push( + entry('React/RCTMessageThread.h', 'cxx', 'React/Base/RCTMessageThread.h'), + ); + const plan = planFromInventoryForTest(m); + expect( + plan.react.find(e => e.naturalPath === 'React/RCTMessageThread.h') + ?.relPath, + ).toBe('RCTMessageThread.h'); + }); + + test('fails closed if an R12 umbrella becomes a modular candidate', () => { + const m = validManifest(); + m.headers.push( + entry( + 'React/Debug.h', + 'objc-modular-candidate', + 'ReactCommon/react/debug/React/Debug.h', + ), + ); + expect(() => planFromInventoryForTest(m)).toThrow(/R12/); + }); +}); + describe('DEPS_NAMESPACES (R2 — the deps sidecar namespace set)', () => { test('includes SocketRocket: one physical home, in the sidecar', () => { // Pre-sidecar, SocketRocket was excluded from relocation because a REAL diff --git a/packages/react-native/scripts/ios-prebuild/headers-spec.js b/packages/react-native/scripts/ios-prebuild/headers-spec.js index e1bde3dc419b..8696ea836bfa 100644 --- a/packages/react-native/scripts/ios-prebuild/headers-spec.js +++ b/packages/react-native/scripts/ios-prebuild/headers-spec.js @@ -79,6 +79,20 @@ * one-line redirect shim (`#import `). Shims that are namespace- * module members are fine: they import the owning module, so declarations * stay single-owned. + * R12. A namespace module's OWN umbrella (ReactCommon//React/.h, + * natural path React/.h) ships in ReactNativeHeaders — it is NOT + * hoisted into React.framework by R1. ReactNativeHeaders is the LOWER + * layer (React.framework imports it: RCTCallInvoker.h pulls + * ), so a framework-owned umbrella turns every + * `#include ` inside a lowercase-namespace header into an import + * of module React and closes a cycle: + * React -> ReactNativeHeaders_react -> React + * Found empirically: react/timing/primitives.h -> . Kept in + * ReactNativeHeaders the same spelling still resolves — framework lookup + * misses and clang falls back to the header search path — textually and + * within one artifact, so no module edge is created. These umbrellas are + * objc-blocked by construction (they re-export their module's C++ + * surface), so they were never R4 umbrella or R5 module members anyway. */ const fs = require('node:fs'); @@ -280,6 +294,13 @@ function renderNamespaceUmbrella( return `#ifdef __OBJC__\n#import \n#endif\n\n${imports}\n`; } +// R12: a namespace module's own umbrella — physically nested inside the module +// it re-exports (ReactCommon/react/debug/React/Debug.h), which is what tells it +// apart from the ~310 genuine React.framework headers that also carry a +// `React/` natural path but live under React/, Libraries/, ReactApple/, ... +const NS_MODULE_UMBRELLA_RE /*: RegExp */ = + /^ReactCommon\/.+\/React\/[^/]+\.h$/; + /** * Computes the full layout plan from the header inventory manifest * (build/header-inventory.json — regenerate with header-inventory.js). @@ -307,7 +328,13 @@ function planFromInventory( let bucketKey; let entryList; let relPath; - if (np.startsWith('React/')) { + if (np.startsWith('React/') && NS_MODULE_UMBRELLA_RE.test(source)) { + // R12: a namespace module's own umbrella stays in the LOWER layer, so + // including it from that namespace cannot create a React module edge. + relPath = np; + bucketKey = `ReactNativeHeaders/${relPath}`; + entryList = reactNativeHeaders; + } else if (np.startsWith('React/')) { relPath = np.slice(6); // R1: hoist React/ to the framework Headers root bucketKey = `React.framework/${relPath}`; entryList = react; @@ -332,8 +359,13 @@ function planFromInventory( seen.set(bucketKey, source); entryList.push({relPath, source, naturalPath: np}); - // R4: React umbrella membership. - if (np.startsWith('React/') && isUmbrellaSafe(h, root)) { + // R4: React umbrella membership. Only headers the framework actually ships + // (R12 umbrellas carry a React/ natural path but live in ReactNativeHeaders). + if ( + entryList === react && + np.startsWith('React/') && + isUmbrellaSafe(h, root) + ) { umbrella.push(np); } // R5: namespace modules (only for ReactNativeHeaders namespaces). Every @@ -346,6 +378,19 @@ function planFromInventory( if (entryList === reactNativeHeaders) { const ns = np.split('/')[0]; if (isUmbrellaSafe(h, root)) { + // R12 assert: an R12-routed umbrella lands under the `React` namespace + // here, and renderNamespaceModuleMap only renames the lowercase `react` + // one — so its module would be named `React` and alias the framework + // module, reintroducing the cycle R12 exists to break. These umbrellas + // are objc-blocked today so they never reach this branch; fail closed + // if that ever changes. + if (ns === 'React') { + throw new Error( + `R12: '${np}' is a modular candidate in ReactNativeHeaders. Its ` + + `namespace module would be named 'React' and alias the React ` + + `framework module. Keep it out of the modular surface.`, + ); + } // R5 exemption assert: a namespace whose name is not a valid module // identifier cannot get a module, so a modular-candidate header in it // would be silently non-modular — consumers importing it from a