lighthouse 9.5.0-dev.20220607 → 9.5.0-dev.20220610

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 (40) hide show
  1. package/dist/report/flow.js +3 -3
  2. package/esm-utils.mjs +40 -0
  3. package/flow-report/tsconfig.json +0 -2
  4. package/lighthouse-cli/bin.js +1 -1
  5. package/lighthouse-cli/index.js +1 -4
  6. package/lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js +1 -4
  7. package/lighthouse-core/config/config-helpers.js +16 -14
  8. package/lighthouse-core/config/config.js +37 -19
  9. package/lighthouse-core/fraggle-rock/config/config.js +10 -9
  10. package/lighthouse-core/fraggle-rock/config/filters.js +1 -0
  11. package/lighthouse-core/fraggle-rock/gather/driver.js +8 -12
  12. package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +2 -1
  13. package/lighthouse-core/fraggle-rock/gather/session.js +12 -38
  14. package/lighthouse-core/fraggle-rock/gather/snapshot-runner.js +2 -1
  15. package/lighthouse-core/fraggle-rock/gather/timespan-runner.js +2 -1
  16. package/lighthouse-core/fraggle-rock/user-flow.js +1 -1
  17. package/lighthouse-core/gather/connections/cri.js +3 -17
  18. package/lighthouse-core/gather/driver/network-monitor.js +28 -10
  19. package/lighthouse-core/gather/driver/target-manager.js +29 -17
  20. package/lighthouse-core/gather/driver/wait-for-condition.js +4 -3
  21. package/lighthouse-core/gather/driver.js +2 -12
  22. package/lighthouse-core/gather/gather-runner.js +15 -0
  23. package/lighthouse-core/gather/gatherers/devtools-log.js +56 -2
  24. package/lighthouse-core/index.js +3 -3
  25. package/lighthouse-core/lib/asset-saver.js +3 -4
  26. package/lighthouse-core/lib/network-recorder.js +2 -2
  27. package/lighthouse-core/runner.js +1 -0
  28. package/package.json +4 -4
  29. package/report/generator/tsconfig.json +0 -2
  30. package/report/renderer/swap-locale-feature.js +2 -2
  31. package/report/tsconfig.json +0 -2
  32. package/shared/tsconfig.json +0 -2
  33. package/tsconfig-base.json +6 -1
  34. package/tsconfig.json +2 -3
  35. package/types/artifacts.d.ts +2 -0
  36. package/types/gatherer.d.ts +0 -2
  37. package/types/lhr/lhr.d.ts +2 -0
  38. package/types/lhr/tsconfig.json +0 -2
  39. package/types/lighthouse-logger/index.d.ts +1 -0
  40. package/lighthouse-core/gather/devtools-log.js +0 -61
