K30 / scripts /analyze-bundle.js
Raí Santos
oi
98ace4c
// 📊 Script de Análise de Performance do Bundle
// Analisa o tamanho dos arquivos e identifica oportunidades de otimização
const fs = require('fs');
const path = require('path');
console.log('🔍 ANÁLISE DE PERFORMANCE DO BUNDLE\n');
console.log('═'.repeat(80));
// Função para calcular tamanho legível
function formatBytes(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
}
// Função para analisar arquivo
function analyzeFile(filePath) {
const stats = fs.statSync(filePath);
const content = fs.readFileSync(filePath, 'utf8');
return {
path: filePath,
size: stats.size,
sizeFormatted: formatBytes(stats.size),
lines: content.split('\n').length,
characters: content.length,
// Estimativa de tamanho gzipped (aproximadamente 30% do original para texto)
gzipEstimate: formatBytes(Math.round(stats.size * 0.3))
};
}
// Arquivos principais para análise
const filesToAnalyze = [
'public/app.js',
'public/styles.css',
'public/index.html',
'public/sw.js',
'public/manifest.json'
];
let totalSize = 0;
const results = [];
console.log('\n📁 ARQUIVOS PRINCIPAIS:\n');
filesToAnalyze.forEach(file => {
try {
const analysis = analyzeFile(file);
results.push(analysis);
totalSize += analysis.size;
console.log(`📄 ${path.basename(file)}`);
console.log(` Tamanho: ${analysis.sizeFormatted}`);
console.log(` Linhas: ${analysis.lines.toLocaleString()}`);
console.log(` Gzip (est.): ${analysis.gzipEstimate}`);
console.log('');
} catch (error) {
console.log(`⚠️ ${file} - Arquivo não encontrado`);
}
});
console.log('─'.repeat(80));
console.log(`\n📊 TOTAL: ${formatBytes(totalSize)}`);
console.log(`📦 GZIP ESTIMADO: ${formatBytes(Math.round(totalSize * 0.3))}`);
// Análise de app.js para identificar oportunidades
console.log('\n═'.repeat(80));
console.log('🔬 ANÁLISE DETALHADA DO app.js\n');
try {
const appJs = fs.readFileSync('public/app.js', 'utf8');
// Contar funções
const functionCount = (appJs.match(/function\s+\w+/g) || []).length;
const arrowFunctionCount = (appJs.match(/=>\s*{/g) || []).length;
const classCount = (appJs.match(/class\s+\w+/g) || []).length;
// Contar comentários
const commentLines = (appJs.match(/\/\/.+|\/\*[\s\S]*?\*\//g) || []).length;
// Contar imports/requires (se houver)
const imports = (appJs.match(/import\s+.+from|require\(.+\)/g) || []).length;
console.log(`📦 Classes: ${classCount}`);
console.log(`🔧 Funções: ${functionCount}`);
console.log(`➡️ Arrow Functions: ${arrowFunctionCount}`);
console.log(`💬 Comentários: ${commentLines}`);
console.log(`📥 Imports: ${imports}`);
// Análise de strings grandes (possíveis JSON embutidos)
const largeStrings = appJs.match(/['"`][\s\S]{500,}['"`]/g) || [];
if (largeStrings.length > 0) {
console.log(`\n⚠️ ${largeStrings.length} strings grandes encontradas (>500 chars)`);
console.log(` Considere mover para arquivos JSON externos`);
}
} catch (error) {
console.log('⚠️ Erro ao analisar app.js');
}
// Recomendações
console.log('\n═'.repeat(80));
console.log('💡 RECOMENDAÇÕES DE OTIMIZAÇÃO\n');
const recommendations = [];
if (totalSize > 300000) {
recommendations.push({
priority: 'ALTA',
issue: 'Bundle muito grande (>300KB)',
solution: 'Implementar code splitting e lazy loading'
});
}
if (results.find(r => r.path.includes('app.js') && r.size > 150000)) {
recommendations.push({
priority: 'ALTA',
issue: 'app.js muito grande (>150KB)',
solution: 'Dividir em módulos menores e carregar sob demanda'
});
}
if (results.find(r => r.path.includes('styles.css') && r.size > 50000)) {
recommendations.push({
priority: 'MÉDIA',
issue: 'styles.css grande (>50KB)',
solution: 'Remover CSS não utilizado com PurgeCSS'
});
}
recommendations.push({
priority: 'MÉDIA',
issue: 'Sem minificação',
solution: 'Implementar minificação com Terser (JS) e cssnano (CSS)'
});
recommendations.push({
priority: 'ALTA',
issue: 'Sem compressão Gzip/Brotli',
solution: 'Configurar compressão no servidor'
});
recommendations.push({
priority: 'BAIXA',
issue: 'Otimização de imagens',
solution: 'Converter SVGs para WebP quando apropriado'
});
recommendations.forEach((rec, index) => {
console.log(`${index + 1}. [${rec.priority}] ${rec.issue}`);
console.log(` 💡 ${rec.solution}\n`);
});
console.log('═'.repeat(80));
console.log('\n✅ Análise completa! Execute os scripts de otimização para melhorar o desempenho.\n');
// Salvar relatório em JSON
const report = {
timestamp: new Date().toISOString(),
files: results,
totalSize: totalSize,
totalSizeFormatted: formatBytes(totalSize),
gzipEstimate: formatBytes(Math.round(totalSize * 0.3)),
recommendations: recommendations
};
fs.writeFileSync('scripts/bundle-analysis.json', JSON.stringify(report, null, 2));
console.log('📄 Relatório salvo em: scripts/bundle-analysis.json\n');