lighthouse 9.6.4 → 9.6.7
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.
- package/lighthouse-core/audits/lcp-lazy-loaded.js +4 -3
- package/lighthouse-core/audits/network-requests.js +9 -0
- package/lighthouse-core/audits/preload-lcp-image.js +7 -3
- package/lighthouse-core/computed/main-thread-tasks.js +2 -1
- package/lighthouse-core/gather/gatherers/trace-elements.js +32 -13
- package/lighthouse-core/lib/tracehouse/main-thread-tasks.js +4 -3
- package/lighthouse-core/lib/url-shim.js +2 -2
- package/package.json +2 -2
- package/types/artifacts.d.ts +1 -0
- package/changelog.md +0 -6028
|
@@ -50,8 +50,9 @@ class LargestContentfulPaintLazyLoaded extends Audit {
|
|
|
50
50
|
* @return {LH.Audit.Product}
|
|
51
51
|
*/
|
|
52
52
|
static audit(artifacts) {
|
|
53
|
-
const lcpElement = artifacts.TraceElements
|
|
54
|
-
|
|
53
|
+
const lcpElement = artifacts.TraceElements.find(element => {
|
|
54
|
+
return element.traceEventType === 'largest-contentful-paint' && element.type === 'image';
|
|
55
|
+
});
|
|
55
56
|
const lcpElementImage = lcpElement ? artifacts.ImageElements.find(elem => {
|
|
56
57
|
return elem.node.devtoolsNodePath === lcpElement.node.devtoolsNodePath;
|
|
57
58
|
}) : undefined;
|
|
@@ -59,7 +60,7 @@ class LargestContentfulPaintLazyLoaded extends Audit {
|
|
|
59
60
|
|
|
60
61
|
if (!lcpElementImage ||
|
|
61
62
|
!this.isImageInViewport(lcpElementImage, artifacts.ViewportDimensions)) {
|
|
62
|
-
return {score:
|
|
63
|
+
return {score: null, notApplicable: true};
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
/** @type {LH.Audit.Details.Table['headings']} */
|
|
@@ -73,6 +73,7 @@ class NetworkRequests extends Audit {
|
|
|
73
73
|
statusCode: record.statusCode,
|
|
74
74
|
mimeType: record.mimeType,
|
|
75
75
|
resourceType: record.resourceType,
|
|
76
|
+
priority: record.priority,
|
|
76
77
|
isLinkPreload,
|
|
77
78
|
experimentalFromMainFrame,
|
|
78
79
|
lrEndTimeDeltaMs: endTimeDeltaMs, // Only exists on Lightrider runs
|
|
@@ -110,6 +111,14 @@ class NetworkRequests extends Audit {
|
|
|
110
111
|
|
|
111
112
|
const tableDetails = Audit.makeTableDetails(headings, results);
|
|
112
113
|
|
|
114
|
+
// Include starting timestamp to allow syncing requests with navStart/metric timestamps.
|
|
115
|
+
const networkStartTimeTs = Number.isFinite(earliestStartTime) ?
|
|
116
|
+
earliestStartTime * 1_000_000 : undefined;
|
|
117
|
+
tableDetails.debugData = {
|
|
118
|
+
type: 'debugdata',
|
|
119
|
+
networkStartTimeTs,
|
|
120
|
+
};
|
|
121
|
+
|
|
113
122
|
return {
|
|
114
123
|
score: 1,
|
|
115
124
|
details: tableDetails,
|
|
@@ -88,7 +88,7 @@ class PreloadLCPImageAudit extends Audit {
|
|
|
88
88
|
/**
|
|
89
89
|
* @param {LH.Artifacts.NetworkRequest} mainResource
|
|
90
90
|
* @param {LH.Gatherer.Simulation.GraphNode} graph
|
|
91
|
-
* @param {LH.Artifacts.TraceElement
|
|
91
|
+
* @param {LH.Artifacts.TraceElement} lcpElement
|
|
92
92
|
* @param {Array<LH.Artifacts.ImageElement>} imageElements
|
|
93
93
|
* @return {{lcpNodeToPreload?: LH.Gatherer.Simulation.GraphNetworkNode, initiatorPath?: InitiatorPath}}
|
|
94
94
|
*/
|
|
@@ -121,14 +121,14 @@ class PreloadLCPImageAudit extends Audit {
|
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
123
|
* Computes the estimated effect of preloading the LCP image.
|
|
124
|
-
* @param {LH.Artifacts.TraceElement
|
|
124
|
+
* @param {LH.Artifacts.TraceElement} lcpElement
|
|
125
125
|
* @param {LH.Gatherer.Simulation.GraphNetworkNode|undefined} lcpNode
|
|
126
126
|
* @param {LH.Gatherer.Simulation.GraphNode} graph
|
|
127
127
|
* @param {LH.Gatherer.Simulation.Simulator} simulator
|
|
128
128
|
* @return {{wastedMs: number, results: Array<{node: LH.Audit.Details.NodeValue, url: string, wastedMs: number}>}}
|
|
129
129
|
*/
|
|
130
130
|
static computeWasteWithGraph(lcpElement, lcpNode, graph, simulator) {
|
|
131
|
-
if (!
|
|
131
|
+
if (!lcpNode) {
|
|
132
132
|
return {
|
|
133
133
|
wastedMs: 0,
|
|
134
134
|
results: [],
|
|
@@ -222,6 +222,10 @@ class PreloadLCPImageAudit extends Audit {
|
|
|
222
222
|
const lcpElement = artifacts.TraceElements
|
|
223
223
|
.find(element => element.traceEventType === 'largest-contentful-paint');
|
|
224
224
|
|
|
225
|
+
if (!lcpElement || lcpElement.type !== 'image') {
|
|
226
|
+
return {score: null, notApplicable: true};
|
|
227
|
+
}
|
|
228
|
+
|
|
225
229
|
const [mainResource, lanternLCP, simulator] = await Promise.all([
|
|
226
230
|
MainResource.request({devtoolsLog, URL}, context),
|
|
227
231
|
LanternLCP.request(metricData, context),
|
|
@@ -17,7 +17,8 @@ class MainThreadTasks {
|
|
|
17
17
|
*/
|
|
18
18
|
static async compute_(trace, context) {
|
|
19
19
|
const {mainThreadEvents, frames, timestamps} = await ProcessedTrace.request(trace, context);
|
|
20
|
-
return MainThreadTasks_.getMainThreadTasks(mainThreadEvents, frames, timestamps.traceEnd
|
|
20
|
+
return MainThreadTasks_.getMainThreadTasks(mainThreadEvents, frames, timestamps.traceEnd,
|
|
21
|
+
timestamps.timeOrigin);
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -24,7 +24,7 @@ const ProcessedNavigation = require('../../computed/processed-navigation.js');
|
|
|
24
24
|
const LighthouseError = require('../../lib/lh-error.js');
|
|
25
25
|
const ComputedResponsivenes = require('../../computed/metrics/responsiveness.js');
|
|
26
26
|
|
|
27
|
-
/** @typedef {{nodeId: number, score?: number, animations?: {name?: string, failureReasonsMask?: number, unsupportedProperties?: string[]}[]}} TraceElementData */
|
|
27
|
+
/** @typedef {{nodeId: number, score?: number, animations?: {name?: string, failureReasonsMask?: number, unsupportedProperties?: string[]}[], type?: string}} TraceElementData */
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* @this {HTMLElement}
|
|
@@ -213,6 +213,34 @@ class TraceElements extends FRGatherer {
|
|
|
213
213
|
return animatedElementData;
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
+
/**
|
|
217
|
+
* @param {LH.Artifacts.ProcessedTrace} processedTrace
|
|
218
|
+
* @param {LH.Gatherer.FRTransitionalContext} context
|
|
219
|
+
* @return {Promise<{nodeId: number, type: string} | undefined>}
|
|
220
|
+
*/
|
|
221
|
+
static async getLcpElement(processedTrace, context) {
|
|
222
|
+
let processedNavigation;
|
|
223
|
+
try {
|
|
224
|
+
processedNavigation = await ProcessedNavigation.request(processedTrace, context);
|
|
225
|
+
} catch (err) {
|
|
226
|
+
// If we were running in timespan mode and there was no paint, treat LCP as missing.
|
|
227
|
+
if (context.gatherMode === 'timespan' && err.code === LighthouseError.errors.NO_FCP.code) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
throw err;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// These should exist, but trace types are loose.
|
|
235
|
+
const lcpData = processedNavigation.largestContentfulPaintEvt?.args?.data;
|
|
236
|
+
if (lcpData?.nodeId === undefined || !lcpData.type) return;
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
nodeId: lcpData.nodeId,
|
|
240
|
+
type: lcpData.type,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
216
244
|
/**
|
|
217
245
|
* @param {LH.Gatherer.FRTransitionalContext} context
|
|
218
246
|
*/
|
|
@@ -241,26 +269,16 @@ class TraceElements extends FRGatherer {
|
|
|
241
269
|
}
|
|
242
270
|
|
|
243
271
|
const processedTrace = await ProcessedTrace.request(trace, context);
|
|
244
|
-
const {largestContentfulPaintEvt} = await ProcessedNavigation
|
|
245
|
-
.request(processedTrace, context)
|
|
246
|
-
.catch(err => {
|
|
247
|
-
// If we were running in timespan mode and there was no paint, treat LCP as missing.
|
|
248
|
-
if (context.gatherMode === 'timespan' && err.code === LighthouseError.errors.NO_FCP.code) {
|
|
249
|
-
return {largestContentfulPaintEvt: undefined};
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
throw err;
|
|
253
|
-
});
|
|
254
272
|
const {mainThreadEvents} = processedTrace;
|
|
255
273
|
|
|
256
|
-
const
|
|
274
|
+
const lcpNodeData = await TraceElements.getLcpElement(processedTrace, context);
|
|
257
275
|
const clsNodeData = TraceElements.getTopLayoutShiftElements(mainThreadEvents);
|
|
258
276
|
const animatedElementData = await this.getAnimatedElements(mainThreadEvents);
|
|
259
277
|
const responsivenessElementData = await TraceElements.getResponsivenessElement(trace, context);
|
|
260
278
|
|
|
261
279
|
/** @type {Map<string, TraceElementData[]>} */
|
|
262
280
|
const backendNodeDataMap = new Map([
|
|
263
|
-
['largest-contentful-paint',
|
|
281
|
+
['largest-contentful-paint', lcpNodeData ? [lcpNodeData] : []],
|
|
264
282
|
['layout-shift', clsNodeData],
|
|
265
283
|
['animation', animatedElementData],
|
|
266
284
|
['responsiveness', responsivenessElementData ? [responsivenessElementData] : []],
|
|
@@ -299,6 +317,7 @@ class TraceElements extends FRGatherer {
|
|
|
299
317
|
score: backendNodeData[i].score,
|
|
300
318
|
animations: backendNodeData[i].animations,
|
|
301
319
|
nodeId: backendNodeId,
|
|
320
|
+
type: backendNodeData[i].type,
|
|
302
321
|
});
|
|
303
322
|
}
|
|
304
323
|
}
|
|
@@ -561,9 +561,10 @@ class MainThreadTasks {
|
|
|
561
561
|
* @param {LH.TraceEvent[]} mainThreadEvents
|
|
562
562
|
* @param {Array<{id: string, url: string}>} frames
|
|
563
563
|
* @param {number} traceEndTs
|
|
564
|
+
* @param {number} [traceStartTs] Optional time-0 ts for tasks. Tasks before this point will have negative start/end times. Defaults to the first task found.
|
|
564
565
|
* @return {TaskNode[]}
|
|
565
566
|
*/
|
|
566
|
-
static getMainThreadTasks(mainThreadEvents, frames, traceEndTs) {
|
|
567
|
+
static getMainThreadTasks(mainThreadEvents, frames, traceEndTs, traceStartTs) {
|
|
567
568
|
const timers = new Map();
|
|
568
569
|
const xhrs = new Map();
|
|
569
570
|
const frameURLsById = new Map();
|
|
@@ -587,8 +588,8 @@ class MainThreadTasks {
|
|
|
587
588
|
priorTaskData.lastTaskURLs = task.attributableURLs;
|
|
588
589
|
}
|
|
589
590
|
|
|
590
|
-
// Rebase all the times to be relative to start of trace
|
|
591
|
-
const firstTs =
|
|
591
|
+
// Rebase all the times to be relative to start of trace and covert to ms.
|
|
592
|
+
const firstTs = traceStartTs ?? tasks[0].startTime;
|
|
592
593
|
for (const task of tasks) {
|
|
593
594
|
task.startTime = (task.startTime - firstTs) / 1000;
|
|
594
595
|
task.endTime = (task.endTime - firstTs) / 1000;
|
|
@@ -236,9 +236,9 @@ class URLShim extends URL {
|
|
|
236
236
|
}
|
|
237
237
|
|
|
238
238
|
if (url.protocol === 'data:') {
|
|
239
|
-
const match = url.pathname.match(
|
|
239
|
+
const match = url.pathname.match(/^(image\/(png|jpeg|svg\+xml|webp|gif|avif))[;,]/);
|
|
240
240
|
if (!match) return undefined;
|
|
241
|
-
return match[
|
|
241
|
+
return match[1];
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
const match = url.pathname.toLowerCase().match(/\.(png|jpeg|jpg|svg|webp|gif|avif)$/);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lighthouse",
|
|
3
|
-
"version": "9.6.
|
|
3
|
+
"version": "9.6.7",
|
|
4
4
|
"description": "Automated auditing, performance metrics, and best practices for the web.",
|
|
5
5
|
"main": "./lighthouse-core/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -184,7 +184,7 @@
|
|
|
184
184
|
"axe-core": "4.4.1",
|
|
185
185
|
"chrome-launcher": "^0.15.0",
|
|
186
186
|
"configstore": "^5.0.1",
|
|
187
|
-
"csp_evaluator": "1.1.
|
|
187
|
+
"csp_evaluator": "1.1.1",
|
|
188
188
|
"cssstyle": "1.2.1",
|
|
189
189
|
"enquirer": "^2.3.6",
|
|
190
190
|
"http-link-header": "^0.8.0",
|
package/types/artifacts.d.ts
CHANGED