diff --git a/.github/workflows/microsoft-pr.yml b/.github/workflows/microsoft-pr.yml index 50fed45a7ef32e..3c5fd4bed9a6ce 100644 --- a/.github/workflows/microsoft-pr.yml +++ b/.github/workflows/microsoft-pr.yml @@ -63,3 +63,19 @@ jobs: run: | echo "Target branch: ${{ github.base_ref }}" yarn nx release --dry-run --verbose + yarn-constraints: + name: "Check Yarn Constraints" + permissions: {} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + filter: blob:none + fetch-depth: 0 + - uses: actions/setup-node@v4 + with: + node-version: '22' + - name: Install dependencies + run: yarn + - name: Check constraints + run: yarn constraints diff --git a/docs/Releases.md b/docs/Releases.md index c70a7a795be474..4fda4f563b5c3b 100644 --- a/docs/Releases.md +++ b/docs/Releases.md @@ -215,3 +215,13 @@ We do this so that our first release will have a proper patch version of 0, as s ``` + +## Hermes Compatibility (0.74 and above) + +*Note: This is only applicable if Hermes is enabled (I.E: `USE_HERMES=1 pod install`.* + +React Native macOS updates the patch version automatically for every change that comes through, so our patch number can differ wildly from that of vanilla React Native. + +To ensure the best possible Hermes compatibility, we specify a peer dependency in React Native macOS's `package.json` indicating which version of upstream React Native best corresponds with our own version. For example, our 0.74.34 corresponds to upstream's 0.74.7, and we reflect this connection in [this particular version of `package.json`](https://github.com/microsoft/react-native-macos/blob/2db3abeb5d4318fee3abdff4a4d1a68967223135/packages/react-native/package.json#L103). + +For stable branches, the existence of this peer dependency is enforced as part of our CIs via [Yarn Constraints](https://yarnpkg.com/features/constraints). \ No newline at end of file diff --git a/package.json b/package.json index aa96dc9b4ace49..af8aff691ab75e 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "@tsconfig/node18": "1.0.1", "@types/react": "^18.2.6", "@typescript-eslint/parser": "^7.1.1", + "@yarnpkg/types": "^4.0.1", "ansi-styles": "^4.2.1", "babel-plugin-minify-dead-code-elimination": "^0.5.2", "babel-plugin-syntax-hermes-parser": "0.23.1", @@ -94,7 +95,7 @@ "mkdirp": "^0.5.1", "node-fetch": "^2.2.0", "nullthrows": "^1.1.1", - "nx": "21.2.4", + "nx": "^21.2.4", "prettier": "2.8.8", "prettier-plugin-hermes-parser": "0.23.1", "react": "18.3.1", diff --git a/packages/nx-release-version/package.json b/packages/nx-release-version/package.json index e590e3ff7633ea..e398faa32f5c53 100644 --- a/packages/nx-release-version/package.json +++ b/packages/nx-release-version/package.json @@ -1,6 +1,7 @@ { "name": "@react-native-macos/nx-release-version", "version": "0.0.1-dev", + "private": true, "description": "Nx Release Version Actions for React Native macOS", "homepage": "https://github.com/microsoft/react-native-macos/tree/HEAD/packages/nx-release-version#readme", "license": "MIT", diff --git a/packages/rn-tester/package.json b/packages/rn-tester/package.json index e90d73167fe641..179363e68ab53b 100644 --- a/packages/rn-tester/package.json +++ b/packages/rn-tester/package.json @@ -23,8 +23,8 @@ "clean-ios": "rm -rf build/generated/ios Pods Podfile.lock" }, "dependencies": { - "@react-native/oss-library-example": "0.76.10", - "@react-native/popup-menu-android": "workspace:*", + "@react-native/oss-library-example": "workspace:*", + "@react-native/popup-menu-android": "0.76.9", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "nullthrows": "^1.1.1" diff --git a/yarn.config.cjs b/yarn.config.cjs new file mode 100644 index 00000000000000..3f63e794a0a237 --- /dev/null +++ b/yarn.config.cjs @@ -0,0 +1,116 @@ +// @ts-check + +/** @type {import('@yarnpkg/types')} */ +const {defineConfig} = require('@yarnpkg/types'); + +/** + * @typedef {import('@yarnpkg/types').Yarn.Constraints.Context} Context + * @typedef {import('@yarnpkg/types').Yarn.Constraints.Workspace} Workspace + * @typedef {import('@yarnpkg/types').Yarn.Constraints.Dependency} Dependency + */ + +/** + * Enforce that react-native-macos declares a peer dependency on react-native on release branches, + * except on the main branch, where there is no published version of React Native to align to. + * @param {Context} context + */ +function expectReactNativePeerDependency({Yarn}) { + const rnmWorkspace = Yarn.workspace({ident: 'react-native-macos'}); + if (!rnmWorkspace) { + // Report error on root workspace since react-native-macos doesn't exist + Yarn.workspace().error('react-native-macos workspace must exist in the monorepo'); + return; + } + + // Check if react-native-macos version is 1000.0.0 - implying we are on the main branch + const isMainBranch = rnmWorkspace.manifest.version === '1000.0.0'; + if (!isMainBranch) { + const rnPeerDependency = rnmWorkspace.pkg.peerDependencies.get('react-native'); + if (!rnPeerDependency) { + rnmWorkspace.error('react-native-macos must declare a peer dependency on react-native on release branches'); + } + } +} + +/** + * Enforce that all @react-native/ scoped packages use the same version + * as the react-native peer dependency declared in react-native-macos. + * On the main branch, enforce that we use workspace:* for @react-native/ packages. + * @param {Context} context + */ +function enforceReactNativeVersionConsistency({Yarn}) { + const rnmWorkspace = Yarn.workspace({ident: 'react-native-macos'}); + if (!rnmWorkspace) { + // Report error on root workspace since react-native-macos doesn't exist + Yarn.workspace().error('react-native-macos workspace must exist in the monorepo'); + return; + } + + // Check if react-native-macos version is 1000.0.0 - implying we are on the main branch + const isMainBranch = rnmWorkspace.manifest.version === '1000.0.0'; + + let targetVersion; + if (isMainBranch) { + // On main branch, use workspace:* for @react-native/ packages + targetVersion = 'workspace:*'; + } else { + const rnPeerDependency = rnmWorkspace.pkg.peerDependencies.get('react-native'); + if (!rnPeerDependency) { + rnmWorkspace.error('react-native-macos must declare a peer dependency on react-native on release branches'); + return; + } + targetVersion = rnPeerDependency; + } // Enforce this version on all @react-native/ scoped packages across all workspaces + for (const dependency of Yarn.dependencies()) { + if (dependency.ident.startsWith('@react-native/')) { + // Check if the target package is private (not published) + const targetWorkspace = Yarn.workspace({ident: dependency.ident}); + const isPrivatePackage = targetWorkspace && targetWorkspace.manifest.private; + + if (isPrivatePackage) { + // Private packages should always use workspace:* since they're not published + dependency.update('workspace:*'); + } else { + dependency.update(targetVersion); + } + } + } +} + +/** + * Enforce that all @react-native-macos/ scoped packages use the same version + * as react-native-macos, but only for non-private packages. + * @param {Context} context + */ +function enforceReactNativeMacosVersionConsistency({Yarn}) { + const rnmWorkspace = Yarn.workspace({ident: 'react-native-macos'}); + if (!rnmWorkspace) { + // Report error on root workspace since react-native-macos doesn't exist + Yarn.workspace().error('react-native-macos workspace must exist in the monorepo'); + return; + } + + const targetVersion = rnmWorkspace.manifest.version; + if (!targetVersion) { + rnmWorkspace.error('react-native-macos must have a version'); + return; + } + + // Enforce this version on all non-private @react-native-macos/ scoped packages + for (const workspace of Yarn.workspaces()) { + const isReactNativeMacosScoped = workspace.ident && workspace.ident.startsWith('@react-native-macos/'); + const isPrivate = workspace.manifest.private; + + if (isReactNativeMacosScoped && !isPrivate) { + workspace.set('version', targetVersion); + } + } +} + +module.exports = defineConfig({ + constraints: async ctx => { + expectReactNativePeerDependency(ctx); + enforceReactNativeVersionConsistency(ctx); + enforceReactNativeMacosVersionConsistency(ctx); + }, +}); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 2e657fcb5c3ffa..96186939e23b7f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2849,13 +2849,6 @@ __metadata: languageName: node linkType: hard -"@nx/nx-darwin-arm64@npm:21.2.4": - version: 21.2.4 - resolution: "@nx/nx-darwin-arm64@npm:21.2.4" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - "@nx/nx-darwin-arm64@npm:21.3.7": version: 21.3.7 resolution: "@nx/nx-darwin-arm64@npm:21.3.7" @@ -2863,10 +2856,10 @@ __metadata: languageName: node linkType: hard -"@nx/nx-darwin-x64@npm:21.2.4": - version: 21.2.4 - resolution: "@nx/nx-darwin-x64@npm:21.2.4" - conditions: os=darwin & cpu=x64 +"@nx/nx-darwin-arm64@npm:21.3.8": + version: 21.3.8 + resolution: "@nx/nx-darwin-arm64@npm:21.3.8" + conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -2877,10 +2870,10 @@ __metadata: languageName: node linkType: hard -"@nx/nx-freebsd-x64@npm:21.2.4": - version: 21.2.4 - resolution: "@nx/nx-freebsd-x64@npm:21.2.4" - conditions: os=freebsd & cpu=x64 +"@nx/nx-darwin-x64@npm:21.3.8": + version: 21.3.8 + resolution: "@nx/nx-darwin-x64@npm:21.3.8" + conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -2891,10 +2884,10 @@ __metadata: languageName: node linkType: hard -"@nx/nx-linux-arm-gnueabihf@npm:21.2.4": - version: 21.2.4 - resolution: "@nx/nx-linux-arm-gnueabihf@npm:21.2.4" - conditions: os=linux & cpu=arm +"@nx/nx-freebsd-x64@npm:21.3.8": + version: 21.3.8 + resolution: "@nx/nx-freebsd-x64@npm:21.3.8" + conditions: os=freebsd & cpu=x64 languageName: node linkType: hard @@ -2905,10 +2898,10 @@ __metadata: languageName: node linkType: hard -"@nx/nx-linux-arm64-gnu@npm:21.2.4": - version: 21.2.4 - resolution: "@nx/nx-linux-arm64-gnu@npm:21.2.4" - conditions: os=linux & cpu=arm64 & libc=glibc +"@nx/nx-linux-arm-gnueabihf@npm:21.3.8": + version: 21.3.8 + resolution: "@nx/nx-linux-arm-gnueabihf@npm:21.3.8" + conditions: os=linux & cpu=arm languageName: node linkType: hard @@ -2919,10 +2912,10 @@ __metadata: languageName: node linkType: hard -"@nx/nx-linux-arm64-musl@npm:21.2.4": - version: 21.2.4 - resolution: "@nx/nx-linux-arm64-musl@npm:21.2.4" - conditions: os=linux & cpu=arm64 & libc=musl +"@nx/nx-linux-arm64-gnu@npm:21.3.8": + version: 21.3.8 + resolution: "@nx/nx-linux-arm64-gnu@npm:21.3.8" + conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard @@ -2933,10 +2926,10 @@ __metadata: languageName: node linkType: hard -"@nx/nx-linux-x64-gnu@npm:21.2.4": - version: 21.2.4 - resolution: "@nx/nx-linux-x64-gnu@npm:21.2.4" - conditions: os=linux & cpu=x64 & libc=glibc +"@nx/nx-linux-arm64-musl@npm:21.3.8": + version: 21.3.8 + resolution: "@nx/nx-linux-arm64-musl@npm:21.3.8" + conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard @@ -2947,10 +2940,10 @@ __metadata: languageName: node linkType: hard -"@nx/nx-linux-x64-musl@npm:21.2.4": - version: 21.2.4 - resolution: "@nx/nx-linux-x64-musl@npm:21.2.4" - conditions: os=linux & cpu=x64 & libc=musl +"@nx/nx-linux-x64-gnu@npm:21.3.8": + version: 21.3.8 + resolution: "@nx/nx-linux-x64-gnu@npm:21.3.8" + conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard @@ -2961,10 +2954,10 @@ __metadata: languageName: node linkType: hard -"@nx/nx-win32-arm64-msvc@npm:21.2.4": - version: 21.2.4 - resolution: "@nx/nx-win32-arm64-msvc@npm:21.2.4" - conditions: os=win32 & cpu=arm64 +"@nx/nx-linux-x64-musl@npm:21.3.8": + version: 21.3.8 + resolution: "@nx/nx-linux-x64-musl@npm:21.3.8" + conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard @@ -2975,10 +2968,10 @@ __metadata: languageName: node linkType: hard -"@nx/nx-win32-x64-msvc@npm:21.2.4": - version: 21.2.4 - resolution: "@nx/nx-win32-x64-msvc@npm:21.2.4" - conditions: os=win32 & cpu=x64 +"@nx/nx-win32-arm64-msvc@npm:21.3.8": + version: 21.3.8 + resolution: "@nx/nx-win32-arm64-msvc@npm:21.3.8" + conditions: os=win32 & cpu=arm64 languageName: node linkType: hard @@ -2989,6 +2982,13 @@ __metadata: languageName: node linkType: hard +"@nx/nx-win32-x64-msvc@npm:21.3.8": + version: 21.3.8 + resolution: "@nx/nx-win32-x64-msvc@npm:21.3.8" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@nx/workspace@npm:21.3.7": version: 21.3.7 resolution: "@nx/workspace@npm:21.3.7" @@ -3363,6 +3363,7 @@ __metadata: "@tsconfig/node18": "npm:1.0.1" "@types/react": "npm:^18.2.6" "@typescript-eslint/parser": "npm:^7.1.1" + "@yarnpkg/types": "npm:^4.0.1" ansi-styles: "npm:^4.2.1" babel-plugin-minify-dead-code-elimination: "npm:^0.5.2" babel-plugin-syntax-hermes-parser: "npm:0.23.1" @@ -3401,7 +3402,7 @@ __metadata: mkdirp: "npm:^0.5.1" node-fetch: "npm:^2.2.0" nullthrows: "npm:^1.1.1" - nx: "npm:21.2.4" + nx: "npm:^21.2.4" prettier: "npm:2.8.8" prettier-plugin-hermes-parser: "npm:0.23.1" react: "npm:18.3.1" @@ -3750,7 +3751,7 @@ __metadata: languageName: unknown linkType: soft -"@react-native/oss-library-example@npm:0.76.10, @react-native/oss-library-example@workspace:packages/react-native-test-library": +"@react-native/oss-library-example@workspace:*, @react-native/oss-library-example@workspace:packages/react-native-test-library": version: 0.0.0-use.local resolution: "@react-native/oss-library-example@workspace:packages/react-native-test-library" dependencies: @@ -3763,7 +3764,7 @@ __metadata: languageName: unknown linkType: soft -"@react-native/popup-menu-android@workspace:*, @react-native/popup-menu-android@workspace:packages/react-native-popup-menu-android": +"@react-native/popup-menu-android@npm:0.76.9, @react-native/popup-menu-android@workspace:packages/react-native-popup-menu-android": version: 0.0.0-use.local resolution: "@react-native/popup-menu-android@workspace:packages/react-native-popup-menu-android" dependencies: @@ -3800,8 +3801,8 @@ __metadata: "@react-native-community/cli-platform-android": "npm:15.0.1" "@react-native-community/cli-platform-apple": "npm:15.0.1" "@react-native-community/cli-platform-ios": "npm:15.0.1" - "@react-native/oss-library-example": "npm:0.76.10" - "@react-native/popup-menu-android": "workspace:*" + "@react-native/oss-library-example": "workspace:*" + "@react-native/popup-menu-android": "npm:0.76.9" flow-enums-runtime: "npm:^0.0.6" invariant: "npm:^2.2.4" nullthrows: "npm:^1.1.1" @@ -4658,6 +4659,15 @@ __metadata: languageName: node linkType: hard +"@yarnpkg/types@npm:^4.0.1": + version: 4.0.1 + resolution: "@yarnpkg/types@npm:4.0.1" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/90226789475680ba599833571dd76c0718dd5b4c5022481263ef309d6a628f6246671cd6ca86e49c966ddefa7aca6ccef82240dc1476d2cea702ea5bee2a6b72 + languageName: node + linkType: hard + "@zkochan/js-yaml@npm:0.0.7": version: 0.0.7 resolution: "@zkochan/js-yaml@npm:0.0.7" @@ -9332,7 +9342,7 @@ __metadata: languageName: node linkType: hard -"jest-diff@npm:^29.4.1, jest-diff@npm:^29.7.0": +"jest-diff@npm:^29.7.0": version: 29.7.0 resolution: "jest-diff@npm:29.7.0" dependencies: @@ -11314,21 +11324,21 @@ __metadata: languageName: node linkType: hard -"nx@npm:21.2.4": - version: 21.2.4 - resolution: "nx@npm:21.2.4" +"nx@npm:21.3.7": + version: 21.3.7 + resolution: "nx@npm:21.3.7" dependencies: "@napi-rs/wasm-runtime": "npm:0.2.4" - "@nx/nx-darwin-arm64": "npm:21.2.4" - "@nx/nx-darwin-x64": "npm:21.2.4" - "@nx/nx-freebsd-x64": "npm:21.2.4" - "@nx/nx-linux-arm-gnueabihf": "npm:21.2.4" - "@nx/nx-linux-arm64-gnu": "npm:21.2.4" - "@nx/nx-linux-arm64-musl": "npm:21.2.4" - "@nx/nx-linux-x64-gnu": "npm:21.2.4" - "@nx/nx-linux-x64-musl": "npm:21.2.4" - "@nx/nx-win32-arm64-msvc": "npm:21.2.4" - "@nx/nx-win32-x64-msvc": "npm:21.2.4" + "@nx/nx-darwin-arm64": "npm:21.3.7" + "@nx/nx-darwin-x64": "npm:21.3.7" + "@nx/nx-freebsd-x64": "npm:21.3.7" + "@nx/nx-linux-arm-gnueabihf": "npm:21.3.7" + "@nx/nx-linux-arm64-gnu": "npm:21.3.7" + "@nx/nx-linux-arm64-musl": "npm:21.3.7" + "@nx/nx-linux-x64-gnu": "npm:21.3.7" + "@nx/nx-linux-x64-musl": "npm:21.3.7" + "@nx/nx-win32-arm64-msvc": "npm:21.3.7" + "@nx/nx-win32-x64-msvc": "npm:21.3.7" "@yarnpkg/lockfile": "npm:^1.1.0" "@yarnpkg/parsers": "npm:3.0.2" "@zkochan/js-yaml": "npm:0.0.7" @@ -11344,7 +11354,7 @@ __metadata: flat: "npm:^5.0.2" front-matter: "npm:^4.0.2" ignore: "npm:^5.0.4" - jest-diff: "npm:^29.4.1" + jest-diff: "npm:^30.0.2" jsonc-parser: "npm:3.2.0" lines-and-columns: "npm:2.0.3" minimatch: "npm:9.0.3" @@ -11395,25 +11405,25 @@ __metadata: bin: nx: bin/nx.js nx-cloud: bin/nx-cloud.js - checksum: 10c0/06905dfea003776f38704d5f187db1a03105251a90e6b4f7029ad5c0b0b561fa157d2b81d5305e4c8eaf73cd1f602d59450494cdd9bd6111c20f5cf2e3c09540 + checksum: 10c0/97389e08f34c3d44bbbf08c847f58fb866c527b5c9a88e601d4b3d774798913300381e4a0009aad429ee0631382dc606bb18e2f9c4ea2949da3ab65af8514e22 languageName: node linkType: hard -"nx@npm:21.3.7": - version: 21.3.7 - resolution: "nx@npm:21.3.7" +"nx@npm:^21.2.4": + version: 21.3.8 + resolution: "nx@npm:21.3.8" dependencies: "@napi-rs/wasm-runtime": "npm:0.2.4" - "@nx/nx-darwin-arm64": "npm:21.3.7" - "@nx/nx-darwin-x64": "npm:21.3.7" - "@nx/nx-freebsd-x64": "npm:21.3.7" - "@nx/nx-linux-arm-gnueabihf": "npm:21.3.7" - "@nx/nx-linux-arm64-gnu": "npm:21.3.7" - "@nx/nx-linux-arm64-musl": "npm:21.3.7" - "@nx/nx-linux-x64-gnu": "npm:21.3.7" - "@nx/nx-linux-x64-musl": "npm:21.3.7" - "@nx/nx-win32-arm64-msvc": "npm:21.3.7" - "@nx/nx-win32-x64-msvc": "npm:21.3.7" + "@nx/nx-darwin-arm64": "npm:21.3.8" + "@nx/nx-darwin-x64": "npm:21.3.8" + "@nx/nx-freebsd-x64": "npm:21.3.8" + "@nx/nx-linux-arm-gnueabihf": "npm:21.3.8" + "@nx/nx-linux-arm64-gnu": "npm:21.3.8" + "@nx/nx-linux-arm64-musl": "npm:21.3.8" + "@nx/nx-linux-x64-gnu": "npm:21.3.8" + "@nx/nx-linux-x64-musl": "npm:21.3.8" + "@nx/nx-win32-arm64-msvc": "npm:21.3.8" + "@nx/nx-win32-x64-msvc": "npm:21.3.8" "@yarnpkg/lockfile": "npm:^1.1.0" "@yarnpkg/parsers": "npm:3.0.2" "@zkochan/js-yaml": "npm:0.0.7" @@ -11480,7 +11490,7 @@ __metadata: bin: nx: bin/nx.js nx-cloud: bin/nx-cloud.js - checksum: 10c0/97389e08f34c3d44bbbf08c847f58fb866c527b5c9a88e601d4b3d774798913300381e4a0009aad429ee0631382dc606bb18e2f9c4ea2949da3ab65af8514e22 + checksum: 10c0/44c944b8185a6935db9094a4a361884fdcd7a303cb1cd21c9abab37d43941322a0ed99f09673f2aef810893a0a3928d46177cec9b2f286427e4f8a570c626ca3 languageName: node linkType: hard