lighthouse 11.7.0-dev.20240415 → 11.7.0-dev.20240417

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 (109) hide show
  1. package/cli/bin.js +0 -7
  2. package/cli/cli-flags.d.ts +1 -5
  3. package/cli/cli-flags.js +1 -5
  4. package/cli/test/smokehouse/config/exclusions.js +2 -0
  5. package/cli/test/smokehouse/core-tests.js +2 -2
  6. package/core/audits/byte-efficiency/render-blocking-resources.js +3 -1
  7. package/core/audits/byte-efficiency/unminified-css.js +2 -2
  8. package/core/audits/byte-efficiency/unused-css-rules.js +3 -1
  9. package/core/audits/font-display.js +2 -2
  10. package/core/audits/resource-summary.d.ts +2 -0
  11. package/core/audits/resource-summary.js +5 -4
  12. package/core/audits/seo/link-text.js +12 -0
  13. package/core/computed/page-dependency-graph.js +1 -6
  14. package/core/computed/resource-summary.d.ts +10 -12
  15. package/core/computed/resource-summary.js +12 -19
  16. package/core/computed/unused-css.d.ts +3 -1
  17. package/core/computed/unused-css.js +6 -5
  18. package/core/config/config-helpers.js +0 -4
  19. package/core/config/constants.js +0 -1
  20. package/core/config/default-config.js +1 -14
  21. package/core/gather/driver/navigation.js +4 -1
  22. package/core/gather/driver/prepare.js +1 -1
  23. package/core/gather/driver/target-manager.js +3 -2
  24. package/core/gather/driver.d.ts +12 -0
  25. package/core/gather/driver.js +26 -0
  26. package/core/gather/gatherers/css-usage.d.ts +0 -21
  27. package/core/gather/gatherers/css-usage.js +9 -119
  28. package/core/gather/gatherers/stylesheets.d.ts +26 -0
  29. package/core/gather/gatherers/stylesheets.js +138 -0
  30. package/core/gather/navigation-runner.js +3 -1
  31. package/core/gather/session.js +2 -1
  32. package/core/lib/emulation.js +2 -2
  33. package/core/lib/i18n/i18n.d.ts +0 -1
  34. package/core/lib/i18n/i18n.js +0 -2
  35. package/core/lib/lh-error.d.ts +9 -0
  36. package/core/lib/lh-error.js +10 -0
  37. package/core/lib/navigation-error.js +3 -0
  38. package/core/runner.js +0 -1
  39. package/dist/report/bundle.esm.js +2 -19
  40. package/dist/report/flow.js +5 -22
  41. package/dist/report/standalone.js +3 -20
  42. package/package.json +3 -3
  43. package/readme.md +2 -2
  44. package/report/assets/styles.css +0 -17
  45. package/report/renderer/components.js +1 -1
  46. package/report/renderer/dom.d.ts +1 -1
  47. package/report/renderer/dom.js +1 -1
  48. package/report/renderer/performance-category-renderer.js +0 -21
  49. package/shared/localization/locales/ar-XB.json +61 -91
  50. package/shared/localization/locales/ar.json +61 -91
  51. package/shared/localization/locales/bg.json +61 -91
  52. package/shared/localization/locales/ca.json +61 -91
  53. package/shared/localization/locales/cs.json +61 -91
  54. package/shared/localization/locales/da.json +61 -91
  55. package/shared/localization/locales/de.json +61 -91
  56. package/shared/localization/locales/el.json +61 -91
  57. package/shared/localization/locales/en-GB.json +61 -91
  58. package/shared/localization/locales/en-US.json +3 -30
  59. package/shared/localization/locales/en-XA.json +61 -91
  60. package/shared/localization/locales/en-XL.json +3 -30
  61. package/shared/localization/locales/es-419.json +61 -91
  62. package/shared/localization/locales/es.json +61 -91
  63. package/shared/localization/locales/fi.json +61 -91
  64. package/shared/localization/locales/fil.json +60 -90
  65. package/shared/localization/locales/fr.json +61 -91
  66. package/shared/localization/locales/he.json +61 -91
  67. package/shared/localization/locales/hi.json +61 -91
  68. package/shared/localization/locales/hr.json +62 -92
  69. package/shared/localization/locales/hu.json +61 -91
  70. package/shared/localization/locales/id.json +61 -91
  71. package/shared/localization/locales/it.json +61 -91
  72. package/shared/localization/locales/ja.json +61 -91
  73. package/shared/localization/locales/ko.json +61 -91
  74. package/shared/localization/locales/lt.json +61 -91
  75. package/shared/localization/locales/lv.json +61 -91
  76. package/shared/localization/locales/nl.json +61 -91
  77. package/shared/localization/locales/no.json +61 -91
  78. package/shared/localization/locales/pl.json +60 -90
  79. package/shared/localization/locales/pt-PT.json +61 -91
  80. package/shared/localization/locales/pt.json +63 -93
  81. package/shared/localization/locales/ro.json +61 -91
  82. package/shared/localization/locales/ru.json +61 -91
  83. package/shared/localization/locales/sk.json +61 -91
  84. package/shared/localization/locales/sl.json +61 -91
  85. package/shared/localization/locales/sr-Latn.json +61 -91
  86. package/shared/localization/locales/sr.json +61 -91
  87. package/shared/localization/locales/sv.json +61 -91
  88. package/shared/localization/locales/ta.json +60 -90
  89. package/shared/localization/locales/te.json +61 -91
  90. package/shared/localization/locales/th.json +61 -91
  91. package/shared/localization/locales/tr.json +61 -91
  92. package/shared/localization/locales/uk.json +61 -91
  93. package/shared/localization/locales/vi.json +60 -90
  94. package/shared/localization/locales/zh-HK.json +61 -91
  95. package/shared/localization/locales/zh-TW.json +60 -90
  96. package/shared/localization/locales/zh.json +61 -91
  97. package/tsconfig.json +0 -1
  98. package/types/artifacts.d.ts +3 -1
  99. package/types/externs.d.ts +0 -2
  100. package/types/gatherer.d.ts +2 -0
  101. package/types/lh.d.ts +0 -2
  102. package/types/lhr/settings.d.ts +0 -4
  103. package/core/audits/performance-budget.d.ts +0 -39
  104. package/core/audits/performance-budget.js +0 -154
  105. package/core/audits/timing-budget.d.ts +0 -43
  106. package/core/audits/timing-budget.js +0 -175
  107. package/core/config/budget.d.ts +0 -74
  108. package/core/config/budget.js +0 -340
  109. package/types/lhr/budget.d.ts +0 -60
