lighthouse 8.6.0 → 9.0.0-dev.20211118

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 (210) hide show
  1. package/changelog.md +121 -0
  2. package/dist/report/bundle.esm.js +6009 -0
  3. package/dist/report/flow.js +98 -25
  4. package/dist/report/standalone.js +41 -35
  5. package/flow-report/.eslintrc.cjs +2 -0
  6. package/flow-report/assets/standalone-flow-template.html +3 -4
  7. package/flow-report/assets/styles.css +108 -31
  8. package/flow-report/src/app.tsx +25 -28
  9. package/flow-report/src/common.tsx +28 -15
  10. package/flow-report/src/header.tsx +12 -13
  11. package/flow-report/src/help-dialog.tsx +3 -4
  12. package/flow-report/src/i18n/i18n.tsx +57 -16
  13. package/flow-report/src/i18n/ui-strings.js +50 -0
  14. package/flow-report/src/icons.tsx +36 -8
  15. package/flow-report/src/sidebar/flow.tsx +3 -3
  16. package/flow-report/src/sidebar/sidebar.tsx +35 -14
  17. package/flow-report/src/summary/category.tsx +97 -24
  18. package/flow-report/src/summary/summary.tsx +21 -14
  19. package/flow-report/src/topbar.tsx +13 -12
  20. package/flow-report/src/util.ts +61 -27
  21. package/flow-report/src/wrappers/category-score.tsx +9 -27
  22. package/flow-report/src/wrappers/markdown.tsx +18 -0
  23. package/flow-report/src/wrappers/report.tsx +26 -37
  24. package/flow-report/standalone-flow.tsx +5 -4
  25. package/flow-report/test/common-test.tsx +32 -17
  26. package/flow-report/test/flow-report-pptr-test.ts +46 -0
  27. package/flow-report/test/header-test.tsx +6 -6
  28. package/flow-report/test/setup/env-setup.ts +11 -5
  29. package/flow-report/test/sidebar/sidebar-test.tsx +43 -1
  30. package/flow-report/test/summary/category-test.tsx +114 -21
  31. package/flow-report/test/summary/summary-test.tsx +4 -7
  32. package/flow-report/test/topbar-test.tsx +15 -14
  33. package/flow-report/test/util-test.tsx +69 -11
  34. package/flow-report/test/wrappers/category-score-test.tsx +84 -0
  35. package/flow-report/test/wrappers/markdown-test.tsx +17 -0
  36. package/flow-report/tsconfig.json +1 -0
  37. package/jest.config.js +4 -2
  38. package/lighthouse-cli/.eslintrc.cjs +2 -0
  39. package/lighthouse-cli/cli-flags.js +1 -1
  40. package/lighthouse-cli/run.js +1 -1
  41. package/lighthouse-cli/test/smokehouse/lighthouse-runners/bundle.js +4 -2
  42. package/lighthouse-cli/test/smokehouse/lighthouse-runners/cli.js +1 -1
  43. package/lighthouse-cli/test/smokehouse/smokehouse.js +1 -1
  44. package/lighthouse-core/audits/accessibility/axe-audit.js +7 -1
  45. package/lighthouse-core/audits/audit.js +1 -1
  46. package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +2 -0
  47. package/lighthouse-core/audits/byte-efficiency/modern-image-formats.js +4 -2
  48. package/lighthouse-core/audits/byte-efficiency/offscreen-images.js +11 -10
  49. package/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js +5 -3
  50. package/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js +2 -1
  51. package/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +2 -1
  52. package/lighthouse-core/audits/deprecations.js +33 -11
  53. package/lighthouse-core/audits/dobetterweb/uses-http2.js +27 -11
  54. package/lighthouse-core/audits/errors-in-console.js +0 -2
  55. package/lighthouse-core/audits/image-aspect-ratio.js +5 -5
  56. package/lighthouse-core/audits/image-size-responsive.js +6 -6
  57. package/lighthouse-core/audits/installable-manifest.js +16 -7
  58. package/lighthouse-core/audits/preload-lcp-image.js +7 -5
  59. package/lighthouse-core/audits/script-treemap-data.js +1 -1
  60. package/lighthouse-core/audits/unsized-images.js +2 -3
  61. package/lighthouse-core/computed/image-records.js +4 -3
  62. package/lighthouse-core/computed/metrics/lantern-total-blocking-time.js +3 -4
  63. package/lighthouse-core/computed/metrics/tbt-utils.js +57 -0
  64. package/lighthouse-core/computed/metrics/total-blocking-time.js +3 -53
  65. package/lighthouse-core/computed/resource-summary.js +1 -1
  66. package/lighthouse-core/config/config-helpers.js +21 -10
  67. package/lighthouse-core/config/constants.js +3 -4
  68. package/lighthouse-core/config/default-config.js +61 -71
  69. package/lighthouse-core/fraggle-rock/config/config.js +1 -1
  70. package/lighthouse-core/fraggle-rock/config/default-config.js +4 -5
  71. package/lighthouse-core/fraggle-rock/gather/base-gatherer.js +6 -1
  72. package/lighthouse-core/fraggle-rock/gather/session.js +3 -4
  73. package/lighthouse-core/gather/connections/cri.js +1 -1
  74. package/lighthouse-core/gather/driver/storage.js +0 -1
  75. package/lighthouse-core/gather/driver.js +3 -7
  76. package/lighthouse-core/gather/fetcher.js +4 -7
  77. package/lighthouse-core/gather/gather-runner.js +29 -6
  78. package/lighthouse-core/gather/gatherers/accessibility.js +27 -0
  79. package/lighthouse-core/gather/gatherers/dobetterweb/response-compression.js +1 -0
  80. package/lighthouse-core/gather/gatherers/image-elements.js +0 -3
  81. package/lighthouse-core/gather/gatherers/inspector-issues.js +5 -0
  82. package/lighthouse-core/lib/asset-saver.js +7 -4
  83. package/lighthouse-core/lib/emulation.js +45 -1
  84. package/lighthouse-core/lib/i18n/i18n.js +19 -8
  85. package/lighthouse-core/lib/lh-env.js +2 -0
  86. package/lighthouse-core/lib/page-functions.js +2 -1
  87. package/lighthouse-core/lib/proto-preprocessor.js +14 -2
  88. package/lighthouse-core/lib/statistics.js +26 -39
  89. package/lighthouse-core/lib/tappable-rects.js +8 -15
  90. package/lighthouse-core/lib/url-shim.js +1 -1
  91. package/lighthouse-core/runner.js +1 -1
  92. package/lighthouse-core/util-commonjs.js +65 -46
  93. package/package.json +27 -17
  94. package/readme.md +3 -3
  95. package/report/.eslintrc.cjs +2 -0
  96. package/report/README.md +1 -1
  97. package/report/assets/standalone-template.html +2 -3
  98. package/report/assets/styles.css +362 -256
  99. package/report/assets/templates.html +21 -76
  100. package/report/clients/bundle.js +1 -0
  101. package/report/clients/standalone.js +6 -15
  102. package/report/generator/README.md +1 -1
  103. package/report/renderer/api.js +66 -0
  104. package/report/renderer/category-renderer.js +76 -22
  105. package/report/renderer/components.js +59 -110
  106. package/report/renderer/crc-details-renderer.js +15 -12
  107. package/report/renderer/dom.js +16 -1
  108. package/report/renderer/drop-down-menu.js +2 -2
  109. package/report/renderer/element-screenshot-renderer.js +8 -8
  110. package/report/renderer/features-util.js +1 -1
  111. package/report/renderer/open-tab.js +9 -3
  112. package/report/renderer/performance-category-renderer.js +55 -34
  113. package/report/renderer/pwa-category-renderer.js +0 -10
  114. package/report/renderer/report-renderer.js +92 -44
  115. package/report/renderer/report-ui-features.js +36 -33
  116. package/report/renderer/swap-locale-feature.js +3 -3
  117. package/report/renderer/topbar-features.js +53 -48
  118. package/report/renderer/util.js +66 -46
  119. package/report/test/clients/bundle-test.js +70 -0
  120. package/report/test/renderer/category-renderer-test.js +76 -3
  121. package/report/test/renderer/i18n-test.js +0 -7
  122. package/report/test/renderer/performance-category-renderer-test.js +51 -14
  123. package/report/test/renderer/pwa-category-renderer-test.js +6 -6
  124. package/report/test/renderer/report-renderer-axe-test.js +11 -13
  125. package/report/test/renderer/report-renderer-test.js +52 -22
  126. package/report/test/renderer/report-ui-features-test.js +20 -10
  127. package/report/test/renderer/text-encoding-test.js +1 -1
  128. package/report/test/renderer/util-test.js +35 -31
  129. package/report/test-assets/faux-psi-template.html +3 -11
  130. package/report/test-assets/faux-psi.js +26 -22
  131. package/report/types/html-renderer.d.ts +2 -0
  132. package/report/types/report-renderer.d.ts +28 -0
  133. package/shared/localization/format.js +64 -30
  134. package/shared/localization/locales/ar-XB.json +72 -75
  135. package/shared/localization/locales/ar.json +72 -75
  136. package/shared/localization/locales/bg.json +72 -75
  137. package/shared/localization/locales/ca.json +72 -75
  138. package/shared/localization/locales/cs.json +89 -92
  139. package/shared/localization/locales/da.json +72 -75
  140. package/shared/localization/locales/de.json +89 -92
  141. package/shared/localization/locales/el.json +72 -75
  142. package/shared/localization/locales/en-GB.json +72 -75
  143. package/shared/localization/locales/en-US.json +78 -75
  144. package/shared/localization/locales/en-XA.json +72 -75
  145. package/shared/localization/locales/en-XL.json +78 -75
  146. package/shared/localization/locales/es-419.json +90 -93
  147. package/shared/localization/locales/es.json +90 -93
  148. package/shared/localization/locales/fi.json +72 -75
  149. package/shared/localization/locales/fil.json +72 -75
  150. package/shared/localization/locales/fr.json +89 -92
  151. package/shared/localization/locales/he.json +73 -76
  152. package/shared/localization/locales/hi.json +72 -75
  153. package/shared/localization/locales/hr.json +72 -75
  154. package/shared/localization/locales/hu.json +89 -92
  155. package/shared/localization/locales/id.json +89 -92
  156. package/shared/localization/locales/it.json +89 -92
  157. package/shared/localization/locales/ja.json +89 -92
  158. package/shared/localization/locales/ko.json +89 -92
  159. package/shared/localization/locales/lt.json +89 -92
  160. package/shared/localization/locales/lv.json +95 -98
  161. package/shared/localization/locales/nl.json +89 -92
  162. package/shared/localization/locales/no.json +72 -75
  163. package/shared/localization/locales/pl.json +72 -75
  164. package/shared/localization/locales/pt-PT.json +72 -75
  165. package/shared/localization/locales/pt.json +89 -92
  166. package/shared/localization/locales/ro.json +90 -93
  167. package/shared/localization/locales/ru.json +89 -92
  168. package/shared/localization/locales/sk.json +89 -92
  169. package/shared/localization/locales/sl.json +89 -92
  170. package/shared/localization/locales/sr-Latn.json +89 -92
  171. package/shared/localization/locales/sr.json +89 -92
  172. package/shared/localization/locales/sv.json +72 -75
  173. package/shared/localization/locales/ta.json +89 -92
  174. package/shared/localization/locales/te.json +89 -92
  175. package/shared/localization/locales/th.json +89 -92
  176. package/shared/localization/locales/tr.json +89 -92
  177. package/shared/localization/locales/uk.json +92 -95
  178. package/shared/localization/locales/vi.json +89 -92
  179. package/shared/localization/locales/zh-HK.json +89 -92
  180. package/shared/localization/locales/zh-TW.json +89 -92
  181. package/shared/localization/locales/zh.json +89 -92
  182. package/shared/localization/locales.js +6 -0
  183. package/shared/test/localization/format-test.js +45 -9
  184. package/shared/test/localization/swap-locale-test.js +0 -12
  185. package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +19 -11
  186. package/third-party/chromium-synchronization/installability-errors-test.js +3 -2
  187. package/third-party/download-content-shell/download-content-shell.js +2 -2
  188. package/third-party/download-content-shell/utils.js +0 -24
  189. package/third-party/snyk/snapshot.json +4 -1
  190. package/tsconfig-all.json +2 -2
  191. package/tsconfig.json +0 -1
  192. package/types/artifacts.d.ts +7 -7
  193. package/types/enquirer.d.ts +3 -1
  194. package/types/lhr/audit-details.d.ts +6 -5
  195. package/types/lhr/flow.d.ts +3 -2
  196. package/types/lhr/lhr.d.ts +1 -1
  197. package/types/node.d.ts +5 -5
  198. package/types/rollup-plugin-postprocess.d.ts +10 -0
  199. package/flow-report/src/wrappers/report-renderer.tsx +0 -46
  200. package/lighthouse-core/audits/dobetterweb/appcache-manifest.js +0 -67
  201. package/lighthouse-core/audits/dobetterweb/external-anchors-use-rel-noopener.js +0 -96
  202. package/lighthouse-core/audits/redirects-http.js +0 -59
  203. package/lighthouse-core/gather/gatherers/dobetterweb/appcache.js +0 -34
  204. package/lighthouse-core/gather/gatherers/http-redirect.js +0 -46
  205. package/lighthouse-core/lib/i18n/locales/en-US.ctc.json +0 -6699
  206. package/lighthouse-core/lib/i18n/locales/en-XL.ctc.json +0 -6672
  207. package/report/clients/psi.js +0 -158
  208. package/report/test/clients/psi-test.js +0 -143
  209. package/shared/localization/locales/en-US.ctc.json +0 -6877
  210. package/shared/localization/locales/en-XL.ctc.json +0 -6852
