@monorepolint/rules 0.6.0-alpha.3 → 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 (62) 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 +73 -74
  5. package/.turbo/turbo-transpile-typescript.log +4 -4
  6. package/CHANGELOG.md +10 -0
  7. package/build/js/index.js +104 -69
  8. package/build/js/index.js.map +1 -1
  9. package/build/tsconfig.tsbuildinfo +1 -1
  10. package/build/types/bannedDependencies.d.ts.map +1 -1
  11. package/build/types/forceError.d.ts +4 -0
  12. package/build/types/forceError.d.ts.map +1 -0
  13. package/build/types/index.d.ts +2 -0
  14. package/build/types/index.d.ts.map +1 -1
  15. package/build/types/mustSatisfyPeerDependencies.d.ts.map +1 -1
  16. package/build/types/nestedWorkspaces.d.ts.map +1 -1
  17. package/build/types/oncePerPackage.d.ts +11 -0
  18. package/build/types/oncePerPackage.d.ts.map +1 -0
  19. package/build/types/requireDependency.d.ts.map +1 -1
  20. package/build/types/util/packageDependencyGraphService.d.ts.map +1 -1
  21. package/coverage/clover.xml +516 -469
  22. package/coverage/coverage-final.json +12 -10
  23. package/coverage/index.html +15 -15
  24. package/coverage/src/alphabeticalDependencies.ts.html +6 -6
  25. package/coverage/src/alphabeticalScripts.ts.html +1 -1
  26. package/coverage/src/bannedDependencies.ts.html +7 -10
  27. package/coverage/src/consistentDependencies.ts.html +7 -13
  28. package/coverage/src/consistentVersions.ts.html +31 -46
  29. package/coverage/src/fileContents.ts.html +1 -1
  30. package/coverage/src/forceError.ts.html +184 -0
  31. package/coverage/src/index.html +73 -43
  32. package/coverage/src/index.ts.html +10 -4
  33. package/coverage/src/mustSatisfyPeerDependencies.ts.html +16 -61
  34. package/coverage/src/nestedWorkspaces.ts.html +10 -22
  35. package/coverage/src/oncePerPackage.ts.html +181 -0
  36. package/coverage/src/packageEntry.ts.html +1 -1
  37. package/coverage/src/packageOrder.ts.html +1 -1
  38. package/coverage/src/packageScript.ts.html +1 -1
  39. package/coverage/src/requireDependency.ts.html +7 -13
  40. package/coverage/src/standardTsconfig.ts.html +6 -6
  41. package/coverage/src/util/checkAlpha.ts.html +1 -1
  42. package/coverage/src/util/createRuleFactory.ts.html +1 -1
  43. package/coverage/src/util/index.html +10 -10
  44. package/coverage/src/util/makeDirectory.ts.html +1 -1
  45. package/coverage/src/util/packageDependencyGraphService.ts.html +7 -13
  46. package/package.json +4 -4
  47. package/src/__tests__/fileContents.spec.ts +1 -5
  48. package/src/__tests__/nestedWorkspaces.spec.ts +1 -2
  49. package/src/__tests__/packageEntry.spec.ts +1 -6
  50. package/src/__tests__/packageOrder.spec.ts +1 -6
  51. package/src/__tests__/packageScript.spec.ts +1 -6
  52. package/src/bannedDependencies.ts +1 -2
  53. package/src/consistentDependencies.ts +1 -3
  54. package/src/consistentVersions.ts +24 -29
  55. package/src/forceError.ts +33 -0
  56. package/src/index.ts +2 -0
  57. package/src/mustSatisfyPeerDependencies.ts +10 -25
  58. package/src/nestedWorkspaces.ts +4 -8
  59. package/src/oncePerPackage.ts +32 -0
  60. package/src/requireDependency.ts +1 -3
  61. package/src/util/packageDependencyGraphService.ts +1 -3
  62. package/vitest.config.mjs +1 -5
@@ -76,8 +76,7 @@ const ensurePackageIsCorrectVersion = (
76
76
  `Expected dependency on ${dependencyPackageName} to match version defined in monorepolint configuration '${expectedPackageDependencyValue}', got '${actualPackageDependencyValue}' instead.`,
77
77
  fixer: () =>
78
78
  mutateJson<PackageJson>(packageJsonPath, context.host, (input) => {
79
- input.dependencies![dependencyPackageName] =
80
- expectedPackageDependencyValue;
79
+ input.dependencies![dependencyPackageName] = expectedPackageDependencyValue;
81
80
  return input;
82
81
  }),
83
82
  });
