lighthouse 10.3.0 → 10.4.0-dev.20230711

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 (126) hide show
  1. package/cli/test/smokehouse/frontends/smokehouse-bin.js +38 -22
  2. package/cli/test/smokehouse/lighthouse-runners/bundle.d.ts +1 -2
  3. package/cli/test/smokehouse/lighthouse-runners/bundle.js +11 -24
  4. package/cli/test/smokehouse/lighthouse-runners/cli.d.ts +1 -2
  5. package/cli/test/smokehouse/lighthouse-runners/cli.js +3 -7
  6. package/cli/test/smokehouse/lighthouse-runners/devtools.d.ts +1 -2
  7. package/cli/test/smokehouse/lighthouse-runners/devtools.js +1 -11
  8. package/cli/test/smokehouse/readme.md +0 -2
  9. package/cli/test/smokehouse/report-assert.d.ts +1 -2
  10. package/cli/test/smokehouse/report-assert.js +2 -19
  11. package/cli/test/smokehouse/smokehouse.js +3 -32
  12. package/core/audits/accessibility/aria-dialog-name.d.ts +10 -0
  13. package/core/audits/accessibility/aria-dialog-name.js +45 -0
  14. package/core/audits/accessibility/aria-text.d.ts +10 -0
  15. package/core/audits/accessibility/aria-text.js +44 -0
  16. package/core/audits/accessibility/empty-heading.d.ts +10 -0
  17. package/core/audits/accessibility/empty-heading.js +45 -0
  18. package/core/audits/accessibility/identical-links-same-purpose.d.ts +10 -0
  19. package/core/audits/accessibility/identical-links-same-purpose.js +45 -0
  20. package/core/audits/accessibility/landmark-one-main.d.ts +10 -0
  21. package/core/audits/accessibility/landmark-one-main.js +44 -0
  22. package/core/audits/accessibility/link-in-text-block.d.ts +10 -0
  23. package/core/audits/accessibility/link-in-text-block.js +44 -0
  24. package/core/audits/accessibility/select-name.d.ts +10 -0
  25. package/core/audits/accessibility/select-name.js +44 -0
  26. package/core/audits/accessibility/target-size.d.ts +10 -0
  27. package/core/audits/accessibility/target-size.js +45 -0
  28. package/core/audits/audit.d.ts +6 -0
  29. package/core/audits/audit.js +12 -0
  30. package/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +22 -6
  31. package/core/audits/byte-efficiency/byte-efficiency-audit.js +97 -25
  32. package/core/audits/byte-efficiency/render-blocking-resources.js +1 -0
  33. package/core/audits/long-tasks.d.ts +48 -0
  34. package/core/audits/long-tasks.js +146 -19
  35. package/core/audits/non-composited-animations.js +7 -0
  36. package/core/audits/prioritize-lcp-image.js +2 -1
  37. package/core/audits/redirects.js +4 -0
  38. package/core/audits/seo/link-text.js +10 -0
  39. package/core/audits/unsized-images.js +3 -0
  40. package/core/audits/viewport.js +11 -0
  41. package/core/audits/work-during-interaction.js +13 -2
  42. package/core/computed/js-bundles.js +1 -1
  43. package/core/computed/metrics/tbt-utils.d.ts +26 -0
  44. package/core/computed/metrics/tbt-utils.js +48 -28
  45. package/core/computed/metrics/total-blocking-time.js +1 -1
  46. package/core/computed/tbt-impact-tasks.d.ts +54 -0
  47. package/core/computed/tbt-impact-tasks.js +221 -0
  48. package/core/config/default-config.js +17 -0
  49. package/core/config/filters.d.ts +9 -9
  50. package/core/config/filters.js +7 -7
  51. package/core/config/validation.js +12 -0
  52. package/core/gather/base-gatherer.d.ts +1 -3
  53. package/core/gather/base-gatherer.js +1 -3
  54. package/core/gather/gatherers/accessibility.js +8 -3
  55. package/core/gather/gatherers/seo/font-size.d.ts +0 -1
  56. package/core/gather/gatherers/seo/font-size.js +0 -1
  57. package/core/gather/gatherers/source-maps.js +3 -2
  58. package/core/lib/cdt/SDK.d.ts +1 -1
  59. package/core/lib/cdt/SDK.js +2 -2
  60. package/core/lib/dependency-graph/simulator/simulator.js +3 -1
  61. package/core/lib/lh-error.d.ts +14 -2
  62. package/core/lib/lh-error.js +7 -1
  63. package/core/lib/navigation-error.d.ts +2 -2
  64. package/core/lib/navigation-error.js +1 -1
  65. package/core/lib/network-recorder.js +1 -0
  66. package/core/lib/stack-packs.js +4 -0
  67. package/dist/report/bundle.esm.js +2 -1
  68. package/dist/report/flow.js +9 -9
  69. package/dist/report/standalone.js +2 -2
  70. package/package.json +5 -5
  71. package/report/assets/styles.css +5 -1
  72. package/report/renderer/category-renderer.js +1 -0
  73. package/report/renderer/components.js +1 -1
  74. package/shared/localization/locales/ar-XB.json +233 -2
  75. package/shared/localization/locales/ar.json +233 -2
  76. package/shared/localization/locales/bg.json +233 -2
  77. package/shared/localization/locales/ca.json +233 -2
  78. package/shared/localization/locales/cs.json +246 -15
  79. package/shared/localization/locales/da.json +239 -8
  80. package/shared/localization/locales/de.json +238 -7
  81. package/shared/localization/locales/el.json +234 -3
  82. package/shared/localization/locales/en-GB.json +233 -2
  83. package/shared/localization/locales/en-US.json +87 -0
  84. package/shared/localization/locales/en-XA.json +233 -2
  85. package/shared/localization/locales/en-XL.json +87 -0
  86. package/shared/localization/locales/es-419.json +233 -2
  87. package/shared/localization/locales/es.json +233 -2
  88. package/shared/localization/locales/fi.json +233 -2
  89. package/shared/localization/locales/fil.json +236 -5
  90. package/shared/localization/locales/fr.json +235 -4
  91. package/shared/localization/locales/he.json +233 -2
  92. package/shared/localization/locales/hi.json +236 -5
  93. package/shared/localization/locales/hr.json +234 -3
  94. package/shared/localization/locales/hu.json +233 -2
  95. package/shared/localization/locales/id.json +233 -2
  96. package/shared/localization/locales/it.json +234 -3
  97. package/shared/localization/locales/ja.json +233 -2
  98. package/shared/localization/locales/ko.json +233 -2
  99. package/shared/localization/locales/lt.json +233 -2
  100. package/shared/localization/locales/lv.json +250 -19
  101. package/shared/localization/locales/nl.json +233 -2
  102. package/shared/localization/locales/no.json +235 -4
  103. package/shared/localization/locales/pl.json +233 -2
  104. package/shared/localization/locales/pt-PT.json +233 -2
  105. package/shared/localization/locales/pt.json +234 -3
  106. package/shared/localization/locales/ro.json +233 -2
  107. package/shared/localization/locales/ru.json +233 -2
  108. package/shared/localization/locales/sk.json +233 -2
  109. package/shared/localization/locales/sl.json +233 -2
  110. package/shared/localization/locales/sr-Latn.json +233 -2
  111. package/shared/localization/locales/sr.json +233 -2
  112. package/shared/localization/locales/sv.json +233 -2
  113. package/shared/localization/locales/ta.json +233 -2
  114. package/shared/localization/locales/te.json +233 -2
  115. package/shared/localization/locales/th.json +233 -2
  116. package/shared/localization/locales/tr.json +233 -2
  117. package/shared/localization/locales/uk.json +233 -2
  118. package/shared/localization/locales/vi.json +233 -2
  119. package/shared/localization/locales/zh-HK.json +233 -2
  120. package/shared/localization/locales/zh-TW.json +235 -4
  121. package/shared/localization/locales/zh.json +239 -8
  122. package/tsconfig.json +1 -1
  123. package/types/artifacts.d.ts +9 -2
  124. package/types/internal/smokehouse.d.ts +1 -3
  125. package/core/audits/metrics/first-contentful-paint-3g.d.ts +0 -15
  126. package/core/audits/metrics/first-contentful-paint-3g.js +0 -69
