lighthouse 10.2.0 → 10.3.0-dev.20230613

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 (132) hide show
  1. package/cli/test/smokehouse/__snapshots__/report-assert-test.js.snap +4 -6
  2. package/core/audits/accessibility/html-xml-lang-mismatch.d.ts +10 -0
  3. package/core/audits/accessibility/html-xml-lang-mismatch.js +47 -0
  4. package/core/audits/accessibility/input-button-name.d.ts +10 -0
  5. package/core/audits/accessibility/input-button-name.js +44 -0
  6. package/core/audits/accessibility/table-fake-caption.d.ts +10 -0
  7. package/core/audits/accessibility/table-fake-caption.js +47 -0
  8. package/core/audits/accessibility/td-has-header.d.ts +10 -0
  9. package/core/audits/accessibility/td-has-header.js +45 -0
  10. package/core/audits/audit.d.ts +2 -1
  11. package/core/audits/audit.js +5 -2
  12. package/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +3 -3
  13. package/core/audits/byte-efficiency/byte-efficiency-audit.js +14 -19
  14. package/core/audits/deprecations.js +1 -1
  15. package/core/audits/largest-contentful-paint-element.d.ts +14 -2
  16. package/core/audits/largest-contentful-paint-element.js +49 -31
  17. package/core/audits/layout-shift-elements.d.ts +3 -2
  18. package/core/audits/layout-shift-elements.js +10 -2
  19. package/core/audits/metrics.js +0 -2
  20. package/core/audits/network-requests.js +6 -0
  21. package/core/audits/seo/crawlable-anchors.js +4 -0
  22. package/core/audits/server-response-time.d.ts +2 -1
  23. package/core/audits/server-response-time.js +17 -3
  24. package/core/audits/uses-rel-preconnect.js +20 -4
  25. package/core/computed/entity-classification.d.ts +14 -1
  26. package/core/computed/entity-classification.js +61 -6
  27. package/core/computed/metrics/cumulative-layout-shift.d.ts +2 -9
  28. package/core/computed/metrics/cumulative-layout-shift.js +3 -16
  29. package/core/computed/metrics/time-to-first-byte.js +10 -7
  30. package/core/computed/metrics/timing-summary.js +0 -3
  31. package/core/config/default-config.js +8 -0
  32. package/core/gather/driver/execution-context.js +13 -5
  33. package/core/gather/driver/target-manager.d.ts +13 -0
  34. package/core/gather/driver/target-manager.js +49 -0
  35. package/core/gather/gatherers/accessibility.js +19 -20
  36. package/core/gather/gatherers/anchor-elements.js +2 -0
  37. package/core/gather/gatherers/global-listeners.js +38 -21
  38. package/core/gather/gatherers/inspector-issues.js +2 -1
  39. package/core/gather/gatherers/trace-elements.d.ts +3 -2
  40. package/core/gather/gatherers/trace-elements.js +10 -19
  41. package/core/legacy/config/config.d.ts +1 -1
  42. package/core/legacy/config/config.js +1 -1
  43. package/core/legacy/gather/driver.d.ts +1 -0
  44. package/core/legacy/gather/driver.js +11 -0
  45. package/core/lib/asset-saver.d.ts +4 -0
  46. package/core/lib/asset-saver.js +19 -0
  47. package/core/lib/bf-cache-strings.d.ts +3 -0
  48. package/core/lib/bf-cache-strings.js +248 -232
  49. package/core/lib/cdt/generated/SourceMap.d.ts +18 -31
  50. package/core/lib/cdt/generated/SourceMap.js +51 -101
  51. package/core/lib/dependency-graph/simulator/network-analyzer.d.ts +6 -2
  52. package/core/lib/dependency-graph/simulator/network-analyzer.js +45 -13
  53. package/core/lib/deprecation-description.d.ts +17 -0
  54. package/core/lib/deprecation-description.js +71 -0
  55. package/core/lib/deprecations-strings.d.ts +282 -63
  56. package/core/lib/deprecations-strings.js +282 -551
  57. package/core/lib/lh-error.d.ts +9 -4
  58. package/core/lib/lh-error.js +15 -11
  59. package/core/lib/network-request.d.ts +13 -6
  60. package/core/lib/network-request.js +39 -14
  61. package/core/lib/tracehouse/trace-processor.js +1 -1
  62. package/core/lib/url-utils.js +4 -0
  63. package/core/runner.js +4 -2
  64. package/dist/report/bundle.esm.js +24 -7
  65. package/dist/report/flow.js +3 -3
  66. package/dist/report/standalone.js +8 -8
  67. package/package.json +12 -12
  68. package/readme.md +7 -0
  69. package/report/assets/styles.css +18 -2
  70. package/report/assets/templates.html +4 -0
  71. package/report/renderer/category-renderer.js +5 -2
  72. package/report/renderer/components.js +2 -2
  73. package/report/renderer/dom.d.ts +4 -1
  74. package/report/renderer/dom.js +3 -2
  75. package/shared/localization/locales/ar-XB.json +20 -170
  76. package/shared/localization/locales/ar.json +20 -170
  77. package/shared/localization/locales/bg.json +19 -169
  78. package/shared/localization/locales/ca.json +19 -169
  79. package/shared/localization/locales/cs.json +19 -169
  80. package/shared/localization/locales/da.json +19 -169
  81. package/shared/localization/locales/de.json +19 -169
  82. package/shared/localization/locales/el.json +19 -169
  83. package/shared/localization/locales/en-GB.json +9 -159
  84. package/shared/localization/locales/en-US.json +151 -70
  85. package/shared/localization/locales/en-XA.json +20 -170
  86. package/shared/localization/locales/en-XL.json +151 -70
  87. package/shared/localization/locales/es-419.json +19 -169
  88. package/shared/localization/locales/es.json +19 -169
  89. package/shared/localization/locales/fi.json +19 -169
  90. package/shared/localization/locales/fil.json +17 -167
  91. package/shared/localization/locales/fr.json +18 -168
  92. package/shared/localization/locales/he.json +19 -169
  93. package/shared/localization/locales/hi.json +20 -170
  94. package/shared/localization/locales/hr.json +19 -169
  95. package/shared/localization/locales/hu.json +19 -169
  96. package/shared/localization/locales/id.json +19 -169
  97. package/shared/localization/locales/it.json +19 -169
  98. package/shared/localization/locales/ja.json +19 -169
  99. package/shared/localization/locales/ko.json +19 -169
  100. package/shared/localization/locales/lt.json +20 -170
  101. package/shared/localization/locales/lv.json +20 -170
  102. package/shared/localization/locales/nl.json +18 -168
  103. package/shared/localization/locales/no.json +19 -169
  104. package/shared/localization/locales/pl.json +19 -169
  105. package/shared/localization/locales/pt-PT.json +19 -169
  106. package/shared/localization/locales/pt.json +19 -169
  107. package/shared/localization/locales/ro.json +19 -169
  108. package/shared/localization/locales/ru.json +19 -169
  109. package/shared/localization/locales/sk.json +20 -170
  110. package/shared/localization/locales/sl.json +19 -169
  111. package/shared/localization/locales/sr-Latn.json +19 -169
  112. package/shared/localization/locales/sr.json +19 -169
  113. package/shared/localization/locales/sv.json +19 -169
  114. package/shared/localization/locales/ta.json +19 -169
  115. package/shared/localization/locales/te.json +19 -169
  116. package/shared/localization/locales/th.json +19 -169
  117. package/shared/localization/locales/tr.json +19 -169
  118. package/shared/localization/locales/uk.json +19 -169
  119. package/shared/localization/locales/vi.json +20 -170
  120. package/shared/localization/locales/zh-HK.json +19 -169
  121. package/shared/localization/locales/zh-TW.json +19 -169
  122. package/shared/localization/locales/zh.json +19 -169
  123. package/shared/util.d.ts +6 -0
  124. package/shared/util.js +14 -1
  125. package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +2 -1
  126. package/tsconfig-base.json +1 -1
  127. package/types/artifacts.d.ts +10 -8
  128. package/types/audit.d.ts +5 -0
  129. package/types/gatherer.d.ts +1 -0
  130. package/types/lhr/audit-details.d.ts +1 -0
  131. package/types/lhr/audit-result.d.ts +10 -0
  132. package/types/internal/es-main.d.ts +0 -18
