@@ -3,17 +3,92 @@ import url from 'url';
33import * as fs from 'fs/promises' ;
44import path from 'path' ;
55
6+ interface Person {
7+ name : string ;
8+ }
9+
10+ const currentDirectory = url . fileURLToPath ( new URL ( '.' , import . meta. url ) ) ;
11+ const teamMembersPath = path . resolve ( currentDirectory , '../data/about/teamMembers.json' ) ;
12+ const imagesDirectory = path . resolve ( currentDirectory , '../public/static/branding/about' ) ;
13+
14+ // Mirror the image filename derivation used in docs/src/components/about/Team.tsx.
15+ function imageFileName ( name : string ) {
16+ return `${ name
17+ . split ( ' ' )
18+ . map ( ( part ) => part . toLowerCase ( ) )
19+ . join ( '-' ) } .png`;
20+ }
21+
22+ async function fileExists ( filePath : string ) {
23+ try {
24+ await fs . access ( filePath ) ;
25+ return true ;
26+ } catch {
27+ return false ;
28+ }
29+ }
30+
31+ async function readCurrentMembers ( ) : Promise < Person [ ] > {
32+ try {
33+ return JSON . parse ( await fs . readFile ( teamMembersPath , 'utf8' ) ) ;
34+ } catch {
35+ return [ ] ;
36+ }
37+ }
38+
39+ // Delete images of removed members and report new members missing a photo.
40+ async function syncImagesAndSummary ( previousMembers : Person [ ] , people : Person [ ] , ciMode : boolean ) {
41+ const previousNames = new Set ( previousMembers . map ( ( person ) => person . name ) ) ;
42+ const nextNames = new Set ( people . map ( ( person ) => person . name ) ) ;
43+ const added = people . filter ( ( person ) => ! previousNames . has ( person . name ) ) ;
44+ const removed = previousMembers . filter ( ( person ) => ! nextNames . has ( person . name ) ) ;
45+
46+ // Delete images of members who are no longer part of the team.
47+ await Promise . all (
48+ removed . map ( async ( person ) => {
49+ const imagePath = path . join ( imagesDirectory , imageFileName ( person . name ) ) ;
50+ if ( await fileExists ( imagePath ) ) {
51+ await fs . unlink ( imagePath ) ;
52+ }
53+ } ) ,
54+ ) ;
55+
56+ // Flag new members whose image still needs to be added manually.
57+ const missingImages = (
58+ await Promise . all (
59+ added . map ( async ( person ) => {
60+ const fileName = imageFileName ( person . name ) ;
61+ const exists = await fileExists ( path . join ( imagesDirectory , fileName ) ) ;
62+ return exists ? null : `${ person . name } (\`${ fileName } \`)` ;
63+ } ) ,
64+ )
65+ ) . filter ( ( item ) : item is string => item !== null ) ;
66+
67+ const summary = missingImages . length
68+ ? `### ⚠️ Missing images\n\nAdd a photo for the following new members to \`docs/public/static/branding/about/\`:\n\n${ missingImages
69+ . map ( ( entry ) => `- ${ entry } ` )
70+ . join ( '\n' ) } `
71+ : '' ;
72+
73+ if ( summary ) {
74+ if ( ciMode ) {
75+ await fs . writeFile ( process . env . SYNC_SUMMARY_FILE as string , summary , 'utf8' ) ;
76+ } else {
77+ console . log ( summary ) ;
78+ }
79+ }
80+ }
81+
682async function run ( ) {
83+ // Read the current roster before overwriting, only when a diff is needed.
84+ const previousMembers = process . env . SYNC_SUMMARY_FILE ? await readCurrentMembers ( ) : [ ] ;
85+
786 const response = await fetch ( 'https://frontend-public.mui.com/api/mui-about' ) ;
8- const { people } = await response . json ( ) ;
87+ const { people } : { people : Person [ ] } = await response . json ( ) ;
988
10- const currentDirectory = url . fileURLToPath ( new URL ( '.' , import . meta . url ) ) ;
89+ await fs . writeFile ( teamMembersPath , JSON . stringify ( people ) , 'utf8' ) ;
1190
12- await fs . writeFile (
13- path . resolve ( currentDirectory , '../data/about/teamMembers.json' ) ,
14- JSON . stringify ( people ) ,
15- 'utf8' ,
16- ) ;
91+ await syncImagesAndSummary ( previousMembers , people , ! ! process . env . SYNC_SUMMARY_FILE ) ;
1792
1893 console . log ( 'done' ) ;
1994}
0 commit comments