lighthouse 11.6.0 → 11.7.0-dev.20240321

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 (155) hide show
  1. package/CONTRIBUTING.md +2 -2
  2. package/cli/test/smokehouse/core-tests.js +2 -2
  3. package/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +2 -2
  4. package/core/audits/byte-efficiency/byte-efficiency-audit.js +7 -7
  5. package/core/audits/byte-efficiency/render-blocking-resources.d.ts +4 -3
  6. package/core/audits/byte-efficiency/render-blocking-resources.js +6 -6
  7. package/core/audits/dobetterweb/uses-http2.d.ts +3 -3
  8. package/core/audits/dobetterweb/uses-http2.js +4 -4
  9. package/core/audits/installable-manifest.d.ts +2 -0
  10. package/core/audits/installable-manifest.js +2 -0
  11. package/core/audits/metrics/interaction-to-next-paint.js +1 -3
  12. package/core/audits/preload-fonts.d.ts +1 -5
  13. package/core/audits/preload-fonts.js +1 -10
  14. package/core/audits/prioritize-lcp-image.d.ts +1 -1
  15. package/core/audits/uses-rel-preload.d.ts +1 -5
  16. package/core/audits/uses-rel-preload.js +1 -11
  17. package/core/audits/work-during-interaction.js +0 -8
  18. package/core/computed/document-urls.js +1 -1
  19. package/core/computed/js-bundles.d.ts +1 -1
  20. package/core/computed/load-simulator.d.ts +2 -2
  21. package/core/computed/load-simulator.js +3 -2
  22. package/core/computed/main-resource.js +1 -1
  23. package/core/computed/metrics/lantern-first-contentful-paint.d.ts +18 -6
  24. package/core/computed/metrics/lantern-first-contentful-paint.js +4 -4
  25. package/core/computed/metrics/lantern-first-meaningful-paint.d.ts +14 -2
  26. package/core/computed/metrics/lantern-first-meaningful-paint.js +1 -1
  27. package/core/computed/metrics/lantern-interactive.d.ts +8 -6
  28. package/core/computed/metrics/lantern-interactive.js +3 -3
  29. package/core/computed/metrics/lantern-largest-contentful-paint.d.ts +14 -2
  30. package/core/computed/metrics/lantern-largest-contentful-paint.js +1 -1
  31. package/core/computed/metrics/lantern-max-potential-fid.d.ts +8 -2
  32. package/core/computed/metrics/lantern-max-potential-fid.js +3 -3
  33. package/core/computed/metrics/lantern-metric.d.ts +4 -63
  34. package/core/computed/metrics/lantern-metric.js +6 -124
  35. package/core/computed/metrics/lantern-speed-index.d.ts +8 -2
  36. package/core/computed/metrics/lantern-speed-index.js +3 -3
  37. package/core/computed/metrics/lantern-total-blocking-time.d.ts +8 -2
  38. package/core/computed/metrics/lantern-total-blocking-time.js +3 -3
  39. package/core/computed/metrics/responsiveness.d.ts +5 -13
  40. package/core/computed/metrics/responsiveness.js +10 -14
  41. package/core/computed/module-duplication.d.ts +1 -1
  42. package/core/computed/network-analysis.js +1 -1
  43. package/core/computed/page-dependency-graph.d.ts +4 -57
  44. package/core/computed/page-dependency-graph.js +4 -451
  45. package/core/config/config-helpers.js +1 -1
  46. package/core/config/default-config.js +0 -5
  47. package/core/config/experimental-config.js +11 -0
  48. package/core/config/metrics-to-audits.js +0 -2
  49. package/core/gather/driver/execution-context.d.ts +2 -1
  50. package/core/gather/driver/execution-context.js +9 -9
  51. package/core/gather/driver/wait-for-condition.js +4 -2
  52. package/core/gather/gatherers/accessibility.js +2 -1
  53. package/core/gather/gatherers/css-usage.d.ts +1 -9
  54. package/core/gather/gatherers/css-usage.js +23 -25
  55. package/core/gather/gatherers/devtools-log.d.ts +4 -0
  56. package/core/gather/gatherers/devtools-log.js +7 -0
  57. package/core/gather/gatherers/full-page-screenshot.d.ts +4 -0
  58. package/core/gather/gatherers/full-page-screenshot.js +31 -35
  59. package/core/gather/gatherers/root-causes.js +1 -3
  60. package/core/gather/gatherers/trace-elements.js +1 -1
  61. package/core/gather/gatherers/trace.d.ts +1 -0
  62. package/core/gather/gatherers/trace.js +4 -0
  63. package/core/gather/navigation-runner.js +49 -62
  64. package/core/gather/session.js +5 -0
  65. package/core/gather/snapshot-runner.js +25 -25
  66. package/core/gather/timespan-runner.js +24 -25
  67. package/core/lib/asset-saver.js +1 -1
  68. package/core/lib/emulation.js +0 -3
  69. package/core/lib/{dependency-graph → lantern}/base-node.d.ts +12 -8
  70. package/core/lib/{dependency-graph → lantern}/base-node.js +8 -3
  71. package/core/lib/{dependency-graph → lantern}/cpu-node.d.ts +5 -1
  72. package/core/lib/{dependency-graph → lantern}/cpu-node.js +4 -0
  73. package/core/lib/lantern/lantern.d.ts +8 -0
  74. package/core/lib/lantern/lantern.js +31 -0
  75. package/core/lib/lantern/metric.d.ts +71 -0
  76. package/core/lib/lantern/metric.js +143 -0
  77. package/core/lib/{dependency-graph → lantern}/network-node.d.ts +17 -10
  78. package/core/lib/lantern/network-node.js +114 -0
  79. package/core/lib/lantern/page-dependency-graph.d.ts +58 -0
  80. package/core/lib/lantern/page-dependency-graph.js +463 -0
  81. package/core/lib/{dependency-graph → lantern}/simulator/connection-pool.d.ts +14 -14
  82. package/core/lib/{dependency-graph → lantern}/simulator/connection-pool.js +7 -7
  83. package/core/lib/{dependency-graph → lantern}/simulator/dns-cache.d.ts +5 -5
  84. package/core/lib/{dependency-graph → lantern}/simulator/dns-cache.js +3 -3
  85. package/core/lib/{dependency-graph → lantern}/simulator/network-analyzer.d.ts +36 -32
  86. package/core/lib/{dependency-graph → lantern}/simulator/network-analyzer.js +21 -17
  87. package/core/lib/{dependency-graph → lantern}/simulator/simulator.d.ts +18 -15
  88. package/core/lib/{dependency-graph → lantern}/simulator/simulator.js +18 -15
  89. package/core/lib/lantern/types/lantern.d.ts +129 -0
  90. package/core/lib/lantern/types/lantern.js +7 -0
  91. package/core/lib/lantern-trace-saver.d.ts +4 -4
  92. package/core/lib/lantern-trace-saver.js +6 -4
  93. package/core/lib/navigation-error.js +1 -1
  94. package/core/lib/network-recorder.js +2 -2
  95. package/core/lib/network-request.d.ts +6 -0
  96. package/core/lib/network-request.js +12 -0
  97. package/package.json +4 -5
  98. package/shared/localization/locales/ar-XB.json +1 -1
  99. package/shared/localization/locales/ar.json +1 -1
  100. package/shared/localization/locales/bg.json +1 -1
  101. package/shared/localization/locales/ca.json +1 -1
  102. package/shared/localization/locales/cs.json +1 -1
  103. package/shared/localization/locales/da.json +2 -2
  104. package/shared/localization/locales/de.json +1 -1
  105. package/shared/localization/locales/el.json +1 -1
  106. package/shared/localization/locales/en-GB.json +1 -1
  107. package/shared/localization/locales/en-US.json +3 -0
  108. package/shared/localization/locales/en-XA.json +1 -1
  109. package/shared/localization/locales/en-XL.json +3 -0
  110. package/shared/localization/locales/es-419.json +1 -1
  111. package/shared/localization/locales/es.json +1 -1
  112. package/shared/localization/locales/fi.json +1 -1
  113. package/shared/localization/locales/fil.json +1 -1
  114. package/shared/localization/locales/he.json +1 -1
  115. package/shared/localization/locales/hi.json +1 -1
  116. package/shared/localization/locales/hr.json +1 -1
  117. package/shared/localization/locales/hu.json +1 -1
  118. package/shared/localization/locales/id.json +1 -1
  119. package/shared/localization/locales/it.json +1 -1
  120. package/shared/localization/locales/ko.json +1 -1
  121. package/shared/localization/locales/lt.json +1 -1
  122. package/shared/localization/locales/nl.json +1 -1
  123. package/shared/localization/locales/no.json +1 -1
  124. package/shared/localization/locales/pl.json +1 -1
  125. package/shared/localization/locales/pt-PT.json +1 -1
  126. package/shared/localization/locales/pt.json +1 -1
  127. package/shared/localization/locales/ro.json +1 -1
  128. package/shared/localization/locales/sk.json +1 -1
  129. package/shared/localization/locales/sl.json +1 -1
  130. package/shared/localization/locales/sr-Latn.json +1 -1
  131. package/shared/localization/locales/sr.json +1 -1
  132. package/shared/localization/locales/sv.json +1 -1
  133. package/shared/localization/locales/ta.json +1 -1
  134. package/shared/localization/locales/te.json +1 -1
  135. package/shared/localization/locales/th.json +4 -4
  136. package/shared/localization/locales/tr.json +1 -1
  137. package/shared/localization/locales/uk.json +1 -1
  138. package/shared/localization/locales/vi.json +1 -1
  139. package/shared/localization/locales/zh-HK.json +1 -1
  140. package/shared/localization/locales/zh-TW.json +1 -1
  141. package/shared/localization/locales/zh.json +1 -1
  142. package/third-party/chromium-synchronization/installability-errors-test.js +1 -1
  143. package/tsconfig.json +7 -5
  144. package/types/artifacts.d.ts +3 -5
  145. package/types/gatherer.d.ts +8 -35
  146. package/types/lhr/lhr.d.ts +1 -1
  147. package/core/audits/script-elements-test-audit.d.ts +0 -14
  148. package/core/audits/script-elements-test-audit.js +0 -29
  149. package/core/gather/gatherers/script-elements.d.ts +0 -21
  150. package/core/gather/gatherers/script-elements.js +0 -100
  151. package/core/lib/dependency-graph/network-node.js +0 -99
  152. /package/core/lib/{dependency-graph → lantern}/simulator/simulator-timing-map.d.ts +0 -0
  153. /package/core/lib/{dependency-graph → lantern}/simulator/simulator-timing-map.js +0 -0
  154. /package/core/lib/{dependency-graph → lantern}/simulator/tcp-connection.d.ts +0 -0
  155. /package/core/lib/{dependency-graph → lantern}/simulator/tcp-connection.js +0 -0
