lighthouse 12.8.2 → 13.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 (270) hide show
  1. package/cli/cli-flags.js +1 -1
  2. package/cli/test/smokehouse/config/exclusions.js +0 -2
  3. package/cli/test/smokehouse/version-check.d.ts +1 -1
  4. package/core/audits/accessibility/accesskeys.js +3 -3
  5. package/core/audits/accessibility/aria-allowed-attr.js +3 -3
  6. package/core/audits/accessibility/aria-allowed-role.js +2 -1
  7. package/core/audits/accessibility/aria-command-name.js +1 -1
  8. package/core/audits/accessibility/aria-conditional-attr.js +1 -1
  9. package/core/audits/accessibility/aria-deprecated-role.js +1 -1
  10. package/core/audits/accessibility/aria-dialog-name.js +1 -1
  11. package/core/audits/accessibility/aria-hidden-body.js +3 -3
  12. package/core/audits/accessibility/aria-hidden-focus.js +3 -3
  13. package/core/audits/accessibility/aria-input-field-name.js +3 -3
  14. package/core/audits/accessibility/aria-meter-name.js +1 -1
  15. package/core/audits/accessibility/aria-progressbar-name.js +1 -1
  16. package/core/audits/accessibility/aria-prohibited-attr.js +1 -1
  17. package/core/audits/accessibility/aria-required-attr.js +3 -3
  18. package/core/audits/accessibility/aria-required-children.js +3 -3
  19. package/core/audits/accessibility/aria-required-parent.js +3 -3
  20. package/core/audits/accessibility/aria-roles.js +3 -3
  21. package/core/audits/accessibility/aria-text.js +3 -3
  22. package/core/audits/accessibility/aria-toggle-field-name.js +3 -3
  23. package/core/audits/accessibility/aria-tooltip-name.js +1 -1
  24. package/core/audits/accessibility/aria-treeitem-name.js +1 -1
  25. package/core/audits/accessibility/aria-valid-attr-value.js +3 -3
  26. package/core/audits/accessibility/aria-valid-attr.js +3 -3
  27. package/core/audits/accessibility/button-name.js +3 -3
  28. package/core/audits/accessibility/bypass.js +3 -3
  29. package/core/audits/accessibility/color-contrast.js +3 -3
  30. package/core/audits/accessibility/definition-list.js +3 -3
  31. package/core/audits/accessibility/dlitem.js +3 -3
  32. package/core/audits/accessibility/document-title.js +3 -3
  33. package/core/audits/accessibility/duplicate-id-aria.js +3 -3
  34. package/core/audits/accessibility/empty-heading.js +3 -3
  35. package/core/audits/accessibility/form-field-multiple-labels.js +3 -3
  36. package/core/audits/accessibility/frame-title.js +3 -3
  37. package/core/audits/accessibility/heading-order.js +3 -3
  38. package/core/audits/accessibility/html-has-lang.js +3 -3
  39. package/core/audits/accessibility/html-lang-valid.js +3 -3
  40. package/core/audits/accessibility/html-xml-lang-mismatch.js +3 -3
  41. package/core/audits/accessibility/identical-links-same-purpose.js +3 -3
  42. package/core/audits/accessibility/image-alt.js +3 -3
  43. package/core/audits/accessibility/image-redundant-alt.js +4 -3
  44. package/core/audits/accessibility/input-button-name.js +3 -3
  45. package/core/audits/accessibility/input-image-alt.js +3 -3
  46. package/core/audits/accessibility/label-content-name-mismatch.js +3 -3
  47. package/core/audits/accessibility/label.js +3 -3
  48. package/core/audits/accessibility/landmark-one-main.js +3 -4
  49. package/core/audits/accessibility/link-in-text-block.js +3 -3
  50. package/core/audits/accessibility/link-name.js +3 -3
  51. package/core/audits/accessibility/list.js +3 -3
  52. package/core/audits/accessibility/listitem.js +3 -3
  53. package/core/audits/accessibility/meta-refresh.js +3 -3
  54. package/core/audits/accessibility/meta-viewport.js +3 -3
  55. package/core/audits/accessibility/object-alt.js +3 -3
  56. package/core/audits/accessibility/select-name.js +3 -3
  57. package/core/audits/accessibility/skip-link.js +3 -3
  58. package/core/audits/accessibility/tabindex.js +3 -3
  59. package/core/audits/accessibility/table-duplicate-name.js +4 -3
  60. package/core/audits/accessibility/table-fake-caption.js +3 -3
  61. package/core/audits/accessibility/target-size.js +3 -3
  62. package/core/audits/accessibility/td-has-header.js +3 -3
  63. package/core/audits/accessibility/td-headers-attr.js +3 -3
  64. package/core/audits/accessibility/th-has-data-cells.js +3 -3
  65. package/core/audits/accessibility/valid-lang.js +3 -3
  66. package/core/audits/accessibility/video-caption.js +3 -3
  67. package/core/audits/audit.d.ts +0 -4
  68. package/core/audits/audit.js +2 -13
  69. package/core/audits/insights/cls-culprits-insight.js +1 -1
  70. package/core/audits/insights/dom-size-insight.js +11 -7
  71. package/core/audits/insights/font-display-insight.js +3 -1
  72. package/core/audits/insights/image-delivery-insight.js +4 -1
  73. package/core/audits/insights/insight-audit.d.ts +6 -4
  74. package/core/audits/insights/insight-audit.js +27 -8
  75. package/core/audits/insights/third-parties-insight.js +1 -1
  76. package/core/audits/layout-shifts.js +1 -1
  77. package/core/audits/predictive-perf.js +2 -2
  78. package/core/audits/seo/crawlable-anchors.js +2 -3
  79. package/core/audits/seo/manual/structured-data.js +1 -1
  80. package/core/audits/server-response-time.d.ts +0 -5
  81. package/core/audits/server-response-time.js +12 -26
  82. package/core/computed/metrics/cumulative-layout-shift.js +2 -2
  83. package/core/computed/metrics/lantern-metric.js +3 -3
  84. package/core/computed/metrics/lcp-breakdown.d.ts +10 -5
  85. package/core/computed/metrics/lcp-breakdown.js +50 -22
  86. package/core/computed/metrics/time-to-first-byte.js +33 -10
  87. package/core/computed/metrics/timing-summary.js +3 -2
  88. package/core/computed/page-dependency-graph.js +1 -1
  89. package/core/computed/trace-engine-result.js +2 -2
  90. package/core/config/default-config.js +110 -152
  91. package/core/config/experimental-config.js +1 -32
  92. package/core/config/filters.js +6 -9
  93. package/core/config/lr-desktop-config.js +0 -1
  94. package/core/config/lr-mobile-config.js +0 -1
  95. package/core/gather/driver/target-manager.d.ts +1 -1
  96. package/core/gather/driver.d.ts +1 -1
  97. package/core/gather/gatherers/anchor-elements.js +8 -24
  98. package/core/gather/gatherers/image-elements.js +32 -6
  99. package/core/gather/gatherers/inspector-issues.js +1 -28
  100. package/core/gather/gatherers/trace-elements.d.ts +2 -11
  101. package/core/gather/gatherers/trace-elements.js +9 -39
  102. package/core/gather/navigation-runner.js +0 -3
  103. package/core/gather/session.d.ts +1 -1
  104. package/core/lib/asset-saver.d.ts +2 -2
  105. package/core/lib/asset-saver.js +33 -43
  106. package/core/lib/bf-cache-strings.js +10 -9
  107. package/core/lib/deprecations-strings.js +5 -5
  108. package/core/lib/emulation.d.ts +10 -0
  109. package/core/lib/emulation.js +21 -6
  110. package/core/lib/legacy-javascript/legacy-javascript.js +4 -11
  111. package/core/lib/network-request.d.ts +0 -7
  112. package/core/lib/network-request.js +0 -16
  113. package/core/lib/proto-preprocessor.js +10 -25
  114. package/core/runner.js +1 -8
  115. package/core/scoring.js +1 -1
  116. package/dist/report/bundle.esm.js +10 -49
  117. package/dist/report/flow.js +12 -51
  118. package/dist/report/standalone.js +11 -50
  119. package/flow-report/src/i18n/i18n.d.ts +4 -6
  120. package/package.json +16 -19
  121. package/readme.md +2 -2
  122. package/report/assets/styles.css +0 -39
  123. package/report/renderer/api.js +0 -1
  124. package/report/renderer/category-renderer.js +6 -0
  125. package/report/renderer/components.js +1 -1
  126. package/report/renderer/details-renderer.d.ts +1 -2
  127. package/report/renderer/details-renderer.js +0 -1
  128. package/report/renderer/dom.d.ts +0 -13
  129. package/report/renderer/dom.js +0 -38
  130. package/report/renderer/performance-category-renderer.d.ts +0 -26
  131. package/report/renderer/performance-category-renderer.js +10 -142
  132. package/report/renderer/report-ui-features.d.ts +0 -1
  133. package/report/renderer/report-ui-features.js +2 -13
  134. package/report/renderer/report-utils.d.ts +2 -3
  135. package/report/renderer/report-utils.js +4 -6
  136. package/report/types/report-renderer.d.ts +0 -6
  137. package/shared/localization/locales/ar-XB.json +107 -455
  138. package/shared/localization/locales/ar.json +107 -455
  139. package/shared/localization/locales/bg.json +96 -444
  140. package/shared/localization/locales/ca.json +96 -444
  141. package/shared/localization/locales/cs.json +96 -444
  142. package/shared/localization/locales/da.json +96 -444
  143. package/shared/localization/locales/de.json +96 -444
  144. package/shared/localization/locales/el.json +96 -444
  145. package/shared/localization/locales/en-GB.json +96 -444
  146. package/shared/localization/locales/en-US.json +116 -467
  147. package/shared/localization/locales/en-XA.json +93 -441
  148. package/shared/localization/locales/en-XL.json +116 -467
  149. package/shared/localization/locales/es-419.json +96 -444
  150. package/shared/localization/locales/es.json +96 -444
  151. package/shared/localization/locales/fi.json +96 -444
  152. package/shared/localization/locales/fil.json +96 -444
  153. package/shared/localization/locales/fr.json +96 -444
  154. package/shared/localization/locales/he.json +118 -466
  155. package/shared/localization/locales/hi.json +96 -444
  156. package/shared/localization/locales/hr.json +100 -448
  157. package/shared/localization/locales/hu.json +96 -444
  158. package/shared/localization/locales/id.json +96 -444
  159. package/shared/localization/locales/it.json +96 -444
  160. package/shared/localization/locales/ja.json +96 -444
  161. package/shared/localization/locales/ko.json +97 -445
  162. package/shared/localization/locales/lt.json +96 -444
  163. package/shared/localization/locales/lv.json +97 -445
  164. package/shared/localization/locales/nl.json +96 -444
  165. package/shared/localization/locales/no.json +96 -444
  166. package/shared/localization/locales/pl.json +96 -444
  167. package/shared/localization/locales/pt-PT.json +96 -444
  168. package/shared/localization/locales/pt.json +97 -445
  169. package/shared/localization/locales/ro.json +97 -445
  170. package/shared/localization/locales/ru.json +96 -444
  171. package/shared/localization/locales/sk.json +96 -444
  172. package/shared/localization/locales/sl.json +96 -444
  173. package/shared/localization/locales/sr-Latn.json +96 -444
  174. package/shared/localization/locales/sr.json +96 -444
  175. package/shared/localization/locales/sv.json +96 -444
  176. package/shared/localization/locales/ta.json +96 -444
  177. package/shared/localization/locales/te.json +97 -445
  178. package/shared/localization/locales/th.json +96 -444
  179. package/shared/localization/locales/tr.json +96 -444
  180. package/shared/localization/locales/uk.json +96 -444
  181. package/shared/localization/locales/vi.json +96 -444
  182. package/shared/localization/locales/zh-HK.json +96 -444
  183. package/shared/localization/locales/zh-TW.json +97 -445
  184. package/shared/localization/locales/zh.json +96 -444
  185. package/shared/localization/locales.d.ts +2 -0
  186. package/shared/localization/locales.js +130 -139
  187. package/shared/tsconfig.json +2 -0
  188. package/tsconfig-base.json +2 -2
  189. package/tsconfig.json +1 -4
  190. package/types/artifacts.d.ts +6 -81
  191. package/types/audit.d.ts +1 -1
  192. package/types/lhr/settings.d.ts +1 -1
  193. package/core/audits/byte-efficiency/duplicated-javascript.d.ts +0 -45
  194. package/core/audits/byte-efficiency/duplicated-javascript.js +0 -223
  195. package/core/audits/byte-efficiency/efficient-animated-content.d.ts +0 -22
  196. package/core/audits/byte-efficiency/efficient-animated-content.js +0 -93
  197. package/core/audits/byte-efficiency/legacy-javascript.d.ts +0 -28
  198. package/core/audits/byte-efficiency/legacy-javascript.js +0 -144
  199. package/core/audits/byte-efficiency/modern-image-formats.d.ts +0 -38
  200. package/core/audits/byte-efficiency/modern-image-formats.js +0 -187
  201. package/core/audits/byte-efficiency/offscreen-images.d.ts +0 -63
  202. package/core/audits/byte-efficiency/offscreen-images.js +0 -240
  203. package/core/audits/byte-efficiency/render-blocking-resources.d.ts +0 -53
  204. package/core/audits/byte-efficiency/render-blocking-resources.js +0 -312
  205. package/core/audits/byte-efficiency/uses-long-cache-ttl.d.ts +0 -59
  206. package/core/audits/byte-efficiency/uses-long-cache-ttl.js +0 -293
  207. package/core/audits/byte-efficiency/uses-optimized-images.d.ts +0 -33
  208. package/core/audits/byte-efficiency/uses-optimized-images.js +0 -146
  209. package/core/audits/byte-efficiency/uses-responsive-images-snapshot.d.ts +0 -16
  210. package/core/audits/byte-efficiency/uses-responsive-images-snapshot.js +0 -106
  211. package/core/audits/byte-efficiency/uses-responsive-images.d.ts +0 -44
  212. package/core/audits/byte-efficiency/uses-responsive-images.js +0 -202
  213. package/core/audits/byte-efficiency/uses-text-compression.d.ts +0 -14
  214. package/core/audits/byte-efficiency/uses-text-compression.js +0 -108
  215. package/core/audits/critical-request-chains.d.ts +0 -44
  216. package/core/audits/critical-request-chains.js +0 -221
  217. package/core/audits/dobetterweb/dom-size.d.ts +0 -32
  218. package/core/audits/dobetterweb/dom-size.js +0 -182
  219. package/core/audits/dobetterweb/no-document-write.d.ts +0 -16
  220. package/core/audits/dobetterweb/no-document-write.js +0 -86
  221. package/core/audits/dobetterweb/uses-http2.d.ts +0 -72
  222. package/core/audits/dobetterweb/uses-http2.js +0 -276
  223. package/core/audits/dobetterweb/uses-passive-event-listeners.d.ts +0 -16
  224. package/core/audits/dobetterweb/uses-passive-event-listeners.js +0 -69
  225. package/core/audits/font-display.d.ts +0 -32
  226. package/core/audits/font-display.js +0 -195
  227. package/core/audits/largest-contentful-paint-element.d.ts +0 -34
  228. package/core/audits/largest-contentful-paint-element.js +0 -181
  229. package/core/audits/lcp-lazy-loaded.d.ts +0 -22
  230. package/core/audits/lcp-lazy-loaded.js +0 -115
  231. package/core/audits/metrics/first-meaningful-paint.d.ts +0 -12
  232. package/core/audits/metrics/first-meaningful-paint.js +0 -47
  233. package/core/audits/preload-fonts.d.ts +0 -25
  234. package/core/audits/preload-fonts.js +0 -97
  235. package/core/audits/prioritize-lcp-image.d.ts +0 -74
  236. package/core/audits/prioritize-lcp-image.js +0 -297
  237. package/core/audits/seo/font-size.d.ts +0 -24
  238. package/core/audits/seo/font-size.js +0 -344
  239. package/core/audits/third-party-facades.d.ts +0 -41
  240. package/core/audits/third-party-facades.js +0 -234
  241. package/core/audits/third-party-summary.d.ts +0 -78
  242. package/core/audits/third-party-summary.js +0 -236
  243. package/core/audits/uses-rel-preconnect.d.ts +0 -37
  244. package/core/audits/uses-rel-preconnect.js +0 -286
  245. package/core/audits/uses-rel-preload.d.ts +0 -57
  246. package/core/audits/uses-rel-preload.js +0 -263
  247. package/core/audits/viewport.d.ts +0 -17
  248. package/core/audits/viewport.js +0 -87
  249. package/core/audits/work-during-interaction.d.ts +0 -81
  250. package/core/audits/work-during-interaction.js +0 -287
  251. package/core/computed/critical-request-chains.d.ts +0 -42
  252. package/core/computed/critical-request-chains.js +0 -143
  253. package/core/computed/viewport-meta.d.ts +0 -37
  254. package/core/computed/viewport-meta.js +0 -71
  255. package/core/gather/gatherers/cache-contents.d.ts +0 -11
  256. package/core/gather/gatherers/cache-contents.js +0 -56
  257. package/core/gather/gatherers/devtools-log-compat.d.ts +0 -13
  258. package/core/gather/gatherers/devtools-log-compat.js +0 -35
  259. package/core/gather/gatherers/dobetterweb/domstats.d.ts +0 -10
  260. package/core/gather/gatherers/dobetterweb/domstats.js +0 -102
  261. package/core/gather/gatherers/dobetterweb/optimized-images.d.ts +0 -48
  262. package/core/gather/gatherers/dobetterweb/optimized-images.js +0 -169
  263. package/core/gather/gatherers/dobetterweb/response-compression.d.ts +0 -23
  264. package/core/gather/gatherers/dobetterweb/response-compression.js +0 -136
  265. package/core/gather/gatherers/seo/font-size.d.ts +0 -131
  266. package/core/gather/gatherers/seo/font-size.js +0 -347
  267. package/core/gather/gatherers/trace-compat.d.ts +0 -13
  268. package/core/gather/gatherers/trace-compat.js +0 -35
  269. package/types/internal/metaviewport-parser.d.ts +0 -13
  270. package/types/internal/parse-cache-control.d.ts +0 -20
