pkg-stats 0.0.1 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- package/bin.js +5 -0
- package/dist/bin.js +87 -14
- package/dist/chart.js +1 -1
- package/dist/colors.js +11 -0
- package/dist/index.js +1 -2
- package/dist/version.js +2 -2
- package/package.json +23 -9
package/bin.js
ADDED
package/dist/bin.js
CHANGED
@@ -1,10 +1,27 @@
|
|
1
|
-
import
|
2
|
-
import
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
const
|
1
|
+
import chalk from 'chalk';
|
2
|
+
import minimist from 'minimist';
|
3
|
+
import { renderChart } from './chart.js';
|
4
|
+
import { gradients } from './colors.js';
|
5
|
+
import { parseVersion, versionCompare } from './version.js';
|
6
|
+
export async function pkgStats(argv) {
|
7
|
+
const options = parseCliOptions(argv);
|
8
|
+
if (options.help) {
|
9
|
+
printHelp();
|
10
|
+
return;
|
11
|
+
}
|
12
|
+
let data;
|
13
|
+
try {
|
14
|
+
const response = await fetch(`https://api.npmjs.org/versions/${encodeURIComponent(options.name)}/last-week`);
|
15
|
+
data = await response.json();
|
16
|
+
}
|
17
|
+
catch (error) {
|
18
|
+
console.error(`Failed to fetch data for package "${options.name}"`);
|
19
|
+
return;
|
20
|
+
}
|
21
|
+
if (!Object.keys(data.downloads).length) {
|
22
|
+
console.error(`No data found for package "${options.name}".\n`);
|
23
|
+
process.exit(1);
|
24
|
+
}
|
8
25
|
const rawStats = Object.keys(data.downloads)
|
9
26
|
.map((versionString) => {
|
10
27
|
const version = parseVersion(versionString);
|
@@ -14,16 +31,67 @@ export async function bin() {
|
|
14
31
|
};
|
15
32
|
})
|
16
33
|
.sort(versionCompare);
|
17
|
-
|
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
|
+
}
|
18
44
|
const totalDownloads = Object.values(groupedStats).reduce((sum, version) => sum + version.downloads, 0);
|
19
|
-
|
20
|
-
|
21
|
-
|
45
|
+
const groupedStatsToDisplay = options.top
|
46
|
+
? pickTopStats(groupedStats, options.top)
|
47
|
+
: groupedStats;
|
48
|
+
console.log(chalk.bold(`\nNPM weekly downloads for ${chalk.cyan(options.name)}\n`));
|
49
|
+
console.log(`Total: ${chalk.cyan(totalDownloads.toLocaleString())}\n`);
|
50
|
+
console.log(options.top ? `Top ${options.top} versions:\n` : 'By version:\n');
|
51
|
+
const colors = gradients.passion(groupedStatsToDisplay.length);
|
22
52
|
const maxDownloads = Math.max(...groupedStats.map((v) => v.downloads));
|
23
|
-
|
24
|
-
|
53
|
+
groupedStatsToDisplay.forEach((item, i) => {
|
54
|
+
const version = options.group != 'patch' ? `${item.versionString}.x` : item.versionString;
|
55
|
+
const chart = renderChart(item.downloads / maxDownloads);
|
56
|
+
const downloads = formatDownloads(item.downloads, maxDownloads);
|
57
|
+
const color = chalk.hex(colors[i]);
|
58
|
+
console.log(`${version.padStart(8)} ${color(chart)} ${color(downloads.padStart(6))}`);
|
59
|
+
});
|
60
|
+
console.log('');
|
61
|
+
}
|
62
|
+
function parseCliOptions(argv) {
|
63
|
+
const options = minimist(argv, {
|
64
|
+
string: ['group', 'top'],
|
65
|
+
boolean: ['help'],
|
66
|
+
alias: { g: 'group', h: 'help', t: 'top' },
|
67
|
+
});
|
68
|
+
let group = 'major';
|
69
|
+
if (options.group === 'minor' || options.minor) {
|
70
|
+
group = 'minor';
|
71
|
+
}
|
72
|
+
else if (options.group === 'patch' || options.patch) {
|
73
|
+
group = 'patch';
|
25
74
|
}
|
26
|
-
|
75
|
+
const top = options.top ? parseInt(options.top) : undefined;
|
76
|
+
if (!options._[0]) {
|
77
|
+
console.error('Package name is required');
|
78
|
+
process.exit(1);
|
79
|
+
}
|
80
|
+
return { name: options._[0], group, help: options.help, top };
|
81
|
+
}
|
82
|
+
function printHelp() {
|
83
|
+
console.log(`
|
84
|
+
Usage:
|
85
|
+
pkg-stats [options] <package-name>
|
86
|
+
|
87
|
+
Options:
|
88
|
+
-h, --help Show help
|
89
|
+
--group <group> Group by major, minor, or patch (default: major)
|
90
|
+
--major Group by major
|
91
|
+
--minor Group by minor
|
92
|
+
--patch Group by patch
|
93
|
+
--top <number> Show top <number> versions
|
94
|
+
`);
|
27
95
|
}
|
28
96
|
function sumByMajor(stats) {
|
29
97
|
const result = {};
|
@@ -80,3 +148,8 @@ function formatDownloads(downloads, maxDownloads) {
|
|
80
148
|
}
|
81
149
|
return downloads.toString();
|
82
150
|
}
|
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/chart.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
export function renderChart(value, { length = 50 } = {}) {
|
2
2
|
const filledChars = Math.round(value * length);
|
3
3
|
const emptyChars = length - filledChars;
|
4
|
-
return
|
4
|
+
return '█'.repeat(filledChars) + ' '.repeat(emptyChars);
|
5
5
|
}
|
package/dist/colors.js
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
import tinygradient from 'tinygradient';
|
2
|
+
// See: https://github.com/bokub/gradient-string/blob/465e86c8499a7f427c45afb1861f1444a2db74b9/src/index.ts#L166
|
3
|
+
export const gradients = {
|
4
|
+
mind: (count) => toColors(tinygradient(['#473b7b', '#3584a7', '#30d2be']).rgb(count)),
|
5
|
+
pastel: (count) => toColors(tinygradient(['#74ebd5', '#74ecd5']).hsv(count, 'long')),
|
6
|
+
passion: (count) => toColors(tinygradient(['#f43b47', '#453a94']).rgb(count)),
|
7
|
+
retro: (count) => toColors(tinygradient(['#4150AB', '#AE6F97', '#EFCB84']).rgb(count)),
|
8
|
+
};
|
9
|
+
function toColors(colors) {
|
10
|
+
return colors.map((c) => c.toHexString());
|
11
|
+
}
|
package/dist/index.js
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
|
2
|
-
bin();
|
1
|
+
export { pkgStats } from './bin.js';
|
package/dist/version.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
export function parseVersion(version) {
|
2
|
-
const [versionCore, preRelease] = version.split(
|
3
|
-
const [major, minor, patch] = versionCore.split(
|
2
|
+
const [versionCore, preRelease] = version.split('-');
|
3
|
+
const [major, minor, patch] = versionCore.split('.');
|
4
4
|
return {
|
5
5
|
major: Number(major),
|
6
6
|
minor: Number(minor),
|
package/package.json
CHANGED
@@ -1,21 +1,35 @@
|
|
1
1
|
{
|
2
2
|
"name": "pkg-stats",
|
3
|
-
"version": "0.
|
4
|
-
"description": "Beautiful package download stats",
|
3
|
+
"version": "0.1.1",
|
4
|
+
"description": "Beautiful NPM package download stats",
|
5
|
+
"author": "Maciej Jastrzębski <mdjastrzebski@gmail.com> (https://github.com/mdjastrzebski)",
|
6
|
+
"license": "MIT",
|
7
|
+
"keywords": [
|
8
|
+
"npm",
|
9
|
+
"package",
|
10
|
+
"stats",
|
11
|
+
"downloads",
|
12
|
+
"weekly"
|
13
|
+
],
|
5
14
|
"type": "module",
|
6
15
|
"main": "dist/index.js",
|
7
16
|
"files": [
|
8
17
|
"dist"
|
9
18
|
],
|
10
|
-
"
|
11
|
-
"
|
19
|
+
"bin": {
|
20
|
+
"pkg-stats": "./bin.js"
|
21
|
+
},
|
22
|
+
"dependencies": {
|
23
|
+
"chalk": "^5.4.1",
|
24
|
+
"minimist": "^1.2.8",
|
25
|
+
"tinygradient": "^1.1.5"
|
12
26
|
},
|
13
|
-
"keywords": [],
|
14
|
-
"author": "",
|
15
|
-
"license": "MIT",
|
16
|
-
"packageManager": "pnpm@9.15.0+sha512.76e2379760a4328ec4415815bcd6628dee727af3779aaa4c914e3944156c4299921a89f976381ee107d41f12cfa4b66681ca9c718f0668fa0831ed4c6d8ba56c",
|
17
27
|
"devDependencies": {
|
28
|
+
"@types/minimist": "^1.2.5",
|
18
29
|
"@types/node": "^22.10.5",
|
19
30
|
"typescript": "^5.7.3"
|
31
|
+
},
|
32
|
+
"scripts": {
|
33
|
+
"build": "tsc"
|
20
34
|
}
|
21
|
-
}
|
35
|
+
}
|