Skip to content

Commit d23eab9

Browse files
authored
feat: show list of packages containing each dependency version (#198)
1 parent ee4329c commit d23eab9

File tree

5 files changed

+116
-78
lines changed

5 files changed

+116
-78
lines changed

README.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,20 @@ If there are any dependency mismatches, the program will exit with failure and o
4343

4444
```pt
4545
Found 2 dependencies with mismatching versions across the workspace.
46-
╔════════╤════════╗
47-
║ eslint │ Usages ║
48-
╟────────┼────────╢
49-
║ ^7.0.0 │ 5 ║
50-
╟────────┼────────╢
51-
║ ^8.0.0 │ 1 ║
52-
╚════════╧════════╝
53-
╔═════════╤════════╗
54-
║ globby │ Usages ║
55-
╟─────────┼────────╢
56-
║ ^7.1.1 │ 1 ║
57-
╟─────────┼────────╢
58-
║ ^11.0.0 │ 2 ║
59-
╚═════════╧════════╝
46+
╔════════╤════════╤═════════════════════════════
47+
║ eslint │ Usages │ Packages
48+
╟────────┼────────┼─────────────────────────────
49+
║ ^7.0.0 │ 5 │ foo, bar, baz, and 2 others
50+
╟────────┼────────┼─────────────────────────────
51+
║ ^8.0.0 │ 1 │ @some-scope/package-name
52+
╚════════╧══════════════════════════════════════
53+
╔═════════╤════════╤════════════════════════════════════════
54+
║ globby │ Usages │ Packages
55+
╟─────────┼────────┼────────────────────────────────────────
56+
║ ^7.1.1 │ 1 │ packages/my-favorite-package
57+
╟─────────┼────────┼────────────────────────────────────────
58+
║ ^11.0.0 │ 2 │ packages/hello-world, packages/foo-bar
59+
╚═════════╧════════╧════════════════════════════════════════
6060
```
6161

6262
## Options

lib/dependency-versions.ts

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@ import type { PackageJson } from 'type-fest';
33
import { getPackageJsonPaths } from './workspace.js';
44
import semver from 'semver';
55
import editJsonFile from 'edit-json-file';
6+
import { dirname, relative } from 'node:path';
67

78
export type DependenciesToVersionsSeen = Map<
89
string,
9-
{ title: string; version: string }[]
10+
{ packageName: string; version: string }[]
1011
>;
1112

1213
export type MismatchingDependencyVersions = Array<{
1314
dependency: string;
1415
versions: {
1516
version: string;
16-
count: number;
17+
packages: string[];
1718
}[];
1819
}>;
1920

@@ -24,12 +25,12 @@ export type MismatchingDependencyVersions = Array<{
2425
*
2526
* {
2627
* 'ember-cli': [
27-
* { title: '@scope/package1/package.json', version: '~3.18.0' },
28-
* { title: '@scope/package2/package.json', version: '~3.18.0' }
28+
* { packageName: '@scope/package1', version: '~3.18.0' },
29+
* { packageName: '@scope/package2', version: '~3.18.0' }
2930
* ]
3031
* 'eslint': [
31-
* { title: '@scope/package1/package.json', version: '^7.0.0' },
32-
* { title: '@scope/package2/package.json', version: '^7.0.0' }
32+
* { packageName: '@scope/package1', version: '^7.0.0' },
33+
* { packageName: '@scope/package2', version: '^7.0.0' }
3334
* ]
3435
* }
3536
*/
@@ -38,7 +39,7 @@ export function calculateVersionsForEachDependency(
3839
): DependenciesToVersionsSeen {
3940
const dependenciesToVersionsSeen: DependenciesToVersionsSeen = new Map<
4041
string,
41-
{ title: string; version: string }[]
42+
{ packageName: string; version: string }[]
4243
>();
4344
getPackageJsonPaths(root).forEach((packageJsonPath) =>
4445
recordDependencyVersionsForPackageJson(
@@ -60,7 +61,7 @@ function recordDependencyVersionsForPackageJson(
6061
return;
6162
}
6263

63-
const title = packageJsonPath.replace(`${root}/`, '');
64+
const packageName = dirname(relative(root, packageJsonPath));
6465
const packageJson: PackageJson = JSON.parse(
6566
readFileSync(packageJsonPath, 'utf-8')
6667
);
@@ -72,7 +73,7 @@ function recordDependencyVersionsForPackageJson(
7273
recordDependencyVersion(
7374
dependenciesToVersionsSeen,
7475
dependency,
75-
title,
76+
packageName,
7677
dependencyVersion
7778
);
7879
}
@@ -85,7 +86,7 @@ function recordDependencyVersionsForPackageJson(
8586
recordDependencyVersion(
8687
dependenciesToVersionsSeen,
8788
dependency,
88-
title,
89+
packageName,
8990
dependencyVersion
9091
);
9192
}
@@ -95,7 +96,7 @@ function recordDependencyVersionsForPackageJson(
9596
function recordDependencyVersion(
9697
dependenciesToVersionsSeen: DependenciesToVersionsSeen,
9798
dependency: string,
98-
title: string,
99+
packageName: string,
99100
version: string
100101
) {
101102
if (!dependenciesToVersionsSeen.has(dependency)) {
@@ -105,7 +106,7 @@ function recordDependencyVersion(
105106
/* istanbul ignore if */
106107
if (list) {
107108
// `list` should always exist at this point, this if statement is just to please TypeScript.
108-
list.push({ title, version });
109+
list.push({ packageName, version });
109110
}
110111
}
111112

@@ -125,13 +126,18 @@ export function calculateMismatchingVersions(
125126
const uniqueVersions = [
126127
...new Set(versionList.map((obj) => obj.version)),
127128
].sort();
128-
const uniqueVersionsWithCounts = uniqueVersions.map((uniqueVersion) => ({
129-
version: uniqueVersion,
130-
count: versionList.filter((obj) => obj.version === uniqueVersion)
131-
.length,
132-
}));
129+
133130
if (uniqueVersions.length > 1) {
134-
return { dependency, versions: uniqueVersionsWithCounts };
131+
const uniqueVersionsWithInfo = uniqueVersions.map((uniqueVersion) => {
132+
const matchingVersions = versionList.filter(
133+
(obj) => obj.version === uniqueVersion
134+
);
135+
return {
136+
version: uniqueVersion,
137+
packages: matchingVersions.map((obj) => obj.packageName).sort(),
138+
};
139+
});
140+
return { dependency, versions: uniqueVersionsWithInfo };
135141
}
136142

137143
return undefined;
@@ -170,9 +176,7 @@ export function fixMismatchingVersions(
170176
// Return any mismatching versions that are still present after attempting fixes.
171177
return mismatchingVersions
172178
.map((mismatchingVersion) => {
173-
const versions = mismatchingVersion.versions.map(
174-
(versionAndCount) => versionAndCount.version
175-
);
179+
const versions = mismatchingVersion.versions.map((obj) => obj.version);
176180
let sortedVersions;
177181
try {
178182
sortedVersions = versions.sort(compareRanges);

lib/output.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,22 @@ export function mismatchingVersionsToOutput(
1212

1313
const tables = mismatchingDependencyVersions
1414
.map((obj) => {
15-
const headers = [chalk.bold(obj.dependency), 'Usages'];
15+
const headers = [chalk.bold(obj.dependency), 'Usages', 'Packages'];
1616
const rows = obj.versions
1717
.sort((a, b) => compareRanges(a.version, b.version))
18-
.map((versionObj) => [
19-
chalk.redBright(versionObj.version),
20-
versionObj.count,
21-
]);
18+
.map((versionObj) => {
19+
const packages =
20+
versionObj.packages.length > 3
21+
? `${versionObj.packages.slice(0, 3).join(', ')}, and ${
22+
versionObj.packages.length - 3
23+
} other${versionObj.packages.length - 3 === 1 ? '' : 's'}`
24+
: versionObj.packages.join(', ');
25+
return [
26+
chalk.redBright(versionObj.version),
27+
versionObj.packages.length,
28+
packages,
29+
];
30+
});
2231
return table([headers, ...rows]);
2332
})
2433
.join('');

test/lib/dependency-versions.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
import mockFs from 'mock-fs';
1818
import { readFileSync } from 'node:fs';
1919
import type { PackageJson } from 'type-fest';
20+
import { join } from 'node:path';
2021

2122
describe('Utils | dependency-versions', function () {
2223
describe('#calculateMismatchingVersions', function () {
@@ -35,25 +36,28 @@ describe('Utils | dependency-versions', function () {
3536
dependency: 'baz',
3637
versions: [
3738
{
38-
count: 1,
3939
version: '^7.8.9',
40+
packages: [join('scope1', 'package1')],
4041
},
4142
{
42-
count: 1,
4343
version: '^8.0.0',
44+
packages: [join('scope1', 'package2')],
4445
},
4546
],
4647
},
4748
{
4849
dependency: 'foo',
4950
versions: [
5051
{
51-
count: 2,
5252
version: '1.2.0',
53+
packages: [
54+
join('scope1', 'package2'),
55+
join('scope1', 'package3'),
56+
],
5357
},
5458
{
55-
count: 1,
5659
version: '1.3.0',
60+
packages: [join('scope1', 'package1')],
5761
},
5862
],
5963
},
@@ -94,12 +98,12 @@ describe('Utils | dependency-versions', function () {
9498
dependency: 'baz',
9599
versions: [
96100
{
97-
count: 1,
98101
version: '^7.8.9',
102+
packages: [join('scope1', 'package1')],
99103
},
100104
{
101-
count: 1,
102105
version: '^8.0.0',
106+
packages: [join('scope1', 'package2')],
103107
},
104108
],
105109
},
@@ -185,12 +189,12 @@ describe('Utils | dependency-versions', function () {
185189
dependency: 'bar',
186190
versions: [
187191
{
188-
count: 1,
189192
version: '^3.0.0',
193+
packages: [join('scope1', 'package1')],
190194
},
191195
{
192-
count: 1,
193196
version: 'invalidVersion',
197+
packages: [join('scope1', 'package2')],
194198
},
195199
],
196200
},

test/lib/output.ts

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,50 +10,71 @@ describe('Utils | output', function () {
1010
{
1111
dependency: 'foo',
1212
versions: [
13-
{ version: '1.2.3', count: 1 },
14-
{ version: '4.5.6', count: 2 },
13+
{ version: '1.2.3', packages: ['foo'] },
14+
{ version: '4.5.6', packages: ['bar', 'baz'] },
1515
],
1616
},
1717
{
1818
dependency: 'bar',
1919
versions: [
20-
{ version: '2.0.0', count: 4 },
21-
{ version: '1.4.0', count: 3 },
20+
{
21+
version: '2.0.0',
22+
packages: ['package1', 'package2', 'package3', 'package4'],
23+
},
24+
{
25+
version: '1.4.0',
26+
packages: [
27+
'package5',
28+
'package6',
29+
'package7',
30+
'package8',
31+
'package9',
32+
],
33+
},
2234
],
2335
},
2436
{
2537
dependency: 'baz',
2638
versions: [
27-
{ version: '^2.0.0', count: 1 },
28-
{ version: '~2.0.0', count: 1 },
29-
{ version: '^1.0.0', count: 1 },
39+
{
40+
version: '^2.0.0',
41+
packages: ['package1'],
42+
},
43+
{
44+
version: '~2.0.0',
45+
packages: ['package2'],
46+
},
47+
{
48+
version: '^1.0.0',
49+
packages: ['package3'],
50+
},
3051
],
3152
},
3253
]),
3354
`Found 3 dependencies with mismatching versions across the workspace.
34-
╔═══════╤════════╗
35-
║ \u001B[1mfoo\u001B[22m │ Usages ║
36-
╟───────┼────────╢
37-
║ \u001B[91m1.2.3\u001B[39m │ 1 ║
38-
╟───────┼────────╢
39-
║ \u001B[91m4.5.6\u001B[39m │ 2 ║
40-
╚═══════╧════════╝
41-
╔═══════╤════════╗
42-
║ \u001B[1mbar\u001B[22m │ Usages ║
43-
╟───────┼────────╢
44-
║ \u001B[91m1.4.0\u001B[39m │ 3
45-
╟───────┼────────╢
46-
║ \u001B[91m2.0.0\u001B[39m │ 4 ║
47-
╚═══════╧════════╝
48-
╔════════╤════════╗
49-
║ \u001B[1mbaz\u001B[22m │ Usages ║
50-
╟────────┼────────╢
51-
║ \u001B[91m^1.0.0\u001B[39m │ 1 ║
52-
╟────────┼────────╢
53-
║ \u001B[91m~2.0.0\u001B[39m │ 1 ║
54-
╟────────┼────────╢
55-
║ \u001B[91m^2.0.0\u001B[39m │ 1 ║
56-
╚════════╧════════╝
55+
╔═══════╤════════╤══════════
56+
║ \u001B[1mfoo\u001B[22m │ Usages │ Packages
57+
╟───────┼────────┼──────────
58+
║ \u001B[91m1.2.3\u001B[39m │ 1 │ foo
59+
╟───────┼────────┼──────────
60+
║ \u001B[91m4.5.6\u001B[39m │ 2 │ bar, baz
61+
╚═══════╧════════╧══════════
62+
╔═══════╤════════╤════════════════════════════════════════════
63+
║ \u001B[1mbar\u001B[22m │ Usages │ Packages
64+
╟───────┼────────┼────────────────────────────────────────────
65+
║ \u001B[91m1.4.0\u001B[39m │ 5 │ package5, package6, package7, and 2 others
66+
╟───────┼────────┼────────────────────────────────────────────
67+
║ \u001B[91m2.0.0\u001B[39m │ 4 │ package1, package2, package3, and 1 other
68+
╚═══════╧════════╧════════════════════════════════════════════
69+
╔════════╤════════╤══════════
70+
║ \u001B[1mbaz\u001B[22m │ Usages │ Packages
71+
╟────────┼────────┼──────────
72+
║ \u001B[91m^1.0.0\u001B[39m │ 1 │ package3
73+
╟────────┼────────┼──────────
74+
║ \u001B[91m~2.0.0\u001B[39m │ 1 │ package2
75+
╟────────┼────────┼──────────
76+
║ \u001B[91m^2.0.0\u001B[39m │ 1 │ package1
77+
╚════════╧════════╧══════════
5778
`
5879
);
5980
});

0 commit comments

Comments
 (0)