@monorepolint/rules 0.5.0-alpha.12 → 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.
- package/.turbo/turbo-clean.log +4 -0
- package/.turbo/turbo-compile-typescript.log +4 -0
- package/.turbo/turbo-lint.log +14 -0
- package/.turbo/turbo-test.log +666 -0
- package/.turbo/turbo-transpile-typescript.log +18 -0
- package/build/js/chunk-A2MUUSNE.js +26 -0
- package/build/js/chunk-A2MUUSNE.js.map +1 -0
- package/build/js/index.js +1402 -0
- package/build/js/index.js.map +1 -0
- package/build/js/public/util.js +7 -0
- package/build/js/public/util.js.map +1 -0
- package/build/tsconfig.tsbuildinfo +1 -0
- package/build/types/__tests__/alphabeticalScripts.spec.d.ts.map +1 -0
- package/build/types/__tests__/bannedDependencies.spec.d.ts +2 -0
- package/build/types/__tests__/bannedDependencies.spec.d.ts.map +1 -0
- package/build/types/__tests__/consistentDependencies.spec.d.ts.map +1 -0
- package/build/types/__tests__/consistentVersions.spec.d.ts +8 -0
- package/build/types/__tests__/consistentVersions.spec.d.ts.map +1 -0
- package/build/types/__tests__/fileContents.spec.d.ts +8 -0
- package/build/types/__tests__/fileContents.spec.d.ts.map +1 -0
- package/build/types/__tests__/mustSatisfyPeerDependencies.spec.d.ts +8 -0
- package/build/types/__tests__/mustSatisfyPeerDependencies.spec.d.ts.map +1 -0
- package/build/types/__tests__/nestedWorkspaces.spec.d.ts +2 -0
- package/build/types/__tests__/nestedWorkspaces.spec.d.ts.map +1 -0
- package/build/types/__tests__/packageEntry.spec.d.ts.map +1 -0
- package/build/types/__tests__/packageOrder.spec.d.ts.map +1 -0
- package/build/types/__tests__/packageScript.spec.d.ts.map +1 -0
- package/build/types/__tests__/requireDependency.spec.d.ts +2 -0
- package/build/types/__tests__/requireDependency.spec.d.ts.map +1 -0
- package/build/types/__tests__/utils.d.ts +81 -0
- package/build/types/__tests__/utils.d.ts.map +1 -0
- package/build/types/alphabeticalDependencies.d.ts +8 -0
- package/build/types/alphabeticalDependencies.d.ts.map +1 -0
- package/build/types/alphabeticalScripts.d.ts +8 -0
- package/build/types/alphabeticalScripts.d.ts.map +1 -0
- package/build/types/bannedDependencies.d.ts +66 -0
- package/build/types/bannedDependencies.d.ts.map +1 -0
- package/build/types/consistentDependencies.d.ts +18 -0
- package/build/types/consistentDependencies.d.ts.map +1 -0
- package/build/types/consistentVersions.d.ts +25 -0
- package/build/types/consistentVersions.d.ts.map +1 -0
- package/build/types/fileContents.d.ts +39 -0
- package/build/types/fileContents.d.ts.map +1 -0
- package/build/types/index.d.ts +20 -0
- package/build/types/index.d.ts.map +1 -0
- package/build/types/mustSatisfyPeerDependencies.d.ts +427 -0
- package/build/types/mustSatisfyPeerDependencies.d.ts.map +1 -0
- package/build/types/nestedWorkspaces.d.ts +10 -0
- package/build/types/nestedWorkspaces.d.ts.map +1 -0
- package/build/types/packageEntry.d.ts +62 -0
- package/build/types/packageEntry.d.ts.map +1 -0
- package/build/types/packageOrder.d.ts +12 -0
- package/build/types/packageOrder.d.ts.map +1 -0
- package/build/types/packageScript.d.ts +47 -0
- package/build/types/packageScript.d.ts.map +1 -0
- package/build/types/public/util.d.ts +2 -0
- package/build/types/public/util.d.ts.map +1 -0
- package/build/types/requireDependency.d.ts +50 -0
- package/build/types/requireDependency.d.ts.map +1 -0
- package/build/types/standardTsconfig.d.ts +29 -0
- package/build/types/standardTsconfig.d.ts.map +1 -0
- package/{lib → build/types}/util/checkAlpha.d.ts +2 -1
- package/build/types/util/checkAlpha.d.ts.map +1 -0
- package/build/types/util/makeDirectory.d.ts +8 -0
- package/build/types/util/makeDirectory.d.ts.map +1 -0
- package/build/types/util/makeRule.d.ts +12 -0
- package/build/types/util/makeRule.d.ts.map +1 -0
- package/build/types/util/packageDependencyGraphService.d.ts +37 -0
- package/build/types/util/packageDependencyGraphService.d.ts.map +1 -0
- package/package.json +45 -20
- package/src/__tests__/alphabeticalScripts.spec.ts +33 -31
- package/src/__tests__/bannedDependencies.spec.ts +189 -0
- package/src/__tests__/consistentDependencies.spec.ts +42 -26
- package/src/__tests__/consistentVersions.spec.ts +224 -0
- package/src/__tests__/fileContents.spec.ts +101 -0
- package/src/__tests__/mustSatisfyPeerDependencies.spec.ts +1189 -0
- package/src/__tests__/nestedWorkspaces.spec.ts +153 -0
- package/src/__tests__/packageEntry.spec.ts +127 -49
- package/src/__tests__/packageOrder.spec.ts +68 -53
- package/src/__tests__/packageScript.spec.ts +124 -98
- package/src/__tests__/requireDependency.spec.ts +152 -0
- package/src/__tests__/utils.ts +113 -13
- package/src/alphabeticalDependencies.ts +7 -9
- package/src/alphabeticalScripts.ts +7 -9
- package/src/bannedDependencies.ts +135 -46
- package/src/consistentDependencies.ts +41 -17
- package/src/consistentVersions.ts +141 -0
- package/src/fileContents.ts +44 -37
- package/src/index.ts +13 -9
- package/src/mustSatisfyPeerDependencies.ts +744 -0
- package/src/nestedWorkspaces.ts +60 -0
- package/src/packageEntry.ts +72 -28
- package/src/packageOrder.ts +17 -13
- package/src/packageScript.ts +15 -19
- package/src/public/util.ts +1 -0
- package/src/requireDependency.ts +71 -0
- package/src/standardTsconfig.ts +51 -27
- package/src/util/checkAlpha.ts +9 -6
- package/src/util/makeDirectory.ts +24 -0
- package/src/util/makeRule.ts +29 -0
- package/src/util/packageDependencyGraphService.ts +114 -0
- package/tsconfig.json +10 -2
- package/lib/__tests__/alphabeticalScripts.spec.d.ts.map +0 -1
- package/lib/__tests__/alphabeticalScripts.spec.js +0 -61
- package/lib/__tests__/alphabeticalScripts.spec.js.map +0 -1
- package/lib/__tests__/consistentDependencies.spec.d.ts.map +0 -1
- package/lib/__tests__/consistentDependencies.spec.js +0 -107
- package/lib/__tests__/consistentDependencies.spec.js.map +0 -1
- package/lib/__tests__/packageEntry.spec.d.ts.map +0 -1
- package/lib/__tests__/packageEntry.spec.js +0 -99
- package/lib/__tests__/packageEntry.spec.js.map +0 -1
- package/lib/__tests__/packageOrder.spec.d.ts.map +0 -1
- package/lib/__tests__/packageOrder.spec.js +0 -115
- package/lib/__tests__/packageOrder.spec.js.map +0 -1
- package/lib/__tests__/packageScript.spec.d.ts.map +0 -1
- package/lib/__tests__/packageScript.spec.js +0 -172
- package/lib/__tests__/packageScript.spec.js.map +0 -1
- package/lib/__tests__/utils.d.ts +0 -9
- package/lib/__tests__/utils.d.ts.map +0 -1
- package/lib/__tests__/utils.js +0 -27
- package/lib/__tests__/utils.js.map +0 -1
- package/lib/alphabeticalDependencies.d.ts +0 -12
- package/lib/alphabeticalDependencies.d.ts.map +0 -1
- package/lib/alphabeticalDependencies.js +0 -21
- package/lib/alphabeticalDependencies.js.map +0 -1
- package/lib/alphabeticalScripts.d.ts +0 -12
- package/lib/alphabeticalScripts.d.ts.map +0 -1
- package/lib/alphabeticalScripts.js +0 -19
- package/lib/alphabeticalScripts.js.map +0 -1
- package/lib/bannedDependencies.d.ts +0 -15
- package/lib/bannedDependencies.d.ts.map +0 -1
- package/lib/bannedDependencies.js +0 -57
- package/lib/bannedDependencies.js.map +0 -1
- package/lib/consistentDependencies.d.ts +0 -10
- package/lib/consistentDependencies.d.ts.map +0 -1
- package/lib/consistentDependencies.js +0 -57
- package/lib/consistentDependencies.js.map +0 -1
- package/lib/fileContents.d.ts +0 -25
- package/lib/fileContents.d.ts.map +0 -1
- package/lib/fileContents.js +0 -77
- package/lib/fileContents.js.map +0 -1
- package/lib/index.d.ts +0 -16
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js +0 -27
- package/lib/index.js.map +0 -1
- package/lib/packageEntry.d.ts +0 -16
- package/lib/packageEntry.d.ts.map +0 -1
- package/lib/packageEntry.js +0 -40
- package/lib/packageEntry.js.map +0 -1
- package/lib/packageOrder.d.ts +0 -12
- package/lib/packageOrder.d.ts.map +0 -1
- package/lib/packageOrder.js +0 -103
- package/lib/packageOrder.js.map +0 -1
- package/lib/packageScript.d.ts +0 -25
- package/lib/packageScript.d.ts.map +0 -1
- package/lib/packageScript.js +0 -89
- package/lib/packageScript.js.map +0 -1
- package/lib/standardTsconfig.d.ts +0 -33
- package/lib/standardTsconfig.d.ts.map +0 -1
- package/lib/standardTsconfig.js +0 -98
- package/lib/standardTsconfig.js.map +0 -1
- package/lib/util/checkAlpha.d.ts.map +0 -1
- package/lib/util/checkAlpha.js +0 -47
- package/lib/util/checkAlpha.js.map +0 -1
- package/tsconfig.tsbuildinfo +0 -2484
- /package/{lib → build/types}/__tests__/alphabeticalScripts.spec.d.ts +0 -0
- /package/{lib → build/types}/__tests__/consistentDependencies.spec.d.ts +0 -0
- /package/{lib → build/types}/__tests__/packageEntry.spec.d.ts +0 -0
- /package/{lib → build/types}/__tests__/packageOrder.spec.d.ts +0 -0
- /package/{lib → build/types}/__tests__/packageScript.spec.d.ts +0 -0
- /package/{jest.config.js → jest.config.cjs} +0 -0
|
@@ -5,65 +5,154 @@
|
|
|
5
5
|
*
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { Context
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
11
|
-
import
|
|
8
|
+
import { Context } from "@monorepolint/config";
|
|
9
|
+
import { matchesAnyGlob } from "@monorepolint/utils";
|
|
10
|
+
import { AggregateTiming } from "@monorepolint/utils";
|
|
11
|
+
import * as path from "node:path";
|
|
12
12
|
import * as r from "runtypes";
|
|
13
|
+
import { makeRule } from "./util/makeRule.js";
|
|
14
|
+
import { IPackageDependencyGraphNode, PackageDependencyGraphService } from "./util/packageDependencyGraphService.js";
|
|
15
|
+
// FIXME: This rule is messed. bannedTransitiveDependencies doesnt glob
|
|
13
16
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
const bannedDepGlobsField = r.Union(
|
|
18
|
+
r.Array(r.String),
|
|
19
|
+
r.Record({
|
|
20
|
+
glob: r.Array(r.String).optional(),
|
|
21
|
+
exact: r.Array(r.String).optional(),
|
|
22
|
+
})
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const Options = r.Union(
|
|
26
|
+
r.Record({
|
|
27
|
+
bannedDependencies: bannedDepGlobsField,
|
|
28
|
+
bannedTransitiveDependencies: r.Undefined.optional(),
|
|
29
|
+
}),
|
|
30
|
+
|
|
31
|
+
r.Record({
|
|
32
|
+
bannedDependencies: bannedDepGlobsField.optional(),
|
|
33
|
+
bannedTransitiveDependencies: r.Array(r.String),
|
|
34
|
+
}),
|
|
35
|
+
|
|
36
|
+
r.Record({
|
|
37
|
+
bannedDependencies: bannedDepGlobsField.optional(),
|
|
38
|
+
bannedTransitiveDependencies: r.Array(r.String).optional(),
|
|
39
|
+
})
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
export type Options = r.Static<typeof Options>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* We use this locally to avoid making a billion sets. Because check is called once per package
|
|
46
|
+
* (with the exact same config object reference) we can save quite a bit of time by reusing this cache.
|
|
47
|
+
*/
|
|
48
|
+
const setCache = new Map<ReadonlyArray<string>, Set<string>>();
|
|
49
|
+
|
|
50
|
+
const aggregateTiming = new AggregateTiming(":bannedDependencies stats");
|
|
51
|
+
|
|
52
|
+
export const bannedDependencies = makeRule<Options>({
|
|
53
|
+
name: "bannedDependencies",
|
|
54
|
+
check: (context, opts, extra) => {
|
|
55
|
+
aggregateTiming.start(extra?.id ?? "unknown id");
|
|
17
56
|
|
|
18
|
-
|
|
57
|
+
const packageJson = context.getPackageJson();
|
|
58
|
+
const packagePath = context.getPackageJsonPath();
|
|
19
59
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
60
|
+
const curDeps = packageJson.dependencies && Object.keys(packageJson.dependencies);
|
|
61
|
+
const curDevDeps = packageJson.devDependencies && Object.keys(packageJson.devDependencies);
|
|
62
|
+
const curPeerDeps = packageJson.peerDependencies && Object.keys(packageJson.peerDependencies);
|
|
63
|
+
|
|
64
|
+
const { bannedDependencies: banned, bannedTransitiveDependencies: transitives } = opts;
|
|
65
|
+
|
|
66
|
+
const globs = banned && (Array.isArray(banned) ? banned : banned.glob);
|
|
67
|
+
const exacts = banned && (Array.isArray(banned) ? undefined : banned.exact);
|
|
68
|
+
|
|
69
|
+
const violations = new Set<string>();
|
|
70
|
+
|
|
71
|
+
if (globs) {
|
|
72
|
+
if (curDeps) populateProblemsGlobs(globs, curDeps, violations);
|
|
73
|
+
if (curDevDeps) populateProblemsGlobs(globs, curDevDeps, violations);
|
|
74
|
+
if (curPeerDeps) populateProblemsGlobs(globs, curPeerDeps, violations);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (exacts) {
|
|
78
|
+
let set = setCache.get(exacts);
|
|
79
|
+
if (set === undefined) {
|
|
80
|
+
set = new Set(exacts);
|
|
81
|
+
setCache.set(exacts, set);
|
|
82
|
+
}
|
|
83
|
+
if (curDeps) populateProblemsExact(set, curDeps, violations);
|
|
84
|
+
if (curDevDeps) populateProblemsExact(set, curDevDeps, violations);
|
|
85
|
+
if (curPeerDeps) populateProblemsExact(set, curPeerDeps, violations);
|
|
86
|
+
}
|
|
24
87
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
88
|
+
if (violations.size > 0) {
|
|
89
|
+
context.addError({
|
|
90
|
+
file: packagePath,
|
|
91
|
+
message:
|
|
92
|
+
`Found ${violations.size} banned dependencies of package.json:\n\t` +
|
|
93
|
+
Array.from(violations)
|
|
94
|
+
.map((v) => `'${v}'`)
|
|
95
|
+
.join(", "),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (transitives) {
|
|
100
|
+
let set = setCache.get(transitives);
|
|
101
|
+
if (set === undefined) {
|
|
102
|
+
set = new Set(transitives);
|
|
103
|
+
setCache.set(transitives, set);
|
|
104
|
+
}
|
|
105
|
+
checkTransitives(context, set);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
aggregateTiming.stop();
|
|
28
109
|
},
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// tslint:disable-next-line:no-shadowed-variable
|
|
35
|
-
bannedDependencies: ReadonlyArray<string>,
|
|
36
|
-
block: "dependencies" | "devDependencies" | "peerDependencies"
|
|
37
|
-
) {
|
|
38
|
-
const packageJson = context.getPackageJson();
|
|
39
|
-
const packagePath = context.getPackageJsonPath();
|
|
110
|
+
validateOptions: Options.check,
|
|
111
|
+
printStats: () => {
|
|
112
|
+
aggregateTiming.printResults();
|
|
113
|
+
},
|
|
114
|
+
});
|
|
40
115
|
|
|
41
|
-
|
|
116
|
+
function populateProblemsExact(banned: Set<string>, dependencies: ReadonlyArray<string>, violations: Set<string>) {
|
|
117
|
+
for (const dependency of dependencies) {
|
|
118
|
+
if (banned.has(dependency)) {
|
|
119
|
+
violations.add(dependency);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
42
123
|
|
|
43
|
-
|
|
44
|
-
|
|
124
|
+
function populateProblemsGlobs(
|
|
125
|
+
bannedDependencyGlobs: ReadonlyArray<string>,
|
|
126
|
+
dependencies: ReadonlyArray<string>,
|
|
127
|
+
violations: Set<string>
|
|
128
|
+
) {
|
|
129
|
+
for (const dependency of dependencies) {
|
|
130
|
+
if (matchesAnyGlob(dependency, bannedDependencyGlobs)) {
|
|
131
|
+
violations.add(dependency);
|
|
132
|
+
}
|
|
45
133
|
}
|
|
134
|
+
}
|
|
46
135
|
|
|
47
|
-
|
|
136
|
+
// This function is slow. God help you if you use this on a big repo
|
|
137
|
+
function checkTransitives(context: Context, banned: Set<string>) {
|
|
138
|
+
const graphService = new PackageDependencyGraphService();
|
|
139
|
+
const root = graphService.buildDependencyGraph(path.resolve(context.getPackageJsonPath()), context.host);
|
|
140
|
+
for (const { dependencies, importPath } of graphService.traverse(root)) {
|
|
141
|
+
for (const [dependency] of dependencies) {
|
|
142
|
+
if (banned.has(dependency)) {
|
|
143
|
+
// Remove the starting package since it's obvious in CLI output.
|
|
144
|
+
const [, ...importPathWithoutRoot] = importPath;
|
|
145
|
+
const pathing = [...importPathWithoutRoot.map(nameOrPackageJsonPath), dependency].join(" -> ");
|
|
48
146
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
147
|
+
context.addError({
|
|
148
|
+
file: root.paths.packageJsonPath,
|
|
149
|
+
message: `Banned transitive dependencies in repo: ${pathing}`,
|
|
150
|
+
});
|
|
53
151
|
}
|
|
54
152
|
}
|
|
55
153
|
}
|
|
154
|
+
}
|
|
56
155
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
file: packagePath,
|
|
60
|
-
message: `Banned depdendencies in ${block} in package.json`,
|
|
61
|
-
longMessage: diff(expectedDependencies, dependencies, { expand: true }),
|
|
62
|
-
fixer: () => {
|
|
63
|
-
const newPackageJson = { ...packageJson };
|
|
64
|
-
newPackageJson[block] = expectedDependencies;
|
|
65
|
-
writeJson(packagePath, newPackageJson);
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
}
|
|
156
|
+
function nameOrPackageJsonPath(node: IPackageDependencyGraphNode): string {
|
|
157
|
+
return node.packageJson.name ?? node.paths.packageJsonPath;
|
|
69
158
|
}
|
|
@@ -5,24 +5,30 @@
|
|
|
5
5
|
*
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { Context
|
|
9
|
-
import {
|
|
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";
|
|
11
|
+
import { makeRule } from "./util/makeRule.js";
|
|
12
|
+
const Options = r
|
|
13
|
+
.Record({
|
|
14
|
+
ignoredDependencies: r.Array(r.String).Or(r.Undefined),
|
|
15
|
+
})
|
|
16
|
+
.Or(r.Undefined);
|
|
17
|
+
export type Options = r.Static<typeof Options>;
|
|
12
18
|
|
|
13
|
-
const
|
|
14
|
-
type Options = r.Static<typeof Options>;
|
|
19
|
+
const skippedVersions = ["*", "latest"];
|
|
15
20
|
|
|
16
|
-
export const consistentDependencies = {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
checkDeps(context, "
|
|
21
|
+
export const consistentDependencies = makeRule<Options>({
|
|
22
|
+
name: "consistentDependencies",
|
|
23
|
+
check: (context, args) => {
|
|
24
|
+
checkDeps(context, args, "dependencies");
|
|
25
|
+
checkDeps(context, args, "devDependencies");
|
|
20
26
|
// we don't check peer deps since they can be more lenient
|
|
21
27
|
},
|
|
22
|
-
|
|
23
|
-
}
|
|
28
|
+
validateOptions: Options.check,
|
|
29
|
+
});
|
|
24
30
|
|
|
25
|
-
function checkDeps(context: Context, block: "dependencies" | "devDependencies" | "peerDependencies") {
|
|
31
|
+
function checkDeps(context: Context, args: Options, block: "dependencies" | "devDependencies" | "peerDependencies") {
|
|
26
32
|
const packageJson = context.getPackageJson();
|
|
27
33
|
const packagePath = context.getPackageJsonPath();
|
|
28
34
|
const dependencies = packageJson[block];
|
|
@@ -30,37 +36,55 @@ function checkDeps(context: Context, block: "dependencies" | "devDependencies" |
|
|
|
30
36
|
const workspacePackageJson = context.getWorkspaceContext().getPackageJson();
|
|
31
37
|
const workspaceDependencies = workspacePackageJson[block];
|
|
32
38
|
|
|
33
|
-
|
|
39
|
+
const ignoredDeps = args?.ignoredDependencies ?? [];
|
|
40
|
+
const depsToCheck =
|
|
41
|
+
workspaceDependencies == null || ignoredDeps.length === 0
|
|
42
|
+
? workspaceDependencies
|
|
43
|
+
: omit(workspaceDependencies, ignoredDeps);
|
|
44
|
+
|
|
45
|
+
if (dependencies === undefined || depsToCheck === undefined) {
|
|
34
46
|
return;
|
|
35
47
|
}
|
|
36
48
|
|
|
37
49
|
const expectedDependencies = {
|
|
38
50
|
...dependencies,
|
|
39
|
-
...filterKeys(
|
|
51
|
+
...filterKeys(depsToCheck, dependencies),
|
|
40
52
|
};
|
|
41
53
|
|
|
42
54
|
if (JSON.stringify(dependencies) !== JSON.stringify(expectedDependencies)) {
|
|
43
55
|
context.addError({
|
|
44
56
|
file: packagePath,
|
|
45
|
-
message: `
|
|
57
|
+
message: `Inconsistent ${block} with root in package.json`,
|
|
46
58
|
longMessage: diff(expectedDependencies, dependencies, { expand: true }),
|
|
47
59
|
fixer: () => {
|
|
48
60
|
const newPackageJson = { ...packageJson };
|
|
49
61
|
newPackageJson[block] = expectedDependencies;
|
|
50
|
-
writeJson(packagePath, newPackageJson);
|
|
62
|
+
context.host.writeJson(packagePath, newPackageJson);
|
|
51
63
|
},
|
|
52
64
|
});
|
|
53
65
|
}
|
|
54
66
|
}
|
|
55
67
|
|
|
56
68
|
function filterKeys(ob: Record<string, string>, filterOb: Record<string, string>) {
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
70
|
const newOb: Record<string, any> = {};
|
|
58
71
|
|
|
59
72
|
for (const key of Object.keys(filterOb)) {
|
|
60
|
-
if (ob[key] !== undefined && filterOb[key]
|
|
73
|
+
if (ob[key] !== undefined && skippedVersions.indexOf(filterOb[key]) === -1) {
|
|
61
74
|
newOb[key] = ob[key];
|
|
62
75
|
}
|
|
63
76
|
}
|
|
64
77
|
|
|
65
78
|
return newOb;
|
|
66
79
|
}
|
|
80
|
+
|
|
81
|
+
function omit<T>(obj: Record<string, T>, keysToOmit: readonly string[]): Record<string, T> {
|
|
82
|
+
const newObj: Record<string, T> = {};
|
|
83
|
+
|
|
84
|
+
const filtered = Object.entries(obj).filter(([key]) => !keysToOmit.includes(key));
|
|
85
|
+
for (const [key, value] of filtered) {
|
|
86
|
+
newObj[key] = value;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return newObj;
|
|
90
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright 2020 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 * as r from "runtypes";
|
|
11
|
+
import { coerce, SemVer } from "semver";
|
|
12
|
+
import { makeRule } from "./util/makeRule.js";
|
|
13
|
+
export const Options = r.Record({
|
|
14
|
+
matchDependencyVersions: r.Dictionary(r.Union(r.String, r.Array(r.String))),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export type Options = r.Static<typeof Options>;
|
|
18
|
+
|
|
19
|
+
export const consistentVersions = makeRule({
|
|
20
|
+
name: "consistentVersions",
|
|
21
|
+
check: checkConsistentVersions,
|
|
22
|
+
validateOptions: Options.check,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
function checkConsistentVersions(context: Context, options: Options) {
|
|
26
|
+
for (const [dependencyPackageName, expectedPackageDependencyValue] of Object.entries(
|
|
27
|
+
options.matchDependencyVersions
|
|
28
|
+
)) {
|
|
29
|
+
if (Array.isArray(expectedPackageDependencyValue)) {
|
|
30
|
+
ensurePackageMatchesSomeVersion(context, dependencyPackageName, expectedPackageDependencyValue);
|
|
31
|
+
} else {
|
|
32
|
+
ensurePackageIsCorrectVersion(context, dependencyPackageName, expectedPackageDependencyValue);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const ensurePackageIsCorrectVersion = (
|
|
38
|
+
context: Context,
|
|
39
|
+
dependencyPackageName: string,
|
|
40
|
+
expectedPackageDependencyValue: string
|
|
41
|
+
) => {
|
|
42
|
+
const packageJson = context.getPackageJson();
|
|
43
|
+
const packageJsonPath = context.getPackageJsonPath();
|
|
44
|
+
|
|
45
|
+
const expectedPackageDependencyVersion = coerce(expectedPackageDependencyValue);
|
|
46
|
+
if (expectedPackageDependencyVersion == null) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`Malformed expected package dependency version defined in monorepolint configuration: ${dependencyPackageName} @ '${expectedPackageDependencyValue}'`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const actualPackageDependencyValue = packageJson.dependencies && packageJson.dependencies[dependencyPackageName];
|
|
53
|
+
const actualPackageDependencyVersion = coerce(actualPackageDependencyValue);
|
|
54
|
+
if (
|
|
55
|
+
actualPackageDependencyVersion != null &&
|
|
56
|
+
actualPackageDependencyVersion.raw !== expectedPackageDependencyVersion.raw
|
|
57
|
+
) {
|
|
58
|
+
context.addError({
|
|
59
|
+
file: packageJsonPath,
|
|
60
|
+
message: `Expected dependency on ${dependencyPackageName} to match version defined in monorepolint configuration '${expectedPackageDependencyValue}', got '${actualPackageDependencyValue}' instead.`,
|
|
61
|
+
fixer: () =>
|
|
62
|
+
mutateJson<PackageJson>(packageJsonPath, context.host, (input) => {
|
|
63
|
+
input.dependencies![dependencyPackageName] = expectedPackageDependencyValue;
|
|
64
|
+
return input;
|
|
65
|
+
}),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const actualPackageDevDependencyValue =
|
|
70
|
+
packageJson.devDependencies && packageJson.devDependencies[dependencyPackageName];
|
|
71
|
+
const actualPackageDevDependencyVersion = coerce(actualPackageDevDependencyValue);
|
|
72
|
+
if (
|
|
73
|
+
actualPackageDevDependencyVersion != null &&
|
|
74
|
+
actualPackageDevDependencyVersion.raw !== expectedPackageDependencyVersion.raw
|
|
75
|
+
) {
|
|
76
|
+
context.addError({
|
|
77
|
+
file: packageJsonPath,
|
|
78
|
+
message: `Expected devDependency on ${dependencyPackageName} to match version defined in monorepolint configuration '${expectedPackageDependencyValue}', got '${actualPackageDevDependencyValue}' instead`,
|
|
79
|
+
fixer: () =>
|
|
80
|
+
mutateJson<PackageJson>(packageJsonPath, context.host, (input) => {
|
|
81
|
+
input.devDependencies![dependencyPackageName] = expectedPackageDependencyValue;
|
|
82
|
+
return input;
|
|
83
|
+
}),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const ensurePackageMatchesSomeVersion = (
|
|
89
|
+
context: Context,
|
|
90
|
+
dependencyPackageName: string,
|
|
91
|
+
acceptedPackageDependencyValues: string[]
|
|
92
|
+
) => {
|
|
93
|
+
const packageJson = context.getPackageJson();
|
|
94
|
+
const packageJsonPath = context.getPackageJsonPath();
|
|
95
|
+
|
|
96
|
+
const acceptedPackageDependencyVersions: SemVer[] = acceptedPackageDependencyValues.map(
|
|
97
|
+
(acceptedPackageDependencyValue) => {
|
|
98
|
+
const acceptedPackageDependencyVersion = coerce(acceptedPackageDependencyValue);
|
|
99
|
+
if (acceptedPackageDependencyVersion == null) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Malformed accepted package dependency version defined in monorepolint configuration: ${dependencyPackageName} @ '${acceptedPackageDependencyValue}'`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
return acceptedPackageDependencyVersion;
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const actualPackageDependencyValue = packageJson.dependencies && packageJson.dependencies[dependencyPackageName];
|
|
109
|
+
const actualPackageDependencyVersion = coerce(actualPackageDependencyValue);
|
|
110
|
+
if (
|
|
111
|
+
actualPackageDependencyVersion != null &&
|
|
112
|
+
acceptedPackageDependencyVersions.every(
|
|
113
|
+
(acceptedPackageDependencyVersion) => actualPackageDependencyVersion.raw !== acceptedPackageDependencyVersion.raw
|
|
114
|
+
)
|
|
115
|
+
) {
|
|
116
|
+
context.addError({
|
|
117
|
+
file: packageJsonPath,
|
|
118
|
+
message: `Expected dependency on ${dependencyPackageName} to match one of '${JSON.stringify(
|
|
119
|
+
acceptedPackageDependencyValues
|
|
120
|
+
)}', got '${actualPackageDependencyValue}' instead.`,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const actualPackageDevDependencyValue =
|
|
125
|
+
packageJson.devDependencies && packageJson.devDependencies[dependencyPackageName];
|
|
126
|
+
const actualPackageDevDependencyVersion = coerce(actualPackageDevDependencyValue);
|
|
127
|
+
if (
|
|
128
|
+
actualPackageDevDependencyVersion != null &&
|
|
129
|
+
acceptedPackageDependencyVersions.every(
|
|
130
|
+
(acceptedPackageDependencyVersion) =>
|
|
131
|
+
actualPackageDevDependencyVersion.raw !== acceptedPackageDependencyVersion.raw
|
|
132
|
+
)
|
|
133
|
+
) {
|
|
134
|
+
context.addError({
|
|
135
|
+
file: packageJsonPath,
|
|
136
|
+
message: `Expected devDependency on ${dependencyPackageName} to match one of '${JSON.stringify(
|
|
137
|
+
acceptedPackageDependencyValues
|
|
138
|
+
)}', got '${actualPackageDevDependencyValue}' instead.`,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
};
|
package/src/fileContents.ts
CHANGED
|
@@ -1,50 +1,48 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* Copyright
|
|
2
|
+
* Copyright 2023 Palantir Technologies, Inc.
|
|
3
3
|
*
|
|
4
4
|
* Licensed under the MIT license. See LICENSE file in the project root for details.
|
|
5
5
|
*
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { Context } from "@monorepolint/
|
|
9
|
-
import {
|
|
10
|
-
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
11
|
-
import diff from "jest-diff";
|
|
8
|
+
import { Context } from "@monorepolint/config";
|
|
9
|
+
import { diff } from "jest-diff";
|
|
12
10
|
import * as path from "path";
|
|
13
11
|
import * as r from "runtypes";
|
|
14
|
-
|
|
12
|
+
import { makeRule } from "./util/makeRule.js";
|
|
15
13
|
const Options = r.Union(
|
|
16
14
|
r.Record({
|
|
17
15
|
file: r.String,
|
|
18
|
-
generator: r.Function,
|
|
19
|
-
template: r.Undefined,
|
|
20
|
-
templateFile: r.Undefined,
|
|
16
|
+
generator: r.Function.withGuard((x): x is (context: Context) => string | Promise<string> => x != undefined),
|
|
17
|
+
template: r.Undefined.optional(),
|
|
18
|
+
templateFile: r.Undefined.optional(),
|
|
21
19
|
}),
|
|
22
20
|
|
|
23
21
|
r.Record({
|
|
24
22
|
file: r.String,
|
|
25
|
-
generator: r.Undefined,
|
|
23
|
+
generator: r.Undefined.optional(),
|
|
26
24
|
template: r.String,
|
|
27
|
-
templateFile: r.Undefined,
|
|
25
|
+
templateFile: r.Undefined.optional(),
|
|
28
26
|
}),
|
|
29
27
|
|
|
30
28
|
r.Record({
|
|
31
29
|
file: r.String,
|
|
32
|
-
generator: r.Undefined,
|
|
33
|
-
template: r.Undefined,
|
|
30
|
+
generator: r.Undefined.optional(),
|
|
31
|
+
template: r.Undefined.optional(),
|
|
34
32
|
templateFile: r.String,
|
|
35
33
|
})
|
|
36
34
|
);
|
|
37
35
|
|
|
38
36
|
type Options = r.Static<typeof Options>;
|
|
39
37
|
|
|
40
|
-
export const fileContents = {
|
|
41
|
-
|
|
38
|
+
export const fileContents = makeRule<Options>({
|
|
39
|
+
name: "fileContents",
|
|
40
|
+
check: async (context, opts) => {
|
|
42
41
|
const fullPath = path.join(context.packageDir, opts.file);
|
|
43
|
-
const
|
|
44
|
-
const expectedContent = generator(context);
|
|
45
|
-
|
|
46
|
-
const actualContent = existsSync(fullPath) ? readFileSync(fullPath, "utf-8") : undefined;
|
|
42
|
+
const expectedContent = await getExpectedContents(context, opts);
|
|
47
43
|
|
|
44
|
+
const pathExists = context.host.exists(fullPath);
|
|
45
|
+
const actualContent = pathExists ? context.host.readFile(fullPath, { encoding: "utf-8" }) : undefined;
|
|
48
46
|
if (actualContent !== expectedContent) {
|
|
49
47
|
context.addError({
|
|
50
48
|
file: fullPath,
|
|
@@ -52,36 +50,45 @@ export const fileContents = {
|
|
|
52
50
|
longMessage: diff(expectedContent, actualContent, { expand: true }),
|
|
53
51
|
fixer: () => {
|
|
54
52
|
if (expectedContent === undefined) {
|
|
55
|
-
|
|
53
|
+
if (pathExists) context.host.deleteFile(fullPath);
|
|
56
54
|
} else {
|
|
57
|
-
|
|
55
|
+
context.host.mkdir(path.dirname(fullPath), { recursive: true });
|
|
56
|
+
context.host.writeFile(fullPath, expectedContent, { encoding: "utf-8" });
|
|
58
57
|
}
|
|
59
58
|
},
|
|
60
59
|
});
|
|
61
60
|
}
|
|
62
61
|
},
|
|
63
|
-
|
|
64
|
-
}
|
|
62
|
+
validateOptions: Options.check,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const optionsCache = new Map<
|
|
66
|
+
Options,
|
|
67
|
+
((context: Context) => Promise<string> | string | undefined) | string | undefined
|
|
68
|
+
>();
|
|
69
|
+
|
|
70
|
+
async function getExpectedContents(context: Context, opts: Options) {
|
|
71
|
+
// we need to use has because undefined is a valid value in the cache
|
|
72
|
+
if (optionsCache.has(opts)) {
|
|
73
|
+
const cachedEntry = optionsCache.get(opts);
|
|
74
|
+
if (cachedEntry && typeof cachedEntry === "function") {
|
|
75
|
+
return cachedEntry(context);
|
|
76
|
+
}
|
|
77
|
+
return cachedEntry;
|
|
78
|
+
}
|
|
65
79
|
|
|
66
|
-
function getGenerator(context: Context, opts: Options) {
|
|
67
80
|
if (opts.generator) {
|
|
68
|
-
|
|
81
|
+
optionsCache.set(opts, opts.generator);
|
|
82
|
+
return opts.generator(context);
|
|
69
83
|
} else if (opts.templateFile) {
|
|
70
84
|
const { packageDir: workspacePackageDir } = context.getWorkspaceContext();
|
|
71
85
|
const fullPath = path.resolve(workspacePackageDir, opts.templateFile);
|
|
72
|
-
const template =
|
|
86
|
+
const template = context.host.readFile(fullPath, { encoding: "utf-8" });
|
|
73
87
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return makeGenerator(opts.template);
|
|
88
|
+
optionsCache.set(opts, template);
|
|
89
|
+
return template;
|
|
77
90
|
} else {
|
|
78
|
-
|
|
91
|
+
optionsCache.set(opts, opts.template);
|
|
92
|
+
return opts.template;
|
|
79
93
|
}
|
|
80
94
|
}
|
|
81
|
-
|
|
82
|
-
function makeGenerator(template: string) {
|
|
83
|
-
// tslint:disable-next-line:variable-name
|
|
84
|
-
return function generator(_context: Context) {
|
|
85
|
-
return template;
|
|
86
|
-
};
|
|
87
|
-
}
|
package/src/index.ts
CHANGED
|
@@ -5,12 +5,16 @@
|
|
|
5
5
|
*
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
export { alphabeticalDependencies } from "./alphabeticalDependencies";
|
|
9
|
-
export { alphabeticalScripts } from "./alphabeticalScripts";
|
|
10
|
-
export { bannedDependencies } from "./bannedDependencies";
|
|
11
|
-
export { consistentDependencies } from "./consistentDependencies";
|
|
12
|
-
export {
|
|
13
|
-
export {
|
|
14
|
-
export {
|
|
15
|
-
export {
|
|
16
|
-
export {
|
|
8
|
+
export { alphabeticalDependencies } from "./alphabeticalDependencies.js";
|
|
9
|
+
export { alphabeticalScripts } from "./alphabeticalScripts.js";
|
|
10
|
+
export { bannedDependencies } from "./bannedDependencies.js";
|
|
11
|
+
export { consistentDependencies } from "./consistentDependencies.js";
|
|
12
|
+
export { consistentVersions } from "./consistentVersions.js";
|
|
13
|
+
export { fileContents } from "./fileContents.js";
|
|
14
|
+
export { mustSatisfyPeerDependencies } from "./mustSatisfyPeerDependencies.js";
|
|
15
|
+
export { packageOrder } from "./packageOrder.js";
|
|
16
|
+
export { packageEntry } from "./packageEntry.js";
|
|
17
|
+
export { packageScript } from "./packageScript.js";
|
|
18
|
+
export { standardTsconfig } from "./standardTsconfig.js";
|
|
19
|
+
export { nestedWorkspaces } from "./nestedWorkspaces.js";
|
|
20
|
+
export { requireDependency } from "./requireDependency.js";
|