pkg-stats 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- package/bin.js +1 -1
- package/dist/__tests__/chart.test.js +7 -0
- package/dist/bin.js +9 -50
- package/dist/cli-options.js +24 -0
- package/dist/colors.js +19 -9
- package/dist/npm-api.js +14 -0
- package/package.json +28 -8
package/bin.js
CHANGED
@@ -0,0 +1,7 @@
|
|
1
|
+
import { expect, test } from 'vitest';
|
2
|
+
import { renderChart } from '../chart.js';
|
3
|
+
test('renderChart basic tests', () => {
|
4
|
+
expect(renderChart(0.0, { length: 10 })).toMatchInlineSnapshot(`" "`);
|
5
|
+
expect(renderChart(0.5, { length: 10 })).toMatchInlineSnapshot(`"█████ "`);
|
6
|
+
expect(renderChart(1.0, { length: 10 })).toMatchInlineSnapshot(`"██████████"`);
|
7
|
+
});
|
package/dist/bin.js
CHANGED
@@ -1,26 +1,22 @@
|
|
1
1
|
import chalk from 'chalk';
|
2
|
-
import minimist from 'minimist';
|
3
2
|
import { renderChart } from './chart.js';
|
3
|
+
import { parseCliOptions } from './cli-options.js';
|
4
4
|
import { getColors } from './colors.js';
|
5
|
-
import {
|
5
|
+
import { fetchNpmLastWeekDownloads } from './npm-api.js';
|
6
6
|
import { groupByType, pickTopStats } from './stats.js';
|
7
|
+
import { parseVersion, versionCompare } from './version.js';
|
7
8
|
export async function pkgStats(argv) {
|
8
9
|
const options = parseCliOptions(argv);
|
9
|
-
if (options.help) {
|
10
|
-
printHelp();
|
11
|
-
return;
|
12
|
-
}
|
13
10
|
let data;
|
14
11
|
try {
|
15
|
-
|
16
|
-
data = await response.json();
|
12
|
+
data = await fetchNpmLastWeekDownloads(options.packageName);
|
17
13
|
}
|
18
14
|
catch (error) {
|
19
|
-
console.error(`Failed to fetch data for package "${options.
|
15
|
+
console.error(`Failed to fetch data for package "${options.packageName}"`, error);
|
20
16
|
return;
|
21
17
|
}
|
22
18
|
if (!Object.keys(data.downloads).length) {
|
23
|
-
console.error(`No data found for package "${options.
|
19
|
+
console.error(`No data found for package "${options.packageName}".\n`);
|
24
20
|
process.exit(1);
|
25
21
|
}
|
26
22
|
const rawStats = Object.keys(data.downloads)
|
@@ -32,14 +28,14 @@ export async function pkgStats(argv) {
|
|
32
28
|
};
|
33
29
|
})
|
34
30
|
.sort(versionCompare);
|
35
|
-
|
31
|
+
const groupedStats = groupByType(options.group, rawStats);
|
36
32
|
const totalDownloads = Object.values(groupedStats).reduce((sum, version) => sum + version.downloads, 0);
|
37
33
|
const groupedStatsToDisplay = options.top
|
38
34
|
? pickTopStats(groupedStats, options.top)
|
39
35
|
: groupedStats;
|
40
|
-
const colors = getColors(groupedStatsToDisplay.length);
|
36
|
+
const colors = getColors(groupedStatsToDisplay.length, options.color);
|
41
37
|
const primaryColor = chalk.hex(colors[0]);
|
42
|
-
console.log(chalk.bold(`\nNPM weekly downloads for ${primaryColor(options.
|
38
|
+
console.log(chalk.bold(`\nNPM weekly downloads for ${primaryColor(options.packageName)}\n`));
|
43
39
|
console.log(`Total: ${primaryColor(totalDownloads.toLocaleString())} last week\n`);
|
44
40
|
console.log(options.top ? `Top ${options.top} versions:\n` : 'By version:\n');
|
45
41
|
const maxDownloads = Math.max(...groupedStats.map((v) => v.downloads));
|
@@ -53,43 +49,6 @@ export async function pkgStats(argv) {
|
|
53
49
|
});
|
54
50
|
console.log('');
|
55
51
|
}
|
56
|
-
function parseCliOptions(argv) {
|
57
|
-
const options = minimist(argv, {
|
58
|
-
string: ['group', 'top'],
|
59
|
-
boolean: ['help'],
|
60
|
-
alias: { g: 'group', h: 'help', t: 'top' },
|
61
|
-
});
|
62
|
-
let group;
|
63
|
-
if (options.group === 'major' || options.major) {
|
64
|
-
group = 'major';
|
65
|
-
}
|
66
|
-
else if (options.group === 'minor' || options.minor) {
|
67
|
-
group = 'minor';
|
68
|
-
}
|
69
|
-
else if (options.group === 'patch' || options.patch) {
|
70
|
-
group = 'patch';
|
71
|
-
}
|
72
|
-
const top = options.top ? parseInt(options.top) : undefined;
|
73
|
-
if (!options._[0]) {
|
74
|
-
console.error('Package name is required');
|
75
|
-
process.exit(1);
|
76
|
-
}
|
77
|
-
return { name: options._[0], group, help: options.help, top };
|
78
|
-
}
|
79
|
-
function printHelp() {
|
80
|
-
console.log(`
|
81
|
-
Usage:
|
82
|
-
pkg-stats [options] <package-name>
|
83
|
-
|
84
|
-
Options:
|
85
|
-
-h, --help Show help
|
86
|
-
--group <group> Group by major, minor, or patch (default: major)
|
87
|
-
--major Group by major
|
88
|
-
--minor Group by minor
|
89
|
-
--patch Group by patch
|
90
|
-
--top <number> Show top <number> versions
|
91
|
-
`);
|
92
|
-
}
|
93
52
|
function formatDownloads(downloads, maxDownloads) {
|
94
53
|
if (maxDownloads > 1000000) {
|
95
54
|
return `${(downloads / 1000000).toFixed(1)}M`;
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import { program } from 'commander';
|
2
|
+
import { COLOR_SCHEMES } from './colors.js';
|
3
|
+
export function parseCliOptions(argv) {
|
4
|
+
program
|
5
|
+
.name('pkg-stats')
|
6
|
+
.description('Show NPM weekly downloads stats for a package')
|
7
|
+
.argument('<package-name>', 'Package name')
|
8
|
+
.option('--major', 'Group by major version')
|
9
|
+
.option('--minor', 'Group by minor version')
|
10
|
+
.option('--patch', 'Group by patch version')
|
11
|
+
.option('-t, --top <number>', 'Show top <number> versions')
|
12
|
+
.option('-c, --color <color>', 'Color scheme: ' + COLOR_SCHEMES.join(', '))
|
13
|
+
.parse(argv);
|
14
|
+
const args = program.args;
|
15
|
+
const options = program.opts();
|
16
|
+
return {
|
17
|
+
packageName: args[0],
|
18
|
+
group: options.major ? 'major' : options.minor ? 'minor' : options.patch ? 'patch' : undefined,
|
19
|
+
top: options.top !== undefined ? parseInt(options.top) : undefined,
|
20
|
+
color: options.color && COLOR_SCHEMES.includes(options.color)
|
21
|
+
? options.color
|
22
|
+
: undefined,
|
23
|
+
};
|
24
|
+
}
|
package/dist/colors.js
CHANGED
@@ -9,7 +9,7 @@ const gradients = {
|
|
9
9
|
vice: { colors: ['#5ee7df', '#b490ca'], options: { interpolation: 'hsv' } },
|
10
10
|
passion: { colors: ['#f43b47', '#453a94'], options: {} },
|
11
11
|
fruit: { colors: ['#ff4e50', '#f9d423'], options: {} },
|
12
|
-
|
12
|
+
insta: { colors: ['#833ab4', '#fd1d1d', '#fcb045'], options: {} },
|
13
13
|
retro: {
|
14
14
|
colors: [
|
15
15
|
'#3f51b1',
|
@@ -25,20 +25,30 @@ const gradients = {
|
|
25
25
|
options: {},
|
26
26
|
},
|
27
27
|
summer: { colors: ['#fdbb2d', '#22c1c3'], options: {} },
|
28
|
-
rainbow: {
|
29
|
-
|
28
|
+
rainbow: {
|
29
|
+
colors: ['#ff0100', '#ff0000'],
|
30
|
+
options: { interpolation: 'hsv', hsvSpin: 'long', padEnd: 0.1 },
|
31
|
+
},
|
32
|
+
pastel: {
|
33
|
+
colors: ['#74ebd5', '#74ecd5'],
|
34
|
+
options: { interpolation: 'hsv', hsvSpin: 'long', padEnd: 0.1 },
|
35
|
+
},
|
30
36
|
};
|
37
|
+
export const COLOR_SCHEMES = Object.keys(gradients);
|
31
38
|
export function getColors(count, colorScheme) {
|
32
|
-
const { colors, options } = gradients[colorScheme ??
|
33
|
-
|
39
|
+
const { colors, options } = gradients[colorScheme ?? getColorOfDay()];
|
40
|
+
const paddedCount = count + (options.padEnd ? Math.ceil(count * options.padEnd) : 0);
|
41
|
+
if (paddedCount < colors.length) {
|
34
42
|
return colors;
|
35
43
|
}
|
36
44
|
const gradient = tinygradient(colors);
|
37
45
|
const tinyColors = options.interpolation === 'hsv'
|
38
|
-
? gradient.hsv(
|
39
|
-
: gradient.rgb(
|
46
|
+
? gradient.hsv(paddedCount, options.hsvSpin ?? false)
|
47
|
+
: gradient.rgb(paddedCount);
|
40
48
|
return tinyColors.map((c) => c.toHexString());
|
41
49
|
}
|
42
|
-
function
|
43
|
-
|
50
|
+
function getColorOfDay() {
|
51
|
+
const date = new Date();
|
52
|
+
const index = date.getDate() + date.getMonth() * 30 + date.getFullYear() * 360;
|
53
|
+
return COLOR_SCHEMES[index % COLOR_SCHEMES.length];
|
44
54
|
}
|
package/dist/npm-api.js
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
export async function fetchNpmLastWeekDownloads(packageName) {
|
2
|
+
const response = await fetch(`https://api.npmjs.org/versions/${encodeURIComponent(packageName)}/last-week`);
|
3
|
+
if (!response.ok) {
|
4
|
+
throw new Error(`Failed to fetch data for package "${packageName}. Status: ${response.status}`);
|
5
|
+
}
|
6
|
+
const json = await response.json();
|
7
|
+
if (!json.downloads) {
|
8
|
+
throw new Error('No downloads found');
|
9
|
+
}
|
10
|
+
return {
|
11
|
+
package: packageName,
|
12
|
+
downloads: json.downloads,
|
13
|
+
};
|
14
|
+
}
|
package/package.json
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
{
|
2
2
|
"name": "pkg-stats",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.3.0",
|
4
4
|
"description": "Beautiful NPM package download stats",
|
5
|
-
"author": "Maciej Jastrzębski <mdjastrzebski@gmail.com> (https://github.com/mdjastrzebski)",
|
6
5
|
"license": "MIT",
|
6
|
+
"repository": {
|
7
|
+
"type": "git",
|
8
|
+
"url": "https://github.com/mdjastrzebski/pkg-stats"
|
9
|
+
},
|
10
|
+
"homepage": "https://github.com/mdjastrzebski/pkg-stats",
|
11
|
+
"author": "Maciej Jastrzębski <mdjastrzebski@gmail.com> (https://github.com/mdjastrzebski)",
|
7
12
|
"keywords": [
|
8
13
|
"npm",
|
9
14
|
"package",
|
@@ -19,17 +24,32 @@
|
|
19
24
|
"bin": {
|
20
25
|
"pkg-stats": "./bin.js"
|
21
26
|
},
|
27
|
+
"scripts": {
|
28
|
+
"build": "tsc",
|
29
|
+
"test": "vitest",
|
30
|
+
"typecheck": "tsc --noEmit",
|
31
|
+
"lint": "eslint",
|
32
|
+
"release": "release-it",
|
33
|
+
"validate": "pnpm lint && pnpm typecheck && pnpm test -- --no-watch"
|
34
|
+
},
|
22
35
|
"dependencies": {
|
23
36
|
"chalk": "^5.4.1",
|
24
|
-
"
|
37
|
+
"commander": "^13.0.0",
|
25
38
|
"tinygradient": "^1.1.5"
|
26
39
|
},
|
27
40
|
"devDependencies": {
|
41
|
+
"@eslint/js": "^9.18.0",
|
42
|
+
"@release-it/conventional-changelog": "^10.0.0",
|
28
43
|
"@types/minimist": "^1.2.5",
|
29
44
|
"@types/node": "^22.10.5",
|
30
|
-
"
|
45
|
+
"eslint": "^9.18.0",
|
46
|
+
"eslint-plugin-simple-import-sort": "^12.1.1",
|
47
|
+
"globals": "^15.14.0",
|
48
|
+
"redent": "^4.0.0",
|
49
|
+
"release-it": "^18.1.1",
|
50
|
+
"typescript": "^5.7.3",
|
51
|
+
"typescript-eslint": "^8.19.1",
|
52
|
+
"vitest": "^2.1.8"
|
31
53
|
},
|
32
|
-
"
|
33
|
-
|
34
|
-
}
|
35
|
-
}
|
54
|
+
"packageManager": "pnpm@9.15.3+sha512.1f79bc245a66eb0b07c5d4d83131240774642caaa86ef7d0434ab47c0d16f66b04e21e0c086eb61e62c77efc4d7f7ec071afad3796af64892fae66509173893a"
|
55
|
+
}
|