@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
@@ -1301,4 +1301,48 @@ describe("mustSatisfyPeerDependencies", () => {
1301
1301
  expect(addErrorSpy).toHaveBeenCalledTimes(2);
1302
1302
  addErrorSpy.mockReset();
1303
1303
  });
1304
+
1305
+ describe("Options Validation", () => {
1306
+ it("should accept valid options", () => {
1307
+ const ruleModule = mustSatisfyPeerDependencies({ options: {} });
1308
+
1309
+ // Based on the complex union type, {} with all undefined properties is valid
1310
+ expect(() => ruleModule.validateOptions({})).not.toThrow();
1311
+ expect(() =>
1312
+ ruleModule.validateOptions({
1313
+ skipUnparseableRanges: undefined,
1314
+ dependencyWhitelist: undefined,
1315
+ dependencyBlacklist: undefined,
1316
+ enforceForDevDependencies: undefined,
1317
+ })
1318
+ ).not.toThrow();
1319
+ expect(() => ruleModule.validateOptions({ skipUnparseableRanges: true })).not.toThrow();
1320
+ expect(() => ruleModule.validateOptions({ dependencyWhitelist: ["react", "react-dom"] })).not
1321
+ .toThrow();
1322
+ expect(() => ruleModule.validateOptions({ dependencyBlacklist: ["lodash"] })).not.toThrow();
1323
+ expect(() => ruleModule.validateOptions({ enforceForDevDependencies: true })).not.toThrow();
1324
+
1325
+ // Combinations
1326
+ expect(() =>
1327
+ ruleModule.validateOptions({
1328
+ skipUnparseableRanges: true,
1329
+ dependencyWhitelist: ["react"],
1330
+ })
1331
+ ).not.toThrow();
1332
+ });
1333
+
1334
+ it("should reject invalid options", () => {
1335
+ const ruleModule = mustSatisfyPeerDependencies({ options: {} });
1336
+
1337
+ // Note: undefined alone doesn't match any of the union variants
1338
+ // @ts-expect-error testing invalid input
1339
+ expect(() => ruleModule.validateOptions(undefined)).toThrow();
1340
+ // @ts-expect-error testing invalid input
1341
+ expect(() => ruleModule.validateOptions({ skipUnparseableRanges: "true" })).toThrow();
1342
+ // @ts-expect-error testing invalid input
1343
+ expect(() => ruleModule.validateOptions({ dependencyWhitelist: "react" })).toThrow();
1344
+ // @ts-expect-error testing invalid input
1345
+ expect(() => ruleModule.validateOptions({ dependencyBlacklist: 123 })).toThrow();
1346
+ });
1347
+ });
1304
1348
  });
@@ -153,4 +153,18 @@ describe("nestedWorkspaces", () => {
153
153
 
154
154
  expect((await checkAndSpy()).addErrorSpy).toHaveBeenCalledTimes(0);
155
155
  });
156
+
157
+ describe("Options Validation", () => {
158
+ it("should accept undefined options", () => {
159
+ const ruleModule = nestedWorkspaces({ options: undefined });
160
+ expect(() => ruleModule.validateOptions(undefined)).not.toThrow();
161
+ });
162
+
163
+ it("should reject non-undefined options", () => {
164
+ const ruleModule = nestedWorkspaces({ options: undefined });
165
+ expect(() => ruleModule.validateOptions({})).toThrow();
166
+ expect(() => ruleModule.validateOptions("string")).toThrow();
167
+ expect(() => ruleModule.validateOptions([])).toThrow();
168
+ });
169
+ });
156
170
  });
