1- import { siteConfig } from '../lib/site'
21import { execFileSync } from 'node:child_process'
32import path from 'node:path'
3+ import process from 'node:process'
4+ import { siteConfig } from '../lib/site'
45
56interface GitHubContributor {
67 readonly avatar_url : string
@@ -44,10 +45,12 @@ const CONTRIBUTORS_PER_PAGE = 100
4445const MAX_CONTRIBUTOR_PAGES = 10
4546const CONTRIBUTORS_REVALIDATE_SECONDS = 60 * 60 * 12
4647const REPO_ROOT = path . resolve ( process . cwd ( ) , '..' )
48+ const LEADING_SLASHES_PATTERN = / ^ \/ + /
49+ const CO_AUTHOR_PREFIX = 'co-authored-by:'
4750
4851function getRepoCoordinates ( repoUrl : string ) {
4952 const url = new URL ( repoUrl )
50- const [ owner , repo ] = url . pathname . replace ( / ^ \/ + / , '' ) . split ( '/' )
53+ const [ owner , repo ] = url . pathname . replace ( LEADING_SLASHES_PATTERN , '' ) . split ( '/' )
5154
5255 if ( ! owner || ! repo ) {
5356 throw new Error ( `Invalid GitHub repository URL: ${ repoUrl } ` )
@@ -58,10 +61,13 @@ function getRepoCoordinates(repoUrl: string) {
5861
5962function getGitHubHeaders ( ) {
6063 const token = process . env . GITHUB_TOKEN ?? process . env . GH_TOKEN
64+ const authorization = token != null && token !== ''
65+ ? { Authorization : `Bearer ${ token } ` }
66+ : { }
6167
6268 return {
6369 Accept : 'application/vnd.github+json' ,
64- ...( token ? { Authorization : `Bearer ${ token } ` } : { } )
70+ ...authorization
6571 }
6672}
6773
@@ -117,20 +123,36 @@ async function fetchGitHubUser(login: string): Promise<ResolvedGitHubUser | null
117123}
118124
119125function parseCoAuthors ( message : string ) {
120- const matches = message . matchAll ( / ^ C o - a u t h o r e d - b y : \s + ( .+ ?) \s + < ( .+ ?) > $ / gim)
121126 const coAuthors : CoAuthorIdentity [ ] = [ ]
122127
123- for ( const match of matches ) {
124- const [ , name , email ] = match
128+ for ( const rawLine of message . split ( '\n' ) ) {
129+ const line = rawLine . trim ( )
125130
126- if ( ! name || ! email ) {
131+ if ( ! line . toLowerCase ( ) . startsWith ( CO_AUTHOR_PREFIX ) ) {
132+ continue
133+ }
134+
135+ const footer = line . slice ( CO_AUTHOR_PREFIX . length ) . trim ( )
136+ const openAngleBracketIndex = footer . lastIndexOf ( '<' )
137+ const closeAngleBracketIndex = footer . endsWith ( '>' )
138+ ? footer . length - 1
139+ : - 1
140+
141+ if ( openAngleBracketIndex <= 0 || closeAngleBracketIndex <= openAngleBracketIndex ) {
142+ continue
143+ }
144+
145+ const name = footer . slice ( 0 , openAngleBracketIndex ) . trim ( )
146+ const email = footer . slice ( openAngleBracketIndex + 1 , closeAngleBracketIndex ) . trim ( )
147+
148+ if ( name === '' || email === '' ) {
127149 continue
128150 }
129151
130152 coAuthors . push ( {
131153 count : 1 ,
132- email : email . trim ( ) ,
133- name : name . trim ( )
154+ email,
155+ name
134156 } )
135157 }
136158
@@ -167,7 +189,7 @@ function getCoAuthorSearchQueries(identity: CoAuthorIdentity) {
167189
168190 queries . push ( `${ identity . name } in:login` )
169191
170- return Array . from ( new Set ( queries ) )
192+ return [ ... new Set ( queries ) ]
171193}
172194
173195function getKnownCoAuthorProfile ( identity : CoAuthorIdentity ) : KnownCoAuthorProfile | null {
@@ -252,7 +274,7 @@ async function getCoAuthors() {
252274 }
253275 }
254276
255- return Array . from ( coAuthors . values ( ) ) . sort ( ( left , right ) => right . count - left . count )
277+ return [ ... coAuthors . values ( ) ] . sort ( ( left , right ) => right . count - left . count )
256278}
257279
258280async function resolveCoAuthor ( identity : CoAuthorIdentity ) {
@@ -355,34 +377,33 @@ async function getContributorCards() {
355377 cards . set ( key , value )
356378 }
357379
358- return Array . from ( cards . values ( ) )
359- . map ( contributor => {
360- if ( contributor . htmlUrl === 'https://github.com/cursoragent' ) {
361- return {
362- ...contributor ,
363- kind : 'agent' as const ,
364- label : 'cursoragent'
365- }
380+ return Array . from ( cards . values ( ) , contributor => {
381+ if ( contributor . htmlUrl === 'https://github.com/cursoragent' ) {
382+ return {
383+ ...contributor ,
384+ kind : 'agent' as const ,
385+ label : 'cursoragent'
366386 }
387+ }
367388
368- if ( contributor . htmlUrl === 'https://github.com/anthropics-claude-code' ) {
369- return {
370- ...contributor ,
371- kind : 'agent' as const ,
372- label : 'Claude Code'
373- }
389+ if ( contributor . htmlUrl === 'https://github.com/anthropics-claude-code' ) {
390+ return {
391+ ...contributor ,
392+ kind : 'agent' as const ,
393+ label : 'Claude Code'
374394 }
395+ }
375396
376- if ( contributor . htmlUrl === 'https://github.com/windsurf' ) {
377- return {
378- ...contributor ,
379- kind : 'agent' as const ,
380- label : 'Windsurf'
381- }
397+ if ( contributor . htmlUrl === 'https://github.com/windsurf' ) {
398+ return {
399+ ...contributor ,
400+ kind : 'agent' as const ,
401+ label : 'Windsurf'
382402 }
403+ }
383404
384- return contributor
385- } )
405+ return contributor
406+ } )
386407 . sort ( ( left , right ) => right . sortWeight - left . sortWeight )
387408}
388409
0 commit comments