lighthouse 8.4.0 → 8.5.0-dev.20210921

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 (107) hide show
  1. package/changelog.md +79 -0
  2. package/flow-report/assets/styles.css +85 -61
  3. package/flow-report/src/app.tsx +6 -2
  4. package/flow-report/src/common.tsx +0 -41
  5. package/flow-report/src/icons.tsx +11 -0
  6. package/flow-report/src/summary/summary.tsx +32 -25
  7. package/flow-report/src/topbar.tsx +60 -0
  8. package/flow-report/src/util.ts +0 -1
  9. package/flow-report/src/wrappers/{gauge.tsx → category-score.tsx} +11 -5
  10. package/flow-report/test/summary/summary-test.tsx +3 -3
  11. package/flow-report/tsconfig.json +6 -22
  12. package/{lighthouse-core/gather/gatherers/gather-context.js → lighthouse-cli/.eslintrc.cjs} +20 -18
  13. package/lighthouse-cli/bin.js +31 -21
  14. package/lighthouse-cli/cli-flags.js +12 -7
  15. package/lighthouse-cli/commands/commands.js +2 -7
  16. package/lighthouse-cli/commands/list-audits.js +2 -2
  17. package/lighthouse-cli/commands/list-trace-categories.js +2 -2
  18. package/lighthouse-cli/index.js +3 -1
  19. package/lighthouse-cli/package.json +4 -0
  20. package/lighthouse-cli/printer.js +4 -3
  21. package/lighthouse-cli/run.js +14 -15
  22. package/lighthouse-cli/sentry-prompt.js +5 -6
  23. package/lighthouse-cli/test/smokehouse/frontends/back-compat-util.js +1 -1
  24. package/lighthouse-cli/test/smokehouse/frontends/lib.js +5 -4
  25. package/lighthouse-cli/test/smokehouse/frontends/node.js +1 -1
  26. package/lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js +93 -14
  27. package/lighthouse-cli/test/smokehouse/lib/child-process-error.js +1 -1
  28. package/lighthouse-cli/test/smokehouse/lib/concurrent-mapper.js +1 -1
  29. package/lighthouse-cli/test/smokehouse/lib/local-console.js +1 -1
  30. package/lighthouse-cli/test/smokehouse/lighthouse-runners/bundle.js +20 -16
  31. package/lighthouse-cli/test/smokehouse/lighthouse-runners/cli.js +15 -12
  32. package/lighthouse-cli/test/smokehouse/report-assert.js +23 -13
  33. package/lighthouse-cli/test/smokehouse/smokehouse.js +32 -11
  34. package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +0 -2
  35. package/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js +6 -1
  36. package/lighthouse-core/audits/dobetterweb/js-libraries.js +5 -0
  37. package/lighthouse-core/audits/dobetterweb/uses-http2.js +1 -0
  38. package/lighthouse-core/audits/lcp-lazy-loaded.js +1 -0
  39. package/lighthouse-core/audits/oopif-iframe-test-audit.js +30 -0
  40. package/lighthouse-core/computed/metrics/metric.js +2 -1
  41. package/lighthouse-core/config/config-helpers.js +47 -3
  42. package/lighthouse-core/config/config.js +3 -46
  43. package/lighthouse-core/config/default-config.js +0 -1
  44. package/lighthouse-core/fraggle-rock/config/config.js +22 -4
  45. package/lighthouse-core/fraggle-rock/config/default-config.js +0 -9
  46. package/lighthouse-core/fraggle-rock/config/filters.js +3 -0
  47. package/lighthouse-core/fraggle-rock/config/validation.js +19 -1
  48. package/lighthouse-core/fraggle-rock/gather/base-artifacts.js +8 -3
  49. package/lighthouse-core/fraggle-rock/gather/driver.js +10 -0
  50. package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +40 -19
  51. package/lighthouse-core/fraggle-rock/gather/runner-helpers.js +3 -0
  52. package/lighthouse-core/fraggle-rock/gather/session.js +44 -1
  53. package/lighthouse-core/fraggle-rock/gather/snapshot-runner.js +4 -1
  54. package/lighthouse-core/fraggle-rock/gather/timespan-runner.js +5 -1
  55. package/lighthouse-core/gather/devtools-log.js +9 -3
  56. package/lighthouse-core/gather/driver/network-monitor.js +57 -13
  57. package/lighthouse-core/gather/driver/prepare.js +43 -17
  58. package/lighthouse-core/gather/driver/target-manager.js +125 -0
  59. package/lighthouse-core/gather/driver.js +20 -0
  60. package/lighthouse-core/gather/fetcher.js +2 -2
  61. package/lighthouse-core/gather/gather-runner.js +1 -1
  62. package/lighthouse-core/gather/gatherers/css-usage.js +0 -2
  63. package/lighthouse-core/gather/gatherers/devtools-log.js +11 -9
  64. package/lighthouse-core/gather/gatherers/js-usage.js +0 -1
  65. package/lighthouse-core/gather/gatherers/script-elements.js +4 -5
  66. package/lighthouse-core/gather/gatherers/seo/robots-txt.js +3 -3
  67. package/lighthouse-core/gather/gatherers/trace.js +4 -5
  68. package/lighthouse-core/index.js +4 -0
  69. package/lighthouse-core/lib/emulation.js +0 -1
  70. package/lighthouse-core/lib/i18n/locales/en-US.json +6 -0
  71. package/lighthouse-core/lib/i18n/locales/en-XL.json +6 -0
  72. package/lighthouse-core/lib/network-recorder.js +8 -0
  73. package/lighthouse-core/lib/proto-preprocessor.js +5 -10
  74. package/lighthouse-core/runner.js +1 -3
  75. package/package.json +12 -9
  76. package/readme.md +2 -0
  77. package/report/assets/styles.css +120 -12
  78. package/report/assets/templates.html +13 -0
  79. package/report/clients/psi.js +5 -1
  80. package/report/generator/tsconfig.json +5 -16
  81. package/report/renderer/category-renderer.js +58 -5
  82. package/report/renderer/components.js +25 -5
  83. package/report/renderer/performance-category-renderer.js +5 -5
  84. package/report/renderer/pwa-category-renderer.js +10 -0
  85. package/report/renderer/report-renderer.js +12 -3
  86. package/report/renderer/report-ui-features.js +1 -4
  87. package/report/test/renderer/category-renderer-test.js +19 -0
  88. package/report/tsconfig.json +6 -21
  89. package/report/types/report-result.d.ts +1 -1
  90. package/root.js +3 -1
  91. package/third-party/snyk/snapshot.json +2 -2
  92. package/tsconfig-all.json +15 -0
  93. package/tsconfig-base.json +24 -0
  94. package/tsconfig.json +8 -18
  95. package/types/artifacts.d.ts +7 -7
  96. package/types/config.d.ts +1 -1
  97. package/types/gatherer.d.ts +7 -1
  98. package/types/lhr/flow.d.ts +0 -1
  99. package/types/lhr/lhr.d.ts +5 -1
  100. package/types/lhr/tsconfig.json +4 -14
  101. package/types/protocol.d.ts +2 -0
  102. package/types/smokehouse.d.ts +2 -1
  103. package/dist/report/flow.js +0 -149
  104. package/dist/report/standalone.js +0 -228
  105. package/flow-report/test/common-test.tsx +0 -131
  106. package/lighthouse-core/gather/gatherers/host-form-factor.js +0 -31
  107. package/lighthouse-core/gather/gatherers/host-user-agent.js +0 -29
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lighthouse",
3
- "version": "8.4.0",
3
+ "version": "8.5.0-dev.20210921",
4
4
  "description": "Automated auditing, performance metrics, and best practices for the web.",