@@ -10,6 +10,7 @@ const ComputedMetric = require('./metric.js');
10
10
  const TraceProcessor = require('../../lib/tracehouse/trace-processor.js');
11
11
  const LanternTotalBlockingTime = require('./lantern-total-blocking-time.js');
12
12
  const TimetoInteractive = require('./interactive.js');
13
+ const {calculateSumOfBlockingTime} = require('./tbt-utils.js');
13
14
 
14
15
  /**
15
16
  * @fileoverview This audit determines Total Blocking Time.
@@ -25,57 +26,6 @@ const TimetoInteractive = require('./interactive.js');
25
26
  * to smaller improvements to main thread responsiveness.
26
27
  */
27
28
  class TotalBlockingTime extends ComputedMetric {
28
- /**
29
- * @return {number}
30
- */
31
- static get BLOCKING_TIME_THRESHOLD() {
32
- return 50;
33
- }
34
-
35
- /**
36
- * @param {Array<{start: number, end: number, duration: number}>} topLevelEvents
37
- * @param {number} startTimeMs
38
- * @param {number} endTimeMs
39
- * @return {number}
40
- */
41
- static calculateSumOfBlockingTime(topLevelEvents, startTimeMs, endTimeMs) {
42
- if (endTimeMs <= startTimeMs) return 0;
43
-
44
- const threshold = TotalBlockingTime.BLOCKING_TIME_THRESHOLD;
45
- let sumBlockingTime = 0;
46
- for (const event of topLevelEvents) {
47
- // Early exit for small tasks, which should far outnumber long tasks.
48
- if (event.duration < threshold) continue;
49
-
50
- // We only want to consider tasks that fall in our time range (FCP and TTI for navigations).
51
- // FCP is picked as the lower bound because there is little risk of user input happening
52
- // before FCP so Long Queuing Qelay regions do not harm user experience. Developers should be
53
- // optimizing to reach FCP as fast as possible without having to worry about task lengths.
54
- if (event.end < startTimeMs) continue;
55
-
56
- // TTI is picked as the upper bound because we want a well defined end point for page load.
57
- if (event.start > endTimeMs) continue;
58
-
59
- // We first perform the clipping, and then calculate Blocking Region. So if we have a 150ms
60
- // task [0, 150] and FCP happens midway at 50ms, we first clip the task to [50, 150], and then
61
- // calculate the Blocking Region to be [100, 150]. The rational here is that tasks before FCP
62
- // are unimportant, so we care whether the main thread is busy more than 50ms at a time only
63
- // after FCP.
64
- const clippedStart = Math.max(event.start, startTimeMs);
65
- const clippedEnd = Math.min(event.end, endTimeMs);
66
- const clippedDuration = clippedEnd - clippedStart;
67
- if (clippedDuration < threshold) continue;
68
-
69
- // The duration of the task beyond 50ms at the beginning is considered the Blocking Region.
70
- // Example:
71
- // [ 250ms Task ]
72
- // | First 50ms | Blocking Region (200ms) |
73
- sumBlockingTime += clippedDuration - threshold;
74
- }
75
-
76
- return sumBlockingTime;
77
- }
78
-
79
29
  /**
80
30
  * @param {LH.Artifacts.MetricComputationData} data
81
31
  * @param {LH.Artifacts.ComputedContext} context
@@ -100,7 +50,7 @@ class TotalBlockingTime extends ComputedMetric {
100
50
  const interactiveTimeMs = (await TimetoInteractive.request(metricData, context)).timing;
101
51
 
102
52
  return {
103
- timing: TotalBlockingTime.calculateSumOfBlockingTime(
53
+ timing: calculateSumOfBlockingTime(
104
54
  events,
105
55
  firstContentfulPaint,
106
56
  interactiveTimeMs
@@ -108,7 +58,7 @@ class TotalBlockingTime extends ComputedMetric {
108
58
  };
109
59
  } else {
110
60
  return {
111
- timing: TotalBlockingTime.calculateSumOfBlockingTime(
61
+ timing: calculateSumOfBlockingTime(
112
62
  events,
113
63
  0,
114
64
  data.processedTrace.timestamps.traceEnd
@@ -10,7 +10,7 @@ const NetworkRecords = require('./network-records.js');
10
10
  const URL = require('../lib/url-shim.js');
11
11
  const NetworkRequest = require('../lib/network-request.js');
12
12
  const Budget = require('../config/budget.js');
13
- const Util = require('../util-commonjs.js');
13
+ const {Util} = require('../util-commonjs.js');
14
14
 
15
15
  /** @typedef {{count: number, resourceSize: number, transferSize: number}} ResourceEntry */
16
16
 
@@ -203,6 +203,18 @@ function expandAuditShorthand(audit) {
203
203
  }
204
204
  }
