lighthouse
Version:
Automated auditing, performance metrics, and best practices for the web.
171 lines (154 loc) • 8.36 kB
JavaScript
/**
* @license
* Copyright 2019 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import log from 'lighthouse-logger';
import {ProcessedTrace} from '../processed-trace.js';
import {ProcessedNavigation} from '../processed-navigation.js';
import {Speedline} from '../speedline.js';
import {FirstContentfulPaint} from './first-contentful-paint.js';
import {FirstContentfulPaintAllFrames} from './first-contentful-paint-all-frames.js';
import {LargestContentfulPaint} from './largest-contentful-paint.js';
import {LargestContentfulPaintAllFrames} from './largest-contentful-paint-all-frames.js';
import {Interactive} from './interactive.js';
import {CumulativeLayoutShift} from './cumulative-layout-shift.js';
import {SpeedIndex} from './speed-index.js';
import {MaxPotentialFID} from './max-potential-fid.js';
import {TotalBlockingTime} from './total-blocking-time.js';
import {makeComputedArtifact} from '../computed-artifact.js';
import {TimeToFirstByte} from './time-to-first-byte.js';
import {LCPBreakdown} from './lcp-breakdown.js';
import {isUnderTest} from '../../lib/lh-env.js';
class TimingSummary {
/**
* @param {LH.Trace} trace
* @param {LH.DevtoolsLog} devtoolsLog
* @param {LH.Artifacts['GatherContext']} gatherContext
* @param {LH.Util.ImmutableObject<LH.Config.Settings>} settings
* @param {LH.Artifacts['URL']} URL
* @param {LH.Artifacts['SourceMaps']} SourceMaps
* @param {LH.Artifacts.ComputedContext} context
* @return {Promise<{metrics: LH.Artifacts.TimingSummary, debugInfo: Record<string,boolean>}>}
*/
static async summarize(trace, devtoolsLog, gatherContext, settings, URL, SourceMaps, context) {
const metricComputationData =
{trace, devtoolsLog, gatherContext, settings, URL, SourceMaps, simulator: null};
/**
* @template TArtifacts
* @template TReturn
* @param {{request: (artifact: TArtifacts, context: LH.Artifacts.ComputedContext) => Promise<TReturn>}} Artifact
* @param {TArtifacts} artifact
* @return {Promise<TReturn|undefined>}
*/
const requestOrUndefined = (Artifact, artifact) => {
return Artifact.request(artifact, context).catch(err => {
if (isUnderTest) {
log.error('lh:computed:TimingSummary', err);
}
return undefined;
});
};
/* eslint-disable max-len */
const processedTrace = await ProcessedTrace.request(trace, context);
const processedNavigation = await requestOrUndefined(ProcessedNavigation, trace);
const speedline = await Speedline.request(trace, context);
const firstContentfulPaint = await requestOrUndefined(FirstContentfulPaint, metricComputationData);
const firstContentfulPaintAllFrames = await requestOrUndefined(FirstContentfulPaintAllFrames, metricComputationData);
const largestContentfulPaint = await requestOrUndefined(LargestContentfulPaint, metricComputationData);
const largestContentfulPaintAllFrames = await requestOrUndefined(LargestContentfulPaintAllFrames, metricComputationData);
const interactive = await requestOrUndefined(Interactive, metricComputationData);
const cumulativeLayoutShiftValues = await requestOrUndefined(CumulativeLayoutShift, trace);
const maxPotentialFID = await requestOrUndefined(MaxPotentialFID, metricComputationData);
const speedIndex = await requestOrUndefined(SpeedIndex, metricComputationData);
const totalBlockingTime = await requestOrUndefined(TotalBlockingTime, metricComputationData);
const lcpBreakdown = await requestOrUndefined(LCPBreakdown, metricComputationData);
const ttfb = await requestOrUndefined(TimeToFirstByte, metricComputationData);
const {
cumulativeLayoutShift,
cumulativeLayoutShiftMainFrame,
} = cumulativeLayoutShiftValues || {};
/** @type {LH.Artifacts.TimingSummary} */
const metrics = {
// Include the simulated/observed performance metrics
firstContentfulPaint: firstContentfulPaint?.timing,
firstContentfulPaintTs: firstContentfulPaint?.timestamp,
firstContentfulPaintAllFrames: firstContentfulPaintAllFrames?.timing,
firstContentfulPaintAllFramesTs: firstContentfulPaintAllFrames?.timestamp,
largestContentfulPaint: largestContentfulPaint?.timing,
largestContentfulPaintTs: largestContentfulPaint?.timestamp,
largestContentfulPaintAllFrames: largestContentfulPaintAllFrames?.timing,
largestContentfulPaintAllFramesTs: largestContentfulPaintAllFrames?.timestamp,
interactive: interactive?.timing,
interactiveTs: interactive?.timestamp,
speedIndex: speedIndex?.timing,
speedIndexTs: speedIndex?.timestamp,
totalBlockingTime: totalBlockingTime?.timing,
maxPotentialFID: maxPotentialFID?.timing,
cumulativeLayoutShift,
cumulativeLayoutShiftMainFrame,
lcpLoadStart: lcpBreakdown?.loadStart,
lcpLoadEnd: lcpBreakdown?.loadEnd,
timeToFirstByte: ttfb?.timing,
timeToFirstByteTs: ttfb?.timestamp,
// Include all timestamps of interest from the processed trace
observedTimeOrigin: processedTrace.timings.timeOrigin,
observedTimeOriginTs: processedTrace.timestamps.timeOrigin,
// For now, navigationStart is always timeOrigin.
observedNavigationStart: processedNavigation?.timings.timeOrigin,
observedNavigationStartTs: processedNavigation?.timestamps.timeOrigin,
observedFirstPaint: processedNavigation?.timings.firstPaint,
observedFirstPaintTs: processedNavigation?.timestamps.firstPaint,
observedFirstContentfulPaint: processedNavigation?.timings.firstContentfulPaint,
observedFirstContentfulPaintTs: processedNavigation?.timestamps.firstContentfulPaint,
observedFirstContentfulPaintAllFrames: processedNavigation?.timings.firstContentfulPaintAllFrames,
observedFirstContentfulPaintAllFramesTs: processedNavigation?.timestamps.firstContentfulPaintAllFrames,
observedLargestContentfulPaint: processedNavigation?.timings.largestContentfulPaint,
observedLargestContentfulPaintTs: processedNavigation?.timestamps.largestContentfulPaint,
observedLargestContentfulPaintAllFrames: processedNavigation?.timings.largestContentfulPaintAllFrames,
observedLargestContentfulPaintAllFramesTs: processedNavigation?.timestamps.largestContentfulPaintAllFrames,
observedTraceEnd: processedTrace.timings.traceEnd,
observedTraceEndTs: processedTrace.timestamps.traceEnd,
observedLoad: processedNavigation?.timings.load,
observedLoadTs: processedNavigation?.timestamps.load,
observedDomContentLoaded: processedNavigation?.timings.domContentLoaded,
observedDomContentLoadedTs: processedNavigation?.timestamps.domContentLoaded,
observedCumulativeLayoutShift: cumulativeLayoutShift,
observedCumulativeLayoutShiftMainFrame: cumulativeLayoutShiftMainFrame,
// Include some visual metrics from speedline
observedFirstVisualChange: speedline.first,
observedFirstVisualChangeTs: (speedline.first + speedline.beginning) * 1000,
observedLastVisualChange: speedline.complete,
observedLastVisualChangeTs: (speedline.complete + speedline.beginning) * 1000,
observedSpeedIndex: speedline.speedIndex,
observedSpeedIndexTs: (speedline.speedIndex + speedline.beginning) * 1000,
};
/* eslint-enable max-len */
/** @type {Record<string,boolean>} */
const debugInfo = {
lcpInvalidated: !!processedNavigation?.lcpInvalidated,
};
return {metrics, debugInfo};
}
/**
* @param {{trace: LH.Trace, devtoolsLog: LH.DevtoolsLog, gatherContext: LH.Artifacts['GatherContext']; settings: LH.Util.ImmutableObject<LH.Config.Settings>, URL: LH.Artifacts['URL'], SourceMaps: LH.Artifacts['SourceMaps']}} data
* @param {LH.Artifacts.ComputedContext} context
* @return {Promise<{metrics: LH.Artifacts.TimingSummary, debugInfo: Record<string,boolean>}>}
*/
static async compute_(data, context) {
return TimingSummary.summarize(
data.trace,
data.devtoolsLog,
data.gatherContext,
data.settings,
data.URL,
data.SourceMaps,
context
);
}
}
const TimingSummaryComputed = makeComputedArtifact(
TimingSummary,
['devtoolsLog', 'gatherContext', 'settings', 'trace', 'URL', 'SourceMaps']
);
export {TimingSummaryComputed as TimingSummary};