lighthouse 12.0.0-dev.20240512 → 12.0.0-dev.20240514

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.
package/cli/run.js CHANGED
@@ -9,7 +9,6 @@
9
9
  import path from 'path';
10
10
  import os from 'os';
11
11
 
12
- import psList from 'ps-list';
13
12
  import * as ChromeLauncher from 'chrome-launcher';
14
13
  import yargsParser from 'yargs-parser';
15
14
  import log from 'lighthouse-logger';
@@ -176,36 +175,6 @@ async function saveResults(runnerResult, flags) {
176
175
  }
177
176
  }
178
177
 
179
- /**
180
- * Attempt to kill the launched Chrome, if defined.
181
- * @param {ChromeLauncher.LaunchedChrome=} launchedChrome
182
- * @return {Promise<void>}
183
- */
184
- async function potentiallyKillChrome(launchedChrome) {
185
- if (!launchedChrome) return;
186
-
187
- /** @type {NodeJS.Timeout} */
188
- let timeout;
189
- const timeoutPromise = new Promise((_, reject) => {
190
- timeout = setTimeout(reject, 5000, new Error('Timed out waiting to kill Chrome'));
191
- });
192
-
193
- return Promise.race([
194
- launchedChrome.kill(),
195
- timeoutPromise,
196
- ]).catch(async err => {
197
- const runningProcesses = await psList();
198
- if (!runningProcesses.some(proc => proc.pid === launchedChrome.pid)) {
199
- log.warn('CLI', 'Warning: Chrome process could not be killed because it already exited.');
200
- return;
201
- }
202
-
203
- throw new Error(`Couldn't quit Chrome process. ${err}`);
204
- }).finally(() => {
205
- clearTimeout(timeout);
206
- });
207
- }
208
-
209
178
  /**
210
179
  * @param {string} url
211
180
  * @param {LH.CliFlags} flags
@@ -214,12 +183,10 @@ async function potentiallyKillChrome(launchedChrome) {
214
183
  */
215
184
  async function runLighthouse(url, flags, config) {
216
185
  /** @param {any} reason */
217
- async function handleTheUnhandled(reason) {
186
+ function handleTheUnhandled(reason) {
218
187
  process.stderr.write(`Unhandled Rejection. Reason: ${reason}\n`);
219
- await potentiallyKillChrome(launchedChrome).catch(() => {});
220
- setTimeout(_ => {
221
- process.exit(1);
222
- }, 100);
188
+ launchedChrome?.kill();
189
+ process.exit(1);
223
190
  }
224
191
  process.on('unhandledRejection', handleTheUnhandled);
225
192
 
@@ -247,7 +214,7 @@ async function runLighthouse(url, flags, config) {
247
214
  await saveResults(runnerResult, flags);
248
215
  }
249
216
 
250
- await potentiallyKillChrome(launchedChrome);
217
+ launchedChrome?.kill();
251
218
  process.removeListener('unhandledRejection', handleTheUnhandled);
252
219
 
253
220
  // Runtime errors indicate something was *very* wrong with the page result.
@@ -265,7 +232,7 @@ async function runLighthouse(url, flags, config) {
265
232
 
266
233
  return runnerResult;
267
234
  } catch (err) {
268
- await potentiallyKillChrome(launchedChrome).catch(() => {});
235
+ launchedChrome?.kill();
269
236
  return printErrorAndExit(err);
270
237
  }
271
238
  }
@@ -97,7 +97,7 @@ async function runBundledLighthouse(url, config, testRunnerOptions) {
97
97
  };
98
98
  } finally {
99
99
  // Clean up and return results.
100
- await launchedChrome.kill();
100
+ launchedChrome.kill();
101
101
  }
102
102
  }
103
103
 
@@ -123,7 +123,7 @@ async function gotoURL(driver, requestor, options) {
123
123
  }
124
124
 
125
125
  const waitConditions = await Promise.race([
126
- driver.fatalRejection.promise,
126
+ session.onCrashPromise(),
127
127
  Promise.all(waitConditionPromises),
128
128
  ]);
