@monorepolint/rules 0.6.0-alpha.4 → 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 +443 -92
  5. package/.turbo/turbo-transpile-typescript.log +5 -5
  6. package/CHANGELOG.md +112 -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 +1420 -1452
  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 +15 -15
  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 +31 -31
  61. package/coverage/src/index.html +104 -89
  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 +31 -31
  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 +212 -212
  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 +15 -15
  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
@@ -10,6 +10,7 @@ import { readFileSync, writeFileSync } from "node:fs";
10
10
  import * as path from "node:path";
11
11
  import * as tmp from "tmp";
12
12
  import { afterEach, describe, expect, it, vi } from "vitest";
13
+ import { REMOVE } from "../REMOVE.js";
13
14
  import { requireDependency } from "../requireDependency.js";
14
15
  import { makeDirectoryRecursively } from "../util/makeDirectory.js";
15
16
  import { jsonToString } from "./utils.js";
@@ -55,11 +56,18 @@ const OPTIONS = {
55
56
  },
56
57
  devDependencies: {
57
58
  bar: "^2.0.0",
58
- baz: undefined,
59
+ baz: REMOVE,
59
60
  },
60
61
  } as const;
61
62
 
62
- const CORRECT_OUTPUT = jsonToString(OPTIONS);
63
+ const CORRECT_OUTPUT = jsonToString({
64
+ dependencies: {
65
+ foo: "1.0.0",
66
+ },
67
+ devDependencies: {
68
+ bar: "^2.0.0",
69
+ },
70
+ });
63
71
 
