@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.
Files changed (107) 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 +446 -94
  5. package/.turbo/turbo-transpile-typescript.log +5 -5
  6. package/CHANGELOG.md +102 -0
  7. package/build/js/index.js +413 -368
  8. package/build/js/index.js.map +1 -1
  9. package/build/tsconfig.tsbuildinfo +1 -1
  10. package/build/types/REMOVE.d.ts +2 -0
  11. package/build/types/REMOVE.d.ts.map +1 -0
  12. package/build/types/__tests__/alphabeticalDependencies.spec.d.ts +8 -0
  13. package/build/types/__tests__/alphabeticalDependencies.spec.d.ts.map +1 -0
  14. package/build/types/__tests__/forceError.spec.d.ts +8 -0
  15. package/build/types/__tests__/forceError.spec.d.ts.map +1 -0
  16. package/build/types/__tests__/oncePerPackage.spec.d.ts +8 -0
  17. package/build/types/__tests__/oncePerPackage.spec.d.ts.map +1 -0
  18. package/build/types/__tests__/standardTsconfig.spec.d.ts +8 -0
  19. package/build/types/__tests__/standardTsconfig.spec.d.ts.map +1 -0
  20. package/build/types/bannedDependencies.d.ts +9 -33
  21. package/build/types/bannedDependencies.d.ts.map +1 -1
  22. package/build/types/consistentDependencies.d.ts +6 -6
  23. package/build/types/consistentDependencies.d.ts.map +1 -1
  24. package/build/types/consistentVersions.d.ts +6 -10
  25. package/build/types/consistentVersions.d.ts.map +1 -1
  26. package/build/types/fileContents.d.ts +3 -2
  27. package/build/types/fileContents.d.ts.map +1 -1
  28. package/build/types/index.d.ts +1 -0
  29. package/build/types/index.d.ts.map +1 -1
  30. package/build/types/mustSatisfyPeerDependencies.d.ts +12 -190
  31. package/build/types/mustSatisfyPeerDependencies.d.ts.map +1 -1
  32. package/build/types/nestedWorkspaces.d.ts +2 -2
  33. package/build/types/nestedWorkspaces.d.ts.map +1 -1
  34. package/build/types/oncePerPackage.d.ts +6 -6
  35. package/build/types/oncePerPackage.d.ts.map +1 -1
  36. package/build/types/packageEntry.d.ts +11 -33
  37. package/build/types/packageEntry.d.ts.map +1 -1
  38. package/build/types/packageOrder.d.ts +2 -1
  39. package/build/types/packageOrder.d.ts.map +1 -1
  40. package/build/types/packageScript.d.ts +13 -22
  41. package/build/types/packageScript.d.ts.map +1 -1
  42. package/build/types/requireDependency.d.ts +5 -20
  43. package/build/types/requireDependency.d.ts.map +1 -1
  44. package/build/types/standardTsconfig.d.ts +12 -19
  45. package/build/types/standardTsconfig.d.ts.map +1 -1
  46. package/build/types/util/zodSchemas.d.ts +14 -0
  47. package/build/types/util/zodSchemas.d.ts.map +1 -0
  48. package/coverage/block-navigation.js +1 -1
  49. package/coverage/clover.xml +1454 -1442
  50. package/coverage/coverage-final.json +21 -19
  51. package/coverage/index.html +27 -27
  52. package/coverage/sorter.js +21 -7
  53. package/coverage/src/REMOVE.ts.html +88 -0
  54. package/coverage/src/alphabeticalDependencies.ts.html +34 -34
  55. package/coverage/src/alphabeticalScripts.ts.html +5 -5
  56. package/coverage/src/bannedDependencies.ts.html +20 -53
  57. package/coverage/src/consistentDependencies.ts.html +20 -14
  58. package/coverage/src/consistentVersions.ts.html +330 -183
  59. package/coverage/src/fileContents.ts.html +223 -88
  60. package/coverage/src/forceError.ts.html +60 -60
  61. package/coverage/src/index.html +127 -112
  62. package/coverage/src/index.ts.html +11 -5
  63. package/coverage/src/mustSatisfyPeerDependencies.ts.html +15 -501
  64. package/coverage/src/nestedWorkspaces.ts.html +5 -5
  65. package/coverage/src/oncePerPackage.ts.html +59 -59
  66. package/coverage/src/packageEntry.ts.html +121 -91
  67. package/coverage/src/packageOrder.ts.html +44 -14
  68. package/coverage/src/packageScript.ts.html +235 -88
  69. package/coverage/src/requireDependency.ts.html +241 -82
  70. package/coverage/src/standardTsconfig.ts.html +193 -193
  71. package/coverage/src/util/checkAlpha.ts.html +40 -40
  72. package/coverage/src/util/createRuleFactory.ts.html +19 -19
  73. package/coverage/src/util/index.html +30 -15
  74. package/coverage/src/util/makeDirectory.ts.html +11 -11
  75. package/coverage/src/util/packageDependencyGraphService.ts.html +1 -1
  76. package/coverage/src/util/zodSchemas.ts.html +130 -0
  77. package/package.json +14 -14
  78. package/src/REMOVE.ts +1 -0
  79. package/src/__tests__/alphabeticalDependencies.spec.ts +102 -0
  80. package/src/__tests__/alphabeticalScripts.spec.ts +18 -0
  81. package/src/__tests__/bannedDependencies.spec.ts +49 -0
  82. package/src/__tests__/consistentDependencies.spec.ts +23 -0
  83. package/src/__tests__/consistentVersions.spec.ts +142 -0
  84. package/src/__tests__/fileContents.spec.ts +348 -0
  85. package/src/__tests__/forceError.spec.ts +70 -0
  86. package/src/__tests__/mustSatisfyPeerDependencies.spec.ts +44 -0
  87. package/src/__tests__/nestedWorkspaces.spec.ts +14 -0
  88. package/src/__tests__/oncePerPackage.spec.ts +75 -0
  89. package/src/__tests__/packageEntry.spec.ts +177 -0
  90. package/src/__tests__/packageOrder.spec.ts +22 -0
  91. package/src/__tests__/packageScript.spec.ts +549 -0
  92. package/src/__tests__/requireDependency.spec.ts +259 -2
  93. package/src/__tests__/standardTsconfig.spec.ts +91 -0
  94. package/src/bannedDependencies.ts +14 -25
  95. package/src/consistentDependencies.ts +10 -8
  96. package/src/consistentVersions.ts +132 -83
  97. package/src/fileContents.ts +80 -35
  98. package/src/index.ts +2 -0
  99. package/src/mustSatisfyPeerDependencies.ts +10 -172
  100. package/src/nestedWorkspaces.ts +4 -4
  101. package/src/oncePerPackage.ts +6 -6
  102. package/src/packageEntry.ts +60 -50
  103. package/src/packageOrder.ts +19 -9
  104. package/src/packageScript.ts +67 -18
  105. package/src/requireDependency.ts +84 -31
  106. package/src/standardTsconfig.ts +26 -26
  107. package/src/util/zodSchemas.ts +15 -0
