lighthouse 11.7.0-dev.20240403 → 11.7.0-dev.20240405

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.
@@ -78,7 +78,7 @@ class LayoutShifts extends Audit {
78
78
  .slice(0, MAX_LAYOUT_SHIFTS);
79
79
  for (const event of topLayoutShiftEvents) {
80
80
  const biggestImpactNodeId = TraceElements.getBiggestImpactNodeForShiftEvent(
81
- event.args.data.impacted_nodes || [], impactByNodeId);
81
+ event.args.data.impacted_nodes || [], impactByNodeId, event);
82
82
  const biggestImpactElement = traceElements.find(t => t.nodeId === biggestImpactNodeId);
83
83
 
84
84
  // Turn root causes into sub-items.
@@ -25,9 +25,10 @@ declare class TraceElements extends BaseGatherer {
25
25
  *
26
26
  * @param {LH.Artifacts.TraceImpactedNode[]} impactedNodes
27
27
  * @param {Map<number, number>} impactByNodeId
28
+ * @param {import('../../lib/trace-engine.js').SaneSyntheticLayoutShift} event Only for debugging
28
29
  * @return {number|undefined}
29
30
  */
30
- static getBiggestImpactNodeForShiftEvent(impactedNodes: LH.Artifacts.TraceImpactedNode[], impactByNodeId: Map<number, number>): number | undefined;
31
+ static getBiggestImpactNodeForShiftEvent(impactedNodes: LH.Artifacts.TraceImpactedNode[], impactByNodeId: Map<number, number>, event: import('../../lib/trace-engine.js').SaneSyntheticLayoutShift): number | undefined;
31
32
  /**
32
33
  * This function finds the top (up to 15) layout shifts on the page, and returns
33
34
  * the id of the largest impacted node of each shift, along with any related nodes
@@ -88,19 +88,49 @@ class TraceElements extends BaseGatherer {
88
88
  *
89
89
  * @param {LH.Artifacts.TraceImpactedNode[]} impactedNodes
90
90
  * @param {Map<number, number>} impactByNodeId
91
+ * @param {import('../../lib/trace-engine.js').SaneSyntheticLayoutShift} event Only for debugging
91
92
  * @return {number|undefined}
92
93
  */
93
- static getBiggestImpactNodeForShiftEvent(impactedNodes, impactByNodeId) {
94
- let biggestImpactNodeId;
95
- let biggestImpactNodeScore = Number.NEGATIVE_INFINITY;
96
- for (const node of impactedNodes) {
97
- const impactScore = impactByNodeId.get(node.node_id);
98
- if (impactScore !== undefined && impactScore > biggestImpactNodeScore) {
99
- biggestImpactNodeId = node.node_id;
100
- biggestImpactNodeScore = impactScore;
94
+ static getBiggestImpactNodeForShiftEvent(impactedNodes, impactByNodeId, event) {
95
+ try {
96
+ let biggestImpactNodeId;
97
+ let biggestImpactNodeScore = Number.NEGATIVE_INFINITY;
98
+ for (const node of impactedNodes) {
99
+ const impactScore = impactByNodeId.get(node.node_id);
100
+ if (impactScore !== undefined && impactScore > biggestImpactNodeScore) {
101
+ biggestImpactNodeId = node.node_id;
102
+ biggestImpactNodeScore = impactScore;
103
+ }
101
104
  }
105
+ return biggestImpactNodeId;
106
+ } catch (err) {
107
+ // See https://github.com/GoogleChrome/lighthouse/issues/15870
108
+ // `impactedNodes` should always be an array here, but it can randomly be something else for
109
+ // currently unknown reasons. This exception handling will help us identify what
110
+ // `impactedNodes` really is and also prevent the error from being fatal.
111
+
112
+ // It's possible `impactedNodes` is not JSON serializable, so let's add more supplemental
113
+ // fields just in case.
114
+ const impactedNodesType = typeof impactedNodes;
115
+ const impactedNodesClassName = impactedNodes?.constructor?.name;
116
+
117
+ let impactedNodesJson;
118
+ let eventJson;
119
+ try {
120
+ impactedNodesJson = JSON.parse(JSON.stringify(impactedNodes));
121
+ eventJson = JSON.parse(JSON.stringify(event));
122
+ } catch {}
123
+
124
+ Sentry.captureException(err, {
125
+ extra: {
126
+ impactedNodes: impactedNodesJson,
127
+ event: eventJson,
128
+ impactedNodesType,
129
+ impactedNodesClassName,
130
+ },
131
+ });
132
+ return;
102
133
  }
103
- return biggestImpactNodeId;
104
134
  }
105
135
 
106
136
  /**
@@ -129,7 +159,7 @@ class TraceElements extends BaseGatherer {
129
159
  const nodeIds = [];
130
160
  const impactedNodes = event.args.data.impacted_nodes || [];
131
161
  const biggestImpactedNodeId =
132
- this.getBiggestImpactNodeForShiftEvent(impactedNodes, impactByNodeId);
162
+ this.getBiggestImpactNodeForShiftEvent(impactedNodes, impactByNodeId, event);
133
163
  if (biggestImpactedNodeId !== undefined) {
134
164
  nodeIds.push(biggestImpactedNodeId);
135
165
  }
@@ -403,6 +403,9 @@ class TraceProcessor {
403
403
  */
404
404
  static getMainThreadTopLevelEvents(trace, startTime = 0, endTime = Infinity) {
405
405
  const topLevelEvents = [];
406
+ /** @type {ToplevelEvent|undefined} */
407
+ let prevToplevel = undefined;
408
+
406
409
  // note: mainThreadEvents is already sorted by event start
407
410
  for (const event of trace.mainThreadEvents) {
408
411
  if (!this.isScheduleableTask(event) || !event.dur) continue;
@@ -411,11 +414,21 @@ class TraceProcessor {
411
414
  const end = (event.ts + event.dur - trace.timeOriginEvt.ts) / 1000;
412
415
  if (start > endTime || end < startTime) continue;
413
416
 
414
- topLevelEvents.push({
417
+ // Temporary fix for a Chrome bug where some RunTask events can be overlapping.
418
+ // We correct that here be ensuring each RunTask ends at least 1 microsecond before the next
419
+ // https://github.com/GoogleChrome/lighthouse/issues/15896
420
+ // https://issues.chromium.org/issues/329678173
421
+ if (prevToplevel && start < prevToplevel.end) {
422
+ prevToplevel.end = start - 0.001;
423
+ }
424
+
425
+ prevToplevel = {
415
426
  start,
416
427
  end,
417
428
  duration: event.dur / 1000,
418
- });
429
+ };
430
+
431
+ topLevelEvents.push(prevToplevel);
419
432
  }
420
433
 
421
434
  return topLevelEvents;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lighthouse",
3
3
  "type": "module",
4
- "version": "11.7.0-dev.20240403",
4
+ "version": "11.7.0-dev.20240405",
5
5
  "description": "Automated auditing, performance metrics, and best practices for the web.",
6
6
  "main": "./core/index.js",
7
7
  "bin": {