pkg-stats 0.3.0 → 0.4.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/__tests__/chart.test.js +1 -1
- package/dist/bin.js +13 -51
- package/dist/chart.js +4 -2
- package/dist/cli-options.js +101 -19
- package/dist/format.js +9 -0
- package/dist/mode/compare-packages.js +48 -0
- package/dist/mode/package-details.js +57 -0
- package/dist/mode/package-stats.js +57 -0
- package/dist/mode/single-package.js +58 -0
- package/dist/mode/single-package.ts +0 -0
- package/dist/stats.js +20 -10
- package/package.json +3 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { expect, test } from 'vitest';
|
|
2
2
|
import { renderChart } from '../chart.js';
|
|
3
3
|
test('renderChart basic tests', () => {
|
|
4
|
-
expect(renderChart(0.0, { length: 10 })).toMatchInlineSnapshot(`"
|
|
4
|
+
expect(renderChart(0.0, { length: 10 })).toMatchInlineSnapshot(`"▏ "`);
|
|
5
5
|
expect(renderChart(0.5, { length: 10 })).toMatchInlineSnapshot(`"█████ "`);
|
|
6
6
|
expect(renderChart(1.0, { length: 10 })).toMatchInlineSnapshot(`"██████████"`);
|
|
7
7
|
});
|
package/dist/bin.js
CHANGED
|
@@ -1,60 +1,22 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { fetchNpmLastWeekDownloads } from './npm-api.js';
|
|
6
|
-
import { groupByType, pickTopStats } from './stats.js';
|
|
7
|
-
import { parseVersion, versionCompare } from './version.js';
|
|
2
|
+
import { parseCliOptions, showHelp } from './cli-options.js';
|
|
3
|
+
import { comparePackages } from './mode/compare-packages.js';
|
|
4
|
+
import { printPackageStats } from './mode/package-stats.js';
|
|
8
5
|
export async function pkgStats(argv) {
|
|
9
|
-
|
|
10
|
-
let data;
|
|
6
|
+
let options;
|
|
11
7
|
try {
|
|
12
|
-
|
|
8
|
+
options = parseCliOptions(argv);
|
|
13
9
|
}
|
|
14
10
|
catch (error) {
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
showHelp();
|
|
12
|
+
console.error(chalk.red(`Error parsing CLI options: ${error instanceof Error ? error.message : error}`));
|
|
13
|
+
process.exit(2);
|
|
17
14
|
}
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
process.exit(1);
|
|
15
|
+
if (options.packageNames.length === 1) {
|
|
16
|
+
await printPackageStats(options.packageNames[0], options);
|
|
21
17
|
}
|
|
22
|
-
|
|
23
|
-
.
|
|
24
|
-
const version = parseVersion(versionString);
|
|
25
|
-
return {
|
|
26
|
-
...version,
|
|
27
|
-
downloads: data.downloads[versionString],
|
|
28
|
-
};
|
|
29
|
-
})
|
|
30
|
-
.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);
|
|
37
|
-
const primaryColor = chalk.hex(colors[0]);
|
|
38
|
-
console.log(chalk.bold(`\nNPM weekly downloads for ${primaryColor(options.packageName)}\n`));
|
|
39
|
-
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) => {
|
|
43
|
-
const versionParts = item.versionString.split('.');
|
|
44
|
-
const version = versionParts.length < 3 ? `${item.versionString}.x` : item.versionString;
|
|
45
|
-
const chart = renderChart(item.downloads / maxDownloads);
|
|
46
|
-
const downloads = formatDownloads(item.downloads, maxDownloads);
|
|
47
|
-
const color = chalk.hex(colors[i]);
|
|
48
|
-
console.log(`${version.padStart(8)} ${color(chart)} ${color(downloads.padStart(6))}`);
|
|
49
|
-
});
|
|
50
|
-
console.log('');
|
|
51
|
-
}
|
|
52
|
-
function formatDownloads(downloads, maxDownloads) {
|
|
53
|
-
if (maxDownloads > 1000000) {
|
|
54
|
-
return `${(downloads / 1000000).toFixed(1)}M`;
|
|
18
|
+
else {
|
|
19
|
+
await comparePackages(options.packageNames, options);
|
|
55
20
|
}
|
|
56
|
-
|
|
57
|
-
return `${(downloads / 1000).toFixed(1)}K`;
|
|
58
|
-
}
|
|
59
|
-
return downloads.toString();
|
|
21
|
+
console.log('');
|
|
60
22
|
}
|
package/dist/chart.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export function renderChart(value, { length = 50 } = {}) {
|
|
2
2
|
const filledChars = Math.round(value * length);
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
if (filledChars === 0) {
|
|
4
|
+
return '▏' + ' '.repeat(length - 1);
|
|
5
|
+
}
|
|
6
|
+
return '█'.repeat(filledChars) + ' '.repeat(length - filledChars);
|
|
5
7
|
}
|
package/dist/cli-options.js
CHANGED
|
@@ -1,24 +1,106 @@
|
|
|
1
|
-
import
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import meow from 'meow';
|
|
3
|
+
import redent from 'redent';
|
|
2
4
|
import { COLOR_SCHEMES } from './colors.js';
|
|
5
|
+
const colorCommand = chalk.hex('#22c1c3');
|
|
6
|
+
const colorOption = chalk.hex('#fdbb2d');
|
|
7
|
+
const HELP = `
|
|
8
|
+
${colorCommand('pkg-stats')} <package> - Show version stats
|
|
9
|
+
${colorCommand('pkg-stats')} <package-1> <package-2>... - Compare between packages
|
|
10
|
+
|
|
11
|
+
Options:
|
|
12
|
+
${colorOption('--major')} Group by major version
|
|
13
|
+
${colorOption('--minor')} Group by minor version
|
|
14
|
+
${colorOption('--patch')} Group by patch version
|
|
15
|
+
${colorOption('-t, --top')} <number> Show top <number> most downloaded versions
|
|
16
|
+
${colorOption('-a, --all')} Include all versions in output, even those with minimal downloads
|
|
17
|
+
${colorOption('-c, --color')} <scheme> ${wrapOption(`Choose color scheme from: ${COLOR_SCHEMES.sort().join(', ')}`, 50, 24)}
|
|
18
|
+
|
|
19
|
+
Examples:
|
|
20
|
+
${chalk.dim('# Show stats for react')}
|
|
21
|
+
${colorCommand('pkg-stats')} react
|
|
22
|
+
|
|
23
|
+
${chalk.dim('# Compare react, vue, angular and svelte')}
|
|
24
|
+
${colorCommand('pkg-stats')} react vue @angular/core svelte
|
|
25
|
+
|
|
26
|
+
${chalk.dim('# Show top 10 major versions of lodash')}
|
|
27
|
+
${colorCommand('pkg-stats')} lodash ${colorOption('--major -t 10')}
|
|
28
|
+
`;
|
|
29
|
+
function wrapOption(text, maxLength, indent) {
|
|
30
|
+
const words = text.split(' ');
|
|
31
|
+
let result = '';
|
|
32
|
+
let currentLine = words[0];
|
|
33
|
+
for (let i = 1; i < words.length; i++) {
|
|
34
|
+
const nextCurrentLine = currentLine + ' ' + words[i];
|
|
35
|
+
if (nextCurrentLine.length <= maxLength) {
|
|
36
|
+
currentLine = nextCurrentLine;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
result += `\n${currentLine}`;
|
|
40
|
+
currentLine = words[i];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (currentLine) {
|
|
44
|
+
result += `\n${currentLine}`;
|
|
45
|
+
}
|
|
46
|
+
return redent(result.trim(), indent).trim();
|
|
47
|
+
}
|
|
48
|
+
export function showHelp() {
|
|
49
|
+
console.log(redent(HELP, 2));
|
|
50
|
+
}
|
|
3
51
|
export function parseCliOptions(argv) {
|
|
4
|
-
|
|
5
|
-
.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
52
|
+
const cli = meow(HELP, {
|
|
53
|
+
argv: argv.slice(2),
|
|
54
|
+
autoHelp: true,
|
|
55
|
+
description: 'Show NPM weekly downloads stats:',
|
|
56
|
+
importMeta: import.meta,
|
|
57
|
+
flags: {
|
|
58
|
+
help: {
|
|
59
|
+
type: 'boolean',
|
|
60
|
+
shortFlag: 'h',
|
|
61
|
+
},
|
|
62
|
+
major: {
|
|
63
|
+
type: 'boolean',
|
|
64
|
+
shortFlag: 'm',
|
|
65
|
+
},
|
|
66
|
+
minor: {
|
|
67
|
+
type: 'boolean',
|
|
68
|
+
},
|
|
69
|
+
patch: {
|
|
70
|
+
type: 'boolean',
|
|
71
|
+
},
|
|
72
|
+
top: {
|
|
73
|
+
shortFlag: 't',
|
|
74
|
+
type: 'number',
|
|
75
|
+
},
|
|
76
|
+
all: {
|
|
77
|
+
type: 'boolean',
|
|
78
|
+
shortFlag: 'a',
|
|
79
|
+
},
|
|
80
|
+
color: {
|
|
81
|
+
shortFlag: 'c',
|
|
82
|
+
type: 'string',
|
|
83
|
+
choices: COLOR_SCHEMES,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
if (cli.flags.help) {
|
|
88
|
+
cli.showHelp();
|
|
89
|
+
}
|
|
90
|
+
if (!cli.input.length) {
|
|
91
|
+
throw new Error('At least one <package-name> is required');
|
|
92
|
+
}
|
|
16
93
|
return {
|
|
17
|
-
|
|
18
|
-
group:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
94
|
+
packageNames: cli.input,
|
|
95
|
+
group: cli.flags.major
|
|
96
|
+
? 'major'
|
|
97
|
+
: cli.flags.minor
|
|
98
|
+
? 'minor'
|
|
99
|
+
: cli.flags.patch
|
|
100
|
+
? 'patch'
|
|
101
|
+
: undefined,
|
|
102
|
+
top: cli.flags.top,
|
|
103
|
+
all: cli.flags.all,
|
|
104
|
+
color: cli.flags.color,
|
|
23
105
|
};
|
|
24
106
|
}
|
package/dist/format.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { renderChart } from '../chart.js';
|
|
3
|
+
import { getColors } from '../colors.js';
|
|
4
|
+
import { formatDownloads } from '../format.js';
|
|
5
|
+
import { fetchNpmLastWeekDownloads } from '../npm-api.js';
|
|
6
|
+
export async function comparePackages(packageNames, options) {
|
|
7
|
+
const rawPackages = await Promise.all(packageNames.map((packageName) => fetchPackageData(packageName)));
|
|
8
|
+
const packagesToDisplay = rawPackages
|
|
9
|
+
.filter((pkg) => pkg !== undefined)
|
|
10
|
+
.sort((a, b) => b.downloads - a.downloads);
|
|
11
|
+
if (packagesToDisplay.length === 0) {
|
|
12
|
+
console.error(chalk.red('\nNo packages found.\n'));
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
console.log(chalk.bold(`\nNPM weekly downloads\n`));
|
|
16
|
+
const maxDownloads = Math.max(...packagesToDisplay.map((v) => v.downloads));
|
|
17
|
+
const displayData = packagesToDisplay.map((item) => {
|
|
18
|
+
return {
|
|
19
|
+
name: item.packageName,
|
|
20
|
+
chart: renderChart(item.downloads / maxDownloads),
|
|
21
|
+
downloads: formatDownloads(item.downloads, maxDownloads),
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
const maxNameLength = Math.max(...displayData.map((item) => item.name.length));
|
|
25
|
+
const maxDownloadsLength = Math.max(...displayData.map((item) => item.downloads.length));
|
|
26
|
+
const colors = getColors(packagesToDisplay.length, options.color);
|
|
27
|
+
displayData.forEach((item, i) => {
|
|
28
|
+
const color = chalk.hex(colors[i]);
|
|
29
|
+
console.log(`${item.name.padStart(2 + maxNameLength)} ${color(item.chart)} ${color(item.downloads.padStart(maxDownloadsLength))}`);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
async function fetchPackageData(packageName) {
|
|
33
|
+
try {
|
|
34
|
+
const data = await fetchNpmLastWeekDownloads(packageName);
|
|
35
|
+
if (!Object.keys(data.downloads).length) {
|
|
36
|
+
console.warn(chalk.yellow(`No data found for package "${packageName}".`));
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
packageName,
|
|
41
|
+
downloads: Object.values(data.downloads).reduce((sum, downloads) => sum + downloads, 0),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.warn(chalk.yellow(`Failed to fetch data for package "${packageName}"`, error));
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { renderChart } from '../chart.js';
|
|
3
|
+
import { getColors } from '../colors.js';
|
|
4
|
+
import { formatDownloads } from '../format.js';
|
|
5
|
+
import { fetchNpmLastWeekDownloads } from '../npm-api.js';
|
|
6
|
+
import { filterStats, groupStats } from '../stats.js';
|
|
7
|
+
import { parseVersion, versionCompare } from '../version.js';
|
|
8
|
+
export async function packageDetails(packageName, options) {
|
|
9
|
+
let data;
|
|
10
|
+
try {
|
|
11
|
+
data = await fetchNpmLastWeekDownloads(packageName);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
console.error(`Failed to fetch data for package "${packageName}"`, error);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (!Object.keys(data.downloads).length) {
|
|
18
|
+
console.error(`No data found for package "${packageName}".\n`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const npmStats = Object.keys(data.downloads)
|
|
22
|
+
.map((versionString) => {
|
|
23
|
+
const version = parseVersion(versionString);
|
|
24
|
+
return {
|
|
25
|
+
...version,
|
|
26
|
+
downloads: data.downloads[versionString],
|
|
27
|
+
};
|
|
28
|
+
})
|
|
29
|
+
.sort(versionCompare);
|
|
30
|
+
const { type, stats } = groupStats(npmStats, options.group);
|
|
31
|
+
const totalDownloads = Object.values(stats).reduce((sum, version) => sum + version.downloads, 0);
|
|
32
|
+
const statsToDisplay = filterStats(stats, {
|
|
33
|
+
totalDownloads,
|
|
34
|
+
all: options.all,
|
|
35
|
+
top: options.top,
|
|
36
|
+
});
|
|
37
|
+
const colors = getColors(statsToDisplay.length, options.color);
|
|
38
|
+
const primaryColor = chalk.hex(colors[0]);
|
|
39
|
+
console.log(chalk.bold(`\nNPM weekly downloads for ${primaryColor(packageName)}\n`));
|
|
40
|
+
console.log(`Total: ${primaryColor(totalDownloads.toLocaleString())} last week\n`);
|
|
41
|
+
console.log(options.top ? `Top ${options.top} ${type} versions:\n` : `By ${type} version:\n`);
|
|
42
|
+
const maxDownloads = Math.max(...stats.map((v) => v.downloads));
|
|
43
|
+
const displayData = statsToDisplay.map((item) => {
|
|
44
|
+
const versionParts = item.versionString.split('.');
|
|
45
|
+
return {
|
|
46
|
+
version: versionParts.length < 3 ? `${item.versionString}.x` : item.versionString,
|
|
47
|
+
chart: renderChart(item.downloads / maxDownloads),
|
|
48
|
+
downloads: formatDownloads(item.downloads, maxDownloads),
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
const maxVersionLength = Math.max(...displayData.map((item) => item.version.length));
|
|
52
|
+
const maxDownloadsLength = Math.max(...displayData.map((item) => item.downloads.length));
|
|
53
|
+
displayData.forEach((item, i) => {
|
|
54
|
+
const color = chalk.hex(colors[i]);
|
|
55
|
+
console.log(`${item.version.padStart(2 + maxVersionLength)} ${color(item.chart)} ${color(item.downloads.padStart(maxDownloadsLength))}`);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { renderChart } from '../chart.js';
|
|
3
|
+
import { getColors } from '../colors.js';
|
|
4
|
+
import { formatDownloads } from '../format.js';
|
|
5
|
+
import { fetchNpmLastWeekDownloads } from '../npm-api.js';
|
|
6
|
+
import { filterStats, groupStats } from '../stats.js';
|
|
7
|
+
import { parseVersion, versionCompare } from '../version.js';
|
|
8
|
+
export async function printPackageStats(packageName, options) {
|
|
9
|
+
let data;
|
|
10
|
+
try {
|
|
11
|
+
data = await fetchNpmLastWeekDownloads(packageName);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
console.error(`Failed to fetch data for package "${packageName}"`, error);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (!Object.keys(data.downloads).length) {
|
|
18
|
+
console.error(`No data found for package "${packageName}".\n`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const npmStats = Object.keys(data.downloads)
|
|
22
|
+
.map((versionString) => {
|
|
23
|
+
const version = parseVersion(versionString);
|
|
24
|
+
return {
|
|
25
|
+
...version,
|
|
26
|
+
downloads: data.downloads[versionString],
|
|
27
|
+
};
|
|
28
|
+
})
|
|
29
|
+
.sort(versionCompare);
|
|
30
|
+
const { type, stats } = groupStats(npmStats, options.group);
|
|
31
|
+
const totalDownloads = Object.values(stats).reduce((sum, version) => sum + version.downloads, 0);
|
|
32
|
+
const statsToDisplay = filterStats(stats, {
|
|
33
|
+
totalDownloads,
|
|
34
|
+
all: options.all,
|
|
35
|
+
top: options.top,
|
|
36
|
+
});
|
|
37
|
+
const colors = getColors(statsToDisplay.length, options.color);
|
|
38
|
+
const primaryColor = chalk.hex(colors[0]);
|
|
39
|
+
console.log(chalk.bold(`\nNPM weekly downloads for ${primaryColor(packageName)}\n`));
|
|
40
|
+
console.log(`Total: ${primaryColor(totalDownloads.toLocaleString())} last week\n`);
|
|
41
|
+
console.log(options.top ? `Top ${options.top} ${type} versions:\n` : `By ${type} version:\n`);
|
|
42
|
+
const maxDownloads = Math.max(...stats.map((v) => v.downloads));
|
|
43
|
+
const displayData = statsToDisplay.map((item) => {
|
|
44
|
+
const versionParts = item.versionString.split('.');
|
|
45
|
+
return {
|
|
46
|
+
version: versionParts.length < 3 ? `${item.versionString}.x` : item.versionString,
|
|
47
|
+
chart: renderChart(item.downloads / maxDownloads),
|
|
48
|
+
downloads: formatDownloads(item.downloads, maxDownloads),
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
const maxVersionLength = Math.max(...displayData.map((item) => item.version.length));
|
|
52
|
+
const maxDownloadsLength = Math.max(...displayData.map((item) => item.downloads.length));
|
|
53
|
+
displayData.forEach((item, i) => {
|
|
54
|
+
const color = chalk.hex(colors[i]);
|
|
55
|
+
console.log(`${item.version.padStart(2 + maxVersionLength)} ${color(item.chart)} ${color(item.downloads.padStart(maxDownloadsLength))}`);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { renderChart } from '../chart.js';
|
|
3
|
+
import { getColors } from '../colors.js';
|
|
4
|
+
import { formatDownloads } from '../format.js';
|
|
5
|
+
import { fetchNpmLastWeekDownloads } from '../npm-api.js';
|
|
6
|
+
import { filterStats, groupStats } from '../stats.js';
|
|
7
|
+
import { parseVersion, versionCompare } from '../version.js';
|
|
8
|
+
export async function singlePackage(packageName, options) {
|
|
9
|
+
let data;
|
|
10
|
+
try {
|
|
11
|
+
data = await fetchNpmLastWeekDownloads(packageName);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
console.error(`Failed to fetch data for package "${packageName}"`, error);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (!Object.keys(data.downloads).length) {
|
|
18
|
+
console.error(`No data found for package "${packageName}".\n`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const npmStats = Object.keys(data.downloads)
|
|
22
|
+
.map((versionString) => {
|
|
23
|
+
const version = parseVersion(versionString);
|
|
24
|
+
return {
|
|
25
|
+
...version,
|
|
26
|
+
downloads: data.downloads[versionString],
|
|
27
|
+
};
|
|
28
|
+
})
|
|
29
|
+
.sort(versionCompare);
|
|
30
|
+
const { type, stats } = groupStats(npmStats, options.group);
|
|
31
|
+
const totalDownloads = Object.values(stats).reduce((sum, version) => sum + version.downloads, 0);
|
|
32
|
+
const statsToDisplay = filterStats(stats, {
|
|
33
|
+
totalDownloads,
|
|
34
|
+
all: options.all,
|
|
35
|
+
top: options.top,
|
|
36
|
+
});
|
|
37
|
+
const colors = getColors(statsToDisplay.length, options.color);
|
|
38
|
+
const primaryColor = chalk.hex(colors[0]);
|
|
39
|
+
console.log(chalk.bold(`\nNPM weekly downloads for ${primaryColor(packageName)}\n`));
|
|
40
|
+
console.log(`Total: ${primaryColor(totalDownloads.toLocaleString())} last week\n`);
|
|
41
|
+
console.log(options.top ? `Top ${options.top} ${type} versions:\n` : `By ${type} version:\n`);
|
|
42
|
+
const maxDownloads = Math.max(...stats.map((v) => v.downloads));
|
|
43
|
+
const displayData = statsToDisplay.map((item) => {
|
|
44
|
+
const versionParts = item.versionString.split('.');
|
|
45
|
+
return {
|
|
46
|
+
version: versionParts.length < 3 ? `${item.versionString}.x` : item.versionString,
|
|
47
|
+
chart: renderChart(item.downloads / maxDownloads),
|
|
48
|
+
downloads: formatDownloads(item.downloads, maxDownloads),
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
const maxVersionLength = Math.max(...displayData.map((item) => item.version.length));
|
|
52
|
+
const maxDownloadsLength = Math.max(...displayData.map((item) => item.downloads.length));
|
|
53
|
+
displayData.forEach((item, i) => {
|
|
54
|
+
const color = chalk.hex(colors[i]);
|
|
55
|
+
console.log(`${item.version.padStart(2 + maxVersionLength)} ${color(item.chart)} ${color(item.downloads.padStart(maxDownloadsLength))}`);
|
|
56
|
+
});
|
|
57
|
+
console.log('');
|
|
58
|
+
}
|
|
File without changes
|
package/dist/stats.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import { versionCompare } from './version.js';
|
|
2
|
-
export function
|
|
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
|
|
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
|
|
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 = {};
|
|
@@ -65,7 +65,17 @@ function groupByPatch(stats) {
|
|
|
65
65
|
}
|
|
66
66
|
return Object.values(result).sort((a, b) => versionCompare(a.version, b.version));
|
|
67
67
|
}
|
|
68
|
-
export function
|
|
68
|
+
export function filterStats(stats, options) {
|
|
69
|
+
if (options.all) {
|
|
70
|
+
return stats;
|
|
71
|
+
}
|
|
72
|
+
if (options.top) {
|
|
73
|
+
return pickTopStats(stats, options.top);
|
|
74
|
+
}
|
|
75
|
+
const downloadThreshold = 0.005 * options.totalDownloads; // 0.5%
|
|
76
|
+
return stats.filter((stat) => stat.downloads >= downloadThreshold);
|
|
77
|
+
}
|
|
78
|
+
function pickTopStats(stats, top) {
|
|
69
79
|
const sortedStats = stats.sort((a, b) => b.downloads - a.downloads);
|
|
70
80
|
const topStats = sortedStats.slice(0, top);
|
|
71
81
|
return topStats.sort((a, b) => versionCompare(a.version, b.version));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pkg-stats",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
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
|
-
"
|
|
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",
|