@monorepolint/rules 0.6.0-alpha.2 → 0.6.0-alpha.4
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 +161 -102
- package/.turbo/turbo-transpile-typescript.log +4 -4
- package/CHANGELOG.md +19 -0
- package/build/js/index.js +429 -241
- package/build/js/index.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/__tests__/utils.d.ts +0 -1
- package/build/types/__tests__/utils.d.ts.map +1 -1
- package/build/types/bannedDependencies.d.ts.map +1 -1
- package/build/types/fileContents.d.ts.map +1 -1
- package/build/types/forceError.d.ts +4 -0
- package/build/types/forceError.d.ts.map +1 -0
- package/build/types/index.d.ts +5 -3
- package/build/types/index.d.ts.map +1 -1
- package/build/types/mustSatisfyPeerDependencies.d.ts.map +1 -1
- package/build/types/nestedWorkspaces.d.ts.map +1 -1
- package/build/types/oncePerPackage.d.ts +11 -0
- package/build/types/oncePerPackage.d.ts.map +1 -0
- package/build/types/packageEntry.d.ts.map +1 -1
- package/build/types/packageOrder.d.ts.map +1 -1
- package/build/types/packageScript.d.ts.map +1 -1
- package/build/types/requireDependency.d.ts +12 -12
- package/build/types/requireDependency.d.ts.map +1 -1
- package/build/types/util/checkAlpha.d.ts.map +1 -1
- package/build/types/util/createRuleFactory.d.ts.map +1 -1
- package/build/types/util/packageDependencyGraphService.d.ts.map +1 -1
- package/coverage/clover.xml +1201 -813
- package/coverage/coverage-final.json +20 -18
- package/coverage/index.html +20 -20
- package/coverage/src/alphabeticalDependencies.ts.html +11 -11
- package/coverage/src/alphabeticalScripts.ts.html +5 -5
- package/coverage/src/bannedDependencies.ts.html +76 -28
- package/coverage/src/consistentDependencies.ts.html +51 -18
- package/coverage/src/consistentVersions.ts.html +144 -48
- package/coverage/src/fileContents.ts.html +47 -23
- package/coverage/src/forceError.ts.html +184 -0
- package/coverage/src/index.html +96 -66
- package/coverage/src/index.ts.html +39 -33
- package/coverage/src/mustSatisfyPeerDependencies.ts.html +323 -80
- package/coverage/src/nestedWorkspaces.ts.html +47 -20
- package/coverage/src/oncePerPackage.ts.html +181 -0
- package/coverage/src/packageEntry.ts.html +40 -19
- package/coverage/src/packageOrder.ts.html +30 -12
- package/coverage/src/packageScript.ts.html +81 -27
- package/coverage/src/requireDependency.ts.html +77 -32
- package/coverage/src/standardTsconfig.ts.html +84 -21
- package/coverage/src/util/checkAlpha.ts.html +18 -9
- package/coverage/src/util/createRuleFactory.ts.html +16 -4
- package/coverage/src/util/index.html +17 -17
- package/coverage/src/util/makeDirectory.ts.html +5 -5
- package/coverage/src/util/packageDependencyGraphService.ts.html +94 -19
- package/package.json +4 -5
- package/src/__tests__/alphabeticalScripts.spec.ts +12 -4
- package/src/__tests__/bannedDependencies.spec.ts +45 -16
- package/src/__tests__/consistentDependencies.spec.ts +11 -5
- package/src/__tests__/consistentVersions.spec.ts +72 -18
- package/src/__tests__/fileContents.spec.ts +5 -5
- package/src/__tests__/mustSatisfyPeerDependencies.spec.ts +191 -76
- package/src/__tests__/nestedWorkspaces.spec.ts +10 -7
- package/src/__tests__/packageEntry.spec.ts +48 -47
- package/src/__tests__/packageOrder.spec.ts +72 -71
- package/src/__tests__/packageScript.spec.ts +19 -10
- package/src/__tests__/requireDependency.spec.ts +12 -6
- package/src/__tests__/utils.ts +16 -7
- package/src/bannedDependencies.ts +32 -16
- package/src/consistentDependencies.ts +19 -8
- package/src/consistentVersions.ts +70 -38
- package/src/fileContents.ts +19 -11
- package/src/forceError.ts +33 -0
- package/src/index.ts +5 -3
- package/src/mustSatisfyPeerDependencies.ts +141 -60
- package/src/nestedWorkspaces.ts +19 -10
- package/src/oncePerPackage.ts +32 -0
- package/src/packageEntry.ts +18 -11
- package/src/packageOrder.ts +9 -3
- package/src/packageScript.ts +37 -19
- package/src/requireDependency.ts +26 -11
- package/src/standardTsconfig.ts +31 -10
- package/src/util/checkAlpha.ts +5 -2
- package/src/util/createRuleFactory.ts +6 -2
- package/src/util/packageDependencyGraphService.ts +38 -13
- package/vitest.config.mjs +6 -7
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
import { Context } from "@monorepolint/config";
|
|
9
9
|
import { Host, mutateJson, PackageJson } from "@monorepolint/utils";
|
|
10
10
|
import * as path from "node:path";
|
|
11
|
+
import resolvePackagePath from "resolve-package-path";
|
|
11
12
|
import * as r from "runtypes";
|
|
12
13
|
import { coerce } from "semver";
|
|
13
|
-
import resolvePackagePath from "resolve-package-path";
|
|
14
14
|
import { createRuleFactory } from "./util/createRuleFactory.js";
|
|
15
15
|
|
|
16
16
|
const Options = r.Union(
|
|
@@ -29,7 +29,7 @@ const Options = r.Union(
|
|
|
29
29
|
dependencyWhitelist: r.Undefined,
|
|
30
30
|
dependencyBlacklist: r.Undefined,
|
|
31
31
|
enforceForDevDependencies: r.Undefined,
|
|
32
|
-
})
|
|
32
|
+
}),
|
|
33
33
|
),
|
|
34
34
|
r
|
|
35
35
|
.Record({
|
|
@@ -40,7 +40,7 @@ const Options = r.Union(
|
|
|
40
40
|
skipUnparseableRanges: r.Undefined,
|
|
41
41
|
dependencyBlacklist: r.Undefined,
|
|
42
42
|
enforceForDevDependencies: r.Undefined,
|
|
43
|
-
})
|
|
43
|
+
}),
|
|
44
44
|
),
|
|
45
45
|
r
|
|
46
46
|
.Record({
|
|
@@ -51,7 +51,7 @@ const Options = r.Union(
|
|
|
51
51
|
skipUnparseableRanges: r.Undefined,
|
|
52
52
|
dependencyWhitelist: r.Undefined,
|
|
53
53
|
enforceForDevDependencies: r.Undefined,
|
|
54
|
-
})
|
|
54
|
+
}),
|
|
55
55
|
),
|
|
56
56
|
r
|
|
57
57
|
.Record({
|
|
@@ -62,7 +62,7 @@ const Options = r.Union(
|
|
|
62
62
|
skipUnparseableRanges: r.Undefined,
|
|
63
63
|
dependencyWhitelist: r.Undefined,
|
|
64
64
|
dependencyBlacklist: r.Undefined,
|
|
65
|
-
})
|
|
65
|
+
}),
|
|
66
66
|
),
|
|
67
67
|
r
|
|
68
68
|
.Record({
|
|
@@ -73,7 +73,7 @@ const Options = r.Union(
|
|
|
73
73
|
r.Partial({
|
|
74
74
|
dependencyBlacklist: r.Undefined,
|
|
75
75
|
enforceForDevDependencies: r.Undefined,
|
|
76
|
-
})
|
|
76
|
+
}),
|
|
77
77
|
),
|
|
78
78
|
r
|
|
79
79
|
.Record({
|
|
@@ -84,7 +84,7 @@ const Options = r.Union(
|
|
|
84
84
|
r.Partial({
|
|
85
85
|
dependencyWhitelist: r.Undefined,
|
|
86
86
|
enforceForDevDependencies: r.Undefined,
|
|
87
|
-
})
|
|
87
|
+
}),
|
|
88
88
|
),
|
|
89
89
|
r
|
|
90
90
|
.Record({
|
|
@@ -95,7 +95,7 @@ const Options = r.Union(
|
|
|
95
95
|
r.Partial({
|
|
96
96
|
dependencyWhitelist: r.Undefined,
|
|
97
97
|
dependencyBlacklist: r.Undefined,
|
|
98
|
-
})
|
|
98
|
+
}),
|
|
99
99
|
),
|
|
100
100
|
r
|
|
101
101
|
.Record({
|
|
@@ -106,7 +106,7 @@ const Options = r.Union(
|
|
|
106
106
|
r.Partial({
|
|
107
107
|
skipUnparseableRanges: r.Undefined,
|
|
108
108
|
enforceForDevDependencies: r.Undefined,
|
|
109
|
-
})
|
|
109
|
+
}),
|
|
110
110
|
),
|
|
111
111
|
r
|
|
112
112
|
.Record({
|
|
@@ -117,7 +117,7 @@ const Options = r.Union(
|
|
|
117
117
|
r.Partial({
|
|
118
118
|
skipUnparseableRanges: r.Undefined,
|
|
119
119
|
dependencyBlacklist: r.Undefined,
|
|
120
|
-
})
|
|
120
|
+
}),
|
|
121
121
|
),
|
|
122
122
|
r
|
|
123
123
|
.Record({
|
|
@@ -128,7 +128,7 @@ const Options = r.Union(
|
|
|
128
128
|
r.Partial({
|
|
129
129
|
skipUnparseableRanges: r.Undefined,
|
|
130
130
|
dependencyWhitelist: r.Undefined,
|
|
131
|
-
})
|
|
131
|
+
}),
|
|
132
132
|
),
|
|
133
133
|
r
|
|
134
134
|
.Record({
|
|
@@ -139,7 +139,7 @@ const Options = r.Union(
|
|
|
139
139
|
.And(
|
|
140
140
|
r.Partial({
|
|
141
141
|
enforceForDevDependencies: r.Undefined,
|
|
142
|
-
})
|
|
142
|
+
}),
|
|
143
143
|
),
|
|
144
144
|
r
|
|
145
145
|
.Record({
|
|
@@ -150,7 +150,7 @@ const Options = r.Union(
|
|
|
150
150
|
.And(
|
|
151
151
|
r.Partial({
|
|
152
152
|
dependencyBlacklist: r.Undefined,
|
|
153
|
-
})
|
|
153
|
+
}),
|
|
154
154
|
),
|
|
155
155
|
r
|
|
156
156
|
.Record({
|
|
@@ -161,7 +161,7 @@ const Options = r.Union(
|
|
|
161
161
|
.And(
|
|
162
162
|
r.Partial({
|
|
163
163
|
dependencyWhitelist: r.Undefined,
|
|
164
|
-
})
|
|
164
|
+
}),
|
|
165
165
|
),
|
|
166
166
|
r
|
|
167
167
|
.Record({
|
|
@@ -172,14 +172,14 @@ const Options = r.Union(
|
|
|
172
172
|
.And(
|
|
173
173
|
r.Partial({
|
|
174
174
|
skipUnparseableRanges: r.Undefined,
|
|
175
|
-
})
|
|
175
|
+
}),
|
|
176
176
|
),
|
|
177
177
|
r.Record({
|
|
178
178
|
skipUnparseableRanges: r.Boolean,
|
|
179
179
|
dependencyWhitelist: r.Array(r.String),
|
|
180
180
|
dependencyBlacklist: r.Array(r.String),
|
|
181
181
|
enforceForDevDependencies: r.Boolean,
|
|
182
|
-
})
|
|
182
|
+
}),
|
|
183
183
|
);
|
|
184
184
|
|
|
185
185
|
export type Options = r.Static<typeof Options>;
|
|
@@ -270,7 +270,12 @@ interface IResolvedPeerDependencyRequirement {
|
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
function checkSatisfyPeerDependencies(context: Context, opts: Options) {
|
|
273
|
-
const {
|
|
273
|
+
const {
|
|
274
|
+
dependencyBlacklist,
|
|
275
|
+
dependencyWhitelist,
|
|
276
|
+
enforceForDevDependencies,
|
|
277
|
+
skipUnparseableRanges,
|
|
278
|
+
} = opts;
|
|
274
279
|
const packageJsonPath = path.resolve(context.getPackageJsonPath());
|
|
275
280
|
const packageJson = context.host.readJson(packageJsonPath) as PackageJson;
|
|
276
281
|
const packageDependencies = packageJson.dependencies || {};
|
|
@@ -279,8 +284,18 @@ function checkSatisfyPeerDependencies(context: Context, opts: Options) {
|
|
|
279
284
|
const packageName = packageJson.name || packageJsonPath;
|
|
280
285
|
|
|
281
286
|
// check that no peer dependencies are also declared as regular dependencies
|
|
282
|
-
for (
|
|
283
|
-
|
|
287
|
+
for (
|
|
288
|
+
const [peerDependencyName, peerDependencyRange] of Object.entries(
|
|
289
|
+
packagePeerDependencies,
|
|
290
|
+
)
|
|
291
|
+
) {
|
|
292
|
+
if (
|
|
293
|
+
shouldSkipPackage({
|
|
294
|
+
dependencyBlacklist,
|
|
295
|
+
dependencyWhitelist,
|
|
296
|
+
packageName: peerDependencyName,
|
|
297
|
+
})
|
|
298
|
+
) {
|
|
284
299
|
continue;
|
|
285
300
|
}
|
|
286
301
|
|
|
@@ -288,37 +303,59 @@ function checkSatisfyPeerDependencies(context: Context, opts: Options) {
|
|
|
288
303
|
if (dependencyRange != null) {
|
|
289
304
|
context.addError({
|
|
290
305
|
file: packageJsonPath,
|
|
291
|
-
message:
|
|
292
|
-
`
|
|
293
|
-
`Peer dependency '${peerDependencyRange}' and regular dependency '${dependencyRange}'.`,
|
|
306
|
+
message: `[0] Package ${packageName} has overloaded ${peerDependencyName} dependencies.\n\t`
|
|
307
|
+
+ `Peer dependency '${peerDependencyRange}' and regular dependency '${dependencyRange}'.`,
|
|
294
308
|
});
|
|
295
309
|
}
|
|
296
310
|
}
|
|
297
311
|
|
|
298
312
|
// map of all inherited peer dependency requirements
|
|
299
|
-
const allRequiredPeerDependencies: {
|
|
313
|
+
const allRequiredPeerDependencies: {
|
|
314
|
+
[peerDependencyName: string]: IPeerDependencyRequirement[];
|
|
315
|
+
} = {};
|
|
300
316
|
|
|
301
317
|
// for each of this package's dependencies, add the dependency's peer requirements into `allRequiredPeerDependencies`
|
|
302
318
|
const allDependencies = enforceForDevDependencies
|
|
303
|
-
? [
|
|
319
|
+
? [
|
|
320
|
+
...Object.keys(packageDependencies),
|
|
321
|
+
...Object.keys(packageDevDependencies),
|
|
322
|
+
]
|
|
304
323
|
: Object.keys(packageDependencies);
|
|
305
324
|
for (const dependency of allDependencies) {
|
|
306
|
-
const dependencyPackageJsonPath = resolvePackagePath(
|
|
325
|
+
const dependencyPackageJsonPath = resolvePackagePath(
|
|
326
|
+
dependency,
|
|
327
|
+
path.dirname(packageJsonPath),
|
|
328
|
+
);
|
|
307
329
|
if (dependencyPackageJsonPath == null) {
|
|
308
|
-
throw new Error(
|
|
330
|
+
throw new Error(
|
|
331
|
+
`Could not resolve ${dependency} from ${path.dirname(packageJsonPath)}`,
|
|
332
|
+
);
|
|
309
333
|
}
|
|
310
|
-
const dependencyPackageJson = context.host.readJson(
|
|
334
|
+
const dependencyPackageJson = context.host.readJson(
|
|
335
|
+
dependencyPackageJsonPath,
|
|
336
|
+
) as PackageJson;
|
|
311
337
|
const requiredPeerDependencies = dependencyPackageJson.peerDependencies;
|
|
312
338
|
if (requiredPeerDependencies == null) {
|
|
313
339
|
continue;
|
|
314
340
|
}
|
|
315
|
-
for (
|
|
316
|
-
|
|
341
|
+
for (
|
|
342
|
+
const [peerDependencyName, range] of Object.entries(
|
|
343
|
+
requiredPeerDependencies,
|
|
344
|
+
)
|
|
345
|
+
) {
|
|
346
|
+
if (
|
|
347
|
+
shouldSkipPackage({
|
|
348
|
+
dependencyBlacklist,
|
|
349
|
+
dependencyWhitelist,
|
|
350
|
+
packageName: peerDependencyName,
|
|
351
|
+
})
|
|
352
|
+
) {
|
|
317
353
|
continue;
|
|
318
354
|
}
|
|
319
355
|
|
|
320
356
|
if (!isValidRange(range)) {
|
|
321
|
-
const message =
|
|
357
|
+
const message =
|
|
358
|
+
`Unable to parse ${dependencyPackageJson.name}'s ${peerDependencyName} peer dependency range '${range}'.`;
|
|
322
359
|
if (skipUnparseableRanges) {
|
|
323
360
|
context.addWarning({ file: dependencyPackageJsonPath, message });
|
|
324
361
|
continue;
|
|
@@ -328,26 +365,40 @@ function checkSatisfyPeerDependencies(context: Context, opts: Options) {
|
|
|
328
365
|
if (allRequiredPeerDependencies[peerDependencyName] == null) {
|
|
329
366
|
allRequiredPeerDependencies[peerDependencyName] = [];
|
|
330
367
|
}
|
|
331
|
-
allRequiredPeerDependencies[peerDependencyName].push({
|
|
368
|
+
allRequiredPeerDependencies[peerDependencyName].push({
|
|
369
|
+
fromPackageName: dependencyPackageJson.name!,
|
|
370
|
+
range,
|
|
371
|
+
});
|
|
332
372
|
}
|
|
333
373
|
}
|
|
334
374
|
|
|
335
|
-
for (
|
|
375
|
+
for (
|
|
376
|
+
const [peerDependencyName, peerDependencyRequirements] of Object.entries(
|
|
377
|
+
allRequiredPeerDependencies,
|
|
378
|
+
)
|
|
379
|
+
) {
|
|
336
380
|
// for each inherited peer dependency, determine the strictest range
|
|
337
381
|
let mostStrictPeerRequirement: IResolvedPeerDependencyRequirement = {
|
|
338
382
|
fromPeerDependencyRequirements: [peerDependencyRequirements[0]],
|
|
339
383
|
range: peerDependencyRequirements[0].range,
|
|
340
384
|
};
|
|
341
385
|
for (const peerRequirement of peerDependencyRequirements) {
|
|
342
|
-
if (
|
|
386
|
+
if (
|
|
387
|
+
doesASatisfyB(mostStrictPeerRequirement.range, peerRequirement.range)
|
|
388
|
+
) {
|
|
343
389
|
continue;
|
|
344
|
-
} else if (
|
|
390
|
+
} else if (
|
|
391
|
+
doesASatisfyB(peerRequirement.range, mostStrictPeerRequirement.range)
|
|
392
|
+
) {
|
|
345
393
|
mostStrictPeerRequirement = {
|
|
346
394
|
fromPeerDependencyRequirements: [peerRequirement],
|
|
347
395
|
range: peerRequirement.range,
|
|
348
396
|
};
|
|
349
397
|
} else {
|
|
350
|
-
const maybeIntersection = findIntersection(
|
|
398
|
+
const maybeIntersection = findIntersection(
|
|
399
|
+
peerRequirement.range,
|
|
400
|
+
mostStrictPeerRequirement.range,
|
|
401
|
+
);
|
|
351
402
|
if (maybeIntersection !== undefined) {
|
|
352
403
|
mostStrictPeerRequirement = {
|
|
353
404
|
fromPeerDependencyRequirements: [
|
|
@@ -360,9 +411,9 @@ function checkSatisfyPeerDependencies(context: Context, opts: Options) {
|
|
|
360
411
|
context.addError({
|
|
361
412
|
file: packageJsonPath,
|
|
362
413
|
message:
|
|
363
|
-
`[1] Package ${packageName} has conflicting inherited ${peerDependencyName} peer dependencies.\n\t`
|
|
364
|
-
`Dependency ${peerRequirement.fromPackageName} requires '${peerRequirement.range}' but\n\t`
|
|
365
|
-
getMostStrictStatement(mostStrictPeerRequirement),
|
|
414
|
+
`[1] Package ${packageName} has conflicting inherited ${peerDependencyName} peer dependencies.\n\t`
|
|
415
|
+
+ `Dependency ${peerRequirement.fromPackageName} requires '${peerRequirement.range}' but\n\t`
|
|
416
|
+
+ getMostStrictStatement(mostStrictPeerRequirement),
|
|
366
417
|
});
|
|
367
418
|
}
|
|
368
419
|
}
|
|
@@ -373,18 +424,21 @@ function checkSatisfyPeerDependencies(context: Context, opts: Options) {
|
|
|
373
424
|
const packageDependencyRange = packageDependencies[peerDependencyName];
|
|
374
425
|
if (packageDependencyRange != null) {
|
|
375
426
|
if (!isValidRange(packageDependencyRange)) {
|
|
376
|
-
const message =
|
|
427
|
+
const message =
|
|
428
|
+
`Unable to parse ${packageName}'s ${peerDependencyName} dependency range '${packageDependencyRange}'.`;
|
|
377
429
|
if (skipUnparseableRanges) {
|
|
378
430
|
context.addWarning({ file: packageJsonPath, message });
|
|
379
431
|
} else {
|
|
380
432
|
throw new Error(message);
|
|
381
433
|
}
|
|
382
|
-
} else if (
|
|
434
|
+
} else if (
|
|
435
|
+
!doesASatisfyB(packageDependencyRange, mostStrictPeerRequirement.range)
|
|
436
|
+
) {
|
|
383
437
|
context.addError({
|
|
384
438
|
file: packageJsonPath,
|
|
385
439
|
message:
|
|
386
|
-
`[2] Package ${packageName} dependency on ${peerDependencyName} '${packageDependencyRange}' does not satisfy inherited peer dependencies.\n\t`
|
|
387
|
-
getMostStrictStatement(mostStrictPeerRequirement),
|
|
440
|
+
`[2] Package ${packageName} dependency on ${peerDependencyName} '${packageDependencyRange}' does not satisfy inherited peer dependencies.\n\t`
|
|
441
|
+
+ getMostStrictStatement(mostStrictPeerRequirement),
|
|
388
442
|
});
|
|
389
443
|
}
|
|
390
444
|
}
|
|
@@ -396,8 +450,8 @@ function checkSatisfyPeerDependencies(context: Context, opts: Options) {
|
|
|
396
450
|
context.addError({
|
|
397
451
|
file: packageJsonPath,
|
|
398
452
|
message:
|
|
399
|
-
`[3] Package ${packageName} is missing required ${peerDependencyName} dependency.\n\t`
|
|
400
|
-
getMostStrictStatement(mostStrictPeerRequirement),
|
|
453
|
+
`[3] Package ${packageName} is missing required ${peerDependencyName} dependency.\n\t`
|
|
454
|
+
+ getMostStrictStatement(mostStrictPeerRequirement),
|
|
401
455
|
fixer: getAddDependencyTypeFixer({
|
|
402
456
|
packageJsonPath,
|
|
403
457
|
dependencyType: "peerDependencies",
|
|
@@ -412,18 +466,24 @@ function checkSatisfyPeerDependencies(context: Context, opts: Options) {
|
|
|
412
466
|
// the range must be equal to or stricter than `mostStrictPeerRequirement`
|
|
413
467
|
if (packagePeerDependencyRange != null) {
|
|
414
468
|
if (!isValidRange(packagePeerDependencyRange)) {
|
|
415
|
-
const message =
|
|
469
|
+
const message =
|
|
470
|
+
`Unable to parse ${packageName}'s ${peerDependencyName} peer dependency range '${packagePeerDependencyRange}'.`;
|
|
416
471
|
if (skipUnparseableRanges) {
|
|
417
472
|
context.addWarning({ file: packageJsonPath, message });
|
|
418
473
|
} else {
|
|
419
474
|
throw new Error(message);
|
|
420
475
|
}
|
|
421
|
-
} else if (
|
|
476
|
+
} else if (
|
|
477
|
+
!doesASatisfyB(
|
|
478
|
+
packagePeerDependencyRange,
|
|
479
|
+
mostStrictPeerRequirement.range,
|
|
480
|
+
)
|
|
481
|
+
) {
|
|
422
482
|
context.addError({
|
|
423
483
|
file: packageJsonPath,
|
|
424
484
|
message:
|
|
425
|
-
`[4] Package ${packageName} peer dependency on ${peerDependencyName} '${packagePeerDependencyRange}' is not strict enough.\n\t`
|
|
426
|
-
getMostStrictStatement(mostStrictPeerRequirement),
|
|
485
|
+
`[4] Package ${packageName} peer dependency on ${peerDependencyName} '${packagePeerDependencyRange}' is not strict enough.\n\t`
|
|
486
|
+
+ getMostStrictStatement(mostStrictPeerRequirement),
|
|
427
487
|
fixer: getAddDependencyTypeFixer({
|
|
428
488
|
packageJsonPath,
|
|
429
489
|
dependencyType: "peerDependencies",
|
|
@@ -448,28 +508,34 @@ function shouldSkipPackage({
|
|
|
448
508
|
}) {
|
|
449
509
|
// blacklist should take precedance
|
|
450
510
|
if (
|
|
451
|
-
(dependencyBlacklist != null && dependencyBlacklist.includes(packageName))
|
|
452
|
-
(dependencyWhitelist != null
|
|
511
|
+
(dependencyBlacklist != null && dependencyBlacklist.includes(packageName))
|
|
512
|
+
|| (dependencyWhitelist != null
|
|
513
|
+
&& !dependencyWhitelist.includes(packageName))
|
|
453
514
|
) {
|
|
454
515
|
return true;
|
|
455
516
|
}
|
|
456
517
|
return false;
|
|
457
518
|
}
|
|
458
519
|
|
|
459
|
-
function getMostStrictStatement(
|
|
520
|
+
function getMostStrictStatement(
|
|
521
|
+
mostStrictPeerRequirement: IResolvedPeerDependencyRequirement,
|
|
522
|
+
) {
|
|
460
523
|
if (mostStrictPeerRequirement.fromPeerDependencyRequirements.length === 1) {
|
|
461
|
-
const dependencyName = mostStrictPeerRequirement.fromPeerDependencyRequirements[0]
|
|
524
|
+
const dependencyName = mostStrictPeerRequirement.fromPeerDependencyRequirements[0]
|
|
525
|
+
.fromPackageName;
|
|
462
526
|
return `Dependency ${dependencyName} requires '${mostStrictPeerRequirement.range}'.`;
|
|
463
527
|
} else {
|
|
464
|
-
const dependencyNames = mostStrictPeerRequirement
|
|
528
|
+
const dependencyNames = mostStrictPeerRequirement
|
|
529
|
+
.fromPeerDependencyRequirements
|
|
465
530
|
.map((peerDependencyRequirement) => peerDependencyRequirement.fromPackageName)
|
|
466
531
|
.join(", ");
|
|
467
|
-
const dependencyRequirements = mostStrictPeerRequirement
|
|
532
|
+
const dependencyRequirements = mostStrictPeerRequirement
|
|
533
|
+
.fromPeerDependencyRequirements
|
|
468
534
|
.map((peerDependencyRequirement) => `'${peerDependencyRequirement.range}'`)
|
|
469
535
|
.join(", ");
|
|
470
536
|
return (
|
|
471
|
-
`Dependencies [${dependencyNames}] require [${dependencyRequirements}] respectively, `
|
|
472
|
-
`resolving to '${mostStrictPeerRequirement.range}'.`
|
|
537
|
+
`Dependencies [${dependencyNames}] require [${dependencyRequirements}] respectively, `
|
|
538
|
+
+ `resolving to '${mostStrictPeerRequirement.range}'.`
|
|
473
539
|
);
|
|
474
540
|
}
|
|
475
541
|
}
|
|
@@ -503,7 +569,10 @@ function getMostStrictStatement(mostStrictPeerRequirement: IResolvedPeerDependen
|
|
|
503
569
|
* @param b version range that matches `RANGE_REGEX`
|
|
504
570
|
* @returns the maximum intersecting `ValidRange`, or `undefined` if there is no intersection
|
|
505
571
|
*/
|
|
506
|
-
export function findIntersection(
|
|
572
|
+
export function findIntersection(
|
|
573
|
+
a: ValidRange,
|
|
574
|
+
b: ValidRange,
|
|
575
|
+
): ValidRange | undefined {
|
|
507
576
|
if (doesASatisfyB(a, b)) {
|
|
508
577
|
return a;
|
|
509
578
|
} else if (doesASatisfyB(b, a)) {
|
|
@@ -574,9 +643,19 @@ export function findIntersection(a: ValidRange, b: ValidRange): ValidRange | und
|
|
|
574
643
|
// there is no intersecting `b` major version for this `a` major version
|
|
575
644
|
return undefined;
|
|
576
645
|
}
|
|
577
|
-
if (
|
|
646
|
+
if (
|
|
647
|
+
doesASatisfyB(
|
|
648
|
+
aVersion as ValidRange,
|
|
649
|
+
majorMatchingBVersion as ValidRange,
|
|
650
|
+
)
|
|
651
|
+
) {
|
|
578
652
|
return aVersion;
|
|
579
|
-
} else if (
|
|
653
|
+
} else if (
|
|
654
|
+
doesASatisfyB(
|
|
655
|
+
majorMatchingBVersion as ValidRange,
|
|
656
|
+
aVersion as ValidRange,
|
|
657
|
+
)
|
|
658
|
+
) {
|
|
580
659
|
return majorMatchingBVersion;
|
|
581
660
|
} else {
|
|
582
661
|
return undefined;
|
|
@@ -673,7 +752,9 @@ export function doesASatisfyB(a: ValidRange, b: ValidRange): boolean {
|
|
|
673
752
|
|
|
674
753
|
const aVersionIsRange = isMajorVersionRange(aVersion);
|
|
675
754
|
const majorMatchingBSemVer = coerce(majorMatchingBVersion)!;
|
|
676
|
-
const majorMatchingBVersionIsRange = isMajorVersionRange(
|
|
755
|
+
const majorMatchingBVersionIsRange = isMajorVersionRange(
|
|
756
|
+
majorMatchingBVersion,
|
|
757
|
+
);
|
|
677
758
|
|
|
678
759
|
if (majorMatchingBVersionIsRange) {
|
|
679
760
|
// `a` satisfies `b` so long as `aSemVer` is greater than or equal to `majorMatchingBSemVer`
|
package/src/nestedWorkspaces.ts
CHANGED
|
@@ -21,18 +21,22 @@ export const nestedWorkspaces = createRuleFactory({
|
|
|
21
21
|
const rootPackageJson = context.getWorkspaceContext().getPackageJson();
|
|
22
22
|
|
|
23
23
|
// Expand a set of globs covering all package.json files in the entire repo (except the root)
|
|
24
|
-
const packageJsonPaths = globby.globbySync([
|
|
24
|
+
const packageJsonPaths = globby.globbySync([
|
|
25
|
+
"*/**/package.json",
|
|
26
|
+
"!**/node_modules/**",
|
|
27
|
+
]);
|
|
25
28
|
|
|
26
29
|
const workspaces = Array.isArray(rootPackageJson.workspaces)
|
|
27
30
|
? rootPackageJson.workspaces
|
|
28
31
|
: rootPackageJson.workspaces !== undefined
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
? rootPackageJson.workspaces.packages
|
|
33
|
+
: undefined;
|
|
31
34
|
|
|
32
35
|
if (workspaces === undefined && packageJsonPaths.length > 0) {
|
|
33
36
|
context.addError({
|
|
34
37
|
file: context.getPackageJsonPath(),
|
|
35
|
-
message:
|
|
38
|
+
message:
|
|
39
|
+
"The \"workspace\" field is missing, even though there are workspaces in the repository.",
|
|
36
40
|
});
|
|
37
41
|
return;
|
|
38
42
|
}
|
|
@@ -41,18 +45,23 @@ export const nestedWorkspaces = createRuleFactory({
|
|
|
41
45
|
const workspacePackageJsons = (workspaces || []).map((item) => `${item}/package.json`);
|
|
42
46
|
|
|
43
47
|
// Expand the globs to get an array of all package.json files that are in packages specified by a workspace.
|
|
44
|
-
const expandedWorkspacesGlobs = globby.globbySync([
|
|
48
|
+
const expandedWorkspacesGlobs = globby.globbySync([
|
|
49
|
+
...workspacePackageJsons,
|
|
50
|
+
"!**/node_modules/**",
|
|
51
|
+
]);
|
|
45
52
|
|
|
46
53
|
// Ensure there are no package.jsons which are not included in the globbed workspaces set
|
|
47
|
-
const difference = packageJsonPaths.filter((packageJsonPath) =>
|
|
54
|
+
const difference = packageJsonPaths.filter((packageJsonPath) =>
|
|
55
|
+
!expandedWorkspacesGlobs.includes(packageJsonPath)
|
|
56
|
+
);
|
|
48
57
|
|
|
49
58
|
if (difference.length !== 0) {
|
|
50
|
-
const differencesList = difference.map((packageJsonPath) => path.dirname(packageJsonPath))
|
|
59
|
+
const differencesList = difference.map((packageJsonPath) => path.dirname(packageJsonPath))
|
|
60
|
+
.join(", ");
|
|
51
61
|
context.addError({
|
|
52
62
|
file: context.getPackageJsonPath(),
|
|
53
|
-
message:
|
|
54
|
-
|
|
55
|
-
'You may be able to use a glob to avoid listing each workspace individually, e.g. "packages/nested-workspace/*".',
|
|
63
|
+
message: `The "workspace" field is missing one or more values: ${differencesList}. `
|
|
64
|
+
+ "You may be able to use a glob to avoid listing each workspace individually, e.g. \"packages/nested-workspace/*\".",
|
|
56
65
|
});
|
|
57
66
|
}
|
|
58
67
|
},
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as r from "runtypes";
|
|
2
|
+
import { createRuleFactory } from "./util/createRuleFactory.js";
|
|
3
|
+
|
|
4
|
+
export const Options = r.Record({
|
|
5
|
+
singletonKey: r.String.Or(r.Symbol),
|
|
6
|
+
customMessage: r.String.optional(),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export type Options = r.Static<typeof Options>;
|
|
10
|
+
|
|
11
|
+
const visitedMap = new Map<string | symbol, Set<string>>();
|
|
12
|
+
|
|
13
|
+
export const oncePerPackage = createRuleFactory<Options>({
|
|
14
|
+
name: "oncePerPackage",
|
|
15
|
+
|
|
16
|
+
check: async (context, options) => {
|
|
17
|
+
const visited = visitedMap.get(options.singletonKey) ?? new Set<string>();
|
|
18
|
+
visitedMap.set(options.singletonKey, visited);
|
|
19
|
+
|
|
20
|
+
if (visited.has(context.getName())) {
|
|
21
|
+
context.addError({
|
|
22
|
+
message: "This package has already been visited for this key: "
|
|
23
|
+
+ options.singletonKey.toString(),
|
|
24
|
+
file: context.getPackageJsonPath(),
|
|
25
|
+
});
|
|
26
|
+
} else {
|
|
27
|
+
visited.add(context.getName());
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
validateOptions: Options.assert,
|
|
32
|
+
});
|
package/src/packageEntry.ts
CHANGED
|
@@ -18,7 +18,7 @@ export const Options = r.Union(
|
|
|
18
18
|
.And(
|
|
19
19
|
r.Partial({
|
|
20
20
|
entriesExist: r.Undefined,
|
|
21
|
-
})
|
|
21
|
+
}),
|
|
22
22
|
),
|
|
23
23
|
r
|
|
24
24
|
.Record({
|
|
@@ -27,12 +27,12 @@ export const Options = r.Union(
|
|
|
27
27
|
.And(
|
|
28
28
|
r.Partial({
|
|
29
29
|
entries: r.Undefined,
|
|
30
|
-
})
|
|
30
|
+
}),
|
|
31
31
|
),
|
|
32
32
|
r.Record({
|
|
33
33
|
entries: r.Dictionary(r.Unknown), // string => unknown, enforces existence of keys and their values
|
|
34
34
|
entriesExist: r.Array(r.String),
|
|
35
|
-
})
|
|
35
|
+
}),
|
|
36
36
|
);
|
|
37
37
|
|
|
38
38
|
export type Options = r.Static<typeof Options>;
|
|
@@ -46,21 +46,28 @@ export const packageEntry = createRuleFactory<Options>({
|
|
|
46
46
|
for (const key of Object.keys(options.entries)) {
|
|
47
47
|
const value = options.entries[key];
|
|
48
48
|
|
|
49
|
-
const entryDiff = diff(
|
|
49
|
+
const entryDiff = diff(
|
|
50
|
+
JSON.stringify(value) + "\n",
|
|
51
|
+
(JSON.stringify(packageJson[key]) || "") + "\n",
|
|
52
|
+
);
|
|
50
53
|
if (
|
|
51
|
-
(typeof value !== "object" && value !== packageJson[key])
|
|
52
|
-
entryDiff == null
|
|
53
|
-
!entryDiff.includes("Compared values have no visual difference")
|
|
54
|
+
(typeof value !== "object" && value !== packageJson[key])
|
|
55
|
+
|| entryDiff == null
|
|
56
|
+
|| !entryDiff.includes("Compared values have no visual difference")
|
|
54
57
|
) {
|
|
55
58
|
context.addError({
|
|
56
59
|
file: context.getPackageJsonPath(),
|
|
57
60
|
message: createStandardizedEntryErrorMessage(key),
|
|
58
61
|
longMessage: entryDiff,
|
|
59
62
|
fixer: () => {
|
|
60
|
-
mutateJson<PackageJson>(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
mutateJson<PackageJson>(
|
|
64
|
+
context.getPackageJsonPath(),
|
|
65
|
+
context.host,
|
|
66
|
+
(input) => {
|
|
67
|
+
input[key] = value;
|
|
68
|
+
return input;
|
|
69
|
+
},
|
|
70
|
+
);
|
|
64
71
|
},
|
|
65
72
|
});
|
|
66
73
|
}
|
package/src/packageOrder.ts
CHANGED
|
@@ -57,9 +57,13 @@ export const packageOrder = createRuleFactory<Options>({
|
|
|
57
57
|
const packageJson = context.getPackageJson();
|
|
58
58
|
const packagePath = context.getPackageJsonPath();
|
|
59
59
|
|
|
60
|
-
const order: string[] | OrderFunction = opts === undefined
|
|
60
|
+
const order: string[] | OrderFunction = opts === undefined
|
|
61
|
+
? defaultKeyOrder
|
|
62
|
+
: opts.order;
|
|
61
63
|
|
|
62
|
-
const comparator = isOrderFunction(order)
|
|
64
|
+
const comparator = isOrderFunction(order)
|
|
65
|
+
? order(context)
|
|
66
|
+
: createComparator(order);
|
|
63
67
|
|
|
64
68
|
const actualOrder = Object.keys(packageJson);
|
|
65
69
|
const expectedOrder = actualOrder.slice().sort(comparator); // sort mutates, so we need to copy the previous result
|
|
@@ -118,6 +122,8 @@ function createComparator(order: ReadonlyArray<string>) {
|
|
|
118
122
|
};
|
|
119
123
|
}
|
|
120
124
|
|
|
121
|
-
function isOrderFunction(
|
|
125
|
+
function isOrderFunction(
|
|
126
|
+
order: ReadonlyArray<string> | OrderFunction,
|
|
127
|
+
): order is OrderFunction {
|
|
122
128
|
return !Array.isArray(order);
|
|
123
129
|
}
|