-
Notifications
You must be signed in to change notification settings - Fork 1k
/
yarn.config.cjs
181 lines (165 loc) · 4.95 KB
/
yarn.config.cjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/* eslint-env node */
// @ts-check
/**
* @typedef {import('@yarnpkg/types').Yarn.Constraints.Context} Context
* @typedef {import('@yarnpkg/types').Yarn.Constraints.Workspace} Workspace
*/
/** @type {import('@yarnpkg/types')} */
const { defineConfig } = require(`@yarnpkg/types`)
/**
* This rule will enforce that a workspace MUST depend on the same version of a
* dependency as the one used by the other workspaces.
*
* @param {Context} context
*/
function enforceConsistentDependenciesAcrossTheProject({ Yarn }) {
for (const dependency of Yarn.dependencies()) {
if (dependency.type === `peerDependencies`) {
continue
}
for (const otherDependency of Yarn.dependencies({
ident: dependency.ident,
})) {
if (otherDependency.type === `peerDependencies`) {
continue
}
if (
(dependency.type === `devDependencies` ||
otherDependency.type === `devDependencies`) &&
Yarn.workspace({ ident: otherDependency.ident })
) {
continue
}
dependency.update(otherDependency.range)
}
}
}
/**
* This rule will enforce that a workspace MUST depend on the same version of a
* dependency as the one used by the other workspaces.
*
* @param {Context} context
*/
function enforceWorkspaceDependenciesWhenPossible({ Yarn }) {
for (const dependency of Yarn.dependencies()) {
if (!Yarn.workspace({ ident: dependency.ident })) {
continue
}
dependency.update(`workspace:*`)
}
}
/**
* This rule will enforce that a dependency doesn't appear in both `dependencies`
* and `devDependencies`.
*
* @param {Context} context
*/
function enforceNotProdAndDevDependencies({ Yarn }) {
for (const workspace of Yarn.workspaces()) {
const dependencies = Yarn.dependencies({ workspace, type: 'dependencies' })
const devDependencies = Yarn.dependencies({
workspace,
type: 'devDependencies',
})
for (const dependency of dependencies) {
if (
devDependencies.find(
(devDependency) => devDependency.ident === dependency.ident,
)
) {
dependency.error(
`The dependency '${dependency.ident}' should not appear in both dependencies and devDependencies`,
)
}
}
}
}
/**
* This rule will enforce that any package built with babel (identified by the
* presence of a 'build:js' script in its `package.json`) must depend on the
* '@babel/runtime-corejs3' and 'core-js' packages.
*
* @param {Context} context
*/
function enforceBabelDependencies({ Yarn }) {
for (const workspace of Yarn.workspaces()) {
const packageJson = workspace.manifest
if (!packageJson.scripts?.[`build:js`]) {
continue
}
const dependencies = Yarn.dependencies({
workspace,
type: 'dependencies',
})
const requiredDependencies = [`@babel/runtime-corejs3`, `core-js`]
for (const dependency of requiredDependencies) {
if (!dependencies.find((dep) => dep.ident === dependency)) {
workspace.error(
`The package '${workspace.cwd}' must depend on '${dependency}' to build with babel`,
)
}
}
}
}
/**
* This rule will enforce that the specified fields are present in the
* `package.json` of all workspaces.
*
* @param {Context} context
* @param {string[]} fields
*/
function enforceFieldsOnAllWorkspaces({ Yarn }, fields) {
for (const workspace of Yarn.workspaces()) {
// Skip the root workspace
if (workspace.cwd === '.') {
continue
}
for (const field of fields) {
if (!workspace.manifest[field]) {
workspace.error(
`The field '${field}' is required in the package.json of '${workspace.cwd}'`,
)
}
}
}
}
/**
* This rule will enforce that the specified fields are present in the
* `package.json` of all workspaces and that they have the expected value.
*
* @param {Context} context
* @param {Record<string, ((workspace: Workspace) => any) | string>} fields
*/
function enforceFieldsWithValuesOnAllWorkspaces({ Yarn }, fields) {
for (const workspace of Yarn.workspaces()) {
// Skip the root workspace
if (workspace.cwd === '.') {
continue
}
for (const [field, value] of Object.entries(fields)) {
workspace.set(
field,
typeof value === `function` ? value(workspace) : value,
)
}
}
}
module.exports = defineConfig({
constraints: async (ctx) => {
enforceConsistentDependenciesAcrossTheProject(ctx)
enforceWorkspaceDependenciesWhenPossible(ctx)
enforceNotProdAndDevDependencies(ctx)
enforceBabelDependencies(ctx)
enforceFieldsOnAllWorkspaces(ctx, [
'name',
'version',
// 'description', // TODO(jgmw): Add description to all packages and uncomment this line
])
enforceFieldsWithValuesOnAllWorkspaces(ctx, {
license: 'MIT',
['repository.type']: 'git',
['repository.url']: 'git+https://github.com/redwoodjs/redwood.git',
['repository.directory']: (workspace) => workspace.cwd,
})
},
})