lighthouse 9.5.0-dev.20230122 → 9.5.0-dev.20230124

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 (131) hide show
  1. package/.codecov.yml +0 -5
  2. package/core/audits/audit.js +1 -1
  3. package/core/audits/bf-cache.js +1 -1
  4. package/core/audits/byte-efficiency/legacy-javascript.d.ts +1 -0
  5. package/core/audits/byte-efficiency/legacy-javascript.js +7 -3
  6. package/core/audits/dobetterweb/uses-http2.d.ts +5 -2
  7. package/core/audits/dobetterweb/uses-http2.js +12 -7
  8. package/core/audits/screenshot-thumbnails.d.ts +2 -1
  9. package/core/audits/screenshot-thumbnails.js +14 -9
  10. package/core/audits/third-party-facades.d.ts +3 -2
  11. package/core/audits/third-party-facades.js +12 -9
  12. package/core/audits/third-party-summary.d.ts +7 -6
  13. package/core/audits/third-party-summary.js +16 -14
  14. package/core/audits/valid-source-maps.d.ts +5 -3
  15. package/core/audits/valid-source-maps.js +11 -9
  16. package/core/computed/entity-classification.d.ts +29 -0
  17. package/core/computed/entity-classification.js +102 -0
  18. package/core/computed/processed-navigation.d.ts +12 -6
  19. package/core/computed/processed-navigation.js +19 -6
  20. package/core/computed/resource-summary.d.ts +4 -2
  21. package/core/computed/resource-summary.js +8 -5
  22. package/core/lib/url-utils.js +1 -1
  23. package/core/runner.d.ts +10 -0
  24. package/core/runner.js +48 -0
  25. package/core/util.cjs +43 -10
  26. package/core/util.d.cts +114 -2
  27. package/dist/report/bundle.esm.js +858 -883
  28. package/dist/report/flow.js +33 -23
  29. package/dist/report/standalone.js +27 -17
  30. package/flow-report/assets/standalone-flow-template.html +1 -1
  31. package/flow-report/src/common.tsx +1 -1
  32. package/flow-report/src/i18n/i18n.d.ts +103 -100
  33. package/flow-report/src/i18n/i18n.tsx +23 -18
  34. package/flow-report/src/sidebar/sidebar.tsx +3 -3
  35. package/flow-report/src/summary/category.tsx +7 -7
  36. package/flow-report/src/summary/summary.tsx +2 -2
  37. package/flow-report/src/topbar.tsx +4 -28
  38. package/package.json +5 -5
  39. package/readme.md +1 -1
  40. package/report/assets/standalone-template.html +1 -1
  41. package/report/assets/styles.css +3 -3
  42. package/report/assets/templates.html +4 -28
  43. package/report/renderer/category-renderer.js +19 -17
  44. package/report/renderer/components.js +119 -202
  45. package/report/renderer/crc-details-renderer.js +7 -6
  46. package/report/renderer/details-renderer.js +7 -6
  47. package/report/renderer/dom.js +1 -1
  48. package/report/renderer/element-screenshot-renderer.js +3 -3
  49. package/report/renderer/{i18n.d.ts → i18n-formatter.d.ts} +3 -9
  50. package/report/renderer/{i18n.js → i18n-formatter.js} +2 -11
  51. package/report/renderer/performance-category-renderer.js +17 -15
  52. package/report/renderer/pwa-category-renderer.js +5 -4
  53. package/report/renderer/report-globals.d.ts +21 -0
  54. package/report/renderer/report-globals.js +49 -0
  55. package/report/renderer/report-renderer.js +22 -24
  56. package/report/renderer/report-ui-features.js +9 -8
  57. package/report/renderer/report-utils.d.ts +116 -0
  58. package/report/renderer/{util.js → report-utils.js} +100 -415
  59. package/report/renderer/snippet-renderer.js +3 -2
  60. package/report/test/renderer/category-renderer-test.js +10 -5
  61. package/report/test/renderer/crc-details-renderer-test.js +8 -4
  62. package/report/test/renderer/details-renderer-test.js +8 -4
  63. package/report/test/renderer/dom-test.js +8 -4
  64. package/report/test/renderer/element-screenshot-renderer-test.js +9 -5
  65. package/report/test/renderer/{i18n-test.js → i18n-formatter-test.js} +15 -21
  66. package/report/test/renderer/performance-category-renderer-test.js +15 -10
  67. package/report/test/renderer/pwa-category-renderer-test.js +11 -6
  68. package/report/test/renderer/report-renderer-test.js +2 -2
  69. package/report/test/renderer/report-ui-features-test.js +3 -3
  70. package/report/test/renderer/{util-test.js → report-utils-test.js} +37 -235
  71. package/report/test/renderer/snippet-renderer-test.js +8 -4
  72. package/report/test-assets/lhr-3.0.0.json +2332 -0
  73. package/report/test-assets/lhr-4.3.0.json +4754 -0
  74. package/report/test-assets/lhr-5.0.0.json +5994 -0
  75. package/report/test-assets/lhr-6.0.0.json +7222 -0
  76. package/report/test-assets/lhr-8.5.0.json +8679 -0
  77. package/shared/localization/format.js +5 -2
  78. package/shared/localization/locales/ar-XB.json +50 -50
  79. package/shared/localization/locales/ar.json +50 -50
  80. package/shared/localization/locales/bg.json +50 -50
  81. package/shared/localization/locales/ca.json +50 -50
  82. package/shared/localization/locales/cs.json +50 -50
  83. package/shared/localization/locales/da.json +50 -50
  84. package/shared/localization/locales/de.json +50 -50
  85. package/shared/localization/locales/el.json +50 -50
  86. package/shared/localization/locales/en-GB.json +50 -50
  87. package/shared/localization/locales/en-US.json +55 -55
  88. package/shared/localization/locales/en-XA.json +50 -50
  89. package/shared/localization/locales/en-XL.json +55 -55
  90. package/shared/localization/locales/es-419.json +50 -50
  91. package/shared/localization/locales/es.json +50 -50
  92. package/shared/localization/locales/fi.json +50 -50
  93. package/shared/localization/locales/fil.json +50 -50
  94. package/shared/localization/locales/fr.json +50 -50
  95. package/shared/localization/locales/he.json +50 -50
  96. package/shared/localization/locales/hi.json +50 -50
  97. package/shared/localization/locales/hr.json +50 -50
  98. package/shared/localization/locales/hu.json +50 -50
  99. package/shared/localization/locales/id.json +50 -50
  100. package/shared/localization/locales/it.json +50 -50
  101. package/shared/localization/locales/ja.json +50 -50
  102. package/shared/localization/locales/ko.json +50 -50
  103. package/shared/localization/locales/lt.json +50 -50
  104. package/shared/localization/locales/lv.json +50 -50
  105. package/shared/localization/locales/nl.json +50 -50
  106. package/shared/localization/locales/no.json +50 -50
  107. package/shared/localization/locales/pl.json +50 -50
  108. package/shared/localization/locales/pt-PT.json +50 -50
  109. package/shared/localization/locales/pt.json +50 -50
  110. package/shared/localization/locales/ro.json +50 -50
  111. package/shared/localization/locales/ru.json +50 -50
  112. package/shared/localization/locales/sk.json +50 -50
  113. package/shared/localization/locales/sl.json +50 -50
  114. package/shared/localization/locales/sr-Latn.json +50 -50
  115. package/shared/localization/locales/sr.json +50 -50
  116. package/shared/localization/locales/sv.json +50 -50
  117. package/shared/localization/locales/ta.json +50 -50
  118. package/shared/localization/locales/te.json +50 -50
  119. package/shared/localization/locales/th.json +50 -50
  120. package/shared/localization/locales/tr.json +50 -50
  121. package/shared/localization/locales/uk.json +50 -50
  122. package/shared/localization/locales/vi.json +50 -50
  123. package/shared/localization/locales/zh-HK.json +50 -50
  124. package/shared/localization/locales/zh-TW.json +50 -50
  125. package/shared/localization/locales/zh.json +50 -50
  126. package/shared/test/util-test.js +214 -0
  127. package/shared/util.d.ts +122 -0
  128. package/shared/util.js +332 -0
  129. package/types/artifacts.d.ts +14 -0
  130. package/types/lhr/lhr.d.ts +33 -0
  131. package/report/renderer/util.d.ts +0 -230
