@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
@@ -0,0 +1,60 @@
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 * as globby from "globby";
9
+ import * as path from "node:path";
10
+ import * as r from "runtypes";
11
+ import { makeRule } from "./util/makeRule.js";
12
+
13
+ export const Options = r.Undefined;
14
+
15
+ type Options = r.Static<typeof Options>;
16
+
17
+ // Enforce that the root package.json contains all of the workspaces in the repo (including nested packages)
18
+ export const nestedWorkspaces = makeRule({
19
+ name: "nestedWorkspaces",
20
+ check: (context) => {
21
+ const rootPackageJson = context.getWorkspaceContext().getPackageJson();
22
+
23
+ // Expand a set of globs covering all package.json files in the entire repo (except the root)
24
+ const packageJsonPaths = globby.globbySync(["*/**/package.json", "!**/node_modules/**"]);
25
+
26
+ const workspaces = Array.isArray(rootPackageJson.workspaces)
27
+ ? rootPackageJson.workspaces
28
+ : rootPackageJson.workspaces !== undefined
29
+ ? rootPackageJson.workspaces.packages
30
+ : undefined;
31
+
32
+ if (workspaces === undefined && packageJsonPaths.length > 0) {
33
+ context.addError({
34
+ file: context.getPackageJsonPath(),
35
+ message: 'The "workspace" field is missing, even though there are workspaces in the repository.',
36
+ });
37
+ return;
38
+ }
39
+
40
+ // Build a set of globs for each package.json that exists in packages specified by a workspace.
41
+ const workspacePackageJsons = (workspaces || []).map((item) => `${item}/package.json`);
42
+
43
+ // Expand the globs to get an array of all package.json files that are in packages specified by a workspace.
44
+ const expandedWorkspacesGlobs = globby.globbySync([...workspacePackageJsons, "!**/node_modules/**"]);
45
+
46
+ // Ensure there are no package.jsons which are not included in the globbed workspaces set
47
+ const difference = packageJsonPaths.filter((packageJsonPath) => !expandedWorkspacesGlobs.includes(packageJsonPath));
48
+
49
+ if (difference.length !== 0) {
50
+ const differencesList = difference.map((packageJsonPath) => path.dirname(packageJsonPath)).join(", ");
51
+ context.addError({
52
+ file: context.getPackageJsonPath(),
53
+ message:
54
+ `The "workspace" field is missing one or more values: ${differencesList}. ` +
55
+ 'You may be able to use a glob to avoid listing each workspace individually, e.g. "packages/nested-workspace/*".',
56
+ });
57
+ }
58
+ },
59
+ validateOptions: Options.check,
60
+ });
@@ -5,42 +5,86 @@
5
5
  *
6
6
  */
7
7
 
8
- import { Context, RuleModule } from "@monorepolint/core";
9
8
  import { mutateJson, PackageJson } from "@monorepolint/utils";
10
- import diff from "jest-diff";
9
+ import { diff } from "jest-diff";
11
10
  import * as r from "runtypes";
11
+ import { makeRule } from "./util/makeRule.js";
12
12
 
13
- export const Options = r.Record({
14
- entries: r.Dictionary(r.Unknown), // string => unknown
15
- });
13
+ export const Options = r.Union(
14
+ r
15
+ .Record({
16
+ entries: r.Dictionary(r.Unknown), // string => unknown, enforces existence of keys and their values
17
+ })
18
+ .And(
19
+ r.Partial({
20
+ entriesExist: r.Undefined,
21
+ })
22
+ ),
23
+ r
24
+ .Record({
25
+ entriesExist: r.Array(r.String), // enforces existence of keys, but not values
26
+ })
27
+ .And(
28
+ r.Partial({
29
+ entries: r.Undefined,
30
+ })
31
+ ),
32
+ r.Record({
33
+ entries: r.Dictionary(r.Unknown), // string => unknown, enforces existence of keys and their values
34
+ entriesExist: r.Array(r.String),
35
+ })
36
+ );
16
37
 
