pkg-stats 0.6.0 → 0.7.0
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/dist/__tests__/cli-options.test.js +18 -0
- package/dist/__tests__/stats.test.js +50 -0
- package/dist/cli-options.js +13 -0
- package/dist/mode/package-stats.js +5 -2
- package/dist/stats.js +5 -1
- package/package.json +17 -14
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { expect, test } from 'vitest';
|
|
2
|
+
import { parseCliOptions } from '../cli-options.js';
|
|
3
|
+
test('parseCliOptions defaults to version sorting', () => {
|
|
4
|
+
const options = parseCliOptions(['node', 'pkg-stats', 'react', '--color', 'atlas']);
|
|
5
|
+
expect(options.sort).toBe('version');
|
|
6
|
+
});
|
|
7
|
+
test('parseCliOptions supports download sorting', () => {
|
|
8
|
+
const options = parseCliOptions([
|
|
9
|
+
'node',
|
|
10
|
+
'pkg-stats',
|
|
11
|
+
'react',
|
|
12
|
+
'--sort',
|
|
13
|
+
'downloads',
|
|
14
|
+
'--color',
|
|
15
|
+
'atlas',
|
|
16
|
+
]);
|
|
17
|
+
expect(options.sort).toBe('downloads');
|
|
18
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { expect, test } from 'vitest';
|
|
2
|
+
import { downloadCompare, filterStats } from '../stats.js';
|
|
3
|
+
const stats = [
|
|
4
|
+
{
|
|
5
|
+
version: { major: 3 },
|
|
6
|
+
versionString: '3.x',
|
|
7
|
+
downloads: 100,
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
version: { major: 2 },
|
|
11
|
+
versionString: '2.x',
|
|
12
|
+
downloads: 300,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
version: { major: 1 },
|
|
16
|
+
versionString: '1.x',
|
|
17
|
+
downloads: 200,
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
test('filterStats keeps top downloads in version order by default', () => {
|
|
21
|
+
expect(filterStats(stats, {
|
|
22
|
+
totalDownloads: 600,
|
|
23
|
+
top: 2,
|
|
24
|
+
}).map((stat) => stat.versionString)).toEqual(['2.x', '1.x']);
|
|
25
|
+
});
|
|
26
|
+
test('downloadCompare sorts by downloads descending', () => {
|
|
27
|
+
expect([...stats].sort(downloadCompare).map((stat) => stat.versionString)).toEqual([
|
|
28
|
+
'2.x',
|
|
29
|
+
'1.x',
|
|
30
|
+
'3.x',
|
|
31
|
+
]);
|
|
32
|
+
});
|
|
33
|
+
test('downloadCompare falls back to version order for ties', () => {
|
|
34
|
+
const tiedStats = [
|
|
35
|
+
{
|
|
36
|
+
version: { major: 1 },
|
|
37
|
+
versionString: '1.x',
|
|
38
|
+
downloads: 100,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
version: { major: 2 },
|
|
42
|
+
versionString: '2.x',
|
|
43
|
+
downloads: 100,
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
expect([...tiedStats].sort(downloadCompare).map((stat) => stat.versionString)).toEqual([
|
|
47
|
+
'2.x',
|
|
48
|
+
'1.x',
|
|
49
|
+
]);
|
|
50
|
+
});
|
package/dist/cli-options.js
CHANGED
|
@@ -12,6 +12,7 @@ Options:
|
|
|
12
12
|
${colorOption('--major')} Group by major version
|
|
13
13
|
${colorOption('--minor')} Group by minor version
|
|
14
14
|
${colorOption('--patch')} Group by patch version
|
|
15
|
+
${colorOption('--sort')} <order> Sort results by version or downloads
|
|
15
16
|
${colorOption('-t, --top')} <number> Show top <number> most downloaded versions
|
|
16
17
|
${colorOption('-a, --all')} Include all versions in output, even those with minimal downloads
|
|
17
18
|
${colorOption('-c, --color')} <scheme> ${wrapOption(`Choose color scheme from: ${COLOR_SCHEMES.sort().join(', ')}`, 50, 24)}
|
|
@@ -48,6 +49,7 @@ function wrapOption(text, maxLength, indent) {
|
|
|
48
49
|
export function showHelp() {
|
|
49
50
|
console.log(redent(HELP, 2));
|
|
50
51
|
}
|
|
52
|
+
const SORT_ORDERS = ['version', 'downloads'];
|
|
51
53
|
export function parseCliOptions(argv) {
|
|
52
54
|
const cli = meow(HELP, {
|
|
53
55
|
argv: argv.slice(2),
|
|
@@ -69,6 +71,10 @@ export function parseCliOptions(argv) {
|
|
|
69
71
|
patch: {
|
|
70
72
|
type: 'boolean',
|
|
71
73
|
},
|
|
74
|
+
sort: {
|
|
75
|
+
type: 'string',
|
|
76
|
+
choices: [...SORT_ORDERS],
|
|
77
|
+
},
|
|
72
78
|
top: {
|
|
73
79
|
shortFlag: 't',
|
|
74
80
|
type: 'number',
|
|
@@ -100,10 +106,17 @@ export function parseCliOptions(argv) {
|
|
|
100
106
|
? 'patch'
|
|
101
107
|
: undefined,
|
|
102
108
|
top: cli.flags.top,
|
|
109
|
+
sort: coalesceSortOrder(cli.flags.sort) ?? 'version',
|
|
103
110
|
all: cli.flags.all ?? false,
|
|
104
111
|
color: coalesceColor(cli.flags.color ?? process.env.PKG_STATS_COLOR_SCHEME) ?? getColorOfDay(),
|
|
105
112
|
};
|
|
106
113
|
}
|
|
114
|
+
function coalesceSortOrder(sort) {
|
|
115
|
+
if (sort && SORT_ORDERS.includes(sort)) {
|
|
116
|
+
return sort;
|
|
117
|
+
}
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
107
120
|
function coalesceColor(color) {
|
|
108
121
|
if (color && COLOR_SCHEMES.includes(color)) {
|
|
109
122
|
return color;
|
|
@@ -3,7 +3,7 @@ import { getPrimaryColor } from '../colors.js';
|
|
|
3
3
|
import { formatPercentage } from '../format.js';
|
|
4
4
|
import { fetchNpmLastWeekDownloads } from '../npm-api.js';
|
|
5
5
|
import { printChart } from '../output.js';
|
|
6
|
-
import { filterStats, groupStats } from '../stats.js';
|
|
6
|
+
import { downloadCompare, filterStats, groupStats } from '../stats.js';
|
|
7
7
|
import { parseVersion, versionCompare } from '../version.js';
|
|
8
8
|
export async function printPackageStats(packageName, options) {
|
|
9
9
|
let data;
|
|
@@ -29,11 +29,14 @@ export async function printPackageStats(packageName, options) {
|
|
|
29
29
|
.sort(versionCompare);
|
|
30
30
|
const { type, stats } = groupStats(npmStats, options.group);
|
|
31
31
|
const totalDownloads = Object.values(stats).reduce((sum, version) => sum + version.downloads, 0);
|
|
32
|
-
const
|
|
32
|
+
const filteredStats = filterStats(stats, {
|
|
33
33
|
totalDownloads,
|
|
34
34
|
all: options.all,
|
|
35
35
|
top: options.top,
|
|
36
36
|
});
|
|
37
|
+
const statsToDisplay = options.sort === 'downloads'
|
|
38
|
+
? [...filteredStats].sort(downloadCompare)
|
|
39
|
+
: filteredStats;
|
|
37
40
|
const downloadToDisplay = statsToDisplay.reduce((sum, version) => sum + version.downloads, 0);
|
|
38
41
|
if (totalDownloads - downloadToDisplay > 0) {
|
|
39
42
|
statsToDisplay.push({
|
package/dist/stats.js
CHANGED
|
@@ -81,7 +81,11 @@ export function filterStats(stats, options) {
|
|
|
81
81
|
return filtered;
|
|
82
82
|
}
|
|
83
83
|
function pickTopStats(stats, top) {
|
|
84
|
-
const sortedStats = stats.sort((a, b) => b.downloads - a.downloads);
|
|
84
|
+
const sortedStats = [...stats].sort((a, b) => b.downloads - a.downloads);
|
|
85
85
|
const topStats = sortedStats.slice(0, top);
|
|
86
86
|
return topStats.sort((a, b) => versionCompare(a.version, b.version));
|
|
87
87
|
}
|
|
88
|
+
export function downloadCompare(a, b) {
|
|
89
|
+
const downloadDifference = b.downloads - a.downloads;
|
|
90
|
+
return downloadDifference === 0 ? versionCompare(a.version, b.version) : downloadDifference;
|
|
91
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pkg-stats",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Beautiful NPM package download stats",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -24,6 +24,9 @@
|
|
|
24
24
|
"bin": {
|
|
25
25
|
"pkg-stats": "./bin.js"
|
|
26
26
|
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=22.13.0"
|
|
29
|
+
},
|
|
27
30
|
"scripts": {
|
|
28
31
|
"build": "tsc",
|
|
29
32
|
"test": "vitest",
|
|
@@ -33,22 +36,22 @@
|
|
|
33
36
|
"validate": "pnpm lint && pnpm typecheck && pnpm test -- --no-watch"
|
|
34
37
|
},
|
|
35
38
|
"dependencies": {
|
|
36
|
-
"chalk": "^5.
|
|
37
|
-
"meow": "^
|
|
39
|
+
"chalk": "^5.6.2",
|
|
40
|
+
"meow": "^14.1.0",
|
|
38
41
|
"redent": "^4.0.0",
|
|
39
|
-
"tinygradient": "^
|
|
42
|
+
"tinygradient": "^2.0.1"
|
|
40
43
|
},
|
|
41
44
|
"devDependencies": {
|
|
42
|
-
"@eslint/js": "^9.
|
|
43
|
-
"@release-it/conventional-changelog": "^
|
|
44
|
-
"@types/node": "^22.
|
|
45
|
-
"eslint": "^9.
|
|
45
|
+
"@eslint/js": "^9.39.4",
|
|
46
|
+
"@release-it/conventional-changelog": "^11.0.1",
|
|
47
|
+
"@types/node": "^22.19.20",
|
|
48
|
+
"eslint": "^9.39.4",
|
|
46
49
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
47
|
-
"globals": "^15.
|
|
48
|
-
"release-it": "^
|
|
49
|
-
"typescript": "^5.
|
|
50
|
-
"typescript-eslint": "^8.
|
|
51
|
-
"vitest": "^2.1.
|
|
50
|
+
"globals": "^15.15.0",
|
|
51
|
+
"release-it": "^20.2.0",
|
|
52
|
+
"typescript": "^5.9.3",
|
|
53
|
+
"typescript-eslint": "^8.61.0",
|
|
54
|
+
"vitest": "^2.1.9"
|
|
52
55
|
},
|
|
53
|
-
"packageManager": "pnpm@
|
|
56
|
+
"packageManager": "pnpm@11.5.3+sha512.7ac1c919341c213a34dc0d02afb7143c5c26ac26ee8c4782deea821b8ac64d2134a081fd8941dae6e29bbb48f58dfc2b7fbceeccc07cb2f09d219d342a4969ed"
|
|
54
57
|
}
|