64
72
  describe("requireDependency", () => {
65
73
  tmp.setGracefulCleanup();
@@ -155,4 +163,253 @@ describe("requireDependency", () => {
155
163
  const contents = readFile("./packages/wrong/package.json");
156
164
  expect(contents).toEqual(CORRECT_OUTPUT);
157
165
  });
166
+
167
+ describe("Missing package.json handling", () => {
168
+ it("handles gracefully when package.json does not exist", () => {
169
+ const { workspaceContext } = makeWorkspace({ fix: false });
170
+ // Don't create a package.json file
171
+
172
+ expect(() => {
173
+ requireDependency({ options: { dependencies: { foo: "1.0.0" } } }).check(workspaceContext);
174
+ }).toThrow(); // Should throw when trying to get package.json
175
+ });
176
+ });
177
+
178
+ describe("Invalid package.json structure", () => {
179
+ it("handles package.json with non-object dependencies", () => {
180
+ const { addFile, checkAndSpy } = makeWorkspace({ fix: false });
181
+ addFile("./package.json", PACKAGE_ROOT);
182
+ addFile(
183
+ "./packages/test/package.json",
184
+ jsonToString({
185
+ name: "test-package",
186
+ dependencies: "invalid-dependencies", // Should be an object
187
+ }),
188
+ );
189
+
190
+ // This won't throw - it will treat dependencies as undefined and add an error
191
+ const { addErrorSpy } = checkAndSpy("./packages/test");
192
+ requireDependency({ options: { dependencies: { foo: "1.0.0" } } }).check(
193
+ checkAndSpy("./packages/test").context,
194
+ );
195
+
196
+ // Should add error for missing dependencies block
197
+ expect(addErrorSpy).toHaveBeenCalled();
198
+ });
199
+
200
+ it("handles malformed JSON in package.json", () => {
201
+ const { addFile, workspaceContext } = makeWorkspace({ fix: false });
202
+ addFile("./package.json", "{ invalid json }");
203
+
204
+ expect(() => {
205
+ requireDependency({ options: { dependencies: { foo: "1.0.0" } } }).check(workspaceContext);
206
+ }).toThrow(); // Should throw when parsing invalid JSON
207
+ });
208
+ });
209
+
210
+ describe("REMOVE symbol edge cases", () => {
211
+ it("handles REMOVE on non-existent dependency", () => {
212
+ const { addFile, workspaceContext } = makeWorkspace({ fix: true });
213
+ addFile("./package.json", PACKAGE_ROOT);
214
+ addFile(
215
+ "./packages/test/package.json",
216
+ jsonToString({
217
+ name: "test",
218
+ dependencies: {},
219
+ }),
220
+ );
221
+
222
+ const context = workspaceContext.createChildContext(
223
+ path.resolve(workspaceContext.packageDir, "packages/test"),
224
+ );
225
+ const addErrorSpy = vi.spyOn(context, "addError");
226
+
227
+ requireDependency({
228
+ options: {
229
+ dependencies: {
230
+ nonExistent: REMOVE,
231
+ },
232
+ },
233
+ }).check(context);
234
+
235
+ // Should not add any errors since the dependency doesn't exist (desired state achieved)
236
+ expect(addErrorSpy).toHaveBeenCalledTimes(0);
237
+ });
238
+
239
+ it("handles multiple REMOVE operations", () => {
240
+ const { addFile, readFile, workspaceContext } = makeWorkspace({ fix: true });
241
+ addFile("./package.json", PACKAGE_ROOT);
242
+ addFile(
243
+ "./packages/test/package.json",
244
+ jsonToString({
245
+ name: "test",
246
+ dependencies: {
247
+ toRemove1: "1.0.0",
248
+ toRemove2: "2.0.0",
249
+ toKeep: "3.0.0",
250
+ },
251
+ devDependencies: {
252
+ devToRemove: "1.0.0",
253
+ },
254
+ }),
255
+ );
256
+
257
+ const context = workspaceContext.createChildContext(
258
+ path.resolve(workspaceContext.packageDir, "packages/test"),
259
+ );
260
+ const addErrorSpy = vi.spyOn(context, "addError");
261
+
262
+ requireDependency({
263
+ options: {
264
+ dependencies: {
265
+ toRemove1: REMOVE,
266
+ toRemove2: REMOVE,
267
+ },
268
+ devDependencies: {
269
+ devToRemove: REMOVE,
270
+ },
271
+ },
272
+ }).check(context);
273
+
274
+ expect(addErrorSpy).toHaveBeenCalledTimes(3); // Three removals
275
+
276
+ const updatedPackage = JSON.parse(readFile("./packages/test/package.json"));
277
+ expect(updatedPackage.dependencies).toEqual({ toKeep: "3.0.0" });
278
+ expect(updatedPackage.devDependencies).toEqual({});
279
+ });
280
+
281
+ it("handles REMOVE when dependency section doesn't exist", () => {
282
+ const { addFile, workspaceContext } = makeWorkspace({ fix: false });
283
+ addFile("./package.json", PACKAGE_ROOT);
284
+ addFile(
285
+ "./packages/test/package.json",
286
+ jsonToString({
287
+ name: "test",
288
+ // No dependencies section at all
289
+ }),
290
+ );
291
+
292
+ const context = workspaceContext.createChildContext(
293
+ path.resolve(workspaceContext.packageDir, "packages/test"),
294
+ );
295
+ const addErrorSpy = vi.spyOn(context, "addError");
296
+
297
+ requireDependency({
298
+ options: {
299
+ dependencies: {
300
+ nonExistent: REMOVE,
301
+ },
302
+ },
303
+ }).check(context);
304
+
305
+ // No error should be added since we're only trying to remove from a non-existent block
306
+ // This is the improved behavior - don't create dependency blocks just for removal
307
+ expect(addErrorSpy).toHaveBeenCalledTimes(0);
308
+ });
309
+
310
+ it("handles mixed ADD and REMOVE when dependency section doesn't exist", () => {
311
+ const { addFile, readFile, workspaceContext } = makeWorkspace({ fix: true });
312
+ addFile("./package.json", PACKAGE_ROOT);
313
+ addFile(
314
+ "./packages/test/package.json",
315
+ jsonToString({
316
+ name: "test",
317
+ // No dependencies section at all
318
+ }),
319
+ );
320
+
321
+ const context = workspaceContext.createChildContext(
322
+ path.resolve(workspaceContext.packageDir, "packages/test"),
323
+ );
324
+ const addErrorSpy = vi.spyOn(context, "addError");
325
+
326
+ requireDependency({
327
+ options: {
328
+ dependencies: {
329
+ toAdd: "1.0.0",
330
+ nonExistent: REMOVE, // This shouldn't create the block
331
+ },
332
+ },
333
+ }).check(context);
334
+
335
+ // Should add error only for missing block needed for the ADD operation
336
+ expect(addErrorSpy).toHaveBeenCalledTimes(1);
337
+
338
+ const updatedPackage = JSON.parse(readFile("./packages/test/package.json"));
339
+ expect(updatedPackage.dependencies).toEqual({ toAdd: "1.0.0" });
340
+ });
341
+
342
+ it("handles mixed ADD and REMOVE with existing dependencies", () => {
343
+ const { addFile, readFile, workspaceContext } = makeWorkspace({ fix: true });
344
+ addFile("./package.json", PACKAGE_ROOT);
345
+ addFile(
346
+ "./packages/test/package.json",
347
+ jsonToString({
348
+ name: "test",
349
+ dependencies: {
350
+ existing: "1.0.0",
351
+ toRemove: "2.0.0",
352
+ },
353
+ }),
354
+ );
355
+
356
+ const context = workspaceContext.createChildContext(
357
+ path.resolve(workspaceContext.packageDir, "packages/test"),
358
+ );
359
+ const addErrorSpy = vi.spyOn(context, "addError");
360
+
361
+ requireDependency({
362
+ options: {
363
+ dependencies: {
364
+ toAdd: "3.0.0",
365
+ existing: "1.0.0", // Correct version, no error
366
+ toRemove: REMOVE,
367
+ },
368
+ },
369
+ }).check(context);
370
+
371
+ // Should add errors for ADD and REMOVE operations
372
+ expect(addErrorSpy).toHaveBeenCalledTimes(2);
373
+
374
+ const updatedPackage = JSON.parse(readFile("./packages/test/package.json"));
375
+ expect(updatedPackage.dependencies).toEqual({
376
+ existing: "1.0.0",
377
+ toAdd: "3.0.0",
378
+ });
379
+ });
380
+ });
381
+
382
+ describe("Options Validation", () => {
383
+ it("should accept valid options", () => {
384
+ const ruleModule = requireDependency({ options: {} });
385
+
386
+ expect(() => ruleModule.validateOptions({})).not.toThrow();
387
+ expect(() => ruleModule.validateOptions({ dependencies: { "react": "^18.0.0" } })).not
388
+ .toThrow();
389
+ expect(() =>
390
+ ruleModule.validateOptions({
391
+ devDependencies: { "typescript": "^5.0.0" },
392
+ peerDependencies: { "react": ">=16.0.0" },
393
+ })
394
+ ).not.toThrow();
395
+
396
+ // Optional versions (REMOVE)
397
+ expect(() =>
398
+ ruleModule.validateOptions({
399
+ dependencies: { "react": REMOVE },
400
+ })
401
+ ).not.toThrow();
402
+ });
403
+
404
+ it("should reject invalid options", () => {
405
+ const ruleModule = requireDependency({ options: {} });
406
+
407
+ // @ts-expect-error testing invalid input
408
+ expect(() => ruleModule.validateOptions({ dependencies: "react" })).toThrow();
409
+ // @ts-expect-error testing invalid input
410
+ expect(() => ruleModule.validateOptions({ dependencies: { "react": 123 } })).toThrow();
411
+ // @ts-expect-error testing invalid input
412
+ expect(() => ruleModule.validateOptions({ devDependencies: [] })).toThrow();
413
+ });
414
+ });
158
415
  });
