diff --git a/packages/vitepress-plugin-git-changelog/src/vite/helpers.test.ts b/packages/vitepress-plugin-git-changelog/src/vite/helpers.test.ts index c336fe07..f5759d1f 100644 --- a/packages/vitepress-plugin-git-changelog/src/vite/helpers.test.ts +++ b/packages/vitepress-plugin-git-changelog/src/vite/helpers.test.ts @@ -6,11 +6,11 @@ import { defaultReleaseTagURLHandler, defaultReleaseTagsURLHandler, findMapAuthorByEmail, + findMapAuthorByGitHub, findMapAuthorByName, findMapAuthorLink, - getAvatarFromGithubNoreplyAddress, getCoAuthors, - getProfileUrlFromGithubNoreplyAddress, + getGitHubUserNameFromNoreplyAddress, mergeRawCommits, newAvatarForAuthor, parseCommitAuthors, @@ -333,7 +333,8 @@ describe('parseCommitAuthors', () => { { name: 'First Last', email: 'user@users.noreply.github.com', - avatarUrl: 'https://avatars.githubusercontent.com/user?size=80', + i18n: undefined, + avatarUrl: 'https://github.com/user.png', url: 'https://github.com/user', }, ]) @@ -385,6 +386,37 @@ describe('findMapAuthorByEmail', () => { }) }) +describe('findMapAuthorByGitHub', () => { + it('should return the commit author for GitHub noreply email with username in mapAuthors', async () => { + const creators: Contributor[] = [ + { + name: 'John', + username: 'user', + }, + ] + const creator = await findMapAuthorByGitHub(creators, 'John', '123456+user@users.noreply.github.com') + + expect(creator).toEqual(creators[0]) + }) + + it('should return the commit author for GitHub noreply email without username in mapAuthors', async () => { + const creators: Contributor[] = [ + { + name: 'user', + }, + ] + const creator = await findMapAuthorByGitHub(creators, 'user', '123456+user@users.noreply.github.com') + + expect(creator).toEqual({ name: 'user', username: 'user' }) + }) + + it('should return the commit author avatar for GitHub noreply email without mapAuthor', async () => { + const avatar = await findMapAuthorByGitHub(undefined, 'John', '123456+user@users.noreply.github.com') + + expect(avatar).toEqual({ name: 'John', username: 'user' }) + }) +}) + describe('findMapAuthorByName', () => { it('should return the registered creator by name', () => { const creators: Contributor[] = [ @@ -559,57 +591,25 @@ describe('newAvatarForAuthor', () => { expect(avatar).toEqual('https://gravatar.com/avatar/b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514?d=retro') }) - - it('should return the commit author avatar for GitHub noreply email without user ID', async () => { - const avatar = await newAvatarForAuthor(undefined, 'user@users.noreply.github.com') - - expect(avatar).toEqual('https://avatars.githubusercontent.com/user?size=80') - }) - - it('should return the commit author avatar for GitHub noreply email with user ID', async () => { - const avatar = await newAvatarForAuthor(undefined, '123456+user@users.noreply.github.com') - - expect(avatar).toEqual('https://avatars.githubusercontent.com/u/123456?size=80') - }) -}) - -describe('getAvatarFromGithubNoreplyAddress', () => { - it('should return undefined for email it cannot handle', async () => { - const avatar = await getAvatarFromGithubNoreplyAddress('user@example.com') - - expect(avatar).toEqual(undefined) - }) - - it('should return the commit author avatar for GitHub noreply email without user ID', async () => { - const avatar = await getAvatarFromGithubNoreplyAddress('user@users.noreply.github.com') - - expect(avatar).toEqual('https://avatars.githubusercontent.com/user?size=80') - }) - - it('should return the commit author avatar for GitHub noreply email with user ID', async () => { - const avatar = await getAvatarFromGithubNoreplyAddress('123456+user@users.noreply.github.com') - - expect(avatar).toEqual('https://avatars.githubusercontent.com/u/123456?size=80') - }) }) -describe('getProfileUrlFromGithubNoreplyAddress', () => { +describe('getGitHubFromGithubNoreplyAddress', () => { it('should return undefined for email it cannot handle', async () => { - const avatar = await getProfileUrlFromGithubNoreplyAddress('user@example.com') + const result = await getGitHubUserNameFromNoreplyAddress('user@example.com') - expect(avatar).toEqual(undefined) + expect(result).toEqual(undefined) }) - it('should return the GitHub profile URL for GitHub noreply email without user ID', async () => { - const avatar = await getProfileUrlFromGithubNoreplyAddress('user@users.noreply.github.com') + it('should return the commit author for GitHub noreply email without user ID', async () => { + const result = await getGitHubUserNameFromNoreplyAddress('user@users.noreply.github.com') - expect(avatar).toEqual('https://github.com/user') + expect(result).toEqual({ userId: undefined, userName: 'user' }) }) - it('should return the GitHub profile URL for GitHub noreply email with user ID', async () => { - const avatar = await getProfileUrlFromGithubNoreplyAddress('123456+user@users.noreply.github.com') + it('should return the commit author for GitHub noreply email with user ID', async () => { + const result = await getGitHubUserNameFromNoreplyAddress('123456+user@users.noreply.github.com') - expect(avatar).toEqual('https://github.com/user') + expect(result).toEqual({ userId: '123456', userName: 'user' }) }) }) diff --git a/packages/vitepress-plugin-git-changelog/src/vite/helpers.ts b/packages/vitepress-plugin-git-changelog/src/vite/helpers.ts index 4e419d24..1cc2df53 100644 --- a/packages/vitepress-plugin-git-changelog/src/vite/helpers.ts +++ b/packages/vitepress-plugin-git-changelog/src/vite/helpers.ts @@ -377,8 +377,15 @@ export async function parseCommitAuthors(commit: MergedRawCommit, mapContributor author.avatarUrl = await newAvatarForAuthor(targetCreatorByEmail, author.email!) return author } + const targetCreatorByGitHub = findMapAuthorByGitHub(mapContributors, author.name, author.email!) + if (targetCreatorByGitHub) { + author.name = targetCreatorByGitHub.name ?? author.name + author.i18n = findMapAuthorI18n(targetCreatorByGitHub) + author.url = findMapAuthorLink(targetCreatorByGitHub) + author.avatarUrl = await newAvatarForAuthor(targetCreatorByGitHub, author.email!) + return author + } author.avatarUrl = await newAvatarForAuthor(undefined, author.email!) - author.url ||= getProfileUrlFromGithubNoreplyAddress(author.email) return author })) } @@ -410,7 +417,7 @@ export function getCoAuthors(body?: string): CommitAuthor[] { export function findMapAuthorByName(mapContributors: Contributor[] | undefined, author_name: string) { return mapContributors?.find((item) => { - const res = (item.mapByNameAliases && Array.isArray(item.mapByNameAliases) && item.mapByNameAliases.includes(author_name)) || item.name === author_name + const res = (item.mapByNameAliases && Array.isArray(item.mapByNameAliases) && item.mapByNameAliases.includes(author_name)) || item.name === author_name || item.username === author_name if (res) return true @@ -430,6 +437,21 @@ export function findMapAuthorByEmail(mapContributors: Contributor[] | undefined, }) } +export function findMapAuthorByGitHub(mapContributors: Contributor[] | undefined, author_name: string, author_email: string) { + const github = getGitHubUserNameFromNoreplyAddress(author_email) + if (github && github.userName) { + const mappedByName = findMapAuthorByName(mapContributors, github.userName) + if (mappedByName && mappedByName.username) { + mappedByName.username ||= github.userName + return mappedByName + } + return { + name: author_name, + username: github.userName, + } + } +} + export function findMapAuthorLink(creator: Contributor): string | undefined { if (!creator.links && !!creator.username) return `https://github.com/${creator.username}` @@ -457,32 +479,13 @@ export function findMapAuthorI18n(mappedAuthor: Contributor): Record\d+)\+)?(?[a-zA-Z\d-]{1,39})@users.noreply.github.com$/) if (!match || !match.groups) return undefined const { userName, userId } = match.groups - return `https://avatars.githubusercontent.com/${userId ? `u/${userId}` : userName}?size=${size}` -} - -export function getProfileUrlFromGithubNoreplyAddress(email: string | undefined): string | undefined { - if (!email) - return undefined - - const match = email.match(/^(?:(?\d+)\+)?(?[a-zA-Z\d-]{1,39})@users.noreply.github.com$/) - if (!match || !match.groups) - return undefined - - const { userName } = match.groups - - if (!userName) - return undefined - - return `https://github.com/${userName}` + return { userId, userName } } export async function newAvatarForAuthor(mappedAuthor: Contributor | undefined, email: string): Promise { @@ -493,9 +496,5 @@ export async function newAvatarForAuthor(mappedAuthor: Contributor | undefined, return `https://github.com/${mappedAuthor.username}.png` } - const githubProfilePicture = getAvatarFromGithubNoreplyAddress(email) - if (githubProfilePicture != null) - return githubProfilePicture - return `https://gravatar.com/avatar/${await digestStringAsSHA256(email)}?d=retro` }