@@ -0,0 +1,221 @@
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 {makeComputedArtifact} from './computed-artifact.js';
8
+ import {MainThreadTasks} from './main-thread-tasks.js';
9
+ import {FirstContentfulPaint} from './metrics/first-contentful-paint.js';
10
+ import {Interactive} from './metrics/interactive.js';
11
+ import {TotalBlockingTime} from './metrics/total-blocking-time.js';
12
+ import {ProcessedTrace} from './processed-trace.js';
13
+ import {calculateTbtImpactForEvent} from './metrics/tbt-utils.js';
14
+
15
+ /** @typedef {LH.Artifacts.TaskNode & {tbtImpact: number, selfTbtImpact: number}} TBTImpactTask */
16
+
17
+ class TBTImpactTasks {
18
+ /**
19
+ * @param {LH.Artifacts.TaskNode} task
20
+ * @return {LH.Artifacts.TaskNode}
21
+ */
22
+ static getTopLevelTask(task) {
23
+ let topLevelTask = task;
24
+ while (topLevelTask.parent) {
25
+ topLevelTask = topLevelTask.parent;
26
+ }
27
+ return topLevelTask;
28
+ }
29
+
30
+ /**
31
+ * @param {LH.Artifacts.MetricComputationDataInput} metricComputationData
32
+ * @param {LH.Artifacts.ComputedContext} context
33
+ * @return {Promise<{startTimeMs: number, endTimeMs: number}>}
34
+ */
35
+ static async getTbtBounds(metricComputationData, context) {
36
+ const processedTrace = await ProcessedTrace.request(metricComputationData.trace, context);
37
+ if (metricComputationData.gatherContext.gatherMode !== 'navigation') {
38
+ return {
39
+ startTimeMs: 0,
40
+ endTimeMs: processedTrace.timings.traceEnd,
41
+ };
42
+ }
43
+
44
+ const fcpResult = await FirstContentfulPaint.request(metricComputationData, context);
45
+ const ttiResult = await Interactive.request(metricComputationData, context);
46
+
47
+ let startTimeMs = fcpResult.timing;
48
+ let endTimeMs = ttiResult.timing;
49
+
50
+ // When using lantern, we want to get a pessimistic view of the long tasks.
51
+ // This means we assume the earliest possible start time and latest possible end time.
52
+
53
+ if ('optimisticEstimate' in fcpResult) {
54
+ startTimeMs = fcpResult.optimisticEstimate.timeInMs;
55
+ }
56
+
57
+ if ('pessimisticEstimate' in ttiResult) {
58
+ endTimeMs = ttiResult.pessimisticEstimate.timeInMs;
59
+ }
60
+
61
+ return {startTimeMs, endTimeMs};
62
+ }
63
+
64
+ /**
65
+ * @param {LH.Artifacts.TaskNode[]} tasks
66
+ * @param {Map<LH.Artifacts.TaskNode, number>} taskToImpact
67
+ */
68
+ static createImpactTasks(tasks, taskToImpact) {
69
+ /** @type {TBTImpactTask[]} */
70
+ const tbtImpactTasks = [];
71
+
72
+ for (const task of tasks) {
73
+ const tbtImpact = taskToImpact.get(task) || 0;
74
+ let selfTbtImpact = tbtImpact;
75
+
76
+ for (const child of task.children) {
77
+ const childTbtImpact = taskToImpact.get(child) || 0;
78
+ selfTbtImpact -= childTbtImpact;
79
+ }
80
+
81
+ tbtImpactTasks.push({
82
+ ...task,
83
+ tbtImpact,
84
+ selfTbtImpact,
85
+ });
86
+ }
87
+
88
+ return tbtImpactTasks;
89
+ }
90
+
91
+ /**
92
+ * @param {LH.Artifacts.TaskNode[]} tasks
93
+ * @param {number} startTimeMs
94
+ * @param {number} endTimeMs
95
+ * @return {TBTImpactTask[]}
96
+ */
97
+ static computeImpactsFromObservedTasks(tasks, startTimeMs, endTimeMs) {
98
+ /** @type {Map<LH.Artifacts.TaskNode, number>} */
99
+ const taskToImpact = new Map();
100
+
101
+ for (const task of tasks) {
102
+ const event = {
103
+ start: task.startTime,
104
+ end: task.endTime,
105
+ duration: task.duration,
106
+ };
107
+
108
+ const topLevelTask = this.getTopLevelTask(task);
109
+ const topLevelEvent = {
110
+ start: topLevelTask.startTime,
111
+ end: topLevelTask.endTime,
112
+ duration: topLevelTask.duration,
113
+ };
114
+
115
+ const tbtImpact = calculateTbtImpactForEvent(event, startTimeMs, endTimeMs, topLevelEvent);
116
+
117
+ taskToImpact.set(task, tbtImpact);
118
+ }
119
+
120
+ return this.createImpactTasks(tasks, taskToImpact);
121
+ }
122
+
123
+ /**
124
+ * @param {LH.Artifacts.TaskNode[]} tasks
125
+ * @param {LH.Gatherer.Simulation.Result['nodeTimings']} tbtNodeTimings
126
+ * @param {number} startTimeMs
127
+ * @param {number} endTimeMs
128
+ * @return {TBTImpactTask[]}
129
+ */
130
+ static computeImpactsFromLantern(tasks, tbtNodeTimings, startTimeMs, endTimeMs) {
131
+ /** @type {Map<LH.Artifacts.TaskNode, number>} */
132
+ const taskToImpact = new Map();
133
+
134
+ /** @type {Map<LH.Artifacts.TaskNode, {start: number, end: number, duration: number}>} */
135
+ const topLevelTaskToEvent = new Map();
136
+
137
+ /** @type {Map<LH.TraceEvent, LH.Artifacts.TaskNode>} */
138
+ const traceEventToTask = new Map();
139
+ for (const task of tasks) {
140
+ traceEventToTask.set(task.event, task);
141
+ }
142
+
143
+ // Use lantern TBT timings to calculate the TBT impact of top level tasks.
144
+ for (const [node, timing] of tbtNodeTimings) {
145
+ if (node.type !== 'cpu') continue;
146
+
147
+ const event = {
148
+ start: timing.startTime,
149
+ end: timing.endTime,
150
+ duration: timing.duration,
151
+ };
152
+
153
+ const tbtImpact = calculateTbtImpactForEvent(event, startTimeMs, endTimeMs);
154
+
155
+ const task = traceEventToTask.get(node.event);
156
+ if (!task) continue;
157
+
158
+ topLevelTaskToEvent.set(task, event);
159
+ taskToImpact.set(task, tbtImpact);
160
+ }
161
+
162
+ // Interpolate the TBT impact of remaining tasks using the top level ancestor tasks.
163
+ // We don't have any lantern estimates for tasks that are not top level, so we need to estimate
164
+ // the lantern timing based on the task's observed timing relative to it's top level task's observed timing.
165
+ for (const task of tasks) {
166
+ if (taskToImpact.has(task)) continue;
167
+
168
+ const topLevelTask = this.getTopLevelTask(task);
169
+
170
+ const topLevelEvent = topLevelTaskToEvent.get(topLevelTask);
171
+ if (!topLevelEvent) continue;
172
+
173
+ const startRatio = (task.startTime - topLevelTask.startTime) / topLevelTask.duration;
174
+ const start = startRatio * topLevelEvent.duration + topLevelEvent.start;
175
+
176
+ const endRatio = (topLevelTask.endTime - task.endTime) / topLevelTask.duration;
177
+ const end = topLevelEvent.end - endRatio * topLevelEvent.duration;
178
+
179
+ const event = {
180
+ start,
181
+ end,
182
+ duration: end - start,
183
+ };
184
+
185
+ const tbtImpact = calculateTbtImpactForEvent(event, startTimeMs, endTimeMs, topLevelEvent);
186
+
187
+ taskToImpact.set(task, tbtImpact);
188
+ }
189
+
190
+ return this.createImpactTasks(tasks, taskToImpact);
191
+ }
192
+
193
+ /**
194
+ * @param {LH.Artifacts.MetricComputationDataInput} metricComputationData
195
+ * @param {LH.Artifacts.ComputedContext} context
196
+ * @return {Promise<TBTImpactTask[]>}
197
+ */
198
+ static async compute_(metricComputationData, context) {
199
+ const tbtResult = await TotalBlockingTime.request(metricComputationData, context);
200
+ const tasks = await MainThreadTasks.request(metricComputationData.trace, context);
201
+
202
+ const {startTimeMs, endTimeMs} = await this.getTbtBounds(metricComputationData, context);
203
+
204
+ if ('pessimisticEstimate' in tbtResult) {
205
+ return this.computeImpactsFromLantern(
206
+ tasks,
207
+ tbtResult.pessimisticEstimate.nodeTimings,
208
+ startTimeMs,
209
+ endTimeMs
210
+ );
211
+ }
212
+
213
+ return this.computeImpactsFromObservedTasks(tasks, startTimeMs, endTimeMs);
214
+ }
215
+ }
216
+
217
+ const TBTImpactTasksComputed = makeComputedArtifact(
218
+ TBTImpactTasks,
219
+ ['trace', 'devtoolsLog', 'URL', 'gatherContext', 'settings', 'simulator']
220
+ );
221
+ export {TBTImpactTasksComputed as TBTImpactTasks};
@@ -236,6 +236,7 @@ const defaultConfig = {
236
236
  'accessibility/accesskeys',
237
237
  'accessibility/aria-allowed-attr',
238
238
  'accessibility/aria-command-name',
239
+ 'accessibility/aria-dialog-name',
239
240
  'accessibility/aria-hidden-body',
240
241
  'accessibility/aria-hidden-focus',
241
242
  'accessibility/aria-input-field-name',
@@ -245,6 +246,7 @@ const defaultConfig = {
245
246
  'accessibility/aria-required-children',
246
247
  'accessibility/aria-required-parent',
247
248
  'accessibility/aria-roles',
249
+ 'accessibility/aria-text',
248
250
  'accessibility/aria-toggle-field-name',
249
251
  'accessibility/aria-tooltip-name',
250
252
  'accessibility/aria-treeitem-name',
@@ -258,24 +260,30 @@ const defaultConfig = {
258
260
  'accessibility/document-title',
259
261
  'accessibility/duplicate-id-active',
260
262
  'accessibility/duplicate-id-aria',
263
+ 'accessibility/empty-heading',
261
264
  'accessibility/form-field-multiple-labels',
262
265
  'accessibility/frame-title',
263
266
  'accessibility/heading-order',
264
267
  'accessibility/html-has-lang',
265
268
  'accessibility/html-lang-valid',
266
269
  'accessibility/html-xml-lang-mismatch',
270
+ 'accessibility/identical-links-same-purpose',
267
271
  'accessibility/image-alt',
268
272
  'accessibility/input-button-name',
269
273
  'accessibility/input-image-alt',
270
274
  'accessibility/label',
275
+ 'accessibility/landmark-one-main',
271
276
  'accessibility/link-name',
277
+ 'accessibility/link-in-text-block',
272
278
  'accessibility/list',
273
279
  'accessibility/listitem',
274
280
  'accessibility/meta-refresh',
275
281
  'accessibility/meta-viewport',
276
282
  'accessibility/object-alt',
283
+ 'accessibility/select-name',
277
284
  'accessibility/tabindex',
278
285
  'accessibility/table-fake-caption',
286
+ 'accessibility/target-size',
279
287
  'accessibility/td-has-header',
280
288
  'accessibility/td-headers-attr',
281
289
  'accessibility/th-has-data-cells',
@@ -504,6 +512,7 @@ const defaultConfig = {
504
512
  {id: 'accesskeys', weight: 3, group: 'a11y-navigation'},
505
513
  {id: 'aria-allowed-attr', weight: 10, group: 'a11y-aria'},
506
514
  {id: 'aria-command-name', weight: 3, group: 'a11y-aria'},
515
+ {id: 'aria-dialog-name', weight: 3, group: 'a11y-aria'},
507
516
  {id: 'aria-hidden-body', weight: 10, group: 'a11y-aria'},
508
517
  {id: 'aria-hidden-focus', weight: 3, group: 'a11y-aria'},
509
518
  {id: 'aria-input-field-name', weight: 3, group: 'a11y-aria'},
@@ -513,6 +522,7 @@ const defaultConfig = {
513
522
  {id: 'aria-required-children', weight: 10, group: 'a11y-aria'},
514
523
  {id: 'aria-required-parent', weight: 10, group: 'a11y-aria'},
515
524
  {id: 'aria-roles', weight: 10, group: 'a11y-aria'},
525
+ {id: 'aria-text', weight: 3, group: 'a11y-aria'},
516
526
  {id: 'aria-toggle-field-name', weight: 3, group: 'a11y-aria'},
517
527
  {id: 'aria-tooltip-name', weight: 3, group: 'a11y-aria'},
518
528
  {id: 'aria-treeitem-name', weight: 3, group: 'a11y-aria'},
@@ -536,12 +546,14 @@ const defaultConfig = {
536
546
  {id: 'input-button-name', weight: 10, group: 'a11y-names-labels'},
537
547
  {id: 'input-image-alt', weight: 10, group: 'a11y-names-labels'},
538
548
  {id: 'label', weight: 10, group: 'a11y-names-labels'},
549
+ {id: 'link-in-text-block', weight: 3, group: 'a11y-color-contrast'},
539
550
  {id: 'link-name', weight: 3, group: 'a11y-names-labels'},
540
551
  {id: 'list', weight: 3, group: 'a11y-tables-lists'},
541
552
  {id: 'listitem', weight: 3, group: 'a11y-tables-lists'},
542
553
  {id: 'meta-refresh', weight: 10, group: 'a11y-best-practices'},
543
554
  {id: 'meta-viewport', weight: 10, group: 'a11y-best-practices'},
544
555
  {id: 'object-alt', weight: 3, group: 'a11y-names-labels'},
556
+ {id: 'select-name', weight: 3, group: 'a11y-names-labels'},
545
557
  {id: 'tabindex', weight: 3, group: 'a11y-navigation'},
546
558
  {id: 'table-fake-caption', weight: 3, group: 'a11y-tables-lists'},
547
559
  {id: 'td-has-header', weight: 10, group: 'a11y-tables-lists'},
@@ -560,6 +572,11 @@ const defaultConfig = {
560
572
  {id: 'visual-order-follows-dom', weight: 0},
561
573
  {id: 'offscreen-content-hidden', weight: 0},
562
574
  {id: 'use-landmarks', weight: 0},
575
+ // Hidden audits
576
+ {id: 'empty-heading', weight: 0, group: 'hidden'},
577
+ {id: 'identical-links-same-purpose', weight: 0, group: 'hidden'},
578
+ {id: 'landmark-one-main', weight: 0, group: 'hidden'},
579
+ {id: 'target-size', weight: 0, group: 'hidden'},
563
580
  ],
564
581
  },
565
582
  'best-practices': {
@@ -59,25 +59,25 @@ export function filterAuditsByGatherMode(audits: LH.Config.ResolvedConfig['audit
59
59
  * Filters a categories object and their auditRefs down to the set that can be computed using
60
60
  * only the specified audits.
61
61
  *
62
- * @param {LH.Config.LegacyResolvedConfig['categories']} categories
62
+ * @param {LH.Config.ResolvedConfig['categories']} categories
63
63
  * @param {Array<LH.Config.AuditDefn>} availableAudits
64
- * @return {LH.Config.LegacyResolvedConfig['categories']}
64
+ * @return {LH.Config.ResolvedConfig['categories']}
65
65
  */
66
- export function filterCategoriesByAvailableAudits(categories: LH.Config.LegacyResolvedConfig['categories'], availableAudits: Array<LH.Config.AuditDefn>): LH.Config.LegacyResolvedConfig['categories'];
66
+ export function filterCategoriesByAvailableAudits(categories: LH.Config.ResolvedConfig['categories'], availableAudits: Array<LH.Config.AuditDefn>): LH.Config.ResolvedConfig['categories'];
67
67
  /**
68
68
  * Filters a categories object and their auditRefs down to the specified category ids.
69
69
  *
70
- * @param {LH.Config.LegacyResolvedConfig['categories']} categories
70
+ * @param {LH.Config.ResolvedConfig['categories']} categories
71
71
  * @param {string[] | null | undefined} onlyCategories
72
- * @return {LH.Config.LegacyResolvedConfig['categories']}
72
+ * @return {LH.Config.ResolvedConfig['categories']}
73
73
  */
74
- export function filterCategoriesByExplicitFilters(categories: LH.Config.LegacyResolvedConfig['categories'], onlyCategories: string[] | null | undefined): LH.Config.LegacyResolvedConfig['categories'];
74
+ export function filterCategoriesByExplicitFilters(categories: LH.Config.ResolvedConfig['categories'], onlyCategories: string[] | null | undefined): LH.Config.ResolvedConfig['categories'];
75
75
  /**
76
76
  * Optional `supportedModes` property can explicitly exclude a category even if some audits are available.
77
77
  *
78
- * @param {LH.Config.LegacyResolvedConfig['categories']} categories
78
+ * @param {LH.Config.ResolvedConfig['categories']} categories
79
79
  * @param {LH.Gatherer.GatherMode} mode
80
- * @return {LH.Config.LegacyResolvedConfig['categories']}
80
+ * @return {LH.Config.ResolvedConfig['categories']}
81
81
  */
82
- export function filterCategoriesByGatherMode(categories: LH.Config.LegacyResolvedConfig['categories'], mode: LH.Gatherer.GatherMode): LH.Config.LegacyResolvedConfig['categories'];
82
+ export function filterCategoriesByGatherMode(categories: LH.Config.ResolvedConfig['categories'], mode: LH.Gatherer.GatherMode): LH.Config.ResolvedConfig['categories'];
83
83
  //# sourceMappingURL=filters.d.ts.map
@@ -169,9 +169,9 @@ function filterAuditsByGatherMode(audits, mode) {
169
169
  /**
170
170
  * Optional `supportedModes` property can explicitly exclude a category even if some audits are available.
171
171
  *
172
- * @param {LH.Config.LegacyResolvedConfig['categories']} categories
172
+ * @param {LH.Config.ResolvedConfig['categories']} categories
173
173
  * @param {LH.Gatherer.GatherMode} mode
174
- * @return {LH.Config.LegacyResolvedConfig['categories']}
174
+ * @return {LH.Config.ResolvedConfig['categories']}
175
175
  */
176
176
  function filterCategoriesByGatherMode(categories, mode) {
177
177
  if (!categories) return null;
@@ -186,9 +186,9 @@ function filterCategoriesByGatherMode(categories, mode) {
186
186
  /**
187
187
  * Filters a categories object and their auditRefs down to the specified category ids.
188
188
  *
189
- * @param {LH.Config.LegacyResolvedConfig['categories']} categories
189
+ * @param {LH.Config.ResolvedConfig['categories']} categories
190
190
  * @param {string[] | null | undefined} onlyCategories
191
- * @return {LH.Config.LegacyResolvedConfig['categories']}
191
+ * @return {LH.Config.ResolvedConfig['categories']}
192
192
  */
193
193
  function filterCategoriesByExplicitFilters(categories, onlyCategories) {
194
194
  if (!categories || !onlyCategories) return categories;
@@ -202,7 +202,7 @@ function filterCategoriesByExplicitFilters(categories, onlyCategories) {
202
202
  * Logs a warning if any specified onlyCategory is not a known category that can
203
203
  * be included.
204
204
  *
205
- * @param {LH.Config.LegacyResolvedConfig['categories']} allCategories
205
+ * @param {LH.Config.ResolvedConfig['categories']} allCategories
206
206
  * @param {string[] | null} onlyCategories
207
207
  * @return {void}
208
208
  */
@@ -220,9 +220,9 @@ function warnOnUnknownOnlyCategories(allCategories, onlyCategories) {
220
220
  * Filters a categories object and their auditRefs down to the set that can be computed using
221
221
  * only the specified audits.
222
222
  *
223
- * @param {LH.Config.LegacyResolvedConfig['categories']} categories
223
+ * @param {LH.Config.ResolvedConfig['categories']} categories
224
224
  * @param {Array<LH.Config.AuditDefn>} availableAudits
225
- * @return {LH.Config.LegacyResolvedConfig['categories']}
225
+ * @return {LH.Config.ResolvedConfig['categories']}
226
226
  */
227
227
  function filterCategoriesByAvailableAudits(categories, availableAudits) {
228
228
  if (!categories) return categories;
@@ -223,6 +223,12 @@ function assertValidSettings(settings) {
223
223
  throw new Error(`Screen emulation mobile setting (${settings.screenEmulation.mobile}) does not match formFactor setting (${settings.formFactor}). See https://github.com/GoogleChrome/lighthouse/blob/main/docs/emulation.md`);
224
224
  }
225
225
  }
226
+
227
+ const skippedAndOnlyAuditId =
228
+ settings.skipAudits?.find(auditId => settings.onlyAudits?.includes(auditId));
229
+ if (skippedAndOnlyAuditId) {
230
+ throw new Error(`${skippedAndOnlyAuditId} appears in both skipAudits and onlyAudits`);
231
+ }
226
232
  }
227
233
 
228
234
  /**
@@ -253,7 +259,13 @@ function assertArtifactTopologicalOrder(navigations) {
253
259
  function assertValidConfig(resolvedConfig) {
254
260
  const {warnings} = assertValidFRNavigations(resolvedConfig.navigations);
255
261
 
262
+ /** @type {Set<string>} */
263
+ const artifactIds = new Set();
256
264
  for (const artifactDefn of resolvedConfig.artifacts || []) {
265
+ if (artifactIds.has(artifactDefn.id)) {
266
+ throw new Error(`Config defined multiple artifacts with id '${artifactDefn.id}'`);
267
+ }
268
+ artifactIds.add(artifactDefn.id);
257
269
  assertValidFRGatherer(artifactDefn.gatherer);
258
270
  }
259
271
 
@@ -1,8 +1,6 @@
1
1
  export default FRGatherer;
2
2
  /**
3
- * Base class for all gatherers supporting both Fraggle Rock and the legacy flow.
4
- * Most extending classes should implement the Fraggle Rock API and let this class handle translation.
5
- * See core/gather/gatherers/gatherer.js for legacy method explanations.
3
+ * Base class for all gatherers.
6
4
  *
7
5
  * @implements {LH.Gatherer.GathererInstance}
8
6
  * @implements {LH.Gatherer.FRGathererInstance}
@@ -9,9 +9,7 @@ import * as LH from '../../types/lh.js';
9
9
  /* eslint-disable no-unused-vars */
10
10
 
11
11
  /**
12
- * Base class for all gatherers supporting both Fraggle Rock and the legacy flow.
13
- * Most extending classes should implement the Fraggle Rock API and let this class handle translation.
14
- * See core/gather/gatherers/gatherer.js for legacy method explanations.
12
+ * Base class for all gatherers.
15
13
  *
16
14
  * @implements {LH.Gatherer.GathererInstance}
17
15
  * @implements {LH.Gatherer.FRGathererInstance}
@@ -41,18 +41,22 @@ async function runA11yChecks() {
41
41
  // Consider http://go/prcpg for expert review of the aXe rules.
42
42
  'accesskeys': {enabled: true},
43
43
  'area-alt': {enabled: false},
44
+ 'aria-dialog-name': {enabled: true},
44
45
  'aria-roledescription': {enabled: false},
45
46
  'aria-treeitem-name': {enabled: true},
47
+ 'aria-text': {enabled: true},
46
48
  'audio-caption': {enabled: false},
47
49
  'blink': {enabled: false},
48
50
  'duplicate-id': {enabled: false},
51
+ 'empty-heading': {enabled: true},
49
52
  'frame-focusable-content': {enabled: false},
50
53
  'frame-title-unique': {enabled: false},
51
54
  'heading-order': {enabled: true},
52
55
  'html-xml-lang-mismatch': {enabled: true},
53
- 'identical-links-same-purpose': {enabled: false},
56
+ 'identical-links-same-purpose': {enabled: true},
54
57
  'input-button-name': {enabled: true},
55
- 'link-in-text-block': {enabled: false},
58
+ 'landmark-one-main': {enabled: true},
59
+ 'link-in-text-block': {enabled: true},
56
60
  'marquee': {enabled: false},
57
61
  'meta-viewport': {enabled: true},
58
62
  // https://github.com/dequelabs/axe-core/issues/2958
@@ -60,11 +64,12 @@ async function runA11yChecks() {
60
64
  'no-autoplay-audio': {enabled: false},
61
65
  'role-img-alt': {enabled: false},
62
66
  'scrollable-region-focusable': {enabled: false},
63
- 'select-name': {enabled: false},
67
+ 'select-name': {enabled: true},
64
68
  'server-side-image-map': {enabled: false},
65
69
  'svg-img-alt': {enabled: false},
66
70
  'tabindex': {enabled: true},
67
71
  'table-fake-caption': {enabled: true},
72
+ 'target-size': {enabled: true},
68
73
  'td-has-header': {enabled: true},
69
74
  },
70
75
  });
@@ -1,5 +1,4 @@
1
1
  export default FontSize;
2
- export type Driver = typeof import("../../../legacy/gather/driver.js");
3
2
  export type NodeFontData = LH.Artifacts.FontSize['analyzedFailingNodesData'][0];
4
3
  export type BackendIdsToFontData = Map<number, {
5
4
  fontSize: number;
@@ -22,7 +22,6 @@ const MINIMAL_LEGIBLE_FONT_SIZE_PX = 12;
22
22
  // limit number of protocol calls to make sure that gatherer doesn't take more than 1-2s
23
23
  const MAX_NODES_SOURCE_RULE_FETCHED = 50; // number of nodes to fetch the source font-size rule
24
24
 
25
- /** @typedef {import('../../../legacy/gather/driver.js')} Driver */
26
25
  /** @typedef {LH.Artifacts.FontSize['analyzedFailingNodesData'][0]} NodeFontData */
27
26
  /** @typedef {Map<number, {fontSize: number, textLength: number}>} BackendIdsToFontData */
28
27
 
@@ -4,6 +4,7 @@
4
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
5
  */
6
6
 
7
+ import SDK from '../../lib/cdt/SDK.js';
7
8
  import FRGatherer from '../base-gatherer.js';
8
9
 
9
10
  /**
@@ -32,7 +33,7 @@ class SourceMaps extends FRGatherer {
32
33
  if (response.content === null) {
33
34
  throw new Error(`Failed fetching source map (${response.status})`);
34
35
  }
35
- return JSON.parse(response.content);
36
+ return SDK.SourceMap.parseSourceMap(response.content);
36
37
  }
37
38
 
38
39
  /**
@@ -41,7 +42,7 @@ class SourceMaps extends FRGatherer {
41
42
  */
42
43
  parseSourceMapFromDataUrl(sourceMapURL) {
43
44
  const buffer = Buffer.from(sourceMapURL.split(',')[1], 'base64');
44
- return JSON.parse(buffer.toString());
45
+ return SDK.SourceMap.parseSourceMap(buffer.toString());
45
46
  }
46
47
 
47
48
  /**
@@ -1,2 +1,2 @@
1
- export const TextSourceMap: typeof import("./generated/SourceMap.js");
1
+ export const SourceMap: typeof import("./generated/SourceMap.js");
2
2
  //# sourceMappingURL=SDK.d.ts.map
@@ -5,12 +5,12 @@
5
5
  */
6
6
 
7
7
  const SDK = {
8
- TextSourceMap: require('./generated/SourceMap.js'),
8
+ SourceMap: require('./generated/SourceMap.js'),
9
9
  };
10
10
 
11
11
  // Add `lastColumnNumber` to mappings. This will eventually be added to CDT.
12
12
  // @ts-expect-error
13
- SDK.TextSourceMap.prototype.computeLastGeneratedColumns = function() {
13
+ SDK.SourceMap.prototype.computeLastGeneratedColumns = function() {
14
14
  const mappings = this.mappings();
15
15
  if (mappings.length && mappings[0].lastColumnNumber !== undefined) return;
16
16
 
@@ -517,7 +517,9 @@ class Simulator {
517
517
 
518
518
  const wastedBits = wastedBytes * 8;
519
519
  const wastedMs = wastedBits / bitsPerSecond * 1000;
520
- return wastedMs;
520
+
521
+ // This is an estimate of wasted time, so we won't be more precise than 10ms.
522
+ return Math.round(wastedMs / 10) * 10;
521
523
  }
522
524
 
523
525
  /** @return {Map<string, Map<Node, CompleteNodeTiming>>} */
@@ -23,10 +23,22 @@ export type SerializedBaseError = {
23
23
  stack?: string;
24
24
  cause?: unknown;
25
25
  };
26
+ /**
27
+ * The {@link ErrorOptions } type wasn't added until es2022 (Node 16), so we recreate it here to support ts targets before es2022.
28
+ * TODO: Just use `ErrorOptions` if we can't support targets before es2022 in the docs test.
29
+ */
30
+ export type LHErrorOptions = {
31
+ cause: unknown;
32
+ };
26
33
  /**
27
34
  * @typedef {{sentinel: '__LighthouseErrorSentinel', code: string, stack?: string, cause?: unknown, properties?: {[p: string]: string|undefined}}} SerializedLighthouseError
28
35
  * @typedef {{sentinel: '__ErrorSentinel', message: string, code?: string, stack?: string, cause?: unknown}} SerializedBaseError
29
36
  */
37
+ /**
38
+ * The {@link ErrorOptions} type wasn't added until es2022 (Node 16), so we recreate it here to support ts targets before es2022.
39
+ * TODO: Just use `ErrorOptions` if we can't support targets before es2022 in the docs test.
40
+ * @typedef {{cause: unknown}} LHErrorOptions
41
+ */
30
42
  export class LighthouseError extends Error {
31
43
  /**
32
44
  * @param {string} method
@@ -59,9 +71,9 @@ export class LighthouseError extends Error {
59
71
  /**
60
72
  * @param {LighthouseErrorDefinition} errorDefinition
61
73
  * @param {Record<string, string|undefined>=} properties
62
- * @param {ErrorOptions=} options
74
+ * @param {LHErrorOptions=} options
63
75
  */
64
- constructor(errorDefinition: LighthouseErrorDefinition, properties?: Record<string, string | undefined> | undefined, options?: ErrorOptions | undefined);
76
+ constructor(errorDefinition: LighthouseErrorDefinition, properties?: Record<string, string | undefined> | undefined, options?: LHErrorOptions | undefined);
65
77
  code: string;
66
78
  friendlyMessage: import("../index.js").IcuMessage;
67
79
  lhrRuntimeError: boolean;
@@ -111,11 +111,17 @@ const ERROR_SENTINEL = '__ErrorSentinel';
111
111
  * @typedef {{sentinel: '__ErrorSentinel', message: string, code?: string, stack?: string, cause?: unknown}} SerializedBaseError
112
112
  */
113
113
 
114
+ /**
115
+ * The {@link ErrorOptions} type wasn't added until es2022 (Node 16), so we recreate it here to support ts targets before es2022.
116
+ * TODO: Just use `ErrorOptions` if we can't support targets before es2022 in the docs test.
117
+ * @typedef {{cause: unknown}} LHErrorOptions
118
+ */
119
+
114
120
  class LighthouseError extends Error {
115
121
  /**
116
122
  * @param {LighthouseErrorDefinition} errorDefinition
117
123
  * @param {Record<string, string|undefined>=} properties
118
- * @param {ErrorOptions=} options
124
+ * @param {LHErrorOptions=} options
119
125
  */
120
126
  constructor(errorDefinition, properties, options) {
121
127
  super(errorDefinition.code, options);
@@ -15,12 +15,12 @@ export function getInterstitialError(mainRecord: LH.Artifacts.NetworkRequest | u
15
15
  * Returns an error if the page load should be considered failed, e.g. from a
16
16
  * main document request failure, a security issue, etc.
17
17
  * @param {LH.LighthouseError|undefined} navigationError
18
- * @param {{url: string, loadFailureMode: LH.Gatherer.PassContext['passConfig']['loadFailureMode'], networkRecords: Array<LH.Artifacts.NetworkRequest>, warnings: Array<string | LH.IcuMessage>}} context
18
+ * @param {{url: string, loadFailureMode: LH.Config.SharedPassNavigationJson['loadFailureMode'], networkRecords: Array<LH.Artifacts.NetworkRequest>, warnings: Array<string | LH.IcuMessage>}} context
19
19
  * @return {LH.LighthouseError|undefined}
20
20
  */
21
21
  export function getPageLoadError(navigationError: LH.LighthouseError | undefined, context: {
22
22
  url: string;
23
- loadFailureMode: LH.Gatherer.PassContext['passConfig']['loadFailureMode'];
23
+ loadFailureMode: LH.Config.SharedPassNavigationJson['loadFailureMode'];
24
24
  networkRecords: Array<LH.Artifacts.NetworkRequest>;
25
25
  warnings: Array<string | LH.IcuMessage>;
26
26
  }): LH.LighthouseError | undefined;
@@ -110,7 +110,7 @@ function getNonHtmlError(finalRecord) {
110
110
  * Returns an error if the page load should be considered failed, e.g. from a
111
111
  * main document request failure, a security issue, etc.
112
112
  * @param {LH.LighthouseError|undefined} navigationError
113
- * @param {{url: string, loadFailureMode: LH.Gatherer.PassContext['passConfig']['loadFailureMode'], networkRecords: Array<LH.Artifacts.NetworkRequest>, warnings: Array<string | LH.IcuMessage>}} context
113
+ * @param {{url: string, loadFailureMode: LH.Config.SharedPassNavigationJson['loadFailureMode'], networkRecords: Array<LH.Artifacts.NetworkRequest>, warnings: Array<string | LH.IcuMessage>}} context
114
114
  * @return {LH.LighthouseError|undefined}
115
115
  */
116
116
  function getPageLoadError(navigationError, context) {
@@ -80,6 +80,7 @@ class NetworkRecorder extends RequestEventEmitter {
80
80
  const request = new NetworkRequest();
81
81
  request.onRequestWillBeSent(data);
82
82
  request.sessionId = event.sessionId;
83
+ request.sessionTargetType = event.targetType;
83
84
  this.onRequestStarted(request);
84
85
  log.verbose('network', `request will be sent to ${request.url}`);
85
86
  return;
@@ -23,6 +23,10 @@ const stackPacksToInclude = [
23
23
  packId: 'wordpress',
24
24
  requiredStacks: ['js:wordpress'],
25
25
  },
26
+ {
27
+ packId: 'wix',
28
+ requiredStacks: ['js:wix'],
29
+ },
26
30
  {
27
31
  packId: 'wp-rocket',
28
32
  requiredStacks: ['js:wp-rocket'],