lighthouse 11.5.0 → 11.6.0

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.
Files changed (67) hide show
  1. package/core/audits/layout-shifts.js +5 -2
  2. package/core/computed/metrics/cumulative-layout-shift.js +7 -1
  3. package/core/computed/trace-engine-result.d.ts +2 -16
  4. package/core/computed/trace-engine-result.js +12 -3
  5. package/core/gather/driver/wait-for-condition.js +15 -0
  6. package/core/gather/gatherers/root-causes.js +22 -9
  7. package/core/gather/gatherers/trace-elements.js +6 -2
  8. package/core/gather/gatherers/trace.js +2 -1
  9. package/core/gather/runner-helpers.js +4 -1
  10. package/core/lib/trace-engine.d.ts +10 -6
  11. package/core/lib/trace-engine.js +3 -4
  12. package/dist/report/bundle.esm.js +1 -1
  13. package/dist/report/flow.js +1 -1
  14. package/dist/report/standalone.js +1 -1
  15. package/package.json +8 -8
  16. package/report/renderer/report-utils.js +1 -1
  17. package/shared/localization/locales/ar-XB.json +45 -3
  18. package/shared/localization/locales/ar.json +45 -3
  19. package/shared/localization/locales/bg.json +45 -3
  20. package/shared/localization/locales/ca.json +45 -3
  21. package/shared/localization/locales/cs.json +45 -3
  22. package/shared/localization/locales/da.json +52 -10
  23. package/shared/localization/locales/de.json +45 -3
  24. package/shared/localization/locales/el.json +45 -3
  25. package/shared/localization/locales/en-GB.json +45 -3
  26. package/shared/localization/locales/en-US.json +1 -1
  27. package/shared/localization/locales/en-XA.json +45 -3
  28. package/shared/localization/locales/en-XL.json +1 -1
  29. package/shared/localization/locales/es-419.json +45 -3
  30. package/shared/localization/locales/es.json +45 -3
  31. package/shared/localization/locales/fi.json +45 -3
  32. package/shared/localization/locales/fil.json +45 -3
  33. package/shared/localization/locales/fr.json +48 -6
  34. package/shared/localization/locales/he.json +45 -3
  35. package/shared/localization/locales/hi.json +45 -3
  36. package/shared/localization/locales/hr.json +45 -3
  37. package/shared/localization/locales/hu.json +45 -3
  38. package/shared/localization/locales/id.json +45 -3
  39. package/shared/localization/locales/it.json +45 -3
  40. package/shared/localization/locales/ja.json +45 -3
  41. package/shared/localization/locales/ko.json +45 -3
  42. package/shared/localization/locales/lt.json +45 -3
  43. package/shared/localization/locales/lv.json +45 -3
  44. package/shared/localization/locales/nl.json +45 -3
  45. package/shared/localization/locales/no.json +45 -3
  46. package/shared/localization/locales/pl.json +45 -3
  47. package/shared/localization/locales/pt-PT.json +45 -3
  48. package/shared/localization/locales/pt.json +50 -8
  49. package/shared/localization/locales/ro.json +45 -3
  50. package/shared/localization/locales/ru.json +45 -3
  51. package/shared/localization/locales/sk.json +45 -3
  52. package/shared/localization/locales/sl.json +45 -3
  53. package/shared/localization/locales/sr-Latn.json +45 -3
  54. package/shared/localization/locales/sr.json +45 -3
  55. package/shared/localization/locales/sv.json +45 -3
  56. package/shared/localization/locales/ta.json +45 -3
  57. package/shared/localization/locales/te.json +45 -3
  58. package/shared/localization/locales/th.json +46 -4
  59. package/shared/localization/locales/tr.json +46 -4
  60. package/shared/localization/locales/uk.json +45 -3
  61. package/shared/localization/locales/vi.json +45 -3
  62. package/shared/localization/locales/zh-HK.json +45 -3
  63. package/shared/localization/locales/zh-TW.json +45 -3
  64. package/shared/localization/locales/zh.json +49 -7
  65. package/types/artifacts.d.ts +4 -3
  66. package/types/internal/rxjs.d.ts +4 -0
  67. package/types/trace-engine.d.ts +0 -1516
