forked from NullVoxPopuli/package-majors
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils.ts
More file actions
123 lines (91 loc) · 3.37 KB
/
utils.ts
File metadata and controls
123 lines (91 loc) · 3.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import semverCoerce from 'semver/functions/coerce';
import semverCompare from 'semver/functions/compare';
import getMajor from 'semver/functions/major';
import getMinor from 'semver/functions/minor';
import isValid from 'semver/functions/valid';
import type { DownloadsResponse, ErrorResponse, QueryData } from './types';
export type Grouped = ReturnType<typeof groupByMajor>;
export function versionComparator(a: number | string, b: number | string) {
const semverA = semverCoerce(a);
const semverB = semverCoerce(b);
if (semverA === null) return -1;
if (semverB === null) return 1;
return semverCompare(semverA, semverB);
}
/**
* Filters minimum downloads up until the first version that contains more than
* `minDownloads`
*/
export function filterDownloads(
downloads: DownloadsResponse['downloads'],
minDownloads: number
): DownloadsResponse['downloads'] {
// We must copy the object, becaues it's cached and long lived, se we can't mutate it.
const copy = { ...downloads };
const sortedVersions = Object.keys(downloads).sort(versionComparator);
for (const version of sortedVersions) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const downloadCount = downloads[version]!;
if (downloadCount < minDownloads) {
delete copy[version];
} else {
break;
}
}
return copy;
}
export function getTotalDownloads(downloads: DownloadsResponse['downloads']): number {
let total = 0;
for (const [, downloadCount] of Object.entries(downloads)) {
total += downloadCount;
}
return total;
}
export function groupByMajor(downloads: DownloadsResponse['downloads']) {
const groups: Record<number, number> = {};
for (const [version, downloadCount] of Object.entries(downloads)) {
if (!isValid(version)) {
console.error(`${version} is invalid and will be omitted from the dataset.`);
continue;
}
const major = getMajor(version);
groups[major] ||= 0;
groups[major] += downloadCount;
}
return Object.entries(groups).map(([major, downloadCount]) => {
return { version: major, downloadCount };
});
}
export function groupByMinor(downloads: DownloadsResponse['downloads']): Grouped {
const groups: Record<string, number> = {};
for (const [version, downloadCount] of Object.entries(downloads)) {
if (!isValid(version)) {
console.error(`${version} is invalid and will be omitted from the dataset.`);
continue;
}
const major = getMajor(version);
const minor = getMinor(version);
const majorMinor = `${major}.${minor}`;
groups[majorMinor] ||= 0;
groups[majorMinor] += downloadCount;
}
return Object.entries(groups).map(([majorMinor, downloadCount]) => {
return { version: majorMinor, downloadCount };
});
}
export function isDownloadsResponse(data: unknown): data is DownloadsResponse {
if (typeof data !== 'object') return false;
if (data === null) return false;
if (!('package' in data && 'downloads' in data)) return false;
if (typeof data.downloads !== 'object') return false;
if (data.downloads === null) return false;
return true;
}
export function isError(data: unknown): data is ErrorResponse {
if (typeof data !== 'object') return false;
if (data === null) return false;
return 'error' in data;
}
export function hasHistory(histories: QueryData['histories']) {
return Object.values(histories).filter(Boolean).length > 0;
}