Skip to content

Latest commit

 

History

History
384 lines (310 loc) · 22.4 KB

File metadata and controls

384 lines (310 loc) · 22.4 KB

fork.md — React Native macOS fork tracking

Tracking document for this fork of React Native macOS 0.87. The published branch is main.

Purpose

  • Record the upstream baseline this branch is derived from.
  • Record every local commit and what it changes relative to upstream.
  • Provide a conflict map for the next upstream fetch/rebase.
  • Mark vendor/react-native-macos/** as off-limits for the workspace's external lint/format/check tooling.

Direct Meta React Native synchronization is tracked separately in react-native-upstream.md. Do not put Meta-only commits in this Microsoft/macOS rebase record.

The workspace contract that consumes this fork is docs/react-native-macos-architecture.md.

Remotes

upstream points at the canonical Microsoft repo:

Remote URL
upstream https://github.com/microsoft/react-native-macos.git

origin points at the fork's own remote; its URL is intentionally not listed here. Publish only origin/main. Follow Microsoft from upstream/0.87-merge onto main (see Sync procedure). Do not keep aw/* line branches.

CI on this fork is JS only. Source types are Flow (yarn flow-check). TypeScript is only the generated public .d.ts consumer test (yarn build-types then yarn test-typescript-legacy with workspace typescript@6). Do not install TypeScript 7 here: Yarn's builtin compat patch still expects lib/_tsc.js, and the Go tsc is not this repo's typechecker. Native jobs are not run.

Upstream baseline

  • Upstream remote branch: upstream/0.87-merge
  • Baseline commit: d8c588e4a67cbe9d3cdebb27a8909a93448807cd
  • Local branch: main
  • Last functional tip: b033ab7f1b (microsoft#3074 TextInput centering, plus 687571372d RedBox 2.0, decd178e6e Hermes v1 pin, 22d8dea9e3 color/types, and earlier in the table). Later docs(fork) commits sit on top; git rev-parse main is the branch HEAD.
  • Merge-base check: git merge-base main upstream/0.87-merge should print the baseline commit above.
  • Behind upstream/0.87-merge: 0 commits. Microsoft 0.87-merge is frozen; extra macOS fixes on upstream/main are sampled by cherry-pick, not rebase.

Local commits on main (newest first)

Functional local patches and the original tracking commit only. Meta cherry-picks and later docs(fork) bookkeeping live in react-native-upstream.md or in history, not here.

Commit Date Message
b033ab7f1b 2026-08-30 fix(macos): vertically center single-line TextInput content (#3074)
22d8dea9e3 2026-08-30 fix(js): tighten float color object parsing and VirtualizedList types
decd178e6e 2026-08-30 feat(hermes): pin Hermes v1 174344e and fix prebuild include paths
687571372d 2026-08-30 feat(macos): port RedBox 2.0 AppKit overlay via RCTUIKit primitives
285a66c810 2026-08-19 feat(js): fold float color objects in Animated string interpolation
c5017f4233 2026-08-19 feat(js): emit float color objects from oklch()/oklab() and parse color()
e06759da6b 2026-08-19 fix(macos): guard Meta tintColor copy and unmount UIView assert
be9f567c4a 2026-08-19 fix(macos): alias UIFontDescriptorAttributeName for variable fonts
b691d36eb5 2026-08-19 fix(macos): treat interactive roles as keyboard-focusable
45eb9af2b6 2026-08-18 fix(jest): merge duplicate moduleNameMapper keys in jest.config.js
02b12987df 2026-08-18 feat(js): accept {space, r, g, b, a} float color objects on macOS
ee8ed05893 2026-08-18 feat(js): parse oklch() and oklab() colors in normalize-color
440b68323c 2026-08-13 fix(deps): normalize ReactNativeDependencies macOS framework
fe8053d19c 2026-08-13 fix(prebuild): portable Core XCFramework and macOS Hermes slice
d1cfd044ac 2026-08-13 fix(macos): host multiline TextInput and high-contrast types
ead226acfc 2026-08-13 fix(macos): bind display vsync to NSWindow.displayLink
903871d965 2026-08-01 docs(fork): add fork.md upstream tracking document
3184529fff 2026-08-01 feat(fork): Hermes 0.87 compiler upgrade and AppHost runtime patches
dd8f582b78 2026-07-21 feat: prepare ActivityWatch React Native 0.87
e2a5a43f17 2026-07-21 fix: make local packages version-aware
b3f897bec0 2026-07-21 feat: add local macOS package closure

Keep this table in the same commit that changes fork.md. After a rebase, rewrite the SHAs here to the new local commits.

Why the fork exists

main tracks upstream 0.87 with three kinds of local change:

  1. Local package closure (b3f897bec0, e2a5a43f17, dd8f582b78): makes the React Native macOS packages resolvable and version-aware inside this pnpm workspace.
  2. Hermes 0.87 / Metro 0.87 runtime patches (3184529fff): compiler, executor, and JS toolchain divergence from upstream.
  3. macOS host and Core production patches (ead226acfc … 440b68323c): window-bound vsync, TextInput host, portable Core/Hermes XCFrameworks, and ReactNativeDependencies layout that Xcode 27 will load.
  4. CSS Color 4 JS parsing (ee8ed05893, c5017f4233, 285a66c810): oklch() / oklab() / color(srgb|display-p3 ...) support in @react-native/normalize-colors, ahead of upstream TODO T213000437. The conversion math mirrors packages/design-system/src/appearance/color.ts in the consuming workspace (same Ottosson constants, chroma-shrinking gamut mapping). Since c5017f4233 these normalize to {space, r, g, b, a} float objects (not packed int32s) so Display P3 fidelity survives to the native parsers; foldColorObjectToSrgbInt32 packs them back for int32 consumers, and 285a66c810 wires that fold into Animated string interpolation. Drop when upstream ships equivalent parsing.
  5. Float color object pass-through on macOS (02b12987df): normalizeColorObject accepts {space: 'srgb' | 'display-p3', r, g, b, a} so the design-system float color format reaches the existing native float/P3 consumers (RCTConvert -UIColor:, Fabric fromRawValueShared) without 8-bit quantization. Upstream has the native halves but no JS producer gate; drop if upstream adds an equivalent object form.
  6. macOS follow-ups to Meta ports (b691d36eb5, be9f567c4a, e06759da6b): keyboard-focus analog for interactive role, RCTUIKit alias so variable-font axes compile, and iOS-only tintColor / RCTUIView unmount-assert guards. Provenance is in react-native-upstream.md; these SHAs are local and must survive a Microsoft rebase.
  7. RedBox 2.0 AppKit overlay (FX-124 / FX-125): compile and fatal errors use the structured RedBox 2.0 sheet on macOS (code frame, ANSI highlight, native Metro /hot reload). Adapted from microsoft#3054 (c631f745); do not take #3058. Drop when 0.87-merge or main ships an equivalent AppKit V2 controller. Fill the local SHA in the table above when this patch is committed.

Reduce Motion policy lives in the application motion layer, not this fork. Do not look for it here during rebase.

Pinned patches and rebase conflicts

When git rebase --onto upstream/0.87-merge <BASELINE> main hits a file below, keep the local behavior unless upstream already shipped an equivalent fix. Prefer re-applying the intent over preserving the exact hunk.

Commit Files Keep our behavior when Upstream usually changes
ead226acfc ReactApple/Libraries/RCTUIKit/RCTPlatformDisplayLink.m Vsync comes from NSWindow.displayLink (fallback NSScreen.displayLink). Pause is CADisplayLink.paused. Do not restore CVDisplayLinkCreateWithActiveCGDisplays. Any CVDisplayLink rewrite, pause/stop threading, timestamp source
d1cfd044ac TextInput.js, TextInput.flow.js, TextInput-macos-test.js, AccessibilityInfo.d.ts macOS multiline uses the iOS native host. isHighContrastEnabled stays on the typed surface. Platform.OS === 'ios' TextInput branches; new AccessibilityInfo methods
fe8053d19c scripts/ios-prebuild.js, ios-prebuild/build.js, headers-config.js, hermes.js, xcframework.js Caller-owned RN_MACOS_CORE_PREBUILD_ROOT; compiler prefix maps; dSYM embed; strip producer rpaths; versioned Headers/Modules; Hermes universal XCFramework includes the macOS slice; macOS view headers in Core. setup/build/compose flow, xcodebuild flags, symbol copy, Hermes download
440b68323c ReactNativeDependencies.podspec, replace_dependencies_version.js, scripts/cocoapods/rncore.rb Extracted macOS RND framework is versioned (Versions/A + Current links, bundles under Resources). Last-build marker and extract paths are under Pods root. Prebuilt Core HEADER_SEARCH_PATHS stay. tar extract script, last-build file, VFS overlay flags
3184529fff Hermes pins, RCTBridgeModule.h, Hermes executor, babel/metro, RCTViewComponentView.mm Hermes 0.87 compiler + AppHost runtime patches. Any overlapping Hermes/Metro bump
ee8ed05893 packages/normalize-color/index.js, __tests__/normalizeColor-test.js oklch()/oklab() matchers and OKLab→sRGB conversion with chroma-shrinking gamut mapping. Drop only if upstream ships T213000437 with equivalent CSS Color 4 semantics. matcher table, parse helpers, test file
c5017f4233 packages/normalize-color/index.js, index.js.flow, __tests__/normalizeColor-test.js oklch()/oklab()/color() emit {space, r, g, b, a} float objects; srgb classification keeps a half-quantization-step tolerance; foldColorObjectToSrgbInt32 is exported. Drop only with an upstream float-object emitting parser. matcher table, emission helpers, flow decl
285a66c810 Libraries/Animated/nodes/AnimatedInterpolation.js, __tests__/processColorModernSyntax-test.js mapStringToNumericComponents folds float color objects via foldColorObjectToSrgbInt32 before the PlatformColor invariant. string interpolation internals
02b12987df Libraries/StyleSheet/PlatformColorValueTypes.macos.js, __tests__/PlatformColorValueTypesMacOS-float-test.js _normalizeColorObject keeps the 'space' in color branch (validate space/channels, fill a, pass through). Drop only if upstream ships an equivalent float color object form. LocalNativeColorValue shape, branch order in _normalizeColorObject
45eb9af2b6 jest.config.js moduleNameMapper stays a single merged object (setup-env alias + out-of-tree react-native/* mappers). Re-merge if upstream adds new mapper entries. upstream mapper entries in the first literal
b691d36eb5 RCTViewComponentView.mm acceptsFirstResponder honors interactive role / accessibilityRole. needsPanelToBecomeKey stays false. iOS Full Keyboard Access / canBecomeFocused ports
be9f567c4a RCTUIKitCompat.h UIFontDescriptorAttributeName aliases to NSFontDescriptorAttributeName. Apple variable-font / RCTFontUtils
e06759da6b RCTTextInputUtils.mm, RCTViewComponentView.mm tintColor copy stays iOS-only; unmount assert uses RCTUIView, not UIView. TextInput backing-view swap; unmount index assert
FX-124 RCTUITableView.{h,m}, RCTUIButton.{h,m}, RCTUILabel.m, RCTUIKit.h, RCTRedBox.mm, RCTRedBox2Controller.mm, RCTRedBox2AnsiParser* macOS RedBox uses V2 (TARGET_OS_OSX or redBoxV2IOS()). Keep table/button primitives and the AppKit controller. Restore iOS autoRetryTick background guard. Do not restore !TARGET_OS_OSX compile-outs on the V2 controller. microsoft#3054 landing on main/0.87-merge; RCTUIKit table/button additions

Interrupted Core production can leave packages/react-native/.build mapped to the workspace cache. Remove that leftover before the next Core build. Do not commit it.

Do not commit Core version overlays

During Core production the workspace overlays these files from installed upstream React Native (0.87.0-rc.1) and must restore them afterwards:

  • packages/react-native/React/Base/RCTVersion.m
  • packages/react-native/ReactCommon/cxxreact/ReactNativeVersion.h

The committed fork versions stay 1000.0.0. A dirty 0.87.0-rc.1 overlay is producer residue, not a patch. Restore with git checkout -- those two paths before committing.

FX-69 source Hermes (Xcode 27 linker)

Source-Hermes Apple framework scripts passed LINKER:-ld_classic for every Xcode ≥ 15. Xcode 27 removed the classic linker, so that flag fails the source-Hermes lane. Gate it to Xcode 15–16:

  • packages/react-native/sdks/hermes-engine/utils/build-hermes-xcode.sh
  • packages/react-native/sdks/hermes-engine/utils/build-apple-framework.sh

Prebuilt Maven Hermes is unchanged. This is only for the source-Hermes compatibility lane (FX-69: rebuild V1 at static_h 174344e).

FX-69 V1 pin: sdks/.hermes-source-sha is the 40-hex facebook/hermes revision (currently 174344e637705bf9bfd036689e3b89ab5f67fd10). sdks/.hermesv1version stays hermes-v174344e.0.1 for RN version strings.

rn-macos native ensure builds the pinned SHA tarball when it is missing. The VM is always MinSizeRel + HERMESVM_ALLOW_JIT=1, dual-arch macosx, with HERMES_ENABLE_DEBUGGER compiled in (RN still gates the inspector on Debug). native ensure / pack native refuse Maven and overlay destroot into consumer Pods/hermes-engine after pod install. Advance by changing sdks/.hermes-source-sha and running native ensure; roll back the same way. HBC remains 99.

static_h 174344e versioned _HERMES_CTORCONFIG_STRUCT (HERMES_GCCONFIG_VERSION / HERMES_RUNTIME_CONFIG_VERSION, plus RuntimeConfig.FinalizerThreadRunner). HermesInstance.cpp / HermesExecutorFactory.cpp must compile against destroot/include from that tarball, not the 260318099 Public headers: React-RuntimeHermes HEADER_SEARCH_PATHS and Package.swift searchPaths put that include first, and both TUs #error if the version macros are missing. Local tarball HERMES_CLI_PATH is destroot/bin/hermesc (not hermes-compiler npm).

FX-124 RedBox 2.0 AppKit (microsoft#3054)

macOS Debug compile/fatal overlay. Tracking: FX-124 (parent), FX-125 (this adaptation).

Provenance is microsoft#3054 head c631f745 (tree be278997). Taken:

  • RCTUIKit primitives: RCTUITableView, RCTUIButton, RCTUILabel getter/setter completions, umbrella imports
  • RedBox 2.0 AppKit controller + ANSI parser un-gating
  • RCTRedBox.mm reachability so macOS can instantiate V2

Not taken: #3058 (0.85 RN merge; already an ancestor of main), #3056 V1 table consumer, native tests, RNTester project wiring.

Local deltas on top of #3054:

  • macOS always selects RedBox 2.0 (TARGET_OS_OSX || redBoxV2IOS()). The redBoxV2IOS default stays false so iOS is unchanged.
  • autoRetryTick keeps the iOS UIApplicationStateActive guard that #3054 dropped for AppKit.
  • Parser accepts Metro 0.87 oxc path: message syntax errors and Unable to resolve module … from …: frames (not only Babel TransformError / UnableToResolveError in /path: msg (line:col)). Resolve errors expose specifier, importer, and tried paths. Code-frame > N | fills row when Metro omits (line:col).
  • macOS overlay is an inspector (kind, location, specifier/tried, source), not a LogBox red banner. Copy emits that structured report. Clicking the summary or source opens the file via /open-stack-frame.
  • AppKit sheet tracks _sheetPresented plus the host captured at present time. Dismiss pairs presentViewControllerAsSheet: with dismissViewController: on that host (not RCTKeyWindow(), which is the sheet). endSheet alone leaves the VC presented and the next present asserts. Auto-retry timer is added to NSModalPanelRunLoopMode.
  • RCTUILabel wrapping + RCTUITableView automatic row measurement. Custom RedBox cells call prepareForCustomContent so the empty default textLabel does not pin the row to ~2px.
  • Single presenter on macOS: DEV JS errors (fatals, component errors, console.error, Metro compile) and native RCTLog / bundle-load failures go to RCTRedBox2Controller. Warnings (Warning: / type: warn) stay out of the overlay. LogBox's RCTLogBoxView sheet is not shown. Overlay chrome follows Expo LogBox (kind chip, source card, collapsed ignore-listed frames).

RedBox overlay iteration must not go through APP1 native ensure (Core fingerprint invalidates every selected Pod). Use RNTester-macOS against this worktree (fork source + pinned Hermes destroot tarball, not Maven):

  • Parser: compile ReactCommon/react/debug/redbox/RedBoxErrorParser.cpp with a tiny driver (seconds).
  • Overlay GUI: packages/rn-tester/scripts/run-macos-redbox.sh. pods sets HERMES_ENGINE_TARBALL_PATH from sdks/.hermes-source-sha, RCT_NEW_ARCH_ENABLED=1, RN_PLATFORMS=macOS, overlays destroot (HERMES_GCCONFIG_VERSION), then Debug-builds. APIs → RedBox 2.0 overlay. Canned fatal messages do not rebuild native. Break js/RedBoxCompileProbe.js and save for a real Metro overlay.
  • APP1 pack + native ensure --configuration Debug only when a batch is ready to land on the packed consumer.

This invalidates React Core (RCTUIKit + CoreModules). After the fork commit: pack the npm closure and rebuild native dependency artifacts before a session can show the new sheet.

The Hermes 0.87 patch set (3184529fff)

24 files, consolidating the fork's runtime changes for the Hermes 0.87 / Metro 0.87 stack:

  • Dependency pins — packages/react-native/package.json, packages/community-cli-plugin/package.json, packages/metro-config/package.json, yarn.lock: hermes-compiler 260318099.0.1, metro ^0.87, exact Metro dependency bumps.
  • Hermes toolchain — packages/react-native/sdks/.hermesversion, .hermesv1version, version.properties, hermes-utils.rb, build-hermesc-xcode.sh, react-native-xcode.sh: compiler flags (hermes-canary, async generators).
  • Native runtime — RCTBridgeModule.h (dispatch_queue_t OS_OBJECT_USE_OBJC compat), HermesExecutorFactory.cpp / HermesInstance.cpp (executor/instance fixes), ReactNativeFeatureFlagsDefaults.h (enableCppPropsIteratorSetter, preventShadowTreeCommitExhaustion), RCTViewComponentView.mm. RCTPlatformDisplayLink.m in that commit is the older CVDisplayLink wrapper; ead226acfc replaced it.
  • JS toolchain — react-native-babel-preset (unstable_disableTypeScriptTransform), react-native-babel-transformer (babel.config.cjs fallback), packages/polyfills/console.js (spread fix), asset-utils / assets-registry (density buckets), debugger-shell (environment check), generateSchemaInfos.js (codegen separator).
  • Release workflow — .github/workflows/create-draft-release.yml (Hermes version hint).

Microsoft main extras (sampled onto 0.87)

upstream/0.87-merge is the merge baseline and is fully in sync. New macOS fixes after that freeze land on Microsoft main. Do not rebase this fork onto Microsoft main. Cherry-pick product commits; skip docsite/npm publishing.

Upstream Status Notes
08ecdfded7 #3074 TextInput vertical center taken b033ab7f1b Applied clean on 0.87 TextInput
78f5b04387 #3023 Dev loading view clipping deferred Valuable; RCTDevLoadingView.mm does not apply. Hand-port later
efb2eb5010 #3056 RCTUITableView + RedBox V1/V2 skip Overlaps FX-124 RCTUITableView / RedBox 2.0
a93f65c117 #3024 visionOS platform declare skip Not shipping visionOS
docsite / nx / npm trusted publishing skip Not runtime

Sync procedure (run whenever upstream moves)

1. Check whether this branch is behind upstream

cd vendor/react-native-macos

git fetch upstream

# How many upstream commits does main not have yet?
# 0 means fully in sync — stop here, nothing to do.
git rev-list --count <BASELINE>..upstream/0.87-merge

# Sanity: merge-base must still be <BASELINE>
git merge-base main upstream/0.87-merge

2. Rebase local commits onto the new upstream head

git rebase --onto upstream/0.87-merge <BASELINE> main

Resolve conflicts using the table above. After each conflict, confirm the pinned behavior still holds (window-bound display link, multiline TextInput, portable Core, versioned RND).

Verify every local commit survived:

git log --oneline <new-merge-base>..main   # expect the same local commits as before

3. Update this file

  • Update <BASELINE> and the local-commit table above to the new values.
  • Keep the conflict map aligned with the surviving commits.
  • If the rebase rewrote the fork.md commit itself, amend it so this document stays a single clean commit on top of the patch set.

4. Push (submodule first, then the parent repo)

The parent repo's submodule pointer must reference a commit that already exists on the fork's remote — push the submodule branch before the parent.

# a) Push the submodule branch to the fork's own remote
cd vendor/react-native-macos
git push origin main

# b) Record the new submodule HEAD in the parent repo, then push the parent
cd <workspace-root>
git add vendor/react-native-macos
git commit -m "chore(vendor): bump react-native-macos submodule to <new-head>"
git push <parent-remote> <parent-branch>

5. Final verification

cd vendor/react-native-macos
git status --short                                        # clean
git log --oneline <new-merge-base>..main               # all local commits present
cd <workspace-root>
git status --short -- vendor/react-native-macos           # no pending submodule change
git ls-tree HEAD vendor/react-native-macos                # pointer matches submodule HEAD

<BASELINE> and <new-merge-base> are placeholders for the values recorded in the Upstream baseline section and the post-rebase merge-base respectively.

Workspace tooling contract

vendor/react-native-macos must never be linted, formatted or checked by workspace-external tooling. Exclusions live in:

  • .oxfmtrc.json — ignorePatterns: ["vendor/react-native-macos/**"]
  • oxlint.config.ts — ignorePatterns: ["vendor/react-native-macos/**"]
  • sgconfig.yml — ignore: ["vendor/react-native-macos/**"]