887 lines
28 KiB
JavaScript
887 lines
28 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
// Configuración
|
|
const CONFIG = {
|
|
// Rutas de entrada (FontAwesome Pro)
|
|
FA_METADATA: path.join(__dirname, '..', 'assets', 'fontawesome', 'metadata', 'icons.json'),
|
|
FA_CATEGORIES: path.join(__dirname, '..', 'assets', 'fontawesome', 'metadata', 'categories.yml'),
|
|
|
|
// Rutas de salida
|
|
OUTPUT_DIR: path.join(__dirname, '..', 'assets', 'processed'),
|
|
OUTPUT_METADATA: path.join(__dirname, '..', 'assets', 'processed', 'metadata.json'),
|
|
OUTPUT_STYLES: path.join(__dirname, '..', 'assets', 'processed', 'styles.json'),
|
|
OUTPUT_CATEGORIES: path.join(__dirname, '..', 'assets', 'processed', 'categories.json'),
|
|
|
|
// Configuración de procesamiento
|
|
MIN_ICONS_TO_PROCESS: 100,
|
|
VERSION: '2.0.0'
|
|
};
|
|
|
|
// Colores para consola
|
|
const colors = {
|
|
reset: '\x1b[0m',
|
|
green: '\x1b[32m',
|
|
yellow: '\x1b[33m',
|
|
blue: '\x1b[34m',
|
|
red: '\x1b[31m',
|
|
magenta: '\x1b[35m',
|
|
cyan: '\x1b[36m'
|
|
};
|
|
|
|
// Banner
|
|
console.log(colors.cyan + '='.repeat(70));
|
|
console.log(' EXTRACTOR DE METADATA FONTAWESOME PRO');
|
|
console.log(' Versión: ' + CONFIG.VERSION);
|
|
console.log('='.repeat(70) + colors.reset + '\n');
|
|
|
|
/**
|
|
* Crear directorios necesarios
|
|
*/
|
|
function createDirectories() {
|
|
console.log(colors.blue + '📁 Creando directorios...' + colors.reset);
|
|
|
|
const dirs = [
|
|
CONFIG.OUTPUT_DIR,
|
|
path.join(CONFIG.OUTPUT_DIR, 'backup'),
|
|
path.join(CONFIG.OUTPUT_DIR, 'logs')
|
|
];
|
|
|
|
dirs.forEach(dir => {
|
|
if (!fs.existsSync(dir)) {
|
|
fs.mkdirSync(dir, { recursive: true });
|
|
console.log(` ✅ Creado: ${path.relative(process.cwd(), dir)}`);
|
|
} else {
|
|
console.log(` 📁 Existe: ${path.relative(process.cwd(), dir)}`);
|
|
}
|
|
});
|
|
|
|
console.log('');
|
|
}
|
|
|
|
/**
|
|
* Verificar archivos de entrada
|
|
*/
|
|
function checkInputFiles() {
|
|
console.log(colors.blue + '🔍 Verificando archivos de entrada...' + colors.reset);
|
|
|
|
const files = [
|
|
{ path: CONFIG.FA_METADATA, name: 'icons.json', required: true },
|
|
{ path: CONFIG.FA_CATEGORIES, name: 'categories.yml', required: false }
|
|
];
|
|
|
|
let allFound = true;
|
|
|
|
files.forEach(file => {
|
|
if (fs.existsSync(file.path)) {
|
|
const stats = fs.statSync(file.path);
|
|
console.log(` ✅ ${file.name}: ${(stats.size / 1024).toFixed(2)} KB`);
|
|
|
|
if (file.name === 'icons.json') {
|
|
try {
|
|
const content = fs.readFileSync(file.path, 'utf8');
|
|
const data = JSON.parse(content);
|
|
console.log(` Íconos en metadata: ${Object.keys(data).length}`);
|
|
} catch (error) {
|
|
console.log(` ❌ ${file.name}: Error de formato - ${error.message}`);
|
|
allFound = false;
|
|
}
|
|
}
|
|
} else {
|
|
if (file.required) {
|
|
console.log(` ❌ ${file.name}: NO ENCONTRADO (requerido)`);
|
|
allFound = false;
|
|
} else {
|
|
console.log(` ⚠️ ${file.name}: No encontrado (opcional)`);
|
|
}
|
|
}
|
|
});
|
|
|
|
console.log('');
|
|
return allFound;
|
|
}
|
|
|
|
/**
|
|
* Hacer backup de archivos existentes
|
|
*/
|
|
function backupExistingFiles() {
|
|
console.log(colors.blue + '💾 Creando respaldo de archivos existentes...' + colors.reset);
|
|
|
|
const filesToBackup = [
|
|
CONFIG.OUTPUT_METADATA,
|
|
CONFIG.OUTPUT_STYLES,
|
|
CONFIG.OUTPUT_CATEGORIES
|
|
];
|
|
|
|
const backupDir = path.join(CONFIG.OUTPUT_DIR, 'backup', new Date().toISOString().replace(/[:.]/g, '-'));
|
|
|
|
if (!fs.existsSync(backupDir)) {
|
|
fs.mkdirSync(backupDir, { recursive: true });
|
|
}
|
|
|
|
let backedUpCount = 0;
|
|
|
|
filesToBackup.forEach(file => {
|
|
if (fs.existsSync(file)) {
|
|
const fileName = path.basename(file);
|
|
const backupPath = path.join(backupDir, fileName);
|
|
|
|
try {
|
|
fs.copyFileSync(file, backupPath);
|
|
console.log(` ✅ Respaldo: ${fileName}`);
|
|
backedUpCount++;
|
|
} catch (error) {
|
|
console.log(` ❌ Error respaldando ${fileName}: ${error.message}`);
|
|
}
|
|
}
|
|
});
|
|
|
|
if (backedUpCount > 0) {
|
|
console.log(` 📊 Total respaldados: ${backedUpCount} archivos`);
|
|
console.log(` 📍 Ubicación: ${path.relative(process.cwd(), backupDir)}`);
|
|
} else {
|
|
console.log(' ⚠️ No hay archivos existentes para respaldar');
|
|
}
|
|
|
|
console.log('');
|
|
return backupDir;
|
|
}
|
|
|
|
/**
|
|
* Extraer y procesar metadata de íconos
|
|
*/
|
|
function extractIconMetadata() {
|
|
console.log(colors.blue + '📊 Extrayendo metadata de íconos...' + colors.reset);
|
|
|
|
try {
|
|
// Leer archivo original
|
|
const content = fs.readFileSync(CONFIG.FA_METADATA, 'utf8');
|
|
const rawMetadata = JSON.parse(content);
|
|
|
|
const totalIcons = Object.keys(rawMetadata).length;
|
|
console.log(` 📈 Total íconos encontrados: ${totalIcons}`);
|
|
|
|
if (totalIcons < CONFIG.MIN_ICONS_TO_PROCESS) {
|
|
throw new Error(`Muy pocos íconos (${totalIcons}). ¿Metadata corrupto?`);
|
|
}
|
|
|
|
// Procesar cada ícono
|
|
const processedIcons = {};
|
|
let processedCount = 0;
|
|
let stylesFound = new Set();
|
|
let categoriesFound = new Set();
|
|
|
|
Object.entries(rawMetadata).forEach(([iconName, iconData]) => {
|
|
processedCount++;
|
|
|
|
// Mostrar progreso cada 1000 íconos
|
|
if (processedCount % 1000 === 0) {
|
|
console.log(` ⏳ Procesados: ${processedCount}/${totalIcons} íconos...`);
|
|
}
|
|
|
|
// Extraer información básica
|
|
const processedIcon = {
|
|
id: iconName,
|
|
name: iconName,
|
|
label: iconData.label || formatIconName(iconName),
|
|
unicode: iconData.unicode || '',
|
|
version: iconData.version || '6.0.0',
|
|
search: iconData.search || {}
|
|
};
|
|
|
|
// Estilos disponibles
|
|
if (iconData.styles && Array.isArray(iconData.styles)) {
|
|
processedIcon.styles = iconData.styles.map(style => style.toLowerCase());
|
|
iconData.styles.forEach(style => stylesFound.add(style.toLowerCase()));
|
|
} else {
|
|
processedIcon.styles = [];
|
|
}
|
|
|
|
// Categorías
|
|
if (iconData.categories && Array.isArray(iconData.categories)) {
|
|
processedIcon.categories = iconData.categories;
|
|
iconData.categories.forEach(cat => categoriesFound.add(cat));
|
|
} else {
|
|
processedIcon.categories = ['uncategorized'];
|
|
categoriesFound.add('uncategorized');
|
|
}
|
|
|
|
// Información adicional
|
|
if (iconData.svg) {
|
|
processedIcon.svg = {
|
|
width: iconData.svg.width,
|
|
height: iconData.svg.height,
|
|
path: iconData.svg.path
|
|
};
|
|
}
|
|
|
|
// Términos de búsqueda mejorados
|
|
const searchTerms = new Set();
|
|
|
|
// Términos originales
|
|
if (iconData.search && iconData.search.terms) {
|
|
iconData.search.terms.forEach(term => {
|
|
if (term && typeof term === 'string') {
|
|
searchTerms.add(term.toLowerCase());
|
|
}
|
|
});
|
|
}
|
|
|
|
// Agregar nombre y label
|
|
searchTerms.add(iconName.toLowerCase());
|
|
if (iconData.label) {
|
|
searchTerms.add(iconData.label.toLowerCase());
|
|
// Variantes sin espacios
|
|
searchTerms.add(iconData.label.toLowerCase().replace(/\s+/g, '-'));
|
|
}
|
|
|
|
// Agregar sinónimos comunes
|
|
addCommonSynonyms(iconName, searchTerms);
|
|
|
|
processedIcon.searchTerms = Array.from(searchTerms);
|
|
processedIcon.searchCount = searchTerms.size;
|
|
|
|
// Agregar a resultados
|
|
processedIcons[iconName] = processedIcon;
|
|
});
|
|
|
|
console.log(`\n ✅ Procesados: ${processedCount} íconos`);
|
|
console.log(` 🎨 Estilos encontrados: ${stylesFound.size}`);
|
|
console.log(` 📁 Categorías encontradas: ${categoriesFound.size}`);
|
|
|
|
// Guardar metadata procesado
|
|
const metadataOutput = {
|
|
meta: {
|
|
generated: new Date().toISOString(),
|
|
source: 'FontAwesome Pro',
|
|
version: CONFIG.VERSION,
|
|
totalIcons: processedCount,
|
|
totalStyles: stylesFound.size,
|
|
totalCategories: categoriesFound.size
|
|
},
|
|
icons: processedIcons,
|
|
statistics: {
|
|
styles: Array.from(stylesFound).sort(),
|
|
categories: Array.from(categoriesFound).sort(),
|
|
iconsPerStyle: countIconsPerStyle(processedIcons),
|
|
iconsPerCategory: countIconsPerCategory(processedIcons)
|
|
}
|
|
};
|
|
|
|
fs.writeFileSync(CONFIG.OUTPUT_METADATA, JSON.stringify(metadataOutput, null, 2), 'utf8');
|
|
console.log(`\n 💾 Metadata guardado: ${CONFIG.OUTPUT_METADATA}`);
|
|
console.log(` 📊 Tamaño: ${(fs.statSync(CONFIG.OUTPUT_METADATA).size / 1024 / 1024).toFixed(2)} MB`);
|
|
|
|
return metadataOutput;
|
|
|
|
} catch (error) {
|
|
console.error(colors.red + ` ❌ Error procesando metadata: ${error.message}` + colors.reset);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Formatear nombre de ícono
|
|
*/
|
|
function formatIconName(iconName) {
|
|
return iconName
|
|
.split('-')
|
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
.join(' ');
|
|
}
|
|
|
|
/**
|
|
* Agregar sinónimos comunes
|
|
*/
|
|
function addCommonSynonyms(iconName, searchTerms) {
|
|
const synonyms = {
|
|
'home': ['house', 'casa', 'hogar', 'dwelling', 'residence'],
|
|
'user': ['person', 'profile', 'persona', 'perfil', 'account', 'human'],
|
|
'heart': ['love', 'like', 'favorite', 'amor', 'favorito', 'affection'],
|
|
'star': ['favorite', 'rating', 'estrella', 'calificación', 'rate', 'review'],
|
|
'settings': ['cog', 'gear', 'preferences', 'configuración', 'ajustes', 'options'],
|
|
'bell': ['notification', 'alert', 'notificación', 'alerta', 'ring', 'chime'],
|
|
'envelope': ['email', 'mail', 'correo', 'message', 'letter'],
|
|
'calendar': ['date', 'schedule', 'fecha', 'calendario', 'agenda', 'planner'],
|
|
'clock': ['time', 'hour', 'tiempo', 'hora', 'watch', 'timer'],
|
|
'search': ['find', 'lookup', 'buscar', 'encontrar', 'seek', 'locate'],
|
|
'plus': ['add', 'new', 'create', 'agregar', 'añadir', 'insert'],
|
|
'minus': ['remove', 'delete', 'subtract', 'quitar', 'eliminar', 'restar'],
|
|
'check': ['verify', 'confirm', 'mark', 'verificar', 'confirmar', 'tick'],
|
|
'times': ['close', 'exit', 'cancel', 'cerrar', 'salir', 'cancelar'],
|
|
'trash': ['delete', 'remove', 'garbage', 'eliminar', 'basura', 'waste'],
|
|
'edit': ['modify', 'change', 'update', 'modificar', 'cambiar', 'actualizar'],
|
|
'download': ['save', 'get', 'fetch', 'descargar', 'guardar', 'obtener'],
|
|
'upload': ['send', 'share', 'post', 'subir', 'enviar', 'compartir'],
|
|
'print': ['printer', 'hardcopy', 'paper', 'imprimir', 'copiar', 'document']
|
|
};
|
|
|
|
Object.entries(synonyms).forEach(([key, words]) => {
|
|
if (iconName.includes(key)) {
|
|
words.forEach(word => searchTerms.add(word));
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Contar íconos por estilo
|
|
*/
|
|
function countIconsPerStyle(icons) {
|
|
const counts = {};
|
|
|
|
Object.values(icons).forEach(icon => {
|
|
if (icon.styles) {
|
|
icon.styles.forEach(style => {
|
|
counts[style] = (counts[style] || 0) + 1;
|
|
});
|
|
}
|
|
});
|
|
|
|
return counts;
|
|
}
|
|
|
|
/**
|
|
* Contar íconos por categoría
|
|
*/
|
|
function countIconsPerCategory(icons) {
|
|
const counts = {};
|
|
|
|
Object.values(icons).forEach(icon => {
|
|
if (icon.categories) {
|
|
icon.categories.forEach(category => {
|
|
counts[category] = (counts[category] || 0) + 1;
|
|
});
|
|
}
|
|
});
|
|
|
|
return counts;
|
|
}
|
|
|
|
/**
|
|
* Extraer información de estilos
|
|
*/
|
|
function extractStyleInfo(metadata) {
|
|
console.log(colors.blue + '\n🎨 Extrayendo información de estilos...' + colors.reset);
|
|
|
|
// Definición de todos los estilos posibles
|
|
const allStyles = {
|
|
'solid': {
|
|
id: 'solid',
|
|
name: 'Solid',
|
|
prefix: 'fas',
|
|
class: 'fas',
|
|
weight: 900,
|
|
pro: true,
|
|
sharp: false,
|
|
family: 'Font Awesome 6 Pro',
|
|
file: 'fa-solid-900.woff2'
|
|
},
|
|
'regular': {
|
|
id: 'regular',
|
|
name: 'Regular',
|
|
prefix: 'far',
|
|
class: 'far',
|
|
weight: 400,
|
|
pro: true,
|
|
sharp: false,
|
|
family: 'Font Awesome 6 Pro',
|
|
file: 'fa-regular-400.woff2'
|
|
},
|
|
'light': {
|
|
id: 'light',
|
|
name: 'Light',
|
|
prefix: 'fal',
|
|
class: 'fal',
|
|
weight: 300,
|
|
pro: true,
|
|
sharp: false,
|
|
family: 'Font Awesome 6 Pro',
|
|
file: 'fa-light-300.woff2'
|
|
},
|
|
'thin': {
|
|
id: 'thin',
|
|
name: 'Thin',
|
|
prefix: 'fat',
|
|
class: 'fat',
|
|
weight: 100,
|
|
pro: true,
|
|
sharp: false,
|
|
family: 'Font Awesome 6 Pro',
|
|
file: 'fa-thin-100.woff2'
|
|
},
|
|
'duotone': {
|
|
id: 'duotone',
|
|
name: 'Duotone',
|
|
prefix: 'fad',
|
|
class: 'fad',
|
|
weight: 900,
|
|
pro: true,
|
|
sharp: false,
|
|
family: 'Font Awesome 6 Duotone',
|
|
file: 'fa-duotone-900.woff2'
|
|
},
|
|
'sharp-solid': {
|
|
id: 'sharp-solid',
|
|
name: 'Sharp Solid',
|
|
prefix: 'fass',
|
|
class: 'fass',
|
|
weight: 900,
|
|
pro: true,
|
|
sharp: true,
|
|
family: 'Font Awesome 6 Sharp',
|
|
file: 'fa-sharp-solid-900.woff2'
|
|
},
|
|
'sharp-regular': {
|
|
id: 'sharp-regular',
|
|
name: 'Sharp Regular',
|
|
prefix: 'fasr',
|
|
class: 'fasr',
|
|
weight: 400,
|
|
pro: true,
|
|
sharp: true,
|
|
family: 'Font Awesome 6 Sharp',
|
|
file: 'fa-sharp-regular-400.woff2'
|
|
},
|
|
'sharp-light': {
|
|
id: 'sharp-light',
|
|
name: 'Sharp Light',
|
|
prefix: 'fasl',
|
|
class: 'fasl',
|
|
weight: 300,
|
|
pro: true,
|
|
sharp: true,
|
|
family: 'Font Awesome 6 Sharp',
|
|
file: 'fa-sharp-light-300.woff2'
|
|
},
|
|
'sharp-thin': {
|
|
id: 'sharp-thin',
|
|
name: 'Sharp Thin',
|
|
prefix: 'fast',
|
|
class: 'fast',
|
|
weight: 100,
|
|
pro: true,
|
|
sharp: true,
|
|
family: 'Font Awesome 6 Sharp',
|
|
file: 'fa-sharp-thin-100.woff2'
|
|
},
|
|
'sharp-duotone': {
|
|
id: 'sharp-duotone',
|
|
name: 'Sharp Duotone',
|
|
prefix: 'fasd',
|
|
class: 'fasd',
|
|
weight: 900,
|
|
pro: true,
|
|
sharp: true,
|
|
family: 'Font Awesome 6 Sharp Duotone',
|
|
file: 'fa-sharp-duotone-900.woff2'
|
|
},
|
|
'brands': {
|
|
id: 'brands',
|
|
name: 'Brands',
|
|
prefix: 'fab',
|
|
class: 'fab',
|
|
weight: 400,
|
|
pro: false,
|
|
sharp: false,
|
|
family: 'Font Awesome 6 Brands',
|
|
file: 'fa-brands-400.woff2'
|
|
}
|
|
};
|
|
|
|
// Contar íconos por estilo (desde metadata)
|
|
const styleCounts = metadata.statistics.iconsPerStyle || {};
|
|
|
|
// Enriquecer información de estilos
|
|
const stylesInfo = {};
|
|
|
|
Object.keys(allStyles).forEach(styleId => {
|
|
const style = allStyles[styleId];
|
|
const count = styleCounts[styleId] || 0;
|
|
|
|
stylesInfo[styleId] = {
|
|
...style,
|
|
count: count,
|
|
availableInMetadata: count > 0,
|
|
percentage: metadata.meta.totalIcons > 0
|
|
? ((count / metadata.meta.totalIcons) * 100).toFixed(1)
|
|
: '0.0'
|
|
};
|
|
});
|
|
|
|
// Guardar información de estilos
|
|
const stylesOutput = {
|
|
meta: {
|
|
generated: new Date().toISOString(),
|
|
totalStyles: Object.keys(stylesInfo).length,
|
|
stylesWithIcons: Object.values(stylesInfo).filter(s => s.count > 0).length
|
|
},
|
|
styles: stylesInfo,
|
|
statistics: {
|
|
totalIconsByStyle: styleCounts,
|
|
styleCoverage: calculateStyleCoverage(stylesInfo, metadata.meta.totalIcons)
|
|
}
|
|
};
|
|
|
|
fs.writeFileSync(CONFIG.OUTPUT_STYLES, JSON.stringify(stylesOutput, null, 2), 'utf8');
|
|
console.log(` ✅ Información de estilos guardada: ${CONFIG.OUTPUT_STYLES}`);
|
|
|
|
// Mostrar resumen
|
|
console.log('\n 📊 RESUMEN DE ESTILOS:');
|
|
Object.values(stylesInfo)
|
|
.filter(style => style.count > 0)
|
|
.sort((a, b) => b.count - a.count)
|
|
.forEach(style => {
|
|
console.log(` ${style.name.padEnd(20)}: ${style.count.toString().padStart(5)} íconos (${style.percentage}%)`);
|
|
});
|
|
|
|
return stylesOutput;
|
|
}
|
|
|
|
/**
|
|
* Calcular cobertura de estilos
|
|
*/
|
|
function calculateStyleCoverage(stylesInfo, totalIcons) {
|
|
const coverage = {};
|
|
|
|
Object.values(stylesInfo).forEach(style => {
|
|
if (style.count > 0) {
|
|
coverage[style.id] = {
|
|
hasIcons: true,
|
|
count: style.count,
|
|
percentage: ((style.count / totalIcons) * 100).toFixed(1)
|
|
};
|
|
} else {
|
|
coverage[style.id] = {
|
|
hasIcons: false,
|
|
count: 0,
|
|
percentage: '0.0'
|
|
};
|
|
}
|
|
});
|
|
|
|
return coverage;
|
|
}
|
|
|
|
/**
|
|
* Extraer y procesar categorías
|
|
*/
|
|
function extractCategories(metadata) {
|
|
console.log(colors.blue + '\n📁 Extrayendo información de categorías...' + colors.reset);
|
|
|
|
// Mapeo de categorías de FontAwesome a nombres en español
|
|
const categoriesMap = {
|
|
'accessibility': 'Accesibilidad',
|
|
'alert': 'Alertas',
|
|
'animals': 'Animales',
|
|
'arrows': 'Flechas',
|
|
'automotive': 'Automotriz',
|
|
'buildings': 'Edificios',
|
|
'business': 'Negocios',
|
|
'camping': 'Camping',
|
|
'charity': 'Caridad',
|
|
'charts-diagrams': 'Gráficos',
|
|
'childhood': 'Infancia',
|
|
'clothing-fashion': 'Ropa & Moda',
|
|
'coding': 'Programación',
|
|
'communication': 'Comunicación',
|
|
'connectivity': 'Conectividad',
|
|
'construction': 'Construcción',
|
|
'design': 'Diseño',
|
|
'devices-hardware': 'Dispositivos',
|
|
'document': 'Documentos',
|
|
'editing': 'Edición',
|
|
'education': 'Educación',
|
|
'emoji': 'Emoji',
|
|
'energy': 'Energía',
|
|
'files': 'Archivos',
|
|
'film-video': 'Video',
|
|
'food-beverage': 'Comida',
|
|
'fruits-vegetables': 'Frutas',
|
|
'gaming': 'Juegos',
|
|
'gender': 'Género',
|
|
'halloween': 'Halloween',
|
|
'hands': 'Manos',
|
|
'holidays': 'Fiestas',
|
|
'household': 'Hogar',
|
|
'humanitarian': 'Humanitario',
|
|
'logistics': 'Logística',
|
|
'maps': 'Mapas',
|
|
'maritime': 'Marítimo',
|
|
'marketing': 'Marketing',
|
|
'mathematics': 'Matemáticas',
|
|
'medical-health': 'Salud',
|
|
'money': 'Dinero',
|
|
'moving': 'Mudanza',
|
|
'music-audio': 'Música',
|
|
'nature': 'Naturaleza',
|
|
'numbers': 'Números',
|
|
'photos-images': 'Fotos',
|
|
'political': 'Política',
|
|
'punctuation-symbols': 'Símbolos',
|
|
'religion': 'Religión',
|
|
'science': 'Ciencia',
|
|
'science-fiction': 'Sci-Fi',
|
|
'security': 'Seguridad',
|
|
'shapes': 'Formas',
|
|
'shopping': 'Compras',
|
|
'social': 'Social',
|
|
'spinners': 'Spinners',
|
|
'sports-fitness': 'Deportes',
|
|
'text-formatting': 'Texto',
|
|
'time': 'Tiempo',
|
|
'toggle': 'Toggle',
|
|
'transportation': 'Transporte',
|
|
'travel': 'Viajes',
|
|
'users-people': 'Personas',
|
|
'weather': 'Clima',
|
|
'writing': 'Escritura',
|
|
'uncategorized': 'Sin Categoría'
|
|
};
|
|
|
|
// Obtener conteos de categorías desde metadata
|
|
const categoryCounts = metadata.statistics.iconsPerCategory || {};
|
|
|
|
// Procesar categorías
|
|
const categories = [];
|
|
|
|
// Categorías especiales
|
|
categories.push({
|
|
id: 'all',
|
|
name: 'Todos los Íconos',
|
|
icon: 'fas fa-th',
|
|
count: metadata.meta.totalIcons,
|
|
type: 'special'
|
|
});
|
|
|
|
categories.push({
|
|
id: 'favorites',
|
|
name: 'Favoritos',
|
|
icon: 'fas fa-star',
|
|
count: 0,
|
|
type: 'special'
|
|
});
|
|
|
|
categories.push({
|
|
id: 'recent',
|
|
name: 'Recientes',
|
|
icon: 'fas fa-history',
|
|
count: 0,
|
|
type: 'special'
|
|
});
|
|
|
|
// Categorías de FontAwesome
|
|
Object.entries(categoryCounts).forEach(([categoryId, count]) => {
|
|
const categoryName = categoriesMap[categoryId] || formatCategoryName(categoryId);
|
|
|
|
categories.push({
|
|
id: categoryId,
|
|
name: categoryName,
|
|
icon: getCategoryIcon(categoryId),
|
|
count: count,
|
|
type: 'fontawesome'
|
|
});
|
|
});
|
|
|
|
// Ordenar categorías
|
|
categories.sort((a, b) => {
|
|
// Categorías especiales primero
|
|
if (a.type === 'special' && b.type !== 'special') return -1;
|
|
if (a.type !== 'special' && b.type === 'special') return 1;
|
|
|
|
// Luego por nombre
|
|
return a.name.localeCompare(b.name);
|
|
});
|
|
|
|
// Guardar categorías
|
|
const categoriesOutput = {
|
|
meta: {
|
|
generated: new Date().toISOString(),
|
|
totalCategories: categories.length,
|
|
fontawesomeCategories: Object.keys(categoryCounts).length
|
|
},
|
|
categories: categories,
|
|
mapping: categoriesMap
|
|
};
|
|
|
|
fs.writeFileSync(CONFIG.OUTPUT_CATEGORIES, JSON.stringify(categoriesOutput, null, 2), 'utf8');
|
|
console.log(` ✅ Categorías guardadas: ${CONFIG.OUTPUT_CATEGORIES}`);
|
|
console.log(` 📊 Total categorías: ${categories.length}`);
|
|
|
|
// Mostrar top 10 categorías
|
|
console.log('\n 🏆 TOP 10 CATEGORÍAS:');
|
|
categories
|
|
.filter(cat => cat.type === 'fontawesome')
|
|
.sort((a, b) => b.count - a.count)
|
|
.slice(0, 10)
|
|
.forEach((cat, index) => {
|
|
console.log(` ${index + 1}. ${cat.name.padEnd(25)}: ${cat.count} íconos`);
|
|
});
|
|
|
|
return categoriesOutput;
|
|
}
|
|
|
|
/**
|
|
* Formatear nombre de categoría
|
|
*/
|
|
function formatCategoryName(categoryId) {
|
|
return categoryId
|
|
.split('-')
|
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
.join(' ');
|
|
}
|
|
|
|
/**
|
|
* Obtener ícono para categoría
|
|
*/
|
|
function getCategoryIcon(categoryId) {
|
|
const iconMap = {
|
|
'accessibility': 'fas fa-universal-access',
|
|
'alert': 'fas fa-exclamation-triangle',
|
|
'animals': 'fas fa-paw',
|
|
'arrows': 'fas fa-arrow-right',
|
|
'buildings': 'fas fa-building',
|
|
'business': 'fas fa-briefcase',
|
|
'coding': 'fas fa-code',
|
|
'communication': 'fas fa-comments',
|
|
'design': 'fas fa-palette',
|
|
'devices-hardware': 'fas fa-laptop',
|
|
'education': 'fas fa-graduation-cap',
|
|
'files': 'fas fa-folder',
|
|
'food-beverage': 'fas fa-utensils',
|
|
'health': 'fas fa-heartbeat',
|
|
'household': 'fas fa-home',
|
|
'maps': 'fas fa-map',
|
|
'music-audio': 'fas fa-music',
|
|
'nature': 'fas fa-leaf',
|
|
'shopping': 'fas fa-shopping-cart',
|
|
'sports-fitness': 'fas fa-football-ball',
|
|
'travel': 'fas fa-plane',
|
|
'users-people': 'fas fa-user-friends',
|
|
'weather': 'fas fa-cloud-sun',
|
|
'writing': 'fas fa-pen-fancy'
|
|
};
|
|
|
|
return iconMap[categoryId] || 'fas fa-folder';
|
|
}
|
|
|
|
/**
|
|
* Generar reporte de procesamiento
|
|
*/
|
|
function generateReport(metadata, styles, categories) {
|
|
console.log(colors.blue + '\n📋 Generando reporte de procesamiento...' + colors.reset);
|
|
|
|
const reportPath = path.join(CONFIG.OUTPUT_DIR, 'logs', `report-${new Date().toISOString().replace(/[:.]/g, '-')}.json`);
|
|
|
|
const report = {
|
|
meta: {
|
|
generated: new Date().toISOString(),
|
|
version: CONFIG.VERSION,
|
|
duration: new Date() - startTime
|
|
},
|
|
summary: {
|
|
totalIcons: metadata.meta.totalIcons,
|
|
totalStyles: styles.meta.totalStyles,
|
|
stylesWithIcons: styles.meta.stylesWithIcons,
|
|
totalCategories: categories.meta.totalCategories
|
|
},
|
|
files: {
|
|
metadata: CONFIG.OUTPUT_METADATA,
|
|
styles: CONFIG.OUTPUT_STYLES,
|
|
categories: CONFIG.OUTPUT_CATEGORIES
|
|
},
|
|
statistics: {
|
|
iconsPerStyle: metadata.statistics.iconsPerStyle,
|
|
iconsPerCategory: metadata.statistics.iconsPerCategory,
|
|
styleCoverage: styles.statistics.styleCoverage
|
|
}
|
|
};
|
|
|
|
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2), 'utf8');
|
|
console.log(` ✅ Reporte guardado: ${reportPath}`);
|
|
|
|
return report;
|
|
}
|
|
|
|
/**
|
|
* Función principal
|
|
*/
|
|
async function main() {
|
|
global.startTime = new Date();
|
|
|
|
try {
|
|
console.log(colors.green + '🚀 Iniciando extracción de metadata...' + colors.reset);
|
|
|
|
// 1. Crear directorios
|
|
createDirectories();
|
|
|
|
// 2. Verificar archivos de entrada
|
|
if (!checkInputFiles()) {
|
|
console.log(colors.red + '❌ Archivos requeridos no encontrados. Abortando.' + colors.reset);
|
|
process.exit(1);
|
|
}
|
|
|
|
// 3. Hacer backup
|
|
backupExistingFiles();
|
|
|
|
// 4. Extraer metadata de íconos
|
|
const metadata = extractIconMetadata();
|
|
|
|
// 5. Extraer información de estilos
|
|
const styles = extractStyleInfo(metadata);
|
|
|
|
// 6. Extraer y procesar categorías
|
|
const categories = extractCategories(metadata);
|
|
|
|
// 7. Generar reporte
|
|
const report = generateReport(metadata, styles, categories);
|
|
|
|
// 8. Mostrar resumen final
|
|
const duration = ((new Date() - startTime) / 1000).toFixed(2);
|
|
|
|
console.log(colors.green + '\n' + '='.repeat(70));
|
|
console.log(' ✅ EXTRACCIÓN COMPLETADA EXITOSAMENTE');
|
|
console.log('='.repeat(70) + colors.reset);
|
|
|
|
console.log(`\n📊 RESUMEN FINAL:`);
|
|
console.log(` • Íconos procesados: ${metadata.meta.totalIcons}`);
|
|
console.log(` • Estilos encontrados: ${styles.meta.stylesWithIcons}/${styles.meta.totalStyles}`);
|
|
console.log(` • Categorías: ${categories.meta.totalCategories}`);
|
|
console.log(` • Tiempo total: ${duration} segundos`);
|
|
console.log(`\n📁 ARCHIVOS GENERADOS:`);
|
|
console.log(` • ${path.relative(process.cwd(), CONFIG.OUTPUT_METADATA)}`);
|
|
console.log(` • ${path.relative(process.cwd(), CONFIG.OUTPUT_STYLES)}`);
|
|
console.log(` • ${path.relative(process.cwd(), CONFIG.OUTPUT_CATEGORIES)}`);
|
|
console.log(`\n🚀 PRÓXIMOS PASOS:`);
|
|
console.log(` • Ejecuta: npm run build`);
|
|
console.log(` • Luego instala el plugin en Logseq`);
|
|
|
|
console.log(colors.green + '\n🎉 ¡Listo para construir la base de datos final!' + colors.reset);
|
|
|
|
} catch (error) {
|
|
console.error(colors.red + '\n❌ ERROR CRÍTICO:' + colors.reset);
|
|
console.error(` ${error.message}`);
|
|
console.error(`\n🔧 Solución:`);
|
|
console.error(` 1. Verifica que tengas FontAwesome 6 Pro instalado`);
|
|
console.error(` 2. Asegúrate de que metadata/icons.json sea válido`);
|
|
console.error(` 3. Intenta ejecutar 'npm run verify' primero`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Ejecutar
|
|
if (require.main === module) {
|
|
main();
|
|
}
|
|
|
|
// Exportar funciones para uso en otros scripts
|
|
module.exports = {
|
|
extractIconMetadata,
|
|
extractStyleInfo,
|
|
extractCategories,
|
|
createDirectories,
|
|
checkInputFiles,
|
|
CONFIG
|
|
};
|