@@ -31,6 +31,26 @@ export interface ContributorData {
3131 location : string | null ;
3232}
3333
34+ let didWarnGitHubRateLimit = false ;
35+
36+ /**
37+ * Profile values that should not count as an employer.
38+ */
39+ export function isUnavailableSponsorValue ( value : string | null ) : boolean {
40+ if ( ! value ) {
41+ return true ;
42+ }
43+
44+ const normalized = value . toLowerCase ( ) . trim ( ) ;
45+ return [
46+ 'n/a' ,
47+ 'na' ,
48+ 'none' ,
49+ '-' ,
50+ 'not applicable' ,
51+ ] . includes ( normalized ) ;
52+ }
53+
3454/**
3555 * Delay helper for rate limiting.
3656 */
@@ -64,12 +84,12 @@ export function resolveWporgUsername(
6484) : string {
6585 const githubLower = githubUsername . toLowerCase ( ) ;
6686
67- // Check if we have a mapping for this GitHub username
87+ // Use the mapping when GitHub and WP.org usernames differ.
6888 if ( mapping && mapping . githubToWporg [ githubLower ] ) {
6989 return mapping . githubToWporg [ githubLower ] ;
7090 }
7191
72- // No mapping found - assume GitHub username = WP.org username
92+ // Most contributors use the same username on both sites.
7393 return githubUsername ;
7494}
7595
@@ -89,22 +109,33 @@ export async function fetchContributorData(
89109 // Fetch WP.org profile
90110 const wpProfile = await fetchWPOrgProfile ( wporgUsername ) ;
91111
92- let sponsor = wpProfile . employer || null ;
112+ let sponsor = isUnavailableSponsorValue ( wpProfile . employer )
113+ ? null
114+ : wpProfile . employer || null ;
93115 let location = wpProfile . location || null ;
94116
95- // Check GitHub for missing sponsor OR missing location
117+ // Use GitHub to fill whichever profile field WP.org did not provide.
96118 if ( ! sponsor || ! location ) {
97119 const ghUsername = wpProfile . wporgLinkedGitHubUsername || githubUsername ;
98120 try {
99121 const ghProfile = await fetchGitHubUserProfile ( ghUsername ) ;
100122 if ( ! sponsor && ghProfile ?. company ) {
101- sponsor = ghProfile . company . replace ( / ^ @ / , '' ) . trim ( ) || null ;
123+ const company = ghProfile . company . replace ( / ^ @ / , '' ) . trim ( ) ;
124+ sponsor = isUnavailableSponsorValue ( company ) ? null : company ;
102125 }
103126 if ( ! location && ghProfile ?. location ) {
104127 location = ghProfile . location ;
105128 }
106- } catch {
107- if ( verbose ) {
129+ } catch ( error ) {
130+ const message = error instanceof Error ? error . message : String ( error ) ;
131+ if ( message . includes ( 'rate limit exceeded' ) ) {
132+ if ( ! didWarnGitHubRateLimit ) {
133+ console . warn (
134+ `\n ⚠️ GitHub profile fallback hit the rate limit. Set GITHUB_TOKEN and rerun if you want fewer Unknown contributors. ${ message } `
135+ ) ;
136+ didWarnGitHubRateLimit = true ;
137+ }
138+ } else if ( verbose ) {
108139 console . error ( `\n Warning: GitHub fetch failed for ${ ghUsername } ` ) ;
109140 }
110141 }
@@ -133,7 +164,7 @@ export async function fetchContributorProfiles(
133164 existingData || [ ]
134165 ) ;
135166
136- // Filter to usernames we don't already have
167+ // Skip profiles already fetched in this run.
137168 const toFetch = usernames . filter (
138169 ( u ) => ! contributorDataMap . has ( u . toLowerCase ( ) )
139170 ) ;
0 commit comments