5
5
  "main": "./lighthouse-core/index.js",
6
6
  "bin": {
@@ -21,7 +21,7 @@
21
21
  "build-extension-firefox": "node ./build/build-extension.js firefox",
22
22
  "build-devtools": "yarn reset-link && node ./build/build-bundle.js clients/devtools-entry.js dist/lighthouse-dt-bundle.js && node ./build/build-dt-report-resources.js",
23
23
  "build-smokehouse-bundle": "node ./build/build-smokehouse-bundle.js",
24
- "build-lr": "yarn reset-link && node ./build/build-lightrider-bundles.js",
24
+ "build-lr": "yarn reset-link && node ./build/build-lightrider-bundles.js && rollup lighthouse-cli/test/fixtures/static-server.js -o dist/lightrider/static-server.js -f commonjs -p commonjs -p node-resolve -e mime-types,glob",
25
25
  "build-pack": "bash build/build-pack.sh",
26
26
  "build-report": "node build/build-report-components.js && yarn eslint --fix report/renderer/components.js && node build/build-report.js",
27
27
  "build-sample-reports": "yarn build-report && node build/build-sample-reports.js",
@@ -68,7 +68,7 @@
68
68
  "dogfood-lhci": "./lighthouse-core/scripts/dogfood-lhci.sh",
69
69
  "timing-trace": "node lighthouse-core/scripts/generate-timing-trace.js",
70
70
  "changelog": "conventional-changelog --config ./build/changelog-generator/index.js --infile changelog.md --same-file",
71
- "type-check": "tsc --build ./ report/ lighthouse-viewer/ lighthouse-treemap/ flow-report/",
71
+ "type-check": "tsc --build ./tsconfig-all.json",
72
72
  "i18n:checks": "./lighthouse-core/scripts/i18n/assert-strings-collected.sh",
73
73
  "i18n:collect-strings": "node lighthouse-core/scripts/i18n/collect-strings.js",
74
74
  "update:lantern-baseline": "node lighthouse-core/scripts/lantern/update-baseline-lantern-values.js",
@@ -96,7 +96,7 @@
96
96
  "@firebase/auth-types": "0.3.2",
97
97
  "@firebase/util": "0.2.1",
98
98
  "@rollup/plugin-node-resolve": "^13.0.4",
99
- "@rollup/plugin-typescript": "^2.1.0",
99
+ "@rollup/plugin-typescript": "^8.2.5",
100
100
  "@testing-library/preact": "^2.0.1",
101
101
  "@testing-library/preact-hooks": "^1.1.0",
102
102
  "@types/archiver": "^2.1.2",
@@ -123,7 +123,7 @@
123
123
  "@types/tabulator-tables": "^4.9.1",
124
124
  "@types/update-notifier": "^4.1.0",
125
125
  "@types/ws": "^7.0.0",
126
- "@types/yargs": "^15.0.11",
126
+ "@types/yargs": "^16.0.0",
127
127
  "@types/yargs-parser": "^15.0.0",
128
128
  "@typescript-eslint/eslint-plugin": "^4.30.0",
129
129
  "@typescript-eslint/parser": "^4.30.0",
@@ -139,7 +139,8 @@
139
139
  "cpy": "^7.0.1",
140
140
  "cross-env": "^7.0.2",
141
141
  "csv-validator": "^0.0.3",
142
- "devtools-protocol": "0.0.863986",
142
+ "devtools-protocol": "0.0.901419",
143
+ "es-main": "^1.0.2",
143
144
  "eslint": "^7.23.0",
144
145
  "eslint-config-google": "^0.9.1",
145
146
  "eslint-plugin-import": "^2.24.2",
@@ -150,7 +151,7 @@
150
151
  "glob": "^7.1.3",
151
152
  "idb-keyval": "2.2.0",
152
153
  "intl-messageformat-parser": "^1.8.1",
153
- "jest": "^27.0.3",
154
+ "jest": "27.1.1",
154
155
  "jsdom": "^12.2.0",
155
156
  "jsonld": "^5.2.0",
156
157
  "jsonlint-mod": "^1.7.6",
@@ -162,15 +163,17 @@
162
163
  "pako": "^2.0.3",
163
164
  "preact": "^10.5.14",
164
165
  "pretty-json-stringify": "^0.0.2",
165
- "puppeteer": "^9.1.1",
166
+ "puppeteer": "^10.2.0",
166
167
  "rollup": "^2.50.6",
167
168
  "rollup-plugin-commonjs": "^10.1.0",
169
+ "rollup-plugin-node-resolve": "^5.2.0",
170
+ "rollup-plugin-shim": "^1.0.0",
168
171
  "rollup-plugin-terser": "^7.0.2",
169
172
  "tabulator-tables": "^4.9.3",
170
173
  "terser": "^5.3.8",
171
174
  "ts-jest": "^27.0.4",
172
175
  "typed-query-selector": "^2.4.0",
173
- "typescript": "4.4.2",
176
+ "typescript": "4.4.3",
174
177
  "webtreemap-cdt": "^3.2.1"
175
178
  },
