@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.
Files changed (85) hide show
  1. package/.turbo/turbo-clean.log +1 -1
  2. package/.turbo/turbo-compile-typescript.log +1 -1
  3. package/.turbo/turbo-lint.log +1 -1
  4. package/.turbo/turbo-test.log +161 -102
  5. package/.turbo/turbo-transpile-typescript.log +4 -4
  6. package/CHANGELOG.md +19 -0
  7. package/build/js/index.js +429 -241
  8. package/build/js/index.js.map +1 -1
  9. package/build/tsconfig.tsbuildinfo +1 -1
  10. package/build/types/__tests__/utils.d.ts +0 -1
  11. package/build/types/__tests__/utils.d.ts.map +1 -1
  12. package/build/types/bannedDependencies.d.ts.map +1 -1
  13. package/build/types/fileContents.d.ts.map +1 -1
  14. package/build/types/forceError.d.ts +4 -0
  15. package/build/types/forceError.d.ts.map +1 -0
  16. package/build/types/index.d.ts +5 -3
  17. package/build/types/index.d.ts.map +1 -1
  18. package/build/types/mustSatisfyPeerDependencies.d.ts.map +1 -1
  19. package/build/types/nestedWorkspaces.d.ts.map +1 -1
  20. package/build/types/oncePerPackage.d.ts +11 -0
  21. package/build/types/oncePerPackage.d.ts.map +1 -0
  22. package/build/types/packageEntry.d.ts.map +1 -1
  23. package/build/types/packageOrder.d.ts.map +1 -1
  24. package/build/types/packageScript.d.ts.map +1 -1
  25. package/build/types/requireDependency.d.ts +12 -12
  26. package/build/types/requireDependency.d.ts.map +1 -1
  27. package/build/types/util/checkAlpha.d.ts.map +1 -1
  28. package/build/types/util/createRuleFactory.d.ts.map +1 -1
  29. package/build/types/util/packageDependencyGraphService.d.ts.map +1 -1
  30. package/coverage/clover.xml +1201 -813
  31. package/coverage/coverage-final.json +20 -18
  32. package/coverage/index.html +20 -20
  33. package/coverage/src/alphabeticalDependencies.ts.html +11 -11
  34. package/coverage/src/alphabeticalScripts.ts.html +5 -5
  35. package/coverage/src/bannedDependencies.ts.html +76 -28
  36. package/coverage/src/consistentDependencies.ts.html +51 -18
  37. package/coverage/src/consistentVersions.ts.html +144 -48
  38. package/coverage/src/fileContents.ts.html +47 -23
  39. package/coverage/src/forceError.ts.html +184 -0
  40. package/coverage/src/index.html +96 -66
  41. package/coverage/src/index.ts.html +39 -33
  42. package/coverage/src/mustSatisfyPeerDependencies.ts.html +323 -80
  43. package/coverage/src/nestedWorkspaces.ts.html +47 -20
  44. package/coverage/src/oncePerPackage.ts.html +181 -0
  45. package/coverage/src/packageEntry.ts.html +40 -19
  46. package/coverage/src/packageOrder.ts.html +30 -12
  47. package/coverage/src/packageScript.ts.html +81 -27
  48. package/coverage/src/requireDependency.ts.html +77 -32
  49. package/coverage/src/standardTsconfig.ts.html +84 -21
  50. package/coverage/src/util/checkAlpha.ts.html +18 -9
  51. package/coverage/src/util/createRuleFactory.ts.html +16 -4
  52. package/coverage/src/util/index.html +17 -17
  53. package/coverage/src/util/makeDirectory.ts.html +5 -5
  54. package/coverage/src/util/packageDependencyGraphService.ts.html +94 -19
  55. package/package.json +4 -5
  56. package/src/__tests__/alphabeticalScripts.spec.ts +12 -4
  57. package/src/__tests__/bannedDependencies.spec.ts +45 -16
  58. package/src/__tests__/consistentDependencies.spec.ts +11 -5
  59. package/src/__tests__/consistentVersions.spec.ts +72 -18
  60. package/src/__tests__/fileContents.spec.ts +5 -5
  61. package/src/__tests__/mustSatisfyPeerDependencies.spec.ts +191 -76
  62. package/src/__tests__/nestedWorkspaces.spec.ts +10 -7
  63. package/src/__tests__/packageEntry.spec.ts +48 -47
  64. package/src/__tests__/packageOrder.spec.ts +72 -71
  65. package/src/__tests__/packageScript.spec.ts +19 -10
  66. package/src/__tests__/requireDependency.spec.ts +12 -6
  67. package/src/__tests__/utils.ts +16 -7
  68. package/src/bannedDependencies.ts +32 -16
  69. package/src/consistentDependencies.ts +19 -8
  70. package/src/consistentVersions.ts +70 -38
  71. package/src/fileContents.ts +19 -11
  72. package/src/forceError.ts +33 -0
  73. package/src/index.ts +5 -3
  74. package/src/mustSatisfyPeerDependencies.ts +141 -60
  75. package/src/nestedWorkspaces.ts +19 -10
  76. package/src/oncePerPackage.ts +32 -0
  77. package/src/packageEntry.ts +18 -11
  78. package/src/packageOrder.ts +9 -3
  79. package/src/packageScript.ts +37 -19
  80. package/src/requireDependency.ts +26 -11
  81. package/src/standardTsconfig.ts +31 -10
  82. package/src/util/checkAlpha.ts +5 -2
  83. package/src/util/createRuleFactory.ts +6 -2
  84. package/src/util/packageDependencyGraphService.ts +38 -13
  85. 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 { dependencyBlacklist, dependencyWhitelist, enforceForDevDependencies, skipUnparseableRanges } = opts;
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 (const [peerDependencyName, peerDependencyRange] of Object.entries(packagePeerDependencies)) {
283
- if (shouldSkipPackage({ dependencyBlacklist, dependencyWhitelist, packageName: peerDependencyName })) {
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
- `[0] Package ${packageName} has overloaded ${peerDependencyName} dependencies.\n\t` +
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: { [peerDependencyName: string]: IPeerDependencyRequirement[] } = {};
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
- ? [...Object.keys(packageDependencies), ...Object.keys(packageDevDependencies)]
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(dependency, path.dirname(packageJsonPath));
325
+ const dependencyPackageJsonPath = resolvePackagePath(
326
+ dependency,
327
+ path.dirname(packageJsonPath),
328
+ );
307
329
  if (dependencyPackageJsonPath == null) {
308
- throw new Error(`Could not resolve ${dependency} from ${path.dirname(packageJsonPath)}`);
330
+ throw new Error(
331
+ `Could not resolve ${dependency} from ${path.dirname(packageJsonPath)}`,
332
+ );
309
333
  }
310
- const dependencyPackageJson = context.host.readJson(dependencyPackageJsonPath) as PackageJson;
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 (const [peerDependencyName, range] of Object.entries(requiredPeerDependencies)) {
316
- if (shouldSkipPackage({ dependencyBlacklist, dependencyWhitelist, packageName: peerDependencyName })) {
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 = `Unable to parse ${dependencyPackageJson.name}'s ${peerDependencyName} peer dependency range '${range}'.`;
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({ fromPackageName: dependencyPackageJson.name!, range });
368
+ allRequiredPeerDependencies[peerDependencyName].push({
369
+ fromPackageName: dependencyPackageJson.name!,
370
+ range,
371
+ });
332
372
  }
333
373
  }
334
374
 
335
- for (const [peerDependencyName, peerDependencyRequirements] of Object.entries(allRequiredPeerDependencies)) {
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 (doesASatisfyB(mostStrictPeerRequirement.range, peerRequirement.range)) {
386
+ if (
387
+ doesASatisfyB(mostStrictPeerRequirement.range, peerRequirement.range)
388
+ ) {
343
389
  continue;
344
- } else if (doesASatisfyB(peerRequirement.range, mostStrictPeerRequirement.range)) {
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(peerRequirement.range, mostStrictPeerRequirement.range);
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 = `Unable to parse ${packageName}'s ${peerDependencyName} dependency range '${packageDependencyRange}'.`;
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 (!doesASatisfyB(packageDependencyRange, mostStrictPeerRequirement.range)) {
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 = `Unable to parse ${packageName}'s ${peerDependencyName} peer dependency range '${packagePeerDependencyRange}'.`;
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 (!doesASatisfyB(packagePeerDependencyRange, mostStrictPeerRequirement.range)) {
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 && !dependencyWhitelist.includes(packageName))
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(mostStrictPeerRequirement: IResolvedPeerDependencyRequirement) {
520
+ function getMostStrictStatement(
521
+ mostStrictPeerRequirement: IResolvedPeerDependencyRequirement,
522
+ ) {
460
523
  if (mostStrictPeerRequirement.fromPeerDependencyRequirements.length === 1) {
461
- const dependencyName = mostStrictPeerRequirement.fromPeerDependencyRequirements[0].fromPackageName;
524
+ const dependencyName = mostStrictPeerRequirement.fromPeerDependencyRequirements[0]
525
+ .fromPackageName;
462
526
  return `Dependency ${dependencyName} requires '${mostStrictPeerRequirement.range}'.`;
463
527
  } else {
464
- const dependencyNames = mostStrictPeerRequirement.fromPeerDependencyRequirements
528
+ const dependencyNames = mostStrictPeerRequirement
529
+ .fromPeerDependencyRequirements
465
530
  .map((peerDependencyRequirement) => peerDependencyRequirement.fromPackageName)
466
531
  .join(", ");
467
- const dependencyRequirements = mostStrictPeerRequirement.fromPeerDependencyRequirements
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(a: ValidRange, b: ValidRange): ValidRange | undefined {
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 (doesASatisfyB(aVersion as ValidRange, majorMatchingBVersion as ValidRange)) {
646
+ if (
647
+ doesASatisfyB(
648
+ aVersion as ValidRange,
649
+ majorMatchingBVersion as ValidRange,
650
+ )
651
+ ) {
578
652
  return aVersion;
579
- } else if (doesASatisfyB(majorMatchingBVersion as ValidRange, aVersion as ValidRange)) {
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(majorMatchingBVersion);
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`
@@ -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(["*/**/package.json", "!**/node_modules/**"]);
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
- ? rootPackageJson.workspaces.packages
30
- : undefined;
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: 'The "workspace" field is missing, even though there are workspaces in the repository.',
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([...workspacePackageJsons, "!**/node_modules/**"]);
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) => !expandedWorkspacesGlobs.includes(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)).join(", ");
59
+ const differencesList = difference.map((packageJsonPath) => path.dirname(packageJsonPath))
60
+ .join(", ");
51
61
  context.addError({
52
62
  file: context.getPackageJsonPath(),
53
- message:
54
- `The "workspace" field is missing one or more values: ${differencesList}. ` +
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
+ });
@@ -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(JSON.stringify(value) + "\n", (JSON.stringify(packageJson[key]) || "") + "\n");
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>(context.getPackageJsonPath(), context.host, (input) => {
61
- input[key] = value;
62
- return input;
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
  }
@@ -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 ? defaultKeyOrder : opts.order;
60
+ const order: string[] | OrderFunction = opts === undefined
61
+ ? defaultKeyOrder
62
+ : opts.order;
61
63
 
62
- const comparator = isOrderFunction(order) ? order(context) : createComparator(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(order: ReadonlyArray<string> | OrderFunction): order is OrderFunction {
125
+ function isOrderFunction(
126
+ order: ReadonlyArray<string> | OrderFunction,
127
+ ): order is OrderFunction {
122
128
  return !Array.isArray(order);
123
129
  }