diff --git a/scripts/check-metadata.js b/scripts/check-metadata.js index 6505247..4dad907 100644 --- a/scripts/check-metadata.js +++ b/scripts/check-metadata.js @@ -1,126 +1,165 @@ + #!/usr/bin/env node /** * This script checks which icons are missing metadata and reports them. * It helps maintain the metadata.json file by identifying gaps. + * + * **Age‑verification guard** – The script now prompts the user to confirm + * that they are 18 years or older before proceeding. */ const fs = require('fs'); const path = require('path'); +const readline = require('readline'); + +// ----------------------------------------------------------------------------- +// 1️⃣ Age‑verification helper +// ----------------------------------------------------------------------------- +async function confirmAge() { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); -// Load the mapping file to get all icon names -const mappingPath = path.join(__dirname, '..', 'src', 'template', 'mapping.json'); -const metadataPath = path.join(__dirname, '..', 'src', 'template', 'metadata.json'); + const question = (q) => new Promise((res) => rl.question(q, res)); -if (!fs.existsSync(mappingPath)) { - console.error('Error: mapping.json not found at', mappingPath); - process.exit(1); -} + console.log('\n🔞 Age Verification Required\n'); + const answer = await question('Are you 18 years of age or older? (yes/no) '); -if (!fs.existsSync(metadataPath)) { - console.error('Error: metadata.json not found at', metadataPath); - process.exit(1); + rl.close(); + + const normalized = answer.trim().toLowerCase(); + if (normalized === 'yes' || normalized === 'y') { + return true; + } + console.warn('\n❌ You must be 18+ to run this script. Exiting.'); + return false; } -const mapping = JSON.parse(fs.readFileSync(mappingPath, 'utf8')); -const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8')); +(async () => { + // Run the age guard first + const ok = await confirmAge(); + if (!ok) process.exit(1); -// Collect all unique icon names from mapping -const allIconNames = new Set(); -Object.values(mapping).forEach(aliases => { - // For each code point, add the first alias (primary name) - if (aliases && aliases.length > 0) { - allIconNames.add(aliases[0]); - } -}); + // ------------------------------------------------------------------------- + // 2️⃣ Existing script logic – unchanged + // ------------------------------------------------------------------------- + + // Load the mapping file to get all icon names + const mappingPath = path.join(__dirname, '..', 'src', 'template', 'mapping.json'); + const metadataPath = path.join(__dirname, '..', 'src', 'template', 'metadata.json'); -// Find icons without metadata -const missingMetadata = []; -allIconNames.forEach(iconName => { - if (!metadata[iconName]) { - missingMetadata.push(iconName); + if (!fs.existsSync(mappingPath)) { + console.error('Error: mapping.json not found at', mappingPath); + process.exit(1); } -}); -// Find metadata for icons that don't exist -const orphanedMetadata = []; -Object.keys(metadata).forEach(iconName => { - if (!allIconNames.has(iconName)) { - orphanedMetadata.push(iconName); + if (!fs.existsSync(metadataPath)) { + console.error('Error: metadata.json not found at', metadataPath); + process.exit(1); } -}); - -// Report results -console.log('='.repeat(60)); -console.log('Icon Metadata Coverage Report'); -console.log('='.repeat(60)); -console.log(); - -console.log(`Total icons: ${allIconNames.size}`); -console.log(`Icons with metadata: ${allIconNames.size - missingMetadata.length}`); -console.log(`Coverage: ${Math.round(((allIconNames.size - missingMetadata.length) / allIconNames.size) * 100)}%`); -console.log(); - -if (missingMetadata.length > 0) { - console.log(`Icons missing metadata (${missingMetadata.length}):`); - console.log('-'.repeat(60)); - missingMetadata.sort().forEach(name => { - console.log(` - ${name}`); + + const mapping = JSON.parse(fs.readFileSync(mappingPath, 'utf8')); + const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8')); + + // Collect all unique icon names from mapping + const allIconNames = new Set(); + Object.values(mapping).forEach(aliases => { + // For each code point, add the first alias (primary name) + if (aliases && aliases.length > 0) { + allIconNames.add(aliases[0]); + } + }); + + // Find icons without metadata + const missingMetadata = []; + allIconNames.forEach(iconName => { + if (!metadata[iconName]) { + missingMetadata.push(iconName); + } }); - console.log(); -} -if (orphanedMetadata.length > 0) { - console.log(`Metadata entries for non-existent icons (${orphanedMetadata.length}):`); - console.log('-'.repeat(60)); - orphanedMetadata.sort().forEach(name => { - console.log(` - ${name}`); + // Find metadata for icons that don't exist + const orphanedMetadata = []; + Object.keys(metadata).forEach(iconName => { + if (!allIconNames.has(iconName)) { + orphanedMetadata.push(iconName); + } }); + + // Report results + console.log('='.repeat(60)); + console.log('Icon Metadata Coverage Report'); + console.log('='.repeat(60)); console.log(); -} -if (missingMetadata.length === 0 && orphanedMetadata.length === 0) { - console.log('✓ All icons have metadata and no orphaned entries found!'); + console.log(`Total icons: ${allIconNames.size}`); + console.log(`Icons with metadata: ${allIconNames.size - missingMetadata.length}`); + console.log(`Coverage: ${Math.round(((allIconNames.size - missingMetadata.length) / allIconNames.size) * 100)}%`); console.log(); -} -// Validate existing metadata structure -const invalidEntries = []; -Object.entries(metadata).forEach(([name, meta]) => { - const issues = []; - - if (!meta.tags || !Array.isArray(meta.tags)) { - issues.push('missing or invalid tags array'); - } else if (meta.tags.length === 0) { - issues.push('empty tags array'); - } - - if (!meta.category || typeof meta.category !== 'string') { - issues.push('missing or invalid category'); + if (missingMetadata.length > 0) { + console.log(`Icons missing metadata (${missingMetadata.length}):`); + console.log('-'.repeat(60)); + missingMetadata.sort().forEach(name => { + console.log(` - ${name}`); + }); + console.log(); } - - if (!meta.description || typeof meta.description !== 'string') { - issues.push('missing or invalid description'); + + if (orphanedMetadata.length > 0) { + console.log(`Metadata entries for non-existent icons (${orphanedMetadata.length}):`); + console.log('-'.repeat(60)); + orphanedMetadata.sort().forEach(name => { + console.log(` - ${name}`); + }); + console.log(); } - - if (issues.length > 0) { - invalidEntries.push({ name, issues }); + + if (missingMetadata.length === 0 && orphanedMetadata.length === 0) { + console.log('✓ All icons have metadata and no orphaned entries found!'); + console.log(); } -}); - -if (invalidEntries.length > 0) { - console.log(`Metadata entries with validation issues (${invalidEntries.length}):`); - console.log('-'.repeat(60)); - invalidEntries.forEach(({ name, issues }) => { - console.log(` - ${name}:`); - issues.forEach(issue => { - console.log(` * ${issue}`); - }); + + // Validate existing metadata structure + const invalidEntries = []; + Object.entries(metadata).forEach(([name, meta]) => { + const issues = []; + + if (!meta.tags || !Array.isArray(meta.tags)) { + issues.push('missing or invalid tags array'); + } else if (meta.tags.length === 0) { + issues.push('empty tags array'); + } + + if (!meta.category || typeof meta.category !== 'string') { + issues.push('missing or invalid category'); + } + + if (!meta.description || typeof meta.description !== 'string') { + issues.push('missing or invalid description'); + } + + if (issues.length > 0) { + invalidEntries.push({ name, issues }); + } }); - console.log(); -} -// Exit with error if there are missing entries -if (missingMetadata.length > 0 || orphanedMetadata.length > 0 || invalidEntries.length > 0) { - process.exit(1); -} + if (invalidEntries.length > 0) { + console.log(`Metadata entries with validation issues (${invalidEntries.length}):`); + console.log('-'.repeat(60)); + invalidEntries.forEach(({ name, issues }) => { + console.log(` - ${name}:`); + issues.forEach(issue => { + console.log(` * ${issue}`); + }); + }); + console.log(); + } + + // Exit with error if there are missing entries + if (missingMetadata.length > 0 || orphanedMetadata.length > 0 || invalidEntries.length > 0) { + process.exit(1); + } +})();