|
|
|
|
|
|
|
|
|
|
|
const fs = require('fs'); |
|
|
const path = require('path'); |
|
|
|
|
|
console.log('🔍 ANÁLISE DE PERFORMANCE DO BUNDLE\n'); |
|
|
console.log('═'.repeat(80)); |
|
|
|
|
|
|
|
|
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]; |
|
|
} |
|
|
|
|
|
|
|
|
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, |
|
|
|
|
|
gzipEstimate: formatBytes(Math.round(stats.size * 0.3)) |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
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))}`); |
|
|
|
|
|
|
|
|
console.log('\n═'.repeat(80)); |
|
|
console.log('🔬 ANÁLISE DETALHADA DO app.js\n'); |
|
|
|
|
|
try { |
|
|
const appJs = fs.readFileSync('public/app.js', 'utf8'); |
|
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
const commentLines = (appJs.match(/\/\/.+|\/\*[\s\S]*?\*\//g) || []).length; |
|
|
|
|
|
|
|
|
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}`); |
|
|
|
|
|
|
|
|
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'); |
|
|
} |
|
|
|
|
|
|
|
|
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'); |
|
|
|
|
|
|
|
|
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'); |
|
|
|