@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
@@ -7,19 +7,19 @@
7
7
 
8
8
  import { Context } from "@monorepolint/config";
9
9
  import { mutateJson, PackageJson } from "@monorepolint/utils";
10
- import * as r from "runtypes";
11
10
  import { coerce, SemVer } from "semver";
11
+ import { z } from "zod";
12
12
  import { createRuleFactory } from "./util/createRuleFactory.js";
13
- export const Options = r.Record({
14
- matchDependencyVersions: r.Dictionary(r.Union(r.String, r.Array(r.String))),
13
+ export const Options = z.object({
14
+ matchDependencyVersions: z.record(z.string(), z.union([z.string(), z.array(z.string())])),
15
15
  });
16
16
 
17
- export type Options = r.Static<typeof Options>;
17
+ export type Options = z.infer<typeof Options>;
18
18
 
19
19
  export const consistentVersions = createRuleFactory({
20
20
  name: "consistentVersions",
21
21
  check: checkConsistentVersions,
22
- validateOptions: Options.check,
22
+ validateOptions: Options.parse,
23
23
  });
24
24
 
25
25
  function checkConsistentVersions(context: Context, options: Options) {
@@ -45,6 +45,39 @@ function checkConsistentVersions(context: Context, options: Options) {
45
45
  }
46
46
  }
47
47
 
48
+ const shouldUpdateVersion = (
49
+ actualValue: string,
50
+ expectedValue: string,
51
+ isProtocolVersionString: boolean,
52
+ expectedSemVer: SemVer | null,
53
+ ): boolean => {
54
+ if (isProtocolVersionString) {
55
+ return actualValue !== expectedValue;
56
+ }
57
+
58
+ const actualSemVer = coerce(actualValue);
59
+ return actualSemVer != null && actualSemVer.raw !== expectedSemVer!.raw;
60
+ };
61
+
62
+ const matchesAnyVersion = (
63
+ actualValue: string,
64
+ protocolVersions: string[],
65
+ acceptedSemVerVersions: SemVer[],
66
+ ): boolean => {
67
+ if (protocolVersions.includes(actualValue)) {
68
+ return true;
69
+ }
70
+
71
+ const actualSemVer = coerce(actualValue);
72
+ if (actualSemVer == null) {
73
+ return false;
74
+ }
75
+
76
+ return acceptedSemVerVersions.some(
77
+ (acceptedSemVer) => actualSemVer.raw === acceptedSemVer.raw,
78
+ );
79
+ };
80
+
48
81
  const ensurePackageIsCorrectVersion = (
49
82
  context: Context,
50
83
  dependencyPackageName: string,
@@ -53,55 +86,67 @@ const ensurePackageIsCorrectVersion = (
53
86
  const packageJson = context.getPackageJson();
54
87
  const packageJsonPath = context.getPackageJsonPath();
55
88
 
56
- const expectedPackageDependencyVersion = coerce(
57
- expectedPackageDependencyValue,
58
- );
59
- if (expectedPackageDependencyVersion == null) {
60
- throw new Error(
61
- `Malformed expected package dependency version defined in monorepolint configuration: ${dependencyPackageName} @ '${expectedPackageDependencyValue}'`,
62
- );
89
+ // Allow protocol version strings like "catalog:", "workspace:", etc.
90
+ const isProtocolVersion = expectedPackageDependencyValue.endsWith(":");
91
+ let expectedPackageDependencyVersion: SemVer | null = null;
92
+
93
+ if (!isProtocolVersion) {
94
+ expectedPackageDependencyVersion = coerce(expectedPackageDependencyValue);
95
+ if (expectedPackageDependencyVersion == null) {
96
+ throw new Error(
97
+ `Malformed expected package dependency version defined in monorepolint configuration: ${dependencyPackageName} @ '${expectedPackageDependencyValue}'`,
98
+ );
99
+ }
63
100
  }
64
101
 
65
102
  const actualPackageDependencyValue = packageJson.dependencies
66
103
  && packageJson.dependencies[dependencyPackageName];
67
- const actualPackageDependencyVersion = coerce(actualPackageDependencyValue);
68
- if (
69
- actualPackageDependencyVersion != null
70
- && actualPackageDependencyVersion.raw
71
- !== expectedPackageDependencyVersion.raw
72
- ) {
73
- context.addError({
74
- file: packageJsonPath,
75
- message:
76
- `Expected dependency on ${dependencyPackageName} to match version defined in monorepolint configuration '${expectedPackageDependencyValue}', got '${actualPackageDependencyValue}' instead.`,
77
- fixer: () =>
78
- mutateJson<PackageJson>(packageJsonPath, context.host, (input) => {
79
- input.dependencies![dependencyPackageName] = expectedPackageDependencyValue;
80
- return input;
81
- }),
82
- });
104
+
105
+ if (actualPackageDependencyValue != null) {
106
+ const shouldUpdate = shouldUpdateVersion(
107
+ actualPackageDependencyValue,
108
+ expectedPackageDependencyValue,
109
+ isProtocolVersion,
110
+ expectedPackageDependencyVersion,
111
+ );
112
+
113
+ if (shouldUpdate) {
114
+ context.addError({
115
+ file: packageJsonPath,
116
+ message:
117
+ `Expected dependency on ${dependencyPackageName} to match version defined in monorepolint configuration '${expectedPackageDependencyValue}', got '${actualPackageDependencyValue}' instead.`,
118
+ fixer: () =>
119
+ mutateJson<PackageJson>(packageJsonPath, context.host, (input) => {
120
+ input.dependencies![dependencyPackageName] = expectedPackageDependencyValue;
121
+ return input;
122
+ }),
123
+ });
124
+ }
83
125
  }
84
126
 
85
127
  const actualPackageDevDependencyValue = packageJson.devDependencies
86
128
  && packageJson.devDependencies[dependencyPackageName];
87
- const actualPackageDevDependencyVersion = coerce(
88
- actualPackageDevDependencyValue,
89
- );
90
- if (
91
- actualPackageDevDependencyVersion != null
92
- && actualPackageDevDependencyVersion.raw
93
- !== expectedPackageDependencyVersion.raw
94
- ) {
95
- context.addError({
96
- file: packageJsonPath,
97
- message:
98
- `Expected devDependency on ${dependencyPackageName} to match version defined in monorepolint configuration '${expectedPackageDependencyValue}', got '${actualPackageDevDependencyValue}' instead`,
99
- fixer: () =>
100
- mutateJson<PackageJson>(packageJsonPath, context.host, (input) => {
101
- input.devDependencies![dependencyPackageName] = expectedPackageDependencyValue;
102
- return input;
103
- }),
104
- });
129
+
130
+ if (actualPackageDevDependencyValue != null) {
131
+ const shouldUpdateDev = shouldUpdateVersion(
132
+ actualPackageDevDependencyValue,
133
+ expectedPackageDependencyValue,
134
+ isProtocolVersion,
135
+ expectedPackageDependencyVersion,
136
+ );
137
+
138
+ if (shouldUpdateDev) {
139
+ context.addError({
140
+ file: packageJsonPath,
141
+ message:
142
+ `Expected devDependency on ${dependencyPackageName} to match version defined in monorepolint configuration '${expectedPackageDependencyValue}', got '${actualPackageDevDependencyValue}' instead`,
143
+ fixer: () =>
144
+ mutateJson<PackageJson>(packageJsonPath, context.host, (input) => {
145
+ input.devDependencies![dependencyPackageName] = expectedPackageDependencyValue;
146
+ return input;
147
+ }),
148
+ });
149
+ }
105
150
  }
106
151
  };
107
152
 
@@ -113,7 +158,11 @@ const ensurePackageMatchesSomeVersion = (
113
158
  const packageJson = context.getPackageJson();
114
159
  const packageJsonPath = context.getPackageJsonPath();
115
160
 
116
- const acceptedPackageDependencyVersions: SemVer[] = acceptedPackageDependencyValues.map(
161
+ // Separate protocol versions from regular semver versions
162
+ const protocolVersions = acceptedPackageDependencyValues.filter(val => val.endsWith(":"));
163
+ const regularVersions = acceptedPackageDependencyValues.filter(val => !val.endsWith(":"));
164
+
165
+ const acceptedPackageDependencyVersions: SemVer[] = regularVersions.map(
117
166
  (acceptedPackageDependencyValue) => {
118
167
  const acceptedPackageDependencyVersion = coerce(
119
168
  acceptedPackageDependencyValue,
@@ -129,45 +178,45 @@ const ensurePackageMatchesSomeVersion = (
129
178
 
130
179
  const actualPackageDependencyValue = packageJson.dependencies
131
180
  && packageJson.dependencies[dependencyPackageName];
132
- const actualPackageDependencyVersion = coerce(actualPackageDependencyValue);
133
- if (
134
- actualPackageDependencyVersion != null
135
- && acceptedPackageDependencyVersions.every(
136
- (acceptedPackageDependencyVersion) =>
137
- actualPackageDependencyVersion.raw
138
- !== acceptedPackageDependencyVersion.raw,
139
- )
140
- ) {
141
- context.addError({
142
- file: packageJsonPath,
143
- message: `Expected dependency on ${dependencyPackageName} to match one of '${
144
- JSON.stringify(
145
- acceptedPackageDependencyValues,
146
- )
147
- }', got '${actualPackageDependencyValue}' instead.`,
148
- });
181
+
182
+ if (actualPackageDependencyValue != null) {
183
+ const matches = matchesAnyVersion(
184
+ actualPackageDependencyValue,
185
+ protocolVersions,
186
+ acceptedPackageDependencyVersions,
187
+ );
188
+
189
+ if (!matches) {
190
+ context.addError({
191
+ file: packageJsonPath,
192
+ message: `Expected dependency on ${dependencyPackageName} to match one of '${
193
+ JSON.stringify(
194
+ acceptedPackageDependencyValues,
195
+ )
196
+ }', got '${actualPackageDependencyValue}' instead.`,
197
+ });
198
+ }
149
199
  }
150
200
 
151
201
  const actualPackageDevDependencyValue = packageJson.devDependencies
152
202
  && packageJson.devDependencies[dependencyPackageName];
153
- const actualPackageDevDependencyVersion = coerce(
154
- actualPackageDevDependencyValue,
155
- );
156
- if (
157
- actualPackageDevDependencyVersion != null
158
- && acceptedPackageDependencyVersions.every(
159
- (acceptedPackageDependencyVersion) =>
160
- actualPackageDevDependencyVersion.raw
161
- !== acceptedPackageDependencyVersion.raw,
162
- )
163
- ) {
164
- context.addError({
165
- file: packageJsonPath,
166
- message: `Expected devDependency on ${dependencyPackageName} to match one of '${
167
- JSON.stringify(
168
- acceptedPackageDependencyValues,
169
- )
170
- }', got '${actualPackageDevDependencyValue}' instead.`,
171
- });
203
+
204
+ if (actualPackageDevDependencyValue != null) {
205
+ const matches = matchesAnyVersion(
206
+ actualPackageDevDependencyValue,
207
+ protocolVersions,
208
+ acceptedPackageDependencyVersions,
209
+ );
210
+
211
+ if (!matches) {
212
+ context.addError({
213
+ file: packageJsonPath,
214
+ message: `Expected devDependency on ${dependencyPackageName} to match one of '${
215
+ JSON.stringify(
216
+ acceptedPackageDependencyValues,
217
+ )
218
+ }', got '${actualPackageDevDependencyValue}' instead.`,
219
+ });
220
+ }
172
221
  }
173
222
  };
@@ -8,49 +8,69 @@
8
8
  import { Context } from "@monorepolint/config";
9
9
  import { diff } from "jest-diff";
10
10
  import * as path from "path";
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";
13
- const Options = r.Union(
14
- r.Record({
15
- file: r.String,
16
- generator: r.Function.withGuard((
17
- x,
18
- ): x is (context: Context) => string | Promise<string> => x != undefined),
19
- template: r.Undefined.optional(),
20
- templateFile: r.Undefined.optional(),
14
+ import { ZodRemove } from "./util/zodSchemas.js";
15
+
16
+ const Options = z.union([
17
+ z.object({
18
+ file: z.string(),
19
+ generator: z.custom<
20
+ (context: Context) => string | typeof REMOVE | Promise<string | typeof REMOVE>
21
+ >((val) => {
22
+ return typeof val === "function";
23
+ }),
24
+ template: z.undefined().optional(),
25
+ templateFile: z.undefined().optional(),
21
26
  }),
22
- r.Record({
23
- file: r.String,
24
- generator: r.Undefined.optional(),
25
- template: r.String.Or(r.Undefined),
26
- templateFile: r.Undefined.optional(),
27
+ z.object({
28
+ file: z.string(),
29
+ generator: z.undefined().optional(),
30
+ template: z.union([
31
+ z.string(),
32
+ ZodRemove,
33
+ ]),
34
+ templateFile: z.undefined().optional(),
27
35
  }),
28
- r.Record({
29
- file: r.String,
30
- generator: r.Undefined.optional(),
31
- template: r.Undefined.optional(),
32
- templateFile: r.String,
36
+ z.object({
37
+ file: z.string(),
38
+ generator: z.undefined().optional(),
39
+ template: z.undefined().optional(),
40
+ templateFile: z.string(),
33
41
  }),
34
- );
42
+ ]);
35
43
 
36
- type Options = r.Static<typeof Options>;
44
+ type Options = z.infer<typeof Options>;
37
45
 
38
46
  export const fileContents = createRuleFactory<Options>({
39
47
  name: "fileContents",
40
48
  check: async (context, opts) => {
41
49
  const fullPath = path.join(context.packageDir, opts.file);
50
+
51
+ // Validate that the file path doesn't contain null bytes or other invalid characters
52
+ if (fullPath.includes("\0")) {
53
+ throw new Error(`Invalid file path: path contains null bytes`);
54
+ }
55
+
42
56
  const expectedContent = await getExpectedContents(context, opts);
43
57
 
58
+ // If the generator function failed, an unfixable error was already added, so we should return early
59
+ if (expectedContent === Symbol.for("GENERATOR_ERROR")) {
60
+ return;
61
+ }
62
+
44
63
  const pathExists = context.host.exists(fullPath);
45
64
  const actualContent = pathExists
46
65
  ? context.host.readFile(fullPath, { encoding: "utf-8" })
47
- : undefined;
66
+ : REMOVE;
48
67
  if (actualContent !== expectedContent) {
49
- const longMessage = pathExists && expectedContent == undefined
50
- ? undefined
51
- : diff(expectedContent, actualContent, { expand: true });
68
+ const longMessage =
69
+ pathExists && (expectedContent === undefined || expectedContent === REMOVE)
70
+ ? undefined
71
+ : diff(expectedContent, actualContent, { expand: true });
52
72
 
53
- const message = pathExists && expectedContent == undefined
73
+ const message = pathExists && (expectedContent === undefined || expectedContent === REMOVE)
54
74
  ? "File should not exist"
55
75
  : "Expect file contents to match";
56
76
 
@@ -59,9 +79,9 @@ export const fileContents = createRuleFactory<Options>({
59
79
  message,
60
80
  longMessage,
61
81
  fixer: () => {
62
- if (expectedContent === undefined) {
82
+ if (expectedContent === REMOVE) {
63
83
  if (pathExists) context.host.deleteFile(fullPath);
64
- } else {
84
+ } else if (expectedContent !== undefined && typeof expectedContent === "string") {
65
85
  context.host.mkdir(path.dirname(fullPath), { recursive: true });
66
86
  context.host.writeFile(fullPath, expectedContent, {
67
87
  encoding: "utf-8",
@@ -71,29 +91,52 @@ export const fileContents = createRuleFactory<Options>({
71
91
  });
72
92
  }
73
93
  },
74
- validateOptions: Options.check,
94
+ validateOptions: Options.parse,
75
95
  });
76
96
 
77
97
  const optionsCache = new Map<
78
98
  Options,
79
- | ((context: Context) => Promise<string> | string | undefined)
99
+ | ((context: Context) => Promise<string | typeof REMOVE> | string | typeof REMOVE)
80
100
  | string
81
- | undefined
101
+ | typeof REMOVE
82
102
  >();
83
103
 
84
- async function getExpectedContents(context: Context, opts: Options) {
104
+ async function getExpectedContents(
105
+ context: Context,
106
+ opts: Options,
107
+ ): Promise<string | typeof REMOVE | symbol> {
85
108
  // we need to use has because undefined is a valid value in the cache
86
109
  if (optionsCache.has(opts)) {
87
110
  const cachedEntry = optionsCache.get(opts);
88
111
  if (cachedEntry && typeof cachedEntry === "function") {
89
112
  return cachedEntry(context);
90
113
  }
91
- return cachedEntry;
114
+ return cachedEntry as string | typeof REMOVE;
92
115
  }
93
116
 
94
117
  if (opts.generator) {
95
118
  optionsCache.set(opts, opts.generator);
96
- return opts.generator(context);
119
+ try {
120
+ const result = await opts.generator(context);
121
+ if (typeof result !== "string" && result !== REMOVE) {
122
+ throw new Error(`Generator function must return a string or REMOVE, got ${typeof result}`);
123
+ }
124
+ return result;
125
+ } catch (error) {
126
+ // Instead of throwing, create an unfixable error and return a special marker
127
+ const errorMessage = error instanceof Error ? error.message : String(error);
128
+ const fullPath = path.join(context.packageDir, opts.file);
129
+
130
+ context.addError({
131
+ file: fullPath,
132
+ message: `Generator function failed: ${errorMessage}`,
133
+ longMessage:
134
+ `The generator function for file "${opts.file}" threw an error:\n\n${errorMessage}`,
135
+ });
136
+
137
+ // Return a special marker that will prevent the file comparison from proceeding
138
+ return Symbol.for("GENERATOR_ERROR");
139
+ }
97
140
  } else if (opts.templateFile) {
98
141
  const { packageDir: workspacePackageDir } = context.getWorkspaceContext();
99
142
  const fullPath = path.resolve(workspacePackageDir, opts.templateFile);
@@ -101,8 +144,10 @@ async function getExpectedContents(context: Context, opts: Options) {
101
144
 
102
145
  optionsCache.set(opts, template);
103
146
  return template;
104
- } else {
147
+ } else if (opts.template !== undefined) {
105
148
  optionsCache.set(opts, opts.template);
106
149
  return opts.template;
150
+ } else {
151
+ throw new Error("Unable to get expected contents");
107
152
  }
108
153
  }
package/src/index.ts CHANGED
@@ -28,3 +28,5 @@ export {
28
28
  RuleFactoryOptions,
29
29
  ValidateOptionsFn,
30
30
  } from "./util/createRuleFactory.js";
31
+
32
+ export { REMOVE } from "./REMOVE.js";
@@ -9,185 +9,23 @@ import { Context } from "@monorepolint/config";
9
9
  import { Host, mutateJson, PackageJson } from "@monorepolint/utils";
10
10
  import * as path from "node:path";
11
11
  import resolvePackagePath from "resolve-package-path";
12
- import * as r from "runtypes";
13
12
  import { coerce } from "semver";
13
+ import { z } from "zod";
14
14
  import { createRuleFactory } from "./util/createRuleFactory.js";
15
15
 
16
- const Options = r.Union(
17
- r.Partial({
18
- skipUnparseableRanges: r.Undefined,
19
- dependencyWhitelist: r.Undefined,
20
- dependencyBlacklist: r.Undefined,
21
- enforceForDevDependencies: r.Undefined,
22
- }),
23
- r
24
- .Record({
25
- skipUnparseableRanges: r.Boolean,
26
- })
27
- .And(
28
- r.Partial({
29
- dependencyWhitelist: r.Undefined,
30
- dependencyBlacklist: r.Undefined,
31
- enforceForDevDependencies: r.Undefined,
32
- }),
33
- ),
34
- r
35
- .Record({
36
- dependencyWhitelist: r.Array(r.String),
37
- })
38
- .And(
39
- r.Partial({
40
- skipUnparseableRanges: r.Undefined,
41
- dependencyBlacklist: r.Undefined,
42
- enforceForDevDependencies: r.Undefined,
43
- }),
44
- ),
45
- r
46
- .Record({
47
- dependencyBlacklist: r.Array(r.String),
48
- })
49
- .And(
50
- r.Partial({
51
- skipUnparseableRanges: r.Undefined,
52
- dependencyWhitelist: r.Undefined,
53
- enforceForDevDependencies: r.Undefined,
54
- }),
55
- ),
56
- r
57
- .Record({
58
- enforceForDevDependencies: r.Boolean,
59
- })
60
- .And(
61
- r.Partial({
62
- skipUnparseableRanges: r.Undefined,
63
- dependencyWhitelist: r.Undefined,
64
- dependencyBlacklist: r.Undefined,
65
- }),
66
- ),
67
- r
68
- .Record({
69
- skipUnparseableRanges: r.Boolean,
70
- dependencyWhitelist: r.Array(r.String),
71
- })
72
- .And(
73
- r.Partial({
74
- dependencyBlacklist: r.Undefined,
75
- enforceForDevDependencies: r.Undefined,
76
- }),
77
- ),
78
- r
79
- .Record({
80
- skipUnparseableRanges: r.Boolean,
81
- dependencyBlacklist: r.Array(r.String),
82
- })
83
- .And(
84
- r.Partial({
85
- dependencyWhitelist: r.Undefined,
86
- enforceForDevDependencies: r.Undefined,
87
- }),
88
- ),
89
- r
90
- .Record({
91
- skipUnparseableRanges: r.Boolean,
92
- enforceForDevDependencies: r.Boolean,
93
- })
94
- .And(
95
- r.Partial({
96
- dependencyWhitelist: r.Undefined,
97
- dependencyBlacklist: r.Undefined,
98
- }),
99
- ),
100
- r
101
- .Record({
102
- dependencyWhitelist: r.Array(r.String),
103
- dependencyBlacklist: r.Array(r.String),
104
- })
105
- .And(
106
- r.Partial({
107
- skipUnparseableRanges: r.Undefined,
108
- enforceForDevDependencies: r.Undefined,
109
- }),
110
- ),
111
- r
112
- .Record({
113
- dependencyWhitelist: r.Array(r.String),
114
- enforceForDevDependencies: r.Boolean,
115
- })
116
- .And(
117
- r.Partial({
118
- skipUnparseableRanges: r.Undefined,
119
- dependencyBlacklist: r.Undefined,
120
- }),
121
- ),
122
- r
123
- .Record({
124
- dependencyBlacklist: r.Array(r.String),
125
- enforceForDevDependencies: r.Boolean,
126
- })
127
- .And(
128
- r.Partial({
129
- skipUnparseableRanges: r.Undefined,
130
- dependencyWhitelist: r.Undefined,
131
- }),
132
- ),
133
- r
134
- .Record({
135
- skipUnparseableRanges: r.Boolean,
136
- dependencyWhitelist: r.Array(r.String),
137
- dependencyBlacklist: r.Array(r.String),
138
- })
139
- .And(
140
- r.Partial({
141
- enforceForDevDependencies: r.Undefined,
142
- }),
143
- ),
144
- r
145
- .Record({
146
- skipUnparseableRanges: r.Boolean,
147
- dependencyWhitelist: r.Array(r.String),
148
- enforceForDevDependencies: r.Boolean,
149
- })
150
- .And(
151
- r.Partial({
152
- dependencyBlacklist: r.Undefined,
153
- }),
154
- ),
155
- r
156
- .Record({
157
- skipUnparseableRanges: r.Boolean,
158
- dependencyBlacklist: r.Array(r.String),
159
- enforceForDevDependencies: r.Boolean,
160
- })
161
- .And(
162
- r.Partial({
163
- dependencyWhitelist: r.Undefined,
164
- }),
165
- ),
166
- r
167
- .Record({
168
- dependencyWhitelist: r.Array(r.String),
169
- dependencyBlacklist: r.Array(r.String),
170
- enforceForDevDependencies: r.Boolean,
171
- })
172
- .And(
173
- r.Partial({
174
- skipUnparseableRanges: r.Undefined,
175
- }),
176
- ),
177
- r.Record({
178
- skipUnparseableRanges: r.Boolean,
179
- dependencyWhitelist: r.Array(r.String),
180
- dependencyBlacklist: r.Array(r.String),
181
- enforceForDevDependencies: r.Boolean,
182
- }),
183
- );
184
-
185
- export type Options = r.Static<typeof Options>;
16
+ const Options = z.object({
17
+ skipUnparseableRanges: z.boolean().optional(),
18
+ dependencyWhitelist: z.array(z.string()).optional(),
19
+ dependencyBlacklist: z.array(z.string()).optional(),
20
+ enforceForDevDependencies: z.boolean().optional(),
21
+ });
22
+
23
+ export type Options = z.infer<typeof Options>;
186
24
 
187
25
  export const mustSatisfyPeerDependencies = createRuleFactory({
188
26
  name: "mustSatisfyPeerDependencies",
189
27
  check: checkSatisfyPeerDependencies,
190
- validateOptions: Options.check,
28
+ validateOptions: Options.parse,
191
29
  });
192
30
 
193
31
  /**
@@ -7,12 +7,12 @@
7
7
 
8
8
  import * as globby from "globby";
9
9
  import * as path from "node:path";
10
- import * as r from "runtypes";
10
+ import { z } from "zod";
11
11
  import { createRuleFactory } from "./util/createRuleFactory.js";
12
12
 
13
- export const Options = r.Undefined;
13
+ export const Options = z.undefined();
14
14
 
15
- type Options = r.Static<typeof Options>;
15
+ type Options = z.infer<typeof Options>;
16
16
 
17
17
  // Enforce that the root package.json contains all of the workspaces in the repo (including nested packages)
18
18
  export const nestedWorkspaces = createRuleFactory({
@@ -65,5 +65,5 @@ export const nestedWorkspaces = createRuleFactory({
65
65
  });
66
66
  }
67
67
  },
68
- validateOptions: Options.check,
68
+ validateOptions: Options.parse,
69
69
  });