Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit3664c20

Browse files
chore(website): account forthanks.dev donors in sponsors list (typescript-eslint#10172)
* clean up* finish* extract extra variables* add knip ignore* remove unnecessary nullish coalesce* Update tools/scripts/generate-sponsors.mts---------Co-authored-by: Josh Goldberg ✨ <git@joshuakgoldberg.com>
1 parentf034c17 commit3664c20

File tree

5 files changed

+161
-76
lines changed

5 files changed

+161
-76
lines changed

‎knip.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ export default {
2626
'make-dir',
2727
'ncp',
2828
'tmp',
29+
// imported for type purposes only
30+
'website',
2931
],
3032
entry:['tools/release/changelog-renderer.js','tools/scripts/**/*.mts'],
3133
},

‎packages/website/data/sponsors.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,27 @@
132132
"totalDonations":70000,
133133
"website":"https://www.codiga.io"
134134
},
135+
{
136+
"id":"frontendmasters",
137+
"image":"https://avatars.githubusercontent.com/u/5613852?v=4",
138+
"name":"Frontend Masters",
139+
"totalDonations":63327,
140+
"website":"https://FrontendMasters.com"
141+
},
135142
{
136143
"id":"DeepSource",
137144
"image":"https://images.opencollective.com/deepsource/0f18cea/logo.png",
138145
"name":"DeepSource",
139146
"totalDonations":60000,
140147
"website":"https://deepsource.io/"
141148
},
149+
{
150+
"id":"syntaxfm",
151+
"image":"https://avatars.githubusercontent.com/u/130389858?v=4",
152+
"name":"Syntax",
153+
"totalDonations":54449,
154+
"website":"https://syntax.fm"
155+
},
142156
{
143157
"id":"Future Processing",
144158
"image":"https://images.opencollective.com/future-processing/1410d26/logo.png",
@@ -293,6 +307,13 @@
293307
"totalDonations":17000,
294308
"website":"https://balsa.com/"
295309
},
310+
{
311+
"id":"codecov",
312+
"image":"https://avatars.githubusercontent.com/u/8226205?v=4",
313+
"name":"Codecov",
314+
"totalDonations":15292,
315+
"website":"https://codecov.io/"
316+
},
296317
{
297318
"id":"THE PADDING",
298319
"image":"https://images.opencollective.com/thepadding/55e79ad/logo.png",
@@ -314,6 +335,13 @@
314335
"totalDonations":14500,
315336
"website":"https://now4real.com/"
316337
},
338+
{
339+
"id":"getsentry",
340+
"image":"https://avatars.githubusercontent.com/u/1396951?v=4",
341+
"name":"Sentry",
342+
"totalDonations":14436,
343+
"website":"https://sentry.io"
344+
},
317345
{
318346
"id":"Knowledge Work",
319347
"image":"https://images.opencollective.com/knowledge-work/f91b72d/logo.png",

‎packages/website/src/components/FinancialContributors/Sponsor.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function Sponsor({
2727
return(
2828
<Link
2929
className={styles.sponsorLink}
30-
href={sponsor.website??undefined}
30+
href={sponsor.website}
3131
title={sponsor.name}
3232
rel="noopener sponsored"
3333
>
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
exportinterfaceSponsorData{
2-
description?:string;
32
id:string;
43
image:string;
54
name:string;
6-
tier?:string;
75
totalDonations:number;
8-
website?:string;
6+
website:string;
97
}

‎tools/scripts/generate-sponsors.mts

Lines changed: 129 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2,98 +2,155 @@ import * as fs from 'node:fs';
22
import*aspathfrom'node:path';
33

44
importfetchfrom'cross-fetch';
5+
importtype{SponsorData}from'website/src/components/FinancialContributors/types.ts';
56

67
import{PACKAGES_WEBSITE}from'./paths.mts';
78

8-
typeMemberNodes={
9-
account:{
10-
id:string;
11-
imageUrl:string;
12-
name:string;
13-
website:string;
14-
};
15-
totalDonations:{valueInCents:number};
16-
}[];
17-
189
constexcludedNames=newSet([
1910
'Josh Goldberg',// Team member 💖
2011
]);
2112

2213
constfilteredTerms=['casino','deepnude','tiktok'];
2314

24-
const{ members}=(
25-
(await(
26-
awaitfetch('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-
}
15+
constjsonApiFetch=async<T,>(
16+
api:string,
17+
options?:RequestInit,
18+
):Promise<T>=>{
19+
consturl=`https://api.${api}`;
20+
constresponse=awaitfetch(url,options);
21+
if(!response.ok){
22+
console.error({
23+
url,
24+
response:{status:response.status,body:awaitresponse.text()},
25+
});
26+
thrownewError('API call failed.');
27+
}
28+
return(awaitresponse.json())asT;
29+
};
30+
31+
constopenCollectiveSponsorsPromise=jsonApiFetch<{
32+
data:{
33+
collective:{
34+
members:{
35+
nodes:{
36+
account:{
37+
id:string;
38+
imageUrl:string;
39+
name:string;
40+
website:string|null;
41+
};
42+
totalDonations:{valueInCents:number};
43+
}[];
44+
};
45+
};
46+
};
47+
}>('opencollective.com/graphql/v2',{
48+
method:'POST',
49+
headers:{'Content-Type':'application/json'},
50+
body:JSON.stringify({
51+
query:`
52+
{
53+
collective(slug: "typescript-eslint") {
54+
members(limit: 1000, role: BACKER) {
55+
nodes {
56+
account {
57+
id
58+
imageUrl
59+
name
60+
website
61+
}
62+
totalDonations {
63+
valueInCents
5864
}
5965
}
6066
}
61-
`,
62-
}),
63-
})
64-
).json())as{data:{collective:{members:{nodes:MemberNodes}}}}
65-
).data.collective;
67+
}
68+
}
69+
`,
70+
}),
71+
}).then(({ data})=>{
72+
// TODO: remove polyfill in Node 22
73+
constgroupBy=<T,>(
74+
arr:T[],
75+
fn:(item:T)=>string,
76+
):Record<string,T[]>=>{
77+
constgrouped:Record<string,T[]>={};
78+
for(constitemofarr){
79+
(grouped[fn(item)]??=[]).push(item);
80+
}
81+
returngrouped;
82+
};
83+
returnObject.entries(
84+
groupBy(
85+
data.collective.members.nodes,
86+
({ account})=>account.name||account.id,
87+
),
88+
).flatMap(([id,members])=>{
89+
const[
90+
{
91+
account:{ website, ...account},
92+
},
93+
]=members;
94+
returnwebsite
95+
?{
96+
id,
97+
image:account.imageUrl,
98+
name:account.name,
99+
totalDonations:members.reduce(
100+
(sum,{ totalDonations})=>sum+totalDonations.valueInCents,
101+
0,
102+
),
103+
website,
104+
}
105+
:[];
106+
});
107+
});
66108

67-
constsponsors=Object.entries(
68-
// TODO: use Object.groupBy in Node 22
69-
members.nodes.reduce<Record<string,MemberNodes>>((membersById,member)=>{
70-
const{ account}=member;
71-
(membersById[account.name||account.id]??=[]).push(member);
72-
returnmembersById;
73-
},{}),
109+
constthanksDevSponsorsPromise=jsonApiFetch<
110+
Record<'dependers'|'donors',['gh'|'gl',string,number][]>
111+
>('thanks.dev/v1/vip/dependee/gh/typescript-eslint').then(async({ donors})=>
112+
(
113+
awaitPromise.all(
114+
donors
115+
/* GitLab does not have an API to get a user's profile. At the time of writing, only 13% of donors
116+
from thanks.dev came from GitLab rather than GitHub, and none of them met the contribution
117+
threshold. */
118+
.filter(([site])=>site==='gh')
119+
.map(async([,id,totalDonations])=>{
120+
const{ name, ...github}=awaitjsonApiFetch<
121+
Record<'avatar_url'|'blog',string>&{
122+
name:string|null;
123+
}
124+
>(`github.com/users/${id}`);
125+
returnname
126+
?{
127+
id,
128+
image:github.avatar_url,
129+
name,
130+
totalDonations,
131+
website:github.blog||`https://github.com/${id}`,
132+
}
133+
:[];
134+
}),
135+
)
136+
).flat(),
137+
);
138+
139+
constsponsors=(
140+
awaitPromise.all<SponsorData[]>([
141+
openCollectiveSponsorsPromise,
142+
thanksDevSponsorsPromise,
143+
])
74144
)
75-
.map(([id,members])=>{
76-
const[{ account}]=members;
77-
return{
78-
id,
79-
image:account.imageUrl,
80-
name:account.name,
81-
totalDonations:members.reduce(
82-
(sum,{ totalDonations})=>sum+totalDonations.valueInCents,
83-
0,
84-
),
85-
website:account.website,
86-
};
87-
})
145+
.flat()
88146
.filter(
89-
({ id, name, totalDonations, website})=>
147+
({ id, name, totalDonations})=>
90148
!(
91149
filteredTerms.some(filteredTerm=>
92150
name.toLowerCase().includes(filteredTerm),
93151
)||
94152
excludedNames.has(id)||
95-
totalDonations<10000||
96-
!website
153+
totalDonations<10000
97154
),
98155
)
99156
.sort((a,b)=>b.totalDonations-a.totalDonations);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp