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
@@ -87,10 +87,37 @@ function createAxeRuleResultArtifact(result) {
87
87
  // @ts-expect-error - getNodeDetails put into scope via stringification
88
88
  const nodeDetails = getNodeDetails(/** @type {HTMLElement} */ (element));
89
89
 
90
+ /** @type {Set<HTMLElement>} */
91
+ const relatedNodeElements = new Set();
92
+ /** @param {import('axe-core/axe').ImpactValue} impact */
93
+ const impactToNumber =
94
+ (impact) => [null, 'minor', 'moderate', 'serious', 'critical'].indexOf(impact);
95
+ const checkResults = [...node.any, ...node.all, ...node.none]
96
+ // @ts-expect-error CheckResult.impact is a string, even though ImpactValue is a thing.
97
+ .sort((a, b) => impactToNumber(b.impact) - impactToNumber(a.impact));
98
+ for (const checkResult of checkResults) {
99
+ for (const relatedNode of checkResult.relatedNodes || []) {
100
+ /** @type {HTMLElement} */
101
+ // @ts-expect-error - should always exist, just being cautious.
102
+ const relatedElement = relatedNode.element;
103
+
104
+ // Prevent overloading the report with way too many nodes.
105
+ if (relatedNodeElements.size >= 3) break;
106
+ // Should always exist, just being cautious.
107
+ if (!relatedElement) continue;
108
+ if (element === relatedElement) continue;
109
+
110
+ relatedNodeElements.add(relatedElement);
111
+ }
112
+ }
113
+ // @ts-expect-error - getNodeDetails put into scope via stringification
114
+ const relatedNodeDetails = [...relatedNodeElements].map(getNodeDetails);
115
+
90
116
  return {
91
117
  target,
92
118
  failureSummary,
93
119
  node: nodeDetails,
120
+ relatedNodes: relatedNodeDetails,
94
121
  };
95
122
  });
96
123
 
@@ -10,6 +10,7 @@
10
10
  */
11
11
  'use strict';
12
12
 
13
+ const {Buffer} = require('buffer');
13
14
  const FRGatherer = require('../../../fraggle-rock/gather/base-gatherer.js');
14
15
  const URL = require('../../../lib/url-shim.js');
15
16
  const Sentry = require('../../../lib/sentry.js');
@@ -12,7 +12,6 @@
12
12
  const log = require('lighthouse-logger');
13
13
  const FRGatherer = require('../../fraggle-rock/gather/base-gatherer.js');
14
14
  const pageFunctions = require('../../lib/page-functions.js');
15
- const URL = require('../../lib/url-shim.js');
16
15
  const FontSize = require('./seo/font-size.js');
17
16
 
18
17
  /* global window, getElementsInDocument, Image, getNodeDetails, ShadowRoot */
