lighthouse 9.5.0-dev.20230122 → 9.5.0-dev.20230124
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/.codecov.yml +0 -5
- package/core/audits/audit.js +1 -1
- package/core/audits/bf-cache.js +1 -1
- package/core/audits/byte-efficiency/legacy-javascript.d.ts +1 -0
- package/core/audits/byte-efficiency/legacy-javascript.js +7 -3
- package/core/audits/dobetterweb/uses-http2.d.ts +5 -2
- package/core/audits/dobetterweb/uses-http2.js +12 -7
- package/core/audits/screenshot-thumbnails.d.ts +2 -1
- package/core/audits/screenshot-thumbnails.js +14 -9
- package/core/audits/third-party-facades.d.ts +3 -2
- package/core/audits/third-party-facades.js +12 -9
- package/core/audits/third-party-summary.d.ts +7 -6
- package/core/audits/third-party-summary.js +16 -14
- package/core/audits/valid-source-maps.d.ts +5 -3
- package/core/audits/valid-source-maps.js +11 -9
- package/core/computed/entity-classification.d.ts +29 -0
- package/core/computed/entity-classification.js +102 -0
- package/core/computed/processed-navigation.d.ts +12 -6
- package/core/computed/processed-navigation.js +19 -6
- package/core/computed/resource-summary.d.ts +4 -2
- package/core/computed/resource-summary.js +8 -5
- package/core/lib/url-utils.js +1 -1
- package/core/runner.d.ts +10 -0
- package/core/runner.js +48 -0
- package/core/util.cjs +43 -10
- package/core/util.d.cts +114 -2
- package/dist/report/bundle.esm.js +858 -883
- package/dist/report/flow.js +33 -23
- package/dist/report/standalone.js +27 -17
- package/flow-report/assets/standalone-flow-template.html +1 -1
- package/flow-report/src/common.tsx +1 -1
- package/flow-report/src/i18n/i18n.d.ts +103 -100
- package/flow-report/src/i18n/i18n.tsx +23 -18
- package/flow-report/src/sidebar/sidebar.tsx +3 -3
- package/flow-report/src/summary/category.tsx +7 -7
- package/flow-report/src/summary/summary.tsx +2 -2
- package/flow-report/src/topbar.tsx +4 -28
- package/package.json +5 -5
- package/readme.md +1 -1
- package/report/assets/standalone-template.html +1 -1
- package/report/assets/styles.css +3 -3
- package/report/assets/templates.html +4 -28
- package/report/renderer/category-renderer.js +19 -17
- package/report/renderer/components.js +119 -202
- package/report/renderer/crc-details-renderer.js +7 -6
- package/report/renderer/details-renderer.js +7 -6
- package/report/renderer/dom.js +1 -1
- package/report/renderer/element-screenshot-renderer.js +3 -3
- package/report/renderer/{i18n.d.ts → i18n-formatter.d.ts} +3 -9
- package/report/renderer/{i18n.js → i18n-formatter.js} +2 -11
- package/report/renderer/performance-category-renderer.js +17 -15
- package/report/renderer/pwa-category-renderer.js +5 -4
- package/report/renderer/report-globals.d.ts +21 -0
- package/report/renderer/report-globals.js +49 -0
- package/report/renderer/report-renderer.js +22 -24
- package/report/renderer/report-ui-features.js +9 -8
- package/report/renderer/report-utils.d.ts +116 -0
- package/report/renderer/{util.js → report-utils.js} +100 -415
- package/report/renderer/snippet-renderer.js +3 -2
- package/report/test/renderer/category-renderer-test.js +10 -5
- package/report/test/renderer/crc-details-renderer-test.js +8 -4
- package/report/test/renderer/details-renderer-test.js +8 -4
- package/report/test/renderer/dom-test.js +8 -4
- package/report/test/renderer/element-screenshot-renderer-test.js +9 -5
- package/report/test/renderer/{i18n-test.js → i18n-formatter-test.js} +15 -21
- package/report/test/renderer/performance-category-renderer-test.js +15 -10
- package/report/test/renderer/pwa-category-renderer-test.js +11 -6
- package/report/test/renderer/report-renderer-test.js +2 -2
- package/report/test/renderer/report-ui-features-test.js +3 -3
- package/report/test/renderer/{util-test.js → report-utils-test.js} +37 -235
- package/report/test/renderer/snippet-renderer-test.js +8 -4
- package/report/test-assets/lhr-3.0.0.json +2332 -0
- package/report/test-assets/lhr-4.3.0.json +4754 -0
- package/report/test-assets/lhr-5.0.0.json +5994 -0
- package/report/test-assets/lhr-6.0.0.json +7222 -0
- package/report/test-assets/lhr-8.5.0.json +8679 -0
- package/shared/localization/format.js +5 -2
- package/shared/localization/locales/ar-XB.json +50 -50
- package/shared/localization/locales/ar.json +50 -50
- package/shared/localization/locales/bg.json +50 -50
- package/shared/localization/locales/ca.json +50 -50
- package/shared/localization/locales/cs.json +50 -50
- package/shared/localization/locales/da.json +50 -50
- package/shared/localization/locales/de.json +50 -50
- package/shared/localization/locales/el.json +50 -50
- package/shared/localization/locales/en-GB.json +50 -50
- package/shared/localization/locales/en-US.json +55 -55
- package/shared/localization/locales/en-XA.json +50 -50
- package/shared/localization/locales/en-XL.json +55 -55
- package/shared/localization/locales/es-419.json +50 -50
- package/shared/localization/locales/es.json +50 -50
- package/shared/localization/locales/fi.json +50 -50
- package/shared/localization/locales/fil.json +50 -50
- package/shared/localization/locales/fr.json +50 -50
- package/shared/localization/locales/he.json +50 -50
- package/shared/localization/locales/hi.json +50 -50
- package/shared/localization/locales/hr.json +50 -50
- package/shared/localization/locales/hu.json +50 -50
- package/shared/localization/locales/id.json +50 -50
- package/shared/localization/locales/it.json +50 -50
- package/shared/localization/locales/ja.json +50 -50
- package/shared/localization/locales/ko.json +50 -50
- package/shared/localization/locales/lt.json +50 -50
- package/shared/localization/locales/lv.json +50 -50
- package/shared/localization/locales/nl.json +50 -50
- package/shared/localization/locales/no.json +50 -50
- package/shared/localization/locales/pl.json +50 -50
- package/shared/localization/locales/pt-PT.json +50 -50
- package/shared/localization/locales/pt.json +50 -50
- package/shared/localization/locales/ro.json +50 -50
- package/shared/localization/locales/ru.json +50 -50
- package/shared/localization/locales/sk.json +50 -50
- package/shared/localization/locales/sl.json +50 -50
- package/shared/localization/locales/sr-Latn.json +50 -50
- package/shared/localization/locales/sr.json +50 -50
- package/shared/localization/locales/sv.json +50 -50
- package/shared/localization/locales/ta.json +50 -50
- package/shared/localization/locales/te.json +50 -50
- package/shared/localization/locales/th.json +50 -50
- package/shared/localization/locales/tr.json +50 -50
- package/shared/localization/locales/uk.json +50 -50
- package/shared/localization/locales/vi.json +50 -50
- package/shared/localization/locales/zh-HK.json +50 -50
- package/shared/localization/locales/zh-TW.json +50 -50
- package/shared/localization/locales/zh.json +50 -50
- package/shared/test/util-test.js +214 -0
- package/shared/util.d.ts +122 -0
- package/shared/util.js +332 -0
- package/types/artifacts.d.ts +14 -0
- package/types/lhr/lhr.d.ts +33 -0
- package/report/renderer/util.d.ts +0 -230
package/.codecov.yml
CHANGED
package/core/audits/audit.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import * as LH from '../../types/lh.js';
|
|
8
8
|
import {isUnderTest} from '../lib/lh-env.js';
|
|
9
9
|
import * as statistics from '../lib/statistics.js';
|
|
10
|
-
import {Util} from '
|
|
10
|
+
import {Util} from '../../shared/util.js';
|
|
11
11
|
|
|
12
12
|
const DEFAULT_PASS = 'defaultPass';
|
|
13
13
|
|
package/core/audits/bf-cache.js
CHANGED
|
@@ -15,7 +15,7 @@ const UIStrings = {
|
|
|
15
15
|
/** Title of a diagnostic Lighthouse audit that identifies when the back/forward cache is being used. "back/forward" refers to the back and forward buttons found in modern browsers. This title is shown to users if the page attempted to restore from the back/forward cache but the back/forward cache was not used. */
|
|
16
16
|
failureTitle: 'Page prevented back/forward cache restoration',
|
|
17
17
|
/** Description of a diagnostic Lighthouse audit that identifies when the back/forward cache is being used. "back/forward" refers to the back and forward buttons found in modern browsers. */
|
|
18
|
-
description: 'Many navigations are performed by going back to a previous page, or forwards again. The back/forward cache (bfcache) can speed up these return navigations. [Learn more about the bfcache](https://
|
|
18
|
+
description: 'Many navigations are performed by going back to a previous page, or forwards again. The back/forward cache (bfcache) can speed up these return navigations. [Learn more about the bfcache](https://developer.chrome.com/docs/lighthouse/performance/bf-cache/)',
|
|
19
19
|
/** Failure type for an error that the user should be able to address themselves. Shown in a table column with other failure types. */
|
|
20
20
|
actionableFailureType: 'Actionable',
|
|
21
21
|
/** Failure type for an error that the user cannot address in the page's code. Shown in a table column with other failure types. */
|
|
@@ -78,6 +78,7 @@ export namespace UIStrings {
|
|
|
78
78
|
const title: string;
|
|
79
79
|
const description: string;
|
|
80
80
|
}
|
|
81
|
+
import { Audit } from "../audit.js";
|
|
81
82
|
import { ByteEfficiencyAudit } from "./byte-efficiency-audit.js";
|
|
82
83
|
/**
|
|
83
84
|
* Takes a list of patterns (consisting of a name identifier and a RegExp expression string)
|
|
@@ -19,10 +19,11 @@
|
|
|
19
19
|
|
|
20
20
|
import fs from 'fs';
|
|
21
21
|
|
|
22
|
+
import {Audit} from '../audit.js';
|
|
22
23
|
import {ByteEfficiencyAudit} from './byte-efficiency-audit.js';
|
|
24
|
+
import {EntityClassification} from '../../computed/entity-classification.js';
|
|
23
25
|
import {JSBundles} from '../../computed/js-bundles.js';
|
|
24
26
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
25
|
-
import thirdPartyWeb from '../../lib/third-party-web.js';
|
|
26
27
|
import {getRequestForScript} from '../../lib/script-helpers.js';
|
|
27
28
|
import {LH_ROOT} from '../../../root.js';
|
|
28
29
|
|
|
@@ -401,7 +402,10 @@ class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
401
402
|
* @return {Promise<ByteEfficiencyProduct>}
|
|
402
403
|
*/
|
|
403
404
|
static async audit_(artifacts, networkRecords, context) {
|
|
404
|
-
const
|
|
405
|
+
const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
|
|
406
|
+
const classifiedEntities = await EntityClassification.request(
|
|
407
|
+
{URL: artifacts.URL, devtoolsLog}, context);
|
|
408
|
+
|
|
405
409
|
const bundles = await JSBundles.request(artifacts, context);
|
|
406
410
|
|
|
407
411
|
/** @type {Item[]} */
|
|
@@ -450,7 +454,7 @@ class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
450
454
|
const wastedBytesByUrl = new Map();
|
|
451
455
|
for (const item of items) {
|
|
452
456
|
// Only estimate savings if first party code has legacy code.
|
|
453
|
-
if (
|
|
457
|
+
if (classifiedEntities.isFirstParty(item.url)) {
|
|
454
458
|
wastedBytesByUrl.set(item.url, item.wastedBytes);
|
|
455
459
|
}
|
|
456
460
|
}
|
|
@@ -22,9 +22,10 @@ declare class UsesHTTP2Audit extends Audit {
|
|
|
22
22
|
* for the same origin at the exact same time.
|
|
23
23
|
*
|
|
24
24
|
* @param {LH.Artifacts.NetworkRequest} networkRequest
|
|
25
|
+
* @param {LH.Artifacts.EntityClassification} classifiedEntities
|
|
25
26
|
* @return {boolean}
|
|
26
27
|
*/
|
|
27
|
-
static isStaticAsset(networkRequest: LH.Artifacts.NetworkRequest): boolean;
|
|
28
|
+
static isStaticAsset(networkRequest: LH.Artifacts.NetworkRequest, classifiedEntities: LH.Artifacts.EntityClassification): boolean;
|
|
28
29
|
/**
|
|
29
30
|
* Determine the set of resources that aren't HTTP/2 but should be.
|
|
30
31
|
* We're a little conservative about what we surface for a few reasons:
|
|
@@ -45,9 +46,10 @@ declare class UsesHTTP2Audit extends Audit {
|
|
|
45
46
|
* https://www.cachefly.com/http-2-is-not-a-magic-bullet/
|
|
46
47
|
*
|
|
47
48
|
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
49
|
+
* @param {LH.Artifacts.EntityClassification} classifiedEntities
|
|
48
50
|
* @return {Array<{url: string, protocol: string}>}
|
|
49
51
|
*/
|
|
50
|
-
static determineNonHttp2Resources(networkRecords: Array<LH.Artifacts.NetworkRequest
|
|
52
|
+
static determineNonHttp2Resources(networkRecords: Array<LH.Artifacts.NetworkRequest>, classifiedEntities: LH.Artifacts.EntityClassification): Array<{
|
|
51
53
|
url: string;
|
|
52
54
|
protocol: string;
|
|
53
55
|
}>;
|
|
@@ -66,4 +68,5 @@ export namespace UIStrings {
|
|
|
66
68
|
}
|
|
67
69
|
import { Audit } from "../audit.js";
|
|
68
70
|
import { NetworkRequest } from "../../lib/network-request.js";
|
|
71
|
+
import { EntityClassification } from "../../computed/entity-classification.js";
|
|
69
72
|
//# sourceMappingURL=uses-http2.d.ts.map
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
/** @typedef {import('../../lib/dependency-graph/base-node.js').Node} Node */
|
|
14
14
|
|
|
15
15
|
import {Audit} from '../audit.js';
|
|
16
|
-
import
|
|
16
|
+
import {EntityClassification} from '../../computed/entity-classification.js';
|
|
17
17
|
import UrlUtils from '../../lib/url-utils.js';
|
|
18
18
|
import {ByteEfficiencyAudit} from '../byte-efficiency/byte-efficiency-audit.js';
|
|
19
19
|
import {LanternInteractive} from '../../computed/metrics/lantern-interactive.js';
|
|
@@ -120,16 +120,19 @@ class UsesHTTP2Audit extends Audit {
|
|
|
120
120
|
* for the same origin at the exact same time.
|
|
121
121
|
*
|
|
122
122
|
* @param {LH.Artifacts.NetworkRequest} networkRequest
|
|
123
|
+
* @param {LH.Artifacts.EntityClassification} classifiedEntities
|
|
123
124
|
* @return {boolean}
|
|
124
125
|
*/
|
|
125
|
-
static isStaticAsset(networkRequest) {
|
|
126
|
+
static isStaticAsset(networkRequest, classifiedEntities) {
|
|
126
127
|
if (!STATIC_RESOURCE_TYPES.has(networkRequest.resourceType)) return false;
|
|
127
128
|
|
|
128
129
|
// Resources from third-parties that are less than 100 bytes are usually tracking pixels, not actual resources.
|
|
129
130
|
// They can masquerade as static types though (gifs, documents, etc)
|
|
130
131
|
if (networkRequest.resourceSize < 100) {
|
|
131
|
-
|
|
132
|
-
|
|
132
|
+
// This logic needs to be revisited.
|
|
133
|
+
// See https://github.com/GoogleChrome/lighthouse/issues/14661
|
|
134
|
+
const entity = classifiedEntities.entityByUrl.get(networkRequest.url);
|
|
135
|
+
if (entity && !entity.isUnrecognized) return false;
|
|
133
136
|
}
|
|
134
137
|
|
|
135
138
|
return true;
|
|
@@ -155,9 +158,10 @@ class UsesHTTP2Audit extends Audit {
|
|
|
155
158
|
* https://www.cachefly.com/http-2-is-not-a-magic-bullet/
|
|
156
159
|
*
|
|
157
160
|
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
161
|
+
* @param {LH.Artifacts.EntityClassification} classifiedEntities
|
|
158
162
|
* @return {Array<{url: string, protocol: string}>}
|
|
159
163
|
*/
|
|
160
|
-
static determineNonHttp2Resources(networkRecords) {
|
|
164
|
+
static determineNonHttp2Resources(networkRecords, classifiedEntities) {
|
|
161
165
|
/** @type {Array<{url: string, protocol: string}>} */
|
|
162
166
|
const nonHttp2Resources = [];
|
|
163
167
|
|
|
@@ -166,7 +170,7 @@ class UsesHTTP2Audit extends Audit {
|
|
|
166
170
|
/** @type {Map<string, Array<LH.Artifacts.NetworkRequest>>} */
|
|
167
171
|
const groupedByOrigin = new Map();
|
|
168
172
|
for (const record of networkRecords) {
|
|
169
|
-
if (!UsesHTTP2Audit.isStaticAsset(record)) continue;
|
|
173
|
+
if (!UsesHTTP2Audit.isStaticAsset(record, classifiedEntities)) continue;
|
|
170
174
|
if (UrlUtils.isLikeLocalhost(record.parsedURL.host)) continue;
|
|
171
175
|
const existing = groupedByOrigin.get(record.parsedURL.securityOrigin) || [];
|
|
172
176
|
existing.push(record);
|
|
@@ -203,7 +207,8 @@ class UsesHTTP2Audit extends Audit {
|
|
|
203
207
|
const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
|
|
204
208
|
const URL = artifacts.URL;
|
|
205
209
|
const networkRecords = await NetworkRecords.request(devtoolsLog, context);
|
|
206
|
-
const
|
|
210
|
+
const classifiedEntities = await EntityClassification.request({URL, devtoolsLog}, context);
|
|
211
|
+
const resources = UsesHTTP2Audit.determineNonHttp2Resources(networkRecords, classifiedEntities);
|
|
207
212
|
|
|
208
213
|
let displayValue;
|
|
209
214
|
if (resources.length > 0) {
|
|
@@ -7,9 +7,10 @@ declare class ScreenshotThumbnails extends Audit {
|
|
|
7
7
|
* ratio of the original thumbnail.
|
|
8
8
|
*
|
|
9
9
|
* @param {ReturnType<SpeedlineFrame['getParsedImage']>} imageData
|
|
10
|
+
* @param {number} scaledWidth
|
|
10
11
|
* @return {{width: number, height: number, data: Uint8Array}}
|
|
11
12
|
*/
|
|
12
|
-
static scaleImageToThumbnail(imageData: ReturnType<SpeedlineFrame['getParsedImage']
|
|
13
|
+
static scaleImageToThumbnail(imageData: ReturnType<SpeedlineFrame['getParsedImage']>, scaledWidth: number): {
|
|
13
14
|
width: number;
|
|
14
15
|
height: number;
|
|
15
16
|
data: Uint8Array;
|
|
@@ -10,8 +10,7 @@ import {Audit} from './audit.js';
|
|
|
10
10
|
import {LighthouseError} from '../lib/lh-error.js';
|
|
11
11
|
import {Speedline} from '../computed/speedline.js';
|
|
12
12
|
|
|
13
|
-
const NUMBER_OF_THUMBNAILS =
|
|
14
|
-
const THUMBNAIL_WIDTH = 120;
|
|
13
|
+
const NUMBER_OF_THUMBNAILS = 8;
|
|
15
14
|
|
|
16
15
|
/** @typedef {LH.Artifacts.Speedline['frames'][0]} SpeedlineFrame */
|
|
17
16
|
|
|
@@ -34,10 +33,10 @@ class ScreenshotThumbnails extends Audit {
|
|
|
34
33
|
* ratio of the original thumbnail.
|
|
35
34
|
*
|
|
36
35
|
* @param {ReturnType<SpeedlineFrame['getParsedImage']>} imageData
|
|
36
|
+
* @param {number} scaledWidth
|
|
37
37
|
* @return {{width: number, height: number, data: Uint8Array}}
|
|
38
38
|
*/
|
|
39
|
-
static scaleImageToThumbnail(imageData) {
|
|
40
|
-
const scaledWidth = THUMBNAIL_WIDTH;
|
|
39
|
+
static scaleImageToThumbnail(imageData, scaledWidth) {
|
|
41
40
|
const scaleFactor = imageData.width / scaledWidth;
|
|
42
41
|
const scaledHeight = Math.floor(imageData.height / scaleFactor);
|
|
43
42
|
|
|
@@ -79,6 +78,8 @@ class ScreenshotThumbnails extends Audit {
|
|
|
79
78
|
|
|
80
79
|
// Make the minimum time range 3s so sites that load super quickly don't get a single screenshot
|
|
81
80
|
const minimumTimelineDuration = context.options.minimumTimelineDuration || 3000;
|
|
81
|
+
const numberOfThumbnails = context.options.numberOfThumbnails || NUMBER_OF_THUMBNAILS;
|
|
82
|
+
const thumbnailWidth = context.options.thumbnailWidth || null;
|
|
82
83
|
|
|
83
84
|
const thumbnails = [];
|
|
84
85
|
const analyzedFrames = speedline.frames.filter(frame => !frame.isProgressInterpolated());
|
|
@@ -91,13 +92,13 @@ class ScreenshotThumbnails extends Audit {
|
|
|
91
92
|
throw new LighthouseError(LighthouseError.errors.INVALID_SPEEDLINE);
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
for (let i = 1; i <=
|
|
95
|
-
const targetTimestamp = speedline.beginning + timelineEnd * i /
|
|
95
|
+
for (let i = 1; i <= numberOfThumbnails; i++) {
|
|
96
|
+
const targetTimestamp = speedline.beginning + timelineEnd * i / numberOfThumbnails;
|
|
96
97
|
|
|
97
98
|
/** @type {SpeedlineFrame} */
|
|
98
99
|
// @ts-expect-error - there will always be at least one frame by this point. TODO: use nonnullable assertion in TS2.9
|
|
99
100
|
let frameForTimestamp = null;
|
|
100
|
-
if (i ===
|
|
101
|
+
if (i === numberOfThumbnails) {
|
|
101
102
|
frameForTimestamp = analyzedFrames[analyzedFrames.length - 1];
|
|
102
103
|
} else {
|
|
103
104
|
analyzedFrames.forEach(frame => {
|
|
@@ -111,11 +112,15 @@ class ScreenshotThumbnails extends Audit {
|
|
|
111
112
|
const cachedThumbnail = cachedThumbnails.get(frameForTimestamp);
|
|
112
113
|
if (cachedThumbnail) {
|
|
113
114
|
base64Data = cachedThumbnail;
|
|
114
|
-
} else {
|
|
115
|
+
} else if (thumbnailWidth !== null) {
|
|
115
116
|
const imageData = frameForTimestamp.getParsedImage();
|
|
116
|
-
const thumbnailImageData =
|
|
117
|
+
const thumbnailImageData =
|
|
118
|
+
ScreenshotThumbnails.scaleImageToThumbnail(imageData, thumbnailWidth);
|
|
117
119
|
base64Data = jpeg.encode(thumbnailImageData, 90).data.toString('base64');
|
|
118
120
|
cachedThumbnails.set(frameForTimestamp, base64Data);
|
|
121
|
+
} else {
|
|
122
|
+
base64Data = frameForTimestamp.getImage().toString('base64');
|
|
123
|
+
cachedThumbnails.set(frameForTimestamp, base64Data);
|
|
119
124
|
}
|
|
120
125
|
thumbnails.push({
|
|
121
126
|
timing: Math.round(targetTimestamp - speedline.beginning),
|
|
@@ -15,10 +15,10 @@ declare class ThirdPartyFacades extends Audit {
|
|
|
15
15
|
static condenseItems(items: import('./third-party-summary.js').URLSummary[]): void;
|
|
16
16
|
/**
|
|
17
17
|
* @param {Map<string, import('./third-party-summary.js').Summary>} byURL
|
|
18
|
-
* @param {
|
|
18
|
+
* @param {LH.Artifacts.EntityClassification} classifiedEntities
|
|
19
19
|
* @return {FacadableProduct[]}
|
|
20
20
|
*/
|
|
21
|
-
static getProductsWithFacade(byURL: Map<string, import('./third-party-summary.js').Summary>,
|
|
21
|
+
static getProductsWithFacade(byURL: Map<string, import('./third-party-summary.js').Summary>, classifiedEntities: LH.Artifacts.EntityClassification): FacadableProduct[];
|
|
22
22
|
/**
|
|
23
23
|
* @param {LH.Artifacts} artifacts
|
|
24
24
|
* @param {LH.Audit.Context} context
|
|
@@ -38,4 +38,5 @@ export namespace UIStrings {
|
|
|
38
38
|
const categorySocial: string;
|
|
39
39
|
}
|
|
40
40
|
import { Audit } from "./audit.js";
|
|
41
|
+
import { EntityClassification } from "../computed/entity-classification.js";
|
|
41
42
|
//# sourceMappingURL=third-party-facades.d.ts.map
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
|
|
22
22
|
import {Audit} from './audit.js';
|
|
23
23
|
import * as i18n from '../lib/i18n/i18n.js';
|
|
24
|
+
import {EntityClassification} from '../computed/entity-classification.js';
|
|
24
25
|
import thirdPartyWeb from '../lib/third-party-web.js';
|
|
25
26
|
import {NetworkRecords} from '../computed/network-records.js';
|
|
26
|
-
import {MainResource} from '../computed/main-resource.js';
|
|
27
27
|
import {MainThreadTasks} from '../computed/main-thread-tasks.js';
|
|
28
28
|
import ThirdPartySummary from './third-party-summary.js';
|
|
29
29
|
|
|
@@ -121,15 +121,15 @@ class ThirdPartyFacades extends Audit {
|
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
123
|
* @param {Map<string, import('./third-party-summary.js').Summary>} byURL
|
|
124
|
-
* @param {
|
|
124
|
+
* @param {LH.Artifacts.EntityClassification} classifiedEntities
|
|
125
125
|
* @return {FacadableProduct[]}
|
|
126
126
|
*/
|
|
127
|
-
static getProductsWithFacade(byURL,
|
|
127
|
+
static getProductsWithFacade(byURL, classifiedEntities) {
|
|
128
128
|
/** @type {Map<string, FacadableProduct>} */
|
|
129
129
|
const facadableProductMap = new Map();
|
|
130
130
|
for (const url of byURL.keys()) {
|
|
131
|
-
const entity =
|
|
132
|
-
if (!entity ||
|
|
131
|
+
const entity = classifiedEntities.entityByUrl.get(url);
|
|
132
|
+
if (!entity || classifiedEntities.isFirstParty(url)) continue;
|
|
133
133
|
|
|
134
134
|
const product = thirdPartyWeb.getProduct(url);
|
|
135
135
|
if (!product || !product.facades || !product.facades.length) continue;
|
|
@@ -151,14 +151,15 @@ class ThirdPartyFacades extends Audit {
|
|
|
151
151
|
const trace = artifacts.traces[Audit.DEFAULT_PASS];
|
|
152
152
|
const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
|
|
153
153
|
const networkRecords = await NetworkRecords.request(devtoolsLog, context);
|
|
154
|
-
const
|
|
155
|
-
|
|
154
|
+
const classifiedEntities = await EntityClassification.request(
|
|
155
|
+
{URL: artifacts.URL, devtoolsLog}, context);
|
|
156
156
|
const tasks = await MainThreadTasks.request(trace, context);
|
|
157
157
|
const multiplier = settings.throttlingMethod === 'simulate' ?
|
|
158
158
|
settings.throttling.cpuSlowdownMultiplier : 1;
|
|
159
|
-
const summaries = ThirdPartySummary.getSummaries(networkRecords, tasks, multiplier
|
|
159
|
+
const summaries = ThirdPartySummary.getSummaries(networkRecords, tasks, multiplier,
|
|
160
|
+
classifiedEntities);
|
|
160
161
|
const facadableProducts =
|
|
161
|
-
ThirdPartyFacades.getProductsWithFacade(summaries.byURL,
|
|
162
|
+
ThirdPartyFacades.getProductsWithFacade(summaries.byURL, classifiedEntities);
|
|
162
163
|
|
|
163
164
|
/** @type {LH.Audit.Details.TableItem[]} */
|
|
164
165
|
const results = [];
|
|
@@ -188,6 +189,8 @@ class ThirdPartyFacades extends Audit {
|
|
|
188
189
|
transferSize: entitySummary.transferSize,
|
|
189
190
|
blockingTime: entitySummary.blockingTime,
|
|
190
191
|
subItems: {type: 'subitems', items},
|
|
192
|
+
// Add entity manually since facades don't have a single `url`.
|
|
193
|
+
entity: entity.name,
|
|
191
194
|
});
|
|
192
195
|
}
|
|
193
196
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export default ThirdPartySummary;
|
|
2
|
-
export type ThirdPartyEntity = import("third-party-web").IEntity;
|
|
3
2
|
export type Summary = {
|
|
4
3
|
mainThreadTime: number;
|
|
5
4
|
transferSize: number;
|
|
@@ -14,7 +13,7 @@ export type SummaryMaps = {
|
|
|
14
13
|
/**
|
|
15
14
|
* Map of impact summaries for each entity.
|
|
16
15
|
*/
|
|
17
|
-
byEntity: Map<
|
|
16
|
+
byEntity: Map<LH.Artifacts.Entity, Summary>;
|
|
18
17
|
/**
|
|
19
18
|
* Map of impact summaries for each URL.
|
|
20
19
|
*/
|
|
@@ -22,7 +21,7 @@ export type SummaryMaps = {
|
|
|
22
21
|
/**
|
|
23
22
|
* Map of URLs under each entity.
|
|
24
23
|
*/
|
|
25
|
-
urls: Map<
|
|
24
|
+
urls: Map<LH.Artifacts.Entity, string[]>;
|
|
26
25
|
};
|
|
27
26
|
declare class ThirdPartySummary extends Audit {
|
|
28
27
|
/**
|
|
@@ -30,16 +29,17 @@ declare class ThirdPartySummary extends Audit {
|
|
|
30
29
|
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
31
30
|
* @param {Array<LH.Artifacts.TaskNode>} mainThreadTasks
|
|
32
31
|
* @param {number} cpuMultiplier
|
|
32
|
+
* @param {LH.Artifacts.EntityClassification} entityClassification
|
|
33
33
|
* @return {SummaryMaps}
|
|
34
34
|
*/
|
|
35
|
-
static getSummaries(networkRecords: Array<LH.Artifacts.NetworkRequest>, mainThreadTasks: Array<LH.Artifacts.TaskNode>, cpuMultiplier: number): SummaryMaps;
|
|
35
|
+
static getSummaries(networkRecords: Array<LH.Artifacts.NetworkRequest>, mainThreadTasks: Array<LH.Artifacts.TaskNode>, cpuMultiplier: number, entityClassification: LH.Artifacts.EntityClassification): SummaryMaps;
|
|
36
36
|
/**
|
|
37
|
-
* @param {
|
|
37
|
+
* @param {LH.Artifacts.Entity} entity
|
|
38
38
|
* @param {SummaryMaps} summaries
|
|
39
39
|
* @param {Summary} stats
|
|
40
40
|
* @return {Array<URLSummary>}
|
|
41
41
|
*/
|
|
42
|
-
static makeSubItems(entity:
|
|
42
|
+
static makeSubItems(entity: LH.Artifacts.Entity, summaries: SummaryMaps, stats: Summary): Array<URLSummary>;
|
|
43
43
|
/**
|
|
44
44
|
* @param {LH.Artifacts} artifacts
|
|
45
45
|
* @param {LH.Audit.Context} context
|
|
@@ -55,4 +55,5 @@ export namespace UIStrings {
|
|
|
55
55
|
const displayValue: string;
|
|
56
56
|
}
|
|
57
57
|
import { Audit } from "./audit.js";
|
|
58
|
+
import { EntityClassification } from "../computed/entity-classification.js";
|
|
58
59
|
//# sourceMappingURL=third-party-summary.d.ts.map
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {Audit} from './audit.js';
|
|
8
|
+
import {EntityClassification} from '../computed/entity-classification.js';
|
|
8
9
|
import * as i18n from '../lib/i18n/i18n.js';
|
|
9
|
-
import thirdPartyWeb from '../lib/third-party-web.js';
|
|
10
10
|
import {NetworkRecords} from '../computed/network-records.js';
|
|
11
11
|
import {MainThreadTasks} from '../computed/main-thread-tasks.js';
|
|
12
12
|
import {getJavaScriptURLs, getAttributableURLForTask} from '../lib/tracehouse/task-summary.js';
|
|
@@ -33,8 +33,6 @@ const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
|
33
33
|
// A page passes when all third-party code blocks for less than 250 ms.
|
|
34
34
|
const PASS_THRESHOLD_IN_MS = 250;
|
|
35
35
|
|
|
36
|
-
/** @typedef {import("third-party-web").IEntity} ThirdPartyEntity */
|
|
37
|
-
|
|
38
36
|
/**
|
|
39
37
|
* @typedef Summary
|
|
40
38
|
* @property {number} mainThreadTime
|
|
@@ -50,9 +48,9 @@ const PASS_THRESHOLD_IN_MS = 250;
|
|
|
50
48
|
*/
|
|
51
49
|
|
|
52
50
|
/** @typedef SummaryMaps
|
|
53
|
-
* @property {Map<
|
|
51
|
+
* @property {Map<LH.Artifacts.Entity, Summary>} byEntity Map of impact summaries for each entity.
|
|
54
52
|
* @property {Map<string, Summary>} byURL Map of impact summaries for each URL.
|
|
55
|
-
* @property {Map<
|
|
53
|
+
* @property {Map<LH.Artifacts.Entity, string[]>} urls Map of URLs under each entity.
|
|
56
54
|
*/
|
|
57
55
|
|
|
58
56
|
/**
|
|
@@ -83,12 +81,13 @@ class ThirdPartySummary extends Audit {
|
|
|
83
81
|
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
84
82
|
* @param {Array<LH.Artifacts.TaskNode>} mainThreadTasks
|
|
85
83
|
* @param {number} cpuMultiplier
|
|
84
|
+
* @param {LH.Artifacts.EntityClassification} entityClassification
|
|
86
85
|
* @return {SummaryMaps}
|
|
87
86
|
*/
|
|
88
|
-
static getSummaries(networkRecords, mainThreadTasks, cpuMultiplier) {
|
|
87
|
+
static getSummaries(networkRecords, mainThreadTasks, cpuMultiplier, entityClassification) {
|
|
89
88
|
/** @type {Map<string, Summary>} */
|
|
90
89
|
const byURL = new Map();
|
|
91
|
-
/** @type {Map<
|
|
90
|
+
/** @type {Map<LH.Artifacts.Entity, Summary>} */
|
|
92
91
|
const byEntity = new Map();
|
|
93
92
|
const defaultSummary = {mainThreadTime: 0, blockingTime: 0, transferSize: 0};
|
|
94
93
|
|
|
@@ -114,11 +113,11 @@ class ThirdPartySummary extends Audit {
|
|
|
114
113
|
byURL.set(attributableURL, urlSummary);
|
|
115
114
|
}
|
|
116
115
|
|
|
117
|
-
// Map each URL's stat to a particular
|
|
118
|
-
/** @type {Map<
|
|
116
|
+
// Map each URL's stat to a particular entity.
|
|
117
|
+
/** @type {Map<LH.Artifacts.Entity, string[]>} */
|
|
119
118
|
const urls = new Map();
|
|
120
119
|
for (const [url, urlSummary] of byURL.entries()) {
|
|
121
|
-
const entity =
|
|
120
|
+
const entity = entityClassification.entityByUrl.get(url);
|
|
122
121
|
if (!entity) {
|
|
123
122
|
byURL.delete(url);
|
|
124
123
|
continue;
|
|
@@ -139,7 +138,7 @@ class ThirdPartySummary extends Audit {
|
|
|
139
138
|
}
|
|
140
139
|
|
|
141
140
|
/**
|
|
142
|
-
* @param {
|
|
141
|
+
* @param {LH.Artifacts.Entity} entity
|
|
143
142
|
* @param {SummaryMaps} summaries
|
|
144
143
|
* @param {Summary} stats
|
|
145
144
|
* @return {Array<URLSummary>}
|
|
@@ -197,18 +196,21 @@ class ThirdPartySummary extends Audit {
|
|
|
197
196
|
const trace = artifacts.traces[Audit.DEFAULT_PASS];
|
|
198
197
|
const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
|
|
199
198
|
const networkRecords = await NetworkRecords.request(devtoolsLog, context);
|
|
200
|
-
const
|
|
199
|
+
const classifiedEntities = await EntityClassification.request(
|
|
200
|
+
{URL: artifacts.URL, devtoolsLog}, context);
|
|
201
|
+
const firstPartyEntity = classifiedEntities.firstParty;
|
|
201
202
|
const tasks = await MainThreadTasks.request(trace, context);
|
|
202
203
|
const multiplier = settings.throttlingMethod === 'simulate' ?
|
|
203
204
|
settings.throttling.cpuSlowdownMultiplier : 1;
|
|
204
205
|
|
|
205
|
-
const summaries = ThirdPartySummary.getSummaries(
|
|
206
|
+
const summaries = ThirdPartySummary.getSummaries(
|
|
207
|
+
networkRecords, tasks, multiplier, classifiedEntities);
|
|
206
208
|
const overallSummary = {wastedBytes: 0, wastedMs: 0};
|
|
207
209
|
|
|
208
210
|
const results = Array.from(summaries.byEntity.entries())
|
|
209
211
|
// Don't consider the page we're on to be third-party.
|
|
210
212
|
// e.g. Facebook SDK isn't a third-party script on facebook.com
|
|
211
|
-
.filter(([entity]) => !(
|
|
213
|
+
.filter(([entity]) => !(firstPartyEntity && firstPartyEntity === entity))
|
|
212
214
|
.map(([entity, stats]) => {
|
|
213
215
|
overallSummary.wastedBytes += stats.transferSize;
|
|
214
216
|
overallSummary.wastedMs += stats.blockingTime;
|
|
@@ -3,14 +3,15 @@ declare class ValidSourceMaps extends Audit {
|
|
|
3
3
|
/**
|
|
4
4
|
* Returns true if the size of the script exceeds a static threshold.
|
|
5
5
|
* @param {LH.Artifacts.Script} script
|
|
6
|
-
* @param {
|
|
6
|
+
* @param {LH.Artifacts.EntityClassification} classifiedEntities
|
|
7
7
|
* @return {boolean}
|
|
8
8
|
*/
|
|
9
|
-
static isLargeFirstPartyJS(script: LH.Artifacts.Script,
|
|
9
|
+
static isLargeFirstPartyJS(script: LH.Artifacts.Script, classifiedEntities: LH.Artifacts.EntityClassification): boolean;
|
|
10
10
|
/**
|
|
11
11
|
* @param {LH.Artifacts} artifacts
|
|
12
|
+
* @param {LH.Audit.Context} context
|
|
12
13
|
*/
|
|
13
|
-
static audit(artifacts: LH.Artifacts): Promise<{
|
|
14
|
+
static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<{
|
|
14
15
|
score: number;
|
|
15
16
|
details: import("../../types/lhr/audit-details.js").default.Table;
|
|
16
17
|
}>;
|
|
@@ -24,4 +25,5 @@ export namespace UIStrings {
|
|
|
24
25
|
const missingSourceMapItemsWarningMesssage: string;
|
|
25
26
|
}
|
|
26
27
|
import { Audit } from "./audit.js";
|
|
28
|
+
import { EntityClassification } from "../computed/entity-classification.js";
|
|
27
29
|
//# sourceMappingURL=valid-source-maps.d.ts.map
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import thirdPartyWeb from '../lib/third-party-web.js';
|
|
8
7
|
import {Audit} from './audit.js';
|
|
8
|
+
import {EntityClassification} from '../computed/entity-classification.js';
|
|
9
9
|
import * as i18n from '../lib/i18n/i18n.js';
|
|
10
10
|
|
|
11
11
|
const UIStrings = {
|
|
@@ -43,31 +43,33 @@ class ValidSourceMaps extends Audit {
|
|
|
43
43
|
title: str_(UIStrings.title),
|
|
44
44
|
failureTitle: str_(UIStrings.failureTitle),
|
|
45
45
|
description: str_(UIStrings.description),
|
|
46
|
-
requiredArtifacts: ['Scripts', 'SourceMaps', 'URL'],
|
|
46
|
+
requiredArtifacts: ['Scripts', 'SourceMaps', 'URL', 'devtoolsLogs'],
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Returns true if the size of the script exceeds a static threshold.
|
|
52
52
|
* @param {LH.Artifacts.Script} script
|
|
53
|
-
* @param {
|
|
53
|
+
* @param {LH.Artifacts.EntityClassification} classifiedEntities
|
|
54
54
|
* @return {boolean}
|
|
55
55
|
*/
|
|
56
|
-
static isLargeFirstPartyJS(script,
|
|
56
|
+
static isLargeFirstPartyJS(script, classifiedEntities) {
|
|
57
57
|
if (!script.length) return false;
|
|
58
58
|
|
|
59
59
|
const isLargeJS = script.length >= LARGE_JS_BYTE_THRESHOLD;
|
|
60
|
-
const isFirstPartyJS = script.url ?
|
|
61
|
-
thirdPartyWeb.isFirstParty(script.url, thirdPartyWeb.getEntity(finalURL)) : false;
|
|
62
|
-
|
|
60
|
+
const isFirstPartyJS = script.url ? classifiedEntities.isFirstParty(script.url) : false;
|
|
63
61
|
return isLargeJS && isFirstPartyJS;
|
|
64
62
|
}
|
|
65
63
|
|
|
66
64
|
/**
|
|
67
65
|
* @param {LH.Artifacts} artifacts
|
|
66
|
+
* @param {LH.Audit.Context} context
|
|
68
67
|
*/
|
|
69
|
-
static async audit(artifacts) {
|
|
68
|
+
static async audit(artifacts, context) {
|
|
70
69
|
const {SourceMaps} = artifacts;
|
|
70
|
+
const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
|
|
71
|
+
const classifiedEntities = await EntityClassification.request(
|
|
72
|
+
{URL: artifacts.URL, devtoolsLog}, context);
|
|
71
73
|
|
|
72
74
|
/** @type {Set<string>} */
|
|
73
75
|
const isMissingMapForLargeFirstPartyScriptUrl = new Set();
|
|
@@ -77,7 +79,7 @@ class ValidSourceMaps extends Audit {
|
|
|
77
79
|
for (const script of artifacts.Scripts) {
|
|
78
80
|
const sourceMap = SourceMaps.find(m => m.scriptId === script.scriptId);
|
|
79
81
|
const errors = [];
|
|
80
|
-
const isLargeFirstParty = this.isLargeFirstPartyJS(script,
|
|
82
|
+
const isLargeFirstParty = this.isLargeFirstPartyJS(script, classifiedEntities);
|
|
81
83
|
|
|
82
84
|
if (isLargeFirstParty && (!sourceMap || !sourceMap.map)) {
|
|
83
85
|
missingMapsForLargeFirstPartyFile = true;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export { EntityClassificationComputed as EntityClassification };
|
|
2
|
+
export type EntityCache = Map<string, LH.Artifacts.Entity>;
|
|
3
|
+
declare const EntityClassificationComputed: typeof EntityClassification & {
|
|
4
|
+
request: (dependencies: {
|
|
5
|
+
URL: LH.Artifacts['URL'];
|
|
6
|
+
devtoolsLog: import("../index.js").DevtoolsLog;
|
|
7
|
+
}, context: import("../../types/utility-types.js").default.ImmutableObject<{
|
|
8
|
+
computedCache: Map<string, import("../lib/arbitrary-equality-map.js").ArbitraryEqualityMap>;
|
|
9
|
+
}>) => Promise<import("../index.js").Artifacts.EntityClassification>;
|
|
10
|
+
};
|
|
11
|
+
/** @typedef {Map<string, LH.Artifacts.Entity>} EntityCache */
|
|
12
|
+
declare class EntityClassification {
|
|
13
|
+
/**
|
|
14
|
+
* @param {EntityCache} entityCache
|
|
15
|
+
* @param {string} url
|
|
16
|
+
* @return {LH.Artifacts.Entity | undefined}
|
|
17
|
+
*/
|
|
18
|
+
static makeUpAnEntity(entityCache: EntityCache, url: string): LH.Artifacts.Entity | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* @param {{URL: LH.Artifacts['URL'], devtoolsLog: LH.DevtoolsLog}} data
|
|
21
|
+
* @param {LH.Artifacts.ComputedContext} context
|
|
22
|
+
* @return {Promise<LH.Artifacts.EntityClassification>}
|
|
23
|
+
*/
|
|
24
|
+
static compute_(data: {
|
|
25
|
+
URL: LH.Artifacts['URL'];
|
|
26
|
+
devtoolsLog: import("../index.js").DevtoolsLog;
|
|
27
|
+
}, context: LH.Artifacts.ComputedContext): Promise<LH.Artifacts.EntityClassification>;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=entity-classification.d.ts.map
|