lighthouse 12.0.0-dev.20240604 → 12.0.0-dev.20240606

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.
@@ -13,16 +13,14 @@ import AxeAudit from './axe-audit.js';
13
13
  import * as i18n from '../../lib/i18n/i18n.js';
14
14
 
15
15
  const UIStrings = {
16
- /** Title of an accesibility audit that evaluates if the ARIA role attributes are valid for the HTML element. This title is descriptive of the successful state and is shown to users when no user action is required. */
17
- title: 'Values assigned to `role=""` are valid ARIA roles.',
18
- /** Title of an accesibility audit that evaluates if the ARIA role attributes are valid for the HTML element. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed. */
19
- failureTitle: 'Values assigned to `role=""` are not valid ARIA roles.',
16
+ /** Title of an accessibility audit that evaluates if the ARIA role attributes are valid for the HTML element. This title is descriptive of the successful state and is shown to users when no user action is required. */
17
+ title: 'Uses ARIA roles only on compatible elements',
18
+ /** Title of an accessibility audit that evaluates if the ARIA role attributes are valid for the HTML element. This title is descriptive of the failing state and is shown to users when there is a failure that needs to be addressed. */
19
+ failureTitle: 'Uses ARIA roles on incompatible elements',
20
20
  /** Description of a Lighthouse audit that tells the user *why* they should try to pass. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */
21
- description: 'ARIA `role`s enable assistive technologies to know the role of each element on ' +
22
- 'the web page. If the `role` values are misspelled, not existing ARIA `role` values, or ' +
23
- 'abstract roles, then the purpose of the element will not be communicated to users of ' +
24
- 'assistive technologies. ' +
25
- '[Learn more about ARIA roles](https://dequeuniversity.com/rules/axe/4.9/aria-allowed-role).',
21
+ description: 'Many HTML elements can only be assigned certain ARIA roles. Using ARIA ' +
22
+ 'roles where they are not allowed can interfere with the accessibility of the web page. ' +
23
+ '[Learn more about ARIA roles](https://dequeuniversity.com/rules/axe/4.9/aria-allowed-role).',
26
24
  };
27
25
 
28
26
  const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
@@ -1,7 +1,7 @@
1
1
  export default InteractiveMetric;
2
2
  /**
3
3
  * @fileoverview This audit identifies the time the page is "consistently interactive".
4
- * Looks for the first period of at least 5 seconds after FMP where both CPU and network were quiet,
4
+ * Looks for the first period of at least 5 seconds after FCP where both CPU and network were quiet,
5
5
  * and returns the timestamp of the beginning of the CPU quiet period.
6
6
  * @see https://docs.google.com/document/d/1GGiI9-7KeY3TPqS3YT271upUVimo-XiL5mwWorDUD4c/edit#
7
7
  */
@@ -18,7 +18,7 @@ const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
18
18
 
19
19
  /**
20
20
  * @fileoverview This audit identifies the time the page is "consistently interactive".
21
- * Looks for the first period of at least 5 seconds after FMP where both CPU and network were quiet,
21
+ * Looks for the first period of at least 5 seconds after FCP where both CPU and network were quiet,
22
22
  * and returns the timestamp of the beginning of the CPU quiet period.
23
23
  * @see https://docs.google.com/document/d/1GGiI9-7KeY3TPqS3YT271upUVimo-XiL5mwWorDUD4c/edit#
24
24
  */
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import {makeComputedArtifact} from '../computed-artifact.js';
8
- import {LanternFirstMeaningfulPaint} from './lantern-first-meaningful-paint.js';
8
+ import {LanternLargestContentfulPaint} from './lantern-largest-contentful-paint.js';
9
9
  import {Interactive} from '../../lib/lantern/metrics/interactive.js';
10
10
  import {getComputationDataParams, lanternErrorAdapter} from './lantern-metric.js';
11
11
 
