import { readFileSync, writeFileSync } from 'fs'; import { dirname, join } from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Check if --fix flag is provided const shouldFix = process.argv.includes('--fix'); // Extract badge info from README file /** * * @param filePath */ function extractBadgeInfo(filePath) { const content = readFileSync(filePath, 'utf8'); const badgeRegex = /\[!\[.*?\]\(https:\/\/img\.shields\.io\/badge\/(.*?)-([0-9A-Fa-f]{6})\?.*?logo=([a-z0-9-]+)/gi; const badges = {}; let match; while ((match = badgeRegex.exec(content)) !== null) { const fullMatch = match[0]; const label = match[1]; const color = match[2]; const logo = match[3]; // Normalize logo name (lowercase and remove dashes for Simple Icons lookup) const normalizedLogo = logo.toLowerCase().replace(/-/g, ''); if (!badges[normalizedLogo]) { badges[normalizedLogo] = { color: color, logo: logo, label: label, fullMatch: fullMatch }; } } return badges; } // Fix badge colors in README file /** * * @param filePath * @param colorChanges */ function fixBadgeColors(filePath, colorChanges) { let content = readFileSync(filePath, 'utf8'); let changeCount = 0; for (const [logo, change] of Object.entries(colorChanges)) { const oldColor = change.current.toUpperCase(); const newColor = change.official.toUpperCase(); // Create regex to match badges with this logo and old color const badgeRegex = new RegExp( `(\\[!\\[.*?\\]\\(https:\\/\\/img\\.shields\\.io\\/badge\\/.*?-)${oldColor}(\\?.*?logo=${change.logoName})`, 'gi' ); const newContent = content.replace(badgeRegex, `$1${newColor}$2`); if (newContent !== content) { changeCount++; content = newContent; console.log(` āœ… Fixed ${logo}: ${oldColor} → ${newColor}`); } } if (changeCount > 0) { writeFileSync(filePath, content, 'utf8'); return changeCount; } return 0; } /** * */ async function fetchSimpleIcons() { const response = await fetch( 'https://raw.githubusercontent.com/simple-icons/simple-icons/refs/heads/develop/data/simple-icons.json' ); const icons = await response.json(); // Create a map of slug -> hex color const iconMap = {}; icons.forEach(icon => { // Use slug if available, otherwise generate from title const slug = icon.slug || icon.title.toLowerCase().replace(/[^a-z0-9]/g, ''); iconMap[slug] = icon.hex; }); return iconMap; } /** * * @param filePath * @param simpleIcons */ function checkReadme(filePath, simpleIcons) { const fileName = filePath.split(/[\\/]/).pop(); const currentBadges = extractBadgeInfo(filePath); console.log(`\n${'='.repeat(80)}`); console.log(`šŸ“„ ${fileName}`); console.log('='.repeat(80)); console.log( 'Logo Name'.padEnd(25) + 'Current Color'.padEnd(20) + 'Official Color'.padEnd(20) + 'Status' ); console.log('='.repeat(80)); let totalChecked = 0; let correctCount = 0; let incorrectCount = 0; const issues = {}; for (const [logo, badgeInfo] of Object.entries(currentBadges)) { totalChecked++; const currentColor = badgeInfo.color; const officialColor = simpleIcons[logo]; if (!officialColor) { console.log( `${logo.padEnd(25)}${currentColor.padEnd(20)}${'NOT FOUND'.padEnd(20)}āš ļø Missing` ); continue; } const currentUpper = currentColor.toUpperCase(); const officialUpper = officialColor.toUpperCase(); if (currentUpper === officialUpper) { console.log( `${logo.padEnd(25)}${currentColor.padEnd(20)}${officialColor.padEnd(20)}āœ… Correct` ); correctCount++; } else { console.log( `${logo.padEnd(25)}${currentColor.padEnd(20)}${officialColor.padEnd(20)}āŒ Mismatch` ); incorrectCount++; issues[logo] = { current: currentColor, official: officialColor, logoName: badgeInfo.logo }; } } console.log('='.repeat(80)); console.log(`\nSummary for ${fileName}:`); console.log(`Total badges checked: ${totalChecked}`); console.log(`āœ… Correct: ${correctCount}`); console.log(`āŒ Incorrect: ${incorrectCount}`); if (Object.keys(issues).length > 0) { console.log('\nšŸ”§ Badges that need updating:\n'); Object.entries(issues).forEach(([logo, issue]) => { console.log(`${logo}:`); console.log(` Current: ${issue.current}`); console.log(` Official: ${issue.official}`); console.log(` Change: ${issue.current} → ${issue.official}\n`); }); if (shouldFix) { console.log('šŸ”§ Applying fixes...\n'); const fixedCount = fixBadgeColors(filePath, issues); console.log(`āœ… Fixed ${fixedCount} badge(s) in ${fileName}`); } } else { console.log('\nšŸŽ‰ All badge colors are correct!'); } return issues; } /** * */ async function main() { console.log('Fetching Simple Icons data...\n'); const simpleIcons = await fetchSimpleIcons(); const readmes = [ join(__dirname, '..', 'README.md'), join(__dirname, '..', 'README.zh-Hans.md'), join(__dirname, '..', 'README.zh-Hant.md') ]; let anyIssues = false; for (const readmePath of readmes) { const issues = checkReadme(readmePath, simpleIcons); if (Object.keys(issues).length > 0) anyIssues = true; } if (anyIssues && !shouldFix) { console.log('\nšŸ’” Tip: Run with --fix flag to automatically fix all mismatches:'); console.log(' node scripts/fix-badge-colors.js --fix\n'); } else if (!anyIssues) { console.log('\nšŸŽ‰ All badge colors across all READMEs are correct!'); } else { console.log('\nšŸŽ‰ All badge colors have been fixed!'); } } main().catch(console.error);