@@ -8,20 +8,24 @@ export type LighthouseErrorDefinition = {
8
8
  lhrRuntimeError?: boolean | undefined;
9
9
  };
10
10
  export type SerializedLighthouseError = {
11
- [p: string]: string | undefined;
12
11
  sentinel: '__LighthouseErrorSentinel';
13
12
  code: string;
14
13
  stack?: string | undefined;
14
+ cause?: unknown;
15
+ properties?: {
16
+ [p: string]: string | undefined;
17
+ } | undefined;
15
18
  };
16
19
  export type SerializedBaseError = {
17
20
  sentinel: '__ErrorSentinel';
18
21
  message: string;
19
22
  code?: string;
20
23
  stack?: string;
24
+ cause?: unknown;
21
25
  };
22
26
  /**
23
- * @typedef {{sentinel: '__LighthouseErrorSentinel', code: string, stack?: string, [p: string]: string|undefined}} SerializedLighthouseError
24
- * @typedef {{sentinel: '__ErrorSentinel', message: string, code?: string, stack?: string}} SerializedBaseError
27
+ * @typedef {{sentinel: '__LighthouseErrorSentinel', code: string, stack?: string, cause?: unknown, properties?: {[p: string]: string|undefined}}} SerializedLighthouseError
28
+ * @typedef {{sentinel: '__ErrorSentinel', message: string, code?: string, stack?: string, cause?: unknown}} SerializedBaseError
25
29
  */
