From 450668fa59f8d45944bc016186fb91e85585cd6f Mon Sep 17 00:00:00 2001 From: Kiran Murugulla Date: Thu, 13 Nov 2025 16:15:22 -0800 Subject: [PATCH 1/4] dynamically fetching branch url --- .github/workflows/performance-budget.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/performance-budget.yml b/.github/workflows/performance-budget.yml index 18d68de..6cbaae0 100644 --- a/.github/workflows/performance-budget.yml +++ b/.github/workflows/performance-budget.yml @@ -28,9 +28,13 @@ jobs: id: branch-url run: | BRANCH_NAME="${{ github.head_ref }}" - BASE_URL="https://${BRANCH_NAME}--brightpath--kmurugulla.aem.live" + REPO="${{ github.event.repository.name }}" + OWNER="${{ github.repository_owner }}" + BASE_URL="https://${BRANCH_NAME}--${REPO}--${OWNER}.aem.live" echo "base_url=${BASE_URL}" >> $GITHUB_OUTPUT echo "Testing branch: ${BRANCH_NAME}" + echo "Repository: ${REPO}" + echo "Owner: ${OWNER}" echo "Base URL: ${BASE_URL}" - name: Wait for Preview to be Ready From d7f558eb3d9177ad0167628b076884e48c482869 Mon Sep 17 00:00:00 2001 From: Kiran Murugulla Date: Thu, 13 Nov 2025 16:31:25 -0800 Subject: [PATCH 2/4] renaming the workflow , considering all pages performance for final pr status --- .github/workflows/performance-budget.yml | 5 + scripts/perf.js | 378 ++++++++++++----------- 2 files changed, 204 insertions(+), 179 deletions(-) diff --git a/.github/workflows/performance-budget.yml b/.github/workflows/performance-budget.yml index 6cbaae0..51ba4e7 100644 --- a/.github/workflows/performance-budget.yml +++ b/.github/workflows/performance-budget.yml @@ -4,6 +4,11 @@ on: pull_request: branches: [main] +permissions: + contents: read + issues: write + pull-requests: write + env: PERF_TEST_PATHS: '/ /da-demo /ue-editor/demo' diff --git a/scripts/perf.js b/scripts/perf.js index 59f01f5..76a9a58 100755 --- a/scripts/perf.js +++ b/scripts/perf.js @@ -64,213 +64,233 @@ lhci.on('close', (exitCode) => { try { const lhciDir = '.lighthouseci'; - const files = readdirSync(lhciDir).filter((f) => f.startsWith('lhr-') && f.endsWith('.json')); + const files = readdirSync(lhciDir) + .filter((f) => f.startsWith('lhr-') && f.endsWith('.json')) + .sort(); if (files.length === 0) { console.error('No Lighthouse results found'); process.exit(1); } - const reportPath = join(lhciDir, files[files.length - 1]); - const report = JSON.parse(readFileSync(reportPath, 'utf-8')); - - const getDiagnostics = (rep, type) => { - const diagnostics = []; - - if (type === 'fcp' || type === 'lcp') { - const networkRequests = rep.audits['network-requests']; - if (networkRequests?.details?.items) { - const cssJs = networkRequests.details.items - .filter((item) => item.resourceType === 'Script' || item.resourceType === 'Stylesheet') - .filter((item) => !item.url.includes('livereload')) - .sort((a, b) => (b.transferSize || 0) - (a.transferSize || 0)) - .slice(0, 3); - - cssJs.forEach((item) => { - const size = ((item.transferSize || 0) / 1024).toFixed(0); - const fileName = item.url.split('/').pop() || item.url; - const fileType = item.resourceType === 'Script' ? 'JS' : 'CSS'; - diagnostics.push(` • ${fileType}: ${fileName} (${size}KB)`); - }); - } + let globalAllPassed = true; - if (type === 'lcp') { - const bootupTime = rep.audits['bootup-time']; - if (bootupTime?.details?.items?.length > 0) { - const topScript = bootupTime.details.items - .filter((item) => !item.url.includes('Unattributable')) - .sort((a, b) => (b.scripting || 0) - (a.scripting || 0))[0]; - if (topScript && topScript.scripting > 100) { - const time = Math.round(topScript.scripting); - diagnostics.push(` • Slow script execution: ${time}ms`); - } - } - } - } + files.forEach((file, index) => { + const reportPath = join(lhciDir, file); + const report = JSON.parse(readFileSync(reportPath, 'utf-8')); + const testedUrl = report.finalUrl || report.requestedUrl; - if (type === 'render-blocking') { - const networkRequests = rep.audits['network-requests']; - if (networkRequests?.details?.items) { - const css = networkRequests.details.items - .filter((item) => item.resourceType === 'Stylesheet') - .map((item) => { - const size = ((item.transferSize || 0) / 1024).toFixed(0); - const fileName = item.url.split('/').pop(); - return `${fileName} (${size}KB)`; - }); - const js = networkRequests.details.items - .filter((item) => item.resourceType === 'Script' && !item.url.includes('livereload')) - .map((item) => { + if (index > 0) console.log('\n'); + console.log(`\n${'='.repeat(90)}`); + console.log(`📄 Testing: ${testedUrl}`); + console.log(`${'='.repeat(90)}\n`); + + const getDiagnostics = (rep, type) => { + const diagnostics = []; + + if (type === 'fcp' || type === 'lcp') { + const networkRequests = rep.audits['network-requests']; + if (networkRequests?.details?.items) { + const cssJs = networkRequests.details.items + .filter((item) => item.resourceType === 'Script' || item.resourceType === 'Stylesheet') + .filter((item) => !item.url.includes('livereload')) + .sort((a, b) => (b.transferSize || 0) - (a.transferSize || 0)) + .slice(0, 3); + + cssJs.forEach((item) => { const size = ((item.transferSize || 0) / 1024).toFixed(0); - const fileName = item.url.split('/').pop(); - return `${fileName} (${size}KB)`; + const fileName = item.url.split('/').pop() || item.url; + const fileType = item.resourceType === 'Script' ? 'JS' : 'CSS'; + diagnostics.push(` • ${fileType}: ${fileName} (${size}KB)`); }); - if (css.length) diagnostics.push(` • CSS files: ${css.slice(0, 3).join(', ')}`); - if (js.length) diagnostics.push(` • JS files: ${js.slice(0, 3).join(', ')}`); + } + + if (type === 'lcp') { + const bootupTime = rep.audits['bootup-time']; + if (bootupTime?.details?.items?.length > 0) { + const topScript = bootupTime.details.items + .filter((item) => !item.url.includes('Unattributable')) + .sort((a, b) => (b.scripting || 0) - (a.scripting || 0))[0]; + if (topScript && topScript.scripting > 100) { + const time = Math.round(topScript.scripting); + diagnostics.push(` • Slow script execution: ${time}ms`); + } + } + } } - } - return diagnostics; - }; - - const checks = [ - { - test: 'Performance Score', - value: report.categories.performance.score, - threshold: 0.9, - unit: '', - advice: 'Optimize images, reduce JavaScript, improve server response times', - }, - { - test: 'First Contentful Paint', - value: report.audits['first-contentful-paint'].numericValue, - threshold: 2500, - unit: 'ms', - advice: 'Inline critical CSS, defer non-critical scripts, optimize above-the-fold', - diagnostics: () => getDiagnostics(report, 'fcp'), - }, - { - test: 'Largest Contentful Paint', - value: report.audits['largest-contentful-paint'].numericValue, - threshold: 2500, - unit: 'ms', - advice: 'Close to target - eagerly load LCP resources, defer non-critical scripts', - diagnostics: () => { - const diff = report.audits['largest-contentful-paint'].numericValue - 2500; - if (diff < 100 && diff > 0) { - return []; + if (type === 'render-blocking') { + const networkRequests = rep.audits['network-requests']; + if (networkRequests?.details?.items) { + const css = networkRequests.details.items + .filter((item) => item.resourceType === 'Stylesheet') + .map((item) => { + const size = ((item.transferSize || 0) / 1024).toFixed(0); + const fileName = item.url.split('/').pop(); + return `${fileName} (${size}KB)`; + }); + const js = networkRequests.details.items + .filter((item) => item.resourceType === 'Script' && !item.url.includes('livereload')) + .map((item) => { + const size = ((item.transferSize || 0) / 1024).toFixed(0); + const fileName = item.url.split('/').pop(); + return `${fileName} (${size}KB)`; + }); + if (css.length) diagnostics.push(` • CSS files: ${css.slice(0, 3).join(', ')}`); + if (js.length) diagnostics.push(` • JS files: ${js.slice(0, 3).join(', ')}`); } - return getDiagnostics(report, 'lcp'); + } + + return diagnostics; + }; + + const checks = [ + { + test: 'Performance Score', + value: report.categories.performance.score, + threshold: 0.9, + unit: '', + advice: 'Optimize images, reduce JavaScript, improve server response times', }, - }, - { - test: 'Render Blocking Resources', - value: (() => { - const networkRequests = report.audits['network-requests']; - if (!networkRequests?.details?.items) return 0; - const syncResources = networkRequests.details.items - .filter((item) => item.resourceType === 'Script' || item.resourceType === 'Stylesheet') - .filter((item) => !item.url.includes('livereload')); - return syncResources.reduce((sum, item) => sum + (item.transferSize || 0), 0); - })(), - threshold: 102400, - unit: 'KB', - advice: 'Defer non-critical CSS/JS, inline critical CSS', - diagnostics: () => getDiagnostics(report, 'render-blocking'), - }, - { - test: 'Cumulative Layout Shift', - value: report.audits['cumulative-layout-shift'].numericValue, - threshold: 0.1, - unit: '', - advice: 'Add size attributes to images, avoid inserting content above existing content', - }, - { - test: 'Total Blocking Time', - value: report.audits['total-blocking-time'].numericValue, - threshold: 300, - unit: 'ms', - advice: 'Reduce JavaScript execution time, code-split large bundles', - }, - { - test: 'Total Page Weight', - value: report.audits['total-byte-weight'].numericValue, - threshold: 614400, - unit: 'KB', - advice: 'Compress images, minify CSS/JS, remove unused code', - }, - ]; - - console.log('┌─────────────────────────────┬──────────┬──────────────────────────────────────────────────────────────────────────┐'); - console.log('│ Test │ Status │ Current Value → Target │'); - console.log('├─────────────────────────────┼──────────┼──────────────────────────────────────────────────────────────────────────┤'); - - let allPassed = true; - checks.forEach((check) => { - const isScore = check.test.includes('Score'); - const isCLS = check.test === 'Cumulative Layout Shift'; - - let displayValue = check.value; - if (isScore) { - displayValue = (check.value * 100).toFixed(0); - } else if (isCLS) { - displayValue = check.value.toFixed(3); - } else if (check.unit === 'KB') { - displayValue = (check.value / 1024).toFixed(0); - } else { - displayValue = Math.round(check.value); - } + { + test: 'First Contentful Paint', + value: report.audits['first-contentful-paint'].numericValue, + threshold: 2500, + unit: 'ms', + advice: 'Inline critical CSS, defer non-critical scripts, optimize above-the-fold', + diagnostics: () => getDiagnostics(report, 'fcp'), + }, + { + test: 'Largest Contentful Paint', + value: report.audits['largest-contentful-paint'].numericValue, + threshold: 2500, + unit: 'ms', + advice: 'Close to target - eagerly load LCP resources, defer non-critical scripts', + diagnostics: () => { + const diff = report.audits['largest-contentful-paint'].numericValue - 2500; + if (diff < 100 && diff > 0) { + return []; + } + return getDiagnostics(report, 'lcp'); + }, + }, + { + test: 'Render Blocking Resources', + value: (() => { + const networkRequests = report.audits['network-requests']; + if (!networkRequests?.details?.items) return 0; + const syncResources = networkRequests.details.items + .filter((item) => item.resourceType === 'Script' || item.resourceType === 'Stylesheet') + .filter((item) => !item.url.includes('livereload')); + return syncResources.reduce((sum, item) => sum + (item.transferSize || 0), 0); + })(), + threshold: 102400, + unit: 'KB', + advice: 'Defer non-critical CSS/JS, inline critical CSS', + diagnostics: () => getDiagnostics(report, 'render-blocking'), + }, + { + test: 'Cumulative Layout Shift', + value: report.audits['cumulative-layout-shift'].numericValue, + threshold: 0.1, + unit: '', + advice: 'Add size attributes to images, avoid inserting content above existing content', + }, + { + test: 'Total Blocking Time', + value: report.audits['total-blocking-time'].numericValue, + threshold: 300, + unit: 'ms', + advice: 'Reduce JavaScript execution time, code-split large bundles', + }, + { + test: 'Total Page Weight', + value: report.audits['total-byte-weight'].numericValue, + threshold: 614400, + unit: 'KB', + advice: 'Compress images, minify CSS/JS, remove unused code', + }, + ]; + + console.log('┌─────────────────────────────┬──────────┬──────────────────────────────────────────────────────────────────────────┐'); + console.log('│ Test │ Status │ Current Value → Target │'); + console.log('├─────────────────────────────┼──────────┼──────────────────────────────────────────────────────────────────────────┤'); + + let allPassed = true; + checks.forEach((check) => { + const isScore = check.test.includes('Score'); + const isCLS = check.test === 'Cumulative Layout Shift'; + + let displayValue = check.value; + if (isScore) { + displayValue = (check.value * 100).toFixed(0); + } else if (isCLS) { + displayValue = check.value.toFixed(3); + } else if (check.unit === 'KB') { + displayValue = (check.value / 1024).toFixed(0); + } else { + displayValue = Math.round(check.value); + } - let displayThreshold = check.threshold; - if (isScore) { - displayThreshold = (check.threshold * 100).toFixed(0); - } else if (isCLS) { - displayThreshold = check.threshold.toFixed(3); - } else if (check.unit === 'KB') { - displayThreshold = (check.threshold / 1024).toFixed(0); - } + let displayThreshold = check.threshold; + if (isScore) { + displayThreshold = (check.threshold * 100).toFixed(0); + } else if (isCLS) { + displayThreshold = check.threshold.toFixed(3); + } else if (check.unit === 'KB') { + displayThreshold = (check.threshold / 1024).toFixed(0); + } - let passed; - if (isScore) { - passed = check.value >= check.threshold; - } else { - passed = check.value <= check.threshold; - } + let passed; + if (isScore) { + passed = check.value >= check.threshold; + } else { + passed = check.value <= check.threshold; + } - const status = passed ? '✓ PASS' : '✗ FAIL'; - const unit = isScore ? '%' : check.unit; - const comparison = isScore - ? `${displayValue}${unit} ≥ ${displayThreshold}${unit}` - : `${displayValue}${unit} → ${displayThreshold}${unit}`; + const status = passed ? '✓ PASS' : '✗ FAIL'; + const unit = isScore ? '%' : check.unit; + const comparison = isScore + ? `${displayValue}${unit} ≥ ${displayThreshold}${unit}` + : `${displayValue}${unit} → ${displayThreshold}${unit}`; - if (!passed) allPassed = false; + if (!passed) allPassed = false; - const testPadded = check.test.padEnd(27); - const statusPadded = status.padEnd(8); - const comparisonPadded = comparison.padEnd(72); + const testPadded = check.test.padEnd(27); + const statusPadded = status.padEnd(8); + const comparisonPadded = comparison.padEnd(72); - console.log(`│ ${testPadded} │ ${statusPadded} │ ${comparisonPadded} │`); + console.log(`│ ${testPadded} │ ${statusPadded} │ ${comparisonPadded} │`); - if (!passed) { - console.log(`│ │ │ ${check.advice.padEnd(72)} │`); - if (check.diagnostics) { - const details = check.diagnostics(); - if (details.length > 0) { - details.forEach((detail) => { - console.log(`│ │ │ ${detail.padEnd(72)} │`); - }); + if (!passed) { + console.log(`│ │ │ ${check.advice.padEnd(72)} │`); + if (check.diagnostics) { + const details = check.diagnostics(); + if (details.length > 0) { + details.forEach((detail) => { + console.log(`│ │ │ ${detail.padEnd(72)} │`); + }); + } } } + }); + + console.log('└─────────────────────────────┴──────────┴──────────────────────────────────────────────────────────────────────────┘\n'); + + if (allPassed) { + console.log('✅ All checks passed for this page!\n'); + } else { + console.log('❌ Some checks failed for this page.\n'); + globalAllPassed = false; } }); - console.log('└─────────────────────────────┴──────────┴──────────────────────────────────────────────────────────────────────────┘\n'); - - if (allPassed) { - console.log('✅ All performance checks passed!\n'); + console.log(`\n${'='.repeat(90)}`); + if (globalAllPassed) { + console.log('✅ All performance checks passed across all pages!\n'); process.exit(0); } else { - console.log('❌ Performance checks failed. Please optimize before committing.\n'); + console.log('❌ Performance checks failed on one or more pages. Please optimize before committing.\n'); process.exit(1); } } catch (error) { From 98fe8f99297fe00ffb0b905dd7cb4a5a5ca1b521 Mon Sep 17 00:00:00 2001 From: Kiran Murugulla Date: Thu, 13 Nov 2025 16:40:08 -0800 Subject: [PATCH 3/4] fixing lint issues --- scripts/size.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/scripts/size.js b/scripts/size.js index 127d237..e177919 100644 --- a/scripts/size.js +++ b/scripts/size.js @@ -2,10 +2,9 @@ /* eslint-disable no-console */ import { spawn } from 'child_process'; -import { statSync, readdirSync } from 'fs'; -import { join, relative } from 'path'; +import { readdirSync, readFileSync } from 'fs'; +import { join } from 'path'; import { gzipSync } from 'zlib'; -import { readFileSync } from 'fs'; const cyan = '\x1b[96m'; const reset = '\x1b[0m'; @@ -28,7 +27,7 @@ bundlesize.stderr.on('data', (data) => { output += data.toString(); }); -bundlesize.on('close', (exitCode) => { +bundlesize.on('close', () => { clearInterval(spinner); process.stdout.write('\r\x1b[K'); @@ -106,4 +105,3 @@ bundlesize.on('close', (exitCode) => { process.exit(1); } }); - From 3e04522225d865f7debeb85adc536671ce96cbad Mon Sep 17 00:00:00 2001 From: Kiran Murugulla Date: Thu, 13 Nov 2025 17:24:42 -0800 Subject: [PATCH 4/4] adding root causefor LCP failure --- scripts/perf.js | 132 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 97 insertions(+), 35 deletions(-) diff --git a/scripts/perf.js b/scripts/perf.js index 76a9a58..40a0df7 100755 --- a/scripts/perf.js +++ b/scripts/perf.js @@ -32,17 +32,10 @@ const spinner = setInterval(() => { frameIndex = (frameIndex + 1) % frames.length; }, 80); -const env = { - ...process.env, - PERF_PATHS: paths.join(','), - PERF_URL: baseUrl, -}; - const urlArgs = urls.split(', ').flatMap((url) => ['--url', url]); -const lhciArgs = ['lhci', 'collect', '--config', 'lighthouserc.js', ...urlArgs]; +const lhciArgs = ['lhci', 'collect', '--numberOfRuns=1', '--config', 'lighthouserc.js', ...urlArgs]; const lhci = spawn('npx', lhciArgs, { - env, stdio: 'pipe', }); @@ -88,7 +81,92 @@ lhci.on('close', (exitCode) => { const getDiagnostics = (rep, type) => { const diagnostics = []; - if (type === 'fcp' || type === 'lcp') { + if (type === 'lcp') { + const lcpTime = rep.audits['largest-contentful-paint'].numericValue; + const networkRequests = rep.audits['network-requests']; + const ttfb = rep.audits['server-response-time']?.numericValue || 0; + const fcp = rep.audits['first-contentful-paint']?.numericValue || 0; + + if (networkRequests?.details?.items) { + const beforeLCP = networkRequests.details.items + .filter((item) => (item.networkRequestTime || 0) * 1000 < lcpTime) + .filter((item) => !item.url.includes('livereload')); + + const byType = { + Script: beforeLCP.filter((r) => r.resourceType === 'Script'), + Stylesheet: beforeLCP.filter((r) => r.resourceType === 'Stylesheet'), + Image: beforeLCP.filter((r) => r.resourceType === 'Image'), + Font: beforeLCP.filter((r) => r.resourceType === 'Font'), + Document: beforeLCP.filter((r) => r.resourceType === 'Document'), + Other: beforeLCP.filter((r) => !['Script', 'Stylesheet', 'Image', 'Font', 'Document'].includes(r.resourceType)), + }; + + const totalSize = Object.values(byType) + .flat() + .reduce((sum, item) => sum + (item.transferSize || 0), 0); + const totalSizeKB = Math.round(totalSize / 1024); + + diagnostics.push(` • Resources loaded BEFORE LCP (${Math.round(lcpTime)}ms):`); + Object.entries(byType).forEach(([resourceType, items]) => { + if (items.length > 0) { + const typeSize = items.reduce((sum, item) => sum + (item.transferSize || 0), 0); + const typeSizeKB = Math.round(typeSize / 1024); + diagnostics.push(` - ${items.length} ${resourceType.toLowerCase()}${items.length > 1 ? 's' : ''} (${typeSizeKB}KB)`); + } + }); + diagnostics.push(` - TOTAL: ${totalSizeKB}KB ${totalSizeKB > 100 ? '⚠️ (exceeds 100KB recommendation)' : '✓'}`); + + const topHeavy = beforeLCP + .sort((a, b) => (b.transferSize || 0) - (a.transferSize || 0)) + .slice(0, 3) + .filter((item) => (item.transferSize || 0) > 10240); + + if (topHeavy.length > 0) { + diagnostics.push(' • Heaviest resources before LCP:'); + topHeavy.forEach((item) => { + const { url, transferSize, resourceType } = item; + const fileName = url.split('/').pop() || url; + const size = Math.round((transferSize || 0) / 1024); + diagnostics.push(` - ${fileName} (${resourceType}, ${size}KB)`); + }); + } + + if (totalSizeKB < 100) { + diagnostics.push(' • Root cause analysis:'); + diagnostics.push(` - TTFB (server response): ${Math.round(ttfb)}ms`); + diagnostics.push(` - FCP (first paint): ${Math.round(fcp)}ms`); + diagnostics.push(` - LCP delay: ${Math.round(lcpTime - fcp)}ms after first paint`); + + if (ttfb > 600) { + diagnostics.push(' → Slow server response is the main bottleneck'); + } else if (lcpTime - fcp > 1000) { + diagnostics.push(' → LCP element is rendering late after initial paint'); + } else if (fcp > 1800) { + diagnostics.push(' → First paint is delayed (CSS/font blocking)'); + } else { + diagnostics.push(' → LCP timing is close to threshold (may pass on retry)'); + } + } + } + + const lcpElement = rep.audits['largest-contentful-paint-element']; + const lcpNode = lcpElement?.details?.items?.[0]; + if (lcpNode) { + diagnostics.push(` • LCP Element: ${lcpNode.node.nodeLabel || 'Unknown'}`); + } + + const renderBlocking = rep.audits['render-blocking-resources']; + if (renderBlocking?.details?.items?.length > 0) { + diagnostics.push(' • Render-blocking resources:'); + renderBlocking.details.items.slice(0, 3).forEach((item) => { + const fileName = item.url.split('/').pop(); + const wastedMs = Math.round(item.wastedMs || 0); + diagnostics.push(` - ${fileName} (delays by ${wastedMs}ms)`); + }); + } + } + + if (type === 'fcp') { const networkRequests = rep.audits['network-requests']; if (networkRequests?.details?.items) { const cssJs = networkRequests.details.items @@ -97,24 +175,14 @@ lhci.on('close', (exitCode) => { .sort((a, b) => (b.transferSize || 0) - (a.transferSize || 0)) .slice(0, 3); - cssJs.forEach((item) => { - const size = ((item.transferSize || 0) / 1024).toFixed(0); - const fileName = item.url.split('/').pop() || item.url; - const fileType = item.resourceType === 'Script' ? 'JS' : 'CSS'; - diagnostics.push(` • ${fileType}: ${fileName} (${size}KB)`); - }); - } - - if (type === 'lcp') { - const bootupTime = rep.audits['bootup-time']; - if (bootupTime?.details?.items?.length > 0) { - const topScript = bootupTime.details.items - .filter((item) => !item.url.includes('Unattributable')) - .sort((a, b) => (b.scripting || 0) - (a.scripting || 0))[0]; - if (topScript && topScript.scripting > 100) { - const time = Math.round(topScript.scripting); - diagnostics.push(` • Slow script execution: ${time}ms`); - } + if (cssJs.length > 0) { + diagnostics.push(' • Largest CSS/JS files:'); + cssJs.forEach((item) => { + const size = ((item.transferSize || 0) / 1024).toFixed(0); + const fileName = item.url.split('/').pop() || item.url; + const fileType = item.resourceType === 'Script' ? 'JS' : 'CSS'; + diagnostics.push(` - ${fileType}: ${fileName} (${size}KB)`); + }); } } } @@ -165,14 +233,8 @@ lhci.on('close', (exitCode) => { value: report.audits['largest-contentful-paint'].numericValue, threshold: 2500, unit: 'ms', - advice: 'Close to target - eagerly load LCP resources, defer non-critical scripts', - diagnostics: () => { - const diff = report.audits['largest-contentful-paint'].numericValue - 2500; - if (diff < 100 && diff > 0) { - return []; - } - return getDiagnostics(report, 'lcp'); - }, + advice: 'See root cause analysis below', + diagnostics: () => getDiagnostics(report, 'lcp'), }, { test: 'Render Blocking Resources',