lighthouse 8.6.0-dev.20211107 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/report/bundle.esm.js +5994 -0
- package/dist/report/flow.js +16 -16
- package/dist/report/standalone.js +8 -8
- package/flow-report/src/app.tsx +10 -9
- package/flow-report/src/help-dialog.tsx +0 -1
- package/lighthouse-cli/run.js +1 -1
- package/lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js +0 -0
- package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +2 -0
- package/lighthouse-core/audits/deprecations.js +33 -11
- package/lighthouse-core/audits/dobetterweb/uses-http2.js +27 -11
- package/lighthouse-core/audits/installable-manifest.js +5 -3
- package/lighthouse-core/config/constants.js +3 -4
- package/lighthouse-core/config/default-config.js +2 -2
- package/lighthouse-core/fraggle-rock/config/default-config.js +3 -1
- package/lighthouse-core/fraggle-rock/gather/session.js +3 -4
- package/lighthouse-core/gather/driver.js +3 -7
- package/lighthouse-core/gather/fetcher.js +4 -7
- package/lighthouse-core/gather/gather-runner.js +29 -6
- package/lighthouse-core/gather/gatherers/inspector-issues.js +5 -0
- package/lighthouse-core/lib/emulation.js +45 -1
- package/lighthouse-core/util-commonjs.js +17 -0
- package/package.json +8 -5
- package/report/assets/styles.css +9 -8
- package/report/renderer/components.js +1 -1
- package/report/renderer/performance-category-renderer.js +0 -1
- package/report/renderer/report-renderer.js +1 -1
- package/report/renderer/topbar-features.js +2 -1
- package/report/renderer/util.js +17 -0
- package/report/test/renderer/performance-category-renderer-test.js +15 -4
- package/report/test/renderer/util-test.js +22 -0
- package/shared/localization/locales/ar-XB.json +73 -25
- package/shared/localization/locales/ar.json +73 -25
- package/shared/localization/locales/bg.json +73 -25
- package/shared/localization/locales/ca.json +73 -25
- package/shared/localization/locales/cs.json +90 -42
- package/shared/localization/locales/da.json +73 -25
- package/shared/localization/locales/de.json +90 -42
- package/shared/localization/locales/el.json +73 -25
- package/shared/localization/locales/en-GB.json +73 -25
- package/shared/localization/locales/en-US.json +3 -0
- package/shared/localization/locales/en-XA.json +73 -25
- package/shared/localization/locales/en-XL.json +3 -0
- package/shared/localization/locales/es-419.json +91 -43
- package/shared/localization/locales/es.json +91 -43
- package/shared/localization/locales/fi.json +73 -25
- package/shared/localization/locales/fil.json +73 -25
- package/shared/localization/locales/fr.json +90 -42
- package/shared/localization/locales/he.json +74 -26
- package/shared/localization/locales/hi.json +73 -25
- package/shared/localization/locales/hr.json +73 -25
- package/shared/localization/locales/hu.json +90 -42
- package/shared/localization/locales/id.json +90 -42
- package/shared/localization/locales/it.json +90 -42
- package/shared/localization/locales/ja.json +90 -42
- package/shared/localization/locales/ko.json +90 -42
- package/shared/localization/locales/lt.json +90 -42
- package/shared/localization/locales/lv.json +96 -48
- package/shared/localization/locales/nl.json +90 -42
- package/shared/localization/locales/no.json +73 -25
- package/shared/localization/locales/pl.json +73 -25
- package/shared/localization/locales/pt-PT.json +73 -25
- package/shared/localization/locales/pt.json +90 -42
- package/shared/localization/locales/ro.json +91 -43
- package/shared/localization/locales/ru.json +90 -42
- package/shared/localization/locales/sk.json +90 -42
- package/shared/localization/locales/sl.json +90 -42
- package/shared/localization/locales/sr-Latn.json +90 -42
- package/shared/localization/locales/sr.json +90 -42
- package/shared/localization/locales/sv.json +73 -25
- package/shared/localization/locales/ta.json +90 -42
- package/shared/localization/locales/te.json +90 -42
- package/shared/localization/locales/th.json +90 -42
- package/shared/localization/locales/tr.json +90 -42
- package/shared/localization/locales/uk.json +93 -45
- package/shared/localization/locales/vi.json +90 -42
- package/shared/localization/locales/zh-HK.json +90 -42
- package/shared/localization/locales/zh-TW.json +90 -42
- package/shared/localization/locales/zh.json +90 -42
- package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +19 -11
- package/types/artifacts.d.ts +1 -0
- package/types/lhr/audit-details.d.ts +1 -0
- package/types/lhr/lhr.d.ts +1 -1
- package/changelog.md +0 -5490
- package/lighthouse-core/scripts/package.json +0 -4
package/flow-report/src/app.tsx
CHANGED
|
@@ -16,21 +16,22 @@ import {Topbar} from './topbar';
|
|
|
16
16
|
import {Header} from './header';
|
|
17
17
|
import {I18nProvider} from './i18n/i18n';
|
|
18
18
|
|
|
19
|
+
function getAnchorElement(hashState: LH.FlowResult.HashState|null) {
|
|
20
|
+
if (!hashState || !hashState.anchor) return null;
|
|
21
|
+
return document.getElementById(hashState.anchor);
|
|
22
|
+
}
|
|
23
|
+
|
|
19
24
|
const Content: FunctionComponent = () => {
|
|
20
25
|
const hashState = useHashState();
|
|
21
26
|
const ref = useRef<HTMLDivElement>(null);
|
|
22
27
|
|
|
23
28
|
useLayoutEffect(() => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
29
|
+
const el = getAnchorElement(hashState);
|
|
30
|
+
if (el) {
|
|
31
|
+
el.scrollIntoView();
|
|
32
|
+
} else if (ref.current) {
|
|
33
|
+
ref.current.scrollTop = 0;
|
|
30
34
|
}
|
|
31
|
-
|
|
32
|
-
// Scroll to top no anchor is found.
|
|
33
|
-
if (ref.current) ref.current.scrollTop = 0;
|
|
34
35
|
}, [hashState]);
|
|
35
36
|
|
|
36
37
|
return (
|
package/lighthouse-cli/run.js
CHANGED
|
@@ -175,7 +175,7 @@ async function potentiallyKillChrome(launchedChrome) {
|
|
|
175
175
|
/** @type {NodeJS.Timeout} */
|
|
176
176
|
let timeout;
|
|
177
177
|
const timeoutPromise = new Promise((_, reject) => {
|
|
178
|
-
timeout = setTimeout(
|
|
178
|
+
timeout = setTimeout(reject, 5000, new Error('Timed out waiting to kill Chrome'));
|
|
179
179
|
});
|
|
180
180
|
|
|
181
181
|
return Promise.race([
|
|
File without changes
|
|
@@ -206,6 +206,8 @@ class UnusedBytes extends Audit {
|
|
|
206
206
|
*/
|
|
207
207
|
static computeWastedMsWithThroughput(wastedBytes, simulator) {
|
|
208
208
|
const bitsPerSecond = simulator.getOptions().throughput;
|
|
209
|
+
// https://github.com/GoogleChrome/lighthouse/pull/13323#issuecomment-962031709
|
|
210
|
+
if (bitsPerSecond === 0) return 0;
|
|
209
211
|
const wastedBits = wastedBytes * 8;
|
|
210
212
|
const wastedMs = wastedBits / bitsPerSecond * 1000;
|
|
211
213
|
return wastedMs;
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* that contain deprecated API warnings sent by Chrome.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
// TODO: when M97 is sufficiently old, drop support for console messages
|
|
15
|
+
|
|
14
16
|
const Audit = require('./audit.js');
|
|
15
17
|
const i18n = require('../lib/i18n/i18n.js');
|
|
16
18
|
|
|
@@ -45,7 +47,7 @@ class Deprecations extends Audit {
|
|
|
45
47
|
title: str_(UIStrings.title),
|
|
46
48
|
failureTitle: str_(UIStrings.failureTitle),
|
|
47
49
|
description: str_(UIStrings.description),
|
|
48
|
-
requiredArtifacts: ['ConsoleMessages'],
|
|
50
|
+
requiredArtifacts: ['ConsoleMessages', 'InspectorIssues'],
|
|
49
51
|
};
|
|
50
52
|
}
|
|
51
53
|
|
|
@@ -56,16 +58,36 @@ class Deprecations extends Audit {
|
|
|
56
58
|
static audit(artifacts) {
|
|
57
59
|
const entries = artifacts.ConsoleMessages;
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
61
|
+
let deprecations;
|
|
62
|
+
|
|
63
|
+
if (artifacts.InspectorIssues.deprecations.length) {
|
|
64
|
+
deprecations = artifacts.InspectorIssues.deprecations
|
|
65
|
+
.map(deprecation => {
|
|
66
|
+
return {
|
|
67
|
+
value: deprecation.message || '',
|
|
68
|
+
/** @type {LH.Audit.Details.SourceLocationValue} */
|
|
69
|
+
source: {
|
|
70
|
+
type: 'source-location',
|
|
71
|
+
url: deprecation.sourceCodeLocation.url,
|
|
72
|
+
urlProvider: 'network',
|
|
73
|
+
line: deprecation.sourceCodeLocation.lineNumber,
|
|
74
|
+
// Protocol.Audits.SourceCodeLocation.columnNumber is 1-indexed,
|
|
75
|
+
// but we use 0-indexed.
|
|
76
|
+
column: deprecation.sourceCodeLocation.columnNumber - 1,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
} else {
|
|
81
|
+
// Backcompat for <M97.
|
|
82
|
+
// https://bugs.chromium.org/p/chromium/issues/detail?id=1248484
|
|
83
|
+
deprecations = entries.filter(log => log.source === 'deprecation')
|
|
84
|
+
.map(log => {
|
|
85
|
+
return {
|
|
86
|
+
value: log.text,
|
|
87
|
+
source: Audit.makeSourceLocationFromConsoleMessage(log),
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
}
|
|
69
91
|
|
|
70
92
|
/** @type {LH.Audit.Details.Table['headings']} */
|
|
71
93
|
const headings = [
|
|
@@ -61,8 +61,8 @@ class UsesHTTP2Audit extends Audit {
|
|
|
61
61
|
title: str_(UIStrings.title),
|
|
62
62
|
description: str_(UIStrings.description),
|
|
63
63
|
scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
|
|
64
|
-
supportedModes: ['navigation'],
|
|
65
|
-
requiredArtifacts: ['URL', 'devtoolsLogs', 'traces'],
|
|
64
|
+
supportedModes: ['timespan', 'navigation'],
|
|
65
|
+
requiredArtifacts: ['URL', 'devtoolsLogs', 'traces', 'GatherContext'],
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
|
|
@@ -202,24 +202,40 @@ class UsesHTTP2Audit extends Audit {
|
|
|
202
202
|
static async audit(artifacts, context) {
|
|
203
203
|
const trace = artifacts.traces[Audit.DEFAULT_PASS];
|
|
204
204
|
const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
|
|
205
|
+
const networkRecords = await NetworkRecords.request(devtoolsLog, context);
|
|
206
|
+
const resources = UsesHTTP2Audit.determineNonHttp2Resources(networkRecords);
|
|
207
|
+
|
|
208
|
+
let displayValue;
|
|
209
|
+
if (resources.length > 0) {
|
|
210
|
+
displayValue = str_(UIStrings.displayValue, {itemCount: resources.length});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// TODO: Compute actual savings for timespan mode.
|
|
214
|
+
if (artifacts.GatherContext.gatherMode === 'timespan') {
|
|
215
|
+
/** @type {LH.Audit.Details.Table['headings']} */
|
|
216
|
+
const headings = [
|
|
217
|
+
{key: 'url', itemType: 'url', text: str_(i18n.UIStrings.columnURL)},
|
|
218
|
+
{key: 'protocol', itemType: 'text', text: str_(UIStrings.columnProtocol)},
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
const details = Audit.makeTableDetails(headings, resources);
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
displayValue,
|
|
225
|
+
score: resources.length ? 0 : 1,
|
|
226
|
+
details,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
205
230
|
const settings = context && context.settings || {};
|
|
206
231
|
const simulatorOptions = {
|
|
207
232
|
devtoolsLog,
|
|
208
233
|
settings,
|
|
209
234
|
};
|
|
210
|
-
|
|
211
|
-
const networkRecords = await NetworkRecords.request(devtoolsLog, context);
|
|
212
235
|
const graph = await PageDependencyGraph.request({trace, devtoolsLog}, context);
|
|
213
236
|
const simulator = await LoadSimulator.request(simulatorOptions, context);
|
|
214
|
-
|
|
215
|
-
const resources = UsesHTTP2Audit.determineNonHttp2Resources(networkRecords);
|
|
216
237
|
const wastedMs = UsesHTTP2Audit.computeWasteWithTTIGraph(resources, graph, simulator);
|
|
217
238
|
|
|
218
|
-
let displayValue;
|
|
219
|
-
if (resources.length > 0) {
|
|
220
|
-
displayValue = str_(UIStrings.displayValue, {itemCount: resources.length});
|
|
221
|
-
}
|
|
222
|
-
|
|
223
239
|
/** @type {LH.Audit.Details.Opportunity['headings']} */
|
|
224
240
|
const headings = [
|
|
225
241
|
{key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
|
|
@@ -50,9 +50,9 @@ const UIStrings = {
|
|
|
50
50
|
* for the current page encloses the scope and start URL from the manifest. */
|
|
51
51
|
'no-matching-service-worker': `No matching service worker detected. You may need to reload the page, or check that the scope of the service worker for the current page encloses the scope and start URL from the manifest.`,
|
|
52
52
|
/**
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
* @description Error message explaining that the manifest does not contain a suitable icon.
|
|
54
|
+
* @example {192} value0
|
|
55
|
+
*/
|
|
56
56
|
'manifest-missing-suitable-icon': `Manifest does not contain a suitable icon - PNG, SVG or WebP format of at least {value0}\xa0px is required, the sizes attribute must be set, and the purpose attribute, if set, must include "any".`,
|
|
57
57
|
|
|
58
58
|
/**
|
|
@@ -93,6 +93,8 @@ const UIStrings = {
|
|
|
93
93
|
'manifest-location-changed': `Manifest URL changed while the manifest was being fetched.`,
|
|
94
94
|
/** Warning message explaining that the page does not work offline. */
|
|
95
95
|
'warn-not-offline-capable': `Page does not work offline. The page will not be regarded as installable after Chrome 93, stable release August 2021.`,
|
|
96
|
+
/** Error message explaining that Lighthouse failed while detecting a service worker, and directing the user to try again in a new Chrome. */
|
|
97
|
+
'protocol-timeout': `Lighthouse could not determine if there was a service worker. Please try with a newer version of Chrome.`,
|
|
96
98
|
};
|
|
97
99
|
/* eslint-enable max-len */
|
|
98
100
|
|
|
@@ -81,10 +81,9 @@ const screenEmulationMetrics = {
|
|
|
81
81
|
desktop: DESKTOP_EMULATION_METRICS,
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
const MOTOG4_USERAGENT = 'Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
86
|
-
// eslint-disable-
|
|
87
|
-
const DESKTOP_USERAGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4590.2 Safari/537.36 Chrome-Lighthouse';
|
|
84
|
+
|
|
85
|
+
const MOTOG4_USERAGENT = 'Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4695.0 Mobile Safari/537.36 Chrome-Lighthouse'; // eslint-disable-line max-len
|
|
86
|
+
const DESKTOP_USERAGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4695.0 Safari/537.36 Chrome-Lighthouse'; // eslint-disable-line max-len
|
|
88
87
|
|
|
89
88
|
const userAgents = {
|
|
90
89
|
mobile: MOTOG4_USERAGENT,
|
|
@@ -413,10 +413,10 @@ const defaultConfig = {
|
|
|
413
413
|
supportedModes: ['navigation', 'timespan', 'snapshot'],
|
|
414
414
|
auditRefs: [
|
|
415
415
|
{id: 'first-contentful-paint', weight: 10, group: 'metrics', acronym: 'FCP', relevantAudits: m2a.fcpRelevantAudits},
|
|
416
|
-
{id: 'speed-index', weight: 10, group: 'metrics', acronym: 'SI'},
|
|
417
|
-
{id: 'largest-contentful-paint', weight: 25, group: 'metrics', acronym: 'LCP', relevantAudits: m2a.lcpRelevantAudits},
|
|
418
416
|
{id: 'interactive', weight: 10, group: 'metrics', acronym: 'TTI'},
|
|
417
|
+
{id: 'speed-index', weight: 10, group: 'metrics', acronym: 'SI'},
|
|
419
418
|
{id: 'total-blocking-time', weight: 30, group: 'metrics', acronym: 'TBT', relevantAudits: m2a.tbtRelevantAudits},
|
|
419
|
+
{id: 'largest-contentful-paint', weight: 25, group: 'metrics', acronym: 'LCP', relevantAudits: m2a.lcpRelevantAudits},
|
|
420
420
|
{id: 'cumulative-layout-shift', weight: 15, group: 'metrics', acronym: 'CLS', relevantAudits: m2a.clsRelevantAudits},
|
|
421
421
|
|
|
422
422
|
// These are our "invisible" metrics. Not displayed, but still in the LHR.
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
const legacyDefaultConfig = require('../../config/default-config.js');
|
|
9
|
+
const {deepClone} = require('../../config/config-helpers.js');
|
|
9
10
|
|
|
10
11
|
/** @type {LH.Config.AuditJson[]} */
|
|
11
12
|
const frAudits = [
|
|
@@ -22,7 +23,8 @@ const frCategoryAuditRefExtensions = {
|
|
|
22
23
|
/** @return {LH.Config.Json['categories']} */
|
|
23
24
|
function mergeCategories() {
|
|
24
25
|
if (!legacyDefaultConfig.categories) return {};
|
|
25
|
-
|
|
26
|
+
// Don't modify original default config.
|
|
27
|
+
const categories = deepClone(legacyDefaultConfig.categories);
|
|
26
28
|
for (const key of Object.keys(frCategoryAuditRefExtensions)) {
|
|
27
29
|
if (!categories[key]) continue;
|
|
28
30
|
categories[key].auditRefs.push(...frCategoryAuditRefExtensions[key]);
|
|
@@ -150,10 +150,9 @@ class ProtocolSession {
|
|
|
150
150
|
const timeoutPromise = new Promise((resolve, reject) => {
|
|
151
151
|
if (timeoutMs === Infinity) return;
|
|
152
152
|
|
|
153
|
-
timeout = setTimeout((
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}), timeoutMs);
|
|
153
|
+
timeout = setTimeout(reject, timeoutMs, new LHError(LHError.errors.PROTOCOL_TIMEOUT, {
|
|
154
|
+
protocolMethod: method,
|
|
155
|
+
}));
|
|
157
156
|
});
|
|
158
157
|
|
|
159
158
|
const resultPromise = this._session.send(method, ...params);
|
|
@@ -338,13 +338,9 @@ class Driver {
|
|
|
338
338
|
let asyncTimeout;
|
|
339
339
|
const timeoutPromise = new Promise((resolve, reject) => {
|
|
340
340
|
if (timeout === Infinity) return;
|
|
341
|
-
asyncTimeout = setTimeout((
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
{protocolMethod: method}
|
|
345
|
-
);
|
|
346
|
-
reject(err);
|
|
347
|
-
}), timeout);
|
|
341
|
+
asyncTimeout = setTimeout(reject, timeout, new LHError(LHError.errors.PROTOCOL_TIMEOUT, {
|
|
342
|
+
protocolMethod: method,
|
|
343
|
+
}));
|
|
348
344
|
});
|
|
349
345
|
|
|
350
346
|
return Promise.race([
|
|
@@ -195,9 +195,7 @@ class Fetcher {
|
|
|
195
195
|
/** @type {NodeJS.Timeout} */
|
|
196
196
|
let timeoutHandle;
|
|
197
197
|
const timeoutPromise = new Promise((_, reject) => {
|
|
198
|
-
timeoutHandle = setTimeout((
|
|
199
|
-
reject(new Error('Timed out fetching resource'));
|
|
200
|
-
}, options.timeout);
|
|
198
|
+
timeoutHandle = setTimeout(reject, options.timeout, new Error('Timed out fetching resource'));
|
|
201
199
|
});
|
|
202
200
|
|
|
203
201
|
const responsePromise = this._loadNetworkResource(url);
|
|
@@ -283,17 +281,16 @@ class Fetcher {
|
|
|
283
281
|
/* c8 ignore stop */
|
|
284
282
|
|
|
285
283
|
/** @type {NodeJS.Timeout} */
|
|
286
|
-
let
|
|
284
|
+
let asyncTimeout;
|
|
287
285
|
/** @type {Promise<never>} */
|
|
288
286
|
const timeoutPromise = new Promise((_, reject) => {
|
|
289
|
-
|
|
290
|
-
timeoutHandle = setTimeout(() => reject(new Error(errorMessage)), options.timeout);
|
|
287
|
+
asyncTimeout = setTimeout(reject, options.timeout, new Error('Timed out fetching resource.'));
|
|
291
288
|
});
|
|
292
289
|
|
|
293
290
|
const racePromise = Promise.race([
|
|
294
291
|
timeoutPromise,
|
|
295
292
|
requestInterceptionPromise,
|
|
296
|
-
]).finally(() => clearTimeout(
|
|
293
|
+
]).finally(() => clearTimeout(asyncTimeout));
|
|
297
294
|
|
|
298
295
|
// Temporarily disable auto-attaching for this iframe.
|
|
299
296
|
await this.session.sendCommand('Target.setAutoAttach', {
|
|
@@ -422,21 +422,44 @@ class GatherRunner {
|
|
|
422
422
|
static async populateBaseArtifacts(passContext) {
|
|
423
423
|
const status = {msg: 'Populate base artifacts', id: 'lh:gather:populateBaseArtifacts'};
|
|
424
424
|
log.time(status);
|
|
425
|
+
|
|
425
426
|
const baseArtifacts = passContext.baseArtifacts;
|
|
426
427
|
|
|
427
428
|
// Copy redirected URL to artifact.
|
|
428
429
|
baseArtifacts.URL.finalUrl = passContext.url;
|
|
429
430
|
|
|
430
431
|
// Fetch the manifest, if it exists.
|
|
431
|
-
|
|
432
|
-
|
|
432
|
+
try {
|
|
433
|
+
baseArtifacts.WebAppManifest = await WebAppManifest.getWebAppManifest(
|
|
434
|
+
passContext.driver.defaultSession, passContext.url);
|
|
435
|
+
} catch (err) {
|
|
436
|
+
log.error('GatherRunner WebAppManifest', err);
|
|
437
|
+
baseArtifacts.WebAppManifest = null;
|
|
438
|
+
}
|
|
433
439
|
|
|
434
|
-
|
|
435
|
-
baseArtifacts.
|
|
436
|
-
|
|
440
|
+
try {
|
|
441
|
+
if (baseArtifacts.WebAppManifest) {
|
|
442
|
+
baseArtifacts.InstallabilityErrors = await InstallabilityErrors.getInstallabilityErrors(
|
|
443
|
+
passContext.driver.defaultSession);
|
|
444
|
+
}
|
|
445
|
+
} catch (err) {
|
|
446
|
+
log.error('GatherRunner InstallabilityErrors', err);
|
|
447
|
+
baseArtifacts.InstallabilityErrors = {
|
|
448
|
+
errors: [
|
|
449
|
+
{
|
|
450
|
+
errorId: 'protocol-timeout',
|
|
451
|
+
errorArguments: [],
|
|
452
|
+
},
|
|
453
|
+
],
|
|
454
|
+
};
|
|
437
455
|
}
|
|
438
456
|
|
|
439
|
-
|
|
457
|
+
try {
|
|
458
|
+
baseArtifacts.Stacks = await Stacks.collectStacks(passContext.driver.executionContext);
|
|
459
|
+
} catch (err) {
|
|
460
|
+
log.error('GatherRunner Stacks', err);
|
|
461
|
+
baseArtifacts.Stacks = [];
|
|
462
|
+
}
|
|
440
463
|
|
|
441
464
|
// Find the NetworkUserAgent actually used in the devtoolsLogs.
|
|
442
465
|
const devtoolsLog = baseArtifacts.devtoolsLogs[passContext.passConfig.passName];
|
|
@@ -69,6 +69,8 @@ class InspectorIssues extends FRGatherer {
|
|
|
69
69
|
heavyAds: [],
|
|
70
70
|
/** @type {Array<LH.Crdp.Audits.ContentSecurityPolicyIssueDetails>} */
|
|
71
71
|
contentSecurityPolicy: [],
|
|
72
|
+
/** @type {Array<LH.Crdp.Audits.DeprecationIssueDetails>} */
|
|
73
|
+
deprecations: [],
|
|
72
74
|
};
|
|
73
75
|
|
|
74
76
|
for (const issue of this._issues) {
|
|
@@ -105,6 +107,9 @@ class InspectorIssues extends FRGatherer {
|
|
|
105
107
|
if (issue.details.contentSecurityPolicyIssueDetails) {
|
|
106
108
|
artifact.contentSecurityPolicy.push(issue.details.contentSecurityPolicyIssueDetails);
|
|
107
109
|
}
|
|
110
|
+
if (issue.details.deprecationIssueDetails) {
|
|
111
|
+
artifact.deprecations.push(issue.details.deprecationIssueDetails);
|
|
112
|
+
}
|
|
108
113
|
}
|
|
109
114
|
|
|
110
115
|
return artifact;
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
+
const {version: lighthouseVersion} = require('../../package.json');
|
|
9
|
+
|
|
8
10
|
const NO_THROTTLING_METRICS = {
|
|
9
11
|
latency: 0,
|
|
10
12
|
downloadThroughput: 0,
|
|
@@ -16,6 +18,46 @@ const NO_CPU_THROTTLE_METRICS = {
|
|
|
16
18
|
rate: 1,
|
|
17
19
|
};
|
|
18
20
|
|
|
21
|
+
/**
|
|
22
|
+
* @param {string} userAgent
|
|
23
|
+
* @param {LH.Config.Settings['formFactor']} formFactor
|
|
24
|
+
* @return {LH.Crdp.Emulation.SetUserAgentOverrideRequest['userAgentMetadata']}
|
|
25
|
+
*/
|
|
26
|
+
function parseUseragentIntoMetadata(userAgent, formFactor) {
|
|
27
|
+
const match = userAgent.match(/Chrome\/([\d.]+)/); // eg 'Chrome/(71.0.3577.0)'
|
|
28
|
+
const fullVersion = (match && match[1]) || '99.0.1234.0';
|
|
29
|
+
const [version] = fullVersion.split('.', 1);
|
|
30
|
+
const brands = [
|
|
31
|
+
{brand: 'Chromium', version},
|
|
32
|
+
{brand: 'Google Chrome', version},
|
|
33
|
+
{brand: 'Lighthouse', version: lighthouseVersion},
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const motoG4Details = {
|
|
37
|
+
platform: 'Android',
|
|
38
|
+
platformVersion: '6.0',
|
|
39
|
+
architecture: '',
|
|
40
|
+
model: 'Moto G4',
|
|
41
|
+
};
|
|
42
|
+
const macDesktopDetails = {
|
|
43
|
+
platform: 'macOS',
|
|
44
|
+
platformVersion: '10.15.7',
|
|
45
|
+
architecture: 'x86',
|
|
46
|
+
model: '',
|
|
47
|
+
};
|
|
48
|
+
const mobile = formFactor === 'mobile';
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
brands,
|
|
52
|
+
fullVersion,
|
|
53
|
+
// Since config users can supply a custom useragent, they likely are emulating something
|
|
54
|
+
// other than Moto G4 and MacOS Desktop.
|
|
55
|
+
// TODO: Determine how to thoughtfully expose this metadata/client-hints configurability.
|
|
56
|
+
...(mobile ? motoG4Details : macDesktopDetails),
|
|
57
|
+
mobile,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
19
61
|
/**
|
|
20
62
|
* @param {LH.Gatherer.FRProtocolSession} session
|
|
21
63
|
* @param {LH.Config.Settings} settings
|
|
@@ -23,8 +65,10 @@ const NO_CPU_THROTTLE_METRICS = {
|
|
|
23
65
|
*/
|
|
24
66
|
async function emulate(session, settings) {
|
|
25
67
|
if (settings.emulatedUserAgent !== false) {
|
|
68
|
+
const userAgent = /** @type {string} */ (settings.emulatedUserAgent);
|
|
26
69
|
await session.sendCommand('Network.setUserAgentOverride', {
|
|
27
|
-
userAgent
|
|
70
|
+
userAgent,
|
|
71
|
+
userAgentMetadata: parseUseragentIntoMetadata(userAgent, settings.formFactor),
|
|
28
72
|
});
|
|
29
73
|
}
|
|
30
74
|
// See devtools-entry for one usecase for disabling screenEmulation
|
|
@@ -111,6 +111,23 @@ class Util {
|
|
|
111
111
|
/** @type {Map<string, Array<LH.ReportResult.AuditRef>>} */
|
|
112
112
|
const relevantAuditToMetricsMap = new Map();
|
|
113
113
|
|
|
114
|
+
// This backcompat converts old LHRs (<9.0.0) to use the new "hidden" group.
|
|
115
|
+
// Old LHRs used "no group" to identify audits that should be hidden in performance instead of the "hidden" group.
|
|
116
|
+
// Newer LHRs use "no group" to identify opportunities and diagnostics whose groups are assigned by details type.
|
|
117
|
+
const [majorVersion] = clone.lighthouseVersion.split('.').map(Number);
|
|
118
|
+
const perfCategory = clone.categories['performance'];
|
|
119
|
+
if (majorVersion < 9 && perfCategory) {
|
|
120
|
+
if (!clone.categoryGroups) clone.categoryGroups = {};
|
|
121
|
+
clone.categoryGroups['hidden'] = {title: ''};
|
|
122
|
+
for (const auditRef of perfCategory.auditRefs) {
|
|
123
|
+
if (!auditRef.group) {
|
|
124
|
+
auditRef.group = 'hidden';
|
|
125
|
+
} else if (['load-opportunities', 'diagnostics'].includes(auditRef.group)) {
|
|
126
|
+
delete auditRef.group;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
114
131
|
for (const category of Object.values(clone.categories)) {
|
|
115
132
|
// Make basic lookup table for relevantAudits
|
|
116
133
|
category.auditRefs.forEach(metricRef => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lighthouse",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0",
|
|
4
4
|
"description": "Automated auditing, performance metrics, and best practices for the web.",
|
|
5
5
|
"main": "./lighthouse-core/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"node": ">=14.15"
|
|
13
13
|
},
|
|
14
14
|
"scripts": {
|
|
15
|
-
"prepack": "yarn build-report --standalone --flow",
|
|
15
|
+
"prepack": "yarn build-report --standalone --flow --esm",
|
|
16
16
|
"build-all": "npm-run-posix-or-windows build-all:task",
|
|
17
17
|
"build-all:task": "yarn build-report && yarn build-cdt-lib && yarn build-devtools && (yarn build-extension & yarn build-lr & yarn build-viewer & yarn build-treemap & yarn build-smokehouse-bundle & wait) && yarn build-pack",
|
|
18
18
|
"build-all:task:windows": "yarn build-report && yarn build-cdt-lib && yarn build-extension && yarn build-devtools && yarn build-lr && yarn build-viewer && yarn build-treemap && yarn build-smokehouse-bundle",
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
"cpy": "^7.0.1",
|
|
147
147
|
"cross-env": "^7.0.2",
|
|
148
148
|
"csv-validator": "^0.0.3",
|
|
149
|
-
"devtools-protocol": "0.0.
|
|
149
|
+
"devtools-protocol": "0.0.939404",
|
|
150
150
|
"es-main": "^1.0.2",
|
|
151
151
|
"eslint": "^7.23.0",
|
|
152
152
|
"eslint-config-google": "^0.9.1",
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
"jsdom": "^12.2.0",
|
|
163
163
|
"jsonld": "^5.2.0",
|
|
164
164
|
"jsonlint-mod": "^1.7.6",
|
|
165
|
-
"lighthouse-plugin-publisher-ads": "^1.4
|
|
165
|
+
"lighthouse-plugin-publisher-ads": "^1.5.4",
|
|
166
166
|
"magic-string": "^0.25.7",
|
|
167
167
|
"mime-types": "^2.1.30",
|
|
168
168
|
"node-fetch": "^2.6.1",
|
|
@@ -188,7 +188,7 @@
|
|
|
188
188
|
},
|
|
189
189
|
"dependencies": {
|
|
190
190
|
"axe-core": "4.2.3",
|
|
191
|
-
"chrome-launcher": "^0.
|
|
191
|
+
"chrome-launcher": "^0.15.0",
|
|
192
192
|
"configstore": "^5.0.1",
|
|
193
193
|
"csp_evaluator": "1.1.0",
|
|
194
194
|
"cssstyle": "1.2.1",
|
|
@@ -218,6 +218,9 @@
|
|
|
218
218
|
"yargs": "^16.1.1",
|
|
219
219
|
"yargs-parser": "^20.2.4"
|
|
220
220
|
},
|
|
221
|
+
"resolutions": {
|
|
222
|
+
"puppeteer/**/devtools-protocol": "0.0.939404"
|
|
223
|
+
},
|
|
221
224
|
"repository": "GoogleChrome/lighthouse",
|
|
222
225
|
"keywords": [
|
|
223
226
|
"google",
|
package/report/assets/styles.css
CHANGED
|
@@ -482,9 +482,7 @@
|
|
|
482
482
|
border-radius: 3px;
|
|
483
483
|
color: var(--link-color);
|
|
484
484
|
background-color: var(--report-background-color);
|
|
485
|
-
|
|
486
|
-
font-size: var(--report-font-size-secondary);
|
|
487
|
-
line-height: var(--report-line-height-secondary);
|
|
485
|
+
margin: 5px;
|
|
488
486
|
}
|
|
489
487
|
|
|
490
488
|
.lh-button:first-of-type {
|
|
@@ -685,9 +683,8 @@
|
|
|
685
683
|
|
|
686
684
|
.lh-metrics-container {
|
|
687
685
|
display: grid;
|
|
688
|
-
grid-
|
|
686
|
+
grid-auto-rows: 1fr;
|
|
689
687
|
grid-template-columns: 1fr 1fr;
|
|
690
|
-
grid-auto-flow: column;
|
|
691
688
|
grid-column-gap: var(--report-line-height);
|
|
692
689
|
}
|
|
693
690
|
|
|
@@ -695,7 +692,7 @@
|
|
|
695
692
|
border-top: 1px solid var(--report-border-color-secondary);
|
|
696
693
|
}
|
|
697
694
|
|
|
698
|
-
.lh-metric:nth-child(
|
|
695
|
+
.lh-metric:nth-last-child(-n+2) {
|
|
699
696
|
border-bottom: 1px solid var(--report-border-color-secondary);
|
|
700
697
|
}
|
|
701
698
|
|
|
@@ -743,8 +740,11 @@
|
|
|
743
740
|
display: block;
|
|
744
741
|
}
|
|
745
742
|
|
|
746
|
-
.lh-metric
|
|
747
|
-
border-bottom:
|
|
743
|
+
.lh-metric {
|
|
744
|
+
border-bottom: none !important;
|
|
745
|
+
}
|
|
746
|
+
.lh-metric:nth-last-child(1) {
|
|
747
|
+
border-bottom: 1px solid var(--report-border-color-secondary) !important;
|
|
748
748
|
}
|
|
749
749
|
|
|
750
750
|
/* Change the grid to 3 columns for narrow viewport. */
|
|
@@ -1332,6 +1332,7 @@
|
|
|
1332
1332
|
margin-top: 10px;
|
|
1333
1333
|
text-align: center;
|
|
1334
1334
|
color: var(--report-text-color);
|
|
1335
|
+
word-break: keep-all;
|
|
1335
1336
|
}
|
|
1336
1337
|
|
|
1337
1338
|
/* TODO(#8185) use more BEM (.lh-gauge__label--big) instead of relying on descendant selector */
|