pkg-stats 0.3.0 → 0.3.1

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