@@ -12,20 +12,26 @@ const __dirname = dirname(__filename);
1212// Set via environment variable: GITHUB_TOKEN=your_token_here node fetch-github-stars.mjs
1313const GITHUB_TOKEN = process . env . GITHUB_TOKEN ;
1414
15+ // Path to the centralized GitHub stars data file
16+ const GITHUB_STARS_FILE = path . join ( __dirname , '..' , 'manifests' , 'github-stars.json' ) ;
17+
1518// Directories configuration - now pointing to individual file directories
1619const dirsConfig = [
1720 {
1821 directory : 'manifests/extensions' ,
22+ category : 'extensions' ,
1923 githubUrlField : 'communityUrls.github' ,
2024 type : 'nested'
2125 } ,
2226 {
2327 directory : 'manifests/ides' ,
28+ category : 'ides' ,
2429 githubUrlField : 'communityUrls.github' ,
2530 type : 'nested'
2631 } ,
2732 {
2833 directory : 'manifests/clis' ,
34+ category : 'clis' ,
2935 githubUrlField : 'communityUrls.github' ,
3036 type : 'nested'
3137 }
@@ -110,79 +116,82 @@ function sleep(ms) {
110116}
111117
112118// Process a single JSON file
113- async function processFile ( filePath , fileName , type ) {
119+ async function processFile ( filePath , fileName , type , starsData ) {
114120 const content = fs . readFileSync ( filePath , 'utf8' ) ;
115121 const item = JSON . parse ( content ) ;
116122
117123 const githubUrl = getGithubUrl ( item , type ) ;
118124
119125 if ( ! githubUrl ) {
120126 console . log ( ` ⏭️ Skipping ${ item . name || item . id } (no GitHub URL)` ) ;
121- return { updated : false , skipped : true , error : false } ;
127+ return { updated : false , skipped : true , error : false , id : item . id , stars : null } ;
122128 }
123129
124130 const parsed = parseGithubUrl ( githubUrl ) ;
125131 if ( ! parsed ) {
126132 console . log ( ` ❌ Failed to parse GitHub URL for ${ item . name || item . id } : ${ githubUrl } ` ) ;
127- return { updated : false , skipped : false , error : true } ;
133+ return { updated : false , skipped : false , error : true , id : item . id , stars : null } ;
128134 }
129135
130136 try {
131137 console . log ( ` 🔍 Fetching stars for ${ item . name || item . id } (${ parsed . owner } /${ parsed . repo } )...` ) ;
132138 const stars = await fetchStars ( parsed . owner , parsed . repo ) ;
133- item . githubStars = stars ;
134-
135- // Write back to file
136- fs . writeFileSync ( filePath , JSON . stringify ( item , null , 2 ) + '\n' , 'utf8' ) ;
137139 console . log ( ` ✅ Updated ${ item . name || item . id } : ${ stars } k stars` ) ;
138140
139141 // Sleep for 1 second to avoid rate limiting
140142 await sleep ( 1000 ) ;
141- return { updated : true , skipped : false , error : false } ;
143+ return { updated : true , skipped : false , error : false , id : item . id , stars } ;
142144 } catch ( error ) {
143145 console . log ( ` ❌ Error fetching ${ item . name || item . id } : ${ error . message } ` ) ;
144- return { updated : false , skipped : false , error : true } ;
146+ return { updated : false , skipped : false , error : true , id : item . id , stars : null } ;
145147 }
146148}
147149
148150// Process all files in a directory
149- async function processDirectory ( dirConfig ) {
151+ async function processDirectory ( dirConfig , starsData ) {
150152 const dirPath = path . join ( __dirname , '..' , dirConfig . directory ) ;
151153 console . log ( `\n📁 Processing ${ dirConfig . directory } ...` ) ;
152154
153155 if ( ! fs . existsSync ( dirPath ) ) {
154156 console . log ( ` ⚠️ Directory not found: ${ dirPath } ` ) ;
155- return ;
157+ return { categoryData : { } , stats : { updated : 0 , skipped : 0 , errors : 0 } } ;
156158 }
157159
158160 // Get all JSON files in the directory
159161 const files = fs . readdirSync ( dirPath ) . filter ( file => file . endsWith ( '.json' ) ) ;
160162
161163 if ( files . length === 0 ) {
162164 console . log ( ` ⚠️ No JSON files found in ${ dirConfig . directory } ` ) ;
163- return ;
165+ return { categoryData : { } , stats : { updated : 0 , skipped : 0 , errors : 0 } } ;
164166 }
165167
166168 let updated = 0 ;
167169 let skipped = 0 ;
168170 let errors = 0 ;
171+ const categoryData = { } ;
169172
170173 for ( const file of files ) {
171174 const filePath = path . join ( dirPath , file ) ;
172- const result = await processFile ( filePath , file , dirConfig . type ) ;
175+ const result = await processFile ( filePath , file , dirConfig . type , starsData ) ;
173176
174177 if ( result . updated ) updated ++ ;
175178 if ( result . skipped ) skipped ++ ;
176179 if ( result . error ) errors ++ ;
180+
181+ // Store the stars data for this item
182+ if ( result . id ) {
183+ categoryData [ result . id ] = result . stars ;
184+ }
177185 }
178186
179187 console . log ( `\n✨ ${ dirConfig . directory } completed: ${ updated } updated, ${ skipped } skipped, ${ errors } errors` ) ;
188+ return { categoryData, stats : { updated, skipped, errors } } ;
180189}
181190
182191// Main function
183192async function main ( ) {
184193 console . log ( '🚀 Starting GitHub stars fetcher...\n' ) ;
185- console . log ( '📝 Note: Now processing individual JSON files in directories \n' ) ;
194+ console . log ( '📝 Note: Updating centralized github-stars.json file \n' ) ;
186195
187196 if ( ! GITHUB_TOKEN ) {
188197 console . log ( '⚠️ Warning: No GITHUB_TOKEN set. You may hit rate limits (60 requests/hour).' ) ;
@@ -191,16 +200,59 @@ async function main() {
191200 console . log ( '✅ Using GitHub token for authentication\n' ) ;
192201 }
193202
203+ // Load existing stars data or create new structure
204+ let starsData = { extensions : { } , clis : { } , ides : { } } ;
205+ if ( fs . existsSync ( GITHUB_STARS_FILE ) ) {
206+ try {
207+ const content = fs . readFileSync ( GITHUB_STARS_FILE , 'utf8' ) ;
208+ starsData = JSON . parse ( content ) ;
209+ console . log ( '📂 Loaded existing github-stars.json\n' ) ;
210+ } catch ( error ) {
211+ console . log ( '⚠️ Failed to parse existing github-stars.json, creating new one\n' ) ;
212+ }
213+ }
214+
215+ let totalUpdated = 0 ;
216+ let totalSkipped = 0 ;
217+ let totalErrors = 0 ;
218+
219+ // Process each directory and collect stars data
194220 for ( const dirConfig of dirsConfig ) {
195221 try {
196- await processDirectory ( dirConfig ) ;
222+ const { categoryData, stats } = await processDirectory ( dirConfig , starsData ) ;
223+
224+ // Sort the category data by key (alphabetically)
225+ const sortedCategoryData = Object . keys ( categoryData )
226+ . sort ( )
227+ . reduce ( ( acc , key ) => {
228+ acc [ key ] = categoryData [ key ] ;
229+ return acc ;
230+ } , { } ) ;
231+
232+ // Update the stars data for this category
233+ starsData [ dirConfig . category ] = sortedCategoryData ;
234+
235+ totalUpdated += stats . updated ;
236+ totalSkipped += stats . skipped ;
237+ totalErrors += stats . errors ;
197238 } catch ( error ) {
198239 console . error ( `❌ Failed to process ${ dirConfig . directory } :` , error . message ) ;
240+ totalErrors ++ ;
199241 }
200242 }
201243
244+ // Write the updated stars data to file
245+ try {
246+ fs . writeFileSync ( GITHUB_STARS_FILE , JSON . stringify ( starsData , null , 2 ) + '\n' , 'utf8' ) ;
247+ console . log ( '\n📝 Successfully updated manifests/github-stars.json' ) ;
248+ } catch ( error ) {
249+ console . error ( '\n❌ Failed to write github-stars.json:' , error . message ) ;
250+ process . exit ( 1 ) ;
251+ }
252+
202253 console . log ( '\n' + '=' . repeat ( 50 ) ) ;
203254 console . log ( '🎉 All directories processed!' ) ;
255+ console . log ( `📊 Total: ${ totalUpdated } updated, ${ totalSkipped } skipped, ${ totalErrors } errors` ) ;
204256 console . log ( '=' . repeat ( 50 ) ) ;
205257}
206258
0 commit comments