@@ -1,187 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2017 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
- /*
7
- * @fileoverview This audit determines if the images could be smaller when compressed with WebP.
8
- */
9
-
10
-
11
- import {ByteEfficiencyAudit} from './byte-efficiency-audit.js';
12
- import UrlUtils from '../../lib/url-utils.js';
13
- import * as i18n from '../../lib/i18n/i18n.js';
14
-
15
- const UIStrings = {
16
- /** Imperative title of a Lighthouse audit that tells the user to serve images in newer and more efficient image formats in order to enhance the performance of a page. A non-modern image format was designed 20+ years ago. This is displayed in a list of audit titles that Lighthouse generates. */
17
- title: 'Serve images in next-gen formats',
18
- /** Description of a Lighthouse audit that tells the user *why* they should use newer and more efficient image formats. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */
19
- description: 'Image formats like WebP and AVIF often provide better ' +
20
- 'compression than PNG or JPEG, which means faster downloads and less data consumption. ' +
21
- '[Learn more about modern image formats](https://developer.chrome.com/docs/lighthouse/performance/uses-webp-images/).',
22
- };
23
-
24
- const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
25
-
26
- const IGNORE_THRESHOLD_IN_BYTES = 8192;
27
-
28
- class ModernImageFormats extends ByteEfficiencyAudit {
29
- /**
30
- * @return {LH.Audit.Meta}
31
- */
32
- static get meta() {
33
- return {
34
- id: 'modern-image-formats',
35
- title: str_(UIStrings.title),
36
- description: str_(UIStrings.description),
37
- scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
38
- guidanceLevel: 3,
39
- requiredArtifacts: ['OptimizedImages', 'DevtoolsLog', 'Trace', 'URL', 'GatherContext',
40
- 'ImageElements', 'SourceMaps'],
41
- };
42
- }
43
-
44
- /**
45
- * @param {{naturalWidth: number, naturalHeight: number}} imageElement
46
- * @return {number}
47
- */
48
- static estimateWebPSizeFromDimensions(imageElement) {
49
- const totalPixels = imageElement.naturalWidth * imageElement.naturalHeight;
50
- // See uses-optimized-images for the rationale behind our 2 byte-per-pixel baseline and
51
- // JPEG compression ratio of 8:1.
52
- // WebP usually gives ~20% additional savings on top of that, so we will use 10:1.
53
- // This is quite pessimistic as their study shows a photographic compression ratio of ~29:1.
54
- // https://developers.google.com/speed/webp/docs/webp_lossless_alpha_study#results
55
- const expectedBytesPerPixel = 2 * 1 / 10;
56
- return Math.round(totalPixels * expectedBytesPerPixel);
57
- }
58
-
59
- /**
60
- * @param {{naturalWidth: number, naturalHeight: number}} imageElement
61
- * @return {number}
62
- */
63
- static estimateAvifSizeFromDimensions(imageElement) {
64
- const totalPixels = imageElement.naturalWidth * imageElement.naturalHeight;
65
- // See above for the rationale behind our 2 byte-per-pixel baseline and WebP ratio of 10:1.
66
- // AVIF usually gives ~20% additional savings on top of that, so we will use 12:1.
67
- // This is quite pessimistic as Netflix study shows a photographic compression ratio of ~40:1
68
- // (0.4 *bits* per pixel at SSIM 0.97).
69
- // https://netflixtechblog.com/avif-for-next-generation-image-coding-b1d75675fe4
70
- const expectedBytesPerPixel = 2 * 1 / 12;
71
- return Math.round(totalPixels * expectedBytesPerPixel);
72
- }
73
-
74
- /**
75
- * @param {{jpegSize: number | undefined, webpSize: number | undefined}} otherFormatSizes
76
- * @return {number|undefined}
77
- */
78
- static estimateAvifSizeFromWebPAndJpegEstimates(otherFormatSizes) {
79
- if (!otherFormatSizes.jpegSize || !otherFormatSizes.webpSize) return undefined;
80
-
81
- // AVIF saves at least ~50% on JPEG, ~20% on WebP at low quality.
82
- // http://downloads.aomedia.org/assets/pdf/symposium-2019/slides/CyrilConcolato_Netflix-AVIF-AOM-Research-Symposium-2019.pdf
83
- // https://jakearchibald.com/2020/avif-has-landed/
84
- // https://www.finally.agency/blog/what-is-avif-image-format
85
- // See https://github.com/GoogleChrome/lighthouse/issues/12295#issue-840261460 for more.
86
- const estimateFromJpeg = otherFormatSizes.jpegSize * 5 / 10;
87
- const estimateFromWebp = otherFormatSizes.webpSize * 8 / 10;
88
- return estimateFromJpeg / 2 + estimateFromWebp / 2;
89
- }
90
-
91
- /**
92
- * @param {LH.Artifacts} artifacts
93
- * @return {import('./byte-efficiency-audit.js').ByteEfficiencyProduct}
94
- */
95
- static audit_(artifacts) {
96
- const pageURL = artifacts.URL.finalDisplayedUrl;
97
- const images = artifacts.OptimizedImages;
98
- const imageElements = artifacts.ImageElements;
99
- /** @type {Map<string, LH.Artifacts.ImageElement>} */
100
- const imageElementsByURL = new Map();
101
- imageElements.forEach(img => imageElementsByURL.set(img.src, img));
102
-
103
- /** @type {Array<LH.Audit.ByteEfficiencyItem>} */
104
- const items = [];
105
- const warnings = [];
106
- for (const image of images) {
107
- const imageElement = imageElementsByURL.get(image.url);
108
-
109
- if (image.failed) {
110
- warnings.push(`Unable to decode ${UrlUtils.getURLDisplayName(image.url)}`);
111
- continue;
112
- }
113
-
114
- // Skip if the image was already using a modern format.
115
- if (image.mimeType === 'image/webp' || image.mimeType === 'image/avif') continue;
116
-
117
- const jpegSize = image.jpegSize;
118
- let webpSize = image.webpSize;
119
- let avifSize = ModernImageFormats.estimateAvifSizeFromWebPAndJpegEstimates({
120
- jpegSize,
121
- webpSize,
122
- });
123
- let fromProtocol = true;
124
-
125
- if (typeof webpSize === 'undefined') {
126
- if (!imageElement) {
127
- warnings.push(`Unable to locate resource ${UrlUtils.getURLDisplayName(image.url)}`);
128
- continue;
129
- }
130
-
131
- // Skip if we couldn't collect natural image size information.
132
- if (!imageElement.naturalDimensions) continue;
133
- const naturalHeight = imageElement.naturalDimensions.height;
134
- const naturalWidth = imageElement.naturalDimensions.width;
135
- // If naturalHeight or naturalWidth are falsy, information is not valid, skip.
136
- if (!naturalWidth || !naturalHeight) continue;
137
-
138
- webpSize = ModernImageFormats.estimateWebPSizeFromDimensions({
139
- naturalHeight,
140
- naturalWidth,
141
- });
142
- avifSize = ModernImageFormats.estimateAvifSizeFromDimensions({
143
- naturalHeight,
144
- naturalWidth,
145
- });
146
- fromProtocol = false;
147
- }
148
-
149
- if (webpSize === undefined || avifSize === undefined) continue;
150
-
151
- // Visible wasted bytes uses AVIF, but we still include the WebP savings in the LHR.
152
- const wastedWebpBytes = image.originalSize - webpSize;
153
- const wastedBytes = image.originalSize - avifSize;
154
- if (wastedBytes < IGNORE_THRESHOLD_IN_BYTES) continue;
155
-
156
- const url = UrlUtils.elideDataURI(image.url);
157
- const isCrossOrigin = !UrlUtils.originsMatch(pageURL, image.url);
158
-
159
- items.push({
160
- node: imageElement ? ByteEfficiencyAudit.makeNodeItem(imageElement.node) : undefined,
161
- url,
162
- fromProtocol,
163
- isCrossOrigin,
164
- totalBytes: image.originalSize,
165
- wastedBytes,
166
- wastedWebpBytes,
167
- });
168
- }
169
-
170
- /** @type {LH.Audit.Details.Opportunity['headings']} */
171
- const headings = [
172
- {key: 'node', valueType: 'node', label: ''},
173
- {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
174
- {key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnResourceSize)},
175
- {key: 'wastedBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnWastedBytes)},
176
- ];
177
-
178
- return {
179
- warnings,
180
- items,
181
- headings,
182
- };
183
- }
184
- }
185
-
186
- export default ModernImageFormats;
187
- export {UIStrings};
@@ -1,63 +0,0 @@
1
- export default OffscreenImages;
2
- export type WasteResult = {
3
- node: LH.Audit.Details.NodeValue;
4
- url: string;
5
- requestStartTime: number;
6
- totalBytes: number;
7
- wastedBytes: number;
8
- wastedPercent: number;
9
- };
10
- /** @typedef {{node: LH.Audit.Details.NodeValue, url: string, requestStartTime: number, totalBytes: number, wastedBytes: number, wastedPercent: number}} WasteResult */
11
- declare class OffscreenImages extends ByteEfficiencyAudit {
12
- /**
13
- * @param {{top: number, bottom: number, left: number, right: number}} imageRect
14
- * @param {{innerWidth: number, innerHeight: number}} viewportDimensions
15
- * @return {number}
16
- */
17
- static computeVisiblePixels(imageRect: {
18
- top: number;
19
- bottom: number;
20
- left: number;
21
- right: number;
22
- }, viewportDimensions: {
23
- innerWidth: number;
24
- innerHeight: number;
25
- }): number;
26
- /**
27
- * @param {LH.Artifacts.ImageElement} image
28
- * @param {{innerWidth: number, innerHeight: number}} viewportDimensions
29
- * @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
30
- * @return {null|Error|WasteResult}
31
- */
32
- static computeWaste(image: LH.Artifacts.ImageElement, viewportDimensions: {
33
- innerWidth: number;
34
- innerHeight: number;
35
- }, networkRecords: Array<LH.Artifacts.NetworkRequest>): null | Error | WasteResult;
36
- /**
37
- * Filters out image requests that were requested after the last long task based on lantern timings.
38
- *
39
- * @param {WasteResult[]} images
40
- * @param {LH.Artifacts.LanternMetric} lanternMetricData
41
- */
42
- static filterLanternResults(images: WasteResult[], lanternMetricData: LH.Artifacts.LanternMetric): WasteResult[];
43
- /**
44
- * Filters out image requests that were requested after TTI.
45
- *
46
- * @param {WasteResult[]} images
47
- * @param {number} interactiveTimestamp
48
- */
49
- static filterObservedResults(images: WasteResult[], interactiveTimestamp: number): WasteResult[];
50
- /**
51
- * @param {LH.Artifacts} artifacts
52
- * @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
53
- * @param {LH.Audit.Context} context
54
- * @return {Promise<import('./byte-efficiency-audit.js').ByteEfficiencyProduct>}
55
- */
56
- static audit_(artifacts: LH.Artifacts, networkRecords: Array<LH.Artifacts.NetworkRequest>, context: LH.Audit.Context): Promise<import("./byte-efficiency-audit.js").ByteEfficiencyProduct>;
57
- }
58
- export namespace UIStrings {
59
- let title: string;
60
- let description: string;
61
- }
62
- import { ByteEfficiencyAudit } from './byte-efficiency-audit.js';
63
- //# sourceMappingURL=offscreen-images.d.ts.map
@@ -1,240 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2017 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
- /**
7
- * @fileoverview Checks to see if images are displayed only outside of the viewport.
8
- * Images requested after TTI are not flagged as violations.
9
- */
10
-
11
-
12
- import {ByteEfficiencyAudit} from './byte-efficiency-audit.js';
13
- import {NetworkRequest} from '../../lib/network-request.js';
14
- import {Sentry} from '../../lib/sentry.js';
15
- import UrlUtils from '../../lib/url-utils.js';
16
- import * as i18n from '../../lib/i18n/i18n.js';
17
- import {Interactive} from '../../computed/metrics/interactive.js';
18
- import {ProcessedTrace} from '../../computed/processed-trace.js';
19
-
20
- const UIStrings = {
21
- /** Imperative title of a Lighthouse audit that tells the user to defer loading offscreen images. Offscreen images are images located outside of the visible browser viewport. As they are unseen by the user and slow down page load, they should be loaded later, closer to when the user is going to see them. This is displayed in a list of audit titles that Lighthouse generates. */
22
- title: 'Defer offscreen images',
23
- /** Description of a Lighthouse audit that tells the user *why* they should defer loading offscreen images. Offscreen images are images located outside of the visible browser viewport. As they are unseen by the user and slow down page load, they should be loaded later, closer to when the user is going to see them. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */
24
- description:
25
- 'Consider lazy-loading offscreen and hidden images after all critical resources have ' +
26
- 'finished loading to lower time to interactive. ' +
27
- '[Learn how to defer offscreen images](https://developer.chrome.com/docs/lighthouse/performance/offscreen-images/).',
28
- };
29
-
30
- const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
31
-
32
- // See https://github.com/GoogleChrome/lighthouse/issues/10471 for discussion about the thresholds here.
33
- const ALLOWABLE_OFFSCREEN_IN_PX = 100;
34
- const ALLOWABLE_OFFSCREEN_BOTTOM_IN_VIEWPORTS = 3;
35
-
36
- const IGNORE_THRESHOLD_IN_BYTES = 2048;
37
- const IGNORE_THRESHOLD_IN_PERCENT = 75;
38
- const IGNORE_THRESHOLD_IN_MS = 50;
39
-
40
- /** @typedef {{node: LH.Audit.Details.NodeValue, url: string, requestStartTime: number, totalBytes: number, wastedBytes: number, wastedPercent: number}} WasteResult */
41
-
42
- class OffscreenImages extends ByteEfficiencyAudit {
43
- /**
44
- * @return {LH.Audit.Meta}
45
- */
46
- static get meta() {
47
- return {
48
- id: 'offscreen-images',
49
- title: str_(UIStrings.title),
50
- description: str_(UIStrings.description),
51
- scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
52
- supportedModes: ['navigation'],
53
- guidanceLevel: 2,
54
- requiredArtifacts: ['ImageElements', 'ViewportDimensions', 'GatherContext', 'DevtoolsLog',
55
- 'Trace', 'URL', 'SourceMaps'],
56
- };
57
- }
58
-
59
- /**
60
- * @param {{top: number, bottom: number, left: number, right: number}} imageRect
61
- * @param {{innerWidth: number, innerHeight: number}} viewportDimensions
62
- * @return {number}
63
- */
64
- static computeVisiblePixels(imageRect, viewportDimensions) {
65
- const innerWidth = viewportDimensions.innerWidth;
66
- const innerHeight = viewportDimensions.innerHeight;
67
- const allowableOffscreenBottomInPx = ALLOWABLE_OFFSCREEN_BOTTOM_IN_VIEWPORTS *
68
- viewportDimensions.innerHeight;
69
-
70
- const top = Math.max(imageRect.top, -1 * ALLOWABLE_OFFSCREEN_IN_PX);
71
- const right = Math.min(imageRect.right, innerWidth + ALLOWABLE_OFFSCREEN_IN_PX);
72
- const bottom = Math.min(imageRect.bottom, innerHeight + allowableOffscreenBottomInPx);
73
- const left = Math.max(imageRect.left, -1 * ALLOWABLE_OFFSCREEN_IN_PX);
74
-
75
- return Math.max(right - left, 0) * Math.max(bottom - top, 0);
76
- }
77
-
78
- /**
79
- * @param {LH.Artifacts.ImageElement} image
80
- * @param {{innerWidth: number, innerHeight: number}} viewportDimensions
81
- * @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
82
- * @return {null|Error|WasteResult}
83
- */
84
- static computeWaste(image, viewportDimensions, networkRecords) {
85
- const networkRecord = networkRecords.find(record => record.url === image.src);
86
- // If we don't know how big it was, we can't really report savings, treat it as passed.
87
- if (!networkRecord) return null;
88
- // If the image had its loading behavior explicitly controlled already, treat it as passed.
89
- if (image.loading === 'lazy' || image.loading === 'eager') return null;
90
-
91
- const url = UrlUtils.elideDataURI(image.src);
92
- const totalPixels = image.displayedWidth * image.displayedHeight;
93
- const visiblePixels = this.computeVisiblePixels(image.clientRect, viewportDimensions);
94
- // Treat images with 0 area as if they're offscreen. See https://github.com/GoogleChrome/lighthouse/issues/1914
95
- const wastedRatio = totalPixels === 0 ? 1 : 1 - visiblePixels / totalPixels;
96
- const totalBytes = NetworkRequest.getResourceSizeOnNetwork(networkRecord);
97
- const wastedBytes = Math.round(totalBytes * wastedRatio);
98
-
99
- if (!Number.isFinite(wastedRatio)) {
100
- return new Error(`Invalid image sizing information ${url}`);
101
- }
102
-
103
- return {
104
- node: ByteEfficiencyAudit.makeNodeItem(image.node),
105
- url,
106
- requestStartTime: networkRecord.networkRequestTime,
107
- totalBytes,
108
- wastedBytes,
109
- wastedPercent: 100 * wastedRatio,
110
- };
111
- }
112
-
113
- /**
114
- * Filters out image requests that were requested after the last long task based on lantern timings.
115
- *
116
- * @param {WasteResult[]} images
117
- * @param {LH.Artifacts.LanternMetric} lanternMetricData
118
- */
119
- static filterLanternResults(images, lanternMetricData) {
120
- const nodeTimings = lanternMetricData.pessimisticEstimate.nodeTimings;
121
-
122
- // Find the last long task start time
123
- let lastLongTaskStartTime = 0;
124
- // Find the start time of all requests
125
- /** @type {Map<string, number>} */
126
- const startTimesByURL = new Map();
127
- for (const [node, timing] of nodeTimings) {
128
- if (node.type === 'cpu' && timing.duration >= 50) {
129
- lastLongTaskStartTime = Math.max(lastLongTaskStartTime, timing.startTime);
130
- } else if (node.type === 'network') {
131
- startTimesByURL.set(node.request.url, timing.startTime);
132
- }
133
- }
134
-
135
- return images.filter(image => {
136
- // Filter out images that had little waste
137
- if (image.wastedBytes < IGNORE_THRESHOLD_IN_BYTES) return false;
138
- if (image.wastedPercent < IGNORE_THRESHOLD_IN_PERCENT) return false;
139
- // Filter out images that started after the last long task
140
- const imageRequestStartTime = startTimesByURL.get(image.url) || 0;
141
- return imageRequestStartTime < lastLongTaskStartTime - IGNORE_THRESHOLD_IN_MS;
142
- });
143
- }
144
-
145
- /**
146
- * Filters out image requests that were requested after TTI.
147
- *
148
- * @param {WasteResult[]} images
149
- * @param {number} interactiveTimestamp
150
- */
151
- static filterObservedResults(images, interactiveTimestamp) {
152
- return images.filter(image => {
153
- if (image.wastedBytes < IGNORE_THRESHOLD_IN_BYTES) return false;
154
- if (image.wastedPercent < IGNORE_THRESHOLD_IN_PERCENT) return false;
155
- return image.requestStartTime < interactiveTimestamp / 1000 - IGNORE_THRESHOLD_IN_MS;
156
- });
157
- }
158
-
159
- /**
160
- * @param {LH.Artifacts} artifacts
161
- * @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
162
- * @param {LH.Audit.Context} context
163
- * @return {Promise<import('./byte-efficiency-audit.js').ByteEfficiencyProduct>}
164
- */
165
- static async audit_(artifacts, networkRecords, context) {
166
- const {URL, SourceMaps} = artifacts;
167
- const images = artifacts.ImageElements;
168
- const viewportDimensions = artifacts.ViewportDimensions;
169
- const gatherContext = artifacts.GatherContext;
170
- const trace = artifacts.Trace;
171
- const devtoolsLog = artifacts.DevtoolsLog;
172
-
173
- /** @type {string[]} */
174
- const warnings = [];
175
- /** @type {Map<string, WasteResult>} */
176
- const resultsMap = new Map();
177
- for (const image of images) {
178
- const processed = OffscreenImages.computeWaste(image, viewportDimensions, networkRecords);
179
- if (processed === null) {
180
- continue;
181
- }
182
-
183
- if (processed instanceof Error) {
184
- warnings.push(processed.message);
185
- Sentry.captureException(processed, {tags: {audit: this.meta.id}, level: 'warning'});
186
- continue;
187
- }
188
-
189
- // If an image was used more than once, warn only about its least wasteful usage
190
- const existing = resultsMap.get(processed.url);
191
- if (!existing || existing.wastedBytes > processed.wastedBytes) {
192
- resultsMap.set(processed.url, processed);
193
- }
194
- }
195
-
196
- const settings = context.settings;
197
-
198
- let items;
199
- const unfilteredResults = Array.from(resultsMap.values());
200
- // get the interactive time or fallback to getting the end of trace time
201
- try {
202
- const metricComputationData =
203
- {trace, devtoolsLog, gatherContext, settings, URL, SourceMaps, simulator: null};
204
- const interactive = await Interactive.request(metricComputationData, context);
205
-
206
- // use interactive to generate items
207
- const lanternInteractive = /** @type {LH.Artifacts.LanternMetric} */ (interactive);
208
- // Filter out images that were loaded after all CPU activity
209
- items = context.settings.throttlingMethod === 'simulate' ?
210
- OffscreenImages.filterLanternResults(unfilteredResults, lanternInteractive) :
211
- // @ts-expect-error - .timestamp will exist if throttlingMethod isn't lantern
212
- OffscreenImages.filterObservedResults(unfilteredResults, interactive.timestamp);
213
- } catch (err) {
214
- // if the error is during a Lantern run, end of trace may also be inaccurate, so rethrow
215
- if (context.settings.throttlingMethod === 'simulate') {
216
- throw err;
217
- }
218
- // use end of trace as a substitute for finding interactive time
219
- items = OffscreenImages.filterObservedResults(unfilteredResults,
220
- await ProcessedTrace.request(trace, context).then(tot => tot.timestamps.traceEnd));
221
- }
222
-
223
- /** @type {LH.Audit.Details.Opportunity['headings']} */
224
- const headings = [
225
- {key: 'node', valueType: 'node', label: ''},
226
- {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
227
- {key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnResourceSize)},
228
- {key: 'wastedBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnWastedBytes)},
229
- ];
230
-
231
- return {
232
- warnings,
233
- items,
234
- headings,
235
- };
236
- }
237
- }
238
-
239
- export default OffscreenImages;
240
- export {UIStrings};
@@ -1,53 +0,0 @@
1
- export default RenderBlockingResources;
2
- declare class RenderBlockingResources extends Audit {
3
- /**
4
- * @param {LH.Artifacts} artifacts
5
- * @param {LH.Audit.Context} context
6
- * @return {Promise<{fcpWastedMs: number, lcpWastedMs: number, results: Array<{url: string, totalBytes: number, wastedMs: number}>}>}
7
- */
8
- static computeResults(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<{
9
- fcpWastedMs: number;
10
- lcpWastedMs: number;
11
- results: Array<{
12
- url: string;
13
- totalBytes: number;
14
- wastedMs: number;
15
- }>;
16
- }>;
17
- /**
18
- * Estimates how much faster this page would reach FCP if we inlined all the used CSS from the
19
- * render blocking stylesheets and deferred all the scripts. This is more conservative than
20
- * removing all the assets and more aggressive than inlining everything.
21
- *
22
- * *Most* of the time, scripts in the head are there accidentally/due to lack of awareness
23
- * rather than necessity, so we're comfortable with this balance. In the worst case, we're telling
24
- * devs that they should be able to get to a reasonable first paint without JS, which is not a bad
25
- * thing.
26
- *
27
- * @param {LH.Gatherer.Simulation.Simulator} simulator
28
- * @param {LH.Gatherer.Simulation.GraphNode} fcpGraph
29
- * @param {Set<string>} deferredIds
30
- * @param {Map<string, number>} wastedCssBytesByUrl
31
- * @param {LH.Artifacts.DetectedStack[]} Stacks
32
- * @return {number}
33
- */
34
- static estimateSavingsWithGraphs(simulator: LH.Gatherer.Simulation.Simulator, fcpGraph: LH.Gatherer.Simulation.GraphNode, deferredIds: Set<string>, wastedCssBytesByUrl: Map<string, number>, Stacks: LH.Artifacts.DetectedStack[]): number;
35
- /**
36
- * @param {LH.Artifacts} artifacts
37
- * @param {LH.Audit.Context} context
38
- * @return {Promise<Map<string, number>>}
39
- */
40
- static computeWastedCSSBytes(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<Map<string, number>>;
41
- /**
42
- * @param {LH.Artifacts} artifacts
43
- * @param {LH.Audit.Context} context
44
- * @return {Promise<LH.Audit.Product>}
45
- */
46
- static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
47
- }
48
- export namespace UIStrings {
49
- let title: string;
50
- let description: string;
51
- }
52
- import { Audit } from '../audit.js';
53
- //# sourceMappingURL=render-blocking-resources.d.ts.map