lighthouse 12.8.2-dev.20251005 → 12.8.2-dev.20251006
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/cli/test/smokehouse/config/exclusions.js +0 -2
- package/core/audits/audit.js +0 -1
- package/core/audits/insights/cls-culprits-insight.js +1 -1
- package/core/audits/insights/dom-size-insight.js +6 -6
- package/core/audits/redirects.js +1 -0
- package/core/audits/server-response-time.d.ts +0 -5
- package/core/audits/server-response-time.js +12 -26
- package/core/computed/metrics/lcp-breakdown.js +1 -0
- package/core/config/default-config.js +20 -63
- package/core/config/experimental-config.js +1 -26
- package/core/config/filters.js +6 -9
- package/core/config/lr-desktop-config.js +0 -1
- package/core/config/lr-mobile-config.js +0 -1
- package/core/gather/gatherers/trace-elements.js +1 -0
- package/core/lib/proto-preprocessor.js +5 -22
- package/dist/report/bundle.esm.js +10 -49
- package/dist/report/flow.js +12 -51
- package/dist/report/standalone.js +11 -50
- package/flow-report/src/i18n/i18n.d.ts +4 -6
- package/package.json +3 -3
- package/report/assets/styles.css +0 -39
- package/report/renderer/api.js +0 -1
- package/report/renderer/category-renderer.js +6 -0
- package/report/renderer/components.js +1 -1
- package/report/renderer/dom.d.ts +0 -13
- package/report/renderer/dom.js +0 -38
- package/report/renderer/performance-category-renderer.d.ts +0 -26
- package/report/renderer/performance-category-renderer.js +10 -142
- package/report/renderer/report-ui-features.d.ts +0 -1
- package/report/renderer/report-ui-features.js +3 -13
- package/report/renderer/report-utils.d.ts +2 -3
- package/report/renderer/report-utils.js +4 -6
- package/report/types/report-renderer.d.ts +0 -6
- package/shared/localization/locales/ar-XB.json +0 -330
- package/shared/localization/locales/ar.json +0 -330
- package/shared/localization/locales/bg.json +0 -330
- package/shared/localization/locales/ca.json +0 -330
- package/shared/localization/locales/cs.json +0 -330
- package/shared/localization/locales/da.json +0 -330
- package/shared/localization/locales/de.json +0 -330
- package/shared/localization/locales/el.json +0 -330
- package/shared/localization/locales/en-GB.json +0 -330
- package/shared/localization/locales/en-US.json +26 -275
- package/shared/localization/locales/en-XA.json +0 -330
- package/shared/localization/locales/en-XL.json +26 -275
- package/shared/localization/locales/es-419.json +0 -330
- package/shared/localization/locales/es.json +0 -330
- package/shared/localization/locales/fi.json +0 -330
- package/shared/localization/locales/fil.json +0 -330
- package/shared/localization/locales/fr.json +0 -330
- package/shared/localization/locales/he.json +0 -330
- package/shared/localization/locales/hi.json +0 -330
- package/shared/localization/locales/hr.json +0 -330
- package/shared/localization/locales/hu.json +0 -330
- package/shared/localization/locales/id.json +0 -330
- package/shared/localization/locales/it.json +0 -330
- package/shared/localization/locales/ja.json +0 -330
- package/shared/localization/locales/ko.json +0 -330
- package/shared/localization/locales/lt.json +0 -330
- package/shared/localization/locales/lv.json +0 -330
- package/shared/localization/locales/nl.json +0 -330
- package/shared/localization/locales/no.json +0 -330
- package/shared/localization/locales/pl.json +0 -330
- package/shared/localization/locales/pt-PT.json +0 -330
- package/shared/localization/locales/pt.json +0 -330
- package/shared/localization/locales/ro.json +0 -330
- package/shared/localization/locales/ru.json +0 -330
- package/shared/localization/locales/sk.json +0 -330
- package/shared/localization/locales/sl.json +0 -330
- package/shared/localization/locales/sr-Latn.json +0 -330
- package/shared/localization/locales/sr.json +0 -330
- package/shared/localization/locales/sv.json +0 -330
- package/shared/localization/locales/ta.json +0 -330
- package/shared/localization/locales/te.json +0 -330
- package/shared/localization/locales/th.json +0 -330
- package/shared/localization/locales/tr.json +0 -330
- package/shared/localization/locales/uk.json +0 -330
- package/shared/localization/locales/vi.json +0 -330
- package/shared/localization/locales/zh-HK.json +0 -330
- package/shared/localization/locales/zh-TW.json +0 -330
- package/shared/localization/locales/zh.json +0 -330
- package/types/artifacts.d.ts +1 -0
- package/types/audit.d.ts +1 -1
- package/types/lhr/settings.d.ts +1 -1
- package/core/audits/byte-efficiency/duplicated-javascript.d.ts +0 -45
- package/core/audits/byte-efficiency/duplicated-javascript.js +0 -223
- package/core/audits/byte-efficiency/efficient-animated-content.d.ts +0 -22
- package/core/audits/byte-efficiency/efficient-animated-content.js +0 -93
- package/core/audits/byte-efficiency/legacy-javascript.d.ts +0 -28
- package/core/audits/byte-efficiency/legacy-javascript.js +0 -144
- package/core/audits/byte-efficiency/modern-image-formats.d.ts +0 -38
- package/core/audits/byte-efficiency/modern-image-formats.js +0 -187
- package/core/audits/byte-efficiency/render-blocking-resources.d.ts +0 -53
- package/core/audits/byte-efficiency/render-blocking-resources.js +0 -312
- package/core/audits/byte-efficiency/uses-long-cache-ttl.d.ts +0 -59
- package/core/audits/byte-efficiency/uses-long-cache-ttl.js +0 -293
- package/core/audits/byte-efficiency/uses-optimized-images.d.ts +0 -33
- package/core/audits/byte-efficiency/uses-optimized-images.js +0 -146
- package/core/audits/byte-efficiency/uses-responsive-images-snapshot.d.ts +0 -16
- package/core/audits/byte-efficiency/uses-responsive-images-snapshot.js +0 -106
- package/core/audits/byte-efficiency/uses-responsive-images.d.ts +0 -44
- package/core/audits/byte-efficiency/uses-responsive-images.js +0 -202
- package/core/audits/byte-efficiency/uses-text-compression.d.ts +0 -14
- package/core/audits/byte-efficiency/uses-text-compression.js +0 -108
- package/core/audits/critical-request-chains.d.ts +0 -44
- package/core/audits/critical-request-chains.js +0 -221
- package/core/audits/dobetterweb/dom-size.d.ts +0 -32
- package/core/audits/dobetterweb/dom-size.js +0 -182
- package/core/audits/dobetterweb/uses-http2.d.ts +0 -72
- package/core/audits/dobetterweb/uses-http2.js +0 -276
- package/core/audits/font-display.d.ts +0 -32
- package/core/audits/font-display.js +0 -195
- package/core/audits/largest-contentful-paint-element.d.ts +0 -34
- package/core/audits/largest-contentful-paint-element.js +0 -181
- package/core/audits/lcp-lazy-loaded.d.ts +0 -22
- package/core/audits/lcp-lazy-loaded.js +0 -115
- package/core/audits/prioritize-lcp-image.d.ts +0 -74
- package/core/audits/prioritize-lcp-image.js +0 -297
- package/core/audits/third-party-summary.d.ts +0 -78
- package/core/audits/third-party-summary.js +0 -236
- package/core/audits/uses-rel-preconnect.d.ts +0 -37
- package/core/audits/uses-rel-preconnect.js +0 -286
- package/core/audits/viewport.d.ts +0 -17
- package/core/audits/viewport.js +0 -87
- package/core/audits/work-during-interaction.d.ts +0 -81
- package/core/audits/work-during-interaction.js +0 -287
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2017 Google LLC
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @fileoverview Audits a page to see how the size of DOM it creates. Stats like
|
|
9
|
-
* tree depth, # children, and total elements are returned. The score is calculated
|
|
10
|
-
* based solely on the total number of elements found on the page.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
import {Audit} from '../audit.js';
|
|
15
|
-
import * as i18n from '../../lib/i18n/i18n.js';
|
|
16
|
-
import {TBTImpactTasks} from '../../computed/tbt-impact-tasks.js';
|
|
17
|
-
|
|
18
|
-
const UIStrings = {
|
|
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. */
|
|
20
|
-
title: 'Avoids an excessive DOM size',
|
|
21
|
-
/** 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 imperative title is shown to users when there is a significant amount of execution time that could be reduced. */
|
|
22
|
-
failureTitle: 'Avoid an excessive DOM size',
|
|
23
|
-
/** Description of a Lighthouse audit that tells the user *why* they should reduce 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 is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */
|
|
24
|
-
description: 'A large DOM will increase memory usage, cause longer ' +
|
|
25
|
-
'[style calculations](https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations), ' +
|
|
26
|
-
'and produce costly [layout reflows](https://developers.google.com/speed/articles/reflow). [Learn how to avoid an excessive DOM size](https://developer.chrome.com/docs/lighthouse/performance/dom-size/).',
|
|
27
|
-
/** Table column header for the type of statistic. These statistics describe how big the DOM is (count of DOM elements, children, depth). */
|
|
28
|
-
columnStatistic: 'Statistic',
|
|
29
|
-
/** Table column header for the observed value of the DOM statistic. */
|
|
30
|
-
columnValue: 'Value',
|
|
31
|
-
/** [ICU Syntax] Label for an audit identifying the number of DOM elements found in the page. */
|
|
32
|
-
displayValue: `{itemCount, plural,
|
|
33
|
-
=1 {1 element}
|
|
34
|
-
other {# elements}
|
|
35
|
-
}`,
|
|
36
|
-
/** Label for the total number of DOM elements found in the page. */
|
|
37
|
-
statisticDOMElements: 'Total DOM Elements',
|
|
38
|
-
/** Label for the numeric value of the maximum depth in the page's DOM tree. */
|
|
39
|
-
statisticDOMDepth: 'Maximum DOM Depth',
|
|
40
|
-
/** Label for the numeric value of the maximum number of children any DOM element in the page has. The element described will have the most children in the page. */
|
|
41
|
-
statisticDOMWidth: 'Maximum Child Elements',
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
45
|
-
|
|
46
|
-
class DOMSize extends Audit {
|
|
47
|
-
/**
|
|
48
|
-
* @return {LH.Audit.Meta}
|
|
49
|
-
*/
|
|
50
|
-
static get meta() {
|
|
51
|
-
return {
|
|
52
|
-
id: 'dom-size',
|
|
53
|
-
title: str_(UIStrings.title),
|
|
54
|
-
failureTitle: str_(UIStrings.failureTitle),
|
|
55
|
-
description: str_(UIStrings.description),
|
|
56
|
-
scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
|
|
57
|
-
guidanceLevel: 1,
|
|
58
|
-
requiredArtifacts: ['DOMStats', 'URL', 'GatherContext'],
|
|
59
|
-
__internalOptionalArtifacts: ['Trace', 'DevtoolsLog', 'SourceMaps'],
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* @return {LH.Audit.ScoreOptions}
|
|
65
|
-
*/
|
|
66
|
-
static get defaultOptions() {
|
|
67
|
-
return {
|
|
68
|
-
// https://bigquery.cloud.google.com/table/httparchive:lighthouse.2018_04_01_mobile?pli=1
|
|
69
|
-
// see https://www.desmos.com/calculator/tsunbwqt3f
|
|
70
|
-
p10: 818,
|
|
71
|
-
median: 1400,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* @param {LH.Artifacts} artifacts
|
|
77
|
-
* @param {LH.Audit.Context} context
|
|
78
|
-
* @return {Promise<number|undefined>}
|
|
79
|
-
*/
|
|
80
|
-
static async computeTbtImpact(artifacts, context) {
|
|
81
|
-
let tbtImpact = 0;
|
|
82
|
-
|
|
83
|
-
// We still want to surface this audit in snapshot mode, but since we don't compute TBT
|
|
84
|
-
// the impact should always be undefined.
|
|
85
|
-
const {GatherContext, DevtoolsLog, Trace} = artifacts;
|
|
86
|
-
if (GatherContext.gatherMode !== 'navigation') {
|
|
87
|
-
return undefined;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Since the artifacts are optional, it's still possible for them to be missing in navigation mode.
|
|
91
|
-
// Navigation mode does compute TBT so we should surface a numerical savings of 0.
|
|
92
|
-
if (!DevtoolsLog || !Trace) {
|
|
93
|
-
return 0;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const metricComputationData = Audit.makeMetricComputationDataInput(artifacts, context);
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
// The TBT impact of style/layout tasks is correlated to the DOM size.
|
|
100
|
-
// Even in situations where the page forces a style recalc, the DOM size is partially to blame
|
|
101
|
-
// for any time spent blocking the main thread.
|
|
102
|
-
//
|
|
103
|
-
// `tbtImpact` should be exactly 0 for small DOMs since `selfTbtImpact` accounts for the blocking
|
|
104
|
-
// time and not the main thread time.
|
|
105
|
-
const tbtImpactTasks = await TBTImpactTasks.request(metricComputationData, context);
|
|
106
|
-
for (const task of tbtImpactTasks) {
|
|
107
|
-
if (task.group.id !== 'styleLayout') continue;
|
|
108
|
-
tbtImpact += task.selfTbtImpact;
|
|
109
|
-
}
|
|
110
|
-
} catch {}
|
|
111
|
-
|
|
112
|
-
return Math.round(tbtImpact);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* @param {LH.Artifacts} artifacts
|
|
118
|
-
* @param {LH.Audit.Context} context
|
|
119
|
-
* @return {Promise<LH.Audit.Product>}
|
|
120
|
-
*/
|
|
121
|
-
static async audit(artifacts, context) {
|
|
122
|
-
const stats = artifacts.DOMStats;
|
|
123
|
-
|
|
124
|
-
const score = Audit.computeLogNormalScore(
|
|
125
|
-
{p10: context.options.p10, median: context.options.median},
|
|
126
|
-
stats.totalBodyElements
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
/** @type {LH.Audit.Details.Table['headings']} */
|
|
130
|
-
const headings = [
|
|
131
|
-
{key: 'statistic', valueType: 'text', label: str_(UIStrings.columnStatistic)},
|
|
132
|
-
{key: 'node', valueType: 'node', label: str_(i18n.UIStrings.columnElement)},
|
|
133
|
-
{key: 'value', valueType: 'numeric', label: str_(UIStrings.columnValue)},
|
|
134
|
-
];
|
|
135
|
-
|
|
136
|
-
/** @type {LH.Audit.Details.Table['items']} */
|
|
137
|
-
const items = [
|
|
138
|
-
{
|
|
139
|
-
statistic: str_(UIStrings.statisticDOMElements),
|
|
140
|
-
value: {
|
|
141
|
-
type: 'numeric',
|
|
142
|
-
granularity: 1,
|
|
143
|
-
value: stats.totalBodyElements,
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
{
|
|
147
|
-
node: Audit.makeNodeItem(stats.depth),
|
|
148
|
-
statistic: str_(UIStrings.statisticDOMDepth),
|
|
149
|
-
value: {
|
|
150
|
-
type: 'numeric',
|
|
151
|
-
granularity: 1,
|
|
152
|
-
value: stats.depth.max,
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
node: Audit.makeNodeItem(stats.width),
|
|
157
|
-
statistic: str_(UIStrings.statisticDOMWidth),
|
|
158
|
-
value: {
|
|
159
|
-
type: 'numeric',
|
|
160
|
-
granularity: 1,
|
|
161
|
-
value: stats.width.max,
|
|
162
|
-
},
|
|
163
|
-
},
|
|
164
|
-
];
|
|
165
|
-
|
|
166
|
-
const tbtImpact = await this.computeTbtImpact(artifacts, context);
|
|
167
|
-
|
|
168
|
-
return {
|
|
169
|
-
score,
|
|
170
|
-
numericValue: stats.totalBodyElements,
|
|
171
|
-
numericUnit: 'element',
|
|
172
|
-
displayValue: str_(UIStrings.displayValue, {itemCount: stats.totalBodyElements}),
|
|
173
|
-
details: Audit.makeTableDetails(headings, items),
|
|
174
|
-
metricSavings: {
|
|
175
|
-
TBT: tbtImpact,
|
|
176
|
-
},
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
export default DOMSize;
|
|
182
|
-
export {UIStrings};
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
export default UsesHTTP2Audit;
|
|
2
|
-
declare class UsesHTTP2Audit extends Audit {
|
|
3
|
-
/**
|
|
4
|
-
* Computes the estimated effect of all results being converted to http/2 on the provided graph.
|
|
5
|
-
*
|
|
6
|
-
* @param {Array<{url: string}>} results
|
|
7
|
-
* @param {LH.Gatherer.Simulation.GraphNode} graph
|
|
8
|
-
* @param {LH.Gatherer.Simulation.Simulator} simulator
|
|
9
|
-
* @param {{label?: string}=} options
|
|
10
|
-
* @return {{savings: number, simulationBefore: LH.Gatherer.Simulation.Result, simulationAfter: LH.Gatherer.Simulation.Result}}
|
|
11
|
-
*/
|
|
12
|
-
static computeWasteWithGraph(results: Array<{
|
|
13
|
-
url: string;
|
|
14
|
-
}>, graph: LH.Gatherer.Simulation.GraphNode, simulator: LH.Gatherer.Simulation.Simulator, options?: {
|
|
15
|
-
label?: string;
|
|
16
|
-
} | undefined): {
|
|
17
|
-
savings: number;
|
|
18
|
-
simulationBefore: LH.Gatherer.Simulation.Result;
|
|
19
|
-
simulationAfter: LH.Gatherer.Simulation.Result;
|
|
20
|
-
};
|
|
21
|
-
/**
|
|
22
|
-
* Determines whether a network request is a "static resource" that would benefit from H2 multiplexing.
|
|
23
|
-
* XHRs, tracking pixels, etc generally don't benefit as much because they aren't requested en-masse
|
|
24
|
-
* for the same origin at the exact same time.
|
|
25
|
-
*
|
|
26
|
-
* @param {LH.Artifacts.NetworkRequest} networkRequest
|
|
27
|
-
* @param {LH.Artifacts.EntityClassification} classifiedEntities
|
|
28
|
-
* @return {boolean}
|
|
29
|
-
*/
|
|
30
|
-
static isMultiplexableStaticAsset(networkRequest: LH.Artifacts.NetworkRequest, classifiedEntities: LH.Artifacts.EntityClassification): boolean;
|
|
31
|
-
/**
|
|
32
|
-
* Determine the set of resources that aren't HTTP/2 but should be.
|
|
33
|
-
* We're a little conservative about what we surface for a few reasons:
|
|
34
|
-
*
|
|
35
|
-
* - The simulator approximation of HTTP/2 is a little more generous than reality.
|
|
36
|
-
* - There's a bit of debate surrounding HTTP/2 due to its worse performance in environments with high packet loss.**
|
|
37
|
-
* - It's something that you'd have absolutely zero control over with a third-party (can't defer to fix it for example).
|
|
38
|
-
*
|
|
39
|
-
* Therefore, we only surface requests that were...
|
|
40
|
-
*
|
|
41
|
-
* - Served over HTTP/1.1 or earlier
|
|
42
|
-
* - Served over an origin that serves at least 6 static asset requests
|
|
43
|
-
* (if there aren't more requests than browser's max/host, multiplexing isn't as big a deal)
|
|
44
|
-
* - Not served on localhost (h2 is a pain to deal with locally & and CI)
|
|
45
|
-
*
|
|
46
|
-
* ** = https://news.ycombinator.com/item?id=19086639
|
|
47
|
-
* https://www.twilio.com/blog/2017/10/http2-issues.html
|
|
48
|
-
* https://www.cachefly.com/http-2-is-not-a-magic-bullet/
|
|
49
|
-
*
|
|
50
|
-
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
51
|
-
* @param {LH.Artifacts.EntityClassification} classifiedEntities
|
|
52
|
-
* @return {Array<{url: string, protocol: string}>}
|
|
53
|
-
*/
|
|
54
|
-
static determineNonHttp2Resources(networkRecords: Array<LH.Artifacts.NetworkRequest>, classifiedEntities: LH.Artifacts.EntityClassification): Array<{
|
|
55
|
-
url: string;
|
|
56
|
-
protocol: string;
|
|
57
|
-
}>;
|
|
58
|
-
/**
|
|
59
|
-
* @param {LH.Artifacts} artifacts
|
|
60
|
-
* @param {LH.Audit.Context} context
|
|
61
|
-
* @return {Promise<LH.Audit.Product>}
|
|
62
|
-
*/
|
|
63
|
-
static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
|
|
64
|
-
}
|
|
65
|
-
export namespace UIStrings {
|
|
66
|
-
let title: string;
|
|
67
|
-
let description: string;
|
|
68
|
-
let displayValue: string;
|
|
69
|
-
let columnProtocol: string;
|
|
70
|
-
}
|
|
71
|
-
import { Audit } from '../audit.js';
|
|
72
|
-
//# sourceMappingURL=uses-http2.d.ts.map
|
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2016 Google LLC
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @fileoverview Audit a page to ensure that resource loaded over its own
|
|
9
|
-
* origin are over the http/2 protocol.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import {Audit} from '../audit.js';
|
|
13
|
-
import {EntityClassification} from '../../computed/entity-classification.js';
|
|
14
|
-
import UrlUtils from '../../lib/url-utils.js';
|
|
15
|
-
import {NetworkRequest} from '../../lib/network-request.js';
|
|
16
|
-
import {NetworkRecords} from '../../computed/network-records.js';
|
|
17
|
-
import {LoadSimulator} from '../../computed/load-simulator.js';
|
|
18
|
-
import {LanternLargestContentfulPaint} from '../../computed/metrics/lantern-largest-contentful-paint.js';
|
|
19
|
-
import {LanternFirstContentfulPaint} from '../../computed/metrics/lantern-first-contentful-paint.js';
|
|
20
|
-
import * as i18n from '../../lib/i18n/i18n.js';
|
|
21
|
-
|
|
22
|
-
const UIStrings = {
|
|
23
|
-
/** Imperative title of a Lighthouse audit that tells the user to enable HTTP/2. This is displayed in a list of audit titles that Lighthouse generates. */
|
|
24
|
-
title: 'Use HTTP/2',
|
|
25
|
-
/** Description of a Lighthouse audit that tells the user why they should use HTTP/2. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */
|
|
26
|
-
description: 'HTTP/2 offers many benefits over HTTP/1.1, including binary headers and ' +
|
|
27
|
-
'multiplexing. [Learn more about HTTP/2](https://developer.chrome.com/docs/lighthouse/best-practices/uses-http2/).',
|
|
28
|
-
/** [ICU Syntax] Label identifying the number of network requests that were not served with HTTP/2. */
|
|
29
|
-
displayValue: `{itemCount, plural,
|
|
30
|
-
=1 {1 request not served via HTTP/2}
|
|
31
|
-
other {# requests not served via HTTP/2}
|
|
32
|
-
}`,
|
|
33
|
-
/** Label for a column in a data table; entries in the column will be the HTTP Protocol used to make a network request. */
|
|
34
|
-
columnProtocol: 'Protocol',
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
38
|
-
|
|
39
|
-
/** @type {Set<LH.Artifacts.NetworkRequest['resourceType']>} */
|
|
40
|
-
const STATIC_RESOURCE_TYPES = new Set([
|
|
41
|
-
NetworkRequest.TYPES.Document,
|
|
42
|
-
NetworkRequest.TYPES.Font,
|
|
43
|
-
NetworkRequest.TYPES.Image,
|
|
44
|
-
NetworkRequest.TYPES.Stylesheet,
|
|
45
|
-
NetworkRequest.TYPES.Script,
|
|
46
|
-
NetworkRequest.TYPES.Media,
|
|
47
|
-
]);
|
|
48
|
-
|
|
49
|
-
class UsesHTTP2Audit extends Audit {
|
|
50
|
-
/**
|
|
51
|
-
* @return {LH.Audit.Meta}
|
|
52
|
-
*/
|
|
53
|
-
static get meta() {
|
|
54
|
-
return {
|
|
55
|
-
id: 'uses-http2',
|
|
56
|
-
title: str_(UIStrings.title),
|
|
57
|
-
description: str_(UIStrings.description),
|
|
58
|
-
scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
|
|
59
|
-
guidanceLevel: 3,
|
|
60
|
-
supportedModes: ['timespan', 'navigation'],
|
|
61
|
-
requiredArtifacts: ['URL', 'DevtoolsLog', 'Trace', 'GatherContext', 'SourceMaps'],
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Computes the estimated effect of all results being converted to http/2 on the provided graph.
|
|
67
|
-
*
|
|
68
|
-
* @param {Array<{url: string}>} results
|
|
69
|
-
* @param {LH.Gatherer.Simulation.GraphNode} graph
|
|
70
|
-
* @param {LH.Gatherer.Simulation.Simulator} simulator
|
|
71
|
-
* @param {{label?: string}=} options
|
|
72
|
-
* @return {{savings: number, simulationBefore: LH.Gatherer.Simulation.Result, simulationAfter: LH.Gatherer.Simulation.Result}}
|
|
73
|
-
*/
|
|
74
|
-
static computeWasteWithGraph(results, graph, simulator, options) {
|
|
75
|
-
options = Object.assign({label: ''}, options);
|
|
76
|
-
const beforeLabel = `${this.meta.id}-${options.label}-before`;
|
|
77
|
-
const afterLabel = `${this.meta.id}-${options.label}-after`;
|
|
78
|
-
|
|
79
|
-
const urlsToChange = new Set(results.map(result => result.url));
|
|
80
|
-
const simulationBefore = simulator.simulate(graph, {label: beforeLabel});
|
|
81
|
-
|
|
82
|
-
// Update all the protocols to reflect implementing our recommendations
|
|
83
|
-
/** @type {Map<string, string>} */
|
|
84
|
-
const originalProtocols = new Map();
|
|
85
|
-
graph.traverse(node => {
|
|
86
|
-
if (node.type !== 'network') return;
|
|
87
|
-
if (!urlsToChange.has(node.request.url)) return;
|
|
88
|
-
|
|
89
|
-
originalProtocols.set(node.request.requestId, node.request.protocol);
|
|
90
|
-
node.request.protocol = 'h2';
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const simulationAfter = simulator.simulate(graph, {label: afterLabel});
|
|
94
|
-
|
|
95
|
-
// Restore the original protocol after we've done our simulation
|
|
96
|
-
graph.traverse(node => {
|
|
97
|
-
if (node.type !== 'network') return;
|
|
98
|
-
const originalProtocol = originalProtocols.get(node.request.requestId);
|
|
99
|
-
if (originalProtocol === undefined) return;
|
|
100
|
-
node.request.protocol = originalProtocol;
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
const savings = simulationBefore.timeInMs - simulationAfter.timeInMs;
|
|
104
|
-
|
|
105
|
-
return {
|
|
106
|
-
// Round waste to nearest 10ms
|
|
107
|
-
savings: Math.round(Math.max(savings, 0) / 10) * 10,
|
|
108
|
-
simulationBefore,
|
|
109
|
-
simulationAfter,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Determines whether a network request is a "static resource" that would benefit from H2 multiplexing.
|
|
115
|
-
* XHRs, tracking pixels, etc generally don't benefit as much because they aren't requested en-masse
|
|
116
|
-
* for the same origin at the exact same time.
|
|
117
|
-
*
|
|
118
|
-
* @param {LH.Artifacts.NetworkRequest} networkRequest
|
|
119
|
-
* @param {LH.Artifacts.EntityClassification} classifiedEntities
|
|
120
|
-
* @return {boolean}
|
|
121
|
-
*/
|
|
122
|
-
static isMultiplexableStaticAsset(networkRequest, classifiedEntities) {
|
|
123
|
-
if (!STATIC_RESOURCE_TYPES.has(networkRequest.resourceType)) return false;
|
|
124
|
-
|
|
125
|
-
// Resources from third-parties that are less than 100 bytes are usually tracking pixels, not actual resources.
|
|
126
|
-
// They can masquerade as static types though (gifs, documents, etc)
|
|
127
|
-
if (networkRequest.resourceSize < 100) {
|
|
128
|
-
const entity = classifiedEntities.entityByUrl.get(networkRequest.url);
|
|
129
|
-
if (entity) {
|
|
130
|
-
// Third-party assets are multiplexable in their first-party context.
|
|
131
|
-
if (classifiedEntities.firstParty?.name === entity.name) return true;
|
|
132
|
-
// Skip recognizable third-parties' requests.
|
|
133
|
-
if (!entity.isUnrecognized) return false;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return true;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Determine the set of resources that aren't HTTP/2 but should be.
|
|
142
|
-
* We're a little conservative about what we surface for a few reasons:
|
|
143
|
-
*
|
|
144
|
-
* - The simulator approximation of HTTP/2 is a little more generous than reality.
|
|
145
|
-
* - There's a bit of debate surrounding HTTP/2 due to its worse performance in environments with high packet loss.**
|
|
146
|
-
* - It's something that you'd have absolutely zero control over with a third-party (can't defer to fix it for example).
|
|
147
|
-
*
|
|
148
|
-
* Therefore, we only surface requests that were...
|
|
149
|
-
*
|
|
150
|
-
* - Served over HTTP/1.1 or earlier
|
|
151
|
-
* - Served over an origin that serves at least 6 static asset requests
|
|
152
|
-
* (if there aren't more requests than browser's max/host, multiplexing isn't as big a deal)
|
|
153
|
-
* - Not served on localhost (h2 is a pain to deal with locally & and CI)
|
|
154
|
-
*
|
|
155
|
-
* ** = https://news.ycombinator.com/item?id=19086639
|
|
156
|
-
* https://www.twilio.com/blog/2017/10/http2-issues.html
|
|
157
|
-
* https://www.cachefly.com/http-2-is-not-a-magic-bullet/
|
|
158
|
-
*
|
|
159
|
-
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
160
|
-
* @param {LH.Artifacts.EntityClassification} classifiedEntities
|
|
161
|
-
* @return {Array<{url: string, protocol: string}>}
|
|
162
|
-
*/
|
|
163
|
-
static determineNonHttp2Resources(networkRecords, classifiedEntities) {
|
|
164
|
-
/** @type {Array<{url: string, protocol: string}>} */
|
|
165
|
-
const nonHttp2Resources = [];
|
|
166
|
-
|
|
167
|
-
/** @type {Set<string>} */
|
|
168
|
-
const seenURLs = new Set();
|
|
169
|
-
/** @type {Map<string, Array<LH.Artifacts.NetworkRequest>>} */
|
|
170
|
-
const groupedByOrigin = new Map();
|
|
171
|
-
for (const record of networkRecords) {
|
|
172
|
-
if (!UsesHTTP2Audit.isMultiplexableStaticAsset(record, classifiedEntities)) continue;
|
|
173
|
-
if (UrlUtils.isLikeLocalhost(record.parsedURL.host)) continue;
|
|
174
|
-
const existing = groupedByOrigin.get(record.parsedURL.securityOrigin) || [];
|
|
175
|
-
existing.push(record);
|
|
176
|
-
groupedByOrigin.set(record.parsedURL.securityOrigin, existing);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
for (const record of networkRecords) {
|
|
180
|
-
// Skip duplicates.
|
|
181
|
-
if (seenURLs.has(record.url)) continue;
|
|
182
|
-
// Check if record is not served through the service worker, servicer worker uses http/1.1 as a protocol.
|
|
183
|
-
// These can generate false positives (bug: https://github.com/GoogleChrome/lighthouse/issues/7158).
|
|
184
|
-
if (record.fetchedViaServiceWorker) continue;
|
|
185
|
-
// Test the protocol to see if it was http/1.1.
|
|
186
|
-
const isOldHttp = /HTTP\/[01][.\d]?/i.test(record.protocol);
|
|
187
|
-
if (!isOldHttp) continue;
|
|
188
|
-
// Check if the origin has enough requests to bother flagging.
|
|
189
|
-
const group = groupedByOrigin.get(record.parsedURL.securityOrigin) || [];
|
|
190
|
-
if (group.length < 6) continue;
|
|
191
|
-
|
|
192
|
-
seenURLs.add(record.url);
|
|
193
|
-
nonHttp2Resources.push({protocol: record.protocol, url: record.url});
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return nonHttp2Resources;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* @param {LH.Artifacts} artifacts
|
|
201
|
-
* @param {LH.Audit.Context} context
|
|
202
|
-
* @return {Promise<LH.Audit.Product>}
|
|
203
|
-
*/
|
|
204
|
-
static async audit(artifacts, context) {
|
|
205
|
-
const devtoolsLog = artifacts.DevtoolsLog;
|
|
206
|
-
const URL = artifacts.URL;
|
|
207
|
-
const networkRecords = await NetworkRecords.request(devtoolsLog, context);
|
|
208
|
-
const classifiedEntities = await EntityClassification.request({URL, devtoolsLog}, context);
|
|
209
|
-
const resources = UsesHTTP2Audit.determineNonHttp2Resources(networkRecords, classifiedEntities);
|
|
210
|
-
|
|
211
|
-
let displayValue;
|
|
212
|
-
if (resources.length > 0) {
|
|
213
|
-
displayValue = str_(UIStrings.displayValue, {itemCount: resources.length});
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// TODO: Compute actual savings for timespan mode.
|
|
217
|
-
if (artifacts.GatherContext.gatherMode === 'timespan') {
|
|
218
|
-
/** @type {LH.Audit.Details.Table['headings']} */
|
|
219
|
-
const headings = [
|
|
220
|
-
{key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
|
|
221
|
-
{key: 'protocol', valueType: 'text', label: str_(UIStrings.columnProtocol)},
|
|
222
|
-
];
|
|
223
|
-
|
|
224
|
-
const details = Audit.makeTableDetails(headings, resources);
|
|
225
|
-
|
|
226
|
-
return {
|
|
227
|
-
displayValue,
|
|
228
|
-
score: resources.length ? 0 : 1,
|
|
229
|
-
details,
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const settings = context?.settings || {};
|
|
234
|
-
const simulatorOptions = {
|
|
235
|
-
devtoolsLog,
|
|
236
|
-
settings,
|
|
237
|
-
};
|
|
238
|
-
const simulator = await LoadSimulator.request(simulatorOptions, context);
|
|
239
|
-
const metricComputationInput = Audit.makeMetricComputationDataInput(artifacts, context);
|
|
240
|
-
|
|
241
|
-
const {
|
|
242
|
-
pessimisticGraph: fcpGraph,
|
|
243
|
-
} = await LanternFirstContentfulPaint.request(metricComputationInput, context);
|
|
244
|
-
const {
|
|
245
|
-
pessimisticGraph: lcpGraph,
|
|
246
|
-
} = await LanternLargestContentfulPaint.request(metricComputationInput, context);
|
|
247
|
-
|
|
248
|
-
const wasteFcp =
|
|
249
|
-
UsesHTTP2Audit.computeWasteWithGraph(resources,
|
|
250
|
-
fcpGraph, simulator, {label: 'fcp'});
|
|
251
|
-
const wasteLcp =
|
|
252
|
-
UsesHTTP2Audit.computeWasteWithGraph(resources,
|
|
253
|
-
lcpGraph, simulator, {label: 'lcp'});
|
|
254
|
-
|
|
255
|
-
/** @type {LH.Audit.Details.Opportunity['headings']} */
|
|
256
|
-
const headings = [
|
|
257
|
-
{key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
|
|
258
|
-
{key: 'protocol', valueType: 'text', label: str_(UIStrings.columnProtocol)},
|
|
259
|
-
];
|
|
260
|
-
|
|
261
|
-
const details = Audit.makeOpportunityDetails(headings, resources,
|
|
262
|
-
{overallSavingsMs: wasteLcp.savings});
|
|
263
|
-
|
|
264
|
-
return {
|
|
265
|
-
displayValue,
|
|
266
|
-
numericValue: wasteLcp.savings,
|
|
267
|
-
numericUnit: 'millisecond',
|
|
268
|
-
score: resources.length ? 0 : 1,
|
|
269
|
-
details,
|
|
270
|
-
metricSavings: {LCP: wasteLcp.savings, FCP: wasteFcp.savings},
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
export default UsesHTTP2Audit;
|
|
276
|
-
export {UIStrings};
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
export default FontDisplay;
|
|
2
|
-
declare class FontDisplay extends Audit {
|
|
3
|
-
/**
|
|
4
|
-
* @param {LH.Artifacts} artifacts
|
|
5
|
-
* @param {RegExp} passingFontDisplayRegex
|
|
6
|
-
* @return {{passingURLs: Set<string>, failingURLs: Set<string>}}
|
|
7
|
-
*/
|
|
8
|
-
static findFontDisplayDeclarations(artifacts: LH.Artifacts, passingFontDisplayRegex: RegExp): {
|
|
9
|
-
passingURLs: Set<string>;
|
|
10
|
-
failingURLs: Set<string>;
|
|
11
|
-
};
|
|
12
|
-
/**
|
|
13
|
-
* Some pages load many fonts we can't check, so dedupe on origin.
|
|
14
|
-
* @param {Array<string>} warningUrls
|
|
15
|
-
* @return {Array<LH.IcuMessage>}
|
|
16
|
-
*/
|
|
17
|
-
static getWarningsForFontUrls(warningUrls: Array<string>): Array<LH.IcuMessage>;
|
|
18
|
-
/**
|
|
19
|
-
* @param {LH.Artifacts} artifacts
|
|
20
|
-
* @param {LH.Audit.Context} context
|
|
21
|
-
* @return {Promise<LH.Audit.Product>}
|
|
22
|
-
*/
|
|
23
|
-
static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
|
|
24
|
-
}
|
|
25
|
-
export namespace UIStrings {
|
|
26
|
-
let title: string;
|
|
27
|
-
let failureTitle: string;
|
|
28
|
-
let description: string;
|
|
29
|
-
let undeclaredFontOriginWarning: string;
|
|
30
|
-
}
|
|
31
|
-
import { Audit } from './audit.js';
|
|
32
|
-
//# sourceMappingURL=font-display.d.ts.map
|