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.
- package/cli/test/smokehouse/config/exclusions.js +0 -2
- package/core/audits/audit.js +0 -1
- package/core/audits/insights/cls-culprits-insight.js +1 -1
- package/core/audits/insights/dom-size-insight.js +6 -6
- package/core/audits/redirects.js +1 -0
- package/core/audits/server-response-time.d.ts +0 -5
- package/core/audits/server-response-time.js +12 -26
- package/core/computed/metrics/lcp-breakdown.js +1 -0
- package/core/config/default-config.js +20 -63
- package/core/config/experimental-config.js +1 -26
- package/core/config/filters.js +6 -9
- package/core/config/lr-desktop-config.js +0 -1
- package/core/config/lr-mobile-config.js +0 -1
- package/core/gather/gatherers/trace-elements.js +1 -0
- package/core/lib/proto-preprocessor.js +5 -22
- package/dist/report/bundle.esm.js +10 -49
- package/dist/report/flow.js +12 -51
- package/dist/report/standalone.js +11 -50
- package/flow-report/src/i18n/i18n.d.ts +4 -6
- package/package.json +3 -3
- package/report/assets/styles.css +0 -39
- package/report/renderer/api.js +0 -1
- package/report/renderer/category-renderer.js +6 -0
- package/report/renderer/components.js +1 -1
- package/report/renderer/dom.d.ts +0 -13
- package/report/renderer/dom.js +0 -38
- package/report/renderer/performance-category-renderer.d.ts +0 -26
- package/report/renderer/performance-category-renderer.js +10 -142
- package/report/renderer/report-ui-features.d.ts +0 -1
- package/report/renderer/report-ui-features.js +3 -13
- package/report/renderer/report-utils.d.ts +2 -3
- package/report/renderer/report-utils.js +4 -6
- package/report/types/report-renderer.d.ts +0 -6
- package/shared/localization/locales/ar-XB.json +0 -330
- package/shared/localization/locales/ar.json +0 -330
- package/shared/localization/locales/bg.json +0 -330
- package/shared/localization/locales/ca.json +0 -330
- package/shared/localization/locales/cs.json +0 -330
- package/shared/localization/locales/da.json +0 -330
- package/shared/localization/locales/de.json +0 -330
- package/shared/localization/locales/el.json +0 -330
- package/shared/localization/locales/en-GB.json +0 -330
- package/shared/localization/locales/en-US.json +26 -275
- package/shared/localization/locales/en-XA.json +0 -330
- package/shared/localization/locales/en-XL.json +26 -275
- package/shared/localization/locales/es-419.json +0 -330
- package/shared/localization/locales/es.json +0 -330
- package/shared/localization/locales/fi.json +0 -330
- package/shared/localization/locales/fil.json +0 -330
- package/shared/localization/locales/fr.json +0 -330
- package/shared/localization/locales/he.json +0 -330
- package/shared/localization/locales/hi.json +0 -330
- package/shared/localization/locales/hr.json +0 -330
- package/shared/localization/locales/hu.json +0 -330
- package/shared/localization/locales/id.json +0 -330
- package/shared/localization/locales/it.json +0 -330
- package/shared/localization/locales/ja.json +0 -330
- package/shared/localization/locales/ko.json +0 -330
- package/shared/localization/locales/lt.json +0 -330
- package/shared/localization/locales/lv.json +0 -330
- package/shared/localization/locales/nl.json +0 -330
- package/shared/localization/locales/no.json +0 -330
- package/shared/localization/locales/pl.json +0 -330
- package/shared/localization/locales/pt-PT.json +0 -330
- package/shared/localization/locales/pt.json +0 -330
- package/shared/localization/locales/ro.json +0 -330
- package/shared/localization/locales/ru.json +0 -330
- package/shared/localization/locales/sk.json +0 -330
- package/shared/localization/locales/sl.json +0 -330
- package/shared/localization/locales/sr-Latn.json +0 -330
- package/shared/localization/locales/sr.json +0 -330
- package/shared/localization/locales/sv.json +0 -330
- package/shared/localization/locales/ta.json +0 -330
- package/shared/localization/locales/te.json +0 -330
- package/shared/localization/locales/th.json +0 -330
- package/shared/localization/locales/tr.json +0 -330
- package/shared/localization/locales/uk.json +0 -330
- package/shared/localization/locales/vi.json +0 -330
- package/shared/localization/locales/zh-HK.json +0 -330
- package/shared/localization/locales/zh-TW.json +0 -330
- package/shared/localization/locales/zh.json +0 -330
- package/types/artifacts.d.ts +1 -0
- package/types/audit.d.ts +1 -1
- package/types/lhr/settings.d.ts +1 -1
- package/core/audits/byte-efficiency/duplicated-javascript.d.ts +0 -45
- package/core/audits/byte-efficiency/duplicated-javascript.js +0 -223
- package/core/audits/byte-efficiency/efficient-animated-content.d.ts +0 -22
- package/core/audits/byte-efficiency/efficient-animated-content.js +0 -93
- package/core/audits/byte-efficiency/legacy-javascript.d.ts +0 -28
- package/core/audits/byte-efficiency/legacy-javascript.js +0 -144
- package/core/audits/byte-efficiency/modern-image-formats.d.ts +0 -38
- package/core/audits/byte-efficiency/modern-image-formats.js +0 -187
- package/core/audits/byte-efficiency/render-blocking-resources.d.ts +0 -53
- package/core/audits/byte-efficiency/render-blocking-resources.js +0 -312
- package/core/audits/byte-efficiency/uses-long-cache-ttl.d.ts +0 -59
- package/core/audits/byte-efficiency/uses-long-cache-ttl.js +0 -293
- package/core/audits/byte-efficiency/uses-optimized-images.d.ts +0 -33
- package/core/audits/byte-efficiency/uses-optimized-images.js +0 -146
- package/core/audits/byte-efficiency/uses-responsive-images-snapshot.d.ts +0 -16
- package/core/audits/byte-efficiency/uses-responsive-images-snapshot.js +0 -106
- package/core/audits/byte-efficiency/uses-responsive-images.d.ts +0 -44
- package/core/audits/byte-efficiency/uses-responsive-images.js +0 -202
- package/core/audits/byte-efficiency/uses-text-compression.d.ts +0 -14
- package/core/audits/byte-efficiency/uses-text-compression.js +0 -108
- package/core/audits/critical-request-chains.d.ts +0 -44
- package/core/audits/critical-request-chains.js +0 -221
- package/core/audits/dobetterweb/dom-size.d.ts +0 -32
- package/core/audits/dobetterweb/dom-size.js +0 -182
- package/core/audits/dobetterweb/uses-http2.d.ts +0 -72
- package/core/audits/dobetterweb/uses-http2.js +0 -276
- package/core/audits/font-display.d.ts +0 -32
- package/core/audits/font-display.js +0 -195
- package/core/audits/largest-contentful-paint-element.d.ts +0 -34
- package/core/audits/largest-contentful-paint-element.js +0 -181
- package/core/audits/lcp-lazy-loaded.d.ts +0 -22
- package/core/audits/lcp-lazy-loaded.js +0 -115
- package/core/audits/prioritize-lcp-image.d.ts +0 -74
- package/core/audits/prioritize-lcp-image.js +0 -297
- package/core/audits/third-party-summary.d.ts +0 -78
- package/core/audits/third-party-summary.js +0 -236
- package/core/audits/uses-rel-preconnect.d.ts +0 -37
- package/core/audits/uses-rel-preconnect.js +0 -286
- package/core/audits/viewport.d.ts +0 -17
- package/core/audits/viewport.js +0 -87
- package/core/audits/work-during-interaction.d.ts +0 -81
- package/core/audits/work-during-interaction.js +0 -287
|
@@ -1,202 +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 the images used on the page are larger than
|
|
8
|
-
* their display sizes. The audit will list all images that are larger than
|
|
9
|
-
* their display size with DPR (a 1000px wide image displayed as a
|
|
10
|
-
* 500px high-res image on a Retina display is 100% used);
|
|
11
|
-
* However, the audit will only fail pages that use images that have waste
|
|
12
|
-
* beyond a particular byte threshold.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
import {ByteEfficiencyAudit} from './byte-efficiency-audit.js';
|
|
17
|
-
import {NetworkRequest} from '../../lib/network-request.js';
|
|
18
|
-
import {ImageRecords} from '../../computed/image-records.js';
|
|
19
|
-
import UrlUtils from '../../lib/url-utils.js';
|
|
20
|
-
import * as i18n from '../../lib/i18n/i18n.js';
|
|
21
|
-
|
|
22
|
-
const UIStrings = {
|
|
23
|
-
/** Imperative title of a Lighthouse audit that tells the user to resize images to match the display dimensions. This is displayed in a list of audit titles that Lighthouse generates. */
|
|
24
|
-
title: 'Properly size images',
|
|
25
|
-
/** Description of a Lighthouse audit that tells the user *why* they need to serve appropriately sized images. 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. */
|
|
26
|
-
description:
|
|
27
|
-
'Serve images that are appropriately-sized to save cellular data ' +
|
|
28
|
-
'and improve load time. ' +
|
|
29
|
-
'[Learn how to size images](https://developer.chrome.com/docs/lighthouse/performance/uses-responsive-images/).',
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
33
|
-
|
|
34
|
-
const IGNORE_THRESHOLD_IN_BYTES = 4096;
|
|
35
|
-
|
|
36
|
-
// Ignore up to 12KB of waste if an effort was made with breakpoints.
|
|
37
|
-
const IGNORE_THRESHOLD_IN_BYTES_BREAKPOINTS_PRESENT = 12288;
|
|
38
|
-
|
|
39
|
-
class UsesResponsiveImages extends ByteEfficiencyAudit {
|
|
40
|
-
/**
|
|
41
|
-
* @return {LH.Audit.Meta}
|
|
42
|
-
*/
|
|
43
|
-
static get meta() {
|
|
44
|
-
return {
|
|
45
|
-
id: 'uses-responsive-images',
|
|
46
|
-
title: str_(UIStrings.title),
|
|
47
|
-
description: str_(UIStrings.description),
|
|
48
|
-
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
|
|
49
|
-
guidanceLevel: 2,
|
|
50
|
-
requiredArtifacts: ['ImageElements', 'ViewportDimensions', 'GatherContext',
|
|
51
|
-
'DevtoolsLog', 'Trace', 'URL', 'SourceMaps'],
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* @param {LH.Artifacts.ImageElement & {naturalWidth: number, naturalHeight: number}} image
|
|
57
|
-
* @param {LH.Artifacts.ViewportDimensions} ViewportDimensions
|
|
58
|
-
* @return {{width: number, height: number}};
|
|
59
|
-
*/
|
|
60
|
-
static getDisplayedDimensions(image, ViewportDimensions) {
|
|
61
|
-
if (image.displayedWidth && image.displayedHeight) {
|
|
62
|
-
return {
|
|
63
|
-
width: image.displayedWidth * ViewportDimensions.devicePixelRatio,
|
|
64
|
-
height: image.displayedHeight * ViewportDimensions.devicePixelRatio,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// If the image has 0 dimensions, it's probably hidden/offscreen, so we'll be as forgiving as possible
|
|
69
|
-
// and assume it's the size of two viewports. See https://github.com/GoogleChrome/lighthouse/issues/7236
|
|
70
|
-
const viewportWidth = ViewportDimensions.innerWidth;
|
|
71
|
-
const viewportHeight = ViewportDimensions.innerHeight * 2;
|
|
72
|
-
const imageAspectRatio = image.naturalWidth / image.naturalHeight;
|
|
73
|
-
const viewportAspectRatio = viewportWidth / viewportHeight;
|
|
74
|
-
let usedViewportWidth = viewportWidth;
|
|
75
|
-
let usedViewportHeight = viewportHeight;
|
|
76
|
-
if (imageAspectRatio > viewportAspectRatio) {
|
|
77
|
-
usedViewportHeight = viewportWidth / imageAspectRatio;
|
|
78
|
-
} else {
|
|
79
|
-
usedViewportWidth = viewportHeight * imageAspectRatio;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return {
|
|
83
|
-
width: usedViewportWidth * ViewportDimensions.devicePixelRatio,
|
|
84
|
-
height: usedViewportHeight * ViewportDimensions.devicePixelRatio,
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* @param {LH.Artifacts.ImageElement & {naturalWidth: number, naturalHeight: number}} image
|
|
90
|
-
* @param {LH.Artifacts.ViewportDimensions} ViewportDimensions
|
|
91
|
-
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
92
|
-
* @return {null|LH.Audit.ByteEfficiencyItem};
|
|
93
|
-
*/
|
|
94
|
-
static computeWaste(image, ViewportDimensions, networkRecords) {
|
|
95
|
-
const networkRecord = networkRecords.find(record => record.url === image.src);
|
|
96
|
-
// Nothing can be done without network info, ignore images without resource size information.
|
|
97
|
-
if (!networkRecord) {
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const displayed = this.getDisplayedDimensions(image, ViewportDimensions);
|
|
102
|
-
const usedPixels = displayed.width * displayed.height;
|
|
103
|
-
|
|
104
|
-
const url = UrlUtils.elideDataURI(image.src);
|
|
105
|
-
const actualPixels = image.naturalWidth * image.naturalHeight;
|
|
106
|
-
const wastedRatio = 1 - (usedPixels / actualPixels);
|
|
107
|
-
const totalBytes = NetworkRequest.getResourceSizeOnNetwork(networkRecord);
|
|
108
|
-
const wastedBytes = Math.round(totalBytes * wastedRatio);
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
node: ByteEfficiencyAudit.makeNodeItem(image.node),
|
|
112
|
-
url,
|
|
113
|
-
totalBytes,
|
|
114
|
-
wastedBytes,
|
|
115
|
-
wastedPercent: 100 * wastedRatio,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* @param {LH.Artifacts.ImageElement} image
|
|
122
|
-
* @return {number};
|
|
123
|
-
*/
|
|
124
|
-
static determineAllowableWaste(image) {
|
|
125
|
-
if (image.srcset || image.isPicture) {
|
|
126
|
-
return IGNORE_THRESHOLD_IN_BYTES_BREAKPOINTS_PRESENT;
|
|
127
|
-
}
|
|
128
|
-
return IGNORE_THRESHOLD_IN_BYTES;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* @param {LH.Artifacts} artifacts
|
|
133
|
-
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
134
|
-
* @param {LH.Audit.Context} context
|
|
135
|
-
* @return {Promise<import('./byte-efficiency-audit.js').ByteEfficiencyProduct>}
|
|
136
|
-
*/
|
|
137
|
-
static async audit_(artifacts, networkRecords, context) {
|
|
138
|
-
const images = await ImageRecords.request({
|
|
139
|
-
ImageElements: artifacts.ImageElements,
|
|
140
|
-
networkRecords,
|
|
141
|
-
}, context);
|
|
142
|
-
const ViewportDimensions = artifacts.ViewportDimensions;
|
|
143
|
-
/** @type {Map<string, LH.Audit.ByteEfficiencyItem>} */
|
|
144
|
-
const resultsMap = new Map();
|
|
145
|
-
/** @type {Array<string>} */
|
|
146
|
-
const passedImageList = [];
|
|
147
|
-
for (const image of images) {
|
|
148
|
-
// Give SVG a free pass because creating a "responsive" SVG is of questionable value.
|
|
149
|
-
// Ignore CSS images because it's difficult to determine what is a spritesheet,
|
|
150
|
-
// and the reward-to-effort ratio for responsive CSS images is quite low https://css-tricks.com/responsive-images-css/.
|
|
151
|
-
if (image.mimeType === 'image/svg+xml' || image.isCss) {
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Skip if we couldn't collect natural image size information.
|
|
156
|
-
if (!image.naturalDimensions) continue;
|
|
157
|
-
const naturalHeight = image.naturalDimensions.height;
|
|
158
|
-
const naturalWidth = image.naturalDimensions.width;
|
|
159
|
-
// If naturalHeight or naturalWidth are falsy, information is not valid, skip.
|
|
160
|
-
if (!naturalWidth || !naturalHeight) continue;
|
|
161
|
-
const processed =
|
|
162
|
-
UsesResponsiveImages.computeWaste(
|
|
163
|
-
{...image, naturalHeight, naturalWidth},
|
|
164
|
-
ViewportDimensions, networkRecords
|
|
165
|
-
);
|
|
166
|
-
if (!processed) continue;
|
|
167
|
-
|
|
168
|
-
// Verify the image wastes more than the minimum.
|
|
169
|
-
const exceedsAllowableWaste = processed.wastedBytes > this.determineAllowableWaste(image);
|
|
170
|
-
|
|
171
|
-
const existing = resultsMap.get(processed.url);
|
|
172
|
-
// Don't warn about an image that was later used appropriately, or wastes a trivial amount of data.
|
|
173
|
-
if (exceedsAllowableWaste && !passedImageList.includes(processed.url)) {
|
|
174
|
-
if ((!existing || existing.wastedBytes > processed.wastedBytes)) {
|
|
175
|
-
resultsMap.set(processed.url, processed);
|
|
176
|
-
}
|
|
177
|
-
} else {
|
|
178
|
-
// Ensure this url passes for future tests.
|
|
179
|
-
resultsMap.delete(processed.url);
|
|
180
|
-
passedImageList.push(processed.url);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const items = Array.from(resultsMap.values());
|
|
185
|
-
|
|
186
|
-
/** @type {LH.Audit.Details.Opportunity['headings']} */
|
|
187
|
-
const headings = [
|
|
188
|
-
{key: 'node', valueType: 'node', label: ''},
|
|
189
|
-
{key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
|
|
190
|
-
{key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnResourceSize)},
|
|
191
|
-
{key: 'wastedBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnWastedBytes)},
|
|
192
|
-
];
|
|
193
|
-
|
|
194
|
-
return {
|
|
195
|
-
items,
|
|
196
|
-
headings,
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export default UsesResponsiveImages;
|
|
202
|
-
export {UIStrings, str_};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export default ResponsesAreCompressed;
|
|
2
|
-
declare class ResponsesAreCompressed extends ByteEfficiencyAudit {
|
|
3
|
-
/**
|
|
4
|
-
* @param {LH.Artifacts} artifacts
|
|
5
|
-
* @return {import('./byte-efficiency-audit.js').ByteEfficiencyProduct}
|
|
6
|
-
*/
|
|
7
|
-
static audit_(artifacts: LH.Artifacts): import("./byte-efficiency-audit.js").ByteEfficiencyProduct;
|
|
8
|
-
}
|
|
9
|
-
export namespace UIStrings {
|
|
10
|
-
let title: string;
|
|
11
|
-
let description: string;
|
|
12
|
-
}
|
|
13
|
-
import { ByteEfficiencyAudit } from './byte-efficiency-audit.js';
|
|
14
|
-
//# sourceMappingURL=uses-text-compression.d.ts.map
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2017 Google LLC
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
/*
|
|
7
|
-
* @fileoverview Audit a page to ensure that resources loaded with
|
|
8
|
-
* gzip/br/deflate compression.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import {ByteEfficiencyAudit} from './byte-efficiency-audit.js';
|
|
13
|
-
import UrlUtils from '../../lib/url-utils.js';
|
|
14
|
-
import * as i18n from '../../lib/i18n/i18n.js';
|
|
15
|
-
|
|
16
|
-
const UIStrings = {
|
|
17
|
-
/** Imperative title of a Lighthouse audit that tells the user to enable text compression (like gzip) in order to enhance the performance of a page. This is displayed in a list of audit titles that Lighthouse generates. */
|
|
18
|
-
title: 'Enable text compression',
|
|
19
|
-
/** Description of a Lighthouse audit that tells the user *why* their text-based resources should be served with compression (like gzip). 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. */
|
|
20
|
-
description: 'Text-based resources should be served with compression (gzip, deflate or' +
|
|
21
|
-
' brotli) to minimize total network bytes.' +
|
|
22
|
-
' [Learn more about text compression](https://developer.chrome.com/docs/lighthouse/performance/uses-text-compression/).',
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
26
|
-
|
|
27
|
-
const IGNORE_THRESHOLD_IN_BYTES = 1400;
|
|
28
|
-
const IGNORE_THRESHOLD_IN_PERCENT = 0.1;
|
|
29
|
-
|
|
30
|
-
class ResponsesAreCompressed extends ByteEfficiencyAudit {
|
|
31
|
-
/**
|
|
32
|
-
* @return {LH.Audit.Meta}
|
|
33
|
-
*/
|
|
34
|
-
static get meta() {
|
|
35
|
-
return {
|
|
36
|
-
id: 'uses-text-compression',
|
|
37
|
-
title: str_(UIStrings.title),
|
|
38
|
-
description: str_(UIStrings.description),
|
|
39
|
-
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
|
|
40
|
-
guidanceLevel: 3,
|
|
41
|
-
requiredArtifacts: ['ResponseCompression', 'GatherContext', 'DevtoolsLog', 'Trace', 'URL',
|
|
42
|
-
'SourceMaps'],
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* @param {LH.Artifacts} artifacts
|
|
48
|
-
* @return {import('./byte-efficiency-audit.js').ByteEfficiencyProduct}
|
|
49
|
-
*/
|
|
50
|
-
static audit_(artifacts) {
|
|
51
|
-
const uncompressedResponses = artifacts.ResponseCompression;
|
|
52
|
-
|
|
53
|
-
/** @type {Array<LH.Audit.ByteEfficiencyItem>} */
|
|
54
|
-
const items = [];
|
|
55
|
-
uncompressedResponses.forEach(record => {
|
|
56
|
-
// Ignore invalid GZIP size values (undefined, NaN, 0, -n, etc)
|
|
57
|
-
if (!record.gzipSize || record.gzipSize < 0) return;
|
|
58
|
-
|
|
59
|
-
const originalSize = record.resourceSize;
|
|
60
|
-
const gzipSize = record.gzipSize;
|
|
61
|
-
const gzipSavings = originalSize - gzipSize;
|
|
62
|
-
|
|
63
|
-
// Not every resource is smaller when compressed.
|
|
64
|
-
if (record.transferSize < gzipSize) {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// If savings is small, let's be generous and not surface the minor savings.
|
|
69
|
-
if (gzipSavings < IGNORE_THRESHOLD_IN_BYTES) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Require at least 20kb of savings ... or some percentage of total resource size.
|
|
74
|
-
if (gzipSavings < 20_000 && 1 - gzipSize / originalSize < IGNORE_THRESHOLD_IN_PERCENT) {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// remove duplicates
|
|
79
|
-
const url = UrlUtils.elideDataURI(record.url);
|
|
80
|
-
const isDuplicate = items.find(item => item.url === url &&
|
|
81
|
-
item.totalBytes === record.resourceSize);
|
|
82
|
-
if (isDuplicate) {
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
items.push({
|
|
87
|
-
url,
|
|
88
|
-
totalBytes: originalSize,
|
|
89
|
-
wastedBytes: gzipSavings,
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
/** @type {LH.Audit.Details.Opportunity['headings']} */
|
|
94
|
-
const headings = [
|
|
95
|
-
{key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
|
|
96
|
-
{key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnTransferSize)},
|
|
97
|
-
{key: 'wastedBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnWastedBytes)},
|
|
98
|
-
];
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
items,
|
|
102
|
-
headings,
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export default ResponsesAreCompressed;
|
|
108
|
-
export {UIStrings};
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
export default CriticalRequestChains;
|
|
2
|
-
declare class CriticalRequestChains extends Audit {
|
|
3
|
-
/** @typedef {{depth: number, id: string, chainDuration: number, chainTransferSize: number, node: LH.Artifacts.CriticalRequestNode[string]}} CrcNodeInfo */
|
|
4
|
-
/**
|
|
5
|
-
* @param {LH.Artifacts.CriticalRequestNode} tree
|
|
6
|
-
* @param {function(CrcNodeInfo): void} cb
|
|
7
|
-
*/
|
|
8
|
-
static _traverse(tree: LH.Artifacts.CriticalRequestNode, cb: (arg0: {
|
|
9
|
-
depth: number;
|
|
10
|
-
id: string;
|
|
11
|
-
chainDuration: number;
|
|
12
|
-
chainTransferSize: number;
|
|
13
|
-
node: LH.Artifacts.CriticalRequestNode[string];
|
|
14
|
-
}) => void): void;
|
|
15
|
-
/**
|
|
16
|
-
* Get stats about the longest initiator chain (as determined by time duration)
|
|
17
|
-
* @param {LH.Artifacts.CriticalRequestNode} tree
|
|
18
|
-
* @return {{duration: number, length: number, transferSize: number}}
|
|
19
|
-
*/
|
|
20
|
-
static _getLongestChain(tree: LH.Artifacts.CriticalRequestNode): {
|
|
21
|
-
duration: number;
|
|
22
|
-
length: number;
|
|
23
|
-
transferSize: number;
|
|
24
|
-
};
|
|
25
|
-
/**
|
|
26
|
-
* @param {LH.Artifacts.CriticalRequestNode} tree
|
|
27
|
-
* @return {LH.Audit.Details.SimpleCriticalRequestNode}
|
|
28
|
-
*/
|
|
29
|
-
static flattenRequests(tree: LH.Artifacts.CriticalRequestNode): LH.Audit.Details.SimpleCriticalRequestNode;
|
|
30
|
-
/**
|
|
31
|
-
* Audits the page to give a score for First Meaningful Paint.
|
|
32
|
-
* @param {LH.Artifacts} artifacts The artifacts from the gather phase.
|
|
33
|
-
* @param {LH.Audit.Context} context
|
|
34
|
-
* @return {Promise<LH.Audit.Product>}
|
|
35
|
-
*/
|
|
36
|
-
static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
|
|
37
|
-
}
|
|
38
|
-
export namespace UIStrings {
|
|
39
|
-
let title: string;
|
|
40
|
-
let description: string;
|
|
41
|
-
let displayValue: string;
|
|
42
|
-
}
|
|
43
|
-
import { Audit } from './audit.js';
|
|
44
|
-
//# sourceMappingURL=critical-request-chains.d.ts.map
|
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2016 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 {CriticalRequestChains as ComputedChains} from '../computed/critical-request-chains.js';
|
|
10
|
-
|
|
11
|
-
const UIStrings = {
|
|
12
|
-
/** Imperative title of a Lighthouse audit that tells the user to reduce the depth of critical network requests to enhance initial load of a page. Critical request chains are series of dependent network requests that are important for page rendering. For example, here's a 4-request-deep chain: The biglogo.jpg image is required, but is requested via the styles.css style code, which is requested by the initialize.js javascript, which is requested by the page's HTML. This is displayed in a list of audit titles that Lighthouse generates. */
|
|
13
|
-
title: 'Avoid chaining critical requests',
|
|
14
|
-
/** Description of a Lighthouse audit that tells the user *why* they should reduce the depth of critical network requests to enhance initial load of a page . 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. */
|
|
15
|
-
description: 'The Critical Request Chains below show you what resources are ' +
|
|
16
|
-
'loaded with a high priority. Consider reducing ' +
|
|
17
|
-
'the length of chains, reducing the download size of resources, or ' +
|
|
18
|
-
'deferring the download of unnecessary resources to improve page load. ' +
|
|
19
|
-
'[Learn how to avoid chaining critical requests](https://developer.chrome.com/docs/lighthouse/performance/critical-request-chains/).',
|
|
20
|
-
/** [ICU Syntax] Label for an audit identifying the number of sequences of dependent network requests used to load the page. */
|
|
21
|
-
displayValue: `{itemCount, plural,
|
|
22
|
-
=1 {1 chain found}
|
|
23
|
-
other {# chains found}
|
|
24
|
-
}`,
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
28
|
-
|
|
29
|
-
class CriticalRequestChains extends Audit {
|
|
30
|
-
/**
|
|
31
|
-
* @return {LH.Audit.Meta}
|
|
32
|
-
*/
|
|
33
|
-
static get meta() {
|
|
34
|
-
return {
|
|
35
|
-
id: 'critical-request-chains',
|
|
36
|
-
title: str_(UIStrings.title),
|
|
37
|
-
description: str_(UIStrings.description),
|
|
38
|
-
scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE,
|
|
39
|
-
supportedModes: ['navigation'],
|
|
40
|
-
guidanceLevel: 1,
|
|
41
|
-
requiredArtifacts: ['Trace', 'DevtoolsLog', 'URL', 'SourceMaps'],
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** @typedef {{depth: number, id: string, chainDuration: number, chainTransferSize: number, node: LH.Artifacts.CriticalRequestNode[string]}} CrcNodeInfo */
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* @param {LH.Artifacts.CriticalRequestNode} tree
|
|
49
|
-
* @param {function(CrcNodeInfo): void} cb
|
|
50
|
-
*/
|
|
51
|
-
static _traverse(tree, cb) {
|
|
52
|
-
/**
|
|
53
|
-
* @param {LH.Artifacts.CriticalRequestNode} node
|
|
54
|
-
* @param {number} depth
|
|
55
|
-
* @param {number=} networkRequestTime
|
|
56
|
-
* @param {number=} transferSize
|
|
57
|
-
*/
|
|
58
|
-
function walk(node, depth, networkRequestTime, transferSize = 0) {
|
|
59
|
-
const children = Object.keys(node);
|
|
60
|
-
if (children.length === 0) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
children.forEach(id => {
|
|
64
|
-
const child = node[id];
|
|
65
|
-
if (!networkRequestTime) {
|
|
66
|
-
networkRequestTime = child.request.networkRequestTime;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Call the callback with the info for this child.
|
|
70
|
-
cb({
|
|
71
|
-
depth,
|
|
72
|
-
id,
|
|
73
|
-
node: child,
|
|
74
|
-
chainDuration: child.request.networkEndTime - networkRequestTime,
|
|
75
|
-
chainTransferSize: transferSize + child.request.transferSize,
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// Carry on walking.
|
|
79
|
-
if (child.children) {
|
|
80
|
-
walk(child.children, depth + 1, networkRequestTime);
|
|
81
|
-
}
|
|
82
|
-
}, '');
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
walk(tree, 0);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Get stats about the longest initiator chain (as determined by time duration)
|
|
90
|
-
* @param {LH.Artifacts.CriticalRequestNode} tree
|
|
91
|
-
* @return {{duration: number, length: number, transferSize: number}}
|
|
92
|
-
*/
|
|
93
|
-
static _getLongestChain(tree) {
|
|
94
|
-
const longest = {
|
|
95
|
-
duration: 0,
|
|
96
|
-
length: 0,
|
|
97
|
-
transferSize: 0,
|
|
98
|
-
};
|
|
99
|
-
CriticalRequestChains._traverse(tree, opts => {
|
|
100
|
-
const duration = opts.chainDuration;
|
|
101
|
-
if (duration > longest.duration) {
|
|
102
|
-
longest.duration = duration;
|
|
103
|
-
longest.transferSize = opts.chainTransferSize;
|
|
104
|
-
longest.length = opts.depth;
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
// Always return the longest chain + 1 because the depth is zero indexed.
|
|
108
|
-
longest.length++;
|
|
109
|
-
return longest;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* @param {LH.Artifacts.CriticalRequestNode} tree
|
|
114
|
-
* @return {LH.Audit.Details.SimpleCriticalRequestNode}
|
|
115
|
-
*/
|
|
116
|
-
static flattenRequests(tree) {
|
|
117
|
-
/** @type {LH.Audit.Details.SimpleCriticalRequestNode} */
|
|
118
|
-
const flattendChains = {};
|
|
119
|
-
/** @type {Map<string, LH.Audit.Details.SimpleCriticalRequestNode[string]>} */
|
|
120
|
-
const chainMap = new Map();
|
|
121
|
-
|
|
122
|
-
/** @param {CrcNodeInfo} opts */
|
|
123
|
-
function flatten(opts) {
|
|
124
|
-
const request = opts.node.request;
|
|
125
|
-
const simpleRequest = {
|
|
126
|
-
url: request.url,
|
|
127
|
-
startTime: request.networkRequestTime / 1000,
|
|
128
|
-
endTime: request.networkEndTime / 1000,
|
|
129
|
-
responseReceivedTime: request.responseHeadersEndTime / 1000,
|
|
130
|
-
transferSize: request.transferSize,
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
let chain = chainMap.get(opts.id);
|
|
134
|
-
if (chain) {
|
|
135
|
-
chain.request = simpleRequest;
|
|
136
|
-
} else {
|
|
137
|
-
chain = {
|
|
138
|
-
request: simpleRequest,
|
|
139
|
-
};
|
|
140
|
-
flattendChains[opts.id] = chain;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (opts.node.children) {
|
|
144
|
-
for (const chainId of Object.keys(opts.node.children)) {
|
|
145
|
-
// Note: cast should be Partial<>, but filled in when child node is traversed.
|
|
146
|
-
const childChain = /** @type {LH.Audit.Details.SimpleCriticalRequestNode[string]} */ ({
|
|
147
|
-
request: {},
|
|
148
|
-
});
|
|
149
|
-
chainMap.set(chainId, childChain);
|
|
150
|
-
if (!chain.children) {
|
|
151
|
-
chain.children = {};
|
|
152
|
-
}
|
|
153
|
-
chain.children[chainId] = childChain;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
chainMap.set(opts.id, chain);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
CriticalRequestChains._traverse(tree, flatten);
|
|
160
|
-
|
|
161
|
-
return flattendChains;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Audits the page to give a score for First Meaningful Paint.
|
|
166
|
-
* @param {LH.Artifacts} artifacts The artifacts from the gather phase.
|
|
167
|
-
* @param {LH.Audit.Context} context
|
|
168
|
-
* @return {Promise<LH.Audit.Product>}
|
|
169
|
-
*/
|
|
170
|
-
static async audit(artifacts, context) {
|
|
171
|
-
const settings = context.settings;
|
|
172
|
-
const trace = artifacts.Trace;
|
|
173
|
-
const devtoolsLog = artifacts.DevtoolsLog;
|
|
174
|
-
const {URL, SourceMaps} = artifacts;
|
|
175
|
-
const chains =
|
|
176
|
-
await ComputedChains.request({settings, devtoolsLog, trace, URL, SourceMaps}, context);
|
|
177
|
-
let chainCount = 0;
|
|
178
|
-
/**
|
|
179
|
-
* @param {LH.Audit.Details.SimpleCriticalRequestNode} node
|
|
180
|
-
* @param {number} depth
|
|
181
|
-
*/
|
|
182
|
-
function walk(node, depth) {
|
|
183
|
-
const childIds = Object.keys(node);
|
|
184
|
-
|
|
185
|
-
childIds.forEach(id => {
|
|
186
|
-
const child = node[id];
|
|
187
|
-
if (child.children) {
|
|
188
|
-
walk(child.children, depth + 1);
|
|
189
|
-
} else {
|
|
190
|
-
// if the node doesn't have a children field, then it is a leaf, so +1
|
|
191
|
-
chainCount++;
|
|
192
|
-
}
|
|
193
|
-
}, '');
|
|
194
|
-
}
|
|
195
|
-
// Convert
|
|
196
|
-
const flattenedChains = CriticalRequestChains.flattenRequests(chains);
|
|
197
|
-
|
|
198
|
-
// Account for initial navigation
|
|
199
|
-
const initialNavKey = Object.keys(flattenedChains)[0];
|
|
200
|
-
const initialNavChildren = initialNavKey && flattenedChains[initialNavKey].children;
|
|
201
|
-
if (initialNavChildren && Object.keys(initialNavChildren).length > 0) {
|
|
202
|
-
walk(initialNavChildren, 0);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const longestChain = CriticalRequestChains._getLongestChain(chains);
|
|
206
|
-
|
|
207
|
-
return {
|
|
208
|
-
score: Number(chainCount === 0),
|
|
209
|
-
notApplicable: chainCount === 0,
|
|
210
|
-
displayValue: chainCount ? str_(UIStrings.displayValue, {itemCount: chainCount}) : '',
|
|
211
|
-
details: {
|
|
212
|
-
type: 'criticalrequestchain',
|
|
213
|
-
chains: flattenedChains,
|
|
214
|
-
longestChain,
|
|
215
|
-
},
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
export default CriticalRequestChains;
|
|
221
|
-
export {UIStrings};
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
export default DOMSize;
|
|
2
|
-
declare class DOMSize extends Audit {
|
|
3
|
-
/**
|
|
4
|
-
* @return {LH.Audit.ScoreOptions}
|
|
5
|
-
*/
|
|
6
|
-
static get defaultOptions(): LH.Audit.ScoreOptions;
|
|
7
|
-
/**
|
|
8
|
-
* @param {LH.Artifacts} artifacts
|
|
9
|
-
* @param {LH.Audit.Context} context
|
|
10
|
-
* @return {Promise<number|undefined>}
|
|
11
|
-
*/
|
|
12
|
-
static computeTbtImpact(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<number | undefined>;
|
|
13
|
-
/**
|
|
14
|
-
* @param {LH.Artifacts} artifacts
|
|
15
|
-
* @param {LH.Audit.Context} context
|
|
16
|
-
* @return {Promise<LH.Audit.Product>}
|
|
17
|
-
*/
|
|
18
|
-
static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
|
|
19
|
-
}
|
|
20
|
-
export namespace UIStrings {
|
|
21
|
-
let title: string;
|
|
22
|
-
let failureTitle: string;
|
|
23
|
-
let description: string;
|
|
24
|
-
let columnStatistic: string;
|
|
25
|
-
let columnValue: string;
|
|
26
|
-
let displayValue: string;
|
|
27
|
-
let statisticDOMElements: string;
|
|
28
|
-
let statisticDOMDepth: string;
|
|
29
|
-
let statisticDOMWidth: string;
|
|
30
|
-
}
|
|
31
|
-
import { Audit } from '../audit.js';
|
|
32
|
-
//# sourceMappingURL=dom-size.d.ts.map
|