lighthouse 12.8.2-dev.20251004 → 12.8.2-dev.20251006

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 (126) hide show
  1. package/cli/test/smokehouse/config/exclusions.js +0 -2
  2. package/core/audits/audit.js +0 -1
  3. package/core/audits/insights/cls-culprits-insight.js +1 -1
  4. package/core/audits/insights/dom-size-insight.js +6 -6
  5. package/core/audits/redirects.js +1 -0
  6. package/core/audits/server-response-time.d.ts +0 -5
  7. package/core/audits/server-response-time.js +12 -26
  8. package/core/computed/metrics/lcp-breakdown.js +1 -0
  9. package/core/config/default-config.js +20 -63
  10. package/core/config/experimental-config.js +1 -26
  11. package/core/config/filters.js +6 -9
  12. package/core/config/lr-desktop-config.js +0 -1
  13. package/core/config/lr-mobile-config.js +0 -1
  14. package/core/gather/gatherers/trace-elements.js +1 -0
  15. package/core/lib/proto-preprocessor.js +5 -22
  16. package/dist/report/bundle.esm.js +10 -49
  17. package/dist/report/flow.js +12 -51
  18. package/dist/report/standalone.js +11 -50
  19. package/flow-report/src/i18n/i18n.d.ts +4 -6
  20. package/package.json +3 -3
  21. package/report/assets/styles.css +0 -39
  22. package/report/renderer/api.js +0 -1
  23. package/report/renderer/category-renderer.js +6 -0
  24. package/report/renderer/components.js +1 -1
  25. package/report/renderer/dom.d.ts +0 -13
  26. package/report/renderer/dom.js +0 -38
  27. package/report/renderer/performance-category-renderer.d.ts +0 -26
  28. package/report/renderer/performance-category-renderer.js +10 -142
  29. package/report/renderer/report-ui-features.d.ts +0 -1
  30. package/report/renderer/report-ui-features.js +3 -13
  31. package/report/renderer/report-utils.d.ts +2 -3
  32. package/report/renderer/report-utils.js +4 -6
  33. package/report/types/report-renderer.d.ts +0 -6
  34. package/shared/localization/locales/ar-XB.json +0 -330
  35. package/shared/localization/locales/ar.json +0 -330
  36. package/shared/localization/locales/bg.json +0 -330
  37. package/shared/localization/locales/ca.json +0 -330
  38. package/shared/localization/locales/cs.json +0 -330
  39. package/shared/localization/locales/da.json +0 -330
  40. package/shared/localization/locales/de.json +0 -330
  41. package/shared/localization/locales/el.json +0 -330
  42. package/shared/localization/locales/en-GB.json +0 -330
  43. package/shared/localization/locales/en-US.json +26 -275
  44. package/shared/localization/locales/en-XA.json +0 -330
  45. package/shared/localization/locales/en-XL.json +26 -275
  46. package/shared/localization/locales/es-419.json +0 -330
  47. package/shared/localization/locales/es.json +0 -330
  48. package/shared/localization/locales/fi.json +0 -330
  49. package/shared/localization/locales/fil.json +0 -330
  50. package/shared/localization/locales/fr.json +0 -330
  51. package/shared/localization/locales/he.json +0 -330
  52. package/shared/localization/locales/hi.json +0 -330
  53. package/shared/localization/locales/hr.json +0 -330
  54. package/shared/localization/locales/hu.json +0 -330
  55. package/shared/localization/locales/id.json +0 -330
  56. package/shared/localization/locales/it.json +0 -330
  57. package/shared/localization/locales/ja.json +0 -330
  58. package/shared/localization/locales/ko.json +0 -330
  59. package/shared/localization/locales/lt.json +0 -330
  60. package/shared/localization/locales/lv.json +0 -330
  61. package/shared/localization/locales/nl.json +0 -330
  62. package/shared/localization/locales/no.json +0 -330
  63. package/shared/localization/locales/pl.json +0 -330
  64. package/shared/localization/locales/pt-PT.json +0 -330
  65. package/shared/localization/locales/pt.json +0 -330
  66. package/shared/localization/locales/ro.json +0 -330
  67. package/shared/localization/locales/ru.json +0 -330
  68. package/shared/localization/locales/sk.json +0 -330
  69. package/shared/localization/locales/sl.json +0 -330
  70. package/shared/localization/locales/sr-Latn.json +0 -330
  71. package/shared/localization/locales/sr.json +0 -330
  72. package/shared/localization/locales/sv.json +0 -330
  73. package/shared/localization/locales/ta.json +0 -330
  74. package/shared/localization/locales/te.json +0 -330
  75. package/shared/localization/locales/th.json +0 -330
  76. package/shared/localization/locales/tr.json +0 -330
  77. package/shared/localization/locales/uk.json +0 -330
  78. package/shared/localization/locales/vi.json +0 -330
  79. package/shared/localization/locales/zh-HK.json +0 -330
  80. package/shared/localization/locales/zh-TW.json +0 -330
  81. package/shared/localization/locales/zh.json +0 -330
  82. package/types/artifacts.d.ts +1 -0
  83. package/types/audit.d.ts +1 -1
  84. package/types/lhr/settings.d.ts +1 -1
  85. package/core/audits/byte-efficiency/duplicated-javascript.d.ts +0 -45
  86. package/core/audits/byte-efficiency/duplicated-javascript.js +0 -223
  87. package/core/audits/byte-efficiency/efficient-animated-content.d.ts +0 -22
  88. package/core/audits/byte-efficiency/efficient-animated-content.js +0 -93
  89. package/core/audits/byte-efficiency/legacy-javascript.d.ts +0 -28
  90. package/core/audits/byte-efficiency/legacy-javascript.js +0 -144
  91. package/core/audits/byte-efficiency/modern-image-formats.d.ts +0 -38
  92. package/core/audits/byte-efficiency/modern-image-formats.js +0 -187
  93. package/core/audits/byte-efficiency/render-blocking-resources.d.ts +0 -53
  94. package/core/audits/byte-efficiency/render-blocking-resources.js +0 -312
  95. package/core/audits/byte-efficiency/uses-long-cache-ttl.d.ts +0 -59
  96. package/core/audits/byte-efficiency/uses-long-cache-ttl.js +0 -293
  97. package/core/audits/byte-efficiency/uses-optimized-images.d.ts +0 -33
  98. package/core/audits/byte-efficiency/uses-optimized-images.js +0 -146
  99. package/core/audits/byte-efficiency/uses-responsive-images-snapshot.d.ts +0 -16
  100. package/core/audits/byte-efficiency/uses-responsive-images-snapshot.js +0 -106
  101. package/core/audits/byte-efficiency/uses-responsive-images.d.ts +0 -44
  102. package/core/audits/byte-efficiency/uses-responsive-images.js +0 -202
  103. package/core/audits/byte-efficiency/uses-text-compression.d.ts +0 -14
  104. package/core/audits/byte-efficiency/uses-text-compression.js +0 -108
  105. package/core/audits/critical-request-chains.d.ts +0 -44
  106. package/core/audits/critical-request-chains.js +0 -221
  107. package/core/audits/dobetterweb/dom-size.d.ts +0 -32
  108. package/core/audits/dobetterweb/dom-size.js +0 -182
  109. package/core/audits/dobetterweb/uses-http2.d.ts +0 -72
  110. package/core/audits/dobetterweb/uses-http2.js +0 -276
  111. package/core/audits/font-display.d.ts +0 -32
  112. package/core/audits/font-display.js +0 -195
  113. package/core/audits/largest-contentful-paint-element.d.ts +0 -34
  114. package/core/audits/largest-contentful-paint-element.js +0 -181
  115. package/core/audits/lcp-lazy-loaded.d.ts +0 -22
  116. package/core/audits/lcp-lazy-loaded.js +0 -115
  117. package/core/audits/prioritize-lcp-image.d.ts +0 -74
  118. package/core/audits/prioritize-lcp-image.js +0 -297
  119. package/core/audits/third-party-summary.d.ts +0 -78
  120. package/core/audits/third-party-summary.js +0 -236
  121. package/core/audits/uses-rel-preconnect.d.ts +0 -37
  122. package/core/audits/uses-rel-preconnect.js +0 -286
  123. package/core/audits/viewport.d.ts +0 -17
  124. package/core/audits/viewport.js +0 -87
  125. package/core/audits/work-during-interaction.d.ts +0 -81
  126. package/core/audits/work-during-interaction.js +0 -287