17
38
  export type Options = r.Static<typeof Options>;
18
39
 
19
- export const packageEntry = {
20
- check: function expectPackageEntry(context: Context, options: Options) {
40
+ export const packageEntry = makeRule<Options>({
41
+ name: "packageEntry",
42
+ check: (context, options) => {
21
43
  const packageJson = context.getPackageJson();
22
44
 
23
- for (const key of Object.keys(options.entries)) {
24
- const value = options.entries[key];
25
-
26
- const entryDiff = diff(JSON.stringify(value) + "\n", (JSON.stringify(packageJson[key]) || "") + "\n");
27
- if (
28
- (typeof value !== "object" && value !== packageJson[key]) ||
29
- (entryDiff == null || !entryDiff.includes("Compared values have no visual difference"))
30
- ) {
31
- context.addError({
32
- file: context.getPackageJsonPath(),
33
- message: `Expected standardized entry for '${key}'`,
34
- longMessage: entryDiff,
35
- fixer: () => {
36
- mutateJson<PackageJson>(context.getPackageJsonPath(), input => {
37
- input[key] = value;
38
- return input;
39
- });
40
- },
41
- });
45
+ if (options.entries) {
46
+ for (const key of Object.keys(options.entries)) {
47
+ const value = options.entries[key];
48
+
49
+ const entryDiff = diff(JSON.stringify(value) + "\n", (JSON.stringify(packageJson[key]) || "") + "\n");
50
+ if (
51
+ (typeof value !== "object" && value !== packageJson[key]) ||
52
+ entryDiff == null ||
53
+ !entryDiff.includes("Compared values have no visual difference")
54
+ ) {
55
+ context.addError({
56
+ file: context.getPackageJsonPath(),
57
+ message: createStandardizedEntryErrorMessage(key),
58
+ longMessage: entryDiff,
59
+ fixer: () => {
60
+ mutateJson<PackageJson>(context.getPackageJsonPath(), context.host, (input) => {
61
+ input[key] = value;
62
+ return input;
63
+ });
64
+ },
65
+ });
66
+ }
67
+ }
68
+ }
69
+
70
+ if (options.entriesExist) {
71
+ for (const key of options.entriesExist) {
72
+ if (packageJson[key] === undefined) {
73
+ context.addError({
74
+ file: context.getPackageJsonPath(),
75
+ message: createExpectedEntryErrorMessage(key),
76
+ });
77
+ }
42
78
  }
43
79
  }
44
80
  },
45
- optionsRuntype: Options,
46
- } as RuleModule<typeof Options>;
81
+ validateOptions: Options.check,
82
+ });
83
+
84
+ export function createStandardizedEntryErrorMessage(key: string) {
85
+ return `Expected standardized entry for '${key}'`;
86
+ }
87
+
88
+ export function createExpectedEntryErrorMessage(key: string) {
89
+ return `Expected entry for '${key}' to exist`;
90
+ }
@@ -5,11 +5,10 @@
5
5
  *
6
6
  */
7
7
 
8
- import { Context, RuleModule } from "@monorepolint/core";
9
- import { writeJson } from "@monorepolint/utils";
10
- import diff from "jest-diff";
8
+ import { Context } from "@monorepolint/config";
9
+ import { diff } from "jest-diff";
11
10
  import * as r from "runtypes";
12
-
11
+ import { makeRule } from "./util/makeRule.js";
13
12
  type OrderFunction = (context: Context) => (a: string, b: string) => number;
14
13
 
15
14
  const Options = r
@@ -28,9 +27,12 @@ const defaultKeyOrder = [
28
27
  "contributors",
29
28
  "url",
30
29
  "license",
30
+ "type",
31
+ "exports",
31
32
  "private",
32
33
  "engines",
33
34
  "bin",
35
+ "types",
34
36
  "main",
35
37
  "module",
36
38
  "typings",
@@ -49,17 +51,18 @@ const defaultKeyOrder = [
49
51
  "publishConfig",
50
52
  ];
51
53
 
52
- export const packageOrder = {
53
- check: function expectPackageOrder(context: Context, opts: Options) {
54
+ export const packageOrder = makeRule<Options>({
55
+ name: "packageOrder",
56
+ check: (context, opts) => {
54
57
  const packageJson = context.getPackageJson();
55
58
  const packagePath = context.getPackageJsonPath();
56
59
 
57
60
  const order: string[] | OrderFunction = opts === undefined ? defaultKeyOrder : opts.order;
58
61
 
59
- const compartor = isOrderFunction(order) ? order(context) : createCompartor(order);
62
+ const comparator = isOrderFunction(order) ? order(context) : createComparator(order);
60
63
 
61
64
  const actualOrder = Object.keys(packageJson);
62
- const expectedOrder = actualOrder.slice().sort(compartor); // sort mutates, so we need to copy the previous result
65
+ const expectedOrder = actualOrder.slice().sort(comparator); // sort mutates, so we need to copy the previous result
63
66
 
64
67
  if (!arrayOrderCompare(actualOrder, expectedOrder)) {
65
68
  context.addError({
@@ -67,19 +70,20 @@ export const packageOrder = {
67
70
  message: "Incorrect order of fields in package.json",
68
71
  longMessage: diff(expectedOrder, actualOrder, { expand: true }),
69
72
  fixer: () => {
73
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
74
  const expectedPackageJson: Record<string, any> = {};
71
75
 
72
- expectedOrder.forEach(key => {
76
+ expectedOrder.forEach((key) => {
73
77
  expectedPackageJson[key] = packageJson[key];
74
78
  });
75
79
 
76
- writeJson(packagePath, expectedPackageJson);
80
+ context.host.writeJson(packagePath, expectedPackageJson);
77
81
  },
78
82
  });
79
83
  }
80
84
  },
81
- optionsRuntype: Options,
82
- } as RuleModule<typeof Options>;
85
+ validateOptions: Options.check,
86
+ });
83
87
 
84
88
  function arrayOrderCompare(a: ReadonlyArray<string>, b: ReadonlyArray<string>) {
85
89
  for (let index = 0; index < a.length; index++) {
@@ -91,7 +95,7 @@ function arrayOrderCompare(a: ReadonlyArray<string>, b: ReadonlyArray<string>) {
91
95
  return true;
92
96
  }
93
97
 
94
- function createCompartor(order: ReadonlyArray<string>) {
98
+ function createComparator(order: ReadonlyArray<string>) {
95
99
  return (a: string, b: string) => {
96
100
  const aIndex = order.indexOf(a);
97
101
  const bIndex = order.indexOf(b);
@@ -5,24 +5,19 @@
5
5
  *
6
6
  */
7
7
 
8
- import { Context, RuleModule } from "@monorepolint/core";
9
8
  import { mutateJson, PackageJson } from "@monorepolint/utils";
10
- import diff from "jest-diff";
9
+ import { diff } from "jest-diff";
11
10
  import * as r from "runtypes";
11
+ import { makeRule } from "./util/makeRule.js";
12
12
 
13
13
  export const Options = r.Record({
14
14
  scripts: r.Dictionary(
15
15
  r.Union(
16
16
  r.String,
17
- r
18
- .Record({
19
- options: r.Array(r.String.Or(r.Undefined)),
20
- })
21
- .And(
22
- r.Partial({
23
- fixValue: r.Union(r.String, r.Undefined, r.Literal(false)),
24
- })
25
- )
17
+ r.Record({
18
+ options: r.Array(r.String.Or(r.Undefined)),
19
+ fixValue: r.Union(r.String, r.Undefined, r.Literal(false)).optional(),
20
+ })
26
21
  )
27
22
  ), // string => string
28
23
  });
@@ -31,15 +26,16 @@ export type Options = r.Static<typeof Options>;
31
26
 
32
27
  export const MSG_NO_SCRIPTS_BLOCK = "No scripts block in package.json";
33
28
 
34
- export const packageScript = {
35
- check: function expectPackageScript(context: Context, options: Options) {
29
+ export const packageScript = makeRule<Options>({
30
+ name: "packageScript",
31
+ check: (context, options) => {
36
32
  const packageJson = context.getPackageJson();
37
33
  if (packageJson.scripts === undefined) {
38
34
  context.addError({
39
35
  file: context.getPackageJsonPath(),
40
36
  message: MSG_NO_SCRIPTS_BLOCK,
41
37
  fixer: () => {
42
- mutateJson<PackageJson>(context.getPackageJsonPath(), input => {
38
+ mutateJson<PackageJson>(context.getPackageJsonPath(), context.host, (input) => {
43
39
  input.scripts = {};
44
40
  return input;
45
41
  });
@@ -63,7 +59,7 @@ export const packageScript = {
63
59
  }
64
60
  allowedValues.add(q);
65
61
  }
66
- fixToEmpty = value.hasOwnProperty("fixValue") && value.fixValue === undefined;
62
+ fixToEmpty = Object.prototype.hasOwnProperty.call(value, "fixValue") && value.fixValue === undefined;
67
63
  fixValue = value.fixValue;
68
64
  }
69
65
 
@@ -75,7 +71,7 @@ export const packageScript = {
75
71
  if (fixValue !== false && (fixValue !== undefined || fixToEmpty === true)) {
76
72
  const q = fixValue;
77
73
  fixer = () => {
78
- mutateJson<PackageJson>(context.getPackageJsonPath(), input => {
74
+ mutateJson<PackageJson>(context.getPackageJsonPath(), context.host, (input) => {
79
75
  if (fixToEmpty && q === undefined) {
80
76
  delete input.scripts![name];
81
77
  } else {
@@ -87,7 +83,7 @@ export const packageScript = {
87
83
  }
88
84
 
89
85
  const validOptionsString = Array.from(allowedValues.values())
90
- .map(a => (a === undefined ? "(empty)" : `'${a}'`))
86
+ .map((a) => (a === undefined ? "(empty)" : `'${a}'`))
91
87
  .join(", ");
92
88
 
93
89
  context.addError({
@@ -99,5 +95,5 @@ export const packageScript = {
99
95
  }
100
96
  }
101
97
  },
102
- optionsRuntype: Options,
103
- } as RuleModule<typeof Options>;
98
+ validateOptions: Options.check,
99
+ });
@@ -0,0 +1 @@
1
+ export { makeRule } from "../util/makeRule.js";
@@ -0,0 +1,71 @@
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 { mutateJson, PackageJson } from "@monorepolint/utils";
10
+ import { diff } from "jest-diff";
11
+ import * as r from "runtypes";
12
+ import { makeRule } from "./util/makeRule.js";
13
+
14
+ const Options = r.Partial({
15
+ dependencies: r.Dictionary(r.String),
16
+ devDependencies: r.Dictionary(r.String),
17
+ peerDependencies: r.Dictionary(r.String),
18
+ optionalDependencies: r.Dictionary(r.String),
19
+ });
20
+
21
+ type Options = r.Static<typeof Options>;
22
+
23
+ export const requireDependency = makeRule({
24
+ name: "requireDependency",
25
+ check: function expectPackageEntry(context: Context, options: Options) {
26
+ const packageJson = context.getPackageJson();
27
+ const packageJsonPath = context.getPackageJsonPath();
28
+
29
+ [
30
+ "dependencies" as const,
31
+ "devDependencies" as const,
32
+ "peerDependencies" as const,
33
+ "optionalDependencies" as const,
34
+ ].forEach((type) => {
35
+ if (!options[type]) {
36
+ return;
37
+ }
38
+
39
+ if (packageJson[type] === undefined) {
40
+ context.addError({
41
+ file: packageJsonPath,
42
+ message: `No ${type} block, cannot add required ${type}.`,
43
+ fixer: () => {
44
+ mutateJson<PackageJson>(packageJsonPath, context.host, (input) => {
45
+ input[type] = options[type];
46
+ return input;
47
+ });
48
+ },
49
+ });
50
+ return;
51
+ }
52
+
53
+ for (const [dep, version] of Object.entries(options[type]!)) {
54
+ if (packageJson[type][dep] !== version) {
55
+ context.addError({
56
+ file: packageJsonPath,
57
+ message: `Expected dependency ${dep}@${version}`,
58
+ longMessage: diff(`${dep}@${version}\n`, `${dep}@${packageJson[type][dep] || "missing"}\n`)!,
59
+ fixer: () => {
60
+ mutateJson<PackageJson>(packageJsonPath, context.host, (input) => {
61
+ input[type] = { ...input[type], [dep]: version };
62
+ return input;
63
+ });
64
+ },
65
+ });
66
+ }
67
+ }
68
+ });
69
+ },
70
+ validateOptions: Options.check,
71
+ });
@@ -5,20 +5,24 @@
5
5
  *
6
6
  */
7
7
 
8
- import { Context, RuleModule } from "@monorepolint/core";
9
- import { getPackageNameToDir } from "@monorepolint/utils";
10
- import { existsSync, readFileSync, writeFileSync } from "fs";
11
- import diff from "jest-diff";
12
- import minimatch from "minimatch";
8
+ import { Context } from "@monorepolint/config";
9
+ import { matchesAnyGlob } from "@monorepolint/utils";
10
+ import { diff } from "jest-diff";
13
11
  import * as path from "path";
14
12
  import * as r from "runtypes";
13
+ import { makeRule } from "./util/makeRule.js";
14
+
15
+ const DEFAULT_TSCONFIG_FILENAME = "tsconfig.json";
15
16
 
16
17
  const Options = r
17
18
  .Partial({
19
+ file: r.String,
18
20
  generator: r.Function,
21
+ tsconfigReferenceFile: r.String,
19
22
  template: r.Record({}).Or(r.String),
20
23
  templateFile: r.String,
21
24
  excludedReferences: r.Array(r.String).Or(r.Undefined),
25
+ additionalReferences: r.Array(r.String).Or(r.Undefined),
22
26
  })
23
27
  .withConstraint(({ generator, template, templateFile }) => {
24
28
  let count = 0;
@@ -35,15 +39,19 @@ const Options = r
35
39
  return count === 1 || "Expect one of { generator, template, templateFile }";
36
40
  });
37
41
 
38
- export type Options = r.Static<typeof Options>;
42
+ export interface Options extends r.Static<typeof Options> {}
39
43
 
40
- export const standardTsconfig = {
41
- check: function expectStandardTsconfig(context: Context, opts: Options) {
42
- const fullPath = path.resolve(context.packageDir, "tsconfig.json");
44
+ export const standardTsconfig = makeRule<Options>({
45
+ name: "standardTsconfig",
46
+ check: async (context, opts) => {
47
+ const tsconfigFileName = opts.file ?? DEFAULT_TSCONFIG_FILENAME;
48
+ const fullPath = path.resolve(context.packageDir, tsconfigFileName);
43
49
  const generator = getGenerator(context, opts);
44
- const expectedContent = generator(context);
50
+ const expectedContent = await generator(context);
45
51
 
46
- const actualContent = existsSync(fullPath) ? readFileSync(fullPath, "utf-8") : undefined;
52
+ const actualContent = context.host.exists(fullPath)
53
+ ? context.host.readFile(fullPath, { encoding: "utf-8" })
54
+ : undefined;
47
55
 
48
56
  if (expectedContent === undefined) {
49
57
  context.addWarning({
@@ -59,13 +67,15 @@ export const standardTsconfig = {
59
67
  message: "Expect file contents to match",
60
68
  longMessage: diff(expectedContent, actualContent, { expand: true }),
61
69
  fixer: () => {
62
- writeFileSync(fullPath, expectedContent);
70
+ context.host.writeFile(fullPath, expectedContent, {
71
+ encoding: "utf-8",
72
+ });
63
73
  },
64
74
  });
65
75
  }
66
76
  },
67
- optionsRuntype: Options,
68
- } as RuleModule<typeof Options>;
77
+ validateOptions: Options.check,
78
+ });
69
79
 
70
80
  function getGenerator(context: Context, opts: Options) {
71
81
  if (opts.generator) {
@@ -73,37 +83,51 @@ function getGenerator(context: Context, opts: Options) {
73
83
  } else if (opts.templateFile) {
74
84
  const { packageDir: workspacePackageDir } = context.getWorkspaceContext();
75
85
  const fullPath = path.resolve(workspacePackageDir, opts.templateFile);
76
- const template = JSON.parse(readFileSync(fullPath, "utf-8"));
86
+ const template = JSON.parse(context.host.readFile(fullPath, { encoding: "utf-8" }));
77
87
 
78
- return makeGenerator(template, opts.excludedReferences);
88
+ return makeGenerator(template, opts.excludedReferences, opts.additionalReferences, opts.tsconfigReferenceFile);
79
89
  } else if (opts.template) {
80
- return makeGenerator(opts.template, opts.excludedReferences);
90
+ return makeGenerator(opts.template, opts.excludedReferences, opts.additionalReferences, opts.tsconfigReferenceFile);
81
91
  } else {
82
92
  throw new Error("Unable to make generator");
83
93
  }
84
94
  }
85
95
 
86
- function makeGenerator(template: any, excludedReferences: ReadonlyArray<string> = []) {
87
- return function generator(context: Context) {
96
+ function makeGenerator(
97
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
98
+ template: any,
99
+ excludedReferences: ReadonlyArray<string> | undefined,
100
+ additionalReferences: ReadonlyArray<string> | undefined,
101
+ tsconfigReferenceFile?: string
102
+ ) {
103
+ return async function generator(context: Context) {
88
104
  template = {
89
105
  ...template,
90
106
  references: [],
91
107
  }; // make a copy and ensure we have a references array
92
108
 
93
- const nameToDirectory = getPackageNameToDir(context.getWorkspaceContext().packageDir);
109
+ const nameToDirectory = await context.getWorkspaceContext().getPackageNameToDir();
94
110
 
95
111
  const packageJson = context.getPackageJson();
96
112
  const deps = [...Object.keys(packageJson.dependencies || {}), ...Object.keys(packageJson.devDependencies || {})];
113
+ for (const dep of deps) {
114
+ const packageDir = nameToDirectory.get(dep);
115
+ if (packageDir !== undefined && (excludedReferences === undefined || !matchesAnyGlob(dep, excludedReferences))) {
116
+ const absoluteReferencePath =
117
+ tsconfigReferenceFile !== undefined ? path.join(packageDir, tsconfigReferenceFile) : packageDir;
118
+ template.references.push({
119
+ path: path.relative(context.packageDir, absoluteReferencePath),
120
+ });
121
+ }
122
+ }
97
123
 
98
- deps
99
- .filter(
100
- name => nameToDirectory.has(name) && !excludedReferences.some(excludedRef => minimatch(name, excludedRef))
101
- )
102
- .forEach(packageName => {
124
+ if (additionalReferences) {
125
+ for (const additionalReference of additionalReferences) {
103
126
  template.references.push({
104
- path: path.relative(context.packageDir, nameToDirectory.get(packageName)!),
127
+ path: additionalReference,
105
128
  });
106
- });
129
+ }
130
+ }
107
131
 
108
132
  return JSON.stringify(template, undefined, 2) + "\n";
109
133
  };
@@ -5,9 +5,8 @@
5
5
  *
6
6
  */
7
7
 
8
- import { Context } from "@monorepolint/core";
9
- import { writeJson } from "@monorepolint/utils";
10
- import diff from "jest-diff";
8
+ import { Context } from "@monorepolint/config";
9
+ import { diff } from "jest-diff";
11
10
 
12
11
  export function checkAlpha(
13
12
  context: Context,
@@ -28,18 +27,18 @@ export function checkAlpha(
28
27
  if (!arrayOrderCompare(actualOrder, expectedOrder)) {
29
28
  context.addError({
30
29
  file: packagePath,
31
- message: `Incorrect order of ${block} in package.json`,
30
+ message: createIncorrectOrderErrorMessage(block, packageJson.name!),
32
31
  longMessage: diff(expectedOrder, actualOrder, { expand: true }),
33
32
  fixer: () => {
34
33
  const expectedDependencies: Record<string, string> = {};
35
34
 
36
- expectedOrder.forEach(dep => {
35
+ expectedOrder.forEach((dep) => {
37
36
  expectedDependencies[dep] = blockToSort[dep];
38
37
  });
39
38
 
40
39
  const newPackageJson = { ...packageJson };
41
40
  newPackageJson[block] = expectedDependencies;
42
- writeJson(packagePath, newPackageJson);
41
+ context.host.writeJson(packagePath, newPackageJson);
43
42
  },
44
43
  });
45
44
  }
@@ -54,3 +53,7 @@ function arrayOrderCompare(a: ReadonlyArray<string>, b: ReadonlyArray<string>) {
54
53
 
55
54
  return true;
56
55
  }
56
+
57
+ export function createIncorrectOrderErrorMessage(block: string, packageName: string) {
58
+ return `Incorrect order of ${block} in ${packageName}'s package.json`;
59
+ }
@@ -0,0 +1,24 @@
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 { existsSync, mkdirSync } from "fs";
9
+ import * as path from "path";
10
+
11
+ export function makeDirectoryRecursively(directoryPath: string) {
12
+ // node < 10 doesn't support mkdirSync w/ recursive: true
13
+ // so we manually do it instead
14
+ const dirSegments = directoryPath.split(path.sep);
15
+ for (let i = 0; i < dirSegments.length; i++) {
16
+ if (dirSegments[i].length > 0) {
17
+ // we skip the empty segment
18
+ const curDirPath = dirSegments.slice(0, i + 1).join(path.sep);
19
+ if (!existsSync(curDirPath)) {
20
+ mkdirSync(curDirPath);
21
+ }
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,29 @@
1
+ import { Context, RuleEntry, RuleModule } from "@monorepolint/config";
2
+
3
+ let globalId = 0;
4
+ export function makeRule<X>({
5
+ name,
6
+ check,
7
+ validateOptions,
8
+ printStats,
9
+ }: {
10
+ name: string;
11
+ check: (context: Context, options: X, extra: { id: string }) => Promise<unknown> | unknown;
12
+ printStats?: () => void;
13
+ validateOptions: (options: unknown) => asserts options is X;
14
+ }): RuleFunction<X> {
15
+ return function (ruleEntry) {
16
+ const id = `${name} :: ${globalId++}`;
17
+ return {
18
+ id,
19
+ // eslint-disable-next-line @typescript-eslint/no-extra-non-null-assertion
20
+ check: (context) => check(context, ruleEntry.options!!, { id }),
21
+ name,
22
+ validateOptions,
23
+ ruleEntry,
24
+ printStats,
25
+ };
26
+ };
27
+ }
28
+
29
+ type RuleFunction<T> = (ruleEntry: RuleEntry<T>) => RuleModule<T>;