@@ -1,12 +1,12 @@
1
- import * as r from "runtypes";
1
+ import { z } from "zod";
2
2
  import { createRuleFactory } from "./util/createRuleFactory.js";
3
3
 
4
- export const Options = r.Record({
5
- singletonKey: r.String.Or(r.Symbol),
6
- customMessage: r.String.optional(),
4
+ export const Options = z.object({
5
+ singletonKey: z.union([z.string(), z.symbol()]),
6
+ customMessage: z.string().optional(),
7
7
  });
8
8
 
9
- export type Options = r.Static<typeof Options>;
9
+ export type Options = z.infer<typeof Options>;
10
10
 
11
11
  const visitedMap = new Map<string | symbol, Set<string>>();
12
12
 
@@ -28,5 +28,5 @@ export const oncePerPackage = createRuleFactory<Options>({
28
28
  }
29
29
  },
30
30
 
31
- validateOptions: Options.assert,
31
+ validateOptions: Options.parse,
32
32
  });
@@ -7,35 +7,20 @@
7
7
 
8
8
  import { mutateJson, PackageJson } from "@monorepolint/utils";
9
9
  import { diff } from "jest-diff";
10
- import * as r from "runtypes";
10
+ import { z } from "zod";
11
+ import { REMOVE } from "./REMOVE.js";
11
12
  import { createRuleFactory } from "./util/createRuleFactory.js";
