@monorepolint/rules 0.5.0-alpha.11 → 0.5.0-alpha.124

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 (171) hide show
  1. package/.turbo/turbo-clean.log +4 -0
  2. package/.turbo/turbo-compile-typescript.log +4 -0
  3. package/.turbo/turbo-lint.log +14 -0
  4. package/.turbo/turbo-test.log +666 -0
  5. package/.turbo/turbo-transpile-typescript.log +18 -0
  6. package/build/js/chunk-A2MUUSNE.js +26 -0
  7. package/build/js/chunk-A2MUUSNE.js.map +1 -0
  8. package/build/js/index.js +1402 -0
  9. package/build/js/index.js.map +1 -0
  10. package/build/js/public/util.js +7 -0
  11. package/build/js/public/util.js.map +1 -0
  12. package/build/tsconfig.tsbuildinfo +1 -0
  13. package/build/types/__tests__/alphabeticalScripts.spec.d.ts.map +1 -0
  14. package/build/types/__tests__/bannedDependencies.spec.d.ts +2 -0
  15. package/build/types/__tests__/bannedDependencies.spec.d.ts.map +1 -0
  16. package/build/types/__tests__/consistentDependencies.spec.d.ts.map +1 -0
  17. package/build/types/__tests__/consistentVersions.spec.d.ts +8 -0
  18. package/build/types/__tests__/consistentVersions.spec.d.ts.map +1 -0
  19. package/build/types/__tests__/fileContents.spec.d.ts +8 -0
  20. package/build/types/__tests__/fileContents.spec.d.ts.map +1 -0
  21. package/build/types/__tests__/mustSatisfyPeerDependencies.spec.d.ts +8 -0
  22. package/build/types/__tests__/mustSatisfyPeerDependencies.spec.d.ts.map +1 -0
  23. package/build/types/__tests__/nestedWorkspaces.spec.d.ts +2 -0
  24. package/build/types/__tests__/nestedWorkspaces.spec.d.ts.map +1 -0
  25. package/build/types/__tests__/packageEntry.spec.d.ts.map +1 -0
  26. package/build/types/__tests__/packageOrder.spec.d.ts.map +1 -0
  27. package/build/types/__tests__/packageScript.spec.d.ts.map +1 -0
  28. package/build/types/__tests__/requireDependency.spec.d.ts +2 -0
  29. package/build/types/__tests__/requireDependency.spec.d.ts.map +1 -0
  30. package/build/types/__tests__/utils.d.ts +81 -0
  31. package/build/types/__tests__/utils.d.ts.map +1 -0
  32. package/build/types/alphabeticalDependencies.d.ts +8 -0
  33. package/build/types/alphabeticalDependencies.d.ts.map +1 -0
  34. package/build/types/alphabeticalScripts.d.ts +8 -0
  35. package/build/types/alphabeticalScripts.d.ts.map +1 -0
  36. package/build/types/bannedDependencies.d.ts +66 -0
  37. package/build/types/bannedDependencies.d.ts.map +1 -0
  38. package/build/types/consistentDependencies.d.ts +18 -0
  39. package/build/types/consistentDependencies.d.ts.map +1 -0
  40. package/build/types/consistentVersions.d.ts +25 -0
  41. package/build/types/consistentVersions.d.ts.map +1 -0
  42. package/build/types/fileContents.d.ts +39 -0
  43. package/build/types/fileContents.d.ts.map +1 -0
  44. package/build/types/index.d.ts +20 -0
  45. package/build/types/index.d.ts.map +1 -0
  46. package/build/types/mustSatisfyPeerDependencies.d.ts +427 -0
  47. package/build/types/mustSatisfyPeerDependencies.d.ts.map +1 -0
  48. package/build/types/nestedWorkspaces.d.ts +10 -0
  49. package/build/types/nestedWorkspaces.d.ts.map +1 -0
  50. package/build/types/packageEntry.d.ts +62 -0
  51. package/build/types/packageEntry.d.ts.map +1 -0
  52. package/build/types/packageOrder.d.ts +12 -0
  53. package/build/types/packageOrder.d.ts.map +1 -0
  54. package/build/types/packageScript.d.ts +47 -0
  55. package/build/types/packageScript.d.ts.map +1 -0
  56. package/build/types/public/util.d.ts +2 -0
  57. package/build/types/public/util.d.ts.map +1 -0
  58. package/build/types/requireDependency.d.ts +50 -0
  59. package/build/types/requireDependency.d.ts.map +1 -0
  60. package/build/types/standardTsconfig.d.ts +29 -0
  61. package/build/types/standardTsconfig.d.ts.map +1 -0
  62. package/{lib → build/types}/util/checkAlpha.d.ts +2 -1
  63. package/build/types/util/checkAlpha.d.ts.map +1 -0
  64. package/build/types/util/makeDirectory.d.ts +8 -0
  65. package/build/types/util/makeDirectory.d.ts.map +1 -0
  66. package/build/types/util/makeRule.d.ts +12 -0
  67. package/build/types/util/makeRule.d.ts.map +1 -0
  68. package/build/types/util/packageDependencyGraphService.d.ts +37 -0
  69. package/build/types/util/packageDependencyGraphService.d.ts.map +1 -0
  70. package/package.json +45 -20
  71. package/src/__tests__/alphabeticalScripts.spec.ts +33 -31
  72. package/src/__tests__/bannedDependencies.spec.ts +189 -0
  73. package/src/__tests__/consistentDependencies.spec.ts +42 -25
  74. package/src/__tests__/consistentVersions.spec.ts +224 -0
  75. package/src/__tests__/fileContents.spec.ts +101 -0
  76. package/src/__tests__/mustSatisfyPeerDependencies.spec.ts +1189 -0
  77. package/src/__tests__/nestedWorkspaces.spec.ts +153 -0
  78. package/src/__tests__/packageEntry.spec.ts +127 -49
  79. package/src/__tests__/packageOrder.spec.ts +68 -53
  80. package/src/__tests__/packageScript.spec.ts +124 -98
  81. package/src/__tests__/requireDependency.spec.ts +152 -0
  82. package/src/__tests__/utils.ts +113 -13
  83. package/src/alphabeticalDependencies.ts +7 -9
  84. package/src/alphabeticalScripts.ts +7 -9
  85. package/src/bannedDependencies.ts +135 -46
  86. package/src/consistentDependencies.ts +41 -17
  87. package/src/consistentVersions.ts +141 -0
  88. package/src/fileContents.ts +44 -37
  89. package/src/index.ts +13 -9
  90. package/src/mustSatisfyPeerDependencies.ts +744 -0
  91. package/src/nestedWorkspaces.ts +60 -0
  92. package/src/packageEntry.ts +72 -28
  93. package/src/packageOrder.ts +17 -13
  94. package/src/packageScript.ts +15 -19
  95. package/src/public/util.ts +1 -0
  96. package/src/requireDependency.ts +71 -0
  97. package/src/standardTsconfig.ts +51 -27
  98. package/src/util/checkAlpha.ts +9 -6
  99. package/src/util/makeDirectory.ts +24 -0
  100. package/src/util/makeRule.ts +29 -0
  101. package/src/util/packageDependencyGraphService.ts +114 -0
  102. package/tsconfig.json +10 -2
  103. package/lib/__tests__/alphabeticalScripts.spec.d.ts.map +0 -1
  104. package/lib/__tests__/alphabeticalScripts.spec.js +0 -61
  105. package/lib/__tests__/alphabeticalScripts.spec.js.map +0 -1
  106. package/lib/__tests__/consistentDependencies.spec.d.ts.map +0 -1
  107. package/lib/__tests__/consistentDependencies.spec.js +0 -106
  108. package/lib/__tests__/consistentDependencies.spec.js.map +0 -1
  109. package/lib/__tests__/packageEntry.spec.d.ts.map +0 -1
  110. package/lib/__tests__/packageEntry.spec.js +0 -99
  111. package/lib/__tests__/packageEntry.spec.js.map +0 -1
  112. package/lib/__tests__/packageOrder.spec.d.ts.map +0 -1
  113. package/lib/__tests__/packageOrder.spec.js +0 -115
  114. package/lib/__tests__/packageOrder.spec.js.map +0 -1
  115. package/lib/__tests__/packageScript.spec.d.ts.map +0 -1
  116. package/lib/__tests__/packageScript.spec.js +0 -172
  117. package/lib/__tests__/packageScript.spec.js.map +0 -1
  118. package/lib/__tests__/utils.d.ts +0 -9
  119. package/lib/__tests__/utils.d.ts.map +0 -1
  120. package/lib/__tests__/utils.js +0 -27
  121. package/lib/__tests__/utils.js.map +0 -1
  122. package/lib/alphabeticalDependencies.d.ts +0 -12
  123. package/lib/alphabeticalDependencies.d.ts.map +0 -1
  124. package/lib/alphabeticalDependencies.js +0 -21
  125. package/lib/alphabeticalDependencies.js.map +0 -1
  126. package/lib/alphabeticalScripts.d.ts +0 -12
  127. package/lib/alphabeticalScripts.d.ts.map +0 -1
  128. package/lib/alphabeticalScripts.js +0 -19
  129. package/lib/alphabeticalScripts.js.map +0 -1
  130. package/lib/bannedDependencies.d.ts +0 -15
  131. package/lib/bannedDependencies.d.ts.map +0 -1
  132. package/lib/bannedDependencies.js +0 -57
  133. package/lib/bannedDependencies.js.map +0 -1
  134. package/lib/consistentDependencies.d.ts +0 -10
  135. package/lib/consistentDependencies.d.ts.map +0 -1
  136. package/lib/consistentDependencies.js +0 -57
  137. package/lib/consistentDependencies.js.map +0 -1
  138. package/lib/fileContents.d.ts +0 -25
  139. package/lib/fileContents.d.ts.map +0 -1
  140. package/lib/fileContents.js +0 -77
  141. package/lib/fileContents.js.map +0 -1
  142. package/lib/index.d.ts +0 -16
  143. package/lib/index.d.ts.map +0 -1
  144. package/lib/index.js +0 -27
  145. package/lib/index.js.map +0 -1
  146. package/lib/packageEntry.d.ts +0 -16
  147. package/lib/packageEntry.d.ts.map +0 -1
  148. package/lib/packageEntry.js +0 -40
  149. package/lib/packageEntry.js.map +0 -1
  150. package/lib/packageOrder.d.ts +0 -12
  151. package/lib/packageOrder.d.ts.map +0 -1
  152. package/lib/packageOrder.js +0 -103
  153. package/lib/packageOrder.js.map +0 -1
  154. package/lib/packageScript.d.ts +0 -25
  155. package/lib/packageScript.d.ts.map +0 -1
  156. package/lib/packageScript.js +0 -89
  157. package/lib/packageScript.js.map +0 -1
  158. package/lib/standardTsconfig.d.ts +0 -33
  159. package/lib/standardTsconfig.d.ts.map +0 -1
  160. package/lib/standardTsconfig.js +0 -98
  161. package/lib/standardTsconfig.js.map +0 -1
  162. package/lib/util/checkAlpha.d.ts.map +0 -1
  163. package/lib/util/checkAlpha.js +0 -47
  164. package/lib/util/checkAlpha.js.map +0 -1
  165. package/tsconfig.tsbuildinfo +0 -2484
  166. /package/{lib → build/types}/__tests__/alphabeticalScripts.spec.d.ts +0 -0
  167. /package/{lib → build/types}/__tests__/consistentDependencies.spec.d.ts +0 -0
  168. /package/{lib → build/types}/__tests__/packageEntry.spec.d.ts +0 -0
  169. /package/{lib → build/types}/__tests__/packageOrder.spec.d.ts +0 -0
  170. /package/{lib → build/types}/__tests__/packageScript.spec.d.ts +0 -0
  171. /package/{jest.config.js → jest.config.cjs} +0 -0