@@ -310,8 +309,6 @@ class ImageElements extends FRGatherer {
310
309
  let skippedCount = 0;
311
310
 
312
311
  for (const element of elements) {
313
- element.mimeType = URL.guessMimeType(element.src);
314
-
315
312
  if (reachedGatheringBudget) {
316
313
  skippedCount++;
317
314
  continue;
@@ -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;
@@ -16,7 +16,10 @@ const Metrics = require('./traces/pwmetrics-events.js');
16
16
  const NetworkAnalysisComputed = require('../computed/network-analysis.js');
17
17
  const LoadSimulatorComputed = require('../computed/load-simulator.js');
18
18
  const LHError = require('../lib/lh-error.js');
19
- const pipeline = promisify(stream.pipeline);
19
+ // TODO(esmodules): Rollup does not support `promisfy` or `stream.pipeline`. Bundled files
20
+ // don't need anything in this file except for `stringifyReplacer`, so a check for
21
+ // truthiness before using is enough.
22
+ const pipeline = promisify && promisify(stream.pipeline);
20
23
 
21
24
  const artifactsFilename = 'artifacts.json';
22
25
  const traceSuffix = '.trace.json';
@@ -235,9 +238,9 @@ async function saveTrace(traceData, traceFilename) {
235
238
  const traceIter = traceJsonGenerator(traceData);
236
239
  const writeStream = fs.createWriteStream(traceFilename);
237
240
 
238
- // TODO: Can remove Readable.from() in Node 13, promisify(pipeline) in Node 15.
241
+ // TODO: Can remove promisify(pipeline) in Node 15.
239
242
  // https://nodejs.org/api/stream.html#stream_stream_pipeline_streams_callback
240
- return pipeline(stream.Readable.from(traceIter), writeStream);
243
+ return pipeline(traceIter, writeStream);
241
244
  }
242
245
 
243
246
  /**
@@ -250,7 +253,7 @@ function saveDevtoolsLog(devtoolsLog, devtoolLogFilename) {
250
253
  const logIter = arrayOfObjectsJsonGenerator(devtoolsLog);
251
254
  const writeStream = fs.createWriteStream(devtoolLogFilename);
252
255
 
253
- return pipeline(stream.Readable.from(logIter), writeStream);
256
+ return pipeline(logIter, writeStream);
254
257
  }
255
258
 
256
259
  /**
@@ -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
@@ -12,9 +12,11 @@ const lookupClosestLocale = require('lookup-closest-locale');
12
12
  const {getAvailableLocales} = require('../../../shared/localization/format.js');
13
13
  const log = require('lighthouse-logger');
14
14
  const {LH_ROOT} = require('../../../root.js');
15
- const {isIcuMessage, _formatMessage} = require('../../../shared/localization/format.js');
16
-
17
- const DEFAULT_LOCALE = 'en';
15
+ const {
16
+ isIcuMessage,
17
+ formatMessage,
18
+ DEFAULT_LOCALE,
19
+ } = require('../../../shared/localization/format.js');
18
20
 
19
21
  const UIStrings = {
20
22
  /** Used to show the duration in milliseconds that something lasted. The `{timeInMs}` placeholder will be replaced with the time duration, shown in milliseconds (e.g. 63 ms) */
@@ -118,23 +120,32 @@ const UIStrings = {
118
120
  * - supported locales in Intl formatters
119
121
  *
120
122
  * If `locale` isn't provided or one could not be found, DEFAULT_LOCALE is returned.
123
+ *
124
+ * By default any of the locales Lighthouse has strings for can be returned, but this
125
+ * can be overriden with `possibleLocales`, useful e.g. when Lighthouse is bundled and
126
+ * only DEFAULT_LOCALE is available, but `possibleLocales` can be used to select a
127
+ * locale available to be downloaded on demand.
121
128
  * @param {string|string[]=} locales
129
+ * @param {Array<string>=} possibleLocales
122
130
  * @return {LH.Locale}
123
131
  */
124
- function lookupLocale(locales) {
125
- // If Node was built with `--with-intl=none`, `Intl` won't exist.
132
+ function lookupLocale(locales, possibleLocales) {
133
+ // TODO: lookupLocale may need to be split into two functions, one that canonicalizes
134
+ // locales and one that looks up the best locale filename for a given locale.
135
+ // e.g. `en-IE` is canonical, but uses `en-GB.json`. See TODO in locales.js
136
+
126
137
  if (typeof Intl !== 'object') {
138
+ // If Node was built with `--with-intl=none`, `Intl` won't exist.
127
139
  throw new Error('Lighthouse must be run in Node with `Intl` support. See https://nodejs.org/api/intl.html for help');
128
140
  }
129
141
 
130
- // TODO: could do more work to sniff out the user's locale
131
142
  const canonicalLocales = Intl.getCanonicalLocales(locales);
132
143
 
133
144
  // Filter by what's available in this runtime.
134
145
  const availableLocales = Intl.NumberFormat.supportedLocalesOf(canonicalLocales);
135
146
 
136
147
  // Get available locales and transform into object to match `lookupClosestLocale`'s API.
137
- const localesWithMessages = getAvailableLocales();
148
+ const localesWithMessages = possibleLocales || getAvailableLocales();
138
149
  const localesWithmessagesObj = /** @type {Record<LH.Locale, LhlMessages>} */ (
139
150
  Object.fromEntries(localesWithMessages.map(l => [l, {}])));
140
151
 
@@ -182,7 +193,7 @@ function createIcuMessageFn(filename, fileStrings) {
182
193
  return {
183
194
  i18nId,
184
195
  values,
185
- formattedDefault: _formatMessage(message, values, DEFAULT_LOCALE),
196
+ formattedDefault: formatMessage(message, values, DEFAULT_LOCALE),
186
197
  };
187
198
  };
188
199
 
@@ -5,6 +5,8 @@
5
5
  */
6
6
  'use strict';
7
7
 
8
+ const process = require('process');
9
+
8
10
  module.exports = {
9
11
  // NODE_ENV is set to test by jest and by smokehouse CLI runner
10
12
  // CI as a catchall for everything we do in GitHub Actions
@@ -208,7 +208,8 @@ function computeBenchmarkIndex() {
208
208
 
209
209
  while (Date.now() - start < 500) {
210
210
  let s = '';
211
- for (let j = 0; j < 10000; j++) s += 'a'; // eslint-disable-line no-unused-vars
211
+ for (let j = 0; j < 10000; j++) s += 'a';
212
+ if (s.length === 1) throw new Error('will never happen, but prevents compiler optimizations');
212
213
 
213
214
  iterations++;
214
215
  }
@@ -29,10 +29,22 @@ function processForProto(lhr) {
29
29
  // 'ignore unknown fields' in the language of conversion.
30
30
  if (reportJson.configSettings) {
31
31
  // The settings that are in both proto and LHR
32
- const {formFactor, locale, onlyCategories, channel} = reportJson.configSettings;
32
+ const {
33
+ formFactor,
34
+ locale,
35
+ onlyCategories,
36
+ channel,
37
+ throttling,
38
+ throttlingMethod} = reportJson.configSettings;
33
39
 
34
40
  // @ts-expect-error - intentionally only a subset of settings.
35
- reportJson.configSettings = {formFactor, locale, onlyCategories, channel};
41
+ reportJson.configSettings = {
42
+ formFactor,
43
+ locale,
44
+ onlyCategories,
45
+ channel,
46
+ throttling,
47
+ throttlingMethod};
36
48
  }
37
49
 
38
50
  // Remove runtimeError if it is NO_ERROR
@@ -5,6 +5,12 @@
5
5
  */
6
6
  'use strict';
7
7
 
8
+ // The exact double values for the max and min scores possible in each range.
9
+ const MIN_PASSING_SCORE = 0.90000000000000002220446049250313080847263336181640625;
10
+ const MAX_AVERAGE_SCORE = 0.899999999999999911182158029987476766109466552734375;
11
+ const MIN_AVERAGE_SCORE = 0.5;
12
+ const MAX_FAILING_SCORE = 0.499999999999999944488848768742172978818416595458984375;
13
+
8
14
  /**
9
15
  * Approximates the Gauss error function, the probability that a random variable
10
16
  * from the standard normal distribution lies within [-x, x]. Moved from
@@ -28,37 +34,6 @@ function erf(x) {
28
34
  return sign * (1 - y * Math.exp(-x * x));
29
35
  }
30
36
 
31
- /**
32
- * Creates a log-normal distribution à la traceviewer's statistics package.
33
- * Specified by providing the median value, at which the score will be 0.5,
34
- * and the falloff, the initial point of diminishing returns where any
35
- * improvement in value will yield increasingly smaller gains in score. Both
36
- * values should be in the same units (e.g. milliseconds). See
37
- * https://www.desmos.com/calculator/tx1wcjk8ch
38
- * for an interactive view of the relationship between these parameters and
39
- * the typical parameterization (location and shape) of the log-normal
40
- * distribution.
41
- * @param {number} median
42
- * @param {number} falloff
43
- * @return {{computeComplementaryPercentile: function(number): number}}
44
- */
45
- function getLogNormalDistribution(median, falloff) {
46
- const location = Math.log(median);
47
-
48
- // The "falloff" value specified the location of the smaller of the positive
49
- // roots of the third derivative of the log-normal CDF. Calculate the shape
50
- // parameter in terms of that value and the median.
51
- const logRatio = Math.log(falloff / median);
52
- const shape = Math.sqrt(1 - 3 * logRatio - Math.sqrt((logRatio - 3) * (logRatio - 3) - 8)) / 2;
53
-
54
- return {
55
- computeComplementaryPercentile(x) {
56
- const standardizedX = (Math.log(x) - location) / (Math.SQRT2 * shape);
57
- return (1 - erf(standardizedX)) / 2;
58
- },
59
- };
60
- }
61
-
62
37
  /**
63
38
  * Returns the score (1 - percentile) of `value` in a log-normal distribution
64
39
  * specified by the `median` value, at which the score will be 0.5, and a 10th
@@ -76,24 +51,37 @@ function getLogNormalScore({median, p10}, value) {
76
51
  // Required for the log-normal distribution.
77
52
  if (median <= 0) throw new Error('median must be greater than zero');
78
53
  if (p10 <= 0) throw new Error('p10 must be greater than zero');
79
- // Not required, but if p10 > median, it flips around and becomes the p90 point.
54
+ // Not strictly required, but if p10 > median, it flips around and becomes the p90 point.
80
55
  if (p10 >= median) throw new Error('p10 must be less than the median');
81
56
 
82
57
  // Non-positive values aren't in the distribution, so always 1.
83
58
  if (value <= 0) return 1;
84
59
 
85
- // Closest double to `erfc-1(2 * 1/10)`.
60
+ // Closest double to `erfc-1(1/5)`.
86
61
  const INVERSE_ERFC_ONE_FIFTH = 0.9061938024368232;
87
62
 
88
- // Shape (σ) is `log(p10/median) / (sqrt(2)*erfc^-1(2 * 1/10))` and
63
+ // Shape (σ) is `|log(p10/median) / (sqrt(2)*erfc^-1(1/5))|` and
89
64
  // standardizedX is `1/2 erfc(log(value/median) / (sqrt(2)*σ))`, so simplify a bit.
90
- const xLogRatio = Math.log(value / median);
91
- const p10LogRatio = -Math.log(p10 / median); // negate to keep σ positive.
65
+ const xRatio = Math.max(Number.MIN_VALUE, value / median); // value and median are > 0, so is ratio.
66
+ const xLogRatio = Math.log(xRatio);
67
+ const p10Ratio = Math.max(Number.MIN_VALUE, p10 / median); // p10 and median are > 0, so is ratio.
68
+ const p10LogRatio = -Math.log(p10Ratio); // negate to keep σ positive.
92
69
  const standardizedX = xLogRatio * INVERSE_ERFC_ONE_FIFTH / p10LogRatio;
93
70
  const complementaryPercentile = (1 - erf(standardizedX)) / 2;
94
71
 
95
- // Clamp to [0, 1] to avoid any floating-point out-of-bounds issues.
96
- return Math.min(1, Math.max(0, complementaryPercentile));
72
+ // Clamp to avoid floating-point out-of-bounds issues and keep score in expected range.
73
+ let score;
74
+ if (value <= p10) {
75
+ // Passing. Clamp to [0.9, 1].
76
+ score = Math.max(MIN_PASSING_SCORE, Math.min(1, complementaryPercentile));
77
+ } else if (value <= median) {
78
+ // Average. Clamp to [0.5, 0.9).
79
+ score = Math.max(MIN_AVERAGE_SCORE, Math.min(MAX_AVERAGE_SCORE, complementaryPercentile));
80
+ } else {
81
+ // Failing. Clamp to [0, 0.5).
82
+ score = Math.max(0, Math.min(MAX_FAILING_SCORE, complementaryPercentile));
83
+ }
84
+ return score;
97
85
  }
98
86
 
99
87
  /**
@@ -112,6 +100,5 @@ function linearInterpolation(x0, y0, x1, y1, x) {
112
100
 
113
101
  module.exports = {
114
102
  linearInterpolation,
115
- getLogNormalDistribution,
116
103
  getLogNormalScore,
117
104
  };
@@ -5,14 +5,7 @@
5
5
  */
6
6
  'use strict';
7
7
 
8
- const {
9
- filterOutRectsContainedByOthers,
10
- filterOutTinyRects,
11
- rectsTouchOrOverlap,
12
- rectContainsPoint,
13
- getBoundingRect,
14
- getRectCenterPoint,
15
- } = require('./rect-helpers.js');
8
+ const RectHelpers = require('./rect-helpers.js');
16
9
 
17
10
  /**
18
11
  * Merge client rects together and remove small ones. This may result in a larger overall
@@ -23,8 +16,8 @@ const {
23
16
  function getTappableRectsFromClientRects(clientRects) {
24
17
  // 1x1px rect shouldn't be reason to treat the rect as something the user should tap on.
25
18
  // Often they're made invisble in some obscure way anyway, and only exist for e.g. accessibiliity.
26
- clientRects = filterOutTinyRects(clientRects);
27
- clientRects = filterOutRectsContainedByOthers(clientRects);
19
+ clientRects = RectHelpers.filterOutTinyRects(clientRects);
20
+ clientRects = RectHelpers.filterOutRectsContainedByOthers(clientRects);
28
21
  clientRects = mergeTouchingClientRects(clientRects);
29
22
  return clientRects;
30
23
  }
@@ -68,17 +61,17 @@ function mergeTouchingClientRects(clientRects) {
68
61
  const rectsLineUpVertically =
69
62
  almostEqual(crA.left, crB.left) || almostEqual(crA.right, crB.right);
70
63
  const canMerge =
71
- rectsTouchOrOverlap(crA, crB) &&
64
+ RectHelpers.rectsTouchOrOverlap(crA, crB) &&
72
65
  (rectsLineUpHorizontally || rectsLineUpVertically);
73
66
 
74
67
  if (canMerge) {
75
- const replacementClientRect = getBoundingRect([crA, crB]);
76
- const mergedRectCenter = getRectCenterPoint(replacementClientRect);
68
+ const replacementClientRect = RectHelpers.getBoundingRect([crA, crB]);
69
+ const mergedRectCenter = RectHelpers.getRectCenterPoint(replacementClientRect);
77
70
 
78
71
  if (
79
72
  !(
80
- rectContainsPoint(crA, mergedRectCenter) ||
81
- rectContainsPoint(crB, mergedRectCenter)
73
+ RectHelpers.rectContainsPoint(crA, mergedRectCenter) ||
74
+ RectHelpers.rectContainsPoint(crB, mergedRectCenter)
82
75
  )
83
76
  ) {
84
77
  // Don't merge because the new shape is too different from the
@@ -9,7 +9,7 @@
9
9
  * URL shim so we keep our code DRY
10
10
  */
11
11
 
12
- const Util = require('../util-commonjs.js');
12
+ const {Util} = require('../util-commonjs.js');
13
13
 
14
14
  /** @typedef {import('./network-request.js')} NetworkRequest */
15
15
 
@@ -20,6 +20,7 @@ const URL = require('./lib/url-shim.js');
20
20
  const Sentry = require('./lib/sentry.js');
21
21
  const generateReport = require('../report/generator/report-generator.js').generateReport;
22
22
  const LHError = require('./lib/lh-error.js');
23
+ const {version: lighthouseVersion} = require('../package.json');
23
24
 
24
25
  /** @typedef {import('./gather/connections/connection.js')} Connection */
25
26
  /** @typedef {import('./lib/arbitrary-equality-map.js')} ArbitraryEqualityMap */
@@ -112,7 +113,6 @@ class Runner {
112
113
  }
113
114
 
114
115
  // Entering: conclusion of the lighthouse result object
115
- const lighthouseVersion = require('../package.json').version;
116
116
 
117
117
  // Use version from gathering stage.
118
118
  // If accessibility gatherer didn't run or errored, it won't be in credits.