@@ -9,108 +9,161 @@ const {defineConfig} = require('@yarnpkg/types');
99 * @typedef {import('@yarnpkg/types').Yarn.Constraints.Dependency } Dependency
1010 */
1111
12+ // Helpers
13+
14+ // These packages in tools/ were private upstream in React Native and don't follow the same versioning as the public packages
15+ const PACKAGES_TO_IGNORE = [ '@react-native/eslint' , 'public-api' ] ;
16+
1217/**
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.
18+ * Get the React Native macOS workspace
1519 * @param {Context } context
20+ * @returns {Workspace | null } Workspace
1621 */
17- function expectReactNativePeerDependency ( { Yarn} ) {
22+ const getReactNativeMacOSWorkspace = ( { Yarn} ) => {
1823 const rnmWorkspace = Yarn . workspace ( { ident : 'react-native-macos' } ) ;
1924 if ( ! rnmWorkspace ) {
2025 // Report error on root workspace since react-native-macos doesn't exist
2126 Yarn . workspace ( ) . error ( 'react-native-macos workspace must exist in the monorepo' ) ;
22- return ;
2327 }
28+ return rnmWorkspace ;
29+ }
2430
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- }
31+ /**
32+ * Get the peer dependency on react-native declared by react-native-macos
33+ * @param {Context } context
34+ * @returns {string | undefined } Peer dependency version
35+ */
36+ const getReactNativePeerDependency = ( { Yarn} ) => {
37+ const rnmWorkspace = getReactNativeMacOSWorkspace ( { Yarn} ) ;
38+
39+ const rnPeerDependency = rnmWorkspace ?. pkg . peerDependencies . get ( 'react-native' ) ;
40+ if ( ! rnPeerDependency ) {
41+ rnmWorkspace ?. error ( 'react-native-macos must declare a peer dependency on react-native on release branches' ) ;
3242 }
43+ return rnPeerDependency ;
3344}
3445
3546/**
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.
47+ * // Check if react-native-macos version is 1000.0.0, implying we are on the main branch
48+ * @param {Context } context
49+ * @returns {boolean }
50+ */
51+ const isMainBranch = ( { Yarn} ) => {
52+ const rnmWorkspace = getReactNativeMacOSWorkspace ( { Yarn} ) ;
53+ return rnmWorkspace ?. manifest . version === '1000.0.0' ;
54+ }
55+
56+ // Constraints
57+
58+ /**
59+ * Enforce that all @react-native/ scoped packages are private
60+ * @param {Context } context
61+ */
62+ function enforcePrivateReactNativeScopedPackages ( { Yarn} ) {
63+ for ( const dependency of Yarn . dependencies ( ) ) {
64+ if ( dependency . ident . startsWith ( '@react-native/' ) ) {
65+ Yarn . workspace ( { ident : dependency . ident } ) ?. set ( 'private' , true ) ;
66+ }
67+ }
68+ }
69+
70+ /**
71+ * Enforce that react-native-macos declares a peer dependency on react-native on release branches,
72+ * and that this version is consistent across all @react-native/ scoped packages.
73+ * Do not enforce on the main branch, where there is no published version of React Native to align to.
3974 * @param {Context } context
4075 */
4176function 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- }
77+ if ( ! isMainBranch ( { Yarn} ) ) {
78+ const reactNativePeerDependency = getReactNativePeerDependency ( { Yarn} ) ;
4879
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 ;
80+ // Enforce this version on all @react -native/ scoped packages
81+ for ( const workspace of Yarn . workspaces ( ) ) {
82+ if ( workspace . ident ?. startsWith ( '@react-native/' ) && ! PACKAGES_TO_IGNORE . includes ( workspace . ident ) ) {
83+ workspace . set ( 'version' , reactNativePeerDependency ) ;
84+ }
6185 }
62- targetVersion = rnPeerDependency ;
63- } // Enforce this version on all @react -native/ scoped packages across all workspaces
86+ }
87+ }
88+
89+ /**
90+ * Enforce that all @react-native/ scoped dependencies use the same version
91+ * as the react-native peer dependency declared in react-native-macos.
92+ * Do not enforce on the main branch, where there is no published version of React Native to align to.
93+ * @param {Context } context
94+ */
95+ function enforceReactNativeDependencyConsistency ( { Yarn} ) {
6496 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:*' ) ;
97+ if ( dependency . ident . startsWith ( '@react-native/' ) ) {
98+ if ( ! isMainBranch ( { Yarn} ) ) {
99+ const reactNativeVersion = getReactNativePeerDependency ( { Yarn} ) ;
100+
101+ const isRNM = dependency . workspace . ident === 'react-native-macos' ;
102+ const isRNMForkedPackage = dependency . workspace . ident ?. startsWith ( '@react-native-macos/' ) ;
103+
104+ if ( isRNM || isRNMForkedPackage ) {
105+ // Don't use `workspace:*` for packages we publish until nx release with Yarn 4 supports it.
106+ dependency . update ( reactNativeVersion ) ;
107+ } else {
108+ dependency . update ( 'workspace:*' ) ;
109+ }
73110 } else {
74- dependency . update ( targetVersion ) ;
111+ dependency . update ( 'workspace:*' ) ;
75112 }
76113 }
77114 }
78115}
79116
80117/**
81- * Enforce that all @react-native-macos/ scoped packages use the same version
82- * as react-native-macos, but only for non-private packages.
118+ * Enforce that all public @react-native-macos/ scoped packages' versions
119+ * are consistent with react-native-macos
120+ * Do not enforce on the main branch, where we do not publish nightlies yet.
83121 * @param {Context } context
84122 */
85123function 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- }
124+ if ( ! isMainBranch ( { Yarn} ) ) {
125+ const rnmWorkspace = getReactNativeMacOSWorkspace ( { Yarn} ) ;
126+ const rnmVersion = rnmWorkspace ?. manifest . version ;
92127
93- const targetVersion = rnmWorkspace . manifest . version ;
94- if ( ! targetVersion ) {
95- rnmWorkspace . error ( 'react-native-macos must have a version' ) ;
96- return ;
97- }
128+ // Enforce this version on all non-private @react-native-macos/ scoped packages
129+ for ( const workspace of Yarn . workspaces ( ) ) {
130+ const isReactNativeMacosScoped = workspace . ident && workspace . ident . startsWith ( '@react-native-macos/' ) ;
131+ const isPrivate = workspace . manifest . private ;
132+
133+ if ( isReactNativeMacosScoped && ! isPrivate ) {
134+ workspace . set ( 'version' , rnmVersion ) ;
135+ }
136+ }
137+ }
138+ }
98139
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 ) ;
140+ /**
141+ * Enforce that all @react-native-macos/ scoped dependencies use the same version
142+ * as the react-native-macos
143+ * Do not enforce on the main branch, where there is no published version of React Native to align to.
144+ * @param {Context } context
145+ */
146+ function enforceReactNativeMacOSDependencyConsistency ( { Yarn} ) {
147+ const rnmWorkspace = getReactNativeMacOSWorkspace ( { Yarn} ) ;
148+ const rnmVersion = rnmWorkspace ?. manifest . version ;
149+
150+ for ( const dependency of Yarn . dependencies ( ) ) {
151+ if ( dependency . ident . startsWith ( '@react-native-macos/' ) ) {
152+ if ( ! isMainBranch ( { Yarn} ) ) {
153+ dependency . update ( rnmVersion ) ;
154+ } else {
155+ dependency . update ( 'workspace:*' ) ;
156+ }
106157 }
107158 }
108159}
109160
110161module . exports = defineConfig ( {
111162 constraints : async ctx => {
112- expectReactNativePeerDependency ( ctx ) ;
163+ enforcePrivateReactNativeScopedPackages ( ctx ) ;
113164 enforceReactNativeVersionConsistency ( ctx ) ;
165+ enforceReactNativeDependencyConsistency ( ctx ) ;
114166 enforceReactNativeMacosVersionConsistency ( ctx ) ;
167+ enforceReactNativeMacOSDependencyConsistency ( ctx ) ;
115168 } ,
116169} ) ;
0 commit comments