Skip to main content
Glama

Lighthouse MCP

by mizchi
generate-impact-report.tsโ€ข12.6 kB
#!/usr/bin/env tsx /** * Generate detailed third-party impact report * * Usage: pnpm tsx scripts/generate-impact-report.ts */ import { executeL2ThirdPartyImpact } from '../src/tools/l2-third-party-impact.js'; import { executeL2PerformanceAnalysis } from '../src/tools/l2-performance-analysis.js'; import { executeL2UnusedCode } from '../src/tools/l2-unused-code.js'; import * as fs from 'fs/promises'; interface SiteAnalysis { url: string; name: string; thirdParty?: any; performance?: any; unusedCode?: any; } async function analyzeSiteDetails(url: string, siteName: string): Promise<SiteAnalysis> { console.log(`\n๐Ÿ“Š Analyzing ${siteName}...`); const result: SiteAnalysis = { url, name: siteName }; try { // 1. Third-party analysis console.log(' โ€ข Collecting third-party data...'); const thirdPartyResult = await executeL2ThirdPartyImpact({ url, device: 'mobile', compareMode: 'analyze', gather: true, }); result.thirdParty = thirdPartyResult.analysis; // 2. Performance analysis console.log(' โ€ข Analyzing performance patterns...'); const performanceResult = await executeL2PerformanceAnalysis({ url, device: 'mobile', gather: false, // Use cached }); result.performance = performanceResult; // 3. Unused code analysis console.log(' โ€ข Detecting unused code...'); const unusedCodeResult = await executeL2UnusedCode({ url, device: 'mobile', }); result.unusedCode = unusedCodeResult.unusedCode; } catch (error) { console.error(` โŒ Error: ${error}`); } return result; } function generateMarkdownReport(analyses: SiteAnalysis[]): string { let report = `# Third-Party Script Impact Report\n\n`; report += `Generated: ${new Date().toISOString()}\n\n`; report += `## Executive Summary\n\n`; // Summary table report += `| Site | Third-Party Count | Total Blocking (ms) | Performance Score | Unused Code (KB) |\n`; report += `|------|-------------------|---------------------|-------------------|------------------|\n`; for (const analysis of analyses) { const tp = analysis.thirdParty?.summary; const perf = analysis.performance; const unused = analysis.unusedCode; report += `| ${analysis.name} `; report += `| ${tp?.entityCount || 'N/A'} `; report += `| ${tp ? Math.round(tp.totalBlockingTime) : 'N/A'} `; report += `| ${perf?.score || 'N/A'} `; report += `| ${unused ? Math.round(unused.totalWastedBytes / 1024) : 'N/A'} |\n`; } report += `\n---\n\n`; // Detailed analysis for each site for (const analysis of analyses) { report += `## ${analysis.name}\n\n`; report += `URL: ${analysis.url}\n\n`; if (analysis.thirdParty) { const tp = analysis.thirdParty; report += `### ๐ŸŽฏ Third-Party Impact\n\n`; report += `**Summary:**\n`; report += `- Total Entities: ${tp.summary.entityCount}\n`; report += `- Total Blocking Time: ${Math.round(tp.summary.totalBlockingTime)}ms\n`; report += `- Total Main Thread Time: ${Math.round(tp.summary.totalMainThreadTime)}ms\n`; report += `- Total Transfer Size: ${Math.round(tp.summary.totalTransferSize / 1024)}KB\n\n`; report += `**Top 10 Heaviest Scripts:**\n\n`; report += `| Rank | Entity | Blocking (ms) | Main Thread (ms) | Transfer (KB) | Scripts | Impact |\n`; report += `|------|--------|---------------|------------------|---------------|---------|--------|\n`; const topEntities = tp.entities.slice(0, 10); topEntities.forEach((entity: any, i: number) => { const impact = entity.blockingTime > 1000 ? '๐Ÿ”ด Critical' : entity.blockingTime > 500 ? '๐ŸŸ  High' : entity.blockingTime > 250 ? '๐ŸŸก Medium' : '๐ŸŸข Low'; report += `| ${i + 1} `; report += `| ${entity.entity} `; report += `| ${Math.round(entity.blockingTime)} `; report += `| ${Math.round(entity.mainThreadTime)} `; report += `| ${Math.round(entity.transferSize / 1024)} `; report += `| ${entity.subRequests.length} `; report += `| ${impact} |\n`; }); report += `\n**Blocking Time Distribution:**\n`; const critical = tp.entities.filter((e: any) => e.blockingTime > 1000).length; const high = tp.entities.filter((e: any) => e.blockingTime > 500 && e.blockingTime <= 1000).length; const medium = tp.entities.filter((e: any) => e.blockingTime > 250 && e.blockingTime <= 500).length; const low = tp.entities.filter((e: any) => e.blockingTime <= 250).length; report += `- ๐Ÿ”ด Critical (>1000ms): ${critical} scripts\n`; report += `- ๐ŸŸ  High (500-1000ms): ${high} scripts\n`; report += `- ๐ŸŸก Medium (250-500ms): ${medium} scripts\n`; report += `- ๐ŸŸข Low (<250ms): ${low} scripts\n\n`; // Script URLs for top offenders report += `**Detailed Script Breakdown (Top 3):**\n\n`; tp.entities.slice(0, 3).forEach((entity: any, i: number) => { report += `<details>\n`; report += `<summary>${i + 1}. ${entity.entity} (${Math.round(entity.blockingTime)}ms blocking)</summary>\n\n`; report += `| URL | Blocking (ms) | Transfer (KB) |\n`; report += `|-----|---------------|---------------|\n`; entity.subRequests.slice(0, 5).forEach((req: any) => { const shortUrl = req.url.length > 60 ? req.url.substring(0, 57) + '...' : req.url; report += `| ${shortUrl} `; report += `| ${Math.round(req.blockingTime)} `; report += `| ${Math.round(req.transferSize / 1024)} |\n`; }); if (entity.subRequests.length > 5) { report += `\n*... and ${entity.subRequests.length - 5} more scripts*\n`; } report += `\n</details>\n\n`; }); } if (analysis.performance) { const perf = analysis.performance; report += `### ๐Ÿ“ˆ Performance Metrics\n\n`; report += `**Core Web Vitals:**\n`; report += `- LCP: ${perf.metrics.lcp ? Math.round(perf.metrics.lcp) + 'ms' : 'N/A'}\n`; report += `- FCP: ${perf.metrics.fcp ? Math.round(perf.metrics.fcp) + 'ms' : 'N/A'}\n`; report += `- CLS: ${perf.metrics.cls || 'N/A'}\n`; report += `- TBT: ${perf.metrics.tbt ? Math.round(perf.metrics.tbt) + 'ms' : 'N/A'}\n`; report += `- TTI: ${perf.metrics.tti ? Math.round(perf.metrics.tti) + 'ms' : 'N/A'}\n\n`; if (perf.problems && perf.problems.length > 0) { report += `**Top Performance Problems:**\n`; perf.problems.slice(0, 5).forEach((problem: any, i: number) => { const severity = problem.severity === 'critical' ? '๐Ÿ”ด' : problem.severity === 'high' ? '๐ŸŸ ' : problem.severity === 'medium' ? '๐ŸŸก' : '๐ŸŸข'; report += `${i + 1}. ${severity} ${problem.description} (${problem.category})\n`; }); report += `\n`; } } if (analysis.unusedCode) { const unused = analysis.unusedCode; report += `### ๐Ÿ—‘๏ธ Unused Code\n\n`; report += `**Summary:**\n`; report += `- Total Wasted: ${Math.round(unused.totalWastedBytes / 1024)}KB\n`; report += `- Files with Unused Code: ${unused.files.length}\n\n`; if (unused.files.length > 0) { report += `**Top Unused Code by File:**\n\n`; report += `| File | Wasted (KB) | Wasted % | Total (KB) | Type |\n`; report += `|------|-------------|----------|------------|------|\n`; unused.files.slice(0, 10).forEach((file: any) => { const shortUrl = file.url.length > 50 ? '...' + file.url.substring(file.url.length - 47) : file.url; report += `| ${shortUrl} `; report += `| ${Math.round(file.wastedBytes / 1024)} `; report += `| ${file.wastedPercent}% `; report += `| ${Math.round(file.totalBytes / 1024)} `; report += `| ${file.type} |\n`; }); } } report += `\n---\n\n`; } // Recommendations section report += `## ๐Ÿ“‹ Recommendations\n\n`; for (const analysis of analyses) { report += `### ${analysis.name}\n\n`; const tp = analysis.thirdParty; if (tp && tp.entities) { const criticalScripts = tp.entities.filter((e: any) => e.blockingTime > 1000); if (criticalScripts.length > 0) { report += `**Critical Actions Required:**\n`; criticalScripts.slice(0, 3).forEach((entity: any) => { report += `- ๐Ÿ”ด **${entity.entity}**: ${Math.round(entity.blockingTime)}ms blocking\n`; report += ` - Consider lazy loading or removing if not essential\n`; report += ` - Investigate async/defer loading options\n`; }); report += `\n`; } const highImpactScripts = tp.entities.filter((e: any) => e.blockingTime > 500 && e.blockingTime <= 1000); if (highImpactScripts.length > 0) { report += `**High Priority Optimizations:**\n`; highImpactScripts.slice(0, 3).forEach((entity: any) => { report += `- ๐ŸŸ  **${entity.entity}**: ${Math.round(entity.blockingTime)}ms blocking\n`; }); report += `\n`; } } if (analysis.unusedCode && analysis.unusedCode.totalWastedBytes > 100000) { report += `**Code Optimization:**\n`; report += `- Remove or tree-shake ${Math.round(analysis.unusedCode.totalWastedBytes / 1024)}KB of unused code\n`; report += `- Focus on files with >50% unused code\n\n`; } } // Technical details report += `## ๐Ÿ”ง Technical Analysis\n\n`; report += `### Script Loading Patterns\n\n`; for (const analysis of analyses) { if (analysis.thirdParty) { report += `**${analysis.name}:**\n`; // Group by category const categories: { [key: string]: any[] } = {}; analysis.thirdParty.entities.forEach((entity: any) => { const category = entity.entity.includes('Google') ? 'Google/Analytics' : entity.entity.includes('Amazon') ? 'Amazon/Ads' : entity.entity.includes('Facebook') ? 'Facebook/Social' : entity.entity.includes('Tag') ? 'Tag Management' : entity.entity.includes('Ad') ? 'Advertising' : 'Other'; if (!categories[category]) categories[category] = []; categories[category].push(entity); }); Object.entries(categories).forEach(([cat, entities]) => { const totalBlocking = entities.reduce((sum, e) => sum + e.blockingTime, 0); const totalSize = entities.reduce((sum, e) => sum + e.transferSize, 0); report += `- ${cat}: ${entities.length} entities, ${Math.round(totalBlocking)}ms blocking, ${Math.round(totalSize / 1024)}KB\n`; }); report += `\n`; } } report += `\n## ๐Ÿ“Š Methodology\n\n`; report += `This report analyzes:\n`; report += `1. **Third-party scripts**: External JavaScript and resources loaded from different domains\n`; report += `2. **Blocking time**: Time the main thread is blocked by script execution\n`; report += `3. **Transfer size**: Network bytes transferred for each script\n`; report += `4. **Unused code**: JavaScript and CSS bytes that are downloaded but never executed\n\n`; report += `Impact levels:\n`; report += `- ๐Ÿ”ด **Critical**: >1000ms blocking time - Severe performance impact\n`; report += `- ๐ŸŸ  **High**: 500-1000ms - Significant impact\n`; report += `- ๐ŸŸก **Medium**: 250-500ms - Noticeable impact\n`; report += `- ๐ŸŸข **Low**: <250ms - Minor impact\n`; return report; } async function main() { console.log('๐Ÿš€ Generating Third-Party Impact Report\n'); const sites = [ { url: 'https://www.nicovideo.jp/', name: 'ใƒ‹ใ‚ณใƒ‹ใ‚ณๅ‹•็”ป (nicovideo.jp)' }, { url: 'https://www.goal.com/', name: 'Goal.com' }, ]; const analyses: SiteAnalysis[] = []; for (const site of sites) { const analysis = await analyzeSiteDetails(site.url, site.name); analyses.push(analysis); } console.log('\n๐Ÿ“ Generating report...'); const report = generateMarkdownReport(analyses); const filename = `third-party-impact-report-${new Date().toISOString().split('T')[0]}.md`; await fs.writeFile(filename, report); console.log(`\nโœ… Report saved to: ${filename}`); console.log('\nReport preview:'); console.log('================'); console.log(report.substring(0, 500) + '...\n'); } main().catch(console.error);

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mizchi/lighthouse-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server