lighthouse 10.0.2-dev.20230305 → 10.0.2-dev.20230306
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/audit.d.ts +6 -0
- package/core/audits/audit.js +14 -2
- package/core/audits/bootup-time.js +2 -3
- package/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +2 -0
- package/core/audits/byte-efficiency/byte-efficiency-audit.js +3 -1
- package/core/audits/byte-efficiency/total-byte-weight.js +1 -1
- package/core/audits/byte-efficiency/uses-long-cache-ttl.js +2 -2
- package/core/audits/long-tasks.js +2 -1
- package/core/audits/mainthread-work-breakdown.js +2 -1
- package/core/audits/network-rtt.js +1 -1
- package/core/audits/network-server-latency.js +2 -1
- package/core/audits/performance-budget.js +1 -1
- package/core/audits/prioritize-lcp-image.js +1 -1
- package/core/audits/third-party-summary.js +4 -1
- package/core/audits/timing-budget.js +2 -1
- package/core/audits/uses-rel-preconnect.js +1 -1
- package/core/audits/uses-rel-preload.js +1 -1
- package/core/audits/work-during-interaction.js +1 -1
- package/core/lib/lighthouse-compatibility.js +17 -0
- package/core/runner.js +1 -0
- package/dist/report/bundle.esm.js +222 -42
- package/dist/report/flow.js +17 -17
- package/dist/report/standalone.js +16 -16
- package/flow-report/src/i18n/i18n.d.ts +6 -0
- package/package.json +3 -5
- package/report/assets/styles.css +86 -4
- package/report/renderer/components.js +1 -1
- package/report/renderer/details-renderer.d.ts +26 -6
- package/report/renderer/details-renderer.js +144 -21
- package/report/renderer/report-renderer.js +1 -0
- package/report/renderer/report-ui-features.js +18 -9
- package/report/renderer/report-utils.d.ts +13 -4
- package/report/renderer/report-utils.js +43 -11
- package/shared/localization/locales/en-US.json +9 -0
- package/shared/localization/locales/en-XL.json +9 -0
- package/types/lhr/audit-details.d.ts +18 -0
package/core/audits/audit.d.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
export type TableOptions = {
|
|
2
2
|
wastedMs?: number | undefined;
|
|
3
3
|
wastedBytes?: number | undefined;
|
|
4
|
+
sortedBy?: LH.Audit.Details.Table['sortedBy'] | undefined;
|
|
5
|
+
skipSumming?: LH.Audit.Details.Table['skipSumming'] | undefined;
|
|
6
|
+
isEntityGrouped?: LH.Audit.Details.Table['isEntityGrouped'] | undefined;
|
|
4
7
|
};
|
|
5
8
|
export type OpportunityOptions = {
|
|
6
9
|
overallSavingsMs: number;
|
|
7
10
|
overallSavingsBytes?: number | undefined;
|
|
11
|
+
sortedBy?: LH.Audit.Details.Opportunity['sortedBy'] | undefined;
|
|
12
|
+
skipSumming?: LH.Audit.Details.Opportunity['skipSumming'] | undefined;
|
|
13
|
+
isEntityGrouped?: LH.Audit.Details.Opportunity['isEntityGrouped'] | undefined;
|
|
8
14
|
};
|
|
9
15
|
export class Audit {
|
|
10
16
|
/**
|
package/core/audits/audit.js
CHANGED
|
@@ -15,12 +15,18 @@ const DEFAULT_PASS = 'defaultPass';
|
|
|
15
15
|
* @typedef TableOptions
|
|
16
16
|
* @property {number=} wastedMs
|
|
17
17
|
* @property {number=} wastedBytes
|
|
18
|
+
* @property {LH.Audit.Details.Table['sortedBy']=} sortedBy
|
|
19
|
+
* @property {LH.Audit.Details.Table['skipSumming']=} skipSumming
|
|
20
|
+
* @property {LH.Audit.Details.Table['isEntityGrouped']=} isEntityGrouped
|
|
18
21
|
*/
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
24
|
* @typedef OpportunityOptions
|
|
22
25
|
* @property {number} overallSavingsMs
|
|
23
26
|
* @property {number=} overallSavingsBytes
|
|
27
|
+
* @property {LH.Audit.Details.Opportunity['sortedBy']=} sortedBy
|
|
28
|
+
* @property {LH.Audit.Details.Opportunity['skipSumming']=} skipSumming
|
|
29
|
+
* @property {LH.Audit.Details.Opportunity['isEntityGrouped']=} isEntityGrouped
|
|
24
30
|
*/
|
|
25
31
|
|
|
26
32
|
/**
|
|
@@ -139,7 +145,7 @@ class Audit {
|
|
|
139
145
|
* @return {LH.Audit.Details.Table}
|
|
140
146
|
*/
|
|
141
147
|
static makeTableDetails(headings, results, options = {}) {
|
|
142
|
-
const {wastedBytes, wastedMs} = options;
|
|
148
|
+
const {wastedBytes, wastedMs, sortedBy, skipSumming, isEntityGrouped} = options;
|
|
143
149
|
const summary = (wastedBytes || wastedMs) ? {wastedBytes, wastedMs} : undefined;
|
|
144
150
|
if (results.length === 0) {
|
|
145
151
|
return {
|
|
@@ -157,6 +163,9 @@ class Audit {
|
|
|
157
163
|
headings: headings,
|
|
158
164
|
items: results,
|
|
159
165
|
summary,
|
|
166
|
+
sortedBy,
|
|
167
|
+
skipSumming,
|
|
168
|
+
isEntityGrouped,
|
|
160
169
|
};
|
|
161
170
|
}
|
|
162
171
|
|
|
@@ -234,7 +243,7 @@ class Audit {
|
|
|
234
243
|
*/
|
|
235
244
|
static makeOpportunityDetails(headings, items, options) {
|
|
236
245
|
Audit.assertHeadingKeysExist(headings, items);
|
|
237
|
-
const {overallSavingsMs, overallSavingsBytes} = options;
|
|
246
|
+
const {overallSavingsMs, overallSavingsBytes, sortedBy, skipSumming, isEntityGrouped} = options;
|
|
238
247
|
|
|
239
248
|
return {
|
|
240
249
|
type: 'opportunity',
|
|
@@ -242,6 +251,9 @@ class Audit {
|
|
|
242
251
|
items,
|
|
243
252
|
overallSavingsMs,
|
|
244
253
|
overallSavingsBytes,
|
|
254
|
+
sortedBy,
|
|
255
|
+
skipSumming,
|
|
256
|
+
isEntityGrouped,
|
|
245
257
|
};
|
|
246
258
|
}
|
|
247
259
|
|
|
@@ -117,8 +117,6 @@ class BootupTime extends Audit {
|
|
|
117
117
|
runWarnings = [str_(UIStrings.chromeExtensionsWarning)];
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
const summary = {wastedMs: totalBootupTime};
|
|
121
|
-
|
|
122
120
|
/** @type {LH.Audit.Details.Table['headings']} */
|
|
123
121
|
const headings = [
|
|
124
122
|
{key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
|
|
@@ -128,7 +126,8 @@ class BootupTime extends Audit {
|
|
|
128
126
|
label: str_(UIStrings.columnScriptParse)},
|
|
129
127
|
];
|
|
130
128
|
|
|
131
|
-
const details = BootupTime.makeTableDetails(headings, results,
|
|
129
|
+
const details = BootupTime.makeTableDetails(headings, results,
|
|
130
|
+
{wastedMs: totalBootupTime, sortedBy: ['total']});
|
|
132
131
|
|
|
133
132
|
const score = Audit.computeLogNormalScore(
|
|
134
133
|
{p10: context.options.p10, median: context.options.median},
|
|
@@ -7,6 +7,7 @@ export type ByteEfficiencyProduct = {
|
|
|
7
7
|
displayValue?: import("../../index.js").IcuMessage | undefined;
|
|
8
8
|
explanation?: import("../../index.js").IcuMessage | undefined;
|
|
9
9
|
warnings?: (string | import("../../index.js").IcuMessage)[] | undefined;
|
|
10
|
+
sortedBy?: string[] | undefined;
|
|
10
11
|
};
|
|
11
12
|
/**
|
|
12
13
|
* @typedef {object} ByteEfficiencyProduct
|
|
@@ -16,6 +17,7 @@ export type ByteEfficiencyProduct = {
|
|
|
16
17
|
* @property {LH.IcuMessage} [displayValue]
|
|
17
18
|
* @property {LH.IcuMessage} [explanation]
|
|
18
19
|
* @property {Array<string | LH.IcuMessage>} [warnings]
|
|
20
|
+
* @property {Array<string>} [sortedBy]
|
|
19
21
|
*/
|
|
20
22
|
/**
|
|
21
23
|
* @overview Used as the base for all byte efficiency audits. Computes total bytes
|
|
@@ -29,6 +29,7 @@ const WASTED_MS_FOR_SCORE_OF_ZERO = 5000;
|
|
|
29
29
|
* @property {LH.IcuMessage} [displayValue]
|
|
30
30
|
* @property {LH.IcuMessage} [explanation]
|
|
31
31
|
* @property {Array<string | LH.IcuMessage>} [warnings]
|
|
32
|
+
* @property {Array<string>} [sortedBy]
|
|
32
33
|
*/
|
|
33
34
|
|
|
34
35
|
/**
|
|
@@ -228,8 +229,9 @@ class ByteEfficiencyAudit extends Audit {
|
|
|
228
229
|
displayValue = str_(i18n.UIStrings.displayValueByteSavings, {wastedBytes});
|
|
229
230
|
}
|
|
230
231
|
|
|
232
|
+
const sortedBy = result.sortedBy || ['wastedBytes'];
|
|
231
233
|
const details = Audit.makeOpportunityDetails(result.headings, results,
|
|
232
|
-
{overallSavingsMs: wastedMs, overallSavingsBytes: wastedBytes});
|
|
234
|
+
{overallSavingsMs: wastedMs, overallSavingsBytes: wastedBytes, sortedBy});
|
|
233
235
|
|
|
234
236
|
return {
|
|
235
237
|
explanation: result.explanation,
|
|
@@ -93,7 +93,7 @@ class TotalByteWeight extends Audit {
|
|
|
93
93
|
{key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnTransferSize)},
|
|
94
94
|
];
|
|
95
95
|
|
|
96
|
-
const tableDetails = Audit.makeTableDetails(headings, results);
|
|
96
|
+
const tableDetails = Audit.makeTableDetails(headings, results, {sortedBy: ['totalBytes']});
|
|
97
97
|
|
|
98
98
|
return {
|
|
99
99
|
score,
|
|
@@ -280,8 +280,8 @@ class CacheHeaders extends Audit {
|
|
|
280
280
|
displayUnit: 'kb', granularity: 1},
|
|
281
281
|
];
|
|
282
282
|
|
|
283
|
-
const
|
|
284
|
-
|
|
283
|
+
const details = Audit.makeTableDetails(headings, results,
|
|
284
|
+
{wastedBytes: totalWastedBytes, sortedBy: ['totalBytes'], skipSumming: ['cacheLifetimeMs']});
|
|
285
285
|
|
|
286
286
|
return {
|
|
287
287
|
score,
|
|
@@ -104,7 +104,8 @@ class LongTasks extends Audit {
|
|
|
104
104
|
/* eslint-enable max-len */
|
|
105
105
|
];
|
|
106
106
|
|
|
107
|
-
const tableDetails = Audit.makeTableDetails(headings, results
|
|
107
|
+
const tableDetails = Audit.makeTableDetails(headings, results,
|
|
108
|
+
{sortedBy: ['duration'], skipSumming: ['startTime']});
|
|
108
109
|
|
|
109
110
|
let displayValue;
|
|
110
111
|
if (results.length > 0) {
|
|
@@ -114,7 +114,8 @@ class MainThreadWorkBreakdown extends Audit {
|
|
|
114
114
|
];
|
|
115
115
|
|
|
116
116
|
results.sort((a, b) => categoryTotals[b.group] - categoryTotals[a.group]);
|
|
117
|
-
const tableDetails = MainThreadWorkBreakdown.makeTableDetails(headings, results
|
|
117
|
+
const tableDetails = MainThreadWorkBreakdown.makeTableDetails(headings, results,
|
|
118
|
+
{sortedBy: ['duration']});
|
|
118
119
|
|
|
119
120
|
const score = Audit.computeLogNormalScore(
|
|
120
121
|
{p10: context.options.p10, median: context.options.median},
|
|
@@ -73,7 +73,7 @@ class NetworkRTT extends Audit {
|
|
|
73
73
|
{key: 'rtt', valueType: 'ms', granularity: 1, label: str_(i18n.UIStrings.columnTimeSpent)},
|
|
74
74
|
];
|
|
75
75
|
|
|
76
|
-
const tableDetails = Audit.makeTableDetails(headings, results);
|
|
76
|
+
const tableDetails = Audit.makeTableDetails(headings, results, {sortedBy: ['rtt']});
|
|
77
77
|
|
|
78
78
|
return {
|
|
79
79
|
score: 1,
|
|
@@ -72,7 +72,8 @@ class NetworkServerLatency extends Audit {
|
|
|
72
72
|
label: str_(i18n.UIStrings.columnTimeSpent)},
|
|
73
73
|
];
|
|
74
74
|
|
|
75
|
-
const tableDetails = Audit.makeTableDetails(headings, results
|
|
75
|
+
const tableDetails = Audit.makeTableDetails(headings, results,
|
|
76
|
+
{sortedBy: ['serverResponseTime']});
|
|
76
77
|
|
|
77
78
|
return {
|
|
78
79
|
score: Math.max(1 - (maxLatency / 500), 0),
|
|
@@ -319,7 +319,7 @@ class PrioritizeLcpImage extends Audit {
|
|
|
319
319
|
{key: 'wastedMs', valueType: 'timespanMs', label: str_(i18n.UIStrings.columnWastedMs)},
|
|
320
320
|
];
|
|
321
321
|
const details = Audit.makeOpportunityDetails(headings, results,
|
|
322
|
-
{overallSavingsMs: wastedMs});
|
|
322
|
+
{overallSavingsMs: wastedMs, sortedBy: ['wastedMs']});
|
|
323
323
|
|
|
324
324
|
// If LCP element was an image and had valid network records (regardless of
|
|
325
325
|
// if it should be preloaded), it will be found first in the `initiatorPath`.
|
|
@@ -243,12 +243,15 @@ class ThirdPartySummary extends Audit {
|
|
|
243
243
|
};
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
+
const details = Audit.makeTableDetails(headings, results,
|
|
247
|
+
{...overallSummary, isEntityGrouped: true});
|
|
248
|
+
|
|
246
249
|
return {
|
|
247
250
|
score: Number(overallSummary.wastedMs <= PASS_THRESHOLD_IN_MS),
|
|
248
251
|
displayValue: str_(UIStrings.displayValue, {
|
|
249
252
|
timeInMs: overallSummary.wastedMs,
|
|
250
253
|
}),
|
|
251
|
-
details
|
|
254
|
+
details,
|
|
252
255
|
};
|
|
253
256
|
}
|
|
254
257
|
}
|
|
@@ -164,7 +164,8 @@ class TimingBudget extends Audit {
|
|
|
164
164
|
];
|
|
165
165
|
|
|
166
166
|
return {
|
|
167
|
-
details: Audit.makeTableDetails(headers, this.tableItems(budget, summary)
|
|
167
|
+
details: Audit.makeTableDetails(headers, this.tableItems(budget, summary),
|
|
168
|
+
{sortedBy: ['overBudget']}),
|
|
168
169
|
score: 1,
|
|
169
170
|
};
|
|
170
171
|
}
|
|
@@ -234,7 +234,7 @@ class UsesRelPreconnectAudit extends Audit {
|
|
|
234
234
|
];
|
|
235
235
|
|
|
236
236
|
const details = Audit.makeOpportunityDetails(headings, results,
|
|
237
|
-
{overallSavingsMs: maxWasted});
|
|
237
|
+
{overallSavingsMs: maxWasted, sortedBy: ['wastedMs']});
|
|
238
238
|
|
|
239
239
|
return {
|
|
240
240
|
score: ByteEfficiencyAudit.scoreForWastedMs(maxWasted),
|
|
@@ -240,7 +240,7 @@ class UsesRelPreloadAudit extends Audit {
|
|
|
240
240
|
{key: 'wastedMs', valueType: 'timespanMs', label: str_(i18n.UIStrings.columnWastedMs)},
|
|
241
241
|
];
|
|
242
242
|
const details = Audit.makeOpportunityDetails(headings, results,
|
|
243
|
-
{overallSavingsMs: wastedMs});
|
|
243
|
+
{overallSavingsMs: wastedMs, sortedBy: ['wastedMs']});
|
|
244
244
|
|
|
245
245
|
return {
|
|
246
246
|
score: ByteEfficiencyAudit.scoreForWastedMs(wastedMs),
|
|
@@ -81,6 +81,23 @@ function upgradeLhrForCompatibility(lhr) {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
// In 10.0, third-party-summary deprecated entity: LinkValue and switched to entity name string
|
|
85
|
+
if (audit.id === 'third-party-summary') {
|
|
86
|
+
if (audit.details.type === 'opportunity' || audit.details.type === 'table') {
|
|
87
|
+
const {headings, items} = audit.details;
|
|
88
|
+
if (headings[0].valueType === 'link') {
|
|
89
|
+
// Apply upgrade only if we are dealing with an older version (valueType=link marker).
|
|
90
|
+
headings[0].valueType = 'text';
|
|
91
|
+
for (const item of items) {
|
|
92
|
+
if (typeof item.entity === 'object' && item.entity.type === 'link') {
|
|
93
|
+
item.entity = item.entity.text;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
audit.details.isEntityGrouped = true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
84
101
|
// TODO: convert printf-style displayValue.
|
|
85
102
|
// Added: #5099, v3
|
|
86
103
|
// Removed: #6767, v4
|
package/core/runner.js
CHANGED
|
@@ -170,6 +170,7 @@ class Runner {
|
|
|
170
170
|
// Reduce payload size in LHR JSON by omitting whats falsy.
|
|
171
171
|
if (entity === classifiedEntities.firstParty) shortEntity.isFirstParty = true;
|
|
172
172
|
if (entity.isUnrecognized) shortEntity.isUnrecognized = true;
|
|
173
|
+
if (entity.category) shortEntity.category = entity.category;
|
|
173
174
|
entities.push(shortEntity);
|
|
174
175
|
}
|
|
175
176
|
|