@@ -99,8 +98,7 @@ const ensurePackageIsCorrectVersion = (
99
98
  `Expected devDependency on ${dependencyPackageName} to match version defined in monorepolint configuration '${expectedPackageDependencyValue}', got '${actualPackageDevDependencyValue}' instead`,
100
99
  fixer: () =>
101
100
  mutateJson<PackageJson>(packageJsonPath, context.host, (input) => {
102
- input.devDependencies![dependencyPackageName] =
103
- expectedPackageDependencyValue;
101
+ input.devDependencies![dependencyPackageName] = expectedPackageDependencyValue;
104
102
  return input;
105
103
  }),
106
104
  });
@@ -115,20 +113,19 @@ const ensurePackageMatchesSomeVersion = (
115
113
  const packageJson = context.getPackageJson();
116
114
  const packageJsonPath = context.getPackageJsonPath();
117
115
 
118
- const acceptedPackageDependencyVersions: SemVer[] =
119
- acceptedPackageDependencyValues.map(
120
- (acceptedPackageDependencyValue) => {
121
- const acceptedPackageDependencyVersion = coerce(
122
- acceptedPackageDependencyValue,
116
+ const acceptedPackageDependencyVersions: SemVer[] = acceptedPackageDependencyValues.map(
117
+ (acceptedPackageDependencyValue) => {
118
+ const acceptedPackageDependencyVersion = coerce(
119
+ acceptedPackageDependencyValue,
120
+ );
121
+ if (acceptedPackageDependencyVersion == null) {
122
+ throw new Error(
123
+ `Malformed accepted package dependency version defined in monorepolint configuration: ${dependencyPackageName} @ '${acceptedPackageDependencyValue}'`,
123
124
  );
124
- if (acceptedPackageDependencyVersion == null) {
125
- throw new Error(
126
- `Malformed accepted package dependency version defined in monorepolint configuration: ${dependencyPackageName} @ '${acceptedPackageDependencyValue}'`,
127
- );
128
- }
129
- return acceptedPackageDependencyVersion;
130
- },
131
- );
125
+ }
126
+ return acceptedPackageDependencyVersion;
127
+ },
128
+ );
132
129
 
133
130
  const actualPackageDependencyValue = packageJson.dependencies
134
131
  && packageJson.dependencies[dependencyPackageName];
@@ -143,12 +140,11 @@ const ensurePackageMatchesSomeVersion = (
143
140
  ) {
144
141
  context.addError({
145
142
  file: packageJsonPath,
146
- message:
147
- `Expected dependency on ${dependencyPackageName} to match one of '${
148
- JSON.stringify(
149
- acceptedPackageDependencyValues,
150
- )
151
- }', got '${actualPackageDependencyValue}' instead.`,
143
+ message: `Expected dependency on ${dependencyPackageName} to match one of '${
144
+ JSON.stringify(
145
+ acceptedPackageDependencyValues,
146
+ )
147
+ }', got '${actualPackageDependencyValue}' instead.`,
152
148
  });
153
149
  }
154
150
 
@@ -167,12 +163,11 @@ const ensurePackageMatchesSomeVersion = (
167
163
  ) {
168
164
  context.addError({
169
165
  file: packageJsonPath,
170
- message:
171
- `Expected devDependency on ${dependencyPackageName} to match one of '${
172
- JSON.stringify(
173
- acceptedPackageDependencyValues,
174
- )
175
- }', got '${actualPackageDevDependencyValue}' instead.`,
166
+ message: `Expected devDependency on ${dependencyPackageName} to match one of '${
167
+ JSON.stringify(
168
+ acceptedPackageDependencyValues,
169
+ )
170
+ }', got '${actualPackageDevDependencyValue}' instead.`,
176
171
  });
177
172
  }
178
173
  };
@@ -0,0 +1,33 @@
1
+ import { createRuleFactory } from "./util/createRuleFactory.js";
2
+
3
+ export const forceError = createRuleFactory<{ customMessage?: string }>({
4
+ name: "forceError",
5
+
6
+ check: async (context, opts) => {
7
+ context.addError({
8
+ message: opts?.customMessage
9
+ ?? "Forced error (often used to debug package selection)",
10
+ file: context.getPackageJsonPath(),
11
+ });
12
+ },
13
+
14
+ validateOptions: (opts): asserts opts is null | { customMessage: string } => {
15
+ if (opts == null) return;
16
+
17
+ if (typeof opts !== "object") {
18
+ throw new Error("options must be an object if provided");
19
+ }
20
+
21
+ const numKeys = Object.keys(opts).length;
22
+
23
+ if (numKeys === 0) return;
24
+
25
+ if (Object.keys(opts).length > 1 || !("customMessage" in opts)) {
26
+ throw new Error("options must only have `customMessage` property");
27
+ }
28
+
29
+ if (typeof opts.customMessage !== "string") {
30
+ throw new Error("customMessage must be a string");
31
+ }
32
+ },
33
+ });
package/src/index.ts CHANGED
@@ -11,8 +11,10 @@ export { bannedDependencies } from "./bannedDependencies.js";
11
11
  export { consistentDependencies } from "./consistentDependencies.js";
12
12
  export { consistentVersions } from "./consistentVersions.js";
13
13
  export { fileContents } from "./fileContents.js";
14
+ export { forceError } from "./forceError.js";
14
15
  export { mustSatisfyPeerDependencies } from "./mustSatisfyPeerDependencies.js";
15
16
  export { nestedWorkspaces } from "./nestedWorkspaces.js";
17
+ export { oncePerPackage } from "./oncePerPackage.js";
16
18
  export { packageEntry } from "./packageEntry.js";
17
19
  export { packageOrder } from "./packageOrder.js";
18
20
  export { packageScript } from "./packageScript.js";
@@ -303,8 +303,7 @@ function checkSatisfyPeerDependencies(context: Context, opts: Options) {
303
303
  if (dependencyRange != null) {
304
304
  context.addError({
305
305
  file: packageJsonPath,
306
- message:
307
- `[0] Package ${packageName} has overloaded ${peerDependencyName} dependencies.\n\t`
306
+ message: `[0] Package ${packageName} has overloaded ${peerDependencyName} dependencies.\n\t`
308
307
  + `Peer dependency '${peerDependencyRange}' and regular dependency '${dependencyRange}'.`,
309
308
  });
310
309
  }
@@ -446,8 +445,7 @@ function checkSatisfyPeerDependencies(context: Context, opts: Options) {
446
445
 
447
446
  // for every inherited peer dependency, this package must declare a dependency or peer dependency
448
447
  // equal to or stricter than `mostStrictPeerRequirement`
449
- const packagePeerDependencyRange =
450
- packagePeerDependencies[peerDependencyName];
448
+ const packagePeerDependencyRange = packagePeerDependencies[peerDependencyName];
451
449
  if (packageDependencyRange == null && packagePeerDependencyRange == null) {
452
450
  context.addError({
453
451
  file: packageJsonPath,
@@ -523,22 +521,17 @@ function getMostStrictStatement(
523
521
  mostStrictPeerRequirement: IResolvedPeerDependencyRequirement,
524
522
  ) {
525
523
  if (mostStrictPeerRequirement.fromPeerDependencyRequirements.length === 1) {
526
- const dependencyName =
527
- mostStrictPeerRequirement.fromPeerDependencyRequirements[0]
528
- .fromPackageName;
524
+ const dependencyName = mostStrictPeerRequirement.fromPeerDependencyRequirements[0]
525
+ .fromPackageName;
529
526
  return `Dependency ${dependencyName} requires '${mostStrictPeerRequirement.range}'.`;
530
527
  } else {
531
528
  const dependencyNames = mostStrictPeerRequirement
532
529
  .fromPeerDependencyRequirements
533
- .map((peerDependencyRequirement) =>
534
- peerDependencyRequirement.fromPackageName
535
- )
530
+ .map((peerDependencyRequirement) => peerDependencyRequirement.fromPackageName)
536
531
  .join(", ");
537
532
  const dependencyRequirements = mostStrictPeerRequirement
538
533
  .fromPeerDependencyRequirements
539
- .map((peerDependencyRequirement) =>
540
- `'${peerDependencyRequirement.range}'`
541
- )
534
+ .map((peerDependencyRequirement) => `'${peerDependencyRequirement.range}'`)
542
535
  .join(", ");
543
536
  return (
544
537
  `Dependencies [${dependencyNames}] require [${dependencyRequirements}] respectively, `
@@ -612,9 +605,7 @@ export function findIntersection(
612
605
  .map((bVersion) => {
613
606
  const bSemVer = coerce(bVersion)!;
614
607
  if (bVersion.startsWith("^") && bSemVer.major >= aSemVer.major) {
615
- return `^${
616
- bSemVer.compare(aSemVer) >= 0 ? bSemVer.raw : aSemVer.raw
617
- }`;
608
+ return `^${bSemVer.compare(aSemVer) >= 0 ? bSemVer.raw : aSemVer.raw}`;
618
609
  }
619
610
  return bSemVer.compare(aSemVer) !== -1 ? bVersion : undefined;
620
611
  })
@@ -633,9 +624,7 @@ export function findIntersection(
633
624
  .map((aVersion) => {
634
625
  const aSemVer = coerce(aVersion)!;
635
626
  if (aVersion.startsWith("^") && aSemVer.major >= bSemVer.major) {
636
- return `^${
637
- aSemVer.compare(bSemVer) >= 0 ? aSemVer.raw : bSemVer.raw
638
- }`;
627
+ return `^${aSemVer.compare(bSemVer) >= 0 ? aSemVer.raw : bSemVer.raw}`;
639
628
  }
640
629
  return aSemVer.compare(bSemVer) !== -1 ? aVersion : undefined;
641
630
  })
@@ -649,9 +638,7 @@ export function findIntersection(
649
638
  const compatibleVersions = aVersions
650
639
  .map((aVersion) => {
651
640
  const aSemVer = coerce(aVersion)!;
652
- const majorMatchingBVersion = bVersions.find((m) =>
653
- coerce(m)!.major === aSemVer.major
654
- );
641
+ const majorMatchingBVersion = bVersions.find((m) => coerce(m)!.major === aSemVer.major);
655
642
  if (majorMatchingBVersion === undefined) {
656
643
  // there is no intersecting `b` major version for this `a` major version
657
644
  return undefined;
@@ -757,9 +744,7 @@ export function doesASatisfyB(a: ValidRange, b: ValidRange): boolean {
757
744
 
758
745
  return aVersions.every((aVersion) => {
759
746
  const aSemVer = coerce(aVersion)!;
760
- const majorMatchingBVersion = bVersions.find((m) =>
761
- coerce(m)!.major === aSemVer.major
762
- );
747
+ const majorMatchingBVersion = bVersions.find((m) => coerce(m)!.major === aSemVer.major);
763
748
  if (majorMatchingBVersion === undefined) {
764
749
  // `a` permits a major version that is not permitted by `b`, therefore `a` is "less strict"
765
750
  return false;
@@ -42,9 +42,7 @@ export const nestedWorkspaces = createRuleFactory({
42
42
  }
43
43
 
44
44
  // Build a set of globs for each package.json that exists in packages specified by a workspace.
45
- const workspacePackageJsons = (workspaces || []).map((item) =>
46
- `${item}/package.json`
47
- );
45
+ const workspacePackageJsons = (workspaces || []).map((item) => `${item}/package.json`);
48
46
 
49
47
  // Expand the globs to get an array of all package.json files that are in packages specified by a workspace.
50
48
  const expandedWorkspacesGlobs = globby.globbySync([
@@ -58,13 +56,11 @@ export const nestedWorkspaces = createRuleFactory({
58
56
  );
59
57
 
60
58
  if (difference.length !== 0) {
61
- const differencesList = difference.map((packageJsonPath) =>
62
- path.dirname(packageJsonPath)
63
- ).join(", ");
59
+ const differencesList = difference.map((packageJsonPath) => path.dirname(packageJsonPath))
60
+ .join(", ");
64
61
  context.addError({
65
62
  file: context.getPackageJsonPath(),
66
- message:
67
- `The "workspace" field is missing one or more values: ${differencesList}. `
63
+ message: `The "workspace" field is missing one or more values: ${differencesList}. `
68
64
  + "You may be able to use a glob to avoid listing each workspace individually, e.g. \"packages/nested-workspace/*\".",
69
65
  });
70
66
  }
@@ -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
+ });
@@ -44,9 +44,7 @@ export const requireDependency = createRuleFactory({
44
44
  fixer: () => {
45
45
  mutateJson<PackageJson>(packageJsonPath, context.host, (input) => {
46
46
  input[type] = Object.fromEntries(
47
- Object.entries(expectedEntries).filter(([, v]) =>
48
- v !== undefined
49
- ),
47
+ Object.entries(expectedEntries).filter(([, v]) => v !== undefined),
50
48
  ) as Record<string, string>;
51
49
  return input;
52
50
  });
@@ -38,9 +38,7 @@ export interface IPackageDependencyGraphService {
38
38
  }
39
39
 
40
40
  /** Default implementation of the package dependency graph service. */
41
- export class PackageDependencyGraphService
42
- implements IPackageDependencyGraphService
43
- {
41
+ export class PackageDependencyGraphService implements IPackageDependencyGraphService {
44
42
  /** Construct a graph of package dependencies and return the root node. */
45
43
  public buildDependencyGraph(
46
44
  startPackageJsonPath: string,
package/vitest.config.mjs CHANGED
@@ -1,8 +1,4 @@
1
- import {
2
- coverageConfigDefaults,
3
- defaultExclude,
4
- defineProject,
5
- } from "vitest/config";
1
+ import { coverageConfigDefaults, defaultExclude, defineProject } from "vitest/config";
6
2
 
7
3
  export default defineProject({
8
4
  test: {