@@ -1,26 +1,5 @@
1
1
  export default CSSUsage;
2
2
  declare class CSSUsage extends BaseGatherer {
3
- /** @type {LH.Gatherer.ProtocolSession|undefined} */
4
- _session: LH.Gatherer.ProtocolSession | undefined;
5
- /** @type {Map<string, Promise<LH.Artifacts.CSSStyleSheetInfo|Error>>} */
6
- _sheetPromises: Map<string, Promise<LH.Artifacts.CSSStyleSheetInfo | Error>>;
7
- /**
8
- * Initialize as undefined so we can assert results are fetched.
9
- * @type {LH.Crdp.CSS.RuleUsage[]|undefined}
10
- */
11
- _ruleUsage: LH.Crdp.CSS.RuleUsage[] | undefined;
12
- /**
13
- * @param {LH.Crdp.CSS.StyleSheetAddedEvent} event
14
- */
15
- _onStylesheetAdded(event: LH.Crdp.CSS.StyleSheetAddedEvent): void;
16
- /**
17
- * @param {LH.Gatherer.Context} context
18
- */
19
- startInstrumentation(context: LH.Gatherer.Context): Promise<void>;
20
- /**
21
- * @param {LH.Gatherer.Context} context
22
- */
23
- stopInstrumentation(context: LH.Gatherer.Context): Promise<void>;
24
3
  /**
25
4
  * @param {LH.Gatherer.Context} context
26
5
  * @return {Promise<LH.Artifacts['CSSUsage']>}
@@ -8,145 +8,35 @@
8
8
  * @fileoverview Tracks unused CSS rules.
9
9
  */
10
10
 
11
- import log from 'lighthouse-logger';
12
-
13
11
  import BaseGatherer from '../base-gatherer.js';
14
- import {Sentry} from '../../lib/sentry.js';
15
12
 
16
13
  class CSSUsage extends BaseGatherer {
17
- constructor() {
18
- super();
19
- /** @type {LH.Gatherer.ProtocolSession|undefined} */
20
- this._session = undefined;
21
- /** @type {Map<string, Promise<LH.Artifacts.CSSStyleSheetInfo|Error>>} */
22
- this._sheetPromises = new Map();
23
- /**
24
- * Initialize as undefined so we can assert results are fetched.
25
- * @type {LH.Crdp.CSS.RuleUsage[]|undefined}
26
- */
27
- this._ruleUsage = undefined;
28
- this._onStylesheetAdded = this._onStylesheetAdded.bind(this);
29
- }
30
-
31
14
  /** @type {LH.Gatherer.GathererMeta} */
32
15
  meta = {
33
- supportedModes: ['snapshot', 'timespan', 'navigation'],
16
+ supportedModes: ['snapshot', 'navigation'],
34
17
  };
35
18
 
36
- /**
37
- * @param {LH.Crdp.CSS.StyleSheetAddedEvent} event
38
- */
39
- _onStylesheetAdded(event) {
40
- if (!this._session) throw new Error('Session not initialized');
41
- const styleSheetId = event.header.styleSheetId;
42
- const sheetPromise = this._session.sendCommand('CSS.getStyleSheetText', {styleSheetId})
43
- .then(content => ({
44
- header: event.header,
45
- content: content.text,
46
- }))
47
- .catch(/** @param {Error} err */ (err) => {
48
- log.warn(
49
- 'CSSUsage',
50
- `Error fetching content of stylesheet with URL "${event.header.sourceURL}"`
51
- );
52
- Sentry.captureException(err, {
53
- tags: {
54
- gatherer: 'CSSUsage',
55
- },
56
- extra: {
57
- url: event.header.sourceURL,
58
- },
59
- level: 'error',
60
- });
61
- return err;
62
- });
63
- this._sheetPromises.set(styleSheetId, sheetPromise);
64
- }
65
-
66
19
  /**
67
20
  * @param {LH.Gatherer.Context} context
21
+ * @return {Promise<LH.Artifacts['CSSUsage']>}
68
22
  */
69
- async startInstrumentation(context) {
23
+ async getArtifact(context) {
70
24
  const session = context.driver.defaultSession;
71
- this._session = session;
72
-
73
- // Calling `CSS.enable` will emit events for stylesheets currently on the page.
74
- // We want to ignore these events in navigation mode because they are not relevant to the
75
- // navigation that is about to happen. Adding the event listener *after* calling `CSS.enable`
76
- // ensures that the events for pre-existing stylesheets are ignored.
77
- const isNavigation = context.gatherMode === 'navigation';
78
- if (!isNavigation) {
79
- session.on('CSS.styleSheetAdded', this._onStylesheetAdded);
80
- }
25
+ const executionContext = context.driver.executionContext;
81
26
 
82
27
  await session.sendCommand('DOM.enable');
83
28
  await session.sendCommand('CSS.enable');
84
29
  await session.sendCommand('CSS.startRuleUsageTracking');
85
30
 
86
- if (isNavigation) {
87
- session.on('CSS.styleSheetAdded', this._onStylesheetAdded);
88
- }
89
- }
90
-
91
-
92
- /**
93
- * @param {LH.Gatherer.Context} context
94
- */
95
- async stopInstrumentation(context) {
96
- const session = context.driver.defaultSession;
97
- const coverageResponse = await session.sendCommand('CSS.stopRuleUsageTracking');
98
- this._ruleUsage = coverageResponse.ruleUsage;
99
- session.off('CSS.styleSheetAdded', this._onStylesheetAdded);
100
-
101
- // Ensure we finish fetching all stylesheet contents before disabling the CSS domain
102
- await Promise.all(this._sheetPromises.values());
31
+ // Force style to recompute.
32
+ // Doesn't appear to be necessary in newer versions of Chrome.
33
+ await executionContext.evaluateAsync('getComputedStyle(document.body)');
103
34
 
35
+ const {ruleUsage} = await session.sendCommand('CSS.stopRuleUsageTracking');
104
36
  await session.sendCommand('CSS.disable');
105
37
  await session.sendCommand('DOM.disable');
106
- }
107
-
108
- /**
109
- * @param {LH.Gatherer.Context} context
110
- * @return {Promise<LH.Artifacts['CSSUsage']>}
111
- */
112
- async getArtifact(context) {
113
- const executionContext = context.driver.executionContext;
114
-
115
- if (context.gatherMode === 'snapshot') {
116
- await this.startInstrumentation(context);
117
-
118
- // Force style to recompute.
119
- // Doesn't appear to be necessary in newer versions of Chrome.
120
- await executionContext.evaluateAsync('getComputedStyle(document.body)');
121
-
122
- await this.stopInstrumentation(context);
123
- }
124
-
125
- /** @type {Map<string, LH.Artifacts.CSSStyleSheetInfo>} */
126
- const dedupedStylesheets = new Map();
127
- const sheets = await Promise.all(this._sheetPromises.values());
128
-
129
- for (const sheet of sheets) {
130
- // Erroneous sheets will be reported via sentry and the log.
131
- // We can ignore them here without throwing a fatal error.
132
- if (sheet instanceof Error) {
133
- continue;
134
- }
135
-
136
- // Exclude empty stylesheets.
137
- if (sheet.header.length === 0) {
138
- continue;
139
- }
140
-
141
- dedupedStylesheets.set(sheet.content, sheet);
142
- }
143
-
144
- if (!this._ruleUsage) throw new Error('Issue collecting rule usages');
145
38
 
146
- return {
147
- rules: this._ruleUsage,
148
- stylesheets: Array.from(dedupedStylesheets.values()),
149
- };
39
+ return ruleUsage;
150
40
  }
151
41
  }
152
42
 
@@ -0,0 +1,26 @@
1
+ export default Stylesheets;
2
+ declare class Stylesheets extends BaseGatherer {
3
+ /** @type {LH.Gatherer.ProtocolSession|undefined} */
4
+ _session: LH.Gatherer.ProtocolSession | undefined;
5
+ /** @type {Map<string, Promise<LH.Artifacts.CSSStyleSheetInfo|null>>} */
6
+ _sheetPromises: Map<string, Promise<LH.Artifacts.CSSStyleSheetInfo | null>>;
7
+ /**
8
+ * @param {LH.Crdp.CSS.StyleSheetAddedEvent} event
9
+ */
10
+ _onStylesheetAdded(event: LH.Crdp.CSS.StyleSheetAddedEvent): void;
11
+ /**
12
+ * @param {LH.Gatherer.Context} context
13
+ */
14
+ startInstrumentation(context: LH.Gatherer.Context): Promise<void>;
15
+ /**
16
+ * @param {LH.Gatherer.Context} context
17
+ */
18
+ stopInstrumentation(context: LH.Gatherer.Context): Promise<void>;
19
+ /**
20
+ * @param {LH.Gatherer.Context} context
21
+ * @return {Promise<LH.Artifacts['Stylesheets']>}
22
+ */
23
+ getArtifact(context: LH.Gatherer.Context): Promise<LH.Artifacts['Stylesheets']>;
24
+ }
25
+ import BaseGatherer from '../base-gatherer.js';
26
+ //# sourceMappingURL=stylesheets.d.ts.map
@@ -0,0 +1,138 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2024 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ /**
8
+ * @fileoverview Collects all stylesheets and their contents.
9
+ */
10
+
11
+ import log from 'lighthouse-logger';
12
+
13
+ import BaseGatherer from '../base-gatherer.js';
14
+ import {Sentry} from '../../lib/sentry.js';
15
+
16
+ class Stylesheets extends BaseGatherer {
17
+ constructor() {
18
+ super();
19
+ /** @type {LH.Gatherer.ProtocolSession|undefined} */
20
+ this._session = undefined;
21
+ /** @type {Map<string, Promise<LH.Artifacts.CSSStyleSheetInfo|null>>} */
22
+ this._sheetPromises = new Map();
23
+ this._onStylesheetAdded = this._onStylesheetAdded.bind(this);
24
+ }
25
+
26
+ /** @type {LH.Gatherer.GathererMeta} */
27
+ meta = {
28
+ supportedModes: ['snapshot', 'timespan', 'navigation'],
29
+ };
30
+
31
+ /**
32
+ * @param {LH.Crdp.CSS.StyleSheetAddedEvent} event
33
+ */
34
+ _onStylesheetAdded(event) {
35
+ if (!this._session) throw new Error('Session not initialized');
36
+ const styleSheetId = event.header.styleSheetId;
37
+ const sheetPromise = this._session.sendCommand('CSS.getStyleSheetText', {styleSheetId})
38
+ .then(content => ({
39
+ header: event.header,
40
+ content: content.text,
41
+ }))
42
+ .catch(err => {
43
+ log.warn(
44
+ 'Stylesheets',
45
+ `Error fetching content of stylesheet with URL "${event.header.sourceURL}"`
46
+ );
47
+ Sentry.captureException(err, {
48
+ tags: {
49
+ gatherer: 'Stylesheets',
50
+ },
51
+ extra: {
52
+ url: event.header.sourceURL,
53
+ },
54
+ level: 'error',
55
+ });
56
+ return null;
57
+ });
58
+ this._sheetPromises.set(styleSheetId, sheetPromise);
59
+ }
60
+
61
+ /**
62
+ * @param {LH.Gatherer.Context} context
63
+ */
64
+ async startInstrumentation(context) {
65
+ const session = context.driver.defaultSession;
66
+ this._session = session;
67
+
68
+ // Calling `CSS.enable` will emit events for stylesheets currently on the page.
69
+ // We want to ignore these events in navigation mode because they are not relevant to the
70
+ // navigation that is about to happen. Adding the event listener *after* calling `CSS.enable`
71
+ // ensures that the events for pre-existing stylesheets are ignored.
72
+ const isNavigation = context.gatherMode === 'navigation';
73
+ if (!isNavigation) {
74
+ session.on('CSS.styleSheetAdded', this._onStylesheetAdded);
75
+ }
76
+
77
+ await session.sendCommand('DOM.enable');
78
+ await session.sendCommand('CSS.enable');
79
+
80
+ if (isNavigation) {
81
+ session.on('CSS.styleSheetAdded', this._onStylesheetAdded);
82
+ }
83
+ }
84
+
85
+
86
+ /**
87
+ * @param {LH.Gatherer.Context} context
88
+ */
89
+ async stopInstrumentation(context) {
90
+ const session = context.driver.defaultSession;
91
+ session.off('CSS.styleSheetAdded', this._onStylesheetAdded);
92
+
93
+ // Ensure we finish fetching all stylesheet contents before disabling the CSS domain
94
+ await Promise.all(this._sheetPromises.values());
95
+
96
+ await session.sendCommand('CSS.disable');
97
+ await session.sendCommand('DOM.disable');
98
+ }
99
+
100
+ /**
101
+ * @param {LH.Gatherer.Context} context
102
+ * @return {Promise<LH.Artifacts['Stylesheets']>}
103
+ */
104
+ async getArtifact(context) {
105
+ const executionContext = context.driver.executionContext;
106
+
107
+ if (context.gatherMode === 'snapshot') {
108
+ await this.startInstrumentation(context);
109
+
110
+ // Force style to recompute.
111
+ // Doesn't appear to be necessary in newer versions of Chrome.
112
+ await executionContext.evaluateAsync('getComputedStyle(document.body)');
113
+
114
+ await this.stopInstrumentation(context);
115
+ }
116
+
117
+ /** @type {Map<string, LH.Artifacts.CSSStyleSheetInfo>} */
118
+ const dedupedStylesheets = new Map();
119
+ const sheets = await Promise.all(this._sheetPromises.values());
120
+
121
+ for (const sheet of sheets) {
122
+ // Erroneous sheets will be reported via sentry and the log.
123
+ // We can ignore them here without throwing a fatal error.
124
+ if (!sheet) continue;
125
+
126
+ // Exclude empty stylesheets.
127
+ if (sheet.header.length === 0) {
128
+ continue;
129
+ }
130
+
131
+ dedupedStylesheets.set(sheet.content, sheet);
132
+ }
133
+
134
+ return Array.from(dedupedStylesheets.values());
135
+ }
136
+ }
137
+
138
+ export default Stylesheets;
@@ -92,7 +92,9 @@ async function _navigate(navigationContext) {
92
92
  return {requestedUrl, mainDocumentUrl, navigationError: undefined};
93
93
  } catch (err) {
94
94
  if (!(err instanceof LighthouseError)) throw err;
95
- if (err.code !== 'NO_FCP' && err.code !== 'PAGE_HUNG') throw err;
95
+ if (err.code !== 'NO_FCP' && err.code !== 'PAGE_HUNG' && err.code !== 'TARGET_CRASHED') {
96
+ throw err;
97
+ }
96
98
  if (typeof requestor !== 'string') throw err;
97
99
 
98
100
  // TODO: Make the urls optional here so we don't need to throw an error with a callback requestor.
@@ -100,6 +100,7 @@ class ProtocolSession extends CrdpEventEmitter {
100
100
  /** @type {NodeJS.Timer|undefined} */
101
101
  let timeout;
102
102
  const timeoutPromise = new Promise((resolve, reject) => {
103
+ // Unexpected setTimeout invocation to preserve the error stack. https://github.com/GoogleChrome/lighthouse/issues/13332
103
104
  // eslint-disable-next-line max-len
104
105
  timeout = setTimeout(reject, timeoutMs, new LighthouseError(LighthouseError.errors.PROTOCOL_TIMEOUT, {
105
106
  protocolMethod: method,
@@ -139,7 +140,7 @@ class ProtocolSession extends CrdpEventEmitter {
139
140
  async dispose() {
140
141
  // @ts-expect-error Puppeteer expects the handler params to be type `unknown`
141
142
  this._cdpSession.off('*', this._handleProtocolEvent);
142
- await this._cdpSession.detach();
143
+ await this._cdpSession.detach().catch(e => log.verbose('session', 'detach failed', e.message));
143
144
  }
144
145
  }
145
146
 
@@ -128,7 +128,7 @@ function enableNetworkThrottling(session, throttlingSettings) {
128
128
  * @return {Promise<void>}
129
129
  */
130
130
  function clearNetworkThrottling(session) {
131
- return session.sendCommand('Network.emulateNetworkConditions', NO_THROTTLING_METRICS);
131
+ return session.sendCommandAndIgnore('Network.emulateNetworkConditions', NO_THROTTLING_METRICS);
132
132
  }
133
133
 
134
134
  /**
@@ -146,7 +146,7 @@ function enableCPUThrottling(session, throttlingSettings) {
146
146
  * @return {Promise<void>}
147
147
  */
148
148
  function clearCPUThrottling(session) {
149
- return session.sendCommand('Emulation.setCPUThrottlingRate', NO_CPU_THROTTLE_METRICS);
149
+ return session.sendCommandAndIgnore('Emulation.setCPUThrottlingRate', NO_CPU_THROTTLE_METRICS);
150
150
  }
151
151
 
152
152
  export {
@@ -19,7 +19,6 @@ export namespace UIStrings {
19
19
  const columnRequests: string;
20
20
  const columnName: string;
21
21
  const columnSource: string;
22
- const columnOverBudget: string;
23
22
  const columnElement: string;
24
23
  const columnStartTime: string;
25
24
  const columnDuration: string;
@@ -57,8 +57,6 @@ const UIStrings = {
57
57
  columnName: 'Name',
58
58
  /** Label for a column in a data table; entries will be the locations of JavaScript or CSS code, e.g. the name of a Javascript package or module. */
59
59
  columnSource: 'Source',
60
- /** Label for a column in a data table; entries will be how much a predetermined budget has been exeeded by. Depending on the context, this number could represent an excess in quantity or size of network requests, or, an excess in the duration of time that it takes for the page to load.*/
61
- columnOverBudget: 'Over Budget',
62
60
  /** Label for a column in a data table; entries will be a representation of a DOM element. */
63
61
  columnElement: 'Element',
64
62
  /** Label for a column in a data table; entries will be the number of milliseconds since the page started loading. */
@@ -105,6 +105,7 @@ export namespace UIStrings {
105
105
  const missingRequiredArtifact: string;
106
106
  const erroredRequiredArtifact: string;
107
107
  const oldChromeDoesNotSupportFeature: string;
108
+ const targetCrashed: string;
108
109
  }
109
110
  declare namespace ERRORS {
110
111
  namespace NO_SPEEDLINE_FRAMES {
@@ -340,6 +341,14 @@ declare namespace ERRORS {
340
341
  import message_30 = UIStrings.erroredRequiredArtifact;
341
342
  export { message_30 as message };
342
343
  }
344
+ namespace TARGET_CRASHED {
345
+ const code_31: string;
346
+ export { code_31 as code };
347
+ import message_31 = UIStrings.targetCrashed;
348
+ export { message_31 as message };
349
+ const lhrRuntimeError_22: boolean;
350
+ export { lhrRuntimeError_22 as lhrRuntimeError };
351
+ }
343
352
  }
344
353
  export {};
345
354
  //# sourceMappingURL=lh-error.d.ts.map
@@ -91,6 +91,9 @@ const UIStrings = {
91
91
  * @example {Largest Contentful Paint} featureName
92
92
  * */
93
93
  oldChromeDoesNotSupportFeature: 'This version of Chrome is too old to support \'{featureName}\'. Use a newer version to see full results.',
94
+
95
+ /** Error message explaining that the browser tab that Lighthouse is inspecting has crashed. */
96
+ targetCrashed: 'Browser tab has unexpectedly crashed.',
94
97
  };
95
98
 
96
99
  const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
@@ -423,6 +426,13 @@ const ERRORS = {
423
426
  message: UIStrings.erroredRequiredArtifact,
424
427
  },
425
428
 
429
+ /** The page has crashed and will no longer respond to 99% of CDP commmands. */
430
+ TARGET_CRASHED: {
431
+ code: 'TARGET_CRASHED',
432
+ message: UIStrings.targetCrashed,
433
+ lhrRuntimeError: true,
434
+ },
435
+
426
436
  // Hey! When adding a new error type, update lighthouse-result.proto too.
427
437
  // Only necessary for runtime errors, which come from artifacts or pageLoadErrors.
428
438
  };
@@ -150,6 +150,9 @@ function getPageLoadError(navigationError, context) {
150
150
  let finalRecord;
151
151
  if (mainRecord) {
152
152
  finalRecord = NetworkAnalyzer.resolveRedirects(mainRecord);
153
+ } else {
154
+ // We have no network requests to process, use the navError
155
+ return navigationError;
153
156
  }
154
157
 
155
158
  if (finalRecord?.mimeType === XHTML_MIME_TYPE) {
package/core/runner.js CHANGED
@@ -298,7 +298,6 @@ class Runner {
298
298
  auditMode: undefined,
299
299
  output: undefined,
300
300
  channel: undefined,
301
- budgets: undefined,
302
301
  };
303
302
  const normalizedGatherSettings = Object.assign({}, artifacts.settings, overrides);
304
303
  const normalizedAuditSettings = Object.assign({}, settings, overrides);