@@ -20,7 +20,7 @@
20
20
  * critical request chains network tree.
21
21
  */
22
22
 
23
- import {Util} from './util.js';
23
+ import {Globals} from './report-globals.js';
24
24
 
25
25
  /** @typedef {import('./dom.js').DOM} DOM */
26
26
  /** @typedef {import('./details-renderer.js').DetailsRenderer} DetailsRenderer */
@@ -136,9 +136,10 @@ class CriticalRequestChainRenderer {
136
136
  if (!segment.hasChildren) {
137
137
  const {startTime, endTime, transferSize} = segment.node.request;
138
138
  const span = dom.createElement('span', 'lh-crc-node__chain-duration');
139
- span.textContent = ' - ' + Util.i18n.formatMilliseconds((endTime - startTime) * 1000) + ', ';
139
+ span.textContent =
140
+ ' - ' + Globals.i18n.formatMilliseconds((endTime - startTime) * 1000) + ', ';
140
141
  const span2 = dom.createElement('span', 'lh-crc-node__chain-duration');
141
- span2.textContent = Util.i18n.formatBytesToKiB(transferSize, 0.01);
142
+ span2.textContent = Globals.i18n.formatBytesToKiB(transferSize, 0.01);
142
143
 
143
144
  treevalEl.append(span, span2);
144
145
  }
@@ -177,11 +178,11 @@ class CriticalRequestChainRenderer {
177
178
  const containerEl = dom.find('.lh-crc', tmpl);
178
179
 
179
180
  // Fill in top summary.
180
- dom.find('.lh-crc-initial-nav', tmpl).textContent = Util.i18n.strings.crcInitialNavigation;
181
+ dom.find('.lh-crc-initial-nav', tmpl).textContent = Globals.strings.crcInitialNavigation;
181
182
  dom.find('.lh-crc__longest_duration_label', tmpl).textContent =
182
- Util.i18n.strings.crcLongestDurationLabel;
183
+ Globals.strings.crcLongestDurationLabel;
183
184
  dom.find('.lh-crc__longest_duration', tmpl).textContent =
184
- Util.i18n.formatMilliseconds(details.longestChain.duration);
185
+ Globals.i18n.formatMilliseconds(details.longestChain.duration);
185
186
 
186
187
  // Construct visual tree.
187
188
  const root = CRCRenderer.initTree(details.chains);
@@ -25,9 +25,10 @@
25
25
  /** @typedef {LH.FormattedIcu<LH.Audit.Details.ItemValue>} TableItemValue */
26
26
  /** @typedef {LH.FormattedIcu<LH.Audit.Details.TableColumnHeading>} TableColumnHeading */
27
27
 
28
- import {Util} from './util.js';
28
+ import {Util} from '../../shared/util.js';
29
29
  import {CriticalRequestChainRenderer} from './crc-details-renderer.js';
30
30
  import {ElementScreenshotRenderer} from './element-screenshot-renderer.js';
31
+ import {Globals} from './report-globals.js';
31
32
 
32
33
  const URL_PREFIXES = ['http://', 'https://', 'data:'];
33
34
 
@@ -77,9 +78,9 @@ export class DetailsRenderer {
77
78
  */
78
79
  _renderBytes(details) {
79
80
  // TODO: handle displayUnit once we have something other than 'KiB'
80
- const value = Util.i18n.formatBytesToKiB(details.value, details.granularity || 0.1);
81
+ const value = Globals.i18n.formatBytesToKiB(details.value, details.granularity || 0.1);
81
82
  const textEl = this._renderText(value);
82
- textEl.title = Util.i18n.formatBytes(details.value);
83
+ textEl.title = Globals.i18n.formatBytes(details.value);
83
84
  return textEl;
84
85
  }
85
86
 
@@ -90,9 +91,9 @@ export class DetailsRenderer {
90
91
  _renderMilliseconds(details) {
91
92
  let value;
92
93
  if (details.displayUnit === 'duration') {
93
- value = Util.i18n.formatDuration(details.value);
94
+ value = Globals.i18n.formatDuration(details.value);
94
95
  } else {
95
- value = Util.i18n.formatMilliseconds(details.value, details.granularity || 10);
96
+ value = Globals.i18n.formatMilliseconds(details.value, details.granularity || 10);
96
97
  }
97
98
 
98
99
  return this._renderText(value);
@@ -171,7 +172,7 @@ export class DetailsRenderer {
171
172
  * @return {Element}
172
173
  */
173
174
  _renderNumeric(details) {
174
- const value = Util.i18n.formatNumber(details.value, details.granularity || 0.1);
175
+ const value = Globals.i18n.formatNumber(details.value, details.granularity || 0.1);
175
176
  const element = this._dom.createElement('div', 'lh-numeric');
176
177
  element.textContent = value;
177
178
  return element;
@@ -20,7 +20,7 @@
20
20
  /** @typedef {HTMLElementTagNameMap & {[id: string]: HTMLElement}} HTMLElementByTagName */
21
21
  /** @template {string} T @typedef {import('typed-query-selector/parser').ParseSelector<T, Element>} ParseSelector */
22
22
 
23
- import {Util} from './util.js';
23
+ import {Util} from '../../shared/util.js';
24
24
  import {createComponent} from './components.js';
25
25
 
26
26
  export class DOM {
@@ -10,6 +10,8 @@
10
10
  * 2. Display coords (DC suffix): that match the CSS pixel coordinate space of the LH report's page.
11
11
  */
12
12
 
13
+ import {Globals} from './report-globals.js';
14
+
13
15
  /** @typedef {import('./dom.js').DOM} DOM */
14
16
  /** @typedef {LH.Audit.Details.Rect} Rect */
15
17
  /** @typedef {{width: number, height: number}} Size */
@@ -22,8 +24,6 @@
22
24
  * @property {LH.Result.FullPageScreenshot} fullPageScreenshot
23
25
  */
24
26
 
25
- import {Util} from './util.js';
26
-
27
27
  /**
28
28
  * @param {LH.Result.FullPageScreenshot['screenshot']} screenshot
29
29
  * @param {LH.Audit.Details.Rect} rect
@@ -102,7 +102,7 @@ export class ElementScreenshotRenderer {
102
102
  */
103
103
  static renderClipPathInScreenshot(dom, maskEl, positionClip, elementRect, elementPreviewSize) {
104
104
  const clipPathEl = dom.find('clipPath', maskEl);
105
- const clipId = `clip-${Util.getUniqueSuffix()}`;
105
+ const clipId = `clip-${Globals.getUniqueSuffix()}`;
106
106
  clipPathEl.id = clipId;
107
107
  maskEl.style.clipPath = `url(#${clipId})`;
108
108
 
@@ -1,16 +1,10 @@
1
- /**
2
- * @template T
3
- */
4
- export class I18n<T> {
1
+ export class I18nFormatter {
5
2
  /**
6
3
  * @param {LH.Locale} locale
7
- * @param {T} strings
8
4
  */
9
- constructor(locale: LH.Locale, strings: T);
5
+ constructor(locale: LH.Locale);
10
6
  _locale: "en-US" | "en" | "en-AU" | "en-GB" | "en-IE" | "en-SG" | "en-ZA" | "en-IN" | "ar-XB" | "ar" | "bg" | "ca" | "cs" | "da" | "de" | "el" | "en-XL" | "es" | "es-419" | "es-AR" | "es-BO" | "es-BR" | "es-BZ" | "es-CL" | "es-CO" | "es-CR" | "es-CU" | "es-DO" | "es-EC" | "es-GT" | "es-HN" | "es-MX" | "es-NI" | "es-PA" | "es-PE" | "es-PR" | "es-PY" | "es-SV" | "es-US" | "es-UY" | "es-VE" | "fi" | "fil" | "fr" | "he" | "hi" | "hr" | "hu" | "gsw" | "id" | "in" | "it" | "iw" | "ja" | "ko" | "lt" | "lv" | "mo" | "nl" | "nb" | "no" | "pl" | "pt" | "pt-PT" | "ro" | "ru" | "sk" | "sl" | "sr" | "sr-Latn" | "sv" | "ta" | "te" | "th" | "tl" | "tr" | "uk" | "vi" | "zh" | "zh-HK" | "zh-TW";
11
- _strings: T;
12
7
  _cachedNumberFormatters: Map<any, any>;
13
- get strings(): T;
14
8
  /**
15
9
  * @param {number} number
16
10
  * @param {number|undefined} granularity
@@ -103,4 +97,4 @@ export class I18n<T> {
103
97
  */
104
98
  formatDuration(timeInMilliseconds: number): string;
105
99
  }
106
- //# sourceMappingURL=i18n.d.ts.map
100
+ //# sourceMappingURL=i18n-formatter.d.ts.map
@@ -9,27 +9,18 @@ const NBSP2 = '\xa0';
9
9
  const KiB = 1024;
10
10
  const MiB = KiB * KiB;
11
11
 
12
- /**
13
- * @template T
14
- */
15
- export class I18n {
12
+ export class I18nFormatter {
16
13
  /**
17
14
  * @param {LH.Locale} locale
18
- * @param {T} strings
19
15
  */
20
- constructor(locale, strings) {
16
+ constructor(locale) {
21
17
  // When testing, use a locale with more exciting numeric formatting.
22
18
  if (locale === 'en-XA') locale = 'de';
23
19
 
24
20
  this._locale = locale;
25
- this._strings = strings;
26
21
  this._cachedNumberFormatters = new Map();
27
22
  }
28
23
 
29
- get strings() {
30
- return this._strings;
31
- }
32
-
33
24
  /**
34
25
  * @param {number} number
35
26
  * @param {number|undefined} granularity
@@ -17,8 +17,9 @@
17
17
 
18
18
  /** @typedef {import('./dom.js').DOM} DOM */
19
19
 
20
- import {Util} from './util.js';
21
20
  import {CategoryRenderer} from './category-renderer.js';
21
+ import {ReportUtils} from './report-utils.js';
22
+ import {Globals} from './report-globals.js';
22
23
 
23
24
  export class PerformanceCategoryRenderer extends CategoryRenderer {
24
25
  /**
@@ -29,7 +30,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
29
30
  const tmpl = this.dom.createComponent('metric');
30
31
  const element = this.dom.find('.lh-metric', tmpl);
31
32
  element.id = audit.result.id;
32
- const rating = Util.calculateRating(audit.result.score, audit.result.scoreDisplayMode);
33
+ const rating = ReportUtils.calculateRating(audit.result.score, audit.result.scoreDisplayMode);
33
34
  element.classList.add(`lh-metric--${rating}`);
34
35
 
35
36
  const titleEl = this.dom.find('.lh-metric__title', tmpl);
@@ -77,7 +78,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
77
78
  this.dom.find('span.lh-audit__display-text, div.lh-audit__display-text', element);
78
79
  const sparklineWidthPct = `${details.overallSavingsMs / scale * 100}%`;
79
80
  this.dom.find('div.lh-sparkline__bar', element).style.width = sparklineWidthPct;
80
- displayEl.textContent = Util.i18n.formatSeconds(details.overallSavingsMs, 0.01);
81
+ displayEl.textContent = Globals.i18n.formatSeconds(details.overallSavingsMs, 0.01);
81
82
 
82
83
  // Set [title] tooltips
83
84
  if (audit.result.displayValue) {
@@ -144,9 +145,9 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
144
145
  });
145
146
  const paramPairs = [...metricPairs];
146
147
 
147
- if (Util.reportJson) {
148
- paramPairs.push(['device', Util.reportJson.configSettings.formFactor]);
149
- paramPairs.push(['version', Util.reportJson.lighthouseVersion]);
148
+ if (Globals.reportJson) {
149
+ paramPairs.push(['device', Globals.reportJson.configSettings.formFactor]);
150
+ paramPairs.push(['version', Globals.reportJson.lighthouseVersion]);
150
151
  }
151
152
 
152
153
  const params = new URLSearchParams(paramPairs);
@@ -178,7 +179,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
178
179
  * @override
179
180
  */
180
181
  render(category, groups, options) {
181
- const strings = Util.i18n.strings;
182
+ const strings = Globals.strings;
182
183
  const element = this.dom.createElement('div', 'lh-category');
183
184
  element.id = category.id;
184
185
  element.append(this.renderCategoryHeader(category, groups, options));
@@ -190,7 +191,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
190
191
 
191
192
  // Metric descriptions toggle.
192
193
  const checkboxEl = this.dom.createElement('input', 'lh-metrics-toggle__input');
193
- const checkboxId = `lh-metrics-toggle${Util.getUniqueSuffix()}`;
194
+ const checkboxId = `lh-metrics-toggle${Globals.getUniqueSuffix()}`;
194
195
  checkboxEl.setAttribute('aria-label', 'Toggle the display of metric descriptions');
195
196
  checkboxEl.type = 'checkbox';
196
197
  checkboxEl.id = checkboxId;
@@ -200,8 +201,8 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
200
201
  labelEl.htmlFor = checkboxId;
201
202
  const showEl = this.dom.createChildOf(labelEl, 'span', 'lh-metrics-toggle__labeltext--show');
202
203
  const hideEl = this.dom.createChildOf(labelEl, 'span', 'lh-metrics-toggle__labeltext--hide');
203
- showEl.textContent = Util.i18n.strings.expandView;
204
- hideEl.textContent = Util.i18n.strings.collapseView;
204
+ showEl.textContent = Globals.strings.expandView;
205
+ hideEl.textContent = Globals.strings.collapseView;
205
206
 
206
207
  const metricsBoxesEl = this.dom.createElement('div', 'lh-metrics-container');
207
208
  metricsGroupEl.insertBefore(metricsBoxesEl, metricsFooterEl);
@@ -240,7 +241,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
240
241
  // Opportunities
241
242
  const opportunityAudits = category.auditRefs
242
243
  .filter(audit => this._classifyPerformanceAudit(audit) === 'load-opportunity')
243
- .filter(audit => !Util.showAsPassed(audit.result))
244
+ .filter(audit => !ReportUtils.showAsPassed(audit.result))
244
245
  .sort((auditA, auditB) => this._getWastedMs(auditB) - this._getWastedMs(auditA));
245
246
 
246
247
  const filterableMetrics = metricAudits.filter(a => !!a.relevantAudits);
@@ -274,7 +275,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
274
275
  // Diagnostics
275
276
  const diagnosticAudits = category.auditRefs
276
277
  .filter(audit => this._classifyPerformanceAudit(audit) === 'diagnostic')
277
- .filter(audit => !Util.showAsPassed(audit.result))
278
+ .filter(audit => !ReportUtils.showAsPassed(audit.result))
278
279
  .sort((a, b) => {
279
280
  const scoreA = a.result.scoreDisplayMode === 'informative' ? 100 : Number(a.result.score);
280
281
  const scoreB = b.result.scoreDisplayMode === 'informative' ? 100 : Number(b.result.score);
@@ -290,7 +291,8 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
290
291
 
291
292
  // Passed audits
292
293
  const passedAudits = category.auditRefs
293
- .filter(audit => this._classifyPerformanceAudit(audit) && Util.showAsPassed(audit.result));
294
+ .filter(audit =>
295
+ this._classifyPerformanceAudit(audit) && ReportUtils.showAsPassed(audit.result));
294
296
 
295
297
  if (!passedAudits.length) return element;
296
298
 
@@ -333,7 +335,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
333
335
  renderMetricAuditFilter(filterableMetrics, categoryEl) {
334
336
  const metricFilterEl = this.dom.createElement('div', 'lh-metricfilter');
335
337
  const textEl = this.dom.createChildOf(metricFilterEl, 'span', 'lh-metricfilter__text');
336
- textEl.textContent = Util.i18n.strings.showRelevantAudits;
338
+ textEl.textContent = Globals.strings.showRelevantAudits;
337
339
 
338
340
  const filterChoices = /** @type {LH.ReportResult.AuditRef[]} */ ([
339
341
  ({acronym: 'All'}),
@@ -342,7 +344,7 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
342
344
 
343
345
  // Form labels need to reference unique IDs, but multiple reports rendered in the same DOM (eg PSI)
344
346
  // would mean ID conflict. To address this, we 'scope' these radio inputs with a unique suffix.
345
- const uniqSuffix = Util.getUniqueSuffix();
347
+ const uniqSuffix = Globals.getUniqueSuffix();
346
348
  for (const metric of filterChoices) {
347
349
  const elemId = `metric-${metric.acronym}-${uniqSuffix}`;
348
350
  const radioEl = this.dom.createChildOf(metricFilterEl, 'input', 'lh-metricfilter__radio');
@@ -15,8 +15,9 @@
15
15
  * limitations under the License.
16
16
  */
17
17
 
18
- import {Util} from './util.js';
19
18
  import {CategoryRenderer} from './category-renderer.js';
19
+ import {ReportUtils} from './report-utils.js';
20
+ import {Globals} from './report-globals.js';
20
21
 
21
22
  export class PwaCategoryRenderer extends CategoryRenderer {
22
23
  /**
@@ -101,7 +102,7 @@ export class PwaCategoryRenderer extends CategoryRenderer {
101
102
 
102
103
  // Remove any that have a failing audit.
103
104
  for (const auditRef of auditRefs) {
104
- if (!Util.showAsPassed(auditRef.result) && auditRef.group) {
105
+ if (!ReportUtils.showAsPassed(auditRef.result) && auditRef.group) {
105
106
  uniqueGroupIds.delete(auditRef.group);
106
107
  }
107
108
  }
@@ -122,7 +123,7 @@ export class PwaCategoryRenderer extends CategoryRenderer {
122
123
  for (const groupId of groupIds) {
123
124
  const groupAuditRefs = auditRefs.filter(ref => ref.group === groupId);
124
125
  const auditCount = groupAuditRefs.length;
125
- const passedCount = groupAuditRefs.filter(ref => Util.showAsPassed(ref.result)).length;
126
+ const passedCount = groupAuditRefs.filter(ref => ReportUtils.showAsPassed(ref.result)).length;
126
127
 
127
128
  const title = groupDefinitions[groupId].title;
128
129
  tips.push(`${title}: ${passedCount}/${auditCount}`);
@@ -162,7 +163,7 @@ export class PwaCategoryRenderer extends CategoryRenderer {
162
163
  const defsEl = svgRoot.querySelector('defs');
163
164
  if (!defsEl) return;
164
165
 
165
- const idSuffix = Util.getUniqueSuffix();
166
+ const idSuffix = Globals.getUniqueSuffix();
166
167
  const elementsToUpdate = defsEl.querySelectorAll('[id]');
167
168
  for (const el of elementsToUpdate) {
168
169
  const oldId = el.id;
@@ -0,0 +1,21 @@
1
+ export type I18nFormatter = import('./i18n-formatter').I18nFormatter;
2
+ export class Globals {
3
+ /** @type {I18nFormatter} */
4
+ static i18n: I18nFormatter;
5
+ /** @type {typeof UIStrings} */
6
+ static strings: typeof UIStrings;
7
+ /** @type {LH.ReportResult | null} */
8
+ static reportJson: LH.ReportResult | null;
9
+ /**
10
+ * @param {{providedStrings: Record<string, string>; i18n: I18nFormatter; reportJson: LH.ReportResult | null}} options
11
+ */
12
+ static apply(options: {
13
+ providedStrings: Record<string, string>;
14
+ i18n: I18nFormatter;
15
+ reportJson: LH.ReportResult | null;
16
+ }): void;
17
+ static getUniqueSuffix(): number;
18
+ static resetUniqueSuffix(): void;
19
+ }
20
+ import { UIStrings } from "./report-utils.js";
21
+ //# sourceMappingURL=report-globals.d.ts.map
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @license Copyright 2023 The Lighthouse Authors. All Rights Reserved.
3
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
4
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
5
+ */
6
+
7
+ import {UIStrings} from './report-utils.js';
8
+
9
+ /** @typedef {import('./i18n-formatter').I18nFormatter} I18nFormatter */
10
+
11
+ let svgSuffix = 0;
12
+
13
+ class Globals {
14
+ /** @type {I18nFormatter} */
15
+ // @ts-expect-error: Set in report renderer.
16
+ static i18n = null;
17
+
18
+ /** @type {typeof UIStrings} */
19
+ // @ts-expect-error: Set in report renderer.
20
+ static strings = {};
21
+
22
+ /** @type {LH.ReportResult | null} */
23
+ static reportJson = null;
24
+
25
+ /**
26
+ * @param {{providedStrings: Record<string, string>; i18n: I18nFormatter; reportJson: LH.ReportResult | null}} options
27
+ */
28
+ static apply(options) {
29
+ Globals.strings = {
30
+ // Set missing renderer strings to default (english) values.
31
+ ...UIStrings,
32
+ ...options.providedStrings,
33
+ };
34
+ Globals.i18n = options.i18n;
35
+ Globals.reportJson = options.reportJson;
36
+ }
37
+
38
+ static getUniqueSuffix() {
39
+ return svgSuffix++;
40
+ }
41
+
42
+ static resetUniqueSuffix() {
43
+ svgSuffix = 0;
44
+ }
45
+ }
46
+
47
+ export {
48
+ Globals,
49
+ };
@@ -23,11 +23,11 @@
23
23
  import {CategoryRenderer} from './category-renderer.js';
24
24
  import {DetailsRenderer} from './details-renderer.js';
25
25
  import {ElementScreenshotRenderer} from './element-screenshot-renderer.js';
26
- import {I18n} from './i18n.js';
26
+ import {I18nFormatter} from './i18n-formatter.js';
27
27
  import {PerformanceCategoryRenderer} from './performance-category-renderer.js';
28
28
  import {PwaCategoryRenderer} from './pwa-category-renderer.js';
29
- import {Util} from './util.js';
30
-
29
+ import {ReportUtils} from './report-utils.js';
30
+ import {Globals} from './report-globals.js';
31
31
 
32
32
  export class ReportRenderer {
33
33
  /**
@@ -67,7 +67,7 @@ export class ReportRenderer {
67
67
 
68
68
  this._dom.setLighthouseChannel(lhr.configSettings.channel || 'unknown');
69
69
 
70
- const report = Util.prepareReportResult(lhr);
70
+ const report = ReportUtils.prepareReportResult(lhr);
71
71
 
72
72
  this._dom.rootEl.textContent = ''; // Remove previous report.
73
73
  this._dom.rootEl.append(this._renderReport(report));
@@ -108,7 +108,7 @@ export class ReportRenderer {
108
108
 
109
109
  this._renderMetaBlock(report, footer);
110
110
 
111
- this._dom.find('.lh-footer__version_issue', footer).textContent = Util.i18n.strings.footerIssue;
111
+ this._dom.find('.lh-footer__version_issue', footer).textContent = Globals.strings.footerIssue;
112
112
  this._dom.find('.lh-footer__version', footer).textContent = report.lighthouseVersion;
113
113
  return footer;
114
114
  }
@@ -118,7 +118,7 @@ export class ReportRenderer {
118
118
  * @param {DocumentFragment} footer
119
119
  */
120
120
  _renderMetaBlock(report, footer) {
121
- const envValues = Util.getEmulationDescriptions(report.configSettings || {});
121
+ const envValues = ReportUtils.getEmulationDescriptions(report.configSettings || {});
122
122
  const match = report.userAgent.match(/(\w*Chrome\/[\d.]+)/); // \w* to include 'HeadlessChrome'
123
123
  const chromeVer = Array.isArray(match)
124
124
  ? match[1].replace('/', ' ').replace('Chrome', 'Chromium')
@@ -128,35 +128,35 @@ export class ReportRenderer {
128
128
  const axeVersion = report.environment.credits?.['axe-core'];
129
129
 
130
130
  const devicesTooltipTextLines = [
131
- `${Util.i18n.strings.runtimeSettingsBenchmark}: ${benchmarkIndex}`,
132
- `${Util.i18n.strings.runtimeSettingsCPUThrottling}: ${envValues.cpuThrottling}`,
131
+ `${Globals.strings.runtimeSettingsBenchmark}: ${benchmarkIndex}`,
132
+ `${Globals.strings.runtimeSettingsCPUThrottling}: ${envValues.cpuThrottling}`,
133
133
  ];
134
134
  if (envValues.screenEmulation) {
135
135
  devicesTooltipTextLines.push(
136
- `${Util.i18n.strings.runtimeSettingsScreenEmulation}: ${envValues.screenEmulation}`);
136
+ `${Globals.strings.runtimeSettingsScreenEmulation}: ${envValues.screenEmulation}`);
137
137
  }
138
138
  if (axeVersion) {
139
- devicesTooltipTextLines.push(`${Util.i18n.strings.runtimeSettingsAxeVersion}: ${axeVersion}`);
139
+ devicesTooltipTextLines.push(`${Globals.strings.runtimeSettingsAxeVersion}: ${axeVersion}`);
140
140
  }
141
141
 
142
142
  // [CSS icon class, textContent, tooltipText]
143
143
  const metaItems = [
144
144
  ['date',
145
- `Captured at ${Util.i18n.formatDateTime(report.fetchTime)}`],
145
+ `Captured at ${Globals.i18n.formatDateTime(report.fetchTime)}`],
146
146
  ['devices',
147
147
  `${envValues.deviceEmulation} with Lighthouse ${report.lighthouseVersion}`,
148
148
  devicesTooltipTextLines.join('\n')],
149
149
  ['samples-one',
150
- Util.i18n.strings.runtimeSingleLoad,
151
- Util.i18n.strings.runtimeSingleLoadTooltip],
150
+ Globals.strings.runtimeSingleLoad,
151
+ Globals.strings.runtimeSingleLoadTooltip],
152
152
  ['stopwatch',
153
- Util.i18n.strings.runtimeAnalysisWindow],
153
+ Globals.strings.runtimeAnalysisWindow],
154
154
  ['networkspeed',
155
155
  `${envValues.summary}`,
156
- `${Util.i18n.strings.runtimeSettingsNetworkThrottling}: ${envValues.networkThrottling}`],
156
+ `${Globals.strings.runtimeSettingsNetworkThrottling}: ${envValues.networkThrottling}`],
157
157
  ['chrome',
158
158
  `Using ${chromeVer}` + (channel ? ` with ${channel}` : ''),
159
- `${Util.i18n.strings.runtimeSettingsUANetwork}: "${report.environment.networkUserAgent}"`],
159
+ `${Globals.strings.runtimeSettingsUANetwork}: "${report.environment.networkUserAgent}"`],
160
160
  ];
161
161
 
162
162
  const metaItemsEl = this._dom.find('.lh-meta__items', footer);
@@ -184,7 +184,7 @@ export class ReportRenderer {
184
184
 
185
185
  const container = this._dom.createComponent('warningsToplevel');
186
186
  const message = this._dom.find('.lh-warnings__msg', container);
187
- message.textContent = Util.i18n.strings.toplevelWarningsMessage;
187
+ message.textContent = Globals.strings.toplevelWarningsMessage;
188
188
 
189
189
  const warnings = [];
190
190
  for (const warningString of report.runWarnings) {
@@ -238,7 +238,7 @@ export class ReportRenderer {
238
238
  }
239
239
 
240
240
 
241
- if (Util.isPluginCategory(category.id)) {
241
+ if (ReportUtils.isPluginCategory(category.id)) {
242
242
  pluginGauges.push(categoryGauge);
243
243
  } else if (renderer.renderCategoryScore === categoryRenderer.renderCategoryScore) {
244
244
  // The renderer for default categories is just the default CategoryRenderer.
@@ -260,13 +260,11 @@ export class ReportRenderer {
260
260
  * @return {!DocumentFragment}
261
261
  */
262
262
  _renderReport(report) {
263
- const i18n = new I18n(report.configSettings.locale, {
264
- // Set missing renderer strings to default (english) values.
265
- ...Util.UIStrings,
266
- ...report.i18n.rendererFormattedStrings,
263
+ Globals.apply({
264
+ providedStrings: report.i18n.rendererFormattedStrings,
265
+ i18n: new I18nFormatter(report.configSettings.locale),
266
+ reportJson: report,
267
267
  });
268
- Util.i18n = i18n;
269
- Util.reportJson = report;
270
268
 
271
269
  const detailsRenderer = new DetailsRenderer(this._dom, {
272
270
  fullPageScreenshot: report.fullPageScreenshot ?? undefined,
@@ -26,8 +26,9 @@ import {ElementScreenshotRenderer} from './element-screenshot-renderer.js';
26
26
  import {toggleDarkTheme} from './features-util.js';
27
27
  import {openTreemap} from './open-tab.js';
28
28
  import {TopbarFeatures} from './topbar-features.js';
29
- import {Util} from './util.js';
29
+ import {Util} from '../../shared/util.js';
30
30
  import {getLhrFilenamePrefix} from '../generator/file-namer.js';
31
+ import {Globals} from './report-globals.js';
31
32
 
32
33
  /**
33
34
  * @param {HTMLTableElement} tableEl
@@ -106,7 +107,7 @@ export class ReportUIFeatures {
106
107
  this.json.audits['script-treemap-data'] && this.json.audits['script-treemap-data'].details;
107
108
  if (showTreemapApp) {
108
109
  this.addButton({
109
- text: Util.i18n.strings.viewTreemapLabel,
110
+ text: Globals.strings.viewTreemapLabel,
110
111
  icon: 'treemap',
111
112
  onClick: () => openTreemap(this.json),
112
113
  });
@@ -115,8 +116,8 @@ export class ReportUIFeatures {
115
116
  if (this._opts.onViewTrace) {
116
117
  this.addButton({
117
118
  text: lhr.configSettings.throttlingMethod === 'simulate' ?
118
- Util.i18n.strings.viewOriginalTraceLabel :
119
- Util.i18n.strings.viewTraceLabel,
119
+ Globals.strings.viewOriginalTraceLabel :
120
+ Globals.strings.viewTraceLabel,
120
121
  onClick: () => this._opts.onViewTrace?.(),
121
122
  });
122
123
  }
@@ -127,11 +128,11 @@ export class ReportUIFeatures {
127
128
 
128
129
  // Fill in all i18n data.
129
130
  for (const node of this._dom.findAll('[data-i18n]', this._dom.rootEl)) {
130
- // These strings are guaranteed to (at least) have a default English string in Util.UIStrings,
131
+ // These strings are guaranteed to (at least) have a default English string in UIStrings,
131
132
  // so this cannot be undefined as long as `report-ui-features.data-i18n` test passes.
132
133
  const i18nKey = node.getAttribute('data-i18n');
133
- const i18nAttr = /** @type {keyof typeof Util.i18n.strings} */ (i18nKey);
134
- node.textContent = Util.i18n.strings[i18nAttr];
134
+ const i18nAttr = /** @type {keyof typeof Globals.strings} */ (i18nKey);
135
+ node.textContent = Globals.strings[i18nAttr];
135
136
  }
136
137
  }
137
138
 
@@ -273,7 +274,7 @@ export class ReportUIFeatures {
273
274
  this._dom.find('.lh-3p-filter-count', filterTemplate).textContent =
274
275
  `${thirdPartyRows.length}`;
275
276
  this._dom.find('.lh-3p-ui-string', filterTemplate).textContent =
276
- Util.i18n.strings.thirdPartyResourcesLabel;
277
+ Globals.strings.thirdPartyResourcesLabel;
277
278
 
278
279
  const allThirdParty = thirdPartyRows.length === rowEls.length;
279
280
  const allFirstParty = !thirdPartyRows.length;