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.
Files changed (84) hide show
  1. package/dist/report/bundle.esm.js +5994 -0
  2. package/dist/report/flow.js +16 -16
  3. package/dist/report/standalone.js +8 -8
  4. package/flow-report/src/app.tsx +10 -9
  5. package/flow-report/src/help-dialog.tsx +0 -1
  6. package/lighthouse-cli/run.js +1 -1
  7. package/lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js +0 -0
  8. package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +2 -0
  9. package/lighthouse-core/audits/deprecations.js +33 -11
  10. package/lighthouse-core/audits/dobetterweb/uses-http2.js +27 -11
  11. package/lighthouse-core/audits/installable-manifest.js +5 -3
  12. package/lighthouse-core/config/constants.js +3 -4
  13. package/lighthouse-core/config/default-config.js +2 -2
  14. package/lighthouse-core/fraggle-rock/config/default-config.js +3 -1
  15. package/lighthouse-core/fraggle-rock/gather/session.js +3 -4
  16. package/lighthouse-core/gather/driver.js +3 -7
  17. package/lighthouse-core/gather/fetcher.js +4 -7
  18. package/lighthouse-core/gather/gather-runner.js +29 -6
  19. package/lighthouse-core/gather/gatherers/inspector-issues.js +5 -0
  20. package/lighthouse-core/lib/emulation.js +45 -1
  21. package/lighthouse-core/util-commonjs.js +17 -0
  22. package/package.json +8 -5
  23. package/report/assets/styles.css +9 -8
  24. package/report/renderer/components.js +1 -1
  25. package/report/renderer/performance-category-renderer.js +0 -1
  26. package/report/renderer/report-renderer.js +1 -1
  27. package/report/renderer/topbar-features.js +2 -1
  28. package/report/renderer/util.js +17 -0
  29. package/report/test/renderer/performance-category-renderer-test.js +15 -4
  30. package/report/test/renderer/util-test.js +22 -0
  31. package/shared/localization/locales/ar-XB.json +73 -25
  32. package/shared/localization/locales/ar.json +73 -25
  33. package/shared/localization/locales/bg.json +73 -25
  34. package/shared/localization/locales/ca.json +73 -25
  35. package/shared/localization/locales/cs.json +90 -42
  36. package/shared/localization/locales/da.json +73 -25
  37. package/shared/localization/locales/de.json +90 -42
  38. package/shared/localization/locales/el.json +73 -25
  39. package/shared/localization/locales/en-GB.json +73 -25
  40. package/shared/localization/locales/en-US.json +3 -0
  41. package/shared/localization/locales/en-XA.json +73 -25
  42. package/shared/localization/locales/en-XL.json +3 -0
  43. package/shared/localization/locales/es-419.json +91 -43
  44. package/shared/localization/locales/es.json +91 -43
  45. package/shared/localization/locales/fi.json +73 -25
  46. package/shared/localization/locales/fil.json +73 -25
  47. package/shared/localization/locales/fr.json +90 -42
  48. package/shared/localization/locales/he.json +74 -26
  49. package/shared/localization/locales/hi.json +73 -25
  50. package/shared/localization/locales/hr.json +73 -25
  51. package/shared/localization/locales/hu.json +90 -42
  52. package/shared/localization/locales/id.json +90 -42
  53. package/shared/localization/locales/it.json +90 -42
  54. package/shared/localization/locales/ja.json +90 -42
  55. package/shared/localization/locales/ko.json +90 -42
  56. package/shared/localization/locales/lt.json +90 -42
  57. package/shared/localization/locales/lv.json +96 -48
  58. package/shared/localization/locales/nl.json +90 -42
  59. package/shared/localization/locales/no.json +73 -25
  60. package/shared/localization/locales/pl.json +73 -25
  61. package/shared/localization/locales/pt-PT.json +73 -25
  62. package/shared/localization/locales/pt.json +90 -42
  63. package/shared/localization/locales/ro.json +91 -43
  64. package/shared/localization/locales/ru.json +90 -42
  65. package/shared/localization/locales/sk.json +90 -42
  66. package/shared/localization/locales/sl.json +90 -42
  67. package/shared/localization/locales/sr-Latn.json +90 -42
  68. package/shared/localization/locales/sr.json +90 -42
  69. package/shared/localization/locales/sv.json +73 -25
  70. package/shared/localization/locales/ta.json +90 -42
  71. package/shared/localization/locales/te.json +90 -42
  72. package/shared/localization/locales/th.json +90 -42
  73. package/shared/localization/locales/tr.json +90 -42
  74. package/shared/localization/locales/uk.json +93 -45
  75. package/shared/localization/locales/vi.json +90 -42
  76. package/shared/localization/locales/zh-HK.json +90 -42
  77. package/shared/localization/locales/zh-TW.json +90 -42
  78. package/shared/localization/locales/zh.json +90 -42
  79. package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +19 -11
  80. package/types/artifacts.d.ts +1 -0
  81. package/types/lhr/audit-details.d.ts +1 -0
  82. package/types/lhr/lhr.d.ts +1 -1
  83. package/changelog.md +0 -5490
  84. package/lighthouse-core/scripts/package.json +0 -4
