diff --git a/apps/site/authors.json b/apps/site/authors.json index 03624328fa640..9934f96eb6dad 100644 --- a/apps/site/authors.json +++ b/apps/site/authors.json @@ -1,322 +1,324 @@ { "Anna Henningsen": { - "id": "addaleax", + "id": 899444, "name": "Anna Henningsen", "website": "https://github.com/addaleax" }, "Antoine du Hamel": { - "id": "aduh95", + "id": 14309773, "name": "Antoine du Hamel", "website": "https://github.com/aduh95" }, "AugustinMauroy": { - "id": "AugustinMauroy", + "id": 97875033, "name": "Augustin Mauroy", "website": "https://github.com/AugustinMauroy" }, "Aviv Keller": { - "id": "avivkeller", + "id": 38299977, "name": "Aviv Keller", "website": "https://github.com/avivkeller" }, "Ben Noordhuis": { - "id": "bnoordhuis", + "id": 275871, "name": "Ben Noordhuis", "website": "https://github.com/bnoordhuis" }, "Bethany Nicolle Griggs": { - "id": "BethGriggs", + "id": 8297234, "name": "Bethany Nicolle Griggs", "website": "https://github.com/BethGriggs" }, "Brian Muenzenmeyer": { - "id": "bmuenzenmeyer", + "id": 298435, "name": "Brian Muenzenmeyer", "website": "https://github.com/bmuenzenmeyer" }, "Bryan English": { - "id": "bengl", + "id": 110455, "name": "Bryan English", "website": "https://github.com/bengl" }, "Carl Vitullo": { - "id": "vcarl", + "id": 1551487, "name": "Carl Vitullo", "website": "https://github.com/vcarl" }, "Charlie Robbins": { - "id": "indexzero", + "id": 4624, "name": "Charlie Robbins", "website": "https://github.com/indexzero" }, "Chengzhong Wu": { - "id": "legendecas", + "id": 8500303, "name": "Chengzhong Wu", "website": "https://github.com/legendecas" }, "Claudio Wunder": { - "id": "ovflowd", + "id": 12037269, "name": "Claudio Wunder", "website": "https://github.com/ovflowd" }, "Colin Ihrig": { - "id": "cjihrig", + "id": 2512748, "name": "Colin Ihrig", "website": "https://github.com/cjihrig" }, "Danielle Adams": { - "id": "danielleadams", + "id": 6271256, "name": "Danielle Adams", "website": "https://github.com/danielleadams" }, "Daniel Bevenius": { - "id": "danbev", + "id": 432351, "name": "Daniel Bevenius", - "webiste": "https://github.com/danbev" + "website": "https://github.com/danbev" }, "Dave Pacheco": { - "id": "dave-pacheco", + "id": 70340013, "name": "Dave Pacheco" }, "Domenic Denicola": { - "id": "domenic", + "id": 617481, "name": "Domenic Denicola", "website": "https://github.com/domenic" }, "Emelia Smith": { - "id": "thisismissem", + "id": 30827, "name": "Emelia Smith", "website": "https://github.com/thisismissem" }, "Emily Rose": { - "id": "emilyrose", + "id": 32688683, "name": "Emily Rose", "website": "https://github.com/emilyrose" }, "Evan Lucas": { - "id": "evanlucas", + "id": 677994, "name": "Evan Lucas", "website": "https://github.com/evanlucas" }, "flakey5": { - "id": "flakey5", + "id": 73616808, "name": "flakey5", "website": "https://github.com/flakey5" }, "Gibson Fahnestock": { - "id": "gibfahn", + "id": 15943089, "name": "Gibson Fahnestock", "website": "https://github.com/gibfahn" }, "Giovanny Gongora": { - "id": "Gioyik", + "id": 1834718, "name": "Giovanny Gongora", "website": "https://github.com/Gioyik" }, "Guilherme Araújo": { - "id": "araujogui", + "id": 68445491, "name": "Guilherme Araújo", "website": "https://github.com/araujogui" }, "Isaac Schlueter": { - "id": "isaacs", + "id": 9287, "name": "Isaac Schlueter", "website": "https://github.com/isaacs" }, "Italo Casas": { - "id": "italoacasas", + "id": 6354455, "name": "Italo Casas", "website": "https://github.com/italoacasas" }, "James M Snell": { - "id": "jasnell", + "id": 439929, "name": "James M Snell", "website": "https://github.com/jasnell" }, "Jeremiah Senkpiel": { - "id": "Fishrock123", + "id": 1093990, "name": "Jeremiah Senkpiel", "website": "https://github.com/Fishrock123" }, "Joe Sepi": { - "id": "joesepi", + "id": 143668, "name": "Joe Sepi", "website": "https://github.com/joesepi" }, "Joyee Cheung": { - "id": "joyeecheung", + "id": 4299420, "name": "Joyee Cheung", "website": "https://github.com/joyeecheung" }, "Juan José": { - "id": "juanarbol", + "id": 17013303, "name": "Juan José Arboleda", "website": "https://github.com/juanarbol" }, "Juan José Arboleda": { - "id": "juanarbol", + "id": 17013303, "name": "Juan José Arboleda", "website": "https://github.com/juanarbol" }, "Julián Duque": { - "id": "julianduque", + "id": 733877, "name": "Julián Duque", "website": "https://github.com/julianduque" }, "Node.js Releasers": { - "id": "nodejs", + "id": 9950313, "name": "Node.js Release Working Group", "website": "https://github.com/nodejs/release" }, "Node.js Technical Steering Committee": { - "id": "nodejs", + "id": 9950313, "name": "Node.js Technical Steering Committee", "website": "https://github.com/nodejs/tsc" }, "Matteo Collina": { - "id": "mcollina", + "id": 52195, "name": "Matteo Collina", - "webiste": "https://github.com/mcollina" + "website": "https://github.com/mcollina" }, "Marco Ippolito": { - "id": "marco-ippolito", + "id": 36735501, "name": "Marco Ippolito", "website": "https://github.com/marco-ippolito" }, "Matt Cowley": { - "id": "MattIPv4", + "id": 12371363, "name": "Matt Cowley", "website": "https://github.com/MattIPv4" }, "Michael Dawson": { - "id": "mhdawson", + "id": 9373002, "name": "Michael Dawson", "website": "https://github.com/mhdawson" }, "Michaël Zasso": { - "id": "targos", + "id": 2352663, "name": "Michaël Zasso", "website": "https://github.com/targos" }, "Mike Dolan": { - "id": "mike-dolan", - "name": "Mike Dolan" + "id": 7398917, + "name": "Mike Dolan", + "website": "https://github.com/mkdolan" }, "Mikeal Rogers": { - "id": "mikeal", + "id": 579, "name": "Mikeal Rogers", "website": "https://github.com/mikeal" }, "Minwoo Jung": { - "id": "minwoo-jung", + "id": 4557258, "name": "Minwoo Jung", "website": "https://github.com/minwoo-jung" }, "Myles Borins": { - "id": "mylesborins", + "id": 498775, "name": "Myles Borins", "website": "https://github.com/mylesborins" }, "piscisaureus": { - "id": "piscisaureus", + "id": 218257, "name": "Bert Belder", "website": "https://github.com/piscisaureus" }, "Rafael Gonzaga": { - "id": "rafaelgss", + "id": 26234614, "name": "Rafael Gonzaga", "website": "https://github.com/RafaelGSS" }, "Richard Lau": { - "id": "richardlau", + "id": 5445507, "name": "Richard Lau", "website": "https://github.com/richardlau" }, "richiemccoll": { - "id": "richiemccoll", + "id": 12698531, "name": "Richie McColl", "website": "https://github.com/richiemccoll" }, "Robin Bender Ginn": { - "id": "rginn", + "id": 4296937, "name": "Robin Bender Ginn", "website": "https://github.com/rginn" }, "Rod Vagg": { - "id": "rvagg", + "id": 495647, "name": "Rod Vagg", "website": "https://github.com/rvagg" }, "Ruben Bridgewater": { - "id": "BridgeAR", + "id": 8822573, "name": "Ruben Bridgewater", "website": "https://github.com/BridgeAR" }, "Ruy Adorno": { - "id": "ruyadorno", + "id": 220900, "name": "Ruy Adorno", "website": "https://github.com/ruyadorno" }, "Ryan Dahl": { - "id": "ry", + "id": 80, "name": "Ryan Dahl", "website": "https://github.com/ry" }, "Sam Roberts": { - "id": "sam-github", + "id": 17607, "name": "Sam Roberts", - "webiste": "https://github.com/sam-github" + "website": "https://github.com/sam-github" }, "Scott Hammond": { - "id": "scott-hammond", + "id": 95775645, "name": "Scott Hammond" }, "Shelley Vohr": { - "id": "codebytere", + "id": 2036040, "name": "Shelley Vohr", "website": "https://github.com/codebytere" }, "Stewart X Addison": { - "id": "sxa", + "id": 6487691, "name": "Stewart X Addison", "website": "https://github.com/sxa" }, "Steven Sinatra": { - "id": "diagramatics", + "id": 5153378, "name": "Steven Sinatra", "website": "https://github.com/diagramatics" }, "The Node.js Project": { - "id": "nodejs", + "id": 9950313, "name": "The Node.js Project", "website": "https://github.com/nodejs" }, "Tierney Cyren": { - "id": "bnb", + "id": 502396, "name": "Tierney Cyren", "website": "https://github.com/bnb" }, "Timothy J Fontaine": { - "id": "tjfontaine", + "id": 146447, "name": "Timothy J Fontaine", "website": "https://github.com/tjfontaine" }, "Tracy Hinds": { - "id": "tracy-hinds", - "name": "Tracy Hinds" + "id": 1981088, + "name": "Tracy Hinds", + "website": "https://github.com/hackygolucky" }, "Ulises Gascón": { - "id": "UlisesGascon", + "id": 5110813, "name": "Ulises Gascón", "website": "https://github.com/UlisesGascon" }, "Vladimir de Turckheim": { - "id": "vdeturckheim", + "id": 7135896, "name": "Vladimir de Turckheim", - "webiste": "https://github.com/vdeturckheim" + "website": "https://github.com/vdeturckheim" }, "Yosuke Furukawa": { - "id": "yosuke-furukawa", + "id": 555645, "name": "Yosuke Furukawa", "website": "https://github.com/yosuke-furukawa" } diff --git a/apps/site/types/author.ts b/apps/site/types/author.ts index c6867f8d71b5e..ae73fa4e6ff02 100644 --- a/apps/site/types/author.ts +++ b/apps/site/types/author.ts @@ -6,7 +6,7 @@ export type AuthorProps = { }; export type Author = { - id: string; + id: number; name: string; website?: string; }; diff --git a/apps/site/util/__tests__/author.test.mjs b/apps/site/util/__tests__/author.test.mjs index c0e4c40a347f6..3d3f9b0b495e0 100644 --- a/apps/site/util/__tests__/author.test.mjs +++ b/apps/site/util/__tests__/author.test.mjs @@ -52,18 +52,29 @@ describe('mapAuthorToCardAuthors', () => { describe('getAuthorWithId', () => { it('should return author details when author is found', () => { - const result = getAuthorWithId(['nodejs'], true); + const result = getAuthorWithId(['release'], true); assert.deepEqual(result, [ { name: 'Node.js Release Working Group', - nickname: 'nodejs', + nickname: 'release', fallback: 'NJRWG', url: 'https://github.com/nodejs/release', - image: 'https://avatars.githubusercontent.com/nodejs', + image: 'https://avatars.githubusercontent.com/u/9950313', }, ]); }); + it('keeps working groups that share an id as distinct bylines', () => { + const [releasers] = getAuthorWithId(['release'], true); + const [tsc] = getAuthorWithId(['tsc'], true); + + assert.equal(releasers.name, 'Node.js Release Working Group'); + assert.equal(tsc.name, 'Node.js Technical Steering Committee'); + // same org avatar, different bylines + assert.equal(releasers.image, tsc.image); + assert.notEqual(releasers.name, tsc.name); + }); + it('returns objects with GitHub avatars', () => { const result = getAuthorWithId(['someUser'], false); assert.ok(result[0].image.includes('github')); @@ -79,7 +90,7 @@ describe('getAuthorWithName', () => { nickname: 'nodejs', fallback: 'TNJP', url: 'https://github.com/nodejs', - image: 'https://avatars.githubusercontent.com/nodejs', + image: 'https://avatars.githubusercontent.com/u/9950313', }, ]); }); diff --git a/apps/site/util/__tests__/github.test.mjs b/apps/site/util/__tests__/github.test.mjs index 3d359863fc502..d79420326446c 100644 --- a/apps/site/util/__tests__/github.test.mjs +++ b/apps/site/util/__tests__/github.test.mjs @@ -11,8 +11,8 @@ const { getGitHubAvatarUrl, createGitHubSlugger, getGitHubBlobUrl } = describe('gitHubUtils', () => { it('getGitHubAvatarUrl returns the correct URL', () => { assert.equal( - getGitHubAvatarUrl('octocat'), - 'https://avatars.githubusercontent.com/octocat' + getGitHubAvatarUrl('583231'), + 'https://avatars.githubusercontent.com/u/583231' ); }); @@ -28,12 +28,26 @@ describe('gitHubUtils', () => { }); describe('getGitHubAvatarUrl', () => { - it('should return a valid GitHub avatar URL', () => { + it('should return a valid GitHub avatar URL by id', () => { assert.equal( - getGitHubAvatarUrl('octocat'), + getGitHubAvatarUrl('583231'), + 'https://avatars.githubusercontent.com/u/583231' + ); + }); + + it('should return the legacy username-based URL when useLegacyUrl is true', () => { + assert.equal( + getGitHubAvatarUrl('octocat', true), 'https://avatars.githubusercontent.com/octocat' ); }); + + it('should use the id-based URL when useLegacyUrl is false', () => { + assert.equal( + getGitHubAvatarUrl('583231', false), + 'https://avatars.githubusercontent.com/u/583231' + ); + }); }); describe('getGitHubBlobUrl', () => { diff --git a/apps/site/util/author.ts b/apps/site/util/author.ts index 2bb90c4721911..a87e8c7c61310 100644 --- a/apps/site/util/author.ts +++ b/apps/site/util/author.ts @@ -4,6 +4,10 @@ import { getAcronymFromString } from '#site/util/string'; import type { AuthorProps } from '#site/types'; +// Extracts the GitHub username from an author's profile URL, since the numeric +// `id` is only used to build a stable avatar URL and no longer holds the handle +const getGitHubUsername = (website?: string) => website?.split('/').pop(); + export const mapAuthorToCardAuthors = (author: string) => { // Clears text in parentheses const cleanedAuthor = author.replace(/\s*\(.*?\)\s*/g, '').trim(); @@ -20,7 +24,8 @@ export const mapAuthorToCardAuthors = (author: string) => { export const getAuthorWithId = (usernames: Array, hasUrl: boolean) => { const mapIdToAuthor = (username: string) => { const author = Object.values(authors).find( - ({ id }) => id.toLowerCase() === username.toLowerCase() + ({ website }) => + getGitHubUsername(website)?.toLowerCase() === username.toLowerCase() ); if (author) { @@ -29,14 +34,14 @@ export const getAuthorWithId = (usernames: Array, hasUrl: boolean) => { return { image: getGitHubAvatarUrl(id), name, - nickname: id, + nickname: getGitHubUsername(website) ?? name, fallback: getAcronymFromString(name), url: hasUrl ? website : undefined, }; } return { - image: getGitHubAvatarUrl(username), + image: getGitHubAvatarUrl(username, true), nickname: username, fallback: getAcronymFromString(username), url: hasUrl ? `https://github.com/${username}` : undefined, @@ -55,7 +60,7 @@ export const getAuthorWithName = (names: Array, hasUrl: boolean) => { return { image: getGitHubAvatarUrl(id), name, - nickname: id, + nickname: getGitHubUsername(website) ?? name, fallback: getAcronymFromString(name), url: hasUrl ? website : undefined, }; diff --git a/apps/site/util/github.ts b/apps/site/util/github.ts index 3070612c3b5ce..467656d49ed69 100644 --- a/apps/site/util/github.ts +++ b/apps/site/util/github.ts @@ -1,7 +1,12 @@ import GitHubSlugger from 'github-slugger'; -export const getGitHubAvatarUrl = (username: string): string => - `https://avatars.githubusercontent.com/${username}`; +export const getGitHubAvatarUrl = ( + id: string | number, + useLegacyUrl?: boolean +): string => + useLegacyUrl + ? `https://avatars.githubusercontent.com/${id}` // this is not guaranteed to work for all users, as they may change their username + : `https://avatars.githubusercontent.com/u/${id}`; export const createGitHubSlugger = () => { const githubSlugger = new GitHubSlugger();