@@ -1,195 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2017 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
-
7
- import {Audit} from './audit.js';
8
- import UrlUtils from '../lib/url-utils.js';
9
- import * as i18n from '../lib/i18n/i18n.js';
10
- import {Sentry} from '../lib/sentry.js';
11
- import {NetworkRecords} from '../computed/network-records.js';
12
-
13
- const PASSING_FONT_DISPLAY_REGEX = /^(block|fallback|optional|swap)$/;
14
- const CSS_URL_REGEX = /url\((.*?)\)/;
15
- const CSS_URL_GLOBAL_REGEX = new RegExp(CSS_URL_REGEX, 'g');
16
-
17
- const UIStrings = {
18
- /** Title of a diagnostic audit that provides detail on if all the text on a webpage was visible while the page was loading its webfonts. This descriptive title is shown to users when the amount is acceptable and no user action is required. */
19
- title: 'All text remains visible during webfont loads',
20
- /** Title of a diagnostic audit that provides detail on the load of the page's webfonts. Often the text is invisible for seconds before the webfont resource is loaded. This imperative title is shown to users when there is a significant amount of execution time that could be reduced. */
21
- failureTitle: 'Ensure text remains visible during webfont load',
22
- /** Description of a Lighthouse audit that tells the user *why* they should use the font-display CSS feature. 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. */
23
- description:
24
- 'Leverage the `font-display` CSS feature to ensure text is user-visible while ' +
25
- 'webfonts are loading. ' +
26
- '[Learn more about `font-display`](https://developer.chrome.com/docs/lighthouse/performance/font-display/).',
27
- /**
28
- * @description [ICU Syntax] A warning message that is shown when Lighthouse couldn't automatically check some of the page's fonts, telling the user that they will need to manually check the fonts coming from a certain URL origin.
29
- * @example {https://font.cdn.com/} fontOrigin
30
- */
31
- undeclaredFontOriginWarning:
32
- '{fontCountForOrigin, plural, ' +
33
- // eslint-disable-next-line max-len
34
- '=1 {Lighthouse was unable to automatically check the `font-display` value for the origin {fontOrigin}.} ' +
35
- // eslint-disable-next-line max-len
36
- 'other {Lighthouse was unable to automatically check the `font-display` values for the origin {fontOrigin}.}}',
37
- };
38
-
39
- const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
40
-
41
- class FontDisplay extends Audit {
42
- /**
43
- * @return {LH.Audit.Meta}
44
- */
45
- static get meta() {
46
- return {
47
- id: 'font-display',
48
- title: str_(UIStrings.title),
49
- failureTitle: str_(UIStrings.failureTitle),
50
- description: str_(UIStrings.description),
51
- supportedModes: ['navigation'],
52
- guidanceLevel: 3,
53
- requiredArtifacts: ['DevtoolsLog', 'Stylesheets', 'URL'],
54
- scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
55
- };
56
- }
57
-
58
- /**
59
- * @param {LH.Artifacts} artifacts
60
- * @param {RegExp} passingFontDisplayRegex
61
- * @return {{passingURLs: Set<string>, failingURLs: Set<string>}}
62
- */
63
- static findFontDisplayDeclarations(artifacts, passingFontDisplayRegex) {
64
- /** @type {Set<string>} */
65
- const passingURLs = new Set();
66
- /** @type {Set<string>} */
67
- const failingURLs = new Set();
68
-
69
- // Go through all the stylesheets to find all @font-face declarations
70
- for (const stylesheet of artifacts.Stylesheets) {
71
- // Eliminate newlines so we can more easily scan through with a regex
72
- const newlinesStripped = stylesheet.content.replace(/(\r|\n)+/g, ' ');
73
- // Find the @font-faces
74
- const fontFaceDeclarations = newlinesStripped.match(/@font-face\s*{(.*?)}/g) || [];
75
- // Go through all the @font-face declarations to find a declared `font-display: ` property
76
- for (const declaration of fontFaceDeclarations) {
77
- // We'll try to find the URL it's referencing.
78
- const rawFontURLs = declaration.match(CSS_URL_GLOBAL_REGEX);
79
- // If no URLs, we can't really do anything; bail
80
- if (!rawFontURLs) continue;
81
- // Find the font-display value by matching a single token, optionally surrounded by whitespace,
82
- // followed either by a semicolon or the end of a block.
83
- const fontDisplayMatch = declaration.match(/font-display\s*:\s*(\w+)\s*(;|\})/);
84
- const rawFontDisplay = fontDisplayMatch?.[1] || '';
85
- const hasPassingFontDisplay = passingFontDisplayRegex.test(rawFontDisplay);
86
- const targetURLSet = hasPassingFontDisplay ? passingURLs : failingURLs;
87
-
88
- // Finally convert the raw font URLs to the absolute URLs and add them to the set.
89
- const relativeURLs = rawFontURLs
90
- // @ts-expect-error - guaranteed to match from previous regex, pull URL group out
91
- .map(s => s.match(CSS_URL_REGEX)[1].trim())
92
- .map(s => {
93
- // remove any quotes surrounding the URL
94
- if (/^('|").*\1$/.test(s)) {
95
- return s.substr(1, s.length - 2);
96
- }
97
-
98
- return s;
99
- });
100
-
101
- // Convert the relative CSS URL to an absolute URL and add it to the target set.
102
- for (const relativeURL of relativeURLs) {
103
- try {
104
- const relativeRoot = UrlUtils.isValid(stylesheet.header.sourceURL) ?
105
- stylesheet.header.sourceURL : artifacts.URL.finalDisplayedUrl;
106
- const absoluteURL = new URL(relativeURL, relativeRoot);
107
- targetURLSet.add(absoluteURL.href);
108
- } catch (err) {
109
- Sentry.captureException(err, {tags: {audit: this.meta.id}});
110
- }
111
- }
112
- }
113
- }
114
-
115
- return {passingURLs, failingURLs};
116
- }
117
-
118
- /**
119
- * Some pages load many fonts we can't check, so dedupe on origin.
120
- * @param {Array<string>} warningUrls
121
- * @return {Array<LH.IcuMessage>}
122
- */
123
- static getWarningsForFontUrls(warningUrls) {
124
- /** @type {Map<string, number>} */
125
- const warningCountByOrigin = new Map();
126
- for (const warningUrl of warningUrls) {
127
- const origin = UrlUtils.getOrigin(warningUrl);
128
- if (!origin) continue;
129
-
130
- const count = warningCountByOrigin.get(origin) || 0;
131
- warningCountByOrigin.set(origin, count + 1);
132
- }
133
-
134
- const warnings = [...warningCountByOrigin].map(([fontOrigin, fontCountForOrigin]) => {
135
- return str_(UIStrings.undeclaredFontOriginWarning, {fontCountForOrigin, fontOrigin});
136
- });
137
- return warnings;
138
- }
139
-
140
- /**
141
- * @param {LH.Artifacts} artifacts
142
- * @param {LH.Audit.Context} context
143
- * @return {Promise<LH.Audit.Product>}
144
- */
145
- static async audit(artifacts, context) {
146
- const devtoolsLogs = artifacts.DevtoolsLog;
147
- const networkRecords = await NetworkRecords.request(devtoolsLogs, context);
148
- const {passingURLs, failingURLs} =
149
- FontDisplay.findFontDisplayDeclarations(artifacts, PASSING_FONT_DISPLAY_REGEX);
150
- /** @type {Array<string>} */
151
- const warningURLs = [];
152
-
153
- const results = networkRecords
154
- // Find all fonts...
155
- .filter(record => record.resourceType === 'Font')
156
- // ...and that aren't data URLs, the blocking concern doesn't really apply
157
- .filter(record => !/^data:/.test(record.url))
158
- .filter(record => !/^blob:/.test(record.url))
159
- // ...that have a failing font-display value
160
- .filter(record => {
161
- // Failing URLs should be considered.
162
- if (failingURLs.has(record.url)) return true;
163
- // Everything else shouldn't be, but we should warn if we don't recognize the URL at all.
164
- if (!passingURLs.has(record.url)) warningURLs.push(record.url);
165
- return false;
166
- })
167
- .map(record => {
168
- // In reality the end time should be calculated with paint time included
169
- // all browsers wait 3000ms to block text so we make sure 3000 is our max wasted time
170
- const wastedMs = Math.min(record.networkEndTime - record.networkRequestTime, 3000);
171
-
172
- return {
173
- url: record.url,
174
- wastedMs,
175
- };
176
- });
177
-
178
- /** @type {LH.Audit.Details.Table['headings']} */
179
- const headings = [
180
- {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
181
- {key: 'wastedMs', valueType: 'ms', label: str_(i18n.UIStrings.columnWastedMs)},
182
- ];
183
-
184
- const details = Audit.makeTableDetails(headings, results);
185
-
186
- return {
187
- score: Number(results.length === 0),
188
- details,
189
- warnings: FontDisplay.getWarningsForFontUrls(warningURLs),
190
- };
191
- }
192
- }
193
-
194
- export default FontDisplay;
195
- export {UIStrings};
@@ -1,34 +0,0 @@
1
- export default LargestContentfulPaintElement;
2
- declare class LargestContentfulPaintElement extends Audit {
3
- /**
4
- * @param {LH.Artifacts} artifacts
5
- * @return {LH.Audit.Details.Table|undefined}
6
- */
7
- static makeElementTable(artifacts: LH.Artifacts): LH.Audit.Details.Table | undefined;
8
- /**
9
- * @param {number} metricLcp
10
- * @param {LH.Artifacts.MetricComputationDataInput} metricComputationData
11
- * @param {LH.Audit.Context} context
12
- * @return {Promise<LH.Audit.Details.Table>}
13
- */
14
- static makePhaseTable(metricLcp: number, metricComputationData: LH.Artifacts.MetricComputationDataInput, context: LH.Audit.Context): Promise<LH.Audit.Details.Table>;
15
- /**
16
- * @param {LH.Artifacts} artifacts
17
- * @param {LH.Audit.Context} context
18
- * @return {Promise<LH.Audit.Product>}
19
- */
20
- static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
21
- }
22
- export namespace UIStrings {
23
- let title: string;
24
- let description: string;
25
- let columnPhase: string;
26
- let columnPercentOfLCP: string;
27
- let columnTiming: string;
28
- let itemTTFB: string;
29
- let itemLoadDelay: string;
30
- let itemLoadTime: string;
31
- let itemRenderDelay: string;
32
- }
33
- import { Audit } from './audit.js';
34
- //# sourceMappingURL=largest-contentful-paint-element.d.ts.map
@@ -1,181 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2020 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
-
7
- import log from 'lighthouse-logger';
8
-
9
- import {Audit} from './audit.js';
10
- import * as i18n from '../lib/i18n/i18n.js';
11
- import {LargestContentfulPaint as LargestContentfulPaintComputed} from '../computed/metrics/largest-contentful-paint.js';
12
- import LargestContentfulPaint from './metrics/largest-contentful-paint.js';
13
- import {LCPBreakdown} from '../computed/metrics/lcp-breakdown.js';
14
- import {Sentry} from '../lib/sentry.js';
15
-
16
- const UIStrings = {
17
- /** Descriptive title of a diagnostic audit that provides the element that was determined to be the Largest Contentful Paint. */
18
- title: 'Largest Contentful Paint element',
19
- /** Description of a Lighthouse audit that tells the user that the element shown was determined to be the Largest Contentful Paint. */
20
- description: 'This is the largest contentful element painted within the viewport. ' +
21
- '[Learn more about the Largest Contentful Paint element](https://developer.chrome.com/docs/lighthouse/performance/lighthouse-largest-contentful-paint/)',
22
- /** Label for a column in a data table; entries will be the name of a phase in the Largest Contentful Paint (LCP) metric. */
23
- columnPhase: 'Phase',
24
- /** Label for a column in a data table; entries will be the percent of Largest Contentful Paint (LCP) that a phase covers. */
25
- columnPercentOfLCP: '% of LCP',
26
- /** Label for a column in a data table; entries will be the amount of time spent in a phase in the Largest Contentful Paint (LCP) metric. */
27
- columnTiming: 'Timing',
28
- /** Table item value for the Time To First Byte (TTFB) phase of the Largest Contentful Paint (LCP) metric. */
29
- itemTTFB: 'TTFB',
30
- /** Table item value for the load delay phase of the Largest Contentful Paint (LCP) metric. */
31
- itemLoadDelay: 'Load Delay',
32
- /** Table item value for the load time phase of the Largest Contentful Paint (LCP) metric. */
33
- itemLoadTime: 'Load Time',
34
- /** Table item value for the render delay phase of the Largest Contentful Paint (LCP) metric. */
35
- itemRenderDelay: 'Render Delay',
36
- };
37
-
38
- const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
39
-
40
- class LargestContentfulPaintElement extends Audit {
41
- /**
42
- * @return {LH.Audit.Meta}
43
- */
44
- static get meta() {
45
- return {
46
- id: 'largest-contentful-paint-element',
47
- title: str_(UIStrings.title),
48
- description: str_(UIStrings.description),
49
- scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
50
- guidanceLevel: 1,
51
- supportedModes: ['navigation'],
52
- requiredArtifacts:
53
- ['Trace', 'TraceElements', 'DevtoolsLog', 'GatherContext', 'settings', 'URL',
54
- 'SourceMaps'],
55
- };
56
- }
57
-
58
- /**
59
- * @param {LH.Artifacts} artifacts
60
- * @return {LH.Audit.Details.Table|undefined}
61
- */
62
- static makeElementTable(artifacts) {
63
- const lcpElement = artifacts.TraceElements
64
- .find(element => element.traceEventType === 'largest-contentful-paint');
65
- if (!lcpElement) return;
66
-
67
- /** @type {LH.Audit.Details.Table['headings']} */
68
- const headings = [
69
- {key: 'node', valueType: 'node', label: str_(i18n.UIStrings.columnElement)},
70
- ];
71
-
72
- const lcpElementDetails = [{node: Audit.makeNodeItem(lcpElement.node)}];
73
-
74
- return Audit.makeTableDetails(headings, lcpElementDetails);
75
- }
76
-
77
- /**
78
- * @param {number} metricLcp
79
- * @param {LH.Artifacts.MetricComputationDataInput} metricComputationData
80
- * @param {LH.Audit.Context} context
81
- * @return {Promise<LH.Audit.Details.Table>}
82
- */
83
- static async makePhaseTable(metricLcp, metricComputationData, context) {
84
- const {ttfb, loadStart, loadEnd} = await LCPBreakdown.request(metricComputationData, context);
85
-
86
- let loadDelay = 0;
87
- let loadTime = 0;
88
- let renderDelay = metricLcp - ttfb;
89
-
90
- if (loadStart && loadEnd) {
91
- loadDelay = loadStart - ttfb;
92
- loadTime = loadEnd - loadStart;
93
- renderDelay = metricLcp - loadEnd;
94
- }
95
-
96
- const results = [
97
- {phase: str_(UIStrings.itemTTFB), timing: ttfb},
98
- {phase: str_(UIStrings.itemLoadDelay), timing: loadDelay},
99
- {phase: str_(UIStrings.itemLoadTime), timing: loadTime},
100
- {phase: str_(UIStrings.itemRenderDelay), timing: renderDelay},
101
- ].map(result => {
102
- const percent = 100 * result.timing / metricLcp;
103
- const percentStr = `${percent.toFixed(0)}%`;
104
- return {...result, percent: percentStr};
105
- });
106
-
107
- /** @type {LH.Audit.Details.Table['headings']} */
108
- const headings = [
109
- {key: 'phase', valueType: 'text', label: str_(UIStrings.columnPhase)},
110
- {key: 'percent', valueType: 'text', label: str_(UIStrings.columnPercentOfLCP)},
111
- {key: 'timing', valueType: 'ms', label: str_(UIStrings.columnTiming)},
112
- ];
113
-
114
- return Audit.makeTableDetails(headings, results);
115
- }
116
-
117
- /**
118
- * @param {LH.Artifacts} artifacts
119
- * @param {LH.Audit.Context} context
120
- * @return {Promise<LH.Audit.Product>}
121
- */
122
- static async audit(artifacts, context) {
123
- const trace = artifacts.Trace;
124
- const devtoolsLog = artifacts.DevtoolsLog;
125
- const gatherContext = artifacts.GatherContext;
126
- const metricComputationData = {
127
- trace, devtoolsLog, gatherContext,
128
- settings: context.settings, URL: artifacts.URL,
129
- SourceMaps: artifacts.SourceMaps, simulator: null,
130
- };
131
-
132
- const elementTable = this.makeElementTable(artifacts);
133
- if (!elementTable) {
134
- return {
135
- score: null,
136
- notApplicable: true,
137
- metricSavings: {LCP: 0},
138
- };
139
- }
140
-
141
- const items = [elementTable];
142
- let displayValue;
143
- let metricLcp = 0;
144
-
145
- try {
146
- const lcpResult =
147
- await LargestContentfulPaintComputed.request(metricComputationData, context);
148
- metricLcp = lcpResult.timing;
149
- displayValue = str_(i18n.UIStrings.ms, {timeInMs: metricLcp});
150
-
151
- const phaseTable = await this.makePhaseTable(metricLcp, metricComputationData, context);
152
- items.push(phaseTable);
153
- } catch (err) {
154
- Sentry.captureException(err, {
155
- tags: {audit: this.meta.id},
156
- level: 'error',
157
- });
158
- log.error(this.meta.id, err.message);
159
- }
160
-
161
- const details = Audit.makeListDetails(items);
162
-
163
- // Conceptually, this doesn't make much sense as "savings" for this audit since there isn't anything to "fix".
164
- // However, this audit will always be useful when improving LCP and that should be reflected in our impact calculations.
165
- const idealLcp = LargestContentfulPaint.defaultOptions[context.settings.formFactor].scoring.p10;
166
- const lcpSavings = Math.max(0, metricLcp - idealLcp);
167
-
168
- return {
169
- score: lcpSavings ? 0 : 1,
170
- scoreDisplayMode: lcpSavings ? undefined : Audit.SCORING_MODES.INFORMATIVE,
171
- displayValue,
172
- details,
173
- metricSavings: {
174
- LCP: lcpSavings,
175
- },
176
- };
177
- }
178
- }
179
-
180
- export default LargestContentfulPaintElement;
181
- export {UIStrings};
@@ -1,22 +0,0 @@
1
- export default LargestContentfulPaintLazyLoaded;
2
- declare class LargestContentfulPaintLazyLoaded extends Audit {
3
- /**
4
- * @param {LH.Artifacts.ImageElement} image
5
- * @param {LH.Artifacts.ViewportDimensions} viewportDimensions
6
- * @return {boolean}
7
- */
8
- static isImageInViewport(image: LH.Artifacts.ImageElement, viewportDimensions: LH.Artifacts.ViewportDimensions): boolean;
9
- /**
10
- * @param {LH.Artifacts} artifacts
11
- * @param {LH.Audit.Context} context
12
- * @return {Promise<LH.Audit.Product>}
13
- */
14
- static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
15
- }
16
- export namespace UIStrings {
17
- let title: string;
18
- let failureTitle: string;
19
- let description: string;
20
- }
21
- import { Audit } from './audit.js';
22
- //# sourceMappingURL=lcp-lazy-loaded.d.ts.map
@@ -1,115 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2021 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
-
7
- import {Audit} from './audit.js';
8
- import * as i18n from '../lib/i18n/i18n.js';
9
- import {LCPBreakdown} from '../computed/metrics/lcp-breakdown.js';
10
- import {LargestContentfulPaint} from '../computed/metrics/largest-contentful-paint.js';
11
-
12
- const UIStrings = {
13
- /** Title of a Lighthouse audit that provides detail on whether the largest above-the-fold image was loaded with sufficient priority. This descriptive title is shown to users when the image was loaded properly. */
14
- title: 'Largest Contentful Paint image was not lazily loaded',
15
- /** Title of a Lighthouse audit that provides detail on whether the largest above-the-fold image was loaded with sufficient priority. This descriptive title is shown to users when the image was loaded inefficiently using the `loading=lazy` attribute. */
16
- failureTitle: 'Largest Contentful Paint image was lazily loaded',
17
- /** Description of a Lighthouse audit that tells the user why the advice is important. This is displayed after a user expands the section to see more. No character length limits. */
18
- description: 'Above-the-fold images that are lazily loaded render later in the page lifecycle, which can delay the largest contentful paint. [Learn more about optimal lazy loading](https://web.dev/articles/lcp-lazy-loading).',
19
- };
20
-
21
- const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
22
-
23
- const ESTIMATED_PERCENT_SAVINGS = 0.15;
24
-
25
- class LargestContentfulPaintLazyLoaded extends Audit {
26
- /**
27
- * @return {LH.Audit.Meta}
28
- */
29
- static get meta() {
30
- return {
31
- id: 'lcp-lazy-loaded',
32
- title: str_(UIStrings.title),
33
- failureTitle: str_(UIStrings.failureTitle),
34
- description: str_(UIStrings.description),
35
- supportedModes: ['navigation'],
36
- scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
37
- guidanceLevel: 3,
38
- requiredArtifacts: ['TraceElements', 'ViewportDimensions', 'ImageElements',
39
- 'Trace', 'DevtoolsLog', 'GatherContext', 'URL', 'SourceMaps'],
40
- };
41
- }
42
-
43
- /**
44
- * @param {LH.Artifacts.ImageElement} image
45
- * @param {LH.Artifacts.ViewportDimensions} viewportDimensions
46
- * @return {boolean}
47
- */
48
- static isImageInViewport(image, viewportDimensions) {
49
- const imageTop = image.clientRect.top;
50
- const viewportHeight = viewportDimensions.innerHeight;
51
- return imageTop < viewportHeight;
52
- }
53
-
54
- /**
55
- * @param {LH.Artifacts} artifacts
56
- * @param {LH.Audit.Context} context
57
- * @return {Promise<LH.Audit.Product>}
58
- */
59
- static async audit(artifacts, context) {
60
- const lcpElement = artifacts.TraceElements.find(element => {
61
- return element.traceEventType === 'largest-contentful-paint' && element.type === 'image';
62
- });
63
- const lcpElementImage = lcpElement ? artifacts.ImageElements.find(elem => {
64
- return elem.node.devtoolsNodePath === lcpElement.node.devtoolsNodePath;
65
- }) : undefined;
66
-
67
-
68
- if (!lcpElementImage ||
69
- !this.isImageInViewport(lcpElementImage, artifacts.ViewportDimensions)) {
70
- return {
71
- score: null,
72
- notApplicable: true,
73
- metricSavings: {LCP: 0},
74
- };
75
- }
76
-
77
- /** @type {LH.Audit.Details.Table['headings']} */
78
- const headings = [
79
- {key: 'node', valueType: 'node', label: str_(i18n.UIStrings.columnElement)},
80
- ];
81
-
82
- const details = Audit.makeTableDetails(headings, [
83
- {
84
- node: Audit.makeNodeItem(lcpElementImage.node),
85
- },
86
- ]);
87
-
88
- const wasLazyLoaded = lcpElementImage.loading === 'lazy';
89
-
90
- const metricComputationData = Audit.makeMetricComputationDataInput(artifacts, context);
91
- const {timing: metricLcp} =
92
- await LargestContentfulPaint.request(metricComputationData, context);
93
- const lcpBreakdown = await LCPBreakdown.request(metricComputationData, context);
94
- let lcpSavings = 0;
95
- if (wasLazyLoaded && lcpBreakdown.loadStart !== undefined) {
96
- // Estimate the LCP savings using a statistical percentage.
97
- // https://web.dev/articles/lcp-lazy-loading#causal_performance
98
- //
99
- // LCP savings will be at most the LCP load delay.
100
- const lcpLoadDelay = lcpBreakdown.loadStart - lcpBreakdown.ttfb;
101
- lcpSavings = Math.min(metricLcp * ESTIMATED_PERCENT_SAVINGS, lcpLoadDelay);
102
- }
103
-
104
- return {
105
- score: wasLazyLoaded ? 0 : 1,
106
- metricSavings: {
107
- LCP: lcpSavings,
108
- },
109
- details,
110
- };
111
- }
112
- }
113
-
114
- export default LargestContentfulPaintLazyLoaded;
115
- export {UIStrings};
@@ -1,74 +0,0 @@
1
- export default PrioritizeLcpImage;
2
- export type InitiatorType = LH.Crdp.Network.Initiator["type"] | "redirect" | "fallbackToMain";
3
- export type InitiatorPath = Array<{
4
- url: string;
5
- initiatorType: InitiatorType;
6
- }>;
7
- /**
8
- * @typedef {LH.Crdp.Network.Initiator['type']|'redirect'|'fallbackToMain'} InitiatorType
9
- * @typedef {Array<{url: string, initiatorType: InitiatorType}>} InitiatorPath
10
- */
11
- declare class PrioritizeLcpImage extends Audit {
12
- /**
13
- *
14
- * @param {LH.Artifacts.NetworkRequest} request
15
- * @param {LH.Artifacts.NetworkRequest} mainResource
16
- * @param {InitiatorPath} initiatorPath
17
- * @return {boolean}
18
- */
19
- static shouldPreloadRequest(request: LH.Artifacts.NetworkRequest, mainResource: LH.Artifacts.NetworkRequest, initiatorPath: InitiatorPath): boolean;
20
- /**
21
- * @param {LH.Gatherer.Simulation.GraphNode} graph
22
- * @param {NetworkRequest} lcpRecord
23
- * @return {LH.Gatherer.Simulation.GraphNetworkNode|undefined}
24
- */
25
- static findLCPNode(graph: LH.Gatherer.Simulation.GraphNode, lcpRecord: NetworkRequest): LH.Gatherer.Simulation.GraphNetworkNode | undefined;
26
- /**
27
- * Get the initiator path starting with lcpRecord back to mainResource, inclusive.
28
- * Navigation redirects *to* the mainResource are not included.
29
- * Path returned will always be at least [lcpRecord, mainResource].
30
- * @param {NetworkRequest} lcpRecord
31
- * @param {NetworkRequest} mainResource
32
- * @return {InitiatorPath}
33
- */
34
- static getLcpInitiatorPath(lcpRecord: NetworkRequest, mainResource: NetworkRequest): InitiatorPath;
35
- /**
36
- * @param {LH.Artifacts.NetworkRequest} mainResource
37
- * @param {LH.Gatherer.Simulation.GraphNode} graph
38
- * @param {NetworkRequest|undefined} lcpRecord
39
- * @return {{lcpNodeToPreload?: LH.Gatherer.Simulation.GraphNetworkNode, initiatorPath?: InitiatorPath}}
40
- */
41
- static getLCPNodeToPreload(mainResource: LH.Artifacts.NetworkRequest, graph: LH.Gatherer.Simulation.GraphNode, lcpRecord: NetworkRequest | undefined): {
42
- lcpNodeToPreload?: LH.Gatherer.Simulation.GraphNetworkNode;
43
- initiatorPath?: InitiatorPath;
44
- };
45
- /**
46
- * Computes the estimated effect of preloading the LCP image.
47
- * @param {LH.Artifacts.TraceElement} lcpElement
48
- * @param {LH.Gatherer.Simulation.GraphNetworkNode|undefined} lcpNode
49
- * @param {LH.Gatherer.Simulation.GraphNode} graph
50
- * @param {LH.Gatherer.Simulation.Simulator} simulator
51
- * @return {{wastedMs: number, results: Array<{node: LH.Audit.Details.NodeValue, url: string, wastedMs: number}>}}
52
- */
53
- static computeWasteWithGraph(lcpElement: LH.Artifacts.TraceElement, lcpNode: LH.Gatherer.Simulation.GraphNetworkNode | undefined, graph: LH.Gatherer.Simulation.GraphNode, simulator: LH.Gatherer.Simulation.Simulator): {
54
- wastedMs: number;
55
- results: Array<{
56
- node: LH.Audit.Details.NodeValue;
57
- url: string;
58
- wastedMs: number;
59
- }>;
60
- };
61
- /**
62
- * @param {LH.Artifacts} artifacts
63
- * @param {LH.Audit.Context} context
64
- * @return {Promise<LH.Audit.Product>}
65
- */
66
- static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
67
- }
68
- export namespace UIStrings {
69
- let title: string;
70
- let description: string;
71
- }
72
- import { Audit } from './audit.js';
73
- import { NetworkRequest } from '../lib/network-request.js';
74
- //# sourceMappingURL=prioritize-lcp-image.d.ts.map