129
129
  const timedOut = waitConditions.some(condition => condition.timedOut);
@@ -14,10 +14,6 @@ export class Driver implements LH.Gatherer.Driver {
14
14
  /** @type {Fetcher|undefined} */
15
15
  _fetcher: Fetcher | undefined;
16
16
  defaultSession: import("../../types/gatherer.js").default.ProtocolSession;
17
- fatalRejection: {
18
- promise: Promise<never>;
19
- rej: (_: Error) => void;
20
- };
21
17
  /** @return {LH.Gatherer.Driver['executionContext']} */
22
18
  get executionContext(): ExecutionContext;
23
19
  get fetcher(): any;
@@ -27,14 +23,6 @@ export class Driver implements LH.Gatherer.Driver {
27
23
  url(): Promise<string>;
28
24
  /** @return {Promise<void>} */
29
25
  connect(): Promise<void>;
30
- /**
31
- * If the target crashes, we can't continue gathering.
32
- *
33
- * FWIW, if the target unexpectedly detaches (eg the user closed the tab), pptr will
34
- * catch that and reject into our this._cdpSession.send, which we'll alrady handle appropriately
35
- * @return {void}
36
- */
37
- listenForCrashes(): void;
38
26
  /** @return {Promise<void>} */
39
27
  disconnect(): Promise<void>;
40
28
  }
@@ -8,7 +8,6 @@ import log from 'lighthouse-logger';
8
8
 
9
9
  import {ExecutionContext} from './driver/execution-context.js';
10
10
  import {TargetManager} from './driver/target-manager.js';
11
- import {LighthouseError} from '../lib/lh-error.js';
12
11
  import {Fetcher} from './fetcher.js';
13
12
  import {NetworkMonitor} from './driver/network-monitor.js';
14
13
 
@@ -29,6 +28,7 @@ const throwingSession = {
29
28
  sendCommand: throwNotConnectedFn,
30
29
  sendCommandAndIgnore: throwNotConnectedFn,
31
30
  dispose: throwNotConnectedFn,
31
+ onCrashPromise: throwNotConnectedFn,
32
32
  };
33
33
 
34
34
  /** @implements {LH.Gatherer.Driver} */
@@ -48,12 +48,6 @@ class Driver {
48
48
  this._fetcher = undefined;
49
49
 
50
50
  this.defaultSession = throwingSession;
51
-
52
- // Poor man's Promise.withResolvers()
53
- /** @param {Error} _ */
54
- let rej = _ => {};
55
- const promise = /** @type {Promise<never>} */ (new Promise((_, theRej) => rej = theRej));
56
- this.fatalRejection = {promise, rej};
57
51
  }
58
52
 
59
53
  /** @return {LH.Gatherer.Driver['executionContext']} */
@@ -93,30 +87,11 @@ class Driver {
93
87
  this._networkMonitor = new NetworkMonitor(this._targetManager);
94
88
  await this._networkMonitor.enable();
95
89
  this.defaultSession = this._targetManager.rootSession();
96
- this.listenForCrashes();
97
90
  this._executionContext = new ExecutionContext(this.defaultSession);
98
91
  this._fetcher = new Fetcher(this.defaultSession);
99
92
  log.timeEnd(status);
100
93
  }
101
94
 
102
- /**
103
- * If the target crashes, we can't continue gathering.
104
- *
105
- * FWIW, if the target unexpectedly detaches (eg the user closed the tab), pptr will
106
- * catch that and reject into our this._cdpSession.send, which we'll alrady handle appropriately
107
- * @return {void}
108
- */
109
- listenForCrashes() {
110
- this.defaultSession.on('Inspector.targetCrashed', async _ => {
111
- log.error('Driver', 'Inspector.targetCrashed');
112
- // Manually detach so no more CDP traffic is attempted.
113
- // Don't await, else our rejection will be a 'Target closed' protocol error on cross-talk CDP calls.
114
- void this.defaultSession.dispose();
115
- this.fatalRejection.rej(new LighthouseError(LighthouseError.errors.TARGET_CRASHED));
116
- });
117
- }
118
-
119
-
120
95
  /** @return {Promise<void>} */
121
96
  async disconnect() {
122
97
  if (this.defaultSession === throwingSession) return;
@@ -80,6 +80,7 @@ class InspectorIssues extends BaseGatherer {
80
80
  quirksModeIssue: [],
81
81
  cookieIssue: [],
82
82
  sharedArrayBufferIssue: [],
83
+ sharedDictionaryIssue: [],
83
84
  stylesheetLoadingIssue: [],
84
85
  federatedAuthUserInfoRequestIssue: [],
85
86
  };
@@ -18,6 +18,7 @@ export class ProtocolSession extends ProtocolSession_base implements LH.Gatherer
18
18
  * @param {LH.CrdpEvents[E]} params
19
19
  */
20
20
  _handleProtocolEvent<E extends keyof import("puppeteer-core").ProtocolMapping.Events>(method: E, ...params: import("puppeteer-core").ProtocolMapping.Events[E]): void;
21
+ _targetCrashedPromise: Promise<never>;
21
22
  id(): string;
22
23
  /** @param {LH.Crdp.Target.TargetInfo} targetInfo */
23
24
  setTargetInfo(targetInfo: LH.Crdp.Target.TargetInfo): void;
@@ -53,6 +54,7 @@ export class ProtocolSession extends ProtocolSession_base implements LH.Gatherer
53
54
  * @return {Promise<void>}
54
55
  */
55
56
  dispose(): Promise<void>;
57
+ onCrashPromise(): Promise<never>;
56
58
  }
57
59
  export {};
58
60
  //# sourceMappingURL=session.d.ts.map
@@ -44,6 +44,22 @@ class ProtocolSession extends CrdpEventEmitter {
44
44
  this._handleProtocolEvent = this._handleProtocolEvent.bind(this);
45
45
  // @ts-expect-error Puppeteer expects the handler params to be type `unknown`
46
46
  this._cdpSession.on('*', this._handleProtocolEvent);
47
+
48
+ // If the target crashes, we can't continue gathering.
49
+ // FWIW, if the target unexpectedly detaches (eg the user closed the tab), pptr will
50
+ // catch that and reject in this._cdpSession.send, which is caught by us.
51
+ /** @param {Error} _ */
52
+ let rej = _ => {}; // Poor man's Promise.withResolvers()
53
+ this._targetCrashedPromise = /** @type {Promise<never>} */ (
54
+ new Promise((_, theRej) => rej = theRej));
55
+ this.on('Inspector.targetCrashed', async () => {
56
+ log.error('TargetManager', 'Inspector.targetCrashed');
57
+ // Manually detach so no more CDP traffic is attempted.
58
+ // Don't await, else our rejection will be a 'Target closed' protocol error on cross-talk
59
+ // CDP calls.
60
+ void this.dispose();
61
+ rej(new LighthouseError(LighthouseError.errors.TARGET_CRASHED));
62
+ });
47
63
  }
48
64
 
49
65
  id() {
@@ -114,7 +130,8 @@ class ProtocolSession extends CrdpEventEmitter {
114
130
  log.formatProtocol('method <= browser ERR', {method}, 'error');
115
131
  throw LighthouseError.fromProtocolMessage(method, error);
116
132
  });
117
- const resultWithTimeoutPromise = Promise.race([resultPromise, timeoutPromise]);
133
+ const resultWithTimeoutPromise =
134
+ Promise.race([resultPromise, timeoutPromise, this._targetCrashedPromise]);
118
135
 
119
136
  return resultWithTimeoutPromise.finally(() => {
120
137
  if (timeout) clearTimeout(timeout);
@@ -142,6 +159,10 @@ class ProtocolSession extends CrdpEventEmitter {
142
159
  this._cdpSession.off('*', this._handleProtocolEvent);
143
160
  await this._cdpSession.detach().catch(e => log.verbose('session', 'detach failed', e.message));
144
161
  }
162
+
163
+ onCrashPromise() {
164
+ return this._targetCrashedPromise;
165
+ }
145
166
  }
146
167
 
147
168
  export {ProtocolSession};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lighthouse",
3
3
  "type": "module",
4
- "version": "12.0.0-dev.20240512",
4
+ "version": "12.0.0-dev.20240514",
5
5
  "description": "Automated auditing, performance metrics, and best practices for the web.",
6
6
  "main": "./core/index.js",
7
7
  "bin": {
@@ -185,7 +185,7 @@
185
185
  "chrome-launcher": "^1.1.1",
186
186
  "configstore": "^5.0.1",
187
187
  "csp_evaluator": "1.1.1",
188
- "devtools-protocol": "0.0.1232444",
188
+ "devtools-protocol": "0.0.1299070",
189
189
  "enquirer": "^2.3.6",
190
190
  "http-link-header": "^1.1.1",
191
191
  "intl-messageformat": "^10.5.3",
@@ -198,7 +198,6 @@
198
198
  "metaviewport-parser": "0.3.0",
199
199
  "open": "^8.4.0",
200
200
  "parse-cache-control": "1.0.1",
201
- "ps-list": "^8.0.0",
202
201
  "puppeteer-core": "^22.6.5",
203
202
  "robots-parser": "^3.0.1",
204
203
  "semver": "^5.3.0",
@@ -210,8 +209,8 @@
210
209
  "yargs-parser": "^21.0.0"
211
210
  },
212
211
  "resolutions": {
213
- "puppeteer/**/devtools-protocol": "0.0.1232444",
214
- "puppeteer-core/**/devtools-protocol": "0.0.1232444"
212
+ "puppeteer/**/devtools-protocol": "0.0.1299070",
213
+ "puppeteer-core/**/devtools-protocol": "0.0.1299070"
215
214
  },
216
215
  "repository": "GoogleChrome/lighthouse",
217
216
  "keywords": [
@@ -51,6 +51,7 @@ Array [
51
51
  "propertyRuleIssueDetails",
52
52
  "quirksModeIssueDetails",
53
53
  "sharedArrayBufferIssueDetails",
54
+ "sharedDictionaryIssueDetails",
54
55
  "stylesheetLoadingIssueDetails",
55
56
  ]
56
57
  `);
@@ -545,6 +545,7 @@ declare module Artifacts {
545
545
  quirksModeIssue: Crdp.Audits.QuirksModeIssueDetails[];
546
546
  cookieIssue: Crdp.Audits.CookieIssueDetails[];
547
547
  sharedArrayBufferIssue: Crdp.Audits.SharedArrayBufferIssueDetails[];
548
+ sharedDictionaryIssue: Crdp.Audits.SharedDictionaryIssueDetails[];
548
549
  stylesheetLoadingIssue: Crdp.Audits.StylesheetLoadingIssueDetails[];
549
550
  federatedAuthUserInfoRequestIssue: Crdp.Audits.FederatedAuthUserInfoRequestIssueDetails[];
550
551
  }
@@ -35,6 +35,7 @@ declare module Gatherer {
35
35
  sendCommand<TMethod extends keyof CrdpCommands>(method: TMethod, ...params: CrdpCommands[TMethod]['paramsType']): Promise<CrdpCommands[TMethod]['returnType']>;
36
36
  sendCommandAndIgnore<TMethod extends keyof CrdpCommands>(method: TMethod, ...params: CrdpCommands[TMethod]['paramsType']): Promise<void>;
37
37
  dispose(): Promise<void>;
38
+ onCrashPromise(): Promise<never>;
38
39
  }
39
40
 
40
41
  interface Driver {
@@ -49,8 +50,6 @@ declare module Gatherer {
49
50
  off(event: 'protocolevent', callback: (payload: Protocol.RawEventMessage) => void): void
50
51
  };
51
52
  networkMonitor: NetworkMonitor;
52
- listenForCrashes: (() => void);
53
- fatalRejection: {promise: Promise<never>, rej: (reason: Error) => void}
54
53
  }
55
54
 
56
55
  interface Context<TDependencies extends DependencyKey = DefaultDependenciesKey> {