lighthouse 10.0.1-dev.20230214 → 10.0.1-dev.20230216
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -17,32 +17,33 @@ declare class PrioritizeLcpImage extends Audit {
|
|
|
17
17
|
static shouldPreloadRequest(request: LH.Artifacts.NetworkRequest, mainResource: LH.Artifacts.NetworkRequest, initiatorPath: Array<LH.Gatherer.Simulation.GraphNode>): boolean;
|
|
18
18
|
/**
|
|
19
19
|
* @param {LH.Gatherer.Simulation.GraphNode} graph
|
|
20
|
-
* @param {
|
|
20
|
+
* @param {NetworkRequest} lcpRecord
|
|
21
21
|
* @return {{lcpNode: LH.Gatherer.Simulation.GraphNetworkNode|undefined, path: Array<LH.Gatherer.Simulation.GraphNetworkNode>|undefined}}
|
|
22
22
|
*/
|
|
23
|
-
static findLCPNode(graph: LH.Gatherer.Simulation.GraphNode,
|
|
23
|
+
static findLCPNode(graph: LH.Gatherer.Simulation.GraphNode, lcpRecord: NetworkRequest): {
|
|
24
24
|
lcpNode: LH.Gatherer.Simulation.GraphNetworkNode | undefined;
|
|
25
25
|
path: Array<LH.Gatherer.Simulation.GraphNetworkNode> | undefined;
|
|
26
26
|
};
|
|
27
27
|
/**
|
|
28
28
|
* @param {LH.Artifacts.NetworkRequest} mainResource
|
|
29
29
|
* @param {LH.Gatherer.Simulation.GraphNode} graph
|
|
30
|
-
* @param {
|
|
30
|
+
* @param {NetworkRequest|undefined} lcpRecord
|
|
31
31
|
* @return {{lcpNodeToPreload?: LH.Gatherer.Simulation.GraphNetworkNode, initiatorPath?: InitiatorPath}}
|
|
32
32
|
*/
|
|
33
|
-
static getLCPNodeToPreload(mainResource: LH.Artifacts.NetworkRequest, graph: LH.Gatherer.Simulation.GraphNode,
|
|
33
|
+
static getLCPNodeToPreload(mainResource: LH.Artifacts.NetworkRequest, graph: LH.Gatherer.Simulation.GraphNode, lcpRecord: NetworkRequest | undefined): {
|
|
34
34
|
lcpNodeToPreload?: import("../lib/dependency-graph/network-node.js").NetworkNode | undefined;
|
|
35
35
|
initiatorPath?: InitiatorPath | undefined;
|
|
36
36
|
};
|
|
37
37
|
/**
|
|
38
|
-
* Match the LCP event with the paint event to get the
|
|
38
|
+
* Match the LCP event with the paint event to get the request of the image actually painted.
|
|
39
39
|
* This could differ from the `ImageElement` associated with the nodeId if e.g. the LCP
|
|
40
40
|
* was a pseudo-element associated with a node containing a smaller background-image.
|
|
41
41
|
* @param {LH.Trace} trace
|
|
42
42
|
* @param {LH.Artifacts.ProcessedNavigation} processedNavigation
|
|
43
|
-
* @
|
|
43
|
+
* @param {Array<NetworkRequest>} networkRecords
|
|
44
|
+
* @return {NetworkRequest|undefined}
|
|
44
45
|
*/
|
|
45
|
-
static
|
|
46
|
+
static getLcpRecord(trace: LH.Trace, processedNavigation: LH.Artifacts.ProcessedNavigation, networkRecords: Array<NetworkRequest>): NetworkRequest | undefined;
|
|
46
47
|
/**
|
|
47
48
|
* Computes the estimated effect of preloading the LCP image.
|
|
48
49
|
* @param {LH.Artifacts.TraceElement} lcpElement
|
|
@@ -12,6 +12,7 @@ import {LanternLargestContentfulPaint} from '../computed/metrics/lantern-largest
|
|
|
12
12
|
import {LoadSimulator} from '../computed/load-simulator.js';
|
|
13
13
|
import {ByteEfficiencyAudit} from './byte-efficiency/byte-efficiency-audit.js';
|
|
14
14
|
import {ProcessedNavigation} from '../computed/processed-navigation.js';
|
|
15
|
+
import {NetworkRecords} from '../computed/network-records.js';
|
|
15
16
|
|
|
16
17
|
const UIStrings = {
|
|
17
18
|
/** Title of a lighthouse audit that tells a user to preload an image in order to improve their LCP time. */
|
|
@@ -64,15 +65,15 @@ class PrioritizeLcpImage extends Audit {
|
|
|
64
65
|
|
|
65
66
|
/**
|
|
66
67
|
* @param {LH.Gatherer.Simulation.GraphNode} graph
|
|
67
|
-
* @param {
|
|
68
|
+
* @param {NetworkRequest} lcpRecord
|
|
68
69
|
* @return {{lcpNode: LH.Gatherer.Simulation.GraphNetworkNode|undefined, path: Array<LH.Gatherer.Simulation.GraphNetworkNode>|undefined}}
|
|
69
70
|
*/
|
|
70
|
-
static findLCPNode(graph,
|
|
71
|
+
static findLCPNode(graph, lcpRecord) {
|
|
71
72
|
let lcpNode;
|
|
72
73
|
let path;
|
|
73
74
|
graph.traverse((node, traversalPath) => {
|
|
74
75
|
if (node.type !== 'network') return;
|
|
75
|
-
if (node.record.
|
|
76
|
+
if (node.record.requestId === lcpRecord.requestId) {
|
|
76
77
|
lcpNode = node;
|
|
77
78
|
path =
|
|
78
79
|
traversalPath.slice(1).filter(initiator => initiator.type === 'network');
|
|
@@ -87,12 +88,12 @@ class PrioritizeLcpImage extends Audit {
|
|
|
87
88
|
/**
|
|
88
89
|
* @param {LH.Artifacts.NetworkRequest} mainResource
|
|
89
90
|
* @param {LH.Gatherer.Simulation.GraphNode} graph
|
|
90
|
-
* @param {
|
|
91
|
+
* @param {NetworkRequest|undefined} lcpRecord
|
|
91
92
|
* @return {{lcpNodeToPreload?: LH.Gatherer.Simulation.GraphNetworkNode, initiatorPath?: InitiatorPath}}
|
|
92
93
|
*/
|
|
93
|
-
static getLCPNodeToPreload(mainResource, graph,
|
|
94
|
-
if (!
|
|
95
|
-
const {lcpNode, path} = PrioritizeLcpImage.findLCPNode(graph,
|
|
94
|
+
static getLCPNodeToPreload(mainResource, graph, lcpRecord) {
|
|
95
|
+
if (!lcpRecord) return {};
|
|
96
|
+
const {lcpNode, path} = PrioritizeLcpImage.findLCPNode(graph, lcpRecord);
|
|
96
97
|
if (!lcpNode || !path) return {};
|
|
97
98
|
|
|
98
99
|
// eslint-disable-next-line max-len
|
|
@@ -111,14 +112,15 @@ class PrioritizeLcpImage extends Audit {
|
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
/**
|
|
114
|
-
* Match the LCP event with the paint event to get the
|
|
115
|
+
* Match the LCP event with the paint event to get the request of the image actually painted.
|
|
115
116
|
* This could differ from the `ImageElement` associated with the nodeId if e.g. the LCP
|
|
116
117
|
* was a pseudo-element associated with a node containing a smaller background-image.
|
|
117
118
|
* @param {LH.Trace} trace
|
|
118
119
|
* @param {LH.Artifacts.ProcessedNavigation} processedNavigation
|
|
119
|
-
* @
|
|
120
|
+
* @param {Array<NetworkRequest>} networkRecords
|
|
121
|
+
* @return {NetworkRequest|undefined}
|
|
120
122
|
*/
|
|
121
|
-
static
|
|
123
|
+
static getLcpRecord(trace, processedNavigation, networkRecords) {
|
|
122
124
|
// Use main-frame-only LCP to match the metric value.
|
|
123
125
|
const lcpEvent = processedNavigation.largestContentfulPaintEvt;
|
|
124
126
|
if (!lcpEvent) return;
|
|
@@ -131,7 +133,31 @@ class PrioritizeLcpImage extends Audit {
|
|
|
131
133
|
// Get last candidate, in case there was more than one.
|
|
132
134
|
}).sort((a, b) => b.ts - a.ts)[0];
|
|
133
135
|
|
|
134
|
-
|
|
136
|
+
const lcpUrl = lcpImagePaintEvent?.args.data?.imageUrl;
|
|
137
|
+
if (!lcpUrl) return;
|
|
138
|
+
|
|
139
|
+
const candidates = networkRecords.filter(record => {
|
|
140
|
+
return record.url === lcpUrl &&
|
|
141
|
+
record.finished &&
|
|
142
|
+
// Same frame as LCP trace event.
|
|
143
|
+
record.frameId === lcpImagePaintEvent.args.frame &&
|
|
144
|
+
record.networkRequestTime < (processedNavigation.timestamps.largestContentfulPaint || 0);
|
|
145
|
+
}).map(record => {
|
|
146
|
+
// Follow any redirects to find the real image request.
|
|
147
|
+
while (record.redirectDestination) {
|
|
148
|
+
record = record.redirectDestination;
|
|
149
|
+
}
|
|
150
|
+
return record;
|
|
151
|
+
}).filter(record => {
|
|
152
|
+
// Don't select if also loaded by some other means (xhr, etc). `resourceType`
|
|
153
|
+
// isn't set on redirect _sources_, so have to check after following redirects.
|
|
154
|
+
return record.resourceType === 'Image';
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// If there are still multiple candidates, at this point it appears the page
|
|
158
|
+
// simply made multiple requests for the image. The first loaded is the best
|
|
159
|
+
// guess of the request that made the image available for use.
|
|
160
|
+
return candidates.sort((a, b) => a.networkEndTime - b.networkEndTime)[0];
|
|
135
161
|
}
|
|
136
162
|
|
|
137
163
|
/**
|
|
@@ -233,7 +259,8 @@ class PrioritizeLcpImage extends Audit {
|
|
|
233
259
|
const trace = artifacts.traces[PrioritizeLcpImage.DEFAULT_PASS];
|
|
234
260
|
const devtoolsLog = artifacts.devtoolsLogs[PrioritizeLcpImage.DEFAULT_PASS];
|
|
235
261
|
const URL = artifacts.URL;
|
|
236
|
-
const
|
|
262
|
+
const settings = context.settings;
|
|
263
|
+
const metricData = {trace, devtoolsLog, gatherContext, settings, URL};
|
|
237
264
|
const lcpElement = artifacts.TraceElements
|
|
238
265
|
.find(element => element.traceEventType === 'largest-contentful-paint');
|
|
239
266
|
|
|
@@ -241,17 +268,16 @@ class PrioritizeLcpImage extends Audit {
|
|
|
241
268
|
return {score: null, notApplicable: true};
|
|
242
269
|
}
|
|
243
270
|
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
]);
|
|
271
|
+
const networkRecords = await NetworkRecords.request(devtoolsLog, context);
|
|
272
|
+
const processedNavigation = await ProcessedNavigation.request(trace, context);
|
|
273
|
+
const mainResource = await MainResource.request({devtoolsLog, URL}, context);
|
|
274
|
+
const lanternLCP = await LanternLargestContentfulPaint.request(metricData, context);
|
|
275
|
+
const simulator = await LoadSimulator.request({devtoolsLog, settings}, context);
|
|
250
276
|
|
|
251
|
-
const
|
|
277
|
+
const lcpRecord = PrioritizeLcpImage.getLcpRecord(trace, processedNavigation, networkRecords);
|
|
252
278
|
const graph = lanternLCP.pessimisticGraph;
|
|
253
|
-
|
|
254
|
-
|
|
279
|
+
const {lcpNodeToPreload, initiatorPath} = PrioritizeLcpImage.getLCPNodeToPreload(mainResource,
|
|
280
|
+
graph, lcpRecord);
|
|
255
281
|
|
|
256
282
|
const {results, wastedMs} =
|
|
257
283
|
PrioritizeLcpImage.computeWasteWithGraph(lcpElement, lcpNodeToPreload, graph, simulator);
|
package/package.json
CHANGED