CodeVetter · Repo Unpack · Risk map
react/react-native
Dominant risk is the long-running architecture migration: legacy bridge (ReactNativeHost/ReactInstanceManager, BatchedBridge/MessageQueue, runtime codegenNativeComponent fallback) coexists with bridgeless ReactHost + TurboModule proxy, so behavior diverges by mode. Generated-code drift is a second axis — feature flags, TS types, codegen output, and three committed API snapshots all fail CI if not regenerated. The dev-middleware WebSocket inspector endpoints are a security-sensitive surface when the dev server is exposed beyond localhost. Several foot-guns exist for tooling: preinstall mutates yarn.lock, Jest resolution diverges from Metro, and cli.js silently tolerates a missing community CLI.
- Legacy/new architecture split: ReactNativeHost is @Deprecated and logs LegacyArchitectureLogLevel.ERROR; ReactActivityDelegate.getReactNativeHost() is deprecated in favor of ReactHost; code paths still branch on global.RN$Bridgeless (e.g. codegenNativeComponent warns that runtime fallback 'will be an error in the future').
- Runtime/type surface drift: index.js header warns 'Keep this file in sync with index.js.flow' — the runtime getters (incl. the untyped 'Touchable' re-export kept for a react-native-svg call site) can diverge from the declared public API, only caught by generated-types tests.
- Dev-middleware attack surface: InspectorProxy opens unauthenticated WebSocket endpoints '/inspector/device' and '/inspector/debug' proxying full CDP control of connected apps, plus '/open-debugger' which launches local applications via the tool launcher; `start --host`/`--https`/`--cert`/`--key` options determine exposure — dangerous if bound to a non-localhost interface.
- Dynamic resolution fragility: cli.js and react-native.config.js use require.resolve(..., {paths: [process.cwd()]}) for @react-native-community/* packages — correct under pnpm/isolated layouts but silently degrades (empty config, exit(1) with a warning) when the community CLI isn't installed; a module-scope try/catch swallows the missing dependency when cli.js is imported rather than executed.
- preinstall side effect: `yarn install` runs scripts/try-set-hermes-compiler-prebuilt.js which rewrites the hermes-compiler placeholder ('0.0.0') in packages/react-native/package.json and dirties yarn.lock — documented as expected, but it will pollute diffs and confuse automated installs.
- Generated-artifact drift is CI-enforced: codegen output, feature-flag accessors (yarn featureflags), types_generated + ReactNativeApi.d.ts (yarn build-types), scripts/cxx-api/api-snapshots (yarn cxx-api-build/--validate), and ReactAndroid/api/ReactAndroid.api are all regenerated-or-fail; hand-edits get reverted by CI validation.
- Metro bundling hard-depends on a one-time codegen build: without `yarn --cwd packages/react-native-codegen build`, bundling fails with 'Cannot find module @react-native/codegen/lib/parsers/flow/parser' — a documented bootstrap trap for fresh clones.
- Jest fidelity gap: the custom resolver strips `exports` and moduleNameMapper hardcodes subpath aliases, so unit tests exercise a different module-resolution graph than Metro/Node — code relying on export conditions (e.g. types_generated, react-native-legacy-deep-imports) is untested by Jest. (inferred)
- DEV-only behavior divergence: LogBox.install, checkNativeVersion, setUpReactDevTools, InteractionManager-throwing getter, warnOnce deprecation banners, and the RN$Bridgeless codegen warning only run under __DEV__, so prod bundles take meaningfully different code paths (incl. AppContainer-dev vs AppContainer-prod and ReactFabric-dev/-prod/-profiling variants).
- Ecosystem-coupled deprecations: exports for Clipboard, PushNotificationIOS, SafeAreaView, ProgressBarAndroid, DrawerLayoutAndroid, ImageBackground, UTFSequence remain (warnOnce) alongside legacy deep imports './Libraries/*' — removal is a breaking change deferred across releases; InteractionManager already throws in DEV.
- Fantom operational limits: debugging (FANTOM_DEBUG_JS/FANTOM_DEBUG_CPP) is Meta-internal only; FANTOM_PROFILE_CPP needs perf and is banned on CI; runTask() cannot be nested (throws); only a subset of the Jest global API is implemented (no test.each) — contributors must work within these limits or file gaps.
- Large monorepo coupling: the scanned tree is ~3998 files with the file walk capped (1008 skipped); a single change can trigger lint -> build_fantom_runner -> run_fantom_tests (x3 retries) -> build_android -> e2e -> maestro -> build_npm_package chains in test-all.yml, so blast radius of even small changes is expensive CI-wise. (inferred)