perf-audit-cli
Version:
CLI tool for continuous performance monitoring and analysis
151 lines • 6.43 kB
JavaScript
import path from 'path';
import { LighthouseRunner } from "../core/lighthouse-runner.js";
import { exitBasedOnStatus, getCurrentTimestamp, initializeCommand, saveBuildData } from "../utils/command-helpers.js";
import { Logger } from "../utils/logger.js";
import { ReportGenerator } from "../utils/report-generator.js";
import { ConsoleReporter } from "../utils/reporter.js";
export async function lighthouseCommand(url, options) {
const { config, spinner } = await initializeCommand();
if (!LighthouseRunner.validateUrl(url)) {
spinner.fail('Invalid URL provided');
Logger.error('Please provide a valid HTTP or HTTPS URL');
process.exit(1);
}
try {
spinner.text = `Running Lighthouse audit for ${options.device} device...`;
const runner = new LighthouseRunner();
const result = await runner.runAudit({
url,
device: options.device,
throttling: options.throttling,
outputFormat: options.format === 'json' ? 'json' : undefined,
});
const budgetStatus = evaluatePerformanceBudgets(result, config.budgets);
const auditResult = {
timestamp: getCurrentTimestamp(),
serverBundles: [],
clientBundles: [],
lighthouse: result,
recommendations: generatePerformanceRecommendations(result),
budgetStatus,
analysisType: 'client',
};
await saveBuildData(auditResult, { url, device: options.device, metrics: result });
spinner.succeed('Lighthouse audit completed');
switch (options.format) {
case 'json': {
const outputPath = path.join(config.reports.outputDir, `lighthouse-${options.device}-${Date.now()}.json`);
ReportGenerator.generateJsonReport(auditResult, outputPath);
Logger.success(`JSON report saved to: ${outputPath}`);
const output = {
url,
device: options.device,
throttling: options.throttling,
timestamp: auditResult.timestamp,
scores: {
performance: result.performance,
accessibility: result.accessibility,
bestPractices: result.bestPractices,
seo: result.seo,
},
metrics: result.metrics,
budgetStatus: auditResult.budgetStatus,
recommendations: auditResult.recommendations,
...(result.rawResult && { rawLighthouseResult: result.rawResult }),
};
Logger.json(output);
break;
}
case 'console':
default: {
const reporter = new ConsoleReporter(config);
Logger.title(`Lighthouse Audit Results for: ${url}`);
Logger.info(`Device: ${options.device} | Throttling: ${options.throttling ? 'enabled' : 'disabled'}`);
reporter.reportLighthouseResults(auditResult);
break;
}
}
exitBasedOnStatus(auditResult.budgetStatus);
}
catch (error) {
spinner.fail('Lighthouse audit failed');
Logger.error(error instanceof Error ? error.message : 'Unknown error');
if (error instanceof Error && error.message.includes('Chrome')) {
Logger.warn('Tip: Make sure Google Chrome is installed and accessible');
}
process.exit(1);
}
}
function evaluatePerformanceBudgets(metrics, budgets) {
const { lighthouse: lighthouseBudgets, metrics: metricBudgets } = budgets;
let hasError = false;
let hasWarning = false;
if (lighthouseBudgets?.performance.min && metrics.performance < lighthouseBudgets.performance.min) {
hasError = true;
}
else if (lighthouseBudgets?.performance.warning && metrics.performance < lighthouseBudgets.performance.warning) {
hasWarning = true;
}
if (lighthouseBudgets?.accessibility?.min && metrics.accessibility
&& metrics.accessibility < lighthouseBudgets.accessibility.min) {
hasError = true;
}
if (lighthouseBudgets?.seo?.min && metrics.seo && metrics.seo < lighthouseBudgets.seo.min) {
hasError = true;
}
if (metrics.metrics.fcp > metricBudgets.fcp.max) {
hasError = true;
}
else if (metrics.metrics.fcp > metricBudgets.fcp.warning) {
hasWarning = true;
}
if (metrics.metrics.lcp > metricBudgets.lcp.max) {
hasError = true;
}
else if (metrics.metrics.lcp > metricBudgets.lcp.warning) {
hasWarning = true;
}
if (metrics.metrics.cls > metricBudgets.cls.max) {
hasError = true;
}
else if (metrics.metrics.cls > metricBudgets.cls.warning) {
hasWarning = true;
}
if (metrics.metrics.tti > metricBudgets.tti.max) {
hasError = true;
}
else if (metrics.metrics.tti > metricBudgets.tti.warning) {
hasWarning = true;
}
if (hasError)
return 'error';
if (hasWarning)
return 'warning';
return 'ok';
}
function generatePerformanceRecommendations(metrics) {
const recommendations = [];
if (metrics.performance < 90) {
recommendations.push('Consider optimizing images and reducing JavaScript bundle size');
}
if (metrics.metrics.fcp > 2000) {
recommendations.push('Improve First Contentful Paint by optimizing critical rendering path');
}
if (metrics.metrics.lcp > 2500) {
recommendations.push('Reduce Largest Contentful Paint by optimizing images and server response times');
}
if (metrics.metrics.cls > 0.1) {
recommendations.push('Fix layout shifts by setting image dimensions and avoiding dynamic content');
}
if (metrics.metrics.tti > 3500) {
recommendations.push('Reduce Time to Interactive by minimizing JavaScript execution time');
}
if (metrics.accessibility && metrics.accessibility < 95) {
recommendations.push('Improve accessibility by adding alt text, proper headings, and ARIA labels');
}
if (metrics.seo && metrics.seo < 90) {
recommendations.push('Improve SEO by adding meta descriptions, proper heading structure, and structured data');
}
return recommendations;
}
//# sourceMappingURL=lighthouse.js.map