176
179
  "dependencies": {
package/readme.md CHANGED
@@ -363,6 +363,8 @@ This section details services that have integrated Lighthouse data. If you're wo
363
363
 
364
364
  * **[Microlink](https://microlink.io)** — Microlink is a cloud browser as API. It offers Lighthouse reports on demand, making it easy to build any service on top. Similar functionality is available via the underlying open-source project named browserless.
365
365
 
366
+ * **[Peyk](https://peyk.com)** - Peyk is a website change detection & monitoring service. Peyk can detect changes in cookies, network requests, technologies, local & session storage, lighthouse audits, screenshots and so much more. Peyk is offered via free and paid plans.
367
+
366
368
  * **[Wattspeed](https://wattspeed.com/)** — Wattspeed is a free tool that generates snapshots - historical captures of your web pages that include Lighthouse scores, a list of technologies, W3C HTML validator results, DOM size, mixed content info, and more.
367
369
 
368
370
  * **[AwesomeTechStack](https://awesometechstack.com)** — AwesomeTechStack is a free to use website tech stack analyzer. AwesomeTechStack provides insights into the security, modernity, and performance of any website's technology stack and guidance to improve performance. Lighthouse insights are a crucial part of a website's tech stack rating.
@@ -104,8 +104,8 @@
104
104
  --icon-square-size: calc(var(--score-icon-size) * 0.88);
105
105
  --image-preview-size: 48px;
106
106
  --metric-toggle-lines-fill: #7F7F7F;
107
+ --metric-value-font-size: 28px;
107
108
  --metrics-toggle-background-color: var(--color-gray-200);
108
- --screenshot-overlay-background: rgba(0, 0, 0, 0.3);
109
109
  --plugin-badge-background-color: var(--color-white);
110
110
  --plugin-badge-size-big: calc(var(--gauge-circle-size-big) / 2.7);
111
111
  --plugin-badge-size: calc(var(--gauge-circle-size) / 2.7);
@@ -130,9 +130,11 @@
130
130
  --score-icon-margin-right: 12px;
131
131
  --score-icon-margin: 0 var(--score-icon-margin-right) 0 var(--score-icon-margin-left);
132
132
  --score-icon-size: 12px;
133
+ --score-icon-size-big: 16px;
133
134
  --scores-container-padding: 20px 0 20px 0;
134
135
  --scorescale-height: 6px;
135
136
  --scorescale-width: 18px;
137
+ --screenshot-overlay-background: rgba(0, 0, 0, 0.3);
136
138
  --section-padding-vertical: 12px;
137
139
  --snippet-background-color: var(--color-gray-50);
138
140
  --snippet-color: #0938C2;
@@ -677,7 +679,11 @@
677
679
 
678
680
  .lh-metric__innerwrap {
679
681
  display: grid;
680
- grid-template-columns: var(--audit-description-padding-left) 10fr 3fr;
682
+ /**
683
+ * Icon -- Metric Name
684
+ * -- Metric Value
685
+ */
686
+ grid-template-columns: calc(var(--score-icon-size) + var(--score-icon-margin-left) + var(--score-icon-margin-right)) 1fr;
681
687
  align-items: center;
682
688
  padding: 10px 0;
683
689
  }
@@ -703,14 +709,30 @@
703
709
  .lh-metric__description {
704
710
  display: none;
705
711
  grid-column-start: 2;
706
- grid-column-end: 3;
712
+ grid-column-end: 4;
707
713
  color: var(--report-text-color-secondary);
708
714
  }
709
715
 
710
716
  .lh-metric__value {
717
+ font-size: var(--metric-value-font-size);
718
+ margin: calc(var(--default-padding) / 2) 0;
711
719
  white-space: nowrap; /* No wrapping between metric value and the icon */
712
720
  font-weight: 500;
713
- justify-self: end;
721
+ grid-column-start: 2;
722
+ }
723
+
724
+ /* Change the grid to 3 columns for narrow viewport. */
725
+ @media screen and (max-width: 535px) {
726
+ .lh-metric__innerwrap {
727
+ /**
728
+ * Icon -- Metric Name -- Metric Value
729
+ */
730
+ grid-template-columns: calc(var(--score-icon-size) + var(--score-icon-margin-left) + var(--score-icon-margin-right)) 2fr 1fr;
731
+ }
732
+ .lh-metric__value {
733
+ justify-self: end;
734
+ grid-column-start: unset;
735
+ }
714
736
  }
715
737
 
716
738
  /* No-JS toggle switch */
@@ -777,7 +799,7 @@
777
799
  flex: 1;
778
800
  }
779
801
 
780
- .lh-metric .lh-metric__innerwrap::before,
802
+ .lh-metric__icon,
781
803
  .lh-scorescale-range::before {
782
804
  content: '';
783
805
  width: var(--score-icon-size);
@@ -789,7 +811,7 @@
789
811
  .lh-metric--pass .lh-metric__value {
790
812
  color: var(--color-pass-secondary);
791
813
  }
792
- .lh-metric--pass .lh-metric__innerwrap::before {
814
+ .lh-metric--pass .lh-metric__icon {
793
815
  border-radius: 100%;
794
816
  background: var(--color-pass);
795
817
  }
@@ -797,7 +819,7 @@
797
819
  .lh-metric--average .lh-metric__value {
798
820
  color: var(--color-average-secondary);
799
821
  }
800
- .lh-metric--average .lh-metric__innerwrap::before {
822
+ .lh-metric--average .lh-metric__icon {
801
823
  background: var(--color-average);
802
824
  width: var(--icon-square-size);
803
825
  height: var(--icon-square-size);
@@ -806,8 +828,8 @@
806
828
  .lh-metric--fail .lh-metric__value {
807
829
  color: var(--color-fail-secondary);
808
830
  }
809
- .lh-metric--fail .lh-metric__innerwrap::before,
810
- .lh-metric--error .lh-metric__innerwrap::before {
831
+ .lh-metric--fail .lh-metric__icon,
832
+ .lh-metric--error .lh-metric__icon {
811
833
  border-left: calc(var(--score-icon-size) / 2) solid transparent;
812
834
  border-right: calc(var(--score-icon-size) / 2) solid transparent;
813
835
  border-bottom: var(--score-icon-size) solid var(--color-fail);
@@ -1153,6 +1175,80 @@
1153
1175
  stroke: var(--color-not-applicable);
1154
1176
  }
1155
1177
 
1178
+ .lh-fraction__wrapper .lh-fraction__content::before {
1179
+ content: '';
1180
+ height: var(--score-icon-size);
1181
+ width: var(--score-icon-size);
1182
+ margin: var(--score-icon-margin);
1183
+ display: inline-block;
1184
+ }
1185
+ .lh-fraction__wrapper--pass .lh-fraction__content {
1186
+ color: var(--color-pass);
1187
+ }
1188
+ .lh-fraction__wrapper--pass .lh-fraction__background {
1189
+ background-color: var(--color-pass);
1190
+ }
1191
+ .lh-fraction__wrapper--pass .lh-fraction__content::before {
1192
+ background-color: var(--color-pass);
1193
+ border-radius: 50%;
1194
+ }
1195
+ .lh-fraction__wrapper--average .lh-fraction__content {
1196
+ color: var(--color-average);
1197
+ }
1198
+ .lh-fraction__wrapper--average .lh-fraction__background {
1199
+ background-color: var(--color-average);
1200
+ }
1201
+ .lh-fraction__wrapper--average .lh-fraction__content::before {
1202
+ background-color: var(--color-average);
1203
+ }
1204
+ .lh-fraction__wrapper--fail .lh-fraction__content {
1205
+ color: var(--color-fail);
1206
+ }
1207
+ .lh-fraction__wrapper--fail .lh-fraction__background {
1208
+ background-color: var(--color-fail);
1209
+ }
1210
+ .lh-fraction__wrapper--fail .lh-fraction__content::before {
1211
+ border-left: calc(var(--score-icon-size) / 2) solid transparent;
1212
+ border-right: calc(var(--score-icon-size) / 2) solid transparent;
1213
+ border-bottom: var(--score-icon-size) solid var(--color-fail);
1214
+ }
1215
+ .lh-fraction__wrapper--null .lh-fraction__content {
1216
+ color: var(--color-gray-700);
1217
+ }
1218
+ .lh-fraction__wrapper--null .lh-fraction__background {
1219
+ background-color: var(--color-gray-700);
1220
+ }
1221
+ .lh-fraction__wrapper--null .lh-fraction__content::before {
1222
+ border-radius: 50%;
1223
+ border: 2px solid var(--color-gray-700);
1224
+ }
1225
+
1226
+ .lh-fraction__background {
1227
+ position: absolute;
1228
+ height: 100%;
1229
+ width: 100%;
1230
+ border-radius: calc(var(--gauge-circle-size) / 2);
1231
+ opacity: 0.1;
1232
+ z-index: -1;
1233
+ }
1234
+
1235
+ .lh-fraction__content-wrapper {
1236
+ height: var(--gauge-circle-size);
1237
+ display: flex;
1238
+ align-items: center;
1239
+ }
1240
+
1241
+ .lh-fraction__content {
1242
+ display: flex;
1243
+ position: relative;
1244
+ align-items: center;
1245
+ justify-content: center;
1246
+ font-size: var(--gauge-label-font-size);
1247
+ width: max-content;
1248
+ padding: 1em;
1249
+ min-width: 6em;
1250
+ }
1251
+
1156
1252
  .lh-gauge {
1157
1253
  stroke-linecap: round;
1158
1254
  width: var(--gauge-circle-size);
@@ -1181,6 +1277,10 @@
1181
1277
  .lh-category .lh-gauge__svg-wrapper {
1182
1278
  --gauge-circle-size: var(--gauge-circle-size-big);
1183
1279
  }
1280
+ .lh-category .lh-fraction__wrapper {
1281
+ --gauge-circle-size: var(--gauge-circle-size-big);
1282
+ --score-icon-size: var(--score-icon-size-big)
1283
+ }
1184
1284
 
1185
1285
  /* The plugin badge overlay */
1186
1286
  .lh-gauge__wrapper--plugin .lh-gauge__svg-wrapper::before {
@@ -1225,7 +1325,8 @@
1225
1325
  --gauge-percentage-font-size: var(--gauge-percentage-font-size-big);
1226
1326
  }
1227
1327
 
1228
- .lh-gauge__wrapper {
1328
+ .lh-gauge__wrapper,
1329
+ .lh-fraction__wrapper {
1229
1330
  position: relative;
1230
1331
  display: flex;
1231
1332
  align-items: center;
@@ -1240,7 +1341,8 @@
1240
1341
  will-change: opacity; /* Only using for layer promotion */
1241
1342
  }
1242
1343
 
1243
- .lh-gauge__label {
1344
+ .lh-gauge__label,
1345
+ .lh-fraction__label {
1244
1346
  font-size: var(--gauge-label-font-size);
1245
1347
  line-height: var(--gauge-label-line-height);
1246
1348
  margin-top: 10px;
@@ -1249,16 +1351,22 @@
1249
1351
  }
1250
1352
 
1251
1353
  /* TODO(#8185) use more BEM (.lh-gauge__label--big) instead of relying on descendant selector */
1252
- .lh-category .lh-gauge__label {
1354
+ .lh-category .lh-gauge__label,
1355
+ .lh-category .lh-fraction__label {
1253
1356
  --gauge-label-font-size: var(--gauge-label-font-size-big);
1254
1357
  --gauge-label-line-height: var(--gauge-label-line-height-big);
1255
1358
  margin-top: 14px;
1256
1359
  }
1360
+ .lh-category .lh-fraction__content {
1361
+ --gauge-label-font-size: var(--gauge-label-font-size-big);
1362
+ }
1257
1363
 
1258
1364
 
1259
1365
  .lh-scores-header .lh-gauge__wrapper,
1366
+ .lh-scores-header .lh-fraction__wrapper,
1260
1367
  .lh-scores-header .lh-gauge--pwa__wrapper,
1261
1368
  .lh-sticky-header .lh-gauge__wrapper,
1369
+ .lh-sticky-header .lh-fraction__wrapper,
1262
1370
  .lh-sticky-header .lh-gauge--pwa__wrapper {
1263
1371
  width: var(--gauge-wrapper-width);
1264
1372
  }
@@ -110,6 +110,7 @@ limitations under the License.
110
110
  <template id="metric">
111
111
  <div class="lh-metric">
112
112
  <div class="lh-metric__innerwrap">
113
+ <div class="lh-metric__icon"></div>
113
114
  <span class="lh-metric__title"></span>
114
115
  <div class="lh-metric__value"></div>
115
116
  <div class="lh-metric__description"></div>
@@ -562,6 +563,18 @@ limitations under the License.
562
563
  </a>
563
564
  </template>
564
565
 
566
+ <!-- Lighthouse category fraction -->
567
+ <template id="fraction">
568
+ <a href="#" class="lh-fraction__wrapper">
569
+ <div class="lh-fraction__content-wrapper">
570
+ <div class="lh-fraction__content">
571
+ <div class="lh-fraction__background"></div>
572
+ </div>
573
+ </div>
574
+ <div class="lh-fraction__label"></div>
575
+ </a>
576
+ </template>
577
+
565
578
 
566
579
  <!-- Lighthouse PWA badge gauge -->
567
580
  <template id="gaugePwa">
@@ -72,7 +72,11 @@ export function prepareLabData(LHResult, document) {
72
72
  const detailsRenderer = new DetailsRenderer(dom, {fullPageScreenshot});
73
73
  const perfRenderer = new PerformanceCategoryRenderer(dom, detailsRenderer);
74
74
  // PSI environment string will ensure the categoryHeader and permalink elements are excluded
75
- const perfCategoryEl = perfRenderer.render(perfCategory, reportLHR.categoryGroups, 'PSI');
75
+ const perfCategoryEl = perfRenderer.render(
76
+ perfCategory,
77
+ reportLHR.categoryGroups,
78
+ {environment: 'PSI', gatherMode: lhResult.gatherMode}
79
+ );
76
80
 
77
81
  const scoreGaugeEl = dom.find('.lh-score__gauge', perfCategoryEl);
78
82
  scoreGaugeEl.remove();
@@ -1,23 +1,12 @@
1
1
  {
2
+ "extends": "../../tsconfig-base.json",
2
3
  "compilerOptions": {
3
- "composite": true,
4
4
  "outDir": "../../.tmp/tsbuildinfo/report/generator",
5
- "emitDeclarationOnly": true,
6
- "declarationMap": true,
7
5
 
8
- "target": "ES2020",
9
- "module": "commonjs",
10
- "moduleResolution": "node",
11
-
12
- "allowJs": true,
13
- "checkJs": true,
14
- "strict": true,
15
- // TODO: remove the next line to be fully `strict`.
16
- "useUnknownInCatchVariables": false,
17
-
18
- // "listFiles": true,
19
- // "noErrorTruncation": true,
20
- "extendedDiagnostics": true,
6
+ // Limit defs to base JS and DOM (for URL: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/34960).
7
+ "lib": ["es2020", "dom"],
8
+ // Only include `@types/node` from node_modules/.
9
+ "types": ["node"],
21
10
  },
22
11
  "references": [
23
12
  {"path": "../../types/lhr/"},
@@ -171,13 +171,14 @@ export class CategoryRenderer {
171
171
  /**
172
172
  * @param {LH.ReportResult.Category} category
173
173
  * @param {Record<string, LH.Result.ReportGroup>} groupDefinitions
174
+ * @param {{gatherMode: LH.Result.GatherMode}=} options
174
175
  * @return {DocumentFragment}
175
176
  */
176
- renderCategoryHeader(category, groupDefinitions) {
177
+ renderCategoryHeader(category, groupDefinitions, options) {
177
178
  const component = this.dom.createComponent('categoryHeader');
178
179
 
179
180
  const gaugeContainerEl = this.dom.find('.lh-score__gauge', component);
180
- const gaugeEl = this.renderScoreGauge(category, groupDefinitions);
181
+ const gaugeEl = this.renderCategoryScore(category, groupDefinitions, options);
181
182
  gaugeContainerEl.appendChild(gaugeEl);
182
183
 
183
184
  if (category.description) {
@@ -311,6 +312,19 @@ export class CategoryRenderer {
311
312
  return clumpElement;
312
313
  }
313
314
 
315
+ /**
316
+ * @param {LH.ReportResult.Category} category
317
+ * @param {Record<string, LH.Result.ReportGroup>} groupDefinitions
318
+ * @param {{gatherMode: LH.Result.GatherMode}=} options
319
+ * @return {DocumentFragment}
320
+ */
321
+ renderCategoryScore(category, groupDefinitions, options) {
322
+ if (options && (options.gatherMode === 'snapshot' || options.gatherMode === 'timespan')) {
323
+ return this.renderCategoryFraction(category);
324
+ }
325
+ return this.renderScoreGauge(category, groupDefinitions);
326
+ }
327
+
314
328
  /**
315
329
  * @param {LH.ReportResult.Category} category
316
330
  * @param {Record<string, LH.Result.ReportGroup>} groupDefinitions
@@ -353,6 +367,44 @@ export class CategoryRenderer {
353
367
  return tmpl;
354
368
  }
355
369
 
370
+ /**
371
+ * @param {LH.ReportResult.Category} category
372
+ * @return {DocumentFragment}
373
+ */
374
+ renderCategoryFraction(category) {
375
+ const tmpl = this.dom.createComponent('fraction');
376
+ const wrapper = this.dom.find('a.lh-fraction__wrapper', tmpl);
377
+ this.dom.safelySetHref(wrapper, `#${category.id}`);
378
+
379
+ const numAudits = category.auditRefs.length;
380
+
381
+ let numPassed = 0;
382
+ let totalWeight = 0;
383
+ for (const auditRef of category.auditRefs) {
384
+ totalWeight += auditRef.weight;
385
+ if (Util.showAsPassed(auditRef.result)) numPassed++;
386
+ }
387
+
388
+ const fraction = numPassed / numAudits;
389
+ const content = this.dom.find('.lh-fraction__content', tmpl);
390
+ const text = this.dom.createElement('span');
391
+ text.textContent = `${numPassed}/${numAudits}`;
392
+ content.appendChild(text);
393
+
394
+ let rating = Util.calculateRating(fraction);
395
+
396
+ // If none of the available audits can affect the score, a rating isn't useful.
397
+ // The flow report should display the fraction with neutral icon and coloring in this case.
398
+ if (totalWeight === 0) {
399
+ rating = 'null';
400
+ }
401
+
402
+ wrapper.classList.add(`lh-fraction__wrapper--${rating}`);
403
+
404
+ this.dom.find('.lh-fraction__label', tmpl).textContent = category.title;
405
+ return tmpl;
406
+ }
407
+
356
408
  /**
357
409
  * Returns true if an LH category has any non-"notApplicable" audits.
358
410
  * @param {LH.ReportResult.Category} category
@@ -434,13 +486,14 @@ export class CategoryRenderer {
434
486
  * ├── …
435
487
  * ⋮
436
488
  * @param {LH.ReportResult.Category} category
437
- * @param {Object<string, LH.Result.ReportGroup>} [groupDefinitions]
489
+ * @param {Object<string, LH.Result.ReportGroup>=} groupDefinitions
490
+ * @param {{environment?: 'PSI', gatherMode: LH.Result.GatherMode}=} options
438
491
  * @return {Element}
439
492
  */
440
- render(category, groupDefinitions = {}) {
493
+ render(category, groupDefinitions = {}, options) {
441
494
  const element = this.dom.createElement('div', 'lh-category');
442
495
  this.createPermalinkSpan(element, category.id);
443
- element.appendChild(this.renderCategoryHeader(category, groupDefinitions));
496
+ element.appendChild(this.renderCategoryHeader(category, groupDefinitions, options));
444
497
 
445
498
  // Top level clumps for audits, in order they will appear in the report.
446
499
  /** @type {Map<TopLevelClumpId, Array<LH.ReportResult.AuditRef>>} */
@@ -209,6 +209,24 @@ function createFooterComponent(dom) {
209
209
  return el0;
210
210
  }
211
211
 
212
+ /**
213
+ * @param {DOM} dom
214
+ */
215
+ function createFractionComponent(dom) {
216
+ const el0 = dom.document().createDocumentFragment();
217
+ const el1 = dom.createElement('a', 'lh-fraction__wrapper');
218
+ el1.setAttribute('href', '#');
219
+ const el2 = dom.createElement('div', 'lh-fraction__content-wrapper');
220
+ const el3 = dom.createElement('div', 'lh-fraction__content');
221
+ const el4 = dom.createElement('div', 'lh-fraction__background');
222
+ el3.append(' ', el4, ' ');
223
+ el2.append(' ', el3, ' ');
224
+ const el5 = dom.createElement('div', 'lh-fraction__label');
225
+ el1.append(' ', el2, ' ', el5, ' ');
226
+ el0.append(el1);
227
+ return el0;
228
+ }
229
+
212
230
  /**
213
231
  * @param {DOM} dom
214
232
  */
@@ -361,10 +379,11 @@ function createMetricComponent(dom) {
361
379
  const el0 = dom.document().createDocumentFragment();
362
380
  const el1 = dom.createElement('div', 'lh-metric');
363
381
  const el2 = dom.createElement('div', 'lh-metric__innerwrap');
364
- const el3 = dom.createElement('span', 'lh-metric__title');
365
- const el4 = dom.createElement('div', 'lh-metric__value');
366
- const el5 = dom.createElement('div', 'lh-metric__description');
367
- el2.append(' ', el3, ' ', el4, ' ', el5, ' ');
382
+ const el3 = dom.createElement('div', 'lh-metric__icon');
383
+ const el4 = dom.createElement('span', 'lh-metric__title');
384
+ const el5 = dom.createElement('div', 'lh-metric__value');
385
+ const el6 = dom.createElement('div', 'lh-metric__description');
386
+ el2.append(' ', el3, ' ', el4, ' ', el5, ' ', el6, ' ');
368
387
  el1.append(' ', el2, ' ');
369
388
  el0.append(el1);
370
389
  return el0;
@@ -752,7 +771,7 @@ function createWarningsToplevelComponent(dom) {
752
771
  }
753
772
 
754
773
 
755
- /** @typedef {'3pFilter'|'audit'|'categoryHeader'|'chevron'|'clump'|'crc'|'crcChain'|'elementScreenshot'|'envItem'|'footer'|'gauge'|'gaugePwa'|'heading'|'metric'|'metricsToggle'|'opportunity'|'opportunityHeader'|'scorescale'|'scoresWrapper'|'snippet'|'snippetContent'|'snippetHeader'|'snippetLine'|'topbar'|'warningsToplevel'} ComponentName */
774
+ /** @typedef {'3pFilter'|'audit'|'categoryHeader'|'chevron'|'clump'|'crc'|'crcChain'|'elementScreenshot'|'envItem'|'footer'|'fraction'|'gauge'|'gaugePwa'|'heading'|'metric'|'metricsToggle'|'opportunity'|'opportunityHeader'|'scorescale'|'scoresWrapper'|'snippet'|'snippetContent'|'snippetHeader'|'snippetLine'|'topbar'|'warningsToplevel'} ComponentName */
756
775
  /**
757
776
  * @param {DOM} dom
758
777
  * @param {ComponentName} componentName
@@ -770,6 +789,7 @@ export function createComponent(dom, componentName) {
770
789
  case 'elementScreenshot': return createElementScreenshotComponent(dom);
771
790
  case 'envItem': return createEnvItemComponent(dom);
772
791
  case 'footer': return createFooterComponent(dom);
792
+ case 'fraction': return createFractionComponent(dom);
773
793
  case 'gauge': return createGaugeComponent(dom);
774
794
  case 'gaugePwa': return createGaugePwaComponent(dom);
775
795
  case 'heading': return createHeadingComponent(dom);
@@ -155,20 +155,20 @@ export class PerformanceCategoryRenderer extends CategoryRenderer {
155
155
  /**
156
156
  * @param {LH.ReportResult.Category} category
157
157
  * @param {Object<string, LH.Result.ReportGroup>} groups
158
- * @param {'PSI'=} environment 'PSI' and undefined are the only valid values
158
+ * @param {{gatherMode: LH.Result.GatherMode, environment?: 'PSI'}=} options
159
159
  * @return {Element}
160
160
  * @override
161
161
  */
162
- render(category, groups, environment) {
162
+ render(category, groups, options) {
163
163
  const strings = Util.i18n.strings;
164
164
  const element = this.dom.createElement('div', 'lh-category');
165
- if (environment === 'PSI') {
165
+ if (options && options.environment === 'PSI') {
166
166
  const gaugeEl = this.dom.createElement('div', 'lh-score__gauge');
167
- gaugeEl.appendChild(this.renderScoreGauge(category, groups));
167
+ gaugeEl.appendChild(this.renderCategoryScore(category, groups, options));
168
168
  element.appendChild(gaugeEl);
169
169
  } else {
170
170
  this.createPermalinkSpan(element, category.id);
171
- element.appendChild(this.renderCategoryHeader(category, groups));
171
+ element.appendChild(this.renderCategoryHeader(category, groups, options));
172
172
  }
173
173
 
174
174
  // Metrics.
@@ -48,11 +48,21 @@ export class PwaCategoryRenderer extends CategoryRenderer {
48
48
  }
49
49
 
50
50
  /**
51
+ * Alias for backcompat.
51
52
  * @param {LH.ReportResult.Category} category
52
53
  * @param {Record<string, LH.Result.ReportGroup>} groupDefinitions
53
54
  * @return {DocumentFragment}
54
55
  */
55
56
  renderScoreGauge(category, groupDefinitions) {
57
+ return this.renderCategoryScore(category, groupDefinitions);
58
+ }
59
+
60
+ /**
61
+ * @param {LH.ReportResult.Category} category
62
+ * @param {Record<string, LH.Result.ReportGroup>} groupDefinitions
63
+ * @return {DocumentFragment}
64
+ */
65
+ renderCategoryScore(category, groupDefinitions) {
56
66
  // Defer to parent-gauge style if category error.
57
67
  if (category.score === null) {
58
68
  return super.renderScoreGauge(category, groupDefinitions);
@@ -160,11 +160,15 @@ export class ReportRenderer {
160
160
 
161
161
  for (const category of Object.values(report.categories)) {
162
162
  const renderer = specificCategoryRenderers[category.id] || categoryRenderer;
163
- const categoryGauge = renderer.renderScoreGauge(category, report.categoryGroups || {});
163
+ const categoryGauge = renderer.renderCategoryScore(
164
+ category,
165
+ report.categoryGroups || {},
166
+ {gatherMode: report.gatherMode}
167
+ );
164
168
 
165
169
  if (Util.isPluginCategory(category.id)) {
166
170
  pluginGauges.push(categoryGauge);
167
- } else if (renderer.renderScoreGauge === categoryRenderer.renderScoreGauge) {
171
+ } else if (renderer.renderCategoryScore === categoryRenderer.renderCategoryScore) {
168
172
  // The renderer for default categories is just the default CategoryRenderer.
169
173
  // If the functions are equal, then renderer is an instance of CategoryRenderer.
170
174
  // For example, the PWA category uses PwaCategoryRenderer, which overrides
@@ -238,12 +242,17 @@ export class ReportRenderer {
238
242
  }
239
243
 
240
244
  const categories = reportSection.appendChild(this._dom.createElement('div', 'lh-categories'));
245
+ const categoryOptions = {gatherMode: report.gatherMode};
241
246
  for (const category of Object.values(report.categories)) {
242
247
  const renderer = specificCategoryRenderers[category.id] || categoryRenderer;
243
248
  // .lh-category-wrapper is full-width and provides horizontal rules between categories.
244
249
  // .lh-category within has the max-width: var(--report-width);
245
250
  const wrapper = renderer.dom.createChildOf(categories, 'div', 'lh-category-wrapper');
246
- wrapper.appendChild(renderer.render(category, report.categoryGroups));
251
+ wrapper.appendChild(renderer.render(
252
+ category,
253
+ report.categoryGroups,
254
+ categoryOptions
255
+ ));
247
256
  }
248
257
 
249
258
  const reportFragment = this._dom.createFragment();
@@ -219,16 +219,14 @@ export class ReportUIFeatures {
219
219
  return !thirdPartyFilterAuditExclusions.includes(containingAudit.id);
220
220
  });
221
221
 
222
- tablesWithUrls.forEach((tableEl, index) => {
222
+ tablesWithUrls.forEach((tableEl) => {
223
223
  const rowEls = getTableRows(tableEl);
224
224
  const thirdPartyRows = this._getThirdPartyRows(rowEls, this.json.finalUrl);
225
225
 
226
226
  // create input box
227
227
  const filterTemplate = this._dom.createComponent('3pFilter');
228
228
  const filterInput = this._dom.find('input', filterTemplate);
229
- const id = `lh-3p-filter-label--${index}`;
230
229
 
231
- filterInput.id = id;
232
230
  filterInput.addEventListener('change', e => {
233
231
  const shouldHideThirdParty = e.target instanceof HTMLInputElement && !e.target.checked;
234
232
  let even = true;
@@ -250,7 +248,6 @@ export class ReportUIFeatures {
250
248
  }
251
249
  });
252
250
 
253
- this._dom.find('label', filterTemplate).setAttribute('for', id);
254
251
  this._dom.find('.lh-3p-filter-count', filterTemplate).textContent =
255
252
  `${thirdPartyRows.length}`;
256
253
  this._dom.find('.lh-3p-ui-string', filterTemplate).textContent =