#!/usr/bin/env python3 """ Lighthouse Performance Audit Script Runs Lighthouse audits on specified URLs and reports results """ import subprocess import json import sys from pathlib import Path def run_lighthouse(url, output_file=None): """Run Lighthouse audit on a URL""" cmd = [ 'npx', 'lighthouse', url, '--output=json', '--output-path=stdout', '--chrome-flags="--headless --no-sandbox"', '--quiet' ] try: result = subprocess.run(cmd, capture_output=True, text=True, timeout=60) if result.returncode != 0: print(f"Error running Lighthouse on {url}: {result.stderr}") return None data = json.loads(result.stdout) return data except subprocess.TimeoutExpired: print(f"Timeout running Lighthouse on {url}") return None except json.JSONDecodeError: print(f"Failed to parse Lighthouse output for {url}") return None except Exception as e: print(f"Unexpected error for {url}: {e}") return None def extract_scores(lighthouse_data): """Extract key scores from Lighthouse data""" if not lighthouse_data or 'categories' not in lighthouse_data: return None categories = lighthouse_data['categories'] return { 'performance': int(categories.get('performance', {}).get('score', 0) * 100), 'accessibility': int(categories.get('accessibility', {}).get('score', 0) * 100), 'best-practices': int(categories.get('best-practices', {}).get('score', 0) * 100), 'seo': int(categories.get('seo', {}).get('score', 0) * 100), } def get_issues(lighthouse_data): """Extract issues from Lighthouse audit""" if not lighthouse_data or 'audits' not in lighthouse_data: return [] issues = [] audits = lighthouse_data['audits'] # Key audits to check important_audits = [ 'first-contentful-paint', 'largest-contentful-paint', 'cumulative-layout-shift', 'total-blocking-time', 'speed-index', 'interactive', 'image-alt', 'document-title', 'meta-description', 'link-text', 'crawlable-anchors', 'color-contrast', 'tap-targets', 'viewport', 'uses-responsive-images', 'modern-image-formats', 'offscreen-images', 'render-blocking-resources', 'unminified-css', 'unminified-javascript', 'unused-css-rules', 'unused-javascript', ] for audit_id in important_audits: if audit_id in audits: audit = audits[audit_id] if audit.get('score') is not None and audit['score'] < 0.9: issues.append({ 'id': audit_id, 'title': audit.get('title', audit_id), 'score': audit.get('score'), 'displayValue': audit.get('displayValue', ''), 'description': audit.get('description', ''), }) return issues def main(): if len(sys.argv) < 2: print("Usage: python lighthouse_audit.py ") sys.exit(1) url = sys.argv[1] print(f"\nšŸ” Running Lighthouse audit on: {url}\n") data = run_lighthouse(url) if not data: print("āŒ Failed to run Lighthouse audit") sys.exit(1) scores = extract_scores(data) if not scores: print("āŒ Failed to extract scores") sys.exit(1) # Print results print("=" * 60) print("LIGHTHOUSE SCORES") print("=" * 60) for category, score in scores.items(): emoji = "āœ…" if score >= 90 else "āš ļø" if score >= 50 else "āŒ" print(f"{emoji} {category.upper():20s}: {score:3d}/100") print("\n" + "=" * 60) print("ISSUES FOUND") print("=" * 60 + "\n") issues = get_issues(data) if issues: for issue in issues[:10]: # Show top 10 issues score_str = f"{int(issue['score'] * 100)}/100" if issue['score'] is not None else "N/A" print(f"āš ļø {issue['title']}") print(f" Score: {score_str}") if issue['displayValue']: print(f" Value: {issue['displayValue']}") print() else: print("āœ… No major issues found!\n") # Check if all scores >= 90 all_passed = all(score >= 90 for score in scores.values()) if all_passed: print("šŸŽ‰ All scores are 90+! Great job!\n") sys.exit(0) else: print("āš ļø Some scores are below 90. Review issues above.\n") sys.exit(1) if __name__ == '__main__': main()