205
205
 
206
+ /** @type {Map<string, any>} */
207
+ const bundledModules = new Map(/* BUILD_REPLACE_BUNDLED_MODULES */);
208
+
209
+ /**
210
+ * Wraps `require` with an entrypoint for bundled dynamic modules.
211
+ * See build-bundle.js
212
+ * @param {string} requirePath
213
+ */
214
+ function requireWrapper(requirePath) {
215
+ return bundledModules.get(requirePath) || require(requirePath);
216
+ }
217
+
206
218
  /**
207
219
  * @param {string} gathererPath
208
220
  * @param {Array<string>} coreGathererList
@@ -218,7 +230,7 @@ function requireGatherer(gathererPath, coreGathererList, configDir) {
218
230
  requirePath = resolveModulePath(gathererPath, configDir, 'gatherer');
219
231
  }
220
232
 
221
- const GathererClass = /** @type {GathererConstructor} */ (require(requirePath));
233
+ const GathererClass = /** @type {GathererConstructor} */ (requireWrapper(requirePath));
222
234
 
223
235
  return {
224
236
  instance: new GathererClass(),
@@ -228,14 +240,13 @@ function requireGatherer(gathererPath, coreGathererList, configDir) {
228
240
  }
229
241
 
230
242
  /**
231
- *
232
243
  * @param {string} auditPath
233
244
  * @param {Array<string>} coreAuditList
234
245
  * @param {string=} configDir
235
246
  * @return {LH.Config.AuditDefn['implementation']}
236
247
  */
237
248
  function requireAudit(auditPath, coreAuditList, configDir) {
238
- // See if the audit is a Lighthouse core audit.
249
+ // See if the audit is a Lighthouse core audit.
239
250
  const auditPathJs = `${auditPath}.js`;
240
251
  const coreAudit = coreAuditList.find(a => a === auditPathJs);
241
252
  let requirePath = `../audits/${auditPath}`;
@@ -244,14 +255,14 @@ function requireAudit(auditPath, coreAuditList, configDir) {
244
255
  // This is for pubads bundling.
245
256
  requirePath = auditPath;
246
257
  } else {
247
- // Otherwise, attempt to find it elsewhere. This throws if not found.
258
+ // Otherwise, attempt to find it elsewhere. This throws if not found.
248
259
  const absolutePath = resolveModulePath(auditPath, configDir, 'audit');
249
260
  // Use a relative path so bundler can easily expose it.
250
261
  requirePath = path.relative(__dirname, absolutePath);
251
262
  }
252
263
  }
253
264
 
254
- return require(requirePath);
265
+ return requireWrapper(requirePath);
255
266
  }
