@monorepolint/rules 0.6.0-alpha.3 → 0.6.0-alpha.4
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 +1 -1
- package/.turbo/turbo-compile-typescript.log +1 -1
- package/.turbo/turbo-lint.log +1 -1
- package/.turbo/turbo-test.log +73 -74
- package/.turbo/turbo-transpile-typescript.log +4 -4
- package/CHANGELOG.md +10 -0
- package/build/js/index.js +104 -69
- package/build/js/index.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/bannedDependencies.d.ts.map +1 -1
- package/build/types/forceError.d.ts +4 -0
- package/build/types/forceError.d.ts.map +1 -0
- package/build/types/index.d.ts +2 -0
- package/build/types/index.d.ts.map +1 -1
- package/build/types/mustSatisfyPeerDependencies.d.ts.map +1 -1
- package/build/types/nestedWorkspaces.d.ts.map +1 -1
- package/build/types/oncePerPackage.d.ts +11 -0
- package/build/types/oncePerPackage.d.ts.map +1 -0
- package/build/types/requireDependency.d.ts.map +1 -1
- package/build/types/util/packageDependencyGraphService.d.ts.map +1 -1
- package/coverage/clover.xml +516 -469
- package/coverage/coverage-final.json +12 -10
- package/coverage/index.html +15 -15
- package/coverage/src/alphabeticalDependencies.ts.html +6 -6
- package/coverage/src/alphabeticalScripts.ts.html +1 -1
- package/coverage/src/bannedDependencies.ts.html +7 -10
- package/coverage/src/consistentDependencies.ts.html +7 -13
- package/coverage/src/consistentVersions.ts.html +31 -46
- package/coverage/src/fileContents.ts.html +1 -1
- package/coverage/src/forceError.ts.html +184 -0
- package/coverage/src/index.html +73 -43
- package/coverage/src/index.ts.html +10 -4
- package/coverage/src/mustSatisfyPeerDependencies.ts.html +16 -61
- package/coverage/src/nestedWorkspaces.ts.html +10 -22
- package/coverage/src/oncePerPackage.ts.html +181 -0
- package/coverage/src/packageEntry.ts.html +1 -1
- package/coverage/src/packageOrder.ts.html +1 -1
- package/coverage/src/packageScript.ts.html +1 -1
- package/coverage/src/requireDependency.ts.html +7 -13
- package/coverage/src/standardTsconfig.ts.html +6 -6
- package/coverage/src/util/checkAlpha.ts.html +1 -1
- package/coverage/src/util/createRuleFactory.ts.html +1 -1
- package/coverage/src/util/index.html +10 -10
- package/coverage/src/util/makeDirectory.ts.html +1 -1
- package/coverage/src/util/packageDependencyGraphService.ts.html +7 -13
- package/package.json +4 -4
- package/src/__tests__/fileContents.spec.ts +1 -5
- package/src/__tests__/nestedWorkspaces.spec.ts +1 -2
- package/src/__tests__/packageEntry.spec.ts +1 -6
- package/src/__tests__/packageOrder.spec.ts +1 -6
- package/src/__tests__/packageScript.spec.ts +1 -6
- package/src/bannedDependencies.ts +1 -2
- package/src/consistentDependencies.ts +1 -3
- package/src/consistentVersions.ts +24 -29
- package/src/forceError.ts +33 -0
- package/src/index.ts +2 -0
- package/src/mustSatisfyPeerDependencies.ts +10 -25
- package/src/nestedWorkspaces.ts +4 -8
- package/src/oncePerPackage.ts +32 -0
- package/src/requireDependency.ts +1 -3
- package/src/util/packageDependencyGraphService.ts +1 -3
- package/vitest.config.mjs +1 -5
|
@@ -76,8 +76,7 @@ const ensurePackageIsCorrectVersion = (
|
|
|
76
76
|
`Expected dependency on ${dependencyPackageName} to match version defined in monorepolint configuration '${expectedPackageDependencyValue}', got '${actualPackageDependencyValue}' instead.`,
|
|
77
77
|
fixer: () =>
|
|
78
78
|
mutateJson<PackageJson>(packageJsonPath, context.host, (input) => {
|
|
79
|
-
input.dependencies![dependencyPackageName] =
|
|
80
|
-
expectedPackageDependencyValue;
|
|
79
|
+
input.dependencies![dependencyPackageName] = expectedPackageDependencyValue;
|
|
81
80
|
return input;
|
|
82
81
|
}),
|
|
83
82
|
});
|
|
@@ -99,8 +98,7 @@ const ensurePackageIsCorrectVersion = (
|
|
|
99
98
|
`Expected devDependency on ${dependencyPackageName} to match version defined in monorepolint configuration '${expectedPackageDependencyValue}', got '${actualPackageDevDependencyValue}' instead`,
|
|
100
99
|
fixer: () =>
|
|
101
100
|
mutateJson<PackageJson>(packageJsonPath, context.host, (input) => {
|
|
102
|
-
input.devDependencies![dependencyPackageName] =
|
|
103
|
-
expectedPackageDependencyValue;
|
|
101
|
+
input.devDependencies![dependencyPackageName] = expectedPackageDependencyValue;
|
|
104
102
|
return input;
|
|
105
103
|
}),
|
|
106
104
|
});
|
|
@@ -115,20 +113,19 @@ const ensurePackageMatchesSomeVersion = (
|
|
|
115
113
|
const packageJson = context.getPackageJson();
|
|
116
114
|
const packageJsonPath = context.getPackageJsonPath();
|
|
117
115
|
|
|
118
|
-
const acceptedPackageDependencyVersions: SemVer[] =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
116
|
+
const acceptedPackageDependencyVersions: SemVer[] = acceptedPackageDependencyValues.map(
|
|
117
|
+
(acceptedPackageDependencyValue) => {
|
|
118
|
+
const acceptedPackageDependencyVersion = coerce(
|
|
119
|
+
acceptedPackageDependencyValue,
|
|
120
|
+
);
|
|
121
|
+
if (acceptedPackageDependencyVersion == null) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`Malformed accepted package dependency version defined in monorepolint configuration: ${dependencyPackageName} @ '${acceptedPackageDependencyValue}'`,
|
|
123
124
|
);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
return acceptedPackageDependencyVersion;
|
|
130
|
-
},
|
|
131
|
-
);
|
|
125
|
+
}
|
|
126
|
+
return acceptedPackageDependencyVersion;
|
|
127
|
+
},
|
|
128
|
+
);
|
|
132
129
|
|
|
133
130
|
const actualPackageDependencyValue = packageJson.dependencies
|
|
134
131
|
&& packageJson.dependencies[dependencyPackageName];
|
|
@@ -143,12 +140,11 @@ const ensurePackageMatchesSomeVersion = (
|
|
|
143
140
|
) {
|
|
144
141
|
context.addError({
|
|
145
142
|
file: packageJsonPath,
|
|
146
|
-
message:
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}', got '${actualPackageDependencyValue}' instead.`,
|
|
143
|
+
message: `Expected dependency on ${dependencyPackageName} to match one of '${
|
|
144
|
+
JSON.stringify(
|
|
145
|
+
acceptedPackageDependencyValues,
|
|
146
|
+
)
|
|
147
|
+
}', got '${actualPackageDependencyValue}' instead.`,
|
|
152
148
|
});
|
|
153
149
|
}
|
|
154
150
|
|
|
@@ -167,12 +163,11 @@ const ensurePackageMatchesSomeVersion = (
|
|
|
167
163
|
) {
|
|
168
164
|
context.addError({
|
|
169
165
|
file: packageJsonPath,
|
|
170
|
-
message:
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}', got '${actualPackageDevDependencyValue}' instead.`,
|
|
166
|
+
message: `Expected devDependency on ${dependencyPackageName} to match one of '${
|
|
167
|
+
JSON.stringify(
|
|
168
|
+
acceptedPackageDependencyValues,
|
|
169
|
+
)
|
|
170
|
+
}', got '${actualPackageDevDependencyValue}' instead.`,
|
|
176
171
|
});
|
|
177
172
|
}
|
|
178
173
|
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createRuleFactory } from "./util/createRuleFactory.js";
|
|
2
|
+
|
|
3
|
+
export const forceError = createRuleFactory<{ customMessage?: string }>({
|
|
4
|
+
name: "forceError",
|
|
5
|
+
|
|
6
|
+
check: async (context, opts) => {
|
|
7
|
+
context.addError({
|
|
8
|
+
message: opts?.customMessage
|
|
9
|
+
?? "Forced error (often used to debug package selection)",
|
|
10
|
+
file: context.getPackageJsonPath(),
|
|
11
|
+
});
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
validateOptions: (opts): asserts opts is null | { customMessage: string } => {
|
|
15
|
+
if (opts == null) return;
|
|
16
|
+
|
|
17
|
+
if (typeof opts !== "object") {
|
|
18
|
+
throw new Error("options must be an object if provided");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const numKeys = Object.keys(opts).length;
|
|
22
|
+
|
|
23
|
+
if (numKeys === 0) return;
|
|
24
|
+
|
|
25
|
+
if (Object.keys(opts).length > 1 || !("customMessage" in opts)) {
|
|
26
|
+
throw new Error("options must only have `customMessage` property");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (typeof opts.customMessage !== "string") {
|
|
30
|
+
throw new Error("customMessage must be a string");
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -11,8 +11,10 @@ export { bannedDependencies } from "./bannedDependencies.js";
|
|
|
11
11
|
export { consistentDependencies } from "./consistentDependencies.js";
|
|
12
12
|
export { consistentVersions } from "./consistentVersions.js";
|
|
13
13
|
export { fileContents } from "./fileContents.js";
|
|
14
|
+
export { forceError } from "./forceError.js";
|
|
14
15
|
export { mustSatisfyPeerDependencies } from "./mustSatisfyPeerDependencies.js";
|
|
15
16
|
export { nestedWorkspaces } from "./nestedWorkspaces.js";
|
|
17
|
+
export { oncePerPackage } from "./oncePerPackage.js";
|
|
16
18
|
export { packageEntry } from "./packageEntry.js";
|
|
17
19
|
export { packageOrder } from "./packageOrder.js";
|
|
18
20
|
export { packageScript } from "./packageScript.js";
|
|
@@ -303,8 +303,7 @@ function checkSatisfyPeerDependencies(context: Context, opts: Options) {
|
|
|
303
303
|
if (dependencyRange != null) {
|
|
304
304
|
context.addError({
|
|
305
305
|
file: packageJsonPath,
|
|
306
|
-
message:
|
|
307
|
-
`[0] Package ${packageName} has overloaded ${peerDependencyName} dependencies.\n\t`
|
|
306
|
+
message: `[0] Package ${packageName} has overloaded ${peerDependencyName} dependencies.\n\t`
|
|
308
307
|
+ `Peer dependency '${peerDependencyRange}' and regular dependency '${dependencyRange}'.`,
|
|
309
308
|
});
|
|
310
309
|
}
|
|
@@ -446,8 +445,7 @@ function checkSatisfyPeerDependencies(context: Context, opts: Options) {
|
|
|
446
445
|
|
|
447
446
|
// for every inherited peer dependency, this package must declare a dependency or peer dependency
|
|
448
447
|
// equal to or stricter than `mostStrictPeerRequirement`
|
|
449
|
-
const packagePeerDependencyRange =
|
|
450
|
-
packagePeerDependencies[peerDependencyName];
|
|
448
|
+
const packagePeerDependencyRange = packagePeerDependencies[peerDependencyName];
|
|
451
449
|
if (packageDependencyRange == null && packagePeerDependencyRange == null) {
|
|
452
450
|
context.addError({
|
|
453
451
|
file: packageJsonPath,
|
|
@@ -523,22 +521,17 @@ function getMostStrictStatement(
|
|
|
523
521
|
mostStrictPeerRequirement: IResolvedPeerDependencyRequirement,
|
|
524
522
|
) {
|
|
525
523
|
if (mostStrictPeerRequirement.fromPeerDependencyRequirements.length === 1) {
|
|
526
|
-
const dependencyName =
|
|
527
|
-
|
|
528
|
-
.fromPackageName;
|
|
524
|
+
const dependencyName = mostStrictPeerRequirement.fromPeerDependencyRequirements[0]
|
|
525
|
+
.fromPackageName;
|
|
529
526
|
return `Dependency ${dependencyName} requires '${mostStrictPeerRequirement.range}'.`;
|
|
530
527
|
} else {
|
|
531
528
|
const dependencyNames = mostStrictPeerRequirement
|
|
532
529
|
.fromPeerDependencyRequirements
|
|
533
|
-
.map((peerDependencyRequirement) =>
|
|
534
|
-
peerDependencyRequirement.fromPackageName
|
|
535
|
-
)
|
|
530
|
+
.map((peerDependencyRequirement) => peerDependencyRequirement.fromPackageName)
|
|
536
531
|
.join(", ");
|
|
537
532
|
const dependencyRequirements = mostStrictPeerRequirement
|
|
538
533
|
.fromPeerDependencyRequirements
|
|
539
|
-
.map((peerDependencyRequirement) =>
|
|
540
|
-
`'${peerDependencyRequirement.range}'`
|
|
541
|
-
)
|
|
534
|
+
.map((peerDependencyRequirement) => `'${peerDependencyRequirement.range}'`)
|
|
542
535
|
.join(", ");
|
|
543
536
|
return (
|
|
544
537
|
`Dependencies [${dependencyNames}] require [${dependencyRequirements}] respectively, `
|
|
@@ -612,9 +605,7 @@ export function findIntersection(
|
|
|
612
605
|
.map((bVersion) => {
|
|
613
606
|
const bSemVer = coerce(bVersion)!;
|
|
614
607
|
if (bVersion.startsWith("^") && bSemVer.major >= aSemVer.major) {
|
|
615
|
-
return `^${
|
|
616
|
-
bSemVer.compare(aSemVer) >= 0 ? bSemVer.raw : aSemVer.raw
|
|
617
|
-
}`;
|
|
608
|
+
return `^${bSemVer.compare(aSemVer) >= 0 ? bSemVer.raw : aSemVer.raw}`;
|
|
618
609
|
}
|
|
619
610
|
return bSemVer.compare(aSemVer) !== -1 ? bVersion : undefined;
|
|
620
611
|
})
|
|
@@ -633,9 +624,7 @@ export function findIntersection(
|
|
|
633
624
|
.map((aVersion) => {
|
|
634
625
|
const aSemVer = coerce(aVersion)!;
|
|
635
626
|
if (aVersion.startsWith("^") && aSemVer.major >= bSemVer.major) {
|
|
636
|
-
return `^${
|
|
637
|
-
aSemVer.compare(bSemVer) >= 0 ? aSemVer.raw : bSemVer.raw
|
|
638
|
-
}`;
|
|
627
|
+
return `^${aSemVer.compare(bSemVer) >= 0 ? aSemVer.raw : bSemVer.raw}`;
|
|
639
628
|
}
|
|
640
629
|
return aSemVer.compare(bSemVer) !== -1 ? aVersion : undefined;
|
|
641
630
|
})
|
|
@@ -649,9 +638,7 @@ export function findIntersection(
|
|
|
649
638
|
const compatibleVersions = aVersions
|
|
650
639
|
.map((aVersion) => {
|
|
651
640
|
const aSemVer = coerce(aVersion)!;
|
|
652
|
-
const majorMatchingBVersion = bVersions.find((m) =>
|
|
653
|
-
coerce(m)!.major === aSemVer.major
|
|
654
|
-
);
|
|
641
|
+
const majorMatchingBVersion = bVersions.find((m) => coerce(m)!.major === aSemVer.major);
|
|
655
642
|
if (majorMatchingBVersion === undefined) {
|
|
656
643
|
// there is no intersecting `b` major version for this `a` major version
|
|
657
644
|
return undefined;
|
|
@@ -757,9 +744,7 @@ export function doesASatisfyB(a: ValidRange, b: ValidRange): boolean {
|
|
|
757
744
|
|
|
758
745
|
return aVersions.every((aVersion) => {
|
|
759
746
|
const aSemVer = coerce(aVersion)!;
|
|
760
|
-
const majorMatchingBVersion = bVersions.find((m) =>
|
|
761
|
-
coerce(m)!.major === aSemVer.major
|
|
762
|
-
);
|
|
747
|
+
const majorMatchingBVersion = bVersions.find((m) => coerce(m)!.major === aSemVer.major);
|
|
763
748
|
if (majorMatchingBVersion === undefined) {
|
|
764
749
|
// `a` permits a major version that is not permitted by `b`, therefore `a` is "less strict"
|
|
765
750
|
return false;
|
package/src/nestedWorkspaces.ts
CHANGED
|
@@ -42,9 +42,7 @@ export const nestedWorkspaces = createRuleFactory({
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// Build a set of globs for each package.json that exists in packages specified by a workspace.
|
|
45
|
-
const workspacePackageJsons = (workspaces || []).map((item) =>
|
|
46
|
-
`${item}/package.json`
|
|
47
|
-
);
|
|
45
|
+
const workspacePackageJsons = (workspaces || []).map((item) => `${item}/package.json`);
|
|
48
46
|
|
|
49
47
|
// Expand the globs to get an array of all package.json files that are in packages specified by a workspace.
|
|
50
48
|
const expandedWorkspacesGlobs = globby.globbySync([
|
|
@@ -58,13 +56,11 @@ export const nestedWorkspaces = createRuleFactory({
|
|
|
58
56
|
);
|
|
59
57
|
|
|
60
58
|
if (difference.length !== 0) {
|
|
61
|
-
const differencesList = difference.map((packageJsonPath) =>
|
|
62
|
-
|
|
63
|
-
).join(", ");
|
|
59
|
+
const differencesList = difference.map((packageJsonPath) => path.dirname(packageJsonPath))
|
|
60
|
+
.join(", ");
|
|
64
61
|
context.addError({
|
|
65
62
|
file: context.getPackageJsonPath(),
|
|
66
|
-
message:
|
|
67
|
-
`The "workspace" field is missing one or more values: ${differencesList}. `
|
|
63
|
+
message: `The "workspace" field is missing one or more values: ${differencesList}. `
|
|
68
64
|
+ "You may be able to use a glob to avoid listing each workspace individually, e.g. \"packages/nested-workspace/*\".",
|
|
69
65
|
});
|
|
70
66
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as r from "runtypes";
|
|
2
|
+
import { createRuleFactory } from "./util/createRuleFactory.js";
|
|
3
|
+
|
|
4
|
+
export const Options = r.Record({
|
|
5
|
+
singletonKey: r.String.Or(r.Symbol),
|
|
6
|
+
customMessage: r.String.optional(),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export type Options = r.Static<typeof Options>;
|
|
10
|
+
|
|
11
|
+
const visitedMap = new Map<string | symbol, Set<string>>();
|
|
12
|
+
|
|
13
|
+
export const oncePerPackage = createRuleFactory<Options>({
|
|
14
|
+
name: "oncePerPackage",
|
|
15
|
+
|
|
16
|
+
check: async (context, options) => {
|
|
17
|
+
const visited = visitedMap.get(options.singletonKey) ?? new Set<string>();
|
|
18
|
+
visitedMap.set(options.singletonKey, visited);
|
|
19
|
+
|
|
20
|
+
if (visited.has(context.getName())) {
|
|
21
|
+
context.addError({
|
|
22
|
+
message: "This package has already been visited for this key: "
|
|
23
|
+
+ options.singletonKey.toString(),
|
|
24
|
+
file: context.getPackageJsonPath(),
|
|
25
|
+
});
|
|
26
|
+
} else {
|
|
27
|
+
visited.add(context.getName());
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
validateOptions: Options.assert,
|
|
32
|
+
});
|
package/src/requireDependency.ts
CHANGED
|
@@ -44,9 +44,7 @@ export const requireDependency = createRuleFactory({
|
|
|
44
44
|
fixer: () => {
|
|
45
45
|
mutateJson<PackageJson>(packageJsonPath, context.host, (input) => {
|
|
46
46
|
input[type] = Object.fromEntries(
|
|
47
|
-
Object.entries(expectedEntries).filter(([, v]) =>
|
|
48
|
-
v !== undefined
|
|
49
|
-
),
|
|
47
|
+
Object.entries(expectedEntries).filter(([, v]) => v !== undefined),
|
|
50
48
|
) as Record<string, string>;
|
|
51
49
|
return input;
|
|
52
50
|
});
|
|
@@ -38,9 +38,7 @@ export interface IPackageDependencyGraphService {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/** Default implementation of the package dependency graph service. */
|
|
41
|
-
export class PackageDependencyGraphService
|
|
42
|
-
implements IPackageDependencyGraphService
|
|
43
|
-
{
|
|
41
|
+
export class PackageDependencyGraphService implements IPackageDependencyGraphService {
|
|
44
42
|
/** Construct a graph of package dependencies and return the root node. */
|
|
45
43
|
public buildDependencyGraph(
|
|
46
44
|
startPackageJsonPath: string,
|