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.
- package/cli/cli-flags.js +1 -1
- package/cli/test/smokehouse/config/exclusions.js +0 -2
- package/cli/test/smokehouse/version-check.d.ts +1 -1
- package/core/audits/accessibility/accesskeys.js +3 -3
- package/core/audits/accessibility/aria-allowed-attr.js +3 -3
- package/core/audits/accessibility/aria-allowed-role.js +2 -1
- package/core/audits/accessibility/aria-command-name.js +1 -1
- package/core/audits/accessibility/aria-conditional-attr.js +1 -1
- package/core/audits/accessibility/aria-deprecated-role.js +1 -1
- package/core/audits/accessibility/aria-dialog-name.js +1 -1
- package/core/audits/accessibility/aria-hidden-body.js +3 -3
- package/core/audits/accessibility/aria-hidden-focus.js +3 -3
- package/core/audits/accessibility/aria-input-field-name.js +3 -3
- package/core/audits/accessibility/aria-meter-name.js +1 -1
- package/core/audits/accessibility/aria-progressbar-name.js +1 -1
- package/core/audits/accessibility/aria-prohibited-attr.js +1 -1
- package/core/audits/accessibility/aria-required-attr.js +3 -3
- package/core/audits/accessibility/aria-required-children.js +3 -3
- package/core/audits/accessibility/aria-required-parent.js +3 -3
- package/core/audits/accessibility/aria-roles.js +3 -3
- package/core/audits/accessibility/aria-text.js +3 -3
- package/core/audits/accessibility/aria-toggle-field-name.js +3 -3
- package/core/audits/accessibility/aria-tooltip-name.js +1 -1
- package/core/audits/accessibility/aria-treeitem-name.js +1 -1
- package/core/audits/accessibility/aria-valid-attr-value.js +3 -3
- package/core/audits/accessibility/aria-valid-attr.js +3 -3
- package/core/audits/accessibility/button-name.js +3 -3
- package/core/audits/accessibility/bypass.js +3 -3
- package/core/audits/accessibility/color-contrast.js +3 -3
- package/core/audits/accessibility/definition-list.js +3 -3
- package/core/audits/accessibility/dlitem.js +3 -3
- package/core/audits/accessibility/document-title.js +3 -3
- package/core/audits/accessibility/duplicate-id-aria.js +3 -3
- package/core/audits/accessibility/empty-heading.js +3 -3
- package/core/audits/accessibility/form-field-multiple-labels.js +3 -3
- package/core/audits/accessibility/frame-title.js +3 -3
- package/core/audits/accessibility/heading-order.js +3 -3
- package/core/audits/accessibility/html-has-lang.js +3 -3
- package/core/audits/accessibility/html-lang-valid.js +3 -3
- package/core/audits/accessibility/html-xml-lang-mismatch.js +3 -3
- package/core/audits/accessibility/identical-links-same-purpose.js +3 -3
- package/core/audits/accessibility/image-alt.js +3 -3
- package/core/audits/accessibility/image-redundant-alt.js +4 -3
- package/core/audits/accessibility/input-button-name.js +3 -3
- package/core/audits/accessibility/input-image-alt.js +3 -3
- package/core/audits/accessibility/label-content-name-mismatch.js +3 -3
- package/core/audits/accessibility/label.js +3 -3
- package/core/audits/accessibility/landmark-one-main.js +3 -4
- package/core/audits/accessibility/link-in-text-block.js +3 -3
- package/core/audits/accessibility/link-name.js +3 -3
- package/core/audits/accessibility/list.js +3 -3
- package/core/audits/accessibility/listitem.js +3 -3
- package/core/audits/accessibility/meta-refresh.js +3 -3
- package/core/audits/accessibility/meta-viewport.js +3 -3
- package/core/audits/accessibility/object-alt.js +3 -3
- package/core/audits/accessibility/select-name.js +3 -3
- package/core/audits/accessibility/skip-link.js +3 -3
- package/core/audits/accessibility/tabindex.js +3 -3
- package/core/audits/accessibility/table-duplicate-name.js +4 -3
- package/core/audits/accessibility/table-fake-caption.js +3 -3
- package/core/audits/accessibility/target-size.js +3 -3
- package/core/audits/accessibility/td-has-header.js +3 -3
- package/core/audits/accessibility/td-headers-attr.js +3 -3
- package/core/audits/accessibility/th-has-data-cells.js +3 -3
- package/core/audits/accessibility/valid-lang.js +3 -3
- package/core/audits/accessibility/video-caption.js +3 -3
- package/core/audits/audit.d.ts +0 -4
- package/core/audits/audit.js +2 -13
- package/core/audits/insights/cls-culprits-insight.js +1 -1
- package/core/audits/insights/dom-size-insight.js +11 -7
- package/core/audits/insights/font-display-insight.js +3 -1
- package/core/audits/insights/image-delivery-insight.js +4 -1
- package/core/audits/insights/insight-audit.d.ts +6 -4
- package/core/audits/insights/insight-audit.js +27 -8
- package/core/audits/insights/third-parties-insight.js +1 -1
- package/core/audits/layout-shifts.js +1 -1
- package/core/audits/predictive-perf.js +2 -2
- package/core/audits/seo/crawlable-anchors.js +2 -3
- package/core/audits/seo/manual/structured-data.js +1 -1
- package/core/audits/server-response-time.d.ts +0 -5
- package/core/audits/server-response-time.js +12 -26
- package/core/computed/metrics/cumulative-layout-shift.js +2 -2
- package/core/computed/metrics/lantern-metric.js +3 -3
- package/core/computed/metrics/lcp-breakdown.d.ts +10 -5
- package/core/computed/metrics/lcp-breakdown.js +50 -22
- package/core/computed/metrics/time-to-first-byte.js +33 -10
- package/core/computed/metrics/timing-summary.js +3 -2
- package/core/computed/page-dependency-graph.js +1 -1
- package/core/computed/trace-engine-result.js +2 -2
- package/core/config/default-config.js +110 -152
- package/core/config/experimental-config.js +1 -32
- 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/driver/target-manager.d.ts +1 -1
- package/core/gather/driver.d.ts +1 -1
- package/core/gather/gatherers/anchor-elements.js +8 -24
- package/core/gather/gatherers/image-elements.js +32 -6
- package/core/gather/gatherers/inspector-issues.js +1 -28
- package/core/gather/gatherers/trace-elements.d.ts +2 -11
- package/core/gather/gatherers/trace-elements.js +9 -39
- package/core/gather/navigation-runner.js +0 -3
- package/core/gather/session.d.ts +1 -1
- package/core/lib/asset-saver.d.ts +2 -2
- package/core/lib/asset-saver.js +33 -43
- package/core/lib/bf-cache-strings.js +10 -9
- package/core/lib/deprecations-strings.js +5 -5
- package/core/lib/emulation.d.ts +10 -0
- package/core/lib/emulation.js +21 -6
- package/core/lib/legacy-javascript/legacy-javascript.js +4 -11
- package/core/lib/network-request.d.ts +0 -7
- package/core/lib/network-request.js +0 -16
- package/core/lib/proto-preprocessor.js +10 -25
- package/core/runner.js +1 -8
- package/core/scoring.js +1 -1
- 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 +16 -19
- package/readme.md +2 -2
- 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/details-renderer.d.ts +1 -2
- package/report/renderer/details-renderer.js +0 -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 +2 -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 +107 -455
- package/shared/localization/locales/ar.json +107 -455
- package/shared/localization/locales/bg.json +96 -444
- package/shared/localization/locales/ca.json +96 -444
- package/shared/localization/locales/cs.json +96 -444
- package/shared/localization/locales/da.json +96 -444
- package/shared/localization/locales/de.json +96 -444
- package/shared/localization/locales/el.json +96 -444
- package/shared/localization/locales/en-GB.json +96 -444
- package/shared/localization/locales/en-US.json +116 -467
- package/shared/localization/locales/en-XA.json +93 -441
- package/shared/localization/locales/en-XL.json +116 -467
- package/shared/localization/locales/es-419.json +96 -444
- package/shared/localization/locales/es.json +96 -444
- package/shared/localization/locales/fi.json +96 -444
- package/shared/localization/locales/fil.json +96 -444
- package/shared/localization/locales/fr.json +96 -444
- package/shared/localization/locales/he.json +118 -466
- package/shared/localization/locales/hi.json +96 -444
- package/shared/localization/locales/hr.json +100 -448
- package/shared/localization/locales/hu.json +96 -444
- package/shared/localization/locales/id.json +96 -444
- package/shared/localization/locales/it.json +96 -444
- package/shared/localization/locales/ja.json +96 -444
- package/shared/localization/locales/ko.json +97 -445
- package/shared/localization/locales/lt.json +96 -444
- package/shared/localization/locales/lv.json +97 -445
- package/shared/localization/locales/nl.json +96 -444
- package/shared/localization/locales/no.json +96 -444
- package/shared/localization/locales/pl.json +96 -444
- package/shared/localization/locales/pt-PT.json +96 -444
- package/shared/localization/locales/pt.json +97 -445
- package/shared/localization/locales/ro.json +97 -445
- package/shared/localization/locales/ru.json +96 -444
- package/shared/localization/locales/sk.json +96 -444
- package/shared/localization/locales/sl.json +96 -444
- package/shared/localization/locales/sr-Latn.json +96 -444
- package/shared/localization/locales/sr.json +96 -444
- package/shared/localization/locales/sv.json +96 -444
- package/shared/localization/locales/ta.json +96 -444
- package/shared/localization/locales/te.json +97 -445
- package/shared/localization/locales/th.json +96 -444
- package/shared/localization/locales/tr.json +96 -444
- package/shared/localization/locales/uk.json +96 -444
- package/shared/localization/locales/vi.json +96 -444
- package/shared/localization/locales/zh-HK.json +96 -444
- package/shared/localization/locales/zh-TW.json +97 -445
- package/shared/localization/locales/zh.json +96 -444
- package/shared/localization/locales.d.ts +2 -0
- package/shared/localization/locales.js +130 -139
- package/shared/tsconfig.json +2 -0
- package/tsconfig-base.json +2 -2
- package/tsconfig.json +1 -4
- package/types/artifacts.d.ts +6 -81
- 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/offscreen-images.d.ts +0 -63
- package/core/audits/byte-efficiency/offscreen-images.js +0 -240
- 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/no-document-write.d.ts +0 -16
- package/core/audits/dobetterweb/no-document-write.js +0 -86
- package/core/audits/dobetterweb/uses-http2.d.ts +0 -72
- package/core/audits/dobetterweb/uses-http2.js +0 -276
- package/core/audits/dobetterweb/uses-passive-event-listeners.d.ts +0 -16
- package/core/audits/dobetterweb/uses-passive-event-listeners.js +0 -69
- 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/metrics/first-meaningful-paint.d.ts +0 -12
- package/core/audits/metrics/first-meaningful-paint.js +0 -47
- package/core/audits/preload-fonts.d.ts +0 -25
- package/core/audits/preload-fonts.js +0 -97
- package/core/audits/prioritize-lcp-image.d.ts +0 -74
- package/core/audits/prioritize-lcp-image.js +0 -297
- package/core/audits/seo/font-size.d.ts +0 -24
- package/core/audits/seo/font-size.js +0 -344
- package/core/audits/third-party-facades.d.ts +0 -41
- package/core/audits/third-party-facades.js +0 -234
- 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/uses-rel-preload.d.ts +0 -57
- package/core/audits/uses-rel-preload.js +0 -263
- 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
- package/core/computed/critical-request-chains.d.ts +0 -42
- package/core/computed/critical-request-chains.js +0 -143
- package/core/computed/viewport-meta.d.ts +0 -37
- package/core/computed/viewport-meta.js +0 -71
- package/core/gather/gatherers/cache-contents.d.ts +0 -11
- package/core/gather/gatherers/cache-contents.js +0 -56
- package/core/gather/gatherers/devtools-log-compat.d.ts +0 -13
- package/core/gather/gatherers/devtools-log-compat.js +0 -35
- package/core/gather/gatherers/dobetterweb/domstats.d.ts +0 -10
- package/core/gather/gatherers/dobetterweb/domstats.js +0 -102
- package/core/gather/gatherers/dobetterweb/optimized-images.d.ts +0 -48
- package/core/gather/gatherers/dobetterweb/optimized-images.js +0 -169
- package/core/gather/gatherers/dobetterweb/response-compression.d.ts +0 -23
- package/core/gather/gatherers/dobetterweb/response-compression.js +0 -136
- package/core/gather/gatherers/seo/font-size.d.ts +0 -131
- package/core/gather/gatherers/seo/font-size.js +0 -347
- package/core/gather/gatherers/trace-compat.d.ts +0 -13
- package/core/gather/gatherers/trace-compat.js +0 -35
- package/types/internal/metaviewport-parser.d.ts +0 -13
- package/types/internal/parse-cache-control.d.ts +0 -20
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
export default UsesOptimizedImages;
|
|
2
|
-
declare class UsesOptimizedImages extends ByteEfficiencyAudit {
|
|
3
|
-
/**
|
|
4
|
-
* @param {{originalSize: number, jpegSize: number}} image
|
|
5
|
-
* @return {{bytes: number, percent: number}}
|
|
6
|
-
*/
|
|
7
|
-
static computeSavings(image: {
|
|
8
|
-
originalSize: number;
|
|
9
|
-
jpegSize: number;
|
|
10
|
-
}): {
|
|
11
|
-
bytes: number;
|
|
12
|
-
percent: number;
|
|
13
|
-
};
|
|
14
|
-
/**
|
|
15
|
-
* @param {{naturalWidth: number, naturalHeight: number}} imageElement
|
|
16
|
-
* @return {number}
|
|
17
|
-
*/
|
|
18
|
-
static estimateJPEGSizeFromDimensions(imageElement: {
|
|
19
|
-
naturalWidth: number;
|
|
20
|
-
naturalHeight: number;
|
|
21
|
-
}): number;
|
|
22
|
-
/**
|
|
23
|
-
* @param {LH.Artifacts} artifacts
|
|
24
|
-
* @return {import('./byte-efficiency-audit.js').ByteEfficiencyProduct}
|
|
25
|
-
*/
|
|
26
|
-
static audit_(artifacts: LH.Artifacts): import("./byte-efficiency-audit.js").ByteEfficiencyProduct;
|
|
27
|
-
}
|
|
28
|
-
export namespace UIStrings {
|
|
29
|
-
let title: string;
|
|
30
|
-
let description: string;
|
|
31
|
-
}
|
|
32
|
-
import { ByteEfficiencyAudit } from './byte-efficiency-audit.js';
|
|
33
|
-
//# sourceMappingURL=uses-optimized-images.d.ts.map
|
|
@@ -1,146 +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 used are sufficiently larger
|
|
8
|
-
* than JPEG compressed images without metadata at quality 85.
|
|
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 encode images with optimization (better compression). This is displayed in a list of audit titles that Lighthouse generates. */
|
|
18
|
-
title: 'Efficiently encode images',
|
|
19
|
-
/** Description of a Lighthouse audit that tells the user *why* they need to efficiently encode 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. */
|
|
20
|
-
description: 'Optimized images load faster and consume less cellular data. ' +
|
|
21
|
-
'[Learn how to efficiently encode images](https://developer.chrome.com/docs/lighthouse/performance/uses-optimized-images/).',
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
25
|
-
|
|
26
|
-
const IGNORE_THRESHOLD_IN_BYTES = 4096;
|
|
27
|
-
|
|
28
|
-
class UsesOptimizedImages extends ByteEfficiencyAudit {
|
|
29
|
-
/**
|
|
30
|
-
* @return {LH.Audit.Meta}
|
|
31
|
-
*/
|
|
32
|
-
static get meta() {
|
|
33
|
-
return {
|
|
34
|
-
id: 'uses-optimized-images',
|
|
35
|
-
title: str_(UIStrings.title),
|
|
36
|
-
description: str_(UIStrings.description),
|
|
37
|
-
scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
|
|
38
|
-
guidanceLevel: 2,
|
|
39
|
-
requiredArtifacts: ['OptimizedImages', 'ImageElements', 'GatherContext', 'DevtoolsLog',
|
|
40
|
-
'Trace', 'URL', 'SourceMaps'],
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* @param {{originalSize: number, jpegSize: number}} image
|
|
46
|
-
* @return {{bytes: number, percent: number}}
|
|
47
|
-
*/
|
|
48
|
-
static computeSavings(image) {
|
|
49
|
-
const bytes = image.originalSize - image.jpegSize;
|
|
50
|
-
const percent = 100 * bytes / image.originalSize;
|
|
51
|
-
return {bytes, percent};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* @param {{naturalWidth: number, naturalHeight: number}} imageElement
|
|
56
|
-
* @return {number}
|
|
57
|
-
*/
|
|
58
|
-
static estimateJPEGSizeFromDimensions(imageElement) {
|
|
59
|
-
const totalPixels = imageElement.naturalWidth * imageElement.naturalHeight;
|
|
60
|
-
// Even JPEGs with lots of detail can usually be compressed down to <1 byte per pixel
|
|
61
|
-
// Using 4:2:2 subsampling already gets an uncompressed bitmap to 2 bytes per pixel.
|
|
62
|
-
// The compression ratio for JPEG is usually somewhere around 10:1 depending on content, so
|
|
63
|
-
// 8:1 is a reasonable expectation for web content which is 1.5MB for a 6MP image.
|
|
64
|
-
const expectedBytesPerPixel = 2 * 1 / 8;
|
|
65
|
-
return Math.round(totalPixels * expectedBytesPerPixel);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* @param {LH.Artifacts} artifacts
|
|
70
|
-
* @return {import('./byte-efficiency-audit.js').ByteEfficiencyProduct}
|
|
71
|
-
*/
|
|
72
|
-
static audit_(artifacts) {
|
|
73
|
-
const pageURL = artifacts.URL.finalDisplayedUrl;
|
|
74
|
-
const images = artifacts.OptimizedImages;
|
|
75
|
-
const imageElements = artifacts.ImageElements;
|
|
76
|
-
/** @type {Map<string, LH.Artifacts.ImageElement>} */
|
|
77
|
-
const imageElementsByURL = new Map();
|
|
78
|
-
imageElements.forEach(img => imageElementsByURL.set(img.src, img));
|
|
79
|
-
|
|
80
|
-
/** @type {Array<{node?: LH.Audit.Details.NodeValue, url: string, fromProtocol: boolean, isCrossOrigin: boolean, totalBytes: number, wastedBytes: number}>} */
|
|
81
|
-
const items = [];
|
|
82
|
-
const warnings = [];
|
|
83
|
-
for (const image of images) {
|
|
84
|
-
const imageElement = imageElementsByURL.get(image.url);
|
|
85
|
-
|
|
86
|
-
if (image.failed) {
|
|
87
|
-
warnings.push(`Unable to decode ${UrlUtils.getURLDisplayName(image.url)}`);
|
|
88
|
-
continue;
|
|
89
|
-
} else if (/(jpeg|bmp)/.test(image.mimeType) === false) {
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
let jpegSize = image.jpegSize;
|
|
94
|
-
let fromProtocol = true;
|
|
95
|
-
|
|
96
|
-
if (typeof jpegSize === 'undefined') {
|
|
97
|
-
if (!imageElement) {
|
|
98
|
-
warnings.push(`Unable to locate resource ${UrlUtils.getURLDisplayName(image.url)}`);
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Skip if we couldn't collect natural image size information.
|
|
103
|
-
if (!imageElement.naturalDimensions) continue;
|
|
104
|
-
const naturalHeight = imageElement.naturalDimensions.height;
|
|
105
|
-
const naturalWidth = imageElement.naturalDimensions.width;
|
|
106
|
-
// If naturalHeight or naturalWidth are falsy, information is not valid, skip.
|
|
107
|
-
if (!naturalHeight || !naturalWidth) continue;
|
|
108
|
-
jpegSize =
|
|
109
|
-
UsesOptimizedImages.estimateJPEGSizeFromDimensions({naturalHeight, naturalWidth});
|
|
110
|
-
fromProtocol = false;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (image.originalSize < jpegSize + IGNORE_THRESHOLD_IN_BYTES) continue;
|
|
114
|
-
|
|
115
|
-
const url = UrlUtils.elideDataURI(image.url);
|
|
116
|
-
const isCrossOrigin = !UrlUtils.originsMatch(pageURL, image.url);
|
|
117
|
-
const jpegSavings = UsesOptimizedImages.computeSavings({...image, jpegSize});
|
|
118
|
-
|
|
119
|
-
items.push({
|
|
120
|
-
node: imageElement ? ByteEfficiencyAudit.makeNodeItem(imageElement.node) : undefined,
|
|
121
|
-
url,
|
|
122
|
-
fromProtocol,
|
|
123
|
-
isCrossOrigin,
|
|
124
|
-
totalBytes: image.originalSize,
|
|
125
|
-
wastedBytes: jpegSavings.bytes,
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/** @type {LH.Audit.Details.Opportunity['headings']} */
|
|
130
|
-
const headings = [
|
|
131
|
-
{key: 'node', valueType: 'node', label: ''},
|
|
132
|
-
{key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
|
|
133
|
-
{key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnResourceSize)},
|
|
134
|
-
{key: 'wastedBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnWastedBytes)},
|
|
135
|
-
];
|
|
136
|
-
|
|
137
|
-
return {
|
|
138
|
-
warnings,
|
|
139
|
-
items,
|
|
140
|
-
headings,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export default UsesOptimizedImages;
|
|
146
|
-
export {UIStrings};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
export default UsesResponsiveImagesSnapshot;
|
|
2
|
-
declare class UsesResponsiveImagesSnapshot extends Audit {
|
|
3
|
-
/**
|
|
4
|
-
* @param {LH.Artifacts} artifacts
|
|
5
|
-
* @return {Promise<LH.Audit.Product>}
|
|
6
|
-
*/
|
|
7
|
-
static audit(artifacts: LH.Artifacts): Promise<LH.Audit.Product>;
|
|
8
|
-
}
|
|
9
|
-
export namespace UIStrings {
|
|
10
|
-
let title: string;
|
|
11
|
-
let failureTitle: string;
|
|
12
|
-
let columnDisplayedDimensions: string;
|
|
13
|
-
let columnActualDimensions: string;
|
|
14
|
-
}
|
|
15
|
-
import { Audit } from '../audit.js';
|
|
16
|
-
//# sourceMappingURL=uses-responsive-images-snapshot.d.ts.map
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2021 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
|
-
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
import {Audit} from '../audit.js';
|
|
15
|
-
import * as UsesResponsiveImages from './uses-responsive-images.js';
|
|
16
|
-
import UrlUtils from '../../lib/url-utils.js';
|
|
17
|
-
import * as i18n from '../../lib/i18n/i18n.js';
|
|
18
|
-
|
|
19
|
-
const UIStrings = {
|
|
20
|
-
/** Descriptive title of a Lighthouse audit that checks if images match their displayed dimensions. This is displayed when the audit is passing. */
|
|
21
|
-
title: 'Images were appropriate for their displayed size',
|
|
22
|
-
/** Descriptive title of a Lighthouse audit that checks if images match their displayed dimensions. This is displayed when the audit is failing. */
|
|
23
|
-
failureTitle: 'Images were larger than their displayed size',
|
|
24
|
-
/** Label for a column in a data table; entries will be the dimensions of an image as it appears on the page. */
|
|
25
|
-
columnDisplayedDimensions: 'Displayed dimensions',
|
|
26
|
-
/** Label for a column in a data table; entries will be the dimensions of an image from it's source file. */
|
|
27
|
-
columnActualDimensions: 'Actual dimensions',
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
31
|
-
|
|
32
|
-
// Based on byte threshold of 4096, with 3 bytes per pixel.
|
|
33
|
-
const IGNORE_THRESHOLD_IN_PIXELS = 1365;
|
|
34
|
-
|
|
35
|
-
class UsesResponsiveImagesSnapshot extends Audit {
|
|
36
|
-
/**
|
|
37
|
-
* @return {LH.Audit.Meta}
|
|
38
|
-
*/
|
|
39
|
-
static get meta() {
|
|
40
|
-
return {
|
|
41
|
-
id: 'uses-responsive-images-snapshot',
|
|
42
|
-
title: str_(UIStrings.title),
|
|
43
|
-
failureTitle: str_(UIStrings.failureTitle),
|
|
44
|
-
description: UsesResponsiveImages.str_(UsesResponsiveImages.UIStrings.description),
|
|
45
|
-
scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
|
|
46
|
-
supportedModes: ['snapshot'],
|
|
47
|
-
guidanceLevel: 2,
|
|
48
|
-
requiredArtifacts: ['ImageElements', 'ViewportDimensions'],
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* @param {LH.Artifacts} artifacts
|
|
54
|
-
* @return {Promise<LH.Audit.Product>}
|
|
55
|
-
*/
|
|
56
|
-
static async audit(artifacts) {
|
|
57
|
-
let score = 1;
|
|
58
|
-
/** @type {LH.Audit.Details.TableItem[]} */
|
|
59
|
-
const items = [];
|
|
60
|
-
for (const image of artifacts.ImageElements) {
|
|
61
|
-
// Ignore CSS images because it's difficult to determine what is a spritesheet,
|
|
62
|
-
// and the reward-to-effort ratio for responsive CSS images is quite low https://css-tricks.com/responsive-images-css/.
|
|
63
|
-
if (image.isCss) continue;
|
|
64
|
-
|
|
65
|
-
if (!image.naturalDimensions) continue;
|
|
66
|
-
const actual = image.naturalDimensions;
|
|
67
|
-
const displayed = UsesResponsiveImages.default.getDisplayedDimensions(
|
|
68
|
-
{...image, naturalWidth: actual.width, naturalHeight: actual.height},
|
|
69
|
-
artifacts.ViewportDimensions
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
const actualPixels = actual.width * actual.height;
|
|
73
|
-
const usedPixels = displayed.width * displayed.height;
|
|
74
|
-
|
|
75
|
-
if (actualPixels <= usedPixels) continue;
|
|
76
|
-
if (actualPixels - usedPixels > IGNORE_THRESHOLD_IN_PIXELS) score = 0;
|
|
77
|
-
|
|
78
|
-
items.push({
|
|
79
|
-
node: Audit.makeNodeItem(image.node),
|
|
80
|
-
url: UrlUtils.elideDataURI(image.src),
|
|
81
|
-
displayedDimensions: `${displayed.width}x${displayed.height}`,
|
|
82
|
-
actualDimensions: `${actual.width}x${actual.height}`,
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/** @type {LH.Audit.Details.Table['headings']} */
|
|
87
|
-
const headings = [
|
|
88
|
-
/* eslint-disable max-len */
|
|
89
|
-
{key: 'node', valueType: 'node', label: ''},
|
|
90
|
-
{key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
|
|
91
|
-
{key: 'displayedDimensions', valueType: 'text', label: str_(UIStrings.columnDisplayedDimensions)},
|
|
92
|
-
{key: 'actualDimensions', valueType: 'text', label: str_(UIStrings.columnActualDimensions)},
|
|
93
|
-
/* eslint-enable max-len */
|
|
94
|
-
];
|
|
95
|
-
|
|
96
|
-
const details = Audit.makeTableDetails(headings, items);
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
score,
|
|
100
|
-
details,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export default UsesResponsiveImagesSnapshot;
|
|
106
|
-
export {UIStrings};
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
export default UsesResponsiveImages;
|
|
2
|
-
declare class UsesResponsiveImages extends ByteEfficiencyAudit {
|
|
3
|
-
/**
|
|
4
|
-
* @param {LH.Artifacts.ImageElement & {naturalWidth: number, naturalHeight: number}} image
|
|
5
|
-
* @param {LH.Artifacts.ViewportDimensions} ViewportDimensions
|
|
6
|
-
* @return {{width: number, height: number}};
|
|
7
|
-
*/
|
|
8
|
-
static getDisplayedDimensions(image: LH.Artifacts.ImageElement & {
|
|
9
|
-
naturalWidth: number;
|
|
10
|
-
naturalHeight: number;
|
|
11
|
-
}, ViewportDimensions: LH.Artifacts.ViewportDimensions): {
|
|
12
|
-
width: number;
|
|
13
|
-
height: number;
|
|
14
|
-
};
|
|
15
|
-
/**
|
|
16
|
-
* @param {LH.Artifacts.ImageElement & {naturalWidth: number, naturalHeight: number}} image
|
|
17
|
-
* @param {LH.Artifacts.ViewportDimensions} ViewportDimensions
|
|
18
|
-
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
19
|
-
* @return {null|LH.Audit.ByteEfficiencyItem};
|
|
20
|
-
*/
|
|
21
|
-
static computeWaste(image: LH.Artifacts.ImageElement & {
|
|
22
|
-
naturalWidth: number;
|
|
23
|
-
naturalHeight: number;
|
|
24
|
-
}, ViewportDimensions: LH.Artifacts.ViewportDimensions, networkRecords: Array<LH.Artifacts.NetworkRequest>): null | LH.Audit.ByteEfficiencyItem;
|
|
25
|
-
/**
|
|
26
|
-
* @param {LH.Artifacts.ImageElement} image
|
|
27
|
-
* @return {number};
|
|
28
|
-
*/
|
|
29
|
-
static determineAllowableWaste(image: LH.Artifacts.ImageElement): number;
|
|
30
|
-
/**
|
|
31
|
-
* @param {LH.Artifacts} artifacts
|
|
32
|
-
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
33
|
-
* @param {LH.Audit.Context} context
|
|
34
|
-
* @return {Promise<import('./byte-efficiency-audit.js').ByteEfficiencyProduct>}
|
|
35
|
-
*/
|
|
36
|
-
static audit_(artifacts: LH.Artifacts, networkRecords: Array<LH.Artifacts.NetworkRequest>, context: LH.Audit.Context): Promise<import("./byte-efficiency-audit.js").ByteEfficiencyProduct>;
|
|
37
|
-
}
|
|
38
|
-
export namespace UIStrings {
|
|
39
|
-
let title: string;
|
|
40
|
-
let description: string;
|
|
41
|
-
}
|
|
42
|
-
export const str_: (message: string, values?: Record<string, string | number>) => LH.IcuMessage;
|
|
43
|
-
import { ByteEfficiencyAudit } from './byte-efficiency-audit.js';
|
|
44
|
-
//# sourceMappingURL=uses-responsive-images.d.ts.map
|
|
@@ -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};
|