lighthouse 11.7.1 → 12.0.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 (272) hide show
  1. package/CONTRIBUTING.md +2 -2
  2. package/cli/bin.js +0 -7
  3. package/cli/cli-flags.d.ts +1 -5
  4. package/cli/cli-flags.js +4 -8
  5. package/cli/test/smokehouse/__snapshots__/report-assert-test.js.snap +14 -14
  6. package/cli/test/smokehouse/config/exclusions.js +3 -0
  7. package/cli/test/smokehouse/core-tests.js +6 -20
  8. package/cli/test/smokehouse/frontends/smokehouse-bin.js +2 -2
  9. package/core/audits/accessibility/aria-conditional-attr.d.ts +10 -0
  10. package/core/audits/accessibility/aria-conditional-attr.js +43 -0
  11. package/core/audits/accessibility/{duplicate-id-active.d.ts → aria-deprecated-role.d.ts} +3 -3
  12. package/core/audits/accessibility/aria-deprecated-role.js +43 -0
  13. package/core/audits/accessibility/aria-prohibited-attr.d.ts +10 -0
  14. package/core/audits/accessibility/aria-prohibited-attr.js +44 -0
  15. package/core/audits/accessibility/target-size.js +0 -1
  16. package/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +0 -16
  17. package/core/audits/byte-efficiency/byte-efficiency-audit.js +1 -38
  18. package/core/audits/byte-efficiency/offscreen-images.d.ts +0 -11
  19. package/core/audits/byte-efficiency/offscreen-images.js +0 -15
  20. package/core/audits/byte-efficiency/render-blocking-resources.js +27 -26
  21. package/core/audits/byte-efficiency/unminified-css.js +2 -2
  22. package/core/audits/byte-efficiency/unused-css-rules.js +3 -1
  23. package/core/audits/dobetterweb/uses-http2.d.ts +0 -13
  24. package/core/audits/dobetterweb/uses-http2.js +4 -38
  25. package/core/audits/font-display.js +2 -2
  26. package/core/audits/layout-shifts.js +1 -1
  27. package/core/audits/preload-fonts.d.ts +1 -5
  28. package/core/audits/preload-fonts.js +1 -10
  29. package/core/audits/prioritize-lcp-image.js +2 -2
  30. package/core/audits/redirects-http.d.ts +19 -0
  31. package/core/audits/redirects-http.js +75 -0
  32. package/core/audits/resource-summary.d.ts +2 -0
  33. package/core/audits/resource-summary.js +5 -4
  34. package/core/audits/seo/is-crawlable.d.ts +2 -1
  35. package/core/audits/seo/link-text.js +12 -0
  36. package/core/audits/uses-rel-preload.d.ts +1 -5
  37. package/core/audits/uses-rel-preload.js +5 -15
  38. package/core/audits/work-during-interaction.d.ts +2 -2
  39. package/core/audits/work-during-interaction.js +2 -2
  40. package/core/computed/js-bundles.d.ts +1 -1
  41. package/core/computed/metrics/lantern-first-contentful-paint.d.ts +13 -82
  42. package/core/computed/metrics/lantern-first-contentful-paint.js +16 -178
  43. package/core/computed/metrics/lantern-first-meaningful-paint.d.ts +13 -12
  44. package/core/computed/metrics/lantern-first-meaningful-paint.js +11 -49
  45. package/core/computed/metrics/lantern-interactive.d.ts +13 -21
  46. package/core/computed/metrics/lantern-interactive.js +12 -84
  47. package/core/computed/metrics/lantern-largest-contentful-paint.d.ts +13 -25
  48. package/core/computed/metrics/lantern-largest-contentful-paint.js +13 -80
  49. package/core/computed/metrics/lantern-max-potential-fid.d.ts +13 -24
  50. package/core/computed/metrics/lantern-max-potential-fid.js +12 -64
  51. package/core/computed/metrics/lantern-metric.d.ts +14 -18
  52. package/core/computed/metrics/lantern-metric.js +34 -30
  53. package/core/computed/metrics/lantern-speed-index.d.ts +13 -32
  54. package/core/computed/metrics/lantern-speed-index.js +13 -116
  55. package/core/computed/metrics/lantern-total-blocking-time.d.ts +13 -25
  56. package/core/computed/metrics/lantern-total-blocking-time.js +10 -94
  57. package/core/computed/metrics/metric.js +1 -3
  58. package/core/computed/module-duplication.d.ts +1 -1
  59. package/core/computed/navigation-insights.d.ts +36 -0
  60. package/core/computed/navigation-insights.js +35 -0
  61. package/core/computed/page-dependency-graph.js +3 -7
  62. package/core/computed/resource-summary.d.ts +10 -12
  63. package/core/computed/resource-summary.js +12 -19
  64. package/core/computed/trace-engine-result.d.ts +16 -3
  65. package/core/computed/trace-engine-result.js +19 -14
  66. package/core/computed/unused-css.d.ts +3 -1
  67. package/core/computed/unused-css.js +6 -5
  68. package/core/config/config-helpers.js +0 -4
  69. package/core/config/constants.js +0 -1
  70. package/core/config/default-config.js +63 -142
  71. package/core/config/experimental-config.js +11 -0
  72. package/core/config/filters.js +0 -1
  73. package/core/gather/driver/navigation.js +4 -1
  74. package/core/gather/driver/prepare.js +1 -1
  75. package/core/gather/driver/target-manager.js +4 -3
  76. package/core/gather/driver/wait-for-condition.js +2 -3
  77. package/core/gather/driver.d.ts +12 -0
  78. package/core/gather/driver.js +27 -0
  79. package/core/gather/gatherers/accessibility.js +4 -6
  80. package/core/gather/gatherers/css-usage.d.ts +0 -21
  81. package/core/gather/gatherers/css-usage.js +9 -119
  82. package/core/gather/gatherers/root-causes.d.ts +2 -2
  83. package/core/gather/gatherers/root-causes.js +6 -5
  84. package/core/gather/gatherers/stylesheets.d.ts +26 -0
  85. package/core/gather/gatherers/stylesheets.js +138 -0
  86. package/core/gather/gatherers/trace-elements.d.ts +2 -12
  87. package/core/gather/gatherers/trace-elements.js +2 -21
  88. package/core/gather/navigation-runner.js +3 -1
  89. package/core/gather/session.d.ts +8 -0
  90. package/core/gather/session.js +14 -1
  91. package/core/lib/emulation.js +2 -2
  92. package/core/lib/i18n/i18n.d.ts +0 -1
  93. package/core/lib/i18n/i18n.js +0 -2
  94. package/core/lib/lantern/cpu-node.d.ts +7 -1
  95. package/core/lib/lantern/cpu-node.js +12 -2
  96. package/core/lib/lantern/lantern-error.d.ts +8 -0
  97. package/core/lib/lantern/lantern-error.js +9 -0
  98. package/core/lib/lantern/metric.js +2 -6
  99. package/core/lib/lantern/metrics/first-contentful-paint.d.ts +79 -0
  100. package/core/lib/lantern/metrics/first-contentful-paint.js +200 -0
  101. package/core/lib/lantern/metrics/first-meaningful-paint.d.ts +6 -0
  102. package/core/lib/lantern/metrics/first-meaningful-paint.js +64 -0
  103. package/core/lib/lantern/metrics/interactive.d.ts +26 -0
  104. package/core/lib/lantern/metrics/interactive.js +112 -0
  105. package/core/lib/lantern/metrics/largest-contentful-paint.d.ts +19 -0
  106. package/core/lib/lantern/metrics/largest-contentful-paint.js +108 -0
  107. package/core/lib/lantern/metrics/max-potential-fid.d.ts +30 -0
  108. package/core/lib/lantern/metrics/max-potential-fid.js +92 -0
  109. package/core/lib/lantern/metrics/speed-index.d.ts +38 -0
  110. package/core/lib/lantern/metrics/speed-index.js +144 -0
  111. package/core/lib/lantern/metrics/total-blocking-time.d.ts +31 -0
  112. package/core/lib/lantern/metrics/total-blocking-time.js +128 -0
  113. package/core/lib/lantern/network-node.js +28 -4
  114. package/core/lib/lantern/page-dependency-graph.d.ts +4 -4
  115. package/core/lib/lantern/page-dependency-graph.js +19 -7
  116. package/core/lib/lantern/simulator/connection-pool.d.ts +2 -12
  117. package/core/lib/lantern/simulator/connection-pool.js +3 -22
  118. package/core/lib/lantern/simulator/network-analyzer.js +1 -12
  119. package/core/lib/lantern/simulator/simulator.d.ts +2 -4
  120. package/core/lib/lantern/simulator/simulator.js +7 -14
  121. package/core/lib/lantern/types/lantern.d.ts +5 -3
  122. package/core/lib/lantern-trace-saver.js +0 -1
  123. package/core/lib/lh-error.d.ts +9 -0
  124. package/core/lib/lh-error.js +13 -3
  125. package/core/lib/lighthouse-compatibility.js +53 -10
  126. package/core/lib/navigation-error.js +3 -0
  127. package/core/lib/network-request.d.ts +1 -0
  128. package/core/lib/network-request.js +28 -0
  129. package/core/runner.js +18 -25
  130. package/dist/report/bundle.esm.js +11 -112
  131. package/dist/report/flow.js +16 -117
  132. package/dist/report/standalone.js +12 -113
  133. package/flow-report/src/help-dialog.tsx +0 -1
  134. package/flow-report/src/i18n/i18n.d.ts +0 -4
  135. package/flow-report/src/i18n/ui-strings.d.ts +0 -1
  136. package/flow-report/src/i18n/ui-strings.js +0 -2
  137. package/flow-report/src/summary/category.tsx +10 -12
  138. package/package.json +5 -5
  139. package/readme.md +4 -4
  140. package/report/assets/styles.css +1 -60
  141. package/report/assets/templates.html +0 -95
  142. package/report/renderer/category-renderer.js +1 -1
  143. package/report/renderer/components.d.ts +2 -2
  144. package/report/renderer/components.js +2 -103
  145. package/report/renderer/dom.d.ts +1 -1
  146. package/report/renderer/dom.js +1 -1
  147. package/report/renderer/performance-category-renderer.d.ts +0 -7
  148. package/report/renderer/performance-category-renderer.js +1 -32
  149. package/report/renderer/report-renderer.js +3 -13
  150. package/report/renderer/report-ui-features.js +1 -1
  151. package/report/renderer/report-utils.d.ts +0 -1
  152. package/report/renderer/report-utils.js +13 -13
  153. package/shared/localization/locales/ar-XB.json +62 -329
  154. package/shared/localization/locales/ar.json +62 -329
  155. package/shared/localization/locales/bg.json +62 -329
  156. package/shared/localization/locales/ca.json +62 -329
  157. package/shared/localization/locales/cs.json +62 -329
  158. package/shared/localization/locales/da.json +62 -329
  159. package/shared/localization/locales/de.json +62 -329
  160. package/shared/localization/locales/el.json +62 -329
  161. package/shared/localization/locales/en-GB.json +62 -329
  162. package/shared/localization/locales/en-US.json +41 -272
  163. package/shared/localization/locales/en-XA.json +62 -329
  164. package/shared/localization/locales/en-XL.json +41 -272
  165. package/shared/localization/locales/es-419.json +62 -329
  166. package/shared/localization/locales/es.json +62 -329
  167. package/shared/localization/locales/fi.json +62 -329
  168. package/shared/localization/locales/fil.json +61 -328
  169. package/shared/localization/locales/fr.json +62 -329
  170. package/shared/localization/locales/he.json +62 -329
  171. package/shared/localization/locales/hi.json +62 -329
  172. package/shared/localization/locales/hr.json +63 -330
  173. package/shared/localization/locales/hu.json +62 -329
  174. package/shared/localization/locales/id.json +62 -329
  175. package/shared/localization/locales/it.json +62 -329
  176. package/shared/localization/locales/ja.json +62 -329
  177. package/shared/localization/locales/ko.json +62 -329
  178. package/shared/localization/locales/lt.json +62 -329
  179. package/shared/localization/locales/lv.json +62 -329
  180. package/shared/localization/locales/nl.json +62 -329
  181. package/shared/localization/locales/no.json +62 -329
  182. package/shared/localization/locales/pl.json +61 -328
  183. package/shared/localization/locales/pt-PT.json +62 -329
  184. package/shared/localization/locales/pt.json +64 -331
  185. package/shared/localization/locales/ro.json +62 -329
  186. package/shared/localization/locales/ru.json +62 -329
  187. package/shared/localization/locales/sk.json +62 -329
  188. package/shared/localization/locales/sl.json +62 -329
  189. package/shared/localization/locales/sr-Latn.json +62 -329
  190. package/shared/localization/locales/sr.json +62 -329
  191. package/shared/localization/locales/sv.json +62 -329
  192. package/shared/localization/locales/ta.json +61 -328
  193. package/shared/localization/locales/te.json +62 -329
  194. package/shared/localization/locales/th.json +62 -329
  195. package/shared/localization/locales/tr.json +62 -329
  196. package/shared/localization/locales/uk.json +62 -329
  197. package/shared/localization/locales/vi.json +61 -328
  198. package/shared/localization/locales/zh-HK.json +62 -329
  199. package/shared/localization/locales/zh-TW.json +61 -328
  200. package/shared/localization/locales/zh.json +62 -329
  201. package/tsconfig.json +1 -3
  202. package/types/artifacts.d.ts +21 -110
  203. package/types/audit.d.ts +0 -10
  204. package/types/config.d.ts +0 -1
  205. package/types/externs.d.ts +0 -2
  206. package/types/gatherer.d.ts +3 -0
  207. package/types/internal/smokehouse.d.ts +1 -1
  208. package/types/lh.d.ts +0 -2
  209. package/types/lhr/audit-details.d.ts +9 -4
  210. package/types/lhr/lhr.d.ts +0 -4
  211. package/types/lhr/settings.d.ts +0 -4
  212. package/core/audits/accessibility/duplicate-id-active.js +0 -42
  213. package/core/audits/content-width.d.ts +0 -17
  214. package/core/audits/content-width.js +0 -76
  215. package/core/audits/installable-manifest.d.ts +0 -111
  216. package/core/audits/installable-manifest.js +0 -248
  217. package/core/audits/layout-shift-elements.d.ts +0 -16
  218. package/core/audits/layout-shift-elements.js +0 -101
  219. package/core/audits/manual/pwa-cross-browser.d.ts +0 -12
  220. package/core/audits/manual/pwa-cross-browser.js +0 -40
  221. package/core/audits/manual/pwa-each-page-has-url.d.ts +0 -12
  222. package/core/audits/manual/pwa-each-page-has-url.js +0 -38
  223. package/core/audits/manual/pwa-page-transitions.d.ts +0 -12
  224. package/core/audits/manual/pwa-page-transitions.js +0 -38
  225. package/core/audits/maskable-icon.d.ts +0 -25
  226. package/core/audits/maskable-icon.js +0 -70
  227. package/core/audits/multi-check-audit.d.ts +0 -28
  228. package/core/audits/multi-check-audit.js +0 -82
  229. package/core/audits/no-unload-listeners.d.ts +0 -16
  230. package/core/audits/no-unload-listeners.js +0 -86
  231. package/core/audits/performance-budget.d.ts +0 -39
  232. package/core/audits/performance-budget.js +0 -154
  233. package/core/audits/script-elements-test-audit.d.ts +0 -14
  234. package/core/audits/script-elements-test-audit.js +0 -29
  235. package/core/audits/seo/plugins.d.ts +0 -15
  236. package/core/audits/seo/plugins.js +0 -150
  237. package/core/audits/seo/tap-targets.d.ts +0 -50
  238. package/core/audits/seo/tap-targets.js +0 -352
  239. package/core/audits/splash-screen.d.ts +0 -37
  240. package/core/audits/splash-screen.js +0 -98
  241. package/core/audits/themed-omnibox.d.ts +0 -42
  242. package/core/audits/themed-omnibox.js +0 -102
  243. package/core/audits/timing-budget.d.ts +0 -43
  244. package/core/audits/timing-budget.js +0 -175
  245. package/core/computed/manifest-values.d.ts +0 -24
  246. package/core/computed/manifest-values.js +0 -136
  247. package/core/config/budget.d.ts +0 -74
  248. package/core/config/budget.js +0 -340
  249. package/core/config/metrics-to-audits.d.ts +0 -19
  250. package/core/config/metrics-to-audits.js +0 -62
  251. package/core/gather/driver/service-workers.d.ts +0 -16
  252. package/core/gather/driver/service-workers.js +0 -52
  253. package/core/gather/gatherers/dobetterweb/tags-blocking-first-paint.d.ts +0 -47
  254. package/core/gather/gatherers/dobetterweb/tags-blocking-first-paint.js +0 -233
  255. package/core/gather/gatherers/global-listeners.d.ts +0 -27
  256. package/core/gather/gatherers/global-listeners.js +0 -108
  257. package/core/gather/gatherers/installability-errors.d.ts +0 -17
  258. package/core/gather/gatherers/installability-errors.js +0 -56
  259. package/core/gather/gatherers/script-elements.d.ts +0 -21
  260. package/core/gather/gatherers/script-elements.js +0 -100
  261. package/core/gather/gatherers/seo/embedded-content.d.ts +0 -10
  262. package/core/gather/gatherers/seo/embedded-content.js +0 -63
  263. package/core/gather/gatherers/seo/tap-targets.d.ts +0 -21
  264. package/core/gather/gatherers/seo/tap-targets.js +0 -389
  265. package/core/gather/gatherers/service-worker.d.ts +0 -10
  266. package/core/gather/gatherers/service-worker.js +0 -32
  267. package/core/gather/gatherers/web-app-manifest.d.ts +0 -32
  268. package/core/gather/gatherers/web-app-manifest.js +0 -106
  269. package/report/renderer/pwa-category-renderer.d.ts +0 -55
  270. package/report/renderer/pwa-category-renderer.js +0 -184
  271. package/third-party/chromium-synchronization/installability-errors-test.js +0 -79
  272. package/types/lhr/budget.d.ts +0 -60