@@ -0,0 +1,91 @@
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 { Context } from "@monorepolint/config";
9
+ import { beforeEach, describe, expect, it, vi } from "vitest";
10
+ import { standardTsconfig } from "../standardTsconfig.js";
11
+ import { AddErrorSpy, createTestingWorkspace, HOST_FACTORIES, TestingWorkspace } from "./utils.js";
12
+
13
+ describe.each(HOST_FACTORIES)("standardTsconfig ($name)", (hostFactory) => {
14
+ let workspace: TestingWorkspace;
15
+ let spy: AddErrorSpy;
16
+ let context: Context;
17
+
18
+ beforeEach(async () => {
19
+ workspace = await createTestingWorkspace({
20
+ fixFlag: true,
21
+ host: hostFactory.make(),
22
+ });
23
+ context = workspace.context;
24
+ spy = vi.spyOn(workspace.context, "addError");
25
+ });
26
+
27
+ it("creates tsconfig.json when missing with template", async () => {
28
+ const template = { compilerOptions: { strict: true } };
29
+
30
+ await standardTsconfig({ options: { template } }).check(context);
31
+
32
+ expect(spy).toHaveBeenCalledTimes(1);
33
+ expect(workspace.readFile("tsconfig.json")).toContain("\"strict\": true");
34
+ });
35
+
36
+ it("does nothing when tsconfig.json matches template", async () => {
37
+ const template = { compilerOptions: { strict: true } };
38
+ const expectedContent = JSON.stringify({ ...template, references: [] }, undefined, 2) + "\n";
39
+
40
+ workspace.writeFile("tsconfig.json", expectedContent);
41
+
42
+ await standardTsconfig({ options: { template } }).check(context);
43
+
44
+ expect(spy).toHaveBeenCalledTimes(0);
45
+ });
46
+
47
+ describe("Options Validation", () => {
48
+ it("should accept valid options", () => {
49
+ const ruleModule1 = standardTsconfig({
50
+ options: { template: { compilerOptions: { strict: true } } },
51
+ });
52
+ const ruleModule2 = standardTsconfig({ options: { templateFile: "tsconfig.template.json" } });
53
+ const ruleModule3 = standardTsconfig({
54
+ options: { generator: () => JSON.stringify({ compilerOptions: {} }) },
55
+ });
56
+
57
+ expect(() => ruleModule1.validateOptions({ template: { compilerOptions: { strict: true } } }))
58
+ .not.toThrow();
59
+ expect(() => ruleModule2.validateOptions({ templateFile: "tsconfig.template.json" })).not
60
+ .toThrow();
61
+ expect(() =>
62
+ ruleModule3.validateOptions({
63
+ generator: () => JSON.stringify({ compilerOptions: {} }),
64
+ excludedReferences: ["@types/*"],
65
+ })
66
+ ).not.toThrow();
67
+ });
68
+
69
+ it("should reject invalid options", () => {
70
+ const ruleModule = standardTsconfig({ options: { template: {} } });
71
+
72
+ // Missing required source
73
+ expect(() => ruleModule.validateOptions({})).toThrow();
74
+
75
+ // Multiple sources (violates constraint)
76
+ expect(() =>
77
+ ruleModule.validateOptions({
78
+ template: {},
79
+ templateFile: "file.json",
80
+ })
81
+ ).toThrow();
82
+
83
+ expect(() =>
84
+ ruleModule.validateOptions({
85
+ generator: () => "",
86
+ template: {},
87
+ })
88
+ ).toThrow();
89
+ });
90
+ });
91
+ });
@@ -6,10 +6,9 @@
6
6
  */
