Skip to main content
Glama

Lighthouse MCP

by mizchi
resourcePerformance.tsโ€ข7.47 kB
import type { LighthouseReport } from '../types'; export interface JavaScriptAnalysis { totalExecutionTime: number; mainThreadImpact: number; files: Array<{ url: string; executionTime: number; parseTime: number; impact: 'low' | 'medium' | 'high'; }>; recommendations: string[]; } export interface CSSAnalysis { processingTime: number; renderingImpact: number; paintingImpact: number; totalImpact: number; unminifiedFiles: Array<{ url: string; currentSize: number; potentialSavings: number; }>; recommendations: string[]; } export interface ImageOptimization { totalSavingsBytes: number; opportunities: Array<{ type: 'webp' | 'compression' | 'sizing'; savingsBytes: number; files: Array<{ url: string; currentSize: number; savings: number; }>; }>; largestImpact?: { url: string; currentSize: number; potentialSavings: number; }; recommendations: string[]; } export interface ResourcePerformanceAnalysis { javascript?: JavaScriptAnalysis; css?: CSSAnalysis; images?: ImageOptimization; overallScore?: number; bottlenecks?: string[]; } export function analyzeResourcePerformance(report: LighthouseReport): ResourcePerformanceAnalysis { const result: ResourcePerformanceAnalysis = {}; const bottlenecks: string[] = []; // Analyze JavaScript execution const bootupTimeAudit = report.audits?.['bootup-time']; const mainThreadAudit = report.audits?.['mainthread-work-breakdown']; if (bootupTimeAudit?.numericValue !== undefined || bootupTimeAudit?.details?.items) { const totalExecutionTime = bootupTimeAudit.numericValue || 0; let files: any[] = []; if (bootupTimeAudit.details?.items) { const items = bootupTimeAudit.details.items as any[]; files = items.map(item => ({ url: item.url, executionTime: item.scripting || 0, parseTime: item.scriptParseCompile || 0, impact: item.scripting > 1000 ? 'high' as const : item.scripting > 500 ? 'medium' as const : 'low' as const })); } // Get main thread impact let mainThreadImpact = 0; if (mainThreadAudit?.details?.items) { const mainThreadItems = mainThreadAudit.details.items as any[]; const scriptItem = mainThreadItems.find(i => i.group === 'Script Evaluation' || i.group === 'scriptEvaluation' || i.groupLabel?.includes('Script') ); mainThreadImpact = scriptItem?.duration || 0; } const recommendations: string[] = []; if (totalExecutionTime > 3000) { recommendations.push('Consider code splitting'); bottlenecks.push('JavaScript execution'); } else if (totalExecutionTime >= 2000) { bottlenecks.push('JavaScript execution'); } if (files.some(f => f.parseTime > 300)) { recommendations.push('Optimize JavaScript bundle size'); } result.javascript = { totalExecutionTime, mainThreadImpact, files, recommendations }; } // Analyze CSS processing if (mainThreadAudit?.details?.items) { const items = mainThreadAudit.details.items as any[]; const styleItem = items.find(i => i.group === 'Style & Layout' || i.group === 'styleLayout' || i.groupLabel?.includes('Style') ); const renderItem = items.find(i => i.group === 'Rendering' || i.groupLabel?.includes('Render') ); const paintItem = items.find(i => i.group === 'Painting' || i.groupLabel?.includes('Paint') ); const processingTime = styleItem?.duration || 0; const renderingImpact = renderItem?.duration || 0; const paintingImpact = paintItem?.duration || 0; const unminifiedFiles: any[] = []; const unminifiedAudit = report.audits?.['unminified-css']; if (unminifiedAudit?.details?.items) { const cssItems = unminifiedAudit.details.items as any[]; cssItems.forEach(item => { unminifiedFiles.push({ url: item.url, currentSize: item.totalBytes || 0, potentialSavings: item.wastedBytes || 0 }); }); } const recommendations: string[] = []; if (processingTime > 1000) { recommendations.push('Reduce CSS complexity'); bottlenecks.push('CSS processing'); } if (unminifiedFiles.length > 0) { recommendations.push('Minify CSS files'); } result.css = { processingTime, renderingImpact, paintingImpact, totalImpact: processingTime + renderingImpact + paintingImpact, unminifiedFiles, recommendations }; } // Analyze image optimization const imageAudits = [ { audit: 'uses-webp-images', type: 'webp' as const }, { audit: 'uses-optimized-images', type: 'compression' as const }, { audit: 'uses-responsive-images', type: 'sizing' as const } ]; let totalSavingsBytes = 0; const opportunities: any[] = []; const allImageFiles = new Map<string, { size: number; savings: number }>(); imageAudits.forEach(({ audit, type }) => { const auditResult = report.audits?.[audit]; if (auditResult?.details?.items) { const items = auditResult.details.items as any[]; const savingsBytes = auditResult.details.overallSavingsBytes || 0; totalSavingsBytes += savingsBytes; const files = items.map(item => { const existing = allImageFiles.get(item.url) || { size: item.totalBytes || 0, savings: 0 }; existing.savings += item.wastedBytes || 0; allImageFiles.set(item.url, existing); return { url: item.url, currentSize: item.totalBytes || 0, savings: item.wastedBytes || 0 }; }); if (savingsBytes > 0) { opportunities.push({ type, savingsBytes, files }); } } }); if (totalSavingsBytes > 0) { // Find largest impact image let largestImpact = { url: '', currentSize: 0, potentialSavings: 0 }; allImageFiles.forEach((data, url) => { if (data.savings > largestImpact.potentialSavings) { largestImpact = { url, currentSize: data.size, potentialSavings: data.savings }; } }); const recommendations: string[] = []; if (opportunities.some(o => o.type === 'webp')) { recommendations.push('Convert images to WebP format'); } if (opportunities.some(o => o.type === 'compression')) { recommendations.push('Optimize image compression'); } if (opportunities.some(o => o.type === 'sizing')) { recommendations.push('Use responsive images'); } if (totalSavingsBytes > 500000) { bottlenecks.push('Image optimization'); } result.images = { totalSavingsBytes, opportunities, largestImpact: largestImpact.url ? largestImpact : undefined, recommendations }; } // Calculate overall score const scores = []; if (bootupTimeAudit?.score !== undefined) scores.push(bootupTimeAudit.score); if (mainThreadAudit?.score !== undefined) scores.push(mainThreadAudit.score); if (report.audits?.['uses-webp-images']?.score !== undefined) { scores.push(report.audits['uses-webp-images'].score); } if (scores.length > 0) { result.overallScore = (scores.reduce((sum, s) => sum + s, 0) / scores.length) * 100; } if (bottlenecks.length > 0) { result.bottlenecks = bottlenecks; } return result; }

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