UNPKG

lighthouse

Version:

Automated auditing, performance metrics, and best practices for the web.

139 lines (121 loc) 4.47 kB
/** * @license * Copyright 2025 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import {NO_NAVIGATION} from '@paulirish/trace_engine/models/trace/types/TraceEvents.js'; import {ProcessedTrace} from '../../computed/processed-trace.js'; import {TraceEngineResult} from '../../computed/trace-engine-result.js'; import {Audit} from '../audit.js'; /** * @param {LH.Artifacts} artifacts * @param {LH.Audit.Context} context * @return {Promise<import('@paulirish/trace_engine/models/trace/insights/types.js').InsightSet|undefined>} */ async function getInsightSet(artifacts, context) { const settings = context.settings; const trace = artifacts.traces[Audit.DEFAULT_PASS]; const processedTrace = await ProcessedTrace.request(trace, context); const SourceMaps = artifacts.SourceMaps; const traceEngineResult = await TraceEngineResult.request({trace, settings, SourceMaps}, context); const navigationId = processedTrace.timeOriginEvt.args.data?.navigationId; const key = navigationId ?? NO_NAVIGATION; return traceEngineResult.insights.get(key); } /** * @param {LH.Artifacts} artifacts * @param {LH.Audit.Context} context * @param {T} insightName * @param {(insight: import('@paulirish/trace_engine/models/trace/insights/types.js').InsightModels[T]) => LH.Audit.Details|undefined} createDetails * @template {keyof import('@paulirish/trace_engine/models/trace/insights/types.js').InsightModelsType} T * @return {Promise<LH.Audit.Product>} */ async function adaptInsightToAuditProduct(artifacts, context, insightName, createDetails) { const insights = await getInsightSet(artifacts, context); if (!insights) { return { scoreDisplayMode: Audit.SCORING_MODES.NOT_APPLICABLE, score: null, }; } const insight = insights.model[insightName]; if (insight instanceof Error) { return { errorMessage: insight.message, errorStack: insight.stack, score: null, }; } const details = createDetails(insight); if (!details || (details.type === 'table' && details.headings.length === 0)) { return { scoreDisplayMode: Audit.SCORING_MODES.NOT_APPLICABLE, score: null, }; } // This hack is to add metric adorners if an insight category links it to a metric, // but doesn't output a metric savings for that metric. let metricSavings = insight.metricSavings; if (insight.category === 'INP' && !metricSavings?.INP) { metricSavings = {...metricSavings, INP: /** @type {any} */ (0)}; } else if (insight.category === 'CLS' && !metricSavings?.CLS) { metricSavings = {...metricSavings, CLS: /** @type {any} */ (0)}; } else if (insight.category === 'LCP' && !metricSavings?.LCP) { metricSavings = {...metricSavings, LCP: /** @type {any} */ (0)}; } let score = 1; if (insight.state === 'fail') { score = 0; } else if (insightName === 'LCPPhases') { // TODO: change these insights to denote passing/failing/informative. Until then... hack it. score = metricSavings?.LCP ?? 0 >= 1000 ? 0 : 1; } else if (insightName === 'InteractionToNextPaint') { // TODO: change these insights to denote passing/failing/informative. Until then... hack it. score = metricSavings?.INP ?? 0 >= 500 ? 0 : 1; } return { scoreDisplayMode: insight.metricSavings ? Audit.SCORING_MODES.METRIC_SAVINGS : Audit.SCORING_MODES.NUMERIC, score, metricSavings, warnings: insight.warnings, details, }; } /** * @param {LH.Artifacts.TraceElement[]} traceElements * @param {number|null|undefined} nodeId * @return {LH.Audit.Details.NodeValue|undefined} */ function makeNodeItemForNodeId(traceElements, nodeId) { if (typeof nodeId !== 'number') { return; } const traceElement = traceElements.find(te => te.traceEventType === 'trace-engine' && te.nodeId === nodeId); const node = traceElement?.node; if (!node) { return; } return Audit.makeNodeItem(node); } /** * @param {LH.Artifacts.TraceElement[]} traceElements * @param {number|null|undefined} nodeId * @param {LH.IcuMessage|string} label * @return {LH.Audit.Details.Table|undefined} */ function maybeMakeNodeElementTable(traceElements, nodeId, label) { const node = makeNodeItemForNodeId(traceElements, nodeId); if (!node) { return; } return Audit.makeTableDetails([ {key: 'node', valueType: 'node', label}, ], [{node}]); } export { adaptInsightToAuditProduct, makeNodeItemForNodeId, maybeMakeNodeElementTable, };