Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ce8a1e3
fix(prebuild): derive header slices from binary platforms
Saadnajmi Sep 16, 2026
97e2124
fix(prebuild): bound header collision exceptions
Saadnajmi Sep 16, 2026
03f7186
fix(ci): publish complete Apple prebuild header artifacts
Saadnajmi Sep 16, 2026
ffa7618
Saadnajmi Sep 21, 2026
fb5729d
chore: normalize integrated 0.87 dependency selectors
Saadnajmi Sep 16, 2026
beba15d
chore(0.87): align the stable release graph
Saadnajmi Sep 17, 2026
b14fa71
fix(0.87): reconcile shared feature and release integration
Saadnajmi Sep 17, 2026
e0a9e23
fix(0.87): apply approved validation fixes and CocoaPods artifacts
Saadnajmi Sep 17, 2026
597c120
chore(0.87): prepare stable 0.87.0 release
Saadnajmi Sep 21, 2026
215b34c
chore(0.87): refresh approved generated API snapshot
Saadnajmi Sep 21, 2026
1d19278
fix(ci): apply reviewed release validation repairs
Saadnajmi Sep 22, 2026
5b2cedc
fix(ci): align lock metadata with the public registry
Saadnajmi Sep 22, 2026
a483493
fix(ci): refresh validated snapshots and platform fixtures
Saadnajmi Sep 22, 2026
b62db85
fix(ci): align Flow annotations and Jest configuration
Saadnajmi Sep 23, 2026
6c65ca2
fix(ci): align Flow annotations and Jest configuration
Saadnajmi Sep 23, 2026
9b7d594
fix(ci): align Flow annotations and Jest configuration
Saadnajmi Sep 23, 2026
8ce3c9a
fix(ci): align Flow annotations and Jest configuration
Saadnajmi Sep 23, 2026
8750ba4
fix(ci): align Flow annotations and Jest configuration
Saadnajmi Sep 23, 2026
426c53e
fix(ci): align Flow annotations and Jest configuration
Saadnajmi Sep 23, 2026
1a9971c
fix(ci): align Flow annotations and Jest configuration
Saadnajmi Sep 23, 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
2 changes: 1 addition & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
"access": "public",
"baseBranch": "origin/main",
"baseBranch": "origin/0.87-stable",
"bumpVersionsWithWorkspaceProtocolOnly": true,
"changelog": "@changesets/cli/changelog",
"commit": false,
Expand Down
41 changes: 0 additions & 41 deletions .changeset/pre.json

This file was deleted.

6 changes: 0 additions & 6 deletions .changeset/stable-sync.md

This file was deleted.