@@ -69,13 +69,16 @@ class LayoutShifts extends Audit {
69
69
 
70
70
  /** @type {Item[]} */
71
71
  const items = [];
72
- const layoutShiftEvents = clusters.flatMap(c => c.events);
72
+ const layoutShiftEvents =
73
+ /** @type {import('../lib/trace-engine.js').SaneSyntheticLayoutShift[]} */(
74
+ clusters.flatMap(c => c.events)
75
+ );
73
76
  const topLayoutShiftEvents = layoutShiftEvents
74
77
  .sort((a, b) => b.args.data.weighted_score_delta - a.args.data.weighted_score_delta)
75
78
  .slice(0, MAX_LAYOUT_SHIFTS);
76
79
  for (const event of topLayoutShiftEvents) {
77
80
  const biggestImpactNodeId = TraceElements.getBiggestImpactNodeForShiftEvent(
78
- event.args.data.impacted_nodes, impactByNodeId);
81
+ event.args.data.impacted_nodes || [], impactByNodeId);
79
82
  const biggestImpactElement = traceElements.find(t => t.nodeId === biggestImpactNodeId);
80
83
 
81
84
  // Turn root causes into sub-items.
@@ -159,7 +159,13 @@ class CumulativeLayoutShift {
159
159
  LayoutShifts: TraceEngine.TraceHandlers.LayoutShifts,
160
160
  Screenshots: TraceEngine.TraceHandlers.Screenshots,
161
161
  });
162
- await processor.parse(events);
162
+ // eslint-disable-next-line max-len
163
+ await processor.parse(/** @type {import('@paulirish/trace_engine').Types.TraceEvents.TraceEventData[]} */ (
164
+ events
165
+ ));
166
+ if (!processor.data) {
167
+ throw new Error('null trace engine result');
168
+ }
163
169
  return processor.data.LayoutShifts.sessionMaxScore;
164
170
  };
165
171
  const cumulativeLayoutShift = await run(allFrameShiftEvents.map(e => e.event));
@@ -4,14 +4,7 @@ declare const TraceEngineResultComputed: typeof TraceEngineResult & {
4
4
  trace: LH.Trace;
5
5
  }, context: import("../../types/utility-types.js").default.ImmutableObject<{
6
6
  computedCache: Map<string, import("../lib/arbitrary-equality-map.js").ArbitraryEqualityMap>;
7
- }>) => Promise<{
8
- LayoutShifts: {
9
- clusters: {
10
- events: import("../../types/trace-engine.js").LayoutShiftTraceEvent[];
11
- }[];
12
- sessionMaxScore: number;
13
- };
14
- }>;
7
+ }>) => Promise<Readonly<import("@paulirish/trace_engine/models/trace/handlers/types.js").EnabledHandlerDataWithMeta<typeof import("@paulirish/trace_engine/models/trace/handlers/ModelHandlers.js")>>>;
15
8
  };
16
9
  /**
17
10
  * @fileoverview Processes trace with the shared trace engine.
@@ -20,14 +13,7 @@ declare class TraceEngineResult {
20
13
  /**
21
14
  * @param {LH.TraceEvent[]} traceEvents
22
15
  */
23
- static runTraceEngine(traceEvents: LH.TraceEvent[]): Promise<{
24
- LayoutShifts: {
25
- clusters: {
26
- events: import("../../types/trace-engine.js").LayoutShiftTraceEvent[];
27
- }[];
28
- sessionMaxScore: number;
29
- };
30
- }>;
16
+ static runTraceEngine(traceEvents: LH.TraceEvent[]): Promise<Readonly<import("@paulirish/trace_engine/models/trace/handlers/types.js").EnabledHandlerDataWithMeta<typeof import("@paulirish/trace_engine/models/trace/handlers/ModelHandlers.js")>>>;
31
17
  /**
32
18
  * @param {{trace: LH.Trace}} data
33
19
  * @param {LH.Artifacts.ComputedContext} context
@@ -26,8 +26,13 @@ class TraceEngineResult {
26
26
  Samples: TraceEngine.TraceHandlers.Samples,
27
27
  Screenshots: TraceEngine.TraceHandlers.Screenshots,
28
28
  });
29
- await engine.parse(traceEvents);
30
- return engine.data;
29
+ // eslint-disable-next-line max-len
30
+ await engine.parse(/** @type {import('@paulirish/trace_engine').Types.TraceEvents.TraceEventData[]} */ (
31
+ traceEvents
32
+ ));
33
+ // TODO: use TraceEngine.TraceProcessor.createWithAllHandlers above.
34
+ return /** @type {import('@paulirish/trace_engine').Handlers.Types.TraceParseData} */(
35
+ engine.data);
31
36
  }
