Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1e45261
Add Bazel foundation with a rules_js Yarn Berry fork (draft)
Saadnajmi Jul 6, 2026
bebb914
Bazel: first-party package linking + rn-tester macOS bundle/app scaff…
Saadnajmi Jul 6, 2026
97e9501
docs: correct Hermes/XCFramework blocker diagnosis (Xcode/CMake, not …
Saadnajmi Jul 6, 2026
5fe1590
Fix Hermes host build on Xcode 26 (force macOS target for hermesc)
Saadnajmi Jul 6, 2026
6248c78
docs: record the Xcode-26 Hermes host-build fix and status
Saadnajmi Jul 6, 2026
3bbbcff
Allow building a subset of Hermes Apple platforms (HERMES_APPLE_PLATF…
Saadnajmi Jul 6, 2026
85222de
docs: react-native-macos native prebuild now builds on Xcode 26
Saadnajmi Jul 6, 2026
7943e12
Bazel: repo rule to import the prebuilt XCFrameworks (builds on Xcode…
Saadnajmi Jul 6, 2026
97d231d
Berry converter fix (resolutions + trailing space) and rn-tester JS b…
Saadnajmi Jul 7, 2026
80fed2a
docs: precise rn-tester JS bundle status (converter fix, first-party …
Saadnajmi Jul 7, 2026
e9819ba
Bazel: green rn-tester JS bundle (Metro) + codegen (AppSpecs + provid…
Saadnajmi Jul 7, 2026
877a0ad
Bazel: build RNTester.app on Xcode 26 (native host + XCFramework link)
Saadnajmi Jul 7, 2026
1221a93
Bazel: build the FULL rn-tester host (real AppDelegate + codegen + na…
Saadnajmi Jul 7, 2026
dd2733e
Bazel: complete rn-tester host — enable the Fabric NativeComponentExa…
Saadnajmi Jul 7, 2026
0104b7a
Bazel: generate the :pkg BUILD files instead of hand-writing them
Saadnajmi Jul 8, 2026
73b75ac
Bazel: import React/ReactNativeDependencies as dynamic frameworks (fi…
Saadnajmi Jul 8, 2026
290ed74
docs: honest scope/limitations — Bazel for the native slice, not the …
Saadnajmi Jul 8, 2026
f554d36
Merge remote-tracking branch 'upstream/main' into saadnajmi/bazel-sup…
Saadnajmi Jul 9, 2026
c5305dd
build(bazel): repair PR checks after pnpm-mode merge
Saadnajmi Jul 9, 2026
04a4a59
build(bazel): add canonical RNTester macOS app target
Saadnajmi Jul 9, 2026
76396ef
build(bazel): generate React native graph from Package.swift
Saadnajmi Jul 9, 2026
67e3ca3
build(bazel): compile React AppDelegate graph from source
Saadnajmi Jul 9, 2026
c7eedc7
build(bazel): link complete RNTester against source React
Saadnajmi Jul 10, 2026
b7cdf7b
build(bazel): make source RNTester a one-command build
Saadnajmi Jul 10, 2026
cb64261
ci(bazel): verify clean-checkout RNTester source build
Saadnajmi Jul 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .bazelignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
node_modules
.git
.yarn
packages/react-native/.build
packages/rn-tester/Pods
packages/helloworld
private/helloworld
packages/assets/node_modules
packages/babel-plugin-codegen/node_modules
packages/community-cli-plugin/node_modules
packages/core-cli-utils/node_modules
packages/debugger-frontend/node_modules
packages/debugger-shell/node_modules
packages/dev-middleware/node_modules
packages/eslint-config-react-native/node_modules
packages/eslint-plugin-react-native/node_modules
packages/eslint-plugin-specs/node_modules
packages/gradle-plugin/node_modules
packages/metro-config/node_modules
packages/new-app-screen/node_modules
packages/normalize-color/node_modules
packages/polyfills/node_modules
packages/react-native-babel-preset/node_modules
packages/react-native-babel-transformer/node_modules
packages/react-native-codegen/node_modules
packages/react-native-compatibility-check/node_modules
packages/react-native-macos-init/node_modules
packages/react-native-popup-menu-android/node_modules
packages/react-native-test-library/node_modules
packages/react-native/node_modules
packages/rn-tester/node_modules
packages/typescript-config/node_modules
packages/virtualized-lists/node_modules
private/cxx-public-api/node_modules
private/eslint-plugin-monorepo/node_modules
private/monorepo-tests/node_modules
private/react-native-bots/node_modules
private/react-native-codegen-typescript-test/node_modules
private/react-native-fantom/node_modules
tools/bazel/berry/example/node_modules
24 changes: 24 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# react-native-macos Bazel configuration (draft / experimental)
# See docs/bazel.md for the design and current status.

# Our pnpm-lock.yaml is generated on the fly from the Berry yarn.lock by the
# rules_js fork, so we don't pin the module resolution in MODULE.bazel.lock.
common --lockfile_mode=off

# Local disk cache (also the base for the GHA actions/cache remote cache).
build --disk_cache=~/.cache/bazel-rnm-disk

# Nicer output.
build --show_result=1

# Apple: build macOS slices. Scoped in practice to the app target's transitions.
build:macos --apple_platform_type=macos

# React Native's new architecture C++ (Fabric/TurboModules) requires C++20.
# Scope the flag to C++/Obj-C++: objc_library also compiles plain `.m` files, and
# clang rejects `-std=c++20` when it is passed to Objective-C.
build --per_file_copt=.*\\.(cc|cpp|cxx|mm)$@-std=c++20

# CI convenience config.
build:ci --color=yes
build:ci --show_timestamps
1 change: 1 addition & 0 deletions .bazelversion
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
8.7.0
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ packages/*/types_generated
packages/debugger-frontend/dist/**/*
packages/react-native-codegen/lib
**/Pods/*
bazel-*
**/*.macos.js
**/*.windows.js
103 changes: 103 additions & 0 deletions .github/workflows/bazel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Experimental Bazel CI for react-native-macos.
#
# Today this verifies the rules_js *Berry fork* end to end: it builds/tests a target
# whose node_modules are materialized from a Yarn Berry `yarn.lock` (translated to a
# pnpm-lock by tools/bazel/berry, with no committed second lockfile). The macOS app
# slice (rules_apple) is added incrementally — see docs/bazel.md.
name: Bazel (experimental)

on:
pull_request:
paths:
- "MODULE.bazel"
- ".bazelrc"
- ".bazelversion"
- ".bazelignore"
- ".yarnrc.yml"
- "BUILD.bazel"
- "package.json"
- "yarn.lock"
- "tools/bazel/**"
- "packages/react-native/**"
- "packages/rn-tester/**"
- ".github/workflows/bazel.yml"
workflow_dispatch: {}

permissions:
contents: read

concurrency:
group: bazel-${{ github.ref }}
cancel-in-progress: true

jobs:
berry-fork:
name: rules_js Berry fork (JS)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
# Disk cache doubles as a simple GHA-backed Bazel remote cache. For a real
# remote cache at scale, point --remote_cache at BuildBuddy or a self-hosted
# bazel-remote (see docs/bazel.md).
- name: Cache Bazel
uses: actions/cache@v4
with:
path: ~/.cache/bazel-rnm-disk
key: bazel-berry-${{ runner.os }}-${{ hashFiles('MODULE.bazel', 'yarn.lock', 'tools/bazel/**') }}
restore-keys: |
bazel-berry-${{ runner.os }}-
- name: Verify generated workspace BUILD files
run: node tools/bazel/js/gen_package_builds.mjs --all --check
- name: Test the Berry -> rules_js pipeline end to end
run: |
npx --yes @bazel/bazelisk test //tools/bazel/berry/example:verify \
--config=ci --test_output=errors

rntester-macos:
name: RNTester macOS from source
runs-on: macos-26
timeout-minutes: 90
steps:
- uses: actions/checkout@v4
with:
filter: blob:none

- uses: actions/setup-node@v4
with:
node-version: "22"

- name: Cache Bazel
uses: actions/cache@v4
with:
path: ~/.cache/bazel-rnm-disk
key: bazel-rntester-macos-${{ hashFiles('MODULE.bazel', 'yarn.lock', 'packages/react-native/Package.swift', 'tools/bazel/**') }}
restore-keys: |
bazel-rntester-macos-

- name: Build RNTester from a clean checkout
run: |
test ! -e packages/react-native/.build/output/xcframeworks/Debug/React.xcframework
test ! -e packages/react-native/third-party/ReactNativeDependencies.xcframework
test ! -e packages/react-native/.build/artifacts/hermes/destroot
npx --yes @bazel/bazelisk build \
//packages/rn-tester/RNTester-macOS:app \
--config=ci

- name: Verify source-linked app
run: |
mkdir -p "$RUNNER_TEMP/rntester"
unzip -q bazel-bin/packages/rn-tester/RNTesterMacBazelFull.zip \
-d "$RUNNER_TEMP/rntester"
app="$RUNNER_TEMP/rntester/RNTesterMacBazelFull.app"
test -f "$app/Contents/Resources/main.jsbundle"
test -f "$app/Contents/Resources/Assets.car"
test -f "$app/Contents/Resources/PrivacyInfo.xcprivacy"
test "$(find "$app/Contents/Resources" -name 'bottom-nav-*.png' | wc -l | tr -d ' ')" = 6
test ! -e "$app/Contents/Frameworks/React.framework"
test -e "$app/Contents/Frameworks/hermes.framework"
test -e "$app/Contents/Frameworks/ReactNativeDependencies.framework"
! otool -L "$app/Contents/MacOS/RNTesterMacBazelFull" | grep -q React.framework
codesign --verify --deep --strict "$app"
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,17 @@ fix_*.patch
.cursor/rules/nx-rules.mdc
.github/instructions/nx.instructions.md
# macOS]

# Generated by the rules_js Berry fork from yarn.lock (not committed)
/pnpm-lock.yaml

# Berry fork example: generated lock + transient node_modules
/tools/bazel/berry/example/pnpm-lock.yaml
tools/bazel/berry/example/node_modules
tools/bazel/berry/example/.yarn

# Bazel resolution lock (module deps pinned via BCR; pnpm-lock is generated)
/MODULE.bazel.lock

# Bazel convenience symlinks
/bazel-*
19 changes: 19 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
load("@npm//:defs.bzl", "npm_link_all_packages")

bool_flag(
name = "rn_from_source",
build_setting_default = True,
visibility = ["//visibility:public"],
)

config_setting(
name = "rn_from_source_enabled",
flag_values = {":rn_from_source": "true"},
visibility = ["//visibility:public"],
)

# Links the pnpm-resolved (from the Berry yarn.lock via our fork) node_modules
# into the Bazel build graph. Downstream JS targets depend on
# //:node_modules/<pkg>.
npm_link_all_packages(name = "node_modules")
72 changes: 72 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
module(
name = "react_native_macos_monorepo",
version = "0.0.0",
compatibility_level = 1,
)

###############################################################################
# JavaScript / Node toolchain (aspect-build rules_js)
###############################################################################
bazel_dep(name = "aspect_rules_js", version = "3.2.2")
bazel_dep(name = "aspect_rules_ts", version = "3.8.11")
bazel_dep(name = "aspect_bazel_lib", version = "2.22.5")
bazel_dep(name = "bazel_skylib", version = "1.9.0")
bazel_dep(name = "rules_nodejs", version = "6.7.5")

# Local fork of rules_js: teach `npm_translate_lock` to read the Yarn Berry (v2+)
# `yarn.lock` directly, so it stays the single source of truth (no committed
# second lockfile). The patch swaps the `pnpm import` step for our bundled
# Berry -> pnpm-lock converter (tools/bazel/berry/berry_to_pnpm_lock.mjs) when
# the input yarn.lock is a Berry lockfile. See docs/bazel.md.
single_version_override(
module_name = "aspect_rules_js",
patch_strip = 1,
patches = ["//tools/bazel/patches:aspect_rules_js_berry.patch"],
version = "3.2.2",
)

node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node", dev_dependency = True)
node.toolchain(node_version = "22.14.0")

npm = use_extension("@aspect_rules_js//npm:extensions.bzl", "npm", dev_dependency = True)
npm.npm_translate_lock(
name = "npm",
# The Berry yarn.lock is the single source of truth. Our rules_js patch
# converts it to an (uncommitted, gitignored) pnpm-lock.yaml at rule time
# before parsing, so update_pnpm_lock (pnpm import) is left off.
yarn_lock = "//:yarn.lock",
pnpm_lock = "//:pnpm-lock.yaml",
update_pnpm_lock = False,
verify_node_modules_ignored = "//:.bazelignore",
quiet = False,
)
use_repo(npm, "npm")

# Self-contained green proof of the Berry fork (see tools/bazel/berry/example).
npm.npm_translate_lock(
name = "npm_berry_example",
yarn_lock = "//tools/bazel/berry/example:yarn.lock",
pnpm_lock = "//tools/bazel/berry/example:pnpm-lock.yaml",
update_pnpm_lock = False,
verify_node_modules_ignored = "//:.bazelignore",
quiet = False,
)
use_repo(npm, "npm_berry_example")

###############################################################################
# Apple toolchain (rules_apple / rules_swift) — used by the macOS app slice
###############################################################################
bazel_dep(name = "rules_apple", version = "4.5.3")
bazel_dep(name = "rules_swift", version = "3.6.1")
bazel_dep(name = "apple_support", version = "2.7.0")
bazel_dep(name = "rules_cc", version = "0.2.17")
bazel_dep(name = "platforms", version = "1.1.0")

# Expose the SPM-prebuilt React Native XCFrameworks (built by scripts/ios-prebuild.js)
# to Bazel so the macOS app slice can link them.
rn_prebuilt = use_extension("//tools/bazel/apple:prebuilt_xcframeworks.bzl", "rn_prebuilt_xcframeworks_extension")
use_repo(rn_prebuilt, "rn_prebuilt_xcframeworks")

# Canonical header aliases generated directly from the React Native source tree.
rn_source_headers = use_extension("//tools/bazel/apple:source_headers.bzl", "rn_source_headers_extension")
use_repo(rn_source_headers, "rn_source_headers")
Loading
Loading