28 changes: 8 additions & 20 deletions .github/scripts/__tests__/publishing-contract.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -199,13 +199,10 @@ test('repository Changesets policy follows the actual public and private workspa
assert.ok(corePackage && !corePackage.private, 'Missing public core workspace');
assert.ok(listsPackage, 'Missing lists workspace');
const main = corePackage.version === '1000.0.0';
if (!main) parseVersion(corePackage.version);
const prereleaseReview = !main && semver.prerelease(corePackage.version) !== null
&& releasePolicy.baseBranch === 'origin/main';
assert.deepEqual(releasePolicy.ignore, main ? mainPolicy.ignore : []);
assert.equal(releasePolicy.baseBranch, main || prereleaseReview ? 'origin/main'
assert.equal(releasePolicy.baseBranch, main ? 'origin/main'
: `origin/${semver.major(corePackage.version)}.${semver.minor(corePackage.version)}-stable`);
assert.equal(Boolean(listsPackage.private), main, 'Lists must be public on release graphs and private in development');
assert.equal(Boolean(listsPackage.private), main, 'Lists must be public on stable and private on main');
assert.equal(listsPackage.version, corePackage.version);
const publicPackages = [corePackage, listsPackage];
// Derive expectations from manifests, never from the policy or release-plan output.
Expand Down Expand Up @@ -305,9 +302,8 @@ test('repository Changesets policy couples public stable packages without regist
});

test('real Yarn constraints preserve private upstream versions, align public versions, and preserve workspace fork edges', t => {
for (const [version, upstreamVersion] of [['1000.0.0', '0.83.1'], ['0.83.2', '0.83.1'], ['0.87.0-rc.0', '0.87.1']]) {
const main = version === '1000.0.0';
const workspaces = graph(version);
for (const main of [true, false]) {
const workspaces = graph(main ? '1000.0.0' : '0.83.2');
workspaces[1].private = main;
workspaces[1].version = '0.82.0';
workspaces[2].version = '0.82.7';
Expand All @@ -318,7 +314,7 @@ test('real Yarn constraints preserve private upstream versions, align public ver
if (index !== 1) workspaces[index][field][lists] = '*';
}
}
if (!main) workspaces[0].peerDependencies['react-native'] = upstreamVersion;
if (!main) workspaces[0].peerDependencies['react-native'] = '0.83.1';
const {root} = releaseFixture(t, {workspaces});
writeFileSync(join(root, 'yarn.lock'), '');
writeFileSync(join(root, 'yarn.config.cjs'), `module.exports = require(${JSON.stringify(join(repositoryRoot, 'yarn.config.cjs'))});\n`);
Expand All @@ -331,26 +327,18 @@ test('real Yarn constraints preserve private upstream versions, align public ver
yarn(['constraints']);
const actual = workspaces.map((_, index) => JSON.parse(readFileSync(join(root, `packages/p${index}/package.json`), 'utf8')));
assert.equal(actual[0].version, workspaces[0].version);
assert.equal(actual[1].version, version);
assert.equal(actual[1].version, main ? '1000.0.0' : '0.83.2');
assert.equal(actual[2].private, true);
assert.equal(actual[2].version, '0.82.7');
assert.equal(actual[3].version, '1000.0.0');
assert.equal(actual[5].private, true);
assert.equal(actual[5].version, main ? '0.83.0' : upstreamVersion);
assert.equal(actual[5].version, main ? '0.83.0' : '0.83.1');
for (const index of [0, 1, 3]) {
for (const field of ['dependencies', 'devDependencies', 'peerDependencies']) {
assert.equal(actual[index][field]['@react-native/codegen'], main || index === 3 ? 'workspace:*' : upstreamVersion);
assert.equal(actual[index][field]['@react-native/codegen'], main || index === 3 ? 'workspace:*' : '0.83.1');
if (index !== 1) assert.equal(actual[index][field][lists], 'workspace:*');
}
}
if (!main) {
const archive = join(root, 'core.tgz');
yarn(['workspace', core, 'pack', '--out', archive]);
const packed = JSON.parse(execFileSync('tar', ['-xOf', archive, 'package/package.json'], {encoding: 'utf8'}));
assert.equal(packed.version, version);
assert.equal(packed.dependencies[lists], version);
assert.equal(packed.dependencies['@react-native/codegen'], upstreamVersion);
}
}
});

Expand Down
4 changes: 1 addition & 3 deletions .github/scripts/publishing-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ Pending Changesets come from the declared `@changesets/get-release-plan` API, wi

Stable branches must already have their initial release version and React Native peer configured. The publication script does not turn `1000.0.0` into a release. All public release packages must match the core version and branch. Private runtime workspace links are invalid; explicit registry references to the separately published upstream `@react-native/*` packages are valid. Development-only private workspace links are allowed.

Development graphs on main and merge-stage branches keep `1000.0.0`, including private virtualized-lists and upstream workspace links. Their Changesets config defers `react-native-macos` with `ignore`; `@react-native/tester` is also listed for compatibility with older Changesets' dependent validation. This preserves pending core changesets until stable preparation, without changing package privacy or enabling private versions or tags. The independent init package remains versionable. Stable preparation must clear `ignore`, set the stable `baseBranch`, make virtualized-lists public, and configure the release versions and upstream registry dependencies before versioning. The repository-graph test checks these branch-specific source settings.

Public prerelease review heads can retain `origin/main` while RNM and virtualized-lists have the same prerelease version and neither is private or ignored. They are distinct from the placeholder development graph; stable publication and version-wrapper branch guards still apply. The fork-local lists dependency uses `workspace:*` in source so Changesets tracks it, and Yarn packs it as the exact public version, including its prerelease suffix. Upstream registry dependencies keep their exact React Native peer version.
Main and merge-stage branches keep the `1000.0.0` development graph, including private virtualized-lists and upstream workspace links. Their Changesets config defers `react-native-macos` with `ignore`; `@react-native/tester` is also listed for compatibility with older Changesets' dependent validation. This preserves pending core changesets until stable preparation, without changing package privacy or enabling private versions or tags. The independent init package remains versionable. Stable preparation must clear `ignore`, set the stable `baseBranch`, make virtualized-lists public, and configure the release versions and upstream registry dependencies before versioning. The repository-graph test checks these branch-specific source settings.

## Publication and tags

Expand Down
16 changes: 10 additions & 6 deletions .github/workflows/prebuild-ios-core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
required: false
default: ''
use-hermes-prebuilt:
description: 'Whether to resolve Hermes from the latest-v1 npm dist-tag (true) or from the version pinned in version.properties (false)'
description: 'Compatibility input; Hermes always uses the exact version pinned in the fork SDK metadata'
type: boolean
required: false
default: false
Expand Down Expand Up @@ -226,13 +226,12 @@ jobs:
- name: Rename ReactNativeHeaders XCFramework tarball
if: steps.restore-ios-xcframework.outputs.cache-hit != 'true'
run: |
# The compose step already tars ReactNativeHeaders.xcframework standalone;
# published as its own Maven artifact (classifier reactnative-headers-*)
# so SwiftPM consumers can wire it as a separate binaryTarget. It also
# ships inside the ReactCore tarball for the CocoaPods pod.
cp packages/react-native/.build/output/xcframeworks/${{matrix.flavor}}/ReactNativeHeaders.xcframework.tar.gz \
packages/react-native/.build/output/xcframeworks/ReactNativeHeaders${{matrix.flavor}}.xcframework.tar.gz
- name: Upload ReactNativeHeaders XCFramework Artifact
uses: actions/upload-artifact@v6
with:
name: ReactNativeHeaders${{ matrix.flavor }}.xcframework.tar.gz
path: packages/react-native/.build/output/xcframeworks/ReactNativeHeaders${{matrix.flavor}}.xcframework.tar.gz
- name: Upload XCFramework Artifact
uses: actions/upload-artifact@v6
with:
Expand All @@ -243,6 +242,11 @@ jobs:
with:
name: ReactCore${{ matrix.flavor }}.framework.dSYM.tar.gz
path: packages/react-native/.build/output/xcframeworks/ReactCore${{matrix.flavor}}.framework.dSYM.tar.gz
- name: Upload ReactNativeHeaders XCFramework Artifact
uses: actions/upload-artifact@v6
with:
name: ReactNativeHeaders${{ matrix.flavor }}.xcframework.tar.gz
path: packages/react-native/.build/output/xcframeworks/ReactNativeHeaders${{matrix.flavor}}.xcframework.tar.gz
- name: Save cache if present
if: ${{ github.ref == 'refs/heads/main' }} # To avoid that the cache explode
uses: actions/cache/save@v5
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/prebuild-ios-dependencies.yml
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,6 @@ jobs:
run: |
tar -cz -f packages/react-native/third-party/ReactNativeDependenciesHeaders${{ matrix.flavor }}.xcframework.tar.gz \
packages/react-native/third-party/ReactNativeDependenciesHeaders.xcframework
- name: Upload Headers Sidecar XCFramework Artifact
uses: actions/upload-artifact@v6
with:
name: ReactNativeDependenciesHeaders${{ matrix.flavor }}.xcframework.tar.gz
path: packages/react-native/third-party/ReactNativeDependenciesHeaders${{ matrix.flavor }}.xcframework.tar.gz
- name: Show Symbol folder content
if: steps.restore-xcframework.outputs.cache-hit != 'true'
run: ls -lR packages/react-native/third-party/Symbols
Expand All @@ -190,6 +185,11 @@ jobs:
with:
name: ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz
path: packages/react-native/third-party/ReactNativeDependencies${{ matrix.flavor }}.xcframework.tar.gz
- name: Upload Headers Sidecar XCFramework Artifact
uses: actions/upload-artifact@v6
with:
name: ReactNativeDependenciesHeaders${{ matrix.flavor }}.xcframework.tar.gz
path: packages/react-native/third-party/ReactNativeDependenciesHeaders${{ matrix.flavor }}.xcframework.tar.gz
- name: Upload dSYM Artifact
uses: actions/upload-artifact@v6
with:
Expand Down
2 changes: 0 additions & 2 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ module.exports = {
'^react-native/setup-env$':
'<rootDir>/packages/react-native/src/setup-env.js',
// These mappers allow out-of-tree platforms tests to resolve RN imports
'^react-native/setup-env$':
'<rootDir>/packages/react-native/src/setup-env.js',
'^react-native/(.*)': '<rootDir>/packages/react-native/$1',
'^react-native$': '<rootDir>/packages/react-native/index.js',
},
Expand Down
8 changes: 8 additions & 0 deletions packages/react-native/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# react-native-macos

## 0.87.0
### Patch Changes

- 5ce1cb1: Sync to upstream React Native 0.87.0-rc.1.
- Updated dependencies [5ce1cb1]
- @react-native-macos/virtualized-lists@0.87.0
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default class ReactNativeVersion {
static major: number = 0;
static minor: number = 87;
static patch: number = 0;
static prerelease: string | null = 'rc.0';
static prerelease: string | null = null;

static getVersionString(): string {
return `${this.major}.${this.minor}.${this.patch}${this.prerelease != null ? `-${this.prerelease}` : ''}`;
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native/React/Base/RCTVersion.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
RCTVersionMajor: @(0),
RCTVersionMinor: @(87),
RCTVersionPatch: @(0),
RCTVersionPrerelease: @"rc.0",
RCTVersionPrerelease: [NSNull null],
};
});
return __rnVersion;
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native/ReactAndroid/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION_NAME=0.87.0-rc.0
VERSION_NAME=0.87.0
react.internal.publishingGroup=com.facebook.react
react.internal.hermesPublishingGroup=com.facebook.hermes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ public object ReactNativeVersion {
"major" to 0,
"minor" to 87,
"patch" to 0,
"prerelease" to "rc.0"
"prerelease" to null
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ struct ReactNativeVersionType {
int32_t Major = 0;
int32_t Minor = 87;
int32_t Patch = 0;
std::string_view Prerelease = "rc.0";
std::string_view Prerelease = "";
};

constexpr ReactNativeVersionType ReactNativeVersion;
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-macos",
"version": "0.87.0-rc.0",
"version": "0.87.0",
"description": "React Native for macOS",
"license": "MIT",
"repository": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def test_ci_without_pod_executable_uses_the_single_source_tag
assert_equal(METADATA.fetch('HERMES_VERSION_NAME'), spec.version)
assert_equal(checked_in_source, spec.source)
assert_empty(@calls)
assert_equal(". '#{File.join(ENGINE, '..', '..')}/sdks/hermes-engine/utils/create-dummy-hermes-xcframework.sh'", spec.prepare_command)
assert_equal(File.read(File.join(ENGINE, 'utils', 'create-dummy-hermes-xcframework.sh')), spec.prepare_command)
end

def test_explicit_override_order_precedes_the_default_source_policy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ exports[`execute test-app "ReactAppDependencyProvider/ReactAppDependencyProvider
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

version = \\"0.87.0-rc.0\\"
version = \\"0.87.0\\"
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in.
Expand Down Expand Up @@ -399,7 +399,7 @@ exports[`execute test-app "ReactCodegen/ReactCodegen.podspec" should match snaps
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

version = \\"0.87.0-rc.0\\"
version = \\"0.87.0\\"
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in.
Expand Down Expand Up @@ -590,7 +590,7 @@ exports[`execute test-app-legacy "ReactAppDependencyProvider/ReactAppDependencyP
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

version = \\"0.87.0-rc.0\\"
version = \\"0.87.0\\"
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in.
Expand Down Expand Up @@ -882,7 +882,7 @@ exports[`execute test-app-legacy "ReactCodegen/ReactCodegen.podspec" should matc
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

version = \\"0.87.0-rc.0\\"
version = \\"0.87.0\\"
source = { :git => 'https://github.com/facebook/react-native.git' }
if version == '1000.0.0'
# This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in.
Expand Down Expand Up @@ -992,4 +992,4 @@ SCRIPT

end
"
`;
`;
Loading
Loading