@@ -2,202 +2,101 @@ import * as fs from 'node:fs';
2
2
import * as path from 'node:path' ;
3
3
4
4
import fetch from 'cross-fetch' ;
5
- import prettier from 'prettier' ;
6
5
7
6
import { PACKAGES_WEBSITE } from './paths.mts' ;
8
7
9
- const graphqlEndpoint = 'https://api.opencollective.com/graphql/v2' ;
10
-
11
- const queries = {
12
- account : `{
13
- account(slug: "typescript-eslint") {
14
- orders(status: ACTIVE, limit: 1000) {
15
- totalCount
16
- nodes {
17
- tier {
18
- slug
19
- }
20
- fromAccount {
21
- id
22
- imageUrl
23
-
8000
name
24
- website
25
- }
26
- }
27
- }
28
- }
29
- }` ,
30
- collective : `{
31
- collective(slug: "typescript-eslint") {
32
- members(limit: 1000, role: BACKER) {
33
- nodes {
34
- account {
35
- id
36
- imageUrl
37
- name
38
- website
39
- }
40
- tier {
41
- amount {
42
- valueInCents
43
- }
44
- orders(limit: 100) {
45
- nodes {
46
- amount {
47
- valueInCents
48
- }
49
- }
50
- }
51
- }
52
- totalDonations {
53
- valueInCents
54
- }
55
- updatedAt
56
- }
57
- }
58
- }
59
- }` ,
60
- } ;
61
-
62
- interface AccountData {
63
- orders : {
64
- nodes : OrderNode [ ] ;
65
- } ;
66
- }
67
-
68
- interface OrderNode {
69
- fromAccount : MemberAccount ;
70
- tier ?: Tier ;
71
- }
72
-
73
- interface Tier {
74
- slug : string ;
75
- }
76
-
77
- interface CollectiveData {
78
- members : {
79
- nodes : MemberNode [ ] ;
8
+ type MemberNodes = {
9
+ account : {
10
+ id : string ;
11
+ imageUrl : string ;
12
+ name : string ;
13
+ website : string ;
80
14
} ;
81
- }
82
-
83
- interface MemberNode {
84
- account : MemberAccount ;
85
- totalDonations : {
86
- valueInCents : number ;
87
- } ;
88
- }
89
-
90
- interface MemberAccount {
91
- id : string ;
92
- imageUrl : string ;
93
- name : string ;
94
- website : string ;
95
- }
15
+ totalDonations : { valueInCents : number } ;
16
+ } [ ] ;
96
17
97
18
const excludedNames = new Set ( [
98
- 'Guest' , // Apparent anonymous donor equivalent without an avatar
99
19
'Josh Goldberg' , // Team member 💖
100
20
] ) ;
101
21
102
22
const filteredTerms = [ 'casino' , 'deepnude' , 'tiktok' ] ;
103
23
104
- async function requestGraphql < Data > ( key : keyof typeof queries ) : Promise < Data > {
105
- const response = await fetch ( graphqlEndpoint , {
106
- method : 'POST' ,
107
- headers : { 'Content-Type' : 'application/json' } ,
108
- body : JSON . stringify ( { query : queries [ key ] } ) ,
109
- } ) ;
110
-
111
- const { data } = ( await response . json ( ) ) as {
112
- data : Record < typeof key , unknown > ;
113
- } ;
114
- return data [ key ] as Data ;
115
- }
116
-
117
- async function main ( ) : Promise < void > {
118
- const [ account , collective ] = await Promise . all ( [
119
- requestGraphql < AccountData > ( 'account' ) ,
120
- requestGraphql < CollectiveData > ( 'collective' ) ,
121
- ] ) ;
122
-
123
- const accountsById = account . orders . nodes . reduce <
124
- Record < string , MemberAccount >
125
- > ( ( accumulator , account ) => {
126
- const name = account . fromAccount . name || account . fromAccount . id ;
127
- accumulator [ name ] = {
128
- ...accumulator [ name ] ,
129
- ...account . fromAccount ,
130
- } ;
131
- return accumulator ;
132
- } , { } ) ;
133
-
134
- const totalDonationsById = collective . members . nodes . reduce <
135
- Record < string , number >
136
- > ( ( accumulator , member ) => {
137
- const name = member . account . name || member . account . id ;
138
- accumulator [ name ] ||= 0 ;
139
- accumulator [ name ] += member . totalDonations . valueInCents ;
140
- return accumulator ;
141
- } , { } ) ;
142
-
143
- const uniqueNames = new Set < string > ( excludedNames ) ;
144
- const allSponsorsConfig = collective . members . nodes
145
- . map ( member => {
146
- const name = member . account . name || member . account . id ;
147
- const fromAccount = {
148
- ...member . account ,
149
- ...accountsById [ name ] ,
150
- } ;
151
- const totalDonations = totalDonationsById [ name ] ;
152
- const website = fromAccount . website ;
153
-
154
- return {
155
- id : name ,
156
- image : fromAccount . imageUrl ,
157
- name : fromAccount . name ,
158
- totalDonations,
159
- website,
160
- } ;
24
+ const { members } = (
25
+ ( await (
26
+ await fetch ( 'https://api.opencollective.com/graphql/v2' , {
27
+ method : 'POST' ,
28
+ headers : { 'Content-Type' : 'application/json' } ,
29
+ body : JSON . stringify ( {
30
+ query : `
31
+ {
32
+ collective(slug: "typescript-eslint") {
33
+ members(limit: 1000, role: BACKER) {
34
+ nodes {
35
+ account {
36
+ id
37
+ imageUrl
38
+ name
39
+ website
40
+ }
41
+ tier {
42
+ amount {
43
+ valueInCents
44
+ }
45
+ orders(limit: 100) {
46
+ nodes {
47
+ amount {
48
+ valueInCents
49
+ }
50
+ }
51
+ }
52
+ }
53
+ totalDonations {
54
+ valueInCents
55
+ }
56
+ updatedAt
57
+ }
58
+ }
59
+ }
60
+ }
61
+ ` ,
62
+ } ) ,
161
63
} )
162
- . filter ( ( { id, name, totalDonations, website } ) => {
163
- if (
64
+ ) . json ( ) ) as { data : { collective : { members : { nodes : MemberNodes } } } }
65
+ ) . data . collective ;
66
+
67
+ const sponsors = (
68
+ Object . entries (
69
+ Object . groupBy ( members . nodes , ( { account } ) => account . name || account . id ) ,
70
+ // When using `Object.entries` to iterate the result of `Object.groupBy`, we do not get any `undefined`s
71
+ ) as [ string , MemberNodes ] [ ]
72
+ )
73
+ . map ( ( [ id , members ] ) => {
74
+ const [ { account } ] = members ;
75
+ return {
76
+ id,
77
+ image : account . imageUrl ,
78
+ name : account . name ,
79
+ totalDonations : members . reduce (
80
+ ( sum , { totalDonations } ) => sum + totalDonations . valueInCents ,
81
+ 0 ,
82
+ ) ,
83
+ website : account . website ,
84
+ } ;
85
+ } )
86
+ . filter (
87
+ ( { id, name, totalDonations, website } ) =>
88
+ ! (
164
89
filteredTerms . some ( filteredTerm =>
165
90
name . toLowerCase ( ) . includes ( filteredTerm ) ,
166
91
) ||
167
- uniqueNames . has ( id ) ||
92
+ excludedNames . has ( id ) ||
168
93
totalDonations < 10000 ||
169
94
! website
170
- ) {
171
- return false ;
172
- }
173
-
174
- uniqueNames . add ( id ) ;
175
- return true ;
176
- } )
177
- . sort ( ( a , b ) => b . totalDonations - a . totalDonations ) ;
178
-
179
- const rcPath = path . join ( PACKAGES_WEBSITE , 'data' , 'sponsors.json' ) ;
180
- fs . writeFileSync ( rcPath , await stringifyObject ( rcPath , allSponsorsConfig ) ) ;
181
- }
182
-
183
- async function stringifyObject (
184
- filePath : string ,
185
- data : unknown ,
186
- ) : Promise < string > {
187
- const config = await prettier . resolveConfig ( filePath ) ;
188
- const text = JSON . stringify (
189
- data ,
190
- ( _ , value : unknown ) => value ?? undefined ,
191
- 2 ,
192
- ) ;
193
-
194
- return await prettier . format ( text , {
195
- ...config ,
196
- parser : 'json' ,
197
- } ) ;
198
- }
199
-
200
- main ( ) . catch ( ( error : unknown ) => {
201
- console . error ( error ) ;
202
- process . exitCode = 1 ;
203
- } ) ;
95
+ ) ,
96
+ )
97
+ . sort ( ( a , b ) => b . totalDonations - a . totalDonations ) ;
98
+
99
+ fs . writeFileSync (
100
+ path . join ( PACKAGES_WEBSITE , 'data' , 'sponsors.json' ) ,
101
+ `${ JSON . stringify ( sponsors , null , 2 ) } \n` ,
102
+ ) ;
0 commit comments