@@ -0,0 +1,75 @@
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 { oncePerPackage } from "../oncePerPackage.js";
11
+ import { AddErrorSpy, createTestingWorkspace, HOST_FACTORIES, TestingWorkspace } from "./utils.js";
12
+
13
+ describe.each(HOST_FACTORIES)("oncePerPackage ($name)", (hostFactory) => {
14
+ let workspace: TestingWorkspace;
15
+ let spy: AddErrorSpy;
16
+ let context: Context;
17
+
18
+ beforeEach(async () => {
19
+ workspace = await createTestingWorkspace({
20
+ fixFlag: false,
21
+ host: hostFactory.make(),
22
+ });
23
+ context = workspace.context;
24
+ spy = vi.spyOn(workspace.context, "addError");
25
+ });
26
+
27
+ it("allows first visit to a package", () => {
28
+ const uniqueKey = `test-key-${Math.random()}`;
29
+ oncePerPackage({ options: { singletonKey: uniqueKey } }).check(context);
30
+
31
+ expect(spy).toHaveBeenCalledTimes(0);
32
+ });
33
+
34
+ it("prevents second visit to the same package with same key", () => {
35
+ const uniqueKey = `test-key-${Math.random()}`;
36
+ const rule = oncePerPackage({ options: { singletonKey: uniqueKey } });
37
+
38
+ // First visit - should be fine
39
+ rule.check(context);
40
+ expect(spy).toHaveBeenCalledTimes(0);
41
+
42
+ // Second visit - should error
43
+ rule.check(context);
44
+ expect(spy).toHaveBeenCalledTimes(1);
45
+ expect(spy).toHaveBeenCalledWith({
46
+ message: `This package has already been visited for this key: ${uniqueKey}`,
47
+ file: context.getPackageJsonPath(),
48
+ });
49
+ });
50
+
51
+ describe("Options Validation", () => {
52
+ it("should accept valid options", () => {
53
+ const ruleModule = oncePerPackage({ options: { singletonKey: "unique-key" } });
54
+
55
+ expect(() => ruleModule.validateOptions({ singletonKey: "unique-key" })).not.toThrow();
56
+ expect(() =>
57
+ ruleModule.validateOptions({
58
+ singletonKey: Symbol("unique"),
59
+ customMessage: "Custom error message",
60
+ })
61
+ ).not.toThrow();
62
+ });
63
+
64
+ it("should reject invalid options", () => {
65
+ const ruleModule = oncePerPackage({ options: { singletonKey: "unique-key" } });
66
+
67
+ // @ts-expect-error testing invalid input
68
+ expect(() => ruleModule.validateOptions({})).toThrow();
69
+ // @ts-expect-error testing invalid input
70
+ expect(() => ruleModule.validateOptions({ singletonKey: 123 })).toThrow();
71
+ // @ts-expect-error testing invalid input
72
+ expect(() => ruleModule.validateOptions({ customMessage: "test" })).toThrow();
73
+ });
74
+ });
75
+ });
@@ -11,9 +11,11 @@ import { Context, Failure } from "@monorepolint/config";
11
11
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
12
12
  import {
13
13
  createExpectedEntryErrorMessage,
14
+ createRemovalEntryErrorMessage,
14
15
  createStandardizedEntryErrorMessage,
15
16
  packageEntry,
16
17
  } from "../packageEntry.js";
18
+ import { REMOVE } from "../REMOVE.js";
17
19
  import { AddErrorSpy, createTestingWorkspace, HOST_FACTORIES, TestingWorkspace } from "./utils.js";
18
20
 
