pkg-stats 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }