pkg-stats 0.3.0 → 0.3.1

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,12 +1,21 @@
1
1
  import chalk from 'chalk';
2
2
  import { renderChart } from './chart.js';
3
- import { parseCliOptions } from './cli-options.js';
3
+ import { parseCliOptions, showHelp } from './cli-options.js';
4
4
  import { getColors } from './colors.js';
5
+ import { formatDownloads } from './format.js';
5
6
  import { fetchNpmLastWeekDownloads } from './npm-api.js';
6
- import { groupByType, pickTopStats } from './stats.js';
7
+ import { groupStats, pickTopStats } from './stats.js';
7
8
  import { parseVersion, versionCompare } from './version.js';
8
9
  export async function pkgStats(argv) {
9
- const options = parseCliOptions(argv);
10
+ let options;
11
+ try {
12
+ options = parseCliOptions(argv);
13
+ }
14
+ catch (error) {
15
+ showHelp();
16
+ console.error(chalk.red(`Error parsing CLI options: ${error instanceof Error ? error.message : error}`));
17
+ process.exit(2);
18
+ }
10
19
  let data;
11
20
  try {
12
21
  data = await fetchNpmLastWeekDownloads(options.packageName);
@@ -19,7 +28,7 @@ export async function pkgStats(argv) {
19
28
  console.error(`No data found for package "${options.packageName}".\n`);
20
29
  process.exit(1);
21
30
  }
22
- const rawStats = Object.keys(data.downloads)
31
+ const npmStats = Object.keys(data.downloads)
23
32
  .map((versionString) => {
24
33
  const version = parseVersion(versionString);
25
34
  return {
@@ -28,33 +37,22 @@ export async function pkgStats(argv) {
28
37
  };
29
38
  })
30
39
  .sort(versionCompare);
31
- const groupedStats = groupByType(options.group, rawStats);
32
- const totalDownloads = Object.values(groupedStats).reduce((sum, version) => sum + version.downloads, 0);
33
- const groupedStatsToDisplay = options.top
34
- ? pickTopStats(groupedStats, options.top)
35
- : groupedStats;
36
- const colors = getColors(groupedStatsToDisplay.length, options.color);
40
+ const { type, stats } = groupStats(npmStats, options.group);
41
+ const totalDownloads = Object.values(stats).reduce((sum, version) => sum + version.downloads, 0);
42
+ const statsToDisplay = options.top ? pickTopStats(stats, options.top) : stats;
43
+ const colors = getColors(statsToDisplay.length, options.color);
37
44
  const primaryColor = chalk.hex(colors[0]);
38
45
  console.log(chalk.bold(`\nNPM weekly downloads for ${primaryColor(options.packageName)}\n`));
39
46
  console.log(`Total: ${primaryColor(totalDownloads.toLocaleString())} last week\n`);
40
- console.log(options.top ? `Top ${options.top} versions:\n` : 'By version:\n');
41
- const maxDownloads = Math.max(...groupedStats.map((v) => v.downloads));
42
- groupedStatsToDisplay.forEach((item, i) => {
47
+ console.log(options.top ? `Top ${options.top} ${type} versions:\n` : `By ${type} version:\n`);
48
+ const maxDownloads = Math.max(...stats.map((v) => v.downloads));
49
+ statsToDisplay.forEach((item, i) => {
43
50
  const versionParts = item.versionString.split('.');
44
- const version = versionParts.length < 3 ? `${item.versionString}.x` : item.versionString;
51
+ const versionString = versionParts.length < 3 ? `${item.versionString}.x` : item.versionString;
45
52
  const chart = renderChart(item.downloads / maxDownloads);
46
53
  const downloads = formatDownloads(item.downloads, maxDownloads);
47
54
  const color = chalk.hex(colors[i]);
48
- console.log(`${version.padStart(8)} ${color(chart)} ${color(downloads.padStart(6))}`);
55
+ console.log(`${versionString.padStart(8)} ${color(chart)} ${color(downloads.padStart(6))}`);
49
56
  });
50
57
  console.log('');
51
58
  }
52
- function formatDownloads(downloads, maxDownloads) {
53
- if (maxDownloads > 1000000) {
54
- return `${(downloads / 1000000).toFixed(1)}M`;
55
- }
56
- if (maxDownloads > 1000) {
57
- return `${(downloads / 1000).toFixed(1)}K`;
58
- }
59
- return downloads.toString();
60
- }
@@ -1,24 +1,70 @@
1
- import { program } from 'commander';
1
+ import meow from 'meow';
2
+ import redent from 'redent';
2
3
  import { COLOR_SCHEMES } from './colors.js';
4
+ const HELP = `
5
+ pkg-stats <package-name>
6
+
7
+ Show NPM weekly downloads stats for a package
8
+
9
+ Options:
10
+ -h, --help Show help
11
+ --major Group by major version
12
+ --minor Group by minor version
13
+ --patch Group by patch version
14
+ -t, --top <number> Show top <number> versions
15
+ -c, --color <color> Color scheme: ${COLOR_SCHEMES.sort().join(', ')}
16
+ `;
17
+ export function showHelp() {
18
+ console.log(redent(HELP, 2));
19
+ }
3
20
  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();
21
+ const cli = meow(HELP, {
22
+ argv: argv.slice(2),
23
+ autoHelp: true,
24
+ description: 'Show NPM weekly downloads stats for a package',
25
+ importMeta: import.meta,
26
+ flags: {
27
+ help: {
28
+ type: 'boolean',
29
+ shortFlag: 'h',
30
+ },
31
+ major: {
32
+ type: 'boolean',
33
+ shortFlag: 'm',
34
+ },
35
+ minor: {
36
+ type: 'boolean',
37
+ },
38
+ patch: {
39
+ type: 'boolean',
40
+ },
41
+ top: {
42
+ shortFlag: 't',
43
+ type: 'number',
44
+ },
45
+ color: {
46
+ shortFlag: 'c',
47
+ type: 'string',
48
+ choices: COLOR_SCHEMES,
49
+ },
50
+ },
51
+ });
52
+ if (cli.flags.help) {
53
+ cli.showHelp();
54
+ }
55
+ if (!cli.input[0]) {
56
+ throw new Error('<package-name> is required');
57
+ }
16
58
  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,
59
+ packageName: cli.input[0],
60
+ group: cli.flags.major
61
+ ? 'major'
62
+ : cli.flags.minor
63
+ ? 'minor'
64
+ : cli.flags.patch
65
+ ? 'patch'
66
+ : undefined,
67
+ top: cli.flags.top,
68
+ color: cli.flags.color,
23
69
  };
24
70
  }
package/dist/format.js ADDED
@@ -0,0 +1,9 @@
1
+ export function formatDownloads(downloads, maxDownloads) {
2
+ if (maxDownloads > 1000000) {
3
+ return `${(downloads / 1000000).toFixed(1)}M`;
4
+ }
5
+ if (maxDownloads > 1000) {
6
+ return `${(downloads / 1000).toFixed(1)}K`;
7
+ }
8
+ return downloads.toString();
9
+ }
package/dist/stats.js CHANGED
@@ -1,23 +1,23 @@
1
1
  import { versionCompare } from './version.js';
2
- export function groupByType(type, stats) {
2
+ export function groupStats(stats, type) {
3
3
  if (type === 'major') {
4
- return groupByMajor(stats);
4
+ return { type: 'major', stats: groupByMajor(stats) };
5
5
  }
6
6
  if (type === 'minor') {
7
- return groupByMinor(stats);
7
+ return { type: 'minor', stats: groupByMinor(stats) };
8
8
  }
9
9
  if (type === 'patch') {
10
- return groupByPatch(stats);
10
+ return { type: 'patch', stats: groupByPatch(stats) };
11
11
  }
12
12
  const groupedByMajor = groupByMajor(stats);
13
- if (groupedByMajor.length > 1) {
14
- return groupedByMajor;
13
+ if (groupedByMajor.length >= 3) {
14
+ return { type: 'major', stats: groupedByMajor };
15
15
  }
16
16
  const groupedByMinor = groupByMinor(stats);
17
- if (groupedByMinor.length > 1) {
18
- return groupedByMinor;
17
+ if (groupedByMinor.length >= 3) {
18
+ return { type: 'minor', stats: groupedByMinor };
19
19
  }
20
- return groupByPatch(stats);
20
+ return { type: 'patch', stats: groupByPatch(stats) };
21
21
  }
22
22
  function groupByMajor(stats) {
23
23
  const result = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pkg-stats",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Beautiful NPM package download stats",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -34,18 +34,17 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "chalk": "^5.4.1",
37
- "commander": "^13.0.0",
37
+ "meow": "^13.2.0",
38
+ "redent": "^4.0.0",
38
39
  "tinygradient": "^1.1.5"
39
40
  },
40
41
  "devDependencies": {
41
42
  "@eslint/js": "^9.18.0",
42
43
  "@release-it/conventional-changelog": "^10.0.0",
43
- "@types/minimist": "^1.2.5",
44
44
  "@types/node": "^22.10.5",
45
45
  "eslint": "^9.18.0",
46
46
  "eslint-plugin-simple-import-sort": "^12.1.1",
47
47
  "globals": "^15.14.0",
48
- "redent": "^4.0.0",
49
48
  "release-it": "^18.1.1",
50
49
  "typescript": "^5.7.3",
51
50
  "typescript-eslint": "^8.19.1",