@monorepolint/rules 0.6.0-alpha.5 → 0.6.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-clean.log +1 -1
- package/.turbo/turbo-compile-typescript.log +1 -1
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-test.log +446 -94
- package/.turbo/turbo-transpile-typescript.log +5 -5
- package/CHANGELOG.md +102 -0
- package/build/js/index.js +413 -368
- package/build/js/index.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/REMOVE.d.ts +2 -0
- package/build/types/REMOVE.d.ts.map +1 -0
- package/build/types/__tests__/alphabeticalDependencies.spec.d.ts +8 -0
- package/build/types/__tests__/alphabeticalDependencies.spec.d.ts.map +1 -0
- package/build/types/__tests__/forceError.spec.d.ts +8 -0
- package/build/types/__tests__/forceError.spec.d.ts.map +1 -0
- package/build/types/__tests__/oncePerPackage.spec.d.ts +8 -0
- package/build/types/__tests__/oncePerPackage.spec.d.ts.map +1 -0
- package/build/types/__tests__/standardTsconfig.spec.d.ts +8 -0
- package/build/types/__tests__/standardTsconfig.spec.d.ts.map +1 -0
- package/build/types/bannedDependencies.d.ts +9 -33
- package/build/types/bannedDependencies.d.ts.map +1 -1
- package/build/types/consistentDependencies.d.ts +6 -6
- package/build/types/consistentDependencies.d.ts.map +1 -1
- package/build/types/consistentVersions.d.ts +6 -10
- package/build/types/consistentVersions.d.ts.map +1 -1
- package/build/types/fileContents.d.ts +3 -2
- package/build/types/fileContents.d.ts.map +1 -1
- package/build/types/index.d.ts +1 -0
- package/build/types/index.d.ts.map +1 -1
- package/build/types/mustSatisfyPeerDependencies.d.ts +12 -190
- package/build/types/mustSatisfyPeerDependencies.d.ts.map +1 -1
- package/build/types/nestedWorkspaces.d.ts +2 -2
- package/build/types/nestedWorkspaces.d.ts.map +1 -1
- package/build/types/oncePerPackage.d.ts +6 -6
- package/build/types/oncePerPackage.d.ts.map +1 -1
- package/build/types/packageEntry.d.ts +11 -33
- package/build/types/packageEntry.d.ts.map +1 -1
- package/build/types/packageOrder.d.ts +2 -1
- package/build/types/packageOrder.d.ts.map +1 -1
- package/build/types/packageScript.d.ts +13 -22
- package/build/types/packageScript.d.ts.map +1 -1
- package/build/types/requireDependency.d.ts +5 -20
- package/build/types/requireDependency.d.ts.map +1 -1
- package/build/types/standardTsconfig.d.ts +12 -19
- package/build/types/standardTsconfig.d.ts.map +1 -1
- package/build/types/util/zodSchemas.d.ts +14 -0
- package/build/types/util/zodSchemas.d.ts.map +1 -0
- package/coverage/block-navigation.js +1 -1
- package/coverage/clover.xml +1454 -1442
- package/coverage/coverage-final.json +21 -19
- package/coverage/index.html +27 -27
- package/coverage/sorter.js +21 -7
- package/coverage/src/REMOVE.ts.html +88 -0
- package/coverage/src/alphabeticalDependencies.ts.html +34 -34
- package/coverage/src/alphabeticalScripts.ts.html +5 -5
- package/coverage/src/bannedDependencies.ts.html +20 -53
- package/coverage/src/consistentDependencies.ts.html +20 -14
- package/coverage/src/consistentVersions.ts.html +330 -183
- package/coverage/src/fileContents.ts.html +223 -88
- package/coverage/src/forceError.ts.html +60 -60
- package/coverage/src/index.html +127 -112
- package/coverage/src/index.ts.html +11 -5
- package/coverage/src/mustSatisfyPeerDependencies.ts.html +15 -501
- package/coverage/src/nestedWorkspaces.ts.html +5 -5
- package/coverage/src/oncePerPackage.ts.html +59 -59
- package/coverage/src/packageEntry.ts.html +121 -91
- package/coverage/src/packageOrder.ts.html +44 -14
- package/coverage/src/packageScript.ts.html +235 -88
- package/coverage/src/requireDependency.ts.html +241 -82
- package/coverage/src/standardTsconfig.ts.html +193 -193
- package/coverage/src/util/checkAlpha.ts.html +40 -40
- package/coverage/src/util/createRuleFactory.ts.html +19 -19
- package/coverage/src/util/index.html +30 -15
- package/coverage/src/util/makeDirectory.ts.html +11 -11
- package/coverage/src/util/packageDependencyGraphService.ts.html +1 -1
- package/coverage/src/util/zodSchemas.ts.html +130 -0
- package/package.json +14 -14
- package/src/REMOVE.ts +1 -0
- package/src/__tests__/alphabeticalDependencies.spec.ts +102 -0
- package/src/__tests__/alphabeticalScripts.spec.ts +18 -0
- package/src/__tests__/bannedDependencies.spec.ts +49 -0
- package/src/__tests__/consistentDependencies.spec.ts +23 -0
- package/src/__tests__/consistentVersions.spec.ts +142 -0
- package/src/__tests__/fileContents.spec.ts +348 -0
- package/src/__tests__/forceError.spec.ts +70 -0
- package/src/__tests__/mustSatisfyPeerDependencies.spec.ts +44 -0
- package/src/__tests__/nestedWorkspaces.spec.ts +14 -0
- package/src/__tests__/oncePerPackage.spec.ts +75 -0
- package/src/__tests__/packageEntry.spec.ts +177 -0
- package/src/__tests__/packageOrder.spec.ts +22 -0
- package/src/__tests__/packageScript.spec.ts +549 -0
- package/src/__tests__/requireDependency.spec.ts +259 -2
- package/src/__tests__/standardTsconfig.spec.ts +91 -0
- package/src/bannedDependencies.ts +14 -25
- package/src/consistentDependencies.ts +10 -8
- package/src/consistentVersions.ts +132 -83
- package/src/fileContents.ts +80 -35
- package/src/index.ts +2 -0
- package/src/mustSatisfyPeerDependencies.ts +10 -172
- package/src/nestedWorkspaces.ts +4 -4
- package/src/oncePerPackage.ts +6 -6
- package/src/packageEntry.ts +60 -50
- package/src/packageOrder.ts +19 -9
- package/src/packageScript.ts +67 -18
- package/src/requireDependency.ts +84 -31
- package/src/standardTsconfig.ts +26 -26
- package/src/util/zodSchemas.ts +15 -0
|
@@ -7,19 +7,19 @@
|
|
|
7
7
|
|
|
8
8
|
import { Context } from "@monorepolint/config";
|
|
9
9
|
import { mutateJson, PackageJson } from "@monorepolint/utils";
|
|
10
|
-
import * as r from "runtypes";
|
|
11
10
|
import { coerce, SemVer } from "semver";
|
|
11
|
+
import { z } from "zod";
|
|
12
12
|
import { createRuleFactory } from "./util/createRuleFactory.js";
|
|
13
|
-
export const Options =
|
|
14
|
-
matchDependencyVersions:
|
|
13
|
+
export const Options = z.object({
|
|
14
|
+
matchDependencyVersions: z.record(z.string(), z.union([z.string(), z.array(z.string())])),
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
-
export type Options =
|
|
17
|
+
export type Options = z.infer<typeof Options>;
|
|
18
18
|
|
|
19
19
|
export const consistentVersions = createRuleFactory({
|
|
20
20
|
name: "consistentVersions",
|
|
21
21
|
check: checkConsistentVersions,
|
|
22
|
-
validateOptions: Options.
|
|
22
|
+
validateOptions: Options.parse,
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
function checkConsistentVersions(context: Context, options: Options) {
|
|
@@ -45,6 +45,39 @@ function checkConsistentVersions(context: Context, options: Options) {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
const shouldUpdateVersion = (
|
|
49
|
+
actualValue: string,
|
|
50
|
+
expectedValue: string,
|
|
51
|
+
isProtocolVersionString: boolean,
|
|
52
|
+
expectedSemVer: SemVer | null,
|
|
53
|
+
): boolean => {
|
|
54
|
+
if (isProtocolVersionString) {
|
|
55
|
+
return actualValue !== expectedValue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const actualSemVer = coerce(actualValue);
|
|
59
|
+
return actualSemVer != null && actualSemVer.raw !== expectedSemVer!.raw;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const matchesAnyVersion = (
|
|
63
|
+
actualValue: string,
|
|
64
|
+
protocolVersions: string[],
|
|
65
|
+
acceptedSemVerVersions: SemVer[],
|
|
66
|
+
): boolean => {
|
|
67
|
+
if (protocolVersions.includes(actualValue)) {
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const actualSemVer = coerce(actualValue);
|
|
72
|
+
if (actualSemVer == null) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return acceptedSemVerVersions.some(
|
|
77
|
+
(acceptedSemVer) => actualSemVer.raw === acceptedSemVer.raw,
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
48
81
|
const ensurePackageIsCorrectVersion = (
|
|
49
82
|
context: Context,
|
|
50
83
|
dependencyPackageName: string,
|
|
@@ -53,55 +86,67 @@ const ensurePackageIsCorrectVersion = (
|
|
|
53
86
|
const packageJson = context.getPackageJson();
|
|
54
87
|
const packageJsonPath = context.getPackageJsonPath();
|
|
55
88
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
)
|
|
89
|
+
// Allow protocol version strings like "catalog:", "workspace:", etc.
|
|
90
|
+
const isProtocolVersion = expectedPackageDependencyValue.endsWith(":");
|
|
91
|
+
let expectedPackageDependencyVersion: SemVer | null = null;
|
|
92
|
+
|
|
93
|
+
if (!isProtocolVersion) {
|
|
94
|
+
expectedPackageDependencyVersion = coerce(expectedPackageDependencyValue);
|
|
95
|
+
if (expectedPackageDependencyVersion == null) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`Malformed expected package dependency version defined in monorepolint configuration: ${dependencyPackageName} @ '${expectedPackageDependencyValue}'`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
63
100
|
}
|
|
64
101
|
|
|
65
102
|
const actualPackageDependencyValue = packageJson.dependencies
|
|
66
103
|
&& packageJson.dependencies[dependencyPackageName];
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
104
|
+
|
|
105
|
+
if (actualPackageDependencyValue != null) {
|
|
106
|
+
const shouldUpdate = shouldUpdateVersion(
|
|
107
|
+
actualPackageDependencyValue,
|
|
108
|
+
expectedPackageDependencyValue,
|
|
109
|
+
isProtocolVersion,
|
|
110
|
+
expectedPackageDependencyVersion,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
if (shouldUpdate) {
|
|
114
|
+
context.addError({
|
|
115
|
+
file: packageJsonPath,
|
|
116
|
+
message:
|
|
117
|
+
`Expected dependency on ${dependencyPackageName} to match version defined in monorepolint configuration '${expectedPackageDependencyValue}', got '${actualPackageDependencyValue}' instead.`,
|
|
118
|
+
fixer: () =>
|
|
119
|
+
mutateJson<PackageJson>(packageJsonPath, context.host, (input) => {
|
|
120
|
+
input.dependencies![dependencyPackageName] = expectedPackageDependencyValue;
|
|
121
|
+
return input;
|
|
122
|
+
}),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
83
125
|
}
|
|
84
126
|
|
|
85
127
|
const actualPackageDevDependencyValue = packageJson.devDependencies
|
|
86
128
|
&& packageJson.devDependencies[dependencyPackageName];
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
129
|
+
|
|
130
|
+
if (actualPackageDevDependencyValue != null) {
|
|
131
|
+
const shouldUpdateDev = shouldUpdateVersion(
|
|
132
|
+
actualPackageDevDependencyValue,
|
|
133
|
+
expectedPackageDependencyValue,
|
|
134
|
+
isProtocolVersion,
|
|
135
|
+
expectedPackageDependencyVersion,
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
if (shouldUpdateDev) {
|
|
139
|
+
context.addError({
|
|
140
|
+
file: packageJsonPath,
|
|
141
|
+
message:
|
|
142
|
+
`Expected devDependency on ${dependencyPackageName} to match version defined in monorepolint configuration '${expectedPackageDependencyValue}', got '${actualPackageDevDependencyValue}' instead`,
|
|
143
|
+
fixer: () =>
|
|
144
|
+
mutateJson<PackageJson>(packageJsonPath, context.host, (input) => {
|
|
145
|
+
input.devDependencies![dependencyPackageName] = expectedPackageDependencyValue;
|
|
146
|
+
return input;
|
|
147
|
+
}),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
105
150
|
}
|
|
106
151
|
};
|
|
107
152
|
|
|
@@ -113,7 +158,11 @@ const ensurePackageMatchesSomeVersion = (
|
|
|
113
158
|
const packageJson = context.getPackageJson();
|
|
114
159
|
const packageJsonPath = context.getPackageJsonPath();
|
|
115
160
|
|
|
116
|
-
|
|
161
|
+
// Separate protocol versions from regular semver versions
|
|
162
|
+
const protocolVersions = acceptedPackageDependencyValues.filter(val => val.endsWith(":"));
|
|
163
|
+
const regularVersions = acceptedPackageDependencyValues.filter(val => !val.endsWith(":"));
|
|
164
|
+
|
|
165
|
+
const acceptedPackageDependencyVersions: SemVer[] = regularVersions.map(
|
|
117
166
|
(acceptedPackageDependencyValue) => {
|
|
118
167
|
const acceptedPackageDependencyVersion = coerce(
|
|
119
168
|
acceptedPackageDependencyValue,
|
|
@@ -129,45 +178,45 @@ const ensurePackageMatchesSomeVersion = (
|
|
|
129
178
|
|
|
130
179
|
const actualPackageDependencyValue = packageJson.dependencies
|
|
131
180
|
&& packageJson.dependencies[dependencyPackageName];
|
|
132
|
-
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
181
|
+
|
|
182
|
+
if (actualPackageDependencyValue != null) {
|
|
183
|
+
const matches = matchesAnyVersion(
|
|
184
|
+
actualPackageDependencyValue,
|
|
185
|
+
protocolVersions,
|
|
186
|
+
acceptedPackageDependencyVersions,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
if (!matches) {
|
|
190
|
+
context.addError({
|
|
191
|
+
file: packageJsonPath,
|
|
192
|
+
message: `Expected dependency on ${dependencyPackageName} to match one of '${
|
|
193
|
+
JSON.stringify(
|
|
194
|
+
acceptedPackageDependencyValues,
|
|
195
|
+
)
|
|
196
|
+
}', got '${actualPackageDependencyValue}' instead.`,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
149
199
|
}
|
|
150
200
|
|
|
151
201
|
const actualPackageDevDependencyValue = packageJson.devDependencies
|
|
152
202
|
&& packageJson.devDependencies[dependencyPackageName];
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
});
|
|
203
|
+
|
|
204
|
+
if (actualPackageDevDependencyValue != null) {
|
|
205
|
+
const matches = matchesAnyVersion(
|
|
206
|
+
actualPackageDevDependencyValue,
|
|
207
|
+
protocolVersions,
|
|
208
|
+
acceptedPackageDependencyVersions,
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
if (!matches) {
|
|
212
|
+
context.addError({
|
|
213
|
+
file: packageJsonPath,
|
|
214
|
+
message: `Expected devDependency on ${dependencyPackageName} to match one of '${
|
|
215
|
+
JSON.stringify(
|
|
216
|
+
acceptedPackageDependencyValues,
|
|
217
|
+
)
|
|
218
|
+
}', got '${actualPackageDevDependencyValue}' instead.`,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
172
221
|
}
|
|
173
222
|
};
|
package/src/fileContents.ts
CHANGED
|
@@ -8,49 +8,69 @@
|
|
|
8
8
|
import { Context } from "@monorepolint/config";
|
|
9
9
|
import { diff } from "jest-diff";
|
|
10
10
|
import * as path from "path";
|
|
11
|
-
import
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { REMOVE } from "./REMOVE.js";
|
|
12
13
|
import { createRuleFactory } from "./util/createRuleFactory.js";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
import { ZodRemove } from "./util/zodSchemas.js";
|
|
15
|
+
|
|
16
|
+
const Options = z.union([
|
|
17
|
+
z.object({
|
|
18
|
+
file: z.string(),
|
|
19
|
+
generator: z.custom<
|
|
20
|
+
(context: Context) => string | typeof REMOVE | Promise<string | typeof REMOVE>
|
|
21
|
+
>((val) => {
|
|
22
|
+
return typeof val === "function";
|
|
23
|
+
}),
|
|
24
|
+
template: z.undefined().optional(),
|
|
25
|
+
templateFile: z.undefined().optional(),
|
|
21
26
|
}),
|
|
22
|
-
|
|
23
|
-
file:
|
|
24
|
-
generator:
|
|
25
|
-
template:
|
|
26
|
-
|
|
27
|
+
z.object({
|
|
28
|
+
file: z.string(),
|
|
29
|
+
generator: z.undefined().optional(),
|
|
30
|
+
template: z.union([
|
|
31
|
+
z.string(),
|
|
32
|
+
ZodRemove,
|
|
33
|
+
]),
|
|
34
|
+
templateFile: z.undefined().optional(),
|
|
27
35
|
}),
|
|
28
|
-
|
|
29
|
-
file:
|
|
30
|
-
generator:
|
|
31
|
-
template:
|
|
32
|
-
templateFile:
|
|
36
|
+
z.object({
|
|
37
|
+
file: z.string(),
|
|
38
|
+
generator: z.undefined().optional(),
|
|
39
|
+
template: z.undefined().optional(),
|
|
40
|
+
templateFile: z.string(),
|
|
33
41
|
}),
|
|
34
|
-
);
|
|
42
|
+
]);
|
|
35
43
|
|
|
36
|
-
type Options =
|
|
44
|
+
type Options = z.infer<typeof Options>;
|
|
37
45
|
|
|
38
46
|
export const fileContents = createRuleFactory<Options>({
|
|
39
47
|
name: "fileContents",
|
|
40
48
|
check: async (context, opts) => {
|
|
41
49
|
const fullPath = path.join(context.packageDir, opts.file);
|
|
50
|
+
|
|
51
|
+
// Validate that the file path doesn't contain null bytes or other invalid characters
|
|
52
|
+
if (fullPath.includes("\0")) {
|
|
53
|
+
throw new Error(`Invalid file path: path contains null bytes`);
|
|
54
|
+
}
|
|
55
|
+
|
|
42
56
|
const expectedContent = await getExpectedContents(context, opts);
|
|
43
57
|
|
|
58
|
+
// If the generator function failed, an unfixable error was already added, so we should return early
|
|
59
|
+
if (expectedContent === Symbol.for("GENERATOR_ERROR")) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
44
63
|
const pathExists = context.host.exists(fullPath);
|
|
45
64
|
const actualContent = pathExists
|
|
46
65
|
? context.host.readFile(fullPath, { encoding: "utf-8" })
|
|
47
|
-
:
|
|
66
|
+
: REMOVE;
|
|
48
67
|
if (actualContent !== expectedContent) {
|
|
49
|
-
const longMessage =
|
|
50
|
-
|
|
51
|
-
|
|
68
|
+
const longMessage =
|
|
69
|
+
pathExists && (expectedContent === undefined || expectedContent === REMOVE)
|
|
70
|
+
? undefined
|
|
71
|
+
: diff(expectedContent, actualContent, { expand: true });
|
|
52
72
|
|
|
53
|
-
const message = pathExists && expectedContent
|
|
73
|
+
const message = pathExists && (expectedContent === undefined || expectedContent === REMOVE)
|
|
54
74
|
? "File should not exist"
|
|
55
75
|
: "Expect file contents to match";
|
|
56
76
|
|
|
@@ -59,9 +79,9 @@ export const fileContents = createRuleFactory<Options>({
|
|
|
59
79
|
message,
|
|
60
80
|
longMessage,
|
|
61
81
|
fixer: () => {
|
|
62
|
-
if (expectedContent ===
|
|
82
|
+
if (expectedContent === REMOVE) {
|
|
63
83
|
if (pathExists) context.host.deleteFile(fullPath);
|
|
64
|
-
} else {
|
|
84
|
+
} else if (expectedContent !== undefined && typeof expectedContent === "string") {
|
|
65
85
|
context.host.mkdir(path.dirname(fullPath), { recursive: true });
|
|
66
86
|
context.host.writeFile(fullPath, expectedContent, {
|
|
67
87
|
encoding: "utf-8",
|
|
@@ -71,29 +91,52 @@ export const fileContents = createRuleFactory<Options>({
|
|
|
71
91
|
});
|
|
72
92
|
}
|
|
73
93
|
},
|
|
74
|
-
validateOptions: Options.
|
|
94
|
+
validateOptions: Options.parse,
|
|
75
95
|
});
|
|
76
96
|
|
|
77
97
|
const optionsCache = new Map<
|
|
78
98
|
Options,
|
|
79
|
-
| ((context: Context) => Promise<string> | string |
|
|
99
|
+
| ((context: Context) => Promise<string | typeof REMOVE> | string | typeof REMOVE)
|
|
80
100
|
| string
|
|
81
|
-
|
|
|
101
|
+
| typeof REMOVE
|
|
82
102
|
>();
|
|
83
103
|
|
|
84
|
-
async function getExpectedContents(
|
|
104
|
+
async function getExpectedContents(
|
|
105
|
+
context: Context,
|
|
106
|
+
opts: Options,
|
|
107
|
+
): Promise<string | typeof REMOVE | symbol> {
|
|
85
108
|
// we need to use has because undefined is a valid value in the cache
|
|
86
109
|
if (optionsCache.has(opts)) {
|
|
87
110
|
const cachedEntry = optionsCache.get(opts);
|
|
88
111
|
if (cachedEntry && typeof cachedEntry === "function") {
|
|
89
112
|
return cachedEntry(context);
|
|
90
113
|
}
|
|
91
|
-
return cachedEntry;
|
|
114
|
+
return cachedEntry as string | typeof REMOVE;
|
|
92
115
|
}
|
|
93
116
|
|
|
94
117
|
if (opts.generator) {
|
|
95
118
|
optionsCache.set(opts, opts.generator);
|
|
96
|
-
|
|
119
|
+
try {
|
|
120
|
+
const result = await opts.generator(context);
|
|
121
|
+
if (typeof result !== "string" && result !== REMOVE) {
|
|
122
|
+
throw new Error(`Generator function must return a string or REMOVE, got ${typeof result}`);
|
|
123
|
+
}
|
|
124
|
+
return result;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
// Instead of throwing, create an unfixable error and return a special marker
|
|
127
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
128
|
+
const fullPath = path.join(context.packageDir, opts.file);
|
|
129
|
+
|
|
130
|
+
context.addError({
|
|
131
|
+
file: fullPath,
|
|
132
|
+
message: `Generator function failed: ${errorMessage}`,
|
|
133
|
+
longMessage:
|
|
134
|
+
`The generator function for file "${opts.file}" threw an error:\n\n${errorMessage}`,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Return a special marker that will prevent the file comparison from proceeding
|
|
138
|
+
return Symbol.for("GENERATOR_ERROR");
|
|
139
|
+
}
|
|
97
140
|
} else if (opts.templateFile) {
|
|
98
141
|
const { packageDir: workspacePackageDir } = context.getWorkspaceContext();
|
|
99
142
|
const fullPath = path.resolve(workspacePackageDir, opts.templateFile);
|
|
@@ -101,8 +144,10 @@ async function getExpectedContents(context: Context, opts: Options) {
|
|
|
101
144
|
|
|
102
145
|
optionsCache.set(opts, template);
|
|
103
146
|
return template;
|
|
104
|
-
} else {
|
|
147
|
+
} else if (opts.template !== undefined) {
|
|
105
148
|
optionsCache.set(opts, opts.template);
|
|
106
149
|
return opts.template;
|
|
150
|
+
} else {
|
|
151
|
+
throw new Error("Unable to get expected contents");
|
|
107
152
|
}
|
|
108
153
|
}
|
package/src/index.ts
CHANGED
|
@@ -9,185 +9,23 @@ import { Context } from "@monorepolint/config";
|
|
|
9
9
|
import { Host, mutateJson, PackageJson } from "@monorepolint/utils";
|
|
10
10
|
import * as path from "node:path";
|
|
11
11
|
import resolvePackagePath from "resolve-package-path";
|
|
12
|
-
import * as r from "runtypes";
|
|
13
12
|
import { coerce } from "semver";
|
|
13
|
+
import { z } from "zod";
|
|
14
14
|
import { createRuleFactory } from "./util/createRuleFactory.js";
|
|
15
15
|
|
|
16
|
-
const Options =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
.Record({
|
|
25
|
-
skipUnparseableRanges: r.Boolean,
|
|
26
|
-
})
|
|
27
|
-
.And(
|
|
28
|
-
r.Partial({
|
|
29
|
-
dependencyWhitelist: r.Undefined,
|
|
30
|
-
dependencyBlacklist: r.Undefined,
|
|
31
|
-
enforceForDevDependencies: r.Undefined,
|
|
32
|
-
}),
|
|
33
|
-
),
|
|
34
|
-
r
|
|
35
|
-
.Record({
|
|
36
|
-
dependencyWhitelist: r.Array(r.String),
|
|
37
|
-
})
|
|
38
|
-
.And(
|
|
39
|
-
r.Partial({
|
|
40
|
-
skipUnparseableRanges: r.Undefined,
|
|
41
|
-
dependencyBlacklist: r.Undefined,
|
|
42
|
-
enforceForDevDependencies: r.Undefined,
|
|
43
|
-
}),
|
|
44
|
-
),
|
|
45
|
-
r
|
|
46
|
-
.Record({
|
|
47
|
-
dependencyBlacklist: r.Array(r.String),
|
|
48
|
-
})
|
|
49
|
-
.And(
|
|
50
|
-
r.Partial({
|
|
51
|
-
skipUnparseableRanges: r.Undefined,
|
|
52
|
-
dependencyWhitelist: r.Undefined,
|
|
53
|
-
enforceForDevDependencies: r.Undefined,
|
|
54
|
-
}),
|
|
55
|
-
),
|
|
56
|
-
r
|
|
57
|
-
.Record({
|
|
58
|
-
enforceForDevDependencies: r.Boolean,
|
|
59
|
-
})
|
|
60
|
-
.And(
|
|
61
|
-
r.Partial({
|
|
62
|
-
skipUnparseableRanges: r.Undefined,
|
|
63
|
-
dependencyWhitelist: r.Undefined,
|
|
64
|
-
dependencyBlacklist: r.Undefined,
|
|
65
|
-
}),
|
|
66
|
-
),
|
|
67
|
-
r
|
|
68
|
-
.Record({
|
|
69
|
-
skipUnparseableRanges: r.Boolean,
|
|
70
|
-
dependencyWhitelist: r.Array(r.String),
|
|
71
|
-
})
|
|
72
|
-
.And(
|
|
73
|
-
r.Partial({
|
|
74
|
-
dependencyBlacklist: r.Undefined,
|
|
75
|
-
enforceForDevDependencies: r.Undefined,
|
|
76
|
-
}),
|
|
77
|
-
),
|
|
78
|
-
r
|
|
79
|
-
.Record({
|
|
80
|
-
skipUnparseableRanges: r.Boolean,
|
|
81
|
-
dependencyBlacklist: r.Array(r.String),
|
|
82
|
-
})
|
|
83
|
-
.And(
|
|
84
|
-
r.Partial({
|
|
85
|
-
dependencyWhitelist: r.Undefined,
|
|
86
|
-
enforceForDevDependencies: r.Undefined,
|
|
87
|
-
}),
|
|
88
|
-
),
|
|
89
|
-
r
|
|
90
|
-
.Record({
|
|
91
|
-
skipUnparseableRanges: r.Boolean,
|
|
92
|
-
enforceForDevDependencies: r.Boolean,
|
|
93
|
-
})
|
|
94
|
-
.And(
|
|
95
|
-
r.Partial({
|
|
96
|
-
dependencyWhitelist: r.Undefined,
|
|
97
|
-
dependencyBlacklist: r.Undefined,
|
|
98
|
-
}),
|
|
99
|
-
),
|
|
100
|
-
r
|
|
101
|
-
.Record({
|
|
102
|
-
dependencyWhitelist: r.Array(r.String),
|
|
103
|
-
dependencyBlacklist: r.Array(r.String),
|
|
104
|
-
})
|
|
105
|
-
.And(
|
|
106
|
-
r.Partial({
|
|
107
|
-
skipUnparseableRanges: r.Undefined,
|
|
108
|
-
enforceForDevDependencies: r.Undefined,
|
|
109
|
-
}),
|
|
110
|
-
),
|
|
111
|
-
r
|
|
112
|
-
.Record({
|
|
113
|
-
dependencyWhitelist: r.Array(r.String),
|
|
114
|
-
enforceForDevDependencies: r.Boolean,
|
|
115
|
-
})
|
|
116
|
-
.And(
|
|
117
|
-
r.Partial({
|
|
118
|
-
skipUnparseableRanges: r.Undefined,
|
|
119
|
-
dependencyBlacklist: r.Undefined,
|
|
120
|
-
}),
|
|
121
|
-
),
|
|
122
|
-
r
|
|
123
|
-
.Record({
|
|
124
|
-
dependencyBlacklist: r.Array(r.String),
|
|
125
|
-
enforceForDevDependencies: r.Boolean,
|
|
126
|
-
})
|
|
127
|
-
.And(
|
|
128
|
-
r.Partial({
|
|
129
|
-
skipUnparseableRanges: r.Undefined,
|
|
130
|
-
dependencyWhitelist: r.Undefined,
|
|
131
|
-
}),
|
|
132
|
-
),
|
|
133
|
-
r
|
|
134
|
-
.Record({
|
|
135
|
-
skipUnparseableRanges: r.Boolean,
|
|
136
|
-
dependencyWhitelist: r.Array(r.String),
|
|
137
|
-
dependencyBlacklist: r.Array(r.String),
|
|
138
|
-
})
|
|
139
|
-
.And(
|
|
140
|
-
r.Partial({
|
|
141
|
-
enforceForDevDependencies: r.Undefined,
|
|
142
|
-
}),
|
|
143
|
-
),
|
|
144
|
-
r
|
|
145
|
-
.Record({
|
|
146
|
-
skipUnparseableRanges: r.Boolean,
|
|
147
|
-
dependencyWhitelist: r.Array(r.String),
|
|
148
|
-
enforceForDevDependencies: r.Boolean,
|
|
149
|
-
})
|
|
150
|
-
.And(
|
|
151
|
-
r.Partial({
|
|
152
|
-
dependencyBlacklist: r.Undefined,
|
|
153
|
-
}),
|
|
154
|
-
),
|
|
155
|
-
r
|
|
156
|
-
.Record({
|
|
157
|
-
skipUnparseableRanges: r.Boolean,
|
|
158
|
-
dependencyBlacklist: r.Array(r.String),
|
|
159
|
-
enforceForDevDependencies: r.Boolean,
|
|
160
|
-
})
|
|
161
|
-
.And(
|
|
162
|
-
r.Partial({
|
|
163
|
-
dependencyWhitelist: r.Undefined,
|
|
164
|
-
}),
|
|
165
|
-
),
|
|
166
|
-
r
|
|
167
|
-
.Record({
|
|
168
|
-
dependencyWhitelist: r.Array(r.String),
|
|
169
|
-
dependencyBlacklist: r.Array(r.String),
|
|
170
|
-
enforceForDevDependencies: r.Boolean,
|
|
171
|
-
})
|
|
172
|
-
.And(
|
|
173
|
-
r.Partial({
|
|
174
|
-
skipUnparseableRanges: r.Undefined,
|
|
175
|
-
}),
|
|
176
|
-
),
|
|
177
|
-
r.Record({
|
|
178
|
-
skipUnparseableRanges: r.Boolean,
|
|
179
|
-
dependencyWhitelist: r.Array(r.String),
|
|
180
|
-
dependencyBlacklist: r.Array(r.String),
|
|
181
|
-
enforceForDevDependencies: r.Boolean,
|
|
182
|
-
}),
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
export type Options = r.Static<typeof Options>;
|
|
16
|
+
const Options = z.object({
|
|
17
|
+
skipUnparseableRanges: z.boolean().optional(),
|
|
18
|
+
dependencyWhitelist: z.array(z.string()).optional(),
|
|
19
|
+
dependencyBlacklist: z.array(z.string()).optional(),
|
|
20
|
+
enforceForDevDependencies: z.boolean().optional(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export type Options = z.infer<typeof Options>;
|
|
186
24
|
|
|
187
25
|
export const mustSatisfyPeerDependencies = createRuleFactory({
|
|
188
26
|
name: "mustSatisfyPeerDependencies",
|
|
189
27
|
check: checkSatisfyPeerDependencies,
|
|
190
|
-
validateOptions: Options.
|
|
28
|
+
validateOptions: Options.parse,
|
|
191
29
|
});
|
|
192
30
|
|
|
193
31
|
/**
|
package/src/nestedWorkspaces.ts
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
|
|
8
8
|
import * as globby from "globby";
|
|
9
9
|
import * as path from "node:path";
|
|
10
|
-
import
|
|
10
|
+
import { z } from "zod";
|
|
11
11
|
import { createRuleFactory } from "./util/createRuleFactory.js";
|
|
12
12
|
|
|
13
|
-
export const Options =
|
|
13
|
+
export const Options = z.undefined();
|
|
14
14
|
|
|
15
|
-
type Options =
|
|
15
|
+
type Options = z.infer<typeof Options>;
|
|
16
16
|
|
|
17
17
|
// Enforce that the root package.json contains all of the workspaces in the repo (including nested packages)
|
|
18
18
|
export const nestedWorkspaces = createRuleFactory({
|
|
@@ -65,5 +65,5 @@ export const nestedWorkspaces = createRuleFactory({
|
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
|
-
validateOptions: Options.
|
|
68
|
+
validateOptions: Options.parse,
|
|
69
69
|
});
|