@@ -29,8 +29,8 @@ class LanternInteractive extends Interactive {
29
29
  * @return {Promise<LH.Artifacts.LanternMetric>}
30
30
  */
31
31
  static async compute_(data, context) {
32
- const fmpResult = await LanternFirstMeaningfulPaint.request(data, context);
33
- return this.computeMetricWithGraphs(data, context, {fmpResult});
32
+ const lcpResult = await LanternLargestContentfulPaint.request(data, context);
33
+ return this.computeMetricWithGraphs(data, context, {lcpResult});
34
34
  }
35
35
  }
36
36
 
@@ -4,7 +4,7 @@ export type Simulator = import('./simulator/simulator.js').Simulator;
4
4
  export type Extras = {
5
5
  optimistic: boolean;
6
6
  fcpResult?: LH.Artifacts.LanternMetric | undefined;
7
- fmpResult?: LH.Artifacts.LanternMetric | undefined;
7
+ lcpResult?: LH.Artifacts.LanternMetric | undefined;
8
8
  interactiveResult?: LH.Artifacts.LanternMetric | undefined;
9
9
  speedline?: {
10
10
  speedIndex: number;
@@ -17,7 +17,7 @@ export type Extras = {
17
17
  * @typedef Extras
18
18
  * @property {boolean} optimistic
19
19
  * @property {LH.Artifacts.LanternMetric=} fcpResult
20
- * @property {LH.Artifacts.LanternMetric=} fmpResult
20
+ * @property {LH.Artifacts.LanternMetric=} lcpResult
21
21
  * @property {LH.Artifacts.LanternMetric=} interactiveResult
22
22
  * @property {{speedIndex: number}=} speedline
23
23
  */
@@ -16,7 +16,7 @@ import {RESOURCE_TYPES} from '../../lib/network-request.js';
16
16
  * @typedef Extras
17
17
  * @property {boolean} optimistic
18
18
  * @property {LH.Artifacts.LanternMetric=} fcpResult
19
- * @property {LH.Artifacts.LanternMetric=} fmpResult
19
+ * @property {LH.Artifacts.LanternMetric=} lcpResult
20
20
  * @property {LH.Artifacts.LanternMetric=} interactiveResult
21
21
  * @property {{speedIndex: number}=} speedline
22
22
  */
@@ -66,12 +66,12 @@ class Interactive extends Metric {
66
66
  * @return {LH.Gatherer.Simulation.Result}
67
67
  */
68
68
  static getEstimateFromSimulation(simulationResult, extras) {
69
- if (!extras.fmpResult) throw new Error('missing fmpResult');
69
+ if (!extras.lcpResult) throw new Error('missing lcpResult');
70
70
 
71
71
  const lastTaskAt = Interactive.getLastLongTaskEndTime(simulationResult.nodeTimings);
72
72
  const minimumTime = extras.optimistic
73
- ? extras.fmpResult.optimisticEstimate.timeInMs
74
- : extras.fmpResult.pessimisticEstimate.timeInMs;
73
+ ? extras.lcpResult.optimisticEstimate.timeInMs
74
+ : extras.lcpResult.pessimisticEstimate.timeInMs;
75
75
  return {
76
76
  timeInMs: Math.max(minimumTime, lastTaskAt),
77
77
  nodeTimings: simulationResult.nodeTimings,
@@ -84,13 +84,13 @@ class Interactive extends Metric {
84
84
  * @return {Promise<LH.Artifacts.LanternMetric>}
85
85
  */
86
86
  static async compute(data, extras) {
87
- const fmpResult = extras?.fmpResult;
88
- if (!fmpResult) {
89
- throw new Error('FMP is required to calculate the Interactive metric');
87
+ const lcpResult = extras?.lcpResult;
88
+ if (!lcpResult) {
89
+ throw new Error('LCP is required to calculate the Interactive metric');
90
90
  }
91
91
 
92
92
  const metricResult = await super.compute(data, extras);
93
- metricResult.timing = Math.max(metricResult.timing, fmpResult.timing);
93
+ metricResult.timing = Math.max(metricResult.timing, lcpResult.timing);
94
94
  return metricResult;
95
95
  }
96
96
 
@@ -60,7 +60,11 @@ export class NetworkRequest<T = any> {
60
60
  * HTTP cache or going to the network for DNS/connection setup, in milliseconds.
61
61
  */
62
62
  networkRequestTime: number;
63
- /** When the last byte of the response headers is received, in milliseconds. */
63
+ /**
64
+ * When the last byte of the response headers is received, in milliseconds.
65
+ * Equal to networkRequestTime if no data is recieved over the
66
+ * network (ex: cached requests or data urls).
67
+ */
64
68
  responseHeadersEndTime: number;
65
69
  /** When the last byte of the response body is received, in milliseconds. */
66
70
  networkEndTime: number;
@@ -104,7 +104,11 @@ export class NetworkRequest {
104
104
  * HTTP cache or going to the network for DNS/connection setup, in milliseconds.
105
105
  */
106
106
  networkRequestTime: number;
107
- /** When the last byte of the response headers is received, in milliseconds. */
107
+ /**
108
+ * When the last byte of the response headers is received, in milliseconds.
109
+ * Equal to networkRequestTime if no data is recieved over the
110
+ * network (ex: cached requests or data urls).
111
+ */
108
112
  responseHeadersEndTime: number;
109
113
  /** When the last byte of the response body is received, in milliseconds. */
110
114
  networkEndTime: number;
@@ -203,6 +207,7 @@ export class NetworkRequest {
203
207
  * @param {LH.Crdp.Network.ResponseReceivedEvent['type']=} resourceType
204
208
  */
205
209
  _onResponse(response: LH.Crdp.Network.Response, timestamp: number, resourceType?: LH.Crdp.Network.ResponseReceivedEvent['type'] | undefined): void;
210
+ responseTimestamp: number | undefined;
206
211
  /**
207
212
  * Resolve differences between conflicting timing signals. Based on the property setters in DevTools.
208
213
  * @see https://github.com/ChromeDevTools/devtools-frontend/blob/56a99365197b85c24b732ac92b0ac70feed80179/front_end/sdk/NetworkRequest.js#L485-L502
@@ -129,7 +129,11 @@ class NetworkRequest {
129
129
  * HTTP cache or going to the network for DNS/connection setup, in milliseconds.
130
130
  */
131
131
  this.networkRequestTime = -1;
132
- /** When the last byte of the response headers is received, in milliseconds. */
132
+ /**
133
+ * When the last byte of the response headers is received, in milliseconds.
134
+ * Equal to networkRequestTime if no data is recieved over the
135
+ * network (ex: cached requests or data urls).
136
+ */
133
137
  this.responseHeadersEndTime = -1;
134
138
  /** When the last byte of the response body is received, in milliseconds. */
135
139
  this.networkEndTime = -1;
@@ -222,8 +226,9 @@ class NetworkRequest {
222
226
  this.isSecure = UrlUtils.isSecureScheme(this.parsedURL.scheme);
223
227
 
224
228
  this.rendererStartTime = data.timestamp * 1000;
225
- // Expected to be overridden with better value in `_recomputeTimesWithResourceTiming`.
229
+ // These are expected to be overridden with better value in `_recomputeTimesWithResourceTiming`.
226
230
  this.networkRequestTime = this.rendererStartTime;
231
+ this.responseHeadersEndTime = this.rendererStartTime;
227
232
 
228
233
  this.requestMethod = data.request.method;
229
234
 
@@ -350,8 +355,7 @@ class NetworkRequest {
350
355
 
351
356
  if (response.protocol) this.protocol = response.protocol;
352
357
 
353
- // This is updated in _recomputeTimesWithResourceTiming, if timings are present.
354
- this.responseHeadersEndTime = timestamp * 1000;
358
+ this.responseTimestamp = timestamp * 1000;
355
359
 
356
360
  this.transferSize = response.encodedDataLength;
357
361
  this.responseHeadersTransferSize = response.encodedDataLength;
@@ -381,7 +385,7 @@ class NetworkRequest {
381
385
  _recomputeTimesWithResourceTiming(timing) {
382
386
  // Don't recompute times if the data is invalid. RequestTime should always be a thread timestamp.
383
387
  // If we don't have receiveHeadersEnd, we really don't have more accurate data.
384
- if (timing.requestTime === 0 || timing.receiveHeadersEnd === -1) return;
388
+ if (timing.requestTime === -1 || timing.receiveHeadersEnd === -1) return;
385
389
 
386
390
  // Take networkRequestTime and responseHeadersEndTime from timing data for better accuracy.
387
391
  // Before this, networkRequestTime and responseHeadersEndTime were set to bogus values based on
@@ -392,15 +396,15 @@ class NetworkRequest {
392
396
  // See https://raw.githubusercontent.com/GoogleChrome/lighthouse/main/docs/Network-Timings.svg
393
397
  this.networkRequestTime = timing.requestTime * 1000;
394
398
  const headersReceivedTime = this.networkRequestTime + timing.receiveHeadersEnd;
395
- // This was set in `_onResponse` as that event's timestamp.
396
- const responseTimestamp = this.responseHeadersEndTime;
397
399
 
398
400
  // Update this.responseHeadersEndTime. All timing values from the netstack (timing) are well-ordered, and
399
401
  // so are the timestamps from CDP (which this.responseHeadersEndTime belongs to). It shouldn't be possible
400
402
  // that this timing from the netstack is greater than the onResponse timestamp, but just to ensure proper order
401
403
  // is maintained we bound the new timing by the network request time and the response timestamp.
402
404
  this.responseHeadersEndTime = headersReceivedTime;
403
- this.responseHeadersEndTime = Math.min(this.responseHeadersEndTime, responseTimestamp);
405
+ if (this.responseTimestamp !== undefined) {
406
+ this.responseHeadersEndTime = Math.min(this.responseHeadersEndTime, this.responseTimestamp);
407
+ }
404
408
  this.responseHeadersEndTime = Math.max(this.responseHeadersEndTime, this.networkRequestTime);
405
409
 
406
410
  // We're only at responseReceived (_onResponse) at this point.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lighthouse",
3
3
  "type": "module",
4
- "version": "12.0.0-dev.20240604",
4
+ "version": "12.0.0-dev.20240606",
5
5
  "description": "Automated auditing, performance metrics, and best practices for the web.",
6
6
  "main": "./core/index.js",
7
7
  "bin": {
@@ -18,13 +18,13 @@
18
18
  "message": "`[aria-*]` attributes match their roles"
19
19
  },
20
20
  "core/audits/accessibility/aria-allowed-role.js | description": {
21
- "message": "ARIA `role`s enable assistive technologies to know the role of each element on the web page. If the `role` values are misspelled, not existing ARIA `role` values, or abstract roles, then the purpose of the element will not be communicated to users of assistive technologies. [Learn more about ARIA roles](https://dequeuniversity.com/rules/axe/4.9/aria-allowed-role)."
21
+ "message": "Many HTML elements can only be assigned certain ARIA roles. Using ARIA roles where they are not allowed can interfere with the accessibility of the web page. [Learn more about ARIA roles](https://dequeuniversity.com/rules/axe/4.9/aria-allowed-role)."
22
22
  },
23
23
  "core/audits/accessibility/aria-allowed-role.js | failureTitle": {
24
- "message": "Values assigned to `role=\"\"` are not valid ARIA roles."
24
+ "message": "Uses ARIA roles on incompatible elements"
25
25
  },
26
26
  "core/audits/accessibility/aria-allowed-role.js | title": {
27
- "message": "Values assigned to `role=\"\"` are valid ARIA roles."
27
+ "message": "Uses ARIA roles only on compatible elements"
28
28
  },
29
29
  "core/audits/accessibility/aria-command-name.js | description": {
30
30
  "message": "When an element doesn't have an accessible name, screen readers announce it with a generic name, making it unusable for users who rely on screen readers. [Learn how to make command elements more accessible](https://dequeuniversity.com/rules/axe/4.9/aria-command-name)."
@@ -18,13 +18,13 @@
18
18
  "message": "`[aria-*]` ât́t̂ŕîb́ût́êś m̂át̂ćĥ t́ĥéîŕ r̂ól̂éŝ"
19
19
  },
20
20
  "core/audits/accessibility/aria-allowed-role.js | description": {
21
- "message": "ÂŔÎÁ `role`ŝ én̂áb̂ĺê áŝśîśt̂ív̂é t̂éh́n̂ól̂óĝíêś k̂ńô t̂h́ê ŕôĺê óf̂ âćél̂ém̂én̂t́ ôt̂h́ê ẃêb́ p̂áĝé. f́ t̂h́ê `role` v́âĺûéŝ ár̂é m̂ŝśp̂él̂ĺêd́, ó éx̂íŝt́îńĝ ÁR̂ÍÂ `role` v́âĺûéŝ, ór̂ áb̂śŕâć ŕôĺêś, t̂h́êń t̂h́ê ṕûŕp̂óŝé ôf́ t̂h́ê l̂ém̂n̂t́ íl̂n̂ót̂ b́ê ćôḿm̂úíĉt̂éd̂ t́ô úŝér̂ś ôf́ âśíŝt́îv́ê t́êćĥńôĺôǵéŝ. [Ĺêár̂ń m̂ór̂é âb́ôút̂ ÁR̂ÍÂ ŕôĺêś](https://dequeuniversity.com/rules/axe/4.9/aria-allowed-role)."
21
+ "message": "M̂án̂ý ĤT́M̂Ĺ êĺêḿêńt̂ś ĉán̂ ón̂ĺŷ b́ê áŝśîǵn̂éd̂ ćêŕt̂áîń ÂŔÎÁ r̂ól̂éŝ. Úŝín̂ǵ ÂŔÎÁ r̂ól̂éŝ ẃĥér̂é t̂h́êý âŕê ńôt́ âĺl̂óŵéd̂ ćâń îńt̂ér̂f́êŕê ẃît́ĥ t́ĥ âćĉéŝśîb́îĺît́ŷ óf̂ t́ ŵéb̂ ṕâǵê. [Ĺêár̂ń m̂ór̂é âb́ôút̂ ÁR̂ÍÂ ŕôĺêś](https://dequeuniversity.com/rules/axe/4.9/aria-allowed-role)."
22
22
  },
23
23
  "core/audits/accessibility/aria-allowed-role.js | failureTitle": {
24
- "message": "V̂ál̂úêś âśŝíĝńê `role=\"\"` âŕê ńôt́ v̂ál̂íd̂ ÁR̂ÍÂ ŕôĺêś."
24
+ "message": "Ûśêś ÂŔÎÁ r̂óéŝ ó ín̂ćôḿp̂át̂íb̂ĺê él̂ém̂én̂t́ŝ"
25
25
  },
26
26
  "core/audits/accessibility/aria-allowed-role.js | title": {
27
- "message": "V̂ál̂úêś âśŝíĝńê `role=\"\"` âŕê v́âĺîd́ ÂŔÎÁ r̂ól̂éŝ."
27
+ "message": "Ûśêś ÂŔÎÁ r̂óéŝ ón̂ĺŷ ón̂ ćôḿp̂át̂íb̂ĺê él̂ém̂én̂t́ŝ"
28
28
  },
29
29
  "core/audits/accessibility/aria-command-name.js | description": {
30
30
  "message": "Ŵh́êń âń êĺêḿêńt̂ d́ôéŝń't̂ h́âv́ê án̂ áĉćêśŝíb̂ĺê ńâḿê, śĉŕêén̂ ŕêád̂ér̂ś âńn̂óûńĉé ît́ ŵít̂h́ â ǵêńêŕîć n̂ám̂é, m̂ák̂ín̂ǵ ît́ ûńûśâb́l̂é f̂ór̂ úŝér̂ś ŵh́ô ŕêĺŷ ón̂ śĉŕêén̂ ŕêád̂ér̂ś. [L̂éâŕn̂ h́ôẃ t̂ó m̂ák̂é ĉóm̂ḿâńd̂ él̂ém̂én̂t́ŝ ḿôŕê áĉćêśŝíb̂ĺê](https://dequeuniversity.com/rules/axe/4.9/aria-command-name)."