pkg-stats 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +14 -1
- package/bin.js +5 -0
- package/dist/bin.js +14 -68
- package/dist/colors.js +40 -7
- package/dist/stats.js +72 -0
- package/package.json +2 -2
package/README.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
1
|
## PKG Stats
|
2
2
|
|
3
|
-
Beautiful package download stats.
|
3
|
+
Beautiful NPM package download stats.
|
4
|
+
|
5
|
+
```
|
6
|
+
npx pkg-stats react
|
7
|
+
```
|
8
|
+
|
9
|
+
<div align='center'>
|
10
|
+
<img src="https://raw.githubusercontent.com/mdjastrzebski/pkg-stats/main/docs/public/example-react.png" alt="pkg-stats" height="210" width="538" />
|
11
|
+
</div>
|
12
|
+
|
13
|
+
### Options:
|
14
|
+
|
15
|
+
- version grouping: `--group major|minor|patch` (alias `-g`, `--major`, `--minor`, `--patch`)
|
16
|
+
- top version: `--top <number>` (alias `-t`)
|
package/bin.js
ADDED
package/dist/bin.js
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
import chalk from 'chalk';
|
2
2
|
import minimist from 'minimist';
|
3
3
|
import { renderChart } from './chart.js';
|
4
|
-
import {
|
4
|
+
import { getColors } from './colors.js';
|
5
5
|
import { parseVersion, versionCompare } from './version.js';
|
6
|
+
import { groupByType, pickTopStats } from './stats.js';
|
6
7
|
export async function pkgStats(argv) {
|
7
8
|
const options = parseCliOptions(argv);
|
8
9
|
if (options.help) {
|
@@ -31,27 +32,20 @@ export async function pkgStats(argv) {
|
|
31
32
|
};
|
32
33
|
})
|
33
34
|
.sort(versionCompare);
|
34
|
-
let groupedStats;
|
35
|
-
if (options.group === 'patch') {
|
36
|
-
groupedStats = sumByPatch(rawStats);
|
37
|
-
}
|
38
|
-
else if (options.group === 'minor') {
|
39
|
-
groupedStats = sumByMinor(rawStats);
|
40
|
-
}
|
41
|
-
else {
|
42
|
-
groupedStats = sumByMajor(rawStats);
|
43
|
-
}
|
35
|
+
let groupedStats = groupByType(options.group, rawStats);
|
44
36
|
const totalDownloads = Object.values(groupedStats).reduce((sum, version) => sum + version.downloads, 0);
|
45
37
|
const groupedStatsToDisplay = options.top
|
46
38
|
? pickTopStats(groupedStats, options.top)
|
47
39
|
: groupedStats;
|
48
|
-
|
49
|
-
|
40
|
+
const colors = getColors(groupedStatsToDisplay.length);
|
41
|
+
const primaryColor = chalk.hex(colors[0]);
|
42
|
+
console.log(chalk.bold(`\nNPM weekly downloads for ${primaryColor(options.name)}\n`));
|
43
|
+
console.log(`Total: ${primaryColor(totalDownloads.toLocaleString())} last week\n`);
|
50
44
|
console.log(options.top ? `Top ${options.top} versions:\n` : 'By version:\n');
|
51
|
-
const colors = gradients.passion(groupedStatsToDisplay.length);
|
52
45
|
const maxDownloads = Math.max(...groupedStats.map((v) => v.downloads));
|
53
46
|
groupedStatsToDisplay.forEach((item, i) => {
|
54
|
-
const
|
47
|
+
const versionParts = item.versionString.split('.');
|
48
|
+
const version = versionParts.length < 3 ? `${item.versionString}.x` : item.versionString;
|
55
49
|
const chart = renderChart(item.downloads / maxDownloads);
|
56
50
|
const downloads = formatDownloads(item.downloads, maxDownloads);
|
57
51
|
const color = chalk.hex(colors[i]);
|
@@ -65,8 +59,11 @@ function parseCliOptions(argv) {
|
|
65
59
|
boolean: ['help'],
|
66
60
|
alias: { g: 'group', h: 'help', t: 'top' },
|
67
61
|
});
|
68
|
-
let group
|
69
|
-
if (options.group === '
|
62
|
+
let group;
|
63
|
+
if (options.group === 'major' || options.major) {
|
64
|
+
group = 'major';
|
65
|
+
}
|
66
|
+
else if (options.group === 'minor' || options.minor) {
|
70
67
|
group = 'minor';
|
71
68
|
}
|
72
69
|
else if (options.group === 'patch' || options.patch) {
|
@@ -93,52 +90,6 @@ function printHelp() {
|
|
93
90
|
--top <number> Show top <number> versions
|
94
91
|
`);
|
95
92
|
}
|
96
|
-
function sumByMajor(stats) {
|
97
|
-
const result = {};
|
98
|
-
for (const versionStats of stats) {
|
99
|
-
const key = `${versionStats.major}`;
|
100
|
-
const entry = result[key] ?? {
|
101
|
-
version: { major: versionStats.major },
|
102
|
-
versionString: key,
|
103
|
-
downloads: 0,
|
104
|
-
};
|
105
|
-
result[key] = entry;
|
106
|
-
entry.downloads += versionStats.downloads;
|
107
|
-
}
|
108
|
-
return Object.values(result).sort((a, b) => versionCompare(a.version, b.version));
|
109
|
-
}
|
110
|
-
function sumByMinor(stats) {
|
111
|
-
const result = {};
|
112
|
-
for (const versionStats of stats) {
|
113
|
-
const key = `${versionStats.major}.${versionStats.minor}`;
|
114
|
-
const entry = result[key] ?? {
|
115
|
-
version: { major: versionStats.major, minor: versionStats.minor },
|
116
|
-
versionString: key,
|
117
|
-
downloads: 0,
|
118
|
-
};
|
119
|
-
result[key] = entry;
|
120
|
-
entry.downloads += versionStats.downloads;
|
121
|
-
}
|
122
|
-
return Object.values(result).sort((a, b) => versionCompare(a.version, b.version));
|
123
|
-
}
|
124
|
-
function sumByPatch(stats) {
|
125
|
-
const result = {};
|
126
|
-
for (const versionStats of stats) {
|
127
|
-
const key = `${versionStats.major}.${versionStats.minor}.${versionStats.patch}`;
|
128
|
-
const entry = result[key] ?? {
|
129
|
-
version: {
|
130
|
-
major: versionStats.major,
|
131
|
-
minor: versionStats.minor,
|
132
|
-
patch: versionStats.patch,
|
133
|
-
},
|
134
|
-
versionString: key,
|
135
|
-
downloads: 0,
|
136
|
-
};
|
137
|
-
result[key] = entry;
|
138
|
-
entry.downloads += versionStats.downloads;
|
139
|
-
}
|
140
|
-
return Object.values(result).sort((a, b) => versionCompare(a.version, b.version));
|
141
|
-
}
|
142
93
|
function formatDownloads(downloads, maxDownloads) {
|
143
94
|
if (maxDownloads > 1000000) {
|
144
95
|
return `${(downloads / 1000000).toFixed(1)}M`;
|
@@ -148,8 +99,3 @@ function formatDownloads(downloads, maxDownloads) {
|
|
148
99
|
}
|
149
100
|
return downloads.toString();
|
150
101
|
}
|
151
|
-
function pickTopStats(stats, top) {
|
152
|
-
const sortedStats = stats.sort((a, b) => b.downloads - a.downloads);
|
153
|
-
const topStats = sortedStats.slice(0, top);
|
154
|
-
return topStats.sort((a, b) => versionCompare(a.version, b.version));
|
155
|
-
}
|
package/dist/colors.js
CHANGED
@@ -1,11 +1,44 @@
|
|
1
1
|
import tinygradient from 'tinygradient';
|
2
2
|
// See: https://github.com/bokub/gradient-string/blob/465e86c8499a7f427c45afb1861f1444a2db74b9/src/index.ts#L166
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
const gradients = {
|
4
|
+
atlas: { colors: ['#feac5e', '#c779d0', '#4bc0c8'], options: {} },
|
5
|
+
cristal: { colors: ['#bdfff3', '#4ac29a'], options: {} },
|
6
|
+
teen: { colors: ['#77a1d3', '#79cbca', '#e684ae'], options: {} },
|
7
|
+
mind: { colors: ['#473b7b', '#3584a7', '#30d2be'], options: {} },
|
8
|
+
morning: { colors: ['#ff5f6d', '#ffc371'], options: { interpolation: 'hsv' } },
|
9
|
+
vice: { colors: ['#5ee7df', '#b490ca'], options: { interpolation: 'hsv' } },
|
10
|
+
passion: { colors: ['#f43b47', '#453a94'], options: {} },
|
11
|
+
fruit: { colors: ['#ff4e50', '#f9d423'], options: {} },
|
12
|
+
instagram: { colors: ['#833ab4', '#fd1d1d', '#fcb045'], options: {} },
|
13
|
+
retro: {
|
14
|
+
colors: [
|
15
|
+
'#3f51b1',
|
16
|
+
'#5a55ae',
|
17
|
+
'#7b5fac',
|
18
|
+
'#8f6aae',
|
19
|
+
'#a86aa4',
|
20
|
+
'#cc6b8e',
|
21
|
+
'#f18271',
|
22
|
+
'#f3a469',
|
23
|
+
'#f7c978',
|
24
|
+
],
|
25
|
+
options: {},
|
26
|
+
},
|
27
|
+
summer: { colors: ['#fdbb2d', '#22c1c3'], options: {} },
|
28
|
+
rainbow: { colors: ['#ff0000', '#ff0100'], options: { interpolation: 'hsv', hsvSpin: 'long' } },
|
29
|
+
pastel: { colors: ['#74ebd5', '#74ecd5'], options: { interpolation: 'hsv', hsvSpin: 'long' } },
|
8
30
|
};
|
9
|
-
function
|
10
|
-
|
31
|
+
export function getColors(count, colorScheme) {
|
32
|
+
const { colors, options } = gradients[colorScheme ?? getRandomScheme()];
|
33
|
+
if (count < colors.length) {
|
34
|
+
return colors;
|
35
|
+
}
|
36
|
+
const gradient = tinygradient(colors);
|
37
|
+
const tinyColors = options.interpolation === 'hsv'
|
38
|
+
? gradient.hsv(count, options.hsvSpin ?? false)
|
39
|
+
: gradient.rgb(count);
|
40
|
+
return tinyColors.map((c) => c.toHexString());
|
41
|
+
}
|
42
|
+
function getRandomScheme() {
|
43
|
+
return Object.keys(gradients)[Math.floor(Math.random() * Object.keys(gradients).length)];
|
11
44
|
}
|
package/dist/stats.js
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
import { versionCompare } from './version.js';
|
2
|
+
export function groupByType(type, stats) {
|
3
|
+
if (type === 'major') {
|
4
|
+
return groupByMajor(stats);
|
5
|
+
}
|
6
|
+
if (type === 'minor') {
|
7
|
+
return groupByMinor(stats);
|
8
|
+
}
|
9
|
+
if (type === 'patch') {
|
10
|
+
return groupByPatch(stats);
|
11
|
+
}
|
12
|
+
const groupedByMajor = groupByMajor(stats);
|
13
|
+
if (groupedByMajor.length > 1) {
|
14
|
+
return groupedByMajor;
|
15
|
+
}
|
16
|
+
const groupedByMinor = groupByMinor(stats);
|
17
|
+
if (groupedByMinor.length > 1) {
|
18
|
+
return groupedByMinor;
|
19
|
+
}
|
20
|
+
return groupByPatch(stats);
|
21
|
+
}
|
22
|
+
function groupByMajor(stats) {
|
23
|
+
const result = {};
|
24
|
+
for (const versionStats of stats) {
|
25
|
+
const key = `${versionStats.major}`;
|
26
|
+
const entry = result[key] ?? {
|
27
|
+
version: { major: versionStats.major },
|
28
|
+
versionString: key,
|
29
|
+
downloads: 0,
|
30
|
+
};
|
31
|
+
result[key] = entry;
|
32
|
+
entry.downloads += versionStats.downloads;
|
33
|
+
}
|
34
|
+
return Object.values(result).sort((a, b) => versionCompare(a.version, b.version));
|
35
|
+
}
|
36
|
+
function groupByMinor(stats) {
|
37
|
+
const result = {};
|
38
|
+
for (const versionStats of stats) {
|
39
|
+
const key = `${versionStats.major}.${versionStats.minor}`;
|
40
|
+
const entry = result[key] ?? {
|
41
|
+
version: { major: versionStats.major, minor: versionStats.minor },
|
42
|
+
versionString: key,
|
43
|
+
downloads: 0,
|
44
|
+
};
|
45
|
+
result[key] = entry;
|
46
|
+
entry.downloads += versionStats.downloads;
|
47
|
+
}
|
48
|
+
return Object.values(result).sort((a, b) => versionCompare(a.version, b.version));
|
49
|
+
}
|
50
|
+
function groupByPatch(stats) {
|
51
|
+
const result = {};
|
52
|
+
for (const versionStats of stats) {
|
53
|
+
const key = `${versionStats.major}.${versionStats.minor}.${versionStats.patch}`;
|
54
|
+
const entry = result[key] ?? {
|
55
|
+
version: {
|
56
|
+
major: versionStats.major,
|
57
|
+
minor: versionStats.minor,
|
58
|
+
patch: versionStats.patch,
|
59
|
+
},
|
60
|
+
versionString: key,
|
61
|
+
downloads: 0,
|
62
|
+
};
|
63
|
+
result[key] = entry;
|
64
|
+
entry.downloads += versionStats.downloads;
|
65
|
+
}
|
66
|
+
return Object.values(result).sort((a, b) => versionCompare(a.version, b.version));
|
67
|
+
}
|
68
|
+
export function pickTopStats(stats, top) {
|
69
|
+
const sortedStats = stats.sort((a, b) => b.downloads - a.downloads);
|
70
|
+
const topStats = sortedStats.slice(0, top);
|
71
|
+
return topStats.sort((a, b) => versionCompare(a.version, b.version));
|
72
|
+
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "pkg-stats",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.2.0",
|
4
4
|
"description": "Beautiful NPM package download stats",
|
5
5
|
"author": "Maciej Jastrzębski <mdjastrzebski@gmail.com> (https://github.com/mdjastrzebski)",
|
6
6
|
"license": "MIT",
|
@@ -17,7 +17,7 @@
|
|
17
17
|
"dist"
|
18
18
|
],
|
19
19
|
"bin": {
|
20
|
-
"pkg-stats": "./
|
20
|
+
"pkg-stats": "./bin.js"
|
21
21
|
},
|
22
22
|
"dependencies": {
|
23
23
|
"chalk": "^5.4.1",
|