1
+ import axios from "axios"
2
+ import { add , differenceInDays , format , isAfter , parseISO } from "date-fns"
1
3
import { DAY_IN_MS } from "./constants"
2
4
3
5
export type RepositoryQueryResult = {
@@ -104,6 +106,31 @@ export const repositoryStarsQuery = `query RepositoryQuery($owner: String!, $nam
104
106
}
105
107
}`
106
108
109
+ export const getRepoStargazers = async ( owner : string , name : string , page ?: number ) => {
110
+ let url = `https://api.github.com/repos/${ owner } /${ name } /stargazers?per_page=${ 30 } `
111
+
112
+ if ( page !== undefined ) {
113
+ url = `${ url } &page=${ page } `
114
+ }
115
+ return axios . get ( url , {
116
+ headers : {
117
+ Accept : "application/vnd.github.v3.star+json" ,
118
+ Authorization : `token ${ process . env . GITHUB_TOKEN } ` ,
119
+ } ,
120
+ } )
121
+ }
122
+
123
+ export const getRepoStargazersCount = async ( owner : string , name : string ) => {
124
+ const { data } = await axios . get ( `https://api.github.com/repos/${ owner } /${ name } ` , {
125
+ headers : {
126
+ Accept : "application/vnd.github.v3.star+json" ,
127
+ Authorization : `token ${ process . env . GITHUB_TOKEN } ` ,
128
+ } ,
129
+ } )
130
+
131
+ return data . stargazers_count
132
+ }
133
+
107
134
/**
108
135
* Extracts the repository owner and name from a GitHub URL.
109
136
*
@@ -134,12 +161,12 @@ type GetToolScoreProps = {
134
161
/**
135
162
* Calculates a score for a tool based on its GitHub statistics and an optional bump.
136
163
*
137
- * @param props. stars - The number of stars the tool has on GitHub.
138
- * @param props. forks - The number of forks the tool has on GitHub.
139
- * @param props. contributors - The number of contributors to the tool's repository.
140
- * @param props. watchers - The number of watchers the tool has on GitHub.
141
- * @param props. lastCommitDate - The date of the last commit to the tool's repository.
142
- * @param props. bump - An optional bump to the final score.
164
+ * @param stars - The number of stars the tool has on GitHub.
165
+ * @param forks - The number of forks the tool has on GitHub.
166
+ * @param contributors - The number of contributors to the tool's repository.
167
+ * @param watchers - The number of watchers the tool has on GitHub.
168
+ * @param lastCommitDate - The date of the last commit to the tool's repository.
169
+ * @param bump - An optional bump to the final score.
143
170
* @returns The calculated score for the tool.
144
171
*/
145
172
export const calculateHealthScore = ( {
@@ -164,3 +191,70 @@ export const calculateHealthScore = ({
164
191
starsScore + forksScore + contributorsScore + watchersScore - lastCommitPenalty + ( bump || 0 ) ,
165
192
)
166
193
}
194
+
195
+ export const getAllGithubStars = async ( owner : string , name : string , amount = 20 ) => {
196
+ // Get the total amount of stars from GitHub
197
+ const totalStars = await getRepoStargazersCount ( owner , name )
198
+
199
+ // get total pages
200
+ const totalPages = Math . ceil ( totalStars / 100 )
201
+
202
+ // How many pages to skip? We don't want to spam requests
203
+ const pageSkips = totalPages < amount ? amount : Math . ceil ( totalPages / amount )
204
+
205
+ // Send all the requests at the same time
206
+ const starsDates = (
207
+ await Promise . all (
208
+ [ ...new Array ( amount ) ] . map ( async ( _ , index ) => {
209
+ const page = index * pageSkips || 1
210
+ return getRepoStargazers ( owner , name , page ) . then ( res => res . data )
211
+ } ) ,
212
+ )
213
+ )
214
+ . flatMap ( p => p )
215
+ . reduce ( ( acc : any , stars : any ) => {
216
+ const yearMonth = stars . starred_at . split ( "T" ) [ 0 ]
217
+ acc [ yearMonth ] = ( acc [ yearMonth ] || 0 ) + 1
218
+ return acc
219
+ } , { } )
220
+
221
+ console . log ( starsDates )
222
+
223
+ // how many stars did we find from a total of `requestAmount` requests?
224
+ const foundStars = Object . keys ( starsDates ) . reduce ( ( all , current ) => all + starsDates [ current ] , 0 )
225
+
226
+ // Find the earliest date
227
+ const lowestMonthYear = Object . keys ( starsDates ) . reduce ( ( lowest , current ) => {
228
+ const currentDate = parseISO ( current . split ( "T" ) [ 0 ] )
229
+ if ( isAfter ( currentDate , lowest ) ) {
230
+ return currentDate
231
+ }
232
+ return lowest
233
+ } , parseISO ( new Date ( ) . toISOString ( ) ) )
234
+
235
+ // Count dates until today
236
+ const splitDate = differenceInDays ( new Date ( ) , lowestMonthYear ) + 1
237
+
238
+ // Create an array with the amount of stars we didn't find
239
+ const array = [ ...new Array ( totalStars - foundStars ) ]
240
+
241
+ // Set the amount of value to add proportionally for each day
242
+ const splitStars : any [ ] [ ] = [ ]
243
+ for ( let i = splitDate ; i > 0 ; i -- ) {
244
+ splitStars . push ( array . splice ( 0 , Math . ceil ( array . length / i ) ) )
245
+ }
246
+
247
+ // Calculate the amount of stars for each day
248
+ return [ ...new Array ( splitDate ) ] . map ( ( _ , index , arr ) => {
249
+ const yearMonthDay = format ( add ( lowestMonthYear , { days : index } ) , "yyyy-MM-dd" )
250
+ const value = starsDates [ yearMonthDay ] || 0
251
+ return {
252
+ stars : value + splitStars [ index ] . length ,
253
+ date : {
254
+ month : + format ( add ( lowestMonthYear , { days : index } ) , "M" ) ,
255
+ year : + format ( add ( lowestMonthYear , { days : index } ) , "yyyy" ) ,
256
+ day : + format ( add ( lowestMonthYear , { days : index } ) , "d" ) ,
257
+ } ,
258
+ }
259
+ } )
260
+ }
0 commit comments