256
267
 
257
268
  /**
@@ -283,6 +294,7 @@ function resolveSettings(settingsJson = {}, overrides = undefined) {
283
294
  // If a locale is requested in flags or settings, use it. A typical CLI run will not have one,
284
295
  // however `lookupLocale` will always determine which of our supported locales to use (falling
285
296
  // back if necessary).
297
+ // TODO: could do more work to sniff out the user's locale
286
298
  const locale = i18n.lookupLocale((overrides && overrides.locale) || settingsJson.locale);
287
299
 
288
300
  // Fill in missing settings with defaults
@@ -312,7 +324,6 @@ function resolveSettings(settingsJson = {}, overrides = undefined) {
312
324
  return settingsWithFlags;
313
325
  }
314
326
 
315
-
316
327
  /**
317
328
  * @param {LH.Config.Json} configJSON
318
329
  * @param {string | undefined} configDir
@@ -331,7 +342,7 @@ function mergePlugins(configJSON, configDir, flags) {
331
342
  const pluginPath = isBundledEnvironment() ?
332
343
  pluginName :
333
344
  resolveModulePath(pluginName, configDir, 'plugin');
334
- const rawPluginJson = require(pluginPath);
345
+ const rawPluginJson = requireWrapper(pluginPath);
335
346
  const pluginJson = ConfigPlugin.parsePlugin(rawPluginJson, pluginName);
336
347
 
337
348
  configJSON = mergeConfigFragment(configJSON, pluginJson);
@@ -568,12 +579,12 @@ function deepCloneConfigJson(json) {
568
579
  module.exports = {
569
580
  deepClone,
570
581
  deepCloneConfigJson,
571
- mergeOptionsOfItems,
572
582
  mergeConfigFragment,
573
583
  mergeConfigFragmentArrayByKey,
584
+ mergeOptionsOfItems,
574
585
  mergePlugins,
575
- resolveSettings,
576
- resolveGathererToDefn,
577
586
  resolveAuditsToDefns,
587
+ resolveGathererToDefn,
578
588
  resolveModulePath,
589
+ resolveSettings,
579
590
  };
@@ -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,
@@ -96,7 +96,7 @@ const UIStrings = {
96
96
  /** Description of the navigation section within the Search Engine Optimization (SEO) category. Within this section are audits with descriptive titles that highlight ways to make a website accessible to search engine crawlers. */
