pkg-stats 0.5.0 → 0.7.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__/cli-options.test.js +18 -0
- package/dist/__tests__/output.test.js +3 -3
- package/dist/__tests__/stats.test.js +50 -0
- package/dist/cli-options.js +14 -7
- package/dist/mode/compare-packages.js +1 -1
- package/dist/mode/package-stats.js +6 -3
- package/dist/output.js +15 -10
- package/dist/stats.js +5 -1
- package/package.json +17 -14
- package/dist/__tests__/output.js +0 -8
- package/dist/chart.js +0 -7
- package/dist/mode/package-details.js +0 -57
- package/dist/mode/package-info.js +0 -64
- package/dist/mode/single-package.js +0 -58
- package/dist/mode/single-package.ts +0 -0
- package/dist/render.js +0 -23
- package/dist/style.js +0 -16
- package/dist/types.js +0 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { expect, test } from 'vitest';
|
|
2
|
+
import { parseCliOptions } from '../cli-options.js';
|
|
3
|
+
test('parseCliOptions defaults to version sorting', () => {
|
|
4
|
+
const options = parseCliOptions(['node', 'pkg-stats', 'react', '--color', 'atlas']);
|
|
5
|
+
expect(options.sort).toBe('version');
|
|
6
|
+
});
|
|
7
|
+
test('parseCliOptions supports download sorting', () => {
|
|
8
|
+
const options = parseCliOptions([
|
|
9
|
+
'node',
|
|
10
|
+
'pkg-stats',
|
|
11
|
+
'react',
|
|
12
|
+
'--sort',
|
|
13
|
+
'downloads',
|
|
14
|
+
'--color',
|
|
15
|
+
'atlas',
|
|
16
|
+
]);
|
|
17
|
+
expect(options.sort).toBe('downloads');
|
|
18
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { expect, test } from 'vitest';
|
|
2
2
|
import { formatBar } from '../output.js';
|
|
3
3
|
test('renderChart basic tests', () => {
|
|
4
|
-
expect(formatBar(0.0, {
|
|
5
|
-
expect(formatBar(0.5, {
|
|
6
|
-
expect(formatBar(1.0, {
|
|
4
|
+
expect(formatBar(0.0, { width: 10 })).toMatchInlineSnapshot(`"▏ "`);
|
|
5
|
+
expect(formatBar(0.5, { width: 10 })).toMatchInlineSnapshot(`"█████ "`);
|
|
6
|
+
expect(formatBar(1.0, { width: 10 })).toMatchInlineSnapshot(`"██████████"`);
|
|
7
7
|
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { expect, test } from 'vitest';
|
|
2
|
+
import { downloadCompare, filterStats } from '../stats.js';
|
|
3
|
+
const stats = [
|
|
4
|
+
{
|
|
5
|
+
version: { major: 3 },
|
|
6
|
+
versionString: '3.x',
|
|
7
|
+
downloads: 100,
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
version: { major: 2 },
|
|
11
|
+
versionString: '2.x',
|
|
12
|
+
downloads: 300,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
version: { major: 1 },
|
|
16
|
+
versionString: '1.x',
|
|
17
|
+
downloads: 200,
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
test('filterStats keeps top downloads in version order by default', () => {
|
|
21
|
+
expect(filterStats(stats, {
|
|
22
|
+
totalDownloads: 600,
|
|
23
|
+
top: 2,
|
|
24
|
+
}).map((stat) => stat.versionString)).toEqual(['2.x', '1.x']);
|
|
25
|
+
});
|
|
26
|
+
test('downloadCompare sorts by downloads descending', () => {
|
|
27
|
+
expect([...stats].sort(downloadCompare).map((stat) => stat.versionString)).toEqual([
|
|
28
|
+
'2.x',
|
|
29
|
+
'1.x',
|
|
30
|
+
'3.x',
|
|
31
|
+
]);
|
|
32
|
+
});
|
|
33
|
+
test('downloadCompare falls back to version order for ties', () => {
|
|
34
|
+
const tiedStats = [
|
|
35
|
+
{
|
|
36
|
+
version: { major: 1 },
|
|
37
|
+
versionString: '1.x',
|
|
38
|
+
downloads: 100,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
version: { major: 2 },
|
|
42
|
+
versionString: '2.x',
|
|
43
|
+
downloads: 100,
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
expect([...tiedStats].sort(downloadCompare).map((stat) => stat.versionString)).toEqual([
|
|
47
|
+
'2.x',
|
|
48
|
+
'1.x',
|
|
49
|
+
]);
|
|
50
|
+
});
|
package/dist/cli-options.js
CHANGED
|
@@ -12,9 +12,9 @@ Options:
|
|
|
12
12
|
${colorOption('--major')} Group by major version
|
|
13
13
|
${colorOption('--minor')} Group by minor version
|
|
14
14
|
${colorOption('--patch')} Group by patch version
|
|
15
|
+
${colorOption('--sort')} <order> Sort results by version or downloads
|
|
15
16
|
${colorOption('-t, --top')} <number> Show top <number> most downloaded versions
|
|
16
17
|
${colorOption('-a, --all')} Include all versions in output, even those with minimal downloads
|
|
17
|
-
${colorOption('-x, --extended')} Show extended stats
|
|
18
18
|
${colorOption('-c, --color')} <scheme> ${wrapOption(`Choose color scheme from: ${COLOR_SCHEMES.sort().join(', ')}`, 50, 24)}
|
|
19
19
|
|
|
20
20
|
Examples:
|
|
@@ -49,6 +49,7 @@ function wrapOption(text, maxLength, indent) {
|
|
|
49
49
|
export function showHelp() {
|
|
50
50
|
console.log(redent(HELP, 2));
|
|
51
51
|
}
|
|
52
|
+
const SORT_ORDERS = ['version', 'downloads'];
|
|
52
53
|
export function parseCliOptions(argv) {
|
|
53
54
|
const cli = meow(HELP, {
|
|
54
55
|
argv: argv.slice(2),
|
|
@@ -70,6 +71,10 @@ export function parseCliOptions(argv) {
|
|
|
70
71
|
patch: {
|
|
71
72
|
type: 'boolean',
|
|
72
73
|
},
|
|
74
|
+
sort: {
|
|
75
|
+
type: 'string',
|
|
76
|
+
choices: [...SORT_ORDERS],
|
|
77
|
+
},
|
|
73
78
|
top: {
|
|
74
79
|
shortFlag: 't',
|
|
75
80
|
type: 'number',
|
|
@@ -78,10 +83,6 @@ export function parseCliOptions(argv) {
|
|
|
78
83
|
type: 'boolean',
|
|
79
84
|
shortFlag: 'a',
|
|
80
85
|
},
|
|
81
|
-
extended: {
|
|
82
|
-
type: 'boolean',
|
|
83
|
-
shortFlag: 'x',
|
|
84
|
-
},
|
|
85
86
|
color: {
|
|
86
87
|
shortFlag: 'c',
|
|
87
88
|
type: 'string',
|
|
@@ -105,11 +106,17 @@ export function parseCliOptions(argv) {
|
|
|
105
106
|
? 'patch'
|
|
106
107
|
: undefined,
|
|
107
108
|
top: cli.flags.top,
|
|
109
|
+
sort: coalesceSortOrder(cli.flags.sort) ?? 'version',
|
|
108
110
|
all: cli.flags.all ?? false,
|
|
109
|
-
|
|
110
|
-
color: coalesceColor(cli.flags.color) ?? getColorOfDay(),
|
|
111
|
+
color: coalesceColor(cli.flags.color ?? process.env.PKG_STATS_COLOR_SCHEME) ?? getColorOfDay(),
|
|
111
112
|
};
|
|
112
113
|
}
|
|
114
|
+
function coalesceSortOrder(sort) {
|
|
115
|
+
if (sort && SORT_ORDERS.includes(sort)) {
|
|
116
|
+
return sort;
|
|
117
|
+
}
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
113
120
|
function coalesceColor(color) {
|
|
114
121
|
if (color && COLOR_SCHEMES.includes(color)) {
|
|
115
122
|
return color;
|
|
@@ -16,7 +16,7 @@ export async function comparePackages(packageNames, options) {
|
|
|
16
16
|
const items = packagesToDisplay.map((item) => ({
|
|
17
17
|
label: item.packageName,
|
|
18
18
|
value: item.downloads,
|
|
19
|
-
|
|
19
|
+
extra: formatPercentage(item.downloads / maxDownloads),
|
|
20
20
|
}));
|
|
21
21
|
printChart(items, {
|
|
22
22
|
colorScheme: options.color,
|
|
@@ -3,7 +3,7 @@ import { getPrimaryColor } from '../colors.js';
|
|
|
3
3
|
import { formatPercentage } from '../format.js';
|
|
4
4
|
import { fetchNpmLastWeekDownloads } from '../npm-api.js';
|
|
5
5
|
import { printChart } from '../output.js';
|
|
6
|
-
import { filterStats, groupStats } from '../stats.js';
|
|
6
|
+
import { downloadCompare, filterStats, groupStats } from '../stats.js';
|
|
7
7
|
import { parseVersion, versionCompare } from '../version.js';
|
|
8
8
|
export async function printPackageStats(packageName, options) {
|
|
9
9
|
let data;
|
|
@@ -29,11 +29,14 @@ export async function printPackageStats(packageName, options) {
|
|
|
29
29
|
.sort(versionCompare);
|
|
30
30
|
const { type, stats } = groupStats(npmStats, options.group);
|
|
31
31
|
const totalDownloads = Object.values(stats).reduce((sum, version) => sum + version.downloads, 0);
|
|
32
|
-
const
|
|
32
|
+
const filteredStats = filterStats(stats, {
|
|
33
33
|
totalDownloads,
|
|
34
34
|
all: options.all,
|
|
35
35
|
top: options.top,
|
|
36
36
|
});
|
|
37
|
+
const statsToDisplay = options.sort === 'downloads'
|
|
38
|
+
? [...filteredStats].sort(downloadCompare)
|
|
39
|
+
: filteredStats;
|
|
37
40
|
const downloadToDisplay = statsToDisplay.reduce((sum, version) => sum + version.downloads, 0);
|
|
38
41
|
if (totalDownloads - downloadToDisplay > 0) {
|
|
39
42
|
statsToDisplay.push({
|
|
@@ -48,7 +51,7 @@ export async function printPackageStats(packageName, options) {
|
|
|
48
51
|
const items = statsToDisplay.map((item) => ({
|
|
49
52
|
label: item.versionString,
|
|
50
53
|
value: item.downloads,
|
|
51
|
-
|
|
54
|
+
extra: formatPercentage(item.downloads / totalDownloads),
|
|
52
55
|
}));
|
|
53
56
|
printChart(items, {
|
|
54
57
|
colorScheme: options.color,
|
package/dist/output.js
CHANGED
|
@@ -5,24 +5,29 @@ export function printChart(items, options) {
|
|
|
5
5
|
const maxLabelLength = Math.max(...items.map((item) => item.label.length));
|
|
6
6
|
const maxValue = Math.max(...items.map((item) => item.value));
|
|
7
7
|
const maxValueLength = formatDownloads(maxValue, maxValue).length;
|
|
8
|
-
const
|
|
8
|
+
const maxExtraLength = Math.max(...items.map((item) => item.extra?.length ?? 0));
|
|
9
9
|
const colors = getColors(items.length, options.colorScheme);
|
|
10
10
|
const indent = options.indent ?? 0;
|
|
11
|
+
const chartWidth = getTerminalWidth() - indent - maxLabelLength - maxValueLength - maxExtraLength - 5;
|
|
11
12
|
items.forEach((item, i) => {
|
|
12
13
|
const color = chalk.hex(colors[i]);
|
|
13
14
|
const label = ' '.repeat(indent) + item.label.padStart(maxLabelLength);
|
|
14
|
-
const bar = formatBar(item.value / maxValue);
|
|
15
|
+
const bar = formatBar(item.value / maxValue, { width: clamp(chartWidth, 30, 60) });
|
|
15
16
|
const value = formatDownloads(item.value, maxValue).padStart(maxValueLength);
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
: '';
|
|
19
|
-
console.log(`${label} ${color(bar)} ${color(value)}${extended}`);
|
|
17
|
+
const extra = item.extra ? chalk.dim(` ${item.extra}`.padStart(maxExtraLength + 1)) : '';
|
|
18
|
+
console.log(`${label} ${color(bar)} ${color(value)}${extra}`);
|
|
20
19
|
});
|
|
21
20
|
}
|
|
22
|
-
export function formatBar(value, {
|
|
23
|
-
const filledChars = Math.round(value *
|
|
21
|
+
export function formatBar(value, { width = 50 } = {}) {
|
|
22
|
+
const filledChars = Math.round(value * width);
|
|
24
23
|
if (filledChars === 0) {
|
|
25
|
-
return '▏' + ' '.repeat(
|
|
24
|
+
return '▏' + ' '.repeat(width - 1);
|
|
26
25
|
}
|
|
27
|
-
return '█'.repeat(filledChars) + ' '.repeat(
|
|
26
|
+
return '█'.repeat(filledChars) + ' '.repeat(width - filledChars);
|
|
27
|
+
}
|
|
28
|
+
export function getTerminalWidth() {
|
|
29
|
+
return process.stdout.columns;
|
|
30
|
+
}
|
|
31
|
+
function clamp(value, min, max) {
|
|
32
|
+
return Math.min(Math.max(value, min), max);
|
|
28
33
|
}
|
package/dist/stats.js
CHANGED
|
@@ -81,7 +81,11 @@ export function filterStats(stats, options) {
|
|
|
81
81
|
return filtered;
|
|
82
82
|
}
|
|
83
83
|
function pickTopStats(stats, top) {
|
|
84
|
-
const sortedStats = stats.sort((a, b) => b.downloads - a.downloads);
|
|
84
|
+
const sortedStats = [...stats].sort((a, b) => b.downloads - a.downloads);
|
|
85
85
|
const topStats = sortedStats.slice(0, top);
|
|
86
86
|
return topStats.sort((a, b) => versionCompare(a.version, b.version));
|
|
87
87
|
}
|
|
88
|
+
export function downloadCompare(a, b) {
|
|
89
|
+
const downloadDifference = b.downloads - a.downloads;
|
|
90
|
+
return downloadDifference === 0 ? versionCompare(a.version, b.version) : downloadDifference;
|
|
91
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pkg-stats",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Beautiful NPM package download stats",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -24,6 +24,9 @@
|
|
|
24
24
|
"bin": {
|
|
25
25
|
"pkg-stats": "./bin.js"
|
|
26
26
|
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=22.13.0"
|
|
29
|
+
},
|
|
27
30
|
"scripts": {
|
|
28
31
|
"build": "tsc",
|
|
29
32
|
"test": "vitest",
|
|
@@ -33,22 +36,22 @@
|
|
|
33
36
|
"validate": "pnpm lint && pnpm typecheck && pnpm test -- --no-watch"
|
|
34
37
|
},
|
|
35
38
|
"dependencies": {
|
|
36
|
-
"chalk": "^5.
|
|
37
|
-
"meow": "^
|
|
39
|
+
"chalk": "^5.6.2",
|
|
40
|
+
"meow": "^14.1.0",
|
|
38
41
|
"redent": "^4.0.0",
|
|
39
|
-
"tinygradient": "^
|
|
42
|
+
"tinygradient": "^2.0.1"
|
|
40
43
|
},
|
|
41
44
|
"devDependencies": {
|
|
42
|
-
"@eslint/js": "^9.
|
|
43
|
-
"@release-it/conventional-changelog": "^
|
|
44
|
-
"@types/node": "^22.
|
|
45
|
-
"eslint": "^9.
|
|
45
|
+
"@eslint/js": "^9.39.4",
|
|
46
|
+
"@release-it/conventional-changelog": "^11.0.1",
|
|
47
|
+
"@types/node": "^22.19.20",
|
|
48
|
+
"eslint": "^9.39.4",
|
|
46
49
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
47
|
-
"globals": "^15.
|
|
48
|
-
"release-it": "^
|
|
49
|
-
"typescript": "^5.
|
|
50
|
-
"typescript-eslint": "^8.
|
|
51
|
-
"vitest": "^2.1.
|
|
50
|
+
"globals": "^15.15.0",
|
|
51
|
+
"release-it": "^20.2.0",
|
|
52
|
+
"typescript": "^5.9.3",
|
|
53
|
+
"typescript-eslint": "^8.61.0",
|
|
54
|
+
"vitest": "^2.1.9"
|
|
52
55
|
},
|
|
53
|
-
"packageManager": "pnpm@
|
|
56
|
+
"packageManager": "pnpm@11.5.3+sha512.7ac1c919341c213a34dc0d02afb7143c5c26ac26ee8c4782deea821b8ac64d2134a081fd8941dae6e29bbb48f58dfc2b7fbceeccc07cb2f09d219d342a4969ed"
|
|
54
57
|
}
|
package/dist/__tests__/output.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { expect, test } from 'vitest';
|
|
2
|
-
import { formatBar } from '../output.js';
|
|
3
|
-
|
|
4
|
-
test('renderChart basic tests', () => {
|
|
5
|
-
expect(formatBar(0.0, { length: 10 })).toMatchInlineSnapshot(`"▏ "`);
|
|
6
|
-
expect(formatBar(0.5, { length: 10 })).toMatchInlineSnapshot(`"█████ "`);
|
|
7
|
-
expect(formatBar(1.0, { length: 10 })).toMatchInlineSnapshot(`"██████████"`);
|
|
8
|
-
});
|
package/dist/chart.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
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 fetchPackageInfo(packageName, options) {
|
|
9
|
-
const data = await fetchNpmLastWeekDownloads(packageName);
|
|
10
|
-
if (!Object.keys(data.downloads).length) {
|
|
11
|
-
throw new Error(`No data found for package "${packageName}".`);
|
|
12
|
-
}
|
|
13
|
-
const npmStats = Object.keys(data.downloads)
|
|
14
|
-
.map((versionString) => {
|
|
15
|
-
const version = parseVersion(versionString);
|
|
16
|
-
return {
|
|
17
|
-
...version,
|
|
18
|
-
downloads: data.downloads[versionString],
|
|
19
|
-
};
|
|
20
|
-
})
|
|
21
|
-
.sort(versionCompare);
|
|
22
|
-
const { type, stats } = groupStats(npmStats, options.group);
|
|
23
|
-
const totalDownloads = Object.values(stats).reduce((sum, version) => sum + version.downloads, 0);
|
|
24
|
-
return {
|
|
25
|
-
name: packageName,
|
|
26
|
-
totalDownloads,
|
|
27
|
-
groupingType: type,
|
|
28
|
-
stats: stats.map((stat) => ({
|
|
29
|
-
version: stat.versionString,
|
|
30
|
-
downloads: stat.downloads,
|
|
31
|
-
})),
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
export function printPackageInfo({ name, stats, totalDownloads, groupingType }, options) {
|
|
35
|
-
const statsToDisplay = filterStats(stats, {
|
|
36
|
-
totalDownloads,
|
|
37
|
-
all: options.all,
|
|
38
|
-
top: options.top,
|
|
39
|
-
});
|
|
40
|
-
const colors = getColors(statsToDisplay.length, options.color);
|
|
41
|
-
const primaryColor = chalk.hex(colors[0]);
|
|
42
|
-
console.log(chalk.bold(`\nNPM weekly downloads for ${primaryColor(name)}\n`));
|
|
43
|
-
console.log(`Total: ${primaryColor(totalDownloads.toLocaleString())} last week\n`);
|
|
44
|
-
console.log(options.top
|
|
45
|
-
? `Top ${options.top} ${groupingType} versions:\n`
|
|
46
|
-
: `By ${groupingType} version:\n`);
|
|
47
|
-
const maxDownloads = Math.max(...stats.map((v) => v.downloads));
|
|
48
|
-
const displayData = statsToDisplay.map((item) => {
|
|
49
|
-
const versionParts = item.version.split('.');
|
|
50
|
-
return {
|
|
51
|
-
version: versionParts.length < 3 ? `${item.version}.x` : item.version,
|
|
52
|
-
chart: renderChart(item.downloads / maxDownloads),
|
|
53
|
-
downloads: formatDownloads(item.downloads, maxDownloads),
|
|
54
|
-
};
|
|
55
|
-
});
|
|
56
|
-
const maxVersionLength = Math.max(...displayData.map((item) => item.version.length));
|
|
57
|
-
const maxDownloadsLength = Math.max(...displayData.map((item) => item.downloads.length));
|
|
58
|
-
displayData.forEach((item, i) => {
|
|
59
|
-
const color = chalk.hex(colors[i]);
|
|
60
|
-
console.log(`${item.version.padStart(2 + maxVersionLength)} ${color(item.chart)} ${color(item.downloads.padStart(maxDownloadsLength))}`);
|
|
61
|
-
});
|
|
62
|
-
console.log(chalk.bold(`\nNPM weekly downloads for ${chalk.green(name)}\n`));
|
|
63
|
-
console.log(`Total: ${chalk.green(totalDownloads.toLocaleString())} last week\n`);
|
|
64
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
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/render.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { style } from './style.js';
|
|
2
|
-
export function renderChart(value) {
|
|
3
|
-
const filledValues = Math.round(value * style.chart.width * style.chart.pattern.length);
|
|
4
|
-
if (filledValues === 0) {
|
|
5
|
-
return '▏' + (style.rightLabel === 'align-right' ? ' '.repeat(style.chart.width - 1) : '');
|
|
6
|
-
}
|
|
7
|
-
const fullBlocks = Math.floor(filledValues / style.chart.pattern.length);
|
|
8
|
-
const partialBlock = filledValues % style.chart.pattern.length;
|
|
9
|
-
const hasPartialBlock = partialBlock > 0;
|
|
10
|
-
return (style.chart.pattern.at(-1).repeat(fullBlocks) +
|
|
11
|
-
(hasPartialBlock ? style.chart.pattern.at(partialBlock) : '') +
|
|
12
|
-
(style.rightLabel === 'align-right'
|
|
13
|
-
? ' '.repeat(style.chart.width - fullBlocks - (hasPartialBlock ? 1 : 0))
|
|
14
|
-
: ''));
|
|
15
|
-
}
|
|
16
|
-
// export type RenderChartLineOptions = {
|
|
17
|
-
// value: number;
|
|
18
|
-
// leftLabel: string;
|
|
19
|
-
// rightLabel: string;
|
|
20
|
-
// };
|
|
21
|
-
// export function renderChartLine({ value, leftLabel, rightLabel }: RenderChartLineOptions) {
|
|
22
|
-
// return `${leftLabel}${renderChart(value)}${rightLabel}`;
|
|
23
|
-
// }
|
package/dist/style.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export const style = {
|
|
2
|
-
chart: {
|
|
3
|
-
width: 50,
|
|
4
|
-
//pattern: ['⏹'],
|
|
5
|
-
//pattern: ['⏺'],
|
|
6
|
-
//pattern: ['█'],
|
|
7
|
-
//pattern: [' ', '▎', '▌', '▊', '█'],
|
|
8
|
-
//pattern: [' ', '▩'],
|
|
9
|
-
//pattern: [' ', '▰'],
|
|
10
|
-
//pattern: [' ', '∎'],
|
|
11
|
-
pattern: [' ', '█'],
|
|
12
|
-
//pattern: [' ', '▓'],
|
|
13
|
-
zeroPattern: '▏',
|
|
14
|
-
},
|
|
15
|
-
rightLabel: 'align-right',
|
|
16
|
-
};
|
package/dist/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|