@@ -36,7 +36,7 @@ class CSSUsage extends BaseGatherer {
36
36
  /**
37
37
  * @param {LH.Crdp.CSS.StyleSheetAddedEvent} event
38
38
  */
39
- async _onStylesheetAdded(event) {
39
+ _onStylesheetAdded(event) {
40
40
  if (!this._session) throw new Error('Session not initialized');
41
41
  const styleSheetId = event.header.styleSheetId;
42
42
  const sheetPromise = this._session.sendCommand('CSS.getStyleSheetText', {styleSheetId})
@@ -66,41 +66,43 @@ class CSSUsage extends BaseGatherer {
66
66
  /**
67
67
  * @param {LH.Gatherer.Context} context
68
68
  */
69
- async startCSSUsageTracking(context) {
69
+ async startInstrumentation(context) {
70
70
  const session = context.driver.defaultSession;
71
71
  this._session = session;
72
- session.on('CSS.styleSheetAdded', this._onStylesheetAdded);
72
+
73
+ // Calling `CSS.enable` will emit events for stylesheets currently on the page.
74
+ // We want to ignore these events in navigation mode because they are not relevant to the
75
+ // navigation that is about to happen. Adding the event listener *after* calling `CSS.enable`
76
+ // ensures that the events for pre-existing stylesheets are ignored.
77
+ const isNavigation = context.gatherMode === 'navigation';
78
+ if (!isNavigation) {
79
+ session.on('CSS.styleSheetAdded', this._onStylesheetAdded);
80
+ }
73
81
 
74
82
  await session.sendCommand('DOM.enable');
75
83
  await session.sendCommand('CSS.enable');
76
84
  await session.sendCommand('CSS.startRuleUsageTracking');
85
+
86
+ if (isNavigation) {
87
+ session.on('CSS.styleSheetAdded', this._onStylesheetAdded);
88
+ }
77
89
  }
78
90
 
79
91
 
80
92
  /**
81
93
  * @param {LH.Gatherer.Context} context
82
94
  */
83
- async stopCSSUsageTracking(context) {
95
+ async stopInstrumentation(context) {
84
96
  const session = context.driver.defaultSession;
85
97
  const coverageResponse = await session.sendCommand('CSS.stopRuleUsageTracking');
86
98
  this._ruleUsage = coverageResponse.ruleUsage;
87
99
  session.off('CSS.styleSheetAdded', this._onStylesheetAdded);
88
- }
89
100
 
90
- /**
91
- * @param {LH.Gatherer.Context} context
92
- */
93
- async startInstrumentation(context) {
94
- if (context.gatherMode !== 'timespan') return;
95
- await this.startCSSUsageTracking(context);
96
- }
101
+ // Ensure we finish fetching all stylesheet contents before disabling the CSS domain
102
+ await Promise.all(this._sheetPromises.values());
97
103
 
98
- /**
99
- * @param {LH.Gatherer.Context} context
100
- */
101
- async stopInstrumentation(context) {
102
- if (context.gatherMode !== 'timespan') return;
103
- await this.stopCSSUsageTracking(context);
104
+ await session.sendCommand('CSS.disable');
105
+ await session.sendCommand('DOM.disable');
104
106
  }
105
107
 
106
108
  /**
@@ -108,17 +110,16 @@ class CSSUsage extends BaseGatherer {
108
110
  * @return {Promise<LH.Artifacts['CSSUsage']>}
109
111
  */
110
112
  async getArtifact(context) {
111
- const session = context.driver.defaultSession;
112
113
  const executionContext = context.driver.executionContext;
113
114
 
114
- if (context.gatherMode !== 'timespan') {
115
- await this.startCSSUsageTracking(context);
115
+ if (context.gatherMode === 'snapshot') {
116
+ await this.startInstrumentation(context);
116
117
 
117
118
  // Force style to recompute.
118
119
  // Doesn't appear to be necessary in newer versions of Chrome.
119
120
  await executionContext.evaluateAsync('getComputedStyle(document.body)');
120
121
 
121
- await this.stopCSSUsageTracking(context);
122
+ await this.stopInstrumentation(context);
122
123
  }
123
124
 
124
125
  /** @type {Map<string, LH.Artifacts.CSSStyleSheetInfo>} */
@@ -140,9 +141,6 @@ class CSSUsage extends BaseGatherer {
140
141
  dedupedStylesheets.set(sheet.content, sheet);
141
142
  }
142
143
 
143
- await session.sendCommand('CSS.disable');
144
- await session.sendCommand('DOM.disable');
145
-
146
144
  if (!this._ruleUsage) throw new Error('Issue collecting rule usages');
147
145
 
148
146
  return {
@@ -12,6 +12,10 @@ declare class DevtoolsLog extends BaseGatherer {
12
12
  * @param {LH.Gatherer.Context} passContext
13
13
  */
14
14
  stopSensitiveInstrumentation({ driver }: LH.Gatherer.Context): Promise<void>;
15
+ /**
16
+ * @return {LH.Artifacts['DevtoolsLog']}
17
+ */
18
+ getDebugData(): LH.Artifacts['DevtoolsLog'];
15
19
  /**
16
20
  * @return {Promise<LH.Artifacts['DevtoolsLog']>}
17
21
  */
@@ -49,6 +49,13 @@ class DevtoolsLog extends BaseGatherer {
49
49
  driver.targetManager.off('protocolevent', this._onProtocolMessage);
50
50
  }
51
51
 
52
+ /**
53
+ * @return {LH.Artifacts['DevtoolsLog']}
54
+ */
55
+ getDebugData() {
56
+ return this._messageLog.messages;
57
+ }
58
+
52
59
  /**
53
60
  * @return {Promise<LH.Artifacts['DevtoolsLog']>}
54
61
  */
@@ -1,5 +1,9 @@
1
1
  export default FullPageScreenshot;
2
2
  declare class FullPageScreenshot extends BaseGatherer {
3
+ /**
4
+ * @param {LH.Gatherer.Context} context
5
+ */
6
+ waitForNetworkIdle(context: LH.Gatherer.Context): import("../driver/wait-for-condition.js").CancellableWait<void>;
3
7
  /**
4
8
  * @param {LH.Gatherer.Context} context
5
9
  * @param {{height: number, width: number, mobile: boolean}} deviceMetrics
@@ -13,7 +13,10 @@ import {waitForNetworkIdle} from '../driver/wait-for-condition.js';
13
13
  // JPEG quality setting
14
14
  // Exploration and examples of reports using different quality settings: https://docs.google.com/document/d/1ZSffucIca9XDW2eEwfoevrk-OTl7WQFeMf0CgeJAA8M/edit#
15
15
  // Note: this analysis was done for JPEG, but now we use WEBP.
16
- const FULL_PAGE_SCREENSHOT_QUALITY = 30;
16
+ const FULL_PAGE_SCREENSHOT_QUALITY = process.env.LH_FPS_TEST ? 100 : 30;
17
+ // webp currently cant do lossless encoding, so to help tests switch to png
18
+ // Remove when this is resolved: https://bugs.chromium.org/p/chromium/issues/detail?id=1469183
19
+ const FULL_PAGE_SCREENSHOT_FORMAT = process.env.LH_FPS_TEST ? 'png' : 'webp';
17
20
 
18
21
  // https://developers.google.com/speed/webp/faq#what_is_the_maximum_size_a_webp_image_can_be
19
22
  const MAX_WEBP_SIZE = 16383;
@@ -44,17 +47,6 @@ function getObservedDeviceMetrics() {
44
47
  };
45
48
  }
46
49
 
47
- /**
48
- * The screenshot dimensions are sized to `window.outerHeight` / `window.outerWidth`,
49
- * however the bounding boxes of the elements are relative to `window.innerHeight` / `window.innerWidth`.
50
- */
51
- function getScreenshotAreaSize() {
52
- return {
53
- width: window.innerWidth,
54
- height: window.innerHeight,
55
- };
56
- }
57
-
58
50
  function waitForDoubleRaf() {
59
51
  return new Promise((resolve) => {
60
52
  requestAnimationFrame(() => requestAnimationFrame(resolve));
@@ -69,6 +61,21 @@ class FullPageScreenshot extends BaseGatherer {
69
61
  supportedModes: ['snapshot', 'timespan', 'navigation'],
70
62
  };
71
63
 
64
+ /**
65
+ * @param {LH.Gatherer.Context} context
66
+ */
67
+ waitForNetworkIdle(context) {
68
+ const session = context.driver.defaultSession;
69
+ const networkMonitor = context.driver.networkMonitor;
70
+ return waitForNetworkIdle(session, networkMonitor, {
71
+ pretendDCLAlreadyFired: true,
72
+ networkQuietThresholdMs: 1000,
73
+ busyEvent: 'network-critical-busy',
74
+ idleEvent: 'network-critical-idle',
75
+ isIdle: recorder => recorder.isCriticalIdle(),
76
+ });
77
+ }
78
+
72
79
  /**
73
80
  * @param {LH.Gatherer.Context} context
74
81
  * @param {{height: number, width: number, mobile: boolean}} deviceMetrics
@@ -86,15 +93,7 @@ class FullPageScreenshot extends BaseGatherer {
86
93
  );
87
94
  const height = Math.min(fullHeight, MAX_WEBP_SIZE);
88
95
 
89
- // Setup network monitor before we change the viewport.
90
- const networkMonitor = context.driver.networkMonitor;
91
- const waitForNetworkIdleResult = waitForNetworkIdle(session, networkMonitor, {
92
- pretendDCLAlreadyFired: true,
93
- networkQuietThresholdMs: 1000,
94
- busyEvent: 'network-critical-busy',
95
- idleEvent: 'network-critical-idle',
96
- isIdle: recorder => recorder.isCriticalIdle(),
97
- });
96
+ const waitForNetworkIdleResult = this.waitForNetworkIdle(context);
98
97
 
99
98
  await session.sendCommand('Emulation.setDeviceMetricsOverride', {
100
99
  mobile: deviceMetrics.mobile,
@@ -120,22 +119,19 @@ class FullPageScreenshot extends BaseGatherer {
120
119
  * @return {Promise<LH.Result.FullPageScreenshot['screenshot']>}
121
120
  */
122
121
  async _takeScreenshot(context) {
123
- const result = await context.driver.defaultSession.sendCommand('Page.captureScreenshot', {
124
- format: 'webp',
125
- quality: FULL_PAGE_SCREENSHOT_QUALITY,
126
- });
127
- const data = 'data:image/webp;base64,' + result.data;
128
-
129
- const screenshotAreaSize =
130
- await context.driver.executionContext.evaluate(getScreenshotAreaSize, {
131
- args: [],
132
- useIsolation: true,
133
- });
122
+ const [metrics, result] = await Promise.all([
123
+ context.driver.defaultSession.sendCommand('Page.getLayoutMetrics'),
124
+ context.driver.defaultSession.sendCommand('Page.captureScreenshot', {
125
+ format: FULL_PAGE_SCREENSHOT_FORMAT,
126
+ quality: FULL_PAGE_SCREENSHOT_QUALITY,
127
+ }),
128
+ ]);
129
+ const data = `data:image/${FULL_PAGE_SCREENSHOT_FORMAT};base64,` + result.data;
134
130
 
135
131
  return {
136
132
  data,
137
- width: screenshotAreaSize.width,
138
- height: screenshotAreaSize.height,
133
+ width: metrics.cssVisualViewport.clientWidth,
134
+ height: metrics.cssVisualViewport.clientHeight,
139
135
  };
140
136
  }
141
137
 
@@ -159,7 +155,7 @@ class FullPageScreenshot extends BaseGatherer {
159
155
  for (const [node, id] of lhIdToElements.entries()) {
160
156
  // @ts-expect-error - getBoundingClientRect put into scope via stringification
161
157
  const rect = getBoundingClientRect(node);
162
- nodes[id] = rect;
158
+ nodes[id] = {id: node.id, ...rect};
163
159
  }
164
160
 
165
161
  return nodes;
@@ -124,9 +124,7 @@ class RootCauses extends BaseGatherer {
124
124
  rootCauses.layoutShifts[layoutShiftEvents.indexOf(event)] = r;
125
125
  }
126
126
 
127
- // Yeah this gatherer enabled it, and so you'd think it should disable it too...
128
- // ...but we don't give gatherers their own session so this stomps on others.
129
- // await driver.defaultSession.sendCommand('DOM.disable');
127
+ await driver.defaultSession.sendCommand('DOM.disable');
130
128
  await driver.defaultSession.sendCommand('CSS.disable');
131
129
 
132
130
  return rootCauses;
@@ -155,7 +155,7 @@ class TraceElements extends BaseGatherer {
155
155
  const {settings} = context;
156
156
  try {
157
157
  const responsivenessEvent = await Responsiveness.request({trace, settings}, context);
158
- if (!responsivenessEvent || responsivenessEvent.name === 'FallbackTiming') return;
158
+ if (!responsivenessEvent) return;
159
159
  return {nodeId: responsivenessEvent.args.data.nodeId};
160
160
  } catch {
161
161
  // Don't let responsiveness errors sink the rest of the gatherer.
@@ -17,6 +17,7 @@ declare class Trace extends BaseGatherer {
17
17
  * @param {LH.Gatherer.Context} passContext
18
18
  */
19
19
  stopSensitiveInstrumentation({ driver }: LH.Gatherer.Context): Promise<void>;
20
+ getDebugData(): import("../../index.js").Trace;
20
21
  getArtifact(): import("../../index.js").Trace;
21
22
  }
22
23
  import BaseGatherer from '../base-gatherer.js';
@@ -126,6 +126,10 @@ class Trace extends BaseGatherer {
126
126
  this._trace = await Trace.endTraceAndCollectEvents(driver.defaultSession);
127
127
  }
128
128
 
129
+ getDebugData() {
130
+ return this._trace;
131
+ }
132
+
129
133
  getArtifact() {
130
134
  return this._trace;
131
135
  }
@@ -110,31 +110,19 @@ async function _navigate(navigationContext) {
110
110
  * @return {Promise<{devtoolsLog?: LH.DevtoolsLog, records?: Array<LH.Artifacts.NetworkRequest>, trace?: LH.Trace}>}
111
111
  */
112
112
  async function _collectDebugData(navigationContext, phaseState) {
113
- const devtoolsLogArtifactDefn = phaseState.artifactDefinitions.find(
114
- definition => definition.gatherer.instance.meta.symbol === DevtoolsLog.symbol
115
- );
116
- const traceArtifactDefn = phaseState.artifactDefinitions.find(
117
- definition => definition.gatherer.instance.meta.symbol === Trace.symbol
118
- );
119
-
120
- const artifactDefinitions = [devtoolsLogArtifactDefn, traceArtifactDefn].filter(
121
- /**
122
- * @param {LH.Config.AnyArtifactDefn | undefined} defn
123
- * @return {defn is LH.Config.AnyArtifactDefn}
124
- */
125
- defn => Boolean(defn)
126
- );
127
- if (!artifactDefinitions.length) return {};
128
-
129
- await collectPhaseArtifacts({...phaseState, phase: 'getArtifact', artifactDefinitions});
130
- const getArtifactState = phaseState.artifactState.getArtifact;
131
-
132
- const devtoolsLogArtifactId = devtoolsLogArtifactDefn?.id;
133
- const devtoolsLog = devtoolsLogArtifactId && (await getArtifactState[devtoolsLogArtifactId]);
134
- const records = devtoolsLog && (await NetworkRecords.request(devtoolsLog, navigationContext));
113
+ let devtoolsLog;
114
+ let trace;
115
+
116
+ for (const definition of phaseState.artifactDefinitions) {
117
+ const {instance} = definition.gatherer;
118
+ if (instance instanceof DevtoolsLog) {
119
+ devtoolsLog = instance.getDebugData();
120
+ } else if (instance instanceof Trace) {
121
+ trace = instance.getDebugData();
122
+ }
123
+ }
135
124
 
136
- const traceArtifactId = traceArtifactDefn?.id;
137
- const trace = traceArtifactId && (await getArtifactState[traceArtifactId]);
125
+ const records = devtoolsLog && (await NetworkRecords.request(devtoolsLog, navigationContext));
138
126
 
139
127
  return {devtoolsLog, records, trace};
140
128
  }
@@ -277,44 +265,43 @@ async function navigationGather(page, requestor, options = {}) {
277
265
  const isCallback = typeof requestor === 'function';
278
266
 
279
267
  const runnerOptions = {resolvedConfig, computedCache};
280
- const artifacts = await Runner.gather(
281
- async () => {
282
- const normalizedRequestor = isCallback ? requestor : UrlUtils.normalizeUrl(requestor);
283
-
284
- /** @type {LH.Puppeteer.Browser|undefined} */
285
- let lhBrowser = undefined;
286
- /** @type {LH.Puppeteer.Page|undefined} */
287
- let lhPage = undefined;
288
-
289
- // For navigation mode, we shouldn't connect to a browser in audit mode,
290
- // therefore we connect to the browser in the gatherFn callback.
291
- if (!page) {
292
- const {hostname = DEFAULT_HOSTNAME, port = DEFAULT_PORT} = flags;
293
- lhBrowser = await puppeteer.connect({browserURL: `http://${hostname}:${port}`, defaultViewport: null});
294
- lhPage = await lhBrowser.newPage();
295
- page = lhPage;
296
- }
297
-
298
- const driver = new Driver(page);
299
- const context = {
300
- driver,
301
- lhBrowser,
302
- lhPage,
303
- page,
304
- resolvedConfig,
305
- requestor: normalizedRequestor,
306
- computedCache,
307
- };
308
- const {baseArtifacts} = await _setup(context);
309
-
310
- const artifacts = await _navigation({...context, baseArtifacts});
311
-
312
- await _cleanup(context);
313
-
314
- return finalizeArtifacts(baseArtifacts, artifacts);
315
- },
316
- runnerOptions
317
- );
268
+
269
+ const gatherFn = async () => {
270
+ const normalizedRequestor = isCallback ? requestor : UrlUtils.normalizeUrl(requestor);
271
+
272
+ /** @type {LH.Puppeteer.Browser|undefined} */
273
+ let lhBrowser = undefined;
274
+ /** @type {LH.Puppeteer.Page|undefined} */
275
+ let lhPage = undefined;
276
+
277
+ // For navigation mode, we shouldn't connect to a browser in audit mode,
278
+ // therefore we connect to the browser in the gatherFn callback.
279
+ if (!page) {
280
+ const {hostname = DEFAULT_HOSTNAME, port = DEFAULT_PORT} = flags;
281
+ lhBrowser = await puppeteer.connect({browserURL: `http://${hostname}:${port}`, defaultViewport: null});
282
+ lhPage = await lhBrowser.newPage();
283
+ page = lhPage;
284
+ }
285
+
286
+ const driver = new Driver(page);
287
+ const context = {
288
+ driver,
289
+ lhBrowser,
290
+ lhPage,
291
+ page,
292
+ resolvedConfig,
293
+ requestor: normalizedRequestor,
294
+ computedCache,
295
+ };
296
+ const {baseArtifacts} = await _setup(context);
297
+
298
+ const artifacts = await _navigation({...context, baseArtifacts});
299
+
300
+ await _cleanup(context);
301
+
302
+ return finalizeArtifacts(baseArtifacts, artifacts);
303
+ };
304
+ const artifacts = await Runner.gather(gatherFn, runnerOptions);
318
305
  return {artifacts, runnerOptions};
319
306
  }
320
307
 
@@ -6,6 +6,8 @@
6
6
 
7
7
  import EventEmitter from 'events';
8
8
 
9
+ import log from 'lighthouse-logger';
10
+
9
11
  import {LighthouseError} from '../lib/lh-error.js';
10
12
 
11
13
  // Controls how long to wait for a response after sending a DevTools protocol command.
@@ -107,6 +109,9 @@ class ProtocolSession extends CrdpEventEmitter {
107
109
  const resultPromise = this._cdpSession.send(method, ...params, {
108
110
  // Add 50ms to the Puppeteer timeout to ensure the Lighthouse timeout finishes first.
109
111
  timeout: timeoutMs + PPTR_BUFFER,
112
+ }).catch((error) => {
113
+ log.formatProtocol('method <= browser ERR', {method}, 'error');
114
+ throw LighthouseError.fromProtocolMessage(method, error);
110
115
  });
111
116
  const resultWithTimeoutPromise = Promise.race([resultPromise, timeoutPromise]);
112
117
 
@@ -30,35 +30,35 @@ async function snapshotGather(page, options = {}) {
30
30
  const url = await driver.url();
31
31
 
32
32
  const runnerOptions = {resolvedConfig, computedCache};
33
- const artifacts = await Runner.gather(
34
- async () => {
35
- const baseArtifacts =
33
+
34
+ const gatherFn = async () => {
35
+ const baseArtifacts =
36
36
  await getBaseArtifacts(resolvedConfig, driver, {gatherMode: 'snapshot'});
37
- baseArtifacts.URL = {
38
- finalDisplayedUrl: url,
39
- };
37
+ baseArtifacts.URL = {
38
+ finalDisplayedUrl: url,
39
+ };
40
+
41
+ const artifactDefinitions = resolvedConfig.artifacts || [];
42
+ const artifactState = getEmptyArtifactState();
43
+ await collectPhaseArtifacts({
44
+ phase: 'getArtifact',
45
+ gatherMode: 'snapshot',
46
+ driver,
47
+ page,
48
+ baseArtifacts,
49
+ artifactDefinitions,
50
+ artifactState,
51
+ computedCache,
52
+ settings: resolvedConfig.settings,
53
+ });
40
54
 
41
- const artifactDefinitions = resolvedConfig.artifacts || [];
42
- const artifactState = getEmptyArtifactState();
43
- await collectPhaseArtifacts({
44
- phase: 'getArtifact',
45
- gatherMode: 'snapshot',
46
- driver,
47
- page,
48
- baseArtifacts,
49
- artifactDefinitions,
50
- artifactState,
51
- computedCache,
52
- settings: resolvedConfig.settings,
53
- });
55
+ await driver.disconnect();
54
56
 
55
- await driver.disconnect();
57
+ const artifacts = await awaitArtifacts(artifactState);
58
+ return finalizeArtifacts(baseArtifacts, artifacts);
59
+ };
56
60
 
57
- const artifacts = await awaitArtifacts(artifactState);
58
- return finalizeArtifacts(baseArtifacts, artifacts);
59
- },
60
- runnerOptions
61
- );
61
+ const artifacts = await Runner.gather(gatherFn, runnerOptions);
62
62
  return {artifacts, runnerOptions};
63
63
  }
64
64
 
@@ -72,31 +72,30 @@ async function startTimespanGather(page, options = {}) {
72
72
  const finalDisplayedUrl = await driver.url();
73
73
 
74
74
  const runnerOptions = {resolvedConfig, computedCache};
75
- const artifacts = await Runner.gather(
76
- async () => {
77
- baseArtifacts.URL = {finalDisplayedUrl};
78
-
79
- await collectPhaseArtifacts({phase: 'stopSensitiveInstrumentation', ...phaseOptions});
80
- await collectPhaseArtifacts({phase: 'stopInstrumentation', ...phaseOptions});
81
-
82
- // bf-cache-failures can emit `Page.frameNavigated` at the end of the run.
83
- // This can cause us to issue protocol commands after the target closes.
84
- // We should disable our `Page.frameNavigated` handlers before that.
85
- await disableAsyncStacks();
86
-
87
- driver.defaultSession.off('Page.frameNavigated', onFrameNavigated);
88
- if (pageNavigationDetected) {
89
- baseArtifacts.LighthouseRunWarnings.push(str_(UIStrings.warningNavigationDetected));
90
- }
91
-
92
- await collectPhaseArtifacts({phase: 'getArtifact', ...phaseOptions});
93
- await driver.disconnect();
94
-
95
- const artifacts = await awaitArtifacts(artifactState);
96
- return finalizeArtifacts(baseArtifacts, artifacts);
97
- },
98
- runnerOptions
99
- );
75
+ const gatherFn = async () => {
76
+ baseArtifacts.URL = {finalDisplayedUrl};
77
+
78
+ await collectPhaseArtifacts({phase: 'stopSensitiveInstrumentation', ...phaseOptions});
79
+ await collectPhaseArtifacts({phase: 'stopInstrumentation', ...phaseOptions});
80
+
81
+ // bf-cache-failures can emit `Page.frameNavigated` at the end of the run.
82
+ // This can cause us to issue protocol commands after the target closes.
83
+ // We should disable our `Page.frameNavigated` handlers before that.
84
+ await disableAsyncStacks();
85
+
86
+ driver.defaultSession.off('Page.frameNavigated', onFrameNavigated);
87
+ if (pageNavigationDetected) {
88
+ baseArtifacts.LighthouseRunWarnings.push(str_(UIStrings.warningNavigationDetected));
89
+ }
90
+
91
+ await collectPhaseArtifacts({phase: 'getArtifact', ...phaseOptions});
92
+ await driver.disconnect();
93
+
94
+ const artifacts = await awaitArtifacts(artifactState);
95
+ return finalizeArtifacts(baseArtifacts, artifacts);
96
+ };
97
+
98
+ const artifacts = await Runner.gather(gatherFn, runnerOptions);
100
99
  return {artifacts, runnerOptions};
101
100
  },
102
101
  };
@@ -11,7 +11,7 @@ import url from 'url';
11
11
 
12
12
  import log from 'lighthouse-logger';
13
13
 
14
- import {Simulator} from './dependency-graph/simulator/simulator.js';
14
+ import {Simulator} from './lantern/simulator/simulator.js';
15
15
  import lanternTraceSaver from './lantern-trace-saver.js';
16
16
  import {MetricTraceEvents} from './traces/metric-trace-events.js';
17
17
  import {NetworkAnalysis} from '../computed/network-analysis.js';
@@ -4,8 +4,6 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- import {lighthouseVersion} from '../../shared/root.js';
8
-
9
7
  const NO_THROTTLING_METRICS = {
10
8
  latency: 0,
11
9
  downloadThroughput: 0,
@@ -29,7 +27,6 @@ function parseUseragentIntoMetadata(userAgent, formFactor) {
29
27
  const brands = [
30
28
  {brand: 'Chromium', version},
31
29
  {brand: 'Google Chrome', version},
32
- {brand: 'Lighthouse', version: lighthouseVersion},
33
30
  ];
34
31
 
35
32
  const motoGPowerDetails = {
@@ -2,7 +2,7 @@
2
2
  * A union of all types derived from BaseNode, allowing type check discrimination
3
3
  * based on `node.type`. If a new node type is created, it should be added here.
4
4
  */
5
- export type Node = import('./cpu-node.js').CPUNode | import('./network-node.js').NetworkNode;
5
+ export type Node<T = any> = import('./cpu-node.js').CPUNode<T> | import('./network-node.js').NetworkNode<T>;
6
6
  /**
7
7
  * @license
8
8
  * Copyright 2017 Google LLC
@@ -11,7 +11,8 @@ export type Node = import('./cpu-node.js').CPUNode | import('./network-node.js')
11
11
  /**
12
12
  * A union of all types derived from BaseNode, allowing type check discrimination
13
13
  * based on `node.type`. If a new node type is created, it should be added here.
14
- * @typedef {import('./cpu-node.js').CPUNode | import('./network-node.js').NetworkNode} Node
14
+ * @template [T=any]
15
+ * @typedef {import('./cpu-node.js').CPUNode<T> | import('./network-node.js').NetworkNode<T>} Node
15
16
  */
16
17
  /**
17
18
  * @fileoverview This class encapsulates logic for handling resources and tasks used to model the
@@ -25,7 +26,10 @@ export type Node = import('./cpu-node.js').CPUNode | import('./network-node.js')
25
26
  * This allows particular optimizations in this class so that we do no need to check for cycles as
26
27
  * these methods are called and we can always start traversal at the root node.
27
28
  */
28
- export class BaseNode {
29
+ /**
30
+ * @template [T=any]
31
+ */
32
+ export class BaseNode<T = any> {
29
33
  /**
30
34
  * Returns whether the given node has a cycle in its dependent graph by performing a DFS.
31
35
  * @param {Node} node
@@ -134,17 +138,17 @@ export class BaseNode {
134
138
  *
135
139
  * The `getNextNodes` function takes a visited node and returns which nodes to
136
140
  * visit next. It defaults to returning the node's dependents.
137
- * @param {(node: Node, traversalPath: Node[]) => void} callback
138
- * @param {function(Node): Node[]} [getNextNodes]
141
+ * @param {(node: Node<T>, traversalPath: Node<T>[]) => void} callback
142
+ * @param {function(Node<T>): Node<T>[]} [getNextNodes]
139
143
  */
140
- traverse(callback: (node: Node, traversalPath: Node[]) => void, getNextNodes?: ((arg0: Node) => Node[]) | undefined): void;
144
+ traverse(callback: (node: Node<T>, traversalPath: Node<T>[]) => void, getNextNodes?: ((arg0: Node<T>) => Node<T>[]) | undefined): void;
141
145
  /**
142
146
  * @see BaseNode.traverse
143
147
  * @param {function(Node): Node[]} [getNextNodes]
144
148
  */
145
149
  traverseGenerator(getNextNodes?: ((arg0: Node) => Node[]) | undefined): Generator<{
146
- node: Node;
147
- traversalPath: Node[];
150
+ node: Node<any>;
151
+ traversalPath: Node<any>[];
148
152
  }, void, unknown>;
149
153
  /**
150
154
  * @param {Node} node