@@ -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
- if (hashState && hashState.anchor) {
25
- const el = document.getElementById(hashState.anchor);
26
- if (el) {
27
- el.scrollIntoView({behavior: 'smooth'});
28
- return;
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 (
@@ -97,7 +97,6 @@ export const HelpDialog: FunctionComponent<{onClose: () => void}> = ({
97
97
  availableCategories={[
98
98
  strings.categoryPerformance,
99
99
  strings.categoryBestPractices,
100
- strings.categorySeo,
101
100
  ]}
102
101
  />
103
102
  <HelpDialogColumn
@@ -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(() => reject(new Error('Timed out waiting to kill Chrome')), 5000);
178
+ timeout = setTimeout(reject, 5000, new Error('Timed out waiting to kill Chrome'));
179
179
  });
180
180
 
181
181
  return Promise.race([
@@ -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
- const deprecations = entries.filter(log => log.source === 'deprecation')
60
- // TODO(M91): Temporary ignore until Chrome M91 became stable version.
61
- // M91 doesn't throw deprecation on ::-webkit-details-marker.
62
- .filter(log => !log.text.includes('::-webkit-details-marker'))
63
- .map(log => {
64
- return {
65
- value: log.text,
66
- source: Audit.makeSourceLocationFromConsoleMessage(log),
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
- * @description Error message explaining that the manifest does not contain a suitable icon.
54
- * @example {192} value0
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
- // eslint-disable-next-line max-len
85
- const MOTOG4_USERAGENT = 'Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4590.2 Mobile Safari/537.36 Chrome-Lighthouse';
86
- // eslint-disable-next-line max-len
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
- const categories = legacyDefaultConfig.categories;
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
- const err = new LHError(LHError.errors.PROTOCOL_TIMEOUT, {protocolMethod: method});
155
- reject(err);
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
- const err = new LHError(
343
- LHError.errors.PROTOCOL_TIMEOUT,
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 timeoutHandle;
284
+ let asyncTimeout;
287
285
  /** @type {Promise<never>} */
288
286
  const timeoutPromise = new Promise((_, reject) => {
289
- const errorMessage = 'Timed out fetching resource.';
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(timeoutHandle));
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
- baseArtifacts.WebAppManifest = await WebAppManifest.getWebAppManifest(
432
- passContext.driver.defaultSession, passContext.url);
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
- if (baseArtifacts.WebAppManifest) {
435
- baseArtifacts.InstallabilityErrors = await InstallabilityErrors.getInstallabilityErrors(
436
- passContext.driver.defaultSession);
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
- baseArtifacts.Stacks = await Stacks.collectStacks(passContext.driver.executionContext);
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: /** @type {string} */ (settings.emulatedUserAgent),
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": "8.6.0-dev.20211107",
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.901419",
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.1",
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.14.2",
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",
@@ -482,9 +482,7 @@
482
482
  border-radius: 3px;
483
483
  color: var(--link-color);
484
484
  background-color: var(--report-background-color);
485
- padding: var(--default-padding);
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-template-rows: 1fr 1fr 1fr;
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(3n+3) {
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:nth-last-child(-n+1) {
747
- border-bottom: 1px solid var(--report-border-color-secondary);
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 */