#!/usr/bin/env node /** * Script pour vérifier le format des blips et corriger les problèmes */ const fs = require('fs'); const path = require('path'); const REQUIRED_FIELDS = ['title', 'ring', 'quadrant', 'tags']; const VALID_RINGS = ['adopt', 'trial', 'assess', 'hold']; const VALID_QUADRANTS = [ 'technologies-differentiantes', 'technologies-commodite', 'technologies-risque', 'technologies-emergentes' ]; function parseBlip(filePath) { const content = fs.readFileSync(filePath, 'utf-8'); const frontMatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/); if (!frontMatterMatch) { return { error: 'No front matter found', file: filePath }; } const frontMatter = frontMatterMatch[1]; const body = frontMatterMatch[2]; const metadata = {}; for (const line of frontMatter.split('\n')) { const match = line.match(/^(\w+):\s*(.+)$/); if (match) { const key = match[1]; let value = match[2].trim(); // Parser les valeurs if (value === 'true') value = true; else if (value === 'false') value = false; else if (!isNaN(value) && value !== '') value = Number(value); else if (value.startsWith('[')) { value = value.slice(1, -1).split(',').map(v => v.trim().replace(/['"]/g, '')); } else if (value.startsWith('"') && value.endsWith('"')) { value = value.slice(1, -1); } metadata[key] = value; } } return { metadata, body, file: filePath }; } function validateBlip(blip) { const errors = []; const warnings = []; if (blip.error) { return { errors: [blip.error], warnings: [] }; } // Vérifier les champs requis for (const field of REQUIRED_FIELDS) { if (!blip.metadata[field]) { errors.push(`Missing required field: ${field}`); } } // Vérifier le format des valeurs if (blip.metadata.ring && !VALID_RINGS.includes(blip.metadata.ring)) { errors.push(`Invalid ring: ${blip.metadata.ring}. Valid values: ${VALID_RINGS.join(', ')}`); } if (blip.metadata.quadrant && !VALID_QUADRANTS.includes(blip.metadata.quadrant)) { errors.push(`Invalid quadrant: ${blip.metadata.quadrant}. Valid values: ${VALID_QUADRANTS.join(', ')}`); } if (blip.metadata.tags) { if (!Array.isArray(blip.metadata.tags)) { errors.push(`Tags must be an array, got: ${typeof blip.metadata.tags}`); } else if (blip.metadata.tags.length === 0) { warnings.push('Tags array is empty'); } } if (!blip.metadata.title || blip.metadata.title.trim() === '') { errors.push('Title is empty or missing'); } return { errors, warnings }; } function verifyAllBlips(radarDir) { const files = fs.readdirSync(radarDir) .filter(f => f.endsWith('.md')) .map(f => path.join(radarDir, f)); const results = { total: files.length, valid: 0, invalid: 0, errors: [], warnings: [] }; console.log(`\nVérification de ${files.length} fichiers blips...\n`); for (const file of files) { const blip = parseBlip(file); const validation = validateBlip(blip); const filename = path.basename(file); if (validation.errors.length > 0) { results.invalid++; results.errors.push({ file: filename, errors: validation.errors }); console.log(`❌ ${filename}`); validation.errors.forEach(err => console.log(` - ${err}`)); } else { results.valid++; if (validation.warnings.length > 0) { results.warnings.push({ file: filename, warnings: validation.warnings }); console.log(`⚠️ ${filename} (warnings)`); validation.warnings.forEach(warn => console.log(` - ${warn}`)); } else { console.log(`✅ ${filename}`); } } } console.log(`\n📊 Résumé:`); console.log(` ✅ Valides: ${results.valid}`); console.log(` ❌ Invalides: ${results.invalid}`); console.log(` ⚠️ Avertissements: ${results.warnings.length}`); if (results.errors.length > 0) { console.log(`\n❌ Fichiers avec erreurs:`); results.errors.forEach(({ file, errors }) => { console.log(`\n ${file}:`); errors.forEach(err => console.log(` - ${err}`)); }); } if (results.warnings.length > 0) { console.log(`\n⚠️ Fichiers avec avertissements:`); results.warnings.forEach(({ file, warnings }) => { console.log(`\n ${file}:`); warnings.forEach(warn => console.log(` - ${warn}`)); }); } return results; } // Vérifier le format des dates des releases function verifyReleaseDates() { console.log('\n📅 Vérification des dates de release...\n'); const radarDir = path.join(__dirname, '../radar-business'); const dirs = fs.readdirSync(radarDir, { withFileTypes: true }) .filter(dirent => dirent.isDirectory()) .map(dirent => dirent.name); const datePattern = /^\d{4}-\d{2}-\d{2}$/; const errors = []; for (const dir of dirs) { if (datePattern.test(dir)) { const date = new Date(dir); if (isNaN(date.getTime())) { errors.push(`Invalid date format: ${dir} (cannot parse as date)`); console.log(`❌ ${dir} - Date invalide`); } else { console.log(`✅ ${dir} - Date valide (${date.toLocaleDateString('fr-FR')})`); } } else { errors.push(`Invalid date format: ${dir} (must be YYYY-MM-DD)`); console.log(`❌ ${dir} - Format invalide (doit être YYYY-MM-DD)`); } } if (errors.length > 0) { console.log(`\n❌ Erreurs de format de date:`); errors.forEach(err => console.log(` - ${err}`)); } return errors.length === 0; } // Main function main() { const radarDir = path.join(__dirname, '../radar-business/2025-01-15'); if (!fs.existsSync(radarDir)) { console.error(`Répertoire non trouvé: ${radarDir}`); process.exit(1); } // Vérifier les dates const datesValid = verifyReleaseDates(); // Vérifier les blips const results = verifyAllBlips(radarDir); // Résultat final if (results.invalid > 0 || !datesValid) { console.log('\n❌ Des erreurs ont été détectées. Veuillez les corriger.'); process.exit(1); } else { console.log('\n✅ Tous les fichiers sont valides !'); process.exit(0); } } if (require.main === module) { main(); } module.exports = { parseBlip, validateBlip, verifyAllBlips, verifyReleaseDates };