Skip to content

Commit e666b40

Browse files
authored
chore(0.78): add Yarn constraints to enforce dependency alignment (#2583)
## Summary: Backport #2582
1 parent 113b0ec commit e666b40

File tree

8 files changed

+160
-16
lines changed

8 files changed

+160
-16
lines changed

.github/workflows/microsoft-pr.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,20 @@ jobs:
6262
- name: Version and publish packages (dry run)
6363
run: |
6464
echo "Target branch: ${{ github.base_ref }}"
65-
yarn nx release --dry-run --verbose
65+
yarn nx release --dry-run --verbose
66+
yarn-constraints:
67+
name: "Check Yarn Constraints"
68+
permissions: {}
69+
runs-on: ubuntu-latest
70+
steps:
71+
- uses: actions/checkout@v4
72+
with:
73+
filter: blob:none
74+
fetch-depth: 0
75+
- uses: actions/setup-node@v4
76+
with:
77+
node-version: '22'
78+
- name: Install dependencies
79+
run: yarn
80+
- name: Check constraints
81+
run: yarn constraints

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"@tsconfig/node18": "1.0.1",
5858
"@types/react": "^19.0.0",
5959
"@typescript-eslint/parser": "^7.1.1",
60+
"@yarnpkg/types": "^4.0.1",
6061
"ansi-styles": "^4.2.1",
6162
"babel-plugin-minify-dead-code-elimination": "^0.5.2",
6263
"babel-plugin-syntax-hermes-parser": "0.25.1",

packages/nx-release-version/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"name": "@react-native-macos/nx-release-version",
33
"version": "0.0.1-dev",
4+
"private": true,
45
"description": "Nx Release Version Actions for React Native macOS",
56
"homepage": "https://github.com/microsoft/react-native-macos/tree/HEAD/packages/nx-release-version#readme",
67
"license": "MIT",

packages/react-native-test-library/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"name": "@react-native/oss-library-example",
3-
"version": "0.78.3",
3+
"version": "0.78.2",
44
"private": true,
5-
"description": "Package that includes native module exapmle, native component example, targets both the old and the new architecture. It should serve as an example of a real-world OSS library.",
5+
"description": "Package that includes native module example, native component example, targets both the old and the new architecture. It should serve as an example of a real-world OSS library.",
66
"license": "MIT",
77
"homepage": "https://github.com/facebook/react-native.git",
88
"repository": {
@@ -26,7 +26,7 @@
2626
],
2727
"devDependencies": {
2828
"@babel/core": "^7.25.2",
29-
"@react-native/babel-preset": "workspace:*",
29+
"@react-native/babel-preset": "0.78.2",
3030
"react-native-macos": "workspace:*"
3131
},
3232
"peerDependencies": {

packages/react-native/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@
111111
},
112112
"dependencies": {
113113
"@jest/create-cache-key-function": "^29.6.3",
114-
"@react-native-macos/virtualized-lists": "0.78.4",
114+
"@react-native-macos/virtualized-lists": "workspcace:*",
115115
"@react-native/assets-registry": "0.78.2",
116116
"@react-native/codegen": "0.78.2",
117117
"@react-native/community-cli-plugin": "0.78.2",

packages/rn-tester/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@react-native/tester",
3-
"version": "0.78.3",
3+
"version": "0.78.2",
44
"private": true,
55
"description": "React Native tester app.",
66
"license": "MIT",
@@ -27,8 +27,8 @@
2727
"e2e-test-ios": "./scripts/maestro-test-ios.sh"
2828
},
2929
"dependencies": {
30-
"@react-native/oss-library-example": "0.78.3",
31-
"@react-native/popup-menu-android": "workspace:*",
30+
"@react-native/oss-library-example": "workspace:*",
31+
"@react-native/popup-menu-android": "0.78.2",
3232
"flow-enums-runtime": "^0.0.6",
3333
"invariant": "^2.2.4",
3434
"nullthrows": "^1.1.1"

yarn.config.cjs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// @ts-check
2+
3+
/** @type {import('@yarnpkg/types')} */
4+
const {defineConfig} = require('@yarnpkg/types');
5+
6+
/**
7+
* @typedef {import('@yarnpkg/types').Yarn.Constraints.Context} Context
8+
* @typedef {import('@yarnpkg/types').Yarn.Constraints.Workspace} Workspace
9+
* @typedef {import('@yarnpkg/types').Yarn.Constraints.Dependency} Dependency
10+
*/
11+
12+
/**
13+
* Enforce that react-native-macos declares a peer dependency on react-native on release branches,
14+
* except on the main branch, where there is no published version of React Native to align to.
15+
* @param {Context} context
16+
*/
17+
function expectReactNativePeerDependency({Yarn}) {
18+
const rnmWorkspace = Yarn.workspace({ident: 'react-native-macos'});
19+
if (!rnmWorkspace) {
20+
// Report error on root workspace since react-native-macos doesn't exist
21+
Yarn.workspace().error('react-native-macos workspace must exist in the monorepo');
22+
return;
23+
}
24+
25+
// Check if react-native-macos version is 1000.0.0 - implying we are on the main branch
26+
const isMainBranch = rnmWorkspace.manifest.version === '1000.0.0';
27+
if (!isMainBranch) {
28+
const rnPeerDependency = rnmWorkspace.pkg.peerDependencies.get('react-native');
29+
if (!rnPeerDependency) {
30+
rnmWorkspace.error('react-native-macos must declare a peer dependency on react-native on release branches');
31+
}
32+
}
33+
}
34+
35+
/**
36+
* Enforce that all @react-native/ scoped packages use the same version
37+
* as the react-native peer dependency declared in react-native-macos.
38+
* On the main branch, enforce that we use workspace:* for @react-native/ packages.
39+
* @param {Context} context
40+
*/
41+
function enforceReactNativeVersionConsistency({Yarn}) {
42+
const rnmWorkspace = Yarn.workspace({ident: 'react-native-macos'});
43+
if (!rnmWorkspace) {
44+
// Report error on root workspace since react-native-macos doesn't exist
45+
Yarn.workspace().error('react-native-macos workspace must exist in the monorepo');
46+
return;
47+
}
48+
49+
// Check if react-native-macos version is 1000.0.0 - implying we are on the main branch
50+
const isMainBranch = rnmWorkspace.manifest.version === '1000.0.0';
51+
52+
let targetVersion;
53+
if (isMainBranch) {
54+
// On main branch, use workspace:* for @react-native/ packages
55+
targetVersion = 'workspace:*';
56+
} else {
57+
const rnPeerDependency = rnmWorkspace.pkg.peerDependencies.get('react-native');
58+
if (!rnPeerDependency) {
59+
rnmWorkspace.error('react-native-macos must declare a peer dependency on react-native on release branches');
60+
return;
61+
}
62+
targetVersion = rnPeerDependency;
63+
} // Enforce this version on all @react-native/ scoped packages across all workspaces
64+
for (const dependency of Yarn.dependencies()) {
65+
if (dependency.ident.startsWith('@react-native/')) {
66+
// Check if the target package is private (not published)
67+
const targetWorkspace = Yarn.workspace({ident: dependency.ident});
68+
const isPrivatePackage = targetWorkspace && targetWorkspace.manifest.private;
69+
70+
if (isPrivatePackage) {
71+
// Private packages should always use workspace:* since they're not published
72+
dependency.update('workspace:*');
73+
} else {
74+
dependency.update(targetVersion);
75+
}
76+
}
77+
}
78+
}
79+
80+
/**
81+
* Enforce that all @react-native-macos/ scoped packages use the same version
82+
* as react-native-macos, but only for non-private packages.
83+
* @param {Context} context
84+
*/
85+
function enforceReactNativeMacosVersionConsistency({Yarn}) {
86+
const rnmWorkspace = Yarn.workspace({ident: 'react-native-macos'});
87+
if (!rnmWorkspace) {
88+
// Report error on root workspace since react-native-macos doesn't exist
89+
Yarn.workspace().error('react-native-macos workspace must exist in the monorepo');
90+
return;
91+
}
92+
93+
const targetVersion = rnmWorkspace.manifest.version;
94+
if (!targetVersion) {
95+
rnmWorkspace.error('react-native-macos must have a version');
96+
return;
97+
}
98+
99+
// Enforce this version on all non-private @react-native-macos/ scoped packages
100+
for (const workspace of Yarn.workspaces()) {
101+
const isReactNativeMacosScoped = workspace.ident && workspace.ident.startsWith('@react-native-macos/');
102+
const isPrivate = workspace.manifest.private;
103+
104+
if (isReactNativeMacosScoped && !isPrivate) {
105+
workspace.set('version', targetVersion);
106+
}
107+
}
108+
}
109+
110+
module.exports = defineConfig({
111+
constraints: async ctx => {
112+
expectReactNativePeerDependency(ctx);
113+
enforceReactNativeVersionConsistency(ctx);
114+
enforceReactNativeMacosVersionConsistency(ctx);
115+
},
116+
});

yarn.lock

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2738,6 +2738,7 @@ __metadata:
27382738
"@tsconfig/node18": "npm:1.0.1"
27392739
"@types/react": "npm:^19.0.0"
27402740
"@typescript-eslint/parser": "npm:^7.1.1"
2741+
"@yarnpkg/types": "npm:^4.0.1"
27412742
ansi-styles: "npm:^4.2.1"
27422743
babel-plugin-minify-dead-code-elimination: "npm:^0.5.2"
27432744
babel-plugin-syntax-hermes-parser: "npm:0.25.1"
@@ -2802,7 +2803,7 @@ __metadata:
28022803
languageName: unknown
28032804
linkType: soft
28042805

2805-
"@react-native-macos/virtualized-lists@npm:0.78.4, @react-native-macos/virtualized-lists@workspace:packages/virtualized-lists":
2806+
"@react-native-macos/virtualized-lists@workspace:packages/virtualized-lists, @react-native-macos/virtualized-lists@workspcace:*":
28062807
version: 0.0.0-use.local
28072808
resolution: "@react-native-macos/virtualized-lists@workspace:packages/virtualized-lists"
28082809
dependencies:
@@ -2835,7 +2836,7 @@ __metadata:
28352836
languageName: unknown
28362837
linkType: soft
28372838

2838-
"@react-native/babel-preset@npm:0.78.2, @react-native/babel-preset@workspace:*, @react-native/babel-preset@workspace:packages/react-native-babel-preset":
2839+
"@react-native/babel-preset@npm:0.78.2, @react-native/babel-preset@workspace:packages/react-native-babel-preset":
28392840
version: 0.0.0-use.local
28402841
resolution: "@react-native/babel-preset@workspace:packages/react-native-babel-preset"
28412842
dependencies:
@@ -3107,20 +3108,20 @@ __metadata:
31073108
languageName: unknown
31083109
linkType: soft
31093110

3110-
"@react-native/oss-library-example@npm:0.78.3, @react-native/oss-library-example@workspace:packages/react-native-test-library":
3111+
"@react-native/oss-library-example@workspace:*, @react-native/oss-library-example@workspace:packages/react-native-test-library":
31113112
version: 0.0.0-use.local
31123113
resolution: "@react-native/oss-library-example@workspace:packages/react-native-test-library"
31133114
dependencies:
31143115
"@babel/core": "npm:^7.25.2"
3115-
"@react-native/babel-preset": "workspace:*"
3116+
"@react-native/babel-preset": "npm:0.78.2"
31163117
react-native-macos: "workspace:*"
31173118
peerDependencies:
31183119
react: "*"
31193120
react-native-macos: "*"
31203121
languageName: unknown
31213122
linkType: soft
31223123

3123-
"@react-native/popup-menu-android@workspace:*, @react-native/popup-menu-android@workspace:packages/react-native-popup-menu-android":
3124+
"@react-native/popup-menu-android@npm:0.78.2, @react-native/popup-menu-android@workspace:packages/react-native-popup-menu-android":
31243125
version: 0.0.0-use.local
31253126
resolution: "@react-native/popup-menu-android@workspace:packages/react-native-popup-menu-android"
31263127
dependencies:
@@ -3143,8 +3144,8 @@ __metadata:
31433144
"@react-native-community/cli": "npm:15.0.0-alpha.2"
31443145
"@react-native-community/cli-platform-android": "npm:15.0.0-alpha.2"
31453146
"@react-native-community/cli-platform-ios": "npm:15.0.0-alpha.2"
3146-
"@react-native/oss-library-example": "npm:0.78.3"
3147-
"@react-native/popup-menu-android": "workspace:*"
3147+
"@react-native/oss-library-example": "workspace:*"
3148+
"@react-native/popup-menu-android": "npm:0.78.2"
31483149
flow-enums-runtime: "npm:^0.0.6"
31493150
invariant: "npm:^2.2.4"
31503151
nullthrows: "npm:^1.1.1"
@@ -3931,6 +3932,15 @@ __metadata:
39313932
languageName: node
39323933
linkType: hard
39333934

3935+
"@yarnpkg/types@npm:^4.0.1":
3936+
version: 4.0.1
3937+
resolution: "@yarnpkg/types@npm:4.0.1"
3938+
dependencies:
3939+
tslib: "npm:^2.4.0"
3940+
checksum: 10c0/90226789475680ba599833571dd76c0718dd5b4c5022481263ef309d6a628f6246671cd6ca86e49c966ddefa7aca6ccef82240dc1476d2cea702ea5bee2a6b72
3941+
languageName: node
3942+
linkType: hard
3943+
39343944
"@zkochan/js-yaml@npm:0.0.7":
39353945
version: 0.0.7
39363946
resolution: "@zkochan/js-yaml@npm:0.0.7"
@@ -10894,7 +10904,7 @@ __metadata:
1089410904
resolution: "react-native-macos@workspace:packages/react-native"
1089510905
dependencies:
1089610906
"@jest/create-cache-key-function": "npm:^29.6.3"
10897-
"@react-native-macos/virtualized-lists": "npm:0.78.4"
10907+
"@react-native-macos/virtualized-lists": "workspcace:*"
1089810908
"@react-native/assets-registry": "npm:0.78.2"
1089910909
"@react-native/codegen": "npm:0.78.2"
1090010910
"@react-native/community-cli-plugin": "npm:0.78.2"

0 commit comments

Comments
 (0)