@@ -1,26 +1,5 @@
1
1
  export default CSSUsage;
2
2
  declare class CSSUsage extends BaseGatherer {
3
- /** @type {LH.Gatherer.ProtocolSession|undefined} */
4
- _session: LH.Gatherer.ProtocolSession | undefined;
5
- /** @type {Map<string, Promise<LH.Artifacts.CSSStyleSheetInfo|Error>>} */
6
- _sheetPromises: Map<string, Promise<LH.Artifacts.CSSStyleSheetInfo | Error>>;
7
- /**
8
- * Initialize as undefined so we can assert results are fetched.
9
- * @type {LH.Crdp.CSS.RuleUsage[]|undefined}
10
- */
11
- _ruleUsage: LH.Crdp.CSS.RuleUsage[] | undefined;
12
- /**
13
- * @param {LH.Crdp.CSS.StyleSheetAddedEvent} event
14
- */
15
- _onStylesheetAdded(event: LH.Crdp.CSS.StyleSheetAddedEvent): void;
16
- /**
17
- * @param {LH.Gatherer.Context} context
18
- */
19
- startInstrumentation(context: LH.Gatherer.Context): Promise<void>;
20
- /**
21
- * @param {LH.Gatherer.Context} context
22
- */
23
- stopInstrumentation(context: LH.Gatherer.Context): Promise<void>;
24
3
  /**
25
4
  * @param {LH.Gatherer.Context} context
26
5
  * @return {Promise<LH.Artifacts['CSSUsage']>}
@@ -8,145 +8,35 @@
8
8
  * @fileoverview Tracks unused CSS rules.
9
9
  */
10
10
 
11
- import log from 'lighthouse-logger';
12
-
13
11
  import BaseGatherer from '../base-gatherer.js';
14
- import {Sentry} from '../../lib/sentry.js';
15
12
 
16
13
  class CSSUsage extends BaseGatherer {
17
- constructor() {
18
- super();
19
- /** @type {LH.Gatherer.ProtocolSession|undefined} */
20
- this._session = undefined;
21
- /** @type {Map<string, Promise<LH.Artifacts.CSSStyleSheetInfo|Error>>} */
22
- this._sheetPromises = new Map();
23
- /**
24
- * Initialize as undefined so we can assert results are fetched.
25
- * @type {LH.Crdp.CSS.RuleUsage[]|undefined}
26
- */
27
- this._ruleUsage = undefined;
28
- this._onStylesheetAdded = this._onStylesheetAdded.bind(this);
29
- }
30
-
31
14
  /** @type {LH.Gatherer.GathererMeta} */
32
15
  meta = {
33
- supportedModes: ['snapshot', 'timespan', 'navigation'],
16
+ supportedModes: ['snapshot', 'navigation'],
34
17
  };
35
18
 
36
- /**
37
- * @param {LH.Crdp.CSS.StyleSheetAddedEvent} event
38
- */
39
- _onStylesheetAdded(event) {
40
- if (!this._session) throw new Error('Session not initialized');
41
- const styleSheetId = event.header.styleSheetId;
42
- const sheetPromise = this._session.sendCommand('CSS.getStyleSheetText', {styleSheetId})
43
- .then(content => ({
44
- header: event.header,
45
- content: content.text,
46
- }))
47
- .catch(/** @param {Error} err */ (err) => {
48
- log.warn(
49
- 'CSSUsage',
50
- `Error fetching content of stylesheet with URL "${event.header.sourceURL}"`
51
- );
52
- Sentry.captureException(err, {
53
- tags: {
54
- gatherer: 'CSSUsage',
55
- },
56
- extra: {
57
- url: event.header.sourceURL,
58
- },
59
- level: 'error',
60
- });
61
- return err;
62
- });
63
- this._sheetPromises.set(styleSheetId, sheetPromise);
64
- }
65
-
66
19
  /**
67
20
  * @param {LH.Gatherer.Context} context
21
+ * @return {Promise<LH.Artifacts['CSSUsage']>}
68
22
  */
69
- async startInstrumentation(context) {
23
+ async getArtifact(context) {
70
24
  const session = context.driver.defaultSession;
71
- this._session = session;
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
- }
25
+ const executionContext = context.driver.executionContext;
81
26
 
82
27
  await session.sendCommand('DOM.enable');
83
28
  await session.sendCommand('CSS.enable');
84
29
  await session.sendCommand('CSS.startRuleUsageTracking');
85
30
 
86
- if (isNavigation) {
87
- session.on('CSS.styleSheetAdded', this._onStylesheetAdded);
88
- }
89
- }
90
-
91
-
92
- /**
93
- * @param {LH.Gatherer.Context} context
94
- */
95
- async stopInstrumentation(context) {
96
- const session = context.driver.defaultSession;
97
- const coverageResponse = await session.sendCommand('CSS.stopRuleUsageTracking');
98
- this._ruleUsage = coverageResponse.ruleUsage;
99
- session.off('CSS.styleSheetAdded', this._onStylesheetAdded);
100
-
101
- // Ensure we finish fetching all stylesheet contents before disabling the CSS domain
102
- await Promise.all(this._sheetPromises.values());
31
+ // Force style to recompute.
32
+ // Doesn't appear to be necessary in newer versions of Chrome.
33
+ await executionContext.evaluateAsync('getComputedStyle(document.body)');
103
34
 
35
+ const {ruleUsage} = await session.sendCommand('CSS.stopRuleUsageTracking');
104
36
  await session.sendCommand('CSS.disable');
105
37
  await session.sendCommand('DOM.disable');
106
- }
107
-
108
- /**
109
- * @param {LH.Gatherer.Context} context
110
- * @return {Promise<LH.Artifacts['CSSUsage']>}
111
- */
112
- async getArtifact(context) {
113
- const executionContext = context.driver.executionContext;
114
-
115
- if (context.gatherMode === 'snapshot') {
116
- await this.startInstrumentation(context);
117
-
118
- // Force style to recompute.
119
- // Doesn't appear to be necessary in newer versions of Chrome.
120
- await executionContext.evaluateAsync('getComputedStyle(document.body)');
121
-
122
- await this.stopInstrumentation(context);
123
- }
124
-
125
- /** @type {Map<string, LH.Artifacts.CSSStyleSheetInfo>} */
126
- const dedupedStylesheets = new Map();
127
- const sheets = await Promise.all(this._sheetPromises.values());
128
-
129
- for (const sheet of sheets) {
130
- // Erroneous sheets will be reported via sentry and the log.
131
- // We can ignore them here without throwing a fatal error.
132
- if (sheet instanceof Error) {
133
- continue;
134
- }
135
-
136
- // Exclude empty stylesheets.
137
- if (sheet.header.length === 0) {
138
- continue;
139
- }
140
-
141
- dedupedStylesheets.set(sheet.content, sheet);
142
- }
143
-
144
- if (!this._ruleUsage) throw new Error('Issue collecting rule usages');
145
38
 
146
- return {
147
- rules: this._ruleUsage,
148
- stylesheets: Array.from(dedupedStylesheets.values()),
149
- };
39
+ return ruleUsage;
150
40
  }
151
41
  }
152
42
 
@@ -3,10 +3,10 @@ declare class RootCauses extends BaseGatherer {
3
3
  static symbol: symbol;
4
4
  /**
5
5
  * @param {LH.Gatherer.Driver} driver
6
- * @param {LH.Artifacts.TraceEngineResult} traceEngineResult
6
+ * @param {LH.Artifacts.TraceEngineResult['data']} traceParsedData
7
7
  * @return {Promise<LH.Artifacts.TraceEngineRootCauses>}
8
8
  */
9
- static runRootCauseAnalysis(driver: LH.Gatherer.Driver, traceEngineResult: LH.Artifacts.TraceEngineResult): Promise<LH.Artifacts.TraceEngineRootCauses>;
9
+ static runRootCauseAnalysis(driver: LH.Gatherer.Driver, traceParsedData: LH.Artifacts.TraceEngineResult['data']): Promise<LH.Artifacts.TraceEngineRootCauses>;
10
10
  /** @type {LH.Gatherer.GathererMeta<'Trace'>} */
11
11
  meta: LH.Gatherer.GathererMeta<'Trace'>;
12
12
  /**
@@ -21,10 +21,10 @@ class RootCauses extends BaseGatherer {
21
21
 
22
22
  /**
23
23
  * @param {LH.Gatherer.Driver} driver
24
- * @param {LH.Artifacts.TraceEngineResult} traceEngineResult
24
+ * @param {LH.Artifacts.TraceEngineResult['data']} traceParsedData
25
25
  * @return {Promise<LH.Artifacts.TraceEngineRootCauses>}
26
26
  */
27
- static async runRootCauseAnalysis(driver, traceEngineResult) {
27
+ static async runRootCauseAnalysis(driver, traceParsedData) {
28
28
  await driver.defaultSession.sendCommand('DOM.enable');
29
29
  await driver.defaultSession.sendCommand('CSS.enable');
30
30
 
@@ -112,9 +112,10 @@ class RootCauses extends BaseGatherer {
112
112
  layoutShifts: {},
113
113
  };
114
114
  const rootCausesEngine = new TraceEngine.RootCauses(protocolInterface);
115
- const layoutShiftEvents = traceEngineResult.LayoutShifts.clusters.flatMap(c => c.events);
115
+ const layoutShiftEvents = traceParsedData.LayoutShifts.clusters.flatMap(c => c.events);
116
116
  for (const event of layoutShiftEvents) {
117
- const r = await rootCausesEngine.layoutShifts.rootCausesForEvent(traceEngineResult, event);
117
+ // @ts-expect-error The data we do get in the trace processor is still enough here
118
+ const r = await rootCausesEngine.layoutShifts.rootCausesForEvent(traceParsedData, event);
118
119
  if (!r) continue;
119
120
 
120
121
  for (const cause of r.fontChanges) {
@@ -137,7 +138,7 @@ class RootCauses extends BaseGatherer {
137
138
  async getArtifact(context) {
138
139
  const trace = context.dependencies.Trace;
139
140
  const traceEngineResult = await TraceEngineResult.request({trace}, context);
140
- return RootCauses.runRootCauseAnalysis(context.driver, traceEngineResult);
141
+ return RootCauses.runRootCauseAnalysis(context.driver, traceEngineResult.data);
141
142
  }
142
143
  }
143
144
 
@@ -0,0 +1,26 @@
1
+ export default Stylesheets;
2
+ declare class Stylesheets extends BaseGatherer {
3
+ /** @type {LH.Gatherer.ProtocolSession|undefined} */
4
+ _session: LH.Gatherer.ProtocolSession | undefined;
5
+ /** @type {Map<string, Promise<LH.Artifacts.CSSStyleSheetInfo|null>>} */
6
+ _sheetPromises: Map<string, Promise<LH.Artifacts.CSSStyleSheetInfo | null>>;
7
+ /**
8
+ * @param {LH.Crdp.CSS.StyleSheetAddedEvent} event
9
+ */
10
+ _onStylesheetAdded(event: LH.Crdp.CSS.StyleSheetAddedEvent): void;
11
+ /**
12
+ * @param {LH.Gatherer.Context} context
13
+ */
14
+ startInstrumentation(context: LH.Gatherer.Context): Promise<void>;
15
+ /**
16
+ * @param {LH.Gatherer.Context} context
17
+ */
18
+ stopInstrumentation(context: LH.Gatherer.Context): Promise<void>;
19
+ /**
20
+ * @param {LH.Gatherer.Context} context
21
+ * @return {Promise<LH.Artifacts['Stylesheets']>}
22
+ */
23
+ getArtifact(context: LH.Gatherer.Context): Promise<LH.Artifacts['Stylesheets']>;
24
+ }
25
+ import BaseGatherer from '../base-gatherer.js';
26
+ //# sourceMappingURL=stylesheets.d.ts.map
@@ -0,0 +1,138 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2024 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ /**
8
+ * @fileoverview Collects all stylesheets and their contents.
9
+ */
10
+
11
+ import log from 'lighthouse-logger';
12
+
13
+ import BaseGatherer from '../base-gatherer.js';
14
+ import {Sentry} from '../../lib/sentry.js';
15
+
16
+ class Stylesheets extends BaseGatherer {
17
+ constructor() {
18
+ super();
19
+ /** @type {LH.Gatherer.ProtocolSession|undefined} */
20
+ this._session = undefined;
21
+ /** @type {Map<string, Promise<LH.Artifacts.CSSStyleSheetInfo|null>>} */
22
+ this._sheetPromises = new Map();
23
+ this._onStylesheetAdded = this._onStylesheetAdded.bind(this);
24
+ }
25
+
26
+ /** @type {LH.Gatherer.GathererMeta} */
27
+ meta = {
28
+ supportedModes: ['snapshot', 'timespan', 'navigation'],
29
+ };
30
+
31
+ /**
32
+ * @param {LH.Crdp.CSS.StyleSheetAddedEvent} event
33
+ */
34
+ _onStylesheetAdded(event) {
35
+ if (!this._session) throw new Error('Session not initialized');
36
+ const styleSheetId = event.header.styleSheetId;
37
+ const sheetPromise = this._session.sendCommand('CSS.getStyleSheetText', {styleSheetId})
38
+ .then(content => ({
39
+ header: event.header,
40
+ content: content.text,
41
+ }))
42
+ .catch(err => {
43
+ log.warn(
44
+ 'Stylesheets',
45
+ `Error fetching content of stylesheet with URL "${event.header.sourceURL}"`
46
+ );
47
+ Sentry.captureException(err, {
48
+ tags: {
49
+ gatherer: 'Stylesheets',
50
+ },
51
+ extra: {
52
+ url: event.header.sourceURL,
53
+ },
54
+ level: 'error',
55
+ });
56
+ return null;
57
+ });
58
+ this._sheetPromises.set(styleSheetId, sheetPromise);
59
+ }
60
+
61
+ /**
62
+ * @param {LH.Gatherer.Context} context
63
+ */
64
+ async startInstrumentation(context) {
65
+ const session = context.driver.defaultSession;
66
+ this._session = session;
67
+
68
+ // Calling `CSS.enable` will emit events for stylesheets currently on the page.
69
+ // We want to ignore these events in navigation mode because they are not relevant to the
70
+ // navigation that is about to happen. Adding the event listener *after* calling `CSS.enable`
71
+ // ensures that the events for pre-existing stylesheets are ignored.
72
+ const isNavigation = context.gatherMode === 'navigation';
73
+ if (!isNavigation) {
74
+ session.on('CSS.styleSheetAdded', this._onStylesheetAdded);
75
+ }
76
+
77
+ await session.sendCommand('DOM.enable');
78
+ await session.sendCommand('CSS.enable');
79
+
80
+ if (isNavigation) {
81
+ session.on('CSS.styleSheetAdded', this._onStylesheetAdded);
82
+ }
83
+ }
84
+
85
+
86
+ /**
87
+ * @param {LH.Gatherer.Context} context
88
+ */
89
+ async stopInstrumentation(context) {
90
+ const session = context.driver.defaultSession;
91
+ session.off('CSS.styleSheetAdded', this._onStylesheetAdded);
92
+
93
+ // Ensure we finish fetching all stylesheet contents before disabling the CSS domain
94
+ await Promise.all(this._sheetPromises.values());
95
+
96
+ await session.sendCommand('CSS.disable');
97
+ await session.sendCommand('DOM.disable');
98
+ }
99
+
100
+ /**
101
+ * @param {LH.Gatherer.Context} context
102
+ * @return {Promise<LH.Artifacts['Stylesheets']>}
103
+ */
104
+ async getArtifact(context) {
105
+ const executionContext = context.driver.executionContext;
106
+
107
+ if (context.gatherMode === 'snapshot') {
108
+ await this.startInstrumentation(context);
109
+
110
+ // Force style to recompute.
111
+ // Doesn't appear to be necessary in newer versions of Chrome.
112
+ await executionContext.evaluateAsync('getComputedStyle(document.body)');
113
+
114
+ await this.stopInstrumentation(context);
115
+ }
116
+
117
+ /** @type {Map<string, LH.Artifacts.CSSStyleSheetInfo>} */
118
+ const dedupedStylesheets = new Map();
119
+ const sheets = await Promise.all(this._sheetPromises.values());
120
+
121
+ for (const sheet of sheets) {
122
+ // Erroneous sheets will be reported via sentry and the log.
123
+ // We can ignore them here without throwing a fatal error.
124
+ if (!sheet) continue;
125
+
126
+ // Exclude empty stylesheets.
127
+ if (sheet.header.length === 0) {
128
+ continue;
129
+ }
130
+
131
+ dedupedStylesheets.set(sheet.content, sheet);
132
+ }
133
+
134
+ return Array.from(dedupedStylesheets.values());
135
+ }
136
+ }
137
+
138
+ export default Stylesheets;
@@ -9,16 +9,6 @@ export type TraceElementData = {
9
9
  type?: string;
10
10
  };
11
11
  declare class TraceElements extends BaseGatherer {
12
- /**
13
- * This function finds the top (up to 15) elements that shift on the page.
14
- *
15
- * @param {LH.Trace} trace
16
- * @param {LH.Gatherer.Context} context
17
- * @return {Promise<Array<{nodeId: number}>>}
18
- */
19
- static getTopLayoutShiftElements(trace: LH.Trace, context: LH.Gatherer.Context): Promise<Array<{
20
- nodeId: number;
21
- }>>;
22
12
  /**
23
13
  * We want to a single representative node to represent the shift, so let's pick
24
14
  * the one with the largest impact (size x distance moved).
@@ -35,12 +25,12 @@ declare class TraceElements extends BaseGatherer {
35
25
  * that may have caused the shift.
36
26
  *
37
27
  * @param {LH.Trace} trace
38
- * @param {LH.Artifacts.TraceEngineResult} traceEngineResult
28
+ * @param {LH.Artifacts.TraceEngineResult['data']} traceEngineResult
39
29
  * @param {LH.Artifacts.TraceEngineRootCauses} rootCauses
40
30
  * @param {LH.Gatherer.Context} context
41
31
  * @return {Promise<Array<{nodeId: number}>>}
42
32
  */
43
- static getTopLayoutShifts(trace: LH.Trace, traceEngineResult: LH.Artifacts.TraceEngineResult, rootCauses: LH.Artifacts.TraceEngineRootCauses, context: LH.Gatherer.Context): Promise<Array<{
33
+ static getTopLayoutShifts(trace: LH.Trace, traceEngineResult: LH.Artifacts.TraceEngineResult['data'], rootCauses: LH.Artifacts.TraceEngineRootCauses, context: LH.Gatherer.Context): Promise<Array<{
44
34
  nodeId: number;
45
35
  }>>;
46
36
  /**
@@ -28,7 +28,6 @@ import {TraceEngineResult} from '../../computed/trace-engine-result.js';
28
28
 
29
29
  /** @typedef {{nodeId: number, animations?: {name?: string, failureReasonsMask?: number, unsupportedProperties?: string[]}[], type?: string}} TraceElementData */
30
30
 
31
- const MAX_LAYOUT_SHIFT_ELEMENTS = 15;
32
31
  const MAX_LAYOUT_SHIFTS = 15;
33
32
 
34
33
  /**
@@ -66,22 +65,6 @@ class TraceElements extends BaseGatherer {
66
65
  if (name) this.animationIdToName.set(id, name);
67
66
  }
68
67
 
69
- /**
70
- * This function finds the top (up to 15) elements that shift on the page.
71
- *
72
- * @param {LH.Trace} trace
73
- * @param {LH.Gatherer.Context} context
74
- * @return {Promise<Array<{nodeId: number}>>}
75
- */
76
- static async getTopLayoutShiftElements(trace, context) {
77
- const {impactByNodeId} = await CumulativeLayoutShift.request(trace, context);
78
-
79
- return [...impactByNodeId.entries()]
80
- .sort((a, b) => b[1] - a[1])
81
- .slice(0, MAX_LAYOUT_SHIFT_ELEMENTS)
82
- .map(([nodeId]) => ({nodeId}));
83
- }
84
-
85
68
  /**
86
69
  * We want to a single representative node to represent the shift, so let's pick
87
70
  * the one with the largest impact (size x distance moved).
@@ -139,7 +122,7 @@ class TraceElements extends BaseGatherer {
139
122
  * that may have caused the shift.
140
123
  *
141
124
  * @param {LH.Trace} trace
142
- * @param {LH.Artifacts.TraceEngineResult} traceEngineResult
125
+ * @param {LH.Artifacts.TraceEngineResult['data']} traceEngineResult
143
126
  * @param {LH.Artifacts.TraceEngineRootCauses} rootCauses
144
127
  * @param {LH.Gatherer.Context} context
145
128
  * @return {Promise<Array<{nodeId: number}>>}
@@ -338,16 +321,14 @@ class TraceElements extends BaseGatherer {
338
321
  const {mainThreadEvents} = processedTrace;
339
322
 
340
323
  const lcpNodeData = await TraceElements.getLcpElement(trace, context);
341
- const shiftElementsNodeData = await TraceElements.getTopLayoutShiftElements(trace, context);
342
324
  const shiftsData = await TraceElements.getTopLayoutShifts(
343
- trace, traceEngineResult, rootCauses, context);
325
+ trace, traceEngineResult.data, rootCauses, context);
344
326
  const animatedElementData = await this.getAnimatedElements(mainThreadEvents);
345
327
  const responsivenessElementData = await TraceElements.getResponsivenessElement(trace, context);
346
328
 
347
329
  /** @type {Map<string, TraceElementData[]>} */
348
330
  const backendNodeDataMap = new Map([
349
331
  ['largest-contentful-paint', lcpNodeData ? [lcpNodeData] : []],
350
- ['layout-shift-element', shiftElementsNodeData],
351
332
  ['layout-shift', shiftsData],
352
333
  ['animation', animatedElementData],
353
334
  ['responsiveness', responsivenessElementData ? [responsivenessElementData] : []],
@@ -92,7 +92,9 @@ async function _navigate(navigationContext) {
92
92
  return {requestedUrl, mainDocumentUrl, navigationError: undefined};
93
93
  } catch (err) {
94
94
  if (!(err instanceof LighthouseError)) throw err;
95
- if (err.code !== 'NO_FCP' && err.code !== 'PAGE_HUNG') throw err;
95
+ if (err.code !== 'NO_FCP' && err.code !== 'PAGE_HUNG' && err.code !== 'TARGET_CRASHED') {
96
+ throw err;
97
+ }
96
98
  if (typeof requestor !== 'string') throw err;
97
99
 
98
100
  // TODO: Make the urls optional here so we don't need to throw an error with a callback requestor.
@@ -40,6 +40,14 @@ export class ProtocolSession extends ProtocolSession_base implements LH.Gatherer
40
40
  * @return {Promise<LH.CrdpCommands[C]['returnType']>}
41
41
  */
42
42
  sendCommand<C extends keyof import("puppeteer-core").ProtocolMapping.Commands>(method: C, ...params: import("puppeteer-core").ProtocolMapping.Commands[C]["paramsType"]): Promise<import("puppeteer-core").ProtocolMapping.Commands[C]["returnType"]>;
43
+ /**
44
+ * Send and if there's an error response, do not reject.
45
+ * @template {keyof LH.CrdpCommands} C
46
+ * @param {C} method
47
+ * @param {LH.CrdpCommands[C]['paramsType']} params
48
+ * @return {Promise<void>}
49
+ */
50
+ sendCommandAndIgnore<C_1 extends keyof import("puppeteer-core").ProtocolMapping.Commands>(method: C_1, ...params: import("puppeteer-core").ProtocolMapping.Commands[C_1]["paramsType"]): Promise<void>;
43
51
  /**
44
52
  * Disposes of a session so that it can no longer talk to Chrome.
45
53
  * @return {Promise<void>}
@@ -100,6 +100,7 @@ class ProtocolSession extends CrdpEventEmitter {
100
100
  /** @type {NodeJS.Timer|undefined} */
101
101
  let timeout;
102
102
  const timeoutPromise = new Promise((resolve, reject) => {
103
+ // Unexpected setTimeout invocation to preserve the error stack. https://github.com/GoogleChrome/lighthouse/issues/13332
103
104
  // eslint-disable-next-line max-len
104
105
  timeout = setTimeout(reject, timeoutMs, new LighthouseError(LighthouseError.errors.PROTOCOL_TIMEOUT, {
105
106
  protocolMethod: method,
@@ -120,6 +121,18 @@ class ProtocolSession extends CrdpEventEmitter {
120
121
  });
121
122
  }
122
123
 
124
+ /**
125
+ * Send and if there's an error response, do not reject.
126
+ * @template {keyof LH.CrdpCommands} C
127
+ * @param {C} method
128
+ * @param {LH.CrdpCommands[C]['paramsType']} params
129
+ * @return {Promise<void>}
130
+ */
131
+ sendCommandAndIgnore(method, ...params) {
132
+ return this.sendCommand(method, ...params)
133
+ .catch(e => log.verbose('session', method, e.message)).then(_ => void 0);
134
+ }
135
+
123
136
  /**
124
137
  * Disposes of a session so that it can no longer talk to Chrome.
125
138
  * @return {Promise<void>}
@@ -127,7 +140,7 @@ class ProtocolSession extends CrdpEventEmitter {
127
140
  async dispose() {
128
141
  // @ts-expect-error Puppeteer expects the handler params to be type `unknown`
129
142
  this._cdpSession.off('*', this._handleProtocolEvent);
130
- await this._cdpSession.detach();
143
+ await this._cdpSession.detach().catch(e => log.verbose('session', 'detach failed', e.message));
131
144
  }
132
145
  }
133
146
 
@@ -128,7 +128,7 @@ function enableNetworkThrottling(session, throttlingSettings) {
128
128
  * @return {Promise<void>}
129
129
  */
130
130
  function clearNetworkThrottling(session) {
131
- return session.sendCommand('Network.emulateNetworkConditions', NO_THROTTLING_METRICS);
131
+ return session.sendCommandAndIgnore('Network.emulateNetworkConditions', NO_THROTTLING_METRICS);
132
132
  }
133
133
 
134
134
  /**
@@ -146,7 +146,7 @@ function enableCPUThrottling(session, throttlingSettings) {
146
146
  * @return {Promise<void>}
147
147
  */
148
148
  function clearCPUThrottling(session) {
149
- return session.sendCommand('Emulation.setCPUThrottlingRate', NO_CPU_THROTTLE_METRICS);
149
+ return session.sendCommandAndIgnore('Emulation.setCPUThrottlingRate', NO_CPU_THROTTLE_METRICS);
150
150
  }
151
151
 
152
152
  export {
@@ -19,7 +19,6 @@ export namespace UIStrings {
19
19
  const columnRequests: string;
20
20
  const columnName: string;
21
21
  const columnSource: string;
22
- const columnOverBudget: string;
23
22
  const columnElement: string;
24
23
  const columnStartTime: string;
25
24
  const columnDuration: string;
@@ -57,8 +57,6 @@ const UIStrings = {
57
57
  columnName: 'Name',
58
58
  /** Label for a column in a data table; entries will be the locations of JavaScript or CSS code, e.g. the name of a Javascript package or module. */
59
59
  columnSource: 'Source',
60
- /** Label for a column in a data table; entries will be how much a predetermined budget has been exeeded by. Depending on the context, this number could represent an excess in quantity or size of network requests, or, an excess in the duration of time that it takes for the page to load.*/
61
- columnOverBudget: 'Over Budget',
62
60
  /** Label for a column in a data table; entries will be a representation of a DOM element. */
63
61
  columnElement: 'Element',
64
62
  /** Label for a column in a data table; entries will be the number of milliseconds since the page started loading. */
@@ -6,11 +6,17 @@ export class CPUNode<T = any> extends BaseNode<T> {
6
6
  /**
7
7
  * @param {LH.TraceEvent} parentEvent
8
8
  * @param {LH.TraceEvent[]=} childEvents
9
+ * @param {number=} correctedEndTs
9
10
  */
10
- constructor(parentEvent: LH.TraceEvent, childEvents?: LH.TraceEvent[] | undefined);
11
+ constructor(parentEvent: LH.TraceEvent, childEvents?: LH.TraceEvent[] | undefined, correctedEndTs?: number | undefined);
11
12
  _event: LH.TraceEvent;
12
13
  _childEvents: LH.TraceEvent[];
14
+ _correctedEndTs: number | undefined;
13
15
  get type(): "cpu";
16
+ /**
17
+ * @return {number}
18
+ */
19
+ get duration(): number;
14
20
  /**
15
21
  * @return {LH.TraceEvent}
16
22
  */
@@ -15,13 +15,15 @@ class CPUNode extends BaseNode {
15
15
  /**
16
16
  * @param {LH.TraceEvent} parentEvent
17
17
  * @param {LH.TraceEvent[]=} childEvents
18
+ * @param {number=} correctedEndTs
18
19
  */
19
- constructor(parentEvent, childEvents = []) {
20
+ constructor(parentEvent, childEvents = [], correctedEndTs) {
20
21
  const nodeId = `${parentEvent.tid}.${parentEvent.ts}`;
21
22
  super(nodeId);
22
23
 
23
24
  this._event = parentEvent;
24
25
  this._childEvents = childEvents;
26
+ this._correctedEndTs = correctedEndTs;
25
27
  }
26
28
 
27
29
  get type() {
@@ -39,9 +41,17 @@ class CPUNode extends BaseNode {
39
41
  * @return {number}
40
42
  */
41
43
  get endTime() {
44
+ if (this._correctedEndTs) return this._correctedEndTs;
42
45
  return this._event.ts + this._event.dur;
43
46
  }
44
47
 
48
+ /**
49
+ * @return {number}
50
+ */
51
+ get duration() {
52
+ return this.endTime - this.startTime;
53
+ }
54
+
45
55
  /**
46
56
  * @return {LH.TraceEvent}
47
57
  */
@@ -83,7 +93,7 @@ class CPUNode extends BaseNode {
83
93
  * @return {CPUNode}
84
94
  */
85
95
  cloneWithoutRelationships() {
86
- return new CPUNode(this._event, this._childEvents);
96
+ return new CPUNode(this._event, this._childEvents, this._correctedEndTs);
87
97
  }
88
98
  }
89
99
 
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2024 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ export class LanternError extends Error {
7
+ }
8
+ //# sourceMappingURL=lantern-error.d.ts.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2024 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ class LanternError extends Error {}
8
+
9
+ export {LanternError};