32
37
 
33
38
  /**
@@ -61,7 +66,11 @@ class TraceEngineResult {
61
66
  }
62
67
  }
63
68
 
64
- return TraceEngineResult.runTraceEngine(traceEvents);
69
+ const result = await TraceEngineResult.runTraceEngine(traceEvents);
70
+ if (!result) {
71
+ throw new Error('null trace engine result');
72
+ }
73
+ return result;
65
74
  }
66
75
  }
67
76
 
@@ -446,6 +446,21 @@ async function waitForFullyLoaded(session, networkMonitor, options) {
446
446
  // CPU listener. Resolves when the CPU has been idle for cpuQuietThresholdMs after network idle.
447
447
  let resolveOnCPUIdle = waitForNothing();
448
448
 
449
+ if (log.isVerbose()) {
450
+ resolveOnFcp.promise.then(() => {
451
+ log.verbose('waitFor', 'resolveOnFcp fired');
452
+ });
453
+ resolveOnLoadEvent.promise.then(() => {
454
+ log.verbose('waitFor', 'resolveOnLoadEvent fired');
455
+ });
456
+ resolveOnNetworkIdle.promise.then(() => {
457
+ log.verbose('waitFor', 'resolveOnNetworkIdle fired');
458
+ });
459
+ resolveOnCriticalNetworkIdle.promise.then(() => {
460
+ log.verbose('waitFor', 'resolveOnCriticalNetworkIdle fired');
461
+ });
462
+ }
463
+
449
464
  // Wait for all initial load promises. Resolves on cleanup function the clears load
450
465
  // timeout timer.
451
466
  /** @type {Promise<() => Promise<{timedOut: boolean}>>} */