26
30
  export class LighthouseError extends Error {
27
31
  /**
@@ -55,8 +59,9 @@ export class LighthouseError extends Error {
55
59
  /**
56
60
  * @param {LighthouseErrorDefinition} errorDefinition
57
61
  * @param {Record<string, string|undefined>=} properties
62
+ * @param {ErrorOptions=} options
58
63
  */
59
- constructor(errorDefinition: LighthouseErrorDefinition, properties?: Record<string, string | undefined> | undefined);
64
+ constructor(errorDefinition: LighthouseErrorDefinition, properties?: Record<string, string | undefined> | undefined, options?: ErrorOptions | undefined);
60
65
  code: string;
61
66
  friendlyMessage: import("../index.js").IcuMessage;
62
67
  lhrRuntimeError: boolean;
@@ -107,17 +107,18 @@ const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
107
107
  const LHERROR_SENTINEL = '__LighthouseErrorSentinel';
108
108
  const ERROR_SENTINEL = '__ErrorSentinel';
109
109
  /**
110
- * @typedef {{sentinel: '__LighthouseErrorSentinel', code: string, stack?: string, [p: string]: string|undefined}} SerializedLighthouseError
111
- * @typedef {{sentinel: '__ErrorSentinel', message: string, code?: string, stack?: string}} SerializedBaseError
110
+ * @typedef {{sentinel: '__LighthouseErrorSentinel', code: string, stack?: string, cause?: unknown, properties?: {[p: string]: string|undefined}}} SerializedLighthouseError
111
+ * @typedef {{sentinel: '__ErrorSentinel', message: string, code?: string, stack?: string, cause?: unknown}} SerializedBaseError
112
112
  */
113
113
 
114
114
  class LighthouseError extends Error {
115
115
  /**
116
116
  * @param {LighthouseErrorDefinition} errorDefinition
117
117
  * @param {Record<string, string|undefined>=} properties
118
+ * @param {ErrorOptions=} options
118
119
  */
119
- constructor(errorDefinition, properties) {
120
- super(errorDefinition.code);
120
+ constructor(errorDefinition, properties, options) {
121
+ super(errorDefinition.code, options);
121
122
  this.name = 'LighthouseError';
122
123
  this.code = errorDefinition.code;
123
124
  // Add additional properties to be ICU replacements in the error string.
@@ -163,19 +164,20 @@ class LighthouseError extends Error {
163
164
  if (err instanceof LighthouseError) {
164
165
  // Remove class props so that remaining values were what was passed in as `properties`.
165
166
  // eslint-disable-next-line no-unused-vars
166
- const {name, code, message, friendlyMessage, lhrRuntimeError, stack, ...properties} = err;
167
+ const {name, code, message, friendlyMessage, lhrRuntimeError, stack, cause, ...properties} = err;
167
168
 
168
169
  return {
169
170
  sentinel: LHERROR_SENTINEL,
170
171
  code,
171
172
  stack,
172
- ...properties,
173
+ cause,
174
+ properties: /** @type {{ [p: string]: string | undefined }} */ (properties),
173
175
  };
174
176
  }
175
177
 
176
178
  // Unexpected errors won't be LighthouseErrors, but we want them serialized as well.
177
179
  if (err instanceof Error) {
178
- const {message, stack} = err;
180
+ const {message, stack, cause} = err;
179
181
  // @ts-expect-error - code can be helpful for e.g. node errors, so preserve it if it's present.
180
182
  const code = err.code;
181
183
  return {
@@ -183,6 +185,7 @@ class LighthouseError extends Error {
183
185
  message,
184
186
  code,
185
187
  stack,
188
+ cause,
186
189
  };
187
190
  }
188
191
 
@@ -203,17 +206,18 @@ class LighthouseError extends Error {
203
206
  if (possibleError.sentinel === LHERROR_SENTINEL) {
204
207
  // Include sentinel in destructuring so it doesn't end up in `properties`.
205
208
  // eslint-disable-next-line no-unused-vars
206
- const {sentinel, code, stack, ...properties} = /** @type {SerializedLighthouseError} */ (possibleError);
209
+ const {code, stack, cause, properties} = /** @type {SerializedLighthouseError} */ (possibleError);
207
210
  const errorDefinition = LighthouseError.errors[/** @type {keyof typeof ERRORS} */ (code)];
208
- const lhError = new LighthouseError(errorDefinition, properties);
211
+ const lhError = new LighthouseError(errorDefinition, properties, {cause});
209
212
  lhError.stack = stack;
210
213
 
211
214
  return lhError;
212
215
  }
213
216
 
214
217
  if (possibleError.sentinel === ERROR_SENTINEL) {
215
- const {message, code, stack} = /** @type {SerializedBaseError} */ (possibleError);
216
- const error = new Error(message);
218
+ const {message, code, stack, cause} = /** @type {SerializedBaseError} */ (possibleError);
219
+ const opts = cause ? {cause} : undefined;
220
+ const error = new Error(message, opts);
217
221
  Object.assign(error, {code, stack});
218
222
  return error;
219
223
  }
@@ -13,22 +13,22 @@ export type ParsedURL = {
13
13
  host: string;
14
14
  securityOrigin: string;
15
15
  };
16
- /**
17
- * The difference in networkEndTime between the observed Lighthouse networkEndTime and Lightrider's derived networkEndTime.
18
- */
19
16
  export type LightriderStatistics = {
20
17
  /**
21
- * The time spent making a TCP connection (connect + SSL).
18
+ * The difference in networkEndTime between the observed Lighthouse networkEndTime and Lightrider's derived networkEndTime.
22
19
  */
23
20
  endTimeDeltaMs: number;
24
21
  /**
25
- * The time spent requesting a resource from a remote server, we use this to approx RTT.
22
+ * The time spent making a TCP connection (connect + SSL). Note: this is poorly named.
26
23
  */
27
24
  TCPMs: number;
28
25
  /**
29
- * The time spent transferring a resource from a remote server.
26
+ * The time spent requesting a resource from a remote server, we use this to approx RTT. Note: this is poorly names, it really should be "server response time".
30
27
  */
31
28
  requestMs: number;
29
+ /**
30
+ * Time to receive the entire response payload starting the clock on receiving the first fragment (first non-header byte).
31
+ */
32
32
  responseMs: number;
33
33
  };
34
34
  export class NetworkRequest {
@@ -219,6 +219,13 @@ export class NetworkRequest {
219
219
  * LR loses protocol information.
220
220
  */
221
221
  _updateProtocolForLightrider(): void;
222
+ /**
223
+ * TODO(compat): remove M116.
224
+ * `timing.receiveHeadersStart` was added recently, and will be in M116. Until then,
225
+ * set it to receiveHeadersEnd, which is close enough, to allow consumers of NetworkRequest
226
+ * to use the new field without accounting for this backcompat.
227
+ */
228
+ _backfillReceiveHeaderStartTiming(): void;
222
229
  /**
223
230
  * LR gets additional, accurate timing information from its underlying fetch infrastructure. This
224
231
  * is passed in via X-Headers similar to 'X-TotalFetchedSize'.
@@ -57,7 +57,7 @@ import UrlUtils from './url-utils.js';
57
57
 
58
58
  // Lightrider X-Header names for timing information.
59
59
  // See: _updateTransferSizeForLightrider and _updateTimingsForLightrider.
60
- const HEADER_TCP = 'X-TCPMs';
60
+ const HEADER_TCP = 'X-TCPMs'; // Note: this should have been called something like ConnectMs, as it includes SSL.
61
61
  const HEADER_SSL = 'X-SSLMs';
62
62
  const HEADER_REQ = 'X-RequestMs';
63
63
  const HEADER_RES = 'X-ResponseMs';
@@ -80,14 +80,10 @@ const HEADER_PROTOCOL_IS_H2 = 'X-ProtocolIsH2';
80
80
 
81
81
  /**
82
82
  * @typedef LightriderStatistics
83
- * The difference in networkEndTime between the observed Lighthouse networkEndTime and Lightrider's derived networkEndTime.
84
- * @property {number} endTimeDeltaMs
85
- * The time spent making a TCP connection (connect + SSL).
86
- * @property {number} TCPMs
87
- * The time spent requesting a resource from a remote server, we use this to approx RTT.
88
- * @property {number} requestMs
89
- * The time spent transferring a resource from a remote server.
90
- * @property {number} responseMs
83
+ * @property {number} endTimeDeltaMs The difference in networkEndTime between the observed Lighthouse networkEndTime and Lightrider's derived networkEndTime.
84
+ * @property {number} TCPMs The time spent making a TCP connection (connect + SSL). Note: this is poorly named.
85
+ * @property {number} requestMs The time spent requesting a resource from a remote server, we use this to approx RTT. Note: this is poorly names, it really should be "server response time".
86
+ * @property {number} responseMs Time to receive the entire response payload starting the clock on receiving the first fragment (first non-header byte).
91
87
  */
92
88
 
93
89
  /** @type {LH.Util.SelfMap<LH.Crdp.Network.ResourceType>} */
@@ -274,6 +270,7 @@ class NetworkRequest {
274
270
  }
275
271
 
276
272
  this._updateResponseHeadersEndTimeIfNecessary();
273
+ this._backfillReceiveHeaderStartTiming();
277
274
  this._updateTransferSizeForLightrider();
278
275
  this._updateTimingsForLightrider();
279
276
  }
@@ -293,6 +290,7 @@ class NetworkRequest {
293
290
  this.localizedFailDescription = data.errorText;
294
291
 
295
292
  this._updateResponseHeadersEndTimeIfNecessary();
293
+ this._backfillReceiveHeaderStartTiming();
296
294
  this._updateTransferSizeForLightrider();
297
295
  this._updateTimingsForLightrider();
298
296
  }
@@ -315,6 +313,7 @@ class NetworkRequest {
315
313
  this.networkEndTime = data.timestamp * 1000;
316
314
 
317
315
  this._updateResponseHeadersEndTimeIfNecessary();
316
+ this._backfillReceiveHeaderStartTiming();
318
317
  }
319
318
 
320
319
  /**
@@ -341,6 +340,7 @@ class NetworkRequest {
341
340
 
342
341
  if (response.protocol) this.protocol = response.protocol;
343
342
 
343
+ // This is updated in _recomputeTimesWithResourceTiming, if timings are present.
344
344
  this.responseHeadersEndTime = timestamp * 1000;
345
345
 
346
346
  this.transferSize = response.encodedDataLength;
@@ -372,16 +372,27 @@ class NetworkRequest {
372
372
  // Don't recompute times if the data is invalid. RequestTime should always be a thread timestamp.
373
373
  // If we don't have receiveHeadersEnd, we really don't have more accurate data.
374
374
  if (timing.requestTime === 0 || timing.receiveHeadersEnd === -1) return;
375
+
375
376
  // Take networkRequestTime and responseHeadersEndTime from timing data for better accuracy.
377
+ // Before this, networkRequestTime and responseHeadersEndTime were set to bogus values based on
378
+ // CDP event timestamps, though they should be somewhat close to the network timings.
379
+ // Note: requests served from cache never run this function, so they use the "bogus" values.
380
+
376
381
  // Timing's requestTime is a baseline in seconds, rest of the numbers there are ticks in millis.
382
+ // See https://raw.githubusercontent.com/GoogleChrome/lighthouse/main/docs/Network-Timings.svg
377
383
  this.networkRequestTime = timing.requestTime * 1000;
378
384
  const headersReceivedTime = this.networkRequestTime + timing.receiveHeadersEnd;
379
- if (!this.responseHeadersEndTime || this.responseHeadersEndTime < 0) {
380
- this.responseHeadersEndTime = headersReceivedTime;
381
- }
382
-
383
- this.responseHeadersEndTime = Math.min(this.responseHeadersEndTime, headersReceivedTime);
385
+ // This was set in `_onResponse` as that event's timestamp.
386
+ const responseTimestamp = this.responseHeadersEndTime;
387
+
388
+ // Update this.responseHeadersEndTime. All timing values from the netstack (timing) are well-ordered, and
389
+ // so are the timestamps from CDP (which this.responseHeadersEndTime belongs to). It shouldn't be possible
390
+ // that this timing from the netstack is greater than the onResponse timestamp, but just to ensure proper order
391
+ // is maintained we bound the new timing by the network request time and the response timestamp.
392
+ this.responseHeadersEndTime = headersReceivedTime;
393
+ this.responseHeadersEndTime = Math.min(this.responseHeadersEndTime, responseTimestamp);
384
394
  this.responseHeadersEndTime = Math.max(this.responseHeadersEndTime, this.networkRequestTime);
395
+
385
396
  // We're only at responseReceived (_onResponse) at this point.
386
397
  // This networkEndTime may be redefined again after onLoading is done.
387
398
  this.networkEndTime = Math.max(this.networkEndTime, this.responseHeadersEndTime);
@@ -437,6 +448,19 @@ class NetworkRequest {
437
448
  }
438
449
  }
439
450
 
451
+ /**
452
+ * TODO(compat): remove M116.
453
+ * `timing.receiveHeadersStart` was added recently, and will be in M116. Until then,
454
+ * set it to receiveHeadersEnd, which is close enough, to allow consumers of NetworkRequest
455
+ * to use the new field without accounting for this backcompat.
456
+ */
457
+ _backfillReceiveHeaderStartTiming() {
458
+ // Do nothing if a value is already present!
459
+ if (!this.timing || this.timing.receiveHeadersStart !== undefined) return;
460
+
461
+ this.timing.receiveHeadersStart = this.timing.receiveHeadersEnd;
462
+ }
463
+
440
464
  /**
441
465
  * LR gets additional, accurate timing information from its underlying fetch infrastructure. This
442
466
  * is passed in via X-Headers similar to 'X-TotalFetchedSize'.
@@ -473,6 +497,7 @@ class NetworkRequest {
473
497
 
474
498
  // Make sure all times are initialized and are non-negative.
475
499
  const TCPMs = TCPMsHeader ? Math.max(0, parseInt(TCPMsHeader.value)) : 0;
500
+ // This is missing for h2 requests, but present for h1. See b/283843975
476
501
  const SSLMs = SSLMsHeader ? Math.max(0, parseInt(SSLMsHeader.value)) : 0;
477
502
  const requestMs = requestMsHeader ? Math.max(0, parseInt(requestMsHeader.value)) : 0;
478
503
  const responseMs = responseMsHeader ? Math.max(0, parseInt(responseMsHeader.value)) : 0;
@@ -705,7 +705,7 @@ class TraceProcessor {
705
705
  return Boolean(
706
706
  evt.name === 'FrameCommittedInBrowser' &&
707
707
  evt.args.data?.frame &&
708
- evt.args.data.url
708
+ evt.args.data.url !== undefined
709
709
  );
710
710
  }).forEach(evt => {
711
711
  framesById.set(evt.args.data.frame, {
@@ -87,6 +87,10 @@ class UrlUtils {
87
87
  static getOrigin(url) {
88
88
  try {
89
89
  const urlInfo = new URL(url);
90
+ if (urlInfo.protocol === 'chrome-extension:') {
91
+ // Chrome extensions return string "null" as origin, so we reconstruct the extension origin.
92
+ return Util.getChromeExtensionOrigin(url);
93
+ }
90
94
  // check for both host and origin since some URLs schemes like data and file set origin to the
91
95
  // string "null" instead of the object
92
96
  return (urlInfo.host && urlInfo.origin) || null;
package/core/runner.js CHANGED
@@ -429,7 +429,7 @@ class Runner {
429
429
 
430
430
  // Create a friendlier display error and mark it as expected to avoid duplicates in Sentry
431
431
  const error = new LighthouseError(LighthouseError.errors.ERRORED_REQUIRED_ARTIFACT,
432
- {artifactName, errorMessage: artifactError.message});
432
+ {artifactName, errorMessage: artifactError.message}, {cause: artifactError});
433
433
  // @ts-expect-error Non-standard property added to Error
434
434
  error.expected = true;
435
435
  throw error;
@@ -468,7 +468,9 @@ class Runner {
468
468
  Sentry.captureException(err, {tags: {audit: audit.meta.id}, level: 'error'});
469
469
  // Errors become error audit result.
470
470
  const errorMessage = err.friendlyMessage ? err.friendlyMessage : err.message;
471
- auditResult = Audit.generateErrorAuditResult(audit, errorMessage);
471
+ // Prefer the stack trace closest to the error.
472
+ const stack = err.cause?.stack ?? err.stack;
473
+ auditResult = Audit.generateErrorAuditResult(audit, errorMessage, stack);
472
474
  }
473
475
 
474
476
  log.timeEnd(status);