package/esm-utils.mjs ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @license Copyright 2021 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
+ 'use strict';
7
+ // TODO(esmodules): rename to .js when root is type: module
8
+
9
+ import module from 'module';
10
+ import url from 'url';
11
+ import path from 'path';
12
+
13
+ /**
14
+ * Commonjs equivalent of `require.resolve`.
15
+ * @param {string} packageName
16
+ */
17
+ function resolveModulePath(packageName) {
18
+ const require = module.createRequire(import.meta.url);
19
+ return require.resolve(packageName);
20
+ }
21
+
22
+ /**
23
+ * @param {ImportMeta} importMeta
24
+ */
25
+ function getModulePath(importMeta) {
26
+ return url.fileURLToPath(importMeta.url);
27
+ }
28
+
29
+ /**
30
+ * @param {ImportMeta} importMeta
31
+ */
32
+ function getModuleDirectory(importMeta) {
33
+ return path.dirname(getModulePath(importMeta));
34
+ }
35
+
36
+ export {
37
+ resolveModulePath,
38
+ getModulePath,
39
+ getModuleDirectory,
40
+ };
@@ -1,8 +1,6 @@
1
1
  {
2
2
  "extends": "../tsconfig-base.json",
3
3
  "compilerOptions": {
4
- "outDir": "../.tmp/tsbuildinfo/flow-report",
5
-
6
4
  // Limit to base JS and DOM defs.
7
5
  "lib": ["es2020", "dom", "dom.iterable"],
8
6
  // Selectively include types from node_modules/.
@@ -120,7 +120,7 @@ async function begin() {
120
120
  }
121
121
 
122
122
  if (cliFlags.printConfig) {
123
- const config = lighthouse.generateConfig(configJson, cliFlags);
123
+ const config = await lighthouse.generateConfig(configJson, cliFlags);
124
124
  process.stdout.write(config.getPrintString());
125
125
  return;
126
126
  }
@@ -7,7 +7,4 @@
7
7
 
8
8
  import {begin} from './bin.js';
9
9
 
10
- begin().catch(err => {
11
- process.stderr.write(err.stack);
12
- process.exit(1);
13
- });
10
+ await begin();
@@ -255,7 +255,4 @@ async function begin() {
255
255
  process.exit(exitCode);
256
256
  }
257
257
 
258
- begin().catch(e => {
259
- console.error(e);
260
- process.exit(1);
261
- });
258
+ await begin();
@@ -211,7 +211,8 @@ const bundledModules = new Map(/* BUILD_REPLACE_BUNDLED_MODULES */);
211
211
  * See build-bundle.js
212
212
  * @param {string} requirePath
213
213
  */
214
- function requireWrapper(requirePath) {
214
+ async function requireWrapper(requirePath) {
215
+ // This is async because eventually this function needs to do async dynamic imports.
215
216
  return bundledModules.get(requirePath) || require(requirePath);
216
217
  }
217
218
 
@@ -219,9 +220,9 @@ function requireWrapper(requirePath) {
219
220
  * @param {string} gathererPath
220
221
  * @param {Array<string>} coreGathererList
221
222
  * @param {string=} configDir
222
- * @return {LH.Config.GathererDefn}
223
+ * @return {Promise<LH.Config.GathererDefn>}
223
224
  */
224
- function requireGatherer(gathererPath, coreGathererList, configDir) {
225
+ async function requireGatherer(gathererPath, coreGathererList, configDir) {
225
226
  const coreGatherer = coreGathererList.find(a => a === `${gathererPath}.js`);
226
227
 
227
228
  let requirePath = `../gather/gatherers/${gathererPath}`;
@@ -230,7 +231,7 @@ function requireGatherer(gathererPath, coreGathererList, configDir) {
230
231
  requirePath = resolveModulePath(gathererPath, configDir, 'gatherer');
231
232
  }
232
233
 
233
- const GathererClass = /** @type {GathererConstructor} */ (requireWrapper(requirePath));
234
+ const GathererClass = /** @type {GathererConstructor} */ (await requireWrapper(requirePath));
234
235
 
235
236
  return {
236
237
  instance: new GathererClass(),
@@ -243,7 +244,7 @@ function requireGatherer(gathererPath, coreGathererList, configDir) {
243
244
  * @param {string} auditPath
244
245
  * @param {Array<string>} coreAuditList
245
246
  * @param {string=} configDir
246
- * @return {LH.Config.AuditDefn['implementation']}
247
+ * @return {Promise<LH.Config.AuditDefn['implementation']>}
247
248
  */
248
249
  function requireAudit(auditPath, coreAuditList, configDir) {
249
250
  // See if the audit is a Lighthouse core audit.
@@ -328,9 +329,9 @@ function resolveSettings(settingsJson = {}, overrides = undefined) {
328
329
  * @param {LH.Config.Json} configJSON
329
330
  * @param {string | undefined} configDir
330
331
  * @param {{plugins?: string[]} | undefined} flags
331
- * @return {LH.Config.Json}
332
+ * @return {Promise<LH.Config.Json>}
332
333
  */
333
- function mergePlugins(configJSON, configDir, flags) {
334
+ async function mergePlugins(configJSON, configDir, flags) {
334
335
  const configPlugins = configJSON.plugins || [];
335
336
  const flagPlugins = flags?.plugins || [];
336
337
  const pluginNames = new Set([...configPlugins, ...flagPlugins]);
@@ -342,7 +343,7 @@ function mergePlugins(configJSON, configDir, flags) {
342
343
  const pluginPath = isBundledEnvironment() ?
343
344
  pluginName :
344
345
  resolveModulePath(pluginName, configDir, 'plugin');
345
- const rawPluginJson = requireWrapper(pluginPath);
346
+ const rawPluginJson = await requireWrapper(pluginPath);
346
347
  const pluginJson = ConfigPlugin.parsePlugin(rawPluginJson, pluginName);
347
348
 
348
349
  configJSON = mergeConfigFragment(configJSON, pluginJson);
@@ -360,9 +361,9 @@ function mergePlugins(configJSON, configDir, flags) {
360
361
  * @param {LH.Config.GathererJson} gathererJson
361
362
  * @param {Array<string>} coreGathererList
362
363
  * @param {string=} configDir
363
- * @return {LH.Config.GathererDefn}
364
+ * @return {Promise<LH.Config.GathererDefn>}
364
365
  */
365
- function resolveGathererToDefn(gathererJson, coreGathererList, configDir) {
366
+ async function resolveGathererToDefn(gathererJson, coreGathererList, configDir) {
366
367
  const gathererDefn = expandGathererShorthand(gathererJson);
367
368
  if (gathererDefn.instance) {
368
369
  return {
@@ -391,21 +392,21 @@ function resolveGathererToDefn(gathererJson, coreGathererList, configDir) {
391
392
  * leaving only an array of AuditDefns.
392
393
  * @param {LH.Config.Json['audits']} audits
393
394
  * @param {string=} configDir
394
- * @return {Array<LH.Config.AuditDefn>|null}
395
+ * @return {Promise<Array<LH.Config.AuditDefn>|null>}
395
396
  */
396
- function resolveAuditsToDefns(audits, configDir) {
397
+ async function resolveAuditsToDefns(audits, configDir) {
397
398
  if (!audits) {
398
399
  return null;
399
400
  }
400
401
 
401
402
  const coreList = Runner.getAuditList();
402
- const auditDefns = audits.map(auditJson => {
403
+ const auditDefnsPromises = audits.map(async (auditJson) => {
403
404
  const auditDefn = expandAuditShorthand(auditJson);
404
405
  let implementation;
405
406
  if ('implementation' in auditDefn) {
406
407
  implementation = auditDefn.implementation;
407
408
  } else {
408
- implementation = requireAudit(auditDefn.path, coreList, configDir);
409
+ implementation = await requireAudit(auditDefn.path, coreList, configDir);
409
410
  }
410
411
 
411
412
  return {
@@ -414,6 +415,7 @@ function resolveAuditsToDefns(audits, configDir) {
414
415
  options: auditDefn.options || {},
415
416
  };
416
417
  });
418
+ const auditDefns = await Promise.all(auditDefnsPromises);
417
419
 
418
420
  const mergedAuditDefns = mergeOptionsOfItems(auditDefns);
419
421
  mergedAuditDefns.forEach(audit => validation.assertValidAudit(audit));
@@ -38,6 +38,7 @@ const BASE_ARTIFACT_BLANKS = {
38
38
  HostUserAgent: '',
39
39
  NetworkUserAgent: '',
40
40
  BenchmarkIndex: '',
41
+ BenchmarkIndexes: '',
41
42
  WebAppManifest: '',
42
43
  GatherContext: '',
43
44
  InstallabilityErrors: '',
@@ -148,17 +149,18 @@ function assertValidFlags(flags) {
148
149
  }
149
150
  }
150
151
 
151
-
152
152
  /**
153
153
  * @implements {LH.Config.Config}
154
154
  */
155
155
  class Config {
156
156
  /**
157
- * @constructor
158
- * @param {LH.Config.Json=} configJSON
157
+ * Resolves the provided config (inherits from extended config, if set), resolves
158
+ * all referenced modules, and validates.
159
+ * @param {LH.Config.Json=} configJSON If not provided, uses the default config.
159
160
  * @param {LH.Flags=} flags
161
+ * @return {Promise<Config>}
160
162
  */
161
- constructor(configJSON, flags) {
163
+ static async fromJson(configJSON, flags) {
162
164
  const status = {msg: 'Create config', id: 'lh:init:config'};
163
165
  log.time(status, 'verbose');
164
166
  let configPath = flags?.configPath;
@@ -187,7 +189,7 @@ class Config {
187
189
  const configDir = configPath ? path.dirname(configPath) : undefined;
188
190
 
189
191
  // Validate and merge in plugins (if any).
190
- configJSON = mergePlugins(configJSON, configDir, flags);
192
+ configJSON = await mergePlugins(configJSON, configDir, flags);
191
193
 
192
194
  if (flags) {
193
195
  assertValidFlags(flags);
@@ -197,14 +199,28 @@ class Config {
197
199
  // Augment passes with necessary defaults and require gatherers.
198
200
  const passesWithDefaults = Config.augmentPassesWithDefaults(configJSON.passes);
199
201
  Config.adjustDefaultPassForThrottling(settings, passesWithDefaults);
200
- const passes = Config.requireGatherers(passesWithDefaults, configDir);
202
+ const passes = await Config.requireGatherers(passesWithDefaults, configDir);
201
203
 
204
+ const audits = await Config.requireAudits(configJSON.audits, configDir);
205
+
206
+ const config = new Config(configJSON, {settings, passes, audits});
207
+ log.timeEnd(status);
208
+ return config;
209
+ }
210
+
211
+ /**
212
+ * @deprecated `Config.fromJson` should be used instead.
213
+ * @constructor
214
+ * @param {LH.Config.Json} configJSON
215
+ * @param {{settings: LH.Config.Settings, passes: ?LH.Config.Pass[], audits: ?LH.Config.AuditDefn[]}} opts
216
+ */
217
+ constructor(configJSON, opts) {
202
218
  /** @type {LH.Config.Settings} */
203
- this.settings = settings;
219
+ this.settings = opts.settings;
204
220
  /** @type {?Array<LH.Config.Pass>} */
205
- this.passes = passes;
221
+ this.passes = opts.passes;
206
222
  /** @type {?Array<LH.Config.AuditDefn>} */
207
- this.audits = Config.requireAudits(configJSON.audits, configDir);
223
+ this.audits = opts.audits;
208
224
  /** @type {?Record<string, LH.Config.Category>} */
209
225
  this.categories = configJSON.categories || null;
210
226
  /** @type {?Record<string, LH.Config.Group>} */
@@ -214,8 +230,6 @@ class Config {
214
230
 
215
231
  assertValidPasses(this.passes, this.audits);
216
232
  validation.assertValidCategories(this.categories, this.audits, this.groups);
217
-
218
- log.timeEnd(status);
219
233
  }
220
234
 
221
235
  /**
@@ -498,12 +512,12 @@ class Config {
498
512
  * leaving only an array of AuditDefns.
499
513
  * @param {LH.Config.Json['audits']} audits
500
514
  * @param {string=} configDir
501
- * @return {Config['audits']}
515
+ * @return {Promise<Config['audits']>}
502
516
  */
503
- static requireAudits(audits, configDir) {
517
+ static async requireAudits(audits, configDir) {
504
518
  const status = {msg: 'Requiring audits', id: 'lh:config:requireAudits'};
505
519
  log.time(status, 'verbose');
506
- const auditDefns = resolveAuditsToDefns(audits, configDir);
520
+ const auditDefns = await resolveAuditsToDefns(audits, configDir);
507
521
  log.timeEnd(status);
508
522
  return auditDefns;
509
523
  }
@@ -514,9 +528,9 @@ class Config {
514
528
  * provided) using `resolveModulePath`, returning an array of full Passes.
515
529
  * @param {?Array<Required<LH.Config.PassJson>>} passes
516
530
  * @param {string=} configDir
517
- * @return {Config['passes']}
531
+ * @return {Promise<Config['passes']>}
518
532
  */
519
- static requireGatherers(passes, configDir) {
533
+ static async requireGatherers(passes, configDir) {
520
534
  if (!passes) {
521
535
  return null;
522
536
  }
@@ -524,9 +538,11 @@ class Config {
524
538
  log.time(status, 'verbose');
525
539
 
526
540
  const coreList = Runner.getGathererList();
527
- const fullPasses = passes.map(pass => {
528
- const gathererDefns = pass.gatherers
529
- .map(gatherer => resolveGathererToDefn(gatherer, coreList, configDir));
541
+ const fullPassesPromises = passes.map(async (pass) => {
542
+ const gathererDefns = await Promise.all(
543
+ pass.gatherers
544
+ .map(gatherer => resolveGathererToDefn(gatherer, coreList, configDir))
545
+ );
530
546
 
531
547
  // De-dupe gatherers by artifact name because artifact IDs must be unique at runtime.
532
548
  const uniqueDefns = Array.from(
@@ -536,6 +552,8 @@ class Config {
536
552
 
537
553
  return Object.assign(pass, {gatherers: uniqueDefns});
538
554
  });
555
+ const fullPasses = await Promise.all(fullPassesPromises);
556
+
539
557
  log.timeEnd(status);
540
558
  return fullPasses;
541
559
  }
@@ -121,9 +121,9 @@ function resolveArtifactDependencies(artifact, gatherer, artifactDefnsBySymbol)
121
121
  *
122
122
  * @param {LH.Config.ArtifactJson[]|null|undefined} artifacts
123
123
  * @param {string|undefined} configDir
124
- * @return {LH.Config.AnyArtifactDefn[] | null}
124
+ * @return {Promise<LH.Config.AnyArtifactDefn[] | null>}
125
125
  */
126
- function resolveArtifactsToDefns(artifacts, configDir) {
126
+ async function resolveArtifactsToDefns(artifacts, configDir) {
127
127
  if (!artifacts) return null;
128
128
 
129
129
  const status = {msg: 'Resolve artifact definitions', id: 'lh:config:resolveArtifactsToDefns'};
@@ -133,12 +133,12 @@ function resolveArtifactsToDefns(artifacts, configDir) {
133
133
  const artifactDefnsBySymbol = new Map();
134
134
 
135
135
  const coreGathererList = Runner.getGathererList();
136
- const artifactDefns = artifacts.map(artifactJson => {
136
+ const artifactDefnsPromises = artifacts.map(async (artifactJson) => {
137
137
  /** @type {LH.Config.GathererJson} */
138
138
  // @ts-expect-error - remove when legacy runner path is removed.
139
139
  const gathererJson = artifactJson.gatherer;
140
140
 
141
- const gatherer = resolveGathererToDefn(gathererJson, coreGathererList, configDir);
141
+ const gatherer = await resolveGathererToDefn(gathererJson, coreGathererList, configDir);
142
142
  if (!isFRGathererDefn(gatherer)) {
143
143
  throw new Error(`${gatherer.instance.name} gatherer does not have a Fraggle Rock meta obj`);
144
144
  }
@@ -156,6 +156,7 @@ function resolveArtifactsToDefns(artifacts, configDir) {
156
156
  if (symbol) artifactDefnsBySymbol.set(symbol, artifact);
157
157
  return artifact;
158
158
  });
159
+ const artifactDefns = await Promise.all(artifactDefnsPromises);
159
160
 
160
161
  log.timeEnd(status);
161
162
  return artifactDefns;
@@ -242,28 +243,28 @@ function resolveNavigationsToDefns(navigations, artifactDefns, settings) {
242
243
  /**
243
244
  * @param {LH.Config.Json|undefined} configJSON
244
245
  * @param {ConfigContext} context
245
- * @return {{config: LH.Config.FRConfig, warnings: string[]}}
246
+ * @return {Promise<{config: LH.Config.FRConfig, warnings: string[]}>}
246
247
  */
247
- function initializeConfig(configJSON, context) {
248
+ async function initializeConfig(configJSON, context) {
248
249
  const status = {msg: 'Initialize config', id: 'lh:config'};
249
250
  log.time(status, 'verbose');
250
251
 
251
252
  let {configWorkingCopy, configDir} = resolveWorkingCopy(configJSON, context); // eslint-disable-line prefer-const
252
253
 
253
254
  configWorkingCopy = resolveExtensions(configWorkingCopy);
254
- configWorkingCopy = mergePlugins(configWorkingCopy, configDir, context.settingsOverrides);
255
+ configWorkingCopy = await mergePlugins(configWorkingCopy, configDir, context.settingsOverrides);
255
256
 
256
257
  const settings = resolveSettings(configWorkingCopy.settings || {}, context.settingsOverrides);
257
258
  overrideSettingsForGatherMode(settings, context);
258
259
 
259
- const artifacts = resolveArtifactsToDefns(configWorkingCopy.artifacts, configDir);
260
+ const artifacts = await resolveArtifactsToDefns(configWorkingCopy.artifacts, configDir);
260
261
  const navigations = resolveNavigationsToDefns(configWorkingCopy.navigations, artifacts, settings);
261
262
 
262
263
  /** @type {LH.Config.FRConfig} */
263
264
  let config = {
264
265
  artifacts,
265
266
  navigations,
266
- audits: resolveAuditsToDefns(configWorkingCopy.audits, configDir),
267
+ audits: await resolveAuditsToDefns(configWorkingCopy.audits, configDir),
267
268
  categories: configWorkingCopy.categories || null,
268
269
  groups: configWorkingCopy.groups || null,
269
270
  settings,
@@ -14,6 +14,7 @@ const baseArtifactKeySource = {
14
14
  fetchTime: '',
15
15
  LighthouseRunWarnings: '',
16
16
  BenchmarkIndex: '',
17
+ BenchmarkIndexes: '',
17
18
  settings: '',
18
19
  Timing: '',
19
20
  URL: '',
@@ -16,7 +16,7 @@ const throwNotConnectedFn = () => {
16
16
  };
17
17
 
18
18
  /** @type {LH.Gatherer.FRProtocolSession} */
19
- const defaultSession = {
19
+ const throwingSession = {
20
20
  setTargetInfo: throwNotConnectedFn,
21
21
  hasNextProtocolTimeout: throwNotConnectedFn,
22
22
  getNextProtocolTimeout: throwNotConnectedFn,
@@ -26,8 +26,6 @@ const defaultSession = {
26
26
  off: throwNotConnectedFn,
27
27
  addProtocolMessageListener: throwNotConnectedFn,
28
28
  removeProtocolMessageListener: throwNotConnectedFn,
29
- addSessionAttachedListener: throwNotConnectedFn,
30
- removeSessionAttachedListener: throwNotConnectedFn,
31
29
  sendCommand: throwNotConnectedFn,
32
30
  dispose: throwNotConnectedFn,
33
31
  };
@@ -39,14 +37,12 @@ class Driver {
39
37
  */
40
38
  constructor(page) {
41
39
  this._page = page;
42
- /** @type {LH.Gatherer.FRProtocolSession|undefined} */
43
- this._session = undefined;
44
40
  /** @type {ExecutionContext|undefined} */
45
41
  this._executionContext = undefined;
46
42
  /** @type {Fetcher|undefined} */
47
43
  this._fetcher = undefined;
48
44
 
49
- this.defaultSession = defaultSession;
45
+ this.defaultSession = throwingSession;
50
46
  }
51
47
 
52
48
  /** @return {LH.Gatherer.FRTransitionalDriver['executionContext']} */
@@ -68,20 +64,20 @@ class Driver {
68
64
 
69
65
  /** @return {Promise<void>} */
70
66
  async connect() {
71
- if (this._session) return;
67
+ if (this.defaultSession !== throwingSession) return;
72
68
  const status = {msg: 'Connecting to browser', id: 'lh:driver:connect'};
73
69
  log.time(status);
74
70
  const session = await this._page.target().createCDPSession();
75
- this._session = this.defaultSession = new ProtocolSession(session);
76
- this._executionContext = new ExecutionContext(this._session);
77
- this._fetcher = new Fetcher(this._session);
71
+ this.defaultSession = new ProtocolSession(session);
72
+ this._executionContext = new ExecutionContext(this.defaultSession);
73
+ this._fetcher = new Fetcher(this.defaultSession);
78
74
  log.timeEnd(status);
79
75
  }
80
76
 
81
77
  /** @return {Promise<void>} */
82
78
  async disconnect() {
83
- if (!this._session) return;
84
- await this._session.dispose();
79
+ if (this.defaultSession === throwingSession) return;
80
+ await this.defaultSession.dispose();
85
81
  }
86
82
  }
87
83
 
@@ -310,7 +310,8 @@ async function navigationGather(requestor, options) {
310
310
  const {configContext = {}} = options;
311
311
  log.setLevel(configContext.logLevel || 'error');
312
312
 
313
- const {config} = initializeConfig(options.config, {...configContext, gatherMode: 'navigation'});
313
+ const {config} =
314
+ await initializeConfig(options.config, {...configContext, gatherMode: 'navigation'});
314
315
  const computedCache = new Map();
315
316
  const internalOptions = {
316
317
  skipAboutBlank: configContext.skipAboutBlank,
@@ -13,10 +13,10 @@ const DEFAULT_PROTOCOL_TIMEOUT = 30000;
13
13
  /** @implements {LH.Gatherer.FRProtocolSession} */
14
14
  class ProtocolSession {
15
15
  /**
16
- * @param {LH.Puppeteer.CDPSession} session
16
+ * @param {LH.Puppeteer.CDPSession} cdpSession
17
17
  */
18
- constructor(session) {
19
- this._session = session;
18
+ constructor(cdpSession) {
19
+ this._cdpSession = cdpSession;
20
20
  /** @type {LH.Crdp.Target.TargetInfo|undefined} */
21
21
  this._targetInfo = undefined;
22
22
  /** @type {number|undefined} */
@@ -27,6 +27,7 @@ class ProtocolSession {
27
27
 
28
28
  sessionId() {
29
29
  return this._targetInfo && this._targetInfo.type === 'iframe' ?
30
+ // TODO: use this._session.id() for real session id.
30
31
  this._targetInfo.targetId :
31
32
  undefined;
32
33
  }
@@ -64,7 +65,7 @@ class ProtocolSession {
64
65
  * @param {(...args: LH.CrdpEvents[E]) => void} callback
65
66
  */
66
67
  on(eventName, callback) {
67
- this._session.on(eventName, /** @type {*} */ (callback));
68
+ this._cdpSession.on(eventName, /** @type {*} */ (callback));
68
69
  }
69
70
 
70
71
  /**
@@ -74,28 +75,7 @@ class ProtocolSession {
74
75
  * @param {(...args: LH.CrdpEvents[E]) => void} callback
75
76
  */
76
77
  once(eventName, callback) {
77
- this._session.once(eventName, /** @type {*} */ (callback));
78
- }
79
-
80
- /**
81
- * Bind to the puppeteer `sessionattached` listener and return an LH ProtocolSession.
82
- * @param {(session: ProtocolSession) => void} callback
83
- */
84
- addSessionAttachedListener(callback) {
85
- /** @param {LH.Puppeteer.CDPSession} session */
86
- const listener = session => callback(new ProtocolSession(session));
87
- this._callbackMap.set(callback, listener);
88
- this._getConnection().on('sessionattached', listener);
89
- }
90
-
91
- /**
92
- * Unbind to the puppeteer `sessionattached` listener.
93
- * @param {(session: ProtocolSession) => void} callback
94
- */
95
- removeSessionAttachedListener(callback) {
96
- const listener = this._callbackMap.get(callback);
97
- if (!listener) return;
98
- this._getConnection().off('sessionattached', listener);
78
+ this._cdpSession.once(eventName, /** @type {*} */ (callback));
99
79
  }
100
80
 
101
81
  /**
@@ -114,7 +94,7 @@ class ProtocolSession {
114
94
  sessionId: this.sessionId(),
115
95
  });
116
96
  this._callbackMap.set(callback, listener);
117
- this._session.on('*', /** @type {*} */ (listener));
97
+ this._cdpSession.on('*', /** @type {*} */ (listener));
118
98
  }
119
99
 
120
100
  /**
@@ -123,7 +103,7 @@ class ProtocolSession {
123
103
  removeProtocolMessageListener(callback) {
124
104
  const listener = this._callbackMap.get(callback);
125
105
  if (!listener) return;
126
- this._session.off('*', /** @type {*} */ (listener));
106
+ this._cdpSession.off('*', /** @type {*} */ (listener));
127
107
  }
128
108
 
129
109
  /**
@@ -133,7 +113,7 @@ class ProtocolSession {
133
113
  * @param {(...args: LH.CrdpEvents[E]) => void} callback
134
114
  */
135
115
  off(eventName, callback) {
136
- this._session.off(eventName, /** @type {*} */ (callback));
116
+ this._cdpSession.off(eventName, /** @type {*} */ (callback));
137
117
  }
138
118
 
139
119
  /**
@@ -156,7 +136,7 @@ class ProtocolSession {
156
136
  }));
157
137
  });
158
138
 
159
- const resultPromise = this._session.send(method, ...params);
139
+ const resultPromise = this._cdpSession.send(method, ...params);
160
140
  const resultWithTimeoutPromise = Promise.race([resultPromise, timeoutPromise]);
161
141
 
162
142
  return resultWithTimeoutPromise.finally(() => {
@@ -169,14 +149,8 @@ class ProtocolSession {
169
149
  * @return {Promise<void>}
170
150
  */
171
151
  async dispose() {
172
- this._session.removeAllListeners();
173
- await this._session.detach();
174
- }
175
-
176
- _getConnection() {
177
- const connection = this._session.connection();
178
- if (!connection) throw new Error('Connection has been closed.');
179
- return connection;
152
+ this._cdpSession.removeAllListeners();
153
+ await this._cdpSession.detach();
180
154
  }
181
155
  }
182
156
 
@@ -24,7 +24,8 @@ async function snapshotGather(options) {
24
24
  const {configContext = {}} = options;
25
25
  log.setLevel(configContext.logLevel || 'error');
26
26
 
27
- const {config} = initializeConfig(options.config, {...configContext, gatherMode: 'snapshot'});
27
+ const {config} =
28
+ await initializeConfig(options.config, {...configContext, gatherMode: 'snapshot'});
28
29
  const driver = new Driver(options.page);
29
30
  await driver.connect();
30
31
 
@@ -25,7 +25,8 @@ async function startTimespanGather(options) {
25
25
  const {configContext = {}} = options;
26
26
  log.setLevel(configContext.logLevel || 'error');
27
27
 
28
- const {config} = initializeConfig(options.config, {...configContext, gatherMode: 'timespan'});
28
+ const {config} =
29
+ await initializeConfig(options.config, {...configContext, gatherMode: 'timespan'});
29
30
  const driver = new Driver(options.page);
30
31
  await driver.connect();
31
32
 
@@ -205,7 +205,7 @@ async function auditGatherSteps(gatherSteps, options) {
205
205
  // Step specific configs take precedence over a config for the entire flow.
206
206
  const configJson = gatherStep.config || options.config;
207
207
  const {gatherMode} = artifacts.GatherContext;
208
- const {config} = initializeConfig(configJson, {...configContext, gatherMode});
208
+ const {config} = await initializeConfig(configJson, {...configContext, gatherMode});
209
209
  runnerOptions = {
210
210
  config,
211
211
  computedCache: new Map(),
@@ -33,23 +33,9 @@ class CriConnection extends Connection {
33
33
  * @override
34
34
  * @return {Promise<void>}
35
35
  */
36
- connect() {
37
- return this._runJsonCommand('new')
38
- .then(response => this._connectToSocket(/** @type {LH.DevToolsJsonTarget} */(response)))
39
- .catch(_ => {
40
- // COMPAT: headless didn't support `/json/new` before m59. (#970, crbug.com/699392)
41
- // If no support, we fallback and reuse an existing open tab
42
- log.warn('CriConnection', 'Cannot create new tab; reusing open tab.');
43
- return this._runJsonCommand('list').then(tabs => {
44
- if (!Array.isArray(tabs) || tabs.length === 0) {
45
- return Promise.reject(new Error('Cannot create new tab, and no tabs already open.'));
46
- }
47
- const firstTab = tabs[0];
48
- // first, we activate it to a foreground tab, then we connect
49
- return this._runJsonCommand(`activate/${firstTab.id}`)
50
- .then(() => this._connectToSocket(firstTab));
51
- });
52
- });
36
+ async connect() {
37
+ const response = await this._runJsonCommand('new');
38
+ return this._connectToSocket(/** @type {LH.DevToolsJsonTarget} */(response));
53
39
  }
54
40
 
55
41
  /**