Skip to content

Commit 4ecdc45

Browse files
committed
ci: add more constraints
1 parent 38e9c69 commit 4ecdc45

File tree

1 file changed

+117
-64
lines changed

1 file changed

+117
-64
lines changed

yarn.config.cjs

Lines changed: 117 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -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
*/
4176
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-
}
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
*/
85123
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-
}
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

110161
module.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

Comments
 (0)