lighthouse 11.3.0-dev.20231205 → 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.
@@ -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 {getRequestForScript} from '../../lib/script-helpers.js';
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 this.estimateCompressionRatioForContent(
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 = ByteEfficiencyAudit.estimateTransferSize(networkRecord, content.length,
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 = ByteEfficiencyAudit.estimateTransferSize(networkRecord, contentLength,
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 = ByteEfficiencyAudit
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 = {
@@ -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 = ByteEfficiencyAudit.estimateTransferSize(
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);
@@ -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
  };