pkg-stats 0.0.1 → 0.1.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/bin.js CHANGED
@@ -1,10 +1,27 @@
1
- import { renderChart } from "./chart.js";
2
- import { parseVersion, versionCompare } from "./version.js";
3
- const PACKAGE_NAME = process.argv[2];
4
- const NPM_STATS_URL = `https://api.npmjs.org/versions/${encodeURIComponent(PACKAGE_NAME)}/last-week`;
5
- export async function bin() {
6
- const response = await fetch(NPM_STATS_URL);
7
- const data = await response.json();
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
- const groupedStats = sumByMajor(rawStats);
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
- console.log(`${PACKAGE_NAME} weekly downloads\n`);
20
- console.log(`Total: ${totalDownloads.toLocaleString()}.\n`);
21
- console.log("By version:\n");
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
- for (const item of groupedStats) {
24
- console.log(`${item.versionString.padStart(6)} ${renderChart(item.downloads / maxDownloads)} ${formatDownloads(item.downloads, maxDownloads).padStart(6)}`);
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
- console.log(`\nGenerated on ${new Date().toISOString().slice(0, 10)} by npm-stats.`);
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 "".repeat(filledChars) + " ".repeat(emptyChars);
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
- import { bin } from "./bin.js";
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.0.1",
4
- "description": "Beautiful package download stats",
3
+ "version": "0.1.0",
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
- "scripts": {
11
- "build": "tsc"
19
+ "bin": {
20
+ "pkg-stats": "./dist/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
+ }