lighthouse 11.3.0-dev.20231204 → 11.3.0-dev.20231206
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/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +0 -24
- package/core/audits/byte-efficiency/byte-efficiency-audit.js +0 -102
- package/core/audits/byte-efficiency/duplicated-javascript.js +2 -3
- package/core/audits/byte-efficiency/legacy-javascript.d.ts +0 -13
- package/core/audits/byte-efficiency/legacy-javascript.js +2 -36
- package/core/audits/byte-efficiency/unminified-css.js +2 -2
- package/core/audits/byte-efficiency/unminified-javascript.js +2 -3
- package/core/audits/byte-efficiency/unused-javascript.js +2 -3
- package/core/audits/dobetterweb/uses-http2.d.ts +1 -1
- package/core/audits/dobetterweb/uses-http2.js +8 -5
- package/core/computed/unused-css.js +2 -2
- package/core/config/constants.d.ts +2 -2
- package/core/config/constants.js +2 -2
- package/core/lib/script-helpers.d.ts +34 -5
- package/core/lib/script-helpers.js +136 -0
- package/core/lib/url-utils.js +1 -1
- package/core/runner.js +5 -1
- package/dist/report/bundle.esm.js +24 -9
- package/dist/report/flow.js +25 -10
- package/dist/report/standalone.js +25 -10
- package/package.json +2 -2
- package/report/assets/styles.css +20 -5
- package/report/assets/templates.html +1 -1
- package/report/clients/standalone.js +1 -0
- package/report/renderer/components.js +2 -2
- package/report/renderer/report-renderer.js +4 -0
- package/report/types/report-renderer.d.ts +2 -0
- package/tsconfig.json +1 -0
|
@@ -33,29 +33,6 @@ export class ByteEfficiencyAudit extends Audit {
|
|
|
33
33
|
* @return {number}
|
|
34
34
|
*/
|
|
35
35
|
static scoreForWastedMs(wastedMs: number): number;
|
|
36
|
-
/**
|
|
37
|
-
* Estimates the number of bytes this network record would have consumed on the network based on the
|
|
38
|
-
* uncompressed size (totalBytes). Uses the actual transfer size from the network record if applicable.
|
|
39
|
-
*
|
|
40
|
-
* @param {LH.Artifacts.NetworkRequest|undefined} networkRecord
|
|
41
|
-
* @param {number} totalBytes Uncompressed size of the resource
|
|
42
|
-
* @param {LH.Crdp.Network.ResourceType=} resourceType
|
|
43
|
-
* @return {number}
|
|
44
|
-
*/
|
|
45
|
-
static estimateTransferSize(networkRecord: LH.Artifacts.NetworkRequest | undefined, totalBytes: number, resourceType?: LH.Crdp.Network.ResourceType | undefined): number;
|
|
46
|
-
/**
|
|
47
|
-
* Estimates the number of bytes the content of this network record would have consumed on the network based on the
|
|
48
|
-
* uncompressed size (totalBytes). Uses the actual transfer size from the network record if applicable,
|
|
49
|
-
* minus the size of the response headers.
|
|
50
|
-
*
|
|
51
|
-
* This differs from `estimateTransferSize` only in that is subtracts the response headers from the estimate.
|
|
52
|
-
*
|
|
53
|
-
* @param {LH.Artifacts.NetworkRequest|undefined} networkRecord
|
|
54
|
-
* @param {number} totalBytes Uncompressed size of the resource
|
|
55
|
-
* @param {LH.Crdp.Network.ResourceType=} resourceType
|
|
56
|
-
* @return {number}
|
|
57
|
-
*/
|
|
58
|
-
static estimateCompressedContentSize(networkRecord: LH.Artifacts.NetworkRequest | undefined, totalBytes: number, resourceType?: LH.Crdp.Network.ResourceType | undefined): number;
|
|
59
36
|
/**
|
|
60
37
|
* @param {LH.Artifacts} artifacts
|
|
61
38
|
* @param {LH.Audit.Context} context
|
|
@@ -112,5 +89,4 @@ export class ByteEfficiencyAudit extends Audit {
|
|
|
112
89
|
static audit_(artifacts: LH.Artifacts, networkRecords: Array<LH.Artifacts.NetworkRequest>, context: LH.Audit.Context): ByteEfficiencyProduct | Promise<ByteEfficiencyProduct>;
|
|
113
90
|
}
|
|
114
91
|
import { Audit } from '../audit.js';
|
|
115
|
-
import { NetworkRequest } from '../../lib/network-request.js';
|
|
116
92
|
//# sourceMappingURL=byte-efficiency-audit.d.ts.map
|
|
@@ -13,7 +13,6 @@ import {PageDependencyGraph} from '../../computed/page-dependency-graph.js';
|
|
|
13
13
|
import {LanternLargestContentfulPaint} from '../../computed/metrics/lantern-largest-contentful-paint.js';
|
|
14
14
|
import {LanternFirstContentfulPaint} from '../../computed/metrics/lantern-first-contentful-paint.js';
|
|
15
15
|
import {LCPImageRecord} from '../../computed/lcp-image-record.js';
|
|
16
|
-
import {NetworkRequest} from '../../lib/network-request.js';
|
|
17
16
|
|
|
18
17
|
const str_ = i18n.createIcuMessageFn(import.meta.url, {});
|
|
19
18
|
|
|
@@ -59,107 +58,6 @@ class ByteEfficiencyAudit extends Audit {
|
|
|
59
58
|
);
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
/**
|
|
63
|
-
* Estimates the number of bytes this network record would have consumed on the network based on the
|
|
64
|
-
* uncompressed size (totalBytes). Uses the actual transfer size from the network record if applicable.
|
|
65
|
-
*
|
|
66
|
-
* @param {LH.Artifacts.NetworkRequest|undefined} networkRecord
|
|
67
|
-
* @param {number} totalBytes Uncompressed size of the resource
|
|
68
|
-
* @param {LH.Crdp.Network.ResourceType=} resourceType
|
|
69
|
-
* @return {number}
|
|
70
|
-
*/
|
|
71
|
-
static estimateTransferSize(networkRecord, totalBytes, resourceType) {
|
|
72
|
-
if (!networkRecord) {
|
|
73
|
-
// We don't know how many bytes this asset used on the network, but we can guess it was
|
|
74
|
-
// roughly the size of the content gzipped.
|
|
75
|
-
// See https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer for specific CSS/Script examples
|
|
76
|
-
// See https://discuss.httparchive.org/t/file-size-and-compression-savings/145 for fallback multipliers
|
|
77
|
-
switch (resourceType) {
|
|
78
|
-
case 'Stylesheet':
|
|
79
|
-
// Stylesheets tend to compress extremely well.
|
|
80
|
-
return Math.round(totalBytes * 0.2);
|
|
81
|
-
case 'Script':
|
|
82
|
-
case 'Document':
|
|
83
|
-
// Scripts and HTML compress fairly well too.
|
|
84
|
-
return Math.round(totalBytes * 0.33);
|
|
85
|
-
default:
|
|
86
|
-
// Otherwise we'll just fallback to the average savings in HTTPArchive
|
|
87
|
-
return Math.round(totalBytes * 0.5);
|
|
88
|
-
}
|
|
89
|
-
} else if (networkRecord.resourceType === resourceType) {
|
|
90
|
-
// This was a regular standalone asset, just use the transfer size.
|
|
91
|
-
return networkRecord.transferSize || 0;
|
|
92
|
-
} else {
|
|
93
|
-
// This was an asset that was inlined in a different resource type (e.g. HTML document).
|
|
94
|
-
// Use the compression ratio of the resource to estimate the total transferred bytes.
|
|
95
|
-
const transferSize = networkRecord.transferSize || 0;
|
|
96
|
-
const resourceSize = networkRecord.resourceSize || 0;
|
|
97
|
-
// Get the compression ratio, if it's an invalid number, assume no compression.
|
|
98
|
-
const compressionRatio = Number.isFinite(resourceSize) && resourceSize > 0 ?
|
|
99
|
-
(transferSize / resourceSize) : 1;
|
|
100
|
-
return Math.round(totalBytes * compressionRatio);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Estimates the number of bytes the content of this network record would have consumed on the network based on the
|
|
106
|
-
* uncompressed size (totalBytes). Uses the actual transfer size from the network record if applicable,
|
|
107
|
-
* minus the size of the response headers.
|
|
108
|
-
*
|
|
109
|
-
* This differs from `estimateTransferSize` only in that is subtracts the response headers from the estimate.
|
|
110
|
-
*
|
|
111
|
-
* @param {LH.Artifacts.NetworkRequest|undefined} networkRecord
|
|
112
|
-
* @param {number} totalBytes Uncompressed size of the resource
|
|
113
|
-
* @param {LH.Crdp.Network.ResourceType=} resourceType
|
|
114
|
-
* @return {number}
|
|
115
|
-
*/
|
|
116
|
-
static estimateCompressedContentSize(networkRecord, totalBytes, resourceType) {
|
|
117
|
-
if (!networkRecord) {
|
|
118
|
-
// We don't know how many bytes this asset used on the network, but we can guess it was
|
|
119
|
-
// roughly the size of the content gzipped.
|
|
120
|
-
// See https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer for specific CSS/Script examples
|
|
121
|
-
// See https://discuss.httparchive.org/t/file-size-and-compression-savings/145 for fallback multipliers
|
|
122
|
-
switch (resourceType) {
|
|
123
|
-
case 'Stylesheet':
|
|
124
|
-
// Stylesheets tend to compress extremely well.
|
|
125
|
-
return Math.round(totalBytes * 0.2);
|
|
126
|
-
case 'Script':
|
|
127
|
-
case 'Document':
|
|
128
|
-
// Scripts and HTML compress fairly well too.
|
|
129
|
-
return Math.round(totalBytes * 0.33);
|
|
130
|
-
default:
|
|
131
|
-
// Otherwise we'll just fallback to the average savings in HTTPArchive
|
|
132
|
-
return Math.round(totalBytes * 0.5);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Get the size of the response body on the network.
|
|
137
|
-
let contentTransferSize = networkRecord.transferSize || 0;
|
|
138
|
-
if (!NetworkRequest.isContentEncoded(networkRecord)) {
|
|
139
|
-
// This is not encoded, so we can use resourceSize directly.
|
|
140
|
-
// This would be equivalent to transfer size minus headers transfer size, but transfer size
|
|
141
|
-
// may also include bytes for SSL connection etc.
|
|
142
|
-
contentTransferSize = networkRecord.resourceSize;
|
|
143
|
-
} else if (networkRecord.responseHeadersTransferSize) {
|
|
144
|
-
// Subtract the size of the encoded headers.
|
|
145
|
-
contentTransferSize =
|
|
146
|
-
Math.max(0, contentTransferSize - networkRecord.responseHeadersTransferSize);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (networkRecord.resourceType === resourceType) {
|
|
150
|
-
// This was a regular standalone asset, just use the transfer size.
|
|
151
|
-
return contentTransferSize;
|
|
152
|
-
} else {
|
|
153
|
-
// This was an asset that was inlined in a different resource type (e.g. HTML document).
|
|
154
|
-
// Use the compression ratio of the resource to estimate the total transferred bytes.
|
|
155
|
-
const resourceSize = networkRecord.resourceSize || 0;
|
|
156
|
-
// Get the compression ratio, if it's an invalid number, assume no compression.
|
|
157
|
-
const compressionRatio = Number.isFinite(resourceSize) && resourceSize > 0 ?
|
|
158
|
-
(contentTransferSize / resourceSize) : 1;
|
|
159
|
-
return Math.round(totalBytes * compressionRatio);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
61
|
/**
|
|
164
62
|
* @param {LH.Artifacts} artifacts
|
|
165
63
|
* @param {LH.Audit.Context} context
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import {ByteEfficiencyAudit} from './byte-efficiency-audit.js';
|
|
12
12
|
import {ModuleDuplication} from '../../computed/module-duplication.js';
|
|
13
13
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
14
|
-
import {getRequestForScript} from '../../lib/script-helpers.js';
|
|
14
|
+
import {estimateTransferSize, getRequestForScript} from '../../lib/script-helpers.js';
|
|
15
15
|
|
|
16
16
|
const UIStrings = {
|
|
17
17
|
/** Imperative title of a Lighthouse audit that tells the user to remove duplicate JavaScript from their code. This is displayed in a list of audit titles that Lighthouse generates. */
|
|
@@ -109,8 +109,7 @@ class DuplicatedJavascript extends ByteEfficiencyAudit {
|
|
|
109
109
|
* @param {number} contentLength
|
|
110
110
|
*/
|
|
111
111
|
static _estimateTransferRatio(networkRecord, contentLength) {
|
|
112
|
-
const transferSize =
|
|
113
|
-
ByteEfficiencyAudit.estimateTransferSize(networkRecord, contentLength, 'Script');
|
|
112
|
+
const transferSize = estimateTransferSize(networkRecord, contentLength, 'Script');
|
|
114
113
|
return transferSize / contentLength;
|
|
115
114
|
}
|
|
116
115
|
|
|
@@ -60,19 +60,6 @@ declare class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
60
60
|
* @return {number}
|
|
61
61
|
*/
|
|
62
62
|
static estimateWastedBytes(matches: PatternMatchResult[]): number;
|
|
63
|
-
/**
|
|
64
|
-
* Utility function to estimate the ratio of the compression on the resource.
|
|
65
|
-
* This excludes the size of the response headers.
|
|
66
|
-
* Also caches the calculation.
|
|
67
|
-
*
|
|
68
|
-
* Note: duplicated-javascript does this exact thing. In the future, consider
|
|
69
|
-
* making a generic estimator on ByteEfficiencyAudit.
|
|
70
|
-
* @param {Map<string, number>} compressionRatioByUrl
|
|
71
|
-
* @param {string} url
|
|
72
|
-
* @param {LH.Artifacts} artifacts
|
|
73
|
-
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
74
|
-
*/
|
|
75
|
-
static estimateCompressionRatioForContent(compressionRatioByUrl: Map<string, number>, url: string, artifacts: LH.Artifacts, networkRecords: Array<LH.Artifacts.NetworkRequest>): Promise<number>;
|
|
76
63
|
/**
|
|
77
64
|
* @param {LH.Artifacts} artifacts
|
|
78
65
|
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
@@ -24,7 +24,7 @@ import {ByteEfficiencyAudit} from './byte-efficiency-audit.js';
|
|
|
24
24
|
import {EntityClassification} from '../../computed/entity-classification.js';
|
|
25
25
|
import {JSBundles} from '../../computed/js-bundles.js';
|
|
26
26
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
27
|
-
import {
|
|
27
|
+
import {estimateCompressionRatioForContent} from '../../lib/script-helpers.js';
|
|
28
28
|
import {LH_ROOT} from '../../../shared/root.js';
|
|
29
29
|
|
|
30
30
|
const graphJson = fs.readFileSync(
|
|
@@ -390,40 +390,6 @@ class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
390
390
|
return estimatedWastedBytes;
|
|
391
391
|
}
|
|
392
392
|
|
|
393
|
-
/**
|
|
394
|
-
* Utility function to estimate the ratio of the compression on the resource.
|
|
395
|
-
* This excludes the size of the response headers.
|
|
396
|
-
* Also caches the calculation.
|
|
397
|
-
*
|
|
398
|
-
* Note: duplicated-javascript does this exact thing. In the future, consider
|
|
399
|
-
* making a generic estimator on ByteEfficiencyAudit.
|
|
400
|
-
* @param {Map<string, number>} compressionRatioByUrl
|
|
401
|
-
* @param {string} url
|
|
402
|
-
* @param {LH.Artifacts} artifacts
|
|
403
|
-
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
404
|
-
*/
|
|
405
|
-
static async estimateCompressionRatioForContent(compressionRatioByUrl, url,
|
|
406
|
-
artifacts, networkRecords) {
|
|
407
|
-
let compressionRatio = compressionRatioByUrl.get(url);
|
|
408
|
-
if (compressionRatio !== undefined) return compressionRatio;
|
|
409
|
-
|
|
410
|
-
const script = artifacts.Scripts.find(script => script.url === url);
|
|
411
|
-
|
|
412
|
-
if (!script) {
|
|
413
|
-
// Can't find content, so just use 1.
|
|
414
|
-
compressionRatio = 1;
|
|
415
|
-
} else {
|
|
416
|
-
const networkRecord = getRequestForScript(networkRecords, script);
|
|
417
|
-
const contentLength = networkRecord?.resourceSize || script.length || 0;
|
|
418
|
-
const compressedSize =
|
|
419
|
-
ByteEfficiencyAudit.estimateCompressedContentSize(networkRecord, contentLength, 'Script');
|
|
420
|
-
compressionRatio = compressedSize / contentLength;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
compressionRatioByUrl.set(url, compressionRatio);
|
|
424
|
-
return compressionRatio;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
393
|
/**
|
|
428
394
|
* @param {LH.Artifacts} artifacts
|
|
429
395
|
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
@@ -451,7 +417,7 @@ class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
451
417
|
const scriptToMatchResults =
|
|
452
418
|
this.detectAcrossScripts(matcher, artifacts.Scripts, networkRecords, bundles);
|
|
453
419
|
for (const [script, matches] of scriptToMatchResults.entries()) {
|
|
454
|
-
const compressionRatio = await
|
|
420
|
+
const compressionRatio = await estimateCompressionRatioForContent(
|
|
455
421
|
compressionRatioByUrl, script.url, artifacts, networkRecords);
|
|
456
422
|
const wastedBytes = Math.round(this.estimateWastedBytes(matches) * compressionRatio);
|
|
457
423
|
/** @type {typeof items[number]} */
|
|
@@ -8,6 +8,7 @@ import {ByteEfficiencyAudit} from './byte-efficiency-audit.js';
|
|
|
8
8
|
import {UnusedCSS} from '../../computed/unused-css.js';
|
|
9
9
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
10
10
|
import {computeCSSTokenLength as computeTokenLength} from '../../lib/minification-estimator.js';
|
|
11
|
+
import {estimateTransferSize} from '../../lib/script-helpers.js';
|
|
11
12
|
|
|
12
13
|
const UIStrings = {
|
|
13
14
|
/** Imperative title of a Lighthouse audit that tells the user to minify (remove whitespace) the page's CSS code. This is displayed in a list of audit titles that Lighthouse generates. */
|
|
@@ -65,8 +66,7 @@ class UnminifiedCSS extends ByteEfficiencyAudit {
|
|
|
65
66
|
url = contentPreview;
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
const totalBytes =
|
|
69
|
-
'Stylesheet');
|
|
69
|
+
const totalBytes = estimateTransferSize(networkRecord, content.length, 'Stylesheet');
|
|
70
70
|
const wastedRatio = 1 - totalTokenLength / content.length;
|
|
71
71
|
const wastedBytes = Math.round(totalBytes * wastedRatio);
|
|
72
72
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import {ByteEfficiencyAudit} from './byte-efficiency-audit.js';
|
|
8
8
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
9
9
|
import {computeJSTokenLength as computeTokenLength} from '../../lib/minification-estimator.js';
|
|
10
|
-
import {getRequestForScript, isInline} from '../../lib/script-helpers.js';
|
|
10
|
+
import {estimateTransferSize, getRequestForScript, isInline} from '../../lib/script-helpers.js';
|
|
11
11
|
import {Util} from '../../../shared/util.js';
|
|
12
12
|
|
|
13
13
|
const UIStrings = {
|
|
@@ -58,8 +58,7 @@ class UnminifiedJavaScript extends ByteEfficiencyAudit {
|
|
|
58
58
|
const contentLength = scriptContent.length;
|
|
59
59
|
const totalTokenLength = computeTokenLength(scriptContent);
|
|
60
60
|
|
|
61
|
-
const totalBytes =
|
|
62
|
-
'Script');
|
|
61
|
+
const totalBytes = estimateTransferSize(networkRecord, contentLength, 'Script');
|
|
63
62
|
const wastedRatio = 1 - totalTokenLength / contentLength;
|
|
64
63
|
const wastedBytes = Math.round(totalBytes * wastedRatio);
|
|
65
64
|
|
|
@@ -8,7 +8,7 @@ import {ByteEfficiencyAudit} from './byte-efficiency-audit.js';
|
|
|
8
8
|
import {UnusedJavascriptSummary} from '../../computed/unused-javascript-summary.js';
|
|
9
9
|
import {JSBundles} from '../../computed/js-bundles.js';
|
|
10
10
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
11
|
-
import {getRequestForScript} from '../../lib/script-helpers.js';
|
|
11
|
+
import {estimateTransferSize, getRequestForScript} from '../../lib/script-helpers.js';
|
|
12
12
|
|
|
13
13
|
const UIStrings = {
|
|
14
14
|
/** Imperative title of a Lighthouse audit that tells the user to reduce JavaScript that is never evaluated during page load. This is displayed in a list of audit titles that Lighthouse generates. */
|
|
@@ -99,8 +99,7 @@ class UnusedJavaScript extends ByteEfficiencyAudit {
|
|
|
99
99
|
await UnusedJavascriptSummary.request({scriptId, scriptCoverage, bundle}, context);
|
|
100
100
|
if (unusedJsSummary.wastedBytes === 0 || unusedJsSummary.totalBytes === 0) continue;
|
|
101
101
|
|
|
102
|
-
const transfer =
|
|
103
|
-
.estimateTransferSize(networkRecord, unusedJsSummary.totalBytes, 'Script');
|
|
102
|
+
const transfer = estimateTransferSize(networkRecord, unusedJsSummary.totalBytes, 'Script');
|
|
104
103
|
const transferRatio = transfer / unusedJsSummary.totalBytes;
|
|
105
104
|
/** @type {LH.Audit.ByteEfficiencyItem} */
|
|
106
105
|
const item = {
|
|
@@ -42,7 +42,7 @@ declare class UsesHTTP2Audit extends Audit {
|
|
|
42
42
|
* @param {LH.Artifacts.EntityClassification} classifiedEntities
|
|
43
43
|
* @return {boolean}
|
|
44
44
|
*/
|
|
45
|
-
static
|
|
45
|
+
static isMultiplexableStaticAsset(networkRequest: LH.Artifacts.NetworkRequest, classifiedEntities: LH.Artifacts.EntityClassification): boolean;
|
|
46
46
|
/**
|
|
47
47
|
* Determine the set of resources that aren't HTTP/2 but should be.
|
|
48
48
|
* We're a little conservative about what we surface for a few reasons:
|
|
@@ -152,16 +152,19 @@ class UsesHTTP2Audit extends Audit {
|
|
|
152
152
|
* @param {LH.Artifacts.EntityClassification} classifiedEntities
|
|
153
153
|
* @return {boolean}
|
|
154
154
|
*/
|
|
155
|
-
static
|
|
155
|
+
static isMultiplexableStaticAsset(networkRequest, classifiedEntities) {
|
|
156
156
|
if (!STATIC_RESOURCE_TYPES.has(networkRequest.resourceType)) return false;
|
|
157
157
|
|
|
158
158
|
// Resources from third-parties that are less than 100 bytes are usually tracking pixels, not actual resources.
|
|
159
159
|
// They can masquerade as static types though (gifs, documents, etc)
|
|
160
160
|
if (networkRequest.resourceSize < 100) {
|
|
161
|
-
// This logic needs to be revisited.
|
|
162
|
-
// See https://github.com/GoogleChrome/lighthouse/issues/14661
|
|
163
161
|
const entity = classifiedEntities.entityByUrl.get(networkRequest.url);
|
|
164
|
-
if (entity
|
|
162
|
+
if (entity) {
|
|
163
|
+
// Third-party assets are multiplexable in their first-party context.
|
|
164
|
+
if (classifiedEntities.firstParty?.name === entity.name) return true;
|
|
165
|
+
// Skip recognizable third-parties' requests.
|
|
166
|
+
if (!entity.isUnrecognized) return false;
|
|
167
|
+
}
|
|
165
168
|
}
|
|
166
169
|
|
|
167
170
|
return true;
|
|
@@ -199,7 +202,7 @@ class UsesHTTP2Audit extends Audit {
|
|
|
199
202
|
/** @type {Map<string, Array<LH.Artifacts.NetworkRequest>>} */
|
|
200
203
|
const groupedByOrigin = new Map();
|
|
201
204
|
for (const record of networkRecords) {
|
|
202
|
-
if (!UsesHTTP2Audit.
|
|
205
|
+
if (!UsesHTTP2Audit.isMultiplexableStaticAsset(record, classifiedEntities)) continue;
|
|
203
206
|
if (UrlUtils.isLikeLocalhost(record.parsedURL.host)) continue;
|
|
204
207
|
const existing = groupedByOrigin.get(record.parsedURL.securityOrigin) || [];
|
|
205
208
|
existing.push(record);
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {makeComputedArtifact} from './computed-artifact.js';
|
|
8
|
-
import {ByteEfficiencyAudit} from '../audits/byte-efficiency/byte-efficiency-audit.js';
|
|
9
8
|
import {NetworkRecords} from './network-records.js';
|
|
10
9
|
import {Util} from '../../shared/util.js';
|
|
10
|
+
import {estimateTransferSize} from '../lib/script-helpers.js';
|
|
11
11
|
|
|
12
12
|
const PREVIEW_LENGTH = 100;
|
|
13
13
|
|
|
@@ -70,7 +70,7 @@ class UnusedCSS {
|
|
|
70
70
|
usedUncompressedBytes += usedRule.endOffset - usedRule.startOffset;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
const totalTransferredBytes =
|
|
73
|
+
const totalTransferredBytes = estimateTransferSize(
|
|
74
74
|
stylesheetInfo.networkRecord, totalUncompressedBytes, 'Stylesheet');
|
|
75
75
|
const percentUnused = (totalUncompressedBytes - usedUncompressedBytes) / totalUncompressedBytes;
|
|
76
76
|
const wastedBytes = Math.round(percentUnused * totalTransferredBytes);
|
|
@@ -76,7 +76,7 @@ declare const MOTOGPOWER_EMULATION_METRICS: Required<LH.SharedFlagsSettings['scr
|
|
|
76
76
|
* @type {Required<LH.SharedFlagsSettings['screenEmulation']>}
|
|
77
77
|
*/
|
|
78
78
|
declare const DESKTOP_EMULATION_METRICS: Required<LH.SharedFlagsSettings['screenEmulation']>;
|
|
79
|
-
declare const MOTOG4_USERAGENT: "Mozilla/5.0 (Linux; Android 11; moto g power (2022)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
80
|
-
declare const DESKTOP_USERAGENT: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
79
|
+
declare const MOTOG4_USERAGENT: "Mozilla/5.0 (Linux; Android 11; moto g power (2022)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Mobile Safari/537.36";
|
|
80
|
+
declare const DESKTOP_USERAGENT: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36";
|
|
81
81
|
export {};
|
|
82
82
|
//# sourceMappingURL=constants.d.ts.map
|
package/core/config/constants.js
CHANGED
|
@@ -80,8 +80,8 @@ const screenEmulationMetrics = {
|
|
|
80
80
|
};
|
|
81
81
|
|
|
82
82
|
|
|
83
|
-
const MOTOG4_USERAGENT = 'Mozilla/5.0 (Linux; Android 11; moto g power (2022)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
84
|
-
const DESKTOP_USERAGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
83
|
+
const MOTOG4_USERAGENT = 'Mozilla/5.0 (Linux; Android 11; moto g power (2022)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Mobile Safari/537.36'; // eslint-disable-line max-len
|
|
84
|
+
const DESKTOP_USERAGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'; // eslint-disable-line max-len
|
|
85
85
|
|
|
86
86
|
const userAgents = {
|
|
87
87
|
mobile: MOTOG4_USERAGENT,
|
|
@@ -4,14 +4,43 @@
|
|
|
4
4
|
* @return {LH.Artifacts.NetworkRequest|undefined}
|
|
5
5
|
*/
|
|
6
6
|
export function getRequestForScript(networkRecords: LH.Artifacts.NetworkRequest[], script: LH.Artifacts.Script): LH.Artifacts.NetworkRequest | undefined;
|
|
7
|
-
/**
|
|
8
|
-
* @license
|
|
9
|
-
* Copyright 2022 Google LLC
|
|
10
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
11
|
-
*/
|
|
12
7
|
/**
|
|
13
8
|
* @param {LH.Artifacts.Script} script
|
|
14
9
|
* @return {boolean}
|
|
15
10
|
*/
|
|
16
11
|
export function isInline(script: LH.Artifacts.Script): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Estimates the number of bytes the content of this network record would have consumed on the network based on the
|
|
14
|
+
* uncompressed size (totalBytes). Uses the actual transfer size from the network record if applicable,
|
|
15
|
+
* minus the size of the response headers.
|
|
16
|
+
*
|
|
17
|
+
* This differs from `estimateTransferSize` only in that is subtracts the response headers from the estimate.
|
|
18
|
+
*
|
|
19
|
+
* @param {LH.Artifacts.NetworkRequest|undefined} networkRecord
|
|
20
|
+
* @param {number} totalBytes Uncompressed size of the resource
|
|
21
|
+
* @param {LH.Crdp.Network.ResourceType=} resourceType
|
|
22
|
+
* @return {number}
|
|
23
|
+
*/
|
|
24
|
+
export function estimateCompressedContentSize(networkRecord: LH.Artifacts.NetworkRequest | undefined, totalBytes: number, resourceType?: LH.Crdp.Network.ResourceType | undefined): number;
|
|
25
|
+
/**
|
|
26
|
+
* Estimates the number of bytes this network record would have consumed on the network based on the
|
|
27
|
+
* uncompressed size (totalBytes). Uses the actual transfer size from the network record if applicable.
|
|
28
|
+
*
|
|
29
|
+
* @param {LH.Artifacts.NetworkRequest|undefined} networkRecord
|
|
30
|
+
* @param {number} totalBytes Uncompressed size of the resource
|
|
31
|
+
* @param {LH.Crdp.Network.ResourceType=} resourceType
|
|
32
|
+
* @return {number}
|
|
33
|
+
*/
|
|
34
|
+
export function estimateTransferSize(networkRecord: LH.Artifacts.NetworkRequest | undefined, totalBytes: number, resourceType?: LH.Crdp.Network.ResourceType | undefined): number;
|
|
35
|
+
/**
|
|
36
|
+
* Utility function to estimate the ratio of the compression on the resource.
|
|
37
|
+
* This excludes the size of the response headers.
|
|
38
|
+
* Also caches the calculation.
|
|
39
|
+
* @param {Map<string, number>} compressionRatioByUrl
|
|
40
|
+
* @param {string} url
|
|
41
|
+
* @param {LH.Artifacts} artifacts
|
|
42
|
+
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
43
|
+
*/
|
|
44
|
+
export function estimateCompressionRatioForContent(compressionRatioByUrl: Map<string, number>, url: string, artifacts: LH.Artifacts, networkRecords: Array<LH.Artifacts.NetworkRequest>): Promise<number>;
|
|
45
|
+
import { NetworkRequest } from './network-request.js';
|
|
17
46
|
//# sourceMappingURL=script-helpers.d.ts.map
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import {NetworkRequest} from './network-request.js';
|
|
8
|
+
|
|
7
9
|
/**
|
|
8
10
|
* @param {LH.Artifacts.Script} script
|
|
9
11
|
* @return {boolean}
|
|
@@ -25,7 +27,141 @@ function getRequestForScript(networkRecords, script) {
|
|
|
25
27
|
return networkRequest;
|
|
26
28
|
}
|
|
27
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Estimates the number of bytes this network record would have consumed on the network based on the
|
|
32
|
+
* uncompressed size (totalBytes). Uses the actual transfer size from the network record if applicable.
|
|
33
|
+
*
|
|
34
|
+
* @param {LH.Artifacts.NetworkRequest|undefined} networkRecord
|
|
35
|
+
* @param {number} totalBytes Uncompressed size of the resource
|
|
36
|
+
* @param {LH.Crdp.Network.ResourceType=} resourceType
|
|
37
|
+
* @return {number}
|
|
38
|
+
*/
|
|
39
|
+
function estimateTransferSize(networkRecord, totalBytes, resourceType) {
|
|
40
|
+
if (!networkRecord) {
|
|
41
|
+
// We don't know how many bytes this asset used on the network, but we can guess it was
|
|
42
|
+
// roughly the size of the content gzipped.
|
|
43
|
+
// See https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer for specific CSS/Script examples
|
|
44
|
+
// See https://discuss.httparchive.org/t/file-size-and-compression-savings/145 for fallback multipliers
|
|
45
|
+
switch (resourceType) {
|
|
46
|
+
case 'Stylesheet':
|
|
47
|
+
// Stylesheets tend to compress extremely well.
|
|
48
|
+
return Math.round(totalBytes * 0.2);
|
|
49
|
+
case 'Script':
|
|
50
|
+
case 'Document':
|
|
51
|
+
// Scripts and HTML compress fairly well too.
|
|
52
|
+
return Math.round(totalBytes * 0.33);
|
|
53
|
+
default:
|
|
54
|
+
// Otherwise we'll just fallback to the average savings in HTTPArchive
|
|
55
|
+
return Math.round(totalBytes * 0.5);
|
|
56
|
+
}
|
|
57
|
+
} else if (networkRecord.resourceType === resourceType) {
|
|
58
|
+
// This was a regular standalone asset, just use the transfer size.
|
|
59
|
+
return networkRecord.transferSize || 0;
|
|
60
|
+
} else {
|
|
61
|
+
// This was an asset that was inlined in a different resource type (e.g. HTML document).
|
|
62
|
+
// Use the compression ratio of the resource to estimate the total transferred bytes.
|
|
63
|
+
const transferSize = networkRecord.transferSize || 0;
|
|
64
|
+
const resourceSize = networkRecord.resourceSize || 0;
|
|
65
|
+
// Get the compression ratio, if it's an invalid number, assume no compression.
|
|
66
|
+
const compressionRatio = Number.isFinite(resourceSize) && resourceSize > 0 ?
|
|
67
|
+
(transferSize / resourceSize) : 1;
|
|
68
|
+
return Math.round(totalBytes * compressionRatio);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Estimates the number of bytes the content of this network record would have consumed on the network based on the
|
|
74
|
+
* uncompressed size (totalBytes). Uses the actual transfer size from the network record if applicable,
|
|
75
|
+
* minus the size of the response headers.
|
|
76
|
+
*
|
|
77
|
+
* This differs from `estimateTransferSize` only in that is subtracts the response headers from the estimate.
|
|
78
|
+
*
|
|
79
|
+
* @param {LH.Artifacts.NetworkRequest|undefined} networkRecord
|
|
80
|
+
* @param {number} totalBytes Uncompressed size of the resource
|
|
81
|
+
* @param {LH.Crdp.Network.ResourceType=} resourceType
|
|
82
|
+
* @return {number}
|
|
83
|
+
*/
|
|
84
|
+
function estimateCompressedContentSize(networkRecord, totalBytes, resourceType) {
|
|
85
|
+
if (!networkRecord) {
|
|
86
|
+
// We don't know how many bytes this asset used on the network, but we can guess it was
|
|
87
|
+
// roughly the size of the content gzipped.
|
|
88
|
+
// See https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer for specific CSS/Script examples
|
|
89
|
+
// See https://discuss.httparchive.org/t/file-size-and-compression-savings/145 for fallback multipliers
|
|
90
|
+
switch (resourceType) {
|
|
91
|
+
case 'Stylesheet':
|
|
92
|
+
// Stylesheets tend to compress extremely well.
|
|
93
|
+
return Math.round(totalBytes * 0.2);
|
|
94
|
+
case 'Script':
|
|
95
|
+
case 'Document':
|
|
96
|
+
// Scripts and HTML compress fairly well too.
|
|
97
|
+
return Math.round(totalBytes * 0.33);
|
|
98
|
+
default:
|
|
99
|
+
// Otherwise we'll just fallback to the average savings in HTTPArchive
|
|
100
|
+
return Math.round(totalBytes * 0.5);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Get the size of the response body on the network.
|
|
105
|
+
let contentTransferSize = networkRecord.transferSize || 0;
|
|
106
|
+
if (!NetworkRequest.isContentEncoded(networkRecord)) {
|
|
107
|
+
// This is not encoded, so we can use resourceSize directly.
|
|
108
|
+
// This would be equivalent to transfer size minus headers transfer size, but transfer size
|
|
109
|
+
// may also include bytes for SSL connection etc.
|
|
110
|
+
contentTransferSize = networkRecord.resourceSize;
|
|
111
|
+
} else if (networkRecord.responseHeadersTransferSize) {
|
|
112
|
+
// Subtract the size of the encoded headers.
|
|
113
|
+
contentTransferSize =
|
|
114
|
+
Math.max(0, contentTransferSize - networkRecord.responseHeadersTransferSize);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (networkRecord.resourceType === resourceType) {
|
|
118
|
+
// This was a regular standalone asset, just use the transfer size.
|
|
119
|
+
return contentTransferSize;
|
|
120
|
+
} else {
|
|
121
|
+
// This was an asset that was inlined in a different resource type (e.g. HTML document).
|
|
122
|
+
// Use the compression ratio of the resource to estimate the total transferred bytes.
|
|
123
|
+
const resourceSize = networkRecord.resourceSize || 0;
|
|
124
|
+
// Get the compression ratio, if it's an invalid number, assume no compression.
|
|
125
|
+
const compressionRatio = Number.isFinite(resourceSize) && resourceSize > 0 ?
|
|
126
|
+
(contentTransferSize / resourceSize) : 1;
|
|
127
|
+
return Math.round(totalBytes * compressionRatio);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Utility function to estimate the ratio of the compression on the resource.
|
|
133
|
+
* This excludes the size of the response headers.
|
|
134
|
+
* Also caches the calculation.
|
|
135
|
+
* @param {Map<string, number>} compressionRatioByUrl
|
|
136
|
+
* @param {string} url
|
|
137
|
+
* @param {LH.Artifacts} artifacts
|
|
138
|
+
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
139
|
+
*/
|
|
140
|
+
async function estimateCompressionRatioForContent(compressionRatioByUrl, url,
|
|
141
|
+
artifacts, networkRecords) {
|
|
142
|
+
let compressionRatio = compressionRatioByUrl.get(url);
|
|
143
|
+
if (compressionRatio !== undefined) return compressionRatio;
|
|
144
|
+
|
|
145
|
+
const script = artifacts.Scripts.find(script => script.url === url);
|
|
146
|
+
|
|
147
|
+
if (!script) {
|
|
148
|
+
// Can't find content, so just use 1.
|
|
149
|
+
compressionRatio = 1;
|
|
150
|
+
} else {
|
|
151
|
+
const networkRecord = getRequestForScript(networkRecords, script);
|
|
152
|
+
const contentLength = networkRecord?.resourceSize || script.length || 0;
|
|
153
|
+
const compressedSize = estimateCompressedContentSize(networkRecord, contentLength, 'Script');
|
|
154
|
+
compressionRatio = compressedSize / contentLength;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
compressionRatioByUrl.set(url, compressionRatio);
|
|
158
|
+
return compressionRatio;
|
|
159
|
+
}
|
|
160
|
+
|
|
28
161
|
export {
|
|
29
162
|
getRequestForScript,
|
|
30
163
|
isInline,
|
|
164
|
+
estimateCompressedContentSize,
|
|
165
|
+
estimateTransferSize,
|
|
166
|
+
estimateCompressionRatioForContent,
|
|
31
167
|
};
|
package/core/lib/url-utils.js
CHANGED
package/core/runner.js
CHANGED
|
@@ -317,7 +317,11 @@ class Runner {
|
|
|
317
317
|
for (const k of keys) {
|
|
318
318
|
if (!isDeepEqual(normalizedGatherSettings[k], normalizedAuditSettings[k])) {
|
|
319
319
|
throw new Error(
|
|
320
|
-
`Cannot change settings between gathering and auditing
|
|
320
|
+
`Cannot change settings between gathering and auditing…
|
|
321
|
+
Difference found at: \`${k}\`
|
|
322
|
+
${normalizedGatherSettings[k]}
|
|
323
|
+
vs
|
|
324
|
+
${normalizedAuditSettings[k]}`);
|
|
321
325
|
}
|
|
322
326
|
}
|
|
323
327
|
|