13
+ import { ZodRemove } from "./util/zodSchemas.js";
12
14
 
13
- export const Options = r.Union(
14
- r
15
- .Record({
16
- entries: r.Dictionary(r.Unknown), // string => unknown, enforces existence of keys and their values
17
- })
18
- .And(
19
- r.Partial({
20
- entriesExist: r.Undefined,
21
- }),
22
- ),
23
- r
24
- .Record({
25
- entriesExist: r.Array(r.String), // enforces existence of keys, but not values
26
- })
27
- .And(
28
- r.Partial({
29
- entries: r.Undefined,
30
- }),
31
- ),
32
- r.Record({
33
- entries: r.Dictionary(r.Unknown), // string => unknown, enforces existence of keys and their values
34
- entriesExist: r.Array(r.String),
35
- }),
15
+ export const Options = z.object({
16
+ entries: z.record(z.string(), z.union([z.unknown(), ZodRemove])).optional(), // string => unknown | REMOVE, enforces existence of keys and their values or removal
17
+ entriesExist: z.array(z.string()).optional(), // enforces existence of keys, but not values
18
+ }).refine(
19
+ (data) => data.entries !== undefined || data.entriesExist !== undefined,
20
+ { message: "At least one of 'entries' or 'entriesExist' must be provided" },
36
21
  );
37
22
 
38
- export type Options = r.Static<typeof Options>;
23
+ export type Options = z.infer<typeof Options>;
39
24
 
40
25
  export const packageEntry = createRuleFactory<Options>({
41
26
  name: "packageEntry",
@@ -46,30 +31,51 @@ export const packageEntry = createRuleFactory<Options>({
46
31
  for (const key of Object.keys(options.entries)) {
47
32
  const value = options.entries[key];
48
33
 
49
- const entryDiff = diff(
50
- JSON.stringify(value) + "\n",
51
- (JSON.stringify(packageJson[key]) || "") + "\n",
52
- );
53
- if (
54
- (typeof value !== "object" && value !== packageJson[key])
55
- || entryDiff == null
56
- || !entryDiff.includes("Compared values have no visual difference")
57
- ) {
58
- context.addError({
59
- file: context.getPackageJsonPath(),
60
- message: createStandardizedEntryErrorMessage(key),
61
- longMessage: entryDiff,
62
- fixer: () => {
63
- mutateJson<PackageJson>(
64
- context.getPackageJsonPath(),
65
- context.host,
66
- (input) => {
67
- input[key] = value;
68
- return input;
69
- },
70
- );
71
- },
72
- });
34
+ if (value === REMOVE) {
35
+ // Handle removal: only add error if key exists
36
+ if (packageJson[key] !== undefined) {
37
+ context.addError({
38
+ file: context.getPackageJsonPath(),
39
+ message: createRemovalEntryErrorMessage(key),
40
+ fixer: () => {
41
+ mutateJson<PackageJson>(
42
+ context.getPackageJsonPath(),
43
+ context.host,
44
+ (input) => {
45
+ delete input[key];
46
+ return input;
47
+ },
48
+ );
49
+ },
50
+ });
51
+ }
52
+ } else {
53
+ // Handle regular value checking
54
+ const entryDiff = diff(
55
+ JSON.stringify(value) + "\n",
56
+ (JSON.stringify(packageJson[key]) || "") + "\n",
57
+ );
58
+ if (
59
+ (typeof value !== "object" && value !== packageJson[key])
60
+ || entryDiff == null
61
+ || !entryDiff.includes("Compared values have no visual difference")
62
+ ) {
63
+ context.addError({
64
+ file: context.getPackageJsonPath(),
65
+ message: createStandardizedEntryErrorMessage(key),
66
+ longMessage: entryDiff,
67
+ fixer: () => {
68
+ mutateJson<PackageJson>(
69
+ context.getPackageJsonPath(),
70
+ context.host,
71
+ (input) => {
72
+ input[key] = value;
73
+ return input;
74
+ },
75
+ );
76
+ },
77
+ });
78
+ }
73
79
  }
74
80
  }
75
81
  }
@@ -85,7 +91,7 @@ export const packageEntry = createRuleFactory<Options>({
85
91
  }
86
92
  }
87
93
  },
88
- validateOptions: Options.check,
94
+ validateOptions: Options.parse,
89
95
  });
90
96
 
91
97
  export function createStandardizedEntryErrorMessage(key: string) {
@@ -95,3 +101,7 @@ export function createStandardizedEntryErrorMessage(key: string) {
95
101
  export function createExpectedEntryErrorMessage(key: string) {
96
102
  return `Expected entry for '${key}' to exist`;
97
103
  }
104
+
105
+ export function createRemovalEntryErrorMessage(key: string) {
106
+ return `Entry '${key}' should be removed`;
107
+ }
@@ -7,17 +7,27 @@
7
7
 
8
8
  import { Context } from "@monorepolint/config";
9
9
  import { diff } from "jest-diff";
10
- import * as r from "runtypes";
10
+ import { z } from "zod";
11
11
  import { createRuleFactory } from "./util/createRuleFactory.js";
12
12
  type OrderFunction = (context: Context) => (a: string, b: string) => number;
13
13
 
14
- const Options = r
15
- .Record({
16
- order: r.Union(r.Array(r.String), r.Function),
17
- })
18
- .Or(r.Undefined);
19
-
20
- type Options = r.Static<typeof Options>;
14
+ const Options = z.union([
15
+ z.undefined(),
16
+ z.object({
17
+ order: z.union([
18
+ z.array(z.string()),
19
+ z.function({
20
+ input: [z.any()],
21
+ output: z.function({
22
+ input: [z.string(), z.string()],
23
+ output: z.number(),
24
+ }),
25
+ }),
26
+ ]),
27
+ }),
28
+ ]);
29
+
30
+ type Options = z.infer<typeof Options>;
21
31
 
22
32
  const defaultKeyOrder = [
23
33
  "name",
@@ -86,7 +96,7 @@ export const packageOrder = createRuleFactory<Options>({
86
96
  });
87
97
  }
88
98
  },
89
- validateOptions: Options.check,
99
+ validateOptions: Options.parse,
90
100
  });
91
101
 
92
102
  function arrayOrderCompare(a: ReadonlyArray<string>, b: ReadonlyArray<string>) {
@@ -7,22 +7,35 @@
7
7
 
8
8
  import { mutateJson, PackageJson } from "@monorepolint/utils";
9
9
  import { diff } from "jest-diff";
10
- import * as r from "runtypes";
10
+ import { z } from "zod";
11
+ import { REMOVE } from "./REMOVE.js";
11
12
  import { createRuleFactory } from "./util/createRuleFactory.js";
13
+ import { ZodRemove } from "./util/zodSchemas.js";
12
14
 
13
- export const Options = r.Record({
14
- scripts: r.Dictionary(
15
- r.Union(
16
- r.String,
17
- r.Record({
18
- options: r.Array(r.String.Or(r.Undefined)),
19
- fixValue: r.Union(r.String, r.Undefined, r.Literal(false)).optional(),
15
+ export const Options = z.object({
16
+ scripts: z.record(
17
+ z.string(),
18
+ z.union([
19
+ z.string(),
20
+ ZodRemove, // Allow direct REMOVE values like { "build": REMOVE }
21
+ z.object({
22
+ options: z.array(z.union([
23
+ z.string(),
24
+ z.undefined(),
25
+ ZodRemove,
26
+ ])),
27
+ fixValue: z.union([
28
+ z.string(),
29
+ z.undefined(),
30
+ z.literal(false),
31
+ ZodRemove,
32
+ ]).optional(),
20
33
  }),
21
- ),
22
- ), // string => string
34
+ ]),
35
+ ), // string => string | REMOVE | object
23
36
  });
24
37
 
25
- export type Options = r.Static<typeof Options>;
38
+ export type Options = z.infer<typeof Options>;
26
39
 
27
40
  export const MSG_NO_SCRIPTS_BLOCK = "No scripts block in package.json";
28
41
 
@@ -48,28 +61,63 @@ export const packageScript = createRuleFactory<Options>({
48
61
  return;
49
62
  }
50
63
  for (const [name, value] of Object.entries(options.scripts)) {
51
- const allowedValues = new Set<string | undefined>();
52
- let fixValue: string | undefined | false;
64
+ const allowedValues = new Set<string | undefined | typeof REMOVE>();
65
+ let fixValue: string | undefined | false | typeof REMOVE;
53
66
  let allowEmpty = false;
54
67
  let fixToEmpty = false;
68
+ let fixToRemove = false;
55
69
 
56
70
  if (typeof value === "string") {
57
71
  allowedValues.add(value);
58
72
  fixValue = value;
73
+ } else if (value === REMOVE) {
74
+ // Handle direct REMOVE value: script should be removed
75
+ allowedValues.add(REMOVE);
76
+ fixValue = REMOVE;
77
+ fixToRemove = true;
59
78
  } else {
60
79
  for (const q of value.options) {
61
80
  if (q === undefined) {
62
81
  allowEmpty = true;
82
+ } else if (q === REMOVE) {
83
+ allowEmpty = true;
63
84
  }
64
85
  allowedValues.add(q);
65
86
  }
66
87
  fixToEmpty = Object.prototype.hasOwnProperty.call(value, "fixValue")
67
88
  && value.fixValue === undefined;
89
+ fixToRemove = Object.prototype.hasOwnProperty.call(value, "fixValue")
90
+ && value.fixValue === REMOVE;
68
91
  fixValue = value.fixValue;
69
92
  }
70
93
 
71
94
  const actualValue = packageJson.scripts[name];
72
95
 
96
+ // Special handling for direct REMOVE: only error if script exists
97
+ if (value === REMOVE) {
98
+ if (actualValue !== undefined) {
99
+ // Script exists but should be removed
100
+ const fixer = () => {
101
+ mutateJson<PackageJson>(
102
+ context.getPackageJsonPath(),
103
+ context.host,
104
+ (input) => {
105
+ delete input.scripts![name];
106
+ return input;
107
+ },
108
+ );
109
+ };
110
+
111
+ context.addError({
112
+ file: context.getPackageJsonPath(),
113
+ message: `Script '${name}' should be removed`,
114
+ fixer,
115
+ });
116
+ }
117
+ // If script doesn't exist, no error needed
118
+ continue;
119
+ }
120
+
73
121
  if (
74
122
  !allowedValues.has(actualValue)
75
123
  && !(allowEmpty === true && actualValue === undefined)
@@ -77,7 +125,8 @@ export const packageScript = createRuleFactory<Options>({
77
125
  let fixer;
78
126
 
79
127
  if (
80
- fixValue !== false && (fixValue !== undefined || fixToEmpty === true)
128
+ fixValue !== false
129
+ && (fixValue !== undefined || fixToEmpty === true || fixToRemove === true)
81
130
  ) {
82
131
  const q = fixValue;
83
132
  fixer = () => {
@@ -85,10 +134,10 @@ export const packageScript = createRuleFactory<Options>({
85
134
  context.getPackageJsonPath(),
86
135
  context.host,
87
136
  (input) => {
88
- if (fixToEmpty && q === undefined) {
137
+ if ((fixToEmpty && q === undefined) || (fixToRemove && q === REMOVE)) {
89
138
  delete input.scripts![name];
90
139
  } else {
91
- input.scripts![name] = q!;
140
+ input.scripts![name] = q as string;
92
141
  }
93
142
  return input;
94
143
  },
@@ -97,7 +146,7 @@ export const packageScript = createRuleFactory<Options>({
97
146
  }
98
147
 
99
148
  const validOptionsString = Array.from(allowedValues.values())
100
- .map((a) => (a === undefined ? "(empty)" : `'${a}'`))
149
+ .map((a) => (a === undefined || a === REMOVE ? "(empty)" : `'${a}'`))
101
150
  .join(", ");
102
151
 
103
152
  context.addError({
@@ -113,5 +162,5 @@ export const packageScript = createRuleFactory<Options>({
113
162
  }
114
163
  }
115
164
  },
116
- validateOptions: Options.check,
165
+ validateOptions: Options.parse,
117
166
  });
@@ -8,17 +8,43 @@
8
8
  import { Context } from "@monorepolint/config";
9
9
  import { mutateJson, PackageJson } from "@monorepolint/utils";
10
10
  import { diff } from "jest-diff";
11
- import * as r from "runtypes";
11
+ import { z } from "zod";
12
+ import { REMOVE } from "./REMOVE.js";
12
13
  import { createRuleFactory } from "./util/createRuleFactory.js";
14
+ import { ZodRemove } from "./util/zodSchemas.js";
13
15
 
14
- const Options = r.Partial({
15
- dependencies: r.Dictionary(r.String.optional()),
16
- devDependencies: r.Dictionary(r.String.optional()),
17
- peerDependencies: r.Dictionary(r.String.optional()),
18
- optionalDependencies: r.Dictionary(r.String.optional()),
19
- });
16
+ const Options = z.object({
17
+ dependencies: z.record(
18
+ z.string(),
19
+ z.union([
20
+ z.string(),
21
+ ZodRemove,
22
+ ]).optional(),
23
+ ).optional(),
24
+ devDependencies: z.record(
25
+ z.string(),
26
+ z.union([
27
+ z.string(),
28
+ ZodRemove,
29
+ ]).optional(),
30
+ ).optional(),
31
+ peerDependencies: z.record(
32
+ z.string(),
33
+ z.union([
34
+ z.string(),
35
+ ZodRemove,
36
+ ]).optional(),
37
+ ).optional(),
38
+ optionalDependencies: z.record(
39
+ z.string(),
40
+ z.union([
41
+ z.string(),
42
+ ZodRemove,
43
+ ]).optional(),
44
+ ).optional(),
45
+ }).partial();
20
46
 
21
- type Options = r.Static<typeof Options>;
47
+ type Options = z.infer<typeof Options>;
22
48
 
23
49
  export const requireDependency = createRuleFactory({
24
50
  name: "requireDependency",
@@ -37,29 +63,40 @@ export const requireDependency = createRuleFactory({
37
63
  return;
38
64
  }
39
65
 
66
+ // Separate additions from removals upfront
67
+ const dependenciesToAdd = Object.entries(expectedEntries).filter(
68
+ ([, version]) => version !== REMOVE && version !== undefined,
69
+ );
70
+ const dependenciesToRemove = Object.entries(expectedEntries).filter(
71
+ ([, version]) => version === REMOVE,
72
+ );
73
+
74
+ // Handle missing dependency block
40
75
  if (packageJson[type] === undefined) {
41
- context.addError({
42
- file: packageJsonPath,
43
- message: `No ${type} block, cannot add required ${type}.`,
44
- fixer: () => {
45
- mutateJson<PackageJson>(packageJsonPath, context.host, (input) => {
46
- input[type] = Object.fromEntries(
47
- Object.entries(expectedEntries).filter(([, v]) => v !== undefined),
48
- ) as Record<string, string>;
49
- return input;
50
- });
51
- },
52
- });
53
- return;
76
+ // Only create block if there are dependencies to add (REMOVE entries shouldn't create blocks)
77
+ if (dependenciesToAdd.length > 0) {
78
+ context.addError({
79
+ file: packageJsonPath,
80
+ message: `No ${type} block, cannot add required ${type}.`,
81
+ fixer: () => {
82
+ mutateJson<PackageJson>(packageJsonPath, context.host, (input) => {
83
+ input[type] = Object.fromEntries(dependenciesToAdd) as Record<string, string>;
84
+ return input;
85
+ });
86
+ },
87
+ });
88
+ }
89
+ return; // Can't remove from non-existent block
54
90
  }
55
91
 
56
- for (const [dep, version] of Object.entries(options[type]!)) {
92
+ // Process additions
93
+ for (const [dep, version] of dependenciesToAdd) {
57
94
  if (packageJson[type]?.[dep] !== version) {
58
95
  context.addError({
59
96
  file: packageJsonPath,
60
- message: `Expected dependency ${dep}@${version}`,
97
+ message: `Expected dependency ${dep}@${version as string}`,
61
98
  longMessage: diff(
62
- `${dep}@${version}\n`,
99
+ `${dep}@${version as string}\n`,
63
100
  `${dep}@${packageJson[type]![dep] || "missing"}\n`,
64
101
  )!,
65
102
  fixer: () => {
@@ -67,12 +104,28 @@ export const requireDependency = createRuleFactory({
67
104
  packageJsonPath,
68
105
  context.host,
69
106
  (input) => {
70
- if (version === undefined) {
71
- input[type] = { ...input[type] };
72
- delete input[type][dep];
73
- } else {
74
- input[type] = { ...input[type], [dep]: version };
75
- }
107
+ input[type] = { ...input[type], [dep]: version as string };
108
+ return input;
109
+ },
110
+ );
111
+ },
112
+ });
113
+ }
114
+ }
115
+
116
+ // Process removals
117
+ for (const [dep] of dependenciesToRemove) {
118
+ if (packageJson[type]?.[dep] !== undefined) {
119
+ context.addError({
120
+ file: packageJsonPath,
121
+ message: `Dependency ${dep} should be removed`,
122
+ fixer: () => {
123
+ mutateJson<PackageJson>(
124
+ packageJsonPath,
125
+ context.host,
126
+ (input) => {
127
+ input[type] = { ...input[type] };
128
+ delete input[type][dep];
76
129
  return input;
77
130
  },
78
131
  );
@@ -82,5 +135,5 @@ export const requireDependency = createRuleFactory({
82
135
  }
83
136
  });
84
137
  },
85
- validateOptions: Options.check,
138
+ validateOptions: Options.parse,
86
139
  });
@@ -9,38 +9,38 @@ import { Context } from "@monorepolint/config";
9
9
  import { matchesAnyGlob } from "@monorepolint/utils";
10
10
  import { diff } from "jest-diff";
11
11
  import * as path from "path";
12
- import * as r from "runtypes";
12
+ import { z } from "zod";
13
13
  import { createRuleFactory } from "./util/createRuleFactory.js";
14
14
 
15
15
  const DEFAULT_TSCONFIG_FILENAME = "tsconfig.json";
16
16
 
17
- const Options = r
18
- .Partial({
19
- file: r.String,
20
- generator: r.Function,
21
- tsconfigReferenceFile: r.String,
22
- template: r.Record({}).Or(r.String),
23
- templateFile: r.String,
24
- excludedReferences: r.Array(r.String).Or(r.Undefined),
25
- additionalReferences: r.Array(r.String).Or(r.Undefined),
26
- })
27
- .withConstraint(({ generator, template, templateFile }) => {
28
- let count = 0;
29
- if (generator) {
30
- count++;
31
- }
32
- if (template) {
33
- count++;
34
- }
35
- if (templateFile) {
36
- count++;
37
- }
17
+ const Options = z.object({
18
+ file: z.string().optional(),
19
+ generator: z.custom<(context: Context) => Promise<string> | string>().optional(),
20
+ tsconfigReferenceFile: z.string().optional(),
21
+ template: z.record(z.string(), z.unknown()).optional(),
22
+ templateFile: z.string().optional(),
23
+ excludedReferences: z.array(z.string()).optional(),
24
+ additionalReferences: z.array(z.string()).optional(),
25
+ }).partial().refine(({ generator, template, templateFile }) => {
26
+ let count = 0;
27
+ if (generator) {
28
+ count++;
29
+ }
30
+ if (template) {
31
+ count++;
32
+ }
33
+ if (templateFile) {
34
+ count++;
35
+ }
38
36
 
39
- return count === 1 || "Expect one of { generator, template, templateFile }";
40
- });
37
+ return count === 1;
38
+ }, {
39
+ message: "Expect one of { generator, template, templateFile }",
40
+ });
41
41
 
42
42
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
43
- export interface Options extends r.Static<typeof Options> {}
43
+ export interface Options extends z.infer<typeof Options> {}
44
44
 
45
45
  export const standardTsconfig = createRuleFactory<Options>({
46
46
  name: "standardTsconfig",
@@ -75,7 +75,7 @@ export const standardTsconfig = createRuleFactory<Options>({
75
75
  });
76
76
  }
77
77
  },
78
- validateOptions: Options.check,
78
+ validateOptions: Options.parse,
79
79
  });
80
80
 
81
81
  function getGenerator(context: Context, opts: Options) {
@@ -0,0 +1,15 @@
1
+ /*!
2
+ * Copyright 2019 Palantir Technologies, Inc.
3
+ *
4
+ * Licensed under the MIT license. See LICENSE file in the project root for details.
5
+ *
6
+ */
7
+
8
+ import { z } from "zod";
9
+ import { REMOVE } from "../REMOVE.js";
10
+
11
+ /**
12
+ * Reusable Zod schema for the REMOVE symbol.
13
+ * This schema validates that a value is exactly the REMOVE symbol.
14
+ */
15
+ export const ZodRemove = z.custom<typeof REMOVE>((x) => x === REMOVE);