Repository navigation
Run 54 React Native and Expo libraries on macOS, and add real pointer input to the e2e driver - #530
Closed
jmeistrich wants to merge 98 commits into
Closed
jmeistrich wants to merge 98 commits into
jmeistrich wants to merge 98 commits into
Conversation
Move the SDK matrix to Expo 55.0.32, React Native 0.83.10, React 19.2.0, React Native macOS 0.83.0 and React Native Windows 0.83.2, with the Expo Desktop SDK 55 packages (template 55.83.10, metro-config 55.83.3, modules-core and stubs 55.0.2). Expo Desktop CLI, prebuild-config and config-plugins stay on their SDK-agnostic 1.x releases. - Regenerate the expo-audio patch on 55.0.18; upstream now cancels stale artwork loads but still delivers them off the main thread and keeps artwork across sessions. - Retarget the RN macOS source patches, RNW geometry adapter, Expo CLI key patch and autolinking symlink patch; their anchors still match. - Port the iOS and Android @expo/ui adapters to the 55 API. - Raise React peer floors to ^19.2.0.
RNW 0.83.2's CLI requires @react-native-windows/find-dotnet-tools without declaring it, and its react-native.config.js probes for PowerShell when Expo 55 autolinking loads it on macOS. Depend on the helper as Expo Desktop's template does, and exclude RNW from macOS autolinking.
Template 55.83.10 replaces the Objective-C host with a SwiftUI App and a Swift ExpoAppDelegate, so the plugin wrote Spark's AppDelegate.mm into AppDelegate.swift. Swap the Swift delegate for AppDelegate.mm, main.m and the menu storyboard (from the SDK 54 template) in the macOS target.
The Expo Desktop 55 Podfile strips this alias from expo-modules-core during pod install, after Spark fingerprints the package, so the first spark dev after every build asked for a rebuild. Apply the same edit before collecting native packages.
Expo 55 binds --localhost to "localhost", which Node resolves to ::1, while desktop runtimes and Spark's Metro clients connect to 127.0.0.1. Prefer IPv4 in the Expo process so the app can load its bundle.
MMKV builds on macOS with Spark's Nitro patches, but its default store was ~/Documents/mmkv, the user's TCC-protected Documents folder shared by every app. A patch moves it to Application Support under SparkNamespace(), like Spark's other app data. Optional pins in patches/dependencies.json apply a patch when the app itself installs the pinned version; other versions stay unpatched. Adds a Kitchen Sink MMKV screen and a driver flow covering typed values, listeners, persistence across launches and clearState.
Unistyles 3.6.0 ships only a UIKit native runtime. Its optional patch adds a macOS podspec platform and an AppKit runtime: the main window's size and backing scale, appearance changes for adaptive themes, and RTL. Insets, bars and the keyboard report zero on desktop. Adds a Kitchen Sink Unistyles screen, with the Babel plugin scoped to it, and a driver flow that flips the app appearance and sees the theme follow.
Quick Crypto already targets macOS and its OpenSSL has a macOS slice, but fastpbkdf2 includes <endian.h>, which macOS lacks; the patch uses <machine/endian.h> as Mac Catalyst does. Its peer Quick Base64 declares only iOS, so macOS skipped linking it and the first import failed; its patch adds the macOS platform. Adds a Kitchen Sink security screen and a driver flow checking hashes, HMAC, PBKDF2, AES-GCM, random values and WebCrypto digest vectors.
Nitro Fetch's URLSession client compiles for AppKit, but its podspec declares only iOS and tvOS. The patch adds macOS, runs launch prefetches on NSApplicationDidFinishLaunching, and gives its URLCache an absolute directory under the app's namespaced Caches folder: the relative diskPath put Cache.db in the process's working directory on macOS. Adds a Kitchen Sink networking screen that asserts the native client is present (the JS otherwise falls back to global fetch) and a driver flow.
Blur is UIKit only, and without ios.componentProvider in its codegen config RN 0.83 never registers its Fabric components. The patch declares both components and adds AppKit BlurView and VibrancyView on NSVisualEffectView materials, with light/dark styles mapped to appearances and the reduced-transparency fallback. blurAmount has no AppKit equivalent. Adds a Kitchen Sink blur screen and a driver flow checking registration, layout and vibrancy children.
Worklets Core's native code is platform-neutral, but its podspec declares only iOS; the patch adds macOS. The Kitchen Sink adds the worklet Babel plugin and the three Babel plugins it requires, which Expo SDK 55 apps no longer carry. Adds a Kitchen Sink screen and a driver flow running a worklet context, a shared value and a call back to the main runtime.
CocoaPods skips a pod whose podspec lists platforms without macOS, but
codegen still registered the library's Fabric components and TurboModules.
The registry then mapped them to classes that were never built, and the app
crashed at launch inserting nil into the component dictionary
(react-native-fast-image). Spark's macOS native selection now excludes
those codegen libraries from the macOS Podfile only; iOS and Android builds
reading the shared react-native.config.js and Expo autolinking settings
keep them.
The podspec check reads the platforms assignment itself, singular
s.platform or plural s.platforms, in either :osx => or osx: syntax, so
react-native-gesture-handler (platforms { ios: ..., osx: ... }) stays in.
FastImage's native view is UIKit only. Its optional patch adds a macOS entry that renders FastImage and FastImageBackground with React Native's Image: sources, objectFit, tint, placeholders while loading, load events, reload and preload. Cache APIs resolve not ok. onLoad reports the size from Image.getSize, since React Native macOS scales a 1x image's load size by the screen's. Adds a Kitchen Sink screen and an offline driver flow.
The profiler's Hermes module has no UIKit code, but its podspec lists only iOS. The patch adds macOS and writes traces to the app's namespaced Caches folder: on a Mac, the root of ~/Library/Caches is shared by every app. Adds a Kitchen Sink devtools screen and a driver flow that profiles busy JavaScript and reads the trace back.
prepareNativePatches returned an optional pin it declined to patch (another version installed), so non-desktop autolinking still forced that copy. Record a package only once its version matches the pin. Also call the MMKV check's file a file.
ATTrackingManager exists on macOS 11, but react-native-tracking-transparency lists only iOS, checks UIApplication's state before prompting, and its JavaScript returns unavailable off iOS. The patch adds macOS with AppKit's activation check and lets macOS reach the native module. Adds a Kitchen Sink security screen and a driver flow reading the status.
Skia 2.14 supports macOS upstream and needs no Spark patch. Adds a Kitchen Sink screen and a driver flow that read back an on-screen Canvas and an offscreen raster surface by pixel. The Worklets Core Babel plugin, applied app-wide, rewrote Skia's worklets and read its optional Reanimated proxy at load, so the app never rendered. The Kitchen Sink now applies that plugin only to its Worklets Core demo.
Writing the patch receipt reformats a dependency's package.json as two-space JSON, so a package.json hunk that matched a tab-indented upstream file failed on every later preparation. Normalize package.json before applying hunks, so patches target one form.
Blurhash is UIKit only and its codegen config names no component provider, so RN 0.83 never registered its view. The patch declares the provider and adds AppKit support: an NSView showing the decoded image as layer contents, NSImage decoding and encoding, and resize modes as contents gravity. Adds a Kitchen Sink screen and a driver flow that decode a Blurhash in the native view and encode a local image.
Its iOS-only native module is absent on macOS, and the JavaScript read a property of it at import, so importing the library threw. The patch reads it optionally, so desktop reports a 0 radius. Adds a Kitchen Sink screen and a driver flow.
Nitro SQLite supports macOS upstream and links beside Spark's OP-SQLite, since its SQLite symbols are prefixed. Adds a Kitchen Sink storage screen and a driver flow covering parameterized, batched and async queries.
OSLogStore's current-process scope exists on macOS 12, but the library's podspec lists only iOS and only its iOS bindings call native. The patch adds macOS 12 to the podspec and a macOS entry using the iOS bindings. Adds a Kitchen Sink devtools screen and a driver flow that read back a console marker from the unified log.
Nitro ZXing is C++ on top of VisionCamera 5 and Nitro Image, which Spark already builds for macOS; its podspec only lacked the platform. Adds a Kitchen Sink media screen and a driver flow that scan a QR code from a still image.
react-native-graph 1.4.0 builds its line paths with Skia 1's mutable SkPath methods, which Skia 2 removed, so its graphs threw on render. The patch builds them with Skia.PathBuilder instead. Kitchen Sink renders a static and an animated graph with a gradient fill, with a driver flow.
Keep-awake always releases its tag, polls pmset instead of sleeping, and tolerates the assertion development builds already hold. The drawer counts onClose only after it opens, since it reports a close on mount. The bottom sheet retries its snap until the sheet has measured itself. The pressable demo gets the GestureHandlerRootView its native button requires.
The catalog golden still showed 6 populated areas from before the library screens were added; it now matches the current catalog's counts and screens.
A blurhash, thumbhash, SF Symbol or ImageRef source rendered nothing and fired no load events, so a screen waiting on onLoadEnd or onError waited forever. Such a source now reports onLoadStart, onError and onLoadEnd once. Sources also keep their width, height and scale, so a require()d @2x asset draws at its point size rather than at 1x.
The macOS stand-in rebuilt object sources from uri and headers alone, so a source's scale was lost and a @2x image drew at 1x, and a placeholder given as { uri, headers } fell through to defaultSource and never showed. Sources now keep width, height and scale, and uri placeholders load.
The date was applied before minimumDate and maximumDate, and NSDatePicker clamps its date whenever the limits move, so one update changing the value and both limits left the picker on a clamped date that JavaScript never heard about. The limits now apply first, and the date is set after any change to either.
…sing Without NSLocationWhenInUseUsageDescription, expo-location calls RCTFatal, which Debug builds swallow and Release builds throw, so a Release app terminated on its first permission check. On macOS it now logs an error and reports denied, as Debug did. The docs named NSLocationUsageDescription, which CoreLocation and expo-location never read; they now name the WhenInUse key.
The animated LineGraph wraps itself in a GestureDetector, which throws without a GestureHandlerRootView in development builds, so the screen showed the error in a dev Kitchen Sink. The e2e driver bundles with __DEV__ off, which hid it.
Fourteen patched podspecs declared macOS 11 or 12, below Spark's macOS 14 floor. Masked View in particular uses macOS 14's CADisplayLink and only built because the Podfile raised every pod's deployment target. They now declare 14.0.
Driver windows are never shown, so an external CGWindow lookup cannot find one for a real click. Add an opt-in --pointer backend using the existing testID lookup and an external CGEventPostToPid helper. Encode the events in the window-owning process so AppKit preserves the window-local hit point. Show the window without presentation animation and let the gesture demo accept its first background click on macOS. Missing input-posting permission fails immediately without a prompt. **Test plan** Focused native protocol, pointer backend and existing driver backend checks passed. Scoped strict TypeScript checking passed. ✅ Debug arm64 Kitchen Sink build via heavy, -jobs 6, no signing. ✅ Real native mouse-view probe observed one down and one up callback. ✅ Gesture-handler and drawer-layout flows via --backend driver --pointer: GestureDetector's successful tap callback and drawer open/close were observed, including a screenshot showing tap. The console remained locked.
An unknown window or a non-finite point now fails at once with E_NO_WINDOW or E_INVALID_ARGUMENT, as capture does, instead of waiting and reporting E_NOT_SETTLED.
…ettles tapOn passed no timeout, so the driver's fixed 5 s settle wait applied and E_NOT_SETTLED failed the step. It now waits up to the step's timeout in slices under the driver's reply limit, re-resolving the target between them, and taps once the timeout is spent, as it did before the settle wait. pointerEvents with timeoutMs 0 skips the wait.
…ation Main now requires every active pin to resolve to a prepared package. An optional pin that finds another installed version stays unpatched on purpose, so it no longer fails that check.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
…ws outside the gate The 50 library checks this branch adds pass on the driver backend, so the release gate now blocks on them. The gesture-handler and pressable-pointer flows need --pointer, which the gate does not accept, so they move to e2e/pointer/flows/ and run with bun run gate:pointer before a release.
@react-native-community/blur and the other native libraries this branch adds have no Windows implementation yet, so the Windows build excludes them like the rich text libraries. Windows support comes on a separate branch.
The sidebar catalog counts changed as library screens were added, the title bar tint changed slightly, and in Arabic the Dock screen title is now translated and the sidebar uses the Latin digits the shell pins.
Member
Author
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.
This makes 54 libraries that the Kitchen Sink adds for macOS build and work there. 35 have version-pinned Spark patches, plus Quick Base64 as a support library; the rest work without a patch, and that is now verified. Each library gets a row in
docs/external-libraries.md, a Kitchen Sink screen and a macOS e2e flow. The PR also adds opt-in real mouse input to the e2e driver. Windows support is in a separate branch.Behavior changes
~/Documents/mmkv· now:~/Library/Application Support/<namespace>/mmkv, with no migration · when: macOS, react-native-mmkv 4.3.2.docs/external-libraries.md.--pointeronly: the target window is shown and the app activated, then restored at flow end or disconnect. Requires an Accessibility grant. The release gate rejects--pointer, and default runs are unchanged.Changes
patches/dependencies.jsonaddsoptionalpins. A pin on another installed version stays unpatched and also passes chore: upgrade to Expo SDK 55 and React Native 0.83 #529's "every active pin was prepared" check. Patching handles tab-indented manifests and exports maps that hidepackage.json.--source-dir __dir__touse_native_modules!, and patched pods declare macOS 14.bun run e2e:run <flow> --backend driver --pointersends real Quartz mouse events fortapOn. Each tap waits for mounted UI to settle within the step timeout. Seedocs/test-driver.md#real-pointer-input-macos.gesture-handlerandpressable-pointerneed real clicks (--pointer), so they live ine2e/pointer/outside the gate and run withbun run gate:pointerin the pre-release checklist.windows.autolinking.excludelist until the Windows work lands separately.Verification
Run on
a2531141:bun run typecheck: passes.e2e-coverage, release-gate dialog flows). The other,kitchen-sink's Windows rich-text test, comes from a stale localnode_modulespatch; a fresh install has not been tried.bun run e2e:validateandbun run e2e:lint: 0 problems (79 flows).bun.lockagrees with every workspace manifest (static check).macOS, on
a2531141plus the gate commits (99c74c64):e2e:run e2e/flows --backend driver: 72 pass, 23 fail, 4 skip; every failure uses a step the driver backend does not support yet, or was a stale dock golden (now updated, 4/4 pass).bun run gate:pointer(gesture-handler, pressable-pointer with--pointer): 2/2 pass.Not run: Windows, Intel macOS, the full test suite locally (CI runs it).