19
21
  const PACKAGE_MISSING_ENTRY = JSON.stringify(
@@ -54,6 +56,32 @@ const PACKAGE_REPOSITORY = JSON.stringify(
54
56
  2,
55
57
  ) + "\n";
56
58
 
59
+ const PACKAGE_WITH_SCRIPTS = JSON.stringify(
60
+ {
61
+ name: "package",
62
+ version: {},
63
+ scripts: {
64
+ build: "tsc",
65
+ test: "jest",
66
+ },
67
+ dependencies: {},
68
+ license: "UNLICENSED",
69
+ },
70
+ undefined,
71
+ 2,
72
+ ) + "\n";
73
+
74
+ const PACKAGE_WITHOUT_SCRIPTS = JSON.stringify(
75
+ {
76
+ name: "package",
77
+ version: {},
78
+ dependencies: {},
79
+ license: "UNLICENSED",
80
+ },
81
+ undefined,
82
+ 2,
83
+ ) + "\n";
84
+
57
85
  describe.each(HOST_FACTORIES)("expectPackageEntries ($name)", (hostFactory) => {
58
86
  describe("fix: true", () => {
59
87
  let workspace: TestingWorkspace;
@@ -208,5 +236,154 @@ describe.each(HOST_FACTORIES)("expectPackageEntries ($name)", (hostFactory) => {
208
236
 
209
237
  expect(workspace.readFile("package.json")).toEqual(PACKAGE_REPOSITORY);
210
238
  });
239
+
240
+ it("removes existing entries when REMOVE is specified", () => {
241
+ workspace.writeFile("package.json", PACKAGE_WITH_SCRIPTS);
242
+
243
+ packageEntry({
244
+ options: {
245
+ entries: {
246
+ scripts: REMOVE,
247
+ },
248
+ entriesExist: undefined,
249
+ },
250
+ }).check(context);
251
+
252
+ expect(spy).toHaveBeenCalledTimes(1);
253
+
254
+ const failure: Failure = spy.mock.calls[0][0];
255
+ expect(failure).toMatchObject(
256
+ workspace.failureMatcher({
257
+ file: "package.json",
258
+ hasFixer: true,
259
+ message: createRemovalEntryErrorMessage("scripts"),
260
+ }),
261
+ );
262
+
263
+ expect(workspace.readFile("package.json")).toEqual(PACKAGE_WITHOUT_SCRIPTS);
264
+ });
265
+
266
+ it("does not error when REMOVE is specified for non-existent entry", () => {
267
+ workspace.writeFile("package.json", PACKAGE_WITHOUT_SCRIPTS);
268
+
269
+ packageEntry({
270
+ options: {
271
+ entries: {
272
+ scripts: REMOVE,
273
+ },
274
+ entriesExist: undefined,
275
+ },
276
+ }).check(context);
277
+
278
+ expect(spy).toHaveBeenCalledTimes(0);
279
+ expect(workspace.readFile("package.json")).toEqual(PACKAGE_WITHOUT_SCRIPTS);
280
+ });
281
+
282
+ it("handles mix of REMOVE and regular entries", () => {
283
+ workspace.writeFile("package.json", PACKAGE_WITH_SCRIPTS);
284
+
285
+ packageEntry({
286
+ options: {
287
+ entries: {
288
+ scripts: REMOVE,
289
+ description: "A test package",
290
+ },
291
+ entriesExist: undefined,
292
+ },
293
+ }).check(context);
294
+
295
+ expect(spy).toHaveBeenCalledTimes(2);
296
+
297
+ const failure1: Failure = spy.mock.calls[0][0];
298
+ expect(failure1).toMatchObject(
299
+ workspace.failureMatcher({
300
+ file: "package.json",
301
+ hasFixer: true,
302
+ message: createRemovalEntryErrorMessage("scripts"),
303
+ }),
304
+ );
305
+
306
+ const failure2: Failure = spy.mock.calls[1][0];
307
+ expect(failure2).toMatchObject(
308
+ workspace.failureMatcher({
309
+ file: "package.json",
310
+ hasFixer: true,
311
+ message: createStandardizedEntryErrorMessage("description"),
312
+ }),
313
+ );
314
+
315
+ const expectedPackage = JSON.stringify(
316
+ {
317
+ name: "package",
318
+ version: {},
319
+ dependencies: {},
320
+ license: "UNLICENSED",
321
+ description: "A test package",
322
+ },
323
+ undefined,
324
+ 2,
325
+ ) + "\n";
326
+
327
+ expect(workspace.readFile("package.json")).toEqual(expectedPackage);
328
+ });
329
+
330
+ it("handles REMOVE with entriesExist", () => {
331
+ workspace.writeFile("package.json", PACKAGE_WITH_SCRIPTS);
332
+
333
+ packageEntry({
334
+ options: {
335
+ entries: {
336
+ scripts: REMOVE,
337
+ },
338
+ entriesExist: ["license"],
339
+ },
340
+ }).check(context);
341
+
342
+ expect(spy).toHaveBeenCalledTimes(1);
343
+
344
+ const failure: Failure = spy.mock.calls[0][0];
345
+ expect(failure).toMatchObject(
346
+ workspace.failureMatcher({
347
+ file: "package.json",
348
+ hasFixer: true,
349
+ message: createRemovalEntryErrorMessage("scripts"),
350
+ }),
351
+ );
352
+
353
+ expect(workspace.readFile("package.json")).toEqual(PACKAGE_WITHOUT_SCRIPTS);
354
+ });
355
+ });
356
+
357
+ describe("Options Validation", () => {
358
+ it("should accept valid options", () => {
359
+ const ruleModule = packageEntry({ options: { entries: { name: "@scope/package" } } });
360
+
361
+ expect(() =>
362
+ ruleModule.validateOptions({ entries: { name: "@scope/package", version: "1.0.0" } })
363
+ ).not.toThrow();
364
+ expect(() => ruleModule.validateOptions({ entriesExist: ["name", "version"] })).not.toThrow();
365
+ expect(() =>
366
+ ruleModule.validateOptions({
367
+ entries: { license: "MIT" },
368
+ entriesExist: ["description"],
369
+ })
370
+ ).not.toThrow();
371
+ expect(() =>
372
+ ruleModule.validateOptions({
373
+ entries: { scripts: REMOVE, name: "@scope/package" },
374
+ })
375
+ ).not.toThrow();
376
+ });
377
+
378
+ it("should reject invalid options", () => {
379
+ const ruleModule = packageEntry({ options: { entries: { name: "@scope/package" } } });
380
+
381
+ expect(() => ruleModule.validateOptions({})).toThrow();
382
+
383
+ // @ts-expect-error testing invalid input
384
+ expect(() => ruleModule.validateOptions({ entriesExist: "name" })).toThrow();
385
+ // @ts-expect-error testing invalid input
386
+ expect(() => ruleModule.validateOptions({ entries: "invalid" })).toThrow();
387
+ });
211
388
  });
212
389
  });
@@ -182,4 +182,26 @@ describe.each(HOST_FACTORIES)("expectPackageOrder ($name)", (hostFactory) => {
182
182
  );
183
183
  });
184
184
  });
185
+
186
+ describe("Options Validation", () => {
187
+ it("should accept valid options", () => {
188
+ const ruleModule = packageOrder({ options: undefined });
189
+
190
+ expect(() => ruleModule.validateOptions(undefined)).not.toThrow();
191
+ expect(() => ruleModule.validateOptions({ order: ["name", "version", "scripts"] })).not
192
+ .toThrow();
193
+ expect(() =>
194
+ ruleModule.validateOptions({ order: () => (a: string, b: string) => a.localeCompare(b) })
195
+ ).not.toThrow();
196
+ });
197
+
198
+ it("should reject invalid options", () => {
199
+ const ruleModule = packageOrder({ options: undefined });
200
+
201
+ // @ts-expect-error testing invalid input
202
+ expect(() => ruleModule.validateOptions({ order: "name,version" })).toThrow();
203
+ // @ts-expect-error testing invalid input
204
+ expect(() => ruleModule.validateOptions({ order: [123, "version"] })).toThrow();
205
+ });
206
+ });
185
207
  });