@@ -6,13 +6,10 @@
6
6
  */
7
7
 
8
8
  // tslint:disable:no-console
9
- import { createMockFiles } from "./utils";
10
-
11
- // done first since this also mocks 'fs'
12
- const mockFiles: Map<string, string> = createMockFiles();
13
-
14
- import { Failure, PackageContext } from "@monorepolint/core";
15
- import { packageScript } from "../packageScript";
9
+ import { describe, expect, it, beforeEach, jest } from "@jest/globals";
10
+ import { Context, Failure } from "@monorepolint/config";
11
+ import { packageScript } from "../packageScript.js";
12
+ import { AddErrorSpy, createTestingWorkspace, HOST_FACTORIES, TestingWorkspace } from "./utils.js";
16
13
 
17
14
  const json = (a: unknown) => JSON.stringify(a, undefined, 2) + "\n";
18
15
 
@@ -33,120 +30,143 @@ const PACKAGE_WITH_SCRIPTS = json({
33
30
  },
34
31
  });
35
32
 
36
- describe("expectPackageScript", () => {
37
- afterEach(() => {
38
- mockFiles.clear();
39
- });
40
-
33
+ describe.each(HOST_FACTORIES)("expectPackageScript ($name)", (hostFactory) => {
41
34
  describe("fix: false", () => {
42
- const context = new PackageContext(".", {
43
- rules: [],
44
- fix: false,
45
- verbose: false,
46
- silent: true,
47
- });
35
+ let workspace: TestingWorkspace;
36
+ let spy: AddErrorSpy;
48
37
 
49
- const spy = jest.spyOn(context, "addError");
38
+ beforeEach(async () => {
39
+ workspace = await createTestingWorkspace({
40
+ fixFlag: false,
41
+ host: hostFactory.make(),
42
+ });
50
43
 
51
- afterEach(() => {
52
- spy.mockClear();
44
+ spy = jest.spyOn(workspace.context, "addError");
53
45
  });
54
46
 
55
47
  it("handles an empty script section", () => {
56
- mockFiles.set("package.json", PACKAGE_WITHOUT_SCRIPTS);
48
+ workspace.writeFile("package.json", PACKAGE_WITHOUT_SCRIPTS);
57
49
 
58
- packageScript.check(context, {
59
- scripts: {
60
- foo: "bar",
50
+ packageScript({
51
+ options: {
52
+ scripts: {
53
+ foo: "bar",
54
+ },
61
55
  },
62
- });
56
+ }).check(workspace.context);
63
57
 
64
58
  expect(spy).toHaveBeenCalledTimes(1);
65
59
 
66
60
  const failure: Failure = spy.mock.calls[0][0];
67
- expect(failure.file).toBe("package.json");
68
- expect(failure.fixer).not.toBeUndefined();
69
- expect(failure.message).toBe("No scripts block in package.json");
61
+ expect(failure).toMatchObject(
62
+ workspace.failureMatcher({
63
+ file: "package.json",
64
+ hasFixer: true,
65
+ message: "No scripts block in package.json",
66
+ })
67
+ );
70
68
  });
71
69
  });
72
70
 
73
71
  describe("fix: true", () => {
74
- const context = new PackageContext(".", {
75
- rules: [],
76
- fix: true,
77
- verbose: false,
78
- silent: true,
79
- });
80
- const spy = jest.spyOn(context, "addError");
72
+ let workspace: TestingWorkspace;
73
+ let spy: AddErrorSpy;
74
+ let context: Context;
75
+
76
+ beforeEach(async () => {
77
+ workspace = await createTestingWorkspace({
78
+ fixFlag: true,
79
+ host: hostFactory.make(),
80
+ });
81
81
 
82
- afterEach(() => {
83
- spy.mockClear();
82
+ spy = jest.spyOn(workspace.context, "addError");
83
+ context = workspace.context; // minimizing delta
84
84
  });
85
85
 
86
86
  it("fixes an empty script section", () => {
87
- mockFiles.set("package.json", PACKAGE_WITHOUT_SCRIPTS);
87
+ workspace.writeFile("package.json", PACKAGE_WITHOUT_SCRIPTS);
88
88
 
89
- packageScript.check(context, {
90
- scripts: {
91
- foo: "bar",
89
+ packageScript({
90
+ options: {
91
+ scripts: {
92
+ foo: "bar",
93
+ },
92
94
  },
93
- });
95
+ }).check(context);
94
96
 
95
97
  expect(spy).toHaveBeenCalledTimes(1);
96
98
 
97
99
  const failure: Failure = spy.mock.calls[0][0];
98
- expect(failure.file).toBe("package.json");
99
- expect(failure.fixer).not.toBeUndefined();
100
- expect(failure.message).toBe("No scripts block in package.json");
101
-
102
- expect(JSON.parse(mockFiles.get("package.json")!).scripts).toEqual({});
100
+ expect(failure).toMatchObject(
101
+ workspace.failureMatcher({
102
+ file: "package.json",
103
+ hasFixer: true,
104
+ message: "No scripts block in package.json",
105
+ })
106
+ );
107
+
108
+ expect(JSON.parse(workspace.readFile("package.json")!).scripts).toEqual({});
103
109
  });
104
110
 
105
111
  it("adds a script", () => {
106
- mockFiles.set("package.json", PACKAGE_WITH_SCRIPTS);
112
+ workspace.writeFile("package.json", PACKAGE_WITH_SCRIPTS);
107
113
 
108
- packageScript.check(context, {
109
- scripts: {
110
- [MISSING_SCRIPT_NAME]: MISSING_SCRIPT_VALUE,
114
+ packageScript({
115
+ options: {
116
+ scripts: {
117
+ [MISSING_SCRIPT_NAME]: MISSING_SCRIPT_VALUE,
118
+ },
111
119
  },
112
- });
120
+ }).check(context);
113
121
 
114
122
  expect(spy).toHaveBeenCalledTimes(1);
115
123
 
116
124
  const failure: Failure = spy.mock.calls[0][0];
117
- expect(failure.file).toBe("package.json");
118
- expect(failure.fixer).not.toBeUndefined();
119
- expect(failure.message).toMatch(`Expected standardized script entry for '${MISSING_SCRIPT_NAME}'`);
120
-
121
- expect(JSON.parse(mockFiles.get("package.json")!).scripts[MISSING_SCRIPT_NAME]).toEqual(MISSING_SCRIPT_VALUE);
125
+ expect(failure).toMatchObject(
126
+ workspace.failureMatcher({
127
+ file: "package.json",
128
+ hasFixer: true,
129
+ message: expect.stringContaining(
130
+ `Expected standardized script entry for '${MISSING_SCRIPT_NAME}'`
131
+ ) as unknown as string,
132
+ })
133
+ );
134
+
135
+ expect(JSON.parse(workspace.readFile("package.json")!).scripts[MISSING_SCRIPT_NAME]).toEqual(
136
+ MISSING_SCRIPT_VALUE
137
+ );
122
138
  });
123
139
 
124
140
  it("does nothing if the value exists", () => {
125
- mockFiles.set("package.json", PACKAGE_WITH_SCRIPTS);
141
+ workspace.writeFile("package.json", PACKAGE_WITH_SCRIPTS);
126
142
 
127
- packageScript.check(context, {
128
- scripts: {
129
- [SCRIPT_NAME]: SCRIPT_VALUE,
143
+ packageScript({
144
+ options: {
145
+ scripts: {
146
+ [SCRIPT_NAME]: SCRIPT_VALUE,
147
+ },
130
148
  },
131
- });
149
+ }).check(context);
132
150
 
133
151
  expect(spy).not.toHaveBeenCalled();
134
152
 
135
- expect(JSON.parse(mockFiles.get("package.json")!).scripts).toEqual({
153
+ expect(JSON.parse(workspace.readFile("package.json")!).scripts).toEqual({
136
154
  [SCRIPT_NAME]: SCRIPT_VALUE,
137
155
  });
138
156
  });
139
157
 
140
158
  it("errors if long form is used and no value matches and there is no fixValue", () => {
141
- mockFiles.set("package.json", PACKAGE_WITH_SCRIPTS);
142
-
143
- packageScript.check(context, {
144
- scripts: {
145
- foo: {
146
- options: ["a", "b"],
159
+ workspace.writeFile("package.json", PACKAGE_WITH_SCRIPTS);
160
+
161
+ packageScript({
162
+ options: {
163
+ scripts: {
164
+ foo: {
165
+ options: ["a", "b"],
166
+ },
147
167
  },
148
168
  },
149
- });
169
+ }).check(context);
150
170
 
151
171
  const errors = spy.mock.calls;
152
172
 
@@ -155,66 +175,72 @@ describe("expectPackageScript", () => {
155
175
  });
156
176
 
157
177
  it("uses the fixValue for fixing if provided", () => {
158
- mockFiles.set("package.json", PACKAGE_WITH_SCRIPTS);
159
-
160
- packageScript.check(context, {
161
- scripts: {
162
- foo: {
163
- options: ["a", "b"],
164
- fixValue: "a",
178
+ workspace.writeFile("package.json", PACKAGE_WITH_SCRIPTS);
179
+
180
+ packageScript({
181
+ options: {
182
+ scripts: {
183
+ foo: {
184
+ options: ["a", "b"],
185
+ fixValue: "a",
186
+ },
165
187
  },
166
188
  },
167
- });
189
+ }).check(context);
168
190
 
169
191
  const errors = spy.mock.calls;
170
192
 
171
193
  expect(errors.length).toBe(1);
172
194
  expect(errors[0][0].fixer).toBeDefined();
173
195
 
174
- expect(JSON.parse(mockFiles.get("package.json")!).scripts).toEqual({
196
+ expect(JSON.parse(workspace.readFile("package.json")!).scripts).toEqual({
175
197
  [SCRIPT_NAME]: SCRIPT_VALUE,
176
198
  foo: "a",
177
199
  });
178
200
  });
179
201
 
180
202
  it("can fix to empty", () => {
181
- mockFiles.set("package.json", PACKAGE_WITH_SCRIPTS);
182
-
183
- packageScript.check(context, {
184
- scripts: {
185
- [SCRIPT_NAME]: {
186
- options: ["a", undefined],
187
- fixValue: undefined,
203
+ workspace.writeFile("package.json", PACKAGE_WITH_SCRIPTS);
204
+
205
+ packageScript({
206
+ options: {
207
+ scripts: {
208
+ [SCRIPT_NAME]: {
209
+ options: ["a", undefined],
210
+ fixValue: undefined,
211
+ },
188
212
  },
189
213
  },
190
- });
214
+ }).check(context);
191
215
 
192
216
  const errors = spy.mock.calls;
193
217
 
194
218
  expect(errors.length).toBe(1);
195
219
  expect(errors[0][0].fixer).toBeDefined();
196
220
 
197
- expect(JSON.parse(mockFiles.get("package.json")!).scripts).toEqual({});
221
+ expect(JSON.parse(workspace.readFile("package.json")!).scripts).toEqual({});
198
222
  });
199
223
 
200
224
  it("can allow only empty", () => {
201
- mockFiles.set("package.json", PACKAGE_WITH_SCRIPTS);
202
-
203
- packageScript.check(context, {
204
- scripts: {
205
- [SCRIPT_NAME]: {
206
- options: [undefined],
207
- fixValue: undefined,
225
+ workspace.writeFile("package.json", PACKAGE_WITH_SCRIPTS);
226
+
227
+ packageScript({
228
+ options: {
229
+ scripts: {
230
+ [SCRIPT_NAME]: {
231
+ options: [undefined],
232
+ fixValue: undefined,
233
+ },
208
234
  },
209
235
  },
210
- });
236
+ }).check(context);
211
237
 
212
238
  const errors = spy.mock.calls;
213
239
 
214
240
  expect(errors.length).toBe(1);
215
241
  expect(errors[0][0].fixer).toBeDefined();
216
242
 
217
- expect(JSON.parse(mockFiles.get("package.json")!).scripts).toEqual({});
243
+ expect(JSON.parse(workspace.readFile("package.json")!).scripts).toEqual({});
218
244
  });
219
245
  });
220
246
  });
@@ -0,0 +1,152 @@
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
+ import { WorkspaceContextImpl } from "@monorepolint/core";
8
+ import { SimpleHost } from "@monorepolint/utils";
9
+ import { readFileSync, writeFileSync } from "node:fs";
10
+ import * as path from "node:path";
11
+ import * as tmp from "tmp";
12
+ import { requireDependency } from "../requireDependency.js";
13
+ import { makeDirectoryRecursively } from "../util/makeDirectory.js";
14
+ import { jsonToString } from "./utils.js";
15
+ import { describe, expect, it, afterEach, jest } from "@jest/globals";
16
+
17
+ const PACKAGE_ROOT = jsonToString({
18
+ workspaces: {
19
+ packages: ["packages/*"],
20
+ },
21
+ dependencies: {
22
+ foo: "5",
23
+ },
24
+ });
25
+
26
+ const PACKAGE_WITH_NO_ENTRIES = jsonToString({});
27
+
28
+ const PACKAGE_WITH_ENTRIES_MISSING = jsonToString({
29
+ dependencies: {},
30
+ devDependencies: {},
31
+ });
32
+
33
+ const PACKAGE_WITH_WRONG_ENTRIES = jsonToString({
34
+ dependencies: {
35
+ foo: "0.1.0",
36
+ },
37
+ devDependencies: {
38
+ bar: "1.0.0",
39
+ },
40
+ });
41
+
42
+ const PACKAGE_WITH_RIGHT_ENTRIES = jsonToString({
43
+ dependencies: {
44
+ foo: "1.0.0",
45
+ },
46
+ devDependencies: {
47
+ bar: "^2.0.0",
48
+ },
49
+ });
50
+
51
+ const OPTIONS = {
52
+ dependencies: {
53
+ foo: "1.0.0",
54
+ },
55
+ devDependencies: {
56
+ bar: "^2.0.0",
57
+ },
58
+ } as const;
59
+
60
+ const CORRECT_OUTPUT = jsonToString(OPTIONS);
61
+
62
+ describe("requireDependency", () => {
63
+ tmp.setGracefulCleanup();
64
+
65
+ let cleanupJobs: Array<() => void> = [];
66
+
67
+ afterEach(() => {
68
+ for (const cleanupJob of cleanupJobs) {
69
+ cleanupJob();
70
+ }
71
+ cleanupJobs = [];
72
+ });
73
+
74
+ function makeWorkspace({ fix }: { fix: boolean }) {
75
+ const dir: tmp.DirResult = tmp.dirSync({ unsafeCleanup: true });
76
+ cleanupJobs.push(() => dir.removeCallback());
77
+
78
+ const workspaceContext = new WorkspaceContextImpl(
79
+ dir.name,
80
+ {
81
+ rules: [],
82
+ fix,
83
+ verbose: false,
84
+ silent: true,
85
+ },
86
+ new SimpleHost()
87
+ );
88
+
89
+ function checkAndSpy(q: string) {
90
+ const context = workspaceContext.createChildContext(path.resolve(dir.name, q));
91
+ const addErrorSpy = jest.spyOn(context, "addError");
92
+ requireDependency({ options: OPTIONS }).check(context);
93
+ return { context, addErrorSpy };
94
+ }
95
+
96
+ function addFile(filePath: string, content: string) {
97
+ const dirPath = path.resolve(dir.name, path.dirname(filePath));
98
+ const resolvedFilePath = path.resolve(dir.name, filePath);
99
+
100
+ makeDirectoryRecursively(dirPath);
101
+ writeFileSync(resolvedFilePath, content);
102
+ }
103
+
104
+ function readFile(filePath: string) {
105
+ return readFileSync(path.resolve(dir.name, filePath)).toString();
106
+ }
107
+
108
+ return { addFile, readFile, workspaceContext, checkAndSpy };
109
+ }
110
+
111
+ it("checks correctly", () => {
112
+ const { addFile, workspaceContext, checkAndSpy } = makeWorkspace({ fix: false });
113
+ addFile("./package.json", PACKAGE_ROOT);
114
+ addFile("./packages/none/package.json", PACKAGE_WITH_NO_ENTRIES);
115
+ addFile("./packages/missing/package.json", PACKAGE_WITH_ENTRIES_MISSING);
116
+ addFile("./packages/wrong/package.json", PACKAGE_WITH_WRONG_ENTRIES);
117
+ addFile("./packages/right/package.json", PACKAGE_WITH_RIGHT_ENTRIES);
118
+
119
+ requireDependency({ options: OPTIONS }).check(workspaceContext);
120
+
121
+ const none = checkAndSpy("./packages/none");
122
+ expect(none.addErrorSpy).toHaveBeenCalledTimes(2);
123
+
124
+ const missing = checkAndSpy("./packages/missing");
125
+ expect(missing.addErrorSpy).toHaveBeenCalledTimes(2);
126
+
127
+ const wrong = checkAndSpy("./packages/wrong");
128
+ expect(wrong.addErrorSpy).toHaveBeenCalledTimes(2);
129
+
130
+ const right = checkAndSpy("./packages/right");
131
+ expect(right.addErrorSpy).toHaveBeenCalledTimes(0);
132
+ });
133
+
134
+ it("fixes correctly", () => {
135
+ const { addFile, readFile, checkAndSpy } = makeWorkspace({ fix: true });
136
+ addFile("./package.json", PACKAGE_ROOT);
137
+ addFile("./packages/missing/package.json", PACKAGE_WITH_ENTRIES_MISSING);
138
+ addFile("./packages/wrong/package.json", PACKAGE_WITH_WRONG_ENTRIES);
139
+
140
+ const missing = checkAndSpy("./packages/missing");
141
+ expect(missing.addErrorSpy).toHaveBeenCalledTimes(2);
142
+
143
+ const wrong = checkAndSpy("./packages/wrong");
144
+ expect(wrong.addErrorSpy).toHaveBeenCalledTimes(2);
145
+
146
+ const missingContents = readFile("./packages/missing/package.json");
147
+ expect(missingContents).toEqual(CORRECT_OUTPUT);
148
+
149
+ const contents = readFile("./packages/wrong/package.json");
150
+ expect(contents).toEqual(CORRECT_OUTPUT);
151
+ });
152
+ });
@@ -5,23 +5,123 @@
5
5
  *
6
6
  */
7
7
 
8
- export function createMockFiles() {
9
- const mockFiles: Map<string, string> = new Map();
8
+ import { AddErrorOptions, WorkspaceContext } from "@monorepolint/config";
9
+ import { WorkspaceContextImpl } from "@monorepolint/core";
10
+ import { CachingHost, Host, SimpleHost } from "@monorepolint/utils";
11
+ import { expect, jest } from "@jest/globals";
12
+ import * as path from "node:path";
13
+ import * as tmp from "tmp";
10
14
 
11
- jest.mock("fs", () => ({
12
- writeFileSync: function writeFileSync(filePath: string, contents: string) {
13
- mockFiles.set(filePath, contents);
14
- },
15
+ export function jsonToString(obj: unknown) {
16
+ return JSON.stringify(obj, undefined, 2) + "\n";
17
+ }
18
+
19
+ interface TestingWorkspaceOpts {
20
+ host: Host;
21
+ rootProjectName?: string;
22
+ fixFlag: boolean;
23
+ }
24
+
25
+ export async function createTestingWorkspace(inboundOpts: TestingWorkspaceOpts) {
26
+ tmp.setGracefulCleanup();
27
+ const tmpdir = tmp.dirSync();
28
+ const opts = {
29
+ ...inboundOpts,
30
+ rootProjectName: inboundOpts.rootProjectName ?? "rootProject",
31
+ };
32
+ const rootPath = tmpdir.name;
33
+ opts.host.mkdir(rootPath, { recursive: true });
15
34
 
16
- // tslint:disable-next-line: variable-name
17
- readFileSync: function readFileSync(filePath: string, _contentType: string) {
18
- return mockFiles.get(filePath);
35
+ opts.host.writeJson(path.join(rootPath, "package.json"), {
36
+ name: opts.rootProjectName,
37
+ workspaces: {
38
+ packages: ["packages/*"],
19
39
  },
20
- }));
40
+ });
21
41
 
22
- return mockFiles;
42
+ await opts.host.flush();
43
+
44
+ return new DefaultTestingWorkspace(
45
+ { ...opts, rootPath },
46
+ new WorkspaceContextImpl(rootPath, { fix: opts.fixFlag, rules: [] }, opts.host)
47
+ );
23
48
  }
24
49
 
25
- export function jsonToString(obj: {}) {
26
- return JSON.stringify(obj, undefined, 2) + "\n";
50
+ interface RealTestingWorkspaceOpts extends Required<TestingWorkspaceOpts> {
51
+ rootPath: string;
27
52
  }
53
+
54
+ export interface TestingWorkspace {
55
+ /**
56
+ * Adds a utf8 file to `packageName` with `relativePath` to the package directory.
57
+ *
58
+ * @param packageName The child package to add to or undefined for root package
59
+ * @param filePath The path of the file to be written. Will be prepended with package dir
60
+ * @param contents the contents to be written (as utf8)
61
+ */
62
+ writeFile(filePath: string, contents: string): void;
63
+
64
+ writeJsonFile(filePath: string, json: object): void;
65
+ addProject(name: string, fields: object): void;
66
+ getFilePath(filePath: string): string;
67
+ readFile(filePath: string): string;
68
+
69
+ /**
70
+ * Helper method for matching failures via jest `expect().toMatchObject
71
+ *
72
+ */
73
+ failureMatcher(opts: { file: string; message: string; hasFixer: boolean }): any;
74
+
75
+ readonly context: WorkspaceContext;
76
+ }
77
+
78
+ class DefaultTestingWorkspace implements TestingWorkspace {
79
+ constructor(private opts: RealTestingWorkspaceOpts, public readonly context: WorkspaceContext) {}
80
+
81
+ addProject(name: string, fields: object) {
82
+ this.writeJsonFile(path.join("packages", name, "package.json"), {
83
+ name,
84
+ ...fields,
85
+ });
86
+ }
87
+
88
+ writeJsonFile(filePath: string, json: object) {
89
+ this.writeFile(filePath, JSON.stringify(json, undefined, 2));
90
+ }
91
+
92
+ /**
93
+ * Adds a utf8 file to `packageName` with `relativePath` to the package directory.
94
+ *
95
+ * @param packageName The child package to add to or undefined for root package
96
+ * @param filePath The path of the file to be written. Will be prepended with package dir
97
+ * @param contents the contents to be written (as utf8)
98
+ */
99
+ writeFile(filePath: string, contents: string) {
100
+ const fullFilePath = this.getFilePath(filePath);
101
+ this.opts.host.mkdir(path.dirname(fullFilePath), { recursive: true });
102
+ this.opts.host.writeFile(fullFilePath, contents, { encoding: "utf-8" });
103
+ }
104
+
105
+ getFilePath(filePath: string) {
106
+ return path.join(this.opts.rootPath, filePath);
107
+ }
108
+
109
+ readFile(filePath: string) {
110
+ return this.opts.host.readFile(this.getFilePath(filePath), { encoding: "utf-8" });
111
+ }
112
+
113
+ failureMatcher(opts: { file: string; message: string; hasFixer: boolean }) {
114
+ return {
115
+ file: this.getFilePath(opts.file),
116
+ message: opts.message,
117
+ ...(opts.hasFixer ? { fixer: expect.any(Function) } : {}),
118
+ };
119
+ }
120
+ }
121
+
122
+ export type AddErrorSpy = jest.SpiedFunction<(options: AddErrorOptions) => void>;
123
+
124
+ export const HOST_FACTORIES: Array<{ name: string; make: () => Host }> = [
125
+ { name: "SimpleHost", make: () => new SimpleHost() },
126
+ { name: "CachingHost", make: () => new CachingHost() },
127
+ ];
@@ -5,17 +5,15 @@
5
5
  *
6
6
  */
7
7
 
8
- import { Context, RuleModule } from "@monorepolint/core";
9
- import * as r from "runtypes";
10
- import { checkAlpha } from "./util/checkAlpha";
8
+ import { checkAlpha } from "./util/checkAlpha.js";
9
+ import { makeRule } from "./util/makeRule.js";
11
10
 
12
- const Options = r.Undefined;
13
-
14
- export const alphabeticalDependencies: RuleModule<typeof Options> = {
15
- check: function expectAlphabeticalDependencies(context: Context) {
11
+ export const alphabeticalDependencies = makeRule<undefined>({
12
+ name: "alphabeticalDependencies",
13
+ check: (context) => {
16
14
  checkAlpha(context, "dependencies");
17
15
  checkAlpha(context, "devDependencies");
18
16
  checkAlpha(context, "peerDependencies");
19
17
  },
20
- optionsRuntype: Options,
21
- };
18
+ validateOptions: () => {},
19
+ });
@@ -5,15 +5,13 @@
5
5
  *
6
6
  */
7
7
 
8
- import { Context, RuleModule } from "@monorepolint/core";
9
- import * as r from "runtypes";
10
- import { checkAlpha } from "./util/checkAlpha";
8
+ import { checkAlpha } from "./util/checkAlpha.js";
9
+ import { makeRule } from "./util/makeRule.js";
11
10
 
12
- const Options = r.Undefined;
13
-
14
- export const alphabeticalScripts: RuleModule<typeof Options> = {
15
- check: function expectAlphabeticalScripts(context: Context) {
11
+ export const alphabeticalScripts = makeRule<undefined>({
12
+ name: "alphabeticalScripts",
13
+ check: (context) => {
16
14
  checkAlpha(context, "scripts");
17
15
  },
18
- optionsRuntype: Options,
19
- };
16
+ validateOptions: () => {},
17
+ });