Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: import lantern from trace engine #16092

Merged
merged 7 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,3 @@ third-party/**
**/*.d.cts

page-functions-test-case*out*.js
# TODO(15841): remove when importing Lantern from npm
core/lib/lantern/**/*.test.js
9 changes: 3 additions & 6 deletions core/audits/byte-efficiency/byte-efficiency-audit.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ import {LCPImageRecord} from '../../computed/lcp-image-record.js';

const str_ = i18n.createIcuMessageFn(import.meta.url, {});

/** @typedef {import('../../lib/lantern/simulation/Simulator.js').Simulator} Simulator */
/** @typedef {import('../../lib/lantern/BaseNode.js').Node<LH.Artifacts.NetworkRequest>} Node */

// Parameters for log-normal distribution scoring. These values were determined by fitting the
// log-normal cumulative distribution function curve to the former method of linear interpolation
// scoring between the control points {average = 300 ms, poor = 750 ms, zero = 5000 ms} using the
Expand Down Expand Up @@ -96,8 +93,8 @@ class ByteEfficiencyAudit extends Audit {
* Computes the estimated effect of all the byte savings on the provided graph.
*
* @param {Array<LH.Audit.ByteEfficiencyItem>} results The array of byte savings results per resource
* @param {Node} graph
* @param {Simulator} simulator
* @param {LH.Gatherer.Simulation.GraphNode} graph
* @param {LH.Gatherer.Simulation.Simulator} simulator
* @param {{label?: string, providedWastedBytesByUrl?: Map<string, number>}=} options
* @return {{savings: number, simulationBeforeChanges: LH.Gatherer.Simulation.Result, simulationAfterChanges: LH.Gatherer.Simulation.Result}}
*/
Expand Down Expand Up @@ -151,7 +148,7 @@ class ByteEfficiencyAudit extends Audit {

/**
* @param {ByteEfficiencyProduct} result
* @param {Simulator} simulator
* @param {LH.Gatherer.Simulation.Simulator} simulator
* @param {LH.Artifacts.MetricComputationDataInput} metricComputationInput
* @param {LH.Audit.Context} context
* @return {Promise<LH.Audit.Product>}
Expand Down
26 changes: 10 additions & 16 deletions core/audits/byte-efficiency/render-blocking-resources.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,16 @@
* @fileoverview Audit a page to see if it does have resources that are blocking first paint
*/


import {Audit} from '../audit.js';
import * as i18n from '../../lib/i18n/i18n.js';
import {BaseNode} from '../../lib/lantern/lantern.js';
import * as Lantern from '../../lib/lantern/lantern.js';
import {UnusedCSS} from '../../computed/unused-css.js';
import {NetworkRequest} from '../../lib/network-request.js';
import {LoadSimulator} from '../../computed/load-simulator.js';
import {FirstContentfulPaint} from '../../computed/metrics/first-contentful-paint.js';
import {LCPImageRecord} from '../../computed/lcp-image-record.js';
import {NavigationInsights} from '../../computed/navigation-insights.js';


/** @typedef {import('../../lib/lantern/simulation/Simulator.js').Simulator} Simulator */
/** @typedef {import('../../lib/lantern/BaseNode.js').Node<LH.Artifacts.NetworkRequest>} Node */
/** @typedef {import('../../lib/lantern/NetworkNode.js').NetworkNode<LH.Artifacts.NetworkRequest>} NetworkNode */

// Because of the way we detect blocking stylesheets, asynchronously loaded
// CSS with link[rel=preload] and an onload handler (see https://github.com/filamentgroup/loadCSS)
// can be falsely flagged as blocking. Therefore, ignore stylesheets that loaded fast enough
Expand All @@ -44,10 +38,10 @@ const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
/**
* Given a simulation's nodeTimings, return an object with the nodes/timing keyed by network URL
* @param {LH.Gatherer.Simulation.Result['nodeTimings']} nodeTimings
* @return {Map<string, {node: Node, nodeTiming: LH.Gatherer.Simulation.NodeTiming}>}
* @return {Map<string, {node: LH.Gatherer.Simulation.GraphNode, nodeTiming: LH.Gatherer.Simulation.NodeTiming}>}
*/
function getNodesAndTimingByRequestId(nodeTimings) {
/** @type {Map<string, {node: Node, nodeTiming: LH.Gatherer.Simulation.NodeTiming}>} */
/** @type {Map<string, {node: LH.Gatherer.Simulation.GraphNode, nodeTiming: LH.Gatherer.Simulation.NodeTiming}>} */
const requestIdToNode = new Map();

for (const [node, nodeTiming] of nodeTimings) {
Expand All @@ -61,8 +55,8 @@ function getNodesAndTimingByRequestId(nodeTimings) {

/**
* Adjust the timing of a node and its dependencies to account for stack specific overrides.
* @param {Map<Node, LH.Gatherer.Simulation.NodeTiming>} adjustedNodeTimings
* @param {Node} node
* @param {Map<LH.Gatherer.Simulation.GraphNode, LH.Gatherer.Simulation.NodeTiming>} adjustedNodeTimings
* @param {LH.Gatherer.Simulation.GraphNode} node
* @param {LH.Artifacts.DetectedStack[]} Stacks
*/
function adjustNodeTimings(adjustedNodeTimings, node, Stacks) {
Expand All @@ -83,7 +77,7 @@ function adjustNodeTimings(adjustedNodeTimings, node, Stacks) {
* Any stack specific timing overrides should go in this function.
* @see https://github.com/GoogleChrome/lighthouse/issues/2832#issuecomment-591066081
*
* @param {Node} node
* @param {LH.Gatherer.Simulation.GraphNode} node
* @param {LH.Gatherer.Simulation.NodeTiming} nodeTiming
* @param {LH.Artifacts.DetectedStack[]} Stacks
*/
Expand All @@ -93,7 +87,7 @@ function computeStackSpecificTiming(node, nodeTiming, Stacks) {
// AMP will load a linked stylesheet asynchronously if it has not been loaded after 2.1 seconds:
// https://github.com/ampproject/amphtml/blob/8e03ac2f315774070651584a7e046ff24212c9b1/src/font-stylesheet-timeout.js#L54-L59
// Any potential savings must only include time spent on AMP stylesheet nodes before 2.1 seconds.
if (node.type === BaseNode.TYPES.NETWORK &&
if (node.type === Lantern.Graph.BaseNode.types.NETWORK &&
node.request.resourceType === NetworkRequest.TYPES.Stylesheet &&
nodeTiming.endTime > 2100) {
stackSpecificTiming.endTime = Math.max(nodeTiming.startTime, 2100);
Expand Down Expand Up @@ -208,8 +202,8 @@ class RenderBlockingResources extends Audit {
* devs that they should be able to get to a reasonable first paint without JS, which is not a bad
* thing.
*
* @param {Simulator} simulator
* @param {Node} fcpGraph
* @param {LH.Gatherer.Simulation.Simulator} simulator
* @param {LH.Gatherer.Simulation.GraphNode} fcpGraph
* @param {Set<string>} deferredIds
* @param {Map<string, number>} wastedCssBytesByUrl
* @param {LH.Artifacts.DetectedStack[]} Stacks
Expand All @@ -225,7 +219,7 @@ class RenderBlockingResources extends Audit {

// If a node can be deferred, exclude it from the new FCP graph
const canDeferRequest = deferredIds.has(node.id);
if (node.type !== BaseNode.TYPES.NETWORK) return !canDeferRequest;
if (node.type !== Lantern.Graph.BaseNode.types.NETWORK) return !canDeferRequest;

const isStylesheet =
node.request.resourceType === NetworkRequest.TYPES.Stylesheet;
Expand Down
7 changes: 2 additions & 5 deletions core/audits/dobetterweb/uses-http2.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
* origin are over the http/2 protocol.
*/

/** @typedef {import('../../lib/lantern/simulation/Simulator.js').Simulator} Simulator */
/** @typedef {import('../../lib/lantern/BaseNode.js').Node<LH.Artifacts.NetworkRequest>} Node */

import {Audit} from '../audit.js';
import {EntityClassification} from '../../computed/entity-classification.js';
import UrlUtils from '../../lib/url-utils.js';
Expand Down Expand Up @@ -69,8 +66,8 @@ class UsesHTTP2Audit extends Audit {
* Computes the estimated effect of all results being converted to http/2 on the provided graph.
*
* @param {Array<{url: string}>} results
* @param {Node} graph
* @param {Simulator} simulator
* @param {LH.Gatherer.Simulation.GraphNode} graph
* @param {LH.Gatherer.Simulation.Simulator} simulator
* @param {{label?: string}=} options
* @return {{savings: number, simulationBefore: LH.Gatherer.Simulation.Result, simulationAfter: LH.Gatherer.Simulation.Result}}
*/
Expand Down
8 changes: 4 additions & 4 deletions core/audits/long-tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class LongTasks extends Audit {
* most time will be attributed to 'other' (the category of the top-level
* RunTask). See pruning in `PageDependencyGraph.linkCPUNodes`.
* @param {LH.Artifacts.TaskNode} task
* @param {Map<Lantern.TraceEvent, LH.Gatherer.Simulation.NodeTiming>|undefined} taskTimingsByEvent
* @param {Map<Lantern.Types.TraceEvent, LH.Gatherer.Simulation.NodeTiming>|undefined} taskTimingsByEvent
* @param {Map<TaskGroupIds, number>} [timeByTaskGroup]
* @return {{startTime: number, duration: number, timeByTaskGroup: Map<TaskGroupIds, number>}}
*/
Expand Down Expand Up @@ -117,7 +117,7 @@ class LongTasks extends Audit {
/**
* @param {Array<LH.Artifacts.TaskNode>} longTasks
* @param {Set<string>} jsUrls
* @param {Map<Lantern.TraceEvent, LH.Gatherer.Simulation.NodeTiming>|undefined} taskTimingsByEvent
* @param {Map<Lantern.Types.TraceEvent, LH.Gatherer.Simulation.NodeTiming>|undefined} taskTimingsByEvent
* @return {LH.Audit.Details.DebugData}
*/
static makeDebugData(longTasks, jsUrls, taskTimingsByEvent) {
Expand Down Expand Up @@ -155,7 +155,7 @@ class LongTasks extends Audit {
/**
* Get timing from task, overridden by taskTimingsByEvent if provided.
* @param {LH.Artifacts.TaskNode} task
* @param {Map<Lantern.TraceEvent, LH.Gatherer.Simulation.NodeTiming>|undefined} taskTimingsByEvent
* @param {Map<Lantern.Types.TraceEvent, LH.Gatherer.Simulation.NodeTiming>|undefined} taskTimingsByEvent
* @return {Timing}
*/
static getTiming(task, taskTimingsByEvent) {
Expand Down Expand Up @@ -185,7 +185,7 @@ class LongTasks extends Audit {
const metricComputationData = Audit.makeMetricComputationDataInput(artifacts, context);
const tbtResult = await TotalBlockingTime.request(metricComputationData, context);

/** @type {Map<Lantern.TraceEvent, LH.Gatherer.Simulation.NodeTiming>|undefined} */
/** @type {Map<Lantern.Types.TraceEvent, LH.Gatherer.Simulation.NodeTiming>|undefined} */
let taskTimingsByEvent;

if (settings.throttlingMethod === 'simulate') {
Expand Down
14 changes: 8 additions & 6 deletions core/audits/uses-rel-preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import * as Lantern from '../lib/lantern/lantern.js';
import UrlUtils from '../lib/url-utils.js';
import {NetworkRequest} from '../lib/network-request.js';
import {Audit} from './audit.js';
Expand Down Expand Up @@ -61,8 +62,8 @@ class UsesRelPreloadAudit extends Audit {
if (node.type !== 'network') return;
// Don't include the node itself or any CPU nodes in the initiatorPath
const path = traversalPath.slice(1).filter(initiator => initiator.type === 'network');
if (!UsesRelPreloadAudit.shouldPreloadRequest(node.rawRequest, mainResource, path)) return;
urls.add(node.rawRequest.url);
if (!UsesRelPreloadAudit.shouldPreloadRequest(node.request, mainResource, path)) return;
urls.add(node.request.url);
});

return urls;
Expand All @@ -75,6 +76,7 @@ class UsesRelPreloadAudit extends Audit {
* @return {Set<string>}
*/
static getURLsFailedToPreload(graph) {
// TODO: add `fromPrefetchCache` to Lantern.Types.NetworkRequest, then use node.request here instead of rawRequest.
/** @type {Array<LH.Artifacts.NetworkRequest>} */
const requests = [];
graph.traverse(node => node.type === 'network' && requests.push(node.rawRequest));
Expand Down Expand Up @@ -109,8 +111,8 @@ class UsesRelPreloadAudit extends Audit {
* Critical requests deeper than depth 2 are more likely to be a case-by-case basis such that it
* would be a little risky to recommend blindly.
*
* @param {LH.Artifacts.NetworkRequest} request
* @param {LH.Artifacts.NetworkRequest} mainResource
* @param {Lantern.Types.NetworkRequest} request
* @param {Lantern.Types.NetworkRequest} mainResource
* @param {Array<LH.Gatherer.Simulation.GraphNode>} initiatorPath
* @return {boolean}
*/
Expand Down Expand Up @@ -157,7 +159,7 @@ class UsesRelPreloadAudit extends Audit {

if (node.isMainDocument()) {
mainDocumentNode = node;
} else if (node.rawRequest && urls.has(node.rawRequest.url)) {
} else if (node.request && urls.has(node.request.url)) {
nodesToPreload.push(node);
}
});
Expand Down Expand Up @@ -189,7 +191,7 @@ class UsesRelPreloadAudit extends Audit {

const wastedMs = Math.round(timingBefore.endTime - timingAfter.endTime);
if (wastedMs < THRESHOLD_IN_MS) continue;
results.push({url: node.rawRequest.url, wastedMs});
results.push({url: node.request.url, wastedMs});
}

if (!results.length) {
Expand Down
9 changes: 5 additions & 4 deletions core/computed/critical-request-chains.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import * as Lantern from '../lib/lantern/lantern.js';
import {makeComputedArtifact} from './computed-artifact.js';
import {NetworkRequest} from '../lib/network-request.js';
import {MainResource} from './main-resource.js';
Expand All @@ -14,8 +15,8 @@ class CriticalRequestChains {
* For now, we use network priorities as a proxy for "render-blocking"/critical-ness.
* It's imperfect, but there is not a higher-fidelity signal available yet.
* @see https://docs.google.com/document/d/1bCDuq9H1ih9iNjgzyAL0gpwNFiEP4TZS-YLRp_RuMlc
* @param {LH.Artifacts.NetworkRequest} request
* @param {LH.Artifacts.NetworkRequest} mainResource
* @param {Lantern.Types.NetworkRequest} request
* @param {Lantern.Types.NetworkRequest} mainResource
* @return {boolean}
*/
static isCritical(request, mainResource) {
Expand Down Expand Up @@ -105,7 +106,7 @@ class CriticalRequestChains {
graph.traverse((node, traversalPath) => {
seenNodes.add(node);
if (node.type !== 'network') return;
if (!CriticalRequestChains.isCritical(node.rawRequest, mainResource)) return;
if (!CriticalRequestChains.isCritical(node.request, mainResource)) return;

const networkPath = traversalPath
.filter(/** @return {n is LH.Gatherer.Simulation.GraphNetworkNode} */
Expand All @@ -117,7 +118,7 @@ class CriticalRequestChains {
if (networkPath.some(r => !CriticalRequestChains.isCritical(r, mainResource))) return;

// Ignore non-network things (like data urls).
if (NetworkRequest.isNonNetworkRequest(node.rawRequest)) return;
if (NetworkRequest.isNonNetworkRequest(node.request)) return;

addChain(networkPath);
}, getNextNodes);
Expand Down
2 changes: 1 addition & 1 deletion core/computed/document-urls.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class DocumentUrls {
if (!requestedUrl || !mainDocumentUrl) throw new Error('No main frame navigations found');

const initialRequest =
Lantern.Simulation.NetworkAnalyzer.findResourceForUrl(networkRecords, requestedUrl);
Lantern.Core.NetworkAnalyzer.findResourceForUrl(networkRecords, requestedUrl);
if (initialRequest?.redirects?.length) requestedUrl = initialRequest.redirects[0].url;

return {requestedUrl, mainDocumentUrl};
Expand Down
2 changes: 1 addition & 1 deletion core/computed/load-simulator.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class LoadSimulator {
/**
* @param {{devtoolsLog: LH.DevtoolsLog, settings: LH.Audit.Context['settings']}} data
* @param {LH.Artifacts.ComputedContext} context
* @return {Promise<Lantern.Simulation.Simulator>}
* @return {Promise<LH.Gatherer.Simulation.Simulator>}
*/
static async compute_(data, context) {
const networkAnalysis = await NetworkAnalysis.request(data.devtoolsLog, context);
Expand Down
2 changes: 1 addition & 1 deletion core/computed/main-resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class MainResource {
// would have evicted the first request by the time `MainDocumentRequest` (a consumer
// of this computed artifact) attempts to fetch the contents, resulting in a protocol error.
const mainResource =
Lantern.Simulation.NetworkAnalyzer.findLastDocumentForUrl(records, mainDocumentUrl);
Lantern.Core.NetworkAnalyzer.findLastDocumentForUrl(records, mainDocumentUrl);
if (!mainResource) {
throw new Error('Unable to identify the main resource');
}
Expand Down
4 changes: 1 addition & 3 deletions core/computed/metrics/lantern-first-contentful-paint.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@ import * as Lantern from '../../lib/lantern/lantern.js';
import {makeComputedArtifact} from '../computed-artifact.js';
import {getComputationDataParams, lanternErrorAdapter} from './lantern-metric.js';

/** @typedef {import('../../lib/lantern/Metric.js').Extras} Extras */

class LanternFirstContentfulPaint extends Lantern.Metrics.FirstContentfulPaint {
/**
* @param {LH.Artifacts.MetricComputationDataInput} data
* @param {LH.Artifacts.ComputedContext} context
* @param {Omit<Extras, 'optimistic'>=} extras
* @param {Omit<Lantern.Metrics.Extras, 'optimistic'>=} extras
* @return {Promise<LH.Artifacts.LanternMetric>}
*/
static async computeMetricWithGraphs(data, context, extras) {
Expand Down
4 changes: 1 addition & 3 deletions core/computed/metrics/lantern-interactive.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@ import {makeComputedArtifact} from '../computed-artifact.js';
import {LanternLargestContentfulPaint} from './lantern-largest-contentful-paint.js';
import {getComputationDataParams, lanternErrorAdapter} from './lantern-metric.js';

/** @typedef {import('../../lib/lantern/Metric.js').Extras} Extras */

class LanternInteractive extends Lantern.Metrics.Interactive {
/**
* @param {LH.Artifacts.MetricComputationDataInput} data
* @param {LH.Artifacts.ComputedContext} context
* @param {Omit<Extras, 'optimistic'>=} extras
* @param {Omit<Lantern.Metrics.Extras, 'optimistic'>=} extras
* @return {Promise<LH.Artifacts.LanternMetric>}
*/
static async computeMetricWithGraphs(data, context, extras) {
Expand Down
4 changes: 1 addition & 3 deletions core/computed/metrics/lantern-largest-contentful-paint.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@ import {makeComputedArtifact} from '../computed-artifact.js';
import {getComputationDataParams, lanternErrorAdapter} from './lantern-metric.js';
import {LanternFirstContentfulPaint} from './lantern-first-contentful-paint.js';

/** @typedef {import('../../lib/lantern/Metric.js').Extras} Extras */

class LanternLargestContentfulPaint extends Lantern.Metrics.LargestContentfulPaint {
/**
* @param {LH.Artifacts.MetricComputationDataInput} data
* @param {LH.Artifacts.ComputedContext} context
* @param {Omit<Extras, 'optimistic'>=} extras
* @param {Omit<Lantern.Metrics.Extras, 'optimistic'>=} extras
* @return {Promise<LH.Artifacts.LanternMetric>}
*/
static async computeMetricWithGraphs(data, context, extras) {
Expand Down
4 changes: 1 addition & 3 deletions core/computed/metrics/lantern-max-potential-fid.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@ import {makeComputedArtifact} from '../computed-artifact.js';
import {getComputationDataParams, lanternErrorAdapter} from './lantern-metric.js';
import {LanternFirstContentfulPaint} from './lantern-first-contentful-paint.js';

/** @typedef {import('../../lib/lantern/Metric.js').Extras} Extras */

class LanternMaxPotentialFID extends Lantern.Metrics.MaxPotentialFID {
/**
* @param {LH.Artifacts.MetricComputationDataInput} data
* @param {LH.Artifacts.ComputedContext} context
* @param {Omit<Extras, 'optimistic'>=} extras
* @param {Omit<Lantern.Metrics.Extras, 'optimistic'>=} extras
* @return {Promise<LH.Artifacts.LanternMetric>}
*/
static async computeMetricWithGraphs(data, context, extras) {
Expand Down
2 changes: 1 addition & 1 deletion core/computed/metrics/lantern-metric.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ async function getComputationDataParamsFromTrace(data, context) {
* @return {never}
*/
function lanternErrorAdapter(err) {
if (!(err instanceof Lantern.Error)) {
if (!(err instanceof Lantern.Core.LanternError)) {
throw err;
}

Expand Down
4 changes: 1 addition & 3 deletions core/computed/metrics/lantern-speed-index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@ import {getComputationDataParams, lanternErrorAdapter} from './lantern-metric.js
import {Speedline} from '../speedline.js';
import {LanternFirstContentfulPaint} from './lantern-first-contentful-paint.js';

/** @typedef {import('../../lib/lantern/Metric.js').Extras} Extras */

class LanternSpeedIndex extends Lantern.Metrics.SpeedIndex {
/**
* @param {LH.Artifacts.MetricComputationDataInput} data
* @param {LH.Artifacts.ComputedContext} context
* @param {Omit<Extras, 'optimistic'>=} extras
* @param {Omit<Lantern.Metrics.Extras, 'optimistic'>=} extras
* @return {Promise<LH.Artifacts.LanternMetric>}
*/
static async computeMetricWithGraphs(data, context, extras) {
Expand Down
4 changes: 1 addition & 3 deletions core/computed/metrics/lantern-total-blocking-time.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@ import {LanternFirstContentfulPaint} from './lantern-first-contentful-paint.js';
import {LanternInteractive} from './lantern-interactive.js';
import {getComputationDataParams} from './lantern-metric.js';

/** @typedef {import('../../lib/lantern/Metric.js').Extras} Extras */

class LanternTotalBlockingTime extends Lantern.Metrics.TotalBlockingTime {
/**
* @param {LH.Artifacts.MetricComputationDataInput} data
* @param {LH.Artifacts.ComputedContext} context
* @param {Omit<Extras, 'optimistic'>=} extras
* @param {Omit<Lantern.Metrics.Extras, 'optimistic'>=} extras
* @return {Promise<LH.Artifacts.LanternMetric>}
*/
static async computeMetricWithGraphs(data, context, extras) {
Expand Down
Loading
Loading