-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathyarn.config.cjs
More file actions
186 lines (168 loc) · 8.11 KB
/
yarn.config.cjs
File metadata and controls
186 lines (168 loc) · 8.11 KB
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
182
183
184
185
186
/** @type {import('@yarnpkg/types')} */
const { defineConfig } = require("@yarnpkg/types");
const { existsSync } = require("fs");
const path = require("path");
const fs = require("fs");
const ts = require("typescript");
let singleSentryVersion = null;
let singleFakerVersion = null;
let singleWebpackVersion = null;
let singleEmotionStyledVersion = null;
let singleRemotionVersion = null;
let singleStorybookVersion = null;
const isSorted = (arr) => arr.every((v, i, a) => !i || a[i - 1] <= v);
module.exports = defineConfig({
async constraints({ Yarn }) {
for (const workspace of Yarn.workspaces()) {
if (!workspace.manifest.scripts) {
workspace.error(`The workspace ${workspace.ident} is missing "scripts" in package.json.`);
continue;
}
if (!isSorted(Object.keys(workspace.manifest.scripts))) {
workspace.error(`The workspace ${workspace.ident} package.json "scripts" are not sorted.`);
}
// NOTE: A few of the following rules have hard-coded values, this is
// convenient because it allows you to run `yarn constraints --fix`.
if (workspace.ident != "@dvtl/root") {
workspace.unset('scripts.["test"]');
workspace.unset('scripts.["lint"]');
// Ensure every workspace (except the root, because the root does
// recursive checks and is very slow) has doctor.
workspace.set('scripts.["test.yarn"]', "doctor");
workspace.set("devDependencies.@yarnpkg/doctor", "^4.0.2");
if (workspace.ident.startsWith("@dvtl/") && !["@dvtl/nx", "@dvtl/zapier"].includes(workspace.ident)) {
workspace.set("devDependencies.@dvtl/nx", "workspace:*");
}
} else {
if (!isSorted(Object.keys(workspace.manifest.resolutions))) {
workspace.error(`The workspace ${workspace.ident} package.json "resolutions" are not sorted.`);
}
}
// Ensure every workspace has prettier
//
// For the root workspace we don't want prettier to try and check all the
// nested workspaces (because it would be slow), so we do a special case
// there and ignore those paths.
workspace.set(
'scripts.["test.prettier"]',
workspace.ident === "@dvtl/root"
? "prettier --cache --cache-location=.cache/.prettier-cache --list-different '!workspaces/**/*' '!infra/**/*' '**/*'"
: "prettier --cache --cache-location=.cache/.prettier-cache --list-different --ignore-path $(yarn workspace @dvtl/root rootPwd)/.prettierignore '**/*'",
);
workspace.set(
'scripts.["fix.prettier"]',
workspace.ident === "@dvtl/root"
? "prettier --cache --cache-location=.cache/.prettier-cache --list-different '!workspaces/**/*' '!infra/**/*' '**/*' -w"
: "prettier --cache --cache-location=.cache/.prettier-cache --list-different --ignore-path $(yarn workspace @dvtl/root rootPwd)/.prettierignore '**/*' -w",
);
workspace.set("devDependencies.prettier", "^3.3.3");
workspace.set("devDependencies.prettier-plugin-organize-imports", "^4.0.0");
if ("test.eslint" in workspace.manifest.scripts) {
const isEslintViaJestRunner = workspace.manifest.scripts["test.eslint"].includes("jest ");
if ("test.jest" in workspace.manifest.scripts && !isEslintViaJestRunner) {
workspace.set("scripts.['test.eslint']", "jest --selectProjects=eslint --maxWorkers=1");
}
if (isEslintViaJestRunner) {
workspace.set("devDependencies.jest-runner-eslint", "^2.1.2");
workspace.set("jest-runner-eslint.cliOptions.reportUnusedDisableDirectives", "error");
}
}
if ("test.jest" in workspace.manifest.scripts) {
if (!workspace.manifest.scripts["test.jest"].includes("--selectProjects")) {
workspace.set("scripts.['test.jest']", `${workspace.manifest.scripts["test.jest"]} --selectProjects=test`);
}
}
if ("pulumi" in workspace.manifest.scripts) {
// This is an infra workspace
if (!workspace.manifest.scripts.pulumi.includes("PULUMI_NODEJS_TRANSPILE_ONLY=true")) {
workspace.set("scripts.pulumi", `PULUMI_NODEJS_TRANSPILE_ONLY=true ${workspace.manifest.scripts.pulumi}`);
}
}
let tsConfig;
{
const tsConfigJsonPath = path.join(workspace.cwd, "tsconfig.json");
if (existsSync(tsConfigJsonPath)) {
const { config, error } = ts.readConfigFile(tsConfigJsonPath, (path) => fs.readFileSync(path, { encoding: "utf8" }));
if (config == null || error != null) {
workspace.error("Failed to read tsconfig.json file: " + error);
} else {
tsConfig = config;
}
}
}
// "Build-less" workspaces are those with a `main` field referring to a
// `.ts` file, which only works if you're running everything with a "just
// in time" transpiler like esbuild/swc/ts-node etc.
//
// In this case there's no need to _also_ have a build script that just
// runs `tsc`, and there's actually a penalty to doing so because it adds
// to the number of dependency tasks Nx needs to do (since most tasks
// depend on `^build`).
if ((workspace.manifest.main ?? "").endsWith(".ts")) {
if (!workspace.manifest.scripts["test.tsc"]) {
workspace.set("scripts.['test.tsc']", "tsc --noEmit");
}
if (tsConfig?.compilerOptions?.noEmit !== true) {
workspace.error(`The workspace ${workspace.ident} does not have a tsconfig.json file with "noEmit": true.`);
}
}
if ("test.eslint" in workspace.manifest.scripts) {
if (workspace.ident !== "@dvtl/zapier") {
workspace.set("devDependencies.@dvtl/eslint-plugin-dvtl", "workspace:*");
}
if (!("fix.eslint" in workspace.manifest.scripts)) {
workspace.set("scripts.['fix.eslint']", "eslint --fix .");
}
}
for (const [name, value] of Object.entries(workspace.manifest.scripts)) {
const bannedChars = /:|-/g;
if (bannedChars.test(name)) {
workspace.error(
`The script "${name}" contains banned characters (: or -). They must be camel case and not contain colons which make the script global.`,
);
}
if (value.startsWith("sh ")) {
// Don't explicitly call sh, just run the script and rely on the
// shebang line.
workspace.set(`scripts.["${name}"]`, value.replace(/^sh /, ""));
}
}
// There's no need to have a 'version' field if it's a private package and
// the version is `0.0.0`. Previously we thought it was necessary, so we
// used `0.0.0`.
if (workspace.manifest.version === "0.0.0" && workspace.manifest.private) {
workspace.unset("version");
}
}
for (const dep of Yarn.dependencies({ workspace: Yarn.workspace({ ident: "@dvtl/zapier" }) })) {
if (dep.range.startsWith("workspace:")) {
dep.error(`${dep.ident}@${dep.range} is not supported by Zapier due to its build scripts.`);
}
}
for (const dep of Yarn.dependencies()) {
// @sentry/cli doesn't use the same version as the rest of the sentry packages
if (dep.ident.startsWith("@sentry/") && dep.ident !== "@sentry/cli") {
singleSentryVersion ??= dep.range;
dep.update(singleSentryVersion);
} else if (dep.ident.startsWith("@faker-js/")) {
singleFakerVersion ??= dep.range;
dep.update(singleFakerVersion);
} else if (dep.ident === "webpack") {
singleWebpackVersion ??= dep.range;
dep.update(singleWebpackVersion);
} else if (dep.ident === "@emotion/styled") {
singleEmotionStyledVersion ??= dep.range;
dep.update(singleEmotionStyledVersion);
} else if (dep.ident === "remotion" || dep.ident.startsWith("@remotion")) {
singleRemotionVersion ??= dep.range;
dep.update(singleRemotionVersion);
} else if (
(dep.ident === "storybook" || dep.ident.startsWith("@storybook")) &&
dep.ident !== "@storybook/addon-webpack5-compiler-swc"
) {
singleStorybookVersion ??= dep.range;
dep.update(singleStorybookVersion);
}
}
},
});