lighthouse 11.3.0 → 11.4.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/core/audits/accessibility/aria-allowed-role.js +1 -1
- package/core/audits/bf-cache.d.ts +1 -0
- package/core/audits/bf-cache.js +12 -2
- package/core/audits/bootup-time.js +3 -0
- package/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +0 -10
- package/core/audits/byte-efficiency/byte-efficiency-audit.js +6 -46
- package/core/audits/byte-efficiency/duplicated-javascript.d.ts +0 -6
- package/core/audits/byte-efficiency/duplicated-javascript.js +9 -38
- package/core/audits/byte-efficiency/legacy-javascript.d.ts +0 -11
- package/core/audits/byte-efficiency/legacy-javascript.js +5 -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 +10 -11
- package/core/audits/dobetterweb/uses-http2.d.ts +1 -1
- package/core/audits/dobetterweb/uses-http2.js +8 -5
- package/core/audits/layout-shift-elements.js +32 -17
- package/core/audits/metrics/cumulative-layout-shift.js +5 -1
- package/core/audits/metrics/max-potential-fid.d.ts +20 -0
- package/core/audits/metrics/max-potential-fid.js +69 -0
- package/core/audits/resource-summary.d.ts +11 -0
- package/core/audits/resource-summary.js +86 -0
- package/core/audits/third-party-cookies.d.ts +22 -0
- package/core/audits/third-party-cookies.js +112 -0
- package/core/audits/third-party-summary.d.ts +20 -2
- package/core/audits/third-party-summary.js +3 -48
- package/core/computed/entity-classification.js +1 -1
- package/core/computed/metrics/cumulative-layout-shift.d.ts +13 -1
- package/core/computed/metrics/cumulative-layout-shift.js +47 -1
- package/core/computed/metrics/lantern-first-contentful-paint.d.ts +50 -16
- package/core/computed/metrics/lantern-first-contentful-paint.js +56 -59
- package/core/computed/metrics/lantern-first-meaningful-paint.js +10 -12
- package/core/computed/metrics/lantern-largest-contentful-paint.js +9 -12
- package/core/computed/metrics/lantern-metric.d.ts +2 -2
- package/core/computed/metrics/lantern-metric.js +6 -5
- package/core/computed/resource-summary.d.ts +0 -1
- package/core/computed/resource-summary.js +2 -2
- 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/config/default-config.js +4 -0
- package/core/config/filters.js +1 -0
- package/core/gather/base-artifacts.js +2 -1
- package/core/gather/gatherers/css-usage.js +5 -0
- package/core/gather/gatherers/trace-elements.d.ts +6 -15
- package/core/gather/gatherers/trace-elements.js +15 -71
- package/core/gather/navigation-runner.js +1 -1
- package/core/lib/i18n/i18n.d.ts +2 -2
- package/core/lib/i18n/i18n.js +2 -2
- package/core/lib/minification-estimator.js +1 -1
- package/core/lib/network-recorder.d.ts +8 -0
- package/core/lib/network-recorder.js +13 -0
- package/core/lib/network-request.d.ts +11 -0
- package/core/lib/network-request.js +21 -1
- package/core/lib/rect-helpers.d.ts +5 -0
- package/core/lib/rect-helpers.js +15 -0
- package/core/lib/script-helpers.d.ts +34 -5
- package/core/lib/script-helpers.js +136 -0
- package/core/lib/url-utils.d.ts +6 -0
- package/core/lib/url-utils.js +14 -2
- package/core/runner.js +5 -1
- package/core/scripts/download-chrome.sh +56 -13
- package/dist/report/bundle.esm.js +30 -13
- package/dist/report/flow.js +32 -15
- package/dist/report/standalone.js +32 -15
- package/flow-report/src/i18n/i18n.d.ts +4 -0
- package/package.json +5 -4
- package/report/assets/styles.css +25 -8
- package/report/assets/templates.html +1 -1
- package/report/clients/standalone.js +1 -0
- package/report/renderer/components.js +2 -2
- package/report/renderer/explodey-gauge.js +0 -1
- package/report/renderer/performance-category-renderer.js +4 -2
- package/report/renderer/report-renderer.js +12 -1
- package/report/renderer/report-ui-features.js +3 -2
- package/report/renderer/report-utils.d.ts +2 -0
- package/report/renderer/report-utils.js +8 -4
- package/report/types/report-renderer.d.ts +2 -0
- package/shared/localization/locales/ar-XB.json +34 -25
- package/shared/localization/locales/ar.json +35 -26
- package/shared/localization/locales/bg.json +34 -25
- package/shared/localization/locales/ca.json +35 -26
- package/shared/localization/locales/cs.json +34 -25
- package/shared/localization/locales/da.json +51 -42
- package/shared/localization/locales/de.json +38 -29
- package/shared/localization/locales/el.json +34 -25
- package/shared/localization/locales/en-GB.json +35 -26
- package/shared/localization/locales/en-US.json +26 -5
- package/shared/localization/locales/en-XA.json +34 -25
- package/shared/localization/locales/en-XL.json +26 -5
- package/shared/localization/locales/es-419.json +34 -25
- package/shared/localization/locales/es.json +34 -25
- package/shared/localization/locales/fi.json +34 -25
- package/shared/localization/locales/fil.json +35 -26
- package/shared/localization/locales/fr.json +34 -25
- package/shared/localization/locales/he.json +35 -26
- package/shared/localization/locales/hi.json +35 -26
- package/shared/localization/locales/hr.json +35 -26
- package/shared/localization/locales/hu.json +34 -25
- package/shared/localization/locales/id.json +34 -25
- package/shared/localization/locales/it.json +34 -25
- package/shared/localization/locales/ja.json +34 -25
- package/shared/localization/locales/ko.json +35 -26
- package/shared/localization/locales/lt.json +34 -25
- package/shared/localization/locales/lv.json +35 -26
- package/shared/localization/locales/nl.json +35 -26
- package/shared/localization/locales/no.json +34 -25
- package/shared/localization/locales/pl.json +34 -25
- package/shared/localization/locales/pt-PT.json +35 -26
- package/shared/localization/locales/pt.json +35 -26
- package/shared/localization/locales/ro.json +34 -25
- package/shared/localization/locales/ru.json +35 -26
- package/shared/localization/locales/sk.json +35 -26
- package/shared/localization/locales/sl.json +35 -26
- package/shared/localization/locales/sr-Latn.json +34 -25
- package/shared/localization/locales/sr.json +34 -25
- package/shared/localization/locales/sv.json +35 -26
- package/shared/localization/locales/ta.json +35 -26
- package/shared/localization/locales/te.json +35 -26
- package/shared/localization/locales/th.json +34 -25
- package/shared/localization/locales/tr.json +35 -26
- package/shared/localization/locales/uk.json +34 -25
- package/shared/localization/locales/vi.json +34 -25
- package/shared/localization/locales/zh-HK.json +35 -26
- package/shared/localization/locales/zh-TW.json +38 -29
- package/shared/localization/locales/zh.json +34 -25
- package/shared/util.d.ts +14 -2
- package/shared/util.js +25 -3
- package/tsconfig.json +2 -0
- package/types/artifacts.d.ts +5 -2
|
@@ -220,7 +220,7 @@ async function _navigation(navigationContext) {
|
|
|
220
220
|
|
|
221
221
|
// Every required url is initialized to an empty string in `getBaseArtifacts`.
|
|
222
222
|
// If we haven't set all the required urls yet, set them here.
|
|
223
|
-
if (!Object.values(phaseState.baseArtifacts).every(Boolean)) {
|
|
223
|
+
if (!Object.values(phaseState.baseArtifacts.URL).every(Boolean)) {
|
|
224
224
|
phaseState.baseArtifacts.URL = {
|
|
225
225
|
requestedUrl: navigateResult.requestedUrl,
|
|
226
226
|
mainDocumentUrl: navigateResult.mainDocumentUrl,
|
package/core/lib/i18n/i18n.d.ts
CHANGED
|
@@ -69,9 +69,9 @@ export function lookupLocale(locales?: (string | string[]) | undefined, possible
|
|
|
69
69
|
* Returns a function that generates `LH.IcuMessage` objects to localize the
|
|
70
70
|
* messages in `fileStrings` and the shared `i18n.UIStrings`.
|
|
71
71
|
* @param {string} filename
|
|
72
|
-
* @param {Record<string, string
|
|
72
|
+
* @param {Record<string, string>=} fileStrings
|
|
73
73
|
*/
|
|
74
|
-
export function createIcuMessageFn(filename: string, fileStrings
|
|
74
|
+
export function createIcuMessageFn(filename: string, fileStrings?: Record<string, string> | undefined): (message: string, values?: Record<string, string | number> | undefined) => LH.IcuMessage;
|
|
75
75
|
/**
|
|
76
76
|
* Returns true if the given value is a string or an LH.IcuMessage.
|
|
77
77
|
* @param {unknown} value
|
package/core/lib/i18n/i18n.js
CHANGED
|
@@ -168,9 +168,9 @@ function lookupLocale(locales, possibleLocales) {
|
|
|
168
168
|
* Returns a function that generates `LH.IcuMessage` objects to localize the
|
|
169
169
|
* messages in `fileStrings` and the shared `i18n.UIStrings`.
|
|
170
170
|
* @param {string} filename
|
|
171
|
-
* @param {Record<string, string
|
|
171
|
+
* @param {Record<string, string>=} fileStrings
|
|
172
172
|
*/
|
|
173
|
-
function createIcuMessageFn(filename, fileStrings) {
|
|
173
|
+
function createIcuMessageFn(filename, fileStrings = {}) {
|
|
174
174
|
if (filename.startsWith('file://')) filename = url.fileURLToPath(filename);
|
|
175
175
|
|
|
176
176
|
// In the common case, `filename` is an absolute path that needs to be transformed
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-punctuators
|
|
8
8
|
// eslint-disable-next-line max-len
|
|
9
|
-
const PUNCTUATOR_REGEX = /(return|case|{|\(|\[|\.\.\.|;|,|<|>|<=|>=|==|!=|===|!==|\+|-|\*|%|\*\*|\+\+|--|<<|>>|>>>|&|\||\^|!|~|&&|\|\||\?|:|=|\+=|-=|\*=|%=|\*\*=|<<=|>>=|>>>=|&=|\|=|\^=|=>|\/|\/=|\})$/;
|
|
9
|
+
const PUNCTUATOR_REGEX = /(return|case|else|{|\(|\[|\.\.\.|;|,|<|>|<=|>=|==|!=|===|!==|\+|-|\*|%|\*\*|\+\+|--|<<|>>|>>>|&|\||\^|!|~|&&|\|\||\?|:|=|\+=|-=|\*=|%=|\*\*=|<<=|>>=|>>>=|&=|\|=|\^=|=>|\/|\/=|\})$/;
|
|
10
10
|
const WHITESPACE_REGEX = /( |\n|\t)+$/;
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -66,6 +66,14 @@ export class NetworkRecorder extends NetworkRecorder_base {
|
|
|
66
66
|
targetType: LH.Protocol.TargetType;
|
|
67
67
|
sessionId?: string;
|
|
68
68
|
}): void;
|
|
69
|
+
/**
|
|
70
|
+
* @param {{params: LH.Crdp.Network.ResponseReceivedExtraInfoEvent, targetType: LH.Protocol.TargetType, sessionId?: string}} event
|
|
71
|
+
*/
|
|
72
|
+
onResponseReceivedExtraInfo(event: {
|
|
73
|
+
params: LH.Crdp.Network.ResponseReceivedExtraInfoEvent;
|
|
74
|
+
targetType: LH.Protocol.TargetType;
|
|
75
|
+
sessionId?: string;
|
|
76
|
+
}): void;
|
|
69
77
|
/**
|
|
70
78
|
* @param {{params: LH.Crdp.Network.DataReceivedEvent, targetType: LH.Protocol.TargetType, sessionId?: string}} event
|
|
71
79
|
*/
|
|
@@ -138,6 +138,18 @@ class NetworkRecorder extends RequestEventEmitter {
|
|
|
138
138
|
request.onResponseReceived(data);
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
/**
|
|
142
|
+
* @param {{params: LH.Crdp.Network.ResponseReceivedExtraInfoEvent, targetType: LH.Protocol.TargetType, sessionId?: string}} event
|
|
143
|
+
*/
|
|
144
|
+
onResponseReceivedExtraInfo(event) {
|
|
145
|
+
const data = event.params;
|
|
146
|
+
const request = this._findRealRequestAndSetSession(
|
|
147
|
+
data.requestId, event.targetType, event.sessionId);
|
|
148
|
+
if (!request) return;
|
|
149
|
+
log.verbose('network', `${request.url} response received extra info`);
|
|
150
|
+
request.onResponseReceivedExtraInfo(data);
|
|
151
|
+
}
|
|
152
|
+
|
|
141
153
|
/**
|
|
142
154
|
* @param {{params: LH.Crdp.Network.DataReceivedEvent, targetType: LH.Protocol.TargetType, sessionId?: string}} event
|
|
143
155
|
*/
|
|
@@ -196,6 +208,7 @@ class NetworkRecorder extends RequestEventEmitter {
|
|
|
196
208
|
case 'Network.requestWillBeSent': return this.onRequestWillBeSent(event);
|
|
197
209
|
case 'Network.requestServedFromCache': return this.onRequestServedFromCache(event);
|
|
198
210
|
case 'Network.responseReceived': return this.onResponseReceived(event);
|
|
211
|
+
case 'Network.responseReceivedExtraInfo': return this.onResponseReceivedExtraInfo(event);
|
|
199
212
|
case 'Network.dataReceived': return this.onDataReceived(event);
|
|
200
213
|
case 'Network.loadingFinished': return this.onLoadingFinished(event);
|
|
201
214
|
case 'Network.loadingFailed': return this.onLoadingFailed(event);
|
|
@@ -66,6 +66,12 @@ export class NetworkRequest {
|
|
|
66
66
|
* @return {boolean}
|
|
67
67
|
*/
|
|
68
68
|
static isHstsRequest(record: NetworkRequest): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Returns whether the network request was sent encoded.
|
|
71
|
+
* @param {NetworkRequest} record
|
|
72
|
+
* @return {boolean}
|
|
73
|
+
*/
|
|
74
|
+
static isContentEncoded(record: NetworkRequest): boolean;
|
|
69
75
|
/**
|
|
70
76
|
* Resource size is almost always the right one to be using because of the below:
|
|
71
77
|
* `transferSize = resourceSize + headers.length`.
|
|
@@ -98,6 +104,7 @@ export class NetworkRequest {
|
|
|
98
104
|
/** When the last byte of the response body is received, in milliseconds. */
|
|
99
105
|
networkEndTime: number;
|
|
100
106
|
transferSize: number;
|
|
107
|
+
responseHeadersTransferSize: number;
|
|
101
108
|
resourceSize: number;
|
|
102
109
|
fromDiskCache: boolean;
|
|
103
110
|
fromMemoryCache: boolean;
|
|
@@ -155,6 +162,10 @@ export class NetworkRequest {
|
|
|
155
162
|
* @param {LH.Crdp.Network.ResponseReceivedEvent} data
|
|
156
163
|
*/
|
|
157
164
|
onResponseReceived(data: LH.Crdp.Network.ResponseReceivedEvent): void;
|
|
165
|
+
/**
|
|
166
|
+
* @param {LH.Crdp.Network.ResponseReceivedExtraInfoEvent} data
|
|
167
|
+
*/
|
|
168
|
+
onResponseReceivedExtraInfo(data: LH.Crdp.Network.ResponseReceivedExtraInfoEvent): void;
|
|
158
169
|
/**
|
|
159
170
|
* @param {LH.Crdp.Network.DataReceivedEvent} data
|
|
160
171
|
*/
|
|
@@ -135,6 +135,7 @@ class NetworkRequest {
|
|
|
135
135
|
|
|
136
136
|
// Go read the comment on _updateTransferSizeForLightrider.
|
|
137
137
|
this.transferSize = 0;
|
|
138
|
+
this.responseHeadersTransferSize = 0;
|
|
138
139
|
this.resourceSize = 0;
|
|
139
140
|
this.fromDiskCache = false;
|
|
140
141
|
this.fromMemoryCache = false;
|
|
@@ -246,6 +247,13 @@ class NetworkRequest {
|
|
|
246
247
|
this.frameId = data.frameId;
|
|
247
248
|
}
|
|
248
249
|
|
|
250
|
+
/**
|
|
251
|
+
* @param {LH.Crdp.Network.ResponseReceivedExtraInfoEvent} data
|
|
252
|
+
*/
|
|
253
|
+
onResponseReceivedExtraInfo(data) {
|
|
254
|
+
this.responseHeadersText = data.headersText || '';
|
|
255
|
+
}
|
|
256
|
+
|
|
249
257
|
/**
|
|
250
258
|
* @param {LH.Crdp.Network.DataReceivedEvent} data
|
|
251
259
|
*/
|
|
@@ -344,6 +352,7 @@ class NetworkRequest {
|
|
|
344
352
|
this.responseHeadersEndTime = timestamp * 1000;
|
|
345
353
|
|
|
346
354
|
this.transferSize = response.encodedDataLength;
|
|
355
|
+
this.responseHeadersTransferSize = response.encodedDataLength;
|
|
347
356
|
if (typeof response.fromDiskCache === 'boolean') this.fromDiskCache = response.fromDiskCache;
|
|
348
357
|
if (typeof response.fromPrefetchCache === 'boolean') {
|
|
349
358
|
this.fromPrefetchCache = response.fromPrefetchCache;
|
|
@@ -354,7 +363,6 @@ class NetworkRequest {
|
|
|
354
363
|
this.timing = response.timing;
|
|
355
364
|
if (resourceType) this.resourceType = RESOURCE_TYPES[resourceType];
|
|
356
365
|
this.mimeType = response.mimeType;
|
|
357
|
-
this.responseHeadersText = response.headersText || '';
|
|
358
366
|
this.responseHeaders = NetworkRequest._headersDictToHeadersArray(response.headers);
|
|
359
367
|
|
|
360
368
|
this.fetchedViaServiceWorker = !!response.fromServiceWorker;
|
|
@@ -600,6 +608,18 @@ class NetworkRequest {
|
|
|
600
608
|
return reason === 'HSTS' && NetworkRequest.isSecureRequest(destination);
|
|
601
609
|
}
|
|
602
610
|
|
|
611
|
+
/**
|
|
612
|
+
* Returns whether the network request was sent encoded.
|
|
613
|
+
* @param {NetworkRequest} record
|
|
614
|
+
* @return {boolean}
|
|
615
|
+
*/
|
|
616
|
+
static isContentEncoded(record) {
|
|
617
|
+
// FYI: older devtools logs (like our test fixtures) seems to be lower case, while modern logs
|
|
618
|
+
// are Cased-Like-This.
|
|
619
|
+
const pattern = /^content-encoding$/i;
|
|
620
|
+
return record.responseHeaders.some(item => item.name.match(pattern));
|
|
621
|
+
}
|
|
622
|
+
|
|
603
623
|
/**
|
|
604
624
|
* Resource size is almost always the right one to be using because of the below:
|
|
605
625
|
* `transferSize = resourceSize + headers.length`.
|
|
@@ -97,4 +97,9 @@ export function filterOutRectsContainedByOthers(rects: LH.Artifacts.Rect[]): LH.
|
|
|
97
97
|
* @return {LH.Artifacts.Rect[]}
|
|
98
98
|
*/
|
|
99
99
|
export function filterOutTinyRects(rects: LH.Artifacts.Rect[]): LH.Artifacts.Rect[];
|
|
100
|
+
/**
|
|
101
|
+
* @param {Array<number>} rect
|
|
102
|
+
* @return {LH.Artifacts.Rect}
|
|
103
|
+
*/
|
|
104
|
+
export function traceRectToLHRect(rect: Array<number>): LH.Artifacts.Rect;
|
|
100
105
|
//# sourceMappingURL=rect-helpers.d.ts.map
|
package/core/lib/rect-helpers.js
CHANGED
|
@@ -232,6 +232,20 @@ function allRectsContainedWithinEachOther(rectListA, rectListB) {
|
|
|
232
232
|
return true;
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
/**
|
|
236
|
+
* @param {Array<number>} rect
|
|
237
|
+
* @return {LH.Artifacts.Rect}
|
|
238
|
+
*/
|
|
239
|
+
function traceRectToLHRect(rect) {
|
|
240
|
+
const rectArgs = {
|
|
241
|
+
x: rect[0],
|
|
242
|
+
y: rect[1],
|
|
243
|
+
width: rect[2],
|
|
244
|
+
height: rect[3],
|
|
245
|
+
};
|
|
246
|
+
return addRectTopAndBottom(rectArgs);
|
|
247
|
+
}
|
|
248
|
+
|
|
235
249
|
export {
|
|
236
250
|
rectContainsPoint,
|
|
237
251
|
rectContains,
|
|
@@ -248,4 +262,5 @@ export {
|
|
|
248
262
|
allRectsContainedWithinEachOther,
|
|
249
263
|
filterOutRectsContainedByOthers,
|
|
250
264
|
filterOutTinyRects,
|
|
265
|
+
traceRectToLHRect,
|
|
251
266
|
};
|
|
@@ -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>): 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
|
+
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.d.ts
CHANGED
|
@@ -23,6 +23,12 @@ declare class UrlUtils {
|
|
|
23
23
|
* @return {?string}
|
|
24
24
|
*/
|
|
25
25
|
static getOrigin(url: string): string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Returns a primary domain for provided hostname (e.g. www.example.com -> example.com).
|
|
28
|
+
* @param {string|URL} url hostname or URL object
|
|
29
|
+
* @return {string}
|
|
30
|
+
*/
|
|
31
|
+
static getRootDomain(url: string | URL): string;
|
|
26
32
|
/**
|
|
27
33
|
* Check if rootDomains matches
|
|
28
34
|
*
|
package/core/lib/url-utils.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import {getDomain} from 'tldts-icann';
|
|
8
|
+
|
|
7
9
|
import {Util} from '../../shared/util.js';
|
|
8
10
|
import {LighthouseError} from './lh-error.js';
|
|
9
11
|
|
|
@@ -99,6 +101,16 @@ class UrlUtils {
|
|
|
99
101
|
}
|
|
100
102
|
}
|
|
101
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Returns a primary domain for provided hostname (e.g. www.example.com -> example.com).
|
|
106
|
+
* @param {string|URL} url hostname or URL object
|
|
107
|
+
* @return {string}
|
|
108
|
+
*/
|
|
109
|
+
static getRootDomain(url) {
|
|
110
|
+
const parsedUrl = Util.createOrReturnURL(url);
|
|
111
|
+
return getDomain(parsedUrl.href) || parsedUrl.hostname;
|
|
112
|
+
}
|
|
113
|
+
|
|
102
114
|
/**
|
|
103
115
|
* Check if rootDomains matches
|
|
104
116
|
*
|
|
@@ -120,8 +132,8 @@ class UrlUtils {
|
|
|
120
132
|
}
|
|
121
133
|
|
|
122
134
|
// get the string before the tld
|
|
123
|
-
const urlARootDomain =
|
|
124
|
-
const urlBRootDomain =
|
|
135
|
+
const urlARootDomain = UrlUtils.getRootDomain(urlAInfo);
|
|
136
|
+
const urlBRootDomain = UrlUtils.getRootDomain(urlBInfo);
|
|
125
137
|
|
|
126
138
|
return urlARootDomain === urlBRootDomain;
|
|
127
139
|
}
|
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
|
|
|
@@ -6,9 +6,22 @@
|
|
|
6
6
|
##
|
|
7
7
|
|
|
8
8
|
# Download chrome inside of our CI env.
|
|
9
|
+
# Takes one arg - the location to extract ToT chrome to. Defaults to .tmp/chrome-tot
|
|
10
|
+
# If already exists, this script does nothing.
|
|
9
11
|
|
|
10
12
|
set -euo pipefail
|
|
11
13
|
|
|
14
|
+
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
15
|
+
LH_ROOT_DIR="$SCRIPT_DIR/../.."
|
|
16
|
+
|
|
17
|
+
chrome_out=${1:-"$LH_ROOT_DIR/.tmp/chrome-tot"}
|
|
18
|
+
mkdir -p "$LH_ROOT_DIR/.tmp"
|
|
19
|
+
|
|
20
|
+
if [ -e "$chrome_out" ]; then
|
|
21
|
+
echo "cached chrome found"
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
12
25
|
unameOut="$(uname -s)"
|
|
13
26
|
case "${unameOut}" in
|
|
14
27
|
Linux*) machine=Linux;;
|
|
@@ -17,19 +30,49 @@ case "${unameOut}" in
|
|
|
17
30
|
*) machine="UNKNOWN:${unameOut}"
|
|
18
31
|
esac
|
|
19
32
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
elif [ "$machine" == "Linux" ]; then
|
|
23
|
-
url="https://download-chromium.appspot.com/dl/Linux_x64?type=snapshots"
|
|
24
|
-
elif [ "$machine" == "Mac" ]; then
|
|
25
|
-
url="https://download-chromium.appspot.com/dl/Mac?type=snapshots"
|
|
26
|
-
else
|
|
27
|
-
echo "unsupported platform"
|
|
28
|
-
exit 1
|
|
29
|
-
fi
|
|
33
|
+
# Only set this to true when actual ToT is broken and we can't fix it yet.
|
|
34
|
+
should_hardcode_ci=false
|
|
30
35
|
|
|
31
|
-
if [
|
|
32
|
-
|
|
36
|
+
if [[ "${CI:-}" ]] && [ "$should_hardcode_ci" == true ]; then
|
|
37
|
+
rev=1228630
|
|
38
|
+
if [ "$machine" == "MinGw" ]; then
|
|
39
|
+
url="http://commondatastorage.googleapis.com/chromium-browser-snapshots/Win_x64/$rev/chrome-win.zip"
|
|
40
|
+
elif [ "$machine" == "Linux" ]; then
|
|
41
|
+
url="http://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/$rev/chrome-linux.zip"
|
|
42
|
+
elif [ "$machine" == "Mac" ]; then
|
|
43
|
+
arch="$(uname -m)"
|
|
44
|
+
if [ "$arch" == "arm64" ]; then
|
|
45
|
+
url="http://commondatastorage.googleapis.com/chromium-browser-snapshots/Mac_Arm/$rev/chrome-mac.zip"
|
|
46
|
+
else
|
|
47
|
+
url="http://commondatastorage.googleapis.com/chromium-browser-snapshots/Mac/$rev/chrome-mac.zip"
|
|
48
|
+
fi
|
|
49
|
+
else
|
|
50
|
+
echo "unsupported platform"
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
33
53
|
else
|
|
34
|
-
|
|
54
|
+
if [ "$machine" == "MinGw" ]; then
|
|
55
|
+
url="https://download-chromium.appspot.com/dl/Win?type=snapshots"
|
|
56
|
+
elif [ "$machine" == "Linux" ]; then
|
|
57
|
+
url="https://download-chromium.appspot.com/dl/Linux_x64?type=snapshots"
|
|
58
|
+
elif [ "$machine" == "Mac" ]; then
|
|
59
|
+
arch="$(uname -m)"
|
|
60
|
+
if [ "$arch" == "arm64" ]; then
|
|
61
|
+
url="https://download-chromium.appspot.com/dl/Mac_Arm?type=snapshots"
|
|
62
|
+
else
|
|
63
|
+
url="https://download-chromium.appspot.com/dl/Mac?type=snapshots"
|
|
64
|
+
fi
|
|
65
|
+
else
|
|
66
|
+
echo "unsupported platform"
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
35
69
|
fi
|
|
70
|
+
|
|
71
|
+
echo "downloading $url"
|
|
72
|
+
|
|
73
|
+
mkdir -p .tmp-download && cd .tmp-download
|
|
74
|
+
curl "$url" -Lo chrome.zip && unzip -q chrome.zip && rm chrome.zip
|
|
75
|
+
mv * "$chrome_out"
|
|
76
|
+
cd - && rm -rf .tmp-download
|
|
77
|
+
|
|
78
|
+
ls "$chrome_out"
|