7
7
 
8
8
  import { Context } from "@monorepolint/config";
9
- import { matchesAnyGlob } from "@monorepolint/utils";
10
- import { AggregateTiming } from "@monorepolint/utils";
9
+ import { AggregateTiming, matchesAnyGlob } from "@monorepolint/utils";
11
10
  import * as path from "node:path";
12
- import * as r from "runtypes";
11
+ import { z } from "zod";
13
12
  import { createRuleFactory } from "./util/createRuleFactory.js";
14
13
  import {
15
14
  IPackageDependencyGraphNode,
@@ -17,30 +16,20 @@ import {
17
16
  } from "./util/packageDependencyGraphService.js";
18
17
  // FIXME: This rule is messed. bannedTransitiveDependencies doesnt glob
19
18
 
20
- const bannedDepGlobsField = r.Union(
21
- r.Array(r.String),
22
- r.Record({
23
- glob: r.Array(r.String).optional(),
24
- exact: r.Array(r.String).optional(),
19
+ const bannedDepGlobsField = z.union([
20
+ z.array(z.string()),
21
+ z.object({
22
+ glob: z.array(z.string()).optional(),
23
+ exact: z.array(z.string()).optional(),
25
24
  }),
26
- );
25
+ ]);
27
26
 
28
- const Options = r.Union(
29
- r.Record({
30
- bannedDependencies: bannedDepGlobsField,
31
- bannedTransitiveDependencies: r.Undefined.optional(),
32
- }),
33
- r.Record({
34
- bannedDependencies: bannedDepGlobsField.optional(),
35
- bannedTransitiveDependencies: r.Array(r.String),
36
- }),
37
- r.Record({
38
- bannedDependencies: bannedDepGlobsField.optional(),
39
- bannedTransitiveDependencies: r.Array(r.String).optional(),
40
- }),
41
- );
27
+ const Options = z.object({
28
+ bannedDependencies: bannedDepGlobsField.optional(),
29
+ bannedTransitiveDependencies: z.array(z.string()).optional(),
30
+ });
42
31
 
43
- export type Options = r.Static<typeof Options>;
32
+ export type Options = z.infer<typeof Options>;
44
33
 
45
34
  /**
46
35
  * We use this locally to avoid making a billion sets. Because check is called once per package
@@ -113,7 +102,7 @@ export const bannedDependencies = createRuleFactory<Options>({
113
102
 
114
103
  aggregateTiming.stop();
115
104
  },
116
- validateOptions: Options.check,
105
+ validateOptions: Options.parse,
117
106
  printStats: () => {
118
107
  aggregateTiming.printResults();
119
108
  },
@@ -7,14 +7,16 @@
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
- const Options = r
13
- .Record({
14
- ignoredDependencies: r.Array(r.String).Or(r.Undefined),
15
- })
16
- .Or(r.Undefined);
17
- export type Options = r.Static<typeof Options>;
12
+
13
+ const Options = z.union([
14
+ z.undefined(),
15
+ z.object({
16
+ ignoredDependencies: z.array(z.string()),
17
+ }),
18
+ ]);
19
+ export type Options = z.infer<typeof Options>;
18
20
 
19
21
  const skippedVersions = ["*", "latest"];
20
22
 
@@ -25,7 +27,7 @@ export const consistentDependencies = createRuleFactory<Options>({
25
27
  checkDeps(context, args, "devDependencies");
26
28
  // we don't check peer deps since they can be more lenient
27
29
  },
28
- validateOptions: Options.check,
30
+ validateOptions: Options.parse,
29
31
  });
30
32
 
31
33
  function checkDeps(