lighthouse 11.0.0-dev.20230814 → 11.0.0-dev.20230815
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.
|
@@ -7,9 +7,15 @@ declare class DOMSize extends Audit {
|
|
|
7
7
|
/**
|
|
8
8
|
* @param {LH.Artifacts} artifacts
|
|
9
9
|
* @param {LH.Audit.Context} context
|
|
10
|
-
* @return {
|
|
10
|
+
* @return {Promise<number|undefined>}
|
|
11
11
|
*/
|
|
12
|
-
static
|
|
12
|
+
static computeTbtImpact(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<number | undefined>;
|
|
13
|
+
/**
|
|
14
|
+
* @param {LH.Artifacts} artifacts
|
|
15
|
+
* @param {LH.Audit.Context} context
|
|
16
|
+
* @return {Promise<LH.Audit.Product>}
|
|
17
|
+
*/
|
|
18
|
+
static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
|
|
13
19
|
}
|
|
14
20
|
export namespace UIStrings {
|
|
15
21
|
const title: string;
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import {Audit} from '../audit.js';
|
|
15
15
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
16
|
+
import {TBTImpactTasks} from '../../computed/tbt-impact-tasks.js';
|
|
16
17
|
|
|
17
18
|
const UIStrings = {
|
|
18
19
|
/** Title of a diagnostic audit that provides detail on the size of the web page's DOM. The size of a DOM is characterized by the total number of DOM elements and greatest DOM depth. This descriptive title is shown to users when the amount is acceptable and no user action is required. */
|
|
@@ -53,7 +54,8 @@ class DOMSize extends Audit {
|
|
|
53
54
|
failureTitle: str_(UIStrings.failureTitle),
|
|
54
55
|
description: str_(UIStrings.description),
|
|
55
56
|
scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
|
|
56
|
-
requiredArtifacts: ['DOMStats'],
|
|
57
|
+
requiredArtifacts: ['DOMStats', 'URL', 'GatherContext'],
|
|
58
|
+
__internalOptionalArtifacts: ['traces', 'devtoolsLogs'],
|
|
57
59
|
};
|
|
58
60
|
}
|
|
59
61
|
|
|
@@ -69,13 +71,53 @@ class DOMSize extends Audit {
|
|
|
69
71
|
};
|
|
70
72
|
}
|
|
71
73
|
|
|
74
|
+
/**
|
|
75
|
+
* @param {LH.Artifacts} artifacts
|
|
76
|
+
* @param {LH.Audit.Context} context
|
|
77
|
+
* @return {Promise<number|undefined>}
|
|
78
|
+
*/
|
|
79
|
+
static async computeTbtImpact(artifacts, context) {
|
|
80
|
+
let tbtImpact = 0;
|
|
81
|
+
|
|
82
|
+
// We still want to surface this audit in snapshot mode, but since we don't compute TBT
|
|
83
|
+
// the impact should always be undefined.
|
|
84
|
+
const {GatherContext, devtoolsLogs, traces} = artifacts;
|
|
85
|
+
if (GatherContext.gatherMode !== 'navigation') {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Since the artifacts are optional, it's still possible for them to be missing in navigation mode.
|
|
90
|
+
// Navigation mode does compute TBT so we should surface a numerical savings of 0.
|
|
91
|
+
if (!devtoolsLogs?.[Audit.DEFAULT_PASS] || !traces?.[Audit.DEFAULT_PASS]) {
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const metricComputationData = Audit.makeMetricComputationDataInput(artifacts, context);
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
// The TBT impact of style/layout tasks is correlated to the DOM size.
|
|
99
|
+
// Even in situations where the page forces a style recalc, the DOM size is partially to blame
|
|
100
|
+
// for any time spent blocking the main thread.
|
|
101
|
+
//
|
|
102
|
+
// `tbtImpact` should be exactly 0 for small DOMs since `selfTbtImpact` accounts for the blocking
|
|
103
|
+
// time and not the main thread time.
|
|
104
|
+
const tbtImpactTasks = await TBTImpactTasks.request(metricComputationData, context);
|
|
105
|
+
for (const task of tbtImpactTasks) {
|
|
106
|
+
if (task.group.id !== 'styleLayout') continue;
|
|
107
|
+
tbtImpact += task.selfTbtImpact;
|
|
108
|
+
}
|
|
109
|
+
} catch {}
|
|
110
|
+
|
|
111
|
+
return Math.round(tbtImpact);
|
|
112
|
+
}
|
|
113
|
+
|
|
72
114
|
|
|
73
115
|
/**
|
|
74
116
|
* @param {LH.Artifacts} artifacts
|
|
75
117
|
* @param {LH.Audit.Context} context
|
|
76
|
-
* @return {LH.Audit.Product}
|
|
118
|
+
* @return {Promise<LH.Audit.Product>}
|
|
77
119
|
*/
|
|
78
|
-
static audit(artifacts, context) {
|
|
120
|
+
static async audit(artifacts, context) {
|
|
79
121
|
const stats = artifacts.DOMStats;
|
|
80
122
|
|
|
81
123
|
const score = Audit.computeLogNormalScore(
|
|
@@ -120,12 +162,17 @@ class DOMSize extends Audit {
|
|
|
120
162
|
},
|
|
121
163
|
];
|
|
122
164
|
|
|
165
|
+
const tbtImpact = await this.computeTbtImpact(artifacts, context);
|
|
166
|
+
|
|
123
167
|
return {
|
|
124
168
|
score,
|
|
125
169
|
numericValue: stats.totalBodyElements,
|
|
126
170
|
numericUnit: 'element',
|
|
127
171
|
displayValue: str_(UIStrings.displayValue, {itemCount: stats.totalBodyElements}),
|
|
128
172
|
details: Audit.makeTableDetails(headings, items),
|
|
173
|
+
metricSavings: {
|
|
174
|
+
TBT: tbtImpact,
|
|
175
|
+
},
|
|
129
176
|
};
|
|
130
177
|
}
|
|
131
178
|
}
|
|
@@ -8,7 +8,8 @@ import log from 'lighthouse-logger';
|
|
|
8
8
|
|
|
9
9
|
import {Audit} from './audit.js';
|
|
10
10
|
import * as i18n from '../lib/i18n/i18n.js';
|
|
11
|
-
import {LargestContentfulPaint} from '../computed/metrics/largest-contentful-paint.js';
|
|
11
|
+
import {LargestContentfulPaint as LargestContentfulPaintComputed} from '../computed/metrics/largest-contentful-paint.js';
|
|
12
|
+
import LargestContentfulPaint from './metrics/largest-contentful-paint.js';
|
|
12
13
|
import {LCPBreakdown} from '../computed/metrics/lcp-breakdown.js';
|
|
13
14
|
import {Sentry} from '../lib/sentry.js';
|
|
14
15
|
|
|
@@ -124,14 +125,22 @@ class LargestContentfulPaintElement extends Audit {
|
|
|
124
125
|
settings: context.settings, URL: artifacts.URL};
|
|
125
126
|
|
|
126
127
|
const elementTable = this.makeElementTable(artifacts);
|
|
127
|
-
if (!elementTable)
|
|
128
|
+
if (!elementTable) {
|
|
129
|
+
return {
|
|
130
|
+
score: null,
|
|
131
|
+
notApplicable: true,
|
|
132
|
+
metricSavings: {LCP: 0},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
128
135
|
|
|
129
136
|
const items = [elementTable];
|
|
130
137
|
let displayValue;
|
|
138
|
+
let metricLcp = 0;
|
|
131
139
|
|
|
132
140
|
try {
|
|
133
|
-
const
|
|
134
|
-
await
|
|
141
|
+
const lcpResult =
|
|
142
|
+
await LargestContentfulPaintComputed.request(metricComputationData, context);
|
|
143
|
+
metricLcp = lcpResult.timing;
|
|
135
144
|
displayValue = str_(i18n.UIStrings.ms, {timeInMs: metricLcp});
|
|
136
145
|
|
|
137
146
|
const phaseTable = await this.makePhaseTable(metricLcp, metricComputationData, context);
|
|
@@ -146,10 +155,18 @@ class LargestContentfulPaintElement extends Audit {
|
|
|
146
155
|
|
|
147
156
|
const details = Audit.makeListDetails(items);
|
|
148
157
|
|
|
158
|
+
// Conceptually, this doesn't make much sense as "savings" for this audit since there isn't anything to "fix".
|
|
159
|
+
// However, this audit will always be useful when improving LCP and that should be reflected in our impact calculations.
|
|
160
|
+
const idealLcp = LargestContentfulPaint.defaultOptions[context.settings.formFactor].scoring.p10;
|
|
161
|
+
const lcpSavings = Math.max(0, metricLcp - idealLcp);
|
|
162
|
+
|
|
149
163
|
return {
|
|
150
164
|
score: 1,
|
|
151
165
|
displayValue,
|
|
152
166
|
details,
|
|
167
|
+
metricSavings: {
|
|
168
|
+
LCP: lcpSavings,
|
|
169
|
+
},
|
|
153
170
|
};
|
|
154
171
|
}
|
|
155
172
|
}
|
|
@@ -8,9 +8,10 @@ declare class LargestContentfulPaintLazyLoaded extends Audit {
|
|
|
8
8
|
static isImageInViewport(image: LH.Artifacts.ImageElement, viewportDimensions: LH.Artifacts.ViewportDimensions): boolean;
|
|
9
9
|
/**
|
|
10
10
|
* @param {LH.Artifacts} artifacts
|
|
11
|
-
* @
|
|
11
|
+
* @param {LH.Audit.Context} context
|
|
12
|
+
* @return {Promise<LH.Audit.Product>}
|
|
12
13
|
*/
|
|
13
|
-
static audit(artifacts: LH.Artifacts): LH.Audit.Product
|
|
14
|
+
static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
|
|
14
15
|
}
|
|
15
16
|
export namespace UIStrings {
|
|
16
17
|
const title: string;
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
import {Audit} from './audit.js';
|
|
8
8
|
import * as i18n from '../lib/i18n/i18n.js';
|
|
9
|
+
import {LCPBreakdown} from '../computed/metrics/lcp-breakdown.js';
|
|
10
|
+
import {LargestContentfulPaint} from '../computed/metrics/largest-contentful-paint.js';
|
|
9
11
|
|
|
10
12
|
const UIStrings = {
|
|
11
13
|
/** Title of a Lighthouse audit that provides detail on whether the largest above-the-fold image was loaded with sufficient priority. This descriptive title is shown to users when the image was loaded properly. */
|
|
@@ -18,6 +20,8 @@ const UIStrings = {
|
|
|
18
20
|
|
|
19
21
|
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
20
22
|
|
|
23
|
+
const ESTIMATED_PERCENT_SAVINGS = 0.15;
|
|
24
|
+
|
|
21
25
|
class LargestContentfulPaintLazyLoaded extends Audit {
|
|
22
26
|
/**
|
|
23
27
|
* @return {LH.Audit.Meta}
|
|
@@ -29,7 +33,8 @@ class LargestContentfulPaintLazyLoaded extends Audit {
|
|
|
29
33
|
failureTitle: str_(UIStrings.failureTitle),
|
|
30
34
|
description: str_(UIStrings.description),
|
|
31
35
|
supportedModes: ['navigation'],
|
|
32
|
-
requiredArtifacts: ['TraceElements', 'ViewportDimensions', 'ImageElements'
|
|
36
|
+
requiredArtifacts: ['TraceElements', 'ViewportDimensions', 'ImageElements',
|
|
37
|
+
'traces', 'devtoolsLogs', 'GatherContext', 'URL'],
|
|
33
38
|
};
|
|
34
39
|
}
|
|
35
40
|
|
|
@@ -46,9 +51,10 @@ class LargestContentfulPaintLazyLoaded extends Audit {
|
|
|
46
51
|
|
|
47
52
|
/**
|
|
48
53
|
* @param {LH.Artifacts} artifacts
|
|
49
|
-
* @
|
|
54
|
+
* @param {LH.Audit.Context} context
|
|
55
|
+
* @return {Promise<LH.Audit.Product>}
|
|
50
56
|
*/
|
|
51
|
-
static audit(artifacts) {
|
|
57
|
+
static async audit(artifacts, context) {
|
|
52
58
|
const lcpElement = artifacts.TraceElements.find(element => {
|
|
53
59
|
return element.traceEventType === 'largest-contentful-paint' && element.type === 'image';
|
|
54
60
|
});
|
|
@@ -59,7 +65,11 @@ class LargestContentfulPaintLazyLoaded extends Audit {
|
|
|
59
65
|
|
|
60
66
|
if (!lcpElementImage ||
|
|
61
67
|
!this.isImageInViewport(lcpElementImage, artifacts.ViewportDimensions)) {
|
|
62
|
-
return {
|
|
68
|
+
return {
|
|
69
|
+
score: null,
|
|
70
|
+
notApplicable: true,
|
|
71
|
+
metricSavings: {LCP: 0},
|
|
72
|
+
};
|
|
63
73
|
}
|
|
64
74
|
|
|
65
75
|
/** @type {LH.Audit.Details.Table['headings']} */
|
|
@@ -73,8 +83,27 @@ class LargestContentfulPaintLazyLoaded extends Audit {
|
|
|
73
83
|
},
|
|
74
84
|
]);
|
|
75
85
|
|
|
86
|
+
const wasLazyLoaded = lcpElementImage.loading === 'lazy';
|
|
87
|
+
|
|
88
|
+
const metricComputationData = Audit.makeMetricComputationDataInput(artifacts, context);
|
|
89
|
+
const {timing: metricLcp} =
|
|
90
|
+
await LargestContentfulPaint.request(metricComputationData, context);
|
|
91
|
+
const lcpBreakdown = await LCPBreakdown.request(metricComputationData, context);
|
|
92
|
+
let lcpSavings = 0;
|
|
93
|
+
if (wasLazyLoaded && lcpBreakdown.loadStart !== undefined) {
|
|
94
|
+
// Estimate the LCP savings using a statistical percentage.
|
|
95
|
+
// https://web.dev/lcp-lazy-loading/#causal-performance
|
|
96
|
+
//
|
|
97
|
+
// LCP savings will be at most the LCP load delay.
|
|
98
|
+
const lcpLoadDelay = lcpBreakdown.loadStart - lcpBreakdown.ttfb;
|
|
99
|
+
lcpSavings = Math.min(metricLcp * ESTIMATED_PERCENT_SAVINGS, lcpLoadDelay);
|
|
100
|
+
}
|
|
101
|
+
|
|
76
102
|
return {
|
|
77
|
-
score:
|
|
103
|
+
score: wasLazyLoaded ? 0 : 1,
|
|
104
|
+
metricSavings: {
|
|
105
|
+
LCP: lcpSavings,
|
|
106
|
+
},
|
|
78
107
|
details,
|
|
79
108
|
};
|
|
80
109
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lighthouse",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "11.0.0-dev.
|
|
4
|
+
"version": "11.0.0-dev.20230815",
|
|
5
5
|
"description": "Automated auditing, performance metrics, and best practices for the web.",
|
|
6
6
|
"main": "./core/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"unit-viewer": "yarn mocha --testMatch viewer/**/*-test.js",
|
|
57
57
|
"unit-flow": "bash flow-report/test/run-flow-report-tests.sh",
|
|
58
58
|
"unit": "yarn unit-flow && yarn mocha",
|
|
59
|
-
"unit:ci": "NODE_OPTIONS=--max-old-space-size=8192 npm run unit
|
|
59
|
+
"unit:ci": "NODE_OPTIONS=--max-old-space-size=8192 npm run unit",
|
|
60
60
|
"core-unit": "yarn unit-core",
|
|
61
61
|
"cli-unit": "yarn unit-cli",
|
|
62
62
|
"viewer-unit": "yarn unit-viewer",
|