97
97
  seoCrawlingGroupDescription: 'To appear in search results, crawlers need access to your app.',
98
98
  /** Title of the Progressive Web Application (PWA) category of audits. This is displayed at the top of a list of audits focused on topics related to whether or not a site is a progressive web app, e.g. responds offline, uses a service worker, is on https, etc. Also used as a label of a score gauge. */
99
- pwaCategoryTitle: 'Progressive Web App',
99
+ pwaCategoryTitle: 'PWA',
100
100
  /** Description of the Progressive Web Application (PWA) category. This is displayed at the top of a list of audits focused on topics related to whether or not a site is a progressive web app, e.g. responds offline, uses a service worker, is on https, etc. No character length limits. 'Learn More' becomes link text to additional documentation. */
101
101
  pwaCategoryDescription: 'These checks validate the aspects of a Progressive Web App. ' +
102
102
  '[Learn more](https://developers.google.com/web/progressive-web-apps/checklist).',
@@ -147,7 +147,6 @@ const defaultConfig = {
147
147
  'form-elements',
148
148
  'main-document-content',
149
149
  'global-listeners',
150
- 'dobetterweb/appcache',
151
150
  'dobetterweb/doctype',
152
151
  'dobetterweb/domstats',
153
152
  'dobetterweb/optimized-images',
@@ -171,19 +170,9 @@ const defaultConfig = {
171
170
  gatherers: [
172
171
  'service-worker',
173
172
  ],
174
- },
175
- {
176
- passName: 'redirectPass',
177
- loadFailureMode: 'warn',
178
- // Speed up the redirect pass by blocking stylesheets, fonts, and images
179
- blockedUrlPatterns: ['*.css', '*.jpg', '*.jpeg', '*.png', '*.gif', '*.svg', '*.ttf', '*.woff', '*.woff2'],
180
- gatherers: [
181
- 'http-redirect',
182
- ],
183
173
  }],