@@ -32,26 +32,33 @@ class RootCauses extends BaseGatherer {
32
32
  // nodeIds if the DOM domain was enabled before this gatherer, invoke it to be safe.
33
33
  await driver.defaultSession.sendCommand('DOM.getDocument', {depth: -1, pierce: true});
34
34
 
35
+ /** @type {import('@paulirish/trace_engine').RootCauses.RootCauses.RootCauseProtocolInterface} */
35
36
  const protocolInterface = {
36
37
  /** @param {string} url */
37
38
  // eslint-disable-next-line no-unused-vars
38
39
  getInitiatorForRequest(url) {
39
40
  return null;
40
41
  },
41
- /** @param {number[]} backendNodeIds */
42
+ /** @param {import('@paulirish/trace_engine/generated/protocol.js').DOM.BackendNodeId[]} backendNodeIds */
42
43
  async pushNodesByBackendIdsToFrontend(backendNodeIds) {
43
44
  const response = await driver.defaultSession.sendCommand(
44
45
  'DOM.pushNodesByBackendIdsToFrontend', {backendNodeIds});
45
- return response.nodeIds;
46
+ const nodeIds =
47
+ /** @type {import('@paulirish/trace_engine/generated/protocol.js').DOM.NodeId[]} */(
48
+ response.nodeIds);
49
+ return nodeIds;
46
50
  },
47
- /** @param {number} nodeId */
51
+ /** @param {import('@paulirish/trace_engine/generated/protocol.js').DOM.NodeId} nodeId */
48
52
  async getNode(nodeId) {
49
53
  try {
50
54
  const response = await driver.defaultSession.sendCommand('DOM.describeNode', {nodeId});
51
55
  // This always zero, so let's fix it here.
52
56
  // https://bugs.chromium.org/p/chromium/issues/detail?id=1515175
53
57
  response.node.nodeId = nodeId;
54
- return response.node;
58
+ const node =
59
+ /** @type {import('@paulirish/trace_engine/generated/protocol.js').DOM.Node} */(
60
+ response.node);
61
+ return node;
55
62
  } catch (err) {
56
63
  if (err.message.includes('Could not find node with given id')) {
57
64
  // TODO: when injecting an iframe, the engine gets the node of that frame's document element.
@@ -64,7 +71,8 @@ class RootCauses extends BaseGatherer {
64
71
  // When this is fixed, remove this try/catch.
65
72
  // Note: this could be buggy by giving the wrong node detail if a node id meant for a non-main frame
66
73
  // happens to match one from the main frame ... which is pretty likely...
67
- return null;
74
+ // TODO: fix trace engine type to allow returning null.
75
+ return /** @type {any} */(null);
68
76
  }
69
77
  throw err;
70
78
  }
@@ -79,17 +87,20 @@ class RootCauses extends BaseGatherer {
79
87
  return [];
80
88
  }
81
89
  },
82
- /** @param {number} nodeId */
90
+ /** @param {import('@paulirish/trace_engine/generated/protocol.js').DOM.NodeId} nodeId */
83
91
  async getMatchedStylesForNode(nodeId) {
84
92
  try {
85
93
  const response = await driver.defaultSession.sendCommand(
86
94
  'CSS.getMatchedStylesForNode', {nodeId});
87
- return response;
88
- } catch {
89
- return [];
95
+ return {...response, getError() {}};
96
+ } catch (err) {
97
+ return /** @type {any} */({getError() {
98
+ return err.toString();
99
+ }});
90
100
  }
91
101
  },
92
102
  /** @param {string} url */
103
+ // @ts-expect-error not using, dont care about type error.
93
104
  // eslint-disable-next-line no-unused-vars
94
105
  async fontFaceForSource(url) {
95
106
  return null;
@@ -104,6 +115,8 @@ class RootCauses extends BaseGatherer {
104
115
  const layoutShiftEvents = traceEngineResult.LayoutShifts.clusters.flatMap(c => c.events);
105
116
  for (const event of layoutShiftEvents) {
106
117
  const r = await rootCausesEngine.layoutShifts.rootCausesForEvent(traceEngineResult, event);
118
+ if (!r) continue;
119
+
107
120
  for (const cause of r.fontChanges) {
108
121
  // TODO: why isn't trace engine unwrapping this promise ...
109
122
  cause.fontFace = await cause.fontFace;
@@ -117,15 +117,19 @@ class TraceElements extends BaseGatherer {
117
117
  static async getTopLayoutShifts(trace, traceEngineResult, rootCauses, context) {
118
118
  const {impactByNodeId} = await CumulativeLayoutShift.request(trace, context);
119
119
  const clusters = traceEngineResult.LayoutShifts.clusters ?? [];
120
- const layoutShiftEvents = clusters.flatMap(c => c.events);
120
+ const layoutShiftEvents =
121
+ /** @type {import('../../lib/trace-engine.js').SaneSyntheticLayoutShift[]} */(
122
+ clusters.flatMap(c => c.events)
123
+ );
121
124
 
122
125
  return layoutShiftEvents
123
126
  .sort((a, b) => b.args.data.weighted_score_delta - a.args.data.weighted_score_delta)
124
127
  .slice(0, MAX_LAYOUT_SHIFTS)
125
128
  .flatMap(event => {
126
129
  const nodeIds = [];
130
+ const impactedNodes = event.args.data.impacted_nodes || [];
127
131
  const biggestImpactedNodeId =
128
- this.getBiggestImpactNodeForShiftEvent(event.args.data.impacted_nodes, impactByNodeId);
132
+ this.getBiggestImpactNodeForShiftEvent(impactedNodes, impactByNodeId);
129
133
  if (biggestImpactedNodeId !== undefined) {
130
134
  nodeIds.push(biggestImpactedNodeId);
131
135
  }
@@ -61,7 +61,8 @@ class Trace extends BaseGatherer {
61
61
 
62
62
  // Not used by Lighthouse (yet) but included for users that want JS samples when looking at
63
63
  // a trace collected by Lighthouse (e.g. "View Trace" workflow in DevTools)
64
- 'disabled-by-default-v8.cpu_profiler',
64
+ // TODO: Re-enable after investigating b/325659693
65
+ // 'disabled-by-default-v8.cpu_profiler',
65
66
  ];
66
67
  }
67
68
 
@@ -33,7 +33,10 @@ import {Sentry} from '../lib/sentry.js';
33
33
  * @param {Error} error
34
34
  */
35
35
  function createDependencyError(dependency, error) {
36
- return new Error(`Dependency "${dependency.id}" failed with exception: ${error.message}`);
36
+ const err = new Error(`Dependency "${dependency.id}" failed with exception: ${error.message}`);
37
+ // @ts-expect-error - We already reported the original error to Sentry, don't do it again.
38
+ err.expected = true;
39
+ return err;
37
40
  }
38
41
 
39
42
  /** @return {ArtifactState} */
@@ -1,7 +1,11 @@
1
- /** @type {import('../../types/trace-engine.js').TraceProcessor & typeof import('../../types/trace-engine.js').TraceProcessor} */
2
- export const TraceProcessor: import('../../types/trace-engine.js').TraceProcessor & typeof import('../../types/trace-engine.js').TraceProcessor;
3
- /** @type {import('../../types/trace-engine.js').TraceHandlers} */
4
- export const TraceHandlers: any;
5
- /** @type {import('../../types/trace-engine.js').RootCauses & typeof import('../../types/trace-engine.js').RootCauses} */
6
- export const RootCauses: import('../../types/trace-engine.js').RootCauses & typeof import('../../types/trace-engine.js').RootCauses;
1
+ export type SyntheticLayoutShift = import('@paulirish/trace_engine').Types.TraceEvents.SyntheticLayoutShift;
2
+ export type SaneSyntheticLayoutShift = SyntheticLayoutShift & {
3
+ args: {
4
+ data: NonNullable<SyntheticLayoutShift['args']['data']>;
5
+ };
6
+ };
7
+ export const TraceProcessor: typeof TraceEngine.Processor.TraceProcessor;
8
+ export const TraceHandlers: typeof TraceEngine.Handlers.ModelHandlers;
9
+ export const RootCauses: typeof TraceEngine.RootCauses.RootCauses.RootCauses;
10
+ import * as TraceEngine from '@paulirish/trace_engine';
7
11
  //# sourceMappingURL=trace-engine.d.ts.map
@@ -1,15 +1,14 @@
1
- // @ts-expect-error missing types
2
1
  import * as TraceEngine from '@paulirish/trace_engine';
3
2
 
4
3
  import {polyfillDOMRect} from './polyfill-dom-rect.js';
5
4
 
5
+ /** @typedef {import('@paulirish/trace_engine').Types.TraceEvents.SyntheticLayoutShift} SyntheticLayoutShift */
6
+ /** @typedef {SyntheticLayoutShift & {args: {data: NonNullable<SyntheticLayoutShift['args']['data']>}}} SaneSyntheticLayoutShift */
7
+
6
8
  polyfillDOMRect();
7
9
 
8
- /** @type {import('../../types/trace-engine.js').TraceProcessor & typeof import('../../types/trace-engine.js').TraceProcessor} */
9
10
  const TraceProcessor = TraceEngine.Processor.TraceProcessor;
10
- /** @type {import('../../types/trace-engine.js').TraceHandlers} */
11
11
  const TraceHandlers = TraceEngine.Handlers.ModelHandlers;
12
- /** @type {import('../../types/trace-engine.js').RootCauses & typeof import('../../types/trace-engine.js').RootCauses} */
13
12
  const RootCauses = TraceEngine.RootCauses.RootCauses.RootCauses;
14
13
 
15
14
  export {