-
Notifications
You must be signed in to change notification settings - Fork 72
/
useBadges.ts
120 lines (105 loc) · 3.12 KB
/
useBadges.ts
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
import useIsBrowser from "@docusaurus/useIsBrowser";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
interface RegistryResponse {
author: {
name: string;
};
description: string;
'dist-tags': {
latest: string;
};
keywords: string[];
license: string;
maintainers: { name: string; email: string; }[];
name: string;
readme: string;
readmeFilename: string;
time: Record<string, string>;
// versions is not exactly correct but it's good enough for now
versions: Record<string, RegistryResponse>;
};
interface DownloadCountResponse {
downloads: number;
start: string;
end: string;
package: string;
}
interface JSDelivrAPIResponseRanking {
rank: number;
typeRank: number;
total: number;
dates: Record<string, number>;
prev: {
rank: number;
typeRank: number;
total: number;
};
};
interface JSDelivrAPIResponse {
hits: JSDelivrAPIResponseRanking;
bandwidth: JSDelivrAPIResponseRanking;
links: {
self: string;
version: string;
};
}
const MAX_ATTEMPTS = 3;
function useFetch<T>(url: string): T | undefined {
const [result, setResult] = useState<T>();
const isBrowser = useIsBrowser();
const mounted = useRef(false);
useEffect(() => {
mounted.current = isBrowser;
return () => {
mounted.current = false;
};
}, [isBrowser]);
const fetchUrl = useCallback(async (isBrowser: boolean, url: string, attempts = 0) => {
if (isBrowser) {
try {
const r = await fetch(url);
const j = await r.json();
if (mounted.current) {
setResult(j);
}
} catch (err) {
if (attempts > MAX_ATTEMPTS) {
throw new Error(`Could not fetch ${url} after ${MAX_ATTEMPTS} attempts`);
}
setTimeout(() => {
if (!mounted.current) {
fetchUrl(isBrowser, url, attempts + 1);
}
}, 500);
}
}
}, [isBrowser]);
useEffect(() => {
fetchUrl(isBrowser, url);
}, [isBrowser, url])
return result;
};
const useRegistry = (packageName: string) => useFetch<RegistryResponse>(`https://registry.npmjs.org/@upscalerjs/${packageName}`);
const useDownloadCount = (packageName: string) => useFetch<DownloadCountResponse>(`https://api.npmjs.org/downloads/point/last-week/@upscalerjs/${packageName}`);
const useHitCount = (packageName: string) => useFetch<JSDelivrAPIResponse>(`https://data.jsdelivr.com/v1/stats/packages/npm/@upscalerjs/${packageName}?period=week`);
export const useBadges = (packageName: string): {
version: undefined | string;
lastUpdated: undefined | Date;
minifiedSize: undefined | number;
downloadsPerWeek: undefined | number;
cdnHits: undefined | number;
} => {
const {
'dist-tags': distTags,
time,
} = useRegistry(packageName) || {};
const { downloads: downloadsPerWeek } = useDownloadCount(packageName) || {};
const { hits } = useHitCount(packageName) || {};
return useMemo(() => ({
version: distTags?.latest,
lastUpdated: time?.modified ? new Date(time.modified) : undefined,
minifiedSize: undefined,
downloadsPerWeek,
cdnHits: hits?.total,
}), [distTags, time, downloadsPerWeek]);
};