184
174
  audits: [
185
175
  'is-on-https',
186
- 'redirects-http',
187
176
  'service-worker',
188
177
  'viewport',
189
178
  'metrics/first-contentful-paint',
@@ -311,11 +300,9 @@ const defaultConfig = {
311
300
  'byte-efficiency/efficient-animated-content',
312
301
  'byte-efficiency/duplicated-javascript',
313
302
  'byte-efficiency/legacy-javascript',
314
- 'dobetterweb/appcache-manifest',
315
303
  'dobetterweb/doctype',
316
304
  'dobetterweb/charset',
317
305
  'dobetterweb/dom-size',
318
- 'dobetterweb/external-anchors-use-rel-noopener',
319
306
  'dobetterweb/geolocation-on-start',
320
307
  'dobetterweb/inspector-issues',
321
308
  'dobetterweb/no-document-write',
@@ -417,6 +404,8 @@ const defaultConfig = {
417
404
  'best-practices-general': {
418
405
  title: str_(UIStrings.bestPracticesGeneralGroupTitle),
419
406
  },
407
+ // Group for audits that should not be displayed.
408
+ 'hidden': {title: ''},
420
409
  },
421
410
  categories: {
422
411
  'performance': {
@@ -424,67 +413,71 @@ const defaultConfig = {
424
413
  supportedModes: ['navigation', 'timespan', 'snapshot'],
425
414
  auditRefs: [
426
415
  {id: 'first-contentful-paint', weight: 10, group: 'metrics', acronym: 'FCP', relevantAudits: m2a.fcpRelevantAudits},
427
- {id: 'speed-index', weight: 10, group: 'metrics', acronym: 'SI'},
428
- {id: 'largest-contentful-paint', weight: 25, group: 'metrics', acronym: 'LCP', relevantAudits: m2a.lcpRelevantAudits},
429
416
  {id: 'interactive', weight: 10, group: 'metrics', acronym: 'TTI'},
417
+ {id: 'speed-index', weight: 10, group: 'metrics', acronym: 'SI'},
430
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},
431
420
  {id: 'cumulative-layout-shift', weight: 15, group: 'metrics', acronym: 'CLS', relevantAudits: m2a.clsRelevantAudits},
432
421
 
433
- // These are our "invisible" metrics. Not displayed, but still in the LHR
434
- {id: 'max-potential-fid', weight: 0},
435
- {id: 'first-meaningful-paint', weight: 0, acronym: 'FMP'},
422
+ // These are our "invisible" metrics. Not displayed, but still in the LHR.
423
+ {id: 'max-potential-fid', weight: 0, group: 'hidden'},
424
+ {id: 'first-meaningful-paint', weight: 0, acronym: 'FMP', group: 'hidden'},
436
425
 
437
- {id: 'render-blocking-resources', weight: 0, group: 'load-opportunities'},
438
- {id: 'uses-responsive-images', weight: 0, group: 'load-opportunities'},
439
- {id: 'offscreen-images', weight: 0, group: 'load-opportunities'},
440
- {id: 'unminified-css', weight: 0, group: 'load-opportunities'},
441
- {id: 'unminified-javascript', weight: 0, group: 'load-opportunities'},
442
- {id: 'unused-css-rules', weight: 0, group: 'load-opportunities'},
443
- {id: 'unused-javascript', weight: 0, group: 'load-opportunities'},
444
- {id: 'uses-optimized-images', weight: 0, group: 'load-opportunities'},
445
- {id: 'modern-image-formats', weight: 0, group: 'load-opportunities'},
446
- {id: 'uses-text-compression', weight: 0, group: 'load-opportunities'},
447
- {id: 'uses-rel-preconnect', weight: 0, group: 'load-opportunities'},
448
- {id: 'server-response-time', weight: 0, group: 'load-opportunities'},
449
- {id: 'redirects', weight: 0, group: 'load-opportunities'},
450
- {id: 'uses-rel-preload', weight: 0, group: 'load-opportunities'},
451
- {id: 'uses-http2', weight: 0, group: 'load-opportunities'},
452
- {id: 'efficient-animated-content', weight: 0, group: 'load-opportunities'},
453
- {id: 'duplicated-javascript', weight: 0, group: 'load-opportunities'},
454
- {id: 'legacy-javascript', weight: 0, group: 'load-opportunities'},
455
- {id: 'preload-lcp-image', weight: 0, group: 'load-opportunities'},
456
- {id: 'total-byte-weight', weight: 0, group: 'diagnostics'},
457
- {id: 'uses-long-cache-ttl', weight: 0, group: 'diagnostics'},
458
- {id: 'dom-size', weight: 0, group: 'diagnostics'},
459
- {id: 'critical-request-chains', weight: 0, group: 'diagnostics'},
460
- {id: 'user-timings', weight: 0, group: 'diagnostics'},
461
- {id: 'bootup-time', weight: 0, group: 'diagnostics'},
462
- {id: 'mainthread-work-breakdown', weight: 0, group: 'diagnostics'},
463
- {id: 'font-display', weight: 0, group: 'diagnostics'},
426
+ // These audits will be put in "load-opportunities" or "diagnostics" based on their details type.
427
+ {id: 'render-blocking-resources', weight: 0},
428
+ {id: 'uses-responsive-images', weight: 0},
429
+ {id: 'offscreen-images', weight: 0},
430
+ {id: 'unminified-css', weight: 0},
431
+ {id: 'unminified-javascript', weight: 0},
432
+ {id: 'unused-css-rules', weight: 0},
433
+ {id: 'unused-javascript', weight: 0},
434
+ {id: 'uses-optimized-images', weight: 0},
435
+ {id: 'modern-image-formats', weight: 0},
436
+ {id: 'uses-text-compression', weight: 0},
437
+ {id: 'uses-rel-preconnect', weight: 0},
438
+ {id: 'server-response-time', weight: 0},
439
+ {id: 'redirects', weight: 0},
440
+ {id: 'uses-rel-preload', weight: 0},
441
+ {id: 'uses-http2', weight: 0},
442
+ {id: 'efficient-animated-content', weight: 0},
443
+ {id: 'duplicated-javascript', weight: 0},
444
+ {id: 'legacy-javascript', weight: 0},
445
+ {id: 'preload-lcp-image', weight: 0},
446
+ {id: 'total-byte-weight', weight: 0},
447
+ {id: 'uses-long-cache-ttl', weight: 0},
448
+ {id: 'dom-size', weight: 0},
449
+ {id: 'critical-request-chains', weight: 0},
450
+ {id: 'user-timings', weight: 0},
451
+ {id: 'bootup-time', weight: 0},
452
+ {id: 'mainthread-work-breakdown', weight: 0},
453
+ {id: 'font-display', weight: 0},
454
+ {id: 'resource-summary', weight: 0},
455
+ {id: 'third-party-summary', weight: 0},
456
+ {id: 'third-party-facades', weight: 0},
457
+ {id: 'largest-contentful-paint-element', weight: 0},
458
+ {id: 'lcp-lazy-loaded', weight: 0},
459
+ {id: 'layout-shift-elements', weight: 0},
460
+ {id: 'uses-passive-event-listeners', weight: 0},
461
+ {id: 'no-document-write', weight: 0},
462
+ {id: 'long-tasks', weight: 0},
463
+ {id: 'non-composited-animations', weight: 0},
464
+ {id: 'unsized-images', weight: 0},
465
+ {id: 'viewport', weight: 0},
466
+
467
+ // Budget audits.
464
468
  {id: 'performance-budget', weight: 0, group: 'budgets'},
465
469
  {id: 'timing-budget', weight: 0, group: 'budgets'},
466
- {id: 'resource-summary', weight: 0, group: 'diagnostics'},
467
- {id: 'third-party-summary', weight: 0, group: 'diagnostics'},
468
- {id: 'third-party-facades', weight: 0, group: 'diagnostics'},
469
- {id: 'largest-contentful-paint-element', weight: 0, group: 'diagnostics'},
470
- {id: 'lcp-lazy-loaded', weight: 0, group: 'diagnostics'},
471
- {id: 'layout-shift-elements', weight: 0, group: 'diagnostics'},
472
- {id: 'uses-passive-event-listeners', weight: 0, group: 'diagnostics'},
473
- {id: 'no-document-write', weight: 0, group: 'diagnostics'},
474
- {id: 'long-tasks', weight: 0, group: 'diagnostics'},
475
- {id: 'non-composited-animations', weight: 0, group: 'diagnostics'},
476
- {id: 'unsized-images', weight: 0, group: 'diagnostics'},
477
- {id: 'viewport', weight: 0, group: 'diagnostics'},
478
- // Audits past this point don't belong to a group and will not be shown automatically
479
- {id: 'network-requests', weight: 0},
480
- {id: 'network-rtt', weight: 0},
481
- {id: 'network-server-latency', weight: 0},
482
- {id: 'main-thread-tasks', weight: 0},
483
- {id: 'diagnostics', weight: 0},
484
- {id: 'metrics', weight: 0},
485
- {id: 'screenshot-thumbnails', weight: 0},
486
- {id: 'final-screenshot', weight: 0},
487
- {id: 'script-treemap-data', weight: 0},
470
+
471
+ // Audits past this point contain useful data but are not displayed with other audits.
472
+ {id: 'network-requests', weight: 0, group: 'hidden'},
473
+ {id: 'network-rtt', weight: 0, group: 'hidden'},
474
+ {id: 'network-server-latency', weight: 0, group: 'hidden'},
475
+ {id: 'main-thread-tasks', weight: 0, group: 'hidden'},
476
+ {id: 'diagnostics', weight: 0, group: 'hidden'},
477
+ {id: 'metrics', weight: 0, group: 'hidden'},
478
+ {id: 'screenshot-thumbnails', weight: 0, group: 'hidden'},
479
+ {id: 'final-screenshot', weight: 0, group: 'hidden'},
480
+ {id: 'script-treemap-data', weight: 0, group: 'hidden'},
488
481
  ],
489
482
  },
490
483
  'accessibility': {
@@ -560,7 +553,6 @@ const defaultConfig = {
560
553
  auditRefs: [
561
554
  // Trust & Safety
562
555
  {id: 'is-on-https', weight: 1, group: 'best-practices-trust-safety'},
563
- {id: 'external-anchors-use-rel-noopener', weight: 1, group: 'best-practices-trust-safety'},
564
556
  {id: 'geolocation-on-start', weight: 1, group: 'best-practices-trust-safety'},
565
557
  {id: 'notification-on-start', weight: 1, group: 'best-practices-trust-safety'},
566
558
  {id: 'no-vulnerable-libraries', weight: 1, group: 'best-practices-trust-safety'},
@@ -575,7 +567,6 @@ const defaultConfig = {
575
567
  {id: 'charset', weight: 1, group: 'best-practices-browser-compat'},
576
568
  // General Group
577
569
  {id: 'no-unload-listeners', weight: 1, group: 'best-practices-general'},
578
- {id: 'appcache-manifest', weight: 1, group: 'best-practices-general'},
579
570
  {id: 'js-libraries', weight: 0, group: 'best-practices-general'},
580
571
  {id: 'deprecations', weight: 1, group: 'best-practices-general'},
581
572
  {id: 'errors-in-console', weight: 1, group: 'best-practices-general'},
@@ -617,7 +608,6 @@ const defaultConfig = {
617
608
  {id: 'installable-manifest', weight: 2, group: 'pwa-installable'},
618
609
  // PWA Optimized
619
610
  {id: 'service-worker', weight: 1, group: 'pwa-optimized'},
620
- {id: 'redirects-http', weight: 2, group: 'pwa-optimized'},
621
611
  {id: 'splash-screen', weight: 1, group: 'pwa-optimized'},
622
612
  {id: 'themed-omnibox', weight: 1, group: 'pwa-optimized'},
623
613
  {id: 'content-width', weight: 1, group: 'pwa-optimized'},
@@ -140,7 +140,7 @@ function resolveArtifactsToDefns(artifacts, configDir) {
140
140
 
141
141
  const gatherer = resolveGathererToDefn(gathererJson, coreGathererList, configDir);
142
142
  if (!isFRGathererDefn(gatherer)) {
143
- throw new Error(`${gatherer.instance.name} gatherer does not support Fraggle Rock`);
143
+ throw new Error(`${gatherer.instance.name} gatherer does not have a Fraggle Rock meta obj`);
144
144
  }
145
145
 
146
146
  /** @type {LH.Config.AnyArtifactDefn} */
@@ -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 = [
@@ -15,14 +16,15 @@ const frAudits = [
15
16
  /** @type {Record<string, LH.Config.AuditRefJson[]>} */
16
17
  const frCategoryAuditRefExtensions = {
17
18
  'performance': [
18
- {id: 'uses-responsive-images-snapshot', weight: 0, group: 'diagnostics'},
19
+ {id: 'uses-responsive-images-snapshot', weight: 0},
19
20
  ],
20
21
  };
21
22
 
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]);
@@ -37,7 +39,6 @@ const artifacts = {
37
39
  Trace: '',
38
40
  Accessibility: '',
39
41
  AnchorElements: '',
40
- AppCacheManifest: '',
41
42
  CacheContents: '',
42
43
  ConsoleMessages: '',
43
44
  CSSUsage: '',
@@ -88,7 +89,6 @@ const defaultConfig = {
88
89
  /* eslint-disable max-len */
89
90
  {id: artifacts.Accessibility, gatherer: 'accessibility'},
90
91
  {id: artifacts.AnchorElements, gatherer: 'anchor-elements'},
91
- {id: artifacts.AppCacheManifest, gatherer: 'dobetterweb/appcache'},
92
92
  {id: artifacts.CacheContents, gatherer: 'cache-contents'},
93
93
  {id: artifacts.ConsoleMessages, gatherer: 'console-messages'},
94
94
  {id: artifacts.CSSUsage, gatherer: 'css-usage'},
@@ -141,7 +141,6 @@ const defaultConfig = {
141
141
 
142
142
  artifacts.Accessibility,
143
143
  artifacts.AnchorElements,
144
- artifacts.AppCacheManifest,
145
144
  artifacts.CacheContents,
146
145
  artifacts.ConsoleMessages,
147
146
  artifacts.CSSUsage,
@@ -62,8 +62,13 @@ class FRGatherer {
62
62
  * @return {keyof LH.GathererArtifacts}
63
63
  */
64
64
  get name() {
65
+ let name = this.constructor.name;
66
+ // Rollup will mangle class names in an known way–just trim until `$`.
67
+ if (name.includes('$')) {
68
+ name = name.substr(0, name.indexOf('$'));
69
+ }
65
70
  // @ts-expect-error - assume that class name has been added to LH.GathererArtifacts.
66
- return this.constructor.name;
71
+ return name;
67
72
  }
68
73
 
69
74
  /**
@@ -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);
@@ -11,7 +11,7 @@ const http = require('http');
11
11
  const log = require('lighthouse-logger');
12
12
  const LighthouseError = require('../../lib/lh-error.js');
13
13
 
14
- const DEFAULT_HOSTNAME = 'localhost';
14
+ const DEFAULT_HOSTNAME = '127.0.0.1';
15
15
  const CONNECT_TIMEOUT = 10000;
16
16
  const DEFAULT_PORT = 9222;
17
17
 
@@ -43,7 +43,6 @@ async function clearDataForOrigin(session, url) {
43
43
  // indexeddb, websql, and localstorage are not cleared to prevent loss of potentially important data.
44
44
  // https://chromedevtools.github.io/debugger-protocol-viewer/tot/Storage/#type-StorageType
45
45
  const typesToClear = [
46
- 'appcache',
47
46
  // 'cookies',
48
47
  'file_systems',
49
48
  'shader_cache',
@@ -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];